aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/SMS.pm
blob: 874108706b2a97e3c58c48688b484fdec70d9d54 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
package FixMyStreet::SMS;

use strict;
use warnings;

use JSON::MaybeXS;
use Moo;
use Number::Phone::Lib;
use WWW::Twilio::API;

use FixMyStreet;
use mySociety::EmailUtil qw(is_valid_email);
use FixMyStreet::DB;

has twilio => (
    is => 'lazy',
    default => sub {
        WWW::Twilio::API->new(
            AccountSid => FixMyStreet->config('TWILIO_ACCOUNT_SID'),
            AuthToken => FixMyStreet->config('TWILIO_AUTH_TOKEN'),
            utf8 => 1,
        );
    },
);

has from => (
    is => 'lazy',
    default => sub { FixMyStreet->config('TWILIO_FROM_PARAMETER') },
);

has messaging_service => (
    is => 'lazy',
    default => sub { FixMyStreet->config('TWILIO_MESSAGING_SERVICE_SID') },
);

sub send_token {
    my ($class, $token_data, $token_scope, $to) = @_;

    # Random number between 10,000 and 75,535
    my $random = 10000 + unpack('n', mySociety::Random::random_bytes(2, 1));
    $token_data->{code} = $random;
    my $token_obj = FixMyStreet::DB->resultset("Token")->create({
        scope => $token_scope,
        data => $token_data,
    });
    my $body = sprintf(_("Your verification code is %s"), $random);

    my $result = $class->new->send(to => $to, body => $body);
    return {
        random => $random,
        token => $token_obj->token,
        %$result,
    };
}

sub send {
    my ($self, %params) = @_;
    my $output = $self->twilio->POST('Messages.json', 
        $self->from ? (From => $self->from) : (),
        $self->messaging_service ? (MessagingServiceSid => $self->messaging_service) : (),
        To => $params{to},
        Body => $params{body},
    );
    my $data = decode_json($output->{content});
    if ($output->{code} >= 400) {
        return { error => "$data->{message} ($data->{code})" };
    }
    return { success => $data->{sid} };
}

=head2 parse_username

Given a string that might be an email address or a phone number,
return what we think it is, and if it's valid one of those. Or
undef if it's empty.

=cut

sub parse_username {
    my ($class, $username) = @_;

    return { type => 'email', username => $username } unless $username;

    $username = lc $username;
    $username =~ s/\s+//g;

    return { type => 'email', email => $username, username => $username } if is_valid_email($username);

    my $type = $username =~ /^[^a-z]+$/i ? 'phone' : 'email';
    my $phone = do {
        if ($username =~ /^\+/) {
            # If already in international format, use that
            Number::Phone::Lib->new($username)
        } else {
            # Otherwise, assume it is country configured
            my $country = FixMyStreet->config('PHONE_COUNTRY');
            Number::Phone::Lib->new($country, $username);
        }
    };

    my $may_be_mobile = 0;
    if ($phone) {
        $type = 'phone';
        # Store phone without spaces
        ($username = $phone->format) =~ s/\s+//g;
        # Is this mobile definitely or possibly a mobile? (+1 numbers)
        $may_be_mobile = 1 if $phone->is_mobile || (!defined $phone->is_mobile && $phone->is_geographic);
    }

    return {
        type => $type,
        phone => $phone,
        may_be_mobile => $may_be_mobile,
        username => $username,
    };
}

1;