slackbot_frd 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c5c6be64e04fc7a8be46b5dacab72fee1612e5b5
4
+ data.tar.gz: 9180a45cb3f7acbe6db6fb9cd74c4ca0f85c85e4
5
+ SHA512:
6
+ metadata.gz: 395ab58d08a47f062b529998bd0fe4a515930d3013176b0e60a58ffba5d587fc18e51c5b4c5629cb2c03c027d464dd867637c4041b012d4c629a509221eaeffe
7
+ data.tar.gz: e767617e733d4c580d08fb04a82e944f46d0cfb388f5e4265e5fd7c933cbb4684d3476befc95210bff29c85d56c81d41fe6464e399d5abce550ab1abce169242
data/bin/slackbot-frd ADDED
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thor'
4
+ require 'json'
5
+
6
+ require 'slackbot_frd/initializer/bot_starter'
7
+ require 'slackbot_frd/lib/slack_connection'
8
+ require 'slackbot_frd/lib/bot'
9
+
10
+ begin
11
+ require 'byebug'
12
+ rescue LoadError
13
+ end
14
+
15
+ DEBUG = true
16
+
17
+ PID_FILE_WATCHER = "/tmp/slackbot-frd-watcher.pid"
18
+ PID_FILE_CONNECTION = "/tmp/slackbot-frd-connection.pid"
19
+ BOT_LIST_FILE = "/tmp/slackbot-frd-bot-list.pid"
20
+ ERROR_FILE = "/tmp/slackbot-frd-error-file.pid"
21
+ DEFAULT_CONFIG_FILE = "slackbot-frd.conf"
22
+ LOG_FILE = "slackbot-frd.log"
23
+
24
+ class SlackbotFrdBin < Thor
25
+ desc "list", "List all bots"
26
+ long_desc <<-LONGDESC
27
+ list will print out all available bots
28
+
29
+ > $ slackbot-frd list -- List all installed bots
30
+ LONGDESC
31
+ def list
32
+ # TODO
33
+ end
34
+
35
+ desc "start [bot1] [bot2] [botx...]", "Start all specified bots, or all bots"
36
+ long_desc <<-LONGDESC
37
+ start [bot1] [bot2] [botx...] will start the specified bots.
38
+ If no bots are specified, all available bots will be run.
39
+
40
+ params set via explicit flags will overwrite conflicting environment variables,
41
+ and environment variables will overwrite conflicting config file params.
42
+
43
+ > $ slackbot-frd start -- Start all available bots
44
+ LONGDESC
45
+ option :daemonize, type: :boolean, aliases: 'd'
46
+ option :botdir, type: :string, aliases: 'b'
47
+ option 'config-file'.to_sym, type: :string, aliases: 'c'
48
+ option :token, type: :string, aliases: 't'
49
+ def start(*bots)
50
+ config_file = options['config-file'.to_sym]
51
+ config_file = "#{Dir.pwd}/#{DEFAULT_CONFIG_FILE}" unless config_file
52
+ json = config_file_json(config_file) if config_file
53
+ json ||= {}
54
+
55
+ daemonize = false
56
+ daemonize = json["daemonize"] if json["daemonize"]
57
+ daemonize = ENV["SLACKBOT_FRD_DAEMONIZE"] if ENV["SLACKBOT_FRD_DAEMONIZE"]
58
+ daemonize = options[:daemonize] if options[:daemonize]
59
+
60
+ botdir = Dir.pwd
61
+ botdir = json["botdir"] if json["botdir"]
62
+ botdir = ENV["SLACKBOT_FRD_BOTDIR"] if ENV["SLACKBOT_FRD_BOTDIR"]
63
+ botdir = options[:botdir] if options[:botdir]
64
+ botdir = File.expand_path(botdir)
65
+
66
+ SlackbotFrd::Log.logfile = "#{botdir}/#{LOG_FILE}"
67
+ SlackbotFrd::Log.info("Logging to file '#{SlackbotFrd::Log.logfile}'")
68
+
69
+ token = json["token"]
70
+ token = ENV["SLACKBOT_FRD_TOKEN"] if ENV["SLACKBOT_FRD_TOKEN"]
71
+ token = options[:token] if options[:token]
72
+ unless token
73
+ SlackbotFrd::Log.error("No token found. Cannot authenticate to Slack")
74
+ return
75
+ end
76
+
77
+ if daemonize
78
+ set_watcher_pid(Process.fork{ watch_connection(bots, token, botdir, true) })
79
+ else
80
+ watch_connection(bots, token, botdir)
81
+ end
82
+ end
83
+
84
+ desc "stop", "Stop all bots"
85
+ long_desc <<-LONGDESC
86
+ stop will stop all bots
87
+
88
+ > $ slackbot-frd stop -- Stop all running bots
89
+ LONGDESC
90
+
91
+ def stop
92
+ # first kill the watcher, then kill the connection
93
+ kill_pid(watcher_pid)
94
+ kill_pid(connection_pid)
95
+ end
96
+
97
+ desc "restart", "Stop all bots and restart them"
98
+ long_desc <<-LONGDESC
99
+ restart will restart all bots
100
+
101
+ > $ slackbot-frd restart -- Restart all running bots
102
+ LONGDESC
103
+ def restart
104
+ stop
105
+ start(set_bots)
106
+ end
107
+
108
+ private
109
+ def config_file_json(config_file)
110
+ if File.exists?(config_file)
111
+ content = File.read(config_file)
112
+ return JSON.parse(content)
113
+ end
114
+ nil
115
+ end
116
+
117
+ private
118
+ def bots_from_file
119
+ File.read(BOT_LIST_FILE).split
120
+ end
121
+
122
+ private
123
+ def set_bots_in_file(bots)
124
+ File.write(BOT_LIST_FILE, "#{bots.join("\n")}")
125
+ end
126
+
127
+ private
128
+ def kill_pid(pid)
129
+ # try 3 times to SIGINT the pid, then SIGKILL it
130
+ 3.times do
131
+ break unless running?(pid)
132
+ Process.kill('SIGINT', pid)
133
+ end
134
+ Process.kill('SIGKILL', pid) if running?(pid)
135
+ # TODO log if the process is still running
136
+ end
137
+
138
+ private
139
+ def watch_connection(bots, token, botdir, daemonize = false)
140
+ until errors
141
+ pid = Process.fork do
142
+ Process.daemon if daemonize
143
+ loop { BotStarter.start_bots(ERROR_FILE, token, botdir, bots) }
144
+ end
145
+ set_connection_pid(pid)
146
+ Process.wait(pid)
147
+ end
148
+ if errors
149
+ puts "Could not start connection: #{errors}"
150
+ end
151
+ end
152
+
153
+ private
154
+ def errors
155
+ return File.read(ERROR_FILE).split if File.exists?(ERROR_FILE)
156
+ nil
157
+ end
158
+
159
+ private
160
+ def running?(pid)
161
+ begin
162
+ Process.getpgid(pid.to_i)
163
+ return true
164
+ rescue Errno::ESRCH
165
+ return false
166
+ end
167
+ end
168
+
169
+ private
170
+ def watcher_pid
171
+ File.read(PID_FILE_WATCHER).to_i
172
+ end
173
+
174
+ private
175
+ def connection_pid
176
+ File.read(PID_FILE_CONNECTION).to_i
177
+ end
178
+
179
+ private
180
+ def set_watcher_pid(pid)
181
+ File.write(PID_FILE_WATCHER, pid)
182
+ end
183
+
184
+ private
185
+ def set_connection_pid(pid)
186
+ File.write(PID_FILE_CONNECTION, pid)
187
+ end
188
+
189
+ private
190
+ def delete_pid_files
191
+ File.delete(PID_FILE_WATCHER)
192
+ File.delete(PID_FILE_CONNECTION)
193
+ end
194
+ end
195
+
196
+ SlackbotFrdBin.start(ARGV)
@@ -0,0 +1,3 @@
1
+ Gem.find_files("slackbot_frd/**/*.rb").each do |path|
2
+ require path.gsub(/\.rb$/, '') unless path =~ /bot.*cli/
3
+ end
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thor'
4
+
5
+ require 'slackbot_frd/lib/slack_connection'
6
+ require 'slackbot_frd/lib/bot'
7
+
8
+ begin
9
+ require 'byebug'
10
+ rescue LoadError
11
+ end
12
+
13
+ class BotStarter
14
+ def self.start_bots(errors_file, token, botdir, bots)
15
+ bot_enabled = ->(bot) { bots.empty? || bots.include?(bot) }
16
+
17
+ # Create a new Connection to pass to the bot classes
18
+ slack_connection = SlackbotFrd::SlackConnection.new(token)
19
+
20
+ load_bot_files(botdir)
21
+
22
+ bots = []
23
+ # instantiate them, and then call their add_callbacks method
24
+ ObjectSpace.each_object(Class).select do |klass|
25
+ if klass != SlackbotFrd::Bot && klass.ancestors.include?(SlackbotFrd::Bot) && bot_enabled.call(klass.name)
26
+ SlackbotFrd::Log.debug("Instantiating class '#{klass.to_s}'")
27
+ b = klass.new
28
+ SlackbotFrd::Log.debug("Adding callbacks to bot '#{klass}'")
29
+ b.add_callbacks(slack_connection)
30
+ bots.push(b)
31
+ end
32
+ end
33
+
34
+ if bots.count == 0
35
+ @error ||= []
36
+ @error.push("No bots loaded")
37
+ SlackbotFrd::Log.error("Not starting: no bots found")
38
+ else
39
+ SlackbotFrd::Log.debug("Starting SlackConnection")
40
+ slack_connection.start
41
+ SlackbotFrd::Log.debug("Connection closed")
42
+ end
43
+ end
44
+
45
+ private
46
+ def self.load_bot_files(top_level_dir)
47
+ Dir["#{File.expand_path(top_level_dir)}/**/*.rb"].each do |f|
48
+ SlackbotFrd::Log.debug("Loading bot file '#{f}'")
49
+ load(f)
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,11 @@
1
+ require 'slackbot_frd/initializer/bot_starter'
2
+
3
+ class BotStarterCli < Thor
4
+ option :token, type: :string, required: true, aliases: 't'
5
+ option :botdir, type: :string, required: true, aliases: ['b', 'd']
6
+ def start(*bots)
7
+ BotStarter.start_bots(options[:token], options[:botdir], bots)
8
+ end
9
+ end
10
+
11
+ BotStarterCli.start(ARGV)
@@ -0,0 +1,18 @@
1
+ # Subclass the bot class to have your bot loaded and run
2
+ module SlackbotFrd
3
+ class Bot
4
+ def self.only(*bots)
5
+ @bots ||= []
6
+ @bots.push(bots).flatten!
7
+ end
8
+
9
+ class << self
10
+ attr_accessor :bots
11
+ end
12
+
13
+ # This is where the bot adds all of their callbacks to the bpbot
14
+ def add_callbacks(slack_connection)
15
+ raise StandardError.new("You must override the define() method for your bot to do anything")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module SlackbotFrd
3
+ class NoTokenError < StandardError
4
+ def initialize(message = nil)
5
+ if message
6
+ super(message)
7
+ else
8
+ super("An API token is required for authenticating to the Slack API")
9
+ end
10
+ end
11
+ end
12
+
13
+ class AuthenticationFailedError < StandardError
14
+ end
15
+
16
+ class InvalidUserError < StandardError
17
+ end
18
+
19
+ class InvalidChannelError < StandardError
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ require 'colorize'
2
+
3
+ module SlackbotFrd
4
+ class Log
5
+ @levels = {verbose: 1, debug: 2, info: 3, warn: 4, error: 5}
6
+ @default_level = :debug
7
+
8
+ class << self
9
+ attr_writer :level
10
+ attr_accessor :logfile
11
+ end
12
+
13
+ def self.level
14
+ return @default_level unless @level
15
+ @level
16
+ end
17
+
18
+ def self.level_on(level)
19
+ @levels[level] >= @levels[self.level]
20
+ end
21
+
22
+ def self.colors
23
+ [:green, :red, :yellow, :none, :blue]
24
+ end
25
+
26
+ def self.error(message)
27
+ log('Error', message, :red) if level_on(:error)
28
+ end
29
+
30
+ def self.warn(message)
31
+ log('Warn', message, :yellow) if level_on(:warn)
32
+ end
33
+
34
+ def self.debug(message)
35
+ log('Debug', message, :green) if level_on(:debug)
36
+ end
37
+
38
+ def self.info(message)
39
+ log('Info', message, :blue) if level_on(:info)
40
+ end
41
+
42
+ def self.verbose(message)
43
+ log('Verbose', message, :magenta) if level_on(:verbose)
44
+ end
45
+
46
+ def self.log(loglevel, message, color = :none)
47
+ om = "#{DateTime.now.strftime('%Y-%m-%e %H:%M:%S.%L %z')}: [#{loglevel}]: #{message}\n"
48
+ print om.send(color)
49
+ begin
50
+ raise StandardError.new("No log file specified. (Set with SlackbotFrd::Log.logfile=)") unless @logfile
51
+ File.open(@logfile, 'a') do |f|
52
+ f.write(om)
53
+ end
54
+ rescue StandardError => e
55
+ puts "OH NO! ERROR WRITING TO LOG FILE!: #{e}"
56
+ end
57
+ om
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,271 @@
1
+ require 'faye/websocket'
2
+ require 'eventmachine'
3
+ require 'json'
4
+
5
+ require 'slackbot_frd/lib/errors'
6
+ require 'slackbot_frd/lib/user_channel_callbacks'
7
+ require 'slackbot_frd/lib/log'
8
+
9
+ require 'slackbot_frd/slack_methods/rtm_start'
10
+ require 'slackbot_frd/slack_methods/chat_post_message'
11
+ require 'slackbot_frd/slack_methods/im_channels_list'
12
+ require 'slackbot_frd/slack_methods/channels_list'
13
+ require 'slackbot_frd/slack_methods/users_list'
14
+
15
+ module SlackbotFrd
16
+ class SlackConnection
17
+ FILE_PATH = File.expand_path(__FILE__)
18
+ APP_ROOT = File.expand_path(File.dirname(File.dirname(FILE_PATH)))
19
+ FILE_DIR = File.dirname(FILE_PATH)
20
+ LOG_FILE = "#{APP_ROOT}/bp-slackbot.log"
21
+ PID_FILE_NAME = "#{APP_ROOT}/bp-slackbot.pid"
22
+
23
+ attr_accessor :token
24
+
25
+ def initialize(token)
26
+ unless token
27
+ SlackbotFrd::Log::error("No token passed to #{self.class}")
28
+ raise NoTokenError.new
29
+ end
30
+
31
+ @token = token
32
+ @event_id = 0
33
+ @on_connected_callbacks = []
34
+ @on_disconnected_callbacks = []
35
+ @on_message_callbacks = UserChannelCallbacks.new
36
+ @on_channel_left_callbacks = UserChannelCallbacks.new
37
+ @on_channel_joined_callbacks = UserChannelCallbacks.new
38
+
39
+ # These hashes are used to map ids to names efficiently
40
+ @user_id_to_name = {}
41
+ @user_name_to_id = {}
42
+ @channel_id_to_name = {}
43
+ @channel_name_to_id = {}
44
+
45
+ restrict_actions_to_channels_joined
46
+ SlackbotFrd::Log::debug("Done initializing #{self.class}")
47
+ end
48
+
49
+ def start
50
+ # Write pid file
51
+ File.write(PID_FILE_NAME, "#{Process.pid}")
52
+
53
+ SlackbotFrd::Log::info("#{self.class}: starting event machine")
54
+
55
+ EM.run do
56
+ wss_url = SlackbotFrd::SlackMethods::RtmStart.wss_url(@token)
57
+ unless wss_url
58
+ str = "No Real Time stream opened by slack. Check for correct authentication token"
59
+ SlackbotFrd::Log.error(str)
60
+ File.append(@errors_file, str)
61
+ return
62
+ end
63
+ @ws = Faye::WebSocket::Client.new(wss_url)
64
+
65
+ @on_connected_callbacks.each { |callback| @ws.on(:open, &callback) }
66
+ @on_disconnected_callbacks.each { |callback| @ws.on(:close, &callback) }
67
+ @ws.on(:message) { |event| process_message_received(event) }
68
+
69
+ # Clean up our pid file
70
+ @ws.on(:close) { |event| File.delete(PID_FILE_NAME) }
71
+ end
72
+
73
+ SlackbotFrd::Log::debug("#{self.class}: event machine started")
74
+ end
75
+
76
+ def event_id
77
+ @event_id += 1
78
+ @event_id
79
+ end
80
+
81
+ def on_connected(&block)
82
+ @on_connected_callbacks.push(block)
83
+ end
84
+
85
+ def on_close(&block)
86
+ @on_disconnected_callbacks.push(block)
87
+ end
88
+
89
+ def on_message(user = :any, channel = :any, &block)
90
+ @on_message_callbacks.add(user_name_to_id(user), channel_name_to_id(channel), block)
91
+ end
92
+
93
+ def on_channel_left(user = :any, channel = :any, &block)
94
+ @on_channel_left_callbacks.add(user_name_to_id(user), channel_name_to_id(channel), block)
95
+ end
96
+
97
+ def on_channel_joined(user = :any, channel = :any, &block)
98
+ u = user_name_to_id(user)
99
+ c = channel_name_to_id(channel)
100
+ @on_channel_joined_callbacks.add(u, c, block)
101
+ end
102
+
103
+ def send_message_as_user(channel, message)
104
+ unless @ws
105
+ SlackbotFrd::Log::error("Cannot send message '#{message}' as user to channel '#{channel}' because not connected to wss stream")
106
+ raise NotConnectedError.new("Not connected to wss stream")
107
+ end
108
+
109
+ resp = @ws.send({
110
+ id: event_id,
111
+ type: "message",
112
+ channel: channel_name_to_id(channel),
113
+ text: message
114
+ }.to_json)
115
+
116
+ SlackbotFrd::Log::debug("#{self.class}: sending message '#{message}' as user to channel '#{channel}'. Response: #{resp}")
117
+ end
118
+
119
+ def send_message(channel, message, username, avatar, avatar_is_emoji)
120
+ resp = SlackbotFrd::SlackMethods::ChatPostMessage.postMessage(
121
+ @token,
122
+ channel_name_to_id(channel),
123
+ message,
124
+ username,
125
+ avatar,
126
+ avatar_is_emoji
127
+ )
128
+ SlackbotFrd::Log::debug("#{self.class}: sending message '#{message}' as user '#{username}' to channel '#{channel}'. Response: #{resp}")
129
+ end
130
+
131
+ def restrict_actions_to_channels_joined(value = true)
132
+ @restrict_actions_to_channels_joined = value
133
+ end
134
+
135
+ def user_id_to_name(user_id)
136
+ return user_id if user_id == :any || user_id == :bot
137
+ unless @user_id_to_name && @user_id_to_name.has_key?(user_id)
138
+ refresh_user_info
139
+ end
140
+ SlackbotFrd::Log::warn("#{self.class}: User id '#{user_id}' not found") unless @user_id_to_name.include?(user_id)
141
+ @user_id_to_name[user_id]
142
+ end
143
+
144
+ def user_name_to_id(user_name)
145
+ return user_name if user_name == :any || user_name == :bot
146
+ unless @user_name_to_id && @user_name_to_id.has_key?(user_name)
147
+ refresh_user_info
148
+ end
149
+ SlackbotFrd::Log::warn("#{self.class}: User name '#{user_name}' not found") unless @user_name_to_id.include?(user_name)
150
+ @user_name_to_id[user_name]
151
+ end
152
+
153
+ def channel_id_to_name(channel_id)
154
+ unless @channel_id_to_name && @channel_id_to_name.has_key?(channel_id)
155
+ refresh_channel_info
156
+ end
157
+ SlackbotFrd::Log::warn("#{self.class}: Channel id '#{channel_id}' not found") unless @channel_id_to_name.include?(channel_id)
158
+ @channel_id_to_name[channel_id]
159
+ end
160
+
161
+ def channel_name_to_id(channel_name)
162
+ return channel_name if channel_name == :any
163
+ nc = normalize_channel_name(channel_name)
164
+ unless @channel_name_to_id && @channel_name_to_id.has_key?(nc)
165
+ refresh_channel_info
166
+ end
167
+ SlackbotFrd::Log::warn("#{self.class}: Channel name '#{nc}' not found") unless @channel_name_to_id.include?(nc)
168
+ @channel_name_to_id[nc]
169
+ end
170
+
171
+ private
172
+ def normalize_channel_name(channel_name)
173
+ return channel_name[1..-1] if channel_name.start_with?('#')
174
+ channel_name
175
+ end
176
+
177
+ private
178
+ def process_message_received(event)
179
+ message = JSON.parse(event.data)
180
+ SlackbotFrd::Log::verbose("#{self.class}: Message received: #{message}")
181
+ if message["type"] == "message"
182
+ if message["subtype"] == "channel_join"
183
+ process_join_message(message)
184
+ elsif message["subtype"] == "channel_leave"
185
+ process_leave_message(message)
186
+ elsif message["subtype"] == "file_share"
187
+ process_file_share(message)
188
+ else
189
+ process_chat_message(message)
190
+ end
191
+ end
192
+ end
193
+
194
+ private
195
+ def process_file_share(message)
196
+ SlackbotFrd::Log::verbose("#{self.class}: Processing file share: #{message}")
197
+ SlackbotFrd::Log::debug("#{self.class}: Not processing file share because it is not implemented:")
198
+ end
199
+
200
+ private
201
+ def process_chat_message(message)
202
+ SlackbotFrd::Log::verbose("#{self.class}: Processing chat message: #{message}")
203
+
204
+ user = message["user"]
205
+ user = :bot if message["subtype"] == "bot_message"
206
+ channel = message["channel"]
207
+ text = message["text"]
208
+
209
+ unless user
210
+ SlackbotFrd::Log::warn("#{self.class}: Chat message doesn't include user! message: #{message}")
211
+ return
212
+ end
213
+
214
+ unless channel
215
+ SlackbotFrd::Log::warn("#{self.class}: Chat message doesn't include channel! message: #{message}")
216
+ return
217
+ end
218
+
219
+ @on_message_callbacks.where_include_all(user, channel).each do |callback|
220
+ # instance_exec allows the user to call send_message and send_message_as_user
221
+ # without prefixing like this: slack_connection.send_message()
222
+ #
223
+ # However, it makes calling functions defined in the class not work, so
224
+ # for now we aren't going to do it
225
+ #
226
+ #instance_exec(user_id_to_name(user), channel_id_to_name(channel), text, &callback)
227
+ callback.call(user_id_to_name(user), channel_id_to_name(channel), text)
228
+ end
229
+ end
230
+
231
+ private
232
+ def process_join_message(message)
233
+ SlackbotFrd::Log::verbose("#{self.class}: Processing join message: #{message}")
234
+ user = message["user"]
235
+ user = :bot if message["subtype"] == "bot_message"
236
+ channel = message["channel"]
237
+ @on_channel_joined_callbacks.where_include_all(user, channel).each do |callback|
238
+ callback.call(user_id_to_name(user), channel_id_to_name(channel))
239
+ end
240
+ end
241
+
242
+ private
243
+ def process_leave_message(message)
244
+ SlackbotFrd::Log::verbose("#{self.class}: Processing leave message: #{message}")
245
+ user = message["user"]
246
+ user = :bot if message["subtype"] == "bot_message"
247
+ channel = message["channel"]
248
+ @on_channel_left_callbacks.where_include_all(user, channel).each do |callback|
249
+ callback.call(user_id_to_name(user), channel_id_to_name(channel))
250
+ end
251
+ end
252
+
253
+ private
254
+ def refresh_user_info
255
+ users_list = SlackbotFrd::SlackMethods::UsersList.new(@token).connect
256
+ @user_id_to_name = users_list.ids_to_names
257
+ @user_name_to_id = users_list.names_to_ids
258
+ end
259
+
260
+ private
261
+ def refresh_channel_info
262
+ channels_list = SlackbotFrd::SlackMethods::ChannelsList.new(@token).connect
263
+ @channel_id_to_name = channels_list.ids_to_names
264
+ @channel_name_to_id = channels_list.names_to_ids
265
+
266
+ im_channels_list = SlackbotFrd::SlackMethods::ImChannelsList.new(@token).connect
267
+ @channel_id_to_name.merge!(im_channels_list.ids_to_names)
268
+ @channel_name_to_id.merge!(im_channels_list.names_to_ids)
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,53 @@
1
+ require 'slackbot_frd/lib/log'
2
+
3
+ module SlackbotFrd
4
+ class UserChannelCallbacks
5
+ def initialize
6
+ @conditions = {}
7
+ @conditions[:any] = {}
8
+ @conditions[:any][:any] = []
9
+ end
10
+
11
+ def init(user, channel)
12
+ unless user
13
+ Log::error("#{self.class}: Invalid user '#{user}'")
14
+ raise InvalidUserError.new
15
+ end
16
+ unless channel
17
+ Log::error("#{self.class}: Invalid channel '#{channel}'")
18
+ raise InvalidChannelError.new
19
+ end
20
+ @conditions[user] ||= {}
21
+ @conditions[user][:any] ||= []
22
+ @conditions[:any][channel] ||= []
23
+ @conditions[user][channel] ||= []
24
+ end
25
+
26
+ def add(user, channel, callback)
27
+ init(user, channel)
28
+ @conditions[user][channel].push(callback)
29
+ end
30
+
31
+ def where(user, channel)
32
+ init(user, channel)
33
+ @conditions[user][channel] || []
34
+ end
35
+
36
+ def where_all
37
+ @conditions[:any][:any] || []
38
+ end
39
+
40
+ def where_include_all(user, channel)
41
+ init(user, channel)
42
+ retval = @conditions[:any][:any].dup || []
43
+ retval.concat(@conditions[user][:any] || [])
44
+ retval.concat(@conditions[:any][channel] || [])
45
+ retval.concat(@conditions[user][channel] || [])
46
+ retval
47
+ end
48
+
49
+ def to_s
50
+ "#{@conditions}"
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,38 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ module SlackbotFrd
5
+ module SlackMethods
6
+ class ChannelsList
7
+ include HTTParty
8
+ base_uri 'https://slack.com/api/channels.list'
9
+
10
+ attr_reader :response
11
+
12
+ def initialize(token)
13
+ @token = token
14
+ end
15
+
16
+ def connect
17
+ @response = JSON.parse(self.class.post('', :body => { token: @token } ).body)
18
+ self
19
+ end
20
+
21
+ def ids_to_names
22
+ retval = {}
23
+ @response["channels"].each do |channel|
24
+ retval[channel["id"]] = channel["name"]
25
+ end
26
+ retval
27
+ end
28
+
29
+ def names_to_ids
30
+ retval = {}
31
+ @response["channels"].each do |channel|
32
+ retval[channel["name"]] = channel["id"]
33
+ end
34
+ retval
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,48 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ module SlackbotFrd
5
+ module SlackMethods
6
+ class ChatPostMessage
7
+ include HTTParty
8
+ base_uri 'https://slack.com/api/chat.postMessage'
9
+
10
+ def self.postMessage(token, channel, message, username = nil, avatar = nil, avatar_is_emoji = nil)
11
+ r = ChatPostMessage.new(token, channel, message, username, avatar, avatar_is_emoji)
12
+ r.postMessage
13
+ end
14
+
15
+ def initialize(token, channel, message, username = nil, avatar = nil, avatar_is_emoji = nil)
16
+ @token = token
17
+ @channel = channel
18
+ @message = message
19
+ @username = username
20
+ @avatar = avatar
21
+ @avatar_is_emoji = avatar_is_emoji
22
+ end
23
+
24
+ def postMessage
25
+ body = {
26
+ token: @token,
27
+ channel: @channel,
28
+ text: @message,
29
+ }
30
+
31
+ if @username
32
+ body.merge!({ username: @username })
33
+
34
+ if @avatar_is_emoji
35
+ body.merge!({ icon_emoji: @avatar })
36
+ else
37
+ body.merge!({ icon_url: @avatar })
38
+ end
39
+ else
40
+ body.merge!({ as_user: true })
41
+ end
42
+
43
+ @response = self.class.post('', :body => body)
44
+ @response
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,38 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ module SlackbotFrd
5
+ module SlackMethods
6
+ class ImChannelsList
7
+ include HTTParty
8
+ base_uri 'https://slack.com/api/im.list'
9
+
10
+ attr_reader :response
11
+
12
+ def initialize(token)
13
+ @token = token
14
+ end
15
+
16
+ def connect
17
+ @response = JSON.parse(self.class.post('', :body => { token: @token } ).body)
18
+ self
19
+ end
20
+
21
+ def ids_to_names
22
+ retval = {}
23
+ @response["ims"].each do |im|
24
+ retval[im["id"]] = im["user"]
25
+ end
26
+ retval
27
+ end
28
+
29
+ def names_to_ids
30
+ retval = {}
31
+ @response["ims"].each do |im|
32
+ retval[im["user"]] = im["id"]
33
+ end
34
+ retval
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,31 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ module SlackbotFrd
5
+ module SlackMethods
6
+ class RtmStart
7
+ include HTTParty
8
+ base_uri 'https://slack.com/api/rtm.start'
9
+
10
+ def self.wss_url(token)
11
+ r = RtmStart.new(token)
12
+ r.connect
13
+ r.wss_url
14
+ end
15
+
16
+ def initialize(token)
17
+ @token = token
18
+ end
19
+
20
+ def connect
21
+ @response = JSON.parse(self.class.post('', :body => { token: @token } ).body)
22
+ @response
23
+ end
24
+
25
+ def wss_url
26
+ #return "ERR" unless @response.has_key?("url")
27
+ @response["url"]
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ require 'httparty'
2
+ require 'json'
3
+
4
+ module SlackbotFrd
5
+ module SlackMethods
6
+ class UsersList
7
+ include HTTParty
8
+ base_uri 'https://slack.com/api/users.list'
9
+
10
+ attr_reader :response
11
+
12
+ def initialize(token)
13
+ @token = token
14
+ end
15
+
16
+ def connect
17
+ @response = JSON.parse(self.class.post('', :body => { token: @token } ).body)
18
+ self
19
+ end
20
+
21
+ def ids_to_names
22
+ retval = {}
23
+ @response["members"].each do |user|
24
+ retval[user["id"]] = user["name"]
25
+ end
26
+ retval
27
+ end
28
+
29
+ def names_to_ids
30
+ retval = {}
31
+ @response["members"].each do |user|
32
+ retval[user["name"]] = user["id"]
33
+ end
34
+ retval
35
+ end
36
+ end
37
+ end
38
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: slackbot_frd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ben Porter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-04-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.13'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.13'
41
+ - !ruby/object:Gem::Dependency
42
+ name: faye-websocket
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.9'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: colorize
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.7'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: thor
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.19'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.19'
83
+ - !ruby/object:Gem::Dependency
84
+ name: daemons
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.2'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: json
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.8'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.8'
111
+ - !ruby/object:Gem::Dependency
112
+ name: byebug
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '4.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '4.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.1'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.1'
139
+ description: The slack web api is good, but very raw. What you need is a great ruby
140
+ framework to abstract away all that. This is it! This framework allows you to
141
+ write bots easily by providing methods that are easy to call. Behind the scenes,
142
+ the framework is negotiating your real time stream, converting channel names and
143
+ user names to and from IDs so you can use the names instead, and parsing/classifying
144
+ the real time messages into useful types that you can hook into. Don't write your
145
+ bot without this.
146
+ email: BenjaminPorter86@gmail.com
147
+ executables:
148
+ - slackbot-frd
149
+ extensions: []
150
+ extra_rdoc_files: []
151
+ files:
152
+ - bin/slackbot-frd
153
+ - lib/slackbot_frd.rb
154
+ - lib/slackbot_frd/initializer/bot_starter.rb
155
+ - lib/slackbot_frd/initializer/bot_starter_cli.rb
156
+ - lib/slackbot_frd/lib/bot.rb
157
+ - lib/slackbot_frd/lib/errors.rb
158
+ - lib/slackbot_frd/lib/log.rb
159
+ - lib/slackbot_frd/lib/slack_connection.rb
160
+ - lib/slackbot_frd/lib/user_channel_callbacks.rb
161
+ - lib/slackbot_frd/slack_methods/channels_list.rb
162
+ - lib/slackbot_frd/slack_methods/chat_post_message.rb
163
+ - lib/slackbot_frd/slack_methods/im_channels_list.rb
164
+ - lib/slackbot_frd/slack_methods/rtm_start.rb
165
+ - lib/slackbot_frd/slack_methods/users_list.rb
166
+ homepage: http://rubygems.org/gems/slackbot_frd
167
+ licenses:
168
+ - MIT
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.2.2
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: slackbot_frd provides a dirt-simple framework for implementing one or more
190
+ slack bots
191
+ test_files: []