slack-smart-bot 1.6.7 → 1.8.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c9d526f540bb7d4c155b112cba9a04cb7b9ab54885d907434a155e5b67e329b
4
- data.tar.gz: 34df5affebadc0f812bc457a41e5cecb0c414c5cbfff04a14bbc6cbb3b8ccae0
3
+ metadata.gz: d35053e4911d77ae985b29fbd1564636eb0d078b38106601c2e126a401a03d5b
4
+ data.tar.gz: d5aadeedbdbe0c7a6528cbeac1f1bb0990d88cf93fdb9f0a085cf9be9ec17c2d
5
5
  SHA512:
6
- metadata.gz: ac91e223e10df1eb7fdcadb398133cc016e09ece01ef5956d8344721abc541dd9e308bd0f12da8220d274c43dfa77c93e3a7990a40fecad40e29e8ef98093e88
7
- data.tar.gz: 15f8c7c141159602355a862159a8b231614001c4ffd61354778254424624d8111a2a3ab03913f55e433799bced95835097348642b952bbf9fc8acd94b7957e51
6
+ metadata.gz: 19ed3708f96befda1f755358eb847facba2afaa62f6a92e08e7f68143e51659ef75ab7ef925c0d07e9a2b02072dcd02ddeda89697855363e50e84dc3f1970af6
7
+ data.tar.gz: d40d1ac18d32074994c2c4ed3b835b74ada4e9ff46bbb5a7122df0f18f1b44eb777a6351ddaad3dd00b8bc129dbb62aee75572b9f3fe625e4fa972add2083107
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
 
@@ -380,7 +386,7 @@ Example:
380
386
  To see available shortcuts: **_`see shortcuts`_** and to delete a particular shortcut: **_`delete shortcut NAME`_**
381
387
 
382
388
  ### Routines
383
- To add specific commands to be run automatically every certain amount of time or a specific time: **_`add routine NAME every NUMBER PERIOD COMMAND`_** or **_`add routine NAME at TIME COMMAND`_**
389
+ To add specific commands to be run automatically every certain amount of time or a specific time: **_`add routine NAME every NUMBER PERIOD COMMAND`_** or **_`add routine NAME at TIME COMMAND`_**. Also just before the command you can supply the channel where you want to publish the results, if not channel supplied then it would be the SmartBot Channel or on the DM if the command is run from there. Remember the SmartBot needs to have access to the channel where you want to publish.
384
390
 
385
391
  If you want to hide the routine executions use `add silent routine`. It won't show the routine name when executing.
386
392
 
@@ -388,6 +394,8 @@ 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`_**
398
+ >**_`add routine clean_custdb on Tuesdays at 09:00 #SREChannel !clean customers db`_**
391
399
 
392
400
  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
401
 
@@ -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)
@@ -183,10 +177,20 @@ class SlackSmartBot
183
177
  get_rules_imported()
184
178
 
185
179
  begin
180
+ #todo: take in consideration the case that the value supplied on config.masters and config.admins are the ids and not the user names
186
181
  @admin_users_id = []
182
+ @master_admin_users_id = []
187
183
  config.admins.each do |au|
188
184
  user_info = client.web_client.users_info(user: "@#{au}")
189
185
  @admin_users_id << user_info.user.id
186
+ if config.masters.include?(au)
187
+ @master_admin_users_id << user_info.user.id
188
+ end
189
+ sleep 1
190
+ end
191
+ (config.masters-config.admins).each do |au|
192
+ user_info = client.web_client.users_info(user: "@#{au}")
193
+ @master_admin_users_id << user_info.user.id
190
194
  sleep 1
191
195
  end
192
196
  rescue Slack::Web::Api::Errors::TooManyRequestsError
@@ -210,7 +214,8 @@ class SlackSmartBot
210
214
  if version_remote != VERSION
211
215
  version_message = ". There is a new available version: #{version_remote}."
212
216
  end
