<s> import logging <EOL> import random <EOL> import string <EOL> from django . conf import settings <EOL> from django . core . exceptions import ValidationError <EOL> from corehq . apps . smsbillables . utils import log_smsbillables_error <EOL> from corehq . apps . users . models import CommCareUser , CouchUser , WebUser <EOL> from django . forms import forms <EOL> from corehq . apps . users . util import format_username <EOL> from dimagi . utils . modules import to_function <EOL> from dimagi . utils . logging import notify_exception <EOL> from corehq import privileges <EOL> from corehq . apps . accounting . utils import domain_has_privilege <EOL> from corehq . apps . sms . util import ( clean_phone_number , clean_text , <EOL> get_backend_classes ) <EOL> from corehq . apps . sms . models import ( OUTGOING , INCOMING , <EOL> PhoneBlacklist , SMS , SelfRegistrationInvitation , MessagingEvent , <EOL> SQLMobileBackend , SQLSMSBackend , QueuedSMS ) <EOL> from corehq . apps . sms . messages import ( get_message , MSG_OPTED_IN , <EOL> MSG_OPTED_OUT , MSG_DUPLICATE_USERNAME , MSG_USERNAME_TOO_LONG , <EOL> MSG_REGISTRATION_WELCOME_CASE , MSG_REGISTRATION_WELCOME_MOBILE_WORKER ) <EOL> from corehq . apps . sms . mixin import ( VerifiedNumber , <EOL> BadSMSConfigException ) <EOL> from corehq . apps . domain . models import Domain <EOL> from datetime import datetime <EOL> from corehq . apps . sms . util import register_sms_contact , strip_plus <EOL> REGISTRATION_KEYWORDS = [ "<STR_LIT>" ] <EOL> REGISTRATION_MOBILE_WORKER_KEYWORDS = [ "<STR_LIT>" ] <EOL> class DomainScopeValidationError ( Exception ) : <EOL> pass <EOL> class BackendAuthorizationException ( Exception ) : <EOL> pass <EOL> def get_utcnow ( ) : <EOL> """<STR_LIT>""" <EOL> return datetime . utcnow ( ) <EOL> class MessageMetadata ( object ) : <EOL> def __init__ ( self , * args , ** kwargs ) : <EOL> self . workflow = kwargs . get ( "<STR_LIT>" , None ) <EOL> self . xforms_session_couch_id = kwargs . get ( "<STR_LIT>" , None ) <EOL> self . reminder_id = kwargs . get ( "<STR_LIT>" , None ) <EOL> self . chat_user_id = kwargs . get ( "<STR_LIT>" , None ) <EOL> self . ignore_opt_out = kwargs . get ( "<STR_LIT>" , None ) <EOL> self . location_id = kwargs . get ( '<STR_LIT>' , None ) <EOL> self . messaging_subevent_id = kwargs . get ( '<STR_LIT>' , None ) <EOL> def add_msg_tags ( msg , metadata ) : <EOL> if msg and metadata : <EOL> fields = ( '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' , <EOL> '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ) <EOL> for field in fields : <EOL> value = getattr ( metadata , field ) <EOL> if value is not None : <EOL> setattr ( msg , field , value ) <EOL> msg . save ( ) <EOL> def log_sms_exception ( msg ) : <EOL> direction = "<STR_LIT>" if msg . direction == OUTGOING else "<STR_LIT>" <EOL> message = "<STR_LIT>" % direction <EOL> notify_exception ( None , message = message , details = { <EOL> '<STR_LIT>' : msg . domain , <EOL> '<STR_LIT:date>' : msg . date , <EOL> '<STR_LIT>' : msg . couch_id , <EOL> } ) <EOL> def get_location_id_by_contact ( domain , contact ) : <EOL> if isinstance ( contact , CommCareUser ) : <EOL> return contact . location_id <EOL> elif isinstance ( contact , WebUser ) : <EOL> return contact . get_location_id ( domain ) <EOL> else : <EOL> return None <EOL> def get_location_id_by_verified_number ( v ) : <EOL> return get_location_id_by_contact ( v . domain , v . owner ) <EOL> def get_sms_class ( ) : <EOL> return QueuedSMS if settings . SMS_QUEUE_ENABLED else SMS <EOL> def send_sms ( domain , contact , phone_number , text , metadata = None ) : <EOL> """<STR_LIT>""" <EOL> if phone_number is None : <EOL> return False <EOL> if isinstance ( phone_number , int ) or isinstance ( phone_number , long ) : <EOL> phone_number = str ( phone_number ) <EOL> phone_number = clean_phone_number ( phone_number ) <EOL> msg = get_sms_class ( ) ( <EOL> domain = domain , <EOL> phone_number = phone_number , <EOL> direction = OUTGOING , <EOL> date = get_utcnow ( ) , <EOL> backend_id = None , <EOL> location_id = get_location_id_by_contact ( domain , contact ) , <EOL> text = text <EOL> ) <EOL> if contact : <EOL> msg . couch_recipient = contact . _id <EOL> msg . couch_recipient_doc_type = contact . doc_type <EOL> add_msg_tags ( msg , metadata ) <EOL> return queue_outgoing_sms ( msg ) <EOL> def send_sms_to_verified_number ( verified_number , text , metadata = None , <EOL> logged_subevent = None ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> backend = verified_number . backend <EOL> except BadSMSConfigException as e : <EOL> if logged_subevent : <EOL> logged_subevent . error ( MessagingEvent . ERROR_GATEWAY_NOT_FOUND , <EOL> additional_error_text = e . message ) <EOL> return False <EOL> raise <EOL> msg = get_sms_class ( ) ( <EOL> couch_recipient_doc_type = verified_number . owner_doc_type , <EOL> couch_recipient = verified_number . owner_id , <EOL> phone_number = "<STR_LIT:+>" + str ( verified_number . phone_number ) , <EOL> direction = OUTGOING , <EOL> date = get_utcnow ( ) , <EOL> domain = verified_number . domain , <EOL> backend_id = backend . couch_id , <EOL> location_id = get_location_id_by_verified_number ( verified_number ) , <EOL> text = text <EOL> ) <EOL> add_msg_tags ( msg , metadata ) <EOL> return queue_outgoing_sms ( msg ) <EOL> def send_sms_with_backend ( domain , phone_number , text , backend_id , metadata = None ) : <EOL> phone_number = clean_phone_number ( phone_number ) <EOL> msg = get_sms_class ( ) ( <EOL> domain = domain , <EOL> phone_number = phone_number , <EOL> direction = OUTGOING , <EOL> date = get_utcnow ( ) , <EOL> backend_id = backend_id , <EOL> text = text <EOL> ) <EOL> add_msg_tags ( msg , metadata ) <EOL> return queue_outgoing_sms ( msg ) <EOL> def send_sms_with_backend_name ( domain , phone_number , text , backend_name , metadata = None ) : <EOL> phone_number = clean_phone_number ( phone_number ) <EOL> backend = SQLMobileBackend . load_by_name ( SQLMobileBackend . SMS , domain , backend_name ) <EOL> msg = get_sms_class ( ) ( <EOL> domain = domain , <EOL> phone_number = phone_number , <EOL> direction = OUTGOING , <EOL> date = get_utcnow ( ) , <EOL> backend_id = backend . couch_id , <EOL> text = text <EOL> ) <EOL> add_msg_tags ( msg , metadata ) <EOL> return queue_outgoing_sms ( msg ) <EOL> def enqueue_directly ( msg ) : <EOL> try : <EOL> from corehq . apps . sms . management . commands . run_sms_queue import SMSEnqueuingOperation <EOL> SMSEnqueuingOperation ( ) . enqueue_directly ( msg ) <EOL> except : <EOL> pass <EOL> def queue_outgoing_sms ( msg ) : <EOL> if settings . SMS_QUEUE_ENABLED : <EOL> try : <EOL> msg . processed = False <EOL> msg . datetime_to_process = msg . date <EOL> msg . queued_timestamp = get_utcnow ( ) <EOL> msg . save ( ) <EOL> except : <EOL> log_sms_exception ( msg ) <EOL> return False <EOL> enqueue_directly ( msg ) <EOL> return True <EOL> else : <EOL> msg . processed = True <EOL> msg_sent = send_message_via_backend ( msg ) <EOL> msg . publish_change ( ) <EOL> if msg_sent : <EOL> create_billable_for_sms ( msg ) <EOL> return msg_sent <EOL> def send_message_via_backend ( msg , backend = None , orig_phone_number = None ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> msg . text = clean_text ( msg . text ) <EOL> except Exception : <EOL> logging . exception ( "<STR_LIT>" % ( msg . date , msg . domain ) ) <EOL> try : <EOL> if not domain_has_privilege ( msg . domain , privileges . OUTBOUND_SMS ) : <EOL> raise Exception ( <EOL> ( "<STR_LIT>" <EOL> "<STR_LIT>" ) % msg . domain <EOL> ) <EOL> phone_obj = PhoneBlacklist . get_by_phone_number_or_none ( msg . phone_number ) <EOL> if phone_obj and not phone_obj . send_sms : <EOL> if msg . ignore_opt_out and phone_obj . can_opt_in : <EOL> pass <EOL> else : <EOL> msg . set_system_error ( SMS . ERROR_PHONE_NUMBER_OPTED_OUT ) <EOL> return False <EOL> if not backend : <EOL> backend = msg . outbound_backend <EOL> if backend . domain_is_authorized ( msg . domain ) : <EOL> backend . send ( msg , orig_phone_number = orig_phone_number ) <EOL> else : <EOL> raise BackendAuthorizationException ( <EOL> "<STR_LIT>" % ( msg . domain , backend . pk ) <EOL> ) <EOL> msg . backend_api = backend . hq_api_id <EOL> msg . backend_id = backend . couch_id <EOL> msg . save ( ) <EOL> return True <EOL> except Exception : <EOL> log_sms_exception ( msg ) <EOL> return False <EOL> def random_password ( ) : <EOL> """<STR_LIT>""" <EOL> chars = string . ascii_uppercase + string . ascii_lowercase + string . digits <EOL> return '<STR_LIT>' . join ( random . choice ( chars ) for x in range ( <NUM_LIT:15> ) ) <EOL> def process_username ( username , domain ) : <EOL> from corehq . apps . users . forms import ( clean_mobile_worker_username , <EOL> get_mobile_worker_max_username_length ) <EOL> max_length = get_mobile_worker_max_username_length ( domain ) <EOL> return clean_mobile_worker_username ( <EOL> domain , <EOL> username , <EOL> name_too_long_message = get_message ( MSG_USERNAME_TOO_LONG , context = ( username , max_length ) ) , <EOL> name_exists_message = get_message ( MSG_DUPLICATE_USERNAME , context = ( username , ) ) <EOL> ) <EOL> def is_registration_text ( text ) : <EOL> keywords = text . strip ( ) . upper ( ) . split ( ) <EOL> if len ( keywords ) == <NUM_LIT:0> : <EOL> return False <EOL> return keywords [ <NUM_LIT:0> ] in REGISTRATION_KEYWORDS <EOL> def process_pre_registration ( msg ) : <EOL> """<STR_LIT>""" <EOL> invitation = SelfRegistrationInvitation . by_phone ( msg . phone_number ) <EOL> if not invitation : <EOL> return False <EOL> domain = Domain . get_by_name ( invitation . domain , strict = True ) <EOL> if not domain . sms_mobile_worker_registration_enabled : <EOL> return False <EOL> text = msg . text . strip ( ) <EOL> if is_registration_text ( text ) : <EOL> return False <EOL> elif invitation . phone_type : <EOL> if invitation . phone_type == SelfRegistrationInvitation . PHONE_TYPE_ANDROID : <EOL> invitation . send_step2_android_sms ( ) <EOL> elif invitation . phone_type == SelfRegistrationInvitation . PHONE_TYPE_OTHER : <EOL> invitation . send_step2_java_sms ( ) <EOL> return True <EOL> elif text == '<STR_LIT:1>' : <EOL> invitation . phone_type = SelfRegistrationInvitation . PHONE_TYPE_ANDROID <EOL> invitation . save ( ) <EOL> invitation . send_step2_android_sms ( ) <EOL> return True <EOL> elif text == '<STR_LIT:2>' : <EOL> invitation . phone_type = SelfRegistrationInvitation . PHONE_TYPE_OTHER <EOL> invitation . save ( ) <EOL> invitation . send_step2_java_sms ( ) <EOL> return True <EOL> else : <EOL> invitation . send_step1_sms ( ) <EOL> return True <EOL> def process_sms_registration ( msg ) : <EOL> """<STR_LIT>""" <EOL> registration_processed = False <EOL> text_words = msg . text . upper ( ) . split ( ) <EOL> keyword1 = text_words [ <NUM_LIT:0> ] if len ( text_words ) > <NUM_LIT:0> else "<STR_LIT>" <EOL> keyword2 = text_words [ <NUM_LIT:1> ] . lower ( ) if len ( text_words ) > <NUM_LIT:1> else "<STR_LIT>" <EOL> keyword3 = text_words [ <NUM_LIT:2> ] if len ( text_words ) > <NUM_LIT:2> else "<STR_LIT>" <EOL> keyword4 = text_words [ <NUM_LIT:3> ] if len ( text_words ) > <NUM_LIT:3> else "<STR_LIT>" <EOL> cleaned_phone_number = strip_plus ( msg . phone_number ) <EOL> if is_registration_text ( msg . text ) and keyword2 != "<STR_LIT>" : <EOL> domain = Domain . get_by_name ( keyword2 , strict = True ) <EOL> if domain is not None : <EOL> if domain_has_privilege ( domain , privileges . INBOUND_SMS ) : <EOL> if keyword3 in REGISTRATION_MOBILE_WORKER_KEYWORDS and domain . sms_mobile_worker_registration_enabled : <EOL> if keyword4 != '<STR_LIT>' : <EOL> username = keyword4 <EOL> else : <EOL> username = cleaned_phone_number <EOL> try : <EOL> username = process_username ( username , domain ) <EOL> password = random_password ( ) <EOL> new_user = CommCareUser . create ( domain . name , username , password ) <EOL> new_user . add_phone_number ( cleaned_phone_number ) <EOL> new_user . save_verified_number ( domain . name , cleaned_phone_number , True , None ) <EOL> new_user . save ( ) <EOL> registration_processed = True <EOL> invitation = SelfRegistrationInvitation . by_phone ( msg . phone_number ) <EOL> if invitation : <EOL> invitation . completed ( ) <EOL> if domain . enable_registration_welcome_sms_for_mobile_worker : <EOL> send_sms ( domain . name , None , cleaned_phone_number , <EOL> get_message ( MSG_REGISTRATION_WELCOME_MOBILE_WORKER , domain = domain . name ) ) <EOL> except ValidationError as e : <EOL> send_sms ( domain . name , None , cleaned_phone_number , e . messages [ <NUM_LIT:0> ] ) <EOL> elif domain . sms_case_registration_enabled : <EOL> register_sms_contact ( <EOL> domain = domain . name , <EOL> case_type = domain . sms_case_registration_type , <EOL> case_name = "<STR_LIT>" , <EOL> user_id = domain . sms_case_registration_user_id , <EOL> contact_phone_number = cleaned_phone_number , <EOL> contact_phone_number_is_verified = "<STR_LIT:1>" , <EOL> owner_id = domain . sms_case_registration_owner_id , <EOL> ) <EOL> registration_processed = True <EOL> if domain . enable_registration_welcome_sms_for_case : <EOL> send_sms ( domain . name , None , cleaned_phone_number , <EOL> get_message ( MSG_REGISTRATION_WELCOME_CASE , domain = domain . name ) ) <EOL> msg . domain = domain . name <EOL> msg . save ( ) <EOL> return registration_processed <EOL> def incoming ( phone_number , text , backend_api , timestamp = None , <EOL> domain_scope = None , backend_message_id = None , <EOL> raw_text = None , backend_id = None ) : <EOL> """<STR_LIT>""" <EOL> if text is None : <EOL> text = "<STR_LIT>" <EOL> phone_number = clean_phone_number ( phone_number ) <EOL> msg = get_sms_class ( ) ( <EOL> phone_number = phone_number , <EOL> direction = INCOMING , <EOL> date = timestamp or get_utcnow ( ) , <EOL> text = text , <EOL> domain_scope = domain_scope , <EOL> backend_api = backend_api , <EOL> backend_id = backend_id , <EOL> backend_message_id = backend_message_id , <EOL> raw_text = raw_text , <EOL> ) <EOL> if settings . SMS_QUEUE_ENABLED : <EOL> msg . processed = False <EOL> msg . datetime_to_process = get_utcnow ( ) <EOL> msg . queued_timestamp = msg . datetime_to_process <EOL> msg . save ( ) <EOL> enqueue_directly ( msg ) <EOL> else : <EOL> msg . processed = True <EOL> msg . save ( ) <EOL> process_incoming ( msg ) <EOL> return msg <EOL> def is_opt_message ( text , keyword_list ) : <EOL> if not isinstance ( text , basestring ) : <EOL> return False <EOL> text = text . strip ( ) . upper ( ) <EOL> return text in keyword_list <EOL> def get_opt_keywords ( msg ) : <EOL> backend_class = get_backend_classes ( ) . get ( msg . backend_api , SQLSMSBackend ) <EOL> return ( <EOL> backend_class . get_opt_in_keywords ( ) , <EOL> backend_class . get_opt_out_keywords ( ) <EOL> ) <EOL> def process_incoming ( msg ) : <EOL> v = VerifiedNumber . by_phone ( msg . phone_number , include_pending = True ) <EOL> if v is not None and v . verified : <EOL> msg . couch_recipient_doc_type = v . owner_doc_type <EOL> msg . couch_recipient = v . owner_id <EOL> msg . domain = v . domain <EOL> msg . location_id = get_location_id_by_verified_number ( v ) <EOL> msg . save ( ) <EOL> if msg . domain_scope : <EOL> if v is None or v . domain != msg . domain_scope : <EOL> raise DomainScopeValidationError ( <EOL> '<STR_LIT>' '<STR_LIT>' <EOL> ) <EOL> can_receive_sms = PhoneBlacklist . can_receive_sms ( msg . phone_number ) <EOL> opt_in_keywords , opt_out_keywords = get_opt_keywords ( msg ) <EOL> if is_opt_message ( msg . text , opt_out_keywords ) and can_receive_sms : <EOL> if PhoneBlacklist . opt_out_sms ( msg . phone_number ) : <EOL> metadata = MessageMetadata ( ignore_opt_out = True ) <EOL> text = get_message ( MSG_OPTED_OUT , v , context = ( opt_in_keywords [ <NUM_LIT:0> ] , ) ) <EOL> if v : <EOL> send_sms_to_verified_number ( v , text , metadata = metadata ) <EOL> else : <EOL> send_sms ( msg . domain , None , msg . phone_number , text , metadata = metadata ) <EOL> elif is_opt_message ( msg . text , opt_in_keywords ) and not can_receive_sms : <EOL> if PhoneBlacklist . opt_in_sms ( msg . phone_number ) : <EOL> text = get_message ( MSG_OPTED_IN , v , context = ( opt_out_keywords [ <NUM_LIT:0> ] , ) ) <EOL> if v : <EOL> send_sms_to_verified_number ( v , text ) <EOL> else : <EOL> send_sms ( msg . domain , None , msg . phone_number , text ) <EOL> elif v is not None and v . verified : <EOL> if domain_has_privilege ( msg . domain , privileges . INBOUND_SMS ) : <EOL> for h in settings . SMS_HANDLERS : <EOL> try : <EOL> handler = to_function ( h ) <EOL> except : <EOL> notify_exception ( None , message = ( '<STR_LIT>' % h ) ) <EOL> continue <EOL> try : <EOL> was_handled = handler ( v , msg . text , msg = msg ) <EOL> except Exception , e : <EOL> log_sms_exception ( msg ) <EOL> was_handled = False <EOL> if was_handled : <EOL> break <EOL> else : <EOL> handled = process_pre_registration ( msg ) <EOL> if not handled : <EOL> handled = process_sms_registration ( msg ) <EOL> if not handled : <EOL> import verify <EOL> verify . process_verification ( v , msg ) <EOL> if ( <EOL> not settings . SMS_QUEUE_ENABLED and <EOL> msg . domain and <EOL> domain_has_privilege ( msg . domain , privileges . INBOUND_SMS ) <EOL> ) : <EOL> create_billable_for_sms ( msg ) <EOL> def create_billable_for_sms ( msg , delay = True ) : <EOL> if not isinstance ( msg , SMS ) : <EOL> raise Exception ( "<STR_LIT>" ) <EOL> if not msg . domain : <EOL> return <EOL> try : <EOL> from corehq . apps . sms . tasks import store_billable <EOL> if delay : <EOL> store_billable . delay ( msg ) <EOL> else : <EOL> store_billable ( msg ) <EOL> except Exception as e : <EOL> log_smsbillables_error ( "<STR_LIT>" % e ) </s>
