slack-smart-bot 1.9.1 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +184 -16
  3. data/lib/slack/smart-bot/comm/ask.rb +55 -49
  4. data/lib/slack/smart-bot/comm/delete.rb +13 -0
  5. data/lib/slack/smart-bot/comm/dont_understand.rb +3 -3
  6. data/lib/slack/smart-bot/comm/event_hello.rb +8 -4
  7. data/lib/slack/smart-bot/comm/get_channel_members.rb +13 -4
  8. data/lib/slack/smart-bot/comm/get_channels.rb +31 -16
  9. data/lib/slack/smart-bot/comm/get_presence.rb +20 -0
  10. data/lib/slack/smart-bot/comm/get_user_info.rb +12 -8
  11. data/lib/slack/smart-bot/comm/get_users.rb +24 -0
  12. data/lib/slack/smart-bot/comm/react.rb +19 -2
  13. data/lib/slack/smart-bot/comm/respond.rb +224 -53
  14. data/lib/slack/smart-bot/comm/respond_direct.rb +2 -3
  15. data/lib/slack/smart-bot/comm/respond_thread.rb +5 -0
  16. data/lib/slack/smart-bot/comm/send_file.rb +38 -34
  17. data/lib/slack/smart-bot/comm/send_msg_channel.rb +27 -22
  18. data/lib/slack/smart-bot/comm/send_msg_user.rb +59 -33
  19. data/lib/slack/smart-bot/comm/unreact.rb +22 -18
  20. data/lib/slack/smart-bot/comm.rb +4 -0
  21. data/lib/slack/smart-bot/commands/general/add_admin.rb +51 -0
  22. data/lib/slack/smart-bot/commands/general/add_announcement.rb +32 -0
  23. data/lib/slack/smart-bot/commands/general/add_team.rb +80 -0
  24. data/lib/slack/smart-bot/commands/general/allow_access.rb +67 -0
  25. data/lib/slack/smart-bot/commands/general/bot_help.rb +69 -33
  26. data/lib/slack/smart-bot/commands/general/bye_bot.rb +0 -7
  27. data/lib/slack/smart-bot/commands/general/delete_announcement.rb +34 -0
  28. data/lib/slack/smart-bot/commands/general/delete_share.rb +34 -0
  29. data/lib/slack/smart-bot/commands/general/delete_team.rb +34 -0
  30. data/lib/slack/smart-bot/commands/general/deny_access.rb +36 -0
  31. data/lib/slack/smart-bot/commands/general/hi_bot.rb +16 -11
  32. data/lib/slack/smart-bot/commands/general/ping_team.rb +100 -0
  33. data/lib/slack/smart-bot/commands/general/poster.rb +116 -0
  34. data/lib/slack/smart-bot/commands/general/remove_admin.rb +58 -0
  35. data/lib/slack/smart-bot/commands/general/see_access.rb +24 -0
  36. data/lib/slack/smart-bot/commands/general/see_admins.rb +33 -0
  37. data/lib/slack/smart-bot/commands/general/see_announcements.rb +115 -0
  38. data/lib/slack/smart-bot/commands/general/see_command_ids.rb +29 -0
  39. data/lib/slack/smart-bot/commands/general/see_favorite_commands.rb +53 -0
  40. data/lib/slack/smart-bot/commands/general/see_shares.rb +41 -0
  41. data/lib/slack/smart-bot/commands/general/see_statuses.rb +91 -0
  42. data/lib/slack/smart-bot/commands/general/see_teams.rb +252 -0
  43. data/lib/slack/smart-bot/commands/general/share_messages.rb +58 -0
  44. data/lib/slack/smart-bot/commands/general/update_team.rb +109 -0
  45. data/lib/slack/smart-bot/commands/general_bot_commands.rb +504 -0
  46. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +4 -6
  47. data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +45 -14
  48. data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +4 -1
  49. data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +6 -3
  50. data/lib/slack/smart-bot/commands/on_bot/admin/pause_routine.rb +3 -1
  51. data/lib/slack/smart-bot/commands/on_bot/admin/remove_routine.rb +4 -4
  52. data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +8 -2
  53. data/lib/slack/smart-bot/commands/on_bot/admin/see_result_routine.rb +33 -0
  54. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +13 -10
  55. data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +6 -3
  56. data/lib/slack/smart-bot/commands/on_bot/admin/start_routine.rb +3 -1
  57. data/lib/slack/smart-bot/commands/on_bot/admin/stop_using_rules_on.rb +4 -1
  58. data/lib/slack/smart-bot/commands/on_bot/admin_master/delete_message.rb +25 -0
  59. data/lib/slack/smart-bot/commands/on_bot/admin_master/get_bot_logs.rb +1 -0
  60. data/lib/slack/smart-bot/commands/on_bot/admin_master/react_to.rb +34 -0
  61. data/lib/slack/smart-bot/commands/on_bot/admin_master/send_message.rb +37 -0
  62. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +4 -5
  63. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +7 -8
  64. data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +400 -0
  65. data/lib/slack/smart-bot/commands/{general → on_bot/general}/bot_status.rb +3 -4
  66. data/lib/slack/smart-bot/commands/on_bot/general/leaderboard.rb +201 -0
  67. data/lib/slack/smart-bot/commands/{general → on_bot/general}/stop_using_rules.rb +12 -6
  68. data/lib/slack/smart-bot/commands/on_bot/general/suggest_command.rb +36 -0
  69. data/lib/slack/smart-bot/commands/{general → on_bot/general}/use_rules.rb +13 -11
  70. data/lib/slack/smart-bot/commands/{general → on_bot/general}/whats_new.rb +2 -1
  71. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +4 -5
  72. data/lib/slack/smart-bot/commands/on_bot/repl.rb +76 -21
  73. data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +3 -4
  74. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +15 -7
  75. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +5 -6
  76. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +5 -6
  77. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +45 -12
  78. data/lib/slack/smart-bot/commands/on_master/admin/kill_bot_on_channel.rb +7 -3
  79. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +4 -1
  80. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +3 -1
  81. data/lib/slack/smart-bot/commands/on_master/admin_master/publish_announcements.rb +33 -0
  82. data/lib/slack/smart-bot/commands/on_master/admin_master/set_general_message.rb +39 -0
  83. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +10 -1
  84. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +28 -14
  85. data/lib/slack/smart-bot/commands/on_master/where_smartbot.rb +41 -0
  86. data/lib/slack/smart-bot/commands.rb +36 -5
  87. data/lib/slack/smart-bot/listen.rb +31 -33
  88. data/lib/slack/smart-bot/process.rb +234 -73
  89. data/lib/slack/smart-bot/process_first.rb +119 -38
  90. data/lib/slack/smart-bot/treat_message.rb +310 -237
  91. data/lib/slack/smart-bot/utils/build_help.rb +2 -2
  92. data/lib/slack/smart-bot/utils/create_routine_thread.rb +81 -46
  93. data/lib/slack/smart-bot/utils/get_access_channels.rb +13 -0
  94. data/lib/slack/smart-bot/utils/get_admins_channels.rb +13 -0
  95. data/lib/slack/smart-bot/utils/get_bots_created.rb +28 -8
  96. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +7 -2
  97. data/lib/slack/smart-bot/utils/get_command_ids.rb +84 -0
  98. data/lib/slack/smart-bot/utils/get_help.rb +79 -73
  99. data/lib/slack/smart-bot/utils/get_repls.rb +22 -2
  100. data/lib/slack/smart-bot/utils/get_routines.rb +22 -2
  101. data/lib/slack/smart-bot/utils/get_shares.rb +12 -0
  102. data/lib/slack/smart-bot/utils/get_teams.rb +22 -0
  103. data/lib/slack/smart-bot/utils/has_access.rb +28 -0
  104. data/lib/slack/smart-bot/utils/is_admin.rb +27 -0
  105. data/lib/slack/smart-bot/utils/save_stats.rb +46 -41
  106. data/lib/slack/smart-bot/utils/save_status.rb +67 -0
  107. data/lib/slack/smart-bot/utils/update_access_channels.rb +8 -0
  108. data/lib/slack/smart-bot/utils/update_admins_channels.rb +8 -0
  109. data/lib/slack/smart-bot/utils/update_bots_file.rb +28 -7
  110. data/lib/slack/smart-bot/utils/update_repls.rb +7 -4
  111. data/lib/slack/smart-bot/utils/update_routines.rb +9 -3
  112. data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +13 -6
  113. data/lib/slack/smart-bot/utils/update_teams.rb +16 -0
  114. data/lib/slack/smart-bot/utils.rb +11 -0
  115. data/lib/slack-smart-bot.rb +72 -12
  116. data/lib/slack-smart-bot_general_commands.rb +61 -0
  117. data/lib/slack-smart-bot_general_rules.rb +5 -2
  118. data/lib/slack-smart-bot_rules.rb +43 -17
  119. data/whats_new.txt +20 -15
  120. metadata +76 -9
  121. data/lib/slack/smart-bot/commands/general/bot_stats.rb +0 -313
