slack-smart-bot 1.7.0 → 1.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -12
  3. data/lib/slack-smart-bot.rb +53 -43
  4. data/lib/slack-smart-bot_general_rules.rb +7 -4
  5. data/lib/slack-smart-bot_rules.rb +8 -6
  6. data/lib/slack/smart-bot/comm.rb +6 -1
  7. data/lib/slack/smart-bot/comm/ask.rb +12 -5
  8. data/lib/slack/smart-bot/comm/dont_understand.rb +1 -1
  9. data/lib/slack/smart-bot/comm/event_hello.rb +30 -0
  10. data/lib/slack/smart-bot/comm/get_channel_members.rb +8 -0
  11. data/lib/slack/smart-bot/comm/get_channels.rb +20 -0
  12. data/lib/slack/smart-bot/comm/get_user_info.rb +16 -0
  13. data/lib/slack/smart-bot/comm/react.rb +21 -8
  14. data/lib/slack/smart-bot/comm/respond.rb +38 -12
  15. data/lib/slack/smart-bot/comm/send_file.rb +1 -1
  16. data/lib/slack/smart-bot/comm/send_msg_channel.rb +2 -2
  17. data/lib/slack/smart-bot/comm/send_msg_user.rb +4 -4
  18. data/lib/slack/smart-bot/comm/unreact.rb +29 -0
  19. data/lib/slack/smart-bot/commands.rb +3 -1
  20. data/lib/slack/smart-bot/commands/general/bot_help.rb +16 -3
  21. data/lib/slack/smart-bot/commands/general/bot_stats.rb +313 -0
  22. data/lib/slack/smart-bot/commands/general/bot_status.rb +1 -1
  23. data/lib/slack/smart-bot/commands/general/bye_bot.rb +1 -1
  24. data/lib/slack/smart-bot/commands/general/hi_bot.rb +1 -1
  25. data/lib/slack/smart-bot/commands/general/use_rules.rb +6 -9
  26. data/lib/slack/smart-bot/commands/general/whats_new.rb +19 -0
  27. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +65 -33
  28. data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +42 -9
  29. data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +3 -7
  30. data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +1 -0
  31. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +9 -2
  32. data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +1 -0
  33. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +1 -1
  34. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +52 -21
  35. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +5 -5
  36. data/lib/slack/smart-bot/commands/on_bot/repl.rb +50 -18
  37. data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +34 -9
  38. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +2 -3
  39. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +1 -1
  40. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +27 -9
  41. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +14 -1
  42. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +3 -3
  43. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +1 -1
  44. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +41 -0
  45. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +4 -8
  46. data/lib/slack/smart-bot/listen.rb +6 -5
  47. data/lib/slack/smart-bot/process.rb +230 -186
  48. data/lib/slack/smart-bot/process_first.rb +104 -87
  49. data/lib/slack/smart-bot/treat_message.rb +128 -52
  50. data/lib/slack/smart-bot/utils.rb +2 -0
  51. data/lib/slack/smart-bot/utils/answer.rb +18 -0
  52. data/lib/slack/smart-bot/utils/answer_delete.rb +15 -0
  53. data/lib/slack/smart-bot/utils/build_help.rb +57 -5
  54. data/lib/slack/smart-bot/utils/create_routine_thread.rb +48 -8
  55. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +1 -7
  56. data/lib/slack/smart-bot/utils/get_help.rb +79 -17
  57. data/lib/slack/smart-bot/utils/save_stats.rb +21 -8
  58. data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +6 -0
  59. data/whats_new.txt +24 -0
  60. metadata +23 -13
  61. data/lib/slack/smart-bot/commands/on_bot/admin_master/bot_stats.rb +0 -135
@@ -1,5 +1,5 @@
1
1
  class SlackSmartBot
2
- def process_first(user, text, dest, dchannel, typem, files, ts, thread_ts)
2
+ def process_first(user, text, dest, dchannel, typem, files, ts, thread_ts, routine)
3
3
  nick = user.name
4
4
  rules_file = ""
5
5
  text.gsub!(/^!!/,'^') # to treat it just as ^
@@ -80,21 +80,21 @@ class SlackSmartBot
80
80
  text.match(/^()\^\s*(.+)\s*/im) or
81
81
  text.match(/^()!\s*(.+)\s*/im) or
