slack-smart-bot 1.8.2 → 1.9.1

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -11
  3. data/lib/slack-smart-bot.rb +43 -44
  4. data/lib/slack-smart-bot_rules.rb +6 -6
  5. data/lib/slack/smart-bot/comm.rb +5 -1
  6. data/lib/slack/smart-bot/comm/ask.rb +12 -5
  7. data/lib/slack/smart-bot/comm/dont_understand.rb +1 -1
  8. data/lib/slack/smart-bot/comm/event_hello.rb +30 -0
  9. data/lib/slack/smart-bot/comm/get_channel_members.rb +8 -0
  10. data/lib/slack/smart-bot/comm/get_channels.rb +20 -0
  11. data/lib/slack/smart-bot/comm/get_user_info.rb +16 -0
  12. data/lib/slack/smart-bot/comm/react.rb +21 -8
  13. data/lib/slack/smart-bot/comm/respond.rb +10 -5
  14. data/lib/slack/smart-bot/comm/send_msg_channel.rb +2 -2
  15. data/lib/slack/smart-bot/comm/send_msg_user.rb +4 -4
  16. data/lib/slack/smart-bot/comm/unreact.rb +21 -8
  17. data/lib/slack/smart-bot/commands.rb +3 -1
  18. data/lib/slack/smart-bot/commands/general/bot_help.rb +16 -3
  19. data/lib/slack/smart-bot/commands/general/bot_stats.rb +313 -0
  20. data/lib/slack/smart-bot/commands/general/bot_status.rb +1 -1
  21. data/lib/slack/smart-bot/commands/general/bye_bot.rb +1 -1
  22. data/lib/slack/smart-bot/commands/general/hi_bot.rb +1 -1
  23. data/lib/slack/smart-bot/commands/general/use_rules.rb +2 -6
  24. data/lib/slack/smart-bot/commands/general/whats_new.rb +19 -0
  25. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +65 -33
  26. data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +3 -7
  27. data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +1 -0
  28. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +8 -2
  29. data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +1 -0
  30. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +1 -1
  31. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +52 -21
  32. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +5 -5
  33. data/lib/slack/smart-bot/commands/on_bot/repl.rb +50 -18
  34. data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +34 -9
  35. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +2 -3
  36. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +1 -1
  37. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +27 -9
  38. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +14 -1
  39. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +3 -3
  40. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +1 -1
  41. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +41 -0
  42. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +4 -8
  43. data/lib/slack/smart-bot/listen.rb +6 -5
  44. data/lib/slack/smart-bot/process.rb +227 -188
  45. data/lib/slack/smart-bot/process_first.rb +104 -87
  46. data/lib/slack/smart-bot/treat_message.rb +98 -38
  47. data/lib/slack/smart-bot/utils.rb +2 -0
  48. data/lib/slack/smart-bot/utils/answer.rb +18 -0
  49. data/lib/slack/smart-bot/utils/answer_delete.rb +15 -0
  50. data/lib/slack/smart-bot/utils/build_help.rb +57 -5
  51. data/lib/slack/smart-bot/utils/create_routine_thread.rb +11 -2
  52. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +1 -7
  53. data/lib/slack/smart-bot/utils/get_help.rb +79 -17
  54. data/lib/slack/smart-bot/utils/save_stats.rb +21 -8
  55. data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +6 -0
  56. data/whats_new.txt +18 -0
  57. metadata +21 -12
  58. data/lib/slack/smart-bot/commands/on_bot/admin_master/bot_stats.rb +0 -195
@@ -14,7 +14,7 @@ class SlackSmartBot
14
14
  gems_remote = `gem list slack-smart-bot --remote`
