diff --git a/Gemfile b/Gemfile index 675ac9d..ab2d344 100644 --- a/Gemfile +++ b/Gemfile @@ -13,3 +13,5 @@ end gem "slack-ruby-client" gem "async-websocket", '~> 0.8.0' # dependency of slack-ruby-client RTM + +gem "rack", "~> 2.2" diff --git a/Gemfile.lock b/Gemfile.lock index 1d4cb18..5822b47 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1305,6 +1305,7 @@ DEPENDENCIES mongo nokogiri puma + rack (~> 2.2) rubyzip sinatra slack-ruby-client diff --git a/bin/init.rb b/bin/init.rb index 62703dc..aa8a691 100644 --- a/bin/init.rb +++ b/bin/init.rb @@ -5,7 +5,10 @@ # XXX using string hash key for backword compatibility... config = { 'slack' => { - 'token' => nil + 'token' => nil, + 'team_id' => nil, + 'use_events_api' => false, + 'signing_secret' => nil, }, 'aws' => { 'access_key_id' => nil, diff --git a/docker-compose.yml b/docker-compose.yml index afc4699..448655d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,6 +7,8 @@ services: container_name: slack-patron-logger volumes: - .:/usr/local/slack-patron + ports: + - "127.0.0.1:9293:9293" restart: always viewer: diff --git a/lib/slack.rb b/lib/slack.rb index 79f118b..a008d1b 100644 --- a/lib/slack.rb +++ b/lib/slack.rb @@ -11,3 +11,7 @@ c.start_method = :rtm_connect c.store_class = Slack::RealTime::Stores::Starter end + +Slack::Events.configure do |c| + c.signing_secret = config['slack']['use_events_api'] ? config['slack']['signing_secret'] : nil +end diff --git a/lib/slack_events_receiver.rb b/lib/slack_events_receiver.rb new file mode 100644 index 0000000..2881830 --- /dev/null +++ b/lib/slack_events_receiver.rb @@ -0,0 +1,84 @@ +require 'rack' + +class SlackEventsReceiver + attr_reader :logger + + def initialize(team_id) + @team_id = team_id + raise ArgumentError unless @team_id and @team_id.start_with? 'T' + end + + def start!(logger) + @logger = logger + + Rack::Server.start( + app: lambda do |env| + req = Rack::Request.new(env) + + begin + Slack::Events::Request.new(req).verify! + process(req.body) + rescue Slack::Events::Request::MissingSigningSecret, + Slack::Events::Request::InvalidSignature, + Slack::Events::Request::TimestampExpired => e + warn 'bad request: %s' % e + [400, {}, ''] + end + end, + Port: 9293, + ) + end + + def process(body) + # https://api.slack.com/apis/connections/events-api + + data = JSON.parse(body.read) + type = data['type'] + + case type + when 'url_verification' + [200, { 'content-type': 'text/plain' }, data['challenge']] + when 'event_callback' + process_event data['event'] if data['team_id'] == @team_id + [204, {}, ''] + else + [400, {}, ''] + end + end + + def process_event(event) + type = event['type'] + event.delete 'type' + + case type + when 'message' + # https://api.slack.com/events/message + return if logger.is_private_channel(event['channel']) + return if logger.is_direct_message(event['channel']) + + puts 'new message' + + logger.insert_message(event) + + when 'team_join' + puts 'new user has joined' + logger.update_users + + when 'user_change' + puts 'user data has changed' + logger.update_users + + when 'channel_created' + puts 'channel has created' + logger.update_channels + + when 'channel_rename' + puts 'channel has renamed' + logger.update_channels + + when 'emoji_changed' + puts 'emoji has changed' + logger.update_emojis + end + end +end diff --git a/lib/slack_logger.rb b/lib/slack_logger.rb index 5622553..bb03446 100644 --- a/lib/slack_logger.rb +++ b/lib/slack_logger.rb @@ -2,8 +2,6 @@ require './lib/slack' require './lib/db' -config = YAML.load_file('./config.yml') - class SlackLogger attr_reader :client @@ -19,6 +17,11 @@ def is_direct_message(channel_name) channel_name[0] == 'D' end + alias :_insert_message :insert_message # FIXME!!! + def insert_message(message) + _insert_message(message) + end + def update_users users = client.users_list['members'] replace_users(users) @@ -50,61 +53,9 @@ def fetch_history(target, channel) end end - # realtime events - def log_realtime - realtime = Slack::RealTime::Client.new - - realtime.on :message do |m| - if is_private_channel(m['channel']) - next - end - if is_direct_message(m['channel']) - next - end - - puts m - insert_message(m) - end - - realtime.on :team_join do |e| - puts "new user has joined" - update_users - end - - realtime.on :user_change do |e| - puts "user data has changed" - update_users - end - - realtime.on :channel_created do |c| - puts "channel has created" - update_channels - end - - realtime.on :channel_rename do |c| - puts "channel has renamed" - update_channels - end - - realtime.on :emoji_changed do |c| - puts "emoji has changed" - update_emojis - end - - # if connection closed, restart the realtime logger - realtime.on :close do - puts "websocket disconnected" - log_realtime - end - - realtime.start! - end - - def start + def start(receiver) begin - realtime_thread = Thread.new { - log_realtime - } + receiver_thread = Thread.new { receiver.start!(self) } update_emojis update_users @@ -119,9 +70,9 @@ def start end # realtime event is joined and dont exit current thread - realtime_thread.join + receiver_thread.join ensure - realtime_thread.kill + receiver_thread.kill end end end diff --git a/lib/slack_rtm_receiver.rb b/lib/slack_rtm_receiver.rb new file mode 100644 index 0000000..af2c922 --- /dev/null +++ b/lib/slack_rtm_receiver.rb @@ -0,0 +1,50 @@ +class SlackRTMReceiver + def start!(logger) + realtime = Slack::RealTime::Client.new + + realtime.on :message do |m| + if logger.is_private_channel(m['channel']) + next + end + if logger.is_direct_message(m['channel']) + next + end + + puts 'new message' + logger.insert_message(m) + end + + realtime.on :team_join do |e| + puts "new user has joined" + logger.update_users + end + + realtime.on :user_change do |e| + puts "user data has changed" + logger.update_users + end + + realtime.on :channel_created do |c| + puts "channel has created" + logger.update_channels + end + + realtime.on :channel_rename do |c| + puts "channel has renamed" + logger.update_channels + end + + realtime.on :emoji_changed do |c| + puts "emoji has changed" + logger.update_emojis + end + + # if connection closed, restart the realtime logger + realtime.on :close do + puts "websocket disconnected" + start! logger + end + + realtime.start! + end +end diff --git a/logger/logger.rb b/logger/logger.rb index 1c2f8a0..1419fd3 100644 --- a/logger/logger.rb +++ b/logger/logger.rb @@ -1,3 +1,10 @@ +require './lib/slack' + require './lib/slack_logger' +require './lib/slack_events_receiver' +require './lib/slack_rtm_receiver' + +config = YAML.load_file('./config.yml') -SlackLogger.new.start +receiver = config['slack']['use_events_api'] ? SlackEventsReceiver.new(config['slack']['team_id']) : SlackRTMReceiver.new +SlackLogger.new.start receiver