82
82
  text.match(/^()<@#{config[:nick_id]}>\s+(.+)\s*/im)
83
- command2 = $2
84
- if text.match?(/^()\^\s*(.+)/im)
85
- add_double_excl = true
86
- addexcl = false
87
- if command2.match?(/^![^!]/) or command2.match?(/^\^/)
83
+ command2 = $2
84
+ if text.match?(/^()\^\s*(.+)/im)
85
+ add_double_excl = true
86
+ addexcl = false
87
+ if command2.match?(/^![^!]/) or command2.match?(/^\^/)
88
88
  command2[0]=''
89
- elsif command2.match?(/^!!/)
89
+ elsif command2.match?(/^!!/)
90
90
  command2[0]=''
91
91
  command2[1]=''
92
- end
93
- else
92
+ end
93
+ else
94
94
  add_double_excl = false
95
95
  addexcl = true
96
- end
97
- command = command2
96
+ end
97
+ command = command2
98
98
  else
99
99
  addexcl = false
100
100
  if text.include?('$') #for shortcuts inside commands
@@ -111,6 +111,10 @@ class SlackSmartBot
111
111
  command.gsub!("$#{sc}", @shortcuts[nick][sc])
112
112
  elsif @shortcuts.key?(:all) and @shortcuts[:all].keys.include?(sc)
113
113
  command.gsub!("$#{sc}", @shortcuts[:all][sc])
114
+ elsif @shortcuts_global.key?(nick) and @shortcuts_global[nick].keys.include?(sc)
115
+ command.gsub!("$#{sc}", @shortcuts_global[nick][sc])
116
+ elsif @shortcuts_global.key?(:all) and @shortcuts_global[:all].keys.include?(sc)
117
+ command.gsub!("$#{sc}", @shortcuts_global[:all][sc])
114
118
  end
115
119
  end
116
120
  command.scan(/\$([^\s]+)/i).flatten.each do |sc|
@@ -119,6 +123,10 @@ class SlackSmartBot
119
123
  command.gsub!("$#{sc}", @shortcuts[nick][sc])
120
124
  elsif @shortcuts.key?(:all) and @shortcuts[:all].keys.include?(sc)
121
125
  command.gsub!("$#{sc}", @shortcuts[:all][sc])
126
+ elsif @shortcuts_global.key?(nick) and @shortcuts_global[nick].keys.include?(sc)
127
+ command.gsub!("$#{sc}", @shortcuts_global[nick][sc])
128
+ elsif @shortcuts_global.key?(:all) and @shortcuts_global[:all].keys.include?(sc)
129
+ command.gsub!("$#{sc}", @shortcuts_global[:all][sc])
122
130
  end
123
131
  end
124
132
  text = command
@@ -126,13 +134,19 @@ class SlackSmartBot
126
134
  text = "^" + text if add_double_excl
127
135
  end
128
136
  if command.scan(/^(shortcut|sc)\s+([^:]+)\s*$/i).any? or
129
- (@shortcuts.keys.include?(:all) and @shortcuts[:all].keys.include?(command)) or
130
- (@shortcuts.keys.include?(nick) and @shortcuts[nick].keys.include?(command))
137
+ (@shortcuts.keys.include?(:all) and @shortcuts[:all].keys.include?(command)) or
138
+ (@shortcuts.keys.include?(nick) and @shortcuts[nick].keys.include?(command)) or
139
+ (@shortcuts_global.keys.include?(:all) and @shortcuts_global[:all].keys.include?(command)) or
140
+ (@shortcuts_global.keys.include?(nick) and @shortcuts_global[nick].keys.include?(command))
131
141
  command = $2.downcase unless $2.nil?
132
142
  if @shortcuts.keys.include?(nick) and @shortcuts[nick].keys.include?(command)
133
143
  text = @shortcuts[nick][command].dup
134
144
  elsif @shortcuts.keys.include?(:all) and @shortcuts[:all].keys.include?(command)
135
145
  text = @shortcuts[:all][command].dup
146
+ elsif @shortcuts_global.keys.include?(nick) and @shortcuts_global[nick].keys.include?(command)
147
+ text = @shortcuts_global[nick][command].dup
148
+ elsif @shortcuts_global.keys.include?(:all) and @shortcuts_global[:all].keys.include?(command)
149
+ text = @shortcuts_global[:all][command].dup
136
150
  else
137
151
  respond "Shortcut not found", dest unless dest[0] == "C" and dchannel != dest #on extended channel
138
152
  return :next #jal
@@ -154,6 +168,7 @@ class SlackSmartBot
154
168
  Thread.current[:files?] = !files.nil? && files.size>0
155
169
  Thread.current[:ts] = ts
156
170
  Thread.current[:thread_ts] = thread_ts
171
+ Thread.current[:routine] = routine
157
172
  if thread_ts.to_s == ''
158
173
  Thread.current[:on_thread] = false
159
174
  Thread.current[:thread_ts] = Thread.current[:ts] # to create the thread if necessary
@@ -187,96 +202,98 @@ class SlackSmartBot
187
202
  command = command2
188
203
  on_demand = true
189
204
  end
190
- if @status == :on and
191
- (@questions.key?(nick) or
192
- (@repl_sessions.key?(nick) and dest==@repl_sessions[nick][:dest] and
193
- ((@repl_sessions[nick][:on_thread] and thread_ts == @repl_sessions[nick][:thread_ts]) or
194
- (!@repl_sessions[nick][:on_thread] and !Thread.current[:on_thread] ))) or
195
- (@listening.key?(nick) and typem != :on_extended and
196
- ((@listening[nick].key?(dest) and !Thread.current[:on_thread]) or
197
- (@listening[nick].key?(thread_ts) and Thread.current[:on_thread] ) )) or
198
- dest[0] == "D" or on_demand)
199
- @logger.info "command: #{nick}> #{command}" unless processed
200
- #todo: verify this
205
+ unless config.on_maintenance and processed
206
+ if @status == :on and
207
+ (!answer.empty? or
208
+ (@repl_sessions.key?(nick) and dest==@repl_sessions[nick][:dest] and
209
+ ((@repl_sessions[nick][:on_thread] and thread_ts == @repl_sessions[nick][:thread_ts]) or
210
+ (!@repl_sessions[nick][:on_thread] and !Thread.current[:on_thread] ))) or
211
+ (@listening.key?(nick) and typem != :on_extended and
212
+ ((@listening[nick].key?(dest) and !Thread.current[:on_thread]) or
213
+ (@listening[nick].key?(thread_ts) and Thread.current[:on_thread] ) )) or
214
+ dest[0] == "D" or on_demand)
215
+ @logger.info "command: #{nick}> #{command}" unless processed
216
+ #todo: verify this
201
217
 
202
- if dest[0] == "C" or dest[0] == "G" or (dest[0] == "D" and typem == :on_call)
203
- if typem != :on_call and @rules_imported.key?(user.id) and @rules_imported[user.id].key?(dchannel)
204
- if @bots_created.key?(@rules_imported[user.id][dchannel])
205
- if @bots_created[@rules_imported[user.id][dchannel]][:status] != :on
206
- respond "The bot on that channel is not :on", dest
207
- rules_file = ""
218
+ if dest[0] == "C" or dest[0] == "G" or (dest[0] == "D" and typem == :on_call)
219
+ if typem != :on_call and @rules_imported.key?(user.id) and @rules_imported[user.id].key?(dchannel)
220
+ if @bots_created.key?(@rules_imported[user.id][dchannel])
221
+ if @bots_created[@rules_imported[user.id][dchannel]][:status] != :on
222
+ respond "The bot on that channel is not :on", dest
223
+ rules_file = ""
224
+ end
208
225
  end
209
226
  end
210
- end
211
- unless rules_file.empty?
212
- begin
213
- eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
214
- rescue Exception => stack
215
- @logger.fatal "ERROR ON RULES FILE: #{rules_file}"
216
- @logger.fatal stack
217
- end
218
- if defined?(rules)
219
- command[0] = "" if command[0] == "!"
220
- command.gsub!(/^@\w+:*\s*/, "")
221
- if method(:rules).parameters.size == 4
222
- rules(user, command, processed, dest)
223
- elsif method(:rules).parameters.size == 5
224
- rules(user, command, processed, dest, files)
225
- else
226
- rules(user, command, processed, dest, files, rules_file)
227
- end
228
- else
229
- @logger.warn "It seems like rules method is not defined"
230
- end
231
- end
232
- elsif @rules_imported.key?(user.id) and @rules_imported[user.id].key?(user.id)
233
- if @bots_created.key?(@rules_imported[user.id][user.id])
234
- if @bots_created[@rules_imported[user.id][user.id]][:status] == :on
227
+ unless rules_file.empty?
235
228
  begin
236
- eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file) and !['.','..'].include?(config.path + rules_file)
229
+ eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
237
230
  rescue Exception => stack
