slack-smart-bot 1.9.1 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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