213
- unless config[:silent]
217
+ if !config[:silent] or ENV['BOT_SILENT'].to_s == 'false'
218
+ ENV['BOT_SILENT'] = 'true' if config[:silent] == 'true' and ENV['BOT_SILENT'].to_s != 'true'
214
219
  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
220
  end
216
221
  @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'
@@ -2,7 +2,7 @@ class SlackSmartBot
2
2
  # list of available emojis: https://www.webfx.com/tools/emoji-cheat-sheet/
3
3
  # react(:thumbsup)
4
4
  def react(emoji, parent=false)
5
- if parent
5
+ if parent or Thread.current[:ts].to_s == ''
6
6
  ts = Thread.current[:thread_ts]
7
7
  else
8
8
  ts = Thread.current[:ts]
@@ -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
@@ -1,10 +1,14 @@
1
1
  class SlackSmartBot
2
2
  # helpadmin: ----------------------------------------------
3
3
  # helpadmin: `add routine NAME every NUMBER PERIOD COMMAND`
4
+ # helpadmin: `add routine NAME every NUMBER PERIOD #CHANNEL COMMAND`
4
5
  # helpadmin: `add routine NAME every NUMBER PERIOD`
5
6
  # helpadmin: `add silent routine NAME every NUMBER PERIOD`
6
7
  # helpadmin: `create routine NAME every NUMBER PERIOD`
7
8
  # helpadmin: `add routine NAME at TIME COMMAND`
9
+ # helpadmin: `add routine NAME at TIME #CHANNEL COMMAND`
10
+ # helpadmin: `add routine NAME on DAYWEEK at TIME COMMAND`
11
+ # helpadmin: `add routine NAME on DAYWEEK at TIME #CHANNEL COMMAND`
8
12
  # helpadmin: `add routine NAME at TIME`
9
13
  # helpadmin: `add silent routine NAME at TIME`
10
14
  # helpadmin: `create routine NAME at TIME`
@@ -15,14 +19,18 @@ class SlackSmartBot
15
19
  # helpadmin: NUMBER: Integer
16
20
  # helpadmin: PERIOD: days, d, hours, h, minutes, mins, min, m, seconds, secs, sec, s
17
21
  # helpadmin: TIME: time at format HH:MM:SS
22
+ # helpadmin: DAYWEEK: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday. And their plurals.
23
+ # helpadmin: #CHANNEL: the destination channel where the results will be published. If not supplied then the bot channel by default or a DM if the command is run from a DM.
18
24
  # helpadmin: COMMAND: any valid smart bot command or rule
19
25
  # helpadmin: Examples:
20
26
  # helpadmin: _add routine example every 30s ruby puts 'a'_
21
27
  # helpadmin: _add routine example every 3 days ruby puts 'a'_
22
28
  # helpadmin: _add routine example at 17:05 ruby puts 'a'_
23
- # helpadmin: _create silent routine every 12 hours !Run customer tests_
29
+ # helpadmin: _create silent routine Example every 12 hours !Run customer tests_
30
+ # helpadmin: _add routine example on Mondays at 05:00 !run customer tests_
31
+ # helpadmin: _add routine example on Tuesdays at 09:00 #SREChannel !run db cleanup_
24
32
  # helpadmin:
25
- def add_routine(dest, from, user, name, type, number_time, period, command_to_run, files, silent)
33
+ def add_routine(dest, from, user, name, type, number_time, period, command_to_run, files, silent, channel)
26
34
  save_stats(__method__)
27
35
  if files.nil? or files.size == 0 or (files.size > 0 and config.masters.include?(from))
28
36
  if config.admins.include?(from)
@@ -30,13 +38,14 @@ class SlackSmartBot
30
38
  respond "I'm sorry but there is already a routine with that name.\nCall `see routines` to see added routines", dest
31
39
  else
32
40
  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]$/) &&
41
+ if (type != "every") && !number_time.match?(/^[01][0-9]:[0-5][0-9]:[0-5][0-9]$/) &&
34
42
  !number_time.match?(/^2[0-3]:[0-5][0-9]:[0-5][0-9]$/)