@@ -0,0 +1,36 @@
1
+ class SlackSmartBot
2
+
3
+ # help: ----------------------------------------------
4
+ # help: `suggest command`
5
+ # help: `random command`
6
+ # help: `command suggestion`
7
+ # help: `suggest rule`
8
+ # help: `random rule`
9
+ # help: `rule suggestion`
10
+ # help: it will display the help content for a random command.
11
+ # help: if used 'rule' then it will display a random rule.
12
+ # help: if used 'command' it will show any kind of command or rule.
13
+ # help: command_id: :suggest_command
14
+ # help:
15
+ def suggest_command(from, dest, dchannel, specific, rules_file)
16
+ save_stats(__method__)
17
+ dont_suggest = []
18
+ help_message = get_help(rules_file, dest, from, specific, true, descriptions: false, only_normal_user: true)
19
+ commands = help_message.gsub(/====+/,'-'*30).split(/^\s*-------*$/).flatten
20
+ commands.reject!{|c| c.match?(/These are specific commands for this bot on this/i) || c.match?(/\A\s*\z/)}
21
+ dont_suggest.each do |ds|
22
+ commands.reject!{|c| c.match?(/:#{ds}\s*$/i)}
23
+ end
24
+ @last_suggested_commands ||= []
25
+ @last_suggested_commands.shift if @last_suggested_commands.size >=5
26
+ command = ''
27
+ begin
28
+ command = commands.sample
29
+ end until !@last_suggested_commands.include?(command) or commands.size <= 5
30
+ @last_suggested_commands << command
31
+ command.gsub!(/^\s*command_id:\s+:\w+\s*$/,'')
32
+ message = "*Command suggestion*:\n#{command}"
33
+ respond message, dest, unfurl_links: false, unfurl_media: false
34
+ end
35
+
36
+ end
@@ -6,22 +6,23 @@ class SlackSmartBot
6
6
  # help: `use CHANNEL`
7
7
  # help: it will use the rules from the specified channel.
8
8
  # help: you need to be part of that channel to be able to use the rules.
9
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#using-rules-from-other-channels|more info>
10
+ # help: command_id: :use_rules
9
11
  # help:
10
12
  def use_rules(dest, channel, user, dchannel)
11
13
  save_stats(__method__)
12
14
  get_bots_created()
13
- if config[:allow_access].key?(__method__) and !config[:allow_access][__method__].include?(user.name) and !config[:allow_access][__method__].include?(user.id) and
14
- (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
15
- respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
16
- else
15
+ if has_access?(__method__, user)
17
16
  #todo: add pagination for case more than 1000 channels on the workspace
18
17
  channels = get_channels()
19
-
18
+ channel.gsub!('#','') # for the case the channel name is in plain text including #
19
+ channel = @channels_name[channel] if @channels_name.key?(channel)
20
20
  channel_found = channels.detect { |c| c.name == channel }
21
- members = get_channel_members(@channels_id[channel]) unless channel_found.nil?
21
+ get_channels_name_and_id() unless @channels_id.key?(channel)
22
+ members = get_channel_members(@channels_id[channel]) unless channel_found.nil? or !@channels_id.key?(channel)
22
23
 
23
- if channel_found.nil?
24
- respond "The channel you are trying to use doesn't exist", dest
24
+ if channel_found.nil? or !@channels_id.key?(channel)
25
+ respond "The channel you are trying to use doesn't exist or cannot be found.", dest
25
26
  elsif channel_found.name == config.master_channel
26
27
  respond "You cannot use the rules from Master Channel on any other channel.", dest
27
28
  elsif !@bots_created.key?(@channels_id[channel])
@@ -30,12 +31,13 @@ class SlackSmartBot
30
31
  respond "The bot in that channel is not :on", dest
31
32
  else
32
33
  if user.id == channel_found.creator or members.include?(user.id)
33
- @rules_imported[user.id] = {} unless @rules_imported.key?(user.id)
34
+ @rules_imported[user.name] = {} unless @rules_imported.key?(user.name)
34
35
  if dest[0] == "C" or dest[0] == "G" #todo: take in consideration bots that are not master
35
- @rules_imported[user.id][dchannel] = channel_found.id
36
+ @rules_imported[user.name][dchannel] = channel_found.id
36
37
  else
37
- @rules_imported[user.id][user.id] = channel_found.id
38
+ @rules_imported[user.name][user.name] = channel_found.id
38
39
  end
40
+ sleep 0.5
39
41
  update_rules_imported()
40
42
  respond "I'm using now the rules from <##{channel_found.id}>", dest
41
43
 
@@ -3,11 +3,12 @@ class SlackSmartBot
3
3
  # help: ----------------------------------------------
4
4
  # help: `What's new`
5
5
  # help: It will display the last user changes on Slack Smart Bot
6
+ # help: command_id: :whats_new
6
7
  # help:
7
8
  def whats_new(user, dest, dchannel, from, display_name)
8
9
  if @status == :on
9
10
  save_stats(__method__)
10
- whats_new_file = (__FILE__).gsub(/lib\/slack\/smart-bot\/commands\/general\/whats_new\.rb$/, "whats_new.txt")
11
+ whats_new_file = (__FILE__).gsub(/lib\/slack\/smart-bot\/commands\/on_bot\/general\/whats_new\.rb$/, "whats_new.txt")
11
12
  whats_new = File.read(whats_new_file)
12
13
  whats_new.split(/^\-\-\-\-\-\-+$/).each do |msg|
13
14
  respond msg
@@ -4,20 +4,19 @@ class SlackSmartBot
4
4
  # help: `get irb SESSION_NAME`
5
5
  # help: `get live SESSION_NAME`
6
6
  # help: Will get the Ruby commands sent on that SESSION_NAME.
7
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
8
+ # help: command_id: :get_repl
7
9
  # help:
8
10
  def get_repl(dest, user, session_name)
9
11
  #todo: add tests
10
12
  save_stats(__method__)
11
- if config[:allow_access].key?(__method__) and !config[:allow_access][__method__].include?(user.name) and !config[:allow_access][__method__].include?(user.id) and
12
- (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
13
- respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
14
- else
13
+ if has_access?(__method__, user)
15
14
  Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
16
15
  Dir.mkdir("#{config.path}/repl/#{@channel_id}") unless Dir.exist?("#{config.path}/repl/#{@channel_id}")
17
16
  if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
18
17
  if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
19
18
  @repls[session_name][:creator_name]!=user.name and
20
- !config.admins.include?(user.name)
19
+ !is_admin?(user.name)
21
20
  respond "The REPL with session name: #{session_name} is private", dest
22
21
  else
23
22
  content = "require 'nice_http'\n"
@@ -17,9 +17,9 @@ 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 `run repl` later.
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_
@@ -29,14 +29,13 @@ class SlackSmartBot
29
29
  # help: _repl CreateCustomer: "It creates a random customer for testing" LOCATION=spain HOST='https://10.30.40.50:8887'_
30
30
  # help: _repl delete_logs_
31
31
  # help: _private repl random-ssn_
32
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
33
+ # help: command_id: :repl
32
34
  # help:
33
35
  def repl(dest, user, session_name, env_vars, rules_file, command, description, type)
34
36
  #todo: add more tests
35
37
  from = user.name
36
- if config[:allow_access].key?(__method__) and !config[:allow_access][__method__].include?(user.name) and !config[:allow_access][__method__].include?(user.id) and
37
- (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
38
- respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
39
- else
38
+ if has_access?(__method__, user)
40
39
  if !@repl_sessions.key?(from)
41
40
  save_stats(__method__)
42
41
  Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
@@ -80,11 +79,14 @@ class SlackSmartBot
80
79
  update_repls()
81
80
  end
82
81
  react :running
82
+ @ts_react ||= {}
83
83
  if Thread.current[:ts].to_s == ''
84
- @ts_react = Thread.current[:thread_ts]
84
+ @ts_react[session_name] = Thread.current[:thread_ts]
85
85
  else
86
- @ts_react = Thread.current[:ts]
86
+ @ts_react[session_name] = Thread.current[:ts]
87
87
  end
88
+ @ts_repl ||= {}
89
+ @ts_repl[session_name] = ''
88
90
 
89
91
  message = "Session name: *#{session_name}*
90
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`.
@@ -117,14 +119,14 @@ class SlackSmartBot
117
119
  end
118
120
 
119
121
  process_to_run = '
120
- ruby -e "' + env_vars.join("\n") + '
122
+ ' + env_vars.join("\n") + '
121
123
  require \"amazing_print\"
122
124
  bindme' + serialt + ' = binding
123
125
  eval(\"require \'nice_http\'\" , bindme' + serialt + ')
124
126
  def ls(obj)
125
127
  (obj.methods - Object.methods)
126
128
  end
127
-
129
+ file_run_path = \"' + + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.rb\"
128
130
  file_input_repl = File.open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.input\", \"r\")
129
131
  ' + pre_execute + '
130
132
  while true do
@@ -143,21 +145,43 @@ class SlackSmartBot
143
145
  end
144
146
  error = false
145
147
  begin
146
- resp_repl = eval(code_to_run_repl.gsub(/^\s*(puts|print|p|pp)\s/, \"\"), bindme' + serialt + ')
148
+ begin
149
+ original_stdout = $stdout
150
+ $stdout = StringIO.new
151
+ resp_repl = eval(code_to_run_repl, bindme' + serialt + ')
152
+ stdout_repl = $stdout.string
153
+ ensure
154
+ $stdout = original_stdout
155
+ end
147
156
  rescue Exception => resp_repl
148
157
  error = true
149
158
  end
150
- if resp_repl.to_s != \"\"
159
+ if error
160
+ open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
161
+ f.puts \"\`\`\`\n#{resp_repl.to_s.gsub(/^.+' + session_name + '\.rb:\d+:/,\"\")}\`\`\`\"
162
+ }
163
+ else
151
164
  if code_to_run_repl.match?(/^\s*p\s+/i)
165
+ resp_repl = stdout_repl unless stdout_repl.to_s == \'\'
166
+ if stdout_repl.to_s == \'\'
167
+ resp_repl = resp_repl.inspect
168
+ else
169
+ resp_repl = stdout_repl
170
+ end
152
171
  open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
153
- f.puts \"\`\`\`\n#{resp_repl.inspect}\n\`\`\`\"
172
+ f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
154
173
  }
155
174
  else
175
+ if stdout_repl.to_s == \'\'
176
+ resp_repl = resp_repl.ai
177
+ else
178
+ resp_repl = stdout_repl
179
+ end
156
180
  open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
157
- f.puts \"\`\`\`\n#{resp_repl.ai}\n\`\`\`\"
181
+ f.puts \"\`\`\`\n#{resp_repl}\`\`\`\"
158
182
  }
159
183
  end
160
- unless error or !add_to_run_repl
184
+ unless !add_to_run_repl
161
185
  open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.run\", \"a+\") {|f|
162
186
  f.puts code_to_run_repl
163
187
  }
@@ -165,16 +189,35 @@ class SlackSmartBot
165
189
  end
166
190
  end
167
191
  end
168
- end"
192
+ end
169
193
  '
170
194
  unless rules_file.empty? # to get the project_folder
171
195
  begin
172
196
  eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
173
197
  end
174
198
  end
175
- started = Time.now
176
- process_to_run = ("cd #{project_folder} &&" + process_to_run) if defined?(project_folder)
199
+ process_to_run.gsub!('\"','"')
200
+ file_run_path = "./tmp/repl/#{@channel_id}/#{session_name}.rb"
201
+ if defined?(project_folder)
202
+ Dir.mkdir("#{project_folder}/tmp/") unless Dir.exist?("#{project_folder}/tmp/")
203
+ Dir.mkdir("#{project_folder}/tmp/repl") unless Dir.exist?("#{project_folder}/tmp/repl")
204
+ Dir.mkdir("#{project_folder}/tmp/repl/#{@channel_id}/") unless Dir.exist?("#{project_folder}/tmp/repl/#{@channel_id}/")
205
+ file_run = File.open(file_run_path.gsub('./',"#{project_folder}/"), "w")
206
+ file_run.write process_to_run
207
+ file_run.close
208
+ else
209
+ Dir.mkdir("./tmp/") unless Dir.exist?("./tmp/")
210
+ Dir.mkdir("./tmp/repl") unless Dir.exist?("./tmp/repl")
211
+ Dir.mkdir("./tmp/repl/#{@channel_id}/") unless Dir.exist?("./tmp/repl/#{@channel_id}/")
212
+ file_run = File.open(file_run_path, "w")
213
+ file_run.write process_to_run
214
+ file_run.close
215
+ end
177
216
 
217
+ process_to_run = "ruby #{file_run_path}"
218
+
219
+ started = Time.now
220
+ process_to_run = ("cd #{project_folder} && " + process_to_run) if defined?(project_folder)
178
221
  stdin, stdout, stderr, wait_thr = Open3.popen3(process_to_run)
179
222
  timeout = 30 * 60 # 30 minutes
180
223
 
@@ -187,7 +230,7 @@ class SlackSmartBot
187
230
  f.puts 'quit'
188
231
  }
189
232
  respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
190
- unreact :running, @ts_react
233
+ unreact :running, @ts_react[@repl_sessions[from].name]
191
234
  pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
192
235
  pids.each do |pid|
193
236
  begin
@@ -201,6 +244,10 @@ class SlackSmartBot
201
244
  sleep 0.2
202
245
  resp_repl = file_output_repl.read
203
246
  if resp_repl.to_s!=''
247
+ if @ts_repl[@repl_sessions[from].name].to_s != ''
248
+ unreact(:running, @ts_repl[@repl_sessions[from].name])
249
+ @ts_repl[@repl_sessions[from].name] = ''
250
+ end
204
251
  if resp_repl.to_s.lines.count < 60 and resp_repl.to_s.size < 3500
205
252
  respond resp_repl, dest
206
253
  else
@@ -213,6 +260,8 @@ class SlackSmartBot
213
260
  @logger.fatal excp
214
261
  end
215
262
  end
263
+ elsif @repl_sessions.key?(from) and @repl_sessions[from][:command].to_s == ''
264
+ respond 'You are already in a repl on this SmartBot. You need to quit that one before starting a new one.'
216
265
  else
217
266
  @repl_sessions[from][:finished] = Time.now
218
267
  code = @repl_sessions[from][:command]
@@ -226,7 +275,9 @@ class SlackSmartBot
226
275
  code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
227
276
  code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or
228
277
  code.match?(/=\s*File/) or code.match?(/=\s*Dir/) or code.match?(/<\s*File/) or code.match?(/<\s*Dir/) or
229
- code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/)
278
+ code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/) or
279
+ code.match?(/=?\s*(require|load)(\(|\s)/i)
280
+
230
281
  respond "Sorry I cannot run this due security reasons", dest
231
282
  else
232
283
  @repl_sessions[from][:input]<<code
@@ -236,7 +287,7 @@ class SlackSmartBot
236
287
  f.puts code
237
288
  }
238
289
  respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
239
- unreact :running, @ts_react
290
+ unreact :running, @ts_react[@repl_sessions[from].name]
240
291
  pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
241
292
  pids.each do |pid|
242
293
  begin
@@ -248,6 +299,10 @@ class SlackSmartBot
248
299
  when /^\s*-/i
249
300
  #ommit
250
301
  else
302
+ if @ts_repl[@repl_sessions[from].name].to_s == ''
303
+ @ts_repl[@repl_sessions[from].name] = Thread.current[:ts]
304
+ react :running
305
+ end
251
306
  open("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input", 'a+') {|f|
252
307
  f.puts code
253
308
  }
@@ -5,14 +5,13 @@ class SlackSmartBot
5
5
  # help: runs the code supplied and returns the output. Also you can send a Ruby file instead. Examples:
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
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#running-ruby-code-on-a-conversation|more info>
9
+ # help: command_id: :ruby_code
8
10
  # help:
9
11
 
10
12
  def ruby_code(dest, user, code, rules_file)
11
13
  save_stats(__method__)
12
- if config[:allow_access].key?(__method__) and !config[:allow_access][__method__].include?(user.name) and !config[:allow_access][__method__].include?(user.id) and
13
- (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
14
- respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
15
- else
14
+ if has_access?(__method__, user)
16
15
  unless code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File.") or
17
16
  code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
18
17
  code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
@@ -9,21 +9,20 @@ class SlackSmartBot
9
9
  # help: It will return only the values that were print out on the repl with puts, print, p or pp
10
10
  # help: Example:
11
11
  # help: _run repl CreateCustomer LOCATION=spain HOST='https://10.30.40.50:8887'_
12
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
13
+ # help: command_id: :run_repl
12
14
  # help:
13
15
  def run_repl(dest, user, session_name, env_vars, rules_file)
14
16
  #todo: add tests
15
17
  from = user.name
16
- if config[:allow_access].key?(__method__) and !config[:allow_access][__method__].include?(user.name) and !config[:allow_access][__method__].include?(user.id) and
17
- (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
18
- respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
19
- else
18
+ if has_access?(__method__, user)
20
19
  save_stats(__method__)
21
20
  Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
22
21
  Dir.mkdir("#{config.path}/repl/#{@channel_id}") unless Dir.exist?("#{config.path}/repl/#{@channel_id}")
23
22
  if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
24
23
  if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
25
24
  @repls[session_name][:creator_name]!=user.name and
26
- !config.admins.include?(user.name)
25
+ !is_admin?(user.name)
27
26
  respond "The REPL with session name: #{session_name} is private", dest
28
27
  else
29
28
  if @repls.key?(session_name) #not temp
@@ -43,7 +42,8 @@ class SlackSmartBot
43
42
  eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
44
43
  end
45
44
  end
46
- if File.exist?("#{project_folder}/.smart-bot-repl") and @repls[session_name][:type] != :private_clean and @repls[session_name][:type] != :public_clean
45
+ if File.exist?("#{project_folder}/.smart-bot-repl") and
46
+ ((@repls.key?(session_name) and @repls[session_name][:type] != :private_clean and @repls[session_name][:type] != :public_clean) or !@repls.key?(session_name))
47
47
  content += File.read("#{project_folder}/.smart-bot-repl")
48
48
  content += "\n"
49
49
  end
@@ -60,7 +60,15 @@ class SlackSmartBot
60
60
  respond "*#{session_name}*: Nothing returned."
61
61
  else
62
62
  if stdout.to_s.lines.count < 60 and stdout.to_s.size < 3500
63
- respond "*#{session_name}*: #{stdout}"
63
+ output = ''
64
+ stdout.each_line do |line|
65
+ if line.match?(/^{.+}$/) or line.match?(/^\[.+\]$/)
66
+ output += "```\n#{line}```\n"
67
+ else
68
+ output +=line
69
+ end
70
+ end
71
+ respond "*#{session_name}*: #{output}"
64
72
  else
65
73
  send_file(dest, "", 'response.rb', "", 'text/plain', "ruby", content: stdout)
66
74
  end
@@ -2,19 +2,18 @@ class SlackSmartBot
2
2
  # help: ----------------------------------------------
3
3
  # help: `see repls`
4
4
  # help: `see irbs`
5
- # help: It will display the repls
5
+ # help: It will display the repls
6
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
7
+ # help: command_id: :see_repls
6
8
  # help:
7
9
  def see_repls(dest, user, typem)
8
10
  #todo: add tests
9
11
  save_stats(__method__)
10
12
  from = user.name
11
- if config[:allow_access].key?(__method__) and !config[:allow_access][__method__].include?(user.name) and !config[:allow_access][__method__].include?(user.id) and
12
- (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
13
- respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
14
- else
13
+ if has_access?(__method__, user)
15
14
  message = ""
16
15
  @repls.sort.to_h.each do |session_name, repl|
17
- if (repl.creator_name == user.name or repl.type == :public or repl.type == :public_clean) or (config.admins.include?(user.name) and typem == :on_dm)
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)
18
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"
19
18
  end
20
19
  end
@@ -3,14 +3,13 @@ class SlackSmartBot
3
3
  # help: `see shortcuts`
4
4
  # help: `see sc`
5
5
  # help: It will display the shortcuts stored for the user and for :all
6
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#shortcuts|more info>
7
+ # help: command_id: :see_shortcuts
6
8
  # help:
7
9
  def see_shortcuts(dest, user, typem)
8
10
  save_stats(__method__)
9
11
  from = user.name
10
- if config[:allow_access].key?(__method__) and !config[:allow_access][__method__].include?(user.name) and !config[:allow_access][__method__].include?(user.id) and
11
- (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
12
- respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
13
- else
12
+ if has_access?(__method__, user)
14
13
  unless typem == :on_extended
15
14
  msg = ""
16
15
  if @shortcuts[:all].keys.size > 0 or @shortcuts_global[:all].keys.size > 0
@@ -30,7 +29,7 @@ class SlackSmartBot
30
29
  end
31
30
  msg2 = ''
32
31
  if @shortcuts.keys.include?(from) and @shortcuts[from].keys.size > 0
33
- new_hash = @shortcuts[from].dup
32
+ new_hash = @shortcuts[from].deep_copy
34
33
  @shortcuts[:all].keys.each { |k| new_hash.delete(k) }
35
34
  if new_hash.keys.size > 0
36
35
  msg2 = "*Available shortcuts for #{from}:*\n"
@@ -40,7 +39,7 @@ class SlackSmartBot
40
39
  end
41
40
  end
42
41
  if @shortcuts_global.keys.include?(from) and @shortcuts_global[from].keys.size > 0
43
- new_hash = @shortcuts_global[from].dup
42
+ new_hash = @shortcuts_global[from].deep_copy
44
43
  @shortcuts_global[:all].keys.each { |k| new_hash.delete(k) }
45
44
  if new_hash.keys.size > 0
46
45
  msg2 = "*Available shortcuts for #{from}:*\n" if msg2 == ''
@@ -2,12 +2,9 @@ class SlackSmartBot
2
2
  def bot_rules(dest, help_command, typem, rules_file, user)
3
3
  save_stats(__method__)
4
4
  from = user.name
5
- if config[:allow_access].key?(__method__) and !config[:allow_access][__method__].include?(user.name) and !config[:allow_access][__method__].include?(user.id) and
6
- (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
7
- respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
8
- else
5
+ if has_access?(__method__, user)
9
6
  if typem == :on_extended or typem == :on_call #for the other cases above.
10
-
7
+ output = []
11
8
  if help_command.to_s != ''
12
9
  help_command = '' if help_command.to_s.match?(/^\s*expanded\s*$/i) or help_command.to_s.match?(/^\s*extended\s*$/i)
13
10
  expanded = true
@@ -17,22 +14,47 @@ class SlackSmartBot
17
14
 
18
15
  help_filtered = get_help(rules_file, dest, from, true, expanded)
19
16
 
17
+ commands = []
18
+ commands_search = []
20
19
  if help_command.to_s != ""
21
20
  help_found = false
22
21
  help_filtered.split(/^\s*-------*$/).each do |h|
23
22
  if h.match?(/[`_]#{help_command}/i)
24
- respond "*#{config.channel}*:#{h}", dest
23
+ output << "*#{config.channel}*:#{h}"
25
24
  help_found = true
25
+ commands << h
26
+ elsif !h.match?(/\A\s*\*/) and !h.match?(/\A\s*=+/) #to avoid general messages for bot help *General rules...*
27
+ all_found = true
28
+ help_command.to_s.split(' ') do |hc|
29
+ unless hc.match?(/^\s*\z/)
30
+ if !h.match?(/#{hc}/i)
31
+ all_found = false
32
+ end
33
+ end
34
+ end
35
+ commands_search << h if all_found
36
+ end
37
+ end
38
+ if commands.size < 10 and help_command.to_s!='' and commands_search.size > 0
39
+ commands_search.shuffle!
40
+ (10-commands.size).times do |n|
41
+ unless commands_search[n].nil?
42
+ output << commands_search[n]
43
+ help_found = true
44
+ end
26
45
  end
27
46
  end
28
- respond("*#{config.channel}*: I didn't find any command starting by `#{help_command}`", dest) unless help_found
47
+ unless help_found
48
+ output << "*#{config.channel}*: I didn't find any command with `#{help_command}`"
49
+ end
50
+
29
51
  else
30
52
  message = "-\n\n\n===================================\n*Rules from channel #{config.channel}*\n"
31
53
  if typem == :on_extended
32
54
  message += "To run the commands on this extended channel, add `!`, `!!` or `^` before the command.\n"
33
55
  end
34
56
  message += help_filtered
35
- respond message, dest
57
+ output << message
36
58
  end
37
59
 
38
60
  unless rules_file.empty?
@@ -41,16 +63,27 @@ class SlackSmartBot
41
63
  end
42
64
  end
43
65
  if defined?(git_project) and git_project.to_s != "" and help_command.to_s == ""
44
- respond "Git project: #{git_project}", dest
66
+ output << "Git project: #{git_project}"
45
67
  else
46
68
  def git_project() "" end
47
69
  def project_folder() "" end
48
70
  end
49
71
  unless expanded
50
- message_not_expanded = "*If you want to see the expanded version of `bot rules`, please call `bot rules expanded`*\n"
51
- message_not_expanded += "*Also to get specific expanded help for a specific command or rule call `bot rules COMMAND`*\n"
52
- respond message_not_expanded
72
+ message_not_expanded = "If you want to see the *expanded* version of *`bot rules`*, please call *`bot rules expanded`*\n"
73
+ message_not_expanded += "Also to get specific *expanded* help for a specific command or rule call *`bot rules COMMAND`*\n"
74
+ output << message_not_expanded
75
+ end
76
+ if output.join("\n").lines.count > 50 and dest[0]!='D'
77
+ dest = :on_thread
78
+ output.unshift('Since there are many lines returned the results are returned on a thread by default.')
79
+ end
80
+ output.each do |h|
81
+ msg = h.gsub(/^\s*command_id:\s+:\w+\s*$/,'')
82
+ unless msg.match?(/\A\s*\z/)
83
+ respond msg, dest, unfurl_links: false, unfurl_media: false
84
+ end
53
85
  end
86
+
54
87
  end
55
88
  end
56
89
  end
@@ -3,6 +3,8 @@ class SlackSmartBot
3
3
  # helpmaster: `kill bot on CHANNEL_NAME`
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
+ # helpmaster: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
7
+ # helpmaster: command_id: :kill_bot_on_channel
6
8
  # helpmaster:
7
9
  def kill_bot_on_channel(dest, from, channel)
8
10
  save_stats(__method__)
@@ -18,14 +20,16 @@ class SlackSmartBot
18
20
  if channel_id.nil?
19
21
  respond "There is no channel with that name: #{channel}, please be sure is written exactly the same", dest
20
22
  elsif @bots_created.keys.include?(channel_id)
21
- if @bots_created[channel_id][:admins].split(",").include?(from)
23
+ @bots_created[channel_id] ||= {}
24
+ if @bots_created[channel_id][:admins].to_s.split(",").include?(from) # todo: consider adding is_admin?
22
25
  if @bots_created[channel_id][:thread].kind_of?(Thread) and @bots_created[channel_id][:thread].alive?
23
26
  @bots_created[channel_id][:thread].kill
24
27
  end
25
28
  @bots_created.delete(channel_id)
26
- update_bots_file()
27
- respond "Bot on channel: #{channel}, has been killed and deleted.", dest
28
29
  send_msg_channel(channel, "Bot has been killed by #{from}")
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()
29
33
  else
30
34
  respond "You need to be the creator or an admin of that bot channel", dest
31
35
  end
@@ -6,11 +6,13 @@ class SlackSmartBot
6
6
  # helpadmin: `close bot`
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
+ # helpadmin: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
10
+ # helpadmin: command_id: :exit_bot
9
11
  # helpadmin:
10
12
  def exit_bot(command, from, dest, display_name)
11
13
  save_stats(__method__)
12
14
  if config.on_master_bot
13
- if config.admins.include?(from) #admin user
15
+ if config.masters.include?(from) #admin user
14
16
  if answer.empty?
15
17
  ask("are you sure?", command, from, dest)
16
18
  else
@@ -21,6 +23,7 @@ class SlackSmartBot
21
23
  @bots_created.each { |key, value|
22
24
  value[:thread] = ""
23
25
  send_msg_channel(key, "Bot has been closed by #{from}")
26
+ save_status :off, :exited, "The admin closed SmartBot on *##{value.channel_name}*"
24
27
  sleep 0.5
25
28
  }
26
29
  update_bots_file()
@@ -7,11 +7,13 @@ class SlackSmartBot
7
7
  # helpmaster: It will send a notification message to all channels the bot joined and private conversations with the bot
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
+ # helpmaster: <https://github.com/MarioRuiz/slack-smart-bot#sending-notifications|more info>
11
+ # helpmaster: command_id: :notify_message
10
12
  # helpmaster:
11
13
  def notify_message(dest, from, where, message)
12
14
  save_stats(__method__)
13
15
  if config.on_master_bot
14
- if config.admins.include?(from) #admin user
16
+ if config.masters.include?(from) #admin user
15
17
  if where.nil? #not all and not channel
16
18
  @bots_created.each do |k, v|
17
19
  respond message, k