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.
- checksums.yaml +4 -4
- data/README.md +18 -11
- data/lib/slack-smart-bot.rb +43 -44
- data/lib/slack-smart-bot_rules.rb +6 -6
- data/lib/slack/smart-bot/comm.rb +5 -1
- data/lib/slack/smart-bot/comm/ask.rb +12 -5
- data/lib/slack/smart-bot/comm/dont_understand.rb +1 -1
- data/lib/slack/smart-bot/comm/event_hello.rb +30 -0
- data/lib/slack/smart-bot/comm/get_channel_members.rb +8 -0
- data/lib/slack/smart-bot/comm/get_channels.rb +20 -0
- data/lib/slack/smart-bot/comm/get_user_info.rb +16 -0
- data/lib/slack/smart-bot/comm/react.rb +21 -8
- data/lib/slack/smart-bot/comm/respond.rb +10 -5
- data/lib/slack/smart-bot/comm/send_msg_channel.rb +2 -2
- data/lib/slack/smart-bot/comm/send_msg_user.rb +4 -4
- data/lib/slack/smart-bot/comm/unreact.rb +21 -8
- data/lib/slack/smart-bot/commands.rb +3 -1
- data/lib/slack/smart-bot/commands/general/bot_help.rb +16 -3
- data/lib/slack/smart-bot/commands/general/bot_stats.rb +313 -0
- data/lib/slack/smart-bot/commands/general/bot_status.rb +1 -1
- data/lib/slack/smart-bot/commands/general/bye_bot.rb +1 -1
- data/lib/slack/smart-bot/commands/general/hi_bot.rb +1 -1
- data/lib/slack/smart-bot/commands/general/use_rules.rb +2 -6
- data/lib/slack/smart-bot/commands/general/whats_new.rb +19 -0
- data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +65 -33
- data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +3 -7
- data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +1 -0
- data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +8 -2
- data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +1 -0
- data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +1 -1
- data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +52 -21
- data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +5 -5
- data/lib/slack/smart-bot/commands/on_bot/repl.rb +50 -18
- data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +34 -9
- data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +2 -3
- data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +1 -1
- data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +27 -9
- data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +14 -1
- data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +3 -3
- data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +1 -1
- data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +41 -0
- data/lib/slack/smart-bot/commands/on_master/create_bot.rb +4 -8
- data/lib/slack/smart-bot/listen.rb +6 -5
- data/lib/slack/smart-bot/process.rb +227 -188
- data/lib/slack/smart-bot/process_first.rb +104 -87
- data/lib/slack/smart-bot/treat_message.rb +98 -38
- data/lib/slack/smart-bot/utils.rb +2 -0
- data/lib/slack/smart-bot/utils/answer.rb +18 -0
- data/lib/slack/smart-bot/utils/answer_delete.rb +15 -0
- data/lib/slack/smart-bot/utils/build_help.rb +57 -5
- data/lib/slack/smart-bot/utils/create_routine_thread.rb +11 -2
- data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +1 -7
- data/lib/slack/smart-bot/utils/get_help.rb +79 -17
- data/lib/slack/smart-bot/utils/save_stats.rb +21 -8
- data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +6 -0
- data/whats_new.txt +18 -0
- metadata +21 -12
- 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
|
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 =
|
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 =
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
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 =
|
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 =
|
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:
|
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
|
-
|
42
|
-
|
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
|
19
|
-
|
20
|
-
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
|
40
|
+
respond 'shortcut not found'
|
40
41
|
end
|
41
42
|
end
|
42
43
|
else
|
43
|
-
|
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
|
-
|
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(\"' +
|
109
|
-
|
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(\"' +
|
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(\"' +
|
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(\"' +
|
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
|