35
43
  respond "Wrong time specified: *#{number_time}*"
36
44
  else
37
45
  file_path = ""
38
46
  every = ""
39
47
  at = ""
48
+ dayweek = ''
40
49
  next_run = Time.now
41
50
  case period.downcase
42
51
  when "days", "d"
@@ -52,16 +61,31 @@ class SlackSmartBot
52
61
  every = "#{number_time} seconds"
53
62
  every_in_seconds = number_time.to_i
54
63
  else # time
64
+ if type != 'at'
65
+ dayweek = type.downcase
66
+ days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']
67
+ incr = days.index(dayweek) - Time.now.wday
68
+ if incr < 0
69
+ incr = (7+incr)*24*60*60
70
+ else
71
+ incr = incr * 24 * 60 * 60
72
+ end
73
+ days = incr/(24*60*60)
74
+ every_in_seconds = 7 * 24 * 60 * 60 # one week
75
+ else
76
+ days = 0
77
+ every_in_seconds = 24 * 60 * 60 # one day
78
+ end
79
+
55
80
  at = number_time
56
- if next_run.strftime("%H:%M:%S") < number_time
81
+ if next_run.strftime("%H:%M:%S") < number_time and days == 0
57
82
  nt = number_time.split(":")
58
83
  next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
59
84
  else
60
- next_run += (24 * 60 * 60) # one more day
85
+ next_run += ((24 * 60 * 60) * days) # one or more days
61
86
  nt = number_time.split(":")
62
87
  next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
63
88
  end
64
- every_in_seconds = 24 * 60 * 60
65
89
  end
66
90
  Dir.mkdir("#{config.path}/routines/#{@channel_id}") unless Dir.exist?("#{config.path}/routines/#{@channel_id}")
67
91
 
@@ -75,12 +99,12 @@ class SlackSmartBot
75
99
  http.get(files[0].url_private_download, save_data: file_path)
76
100
  system("chmod +x #{file_path}")
77
101
  end
78
-
102
+ channel = dest if channel.to_s == ''
79
103
  @routines[@channel_id] = {} unless @routines.key?(@channel_id)
80
104
  @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,
105
+ every: every, every_in_seconds: every_in_seconds, at: at, dayweek: dayweek, file_path: file_path,
82
106
  command: command_to_run.to_s.strip, silent: silent,
83
- next_run: next_run.to_s, dest: dest, last_run: "", last_elapsed: "",
107
+ next_run: next_run.to_s, dest: channel, last_run: "", last_elapsed: "",
84
108
  running: false }
85
109
  update_routines
86
110
  respond "Added routine *`#{name}`* to the channel", dest
@@ -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 = []
@@ -30,7 +32,7 @@ class SlackSmartBot
30
32
  message = ["You need to set stats to true to generate the stats when running the bot instance."]
31
33
  end
32
34
  save_stats(__method__)
33
- if config.masters.include?(from_user) and typem==:on_dm #master admin user
35
+ if (config.masters.include?(from_user) or @master_admin_users_id.include?(from_user)) and typem==:on_dm #master admin user
34
36
  if !File.exist?("#{config.stats_path}.#{Time.now.strftime("%Y-%m")}.log")
35
37
  message<<'No stats'
36
38
  else
@@ -43,17 +45,71 @@ class SlackSmartBot
43
45
  from+= " 00:00:00 +0000"
44
46
  to+= " 23:59:59 +0000"
45
47
  rows = []