238
- @logger.fatal "ERROR ON imported RULES FILE: #{rules_file}"
231
+ @logger.fatal "ERROR ON RULES FILE: #{rules_file}"
239
232
  @logger.fatal stack
240
233
  end
241
- else
242
- respond "The bot on <##{@rules_imported[user.id][user.id]}|#{@bots_created[@rules_imported[user.id][user.id]][:channel_name]}> is not :on", dest
243
- rules_file = ""
234
+ if defined?(rules)
235
+ command[0] = "" if command[0] == "!"
236
+ command.gsub!(/^@\w+:*\s*/, "")
237
+ if method(:rules).parameters.size == 4
238
+ rules(user, command, processed, dest)
239
+ elsif method(:rules).parameters.size == 5
240
+ rules(user, command, processed, dest, files)
241
+ else
242
+ rules(user, command, processed, dest, files, rules_file)
243
+ end
244
+ else
245
+ @logger.warn "It seems like rules method is not defined"
246
+ end
247
+ end
248
+ elsif @rules_imported.key?(user.id) and @rules_imported[user.id].key?(user.id)
249
+ if @bots_created.key?(@rules_imported[user.id][user.id])
250
+ if @bots_created[@rules_imported[user.id][user.id]][:status] == :on
251
+ begin
252
+ eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file) and !['.','..'].include?(config.path + rules_file)
253
+ rescue Exception => stack
254
+ @logger.fatal "ERROR ON imported RULES FILE: #{rules_file}"
255
+ @logger.fatal stack
256
+ end
257
+ else
258
+ respond "The bot on <##{@rules_imported[user.id][user.id]}|#{@bots_created[@rules_imported[user.id][user.id]][:channel_name]}> is not :on", dest
259
+ rules_file = ""
260
+ end
244
261
  end
