App-RoboBot
view release on metacpan or search on metacpan
lib/App/RoboBot/Network/Slack.pm view on Meta::CPAN
# For now, set an arbitrary limit on responses of 4K (SlackRTM says 16K,
# which assuming absolute worst-case with wide characters would be 4K
# glyphs, but even that seems really excesive for a chatbot).
if (length($output) > 4096) {
$output = substr($output, 0, 4096) .
"\n\n... Output truncated ...";
}
$self->client->send({
channel => $response->channel->extradata->{'slack_id'},
type => 'message',
text => $output,
});
$response->clear_content;
return;
}
sub handle_message {
my ($self, $msg) = @_;
$self->log->debug('Received incoming message.');
return unless exists $msg->{'ts'};
return if int($msg->{'ts'}) <= $self->start_ts + 5;
$self->log->debug('Message passed startup timestamp check.');
# Short circuit if this isn't a 'message' type message.
return unless defined $msg && ref($msg) eq 'HASH'
&& exists $msg->{'type'} && $msg->{'type'} eq 'message'
&& exists $msg->{'text'} && $msg->{'text'} =~ m{\w+};
$self->log->debug('Message payload appears valid.');
# Ignore messages which are hidden or have a subtype (these are generall
# message edits or similar events).
# TODO: Consider trapping message_edit subtypes and replacing log history?
# Most likely more work than it's worth, especially since it would
# require direct and special-snowflake interaction with a plugin.
return if exists $msg->{'subtype'} && $msg->{'subtype'} =~ m{\w+};
return if exists $msg->{'hidden'} && $msg->{'hidden'} == 1;
$self->log->debug('Message has no subtype and is not hidden.');
$self->log->debug(sprintf('Resolving nick for Slack ID %s.', $msg->{'user'}));
my $nick = $self->resolve_nick($msg->{'user'});
$self->log->debug(sprintf('Resolving channel for Slack ID %s.', $msg->{'channel'}));
my $channel = $self->resolve_channel($msg->{'channel'});
return unless defined $nick && defined $channel;
my $raw_msg = exists $msg->{'text'} && defined $msg->{'text'} ? $msg->{'text'} : '';
# Remove brackets around URLs. Do alt-named ones first, then URL-only links.
$raw_msg =~ s{<(http[^|>]+)\|[^>]+>}{$1}g;
$raw_msg =~ s{<(http[^>]+)>}{$1}g;
# Unescape a couple things from Slack.
$raw_msg =~ s{\&}{&}g;
$raw_msg =~ s{\<}{<}g;
$raw_msg =~ s{\>}{>}g;
$self->log->debug('Raw message stripped of markup.');
my ($message);
eval {
$message = App::RoboBot::Message->new(
bot => $self->bot,
raw => $raw_msg,
network => $self,
sender => $nick,
channel => $channel,
);
};
return $self->log->fatal($@) if $@;
$self->log->debug('Message object constructed, preparing to process.');
$message->process;
}
sub resolve_channel {
my ($self, $slack_id) = @_;
return $self->channel_cache->{$slack_id} if exists $self->channel_cache->{$slack_id};
my $channel;
my $res = $self->bot->config->db->do(q{
select id, name, extradata
from channels
where network_id = ? and extradata @> ?
}, $self->id, 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;
}
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) };
( run in 0.834 second using v1.01-cache-2.11-cpan-39bf76dae61 )