46
-
48
+ rows_month = {}
49
+ users_month = {}
50
+ commands_month = {}
51
+ users_id_name = {}
52
+ users_name_id = {}
53
+
54
+ # to translate global and enterprise users since sometimes was returning different names/ids
55
+ Dir["#{config.stats_path}.*.log"].sort.each do |file|
56
+ if file >= "#{config.stats_path}.#{from_file}.log" or file <= "#{config.stats_path}.#{to_file}.log"
57
+ CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
58
+ unless users_id_name.key?(row[:user_id])
59
+ users_id_name[row[:user_id]] = row[:user_name]
60
+ end
61
+ unless users_name_id.key?(row[:user_name])
62
+ users_name_id[row[:user_name]] = row[:user_id]
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+
69
+ if user!=''
70
+ user_info = client.web_client.users_info(user: user)
71
+ if users_id_name.key?(user_info.user.id)
72
+ user_name = users_id_name[user_info.user.id]
73
+ else
74
+ user_name = user_info.user.name
75
+ end
76
+ if users_name_id.key?(user_info.user.name)
77
+ user_id = users_name_id[user_info.user.name]
78
+ else
79
+ user_id = user_info.user.id
80
+ end
81
+ end
82
+ master_admins = config.masters.dup
83
+ config.masters.each do |u|
84
+ if users_id_name.key?(u)
85
+ master_admins << users_id_name[u]
86
+ elsif users_name_id.key?(u)
87
+ master_admins << users_name_id[u]
88
+ end
89
+ end
90
+
47
91
  Dir["#{config.stats_path}.*.log"].sort.each do |file|
48
92
  if file >= "#{config.stats_path}.#{from_file}.log" or file <= "#{config.stats_path}.#{to_file}.log"
49
93
  CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
50
94
  row[:date] = row[:date].to_s
51
- if !exclude_masters or (exclude_masters and !config.masters.include?(row[:user_name]))
52
- if user=='' or (user!='' and row[:user_id] == user)
95
+ row[:user_name] = users_id_name[row[:user_id]]
96
+ row[:user_id] = users_name_id[row[:user_name]]
97
+ if !exclude_masters or (exclude_masters and !master_admins.include?(row[:user_name]) and
98
+ !master_admins.include?(row[:user_id]) and
99
+ !@master_admin_users_id.include?(row[:user_id]))
100
+ if user=='' or (user!='' and row[:user_name] == user_name) or (user!='' and row[:user_id] == user_id)
53
101
  if exclude_command == '' or (exclude_command!='' and row[:command]!=exclude_command)
54
102
  if row[:bot_channel_id] == channel_id or channel_id == ''
55
103
  if row[:date] >= from and row[:date] <= to
56
104
  rows << row.to_h
105
+ if monthly
106
+ rows_month[row[:date][0..6]] = 0 unless rows_month.key?(row[:date][0..6])
107
+ users_month[row[:date][0..6]] = [] unless users_month.key?(row[:date][0..6])
108
+ commands_month[row[:date][0..6]] = [] unless commands_month.key?(row[:date][0..6])
109
+ rows_month[row[:date][0..6]] += 1
110
+ users_month[row[:date][0..6]] << row[:user_id]
111
+ commands_month[row[:date][0..6]] << row[:command]
112
+ end
57
113
  end
58
114
  end
59
115
  end
@@ -62,7 +118,6 @@ class SlackSmartBot
62
118
  end
63
119
  end
64
120
  end
65
-
66
121
  total = rows.size
67
122
  if exclude_masters
68
123
  message << 'Excluding master admins'
@@ -79,7 +134,22 @@ class SlackSmartBot
79
134
  message << "*Total calls <##{channel_id}>*: #{total} from #{from_short} to #{to_short}"
80
135
  end
81
136
  if total > 0
82
-
137
+ if monthly
138
+ message << '*Totals by month / commands / users (%new)*'
139
+ all_users = []
140
+ new_users = []
141
+ rows_month.each do |k,v|
142
+ if all_users.empty?
143
+ message_new_users = ''
144
+ else
145
+ new_users = (users_month[k]-all_users).uniq
146
+ message_new_users = "(#{new_users.size*100/users_month[k].uniq.size}%)"
147
+ end
148
+ all_users += users_month[k]
149
+ message << "\t#{k}: #{v} (#{(v.to_f*100/total).round(2)}%) / #{commands_month[k].uniq.size} / #{users_month[k].uniq.size} #{message_new_users}"
150
+ end
151
+ end
152
+
83
153
  if channel_id == ''