245
- end
246
262
 
247
- unless rules_file.empty?
248
- if defined?(rules)
249
- command[0] = "" if command[0] == "!"
250
- command.gsub!(/^@\w+:*\s*/, "")
251
- if method(:rules).parameters.size == 4
252
- rules(user, command, processed, dest)
253
- elsif method(:rules).parameters.size == 5
254
- rules(user, command, processed, dest, files)
263
+ unless rules_file.empty?
264
+ if defined?(rules)
265
+ command[0] = "" if command[0] == "!"
266
+ command.gsub!(/^@\w+:*\s*/, "")
267
+ if method(:rules).parameters.size == 4
268
+ rules(user, command, processed, dest)
269
+ elsif method(:rules).parameters.size == 5
270
+ rules(user, command, processed, dest, files)
271
+ else
272
+ rules(user, command, processed, dest, files, rules_file)
273
+ end
255
274
  else
256
- rules(user, command, processed, dest, files, rules_file)
275
+ @logger.warn "It seems like rules method is not defined"
257
276
  end
258
- else
259
- @logger.warn "It seems like rules method is not defined"
277
+ end
278
+ else
279
+ @logger.info "it is a direct message with no rules file selected so no rules file executed."
280
+ if command.match?(/^\s*bot\s+rules\s*(.*)$/i)
281
+ respond "No rules running. You can use the command `use rules from CHANNEL` to specify the rules you want to use on this private conversation.\n`bot help` to see available commands.", dest
282
+ end
283
+ unless processed
284
+ dont_understand('')
260
285
  end
261
286
  end
262
- else
263
- @logger.info "it is a direct message with no rules file selected so no rules file executed."
264
- if command.match?(/^\s*bot\s+rules\s*$/i)
265
- respond "No rules running. You can use the command `use rules from CHANNEL` to specify the rules you want to use on this private conversation.\n`bot help` to see available commands.", dest
266
- end
267
- unless processed
268
- dont_understand('')
269
- end
270
- end
271
287
 
272
- if processed and @listening.key?(nick)
273
- if Thread.current[:on_thread] and @listening[nick].key?(Thread.current[:thread_ts])
274
- @listening[nick][Thread.current[:thread_ts]] = Time.now
275
- elsif !Thread.current[:on_thread] and @listening[nick].key?(dest)
276
- @listening[nick][dest] = Time.now
288
+ if processed and @listening.key?(nick)
289
+ if Thread.current[:on_thread] and @listening[nick].key?(Thread.current[:thread_ts])
290
+ @listening[nick][Thread.current[:thread_ts]] = Time.now
291
+ elsif !Thread.current[:on_thread] and @listening[nick].key?(dest)
292
+ @listening[nick][dest] = Time.now
293
+ end
277
294
  end
278
- end
279
295
 
296
+ end
280
297
  end
281
298
  rescue Exception => stack
282
299
  @logger.fatal stack
@@ -4,45 +4,57 @@ class SlackSmartBot
4
4
  begin
5
5
  unless data.text.to_s.match(/\A\s*\z/)
6
6
  #to remove italic, bold... from data.text since there is no method on slack api
