slack-smart-bot 1.8.2 → 1.9.1

Sign up to get free protection for your applications and to get access to all the features.
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