84
154
  message << "*Channels*"
85
155
  channels = rows.bot_channel.uniq.sort
@@ -89,21 +159,25 @@ class SlackSmartBot
89
159
  end
90
160
  end
91
161
  if user==''
92
- message << "*Users*"
93
- users = rows.user_name.uniq.sort
162
+ users = rows.user_id.uniq.sort
163
+ message << "*Users* - #{users.size}"
164
+ count_user = {}
94
165
  users.each do |user|
95
- count = rows.count {|h| h.user_name==user}
96
- message << "\t#{user}: #{count} (#{(count.to_f*100/total).round(2)}%)"
166
+ count = rows.count {|h| h.user_id==user}
167
+ count_user[user] = count
168
+ end
169
+ count_user.sort_by {|k,v| -v}.each do |user, count|
170
+ message << "\t#{users_id_name[user]}: #{count} (#{(count.to_f*100/total).round(2)}%)"
97
171
  end
98
172
  end
99
-
100
- message << "*Commands*"
173
+
101
174
  commands = rows.command.uniq.sort
175
+ message << "*Commands* - #{commands.size}"
102
176
  commands.each do |command|
103
177
  count = rows.count {|h| h.command==command}
104
178
  message << "\t#{command}: #{count} (#{(count.to_f*100/total).round(2)}%)"
105
179
  end
106
-
180
+
107
181
  message << "*Message type*"
108
182
  types = rows.type_message.uniq.sort
109
183
  types.each do |type|
@@ -112,7 +186,6 @@ class SlackSmartBot
112
186
  end
113
187
  message << "*Last activity*: #{rows[-1].date} #{rows[-1].bot_channel} #{rows[-1].type_message} #{rows[-1].user_name} #{rows[-1].command}"
114
188
  end
115
-
116
189
  end
117
190
  else
118
191
  message<<"Only Master admin users on a private conversation with the bot can see the bot stats."
@@ -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.sort
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_name}> / 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
@@ -69,15 +69,17 @@ class SlackSmartBot
69
69
  when /^\s*kill\sbot\son\s<#C\w+\|(.+)>\s*$/i, /^kill\sbot\son\s(.+)\s*$/i
70
70
  channel = $1
71
71
  kill_bot_on_channel(dest, from, channel)
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
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<#(C\w+)\|.+>\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<#(C\w+)\|.+>\s*)?(\s.+)?\s*$/i,
74
+ /^\s*(add|create)\s+(silent\s+)?routine\s+(\w+)\s+(at)\s+(\d+:\d+:?\d+?)\s*()(\s<#(C\w+)\|.+>\s*)?(\s.+)?\s*$/i
74
75
  silent = $2.to_s!=''
75
76
  name = $3.downcase
76
77
  type = $4
77
78
  number_time = $5
78
79
  period = $6
79
- command_to_run = $7
80
- add_routine(dest, from, user, name, type, number_time, period, command_to_run, files, silent)
80
+ channel = $8
81
+ command_to_run = $9
82
+ add_routine(dest, from, user, name, type, number_time, period, command_to_run, files, silent, channel)
81
83
  when /^\s*(kill|delete|remove)\s+routine\s+(\w+)\s*$/i
82
84
  name = $2.downcase
83
85
  remove_routine(dest, from, name)
@@ -105,12 +107,15 @@ class SlackSmartBot
105
107
  st_to = st_to.gsub('.','-').gsub('/','-')
106
108
  st_user = opts.scan(/<@([^>]+)>/).join
107
109
  exclude_masters = opts.match?(/exclude\s+masters?/i)
110
+ monthly = false
108
111
  if all_opts.include?('today')
109
112
  st_from = st_to = "#{Time.now.strftime("%Y-%m-%d")}"
113
+ elsif all_opts.include?('monthly')
114
+ monthly = true
110
115
  end
111
116
  exclude_command = opts.scan(/exclude\s+([^\s]+)/i).join
112
117
  exclude_command = '' if exclude_command == 'masters'
113
- bot_stats(dest, from, typem, st_channel, st_from, st_to, st_user, exclude_masters, exclude_command)
118
+ bot_stats(dest, from, typem, st_channel, st_from, st_to, st_user, exclude_masters, exclude_command, monthly)
114
119
  else
115
120
  processed = false
116
121
  end
@@ -166,11 +171,11 @@ class SlackSmartBot
166
171
  ruby_code(dest, user, code, rules_file)
167
172
  when /^\s*(private\s+)?(repl|irb|live)\s*()()()$/i,
168
173
  /^\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,
174
+ /^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s+"([^"]+)"()\s*$/i,
175
+ /^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s+"([^"]+)"\s+(.+)\s*$/i,
171
176
  /^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)()\s+(.+)\s*$/i,
