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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +100 -4
  3. data/lib/slack/smart-bot/ai/open_ai/connect.rb +61 -0
  4. data/lib/slack/smart-bot/ai/open_ai/models.rb +21 -0
  5. data/lib/slack/smart-bot/ai/open_ai/send_gpt_chat.rb +24 -0
  6. data/lib/slack/smart-bot/ai/open_ai/send_image_edit.rb +23 -0
  7. data/lib/slack/smart-bot/ai/open_ai/send_image_generation.rb +18 -0
  8. data/lib/slack/smart-bot/ai/open_ai/send_image_variation.rb +22 -0
  9. data/lib/slack/smart-bot/ai/open_ai/whisper_transcribe.rb +21 -0
  10. data/lib/slack/smart-bot/ai.rb +8 -0
  11. data/lib/slack/smart-bot/comm/get_channel_members.rb +15 -13
  12. data/lib/slack/smart-bot/comm/get_channels.rb +31 -29
  13. data/lib/slack/smart-bot/comm/respond_thread.rb +2 -2
  14. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat.rb +40 -0
  15. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_edit_image.rb +66 -0
  16. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_generate_image.rb +65 -0
  17. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_models.rb +37 -0
  18. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_variations_image.rb +84 -0
  19. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_whisper.rb +51 -0
  20. data/lib/slack/smart-bot/commands/general/bot_help.rb +1 -0
  21. data/lib/slack/smart-bot/commands/general/personal_settings.rb +38 -0
  22. data/lib/slack/smart-bot/commands/general/poster.rb +107 -104
  23. data/lib/slack/smart-bot/commands/general/public_holidays.rb +116 -114
  24. data/lib/slack/smart-bot/commands/general/set_public_holidays.rb +6 -2
  25. data/lib/slack/smart-bot/commands/general/teams/add_team.rb +87 -0
  26. data/lib/slack/smart-bot/commands/general/teams/delete_team.rb +69 -0
  27. data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team.rb +136 -0
  28. data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team_comment.rb +37 -0
  29. data/lib/slack/smart-bot/commands/general/teams/memos/delete_memo_team.rb +83 -0
  30. data/lib/slack/smart-bot/commands/general/teams/memos/see_memo_team.rb +97 -0
  31. data/lib/slack/smart-bot/commands/general/teams/memos/see_memos_team.rb +304 -0
  32. data/lib/slack/smart-bot/commands/general/teams/memos/set_memo_status.rb +66 -0
  33. data/lib/slack/smart-bot/commands/general/teams/ping_team.rb +104 -0
  34. data/lib/slack/smart-bot/commands/general/teams/see_teams.rb +236 -0
  35. data/lib/slack/smart-bot/commands/general/teams/see_vacations_team.rb +183 -0
  36. data/lib/slack/smart-bot/commands/general/teams/update_team.rb +137 -0
  37. data/lib/slack/smart-bot/commands/general_bot_commands.rb +905 -741
  38. data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +379 -353
  39. data/lib/slack/smart-bot/commands/on_bot/repl.rb +87 -189
  40. data/lib/slack/smart-bot/commands/on_bot/repl_client.rb +233 -0
  41. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +17 -4
  42. data/lib/slack/smart-bot/commands.rb +26 -10
  43. data/lib/slack/smart-bot/process.rb +14 -3
  44. data/lib/slack/smart-bot/process_first.rb +36 -2
  45. data/lib/slack/smart-bot/treat_message.rb +28 -0
  46. data/lib/slack/smart-bot/utils/check_vacations.rb +1 -0
  47. data/lib/slack/smart-bot/utils/create_routine_thread.rb +1 -1
  48. data/lib/slack/smart-bot/utils/display_calendar.rb +17 -10
  49. data/lib/slack/smart-bot/utils/encryption/decrypt.rb +23 -0
  50. data/lib/slack/smart-bot/utils/encryption/encrypt.rb +27 -0
  51. data/lib/slack/smart-bot/utils/{encryption_get_key_iv.rb → encryption/encryption_get_key_iv.rb} +12 -8
  52. data/lib/slack/smart-bot/utils/get_help.rb +3 -1
  53. data/lib/slack/smart-bot/utils/get_personal_settings.rb +14 -0
  54. data/lib/slack/smart-bot/utils/get_teams.rb +2 -2
  55. data/lib/slack/smart-bot/utils/get_vacations.rb +2 -2
  56. data/lib/slack/smart-bot/utils/save_stats.rb +3 -1
  57. data/lib/slack/smart-bot/utils/update_personal_settings.rb +18 -0
  58. data/lib/slack/smart-bot/utils/update_teams.rb +1 -1
  59. data/lib/slack/smart-bot/utils/update_vacations.rb +1 -1
  60. data/lib/slack/smart-bot/utils.rb +5 -3
  61. data/lib/slack-smart-bot.rb +12 -0
  62. data/whats_new.txt +13 -14
  63. metadata +63 -15
  64. data/lib/slack/smart-bot/commands/general/add_memo_team.rb +0 -117
  65. data/lib/slack/smart-bot/commands/general/add_team.rb +0 -81
  66. data/lib/slack/smart-bot/commands/general/delete_memo_team.rb +0 -69
  67. data/lib/slack/smart-bot/commands/general/delete_team.rb +0 -55
  68. data/lib/slack/smart-bot/commands/general/ping_team.rb +0 -100
  69. data/lib/slack/smart-bot/commands/general/see_memos_team.rb +0 -202
  70. data/lib/slack/smart-bot/commands/general/see_teams.rb +0 -230
  71. data/lib/slack/smart-bot/commands/general/see_vacations_team.rb +0 -136
  72. data/lib/slack/smart-bot/commands/general/set_memo_status.rb +0 -58
  73. data/lib/slack/smart-bot/commands/general/update_team.rb +0 -131
  74. data/lib/slack/smart-bot/utils/decrypt.rb +0 -15
  75. 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
