slack-smart-bot 1.6.5 → 1.8.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97d3af04c6b231089bad9829779d20b650c878e4782a43d7a43435144c5b9c96
4
- data.tar.gz: 111ae9d729118b0e596845c597a613d6f41fb28d34b0e2ce160419aac863d9de
3
+ metadata.gz: b15c46b5b101a0a31487508457982e8f5d6b81ad41f54338407ecdb2aa9974f3
4
+ data.tar.gz: d32bfceda9068f5f11d1002f255b77a280f3a7b7068f40a632c03b7cb6378127
5
5
  SHA512:
6
- metadata.gz: 9a8183b5c24bcd88484932c23bbf0f2df8fd11efedf4c845c6ac39660ab9c9a1c27f2702b6c5e8d312aac083acc968e6c4119ea5f0cffd4a64330824a7f1df90
7
- data.tar.gz: 7e5d4ac7c6d6056754d33897ece0475839ce8160be966bf5b37dc6a19f71976944459fc2423899af12de17099635b5f85c277dacd610481af372d401574c120a
6
+ metadata.gz: 61c6e4060d928a1562176a0596ac968c7d007998c268816af489a0d36624a768ba1eadd3b6d27be7d19d37984b335d70215f718189165c1a9ac0a3a18130c5a0
7
+ data.tar.gz: 48995f9d416dd8906353508ac9bf86219cd92b2ede2f0043c3752dcc4db95564161ad64f41d86fd9f821fa1dc6d9d5bba3ef5519be037214e0003c3423b595c9
data/README.md CHANGED
@@ -124,6 +124,7 @@ def rules(user, command, processed, dest)
124
124
  respond "zZzzzzzZZZZZZzzzzzzz!"
125
125
  react :sleeping
126
126
  sleep 10
127
+ unreact :sleeping
127
128
  react :sunny
128
129
  when /no/i, /nope/i, /cancel/i
129
130
  @questions.delete(from)
@@ -152,6 +153,8 @@ def rules(user, command, processed, dest)
152
153
  else
153
154
  respond "#{user.name}: #{stdout} #{stderr}"
154
155
  end
156
+
157
+ unreact :runner
155
158
 
156
159
  else
157
160
  unless processed
@@ -159,8 +162,11 @@ def rules(user, command, processed, dest)
159
162
  end
160
163
  end
161
164
  end
