From efdbf8bf9f45a1daebdfa3cc218fa727de8b3512 Mon Sep 17 00:00:00 2001 From: Paul Gregg Date: Sat, 10 Oct 2015 00:05:17 +0100 Subject: [PATCH 1/4] Re-engineered to support finer grained multi-team & multi-channel support with token validation --- slappyhour | 103 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 93 insertions(+), 10 deletions(-) diff --git a/slappyhour b/slappyhour index 6aeceb3..ca97c1f 100755 --- a/slappyhour +++ b/slappyhour @@ -1,14 +1,70 @@ #!/usr/bin/env perl + +# Forked to handle multi-channel sync / token auth +# (but doesn't support 1-n peers like original slappyhour) +# https://github.com/paulgregg/slappyhour-multi + +# With thanks to original slappyhour devs +# https://github.com/obra/slappyhour + use warnings; use strict; use CGI; use JSON; use Gravatar::URL; use LWP::UserAgent; +use Data::Dumper; + +my $debuglevel = 0; # 0=off, 1=errors, 2=info (errors and chat messages), 3=debug +my $debuglog = '/tmp/slappyhour.log'; + +# Define each team as a peer. +# Each peer should have hash sections for: +# - tokens : one token per channel in this peer - to validate message came from this team/channel +# - hooks : one hook per channel in this peer - for us to send message to that team/channel +# - channels : one entry per channel to define the target peer and target channel in the remote peer -my $peers = { asdf => { hook => 'https://asdf.slack.com/services/hooks/incoming-webhook?token=XXXXX', - domain => 'keyboard.io' }, - }; +# In the sample below we are configuring bi-directional communication between 2 teams and 2 channels +# Team1 Team2 +# #t2_general <-> #general +# #passcodes <-> #passcodes +# The structure allows 1-1, 1-n or n-n teams and channels to be synced. +# Note that in slappyhour-multi - target peers/channels must be explicitly defined or your +# message will not be sent to another team channel + +my $peers = { + team1 => { + tokens => { # Specify the channel tokens for this peer + t2_general => 'team1chan1token', + passcodes => 'team1chan2token', + }, + hooks => { # For sending messages to this slack team / channel from this proxy + t2_general => 'https://hooks.slack.com/services/xxxxxxxxx/yyyyyyyyy/abcdefghijklmnopqrstuvwx', + passcodes => 'https://hooks.slack.com/services/xxxxxxxxx/zzzzzzzzz/abcdefghijklmnopqrstuvwx', + }, + channels => { # Specify the channel from this peer and to where it needs to sync to + t2_general => { targets => [ { peer => 'team2', channel => 'general' }, ] }, + passcodes => { targets => [ { peer => 'team2', channel => 'passcodes' }, ] }, + }, + domain => 'pgregg.com' + }, + + team2 => { + tokens => { + general => 'team2chan1token', + passcodes => 'team2chan2token', + }, + hooks => { + general => 'https://hooks.slack.com/services/aaaaaaaaa/bbbbbbbbb/abcdefghijklmnopqrstuvwx', + passcodes => 'https://hooks.slack.com/services/aaaaaaaaa/ccccccccc/abcdefghijklmnopqrstuvwx', + }, + channels => { + general => { targets => [ { peer => 'team1', channel => 'new_general' }, ] }, + passcodes => { targets => [ { peer => 'team1', channel => 'passcodes' }, ] }, + }, + domain => 'pgregg.com' + }, +}; my $q = CGI->new(); my $ua = LWP::UserAgent->new(); @@ -23,19 +79,46 @@ if (($q->param('text') || '') eq '') { exit; } +open(DEBUG, ">>$debuglog") if ($debuglevel>0); +print DEBUG Dumper($q) if ($debuglevel>2); + +#Validate the team / channel token +if (($q->param('token') || '') ne ($peers->{$q->param('team_domain')}->{'tokens'}->{$q->param('channel_name')} || 'X')) { + print "Invalid token. Not accepting message.\n"; + print DEBUG "Invalid token " + . $q->param('token') . " != " . $peers->{$q->param('team_domain')}->{'token'} + . " for channel " . $q->param('channel_name') . "\n" if ($debuglevel); + exit; +} + + my $text = $q->param('text'); utf8::decode($text); -my $payload = encode_json( { channel => '#slappyhour', - username => $q->param('user_name') . "@" . $q->param('team_domain'), - icon_url => gravatar_url( email => $q->param('user_name') . "@" . $peers->{ $q->param('team_domain') }->{'domain'}, default => 'monsterid'), - text => $text }); -for my $peer (keys %$peers) { - next if ($peer eq $q->param('team_domain')); +print DEBUG "> " . $q->param('user_name') . "@" . $q->param('team_domain') . ': ' . $text . "\n" if ($debuglevel>1); + +# Iterate the channels target this team/channel is (explicitly) synced to +for my $target (@{$peers->{$q->param('team_domain')}->{'channels'}->{$q->param('channel_name')}->{targets}}) { + my $peer = $target->{peer}; + my $target_channel = $target->{channel}; + print DEBUG "Target peer: $peer and channel $target_channel\n" if ($debuglevel>2); + eval { print "Sending to $peer\n"; - my $res = $ua->post($peers->{$peer}->{'hook'}, Content => $payload); + + my $payload = encode_json( { + channel => "#$target_channel", + username => $q->param('user_name') . "@" . $q->param('team_domain'), + icon_url => gravatar_url( email => $q->param('user_name') . "@" . $peers->{ $q->param('team_domain') }->{'domain'}, default => 'monsterid'), + text => $text } ); + + print DEBUG $payload . "\n" if ($debuglevel>2); + my $res = $ua->post($peers->{$peer}->{'hooks'}->{$target_channel}, Content => $payload); print $res->status_line . "\n"; + print DEBUG $res->status_line . "\n" if ($debuglevel>2); }; } + +close(DEBUG) if ($debuglevel); + print "That's all for now, folks!\n"; From ac2028e145c021393ab9870b0b8468d6f9be98ca Mon Sep 17 00:00:00 2001 From: Paul Gregg Date: Sat, 10 Oct 2015 00:21:29 +0100 Subject: [PATCH 2/4] Updated copyrights --- slappyhour | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slappyhour b/slappyhour index ca97c1f..8501269 100755 --- a/slappyhour +++ b/slappyhour @@ -3,9 +3,11 @@ # Forked to handle multi-channel sync / token auth # (but doesn't support 1-n peers like original slappyhour) # https://github.com/paulgregg/slappyhour-multi +# Copyright 2015 Paul Gregg https://pgregg.com (for my code only) # With thanks to original slappyhour devs # https://github.com/obra/slappyhour +# Copyright 2014 Keyboardio, Inc. http://keyboard.io use warnings; use strict; From 8870df20e46ce1cabed1885d8c33d85ae1172598 Mon Sep 17 00:00:00 2001 From: Paul Gregg Date: Sat, 10 Oct 2015 00:41:17 +0100 Subject: [PATCH 3/4] Copyright updated --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 515b217..ac59829 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ # Slappyhour -A simple CGI script to bridge a channel between two or more Slack instances. +A simple CGI script to bridge channels between two or more Slack instances. To learn how to set up Slappyhour, have a look at [the setup guide](setup.md). Copyright 2014 Keyboardio, Inc. [http://keyboard.io](http://keyboard.io) +Multi Patch Copyright 2015 Paul Gregg [https://pgregg.com](https://pgregg.com) From a8065649bb293d18412281e1995ec867b165c59b Mon Sep 17 00:00:00 2001 From: Paul Gregg Date: Sat, 10 Oct 2015 00:41:58 +0100 Subject: [PATCH 4/4] Formatting --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ac59829..17af244 100644 --- a/README.md +++ b/README.md @@ -5,5 +5,6 @@ A simple CGI script to bridge channels between two or more Slack instances. To learn how to set up Slappyhour, have a look at [the setup guide](setup.md). Copyright 2014 Keyboardio, Inc. [http://keyboard.io](http://keyboard.io) + Multi Patch Copyright 2015 Paul Gregg [https://pgregg.com](https://pgregg.com)