172
177
  /^\s*(private\s+)?(repl|irb|live)()\s+()(.+)\s*$/i
173
- if $1.to_s!=''
178
+ if $1.to_s!=''
174
179
  type = :private
175
180
  else
176
181
  type = :public
@@ -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
@@ -40,7 +42,9 @@ class SlackSmartBot
40
42
  f.puts "|#{data.channel}|#{data.user}|#{data.text}"
41
43
  }
42
44
  end
43
- if data.channel[0] == "D" or data.channel[0] == "C" or data.channel[0] == "G" #Direct message or Channel or Private Channel
45
+ if data.key?(:dest) and data.dest.to_s!='' # for run routines and publish on different channels
46
+ dest = data.dest
47
+ elsif data.channel[0] == "D" or data.channel[0] == "C" or data.channel[0] == "G" #Direct message or Channel or Private Channel
44
48
  dest = data.channel
45
49
  else # not treated
46
50
  dest = nil
@@ -63,15 +67,15 @@ class SlackSmartBot
63
67
  data.text = $4
64
68
  typem = :on_call
65
69
  end
66
- elsif dest == @master_bot_id
70
+ elsif data.channel == @master_bot_id
67
71
  if config.on_master_bot #only to be treated on master bot channel
68
72
  typem = :on_master
69
73
  end
70
- elsif @bots_created.key?(dest)
71
- if @channel_id == dest #only to be treated by the bot on the channel
74
+ elsif @bots_created.key?(data.channel)
75
+ if @channel_id == data.channel #only to be treated by the bot on the channel
72
76
  typem = :on_bot
73
77
  end
74
- elsif dest[0] == "D" #Direct message
78
+ elsif data.channel[0] == "D" #Direct message
75
79
  get_rules_imported()
76
80
  if @rules_imported.key?(data.user) && @rules_imported[data.user].key?(data.user) and
77
81
  @bots_created.key?(@rules_imported[data.user][data.user])
@@ -83,21 +87,21 @@ class SlackSmartBot
83
87
  #only to be treated by master bot
84
88
  typem = :on_dm
85
89
  end
86
- elsif dest[0] == "C" or dest[0] == "G"
90
+ elsif data.channel[0] == "C" or data.channel[0] == "G"
87
91
  #only to be treated on the channel of the bot. excluding running ruby
88
- if !config.on_master_bot and @bots_created.key?(@channel_id) and @bots_created[@channel_id][:extended].include?(@channels_name[dest]) and
92
+ if !config.on_master_bot and @bots_created.key?(@channel_id) and @bots_created[@channel_id][:extended].include?(@channels_name[data.channel]) and
89
93
  !data.text.match?(/^!?\s*(ruby|code)\s+/) and !data.text.match?(/^!?!?\s*(ruby|code)\s+/) and !data.text.match?(/^\^?\s*(ruby|code)\s+/)
90
94
  typem = :on_extended
91
95
  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+/) )
92
96
  #or in case of running ruby, the master bot
93
97
  @bots_created.each do |k, v|
94
- if v.key?(:extended) and v[:extended].include?(@channels_name[dest])
98
+ if v.key?(:extended) and v[:extended].include?(@channels_name[data.channel])
95
99
  typem = :on_extended
96
100
  break
97
101
  end
