slack-smart-bot 1.10.0 → 1.12.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 +4 -4
- data/README.md +134 -23
- data/lib/slack/smart-bot/comm/delete.rb +13 -0
- data/lib/slack/smart-bot/comm/dont_understand.rb +2 -2
- data/lib/slack/smart-bot/comm/get_channel_members.rb +7 -3
- data/lib/slack/smart-bot/comm/get_presence.rb +20 -0
- data/lib/slack/smart-bot/comm/get_users.rb +1 -1
- data/lib/slack/smart-bot/comm/respond.rb +24 -13
- data/lib/slack/smart-bot/comm/send_msg_user.rb +12 -11
- data/lib/slack/smart-bot/comm/set_status.rb +21 -0
- data/lib/slack/smart-bot/comm.rb +3 -0
- data/lib/slack/smart-bot/commands/general/add_admin.rb +51 -0
- data/lib/slack/smart-bot/commands/general/add_announcement.rb +1 -1
- data/lib/slack/smart-bot/commands/general/add_memo_team.rb +117 -0
- data/lib/slack/smart-bot/commands/general/add_team.rb +80 -0
- data/lib/slack/smart-bot/commands/general/add_vacation.rb +51 -0
- data/lib/slack/smart-bot/commands/general/allow_access.rb +67 -0
- data/lib/slack/smart-bot/commands/general/bot_help.rb +20 -11
- data/lib/slack/smart-bot/commands/general/delete_announcement.rb +1 -1
- data/lib/slack/smart-bot/commands/general/delete_memo_team.rb +69 -0
- data/lib/slack/smart-bot/commands/general/delete_share.rb +1 -1
- data/lib/slack/smart-bot/commands/general/delete_team.rb +54 -0
- data/lib/slack/smart-bot/commands/general/deny_access.rb +36 -0
- data/lib/slack/smart-bot/commands/general/ping_team.rb +100 -0
- data/lib/slack/smart-bot/commands/general/poster.rb +116 -0
- data/lib/slack/smart-bot/commands/general/remove_admin.rb +58 -0
- data/lib/slack/smart-bot/commands/general/remove_vacation.rb +27 -0
- data/lib/slack/smart-bot/commands/general/see_access.rb +24 -0
- data/lib/slack/smart-bot/commands/general/see_admins.rb +33 -0
- data/lib/slack/smart-bot/commands/general/see_announcements.rb +7 -5
- data/lib/slack/smart-bot/commands/general/see_command_ids.rb +29 -0
- data/lib/slack/smart-bot/commands/general/see_favorite_commands.rb +3 -4
- data/lib/slack/smart-bot/commands/general/see_statuses.rb +34 -21
- data/lib/slack/smart-bot/commands/general/see_teams.rb +402 -0
- data/lib/slack/smart-bot/commands/general/see_vacations.rb +58 -0
- data/lib/slack/smart-bot/commands/general/see_vacations_team.rb +136 -0
- data/lib/slack/smart-bot/commands/general/set_memo_status.rb +58 -0
- data/lib/slack/smart-bot/commands/general/share_messages.rb +1 -1
- data/lib/slack/smart-bot/commands/general/update_team.rb +130 -0
- data/lib/slack/smart-bot/commands/general_bot_commands.rb +442 -13
- data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/pause_routine.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/remove_routine.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +3 -2
- data/lib/slack/smart-bot/commands/on_bot/admin/see_result_routine.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +10 -9
- data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/start_routine.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/stop_using_rules_on.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/admin_master/delete_message.rb +25 -0
- data/lib/slack/smart-bot/commands/on_bot/admin_master/get_bot_logs.rb +1 -0
- data/lib/slack/smart-bot/commands/on_bot/admin_master/react_to.rb +3 -1
- data/lib/slack/smart-bot/commands/on_bot/admin_master/send_message.rb +15 -2
- data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +5 -4
- data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +416 -0
- data/lib/slack/smart-bot/commands/{general → on_bot/general}/bot_status.rb +1 -0
- data/lib/slack/smart-bot/commands/{general → on_bot/general}/leaderboard.rb +1 -0
- data/lib/slack/smart-bot/commands/{general → on_bot/general}/stop_using_rules.rb +1 -0
- data/lib/slack/smart-bot/commands/{general → on_bot/general}/suggest_command.rb +6 -0
- data/lib/slack/smart-bot/commands/{general → on_bot/general}/use_rules.rb +1 -0
- data/lib/slack/smart-bot/commands/{general → on_bot/general}/whats_new.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/kill_repl.rb +32 -0
- data/lib/slack/smart-bot/commands/on_bot/repl.rb +73 -15
- data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +1 -0
- data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +117 -28
- data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +2 -1
- data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +3 -2
- data/lib/slack/smart-bot/commands/on_master/admin/kill_bot_on_channel.rb +5 -4
- data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +3 -2
- data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +2 -1
- data/lib/slack/smart-bot/commands/on_master/admin_master/publish_announcements.rb +6 -3
- data/lib/slack/smart-bot/commands/on_master/admin_master/set_general_message.rb +2 -1
- data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +2 -1
- data/lib/slack/smart-bot/commands/on_master/create_bot.rb +1 -0
- data/lib/slack/smart-bot/commands/on_master/where_smartbot.rb +41 -0
- data/lib/slack/smart-bot/commands.rb +30 -7
- data/lib/slack/smart-bot/listen.rb +30 -30
- data/lib/slack/smart-bot/process.rb +53 -23
- data/lib/slack/smart-bot/process_first.rb +2 -2
- data/lib/slack/smart-bot/treat_message.rb +23 -17
- data/lib/slack/smart-bot/utils/build_help.rb +1 -1
- data/lib/slack/smart-bot/utils/check_vacations.rb +43 -0
- data/lib/slack/smart-bot/utils/create_routine_thread.rb +1 -1
- data/lib/slack/smart-bot/utils/get_access_channels.rb +13 -0
- data/lib/slack/smart-bot/utils/get_admins_channels.rb +33 -0
- data/lib/slack/smart-bot/utils/get_bots_created.rb +27 -10
- data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +7 -2
- data/lib/slack/smart-bot/utils/get_command_ids.rb +84 -0
- data/lib/slack/smart-bot/utils/get_help.rb +36 -19
- data/lib/slack/smart-bot/utils/get_repls.rb +22 -2
- data/lib/slack/smart-bot/utils/get_routines.rb +22 -2
- data/lib/slack/smart-bot/utils/get_teams.rb +22 -0
- data/lib/slack/smart-bot/utils/get_vacations.rb +22 -0
- data/lib/slack/smart-bot/utils/has_access.rb +25 -9
- data/lib/slack/smart-bot/utils/is_admin.rb +27 -0
- data/lib/slack/smart-bot/utils/save_stats.rb +52 -42
- data/lib/slack/smart-bot/utils/save_status.rb +22 -7
- data/lib/slack/smart-bot/utils/update_access_channels.rb +8 -0
- data/lib/slack/smart-bot/utils/update_admins_channels.rb +25 -0
- data/lib/slack/smart-bot/utils/update_bots_file.rb +28 -7
- data/lib/slack/smart-bot/utils/update_repls.rb +7 -4
- data/lib/slack/smart-bot/utils/update_routines.rb +9 -3
- data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +13 -6
- data/lib/slack/smart-bot/utils/update_teams.rb +16 -0
- data/lib/slack/smart-bot/utils/update_vacations.rb +16 -0
- data/lib/slack/smart-bot/utils.rb +11 -0
- data/lib/slack-smart-bot.rb +50 -12
- data/lib/slack-smart-bot_general_commands.rb +16 -1
- data/whats_new.txt +12 -30
- metadata +78 -21
- data/lib/slack/smart-bot/commands/general/bot_stats.rb +0 -314
@@ -17,7 +17,7 @@ class SlackSmartBot
|
|
17
17
|
# help: If 'clean' specified the repl won't pre execute the code written on the .smart-bot-repl file
|
18
18
|
# help: To avoid a message to be treated, start the message with '-'.
|
19
19
|
# help: Send _quit_, _bye_ or _exit_ to finish the session.
|
20
|
-
# help: Send puts, print, p or pp if you want to print out something when using
|
20
|
+
# help: Send puts, print, p or pp if you want to print out something when using _run repl_ later.
|
21
21
|
# help: After 30 minutes of no communication with the Smart Bot the session will be dismissed.
|
22
22
|
# help: If you declare on your rules file a method called 'project_folder' returning the path for the project folder, the code will be executed from that folder.
|
23
23
|
# help: By default it will be automatically loaded the gems: string_pattern, nice_hash and nice_http
|
@@ -30,6 +30,7 @@ class SlackSmartBot
|
|
30
30
|
# help: _repl delete_logs_
|
31
31
|
# help: _private repl random-ssn_
|
32
32
|
# help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
|
33
|
+
# help: command_id: :repl
|
33
34
|
# help:
|
34
35
|
def repl(dest, user, session_name, env_vars, rules_file, command, description, type)
|
35
36
|
#todo: add more tests
|
@@ -78,11 +79,14 @@ class SlackSmartBot
|
|
78
79
|
update_repls()
|
79
80
|
end
|
80
81
|
react :running
|
82
|
+
@ts_react ||= {}
|
81
83
|
if Thread.current[:ts].to_s == ''
|
82
|
-
@ts_react = Thread.current[:thread_ts]
|
84
|
+
@ts_react[session_name] = Thread.current[:thread_ts]
|
83
85
|
else
|
84
|
-
@ts_react = Thread.current[:ts]
|
86
|
+
@ts_react[session_name] = Thread.current[:ts]
|
85
87
|
end
|
88
|
+
@ts_repl ||= {}
|
89
|
+
@ts_repl[session_name] = ''
|
86
90
|
|
87
91
|
message = "Session name: *#{session_name}*
|
88
92
|
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`.
|
@@ -115,14 +119,15 @@ class SlackSmartBot
|
|
115
119
|
end
|
116
120
|
|
117
121
|
process_to_run = '
|
118
|
-
|
122
|
+
' + env_vars.join("\n") + '
|
119
123
|
require \"amazing_print\"
|
124
|
+
require \"stringio\"
|
120
125
|
bindme' + serialt + ' = binding
|
121
126
|
eval(\"require \'nice_http\'\" , bindme' + serialt + ')
|
122
127
|
def ls(obj)
|
123
128
|
(obj.methods - Object.methods)
|
124
129
|
end
|
125
|
-
|
130
|
+
file_run_path = \"' + + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.rb\"
|
126
131
|
file_input_repl = File.open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.input\", \"r\")
|
127
132
|
' + pre_execute + '
|
128
133
|
while true do
|
@@ -141,18 +146,40 @@ class SlackSmartBot
|
|
141
146
|
end
|
142
147
|
error = false
|
143
148
|
begin
|
144
|
-
|
149
|
+
begin
|
150
|
+
original_stdout = $stdout
|
151
|
+
$stdout = StringIO.new
|
152
|
+
resp_repl = eval(code_to_run_repl, bindme' + serialt + ')
|
153
|
+
stdout_repl = $stdout.string
|
154
|
+
ensure
|
155
|
+
$stdout = original_stdout
|
156
|
+
end
|
145
157
|
rescue Exception => resp_repl
|
146
158
|
error = true
|
147
159
|
end
|
148
|
-
|
160
|
+
if error
|
161
|
+
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
162
|
+
f.puts \"\`\`\`\n#{resp_repl.to_s.gsub(/^.+' + session_name + '\.rb:\d+:/,\"\")}\`\`\`\"
|
163
|
+
}
|
164
|
+
else
|
149
165
|
if code_to_run_repl.match?(/^\s*p\s+/i)
|
166
|
+
resp_repl = stdout_repl unless stdout_repl.to_s == \'\'
|
167
|
+
if stdout_repl.to_s == \'\'
|
168
|
+
resp_repl = resp_repl.inspect
|
169
|
+
else
|
170
|
+
resp_repl = stdout_repl
|
171
|
+
end
|
150
172
|
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
151
|
-
f.puts \"\`\`\`\n#{resp_repl
|
173
|
+
f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
|
152
174
|
}
|
153
175
|
else
|
176
|
+
if stdout_repl.to_s == \'\'
|
177
|
+
resp_repl = resp_repl.ai
|
178
|
+
else
|
179
|
+
resp_repl = stdout_repl
|
180
|
+
end
|
154
181
|
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
155
|
-
f.puts \"\`\`\`\n#{resp_repl
|
182
|
+
f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
|
156
183
|
}
|
157
184
|
end
|
158
185
|
unless !add_to_run_repl
|
@@ -163,16 +190,35 @@ class SlackSmartBot
|
|
163
190
|
end
|
164
191
|
end
|
165
192
|
end
|
166
|
-
end
|
193
|
+
end
|
167
194
|
'
|
168
195
|
unless rules_file.empty? # to get the project_folder
|
169
196
|
begin
|
170
197
|
eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
|
171
198
|
end
|
172
199
|
end
|
173
|
-
|
174
|
-
|
200
|
+
process_to_run.gsub!('\"','"')
|
201
|
+
file_run_path = "./tmp/repl/#{@channel_id}/#{session_name}.rb"
|
202
|
+
if defined?(project_folder)
|
203
|
+
Dir.mkdir("#{project_folder}/tmp/") unless Dir.exist?("#{project_folder}/tmp/")
|
204
|
+
Dir.mkdir("#{project_folder}/tmp/repl") unless Dir.exist?("#{project_folder}/tmp/repl")
|
205
|
+
Dir.mkdir("#{project_folder}/tmp/repl/#{@channel_id}/") unless Dir.exist?("#{project_folder}/tmp/repl/#{@channel_id}/")
|
206
|
+
file_run = File.open(file_run_path.gsub('./',"#{project_folder}/"), "w")
|
207
|
+
file_run.write process_to_run
|
208
|
+
file_run.close
|
209
|
+
else
|
210
|
+
Dir.mkdir("./tmp/") unless Dir.exist?("./tmp/")
|
211
|
+
Dir.mkdir("./tmp/repl") unless Dir.exist?("./tmp/repl")
|
212
|
+
Dir.mkdir("./tmp/repl/#{@channel_id}/") unless Dir.exist?("./tmp/repl/#{@channel_id}/")
|
213
|
+
file_run = File.open(file_run_path, "w")
|
214
|
+
file_run.write process_to_run
|
215
|
+
file_run.close
|
216
|
+
end
|
175
217
|
|
218
|
+
process_to_run = "ruby #{file_run_path}"
|
219
|
+
|
220
|
+
started = Time.now
|
221
|
+
process_to_run = ("cd #{project_folder} && " + process_to_run) if defined?(project_folder)
|
176
222
|
stdin, stdout, stderr, wait_thr = Open3.popen3(process_to_run)
|
177
223
|
timeout = 30 * 60 # 30 minutes
|
178
224
|
|
@@ -185,7 +231,7 @@ class SlackSmartBot
|
|
185
231
|
f.puts 'quit'
|
186
232
|
}
|
187
233
|
respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
|
188
|
-
unreact :running, @ts_react
|
234
|
+
unreact :running, @ts_react[@repl_sessions[from].name]
|
189
235
|
pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
190
236
|
pids.each do |pid|
|
191
237
|
begin
|
@@ -199,6 +245,10 @@ class SlackSmartBot
|
|
199
245
|
sleep 0.2
|
200
246
|
resp_repl = file_output_repl.read
|
201
247
|
if resp_repl.to_s!=''
|
248
|
+
if @ts_repl[@repl_sessions[from].name].to_s != ''
|
249
|
+
unreact(:running, @ts_repl[@repl_sessions[from].name])
|
250
|
+
@ts_repl[@repl_sessions[from].name] = ''
|
251
|
+
end
|
202
252
|
if resp_repl.to_s.lines.count < 60 and resp_repl.to_s.size < 3500
|
203
253
|
respond resp_repl, dest
|
204
254
|
else
|
@@ -211,6 +261,8 @@ class SlackSmartBot
|
|
211
261
|
@logger.fatal excp
|
212
262
|
end
|
213
263
|
end
|
264
|
+
elsif @repl_sessions.key?(from) and @repl_sessions[from][:command].to_s == ''
|
265
|
+
respond 'You are already in a repl on this SmartBot. You need to quit that one before starting a new one.'
|
214
266
|
else
|
215
267
|
@repl_sessions[from][:finished] = Time.now
|
216
268
|
code = @repl_sessions[from][:command]
|
@@ -224,7 +276,9 @@ class SlackSmartBot
|
|
224
276
|
code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
|
225
277
|
code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or
|
226
278
|
code.match?(/=\s*File/) or code.match?(/=\s*Dir/) or code.match?(/<\s*File/) or code.match?(/<\s*Dir/) or
|
227
|
-
code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/)
|
279
|
+
code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/) or
|
280
|
+
code.match?(/=?\s*(require|load)(\(|\s)/i)
|
281
|
+
|
228
282
|
respond "Sorry I cannot run this due security reasons", dest
|
229
283
|
else
|
230
284
|
@repl_sessions[from][:input]<<code
|
@@ -234,7 +288,7 @@ class SlackSmartBot
|
|
234
288
|
f.puts code
|
235
289
|
}
|
236
290
|
respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
|
237
|
-
unreact :running, @ts_react
|
291
|
+
unreact :running, @ts_react[@repl_sessions[from].name]
|
238
292
|
pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
239
293
|
pids.each do |pid|
|
240
294
|
begin
|
@@ -246,6 +300,10 @@ class SlackSmartBot
|
|
246
300
|
when /^\s*-/i
|
247
301
|
#ommit
|
248
302
|
else
|
303
|
+
if @ts_repl[@repl_sessions[from].name].to_s == ''
|
304
|
+
@ts_repl[@repl_sessions[from].name] = Thread.current[:ts]
|
305
|
+
react :running
|
306
|
+
end
|
249
307
|
open("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input", 'a+') {|f|
|
250
308
|
f.puts code
|
251
309
|
}
|
@@ -6,6 +6,7 @@ class SlackSmartBot
|
|
6
6
|
# help: _code puts (34344/99)*(34+14)_
|
7
7
|
# help: _ruby require 'json'; res=[]; 20.times {res<<rand(100)}; my_json={result: res}; puts my_json.to_json_
|
8
8
|
# help: <https://github.com/MarioRuiz/slack-smart-bot#running-ruby-code-on-a-conversation|more info>
|
9
|
+
# help: command_id: :ruby_code
|
9
10
|
# help:
|
10
11
|
|
11
12
|
def ruby_code(dest, user, code, rules_file)
|
@@ -2,27 +2,39 @@ class SlackSmartBot
|
|
2
2
|
# help: ----------------------------------------------
|
3
3
|
# help: `run repl SESSION_NAME`
|
4
4
|
# help: `run repl SESSION_NAME ENV_VAR=VALUE ENV_VAR=VALUE`
|
5
|
+
# help: `run repl SESSION_NAME PARAMS`
|
5
6
|
# help: `run live SESSION_NAME`
|
6
7
|
# help: `run irb SESSION_NAME`
|
7
|
-
# help: Will run the repl session specified and return the output.
|
8
|
+
# help: Will run the repl session specified and return the output.
|
8
9
|
# help: You can supply the Environmental Variables you need for the Session
|
10
|
+
# help: PARAMS: Also it is possible to supply code that will be run before the repl code on the same session.
|
9
11
|
# help: It will return only the values that were print out on the repl with puts, print, p or pp
|
10
12
|
# help: Example:
|
11
13
|
# help: _run repl CreateCustomer LOCATION=spain HOST='https://10.30.40.50:8887'_
|
12
14
|
# help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
|
15
|
+
# help: command_id: :run_repl
|
13
16
|
# help:
|
14
|
-
def run_repl(dest, user, session_name, env_vars, rules_file)
|
17
|
+
def run_repl(dest, user, session_name, env_vars, prerun, rules_file)
|
15
18
|
#todo: add tests
|
16
19
|
from = user.name
|
17
20
|
if has_access?(__method__, user)
|
18
21
|
save_stats(__method__)
|
19
22
|
Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
|
20
23
|
Dir.mkdir("#{config.path}/repl/#{@channel_id}") unless Dir.exist?("#{config.path}/repl/#{@channel_id}")
|
24
|
+
code = prerun.join("\n")
|
21
25
|
if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
|
22
|
-
if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
|
23
|
-
|
24
|
-
|
26
|
+
if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
|
27
|
+
@repls[session_name][:creator_name] != user.name and
|
28
|
+
!is_admin?(user.name)
|
25
29
|
respond "The REPL with session name: #{session_name} is private", dest
|
30
|
+
elsif !prerun.empty? and (code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File.") or
|
31
|
+
code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
|
32
|
+
code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
|
33
|
+
code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or
|
34
|
+
code.match?(/=\s*File/) or code.match?(/=\s*Dir/) or code.match?(/<\s*File/) or code.match?(/<\s*Dir/) or
|
35
|
+
code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/) or
|
36
|
+
code.match?(/=?\s*(require|load)(\(|\s)/i))
|
37
|
+
respond "Sorry I cannot run this due security reasons", dest
|
26
38
|
else
|
27
39
|
if @repls.key?(session_name) #not temp
|
28
40
|
@repls[session_name][:accessed] = Time.now.to_s
|
@@ -31,47 +43,124 @@ class SlackSmartBot
|
|
31
43
|
else
|
32
44
|
@repls[session_name][:runs_by_others] += 1
|
33
45
|
end
|
34
|
-
update_repls()
|
46
|
+
update_repls()
|
35
47
|
end
|
36
48
|
|
37
49
|
content = env_vars.join("\n")
|
38
50
|
content += "\nrequire 'nice_http'\n"
|
39
51
|
unless rules_file.empty? # to get the project_folder
|
40
52
|
begin
|
41
|
-
eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
|
53
|
+
eval(File.new(config.path + rules_file).read) if File.exist?(config.path + rules_file)
|
42
54
|
end
|
43
55
|
end
|
44
|
-
if File.exist?("#{project_folder}/.smart-bot-repl") and
|
45
|
-
|
56
|
+
if File.exist?("#{project_folder}/.smart-bot-repl") and
|
57
|
+
((@repls.key?(session_name) and @repls[session_name][:type] != :private_clean and @repls[session_name][:type] != :public_clean) or !@repls.key?(session_name))
|
46
58
|
content += File.read("#{project_folder}/.smart-bot-repl")
|
47
59
|
content += "\n"
|
48
60
|
end
|
49
|
-
|
61
|
+
unless prerun.empty?
|
62
|
+
content += prerun.join("\n")
|
63
|
+
content += "\n"
|
64
|
+
end
|
65
|
+
content += File.read("#{config.path}/repl/#{@channel_id}/#{session_name}.run").gsub(/^(quit|exit|bye)$/i, "") #todo: remove this gsub, it will never contain it
|
50
66
|
Dir.mkdir("#{project_folder}/tmp") unless Dir.exist?("#{project_folder}/tmp")
|
51
67
|
Dir.mkdir("#{project_folder}/tmp/repl") unless Dir.exist?("#{project_folder}/tmp/repl")
|
52
|
-
|
53
|
-
|
68
|
+
if Thread.current[:on_thread]
|
69
|
+
# to force stdout.each to be performed every 3 seconds
|
70
|
+
content = "Thread.new do
|
71
|
+
while true do
|
72
|
+
puts ''
|
73
|
+
sleep 3
|
74
|
+
end
|
75
|
+
end
|
76
|
+
#{content}
|
77
|
+
"
|
78
|
+
end
|
79
|
+
random = "5:LN&".gen
|
80
|
+
File.write("#{project_folder}/tmp/repl/#{session_name}_#{user.name}_#{random}.rb", content, mode: "w+")
|
81
|
+
process_to_run = "ruby ./tmp/repl/#{session_name}_#{user.name}_#{random}.rb"
|
54
82
|
process_to_run = ("cd #{project_folder} && #{process_to_run}") if defined?(project_folder)
|
55
|
-
respond "Running REPL #{session_name}"
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
83
|
+
respond "Running REPL #{session_name} (id: #{random})"
|
84
|
+
@run_repls[random] = { user: user.name, name: session_name, pid: '' }
|
85
|
+
react :running
|
86
|
+
|
87
|
+
require "pty"
|
88
|
+
timeout = 60 * 60 * 4 # 4 hours
|
89
|
+
|
90
|
+
started = Time.now
|
91
|
+
results = []
|
92
|
+
begin
|
93
|
+
PTY.spawn(process_to_run) do |stdout, stdin, pid|
|
94
|
+
last_result = -1
|
95
|
+
last_time = Time.now
|
96
|
+
@run_repls[random].pid = pid
|
97
|
+
begin
|
98
|
+
stdout.each do |line|
|
99
|
+
if (Time.now - started) > timeout
|
100
|
+
respond "run REPL session finished. Max time reached: #{session_name} (id: #{random})", dest
|
101
|
+
pids = `pgrep -P #{pid}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
102
|
+
pids.each do |pd|
|
103
|
+
begin
|
104
|
+
Process.kill("KILL", pd)
|
105
|
+
rescue
|
106
|
+
end
|
107
|
+
end
|
108
|
+
break
|
109
|
+
else
|
110
|
+
results << line
|
111
|
+
if Thread.current[:on_thread]
|
112
|
+
if (Time.now - last_time) > 2
|
113
|
+
if (results.size - last_result) < 60 and results[(last_result + 1)..-1].join.size < 3500
|
114
|
+
output = ""
|
115
|
+
results[(last_result + 1)..-1].each do |li|
|
116
|
+
if li.match?(/^\s*{.+}\s*$/) or li.match?(/^\s*\[.+\]\s*$/)
|
117
|
+
output += "```\n#{li}```\n"
|
118
|
+
else
|
119
|
+
output += li
|
120
|
+
end
|
121
|
+
end
|
122
|
+
respond output
|
123
|
+
else
|
124
|
+
send_file(dest, "", "response.rb", "", "text/plain", "ruby", content: results[(last_result + 1)..-1].join)
|
125
|
+
end
|
126
|
+
last_result = results.size - 1
|
127
|
+
last_time = Time.now
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
rescue Errno::EIO
|
133
|
+
@logger.warn "run_repl PTY Errno:EIO error"
|
134
|
+
end
|
135
|
+
if results.empty?
|
136
|
+
respond "*#{session_name}* (id: #{random}): Nothing returned."
|
63
137
|
else
|
64
|
-
|
138
|
+
if last_result != (results.size - 1)
|
139
|
+
if (results.size - last_result) < 60 and results[(last_result + 1)..-1].join.size < 3500
|
140
|
+
output = ""
|
141
|
+
results[(last_result + 1)..-1].each do |li|
|
142
|
+
if li.match?(/^\s*{.+}\s*$/) or li.match?(/^\s*\[.+\]\s*$/)
|
143
|
+
output += "```\n#{li}```\n"
|
144
|
+
else
|
145
|
+
output += li
|
146
|
+
end
|
147
|
+
end
|
148
|
+
if Thread.current[:on_thread]
|
149
|
+
respond output
|
150
|
+
else
|
151
|
+
respond "*#{session_name}* (id: #{random}):\n#{output}"
|
152
|
+
end
|
153
|
+
else
|
154
|
+
send_file(dest, "", "response.rb", "", "text/plain", "ruby", content: results[(last_result + 1)..-1].join)
|
155
|
+
end
|
156
|
+
end
|
65
157
|
end
|
66
158
|
end
|
67
|
-
|
68
|
-
|
69
|
-
respond "*#{session_name}*: #{stdout} #{stderr}"
|
70
|
-
else
|
71
|
-
send_file(dest, "", 'response.rb', "", 'text/plain', "ruby", content: (stdout.to_s+stderr.to_s))
|
72
|
-
end
|
73
|
-
|
159
|
+
rescue PTY::ChildExited
|
160
|
+
@logger.warn "run_repl PTY The child process exited!"
|
74
161
|
end
|
162
|
+
@run_repls.delete(random) if @run_repls.key?(random)
|
163
|
+
unreact :running
|
75
164
|
end
|
76
165
|
else
|
77
166
|
respond "The REPL with session name: #{session_name} doesn't exist on this Smart Bot Channel", dest
|
@@ -4,6 +4,7 @@ class SlackSmartBot
|
|
4
4
|
# help: `see irbs`
|
5
5
|
# help: It will display the repls
|
6
6
|
# help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
|
7
|
+
# help: command_id: :see_repls
|
7
8
|
# help:
|
8
9
|
def see_repls(dest, user, typem)
|
9
10
|
#todo: add tests
|
@@ -12,7 +13,7 @@ class SlackSmartBot
|
|
12
13
|
if has_access?(__method__, user)
|
13
14
|
message = ""
|
14
15
|
@repls.sort.to_h.each do |session_name, repl|
|
15
|
-
if (repl.creator_name == user.name or repl.type == :public or repl.type == :public_clean) or (
|
16
|
+
if (repl.creator_name == user.name or repl.type == :public or repl.type == :public_clean) or (is_admin?(user.name) and typem == :on_dm)
|
16
17
|
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"
|
17
18
|
end
|
18
19
|
end
|
@@ -4,6 +4,7 @@ class SlackSmartBot
|
|
4
4
|
# help: `see sc`
|
5
5
|
# help: It will display the shortcuts stored for the user and for :all
|
6
6
|
# help: <https://github.com/MarioRuiz/slack-smart-bot#shortcuts|more info>
|
7
|
+
# help: command_id: :see_shortcuts
|
7
8
|
# help:
|
8
9
|
def see_shortcuts(dest, user, typem)
|
9
10
|
save_stats(__method__)
|
@@ -28,7 +29,7 @@ class SlackSmartBot
|
|
28
29
|
end
|
29
30
|
msg2 = ''
|
30
31
|
if @shortcuts.keys.include?(from) and @shortcuts[from].keys.size > 0
|
31
|
-
new_hash = @shortcuts[from].
|
32
|
+
new_hash = @shortcuts[from].deep_copy
|
32
33
|
@shortcuts[:all].keys.each { |k| new_hash.delete(k) }
|
33
34
|
if new_hash.keys.size > 0
|
34
35
|
msg2 = "*Available shortcuts for #{from}:*\n"
|
@@ -38,7 +39,7 @@ class SlackSmartBot
|
|
38
39
|
end
|
39
40
|
end
|
40
41
|
if @shortcuts_global.keys.include?(from) and @shortcuts_global[from].keys.size > 0
|
41
|
-
new_hash = @shortcuts_global[from].
|
42
|
+
new_hash = @shortcuts_global[from].deep_copy
|
42
43
|
@shortcuts_global[:all].keys.each { |k| new_hash.delete(k) }
|
43
44
|
if new_hash.keys.size > 0
|
44
45
|
msg2 = "*Available shortcuts for #{from}:*\n" if msg2 == ''
|
@@ -4,6 +4,7 @@ class SlackSmartBot
|
|
4
4
|
# helpmaster: kills the bot on the specified channel
|
5
5
|
# helpmaster: Only works if you are on Master channel and you created that bot or you are an admin user
|
6
6
|
# helpmaster: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
|
7
|
+
# helpmaster: command_id: :kill_bot_on_channel
|
7
8
|
# helpmaster:
|
8
9
|
def kill_bot_on_channel(dest, from, channel)
|
9
10
|
save_stats(__method__)
|
@@ -20,15 +21,15 @@ class SlackSmartBot
|
|
20
21
|
respond "There is no channel with that name: #{channel}, please be sure is written exactly the same", dest
|
21
22
|
elsif @bots_created.keys.include?(channel_id)
|
22
23
|
@bots_created[channel_id] ||= {}
|
23
|
-
if @bots_created[channel_id][:admins].to_s.split(",").include?(from)
|
24
|
+
if @bots_created[channel_id][:admins].to_s.split(",").include?(from) # todo: consider adding is_admin?
|
24
25
|
if @bots_created[channel_id][:thread].kind_of?(Thread) and @bots_created[channel_id][:thread].alive?
|
25
26
|
@bots_created[channel_id][:thread].kill
|
26
27
|
end
|
27
28
|
@bots_created.delete(channel_id)
|
28
|
-
update_bots_file()
|
29
|
-
respond "Bot on channel: #{channel}, has been killed and deleted.", dest
|
30
29
|
send_msg_channel(channel, "Bot has been killed by #{from}")
|
31
|
-
|
30
|
+
respond "Bot on channel: #{channel}, has been killed and deleted.", dest
|
31
|
+
save_status :off, :killed, "The admin killed SmartBot on *##{@channels_name[channel_id]}*"
|
32
|
+
update_bots_file()
|
32
33
|
else
|
33
34
|
respond "You need to be the creator or an admin of that bot channel", dest
|
34
35
|
end
|
@@ -7,11 +7,12 @@ class SlackSmartBot
|
|
7
7
|
# helpadmin: The bot stops running and also stops all the bots created from this master channel
|
8
8
|
# helpadmin: You can use this command only if you are an admin user and you are on the master channel
|
9
9
|
# helpadmin: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
|
10
|
+
# helpadmin: command_id: :exit_bot
|
10
11
|
# helpadmin:
|
11
12
|
def exit_bot(command, from, dest, display_name)
|
12
13
|
save_stats(__method__)
|
13
14
|
if config.on_master_bot
|
14
|
-
if config.
|
15
|
+
if config.masters.include?(from) #admin user
|
15
16
|
if answer.empty?
|
16
17
|
ask("are you sure?", command, from, dest)
|
17
18
|
else
|
@@ -22,7 +23,7 @@ class SlackSmartBot
|
|
22
23
|
@bots_created.each { |key, value|
|
23
24
|
value[:thread] = ""
|
24
25
|
send_msg_channel(key, "Bot has been closed by #{from}")
|
25
|
-
save_status :off, :exited, "The admin closed SmartBot on
|
26
|
+
save_status :off, :exited, "The admin closed SmartBot on *##{value.channel_name}*"
|
26
27
|
sleep 0.5
|
27
28
|
}
|
28
29
|
update_bots_file()
|
@@ -8,11 +8,12 @@ class SlackSmartBot
|
|
8
8
|
# helpmaster: It will send a notification message to the specified channel and to its extended channels
|
9
9
|
# helpmaster: Only works if you are on Master channel and you are a master admin user
|
10
10
|
# helpmaster: <https://github.com/MarioRuiz/slack-smart-bot#sending-notifications|more info>
|
11
|
+
# helpmaster: command_id: :notify_message
|
11
12
|
# helpmaster:
|
12
13
|
def notify_message(dest, from, where, message)
|
13
14
|
save_stats(__method__)
|
14
15
|
if config.on_master_bot
|
15
|
-
if config.
|
16
|
+
if config.masters.include?(from) #admin user
|
16
17
|
if where.nil? #not all and not channel
|
17
18
|
@bots_created.each do |k, v|
|
18
19
|
respond message, k
|
@@ -2,21 +2,24 @@ class SlackSmartBot
|
|
2
2
|
# helpmaster: ----------------------------------------------
|
3
3
|
# helpmaster: `publish announcements`
|
4
4
|
# helpmaster: It will publish on all channels the announcements added by using 'add announcement' command.
|
5
|
+
# helpmaster: It won't be published if less than 11 messages published on the channel since last time this command was called.
|
5
6
|
# helpmaster: Only works if you are on Master channel and you are a master admin user
|
6
7
|
# helpmaster: The messages stored on a DM won't be published.
|
7
8
|
# helpmaster: This is very convenient to be called from a *Routine* for example every weekday at 09:00.
|
8
9
|
# helpmaster: <https://github.com/MarioRuiz/slack-smart-bot#announcements|more info>
|
10
|
+
# helpmaster: command_id: :publish_announcements
|
9
11
|
# helpmaster:
|
10
12
|
def publish_announcements(user)
|
11
13
|
save_stats(__method__)
|
12
14
|
if config.on_master_bot
|
13
|
-
if config.
|
15
|
+
if config.masters.include?(user.name) #admin user
|
14
16
|
channels = Dir.entries("#{config.path}/announcements/")
|
15
17
|
channels.select! {|i| i[/\.csv$/]}
|
16
18
|
channels.each do |channel|
|
17
19
|
channel.gsub!('.csv','')
|
18
|
-
unless channel[0]== 'D'
|
19
|
-
see_announcements(user, '', channel,
|
20
|
+
unless channel[0]== 'D' or (@announcements_activity_after.key?(channel) and @announcements_activity_after[channel] <= 10)
|
21
|
+
see_announcements(user, '', channel, false, true)
|
22
|
+
@announcements_activity_after[channel] = 0
|
20
23
|
sleep 0.5 # to avoid reach ratelimit
|
21
24
|
end
|
22
25
|
end
|
@@ -11,11 +11,12 @@ class SlackSmartBot
|
|
11
11
|
# helpmaster: _set general message `We will be on *maintenance* at *12:00*`_
|
12
12
|
# helpmaster: _set general message `:information_source: Pay attention: We will be on *maintenance* in *#{((Time.new(2021,6,18,13,30,0)-Time.now)/60).to_i} minutes*`_
|
13
13
|
# helpmaster: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
|
14
|
+
# helpmaster: command_id: :set_general_message
|
14
15
|
# helpmaster:
|
15
16
|
def set_general_message(from, status, message)
|
16
17
|
save_stats(__method__)
|
17
18
|
if config.on_master_bot
|
18
|
-
if config.
|
19
|
+
if config.masters.include?(from) #admin user
|
19
20
|
if status == 'on'
|
20
21
|
config.general_message = message
|
21
22
|
respond "General message has been set."
|
@@ -14,11 +14,12 @@ class SlackSmartBot
|
|
14
14
|
# helpmaster: _set maintenance on We are on maintenance. We'll be available again in #{((Time.new(2021,6,18,13,30,0)-Time.now)/60).to_i} minutes_
|
15
15
|
# helpmaster: _turn maintenance on `We are on *maintenance* until *12:00*`_
|
16
16
|
# helpmaster: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
|
17
|
+
# helpmaster: command_id: :set_maintenance
|
17
18
|
# helpmaster:
|
18
19
|
def set_maintenance(from, status, message)
|
19
20
|
save_stats(__method__)
|
20
21
|
if config.on_master_bot
|
21
|
-
if config.
|
22
|
+
if config.masters.include?(from) #admin user
|
22
23
|
if message == ''
|
23
24
|
config.on_maintenance_message = "Sorry I'm on maintenance so I cannot attend your request."
|
24
25
|
else
|
@@ -9,6 +9,7 @@ class SlackSmartBot
|
|
9
9
|
# helpmaster: Follow the instructions in case creating cloud bots
|
10
10
|
# helpmaster: In case 'silent' won't display the Bot initialization message on the CHANNEL_NAME
|
11
11
|
# helpmaster: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
|
12
|
+
# helpmaster: command_id: :create_bot
|
12
13
|
# helpmaster:
|
13
14
|
def create_bot(dest, user, type, channel)
|
14
15
|
cloud = type.include?('cloud')
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
# helpmaster: ----------------------------------------------
|
3
|
+
# helpmaster: `where is smartbot?`
|
4
|
+
# helpmaster: `which channels smartbot?`
|
5
|
+
# helpmaster: `where is a member smartbot?`
|
6
|
+
# helpmaster: It will list all channels where the smartbot is a member.
|
7
|
+
# helpmaster: command_id: :where_smartbot
|
8
|
+
# helpmaster:
|
9
|
+
def where_smartbot(user)
|
10
|
+
#todo: add tests
|
11
|
+
save_stats(__method__)
|
12
|
+
if has_access?(__method__, user)
|
13
|
+
channels = get_channels(bot_is_in: true)
|
14
|
+
message = []
|
15
|
+
extended = @bots_created.values.extended.flatten
|
16
|
+
channels.each do |c|
|
17
|
+
type = ''
|
18
|
+
unless c.id[0] == "D"
|
19
|
+
if @bots_created.key?(c.id)
|
20
|
+
type = '_`(SmartBot)`_'
|
21
|
+
elsif c.id == @master_bot_id
|
22
|
+
type = '_`(Master)`_'
|
23
|
+
elsif extended.include?(c.name)
|
24
|
+
@bots_created.each do |bot,values|
|
25
|
+
if values.extended.include?(c.name)
|
26
|
+
type += "_`(Extended from ##{values.channel_name})`_ "
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
if c.is_private?
|
31
|
+
message << "#{c.id}: *##{c.name}* #{type}"
|
32
|
+
else
|
33
|
+
message << "#{c.id}: *<##{c.id}>* #{type}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
message.sort!
|
38
|
+
respond "*<@#{config.nick_id}> is a member of:*\n\n#{message.join("\n")}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|