slack-smart-bot 1.13.1 → 1.14.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 +100 -4
- data/lib/slack/smart-bot/ai/open_ai/connect.rb +61 -0
- data/lib/slack/smart-bot/ai/open_ai/models.rb +21 -0
- data/lib/slack/smart-bot/ai/open_ai/send_gpt_chat.rb +24 -0
- data/lib/slack/smart-bot/ai/open_ai/send_image_edit.rb +23 -0
- data/lib/slack/smart-bot/ai/open_ai/send_image_generation.rb +18 -0
- data/lib/slack/smart-bot/ai/open_ai/send_image_variation.rb +22 -0
- data/lib/slack/smart-bot/ai/open_ai/whisper_transcribe.rb +21 -0
- data/lib/slack/smart-bot/ai.rb +8 -0
- data/lib/slack/smart-bot/comm/get_channel_members.rb +15 -13
- data/lib/slack/smart-bot/comm/get_channels.rb +31 -29
- data/lib/slack/smart-bot/comm/respond_thread.rb +2 -2
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat.rb +40 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_edit_image.rb +66 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_generate_image.rb +65 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_models.rb +37 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_variations_image.rb +84 -0
- data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_whisper.rb +51 -0
- data/lib/slack/smart-bot/commands/general/bot_help.rb +1 -0
- data/lib/slack/smart-bot/commands/general/personal_settings.rb +38 -0
- data/lib/slack/smart-bot/commands/general/poster.rb +107 -104
- data/lib/slack/smart-bot/commands/general/public_holidays.rb +116 -114
- data/lib/slack/smart-bot/commands/general/set_public_holidays.rb +6 -2
- data/lib/slack/smart-bot/commands/general/teams/add_team.rb +87 -0
- data/lib/slack/smart-bot/commands/general/teams/delete_team.rb +69 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team.rb +136 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team_comment.rb +37 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/delete_memo_team.rb +83 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/see_memo_team.rb +97 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/see_memos_team.rb +304 -0
- data/lib/slack/smart-bot/commands/general/teams/memos/set_memo_status.rb +66 -0
- data/lib/slack/smart-bot/commands/general/teams/ping_team.rb +104 -0
- data/lib/slack/smart-bot/commands/general/teams/see_teams.rb +236 -0
- data/lib/slack/smart-bot/commands/general/teams/see_vacations_team.rb +183 -0
- data/lib/slack/smart-bot/commands/general/teams/update_team.rb +137 -0
- data/lib/slack/smart-bot/commands/general_bot_commands.rb +905 -741
- data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +379 -353
- data/lib/slack/smart-bot/commands/on_bot/repl.rb +87 -189
- data/lib/slack/smart-bot/commands/on_bot/repl_client.rb +233 -0
- data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +17 -4
- data/lib/slack/smart-bot/commands.rb +26 -10
- data/lib/slack/smart-bot/process.rb +14 -3
- data/lib/slack/smart-bot/process_first.rb +36 -2
- data/lib/slack/smart-bot/treat_message.rb +28 -0
- data/lib/slack/smart-bot/utils/check_vacations.rb +1 -0
- data/lib/slack/smart-bot/utils/create_routine_thread.rb +1 -1
- data/lib/slack/smart-bot/utils/display_calendar.rb +17 -10
- data/lib/slack/smart-bot/utils/encryption/decrypt.rb +23 -0
- data/lib/slack/smart-bot/utils/encryption/encrypt.rb +27 -0
- data/lib/slack/smart-bot/utils/{encryption_get_key_iv.rb → encryption/encryption_get_key_iv.rb} +12 -8
- data/lib/slack/smart-bot/utils/get_help.rb +3 -1
- data/lib/slack/smart-bot/utils/get_personal_settings.rb +14 -0
- data/lib/slack/smart-bot/utils/get_teams.rb +2 -2
- data/lib/slack/smart-bot/utils/get_vacations.rb +2 -2
- data/lib/slack/smart-bot/utils/save_stats.rb +3 -1
- data/lib/slack/smart-bot/utils/update_personal_settings.rb +18 -0
- data/lib/slack/smart-bot/utils/update_teams.rb +1 -1
- data/lib/slack/smart-bot/utils/update_vacations.rb +1 -1
- data/lib/slack/smart-bot/utils.rb +5 -3
- data/lib/slack-smart-bot.rb +12 -0
- data/whats_new.txt +13 -14
- metadata +63 -15
- data/lib/slack/smart-bot/commands/general/add_memo_team.rb +0 -117
- data/lib/slack/smart-bot/commands/general/add_team.rb +0 -81
- data/lib/slack/smart-bot/commands/general/delete_memo_team.rb +0 -69
- data/lib/slack/smart-bot/commands/general/delete_team.rb +0 -55
- data/lib/slack/smart-bot/commands/general/ping_team.rb +0 -100
- data/lib/slack/smart-bot/commands/general/see_memos_team.rb +0 -202
- data/lib/slack/smart-bot/commands/general/see_teams.rb +0 -230
- data/lib/slack/smart-bot/commands/general/see_vacations_team.rb +0 -136
- data/lib/slack/smart-bot/commands/general/set_memo_status.rb +0 -58
- data/lib/slack/smart-bot/commands/general/update_team.rb +0 -131
- data/lib/slack/smart-bot/utils/decrypt.rb +0 -15
- data/lib/slack/smart-bot/utils/encrypt.rb +0 -15
@@ -10,7 +10,7 @@ class SlackSmartBot
|
|
10
10
|
# help: `repl SESSION_NAME ENV_VAR=VALUE ENV_VAR='VALUE'`
|
11
11
|
# help: `repl SESSION_NAME: "DESCRIPTION"`
|
12
12
|
# help: `repl SESSION_NAME: "DESCRIPTION" ENV_VAR=VALUE ENV_VAR='VALUE'`
|
13
|
-
# help: Will run all we write as a ruby command and will keep the session values.
|
13
|
+
# help: Will run all we write as a ruby command and will keep the session values.
|
14
14
|
# help: SESSION_NAME only admits from a to Z, numbers, - and _
|
15
15
|
# help: If no SESSION_NAME supplied it will be treated as a temporary REPL
|
16
16
|
# help: If 'private' specified the repl will be accessible only by you and it will be displayed only to you when `see repls`
|
@@ -19,10 +19,12 @@ class SlackSmartBot
|
|
19
19
|
# help: Send _quit_, _bye_ or _exit_ to finish the session.
|
20
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
|
-
# 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.
|
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
|
24
24
|
# help: To pre-execute some ruby when starting the session add the code to .smart-bot-repl file on the project root folder defined on project_folder
|
25
25
|
# help: If you want to see the methods of a class or module you created use _ls TheModuleOrClass_
|
26
|
+
# help: To see the code of a method: _code TheModuleOrClass.my_method_
|
27
|
+
# help: To see the documentation of a method: _doc TheModuleOrClass.my_method_
|
26
28
|
# help: You can supply the Environmental Variables you need for the Session
|
27
29
|
# help: You can add collaborators by sending _add collaborator @USER_ to the session.
|
28
30
|
# help: Examples:
|
@@ -41,9 +43,9 @@ class SlackSmartBot
|
|
41
43
|
save_stats(__method__)
|
42
44
|
Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
|
43
45
|
Dir.mkdir("#{config.path}/repl/#{@channel_id}") unless Dir.exist?("#{config.path}/repl/#{@channel_id}")
|
44
|
-
|
46
|
+
|
45
47
|
serialt = Time.now.strftime("%Y%m%d%H%M%S%N")
|
46
|
-
if session_name.to_s==
|
48
|
+
if session_name.to_s == ""
|
47
49
|
session_name = "#{from}_#{serialt}"
|
48
50
|
temp_repl = true
|
49
51
|
else
|
@@ -51,7 +53,7 @@ class SlackSmartBot
|
|
51
53
|
i = 0
|
52
54
|
name = session_name
|
53
55
|
while File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.input")
|
54
|
-
i+=1
|
56
|
+
i += 1
|
55
57
|
session_name = "#{name}#{i}"
|
56
58
|
end
|
57
59
|
end
|
@@ -65,150 +67,46 @@ class SlackSmartBot
|
|
65
67
|
thread_ts: Thread.current[:thread_ts],
|
66
68
|
collaborators: [],
|
67
69
|
user_type: :creator,
|
68
|
-
user_creator: from
|
70
|
+
user_creator: from,
|
69
71
|
}
|
70
72
|
|
71
73
|
unless temp_repl
|
72
74
|
@repls[session_name] = {
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
75
|
+
created: @repl_sessions[from][:started].to_s,
|
76
|
+
accessed: @repl_sessions[from][:started].to_s,
|
77
|
+
creator_name: user.name,
|
78
|
+
creator_id: user.id,
|
79
|
+
description: description,
|
80
|
+
type: type,
|
81
|
+
runs_by_creator: 0,
|
82
|
+
runs_by_others: 0,
|
83
|
+
gets: 0,
|
82
84
|
}
|
83
|
-
update_repls()
|
85
|
+
update_repls()
|
84
86
|
end
|
85
87
|
react :running
|
86
88
|
@ts_react ||= {}
|
87
|
-
if Thread.current[:ts].to_s ==
|
89
|
+
if Thread.current[:ts].to_s == ""
|
88
90
|
@ts_react[session_name] = Thread.current[:thread_ts]
|
89
91
|
else
|
90
92
|
@ts_react[session_name] = Thread.current[:ts]
|
91
|
-
end
|
92
|
-
@ts_repl ||= {}
|
93
|
-
@ts_repl[session_name] = ''
|
94
|
-
|
95
|
-
message = "Session name: *#{session_name}*
|
96
|
-
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`.
|
97
|
-
In case you need someone to help you with the session you can add collaborators by sending `add collaborator @USER` to the session.
|
98
|
-
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`.
|
99
|
-
Use `p` to print a message raw, exacly like it is returned.
|
100
|
-
If you want to avoid a message to be treated by me, start the message with '-'.
|
101
|
-
After 30 minutes of no communication with the Smart Bot the session will be dismissed.
|
102
|
-
If you want to see the methods of a class or module you created use _ls TheModuleOrClass_
|
103
|
-
You can supply the Environmental Variables you need for the Session
|
104
|
-
Example:
|
105
|
-
_repl CreateCustomer LOCATION=spain HOST='https://10.30.40.50:8887'_
|
106
|
-
"
|
107
|
-
respond message, dest
|
108
|
-
|
109
|
-
File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input", "", mode: "a+")
|
110
|
-
File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.output", "", mode: "a+")
|
111
|
-
File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.run", "", mode: "a+")
|
112
|
-
|
113
|
-
if type != :private_clean and type != :public_clean
|
114
|
-
pre_execute = '
|
115
|
-
if File.exist?(\"./.smart-bot-repl\")
|
116
|
-
begin
|
117
|
-
eval(File.read(\"./.smart-bot-repl\"), bindme' + serialt + ')
|
118
|
-
rescue Exception => resp_repl
|
119
|
-
end
|
120
|
-
end
|
121
|
-
'
|
122
|
-
else
|
123
|
-
pre_execute = ''
|
124
93
|
end
|
94
|
+
@ts_repl ||= {}
|
95
|
+
@ts_repl[session_name] = ""
|
96
|
+
process_to_run = repl_client(from, session_name, type, serialt, env_vars)
|
125
97
|
|
126
|
-
process_to_run = '
|
127
|
-
' + env_vars.join("\n") + '
|
128
|
-
require \"amazing_print\"
|
129
|
-
require \"stringio\"
|
130
|
-
bindme' + serialt + ' = binding
|
131
|
-
eval(\"require \'nice_http\'\" , bindme' + serialt + ')
|
132
|
-
def ls(obj)
|
133
|
-
(obj.methods - Object.methods)
|
134
|
-
end
|
135
|
-
file_run_path = \"' + + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.rb\"
|
136
|
-
file_input_repl = File.open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.input\", \"r\")
|
137
|
-
' + pre_execute + '
|
138
|
-
while true do
|
139
|
-
sleep 0.2
|
140
|
-
code_to_run_repl = file_input_repl.read
|
141
|
-
if code_to_run_repl.to_s!=\"\"
|
142
|
-
add_to_run_repl = true
|
143
|
-
if code_to_run_repl.to_s.match?(/^quit$/i) or
|
144
|
-
code_to_run_repl.to_s.match?(/^exit$/i) or
|
145
|
-
code_to_run_repl.to_s.match?(/^bye bot$/i) or
|
146
|
-
code_to_run_repl.to_s.match?(/^bye$/i)
|
147
|
-
exit
|
148
|
-
else
|
149
|
-
if code_to_run_repl.match?(/^\s*ls\s+(.+)/)
|
150
|
-
add_to_run_repl = false
|
151
|
-
end
|
152
|
-
error = false
|
153
|
-
begin
|
154
|
-
begin
|
155
|
-
original_stdout = $stdout
|
156
|
-
$stdout = StringIO.new
|
157
|
-
resp_repl = eval(code_to_run_repl, bindme' + serialt + ')
|
158
|
-
stdout_repl = $stdout.string
|
159
|
-
ensure
|
160
|
-
$stdout = original_stdout
|
161
|
-
end
|
162
|
-
rescue Exception => resp_repl
|
163
|
-
error = true
|
164
|
-
end
|
165
|
-
if error
|
166
|
-
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
167
|
-
f.puts \"\`\`\`\n#{resp_repl.to_s.gsub(/^.+' + session_name + '\.rb:\d+:/,\"\")}\`\`\`\"
|
168
|
-
}
|
169
|
-
else
|
170
|
-
if code_to_run_repl.match?(/^\s*p\s+/i)
|
171
|
-
resp_repl = stdout_repl unless stdout_repl.to_s == \'\'
|
172
|
-
if stdout_repl.to_s == \'\'
|
173
|
-
resp_repl = resp_repl.inspect
|
174
|
-
else
|
175
|
-
resp_repl = stdout_repl
|
176
|
-
end
|
177
|
-
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
178
|
-
f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
|
179
|
-
}
|
180
|
-
else
|
181
|
-
if stdout_repl.to_s == \'\'
|
182
|
-
resp_repl = resp_repl.ai
|
183
|
-
else
|
184
|
-
resp_repl = stdout_repl
|
185
|
-
end
|
186
|
-
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
187
|
-
f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
|
188
|
-
}
|
189
|
-
end
|
190
|
-
unless !add_to_run_repl
|
191
|
-
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.run\", \"a+\") {|f|
|
192
|
-
f.puts code_to_run_repl
|
193
|
-
}
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
'
|
200
98
|
unless rules_file.empty? # to get the project_folder
|
201
99
|
begin
|
202
|
-
eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
|
100
|
+
eval(File.new(config.path + rules_file).read) if File.exist?(config.path + rules_file)
|
203
101
|
end
|
204
102
|
end
|
205
|
-
|
103
|
+
|
206
104
|
file_run_path = "./tmp/repl/#{@channel_id}/#{session_name}.rb"
|
207
105
|
if defined?(project_folder)
|
208
106
|
Dir.mkdir("#{project_folder}/tmp/") unless Dir.exist?("#{project_folder}/tmp/")
|
209
107
|
Dir.mkdir("#{project_folder}/tmp/repl") unless Dir.exist?("#{project_folder}/tmp/repl")
|
210
108
|
Dir.mkdir("#{project_folder}/tmp/repl/#{@channel_id}/") unless Dir.exist?("#{project_folder}/tmp/repl/#{@channel_id}/")
|
211
|
-
file_run = File.open(file_run_path.gsub(
|
109
|
+
file_run = File.open(file_run_path.gsub("./", "#{project_folder}/"), "w")
|
212
110
|
file_run.write process_to_run
|
213
111
|
file_run.close
|
214
112
|
else
|
@@ -226,87 +124,89 @@ class SlackSmartBot
|
|
226
124
|
process_to_run = ("cd #{project_folder} && " + process_to_run) if defined?(project_folder)
|
227
125
|
stdin, stdout, stderr, wait_thr = Open3.popen3(process_to_run)
|
228
126
|
timeout = 30 * 60 # 30 minutes
|
229
|
-
|
127
|
+
|
230
128
|
file_output_repl = File.open("#{config.path}/repl/#{@channel_id}/#{session_name}.output", "r")
|
231
129
|
@repl_sessions[from][:pid] = wait_thr.pid
|
232
|
-
while (wait_thr.status ==
|
130
|
+
while (wait_thr.status == "run" or wait_thr.status == "sleep") and @repl_sessions.key?(from)
|
233
131
|
begin
|
234
|
-
if (Time.now
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
end
|
132
|
+
if (Time.now - @repl_sessions[from][:finished]) > timeout
|
133
|
+
open("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input", "a+") { |f|
|
134
|
+
f.puts "quit"
|
135
|
+
}
|
136
|
+
respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
|
137
|
+
unreact :running, @ts_react[@repl_sessions[from].name]
|
138
|
+
pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
139
|
+
pids.each do |pid|
|
140
|
+
begin
|
141
|
+
Process.kill("KILL", pid)
|
142
|
+
rescue
|
246
143
|
end
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
144
|
+
end
|
145
|
+
@repl_sessions[from][:collaborators].each do |collaborator|
|
146
|
+
@repl_sessions.delete(collaborator)
|
147
|
+
end
|
148
|
+
@repl_sessions.delete(from)
|
149
|
+
break
|
252
150
|
end
|
253
151
|
sleep 0.2
|
254
152
|
resp_repl = file_output_repl.read
|
255
|
-
if resp_repl.to_s!=
|
256
|
-
if @ts_repl[@repl_sessions[from].name].to_s !=
|
257
|
-
unreact(:running, @ts_repl[@repl_sessions[from].name])
|
258
|
-
@ts_repl[@repl_sessions[from].name] =
|
153
|
+
if resp_repl.to_s != ""
|
154
|
+
if @ts_repl[@repl_sessions[from].name].to_s != ""
|
155
|
+
unreact(:running, @ts_repl[@repl_sessions[from].name])
|
156
|
+
@ts_repl[@repl_sessions[from].name] = ""
|
259
157
|
end
|
260
|
-
if resp_repl.to_s.lines.count < 60 and resp_repl.to_s.size < 3500
|
158
|
+
if (resp_repl.to_s.lines.count < 60 and resp_repl.to_s.size < 3500) or
|
159
|
+
resp_repl.match?(/^\s*[_\*]*`\w+`/im)
|
261
160
|
respond resp_repl, dest
|
262
161
|
else
|
263
|
-
resp_repl.gsub!(/^\s*```/,
|
264
|
-
resp_repl.gsub!(/```\s*$/,
|
265
|
-
send_file(dest, "",
|
162
|
+
resp_repl.gsub!(/^\s*```/, "")
|
163
|
+
resp_repl.gsub!(/```\s*$/, "")
|
164
|
+
send_file(dest, "", "response.rb", "", "text/plain", "ruby", content: resp_repl)
|
266
165
|
end
|
267
166
|
end
|
268
167
|
rescue Exception => excp
|
269
168
|
@logger.fatal excp
|
270
169
|
end
|
271
170
|
end
|
272
|
-
elsif @repl_sessions.key?(from) and @repl_sessions[from][:command].to_s ==
|
273
|
-
respond
|
171
|
+
elsif @repl_sessions.key?(from) and @repl_sessions[from][:command].to_s == ""
|
172
|
+
respond "You are already in a repl on this SmartBot. You need to quit that one before starting a new one."
|
274
173
|
else
|
275
174
|
@repl_sessions[from][:finished] = Time.now
|
276
175
|
code = @repl_sessions[from][:command]
|
277
|
-
@repl_sessions[from][:command] =
|
176
|
+
@repl_sessions[from][:command] = ""
|
278
177
|
code.gsub!("\\n", "\n")
|
279
178
|
code.gsub!("\\r", "\r")
|
280
179
|
# Disabled for the moment since it is deleting lines with '}'
|
281
180
|
#code.gsub!(/^\W*$/, "") #to remove special chars from slack when copy/pasting.
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
181
|
+
if code.match?(/\A\s*-/i)
|
182
|
+
# don't treat
|
183
|
+
elsif code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File.") or
|
184
|
+
code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
|
185
|
+
code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
|
186
|
+
code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or
|
187
|
+
code.match?(/=\s*File/) or code.match?(/=\s*Dir/) or code.match?(/<\s*File/) or code.match?(/<\s*Dir/) or
|
188
|
+
code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/) or
|
189
|
+
code.match?(/=?\s*(require|load)(\(|\s)/i)
|
290
190
|
respond "Sorry I cannot run this due security reasons", dest
|
291
191
|
elsif code.match(/\A\s*add\s+collaborator\s+<@(\w+)>\s*\z/i)
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
192
|
+
collaborator = $1
|
193
|
+
user_info = @users.select { |u| u.id == collaborator or (u.key?(:enterprise_user) and u.enterprise_user.id == collaborator) }[-1]
|
194
|
+
collaborator_name = user_info.name
|
195
|
+
if @repl_sessions.key?(collaborator_name)
|
196
|
+
respond "Sorry, <@#{collaborator}> is already in a repl. Please ask her/him to quit it first.", dest
|
197
|
+
else
|
198
|
+
respond "Collaborator added. Now <@#{collaborator}> can interact with this repl.", dest
|
199
|
+
creator = @repl_sessions[from][:user_creator]
|
200
|
+
@repl_sessions[creator][:collaborators] << collaborator_name
|
201
|
+
@repl_sessions[collaborator_name] = {
|
202
|
+
name: @repl_sessions[from][:name],
|
203
|
+
dest: dest,
|
204
|
+
on_thread: Thread.current[:on_thread],
|
205
|
+
thread_ts: Thread.current[:thread_ts],
|
206
|
+
user_type: :collaborator,
|
207
|
+
user_creator: creator,
|
208
|
+
}
|
209
|
+
end
|
310
210
|
else
|
311
211
|
if @repl_sessions[from][:user_type] == :collaborator
|
312
212
|
@repl_sessions[@repl_sessions[from][:user_creator]][:input] << code
|
@@ -320,7 +220,7 @@ class SlackSmartBot
|
|
320
220
|
@repl_sessions[@repl_sessions[from][:user_creator]][:collaborators].delete(from)
|
321
221
|
@repl_sessions.delete(from)
|
322
222
|
else
|
323
|
-
open("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input",
|
223
|
+
open("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input", "a+") { |f|
|
324
224
|
f.puts code
|
325
225
|
}
|
326
226
|
respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
|
@@ -337,14 +237,12 @@ class SlackSmartBot
|
|
337
237
|
end
|
338
238
|
@repl_sessions.delete(from)
|
339
239
|
end
|
340
|
-
when /\A\s*-/i
|
341
|
-
#ommit
|
342
240
|
else
|
343
|
-
if @ts_repl[@repl_sessions[from].name].to_s ==
|
241
|
+
if @ts_repl[@repl_sessions[from].name].to_s == ""
|
344
242
|
@ts_repl[@repl_sessions[from].name] = Thread.current[:ts]
|
345
|
-
react :running
|
243
|
+
react :running
|
346
244
|
end
|
347
|
-
open("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input",
|
245
|
+
open("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input", "a+") { |f|
|
348
246
|
f.puts code
|
349
247
|
}
|
350
248
|
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
def repl_client(from, session_name, type, serialt, env_vars)
|
3
|
+
message = "Session name: *#{session_name}*
|
4
|
+
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`.
|
5
|
+
In case you need someone to help you with the session you can add collaborators by sending `add collaborator @USER` to the session.
|
6
|
+
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`.
|
7
|
+
Use `p` to print a message raw, exacly like it is returned.
|
8
|
+
If you want to avoid a message to be treated by me, start the message with '-'.
|
9
|
+
After 30 minutes of no communication with the Smart Bot the session will be dismissed.
|
10
|
+
If you want to see the methods of a class or module you created use _ls TheModuleOrClass_
|
11
|
+
To see the code of a method: _code TheModuleOrClass.my_method_. To see the documentation of a method: _doc TheModuleOrClass.my_method_
|
12
|
+
You can supply the Environmental Variables you need for the Session
|
13
|
+
Example:
|
14
|
+
_repl CreateCustomer LOCATION=spain HOST='https://10.30.40.50:8887'_
|
15
|
+
"
|
16
|
+
respond message
|
17
|
+
|
18
|
+
File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input", "", mode: "a+")
|
19
|
+
File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.output", "", mode: "a+")
|
20
|
+
File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.run", "", mode: "a+")
|
21
|
+
|
22
|
+
if type != :private_clean and type != :public_clean
|
23
|
+
pre_execute = '
|
24
|
+
if File.exist?(\"./.smart-bot-repl\")
|
25
|
+
begin
|
26
|
+
eval(File.read(\"./.smart-bot-repl\"), bindme' + serialt + ")
|
27
|
+
rescue Exception => resp_repl
|
28
|
+
end
|
29
|
+
end
|
30
|
+
"
|
31
|
+
else
|
32
|
+
pre_execute = ""
|
33
|
+
end
|
34
|
+
|
35
|
+
process_to_run = "
|
36
|
+
" + env_vars.join("\n") + '
|
37
|
+
require \"amazing_print\"
|
38
|
+
require \"stringio\"
|
39
|
+
require \"method_source\"
|
40
|
+
bindme' + serialt + ' = binding
|
41
|
+
eval(\"require \'nice_http\'\" , bindme' + serialt + ')
|
42
|
+
def get_met_params(obj, m=nil)
|
43
|
+
result = ""
|
44
|
+
if m.nil?
|
45
|
+
met = obj
|
46
|
+
else
|
47
|
+
met = obj.method(m)
|
48
|
+
end
|
49
|
+
met.source.split("\n").each { |line|
|
50
|
+
line.gsub!("def", "")
|
51
|
+
line.gsub!("self.", "")
|
52
|
+
line.strip!
|
53
|
+
line.gsub!(/\A(\w+)\(/, \'*`\1`* (\')
|
54
|
+
line.gsub!(/\A(\w+)$/, \'*`\1`*\')
|
55
|
+
if line.strip[-1] == ")" or (result.empty? and !line.include?("("))
|
56
|
+
line.gsub!(/\A(\w+)\s/, \'*`\1`* \') if !line.include?("(")
|
57
|
+
result << "#{line}"
|
58
|
+
result << " ..." if !line.include?("(") and !line.include?(")")
|
59
|
+
result << "\n"
|
60
|
+
break
|
61
|
+
else
|
62
|
+
result << "#{line}\n"
|
63
|
+
end
|
64
|
+
}
|
65
|
+
result
|
66
|
+
end
|
67
|
+
def ls(obj)
|
68
|
+
result = ""
|
69
|
+
(obj.methods - Object.methods).sort.each do |m|
|
70
|
+
result << get_met_params(obj, m)
|
71
|
+
result << "\n"
|
72
|
+
end
|
73
|
+
puts result
|
74
|
+
end
|
75
|
+
def get_object(obj_txt)
|
76
|
+
met = obj_txt.scan(/\.(\w+)/).flatten.last
|
77
|
+
|
78
|
+
if met.nil? and obj_txt[0].match(/[A-Z]/)
|
79
|
+
obj = Object
|
80
|
+
obj_txt.split("::").each do |cl|
|
81
|
+
if obj.const_defined?(cl.to_sym)
|
82
|
+
obj = obj.const_get(cl)
|
83
|
+
else
|
84
|
+
obj = nil
|
85
|
+
break
|
86
|
+
end
|
87
|
+
end
|
88
|
+
elsif met.nil? and obj_txt[0].match(/[a-z]/)
|
89
|
+
begin
|
90
|
+
obj = self.method(obj_txt)
|
91
|
+
rescue
|
92
|
+
obj = nil
|
93
|
+
end
|
94
|
+
else
|
95
|
+
cl = obj_txt.scan(/([\w\:]+)\./).flatten.first
|
96
|
+
obj = Object
|
97
|
+
cl.split("::").each do |cl|
|
98
|
+
if obj.const_defined?(cl.to_sym)
|
99
|
+
obj = obj.const_get(cl)
|
100
|
+
else
|
101
|
+
obj = nil
|
102
|
+
break
|
103
|
+
end
|
104
|
+
end
|
105
|
+
unless obj.nil?
|
106
|
+
if obj.respond_to?(met)
|
107
|
+
obj = obj.method(met)
|
108
|
+
elsif obj.instance_method(met)
|
109
|
+
obj = obj.instance_method(met)
|
110
|
+
else
|
111
|
+
obj = nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def doc(obj_txt)
|
119
|
+
obj = get_object(obj_txt)
|
120
|
+
if !obj.nil? and obj.respond_to?(:source_location) and obj.respond_to?(:comment) and
|
121
|
+
!obj.source_location.nil? and !obj.comment.nil?
|
122
|
+
result = "_*#{obj.source_location.join(":").gsub(Dir.pwd,"").gsub(Dir.home,"")}*_\n\n"
|
123
|
+
comment = obj.comment.gsub(/^\s*\#/, "")
|
124
|
+
comment.gsub!(/^\s*#+\s*(\R|$)/, "")
|
125
|
+
comment.gsub!(/^\s(\w)([\w\s\-]*):/i, \'*\1\2*:\')
|
126
|
+
comment.gsub!(/^(\s+)(\w[\w\s\-]*):/i, \'\1*`\2`*:\')
|
127
|
+
result << "#{comment}"
|
128
|
+
result << "\n\n"
|
129
|
+
result << get_met_params(obj)
|
130
|
+
else
|
131
|
+
result = "No documentation found for #{obj_txt}. The object doesn\'t exist or it is not accessible."
|
132
|
+
end
|
133
|
+
puts result
|
134
|
+
end
|
135
|
+
|
136
|
+
def source(obj_txt)
|
137
|
+
obj = get_object(obj_txt)
|
138
|
+
if !obj.nil? and obj.respond_to?(:source_location) and obj.respond_to?(:comment) and
|
139
|
+
!obj.source_location.nil? and !obj.comment.nil?
|
140
|
+
result = "# #{obj.source_location.join(":").gsub(Dir.pwd,"").gsub(Dir.home,"")}\n\n"
|
141
|
+
result << "#{obj.source}"
|
142
|
+
else
|
143
|
+
result = "No source code found for #{obj_txt}. The object doesn\'t exist or it is not accessible."
|
144
|
+
end
|
145
|
+
puts result
|
146
|
+
end
|
147
|
+
|
148
|
+
def code(obj_txt)
|
149
|
+
source(obj_txt)
|
150
|
+
end
|
151
|
+
|
152
|
+
file_run_path = \"' + +File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.rb\"
|
153
|
+
file_input_repl = File.open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.input\", \"r\")
|
154
|
+
' + pre_execute + '
|
155
|
+
while true do
|
156
|
+
sleep 0.2
|
157
|
+
code_to_run_repl = file_input_repl.read
|
158
|
+
if code_to_run_repl.to_s!=\"\"
|
159
|
+
add_to_run_repl = true
|
160
|
+
if code_to_run_repl.to_s.match?(/^quit$/i) or
|
161
|
+
code_to_run_repl.to_s.match?(/^exit$/i) or
|
162
|
+
code_to_run_repl.to_s.match?(/^bye bot$/i) or
|
163
|
+
code_to_run_repl.to_s.match?(/^bye$/i)
|
164
|
+
exit
|
165
|
+
else
|
166
|
+
error = false
|
167
|
+
as_it_is = false
|
168
|
+
begin
|
169
|
+
if code_to_run_repl.match?(/^\s*ls\s+(.+)/)
|
170
|
+
add_to_run_repl = false
|
171
|
+
as_it_is = true
|
172
|
+
elsif code_to_run_repl.match(/^\s*doc\s+(.+)/)
|
173
|
+
add_to_run_repl = false
|
174
|
+
code_to_run_repl = \"doc \\\"#{$1}\\\"\"
|
175
|
+
as_it_is = true
|
176
|
+
elsif code_to_run_repl.match(/^\s*(code|source|src)\s+(.+)/)
|
177
|
+
add_to_run_repl = false
|
178
|
+
code_to_run_repl = \"source \\\"#{$2}\\\"\"
|
179
|
+
end
|
180
|
+
begin
|
181
|
+
original_stdout = $stdout
|
182
|
+
$stdout = StringIO.new
|
183
|
+
resp_repl = eval(code_to_run_repl, bindme' + serialt + ')
|
184
|
+
stdout_repl = $stdout.string
|
185
|
+
ensure
|
186
|
+
$stdout = original_stdout
|
187
|
+
end
|
188
|
+
rescue Exception => resp_repl
|
189
|
+
error = true
|
190
|
+
end
|
191
|
+
if error
|
192
|
+
open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.output\", \"a+\") {|f|
|
193
|
+
f.puts \"\`\`\`\n#{resp_repl.to_s.gsub(/^.+' + session_name + '\.rb:\d+:/,\"\")}\`\`\`\"
|
194
|
+
}
|
195
|
+
else
|
196
|
+
if code_to_run_repl.match?(/^\s*p\s+/i)
|
197
|
+
resp_repl = stdout_repl unless stdout_repl.to_s == \'\'
|
198
|
+
if stdout_repl.to_s == \'\'
|
199
|
+
resp_repl = resp_repl.inspect
|
200
|
+
else
|
201
|
+
resp_repl = stdout_repl
|
202
|
+
end
|
203
|
+
open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.output\", \"a+\") {|f|
|
204
|
+
f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
|
205
|
+
}
|
206
|
+
else
|
207
|
+
if stdout_repl.to_s == \'\'
|
208
|
+
resp_repl = resp_repl.ai
|
209
|
+
else
|
210
|
+
resp_repl = stdout_repl
|
211
|
+
end
|
212
|
+
open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.output\", \"a+\") {|f|
|
213
|
+
if as_it_is
|
214
|
+
f.puts resp_repl
|
215
|
+
else
|
216
|
+
f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
|
217
|
+
end
|
218
|
+
}
|
219
|
+
end
|
220
|
+
unless !add_to_run_repl
|
221
|
+
open(\"' + File.expand_path(config.path) + "/repl/" + @channel_id + "/" + session_name + '.run\", \"a+\") {|f|
|
222
|
+
f.puts code_to_run_repl
|
223
|
+
}
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
'
|
230
|
+
process_to_run.gsub!('\"', '"')
|
231
|
+
return process_to_run
|
232
|
+
end
|
233
|
+
end
|