- created: @repl_sessions[from][:started].to_s,
74
- accessed: @repl_sessions[from][:started].to_s,
75
- creator_name: user.name,
76
- creator_id: user.id,
77
- description: description,
78
- type: type,
79
- runs_by_creator: 0,
80
- runs_by_others: 0,
81
- gets: 0
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
- process_to_run.gsub!('\"','"')
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('./',"#{project_folder}/"), "w")
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 == 'run' or wait_thr.status == 'sleep') and @repl_sessions.key?(from)
130
+ while (wait_thr.status == "run" or wait_thr.status == "sleep") and @repl_sessions.key?(from)
233
131
  begin
234
- if (Time.now-@repl_sessions[from][:finished]) > timeout
235
- open("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input", 'a+') {|f|
236
- f.puts 'quit'
237
- }
238
- respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
239
- unreact :running, @ts_react[@repl_sessions[from].name]
240
- pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
241
- pids.each do |pid|
242
- begin
243
- Process.kill("KILL", pid)
244
- rescue
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
- @repl_sessions[from][:collaborators].each do |collaborator|
248
- @repl_sessions.delete(collaborator)
249
- end
250
- @repl_sessions.delete(from)
251
- break
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, "", 'response.rb', "", 'text/plain', "ruby", content: resp_repl)
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 'You are already in a repl on this SmartBot. You need to quit that one before starting a new one.'
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
- if code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File.") or
283
- code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
284
- code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
285
- code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or
286
- code.match?(/=\s*File/) or code.match?(/=\s*Dir/) or code.match?(/<\s*File/) or code.match?(/<\s*Dir/) or
287
- code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/) or
288
- code.match?(/=?\s*(require|load)(\(|\s)/i)
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
- collaborator = $1
293
- user_info = @users.select{|u| u.id == collaborator or (u.key?(:enterprise_user) and u.enterprise_user.id == collaborator)}[-1]
294
- collaborator_name = user_info.name
295
- if @repl_sessions.key?(collaborator_name)
296
- respond "Sorry, <@#{collaborator}> is already in a repl. Please ask her/him to quit it first.", dest
297
- else
298
- respond "Collaborator added. Now <@#{collaborator}> can interact with this repl.", dest
299
- creator = @repl_sessions[from][:user_creator]
300
- @repl_sessions[creator][:collaborators] << collaborator_name
301
- @repl_sessions[collaborator_name] = {
302
- name: @repl_sessions[from][:name],
303
- dest: dest,
304
- on_thread: Thread.current[:on_thread],
305
- thread_ts: Thread.current[:thread_ts],
306
- user_type: :collaborator,
307
- user_creator: creator
308
- }
309
- end
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", 'a+') {|f|
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", 'a+') {|f|
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