98
102
  end
99
103
  end
100
- if dest[0] == "G" and config.on_master_bot and typem != :on_extended #private group
104
+ if data.channel[0] == "G" and config.on_master_bot and typem != :on_extended #private group
101
105
  typem = :on_pg
102
106
  end
103
107
  end
@@ -115,6 +119,8 @@ class SlackSmartBot
115
119
  begin
116
120
  #todo: when changed @questions user_id then move user_info inside the ifs to avoid calling it when not necessary
117
121
  user_info = client.web_client.users_info(user: data.user)
122
+ #user_info.user.id = data.user #todo: remove this line when slack issue with Wxxxx Uxxxx fixed
123
+ data.user = user_info.user.id #todo: remove this line when slack issue with Wxxxx Uxxxx fixed
118
124
  if @questions.key?(user_info.user.name)
119
125
  if data.text.match?(/^\s*(Bye|Bæ|Good\sBye|Adiós|Ciao|Bless|Bless\sBless|Adeu)\s(#{@salutations.join("|")})\s*$/i)
120
126
  @questions.delete(user_info.user.name)
@@ -123,16 +129,12 @@ class SlackSmartBot
123
129
  command = @questions[user_info.user.name]
124
130
  @questions[user_info.user.name] = data.text
125
131
  end
126
- elsif @repl_sessions.key?(user_info.user.name) and dest==@repl_sessions[user_info.user.name][:dest] and
132
+ elsif @repl_sessions.key?(user_info.user.name) and data.channel==@repl_sessions[user_info.user.name][:dest] and
127
133
  ((@repl_sessions[user_info.user.name][:on_thread] and data.thread_ts == @repl_sessions[user_info.user.name][:thread_ts]) or
128
134
  (!@repl_sessions[user_info.user.name][:on_thread] and data.thread_ts.to_s == '' ))
129
135
 
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
136
+ if data.text.match(/^\s*```(.*)```\s*$/im)
137
+ @repl_sessions[user_info.user.name][:command] = $1
136
138
  else
137
139
  @repl_sessions[user_info.user.name][:command] = data.text
138
140
  end
@@ -142,12 +144,8 @@ class SlackSmartBot
142
144
  end
143
145
 
144
146
  #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
147
+ if command.match(/^\s*```(.*)```\s*$/im)
148
+ command = $1
151
149
  elsif command.size >= 2 and
152
150
  ((command[0] == "`" and command[-1] == "`") or (command[0] == "*" and command[-1] == "*") or (command[0] == "_" and command[-1] == "_"))
153
151
  command = command[1..-2]
@@ -176,13 +174,13 @@ class SlackSmartBot
176
174
  if channel_found.nil?
177
175
  @logger.fatal "Not possible to find the channel #{channel_rules_name}"
178
176
  elsif channel_found.name == config.master_channel
179
- respond "You cannot use the rules from Master Channel on any other channel.", dest
177
+ respond "You cannot use the rules from Master Channel on any other channel.", data.channel
180
178
  elsif @status != :on
181
- respond "The bot in that channel is not :on", dest
179
+ respond "The bot in that channel is not :on", data.channel
182
180
  elsif data.user == channel_found.creator or members.include?(data.user)
183
181
  process_first(user_info.user, command, dest, channel_rules, typem, data.files, data.ts, data.thread_ts)
184
182
  else
185
- respond "You need to join the channel <##{channel_found.id}> to be able to use the rules.", dest
183
+ respond "You need to join the channel <##{channel_found.id}> to be able to use the rules.", data.channel
186
184
  end
187
185
  elsif config.on_master_bot and typem == :on_extended and
188
186
  command.size > 0 and command[0] != "-"
@@ -201,7 +199,7 @@ class SlackSmartBot
201
199
  @logger.fatal stack
202
200
  end
203
201
  else
204
- if !config.on_master_bot and !dest.nil? and (dest == @master_bot_id or dest[0] == "D") and
202
+ if !config.on_master_bot and !dest.nil? and (data.channel == @master_bot_id or dest[0] == "D") and
205
203
  data.text.match?(/^\s*bot\s+status\s*$/i) and @admin_users_id.include?(data.user)
206
204
  respond "ping from #{config.channel}", dest
207
205
  elsif !config.on_master_bot and !dest.nil? and data.user == config[:nick_id] and dest == @master_bot_id
@@ -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])
@@ -24,7 +23,11 @@ class SlackSmartBot
24
23
  stdout, stderr, status = Open3.capture3(process_to_run)