162
-
163
165
  ```
166
+
167
+ Also you can add general rules that will be available on all Smart Bot channels to `./rules/general_rules.rb`
168
+
169
+
164
170
  ### How to access the Smart Bot
165
171
  You can access the bot directly on the MASTER CHANNEL, on a secondary channel where the bot is running and directly by opening a private chat with the bot, in this case the conversation will be just between you and the bot.
166
172
 
@@ -388,6 +394,7 @@ Examples:
388
394
  >**_`add routine run_tests every 3h !run tests on customers`_**
389
395
  >**_`add routine clean_db at 17:05 !clean customers temp db`_**
390
396
  >**_`add silent routine clean_db at 17:05 !clean customers temp db`_**
397
+ >**_`add routine clean_custdb on Mondays at 05:00 !clean customers db`_**
391
398
 
392
399
  Also instead of adding a Command to be executed, you can attach a file, then the routine will be created and the attached file will be executed on the criteria specified. Only Master Admins are allowed to use it this way.
393
400
 
@@ -36,13 +36,7 @@ class SlackSmartBot
36
36
  else
37
37
  config[:path] = '.'
38
38
  end
39
- if ENV['BOT_SILENT'].to_s == 'true'
40
- config[:silent] = true
41
- elsif ENV['BOT_SILENT'].to_s == 'false'
42
- config[:silent] = false
43
- else
44
- config[:silent] = false unless config.key?(:silent)
45
- end
39
+ config[:silent] = false unless config.key?(:silent)
46
40
  config[:testing] = false unless config.key?(:testing)
47
41
  config[:simulate] = false unless config.key?(:simulate)
48
42
  config[:stats] = false unless config.key?(:stats)
@@ -210,7 +204,8 @@ class SlackSmartBot
210
204
  if version_remote != VERSION
211
205
  version_message = ". There is a new available version: #{version_remote}."
212
206
  end
213
- unless config[:silent]
207
+ if !config[:silent] or ENV['BOT_SILENT'].to_s == 'false'
208
+ ENV['BOT_SILENT'] = 'true' if config[:silent] == 'true' and ENV['BOT_SILENT'].to_s != 'true'
214
209
  respond "Smart Bot started v#{VERSION}#{version_message}\nIf you want to know what I can do for you: `bot help`.\n`bot rules` if you want to display just the specific rules of this channel.\nYou can talk to me privately if you prefer it."
215
210
  end
216
211
  @routines.each do |ch, rout|
@@ -0,0 +1,33 @@
1
+ # add here the general rules you will be using in all Smart Bots
2
+ def general_rules(user, command, processed, dest, files = [], rules_file = "")
3
+ from = user.name
4
+ display_name = user.profile.display_name
5
+
6
+ begin
7
+ case command
8
+
9
+ # help: ----------------------------------------------
10
+ # help: `echo SOMETHING`
11
+ # help: `INTEGER echo SOMETHING`
12
+ # help: repeats SOMETHING. If INTEGER supplied then that number of times.
13
+ # help: Examples:
14
+ # help: _echo I am the Smart Bot_
15
+ # help: _100 echo :heart:_
16
+ when /^(\d*)\s*echo\s(.+)/i
17
+ save_stats :echo
18
+ $1.to_s == '' ? times = 1 : times = $1.to_i
19
+ respond ($2*times).to_s
20
+
21
+ else
22
+ return false
23
+ end
24
+ return true
25
+ rescue => exception
26
+ if defined?(@logger)
27
+ @logger.fatal exception
28
+ respond "Unexpected error!! Please contact an admin to solve it: <@#{ADMIN_USERS.join(">, <@")}>"
29
+ else
30
+ puts exception
31
+ end
32
+ end
33
+ end
@@ -38,102 +38,98 @@ def rules(user, command, processed, dest, files = [], rules_file = "")
38
38
  from = user.name
39
39
  display_name = user.profile.display_name
40
40
 
41
- begin
42
- case command
41
+ load "#{config.path}/rules/general_rules.rb"
42
+
43
+ unless general_rules(user, command, processed, dest, files, rules_file)
44
+ begin
45
+ case command
43
46
 
44
- # help: ----------------------------------------------
45
- # help: `echo SOMETHING`
46
- # help: repeats SOMETHING
47
- # help: Examples:
48
- # help: _echo I am the Smart Bot_
49
- when /^echo\s(.+)/i
50
- save_stats :echo
51
- respond $1
52
- react :monkey_face
53
-
54
- # help: ----------------------------------------------
55
- # help: `go to sleep`
56
- # help: it will sleep the bot for 5 seconds
57
- # help:
58
- when /^go\sto\ssleep/i
59
- save_stats :to_to_sleep
60
- unless @questions.keys.include?(from)
61
- ask "do you want me to take a siesta?"
62
- else
63
- case @questions[from]
64
- when /yes/i, /yep/i, /sure/i
65
- @questions.delete(from)
66
- respond "I'll be sleeping for 5 secs... just for you"
67
- respond "zZzzzzzZZZZZZzzzzzzz!"
68
- react :sleeping
69
- sleep 5
70
- react :sunny
71
- when /no/i, /nope/i, /cancel/i
72
- @questions.delete(from)
73
- respond "Thanks, I'm happy to be awake"
47
+ # help: ----------------------------------------------
48
+ # help: `go to sleep`
49
+ # help: it will sleep the bot for 5 seconds
50
+ # help:
51
+ when /^go\sto\ssleep/i
52
+ save_stats :go_to_sleep
53
+ unless @questions.keys.include?(from)
54
+ ask "do you want me to take a siesta?"
74
55
  else
75
- respond "I don't understand"
76
- ask "are you sure do you want me to sleep? (yes or no)"
56
+ case @questions[from]
57
+ when /yes/i, /yep/i, /sure/i
58
+ @questions.delete(from)
59
+ respond "I'll be sleeping for 5 secs... just for you"
60
+ respond "zZzzzzzZZZZZZzzzzzzz!"
61
+ react :sleeping
62
+ sleep 5
63
+ unreact :sleeping
64
+ react :sunny
65
+ when /no/i, /nope/i, /cancel/i
66
+ @questions.delete(from)
67
+ respond "Thanks, I'm happy to be awake"
68
+ else
69
+ respond "I don't understand"
70
+ ask "are you sure do you want me to sleep? (yes or no)"
71
+ end
77
72
  end
78
- end
79
73
 
80
- # help: ----------------------------------------------
81
- # help: `run something`
82
- # help: It will run the process and report the results when done
83
- # help:
84
- when /^run something/i
85
- save_stats :run_something
86
- react :runner
74
+ # help: ----------------------------------------------
75
+ # help: `run something`
76
+ # help: It will run the process and report the results when done
77
+ # help:
78
+ when /^run something/i
79
+ save_stats :run_something
80
+ react :runner
87
81
 
88
- process_to_run = "ruby -v"
89
- process_to_run = ("cd #{project_folder} &&" + process_to_run) if defined?(project_folder)
90
- stdout, stderr, status = Open3.capture3(process_to_run)
91
- if stderr == ""
92
- if stdout == ""
93
- respond "#{display_name}: Nothing returned."
82
+ process_to_run = "ruby -v"
83
+ process_to_run = ("cd #{project_folder} &&" + process_to_run) if defined?(project_folder)
84
+ stdout, stderr, status = Open3.capture3(process_to_run)
85
+ unreact :runner
86
+ if stderr == ""
87
+ if stdout == ""
88
+ respond "#{display_name}: Nothing returned."
89
+ else
90
+ respond "#{display_name}: #{stdout}"
91
+ end
94
92
  else
95
- respond "#{display_name}: #{stdout}"
93
+ respond "#{display_name}: #{stdout} #{stderr}"
96
94
  end
97
- else
98
- respond "#{display_name}: #{stdout} #{stderr}"
99
- end
100
-
101
- # Emoticons you can use with `react` command https://www.webfx.com/tools/emoji-cheat-sheet/
102
-
103
- # Examples for respond and respond_direct
104
- # # send 'the message' to the channel or direct message where the command was written
105
- # respond "the message"
106
- # # send 'the message' privately as a direct message to the user that sent the command
107
- # respond_direct "the message"
108
- # # send 'the message' to a specific channel name
109
- # respond "the message", 'my_channel'
110
- # # send 'the message' to a specific channel id
111
- # respond "the message", 'CSU34D33'
112
- # # send 'the message' to a specific user as direct message
113
- # respond "the message", '@theuser'
114
- # # send 'the message' to a specific user id as direct message
115
- # respond "the message", 'US3344D3'
95
+
96
+ # Emoticons you can use with `react` command https://www.webfx.com/tools/emoji-cheat-sheet/
97
+
98
+ # Examples for respond and respond_direct
99
+ # # send 'the message' to the channel or direct message where the command was written
100
+ # respond "the message"
101
+ # # send 'the message' privately as a direct message to the user that sent the command
102
+ # respond_direct "the message"
103
+ # # send 'the message' to a specific channel name
104
+ # respond "the message", 'my_channel'
105
+ # # send 'the message' to a specific channel id
106
+ # respond "the message", 'CSU34D33'
107
+ # # send 'the message' to a specific user as direct message
108
+ # respond "the message", '@theuser'
109
+ # # send 'the message' to a specific user id as direct message
110
+ # respond "the message", 'US3344D3'
116
111
 
117
- # Example downloading a file from slack
118
- # if !files.nil? and files.size == 1 and files[0].filetype == 'yaml'
119
- # require 'nice_http'
120
- # http = NiceHttp.new(host: "https://files.slack.com", headers: { "Authorization" => "Bearer #{config[:token]}" })
121
- # http.get(files[0].url_private_download, save_data: './tmp/')
122
- # end
112
+ # Example downloading a file from slack
113
+ # if !files.nil? and files.size == 1 and files[0].filetype == 'yaml'
114
+ # require 'nice_http'
115
+ # http = NiceHttp.new(host: "https://files.slack.com", headers: { "Authorization" => "Bearer #{config[:token]}" })
116
+ # http.get(files[0].url_private_download, save_data: './tmp/')
117
+ # end
123
118
 
124
- # Examples sending a file to slack:
125
- # send_file(to, msg, filepath, title, format, type = "text")
126
- # send_file(dest, 'the message', "#{project_folder}/temp/logs_ptBI.log", 'title', 'text/plain', "text")
127
- # send_file(dest, 'the message', "#{project_folder}/temp/example.jpeg", 'title', 'image/jpeg', "jpg")
119
+ # Examples sending a file to slack:
120
+ # send_file(to, msg, filepath, title, format, type = "text")
121
+ # send_file(dest, 'the message', "#{project_folder}/temp/logs_ptBI.log", 'title', 'text/plain', "text")
122
+ # send_file(dest, 'the message', "#{project_folder}/temp/example.jpeg", 'title', 'image/jpeg', "jpg")
128
123
 
129
124
 
130
- else
131
- unless processed
132
- dont_understand()
125
+ else
126
+ unless processed
127
+ dont_understand()
128
+ end
133
129
  end
130
+ rescue => exception
131
+ @logger.fatal exception
132
+ respond "Unexpected error!! Please contact an admin to solve it: <@#{config.admins.join(">, <@")}>"
134
133
  end
135
- rescue => exception
136
- @logger.fatal exception
137
- respond "Unexpected error!! Please contact an admin to solve it: <@#{config.admins.join(">, <@")}>"
138
134
  end
139
135
  end
@@ -5,4 +5,5 @@ require_relative 'comm/respond'
5
5
  require_relative 'comm/send_file'
6
6
  require_relative 'comm/send_msg_channel'
7
7
  require_relative 'comm/send_msg_user'
8
- require_relative 'comm/react'
8
+ require_relative 'comm/react'
9
+ require_relative 'comm/unreact'
@@ -38,7 +38,7 @@ class SlackSmartBot
38
38
  f.puts "|#{dest}|#{config[:nick_id]}|#{msg}"
39
39
  }
40
40
  end
41
- elsif dest[0] == "D" or dest[0] == "U" # Direct message
41
+ elsif dest[0] == "D" or dest[0] == "U" or dest[0] == "W" # Direct message
42
42
  send_msg_user(dest, msg)
43
43
  elsif dest[0] == "@"
44
44
  begin
@@ -3,9 +3,12 @@ class SlackSmartBot
3
3
  #to send a file to an user or channel
4
4
  #send_file(dest, 'the message', "#{project_folder}/temp/logs_ptBI.log", 'message to be sent', 'text/plain', "text")
5
5
  #send_file(dest, 'the message', "#{project_folder}/temp/example.jpeg", 'message to be sent', 'image/jpeg', "jpg")
6
- def send_file(to, msg, file, title, format, type = "text")
6
+ #send_file(dest, 'the message', "", 'message to be sent', 'text/plain', "ruby", content: "the content to be sent when no file supplied")
7
+ #send_file(dest, 'the message', "myfile.rb", 'message to be sent', 'text/plain', "ruby", content: "the content to be sent when no file supplied")
8
+ def send_file(to, msg, file, title, format, type = "text", content: '')
7
9
  unless config[:simulate]
8
- if to[0] == "U" #user
10
+ file = 'myfile' if file.to_s == '' and content!=''
11
+ if to[0] == "U" or to[0] == "W" #user
9
12
  im = client.web_client.im_open(user: to)
10
13
  channel = im["channel"]["id"]
11
14
  else
@@ -18,16 +21,29 @@ class SlackSmartBot
18
21
  ts = nil
19
22
  end
20
23
 
21
- client.web_client.files_upload(
22
- channels: channel,
23
- as_user: true,
24
- file: Faraday::UploadIO.new(file, format),
25
- title: title,
26
- filename: file,
27
- filetype: type,
28
- initial_comment: msg,
29
- thread_ts: ts
30
- )
24
+ if content.to_s == ''
25
+ client.web_client.files_upload(
26
+ channels: channel,
27
+ as_user: true,
28
+ file: Faraday::UploadIO.new(file, format),
29
+ title: title,
30
+ filename: file,
31
+ filetype: type,
32
+ initial_comment: msg,
33
+ thread_ts: ts
34
+ )
35
+ else
36
+ client.web_client.files_upload(
37
+ channels: channel,
38
+ as_user: true,
39
+ content: content,
40
+ title: title,
41
+ filename: file,
42
+ filetype: type,
43
+ initial_comment: msg,
44
+ thread_ts: ts
45
+ )
46
+ end
31
47
  end
32
48
  end
33
49
 
@@ -0,0 +1,16 @@
1
+ class SlackSmartBot
2
+ # list of available emojis: https://www.webfx.com/tools/emoji-cheat-sheet/
3
+ # unreact(:thumbsup)
4
+ def unreact(emoji, parent=false)
5
+ if parent
6
+ ts = Thread.current[:thread_ts]
7
+ else
8
+ ts = Thread.current[:ts]
9
+ end
10
+ begin
11
+ client.web_client.reactions_remove(channel: Thread.current[:dest], name: emoji, timestamp: ts)
12
+ rescue Exception => stack
13
+ @logger.warn stack
14
+ end
15
+ end
16
+ end
@@ -5,6 +5,7 @@ class SlackSmartBot
5
5
  # helpadmin: `add silent routine NAME every NUMBER PERIOD`
6
6
  # helpadmin: `create routine NAME every NUMBER PERIOD`
7
7
  # helpadmin: `add routine NAME at TIME COMMAND`
8
+ # helpadmin: `add routine NAME on DAYWEEK at TIME COMMAND`
8
9
  # helpadmin: `add routine NAME at TIME`
9
10
  # helpadmin: `add silent routine NAME at TIME`
10
11
  # helpadmin: `create routine NAME at TIME`
@@ -15,12 +16,14 @@ class SlackSmartBot
15
16
  # helpadmin: NUMBER: Integer
16
17
  # helpadmin: PERIOD: days, d, hours, h, minutes, mins, min, m, seconds, secs, sec, s
17
18
  # helpadmin: TIME: time at format HH:MM:SS
19
+ # helpadmin: DAYWEEK: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday. And their plurals.
18
20
  # helpadmin: COMMAND: any valid smart bot command or rule
19
21
  # helpadmin: Examples:
20
22
  # helpadmin: _add routine example every 30s ruby puts 'a'_
21
23
  # helpadmin: _add routine example every 3 days ruby puts 'a'_
22
24
  # helpadmin: _add routine example at 17:05 ruby puts 'a'_
23
25
  # helpadmin: _create silent routine every 12 hours !Run customer tests_
26
+ # helpadmin: _add routine example on Mondays at 05:00 !run customer tests_
24
27
  # helpadmin:
25
28
  def add_routine(dest, from, user, name, type, number_time, period, command_to_run, files, silent)
26
29
  save_stats(__method__)
@@ -30,13 +33,14 @@ class SlackSmartBot
30
33
  respond "I'm sorry but there is already a routine with that name.\nCall `see routines` to see added routines", dest
31
34
  else
32
35
  number_time += ":00" if number_time.split(":").size == 2
33
- if (type == "at") && !number_time.match?(/^[01][0-9]:[0-5][0-9]:[0-5][0-9]$/) &&
36
+ if (type != "every") && !number_time.match?(/^[01][0-9]:[0-5][0-9]:[0-5][0-9]$/) &&
34
37
  !number_time.match?(/^2[0-3]:[0-5][0-9]:[0-5][0-9]$/)
35
38
  respond "Wrong time specified: *#{number_time}*"
36
39
  else
37
40
  file_path = ""
38
41
  every = ""
39
42
  at = ""
43
+ dayweek = ''
40
44
  next_run = Time.now
41
45
  case period.downcase
42
46
  when "days", "d"
@@ -52,16 +56,31 @@ class SlackSmartBot
52
56
  every = "#{number_time} seconds"
53
57
  every_in_seconds = number_time.to_i
54
58
  else # time
59
+ if type != 'at'
60
+ dayweek = type.downcase
61
+ days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']
62
+ incr = days.index(dayweek) - Time.now.wday
63
+ if incr < 0
64
+ incr = (7+incr)*24*60*60
65
+ else
66
+ incr = incr * 24 * 60 * 60
67
+ end
68
+ days = incr/(24*60*60)
69
+ every_in_seconds = 7 * 24 * 60 * 60 # one week
70
+ else
71
+ days = 0
72
+ every_in_seconds = 24 * 60 * 60 # one day
73
+ end
74
+
55
75
  at = number_time
56
- if next_run.strftime("%H:%M:%S") < number_time
76
+ if next_run.strftime("%H:%M:%S") < number_time and days == 0
57
77
  nt = number_time.split(":")
58
78
  next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
59
79
  else
60
- next_run += (24 * 60 * 60) # one more day
80
+ next_run += ((24 * 60 * 60) * days) # one or more days
61
81
  nt = number_time.split(":")
62
82
  next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
63
83
  end
64
- every_in_seconds = 24 * 60 * 60
65
84
  end
66
85
  Dir.mkdir("#{config.path}/routines/#{@channel_id}") unless Dir.exist?("#{config.path}/routines/#{@channel_id}")
67
86
 
@@ -78,7 +97,7 @@ class SlackSmartBot
78
97
 
79
98
  @routines[@channel_id] = {} unless @routines.key?(@channel_id)
80
99
  @routines[@channel_id][name] = { channel_name: config.channel, creator: from, creator_id: user.id, status: :on,
81
- every: every, every_in_seconds: every_in_seconds, at: at, file_path: file_path,
100
+ every: every, every_in_seconds: every_in_seconds, at: at, dayweek: dayweek, file_path: file_path,
82
101
  command: command_to_run.to_s.strip, silent: silent,
83
102
  next_run: next_run.to_s, dest: dest, last_run: "", last_elapsed: "",
84
103
  running: false }
@@ -54,7 +54,7 @@ class SlackSmartBot
54
54
  respond "Now the rules from <##{@channel_id}> are available on *<##{@channels_id[channel]}>*", dest
55
55
  end
56
56
  respond "<@#{user.id}> extended the rules from <##{@channel_id}> to this channel so now you can talk to the Smart Bot on demand using those rules.", @channels_id[channel]
57
- respond "Use `!` before the command you want to run", @channels_id[channel]
57
+ respond "Use `!` or `^` or `!!` before the command you want to run", @channels_id[channel]
58
58
  respond "To see the specific rules for this bot on this channel: `!bot rules` or `!bot rules COMMAND`", @channels_id[channel]
59
59
  end
60
60
  end
@@ -44,6 +44,7 @@ class SlackSmartBot
44
44
  msg << "\tStatus: #{v[:status]}"
45
45
  msg << "\tEvery: #{v[:every]}" unless v[:every] == ""
46
46
  msg << "\tAt: #{v[:at]}" unless v[:at] == ""
47
+ msg << "\tOn: #{v[:dayweek]}" unless !v.key?(:dayweek) or v[:dayweek].to_s == ""
47
48
  msg << "\tNext Run: #{v[:next_run]}"
48
49
  msg << "\tLast Run: #{v[:last_run]}"
49
50
  msg << "\tTime consumed on last run: #{v[:last_elapsed]}" unless v[:command] !=''
@@ -13,6 +13,7 @@ class SlackSmartBot
13
13
  # helpadmin: `bot stats CHANNEL exclude masters from YYYY/MM/DD to YYYY/MM/DD`
14
14
  # helpadmin: `bot stats today`
15
15
  # helpadmin: `bot stats exclude COMMAND_ID`
16
+ # helpadmin: `bot stats monthly`
16
17
  # helpadmin: To see the bot stats
17
18
  # helpadmin: You can use this command only if you are a Master admin user and if you are in a private conversation with the bot
18
19
  # helpadmin: You need to set stats to true to generate the stats when running the bot instance.
@@ -21,8 +22,9 @@ class SlackSmartBot
21
22
  # helpadmin: _bot stats @peter.wind_
22
23
  # helpadmin: _bot stats #sales from 2019/12/15 to 2019/12/31_
23
24
  # helpadmin: _bot stats #sales today_
25
+ # helpadmin: _bot stats #sales from 2020-01-01 monthly_
24
26
  # helpadmin:
25
- def bot_stats(dest, from_user, typem, channel_id, from, to, user, exclude_masters, exclude_command)
27
+ def bot_stats(dest, from_user, typem, channel_id, from, to, user, exclude_masters, exclude_command, monthly)
26
28
  require 'csv'
27
29
  if config.stats
28
30
  message = []
@@ -34,6 +36,10 @@ class SlackSmartBot
34
36
  if !File.exist?("#{config.stats_path}.#{Time.now.strftime("%Y-%m")}.log")
35
37
  message<<'No stats'
36
38
  else
39
+ if user!=''
40
+ user_info = client.web_client.users_info(user: user)
41
+ user_name = user_info.user.name
42
+ end
37
43
  from = "#{Time.now.strftime("%Y-%m")}-01" if from == ''
38
44
  to = "#{Time.now.strftime("%Y-%m-%d")}" if to == ''
39
45
  from_short = from
@@ -43,17 +49,28 @@ class SlackSmartBot
43
49
  from+= " 00:00:00 +0000"
44
50
  to+= " 23:59:59 +0000"
45
51
  rows = []
52
+ rows_month = {}
53
+ users_month = {}
54
+ commands_month = {}
46
55
 
47
56
  Dir["#{config.stats_path}.*.log"].sort.each do |file|
48
57
  if file >= "#{config.stats_path}.#{from_file}.log" or file <= "#{config.stats_path}.#{to_file}.log"
49
58
  CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
50
59
  row[:date] = row[:date].to_s
51
60
  if !exclude_masters or (exclude_masters and !config.masters.include?(row[:user_name]))
52
- if user=='' or (user!='' and row[:user_id] == user)
61
+ if user=='' or (user!='' and row[:user_name] == user_name)
53
62
  if exclude_command == '' or (exclude_command!='' and row[:command]!=exclude_command)
54
63
  if row[:bot_channel_id] == channel_id or channel_id == ''
55
64
  if row[:date] >= from and row[:date] <= to
56
65
  rows << row.to_h
66
+ if monthly
67
+ rows_month[row[:date][0..6]] = 0 unless rows_month.key?(row[:date][0..6])
68
+ users_month[row[:date][0..6]] = [] unless users_month.key?(row[:date][0..6])
69
+ commands_month[row[:date][0..6]] = [] unless commands_month.key?(row[:date][0..6])
70
+ rows_month[row[:date][0..6]] += 1
71
+ users_month[row[:date][0..6]] << row[:user_name]
72
+ commands_month[row[:date][0..6]] << row[:command]
73
+ end
57
74
  end
58
75
  end
59
76
  end
@@ -79,6 +96,21 @@ class SlackSmartBot
79
96
  message << "*Total calls <##{channel_id}>*: #{total} from #{from_short} to #{to_short}"
80
97
  end
81
98
  if total > 0
99
+ if monthly
100
+ message << '*Totals by month / commands / users (%new)*'
101
+ all_users = []
102
+ new_users = []
103
+ rows_month.each do |k,v|
104
+ if all_users.empty?
105
+ message_new_users = ''
106
+ else
107
+ new_users = (users_month[k]-all_users).uniq
108
+ message_new_users = "(#{new_users.size*100/users_month[k].uniq.size}%)"
109
+ end
110
+ all_users += users_month[k]
111
+ message << "\t#{k}: #{v} (#{(v.to_f*100/total).round(2)}%) / #{commands_month[k].uniq.size} / #{users_month[k].uniq.size} #{message_new_users}"
112
+ end
113
+ end
82
114
 
83
115
  if channel_id == ''
84
116
  message << "*Channels*"
@@ -89,16 +121,16 @@ class SlackSmartBot
89
121
  end
90
122
  end
91
123
  if user==''
92
- message << "*Users*"
93
124
  users = rows.user_name.uniq.sort
125
+ message << "*Users* - #{users.size}"
94
126
  users.each do |user|
95
127
  count = rows.count {|h| h.user_name==user}
96
128
  message << "\t#{user}: #{count} (#{(count.to_f*100/total).round(2)}%)"
97
129
  end
98
130
  end
99
131
 
100
- message << "*Commands*"
101
132
  commands = rows.command.uniq.sort
133
+ message << "*Commands* - #{commands.size}"
102
134
  commands.each do |command|
103
135
  count = rows.count {|h| h.command==command}
104
136
  message << "\t#{command}: #{count} (#{(count.to_f*100/total).round(2)}%)"
@@ -82,6 +82,7 @@ class SlackSmartBot
82
82
  message = "Session name: *#{session_name}*
83
83
  From now on I will execute all you write as a Ruby command and I will keep the session open until you send `quit` or `bye` or `exit`.
84
84
  I will respond with the result so it is not necessary you send `print`, `puts`, `p` or `pp` unless you want it as the output when calling `run repl`.
85
+ Use `p` to print a message raw, exacly like it is returned.
85
86
  If you want to avoid a message to be treated by me, start the message with '-'.
86
87
  After 30 minutes of no communication with the Smart Bot the session will be dismissed.
87
88
  If you want to see the methods of a class or module you created use _ls TheModuleOrClass_
@@ -100,34 +101,8 @@ class SlackSmartBot
100
101
  require \"amazing_print\"
101
102
  bindme' + serialt + ' = binding
102
103
  eval(\"require \'nice_http\'\" , bindme' + serialt + ')
103
- def repl_params(met)
104
- res = []
105
- met.parameters.each do |par|
106
- if par[0]==:req
107
- res << par[1].to_s
108
- elsif par[0]==:keyreq
109
- res << par[1].to_s + \":\"
110
- elsif par[0]==:key
111
- res << \"?\"+par[1].to_s + \":\"
112
- elsif par[0]==:opt
113
- res << \"?\"+par[1].to_s
114
- elsif par[0]==:rest
115
- res << \"*\"+par[1].to_s
116
- elsif par[0]==:keyrest
117
- res << \"**\"+par[1].to_s
118
- else
119
- res << par[1].to_s
120
- end
121
- end
122
- \"(#{res.join(\", \")})\"
123
- end
124
104
  def ls(obj)
125
- mets = (obj.methods - Object.methods)
126
- res = []
127
- mets.each do |met|
128
- res << \"#{met}#{repl_params(obj.method(met))}\"
129
- end
130
- res
105
+ (obj.methods - Object.methods)
131
106
  end
132
107
 
133
108
  file_input_repl = File.open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.input\", \"r\")
@@ -158,9 +133,15 @@ class SlackSmartBot
158
133
  error = true
159
134
  end
160
135
  if resp_repl.to_s != \"\"
161
- open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
162
- f.puts \"\`\`\`\n#{resp_repl.ai}\n\`\`\`\"
163
- }
136
+ if code_to_run_repl.match?(/^\s*p\s+/i)
137
+ open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
138
+ f.puts \"\`\`\`\n#{resp_repl.inspect}\n\`\`\`\"
139
+ }
140
+ else
141
+ open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
142
+ f.puts \"\`\`\`\n#{resp_repl.ai}\n\`\`\`\"
143
+ }
144
+ end
164
145
  unless error or !add_to_run_repl
165
146
  open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.run\", \"a+\") {|f|
166
147
  f.puts code_to_run_repl
@@ -198,7 +179,13 @@ class SlackSmartBot
198
179
  sleep 0.2
199
180
  resp_repl = file_output_repl.read
200
181
  if resp_repl.to_s!=''
201
- respond resp_repl, dest
182
+ if resp_repl.to_s.lines.count < 60 and resp_repl.to_s.size < 3500
183
+ respond resp_repl, dest
184
+ else
185
+ resp_repl.gsub!(/^\s*```/,'')
186
+ resp_repl.gsub!(/```\s*$/,'')
187
+ send_file(dest, "", 'response.rb', "", 'text/plain', "ruby", content: resp_repl)
188
+ end
202
189
  end
203
190
  rescue Exception => excp
204
191
  @logger.fatal excp
@@ -210,7 +197,7 @@ class SlackSmartBot
210
197
  @repl_sessions[from][:command] = ''
211
198
  code.gsub!("\\n", "\n")
212
199
  code.gsub!("\\r", "\r")
213
- # Disabled for the moment sinde it is deleting lines with '}'
200
+ # Disabled for the moment since it is deleting lines with '}'
214
201
  #code.gsub!(/^\W*$/, "") #to remove special chars from slack when copy/pasting.
215
202
  if code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File") or
216
203
  code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
@@ -52,7 +52,7 @@ class SlackSmartBot
52
52
  Dir.mkdir("#{project_folder}/tmp") unless Dir.exist?("#{project_folder}/tmp")
53
53
  Dir.mkdir("#{project_folder}/tmp/repl") unless Dir.exist?("#{project_folder}/tmp/repl")
54
54
  File.write("#{project_folder}/tmp/repl/#{session_name}.rb", content, mode: "w+")
55
- process_to_run = "ruby #{project_folder}/tmp/repl/#{session_name}.rb"
55
+ process_to_run = "ruby ./tmp/repl/#{session_name}.rb"
56
56
  process_to_run = ("cd #{project_folder} && #{process_to_run}") if defined?(project_folder)
57
57
  respond "Running REPL #{session_name}"
58
58
  stdout, stderr, status = Open3.capture3(process_to_run)
@@ -60,10 +60,19 @@ class SlackSmartBot
60
60
  if stdout == ""
61
61
  respond "*#{session_name}*: Nothing returned."
62
62
  else
63
- respond "*#{session_name}*: #{stdout}"
63
+ if stdout.to_s.lines.count < 60 and stdout.to_s.size < 3500
64
+ respond "*#{session_name}*: #{stdout}"
65
+ else
66
+ send_file(dest, "", 'response.rb', "", 'text/plain', "ruby", content: stdout)
67
+ end
64
68
  end
65
69
  else
66
- respond "*#{session_name}*: #{stdout} #{stderr}"
70
+ if (stdout.to_s+stderr.to_s).lines.count < 60
71
+ respond "*#{session_name}*: #{stdout} #{stderr}"
72
+ else
73
+ send_file(dest, "", 'response.rb', "", 'text/plain', "ruby", content: (stdout.to_s+stderr.to_s))
74
+ end
75
+
67
76
  end
68
77
  end
69
78
  else
@@ -15,7 +15,7 @@ class SlackSmartBot
15
15
  message = ""
16
16
  @repls.sort.to_h.each do |session_name, repl|
17
17
  if (repl.creator_name == user.name or repl.type == :public) or (config.admins.include?(user.name) and typem == :on_dm)
18
- message += "(#{repl.type}) *#{session_name}*: #{repl.description} / created: #{repl.created} / accessed: #{repl.accessed} / creator: #{repl.creator} / runs: #{repl.runs_by_creator+repl.runs_by_others} / gets: #{repl.gets} \n"
18
+ message += "(#{repl.type}) *#{session_name}*: #{repl.description} / created: #{repl.created} / accessed: #{repl.accessed} / creator: #{repl.creator_name} / runs: #{repl.runs_by_creator+repl.runs_by_others} / gets: #{repl.gets} \n"
19
19
  end
20
20
  end
21
21
  message = "No repls created" if message == ''
@@ -46,14 +46,19 @@ class SlackSmartBot
46
46
  rules_file = "slack-smart-bot_rules_#{channel_id}_#{from.gsub(" ", "_")}.rb"
47
47
  if defined?(RULES_FOLDER)
48
48
  rules_file = RULES_FOLDER + rules_file
49
+ general_rules_file = RULES_FOLDER + 'general_rules.rb'
49
50
  else
50
51
  Dir.mkdir("#{config.path}/rules") unless Dir.exist?("#{config.path}/rules")
51
52
  Dir.mkdir("#{config.path}/rules/#{channel_id}") unless Dir.exist?("#{config.path}/rules/#{channel_id}")
52
53
  rules_file = "/rules/#{channel_id}/" + rules_file
54
+ general_rules_file = "/rules/general_rules.rb"
53
55
  end
54
56
  default_rules = (__FILE__).gsub(/slack\/smart-bot\/commands\/on_master\/create_bot\.rb$/, "slack-smart-bot_rules.rb")
57
+ default_general_rules = (__FILE__).gsub(/slack\/smart-bot\/commands\/on_master\/create_bot\.rb$/, "slack-smart-bot_general_rules.rb")
58
+
55
59
  File.delete(config.path + rules_file) if File.exist?(config.path + rules_file)
56
60
  FileUtils.copy_file(default_rules, config.path + rules_file) unless File.exist?(config.path + rules_file)
61
+ FileUtils.copy_file(default_general_rules, config.path + general_rules_file) unless File.exist?(config.path + general_rules_file)
57
62
  admin_users = Array.new()
58
63
  creator_info = client.web_client.users_info(user: channel_found.creator)
59
64
  admin_users = [from, creator_info.user.name] + config.masters
@@ -70,7 +70,8 @@ class SlackSmartBot
70
70
  channel = $1
71
71
  kill_bot_on_channel(dest, from, channel)
72
72
  when /^\s*(add|create)\s+(silent\s+)?routine\s+(\w+)\s+(every)\s+(\d+)\s*(days|hours|minutes|seconds|mins|min|secs|sec|d|h|m|s)\s*(\s.+)?\s*$/i,
73
- /^\s*(add|create)\s+(silent\s+)?routine\s+(\w+)\s+(at)\s+(\d+:\d+:?\d+?)\s*()(\s.+)?\s*$/i
73
+ /^\s*(add|create)\s+(silent\s+)?routine\s+(\w+)\s+on\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)s?\s+at\s+(\d+:\d+:?\d+?)\s*()(\s.+)?\s*$/i,
74
+ /^\s*(add|create)\s+(silent\s+)?routine\s+(\w+)\s+(at)\s+(\d+:\d+:?\d+?)\s*()(\s.+)?\s*$/i
74
75
  silent = $2.to_s!=''
75
76
  name = $3.downcase
76
77
  type = $4
@@ -105,12 +106,15 @@ class SlackSmartBot
105
106
  st_to = st_to.gsub('.','-').gsub('/','-')
106
107
  st_user = opts.scan(/<@([^>]+)>/).join
107
108
  exclude_masters = opts.match?(/exclude\s+masters?/i)
109
+ monthly = false
108
110
  if all_opts.include?('today')
109
111
  st_from = st_to = "#{Time.now.strftime("%Y-%m-%d")}"
112
+ elsif all_opts.include?('monthly')
113
+ monthly = true
110
114
  end
111
115
  exclude_command = opts.scan(/exclude\s+([^\s]+)/i).join
112
116
  exclude_command = '' if exclude_command == 'masters'
113
- bot_stats(dest, from, typem, st_channel, st_from, st_to, st_user, exclude_masters, exclude_command)
117
+ bot_stats(dest, from, typem, st_channel, st_from, st_to, st_user, exclude_masters, exclude_command, monthly)
114
118
  else
115
119
  processed = false
116
120
  end
@@ -166,11 +170,11 @@ class SlackSmartBot
166
170
  ruby_code(dest, user, code, rules_file)
167
171
  when /^\s*(private\s+)?(repl|irb|live)\s*()()()$/i,
168
172
  /^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)()()\s*$/i,
169
- /^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s*"(.+)"()\s*$/i,
170
- /^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s*"(.+)"\s+(.+)\s*$/i,
173
+ /^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s+"([^"]+)"()\s*$/i,
174
+ /^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s+"([^"]+)"\s+(.+)\s*$/i,
171
175
  /^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)()\s+(.+)\s*$/i,
172
176
  /^\s*(private\s+)?(repl|irb|live)()\s+()(.+)\s*$/i
173
- if $1.to_s!=''
177
+ if $1.to_s!=''
174
178
  type = :private
175
179
  else
176
180
  type = :public
@@ -206,7 +210,7 @@ class SlackSmartBot
206
210
  end
207
211
  run_repl(dest, user, session_name, env_vars.flatten, rules_file)
208
212
  when /^\s*(delete|remove)\s+(repl|irb|live)\s+([\w\-]+)\s*$/i
209
- repl_name = $3.downcase
213
+ repl_name = $3
210
214
  delete_repl(dest, user, repl_name)
211
215
  when /^\s*see\s+(repls|repl|irb|irbs)\s*$/i
212
216
  see_repls(dest, user, typem)
@@ -32,6 +32,8 @@ class SlackSmartBot
32
32
  data.text = CGI.unescapeHTML(data.text)
33
33
  data.text.gsub!("\u00A0", " ") #to change &nbsp; (asc char 160) into blank space
34
34
  end
35
+ data.text.gsub!('’', "'")
36
+ data.text.gsub!('“', '"')
35
37
  rescue
36
38
  @logger.warn "Impossible to unescape or clean format for data.text:#{data.text}"
37
39
  end
@@ -115,6 +117,7 @@ class SlackSmartBot
115
117
  begin
116
118
  #todo: when changed @questions user_id then move user_info inside the ifs to avoid calling it when not necessary
117
119
  user_info = client.web_client.users_info(user: data.user)
120
+ user_info.user.id = data.user #todo: remove this line when slack issue with Wxxxx Uxxxx fixed
118
121
  if @questions.key?(user_info.user.name)
119
122
  if data.text.match?(/^\s*(Bye|Bæ|Good\sBye|Adiós|Ciao|Bless|Bless\sBless|Adeu)\s(#{@salutations.join("|")})\s*$/i)
120
123
  @questions.delete(user_info.user.name)
@@ -127,12 +130,8 @@ class SlackSmartBot
127
130
  ((@repl_sessions[user_info.user.name][:on_thread] and data.thread_ts == @repl_sessions[user_info.user.name][:thread_ts]) or
128
131
  (!@repl_sessions[user_info.user.name][:on_thread] and data.thread_ts.to_s == '' ))
129
132
 
130
- if data.text.size >= 6 and data.text[0..2] == "```" and data.text[-3..-1] == "```"
131
- if data.text.size == 6
132
- @repl_sessions[user_info.user.name][:command] = ""
133
- else
134
- @repl_sessions[user_info.user.name][:command] = data.text[3..-4]
135
- end
133
+ if data.text.match(/^\s*```(.*)```\s*$/im)
134
+ @repl_sessions[user_info.user.name][:command] = $1
136
135
  else
137
136
  @repl_sessions[user_info.user.name][:command] = data.text
138
137
  end
@@ -142,12 +141,8 @@ class SlackSmartBot
142
141
  end
143
142
 
144
143
  #when added special characters on the message
145
- if command.size >= 6 and command[0..2]=="```" and command[-3..-1]=="```"
146
- if command.size == 6
147
- command = ''
148
- else
149
- command = command[3..-4]
150
- end
144
+ if command.match(/^\s*```(.*)```\s*$/im)
145
+ command = $1
151
146
  elsif command.size >= 2 and
152
147
  ((command[0] == "`" and command[-1] == "`") or (command[0] == "*" and command[-1] == "*") or (command[0] == "_" and command[-1] == "_"))
153
148
  command = command[1..-2]
@@ -13,7 +13,6 @@ class SlackSmartBot
13
13
  ruby = ""
14
14
  end
15
15
  @routines[@channel_id][name][:silent] = false if !@routines[@channel_id][name].key?(:silent)
16
-
17
16
  if @routines[@channel_id][name][:at] == "" or
18
17
  (@routines[@channel_id][name][:at] != "" and @routines[@channel_id][name][:running] and
19
18
  @routines[@channel_id][name][:next_run] != "" and Time.now.to_s >= @routines[@channel_id][name][:next_run])
@@ -55,11 +54,34 @@ class SlackSmartBot
55
54
  require "time"
56
55
  every_in_seconds = Time.parse(@routines[@channel_id][name][:next_run]) - Time.now
57
56
  elsif @routines[@channel_id][name][:at] != "" #coming from start after pause for 'at'
58
- if started.strftime("%H:%M:%S") < @routines[@channel_id][name][:at]
57
+ if @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!=''
58
+ day = @routines[@channel_id][name][:dayweek]
59
+ days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']
60
+ incr = days.index(day) - Time.now.wday
61
+ if incr < 0
62
+ incr = (7+incr)*24*60*60
63
+ else
64
+ incr = incr * 24 * 60 * 60
65
+ end
66
+ days = incr/(24*60*60)
67
+ weekly = true
68
+ else
69
+ days = 0
70
+ weekly = false
71
+ end
72
+
73
+ if started.strftime("%H:%M:%S") < @routines[@channel_id][name][:at] and days == 0
59
74
  nt = @routines[@channel_id][name][:at].split(":")
60
75
  next_run = Time.new(started.year, started.month, started.day, nt[0], nt[1], nt[2])
61
76
  else
62
- next_run = started + (24 * 60 * 60) # one more day
77
+ if days == 0 and started.strftime("%H:%M:%S") >= @routines[@channel_id][name][:at]
78
+ if weekly
79
+ days = 7
80
+ else
81
+ days = 1
82
+ end
83
+ end
84
+ next_run = started + (days * 24 * 60 * 60) # one more day/week
63
85
  nt = @routines[@channel_id][name][:at].split(":")
64
86
  next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
65
87
  end
@@ -32,7 +32,11 @@ class SlackSmartBot
32
32
  help = @help_messages.deep_copy
33
33
  end
34
34
  if rules_file != ""
35
- help[:rules_file] = IO.readlines(config.path+rules_file).join.scan(/#\s*help\s*\w*:(.*)/i).join("\n")
35
+ help[:rules_file] = ''
36
+ help[:rules_file] += IO.readlines(config.path+rules_file).join.scan(/#\s*help\s*\w*:(.*)/i).join("\n") + "\n"
37
+ if File.exist?(config.path+'/rules/general_rules.rb')
38
+ help[:rules_file] += IO.readlines(config.path+'/rules/general_rules.rb').join.scan(/#\s*help\s*\w*:(.*)/i).join("\n")
39
+ end
36
40
  end
37
41
  help = remove_hash_keys(help, :admin_master) unless user_type == :admin_master
38
42
  help = remove_hash_keys(help, :admin) unless user_type == :admin or user_type == :admin_master
metadata CHANGED
@@ -1,35 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slack-smart-bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.5
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mario Ruiz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-15 00:00:00.000000000 Z
11
+ date: 2020-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: slack-ruby-client
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '0.14'
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: 0.14.5
19
+ version: 0.15.0
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '0.15'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: '0.14'
30
27
  - - ">="
31
28
  - !ruby/object:Gem::Version
32
- version: 0.14.5
29
+ version: 0.15.0
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.15'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: nice_http
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -79,6 +79,9 @@ dependencies:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
81
  version: '1'
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 1.2.1
82
85
  type: :runtime
83
86
  prerelease: false
84
87
  version_requirements: !ruby/object:Gem::Requirement
@@ -86,6 +89,9 @@ dependencies:
86
89
  - - "~>"
87
90
  - !ruby/object:Gem::Version
88
91
  version: '1'
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: 1.2.1
89
95
  - !ruby/object:Gem::Dependency
90
96
  name: rspec
91
97
  requirement: !ruby/object:Gem::Requirement
@@ -119,6 +125,7 @@ files:
119
125
  - LICENSE
120
126
  - README.md
121
127
  - lib/slack-smart-bot.rb
128
+ - lib/slack-smart-bot_general_rules.rb
122
129
  - lib/slack-smart-bot_rules.rb
123
130
  - lib/slack/smart-bot/comm.rb
124
131
  - lib/slack/smart-bot/comm/ask.rb
@@ -129,6 +136,7 @@ files:
129
136
  - lib/slack/smart-bot/comm/send_file.rb
130
137
  - lib/slack/smart-bot/comm/send_msg_channel.rb
131
138
  - lib/slack/smart-bot/comm/send_msg_user.rb
139
+ - lib/slack/smart-bot/comm/unreact.rb
132
140
  - lib/slack/smart-bot/commands.rb
133
141
  - lib/slack/smart-bot/commands/general/bot_help.rb
134
142
  - lib/slack/smart-bot/commands/general/bot_status.rb