def clean_name(self): value = self.cleaned_data.get("name") if value is not None: value = value.strip().upper() if value is None or value == "": raise ValidationError(_("This field is required.")) if re.compile("\s").search(value) is not None: raise ValidationError(_("Name may not contain any spaces.")) if self.is_global_backend: # We're using the form to create a global backend, so # ensure name is not duplicated among other global backends is_unique = SQLMobileBackend.name_is_unique( value, backend_id=self._cchq_backend_id ) else: # We're using the form to create a domain-level backend, so # ensure name is not duplicated among other backends owned by this domain is_unique = SQLMobileBackend.name_is_unique( value, domain=self._cchq_domain, backend_id=self._cchq_backend_id ) if not is_unique: raise ValidationError(_("Name is already in use.")) return value
def tearDown(self): SmsBillable.objects.all().delete() SmsUsageFee.objects.all().delete() SmsUsageFeeCriteria.objects.all().delete() SmsGatewayFee.objects.all().delete() self.currency_usd.delete() for api_id, backend_id in self.backend_ids.iteritems(): SQLMobileBackend.load(backend_id, is_couch_id=True).delete()
def tearDown(self): SmsBillable.objects.all().delete() SmsUsageFee.objects.all().delete() SmsUsageFeeCriteria.objects.all().delete() SmsGatewayFee.objects.all().delete() self.currency_usd.delete() for api_id, backend_id in six.iteritems(self.backend_ids): SQLMobileBackend.load(backend_id, is_couch_id=True).delete() FakeTwilioMessageFactory.backend_message_id_to_price = {} super(TestUsageFee, self).tearDown()
def tearDown(self): SmsBillable.objects.all().delete() SmsGatewayFee.objects.all().delete() SmsGatewayFeeCriteria.objects.all().delete() SmsUsageFee.objects.all().delete() SmsUsageFeeCriteria.objects.all().delete() self.currency_usd.delete() self.other_currency.delete() SMS.by_domain(generator.TEST_DOMAIN).delete() for api_id, backend_id in self.backend_ids.iteritems(): SQLMobileBackend.load(backend_id, is_couch_id=True).delete()
def backend(self): from corehq.apps.sms.models import SQLMobileBackend from corehq.apps.sms.util import clean_phone_number if isinstance(self.backend_id, basestring) and self.backend_id.strip() != '': return SQLMobileBackend.load_by_name( SQLMobileBackend.SMS, self.domain, self.backend_id ) else: return SQLMobileBackend.load_default_by_phone_and_domain( SQLMobileBackend.SMS, clean_phone_number(self.phone_number), domain=self.domain )
def general_fields(self): fields = [ crispy.Field('name', css_class='input-xxlarge'), crispy.Field('description', css_class='input-xxlarge', rows="3"), crispy.Field('reply_to_phone_number', css_class='input-xxlarge'), ] if not self.is_global_backend: fields.extend([ crispy.Field( twbscrispy.PrependedText( 'give_other_domains_access', '', data_bind="checked: share_backend" ) ), crispy.Div( 'authorized_domains', data_bind="visible: showAuthorizedDomains", ), ]) if self._cchq_backend_id: backend = SQLMobileBackend.load(self._cchq_backend_id) if backend.show_inbound_api_key_during_edit: self.fields['inbound_api_key'].initial = backend.inbound_api_key fields.append(crispy.Field('inbound_api_key')) return fields
def __init__(self, domain, *args, **kwargs): super(SMSRateCalculatorForm, self).__init__(*args, **kwargs) backends = SQLMobileBackend.get_domain_backends(SQLMobileBackend.SMS, domain) def _get_backend_info(backend): return backend.couch_id, "%s (%s)" % (backend.name, backend.hq_api_id) backends = [_get_backend_info(g) for g in backends] self.fields['gateway'].choices = backends self.helper = FormHelper() self.helper.form_class = "form-horizontal" self.helper.layout = crispy.Layout( crispy.Field( 'gateway', data_bind="value: gateway, events: {change: clearSelect2}", css_class="input-xxlarge", ), crispy.Field( 'direction', data_bind="value: direction, " "event: {change: clearSelect2}", ), crispy.Field( 'country_code', css_class="input-xxlarge", data_bind="value: select2CountryCode.value, " "event: {change: updateRate}", placeholder=_("Please Select a Country Code"), ), )
def get_rate_response(self): gateway = self.data.get('gateway') try: backend_api_id = SQLMobileBackend.get_backend_api_id(gateway, is_couch_id=True) except Exception as e: log_smsbillables_error( "Failed to get backend for calculating an sms rate due to: %s" % e ) raise SMSRateCalculatorError("Could not obtain connection information.") country_code = self.data.get('country_code') if country_code == NONMATCHING_COUNTRY: country_code = None direction = self.data.get('direction') gateway_fee = SmsGatewayFee.get_by_criteria( backend_api_id, direction, backend_instance=gateway, country_code=country_code, ) usage_fee = SmsUsageFee.get_by_criteria(direction, self.request.domain) usd_gateway_fee = gateway_fee.amount / gateway_fee.currency.rate_to_default usd_total = usage_fee.amount + usd_gateway_fee return { 'rate': _("%s per 160 character SMS") % fmt_dollar_amount(usd_total), }
def page_context(self): # The webhook secret is a piece of data that is sent to hq on each # Telerivet inbound request. It's used to tie an inbound request to # a Telerivet backend. webhook_secret = uuid.uuid4().hex # The request token is only used for the purposes of using this UI to # setup a Telerivet backend. We need a way to post the webhook_secret # to create the backend, but we want hq to be the origin of the secret # generation. So instead, the request_token resolves to the webhook_secret # via a redis lookup which expires in 1 week. request_token = uuid.uuid4().hex self.set_cached_webhook_secret(request_token, webhook_secret) domain_has_default_gateway = SQLMobileBackend.get_domain_default_backend( SQLMobileBackend.SMS, self.domain, id_only=True ) is not None return { 'outgoing_sms_form': TelerivetOutgoingSMSForm(), 'test_sms_form': TelerivetPhoneNumberForm(), 'finalize_gateway_form': FinalizeGatewaySetupForm( initial={ 'set_as_default': (FinalizeGatewaySetupForm.NO if domain_has_default_gateway else FinalizeGatewaySetupForm.YES), } ), 'webhook_url': absolute_reverse('telerivet_in'), 'webhook_secret': webhook_secret, 'request_token': request_token, }
def send_verification(domain, user, phone_number, logged_event): backend = SQLMobileBackend.load_default_by_phone_and_domain( SQLMobileBackend.SMS, phone_number, domain=domain ) reply_phone = backend.reply_to_phone_number subevent = logged_event.create_subevent_for_single_sms( user.doc_type, user.get_id ) if reply_phone: message = messages.get_message( messages.MSG_VERIFICATION_START_WITH_REPLY, context=(user.raw_username, reply_phone), domain=domain, language=user.get_language_code() ) else: message = messages.get_message( messages.MSG_VERIFICATION_START_WITHOUT_REPLY, context=(user.raw_username,), domain=domain, language=user.get_language_code() ) send_sms(domain, user, phone_number, message, metadata=MessageMetadata(messaging_subevent_id=subevent.pk)) subevent.completed()
def country_code_response(self): gateway = self.data.get('gateway') try: backend_api_id = SQLMobileBackend.get_backend_api_id(gateway, is_couch_id=True) except Exception: return [] direction = self.data.get('direction') criteria_query = SmsGatewayFeeCriteria.objects.filter( direction=direction, backend_api_id=backend_api_id ) country_codes = criteria_query.exclude( country_code__exact=None ).values_list('country_code', flat=True).distinct() final_codes = [] for code in country_codes: country_name = country_name_from_isd_code_or_empty(code) final_codes.append((code, country_name)) search_term = self.data.get('searchString') if search_term: search_term = search_term.lower().replace('+', '') final_codes = filter( lambda x: (str(x[0]).startswith(search_term) or x[1].lower().startswith(search_term)), final_codes ) final_codes = [(c[0], "+%s%s" % (c[0], " (%s)" % c[1] if c[1] else '')) for c in final_codes] if criteria_query.filter(country_code__exact=None).exists(): final_codes.append(( NONMATCHING_COUNTRY, _('Any Country (Delivery not guaranteed via connection)') )) return final_codes
def test_load(self): backend = SQLTestSMSBackend.objects.create( name='BACKEND', is_global=True, hq_api_id=SQLTestSMSBackend.get_api_id(), ) self.assertBackendsEqual( SQLMobileBackend.load(backend.pk), backend ) self.assertBackendsEqual( SQLMobileBackend.load(backend.pk, api_id=SQLTestSMSBackend.get_api_id()), backend ) self.assertBackendsEqual( SQLMobileBackend.load(backend.couch_id, is_couch_id=True), backend ) self.assertBackendsEqual( SQLMobileBackend.load( backend.couch_id, api_id=SQLTestSMSBackend.get_api_id(), is_couch_id=True ), backend ) backend.soft_delete() with self.assertRaises(SQLMobileBackend.DoesNotExist): SQLMobileBackend.load(backend.pk, api_id=SQLTestSMSBackend.get_api_id()) with self.assertRaises(SQLMobileBackend.DoesNotExist): SQLMobileBackend.load( backend.couch_id, api_id=SQLTestSMSBackend.get_api_id(), is_couch_id=True ) with self.assertRaises(BadSMSConfigException): SQLMobileBackend.load(backend.pk, api_id='this-api-id-does-not-exist') backend.delete()
def _get_twilio_client(backend_instance): from corehq.messaging.smsbackends.twilio.models import SQLTwilioBackend twilio_backend = SQLMobileBackend.load( backend_instance, api_id=SQLTwilioBackend.get_api_id(), is_couch_id=True, include_deleted=True ) config = twilio_backend.config return TwilioRestClient(config.account_sid, config.auth_token)
def tearDown(self): SmsBillable.objects.all().delete() SmsGatewayFee.objects.all().delete() SmsGatewayFeeCriteria.objects.all().delete() SmsUsageFee.objects.all().delete() SmsUsageFeeCriteria.objects.all().delete() self.currency_usd.delete() self.other_currency.delete() SMS.by_domain(generator.TEST_DOMAIN).delete() for api_id, backend_id in self.backend_ids.iteritems(): SQLMobileBackend.load(backend_id, is_couch_id=True).delete() FakeTwilioMessageFactory.backend_message_id_to_price = {} super(TestGatewayFee, self).tearDown()
def set_domain_default_backend_to_test_backend(domain): """ Pass in the name of the domain to set the domain's default sms backend to be the test backend. """ from corehq.apps.sms.models import SQLMobileBackend, SQLMobileBackendMapping test_backend = SQLMobileBackend.get_global_backend_by_name(SQLMobileBackend.SMS, "MOBILE_BACKEND_TEST") if not test_backend: raise Exception("Expected MOBILE_BACKEND_TEST to be created") SQLMobileBackendMapping.set_default_domain_backend(domain, test_backend)
def get_ivr_backend(recipient, verified_number=None, unverified_number=None): if verified_number and verified_number.ivr_backend_id: return SQLMobileBackend.load_by_name( SQLMobileBackend.IVR, verified_number.domain, verified_number.ivr_backend_id ) else: phone_number = (verified_number.phone_number if verified_number else unverified_number) phone_number = strip_plus(str(phone_number)) prefixes = settings.IVR_BACKEND_MAP.keys() prefixes = sorted(prefixes, key=lambda x: len(x), reverse=True) for prefix in prefixes: if phone_number.startswith(prefix): return SQLMobileBackend.get_global_backend_by_name( SQLMobileBackend.IVR, settings.IVR_BACKEND_MAP[prefix] ) return None
def test_get_backend_api_id(self): backend = SQLTestSMSBackend.objects.create( name='BACKEND', is_global=True, hq_api_id=SQLTestSMSBackend.get_api_id(), ) self.assertEquals( SQLMobileBackend.get_backend_api_id(backend.pk), SQLTestSMSBackend.get_api_id() ) self.assertEquals( SQLMobileBackend.get_backend_api_id(backend.couch_id, is_couch_id=True), SQLTestSMSBackend.get_api_id() ) backend.soft_delete() with self.assertRaises(SQLMobileBackend.DoesNotExist): SQLMobileBackend.get_backend_api_id(backend.pk) with self.assertRaises(SQLMobileBackend.DoesNotExist): SQLMobileBackend.get_backend_api_id(backend.couch_id, is_couch_id=True) backend.delete()
def test_multiple_country_prefixes(self): self.assertEqual( SQLMobileBackend.load_default_by_phone_and_domain( SQLMobileBackend.SMS, '256800000000' ).pk, self.backend5.pk ) self.assertEqual( SQLMobileBackend.load_default_by_phone_and_domain( SQLMobileBackend.SMS, '256700000000' ).pk, self.backend6.pk ) self.assertEqual( SQLMobileBackend.load_default_by_phone_and_domain( SQLMobileBackend.SMS, '256750000000' ).pk, self.backend7.pk )
def get_backend_name(backend_id): """ Returns None if the backend is not found, otherwise returns the backend's name. """ if not backend_id: return None from corehq.apps.sms.models import SQLMobileBackend try: return SQLMobileBackend.load(backend_id, is_couch_id=True).name except: return None
def get_inbound_phone_entry(msg): if msg.backend_id: backend = SQLMobileBackend.load(msg.backend_id, is_couch_id=True) if not backend.is_global and toggles.INBOUND_SMS_LENIENCY.enabled(backend.domain): p = PhoneNumber.get_two_way_number_with_domain_scope(msg.phone_number, backend.domains_with_access) return ( p, p is not None ) return ( PhoneNumber.get_reserved_number(msg.phone_number), False )
def send_sms_with_backend_name(domain, phone_number, text, backend_name, metadata=None): phone_number = clean_phone_number(phone_number) backend = SQLMobileBackend.load_by_name(SQLMobileBackend.SMS, domain, backend_name) msg = get_sms_class()( domain=domain, phone_number=phone_number, direction=OUTGOING, date=get_utcnow(), backend_id=backend.couch_id, text=text ) add_msg_tags(msg, metadata) return queue_outgoing_sms(msg)
def _get_backend_tag(backend=None, backend_id=None): assert not (backend_id and backend) if backend_id: try: backend = SQLMobileBackend.load(backend_id, is_couch_id=True) except Exception: backend = None if not backend: return 'unknown' elif backend.is_global: return backend.name else: return f'{backend.domain}/{backend.name}'
def get_rate_table(self, country_code): backends = SQLMobileBackend.get_global_backends(SQLMobileBackend.SMS) def _directed_fee(direction, backend_api_id, backend_instance_id): gateway_fee = SmsGatewayFee.get_by_criteria( backend_api_id, direction, backend_instance=backend_instance_id, country_code=country_code ) if not gateway_fee: return None usd_gateway_fee = gateway_fee.amount / gateway_fee.currency.rate_to_default usage_fee = SmsUsageFee.get_by_criteria(direction) return fmt_dollar_amount(usage_fee.amount + usd_gateway_fee) rate_table = [] from corehq.messaging.smsbackends.test.models import SQLTestSMSBackend for backend_instance in backends: # Skip Testing backends if isinstance(backend_instance, SQLTestSMSBackend): continue # skip if country is not in supported countries if backend_instance.supported_countries: if ('*' not in backend_instance.supported_countries and str(country_code) not in backend_instance.supported_countries): continue gateway_fee_incoming = _directed_fee( INCOMING, backend_instance.hq_api_id, backend_instance.couch_id ) gateway_fee_outgoing = _directed_fee( OUTGOING, backend_instance.hq_api_id, backend_instance.couch_id ) if gateway_fee_outgoing or gateway_fee_incoming: rate_table.append({ 'gateway': backend_instance.display_name, 'inn': gateway_fee_incoming or 'NA', # 'in' is reserved 'out': gateway_fee_outgoing or 'NA' }) return rate_table
def get_rate_table(self, country_code): backends = SQLMobileBackend.get_global_backends(SQLMobileBackend.SMS) def _directed_fee(direction, backend_api_id, backend_instance_id): gateway_fee = SmsGatewayFee.get_by_criteria( backend_api_id, direction, backend_instance=backend_instance_id, country_code=country_code ) if not gateway_fee or gateway_fee.amount is None: return None usd_gateway_fee = gateway_fee.amount / gateway_fee.currency.rate_to_default usage_fee = SmsUsageFee.get_by_criteria(direction) return fmt_dollar_amount(usage_fee.amount + usd_gateway_fee) rate_table = [] from corehq.messaging.smsbackends.test.models import SQLTestSMSBackend for backend_instance in backends: # Skip Testing backends if isinstance(backend_instance, SQLTestSMSBackend): continue # skip if country is not in supported countries if backend_instance.supported_countries: if ('*' not in backend_instance.supported_countries and str(country_code) not in backend_instance.supported_countries): continue gateway_fee_incoming = _directed_fee( INCOMING, backend_instance.hq_api_id, backend_instance.couch_id ) gateway_fee_outgoing = _directed_fee( OUTGOING, backend_instance.hq_api_id, backend_instance.couch_id ) if gateway_fee_outgoing or gateway_fee_incoming: rate_table.append({ 'gateway': backend_instance.display_name, 'inn': gateway_fee_incoming or 'NA', # 'in' is reserved 'out': gateway_fee_outgoing or 'NA' }) return rate_table
def clean_name(self): value = self.cleaned_data.get("name") if value is not None: value = value.strip().upper() if value is None or value == "": raise ValidationError(_("This field is required.")) if re.compile(r"\s").search(value) is not None: raise ValidationError(_("Name may not contain any spaces.")) if self.is_global_backend: # We're using the form to create a global backend, so # ensure name is not duplicated among other global backends is_unique = SQLMobileBackend.name_is_unique( value, backend_id=self.backend_id) else: # We're using the form to create a domain-level backend, so # ensure name is not duplicated among other backends owned by this domain is_unique = SQLMobileBackend.name_is_unique( value, domain=self.domain, backend_id=self.backend_id) if not is_unique: raise ValidationError(_("Name is already in use.")) return value
def send_sms(domain, contact, phone_number, text, metadata=None, logged_subevent=None): """ Sends an outbound SMS. Returns false if it fails. """ if phone_number is None: return False if isinstance(phone_number, six.integer_types): phone_number = str(phone_number) phone_number = clean_phone_number(phone_number) msg = get_sms_class()(domain=domain, phone_number=phone_number, direction=OUTGOING, date=get_utcnow(), backend_id=None, location_id=get_location_id_by_contact( domain, contact), text=text) if contact: msg.couch_recipient = contact.get_id msg.couch_recipient_doc_type = contact.doc_type if domain and contact and is_commcarecase(contact): backend_name = contact.get_case_property('contact_backend_id') backend_name = backend_name.strip() if isinstance( backend_name, six.string_types) else '' soft_assert_type_text(backend_name) if backend_name: try: backend = SQLMobileBackend.load_by_name( SQLMobileBackend.SMS, domain, backend_name) except BadSMSConfigException as e: if logged_subevent: logged_subevent.error( MessagingEvent.ERROR_GATEWAY_NOT_FOUND, additional_error_text=six.text_type(e)) return False msg.backend_id = backend.couch_id add_msg_tags(msg, metadata) return queue_outgoing_sms(msg)
def set_domain_default_backend_to_test_backend(domain): """ Pass in the name of the domain to set the domain's default sms backend to be the test backend. """ from corehq.apps.sms.models import SQLMobileBackend, SQLMobileBackendMapping test_backend = SQLMobileBackend.get_global_backend_by_name( SQLMobileBackend.SMS, 'MOBILE_BACKEND_TEST' ) if not test_backend: raise Exception("Expected MOBILE_BACKEND_TEST to be created") SQLMobileBackendMapping.set_default_domain_backend( domain, test_backend )
def _clean_backend_id(self, backend_id): try: backend_id = int(backend_id) except (ValueError, TypeError): raise ValidationError(_("Invalid Backend Specified.")) try: backend = SQLMobileBackend.load(backend_id) except: raise ValidationError(_("Invalid Backend Specified.")) if (backend.deleted or not backend.is_global or backend.backend_type != SQLMobileBackend.SMS): raise ValidationError(_("Invalid Backend Specified.")) return backend_id
def _get_gateway_fee(cls, backend_api_id, backend_id, phone_number, direction, couch_id, backend_message_id, domain): country_code, national_number = get_country_code_and_national_number( phone_number) backend_instance = None if backend_id is not None: backend_instance = SQLMobileBackend.load( backend_id, api_id=backend_api_id, is_couch_id=True, include_deleted=True, ) is_gateway_billable = ( backend_id is None or backend_instance.is_global or toggles.ENABLE_INCLUDE_SMS_GATEWAY_CHARGING.enabled(domain)) direct_gateway_fee = gateway_fee = multipart_count = conversion_rate = None if is_gateway_billable: if backend_instance and backend_instance.using_api_to_get_fees: if backend_message_id: direct_gateway_fee, multipart_count = \ cls.get_charge_details_through_api(backend_instance, backend_message_id) else: log_smsbillables_error( "Could not create direct gateway fee for message %s: no backend_message_id" % couch_id) # always grab the gateway fee even if using an api gateway_fee = SmsGatewayFee.get_by_criteria( backend_api_id, direction, backend_instance=backend_id, country_code=country_code, national_number=national_number, ) if gateway_fee: conversion_rate = cls.get_conversion_rate(gateway_fee) else: log_smsbillables_error( "No matching gateway fee criteria for SMS %s" % couch_id) return _ProviderChargeInfo(direct_gateway_fee, gateway_fee, multipart_count, conversion_rate)
def gateway_specific_fields(self): domain_backends = SQLMobileBackend.get_domain_backends( SQLMobileBackend.SMS, self.domain, ) backend_choices = [('', _("No Fallback Backend"))] backend_choices.extend([(backend.couch_id, backend.name) for backend in domain_backends if backend.id != self.backend_id]) self.fields['fallback_backend_id'].choices = backend_choices return crispy.Fieldset( _("Turn Settings"), "client_auth_token", "business_id", "template_namespace", "business_auth_token", "fallback_backend_id", )
def page_context(self): # The webhook secret is a piece of data that is sent to hq on each # Telerivet inbound request. It's used to tie an inbound request to # a Telerivet backend. webhook_secret = uuid.uuid4().hex # The request token is only used for the purposes of using this UI to # setup a Telerivet backend. We need a way to post the webhook_secret # to create the backend, but we want hq to be the origin of the secret # generation. So instead, the request_token resolves to the webhook_secret # via a redis lookup which expires in 1 week. request_token = uuid.uuid4().hex TelerivetSetupView.set_cached_webhook_secret(request_token, webhook_secret) domain_has_default_gateway = SQLMobileBackend.get_domain_default_backend( SQLMobileBackend.SMS, self.domain, id_only=True) is not None webhook_url = absolute_reverse('telerivet_in') return { 'outgoing_sms_form': TelerivetOutgoingSMSForm(), 'test_sms_form': TelerivetPhoneNumberForm(), 'finalize_gateway_form': FinalizeGatewaySetupForm( initial={ 'name': 'TELERIVET', 'set_as_default': (FinalizeGatewaySetupForm.NO if domain_has_default_gateway else FinalizeGatewaySetupForm.YES), }), 'webhook_url': webhook_url, 'include_https_notice': webhook_url.startswith('https'), 'webhook_secret': webhook_secret, 'request_token': request_token, 'gateway_list_url': reverse(DomainSmsGatewayListView.urlname, args=[self.domain]), }
def get_active_dimagi_owned_gateway_projects(domains, datespan, interval, datefield="date"): """ Returns list of timestamps and how many domains used a Dimagi owned gateway in the past thrity days before each timestamp """ dimagi_owned_backend_ids = SQLMobileBackend.get_global_backend_ids(SQLMobileBackend.SMS, couch_id=True) backend_filter = {"terms": {"backend_id": dimagi_owned_backend_ids}} histo_data = [] for timestamp in daterange(interval, datespan.startdate, datespan.enddate): t = timestamp f = timestamp - relativedelta(days=30) sms_query = get_sms_query(f, t, "domains", "domain", domains) d = sms_query.filter(backend_filter).run() c = len(d.aggregations.domains.keys) if c > 0: histo_data.append(get_data_point(c, timestamp)) return format_return_data(histo_data, 0, datespan)
def _clean_backend_id(self, backend_id): try: backend_id = int(backend_id) except (ValueError, TypeError): raise ValidationError(_("Invalid Backend Specified.")) try: backend = SQLMobileBackend.load(backend_id) except: raise ValidationError(_("Invalid Backend Specified.")) if ( backend.deleted or not backend.is_global or backend.backend_type != SQLMobileBackend.SMS ): raise ValidationError(_("Invalid Backend Specified.")) return backend_id
def ivr(request): """ Kookoo invokes this view for its main communication with HQ. Point Kookoo's 'url' parameter here. """ # Retrieve all parameters called_number = request.GET.get("called_number", None) outbound_sid = request.GET.get("outbound_sid", None) cid = request.GET.get( "cid", None) # This is the caller id, format being 0..., not 91... sid = request.GET.get("sid", None) operator = request.GET.get("operator", None) circle = request.GET.get("circle", None) event = request.GET.get("event", None) data = request.GET.get("data", None) total_call_duration = request.GET.get("total_call_duration", None) phone_number = cid if phone_number is not None and phone_number.startswith("0"): phone_number = "91" + phone_number[1:] gateway_session_id = "KOOKOO-" + sid # Process the event if event == "NewCall": ivr_event = IVR_EVENT_NEW_CALL elif event == "GotDTMF": ivr_event = IVR_EVENT_INPUT elif event == "Disconnect": ivr_event = IVR_EVENT_DISCONNECT else: ivr_event = IVR_EVENT_DISCONNECT backend = SQLMobileBackend.get_global_backend_by_name( SQLMobileBackend.IVR, 'MOBILE_BACKEND_KOOKOO') with CriticalSection([gateway_session_id], timeout=300): result = incoming(phone_number, gateway_session_id, ivr_event, backend=backend, input_data=data, duration=total_call_duration) return result
def get_inbound_phone_entry(phone_number, backend_id=None): if backend_id: backend = SQLMobileBackend.load(backend_id, is_couch_id=True) if toggles.INBOUND_SMS_LENIENCY.enabled(backend.domain): p = None if toggles.ONE_PHONE_NUMBER_MULTIPLE_CONTACTS.enabled(backend.domain): running_session_info = XFormsSessionSynchronization.get_running_session_info_for_channel( SMSChannel(backend_id=backend_id, phone_number=phone_number) ) contact_id = running_session_info.contact_id if contact_id: p = PhoneNumber.get_phone_number_for_owner(contact_id, phone_number) if p is not None: return ( p, True ) elif running_session_info.session_id: # This would be very unusual, as it would mean the supposedly running form session # is linked to a phone number, contact pair that doesn't exist in the PhoneNumber table notify_error( "Contact from running session has no match in PhoneNumber table. " "Only known way for this to happen is if you " "unregister a phone number for a contact " "while they are in an active session.", details={ 'running_session_info': running_session_info } ) # NOTE: I don't think the backend could ever be global here since global backends # don't have a 'domain' and so the toggles would never be activated if not backend.is_global: p = PhoneNumber.get_two_way_number_with_domain_scope(phone_number, backend.domains_with_access) return ( p, p is not None ) return ( PhoneNumber.get_reserved_number(phone_number), False )
def send_sms(domain, contact, phone_number, text, metadata=None, logged_subevent=None): """ Sends an outbound SMS. Returns false if it fails. """ if phone_number is None: return False if isinstance(phone_number, six.integer_types): phone_number = str(phone_number) phone_number = clean_phone_number(phone_number) msg = get_sms_class()( domain=domain, phone_number=phone_number, direction=OUTGOING, date=get_utcnow(), backend_id=None, location_id=get_location_id_by_contact(domain, contact), text = text ) if contact: msg.couch_recipient = contact.get_id msg.couch_recipient_doc_type = contact.doc_type if domain and contact and is_commcarecase(contact): backend_name = contact.get_case_property('contact_backend_id') backend_name = backend_name.strip() if isinstance(backend_name, six.string_types) else '' soft_assert_type_text(backend_name) if backend_name: try: backend = SQLMobileBackend.load_by_name(SQLMobileBackend.SMS, domain, backend_name) except BadSMSConfigException as e: if logged_subevent: logged_subevent.error(MessagingEvent.ERROR_GATEWAY_NOT_FOUND, additional_error_text=six.text_type(e)) return False msg.backend_id = backend.couch_id add_msg_tags(msg, metadata) return queue_outgoing_sms(msg)
def get_active_dimagi_owned_gateway_projects(domains, datespan, interval, datefield='date'): """ Returns list of timestamps and how many domains used a Dimagi owned gateway in the past thrity days before each timestamp """ dimagi_owned_backend_ids = SQLMobileBackend.get_global_backend_ids(couch_id=True) backend_filter = {'terms': {'backend_id': dimagi_owned_backend_ids}} histo_data = [] for timestamp in daterange(interval, datespan.startdate, datespan.enddate): t = timestamp f = timestamp - relativedelta(days=30) sms_query = get_sms_query(f, t, 'domains', 'domain', domains, DOMAIN_COUNT_UPPER_BOUND) d = sms_query.filter(backend_filter).run() c = len(d.facet('domains', 'terms')) if c > 0: histo_data.append(get_data_point(c, timestamp)) return format_return_data(histo_data, 0, datespan)
def ivr(request): """ Kookoo invokes this view for its main communication with HQ. Point Kookoo's 'url' parameter here. """ # Retrieve all parameters called_number = request.GET.get("called_number", None) outbound_sid = request.GET.get("outbound_sid", None) cid = request.GET.get("cid", None) # This is the caller id, format being 0..., not 91... sid = request.GET.get("sid", None) operator = request.GET.get("operator", None) circle = request.GET.get("circle", None) event = request.GET.get("event", None) data = request.GET.get("data", None) total_call_duration = request.GET.get("total_call_duration", None) phone_number = cid if phone_number is not None and phone_number.startswith("0"): phone_number = "91" + phone_number[1:] gateway_session_id = "KOOKOO-" + sid # Process the event if event == "NewCall": ivr_event = IVR_EVENT_NEW_CALL elif event == "GotDTMF": ivr_event = IVR_EVENT_INPUT elif event == "Disconnect": ivr_event = IVR_EVENT_DISCONNECT else: ivr_event = IVR_EVENT_DISCONNECT backend = SQLMobileBackend.get_global_backend_by_name( SQLMobileBackend.IVR, 'MOBILE_BACKEND_KOOKOO' ) with CriticalSection([gateway_session_id], timeout=300): result = incoming(phone_number, gateway_session_id, ivr_event, backend=backend, input_data=data, duration=total_call_duration) return result
def get_active_dimagi_owned_gateway_projects(domains, datespan, interval, datefield='date'): """ Returns list of timestamps and how many domains used a Dimagi owned gateway in the past thrity days before each timestamp """ dimagi_owned_backend_ids = SQLMobileBackend.get_global_backend_ids( SQLMobileBackend.SMS, couch_id=True) backend_filter = {'terms': {'backend_id': dimagi_owned_backend_ids}} histo_data = [] for timestamp in daterange(interval, datespan.startdate, datespan.enddate): t = timestamp f = timestamp - relativedelta(days=30) sms_query = get_sms_query(f, t, 'domains', 'domain', domains) d = sms_query.filter(backend_filter).run() c = len(d.aggregations.domains.keys) if c > 0: histo_data.append(get_data_point(c, timestamp)) return format_return_data(histo_data, 0, datespan)
def page_context(self): # The webhook secret is a piece of data that is sent to hq on each # Telerivet inbound request. It's used to tie an inbound request to # a Telerivet backend. webhook_secret = uuid.uuid4().hex # The request token is only used for the purposes of using this UI to # setup a Telerivet backend. We need a way to post the webhook_secret # to create the backend, but we want hq to be the origin of the secret # generation. So instead, the request_token resolves to the webhook_secret # via a redis lookup which expires in 1 week. request_token = uuid.uuid4().hex self.set_cached_webhook_secret(request_token, webhook_secret) domain_has_default_gateway = ( SQLMobileBackend.get_domain_default_backend(SQLMobileBackend.SMS, self.domain, id_only=True) is not None ) webhook_url = absolute_reverse("telerivet_in") return { "outgoing_sms_form": TelerivetOutgoingSMSForm(), "test_sms_form": TelerivetPhoneNumberForm(), "finalize_gateway_form": FinalizeGatewaySetupForm( initial={ "name": "TELERIVET", "set_as_default": ( FinalizeGatewaySetupForm.NO if domain_has_default_gateway else FinalizeGatewaySetupForm.YES ), } ), "webhook_url": webhook_url, "include_https_notice": webhook_url.startswith("https"), "webhook_secret": webhook_secret, "request_token": request_token, "gateway_list_url": reverse(DomainSmsGatewayListView.urlname, args=[self.domain]), }
def num_telerivet_backends(domain): from corehq.messaging.smsbackends.telerivet.models import SQLTelerivetBackend backends = SQLMobileBackend.get_domain_backends(SQLMobileBackend.SMS, domain) return len([b for b in backends if isinstance(b, SQLTelerivetBackend)])
def _sms_backend_is_global(sms_backend_id): return SQLMobileBackend.load( sms_backend_id, is_couch_id=True, include_deleted=True ).is_global