7
- #only works when no @user or #channel mentioned
8
7
  if remove_blocks and !data.blocks.nil? and data.blocks.size > 0
8
+ data_text = ''
9
9
  data.blocks.each do |b|
10
10
  if b.type == 'rich_text'
11
11
  if b.elements.size > 0
12
12
  b.elements.each do |e|
13
- if e.type == 'rich_text_section'
14
- if e.elements.size > 0 and (e.elements.type.uniq - ['link', 'text']) == []
15
- data.text = ''
13
+ if e.type == 'rich_text_section' or e.type == 'rich_text_preformatted'
14
+ if e.elements.size > 0 and (e.elements.type.uniq - ['link', 'text', 'user', 'channel']) == []
15
+ data_text += '```' if e.type == 'rich_text_preformatted'
16
16
  e.elements.each do |el|
17
17
  if el.type == 'text'
18
- data.text += el.text
18
+ data_text += el.text
19
+ elsif el.type == 'user'
20
+ data_text += "<@#{el.user_id}>"
21
+ elsif el.type == 'channel'
22
+ tch = data.text.scan(/(<##{el.channel_id}\|[^\>]+>)/).join
23
+ data_text += tch
19
24
  else
20
- data.text += el.url
25
+ data_text += el.url
21
26
  end
22
27
  end
28
+ data_text += '```' if e.type == 'rich_text_preformatted'
23
29
  end
24
- break
25
30
  end
26
31
  end
27
32
  end
28
- break
29
33
  end
30
34
  end
35
+ data.text = data_text unless data_text == ''
31
36
  end
32
37
  data.text = CGI.unescapeHTML(data.text)
33
38
  data.text.gsub!("\u00A0", " ") #to change &nbsp; (asc char 160) into blank space
34
39
  end
40
+ data.text.gsub!('‘', "'")
35
41
  data.text.gsub!('’', "'")
36
- data.text.gsub!('“', '"')
37
- rescue
42
+ data.text.gsub!('“', '"')
43
+ data.text.gsub!('”', '"')
44
+ rescue Exception => exc
38
45
  @logger.warn "Impossible to unescape or clean format for data.text:#{data.text}"
46
+ @logger.warn exc.inspect
39
47
  end
48
+ data.routine = false unless data.key?(:routine)
49
+
40
50
  if config[:testing] and config.on_master_bot
41
51
  open("#{config.path}/buffer.log", "a") { |f|
42
- f.puts "|#{data.channel}|#{data.user}|#{data.text}"
52
+ f.puts "|#{data.channel}|#{data.user}|#{data.user_name}|#{data.text}"
43
53
  }
44
54
  end
45
- if data.channel[0] == "D" or data.channel[0] == "C" or data.channel[0] == "G" #Direct message or Channel or Private Channel
55
+ if data.key?(:dest) and data.dest.to_s!='' # for run routines and publish on different channels
56
+ dest = data.dest
57
+ elsif data.channel[0] == "D" or data.channel[0] == "C" or data.channel[0] == "G" #Direct message or Channel or Private Channel
46
58
  dest = data.channel
47
59
  else # not treated
48
60
  dest = nil
@@ -56,24 +68,42 @@ class SlackSmartBot
56
68
  @pings << $1
57
69
  end
58
70
  typem = :dont_treat
59
- if !dest.nil? and !data.text.nil? and !data.text.to_s.match?(/^\s*$/)
60
- if data.text.match(/^<@#{config[:nick_id]}>\s(on\s)?<#(\w+)\|([^>]+)>\s*:?\s*(.*)/im)
61
- channel_rules = $2
62
- channel_rules_name = $3
63
- # to be treated only on the bot of the requested channel
64
- if @channel_id == channel_rules
65
- data.text = $4
66
- typem = :on_call
71
+ if !dest.nil? and !data.text.nil? and !data.text.to_s.match?(/\A\s*\z/)
72
+ #todo: we need to add mixed channels: @smart-bot on private1 #bot1cm <#CXDDFRDDF|bot2cu>: echo A
73
+ if data.text.match(/^\s*<@#{config[:nick_id]}>\s+(on\s+)?((<#\w+\|[^>]+>\s*)+)\s*:?\s*(.*)/im) or
74
+ data.text.match(/^\s*<@#{config[:nick_id]}>\s+(on\s+)?((#[a-zA-Z0-9]+\s*)+)\s*:?\s*(.*)/im) or
75
+ data.text.match(/^\s*<@#{config[:nick_id]}>\s+(on\s+)?(([a-zA-Z0-9]+\s*)+)\s*:\s*(.*)/im)
76
+ channels_rules = $2 #multiple channels @smart-bot on #channel1 #channel2 echo AAA
77
+ data_text = $4
78
+ channel_rules_name = ''
79
+ channel_rules = ''
80
+ channels_arr = channels_rules.scan(/<#(\w+)\|([^>]+)>/)
81
+ if channels_arr.size == 0
82
+ channels_arr = []
83
+ channels_rules.scan(/([^\s]+)/).each do |cn|
84
+ cna = cn.join.gsub('#','')
85
+ channels_arr << [@channels_id[cna], cna]
86
+ end
67
87
  end
68
- elsif dest == @master_bot_id
88
+ # to be treated only on the bots of the requested channels
89
+ channels_arr.each do |tcid, tcname|
90
+ if @channel_id == tcid
91
+ data.text = data_text
92
+ typem = :on_call
93
+ channel_rules = tcid
94
+ channel_rules_name = tcname
95
+ break
96
+ end
97
+ end
98
+ elsif data.channel == @master_bot_id
69
99
  if config.on_master_bot #only to be treated on master bot channel
70
100
  typem = :on_master
71
101
  end
72
- elsif @bots_created.key?(dest)
73
- if @channel_id == dest #only to be treated by the bot on the channel
102
+ elsif @bots_created.key?(data.channel)
103
+ if @channel_id == data.channel #only to be treated by the bot on the channel
74
104
  typem = :on_bot
75
105
  end
76
- elsif dest[0] == "D" #Direct message
106
+ elsif data.channel[0] == "D" #Direct message
77
107
  get_rules_imported()
78
108
  if @rules_imported.key?(data.user) && @rules_imported[data.user].key?(data.user) and
79
109
  @bots_created.key?(@rules_imported[data.user][data.user])
@@ -85,21 +115,21 @@ class SlackSmartBot
85
115
  #only to be treated by master bot
86
116
  typem = :on_dm
87
117
  end
88
- elsif dest[0] == "C" or dest[0] == "G"
118
+ elsif data.channel[0] == "C" or data.channel[0] == "G"
89
119
  #only to be treated on the channel of the bot. excluding running ruby
90
- if !config.on_master_bot and @bots_created.key?(@channel_id) and @bots_created[@channel_id][:extended].include?(@channels_name[dest]) and
120
+ if !config.on_master_bot and @bots_created.key?(@channel_id) and @bots_created[@channel_id][:extended].include?(@channels_name[data.channel]) and
91
121
  !data.text.match?(/^!?\s*(ruby|code)\s+/) and !data.text.match?(/^!?!?\s*(ruby|code)\s+/) and !data.text.match?(/^\^?\s*(ruby|code)\s+/)
92
122
  typem = :on_extended
93
123
  elsif config.on_master_bot and (data.text.match?(/^!?\s*(ruby|code)\s+/) or data.text.match?(/^!?!?\s*(ruby|code)\s+/) or data.text.match?(/^\^?\s*(ruby|code)\s+/) )
94
124
  #or in case of running ruby, the master bot
95
125
  @bots_created.each do |k, v|
96
- if v.key?(:extended) and v[:extended].include?(@channels_name[dest])
126
+ if v.key?(:extended) and v[:extended].include?(@channels_name[data.channel])
97
127
  typem = :on_extended
98
128
  break
99
129
  end
100
130
  end
101
131
  end
102
- if dest[0] == "G" and config.on_master_bot and typem != :on_extended #private group
132
+ if data.channel[0] == "G" and config.on_master_bot and typem != :on_extended #private group
103
133
  typem = :on_pg
104
134
  end
105
135
  end
@@ -116,18 +146,27 @@ class SlackSmartBot
116
146
  end
117
147
  begin
118
148
  #todo: when changed @questions user_id then move user_info inside the ifs to avoid calling it when not necessary
119
- user_info = client.web_client.users_info(user: data.user)
120
- if @questions.key?(user_info.user.name)
149
+ user_info = get_user_info(data.user)
150
+
151
+ #user_info.user.id = data.user #todo: remove this line when slack issue with Wxxxx Uxxxx fixed
152
+ data.user = user_info.user.id #todo: remove this line when slack issue with Wxxxx Uxxxx fixed
153
+ if data.thread_ts.nil?
154
+ qdest = dest
155
+ else
156
+ qdest = data.thread_ts
157
+ end
158
+ if !answer(user_info.user.name, qdest).empty?
121
159
  if data.text.match?(/^\s*(Bye|Bæ|Good\sBye|Adiós|Ciao|Bless|Bless\sBless|Adeu)\s(#{@salutations.join("|")})\s*$/i)
122
- @questions.delete(user_info.user.name)
160
+ answer_delete(user_info.user.name, qdest)
123
161
  command = data.text
124
162
  else
125
- command = @questions[user_info.user.name]
126
- @questions[user_info.user.name] = data.text
163
+ command = answer(user_info.user.name, qdest)
164
+ @answer[user_info.user.name][qdest] = data.text
165
+ @questions[user_info.user.name] = data.text # to be backwards compatible #todo remove it when 2.0
127
166
  end
128
- elsif @repl_sessions.key?(user_info.user.name) and dest==@repl_sessions[user_info.user.name][:dest] and
167
+ elsif @repl_sessions.key?(user_info.user.name) and data.channel==@repl_sessions[user_info.user.name][:dest] and
129
168
  ((@repl_sessions[user_info.user.name][:on_thread] and data.thread_ts == @repl_sessions[user_info.user.name][:thread_ts]) or
130
- (!@repl_sessions[user_info.user.name][:on_thread] and data.thread_ts.to_s == '' ))
169
+ (!@repl_sessions[user_info.user.name][:on_thread] and data.thread_ts.to_s == '' ))
131
170
 
132
171
  if data.text.match(/^\s*```(.*)```\s*$/im)
133
172
  @repl_sessions[user_info.user.name][:command] = $1
@@ -140,16 +179,16 @@ class SlackSmartBot
140
179
  end
141
180
 
142
181
  #when added special characters on the message
143
- if command.match(/^\s*```(.*)```\s*$/im)
182
+ if command.match(/\A\s*```(.*)```\s*\z/im)
144
183
  command = $1
145
184
  elsif command.size >= 2 and
146
- ((command[0] == "`" and command[-1] == "`") or (command[0] == "*" and command[-1] == "*") or (command[0] == "_" and command[-1] == "_"))
185
+ ((command[0] == "`" and command[-1] == "`") or (command[0] == "*" and command[-1] == "*") or (command[0] == "_" and command[-1] == "_"))
147
186
  command = command[1..-2]
148
187
  end
149
188
 
150
189
  #ruby file attached
151
190
  if !data.files.nil? and data.files.size == 1 and
152
- (command.match?(/^(ruby|code)\s*$/) or (command.match?(/^\s*$/) and data.files[0].filetype == "ruby") or
191
+ (command.match?(/^(ruby|code)\s*$/) or (command.match?(/^\s*$/) and data.files[0].filetype == "ruby") or
153
192
  (typem == :on_call and data.files[0].filetype == "ruby"))
154
193
  res = Faraday.new("https://files.slack.com", headers: { "Authorization" => "Bearer #{config[:token]}" }).get(data.files[0].url_private)
155
194
  command += " ruby" if command != "ruby"
@@ -160,47 +199,68 @@ class SlackSmartBot
160
199
  command = "!" + command unless command[0] == "!" or command.match?(/^\s*$/) or command[0] == "^"
161
200
 
162
201
  #todo: add pagination for case more than 1000 channels on the workspace
163
- channels = client.web_client.conversations_list(
164
- types: "private_channel,public_channel",
165
- limit: "1000",
166
- exclude_archived: "true",
167
- ).channels
202
+ channels = get_channels()
168
203
  channel_found = channels.detect { |c| c.name == channel_rules_name }
169
- members = client.web_client.conversations_members(channel: @channels_id[channel_rules_name]).members unless channel_found.nil?
204
+ members = get_channel_members(@channels_id[channel_rules_name]) unless channel_found.nil?
170
205
  if channel_found.nil?
171
206
  @logger.fatal "Not possible to find the channel #{channel_rules_name}"
172
207
  elsif channel_found.name == config.master_channel
173
- respond "You cannot use the rules from Master Channel on any other channel.", dest
208
+ respond "You cannot use the rules from Master Channel on any other channel.", data.channel
174
209
  elsif @status != :on
175
- respond "The bot in that channel is not :on", dest
210
+ respond "The bot in that channel is not :on", data.channel
176
211
  elsif data.user == channel_found.creator or members.include?(data.user)
177
- process_first(user_info.user, command, dest, channel_rules, typem, data.files, data.ts, data.thread_ts)
212
+ process_first(user_info.user, command, dest, channel_rules, typem, data.files, data.ts, data.thread_ts, data.routine)
178
213
  else
179
- respond "You need to join the channel <##{channel_found.id}> to be able to use the rules.", dest
214
+ respond "You need to join the channel <##{channel_found.id}> to be able to use the rules.", data.channel
180
215
  end
181
216
  elsif config.on_master_bot and typem == :on_extended and
182
217
  command.size > 0 and command[0] != "-"
183
218
  # to run ruby only from the master bot for the case more than one extended
184
- process_first(user_info.user, command, dest, @channel_id, typem, data.files, data.ts, data.thread_ts)
219
+ process_first(user_info.user, command, dest, @channel_id, typem, data.files, data.ts, data.thread_ts, data.routine)
185
220
  elsif !config.on_master_bot and @bots_created[@channel_id].key?(:extended) and
186
221
  @bots_created[@channel_id][:extended].include?(@channels_name[data.channel]) and
187
222
  command.size > 0 and command[0] != "-"
188
- process_first(user_info.user, command, dest, @channel_id, typem, data.files, data.ts, data.thread_ts)
223
+ process_first(user_info.user, command, dest, @channel_id, typem, data.files, data.ts, data.thread_ts, data.routine)
189
224
  elsif (dest[0] == "D" or @channel_id == data.channel or data.user == config[:nick_id]) and
190
225
  command.size > 0 and command[0] != "-"
191
- process_first(user_info.user, command, dest, data.channel, typem, data.files, data.ts, data.thread_ts)
226
+ process_first(user_info.user, command, dest, data.channel, typem, data.files, data.ts, data.thread_ts, data.routine)
192
227
  # if @botname on #channel_rules: do something
193
228
  end
194
229
  rescue Exception => stack
195
230
  @logger.fatal stack
196
231
  end
197
232
  else
198
- if !config.on_master_bot and !dest.nil? and (dest == @master_bot_id or dest[0] == "D") and
233
+ if !config.on_master_bot and !dest.nil? and (data.channel == @master_bot_id or dest[0] == "D") and
199
234
  data.text.match?(/^\s*bot\s+status\s*$/i) and @admin_users_id.include?(data.user)
200
235
  respond "ping from #{config.channel}", dest
201
236
  elsif !config.on_master_bot and !dest.nil? and data.user == config[:nick_id] and dest == @master_bot_id
202
237
  # to treat on other bots the status messages populated on master bot
203
238
  case data.text
239
+ when /From now on I'll be on maintenance status/i
240
+ sleep 2
241
+ if File.exist?("#{config.path}/config_tmp.status")
242
+ file_cts = IO.readlines("#{config.path}/config_tmp.status").join
243
+ unless file_cts.to_s() == ""
244
+ file_cts = eval(file_cts)
245
+ if file_cts.is_a?(Hash) and file_cts.key?(:on_maintenance)
246
+ config.on_maintenance = file_cts.on_maintenance
247
+ config.on_maintenance_message = file_cts.on_maintenance_message
248
+ end
249
+ end
250
+ end
251
+ when /From now on I won't be on maintenance/i
252
+ sleep 2
253
+ if File.exist?("#{config.path}/config_tmp.status")
254
+ file_cts = IO.readlines("#{config.path}/config_tmp.status").join
255
+ unless file_cts.to_s() == ""
256
+ file_cts = eval(file_cts)
257
+ if file_cts.is_a?(Hash) and file_cts.key?(:on_maintenance)
258
+ config.on_maintenance = file_cts.on_maintenance
259
+ config.on_maintenance_message = file_cts.on_maintenance_message
260
+ end
261
+ end
262
+ end
263
+
204
264
  when /^Bot has been (closed|killed) by/i
205
265
  sleep 2
206
266
  get_bots_created()
@@ -213,6 +273,22 @@ class SlackSmartBot
213
273
  when /removed the access to the rules of (.+) from (.+)\.$/i
214
274
  sleep 2
215
275
  get_bots_created()
276
+ when /global shortcut added/
277
+ sleep 2
278
+ if File.exist?("#{config.path}/shortcuts/shortcuts_global.rb")
279
+ file_sc = IO.readlines("#{config.path}/shortcuts/shortcuts_global.rb").join
280
+ unless file_sc.to_s() == ""
281
+ @shortcuts_global = eval(file_sc)
282
+ end
283
+ end
284
+ when /global shortcut deleted/
285
+ sleep 2
286
+ if File.exist?("#{config.path}/shortcuts/shortcuts_global.rb")
287
+ file_sc = IO.readlines("#{config.path}/shortcuts/shortcuts_global.rb").join
288
+ unless file_sc.to_s() == ""
289
+ @shortcuts_global = eval(file_sc)
290
+ end
291
+ end
216
292
  end
217
293
  end
218
294
  end