Example #1
0
        def get_context_data(self, **kwargs):
            context = super(ContactCRUDL.Import, self).get_context_data(**kwargs)
            context['task'] = None
            context['group'] = None
            context['show_form'] = True

            analytics.track(self.request.user.username, 'temba.contact_imported')

            task_id = self.request.REQUEST.get('task', None)
            if task_id:
                tasks = ImportTask.objects.filter(pk=task_id, created_by=self.request.user)

                if tasks:
                    task = tasks[0]
                    context['task'] = task
                    context['show_form'] = False
                    context['results'] = json.loads(task.import_results) if task.import_results else dict()

                    groups = ContactGroup.user_groups.filter(import_task=task)

                    if groups:
                        context['group'] = groups[0]

                    elif not task.status() in ['PENDING', 'RUNNING', 'STARTED']:
                        context['show_form'] = True

            return context
Example #2
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()
            groups = form.cleaned_data["groups"]

            # first archive all catch all message triggers with matching groups
            Trigger.objects.filter(
                org=org, groups__in=groups, trigger_type=Trigger.TYPE_CATCH_ALL, is_active=True
            ).update(is_archived=True)

            # then create a new catch all trigger
            trigger = Trigger.objects.create(
                created_by=user,
                modified_by=user,
                org=org,
                trigger_type=Trigger.TYPE_CATCH_ALL,
                flow=form.cleaned_data["flow"],
            )

            # add all the groups we are relevant for
            for group in groups:
                trigger.groups.add(group)

            analytics.track(self.request.user.username, "temba.trigger_created_catchall")

            response = self.render_to_response(self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #3
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()
            groups = form.cleaned_data['groups']

            # first archive all catch all message triggers with matching groups
            Trigger.objects.filter(org=org,
                                   groups__in=groups,
                                   trigger_type=Trigger.TYPE_CATCH_ALL,
                                   is_active=True).update(is_archived=True)

            # then create a new catch all trigger
            trigger = Trigger.objects.create(
                created_by=user,
                modified_by=user,
                org=org,
                trigger_type=Trigger.TYPE_CATCH_ALL,
                flow=form.cleaned_data['flow'])

            # add all the groups we are relevant for
            for group in groups:
                trigger.groups.add(group)

            analytics.track(self.request.user.username,
                            'temba.trigger_created_catchall')

            response = self.render_to_response(
                self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #4
0
        def form_valid(self, form):
            keyword = form.cleaned_data['keyword']
            join_group = form.cleaned_data['action_join_group']
            start_flow = form.cleaned_data['flow']
            send_msg = form.cleaned_data['response']

            org = self.request.user.get_org()
            group_flow = Flow.create_join_group(org, self.request.user,
                                                join_group, send_msg,
                                                start_flow)

            Trigger.objects.create(created_by=self.request.user,
                                   modified_by=self.request.user,
                                   org=self.request.user.get_org(),
                                   keyword=keyword,
                                   trigger_type=Trigger.TYPE_KEYWORD,
                                   flow=group_flow)

            analytics.track(self.request.user.username,
                            'temba.trigger_created_register',
                            dict(name=join_group.name))

            response = self.render_to_response(
                self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #5
0
        def form_valid(self, form):
            analytics.track(self.request.user.username,
                            'temba.trigger_created_schedule')
            schedule = Schedule.objects.create(created_by=self.request.user,
                                               modified_by=self.request.user)

            if form.starts_never():
                schedule.reset()

            elif form.stopped():
                schedule.reset()

            elif form.starts_now():
                schedule.next_fire = timezone.now() - timedelta(days=1)
                schedule.repeat_period = 'O'
                schedule.repeat_days = 0
                schedule.status = 'S'
                schedule.save()

            else:
                # Scheduled case
                schedule.status = 'S'
                schedule.repeat_period = form.cleaned_data['repeat_period']
                start_time = form.get_start_time()
                if start_time:
                    schedule.next_fire = start_time

                # create our recurrence
                if form.is_recurring():
                    days = None
                    if 'repeat_days' in form.cleaned_data:
                        days = form.cleaned_data['repeat_days']
                    schedule.repeat_days = days
                    schedule.repeat_hour_of_day = schedule.next_fire.hour
                    schedule.repeat_minute_of_hour = schedule.repeat_minute_of_hour
                    schedule.repeat_day_of_month = schedule.next_fire.day
                schedule.save()

            recipients = self.form.cleaned_data['omnibox']

            trigger = Trigger.objects.create(
                flow=self.form.cleaned_data['flow'],
                org=self.request.user.get_org(),
                schedule=schedule,
                trigger_type=Trigger.TYPE_SCHEDULE,
                created_by=self.request.user,
                modified_by=self.request.user)

            for group in recipients['groups']:
                trigger.groups.add(group)

            for contact in recipients['contacts']:
                trigger.contacts.add(contact)

            self.post_save(trigger)

            response = self.render_to_response(
                self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #6
0
        def form_valid(self, form):
            keyword = form.cleaned_data["keyword"]
            join_group = form.cleaned_data["action_join_group"]
            start_flow = form.cleaned_data["flow"]
            send_msg = form.cleaned_data["response"]

            org = self.request.user.get_org()
            group_flow = Flow.create_join_group(org, self.request.user,
                                                join_group, send_msg,
                                                start_flow)

            Trigger.objects.create(
                created_by=self.request.user,
                modified_by=self.request.user,
                org=self.request.user.get_org(),
                keyword=keyword,
                trigger_type=Trigger.TYPE_KEYWORD,
                flow=group_flow,
            )

            analytics.track(self.request.user.username,
                            "temba.trigger_created", dict(type="register"))

            response = self.render_to_response(
                self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #7
0
        def form_valid(self, form):

            user = self.request.user
            org = user.get_org()

            # first archive all missed call triggers
            Trigger.objects.filter(org=org,
                                   trigger_type=Trigger.TYPE_MISSED_CALL,
                                   is_active=True).update(is_archived=True)

            # then create a new missed call trigger
            Trigger.objects.create(
                created_by=user,
                modified_by=user,
                org=org,
                trigger_type=Trigger.TYPE_MISSED_CALL,
                flow=form.cleaned_data["flow"],
            )

            analytics.track(self.request.user.username,
                            "temba.trigger_created", dict(type="missed_call"))

            response = self.render_to_response(
                self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #8
0
        def get_context_data(self, **kwargs):
            context = super(ContactCRUDL.Import, self).get_context_data(**kwargs)
            context['task'] = None
            context['group'] = None
            context['show_form'] = True

            analytics.track(self.request.user.username, 'temba.contact_imported')

            task_id = self.request.REQUEST.get('task', None)
            if task_id:
                tasks = ImportTask.objects.filter(pk=task_id, created_by=self.request.user)

                if tasks:
                    task = tasks[0]
                    context['task'] = task
                    context['show_form'] = False
                    context['results'] = json.loads(task.import_results) if task.import_results else dict()

                    groups = ContactGroup.user_groups.filter(import_task=task)

                    if groups:
                        context['group'] = groups[0]

                    elif not task.status() in ['PENDING', 'RUNNING', 'STARTED']:
                        context['show_form'] = True

            return context
Example #9
0
        def render_to_response(self, context, **response_kwargs):

            analytics.track(self.request.user.username, 'temba.contact_exported')

            user = self.request.user
            org = user.get_org()

            group = None
            group_id = self.request.REQUEST.get('g', None)
            if group_id:
                groups = ContactGroup.user_groups.filter(pk=group_id, org=org)

                if groups:
                    group = groups[0]

            host = self.request.branding['host']

            export = ExportContactsTask.objects.create(created_by=user, modified_by=user, org=org, group=group, host=host)
            export_contacts_task.delay(export.pk)

            from django.contrib import messages
            if not getattr(settings, 'CELERY_ALWAYS_EAGER', False):
                messages.info(self.request, _("We are preparing your export. ") +
                                            _("We will e-mail you at %s when it is ready.") % self.request.user.username)

            else:
                export = ExportContactsTask.objects.get(id=export.pk)
                dl_url = reverse('assets.download', kwargs=dict(type='contact_export', identifier=export.pk))
                messages.info(self.request, _("Export complete, you can find it here: %s (production users will get an email)") % dl_url)

            return HttpResponseRedirect(reverse('contacts.contact_list'))
Example #10
0
def track_org_channel_counts(now=None):
    """
    Run daily, logs to our analytics the number of incoming and outgoing messages/ivr messages per org that had
    more than one message received or sent in the previous day. This helps track engagement of orgs.
    """
    now = now or timezone.now()
    yesterday = (now.astimezone(pytz.utc) - timedelta(days=1)).date()

    stats = [
        dict(key="temba.msg_incoming", count_type=ChannelCount.INCOMING_MSG_TYPE),
        dict(key="temba.msg_outgoing", count_type=ChannelCount.OUTGOING_MSG_TYPE),
        dict(key="temba.ivr_incoming", count_type=ChannelCount.INCOMING_IVR_TYPE),
        dict(key="temba.ivr_outgoing", count_type=ChannelCount.OUTGOING_IVR_TYPE),
    ]

    # calculate each stat and track
    for stat in stats:
        org_counts = (
            Org.objects.filter(
                channels__counts__day=yesterday, channels__counts__count_type=stat["count_type"]
            ).annotate(count=Sum("channels__counts__count"))
        ).prefetch_related("administrators")

        for org in org_counts:
            if org.administrators.all():
                track(org.administrators.all()[0], stat["key"], dict(count=org.count))
Example #11
0
def send_to_flow_node(org_id, user_id, text, **kwargs):
    from django.contrib.auth.models import User
    from temba.contacts.models import Contact
    from temba.orgs.models import Org
    from temba.flows.models import FlowStep

    org = Org.objects.get(pk=org_id)
    user = User.objects.get(pk=user_id)
    simulation = kwargs.get('simulation', 'false') == 'true'
    step_uuid = kwargs.get('s', None)

    qs = Contact.objects.filter(org=org,
                                is_blocked=False,
                                is_stopped=False,
                                is_active=True,
                                is_test=simulation)

    steps = FlowStep.objects.filter(
        run__is_active=True,
        step_uuid=step_uuid,
        left_on=None,
        run__flow__org=org).distinct('contact').select_related('contact')
    contact_uuids = [f.contact.uuid for f in steps]
    contacts = qs.filter(uuid__in=contact_uuids).order_by('name')

    recipients = list(contacts)
    broadcast = Broadcast.create(org, user, text, recipients)
    broadcast.send(expressions_context={})

    analytics.track(user.username, 'temba.broadcast_created',
                    dict(contacts=len(contacts), groups=0, urns=0))
Example #12
0
def send_to_flow_node(org_id, user_id, text, **kwargs):
    from django.contrib.auth.models import User
    from temba.contacts.models import Contact
    from temba.orgs.models import Org
    from temba.flows.models import FlowRun

    org = Org.objects.get(pk=org_id)
    user = User.objects.get(pk=user_id)
    node_uuid = kwargs.get("s", None)

    runs = FlowRun.objects.filter(org=org,
                                  current_node_uuid=node_uuid,
                                  is_active=True)

    contact_ids = list(
        Contact.objects.filter(
            org=org, status=Contact.STATUS_ACTIVE, is_active=True).filter(
                id__in=runs.values_list("contact", flat=True)).values_list(
                    "id", flat=True))

    if contact_ids:
        broadcast = Broadcast.create(org, user, text, contact_ids=contact_ids)
        broadcast.send_async()

        analytics.track(user, "temba.broadcast_created",
                        dict(contacts=len(contact_ids), groups=0, urns=0))
Example #13
0
        def form_valid(self, form):
            analytics.track(self.request.user.username, "temba.trigger_created_schedule")
            schedule = Schedule.objects.create(created_by=self.request.user, modified_by=self.request.user)

            if form.starts_never():
                schedule.reset()

            elif form.stopped():
                schedule.reset()

            elif form.starts_now():
                schedule.next_fire = timezone.now() - timedelta(days=1)
                schedule.repeat_period = "O"
                schedule.repeat_days = 0
                schedule.status = "S"
                schedule.save()

            else:
                # Scheduled case
                schedule.status = "S"
                schedule.repeat_period = form.cleaned_data["repeat_period"]
                start_time = form.get_start_time()
                if start_time:
                    schedule.next_fire = start_time

                # create our recurrence
                if form.is_recurring():
                    days = None
                    if "repeat_days" in form.cleaned_data:
                        days = form.cleaned_data["repeat_days"]
                    schedule.repeat_days = days
                    schedule.repeat_hour_of_day = schedule.next_fire.hour
                    schedule.repeat_minute_of_hour = schedule.repeat_minute_of_hour
                    schedule.repeat_day_of_month = schedule.next_fire.day
                schedule.save()

            recipients = self.form.cleaned_data["omnibox"]

            trigger = Trigger.objects.create(
                flow=self.form.cleaned_data["flow"],
                org=self.request.user.get_org(),
                schedule=schedule,
                trigger_type=Trigger.TYPE_SCHEDULE,
                created_by=self.request.user,
                modified_by=self.request.user,
            )

            for group in recipients["groups"]:
                trigger.groups.add(group)

            for contact in recipients["contacts"]:
                trigger.contacts.add(contact)

            self.post_save(trigger)

            response = self.render_to_response(self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #14
0
        def form_valid(self, form):
            self.form = form
            user = self.request.user
            simulation = self.request.REQUEST.get('simulation',
                                                  'false') == 'true'

            omnibox = self.form.cleaned_data['omnibox']
            has_schedule = self.form.cleaned_data['schedule']

            groups = list(omnibox['groups'])
            contacts = list(omnibox['contacts'])
            urns = list(omnibox['urns'])
            recipients = list()

            if simulation:
                # when simulating make sure we only use test contacts
                for contact in contacts:
                    if contact.is_test:
                        recipients.append(contact)
            else:
                for group in groups:
                    recipients.append(group)
                for contact in contacts:
                    recipients.append(contact)
                for urn in urns:
                    recipients.append(urn)

            schedule = Schedule.objects.create(
                created_by=user, modified_by=user) if has_schedule else None
            broadcast = Broadcast.create(user.get_org(),
                                         user,
                                         self.form.cleaned_data['text'],
                                         recipients,
                                         schedule=schedule)

            if not has_schedule:
                self.post_save(broadcast)
                super(BroadcastCRUDL.Send, self).form_valid(form)

            analytics.track(
                self.request.user.username, 'temba.broadcast_created',
                dict(contacts=len(contacts),
                     groups=len(groups),
                     urns=len(urns)))

            if '_format' in self.request.REQUEST and self.request.REQUEST[
                    '_format'] == 'json':
                data = dict(status="success",
                            redirect=reverse('msgs.broadcast_schedule_read',
                                             args=[broadcast.pk]))
                return HttpResponse(json.dumps(data),
                                    content_type='application/json')
            else:
                if self.form.cleaned_data['schedule']:
                    return HttpResponseRedirect(
                        reverse('msgs.broadcast_schedule_read',
                                args=[broadcast.pk]))
                return HttpResponseRedirect(self.get_success_url())
Example #15
0
    def claim_number(self, user, phone_number, country, role):

        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN, None)

        org = user.get_org()

        plivo_uuid = generate_uuid()
        callback_domain = org.get_brand_domain()
        app_name = "%s/%s" % (callback_domain.lower(), plivo_uuid)

        message_url = "https://" + callback_domain + "%s" % reverse('handlers.plivo_handler', args=['receive', plivo_uuid])
        answer_url = "https://" + settings.AWS_BUCKET_DOMAIN + "/plivo_voice_unavailable.xml"

        headers = http_headers(extra={'Content-Type': "application/json"})
        create_app_url = "https://api.plivo.com/v1/Account/%s/Application/" % auth_id

        response = requests.post(create_app_url, json=dict(app_name=app_name, answer_url=answer_url, message_url=message_url),
                                 headers=headers, auth=(auth_id, auth_token))

        if response.status_code in [201, 200, 202]:
            plivo_app_id = response.json()['app_id']
        else:  # pragma: no cover
            plivo_app_id = None

        plivo_config = {Channel.CONFIG_PLIVO_AUTH_ID: auth_id,
                        Channel.CONFIG_PLIVO_AUTH_TOKEN: auth_token,
                        Channel.CONFIG_PLIVO_APP_ID: plivo_app_id,
                        Channel.CONFIG_CALLBACK_DOMAIN: org.get_brand_domain()}

        plivo_number = phone_number.strip('+ ').replace(' ', '')
        response = requests.get("https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number), headers=headers, auth=(auth_id, auth_token))

        if response.status_code != 200:
            response = requests.post("https://api.plivo.com/v1/Account/%s/PhoneNumber/%s/" % (auth_id, plivo_number), headers=headers, auth=(auth_id, auth_token))

            if response.status_code != 201:  # pragma: no cover
                raise Exception(_("There was a problem claiming that number, please check the balance on your account."))

            response = requests.get("https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number), headers=headers, auth=(auth_id, auth_token))

        if response.status_code == 200:
            response = requests.post("https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
                                     json=dict(app_id=plivo_app_id), headers=headers, auth=(auth_id, auth_token))

            if response.status_code != 202:  # pragma: no cover
                raise Exception(_("There was a problem updating that number, please try again."))

        phone_number = '+' + plivo_number
        phone = phonenumbers.format_number(phonenumbers.parse(phone_number, None),
                                           phonenumbers.PhoneNumberFormat.NATIONAL)

        channel = Channel.create(org, user, country, 'PL', name=phone, address=phone_number,
                                 config=plivo_config, uuid=plivo_uuid)

        analytics.track(user.username, 'temba.channel_claim_plivo', dict(number=phone_number))

        return channel
Example #16
0
        def form_valid(self, form):
            self.form = form
            user = self.request.user
            org = user.get_org()
            simulation = self.request.GET.get("simulation", "false") == "true"

            omnibox = self.form.cleaned_data["omnibox"]
            has_schedule = self.form.cleaned_data["schedule"]
            step_uuid = self.form.cleaned_data.get("step_node", None)
            text = self.form.cleaned_data["text"]

            groups = list(omnibox["groups"])
            contacts = list(omnibox["contacts"])
            urns = list(omnibox["urns"])

            if step_uuid:
                from .tasks import send_to_flow_node

                get_params = {k: v for k, v in self.request.GET.items()}
                get_params.update({"s": step_uuid})
                send_to_flow_node.delay(org.pk, user.pk, text, **get_params)
                if "_format" in self.request.GET and self.request.GET["_format"] == "json":
                    return HttpResponse(json.dumps(dict(status="success")), content_type="application/json")
                else:
                    return HttpResponseRedirect(self.get_success_url())

            # if simulating only use the test contact
            if simulation:
                groups = []
                urns = []
                for contact in contacts:
                    if contact.is_test:
                        contacts = [contact]
                        break

            schedule = Schedule.objects.create(created_by=user, modified_by=user) if has_schedule else None
            broadcast = Broadcast.create(
                org, user, text, groups=groups, contacts=contacts, urns=urns, schedule=schedule, status=QUEUED
            )

            if not has_schedule:
                self.post_save(broadcast)
                super().form_valid(form)

            analytics.track(
                self.request.user.username,
                "temba.broadcast_created",
                dict(contacts=len(contacts), groups=len(groups), urns=len(urns)),
            )

            if "_format" in self.request.GET and self.request.GET["_format"] == "json":
                data = dict(status="success", redirect=reverse("msgs.broadcast_schedule_read", args=[broadcast.pk]))
                return HttpResponse(json.dumps(data), content_type="application/json")
            else:
                if self.form.cleaned_data["schedule"]:
                    return HttpResponseRedirect(reverse("msgs.broadcast_schedule_read", args=[broadcast.pk]))
                return HttpResponseRedirect(self.get_success_url())
Example #17
0
        def form_valid(self, form):
            self.form = form
            user = self.request.user
            org = user.get_org()

            step_uuid = self.form.cleaned_data.get("step_node", None)
            text = self.form.cleaned_data["text"]
            has_schedule = False

            if step_uuid:
                from .tasks import send_to_flow_node

                get_params = {k: v for k, v in self.request.GET.items()}
                get_params.update({"s": step_uuid})
                send_to_flow_node.delay(org.pk, user.pk, text, **get_params)
            else:

                omnibox = omnibox_deserialize(org, self.form.cleaned_data["omnibox"])
                has_schedule = self.form.cleaned_data["schedule"]

                groups = list(omnibox["groups"])
                contacts = list(omnibox["contacts"])
                urns = list(omnibox["urns"])

                schedule = Schedule.create_blank_schedule(org, user) if has_schedule else None
                broadcast = Broadcast.create(
                    org,
                    user,
                    text,
                    groups=groups,
                    contacts=contacts,
                    urns=urns,
                    schedule=schedule,
                    status=QUEUED,
                    template_state=Broadcast.TEMPLATE_STATE_UNEVALUATED,
                )

                if not has_schedule:
                    self.post_save(broadcast)
                    super().form_valid(form)

                analytics.track(
                    self.request.user.username,
                    "temba.broadcast_created",
                    dict(contacts=len(contacts), groups=len(groups), urns=len(urns)),
                )

            if "HTTP_X_PJAX" in self.request.META:
                success_url = "hide"
                if has_schedule:
                    success_url = reverse("msgs.broadcast_schedule_read", args=[broadcast.pk])

                response = self.render_to_response(self.get_context_data())
                response["Temba-Success"] = success_url
                return response

            return HttpResponseRedirect(self.get_success_url())
Example #18
0
        def form_valid(self, form):
            self.form = form
            user = self.request.user
            org = user.get_org()
            simulation = self.request.GET.get('simulation', 'false') == 'true'

            omnibox = self.form.cleaned_data['omnibox']
            has_schedule = self.form.cleaned_data['schedule']
            step_uuid = self.form.cleaned_data.get('step_node', None)
            text = self.form.cleaned_data['text']

            groups = list(omnibox['groups'])
            contacts = list(omnibox['contacts'])
            urns = list(omnibox['urns'])
            recipients = list()

            if step_uuid:
                from .tasks import send_to_flow_node
                get_params = {k: v for k, v in self.request.GET.items()}
                get_params.update({'s': step_uuid})
                send_to_flow_node.delay(org.pk, user.pk, text, **get_params)
                if '_format' in self.request.GET and self.request.GET['_format'] == 'json':
                    return HttpResponse(json.dumps(dict(status="success")), content_type='application/json')
                else:
                    return HttpResponseRedirect(self.get_success_url())

            if simulation:
                # when simulating make sure we only use test contacts
                for contact in contacts:
                    if contact.is_test:
                        recipients.append(contact)
            else:
                for group in groups:
                    recipients.append(group)
                for contact in contacts:
                    recipients.append(contact)
                for urn in urns:
                    recipients.append(urn)

            schedule = Schedule.objects.create(created_by=user, modified_by=user) if has_schedule else None
            broadcast = Broadcast.create(org, user, text, recipients,
                                         schedule=schedule)

            if not has_schedule:
                self.post_save(broadcast)
                super(BroadcastCRUDL.Send, self).form_valid(form)

            analytics.track(self.request.user.username, 'temba.broadcast_created',
                            dict(contacts=len(contacts), groups=len(groups), urns=len(urns)))

            if '_format' in self.request.GET and self.request.GET['_format'] == 'json':
                data = dict(status="success", redirect=reverse('msgs.broadcast_schedule_read', args=[broadcast.pk]))
                return HttpResponse(json.dumps(data), content_type='application/json')
            else:
                if self.form.cleaned_data['schedule']:
                    return HttpResponseRedirect(reverse('msgs.broadcast_schedule_read', args=[broadcast.pk]))
                return HttpResponseRedirect(self.get_success_url())
Example #19
0
        def form_valid(self, form):
            analytics.track(self.request.user.username, 'temba.trigger_created_schedule')
            schedule = Schedule.objects.create(created_by=self.request.user, modified_by=self.request.user)

            if form.starts_never():
                schedule.reset()

            elif form.stopped():
                schedule.reset()

            elif form.starts_now():
                schedule.next_fire = timezone.now() - timedelta(days=1)
                schedule.repeat_period = 'O'
                schedule.repeat_days = 0
                schedule.status = 'S'
                schedule.save()

            else:
                # Scheduled case
                schedule.status = 'S'
                schedule.repeat_period = form.cleaned_data['repeat_period']
                start_time = form.get_start_time()
                if start_time:
                    schedule.next_fire = start_time

                # create our recurrence
                if form.is_recurring():
                    days = None
                    if 'repeat_days' in form.cleaned_data:
                        days = form.cleaned_data['repeat_days']
                    schedule.repeat_days = days
                    schedule.repeat_hour_of_day = schedule.next_fire.hour
                    schedule.repeat_day_of_month = schedule.next_fire.day
                schedule.save()

            recipients = self.form.cleaned_data['omnibox']

            trigger = Trigger.objects.create(flow=self.form.cleaned_data['flow'],
                                             org=self.request.user.get_org(),
                                             schedule=schedule,
                                             trigger_type=SCHEDULE_TRIGGER,
                                             created_by=self.request.user,
                                             modified_by=self.request.user)

            for group in recipients['groups']:
                trigger.groups.add(group)

            for contact in recipients['contacts']:
                trigger.contacts.add(contact)

            self.post_save(trigger)


            response = self.render_to_response(self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #20
0
    def claim_number(self, user, phone_number, country, role):

        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN, None)

        org = user.get_org()

        plivo_uuid = generate_uuid()
        app_name = "%s/%s" % (settings.TEMBA_HOST.lower(), plivo_uuid)

        client = plivo.RestAPI(auth_id, auth_token)

        message_url = "https://" + settings.TEMBA_HOST + "%s" % reverse('handlers.plivo_handler', args=['receive', plivo_uuid])
        answer_url = "https://" + settings.AWS_BUCKET_DOMAIN + "/plivo_voice_unavailable.xml"

        plivo_response_status, plivo_response = client.create_application(params=dict(app_name=app_name,
                                                                                      answer_url=answer_url,
                                                                                      message_url=message_url))

        if plivo_response_status in [201, 200, 202]:
            plivo_app_id = plivo_response['app_id']
        else:  # pragma: no cover
            plivo_app_id = None

        plivo_config = {Channel.CONFIG_PLIVO_AUTH_ID: auth_id,
                        Channel.CONFIG_PLIVO_AUTH_TOKEN: auth_token,
                        Channel.CONFIG_PLIVO_APP_ID: plivo_app_id}

        plivo_number = phone_number.strip('+ ').replace(' ', '')

        plivo_response_status, plivo_response = client.get_number(params=dict(number=plivo_number))

        if plivo_response_status != 200:
            plivo_response_status, plivo_response = client.buy_phone_number(params=dict(number=plivo_number))

            if plivo_response_status != 201:  # pragma: no cover
                raise Exception(_("There was a problem claiming that number, please check the balance on your account."))

            plivo_response_status, plivo_response = client.get_number(params=dict(number=plivo_number))

        if plivo_response_status == 200:
            plivo_response_status, plivo_response = client.modify_number(params=dict(number=plivo_number,
                                                                                     app_id=plivo_app_id))
            if plivo_response_status != 202:  # pragma: no cover
                raise Exception(_("There was a problem updating that number, please try again."))

        phone_number = '+' + plivo_number
        phone = phonenumbers.format_number(phonenumbers.parse(phone_number, None),
                                           phonenumbers.PhoneNumberFormat.NATIONAL)

        channel = Channel.create(org, user, country, 'PL', name=phone, address=phone_number,
                                 config=plivo_config, uuid=plivo_uuid)

        analytics.track(user.username, 'temba.channel_claim_plivo', dict(number=phone_number))

        return channel
Example #21
0
        def form_valid(self, form):
            self.form = form
            user = self.request.user
            org = user.get_org()

            omnibox = self.form.cleaned_data["omnibox"]
            has_schedule = self.form.cleaned_data["schedule"]
            step_uuid = self.form.cleaned_data.get("step_node", None)
            text = self.form.cleaned_data["text"]

            groups = list(omnibox["groups"])
            contacts = list(omnibox["contacts"])
            urns = list(omnibox["urns"])

            if step_uuid:
                from .tasks import send_to_flow_node

                get_params = {k: v for k, v in self.request.GET.items()}
                get_params.update({"s": step_uuid})
                send_to_flow_node.delay(org.pk, user.pk, text, **get_params)
                if "_format" in self.request.GET and self.request.GET["_format"] == "json":
                    return HttpResponse(json.dumps(dict(status="success")), content_type="application/json")
                else:
                    return HttpResponseRedirect(self.get_success_url())

            schedule = Schedule.create_blank_schedule(org, user) if has_schedule else None
            broadcast = Broadcast.create(
                org,
                user,
                text,
                groups=groups,
                contacts=contacts,
                urns=urns,
                schedule=schedule,
                status=QUEUED,
                template_state=Broadcast.TEMPLATE_STATE_UNEVALUATED,
            )

            if not has_schedule:
                self.post_save(broadcast)
                super().form_valid(form)

            analytics.track(
                self.request.user.username,
                "temba.broadcast_created",
                dict(contacts=len(contacts), groups=len(groups), urns=len(urns)),
            )

            if "_format" in self.request.GET and self.request.GET["_format"] == "json":
                data = dict(status="success", redirect=reverse("msgs.broadcast_schedule_read", args=[broadcast.pk]))
                return HttpResponse(json.dumps(data), content_type="application/json")
            else:
                if self.form.cleaned_data["schedule"]:
                    return HttpResponseRedirect(reverse("msgs.broadcast_schedule_read", args=[broadcast.pk]))
                return HttpResponseRedirect(self.get_success_url())
Example #22
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            Trigger.objects.create(created_by=user, modified_by=user, org=org, trigger_type=Trigger.TYPE_FOLLOW,
                                   flow=form.cleaned_data['flow'], channel=form.cleaned_data['channel'])

            analytics.track(self.request.user.username, 'temba.trigger_created_follow')

            response = self.render_to_response(self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #23
0
        def pre_save(self, obj):
            anon = User.objects.get(id=-1)
            obj = super(LeadCRUDL.Create, self).pre_save(obj)
            obj.created_by = anon
            obj.modified_by = anon

            if self.request.user.is_anonymous():
                analytics.identify(obj.email, dict(email=obj.email, plan='None', segment=randint(1, 10),
                                                   brand=self.request.branding['slug']))
                analytics.track(obj.email, 'temba.org_lead')

            return obj
Example #24
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            Trigger.objects.create(created_by=user, modified_by=user, org=org, trigger_type=Trigger.TYPE_FOLLOW,
                                   flow=form.cleaned_data['flow'], channel=form.cleaned_data['channel'])

            analytics.track(self.request.user.username, 'temba.trigger_created_follow')

            response = self.render_to_response(self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #25
0
        def pre_save(self, obj):
            anon = get_anonymous_user()
            obj = super(LeadCRUDL.Create, self).pre_save(obj)
            obj.created_by = anon
            obj.modified_by = anon

            if self.request.user.is_anonymous():
                analytics.identify(obj.email, dict(email=obj.email, plan='None', segment=randint(1, 10),
                                                   brand=self.request.branding['slug']))
                analytics.track(obj.email, 'temba.org_lead')

            return obj
Example #26
0
        def form_valid(self, form):
            try:
                cleaned_data = form.cleaned_data
                org = self.request.user.get_org()

                for key in cleaned_data:
                    if key.startswith('field_'):
                        idx = key[6:]
                        label = cleaned_data["label_%s" % idx]
                        field = cleaned_data[key]
                        show_in_table = cleaned_data["show_%s" % idx]
                        value_type = cleaned_data['type_%s' % idx]

                        if field == '__new_field':
                            if label:
                                analytics.track(self.request.user.username,
                                                'temba.contactfield_created')
                                key = ContactField.make_key(label)
                                ContactField.get_or_create(
                                    org,
                                    key,
                                    label,
                                    show_in_table=show_in_table,
                                    value_type=value_type)
                        else:
                            if label:
                                ContactField.get_or_create(
                                    org,
                                    field.key,
                                    label,
                                    show_in_table=show_in_table,
                                    value_type=value_type)
                            else:
                                ContactField.hide_field(org, field.key)

                if 'HTTP_X_PJAX' not in self.request.META:
                    return HttpResponseRedirect(self.get_success_url())
                else:  # pragma: no cover
                    return self.render_to_response(
                        self.get_context_data(
                            form=form,
                            success_url=self.get_success_url(),
                            success_script=getattr(self, 'success_script',
                                                   None)))

            except IntegrityError as e:  # pragma: no cover
                message = str(e).capitalize()
                errors = self.form._errors.setdefault(
                    forms.forms.NON_FIELD_ERRORS, forms.util.ErrorList())
                errors.append(message)
                return self.render_to_response(
                    self.get_context_data(form=form))
Example #27
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            self.object = Trigger.create(
                org, user, Trigger.TYPE_NEW_CONVERSATION, form.cleaned_data["flow"], form.cleaned_data["channel"]
            )

            analytics.track(self.request.user.username, "temba.trigger_created", dict(type="new_conversation"))

            response = self.render_to_response(self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #28
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            self.object = Trigger.create(
                org, user, Trigger.TYPE_NEW_CONVERSATION, form.cleaned_data["flow"], form.cleaned_data["channel"]
            )

            analytics.track(self.request.user.username, "temba.trigger_created_new_conversation")

            response = self.render_to_response(self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #29
0
    def claim_number(self, user, phone_number, country, role):
        org = user.get_org()

        client = org.get_twilio_client()
        twilio_phones = client.api.incoming_phone_numbers.stream(
            phone_number=phone_number)
        channel_uuid = uuid4()

        # create new TwiML app
        callback_domain = org.get_brand_domain()

        twilio_phone = next(twilio_phones, None)
        if not twilio_phone:
            raise Exception(
                _("Only existing Twilio WhatsApp number are supported"))

        phone = phonenumbers.format_number(
            phonenumbers.parse(phone_number, None),
            phonenumbers.PhoneNumberFormat.NATIONAL)

        number_sid = twilio_phone.sid

        org_config = org.config
        config = {
            Channel.CONFIG_NUMBER_SID: number_sid,
            Channel.CONFIG_ACCOUNT_SID: org_config[Org.CONFIG_TWILIO_SID],
            Channel.CONFIG_AUTH_TOKEN: org_config[Org.CONFIG_TWILIO_TOKEN],
            Channel.CONFIG_CALLBACK_DOMAIN: callback_domain,
        }

        role = Channel.ROLE_SEND + Channel.ROLE_RECEIVE

        channel = Channel.create(
            org,
            user,
            country,
            "TWA",
            name=phone,
            address=phone_number,
            role=role,
            config=config,
            uuid=channel_uuid,
            schemes=[WHATSAPP_SCHEME],
        )

        analytics.track(user.username,
                        "temba.channel_claim_twilio_whatsapp",
                        properties=dict(number=phone_number))

        return channel
Example #30
0
        def pre_save(self, obj):
            anon = User.objects.get(username=settings.ANONYMOUS_USER_NAME)
            obj = super(LeadCRUDL.Create, self).pre_save(obj)
            obj.created_by = anon
            obj.modified_by = anon

            if self.request.user.is_anonymous():
                analytics.identify(
                    obj.email,
                    dict(email=obj.email, plan="None", segment=randint(1, 10), brand=self.request.branding["slug"]),
                )
                analytics.track(obj.email, "temba.org_lead")

            return obj
Example #31
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            trigger = Trigger.objects.create(created_by=user, modified_by=user, org=org, trigger_type=Trigger.TYPE_NEW_CONVERSATION,
                                             flow=form.cleaned_data['flow'], channel=form.cleaned_data['channel'])
            trigger.archive_conflicts(user)
            trigger.channel.set_fb_call_to_action_payload(Channel.GET_STARTED)

            analytics.track(self.request.user.username, 'temba.trigger_created_new_conversation')

            response = self.render_to_response(self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #32
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            trigger = Trigger.objects.create(created_by=user, modified_by=user, org=org, trigger_type=Trigger.TYPE_NEW_CONVERSATION,
                                             flow=form.cleaned_data['flow'], channel=form.cleaned_data['channel'])
            trigger.archive_conflicts(user)
            trigger.channel.set_fb_call_to_action_payload(Channel.GET_STARTED)

            analytics.track(self.request.user.username, 'temba.trigger_created_new_conversation')

            response = self.render_to_response(self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #33
0
    def test_track(self, mock_get_backends):
        good = MagicMock()
        mock_get_backends.return_value = [BadBackend(), good]

        analytics.track(self.user, "foo_created", {"foo_id": 234})

        good.track.assert_called_once_with(self.user, "foo_created",
                                           {"foo_id": 234})
        good.track.reset_mock()

        # anonymous user is a noop
        analytics.track(AnonymousUser(), "foo_created", {"foo_id": 234})

        good.track.assert_not_called()
Example #34
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            trigger = Trigger.objects.create(created_by=user, modified_by=user, org=org,
                                             keyword=form.cleaned_data['keyword'], trigger_type=Trigger.TYPE_USSD_PULL,
                                             flow=form.cleaned_data['flow'], channel=form.cleaned_data['channel'])
            trigger.archive_conflicts(user)

            analytics.track(self.request.user.username, 'temba.trigger_created_ussd')

            response = self.render_to_response(self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #35
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            self.object = Trigger.create(org, user, Trigger.TYPE_FOLLOW,
                                         form.cleaned_data["flow"],
                                         form.cleaned_data["channel"])

            analytics.track(self.request.user.username,
                            "temba.trigger_created_follow")

            response = self.render_to_response(
                self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #36
0
        def form_valid(self, form):
            self.form = form
            user = self.request.user
            simulation = self.request.REQUEST.get("simulation", "false") == "true"

            omnibox = self.form.cleaned_data["omnibox"]
            has_schedule = self.form.cleaned_data["schedule"]

            groups = list(omnibox["groups"])
            contacts = list(omnibox["contacts"])
            urns = list(omnibox["urns"])
            recipients = list()

            if simulation:
                # when simulating make sure we only use test contacts
                for contact in contacts:
                    if contact.is_test:
                        recipients.append(contact)
            else:
                for group in groups:
                    recipients.append(group)
                for contact in contacts:
                    recipients.append(contact)
                for urn in urns:
                    recipients.append(urn)

            schedule = Schedule.objects.create(created_by=user, modified_by=user) if has_schedule else None
            broadcast = Broadcast.create(
                user.get_org(), user, self.form.cleaned_data["text"], recipients, schedule=schedule
            )

            if not has_schedule:
                self.post_save(broadcast)
                super(BroadcastCRUDL.Send, self).form_valid(form)

            analytics.track(
                self.request.user.username,
                "temba.broadcast_created",
                dict(contacts=len(contacts), groups=len(groups), urns=len(urns)),
            )

            if "_format" in self.request.REQUEST and self.request.REQUEST["_format"] == "json":
                data = dict(status="success", redirect=reverse("msgs.broadcast_schedule_read", args=[broadcast.pk]))
                return HttpResponse(json.dumps(data), content_type="application/json")
            else:
                if self.form.cleaned_data["schedule"]:
                    return HttpResponseRedirect(reverse("msgs.broadcast_schedule_read", args=[broadcast.pk]))
                return HttpResponseRedirect(self.get_success_url())
Example #37
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            self.object = Trigger.create(org, user,
                                         Trigger.TYPE_NEW_CONVERSATION,
                                         form.cleaned_data['flow'],
                                         form.cleaned_data['channel'])

            analytics.track(self.request.user.username,
                            'temba.trigger_created_new_conversation')

            response = self.render_to_response(
                self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #38
0
        def render_to_response(self, context, **response_kwargs):

            analytics.track(self.request.user.username, 'temba.contact_exported')

            user = self.request.user
            org = user.get_org()

            group = None
            group_id = self.request.REQUEST.get('g', None)
            if group_id:
                groups = ContactGroup.user_groups.filter(pk=group_id, org=org)

                if groups:
                    group = groups[0]

            host = self.request.branding['host']

            # is there already an export taking place?
            existing = ExportContactsTask.objects.filter(org=org, is_finished=False,
                                                         created_on__gt=timezone.now() - timedelta(hours=24))\
                                                 .order_by('-created_on').first()

            # if there is an existing export, don't allow it
            if existing:
                messages.info(self.request,
                              _("There is already an export in progress, started by %s. You must wait "
                                "for that export to complete before starting another." % existing.created_by.username))

            # otherwise, off we go
            else:
                export = ExportContactsTask.objects.create(created_by=user, modified_by=user, org=org,
                                                           group=group, host=host)
                export_contacts_task.delay(export.pk)

                if not getattr(settings, 'CELERY_ALWAYS_EAGER', False):
                    messages.info(self.request,
                                  _("We are preparing your export. We will e-mail you at %s when it is ready.")
                                  % self.request.user.username)

                else:
                    export = ExportContactsTask.objects.get(id=export.pk)
                    dl_url = reverse('assets.download', kwargs=dict(type='contact_export', identifier=export.pk))
                    messages.info(self.request,
                                  _("Export complete, you can find it here: %s (production users will get an email)")
                                  % dl_url)

            return HttpResponseRedirect(reverse('contacts.contact_list'))
Example #39
0
        def render_to_response(self, context, **response_kwargs):

            analytics.track(self.request.user.username, 'temba.contact_exported')

            user = self.request.user
            org = user.get_org()

            group = None
            group_id = self.request.REQUEST.get('g', None)
            if group_id:
                groups = ContactGroup.user_groups.filter(pk=group_id, org=org)

                if groups:
                    group = groups[0]

            host = self.request.branding['host']

            # is there already an export taking place?
            existing = ExportContactsTask.objects.filter(org=org, is_finished=False,
                                                         created_on__gt=timezone.now() - timedelta(hours=24))\
                                                 .order_by('-created_on').first()

            # if there is an existing export, don't allow it
            if existing:
                messages.info(self.request,
                              _("There is already an export in progress, started by %s. You must wait "
                                "for that export to complete before starting another." % existing.created_by.username))

            # otherwise, off we go
            else:
                export = ExportContactsTask.objects.create(created_by=user, modified_by=user, org=org,
                                                           group=group, host=host)
                export_contacts_task.delay(export.pk)

                if not getattr(settings, 'CELERY_ALWAYS_EAGER', False):
                    messages.info(self.request,
                                  _("We are preparing your export. We will e-mail you at %s when it is ready.")
                                  % self.request.user.username)

                else:
                    export = ExportContactsTask.objects.get(id=export.pk)
                    dl_url = reverse('assets.download', kwargs=dict(type='contact_export', pk=export.pk))
                    messages.info(self.request,
                                  _("Export complete, you can find it here: %s (production users will get an email)")
                                  % dl_url)

            return HttpResponseRedirect(reverse('contacts.contact_list'))
Example #40
0
        def form_valid(self, form):
            self.form = form
            user = self.request.user
            simulation = self.request.REQUEST.get('simulation', 'false') == 'true'

            omnibox = self.form.cleaned_data['omnibox']
            has_schedule = self.form.cleaned_data['schedule']

            schedule = Schedule.objects.create(created_by=user, modified_by=user) if has_schedule else None
            broadcast = Broadcast.create(user, self.form.cleaned_data['text'], schedule=schedule)

            groups = list(omnibox['groups'])
            contacts = list(omnibox['contacts'])
            urns = list(omnibox['urns'])
            recipients = list()

            if simulation:
                # when simulating make sure we only use test contacts
                for contact in contacts:
                    if contact.is_test:
                        recipients.append(contact)
            else:
                for group in groups:
                    recipients.append(group)
                for contact in contacts:
                    recipients.append(contact)
                for urn in urns:
                    recipients.append(urn)

            broadcast.set_recipients(*recipients)

            if not has_schedule:
                self.post_save(broadcast)
                super(BroadcastCRUDL.Send, self).form_valid(form)

            analytics.track(self.request.user.username, 'temba.broadcast_created',
                            dict(contacts=len(contacts), groups=len(groups), urns=len(urns)))

            if '_format' in self.request.REQUEST and self.request.REQUEST['_format'] == 'json':
                data = dict(status="success", redirect=reverse('msgs.broadcast_schedule_read', args=[broadcast.pk]))
                return HttpResponse(json.dumps(data), content_type='application/json')
            else:
                if self.form.cleaned_data['schedule']:
                    return HttpResponseRedirect(reverse('msgs.broadcast_schedule_read', args=[broadcast.pk]))
                return HttpResponseRedirect(self.get_success_url())
Example #41
0
        def form_valid(self, form):
            keyword = form.cleaned_data['keyword']
            join_group = form.cleaned_data['action_join_group']
            start_flow = form.cleaned_data['flow']
            send_msg = form.cleaned_data['response']

            group_flow = Flow.create_join_group_flow(self.request.user, join_group, send_msg, start_flow)

            Trigger.objects.create(created_by=self.request.user, modified_by=self.request.user,
                                   org=self.request.user.get_org(), keyword=keyword,
                                   trigger_type=KEYWORD_TRIGGER,
                                   flow=group_flow)

            analytics.track(self.request.user.username, 'temba.trigger_created_register', dict(name=join_group.name))

            response = self.render_to_response(self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #42
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            # first archive all catch all message triggers
            Trigger.objects.filter(org=org,
                                   trigger_type=CATCH_ALL_TRIGGER,
                                   is_active=True).update(is_archived=True)

            # then create a new catch all trigger
            Trigger.objects.create(created_by=user, modified_by=user, org=org, trigger_type=CATCH_ALL_TRIGGER,
                                   flow=form.cleaned_data['flow'])

            analytics.track(self.request.user.username, 'temba.trigger_created_catchall')

            response = self.render_to_response(self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #43
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            self.object = Trigger.create(
                org,
                user,
                Trigger.TYPE_USSD_PULL,
                form.cleaned_data["flow"],
                form.cleaned_data["channel"],
                keyword=form.cleaned_data["keyword"],
            )

            analytics.track(self.request.user.username, "temba.trigger_created_ussd")

            response = self.render_to_response(self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #44
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            self.object = Trigger.create(
                org,
                user,
                Trigger.TYPE_REFERRAL,
                form.cleaned_data["flow"],
                form.cleaned_data["channel"],
                referrer_id=form.cleaned_data["referrer_id"],
            )

            analytics.track(self.request.user.username, "temba.trigger_created", dict(type="referral"))

            response = self.render_to_response(self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #45
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            # first archive all catch all message triggers
            Trigger.objects.filter(org=org,
                                   trigger_type=CATCH_ALL_TRIGGER,
                                   is_active=True).update(is_archived=True)

            # then create a new catch all trigger
            Trigger.objects.create(created_by=user, modified_by=user, org=org, trigger_type=CATCH_ALL_TRIGGER,
                                   flow=form.cleaned_data['flow'])

            analytics.track(self.request.user.username, 'temba.trigger_created_catchall')

            response = self.render_to_response(self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #46
0
        def form_valid(self, form):
            user = self.request.user
            org = user.get_org()

            self.object = Trigger.create(org,
                                         user,
                                         Trigger.TYPE_USSD_PULL,
                                         form.cleaned_data['flow'],
                                         form.cleaned_data['channel'],
                                         keyword=form.cleaned_data['keyword'])

            analytics.track(self.request.user.username,
                            'temba.trigger_created_ussd')

            response = self.render_to_response(
                self.get_context_data(form=form))
            response['REDIRECT'] = self.get_success_url()
            return response
Example #47
0
        def render_to_response(self, context, **response_kwargs):

            analytics.track(self.request.user.username,
                            'temba.contact_exported')

            user = self.request.user
            org = user.get_org()

            group = None
            group_id = self.request.REQUEST.get('g', None)
            if group_id:
                groups = ContactGroup.objects.filter(pk=group_id, org=org)

                if groups:
                    group = groups[0]

            host = self.request.branding['host']

            export = ExportContactsTask.objects.create(created_by=user,
                                                       modified_by=user,
                                                       org=org,
                                                       group=group,
                                                       host=host)
            export_contacts_task.delay(export.pk)

            from django.contrib import messages
            if not getattr(settings, 'CELERY_ALWAYS_EAGER', False):
                messages.info(
                    self.request,
                    _("We are preparing your export. ") +
                    _("We will e-mail you at %s when it is ready.") %
                    self.request.user.username)

            else:
                export = ExportContactsTask.objects.get(id=export.pk)
                dl_url = "file://%s/%s" % (settings.MEDIA_ROOT,
                                           export.filename)
                messages.info(
                    self.request,
                    _("Export complete, you can find it here: %s (production users will get an email)"
                      ) % dl_url)

            return HttpResponseRedirect(reverse('contacts.contact_list'))
Example #48
0
def send_to_flow_node(org_id, user_id, text, **kwargs):
    from django.contrib.auth.models import User
    from temba.contacts.models import Contact
    from temba.orgs.models import Org
    from temba.flows.models import FlowRun

    org = Org.objects.get(pk=org_id)
    user = User.objects.get(pk=user_id)
    simulation = kwargs.get("simulation", "false") == "true"
    node_uuid = kwargs.get("s", None)

    runs = FlowRun.objects.filter(org=org, current_node_uuid=node_uuid, is_active=True)

    contact_ids = (
        Contact.objects.filter(org=org, is_blocked=False, is_stopped=False, is_active=True, is_test=simulation)
        .filter(id__in=runs.values_list("contact", flat=True))
        .values_list("id", flat=True)
    )

    broadcast = Broadcast.create(org, user, text, contact_ids=contact_ids)
    broadcast.send(expressions_context={})

    analytics.track(user.username, "temba.broadcast_created", dict(contacts=len(contact_ids), groups=0, urns=0))
Example #49
0
        def form_valid(self, form):
            keyword = form.cleaned_data["keyword"]
            join_group = form.cleaned_data["action_join_group"]
            start_flow = form.cleaned_data["flow"]
            send_msg = form.cleaned_data["response"]

            org = self.request.user.get_org()
            group_flow = Flow.create_join_group(org, self.request.user, join_group, send_msg, start_flow)

            Trigger.objects.create(
                created_by=self.request.user,
                modified_by=self.request.user,
                org=self.request.user.get_org(),
                keyword=keyword,
                trigger_type=Trigger.TYPE_KEYWORD,
                flow=group_flow,
            )

            analytics.track(self.request.user.username, "temba.trigger_created_register", dict(name=join_group.name))

            response = self.render_to_response(self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #50
0
        def form_valid(self, form):

            user = self.request.user
            org = user.get_org()

            # first archive all missed call triggers
            Trigger.objects.filter(org=org, trigger_type=Trigger.TYPE_MISSED_CALL, is_active=True).update(
                is_archived=True
            )

            # then create a new missed call trigger
            Trigger.objects.create(
                created_by=user,
                modified_by=user,
                org=org,
                trigger_type=Trigger.TYPE_MISSED_CALL,
                flow=form.cleaned_data["flow"],
            )

            analytics.track(self.request.user.username, "temba.trigger_created_missed_call")

            response = self.render_to_response(self.get_context_data(form=form))
            response["REDIRECT"] = self.get_success_url()
            return response
Example #51
0
        def form_valid(self, form):
            try:
                cleaned_data = form.cleaned_data
                org = self.request.user.get_org()

                for key in cleaned_data:
                    if key.startswith('field_'):
                        idx = key[6:]
                        label = cleaned_data["label_%s" % idx]
                        field = cleaned_data[key]
                        show_in_table = cleaned_data["show_%s" % idx]
                        value_type = cleaned_data['type_%s' % idx]

                        if field == '__new_field':
                            if label:
                                analytics.track(self.request.user.username, 'temba.contactfield_created')
                                key = ContactField.make_key(label)
                                ContactField.get_or_create(org, key, label, show_in_table=show_in_table, value_type=value_type)
                        else:
                            if label:
                                ContactField.get_or_create(org, field.key, label, show_in_table=show_in_table, value_type=value_type)
                            else:
                                ContactField.hide_field(org, field.key)

                if 'HTTP_X_PJAX' not in self.request.META:
                    return HttpResponseRedirect(self.get_success_url())
                else:  # pragma: no cover
                    return self.render_to_response(self.get_context_data(form=form,
                                                                         success_url=self.get_success_url(),
                                                                         success_script=getattr(self, 'success_script', None)))

            except IntegrityError as e:  # pragma: no cover
                message = str(e).capitalize()
                errors = self.form._errors.setdefault(forms.forms.NON_FIELD_ERRORS, forms.utils.ErrorList())
                errors.append(message)
                return self.render_to_response(self.get_context_data(form=form))
Example #52
0
    def claim_number(self, user, phone_number, country, role):
        org = user.get_org()

        client = org.get_twilio_client()
        twilio_phones = client.api.incoming_phone_numbers.stream(phone_number=phone_number)
        channel_uuid = uuid4()

        # create new TwiML app
        callback_domain = org.get_brand_domain()
        new_receive_url = "https://" + callback_domain + reverse("courier.t", args=[channel_uuid, "receive"])
        new_status_url = (
            "https://" + callback_domain + reverse("handlers.twilio_handler", args=["status", channel_uuid])
        )
        new_voice_url = "https://" + callback_domain + reverse("handlers.twilio_handler", args=["voice", channel_uuid])

        new_app = client.api.applications.create(
            friendly_name="%s/%s" % (callback_domain.lower(), channel_uuid),
            sms_url=new_receive_url,
            sms_method="POST",
            voice_url=new_voice_url,
            voice_fallback_url="https://" + settings.TEMBA_HOST+settings.MEDIA_URL+ "voice_unavailable.xml",
            voice_fallback_method="GET",
            status_callback=new_status_url,
            status_callback_method="POST",
        )

        is_short_code = len(phone_number) <= 6
        if is_short_code:
            short_codes = client.api.short_codes.stream(short_code=phone_number)
            short_code = next(short_codes, None)

            if short_code:
                number_sid = short_code.sid
                app_url = "https://" + callback_domain + "%s" % reverse("courier.t", args=[channel_uuid, "receive"])
                client.api.short_codes.get(number_sid).update(sms_url=app_url, sms_method="POST")

                role = Channel.ROLE_SEND + Channel.ROLE_RECEIVE
                phone = phone_number

            else:  # pragma: no cover
                raise Exception(
                    _(
                        "Short code not found on your Twilio Account. "
                        "Please check you own the short code and Try again"
                    )
                )
        else:
            twilio_phone = next(twilio_phones, None)
            if twilio_phone:

                client.api.incoming_phone_numbers.get(twilio_phone.sid).update(
                    voice_application_sid=new_app.sid, sms_application_sid=new_app.sid
                )

            else:  # pragma: needs cover
                twilio_phone = client.api.incoming_phone_numbers.create(
                    phone_number=phone_number, voice_application_sid=new_app.sid, sms_application_sid=new_app.sid
                )

            phone = phonenumbers.format_number(
                phonenumbers.parse(phone_number, None), phonenumbers.PhoneNumberFormat.NATIONAL
            )

            number_sid = twilio_phone.sid

        org_config = org.config
        config = {
            Channel.CONFIG_APPLICATION_SID: new_app.sid,
            Channel.CONFIG_NUMBER_SID: number_sid,
            Channel.CONFIG_ACCOUNT_SID: org_config[ACCOUNT_SID],
            Channel.CONFIG_AUTH_TOKEN: org_config[ACCOUNT_TOKEN],
            Channel.CONFIG_CALLBACK_DOMAIN: callback_domain,
        }

        channel = Channel.create(
            org, user, country, "T", name=phone, address=phone_number, role=role, config=config, uuid=channel_uuid
        )

        analytics.track(user.username, "temba.channel_claim_twilio", properties=dict(number=phone_number))

        return channel
Example #53
0
 def form_valid(self, form):
     analytics.track(self.request.user.username, "temba.trigger_created_keyword")
     return super().form_valid(form)
Example #54
0
    def claim_number(self, user, phone_number, country, role):
        org = user.get_org()

        client = org.get_nexmo_client()
        org_config = org.config
        app_id = org_config.get(NEXMO_APP_ID)

        nexmo_phones = client.get_numbers(phone_number)
        is_shortcode = False

        # try it with just the national code (for short codes)
        if not nexmo_phones:
            parsed = phonenumbers.parse(phone_number, None)
            shortcode = str(parsed.national_number)

            nexmo_phones = client.get_numbers(shortcode)

            if nexmo_phones:
                is_shortcode = True
                phone_number = shortcode

        # buy the number if we have to
        if not nexmo_phones:
            try:
                client.buy_nexmo_number(country, phone_number)
            except Exception as e:
                raise Exception(
                    _(
                        "There was a problem claiming that number, "
                        "please check the balance on your account. " + "Note that you can only claim numbers after "
                        "adding credit to your Nexmo account."
                    )
                    + "\n"
                    + str(e)
                )

        channel_uuid = generate_uuid()
        callback_domain = org.get_brand_domain()
        new_receive_url = "https://" + callback_domain + reverse("courier.nx", args=[channel_uuid, "receive"])

        nexmo_phones = client.get_numbers(phone_number)

        features = [elt.upper() for elt in nexmo_phones[0]["features"]]
        role = ""
        if "SMS" in features:
            role += Channel.ROLE_SEND + Channel.ROLE_RECEIVE

        if "VOICE" in features:
            role += Channel.ROLE_ANSWER + Channel.ROLE_CALL

        # update the delivery URLs for it
        try:
            client.update_nexmo_number(country, phone_number, new_receive_url, app_id)

        except Exception as e:  # pragma: no cover
            # shortcodes don't seem to claim right on nexmo, move forward anyways
            if not is_shortcode:
                raise Exception(
                    _("There was a problem claiming that number, please check the balance on your account.")
                    + "\n"
                    + str(e)
                )

        if is_shortcode:
            phone = phone_number
            nexmo_phone_number = phone_number
        else:
            parsed = phonenumbers.parse(phone_number, None)
            phone = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.INTERNATIONAL)

            # nexmo ships numbers around as E164 without the leading +
            nexmo_phone_number = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164).strip("+")

        config = {
            Channel.CONFIG_NEXMO_APP_ID: app_id,
            Channel.CONFIG_NEXMO_APP_PRIVATE_KEY: org_config[NEXMO_APP_PRIVATE_KEY],
            Channel.CONFIG_NEXMO_API_KEY: org_config[NEXMO_KEY],
            Channel.CONFIG_NEXMO_API_SECRET: org_config[NEXMO_SECRET],
            Channel.CONFIG_CALLBACK_DOMAIN: callback_domain,
        }

        channel = Channel.create(
            org,
            user,
            country,
            "NX",
            name=phone,
            address=phone_number,
            role=role,
            config=config,
            bod=nexmo_phone_number,
            uuid=channel_uuid,
            tps=1,
        )

        analytics.track(user.username, "temba.channel_claim_nexmo", dict(number=phone_number))

        return channel
Example #55
0
 def form_valid(self, form):
     analytics.track(self.request.user.username, 'temba.trigger_created_keyword')
     return super(TriggerCRUDL.Keyword, self).form_valid(form)
Example #56
0
    def claim_number(self, user, phone_number, country, role):

        auth_id = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_ID, None)
        auth_token = self.request.session.get(Channel.CONFIG_PLIVO_AUTH_TOKEN, None)

        org = user.get_org()

        plivo_uuid = generate_uuid()
        callback_domain = org.get_brand_domain()
        app_name = "%s/%s" % (callback_domain.lower(), plivo_uuid)

        message_url = "https://" + callback_domain + "%s" % reverse("courier.pl", args=[plivo_uuid, "receive"])
        answer_url = "https://" + settings.TEMBA_HOST+settings.MEDIA_URL+ "plivo_voice_unavailable.xml"

        headers = http_headers(extra={"Content-Type": "application/json"})
        create_app_url = "https://api.plivo.com/v1/Account/%s/Application/" % auth_id

        response = requests.post(
            create_app_url,
            json=dict(app_name=app_name, answer_url=answer_url, message_url=message_url),
            headers=headers,
            auth=(auth_id, auth_token),
        )

        if response.status_code in [201, 200, 202]:
            plivo_app_id = response.json()["app_id"]
        else:  # pragma: no cover
            plivo_app_id = None

        plivo_config = {
            Channel.CONFIG_PLIVO_AUTH_ID: auth_id,
            Channel.CONFIG_PLIVO_AUTH_TOKEN: auth_token,
            Channel.CONFIG_PLIVO_APP_ID: plivo_app_id,
            Channel.CONFIG_CALLBACK_DOMAIN: org.get_brand_domain(),
        }

        plivo_number = phone_number.strip("+ ").replace(" ", "")
        response = requests.get(
            "https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
            headers=headers,
            auth=(auth_id, auth_token),
        )

        if response.status_code != 200:
            response = requests.post(
                "https://api.plivo.com/v1/Account/%s/PhoneNumber/%s/" % (auth_id, plivo_number),
                headers=headers,
                auth=(auth_id, auth_token),
            )

            if response.status_code != 201:  # pragma: no cover
                raise Exception(
                    _("There was a problem claiming that number, please check the balance on your account.")
                )

            response = requests.get(
                "https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
                headers=headers,
                auth=(auth_id, auth_token),
            )

        if response.status_code == 200:
            response = requests.post(
                "https://api.plivo.com/v1/Account/%s/Number/%s/" % (auth_id, plivo_number),
                json=dict(app_id=plivo_app_id),
                headers=headers,
                auth=(auth_id, auth_token),
            )

            if response.status_code != 202:  # pragma: no cover
                raise Exception(_("There was a problem updating that number, please try again."))

        phone_number = "+" + plivo_number
        phone = phonenumbers.format_number(
            phonenumbers.parse(phone_number, None), phonenumbers.PhoneNumberFormat.NATIONAL
        )

        channel = Channel.create(
            org, user, country, "PL", name=phone, address=phone_number, config=plivo_config, uuid=plivo_uuid
        )

        analytics.track(user.username, "temba.channel_claim_plivo", dict(number=phone_number))

        return channel