File: //home/arjun/projects/buyercall_new/buyercall/buyercall/blueprints/sms/bw_sms_inbound.py
"""
Governs the logic of the incoming lead text messages.
"""
from __future__ import print_function
import logging
from flask import (
Blueprint,
request,
jsonify,
make_response,
current_app as app,
)
import json
from buyercall.blueprints.block_numbers.models import Block
from buyercall.extensions import csrf, db
from sqlalchemy import and_
from sqlalchemy.orm import load_only
log = logging.getLogger(__name__)
bw_sms_inbound = Blueprint(
'bw_sms_inbound', __name__, template_folder='templates'
)
provider = "bandwidth"
# Testing Bandwidth Migration
@bw_sms_inbound.route('/bw/sms', methods=['GET', 'POST'])
@csrf.exempt
def inbound_sms():
""" Entry point for the incoming SMS or MMS messages. This is specifically for Bandwidth API V2. A Location
exist with 2 application attach to it. One is for SMS/MMS and the other for Voice. This call-back url
is specified on the SMS.MMS application.
"""
from buyercall.lib.util_bandwidth import authenticate_bw_request
authenticated = authenticate_bw_request(request.headers)
if authenticated:
# Fetch the request data. This includes message data for incoming sms/mms
args = request.json
lead_inbound_message(args)
status_code = 201
message = jsonify(message='Authentication passed.')
response = make_response(message, status_code)
return response
else:
print('Authentication failed')
status_code = 401
message = jsonify(message='Authentication failed.')
response = make_response(message, status_code)
return response
def lead_inbound_message(args):
# Declare the args parameters to get sms/mms message data
message = args[0].get('message', '')
from_ = message.get('from', '')
to_ = args[0].get('to', '')
if to_:
# Determine the BuyerCall to establish the inbound id and partnership account
from ..phonenumbers.models import Phone
from ..partnership.models import PartnershipAccount,Partnership
inbound = Phone.query.filter(Phone.phonenumber == to_).first()
partnership_account_id = inbound.partnership_account_id
partnership_account = PartnershipAccount.query.get(partnership_account_id) if partnership_account_id else None
partnership_id = partnership_account.partnership_id
# start save inbound-id to resource field of request-log data
try:
from buyercall.blueprints.sysadmin.models import RequestLog
request_id = request.environ.get('HTTP_X_REQUEST_ID')
partnership = Partnership.query.get(partnership_id) if partnership_id else None
update_data = {
"partnership_id": partnership_id if partnership_id else None,
'partnership_name': partnership.name if partnership else None,
"partnership_account_id": partnership_account_id if partnership_account_id else None,
'partnership_account_name': partnership_account.name if partnership_account else None,
"resource": inbound.id if inbound else None,
'user_id': None
}
RequestLog().update_record(request_id, update_data)
# updating the request log with the inbound id
# RequestLog().update_record(
# request_id, {"resource": inbound.id if inbound else None})
except Exception as e:
print(f"The exception in saving inbound-id in lead_inbound_message is {e}")
# end save inbound-id to resource field of request-log data
if inbound:
partnership_account_id = inbound.partnership_account_id
else:
log.info('Unable to match a BC number to to_ field number. args: {}'.format(args))
return ''
delivery_description = args[0].get('description', '')
text = message.get('text', '')
lowercase_text_body = text.lower()
# Determine if there's media attached to the message making it a MMS instead of a sms
media = message.get('media', '')
if media:
message_type = 'mms'
else:
message_type = 'sms'
else:
log.info('Something went wrong with receiving a sms/mms message. args: {}'.format(args))
return ''
# First check if the phone number sending the message is not blocked
is_number_blocked = Block.blocked(inbound.id, from_)
if is_number_blocked:
log.info('This phone number: {} was blocked and the message was not received'.format(from_))
return ''
# Add message to message table
from .bw_sms_tasks import add_bw_message_lead
added_message = add_bw_message_lead(inbound.id, inbound.phonenumber, message_type, message, delivery_description,
partnership_account_id, provider)
if not added_message:
return log.error('Something went wrong with adding the inbound message to the database. '
'Most likely a field length issue')
from ..contacts.models import Contact
contact = Contact.query\
.filter(and_(Contact.id == added_message.contact_id,
Contact.partnership_account_id == partnership_account_id)).first()
# A contact can send a HELP command that needs to return help information to the contact
help_keyword_list = [' help', 'help ', 'help']
if lowercase_text_body in help_keyword_list:
from ..sms.bw_sms_tasks import bw_send_sms
from ..partnership.models import PartnershipAccount
account = PartnershipAccount.query\
.filter(PartnershipAccount.id == partnership_account_id).options(load_only('name')).first()
log.info(account.name)
sender = from_
from ..filters import format_phone_number_bracket
return_text = account.name + ': Help at ' + format_phone_number_bracket(to_) + '. ' + 'Msg&data rates may apply. Reply STOP to unsubscribe.'
bw_send_sms(inbound.id, sender, return_text, agent_list=None)
return ''
# A contact can subscribed again to receive messages. First we need to look for the command
subscribe_keyword_list = ['unstop', ' unstop', 'unstop ', 'start', ' start', 'start ', 'nonarret']
# Check to see if the body text is one of the the subscribed keywords
if lowercase_text_body in subscribe_keyword_list:
if contact:
contact.is_unsubscribe = False
db.session.commit()
else:
log.error('A contact id was not found for the message being sent from: {} using phone number {}'
'and we were unable to unsubscribe the contact from receiving messages'.format(from_, inbound.id))
return ''
if contact.is_unsubscribe:
sender = from_
from ..sms.bw_sms_tasks import bw_send_sms
return_text = 'You replied with the word "STOP" which blocks all texts sent from this number. ' \
'Text back "UNSTOP" to receive messages again.'
bw_send_sms(inbound.id, sender, return_text, agent_list=None)
return ''
# Check to see if the message contains either the text stop or unsubscribe
# If the message contains these keywords we do not forward the message
# And we update the contact as do_not_contact and unsubscribe
unsubscribed_keyword_list = ['stop', ' stop', 'stop ', 'unsubscribe', ' unsubscribe',
'unsubscribe ', 'un-subscribe', 'unsubcribed', 'arret']
# Check to see the text body text is one of the unsubscribe/stop keywords
if lowercase_text_body in unsubscribed_keyword_list:
if contact:
contact.is_unsubscribe = True
db.session.commit()
else:
log.error('A contact id was not found for the message being sent from: {} using phone number {}'
'and we were unable to unsubscribe the contact from receiving messages'.format(from_, inbound.id))
log.info('Message not sent due to unsubscribe keyword in received message text.')
return ''
# Forward SMS/MMS msg using function in bw celery task to assigned agents
from ..sms.bw_sms_tasks import bw_forward_sms
bw_forward_sms.apply_async(args=[inbound.id, added_message.id], countdown=1)
# This checks the phone number SMS settings first and then perform actions based on the sms configuration
if inbound.routing_config.get('configSMSSetup') and inbound.routing_config.get('SMSAutoReply'):
from ..sms.views import send_text_message
text_body = inbound.routing_config.get('SMSAutoReplyText') + ' Reply STOP to unsubscribe.'
media_url = inbound.routing_config.get('SMSAutoReplyImageUrl', '')
if media_url:
media_key = (media_url.split('/', 3)[-1]).split('?')[0].replace('%20', ' ')
bucket = app.config['MMS_BUCKET']
from buyercall.lib.util_boto3_s3 import generate_presigned_aws_url
media_url = generate_presigned_aws_url(media_key, bucket)
send_text_message(inbound.id, from_, text_body, media_url)
# This checks the phone number SMS settings first and then perform keyword actions based on the sms config
if inbound.routing_config.get('configSMSSetup') and inbound.routing_config.get('SMSRuleOne'):
from ..sms.views import send_text_message
text_body = lowercase_text_body
lead = from_
# call_agent = inbound.routing_config.get('SMSRuleOneCallAgent')
keyword = (inbound.routing_config.get('SMSRuleOneKeyword')).lower()
action = inbound.routing_config.get('SMSRuleOneAction')
recipient = inbound.routing_config.get('SMSRuleOneRecipientType')
rule_sms_body = inbound.routing_config.get('SMSRuleOneSMSBody')
rule_sms_agent = inbound.routing_config.get('SMSRuleOneRecipientAgent')
rule_sms_custom = inbound.routing_config.get('SMSRuleOneRecipientCustom')
# rule_sms_email_body = inbound.routing_config.get('SMSRuleOneEmailBody')
# custom_email_addresses = inbound.routing_config.get('SMSRuleOneEmails')
# email_agent = inbound.routing_config.get('SMSRuleOneEmailAgent')
if keyword:
if keyword in text_body:
log.info('the keyword: {} is in the message'.format(keyword))
if action == 'sms' and recipient == 'lead':
if lead:
send_text_message(inbound.id, lead, rule_sms_body, media=None)
else:
log.warning('There is no lead phone number set to receive the sms rule base sms.'
' Inbound id: {}'.format(inbound.id))
if action == 'sms' and recipient == 'agent':
from ..agents.models import Agent
if rule_sms_agent:
agent_no = Agent.query.filter(Agent.id == rule_sms_agent).first()
send_text_message(inbound.id, agent_no.phonenumber, rule_sms_body, media=None)
else:
log.warning('There is no agent set to receive the sms rule base sms. '
'Inbound id: {}'.format(inbound.id))
if action == 'sms' and recipient == 'custom':
if rule_sms_custom:
send_text_message(inbound.id, rule_sms_custom, rule_sms_body, media=None)
else:
log.warning('There is no custom phone number set to receive the sms rule base sms. '
'Inbound id: {}'.format(inbound.id))
if action == 'unsubscribe' and recipient == 'lead':
from ..contacts.models import Contact
contact_lead = Contact.query.filter(Contact.phonenumber_1 == lead)\
.filter(Contact.partnership_account_id == partnership_account_id).first()
contact_lead.is_unsubscribe = True
db.session.add(contact_lead)
db.session.commit()
else:
log.info('The SMS Rule is turned off for this phone number: {}'.format(inbound.phonenumber))
return ''
@bw_sms_inbound.route('/bw/sms/status/callback', methods=['GET', 'POST'])
@csrf.exempt
def sms_status_callback():
""" Entry point for the incoming SMS or MMS messages status callback. This is specifically for Bandwidth API
V2. A Location exist with 2 application attach to it. One is for SMS/MMS and the other for Voice. This call-back url
is specified on the SMS.MMS application.
"""
from buyercall.lib.util_bandwidth import authenticate_bw_request
authenticated = authenticate_bw_request(request.headers)
if authenticated:
# Fetch the request data. This includes message data for incoming sms/mms
args = request.json or request.args
print(args)
status_code = 201
# start save inbound-id to resource field of request-log data
from buyercall.blueprints.phonenumbers import Phone
try:
to_ = args[0].get('to', '')
inbound = Phone.query.filter(Phone.phonenumber == to_).first()
# start save inbound-id to resource field of request-log data
from ..partnership.models import PartnershipAccount,Partnership
partnership_account_id = inbound.partnership_account_id
partnership_account = PartnershipAccount.query.get(partnership_account_id) if partnership_account_id else None
partnership_id = partnership_account.partnership_id
# start save inbound-id to resource field of request-log data
partnership = Partnership.query.get(partnership_id) if partnership_id else None
from buyercall.blueprints.sysadmin.models import RequestLog
request_id = request.environ.get('HTTP_X_REQUEST_ID')
update_data = {
"partnership_id": partnership_id if partnership_id else None,
'partnership_name': partnership.name if partnership else None,
"partnership_account_id": partnership_account_id if partnership_account_id else None,
'partnership_account_name': partnership_account.name if partnership_account else None,
"resource": inbound.id if inbound else None,
'user_id': None}
RequestLog().update_record(request_id, update_data)
# # updating the request log with the inbound id
# RequestLog().update_record(
# request_id, {"resource": inbound.id if inbound else None})
except Exception as e:
print(f"The Exception in sms_status_callback is {e}")
# end save inbound-id to resource field of request-log data
message = args[0].get('message', '')
msg_id = message.get('id', '')
delivery_code = args[0].get('errorCode', 201)
delivery_description = args[0].get('description', '')
delivery_type = args[0].get('type', '')
from ..sms.bw_sms_tasks import update_msg_record
update_msg_record.apply_async(args=[msg_id, delivery_code, delivery_type, delivery_description], countdown=1)
message = jsonify(message='Authentication passed.')
response = make_response(message, status_code)
return response
else:
print('Authentication failed')
status_code = 401
message = jsonify(message='Authentication failed.')
response = make_response(message, status_code)
return response