25
24
  if !@routines[@channel_id][name][:silent] or (@routines[@channel_id][name][:silent] and
26
25
  (!stderr.match?(/\A\s*\z/) or !stdout.match?(/\A\s*\z/)))
27
- respond "routine *`#{name}`*: #{@routines[@channel_id][name][:file_path]}", @routines[@channel_id][name][:dest]
26
+ if @routines[@channel_id][name][:dest]!=@channel_id
27
+ respond "routine from <##{@channel_id}> *`#{name}`*: #{@routines[@channel_id][name][:file_path]}", @routines[@channel_id][name][:dest]
28
+ else
29
+ respond "routine *`#{name}`*: #{@routines[@channel_id][name][:file_path]}", @routines[@channel_id][name][:dest]
30
+ end
28
31
  end
29
32
  if stderr == ""
30
33
  unless stdout.match?(/\A\s*\z/)
@@ -35,10 +38,15 @@ class SlackSmartBot
35
38
  end
36
39
  else #command
37
40
  if !@routines[@channel_id][name][:silent]
38
- respond "routine *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest]
41
+ if @routines[@channel_id][name][:dest]!=@channel_id
42
+ respond "routine from <##{@channel_id}> *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest]
43
+ else
44
+ respond "routine *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest]
45
+ end
39
46
  end
40
47
  started = Time.now
41
- data = { channel: @routines[@channel_id][name][:dest],
48
+ data = { channel: @channel_id,
49
+ dest: @routines[@channel_id][name][:dest],
42
50
  user: @routines[@channel_id][name][:creator_id],
43
51
  text: @routines[@channel_id][name][:command],
44
52
  files: nil }
@@ -55,11 +63,34 @@ class SlackSmartBot
55
63
  require "time"
56
64
  every_in_seconds = Time.parse(@routines[@channel_id][name][:next_run]) - Time.now
57
65
  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]
66
+ if @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!=''
67
+ day = @routines[@channel_id][name][:dayweek]
68
+ days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']
69
+ incr = days.index(day) - Time.now.wday
70
+ if incr < 0
71
+ incr = (7+incr)*24*60*60
72
+ else
73
+ incr = incr * 24 * 60 * 60
74
+ end
75
+ days = incr/(24*60*60)
76
+ weekly = true
77
+ else
78
+ days = 0
79
+ weekly = false
80
+ end
81
+
82
+ if started.strftime("%H:%M:%S") < @routines[@channel_id][name][:at] and days == 0
59
83
  nt = @routines[@channel_id][name][:at].split(":")
60
84
  next_run = Time.new(started.year, started.month, started.day, nt[0], nt[1], nt[2])
61
85
  else
62
- next_run = started + (24 * 60 * 60) # one more day
86
+ if days == 0 and started.strftime("%H:%M:%S") >= @routines[@channel_id][name][:at]
87
+ if weekly
88
+ days = 7
89
+ else
90
+ days = 1
91
+ end
92
+ end
93
+ next_run = started + (days * 24 * 60 * 60) # one more day/week
63
94
  nt = @routines[@channel_id][name][:at].split(":")
64
95
  next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
65
96
  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,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slack-smart-bot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.7
4
+ version: 1.8.2
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-20 00:00:00.000000000 Z
11
+ date: 2020-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: slack-ruby-client
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.14'
19
+ version: '0.15'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 0.14.5
22
+ version: 0.15.1
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '0.14'
29
+ version: '0.15'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 0.14.5
32
+ version: 0.15.1
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