require 'time' require 'slack' require 'slack-rtmapi' require 'ruboty/adapters/base' module Ruboty module Adapters class SlackRTM < Base env :SLACK_TOKEN, "Account's token. get one on https://api.slack.com/web#basics" def run init bind connect end def say(message) realtime.send( type: 'message', channel: message[:to], text: message[:code] ? "```\n#{message[:body]}\n```" : resolve_send_mention(message[:body]), mrkdwn: true ) end private def init response = client.auth_test @user_info_caches = {} @channel_info_cahces = {} ENV['RUBOTY_NAME'] ||= response['user'] make_users_cache make_channels_cache end def bind realtime.on(:message) do |data| method_name = "on_#{data['type']}".to_sym send(method_name, data) if respond_to?(method_name, true) end end def connect Thread.start do loop do sleep 5 set_active end end realtime.main_loop end def url @url ||= ::SlackRTM.get_url(token: ENV['SLACK_TOKEN']) end def client @client ||= ::Slack::Client.new(token: ENV['SLACK_TOKEN']) end def realtime @realtime ||= ::SlackRTM::Client.new(websocket_url: url) end def set_active client.users_setActive end # event handlers def on_message(data) data = resolve_mention!(data) user = user_info(data['user']) robot.receive( body: data['text'], from: data['channel'], from_name: user['name'], to: data['channel'], channel: channel_info(data['channel']), user: user, mention_to: data['mention_to'], time: Time.at(data['ts'].to_f) ) end def on_channel_change(data) make_channels_cache end alias_method :on_channel_deleted, :on_channel_change alias_method :on_channel_renamed, :on_channel_change alias_method :on_channel_archived, :on_channel_change alias_method :on_channel_unarchived, :on_channel_change def on_user_change(data) user = data['user'] || data['bot'] @user_info_caches[user['id']] = user end alias_method :on_bot_added, :on_user_change alias_method :on_bot_changed, :on_user_change def resolve_mention!(data) data = data.dup data['mention_to'] = [] data['text'] = (data['text'] || '').gsub(/\<\@(?[0-9A-Z]+)(?:\|(?[^>]+))?\>/) do |_| name = Regexp.last_match[:name] unless name user = user_info(Regexp.last_match[:uid]) data['mention_to'] << user name = user['name'] end "@#{name}" end data['text'].gsub!(/\[^>]+)\>/) do |_| "@#{Regexp.last_match[:special]}" end data['text'].gsub!(/\<((?[^>|]+)(?:\|(?[^>]*))?)\>/) do |_| Regexp.last_match[:ref] || Regexp.last_match[:link] end data['text'].gsub!(/\#(?[A-Z0-9]+)/) do |_| room_id = Regexp.last_match[:room_id] msg = "##{room_id}" if channel = channel_info(room_id) msg = "##{channel['name']}" end msg end data end def resolve_send_mention(text) text = text.to_s text.gsub!(/@(?[0-9a-z_-]+)/) do |_| mention = Regexp.last_match[:mention] msg = "@#{mention}" @user_info_caches.each_pair do |id, user| if user['name'].downcase == mention.downcase msg = "<@#{id}|#{mention}>" end end msg end text.gsub!(/@(?(?:everyone|group|channel))/) do |_| "" end text.gsub!(/\#(?[a-z0-9_-]+)/) do |_| room_id = Regexp.last_match[:room_id] msg = "##{room_id}" @channel_info_cahces.each_pair do |id, channel| if channel && channel['name'] == room_id msg = "<##{id}|#{room_id}>" end end msg end text end def make_users_cache resp = client.users_list if resp['ok'] resp['members'].each do |user| @user_info_caches[user['id']] = user end end end def make_channels_cache resp = client.channels_list if resp['ok'] resp['channels'].each do |channel| @channel_info_cahces[channel['id']] = channel end end end def user_info(user_id) @user_info_caches[user_id] ||= begin resp = client.users_info(user: user_id) resp['user'] end end def channel_info(channel_id) @channel_info_cahces[channel_id] ||= begin resp = case channel_id when /^C/ client.channels_info(channel: channel_id) else {} end resp['channel'] end end end end end