App-RoboBot

 view release on metacpan or  search on metacpan

lib/App/RoboBot/Network/Slack.pm  view on Meta::CPAN

    my ($json, $chandata);

    # Slack has different API endpooints for channels and private groups, so
    # make sure we're using the right one based on the first character of the
    # identifier. And some, like direct messages, return fairly different data
    # structures, so each type should handle decoding and massaging their own.
    if (substr($slack_id, 0, 1) eq 'C') {
        $json = get('https://slack.com/api/channels.info?token=' . $self->token . '&channel=' . $slack_id);
        eval { $json = decode_json($json) };
        return if $@;

        return unless exists $json->{'ok'} && $json->{'ok'};
        return unless exists $json->{'channel'};
        $chandata = $json->{'channel'};
    } elsif (substr($slack_id, 0, 1) eq 'G') {
        $json = get('https://slack.com/api/groups.info?token=' . $self->token . '&channel=' . $slack_id);
        eval { $json = decode_json($json) };
        return if $@;

        return unless exists $json->{'ok'} && $json->{'ok'};
        return unless exists $json->{'group'};
        $chandata = $json->{'group'};
    } elsif (substr($slack_id, 0, 1) eq 'D') {
        # This was a direct message, which requires some additional handling.
        $json = get('https://slack.com/api/im.list?token=' . $self->token);
        eval { $json = decode_json($json) };
        return if $@;

        return unless exists $json->{'ok'} && $json->{'ok'};
        return unless exists $json->{'ims'} && ref($json->{'ims'}) eq 'ARRAY';

        $chandata = (grep { $_->{'id'} eq $slack_id } @{$json->{'ims'}})[0];
        return unless defined $chandata && ref($chandata) eq 'HASH';
        return unless exists $chandata->{'user'};
        return if $chandata->{'user'} eq 'USLACKBOT';

        my $nick = $self->resolve_nick($chandata->{'user'});

        # Mock up a channel name for the DM history with this user.
        $chandata->{'name'} = sprintf('dm:%s', $nick->name);
    } else {
        # Not a group or a channel or a direct message, bail out.
        return;
    }

    return unless defined $chandata && ref($chandata) eq 'HASH';

    $res = $self->bot->config->db->do(q{
        select id, name, extradata
        from channels
        where network_id = ? and lower(name) = lower(?)
    }, $self->id, $chandata->{'name'});

    if ($res && $res->next) {
        $res->{'extradata'} = decode_json($res->{'extradata'});
        $res->{'extradata'}{'slack_id'} = $slack_id;

        $self->bot->config->db->do(q{
            update channels
            set extradata = ?,
                updated_at = now() where id = ?
        }, encode_json($res->{'extradata'}), $res->{'id'});

        $channel = App::RoboBot::Channel->new(
            id          => $res->{'id'},
            name        => $res->{'name'},
            extradata   => $res->{'extradata'},
            network     => $self,
            config      => $self->bot->config,
        );

        $self->channel_cache->{$slack_id} = $channel;
        return $channel;
    }

    $res = $self->bot->config->db->do(q{
        insert into channels ??? returning id, name, extradata
    }, { name       => $chandata->{'name'},
         network_id => $self->id,
         extradata  => encode_json({ slack_id => $slack_id }),
    });

    if ($res && $res->next) {
        $channel = App::RoboBot::Channel->new(
            id          => $res->{'id'},
            name        => $res->{'name'},
            extradata   => decode_json($res->{'extradata'}),
            network     => $self,
            config      => $self->bot->config,
        );

        $self->channel_cache->{$slack_id} = $channel;
        return $channel;
    }

    return;
}

sub resolve_nick {
    my ($self, $slack_id) = @_;

    # User profile already in our cache (we've seen or created it during this
    # session, so simply return what we have.
    return $self->profile_cache->{$slack_id} if exists $self->profile_cache->{$slack_id};

    # Check database for a nick with this Slack ID. If found, instantiate a new
    # App::RoboBot::Nick object with the data, cache it, and return.
    my $nick;

    my $res = $self->bot->config->db->do(q{
        select id, name, extradata
        from nicks where extradata @> ?
    }, encode_json({ slack_id => $slack_id }));

    if ($res && $res->next) {
        $nick = App::RoboBot::Nick->new(
            id        => $res->{'id'},
            name      => $res->{'name'},
            extradata => decode_json($res->{'extradata'}),
            network   => $self,
            config    => $self->config,
        );

        $self->profile_cache->{$slack_id} = $nick;
        return $nick;
    }

    # We haven't encountered this nick before, so we need to query the SlackAPI
    # for their handle and other profile details.
    my $json = get('https://slack.com/api/users.info?token=' . $self->token . '&user=' . $slack_id);
    return unless defined $json;

    my $userdata = decode_json($json);
    return unless defined $userdata && ref($userdata) eq 'HASH' && exists $userdata->{'ok'} && $userdata->{'ok'};

    # Now that we know their handle, we can see if we already have a record for
    # that. If so, we update it to include their Slack ID, drop it in our cache,
    # and return the nick object.
    $res = $self->bot->config->db->do(q{
        select id, name, extradata
        from nicks
        where lower(name) = lower(?)
    }, $userdata->{'user'}{'name'});

    if ($res && $res->next) {
        $res->{'extradata'} = decode_json($res->{'extradata'});
        $res->{'extradata'}{'slack_id'} = $slack_id;
        $res->{'extradata'}{'full_name'} = $userdata->{'user'}{'profile'}{'real_name'} if exists $userdata->{'user'}{'profile'}{'real_name'};

        $self->bot->config->db->do(q{
            update nicks
            set extradata = ?,
                updated_at = now()
            where id = ?
        }, encode_json($res->{'extradata'}), $res->{'id'});

        $nick = App::RoboBot::Nick->new(
            id        => $res->{'id'},
            name      => $res->{'name'},
            extradata => $res->{'extradata'},
            network   => $self,
            config    => $self->bot->config,
        );

        $self->profile_cache->{$slack_id} = $nick;
        return $nick;
    }

    # And finally, we've had no luck finding any matches, so we assume that the
    # nick is totally new to us. Create a new record, cache that, and return.
    my $extra = { slack_id => $slack_id };
    $extra->{'full_name'} = $userdata->{'user'}{'profile'}{'real_name'} if exists $userdata->{'user'}{'profile'}{'real_name'};

    $res = $self->bot->config->db->do(q{
        insert into nicks ??? returning id, name, extradata
    }, { name      => $userdata->{'user'}{'name'},
         extradata => encode_json($extra),
    });

    if ($res && $res->next) {
        $nick = App::RoboBot::Nick->new(
            id        => $res->{'id'},
            name      => $res->{'name'},
            extradata => decode_json($res->{'extradata'}),
            network   => $self,
            config    => $self->bot->config,
        );

        $self->profile_cache->{$slack_id} = $nick;
        return $nick;
    }

    return;
}

__PACKAGE__->meta->make_immutable;

1;



( run in 1.208 second using v1.01-cache-2.11-cpan-39bf76dae61 )