Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eg/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*
44 changes: 37 additions & 7 deletions lib/Synergy/Channel/Slack.pm
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use utf8;
use Future::AsyncAwait;
use JSON::MaybeXS;
use IO::Async::Timer::Periodic;
use Data::Dumper::Concise;

use Synergy::External::Slack;
use Synergy::Event;
Expand All @@ -18,6 +19,12 @@ my $JSON = JSON::MaybeXS->new->canonical;
with 'Synergy::Role::Channel',
'Synergy::Role::ProvidesUserStatus';

has app_key => (
is => 'ro',
isa => 'Str',
required => 1,
);

has api_key => (
is => 'ro',
isa => 'Str',
Expand All @@ -37,6 +44,7 @@ has slack => (
default => sub ($self) {
my $slack = Synergy::External::Slack->new(
loop => $self->loop,
app_key => $self->app_key,
api_key => $self->api_key,
name => '_external_slack',
privileged_api_key => $self->privileged_api_key,
Expand Down Expand Up @@ -118,15 +126,24 @@ sub _mk_frame_handler ($self) {
return sub ($client, $frame) {
return unless $frame;

my $slack_event;
unless (eval { $slack_event = $JSON->decode($frame) }) {
$Logger->log(['we got a frame: %s', $frame]);

my $frame_data;
unless (eval { $frame_data = $JSON->decode($frame) }) {
$Logger->log("error decoding frame content: <$frame> <$@>");
return;
}

# This is silly, but Websocket::Client's on_frame isn't a stack of
# subs to call, it's only a single sub. -- michael, 2019-02-03
$self->slack->handle_frame($slack_event);
$self->slack->handle_frame($frame_data);

my $slack_event = $frame_data->{payload}->{event};

if (!$slack_event) {
$Logger->log('no event in:', $frame_data);
return;
}

if (! $slack_event->{type} && $slack_event->{reply_to}) {
unless ($slack_event->{ok}) {
Expand All @@ -137,7 +154,7 @@ sub _mk_frame_handler ($self) {
}

if ($slack_event->{type} eq 'hello') {
$Logger->log("Got 'hello' from Slack RTM!");
$Logger->log("Got 'hello' from Slack Socket API!");
return;
}

Expand Down Expand Up @@ -370,11 +387,24 @@ sub send_ephemeral_message ($self, $channel, $user, $text) {
}

sub note_reply ($self, $event, $future, $args = {}) {

$Logger->log(['noting reply: %s', Dumper($event->transport_data)]);

my $ts = $event->transport_data->{ts};
return unless $ts;

$future->on_done(sub ($data) {
unless ($data->{type} eq 'slack') {
$Logger->log(['reply data: %s', Dumper($data)]);

my $decoded = $data->decoded_content({charset => 'none'});
my $dejsond;
unless (eval { $dejsond = $JSON->decode($decoded) }) {
$Logger->log("error decoding response content: <$decoded> <$@>");
return;
}
$Logger->log(['response: %s', $dejsond]);

unless ($dejsond->{message}) {
$Logger->log([
"got bizarre type back from slack future: %s",
$data
Expand All @@ -384,10 +414,10 @@ sub note_reply ($self, $event, $future, $args = {}) {

# Slack reactions results just have { ok: true }
# -- michael, 2019-02-05
return unless $data->{transport_data}{ts};
return unless $dejsond->{ts};

$self->add_reply($event, {
reply_ts => $data->{transport_data}{ts},
reply_ts => $dejsond->{ts},
was_error => $args->{was_error} ? 1 : 0,
});
});
Expand Down
101 changes: 71 additions & 30 deletions lib/Synergy/External/Slack.pm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use Synergy::Logger '$Logger';

with 'Synergy::Role::HubComponent';

has app_key => ( is => 'ro', required => 1 );

has api_key => ( is => 'ro', required => 1 );

has privileged_api_key => (
Expand Down Expand Up @@ -132,9 +134,29 @@ async sub connect ($self) {

my $json;

my $user_info_res = await $self->hub->http_client->GET(
"https://slack.com/api/auth.test", content_type => 'application/x-www-form-urlencoded', headers => { Authorization => "Bearer " . $self->api_key}
);
$json = decode_json($user_info_res->content);
die "Could not connect to Slack RTM: $json->{error}"
unless $json->{ok};

$Logger->log($user_info_res->content);

my $our_name = $json->{user};
$our_name = 'synergy' if $our_name eq 'synergee';

$Logger->log("we have a name: $our_name");

$self->_set_own_name($our_name);
$self->_set_own_id($json->{user_id});
$self->_set_team_data({team => $json->{team}, team_id => $json->{team_id}});

$json = undef;

until ($json) {
my $res = await $self->hub->http_client->GET(
"https://slack.com/api/rtm.connect?token=" . $self->api_key
my $res = await $self->hub->http_client->POST(
"https://slack.com/api/apps.connections.open", '', content_type => 'application/x-www-form-urlencoded', headers => { Authorization => "Bearer " . $self->app_key}
);

if ($res->code == 429) {
Expand All @@ -145,7 +167,9 @@ async sub connect ($self) {

$json = decode_json($res->content);

die "Could not connect to Slack RTM: $json->{error}"
my $reqh = $res->request->header('Authorization');

die "Could not connect to Slack RTM: $json->{error} // $reqh"
unless $json->{ok};
}

Expand All @@ -156,12 +180,6 @@ async sub connect ($self) {
# do. I *think* that reinstalling the app to our workspace would fix this,
# but I'm not entirely sure and I don't want to make everyone open yet
# another DM with synergy, so here we are. -- michael, 2019-06-03
my $our_name = $json->{self}->{name};
$our_name = 'synergy' if $our_name eq 'synergee';

$self->_set_own_name($our_name);
$self->_set_own_id($json->{self}->{id});
$self->_set_team_data($json->{team});

my $client = $self->client;

Expand All @@ -186,7 +204,8 @@ async sub connect ($self) {
notifier_name => 'slack-ping',
interval => 10,
on_tick => sub {
$self->send_frame({ type => 'ping' });
$Logger->log("we will not ping");
#$self->send_frame({ type => 'ping' });
}
);

Expand All @@ -203,41 +222,61 @@ sub send_frame ($self, $frame) {
my $frame_id = $i++;
$frame->{id} = $frame_id;

$Logger->log(['send_frame: %s', Dumper $frame]);

if ($self->connected) {
$Logger->log('sent frame');
$self->client->send_frame(masked => 1, buffer => encode_json($frame));
} else {
$Logger->log('queued frame');
# Save it til after we've successfully reconnected
$self->queue_frame($frame);
}

my $f = $self->loop->new_future;
$self->pending_frames->{$frame_id} = $f;
unless ($frame->{envelope_id}) {

my $timeout = $self->loop->timeout_future(after => 3);
$timeout->on_fail(sub {
$Logger->log("failed to get response from slack; trying to reconnect");
my $f = $self->loop->new_future;
$self->pending_frames->{$frame_id} = $f;

# XXX Blocking here is crappy. This is another place where we've pushed
# the "where is it async" around under the carpet, but haven't fully ironed
# out the lump yet. -- rjbs, 2023-10-10
$self->client->close;
$self->connect->get;

# Also fail any pending futures for this frame.
my $f = delete $self->pending_frames->{$frame_id};
$f->fail("timed out on connection to slack") if $f;
});
my $timeout = $self->loop->timeout_future(after => 10);
$timeout->on_fail(sub {
$Logger->log("failed to get response from slack; trying to reconnect");

$self->pending_timeouts->{$frame_id} = $timeout;
# XXX Blocking here is crappy. This is another place where we've pushed
# the "where is it async" around under the carpet, but haven't fully ironed
# out the lump yet. -- rjbs, 2023-10-10
$self->client->close;
$self->connect->get;

return $f;
# Also fail any pending futures for this frame.
my $f = delete $self->pending_frames->{$frame_id};
$f->fail("timed out on connection to slack") if $f;
});

$self->pending_timeouts->{$frame_id} = $timeout;

return $f;
}
}

sub handle_frame ($self, $slack_event) {
# These are the only way we'll ever hear about somebody who joined, or
# changed their name, after we started up. Without them, we'd go on calling
# them "<unknown user U123ABC>" until the next restart. -- rjbs, 2026-08-19

$Logger->log(['handle_frame: %s', $slack_event ]);

my $type = $slack_event->{type} // '';
my $envelope = $slack_event->{envelope_id};

# acknowledge frame
if ($envelope) {
$self->send_frame({
type => $type,
envelope_id => $slack_event->{envelope_id},
});
}

if ($type eq 'team_join' or $type eq 'user_change') {
$self->_update_user($slack_event->{user});
Expand Down Expand Up @@ -339,13 +378,15 @@ sub _send_plain_text ($self, $channel, $text) {
$channel = $self->dm_channel_for_address($channel);
}

my $f = $self->send_frame({
type => 'message',
my %args = (
channel => $channel,
as_user => \1,
text => $text,
});
);

return $f;
my $http_future = $self->api_call('chat.postMessage', \%args);

return $http_future;
}

sub _send_rich_text ($self, $channel, $rich, $alts) {
Expand Down
Loading