slack-smart-bot 1.10.0 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|