15
15
  version_remote = gems_remote.to_s().scan(/slack-smart-bot \((\d+\.\d+\.\d+)/).join
16
16
  version_message = ""
17
- if version_remote != VERSION
17
+ if Gem::Version.new(version_remote) > Gem::Version.new(VERSION)
18
18
  version_message = " There is a new available version: #{version_remote}."
19
19
  end
20
20
  require "socket"
@@ -4,8 +4,8 @@ class SlackSmartBot
4
4
  # help: `Bye Bot`
5
5
  # help: `Bye Smart`
6
6
  # help: `Bye NAME_OF_THE_BOT`
7
- # help: Also apart of Bye you can use _Bæ, Good Bye, Adiós, Ciao, Bless, Bless Bless, Adeu_
8
7
  # help: Bot stops listening to you
8
+ # help: Also apart of Bye you can use _Bæ, Good Bye, Adiós, Ciao, Bless, Bless Bless, Adeu_
9
9
  # help:
10
10
  def bye_bot(dest, from, display_name)
11
11
  if @status == :on
@@ -5,9 +5,9 @@ class SlackSmartBot
5
5
  # help: `Hi Smart`
6
6
  # help: `Hello Bot` `Hola Bot` `Hallo Bot` `What's up Bot` `Hey Bot` `Hæ Bot`
7
7
  # help: `Hello THE_NAME_OF_THE_BOT`
8
- # help: Also apart of Hello you can use _Hallo, Hi, Hola, What's up, Hey, Hæ_
9
8
  # help: Bot starts listening to you
10
9
  # help: After that if you want to avoid a single message to be treated by the smart bot, start the message by -
10
+ # help: Also apart of Hello you can use _Hallo, Hi, Hola, What's up, Hey, Hæ_
11
11
  # help:
12
12
  def hi_bot(user, dest, dchannel, from, display_name)
13
13
  if @status == :on
@@ -15,14 +15,10 @@ class SlackSmartBot
15
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
16
  else
17
17
  #todo: add pagination for case more than 1000 channels on the workspace
18
- channels = client.web_client.conversations_list(
19
- types: "private_channel,public_channel",
20
- limit: "1000",
21
- exclude_archived: "true",
22
- ).channels
18
+ channels = get_channels()
23
19
 
24
20
  channel_found = channels.detect { |c| c.name == channel }
25
- members = client.web_client.conversations_members(channel: @channels_id[channel]).members unless channel_found.nil?
21
+ members = get_channel_members(@channels_id[channel]) unless channel_found.nil?
26
22
 
27
23
  if channel_found.nil?
28
24
  respond "The channel you are trying to use doesn't exist", dest
@@ -0,0 +1,19 @@
1
+ class SlackSmartBot
2
+
3
+ # help: ----------------------------------------------
4
+ # help: `What's new`
5
+ # help: It will display the last user changes on Slack Smart Bot
6
+ # help:
7
+ def whats_new(user, dest, dchannel, from, display_name)
8
+ if @status == :on
9
+ 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.read(whats_new_file)
12
+ whats_new.split(/^\-\-\-\-\-\-+$/).each do |msg|
13
+ respond msg
14
+ sleep 0.3
15
+ end
16
+ end
17
+ end
18
+ end
19
+
@@ -7,8 +7,10 @@ class SlackSmartBot
7
7
  # help: `add sc for all NAME: COMMAND`
8
8
  # help: `shortcut NAME: COMMAND`
9
9
  # help: `shortcut for all NAME: COMMAND`
10
+ # help: `add global sc for all NAME: COMMAND`
10
11
  # help: It will add a shortcut that will execute the command we supply.
11
12
  # help: In case we supply 'for all' then the shorcut will be available for everybody
13
+ # help: If 'global' or 'generic' supplied and in Master channel then the shortcut will be available in all Bot channels.
12
14
  # help: If you want to use a shortcut as a inline shortcut inside a command you can do it by adding a $ fex: _!run tests $cust1_
13
15
  # help: Example:
14
16
  # help: _add shortcut for all Spanish account: code require 'iso/iban'; 10.times {puts ISO::IBAN.random('ES')}_
@@ -17,7 +19,7 @@ class SlackSmartBot
17
19
  # help: _shortcut Spanish Account_
18
20
  # help: _Spanish Account_
19
21
  # help:
20
- def add_shortcut(dest, user, typem, for_all, shortcut_name, command, command_to_run)
22
+ def add_shortcut(dest, user, typem, for_all, shortcut_name, command, command_to_run, global)
21
23
  save_stats(__method__)
22
24
  unless typem == :on_extended
23
25
  from = user.name
@@ -25,43 +27,73 @@ class SlackSmartBot
25
27
  (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
26
28
  respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
27
29
  else
28
- @shortcuts[from] = Hash.new() unless @shortcuts.keys.include?(from)
29
30
 
30
- found_other = false
31
- if for_all.to_s != ""
32
- @shortcuts.each { |sck, scv|
33
- if sck != :all and sck != from and scv.key?(shortcut_name)
34
- found_other = true
31
+ if global
32
+ if !config.on_master_bot or typem != :on_master
33
+ respond "It is only possible to add global shortcuts from Master channel"
34
+ else
35
+ @shortcuts_global[from] = Hash.new() unless @shortcuts_global.keys.include?(from)
36
+ found_other = false
37
+ if for_all.to_s != ""
38
+ @shortcuts_global.each { |sck, scv|
39
+ if sck != :all and sck != from and scv.key?(shortcut_name)
40
+ found_other = true
41
+ end
42
+ }
35
43
  end
36
- }
37
- end
38
- if !config.admins.include?(from) and @shortcuts[:all].include?(shortcut_name) and !@shortcuts[from].include?(shortcut_name)
39
- respond "Only the creator of the shortcut can modify it", dest
40
- elsif found_other
41
- respond "You cannot create a shortcut for all with the same name than other user is using", dest
42
- elsif !@shortcuts[from].include?(shortcut_name)
43
- #new shortcut
44
- @shortcuts[from][shortcut_name] = command_to_run
45
- @shortcuts[:all][shortcut_name] = command_to_run if for_all.to_s != ""
46
- update_shortcuts_file()
47
- respond "shortcut added", dest
44
+ if @shortcuts_global[:all].include?(shortcut_name) or @shortcuts_global[from].include?(shortcut_name)
45
+ respond "Global shortcut name already in use. Please use another shortcut name."
46
+ elsif found_other
47
+ respond "You cannot create a global shortcut for all with the same name than other user is using."
48
+ elsif !@shortcuts_global[from].include?(shortcut_name)
49
+ #new shortcut
50
+ @shortcuts_global[from][shortcut_name] = command_to_run
51
+ @shortcuts_global[:all][shortcut_name] = command_to_run if for_all.to_s != ""
52
+ update_shortcuts_file()
53
+ respond "global shortcut added"
54
+ else
55
+ respond "Not possible to add the global shortcut" #todo: check if this is ever gonna be the case
56
+ end
57
+ end
48
58
  else
49
- #are you sure? to avoid overwriting existing
50
- unless @questions.keys.include?(from)
51
- ask("The shortcut already exists, are you sure you want to overwrite it?", command, from, dest)
59
+ @shortcuts[from] = Hash.new() unless @shortcuts.keys.include?(from)
60
+
61
+ found_other = false
62
+ if for_all.to_s != ""
63
+ @shortcuts.each { |sck, scv|
64
+ if sck != :all and sck != from and scv.key?(shortcut_name)
65
+ found_other = true
66
+ end
67
+ }
68
+ end
69
+ if !config.admins.include?(from) and @shortcuts[:all].include?(shortcut_name) and !@shortcuts[from].include?(shortcut_name)
70
+ respond "Only the creator of the shortcut can modify it", dest
71
+ elsif found_other
72
+ respond "You cannot create a shortcut for all with the same name than other user is using", dest
73
+ elsif !@shortcuts[from].include?(shortcut_name)
74
+ #new shortcut
75
+ @shortcuts[from][shortcut_name] = command_to_run
76
+ @shortcuts[:all][shortcut_name] = command_to_run if for_all.to_s != ""
77
+ update_shortcuts_file()
78
+ respond "shortcut added", dest
52
79
  else
53
- case @questions[from]
54
- when /^(yes|yep)/i
55
- @shortcuts[from][shortcut_name] = command_to_run
56
- @shortcuts[:all][shortcut_name] = command_to_run if for_all.to_s != ""
57
- update_shortcuts_file()
58
- respond "shortcut added", dest
59
- @questions.delete(from)
60
- when /^no/i
61
- respond "ok, I won't add it", dest
62
- @questions.delete(from)
80
+ #are you sure? to avoid overwriting existing
81
+ if answer.empty?
82
+ ask("The shortcut already exists, are you sure you want to overwrite it?", command, from, dest)
63
83
  else
64
- ask "I don't understand, yes or no?", command, from, dest
84
+ case answer
85
+ when /^(yes|yep)/i
86
+ @shortcuts[from][shortcut_name] = command_to_run
87
+ @shortcuts[:all][shortcut_name] = command_to_run if for_all.to_s != ""
88
+ update_shortcuts_file()
89
+ respond "shortcut added", dest
90
+ answer_delete(from)
91
+ when /^no/i
92
+ respond "ok, I won't add it", dest
93
+ answer_delete(from)
94
+ else
95
+ ask "I don't understand, yes or no?", command, from, dest
96
+ end
65
97
  end
66
98
  end
67
99
  end
@@ -14,15 +14,11 @@ class SlackSmartBot
14
14
  respond "Only admins can extend the rules. Admins on this channel: #{config.admins}", dest
15
15
  else
16
16
  #todo: add pagination for case more than 1000 channels on the workspace
17
- channels = client.web_client.conversations_list(
18
- types: "private_channel,public_channel",
19
- limit: "1000",
20
- exclude_archived: "true",
21
- ).channels
17
+ channels = get_channels()
22
18
 
23
19
  channel_found = channels.detect { |c| c.name == channel }
24
20
  get_channels_name_and_id()
25
- members = client.web_client.conversations_members(channel: @channels_id[channel]).members unless channel_found.nil?
21
+ members = get_channel_members(@channels_id[channel]) unless channel_found.nil?
26
22
  get_bots_created()
27
23
  channels_in_use = []
28
24
  @bots_created.each do |k, v|
@@ -39,7 +35,7 @@ class SlackSmartBot
39
35
  elsif !members.include?(user.id)
40
36
  respond "You need to join that channel first", dest
41
37
  elsif !members.include?(config[:nick_id])
42
- respond "You need to add first to the channel the smart bot user: #{config[:nick]}", dest
38
+ respond "You need to add first to the channel the smart bot user: <@#{config[:nick_id]}>", dest
43
39
  else
44
40
  channels_in_use.each do |channel_in_use|
45
41
  respond "The rules from channel <##{@channels_id[channel_in_use]}> are already in use on that channel", dest
@@ -13,6 +13,7 @@ class SlackSmartBot
13
13
  respond "zZzzzzZzzzzZZZZZZzzzzzzzz", dest
14
14
  @status = :paused
15
15
  @bots_created[@channel_id][:status] = :paused
16
+ update_bots_file()
16
17
  unless config.on_master_bot
17
18
  send_msg_channel config.master_channel, "Changed status on #{config.channel} to :paused"
18
19
  end
@@ -38,8 +38,14 @@ class SlackSmartBot
38
38
  respond "Routines on channel *#{rout_ch.get_values(:channel_name).values.flatten.uniq[0]}*", dest
39
39
  rout_ch.each do |k, v|
40
40
  msg = []
41
- ch != v[:dest] ? directm = " (*DM to #{v[:creator]}*)" : directm = ""
42
- msg << "*`#{k}`*#{directm}"
41
+ if v[:dest][0] == 'D'
42
+ extram = " (*DM to #{v[:creator]}*)"
43
+ elsif v[:dest] != ch
44
+ extram = " (*publish on <##{v[:dest]}>*)"
45
+ else
46
+ extram = ''
47
+ end
48
+ msg << "*`#{k}`*#{extram}"
43
49
  msg << "\tCreator: #{v[:creator]}"
44
50
  msg << "\tStatus: #{v[:status]}"
45
51
  msg << "\tEvery: #{v[:every]}" unless v[:every] == ""
@@ -12,6 +12,7 @@ class SlackSmartBot
12
12
  respond "This bot is running and listening from now on. You can pause again: pause this bot", dest
13
13
  @status = :on
14
14
  @bots_created[@channel_id][:status] = :on
15
+ update_bots_file()
15
16
  unless config.on_master_bot
16
17
  send_msg_channel config.master_channel, "Changed status on #{config.channel} to :on"
17
18
  end
@@ -3,7 +3,6 @@ class SlackSmartBot
3
3
  # help: `delete repl SESSION_NAME`
4
4
  # help: `delete irb SESSION_NAME`
5
5
  # help: `remove repl SESSION_NAME`
6
- # help:
7
6
  # help: Will delete the specified REPL
8
7
  # help: Only the creator of the REPL or an admin can delete REPLs
9
8
  # help:
@@ -20,6 +19,7 @@ class SlackSmartBot
20
19
  @repls.delete(session_name)
21
20
  update_repls()
22
21
  File.rename("#{config.path}/repl/#{@channel_id}/#{session_name}.input", "#{config.path}/repl/#{@channel_id}/#{session_name}_#{Time.now.strftime("%Y%m%d%H%M%S%N")}.deleted")
22
+ File.delete("#{config.path}/repl/#{@channel_id}/#{session_name}.output") if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.output")
23
23
  File.delete("#{config.path}/repl/#{@channel_id}/#{session_name}.run") if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
24
24
  respond "REPL #{session_name} deleted"
25
25
  else
@@ -2,10 +2,12 @@ class SlackSmartBot
2
2
  # help: ----------------------------------------------
3
3
  # help: `delete shortcut NAME`
4
4
  # help: `delete sc NAME`
5
+ # help: `delete global sc NAME`
5
6
  # help: It will delete the shortcut with the supplied name
7
+ # help: 'global' or 'generic' can only be used on Master channel.
6
8
  # help:
7
9
 
8
- def delete_shortcut(dest, user, shortcut, typem, command)
10
+ def delete_shortcut(dest, user, shortcut, typem, command, global)
9
11
  save_stats(__method__)
10
12
  unless typem == :on_extended
11
13
  from = user.name
@@ -15,32 +17,61 @@ class SlackSmartBot
15
17
  else
16
18
  deleted = false
17
19
 
18
- if !config.admins.include?(from) and @shortcuts[:all].include?(shortcut) and !@shortcuts[from].include?(shortcut)
19
- respond "Only the creator of the shortcut or an admin user can delete it", dest
20
- elsif (@shortcuts.keys.include?(from) and @shortcuts[from].keys.include?(shortcut)) or
21
- (config.admins.include?(from) and @shortcuts[:all].include?(shortcut))
22
- #are you sure? to avoid deleting by mistake
23
- unless @questions.keys.include?(from)
24
- ask("are you sure you want to delete it?", command, from, dest)
20
+ if global
21
+ if !config.on_master_bot or typem != :on_master
22
+ respond "It is only possible to delete global shortcuts from Master channel"
25
23
  else
26
- case @questions[from]
27
- when /^(yes|yep)/i
28
- @questions.delete(from)
29
- respond "shortcut deleted!", dest
30
- respond("#{shortcut}: #{@shortcuts[from][shortcut]}", dest) if @shortcuts.key?(from) and @shortcuts[from].key?(shortcut)
31
- respond("#{shortcut}: #{@shortcuts[:all][shortcut]}", dest) if @shortcuts.key?(:all) and @shortcuts[:all].key?(shortcut)
32
- @shortcuts[from].delete(shortcut) if @shortcuts.key?(from) and @shortcuts[from].key?(shortcut)
33
- @shortcuts[:all].delete(shortcut) if @shortcuts.key?(:all) and @shortcuts[:all].key?(shortcut)
24
+ if !config.admins.include?(from) and @shortcuts_global[:all].include?(shortcut) and
25
+ (!@shortcuts_global.key?(from) or !@shortcuts_global[from].include?(shortcut))
26
+ respond "Only the creator of the shortcut or an admin user can delete it"
27
+ elsif (@shortcuts_global.key?(from) and @shortcuts_global[from].keys.include?(shortcut)) or
28
+ (config.admins.include?(from) and @shortcuts_global[:all].include?(shortcut))
29
+
30
+ respond "global shortcut deleted!", dest
31
+ if @shortcuts_global.key?(from) and @shortcuts_global[from].key?(shortcut)
32
+ respond("#{shortcut}: #{@shortcuts_global[from][shortcut]}", dest)
33
+ elsif @shortcuts_global.key?(:all) and @shortcuts_global[:all].key?(shortcut)
34
+ respond("#{shortcut}: #{@shortcuts_global[:all][shortcut]}", dest)
35
+ end
36
+ @shortcuts_global[from].delete(shortcut) if @shortcuts_global.key?(from) and @shortcuts_global[from].key?(shortcut)
37
+ @shortcuts_global[:all].delete(shortcut) if @shortcuts_global.key?(:all) and @shortcuts_global[:all].key?(shortcut)
34
38
  update_shortcuts_file()
35
- when /^no/i
36
- @questions.delete(from)
37
- respond "ok, I won't delete it", dest
38
39
  else
39
- ask("I don't understand, are you sure you want to delete it? (yes or no)", command, from, dest)
40
+ respond 'shortcut not found'
40
41
  end
41
42
  end
42
43
  else
43
- respond "shortcut not found", dest
44
+ if !config.admins.include?(from) and @shortcuts[:all].include?(shortcut) and
45
+ (!@shortcuts.key?(from) or !@shortcuts[from].include?(shortcut))
46
+ respond "Only the creator of the shortcut or an admin user can delete it", dest
47
+ elsif (@shortcuts.keys.include?(from) and @shortcuts[from].keys.include?(shortcut)) or
48
+ (config.admins.include?(from) and @shortcuts[:all].include?(shortcut))
49
+ #are you sure? to avoid deleting by mistake
50
+ if answer.empty?
51
+ ask("are you sure you want to delete it?", command, from, dest)
52
+ else
53
+ case answer
54
+ when /^(yes|yep)/i
55
+ answer_delete(from)
56
+ respond "shortcut deleted!", dest
57
+ if @shortcuts.key?(from) and @shortcuts[from].key?(shortcut)
58
+ respond("#{shortcut}: #{@shortcuts[from][shortcut]}", dest)
59
+ elsif @shortcuts.key?(:all) and @shortcuts[:all].key?(shortcut)
60
+ respond("#{shortcut}: #{@shortcuts[:all][shortcut]}", dest)
61
+ end
62
+ @shortcuts[from].delete(shortcut) if @shortcuts.key?(from) and @shortcuts[from].key?(shortcut)
63
+ @shortcuts[:all].delete(shortcut) if @shortcuts.key?(:all) and @shortcuts[:all].key?(shortcut)
64
+ update_shortcuts_file()
65
+ when /^no/i
66
+ answer_delete(from)
67
+ respond "ok, I won't delete it", dest
68
+ else
69
+ ask("I don't understand, are you sure you want to delete it? (yes or no)", command, from, dest)
70
+ end
71
+ end
72
+ else
73
+ respond "shortcut not found", dest
74
+ end
44
75
  end
45
76
  end
46
77
  end
@@ -3,7 +3,6 @@ class SlackSmartBot
3
3
  # help: `get repl SESSION_NAME`
4
4
  # help: `get irb SESSION_NAME`
5
5
  # help: `get live SESSION_NAME`
6
- # help:
7
6
  # help: Will get the Ruby commands sent on that SESSION_NAME.
8
7
  # help:
9
8
  def get_repl(dest, user, session_name)
@@ -16,22 +15,23 @@ class SlackSmartBot
16
15
  Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
17
16
  Dir.mkdir("#{config.path}/repl/#{@channel_id}") unless Dir.exist?("#{config.path}/repl/#{@channel_id}")
18
17
  if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
19
- if @repls.key?(session_name) and @repls[session_name][:type] == :private and
18
+ if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
20
19
  @repls[session_name][:creator_name]!=user.name and
21
20
  !config.admins.include?(user.name)
22
21
  respond "The REPL with session name: #{session_name} is private", dest
23
22
  else
23
+ content = "require 'nice_http'\n"
24
24
  if @repls.key?(session_name)
25
25
  @repls[session_name][:accessed] = Time.now.to_s
26
26
  @repls[session_name][:gets] += 1
27
27
  update_repls()
28
28
  end
29
-
30
- content = "require 'nice_http'\n"
31
- if File.exist?("#{project_folder}/.smart-bot-repl")
29
+ if !@repls.key?(session_name) or
30
+ (File.exist?("#{project_folder}/.smart-bot-repl") and @repls[session_name][:type] != :private_clean and @repls[session_name][:type] != :public_clean)
32
31
  content += File.read("#{project_folder}/.smart-bot-repl")
33
32
  content += "\n"
34
33
  end
34
+
35
35
  content += File.read("#{config.path}/repl/#{@channel_id}/#{session_name}.run").gsub(/^(quit|exit|bye)$/i,'') #todo: remove this gsub it will never contain it
36
36
  File.write("#{config.path}/repl/#{@channel_id}/#{session_name}.rb", content, mode: "w+")
37
37
  send_file(dest, "REPL #{session_name} on #{config.channel}", "#{config.path}/repl/#{@channel_id}/#{session_name}.rb", " REPL #{session_name} on #{config.channel}", 'text/plain', "ruby")
@@ -5,15 +5,16 @@ class SlackSmartBot
5
5
  # help: `irb`
6
6
  # help: `repl SESSION_NAME`
7
7
  # help: `private repl SESSION_NAME`
8
+ # help: `clean repl SESSION_NAME`
8
9
  # help: `repl ENV_VAR=VALUE`
9
10
  # help: `repl SESSION_NAME ENV_VAR=VALUE ENV_VAR='VALUE'`
10
11
  # help: `repl SESSION_NAME: "DESCRIPTION"`
11
12
  # help: `repl SESSION_NAME: "DESCRIPTION" ENV_VAR=VALUE ENV_VAR='VALUE'`
12
- # help:
13
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`
17
+ # help: If 'clean' specified the repl won't pre execute the code written on the .smart-bot-repl file
17
18
  # help: To avoid a message to be treated, start the message with '-'.
18
19
  # help: Send _quit_, _bye_ or _exit_ to finish the session.
19
20
  # help: Send puts, print, p or pp if you want to print out something when using `run repl` later.
@@ -78,7 +79,13 @@ class SlackSmartBot
78
79
  }
79
80
  update_repls()
80
81
  end
81
-
82
+ react :running
83
+ if Thread.current[:ts].to_s == ''
84
+ @ts_react = Thread.current[:thread_ts]
85
+ else
86
+ @ts_react = Thread.current[:ts]
87
+ end
88
+
82
89
  message = "Session name: *#{session_name}*
83
90
  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`.
84
91
  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`.
@@ -96,6 +103,19 @@ class SlackSmartBot
96
103
  File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.output", "", mode: "a+")
97
104
  File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.run", "", mode: "a+")
98
105
 
106
+ if type != :private_clean and type != :public_clean
107
+ pre_execute = '
108
+ if File.exist?(\"./.smart-bot-repl\")
109
+ begin
110
+ eval(File.read(\"./.smart-bot-repl\"), bindme' + serialt + ')
111
+ rescue Exception => resp_repl
112
+ end
113
+ end
114
+ '
115
+ else
116
+ pre_execute = ''
117
+ end
118
+
99
119
  process_to_run = '
100
120
  ruby -e "' + env_vars.join("\n") + '
101
121
  require \"amazing_print\"
@@ -105,17 +125,12 @@ class SlackSmartBot
105
125
  (obj.methods - Object.methods)
106
126
  end
107
127
 
108
- file_input_repl = File.open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.input\", \"r\")
109
- if File.exist?(\"./.smart-bot-repl\")
110
- begin
111
- eval(File.read(\"./.smart-bot-repl\"), bindme' + serialt + ')
112
- rescue Exception => resp_repl
113
- end
114
- end
128
+ file_input_repl = File.open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.input\", \"r\")
129
+ ' + pre_execute + '
115
130
  while true do
116
131
  sleep 0.2
117
132
  code_to_run_repl = file_input_repl.read
118
- if code_to_run_repl.to_s!=''
133
+ if code_to_run_repl.to_s!=\"\"
119
134
  add_to_run_repl = true
120
135
  if code_to_run_repl.to_s.match?(/^quit$/i) or
121
136
  code_to_run_repl.to_s.match?(/^exit$/i) or
@@ -134,16 +149,16 @@ class SlackSmartBot
134
149
  end
135
150
  if resp_repl.to_s != \"\"
136
151
  if code_to_run_repl.match?(/^\s*p\s+/i)
137
- open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
152
+ open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
138
153
  f.puts \"\`\`\`\n#{resp_repl.inspect}\n\`\`\`\"
139
154
  }
140
155
  else
141
- open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
156
+ open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
142
157
  f.puts \"\`\`\`\n#{resp_repl.ai}\n\`\`\`\"
143
158
  }
144
159
  end
145
160
  unless error or !add_to_run_repl
146
- open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.run\", \"a+\") {|f|
161
+ open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.run\", \"a+\") {|f|
147
162
  f.puts code_to_run_repl
148
163
  }
149
164
  end
@@ -152,7 +167,6 @@ class SlackSmartBot
152
167
  end
153
168
  end"
154
169
  '
155
-
156
170
  unless rules_file.empty? # to get the project_folder
157
171
  begin
158
172
  eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
@@ -160,12 +174,12 @@ class SlackSmartBot
160
174
  end
161
175
  started = Time.now
162
176
  process_to_run = ("cd #{project_folder} &&" + process_to_run) if defined?(project_folder)
163
-
177
+
164
178
  stdin, stdout, stderr, wait_thr = Open3.popen3(process_to_run)
165
179
  timeout = 30 * 60 # 30 minutes
166
180
 
167
181
  file_output_repl = File.open("#{config.path}/repl/#{@channel_id}/#{session_name}.output", "r")
168
-
182
+ @repl_sessions[from][:pid] = wait_thr.pid
169
183
  while (wait_thr.status == 'run' or wait_thr.status == 'sleep') and @repl_sessions.key?(from)
170
184
  begin
171
185
  if (Time.now-@repl_sessions[from][:finished]) > timeout
@@ -173,6 +187,14 @@ class SlackSmartBot
173
187
  f.puts 'quit'
174
188
  }
175
189
  respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
190
+ unreact :running, @ts_react
191
+ pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
192
+ pids.each do |pid|
193
+ begin
194
+ Process.kill("KILL", pid)
195
+ rescue
196
+ end
197
+ end
176
198
  @repl_sessions.delete(from)
177
199
  break
178
200
  end
@@ -199,10 +221,12 @@ class SlackSmartBot
199
221
  code.gsub!("\\r", "\r")
200
222
  # Disabled for the moment since it is deleting lines with '}'
201
223
  #code.gsub!(/^\W*$/, "") #to remove special chars from slack when copy/pasting.
202
- if code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File") or
224
+ if code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File.") or
203
225
  code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
204
226
  code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
205
- code.include?("ENV") or code.match?(/=\s*IO/)
227
+ code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or
228
+ 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/)
206
230
  respond "Sorry I cannot run this due security reasons", dest
207
231
  else
208
232
  @repl_sessions[from][:input]<<code
@@ -212,6 +236,14 @@ class SlackSmartBot
212
236
  f.puts code
213
237
  }
214
238
  respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
239
+ unreact :running, @ts_react
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
246
+ end
215
247
  @repl_sessions.delete(from)
216
248
  when /^\s*-/i
217
249
  #ommit