slack-smart-bot 1.8.1 → 1.10.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.
- checksums.yaml +4 -4
- data/README.md +127 -21
- data/lib/slack/smart-bot/comm/ask.rb +55 -42
- data/lib/slack/smart-bot/comm/dont_understand.rb +2 -2
- data/lib/slack/smart-bot/comm/event_hello.rb +34 -0
- data/lib/slack/smart-bot/comm/get_channel_members.rb +13 -0
- data/lib/slack/smart-bot/comm/get_channels.rb +35 -0
- data/lib/slack/smart-bot/comm/get_user_info.rb +20 -0
- data/lib/slack/smart-bot/comm/get_users.rb +24 -0
- data/lib/slack/smart-bot/comm/react.rb +38 -8
- data/lib/slack/smart-bot/comm/respond.rb +219 -48
- data/lib/slack/smart-bot/comm/respond_direct.rb +2 -3
- data/lib/slack/smart-bot/comm/respond_thread.rb +5 -0
- data/lib/slack/smart-bot/comm/send_file.rb +38 -34
- data/lib/slack/smart-bot/comm/send_msg_channel.rb +27 -22
- data/lib/slack/smart-bot/comm/send_msg_user.rb +58 -33
- data/lib/slack/smart-bot/comm/unreact.rb +24 -7
- data/lib/slack/smart-bot/comm.rb +7 -1
- data/lib/slack/smart-bot/commands/general/add_announcement.rb +32 -0
- data/lib/slack/smart-bot/commands/general/bot_help.rb +68 -28
- data/lib/slack/smart-bot/commands/general/bot_stats.rb +314 -0
- data/lib/slack/smart-bot/commands/general/bot_status.rb +3 -5
- data/lib/slack/smart-bot/commands/general/bye_bot.rb +0 -7
- data/lib/slack/smart-bot/commands/general/delete_announcement.rb +34 -0
- data/lib/slack/smart-bot/commands/general/delete_share.rb +34 -0
- data/lib/slack/smart-bot/commands/general/hi_bot.rb +16 -11
- data/lib/slack/smart-bot/commands/general/leaderboard.rb +200 -0
- data/lib/slack/smart-bot/commands/general/see_announcements.rb +113 -0
- data/lib/slack/smart-bot/commands/general/see_favorite_commands.rb +54 -0
- data/lib/slack/smart-bot/commands/general/see_shares.rb +41 -0
- data/lib/slack/smart-bot/commands/general/see_statuses.rb +78 -0
- data/lib/slack/smart-bot/commands/general/share_messages.rb +58 -0
- data/lib/slack/smart-bot/commands/general/stop_using_rules.rb +11 -6
- data/lib/slack/smart-bot/commands/general/suggest_command.rb +30 -0
- data/lib/slack/smart-bot/commands/general/use_rules.rb +13 -16
- data/lib/slack/smart-bot/commands/general/whats_new.rb +19 -0
- data/lib/slack/smart-bot/commands/general_bot_commands.rb +243 -0
- data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +67 -38
- data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +49 -14
- data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +5 -7
- data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +4 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/pause_routine.rb +1 -0
- data/lib/slack/smart-bot/commands/on_bot/admin/remove_routine.rb +2 -3
- data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +6 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/see_result_routine.rb +32 -0
- data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +12 -4
- data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +4 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/start_routine.rb +1 -0
- data/lib/slack/smart-bot/commands/on_bot/admin/stop_using_rules_on.rb +2 -0
- data/lib/slack/smart-bot/commands/on_bot/admin_master/react_to.rb +32 -0
- data/lib/slack/smart-bot/commands/on_bot/admin_master/send_message.rb +24 -0
- data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +3 -5
- data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +54 -25
- data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +7 -9
- data/lib/slack/smart-bot/commands/on_bot/repl.rb +55 -25
- data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +36 -13
- data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +5 -7
- data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +4 -6
- data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +29 -13
- data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +55 -9
- data/lib/slack/smart-bot/commands/on_master/admin/kill_bot_on_channel.rb +4 -1
- data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +5 -3
- data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +2 -1
- data/lib/slack/smart-bot/commands/on_master/admin_master/publish_announcements.rb +32 -0
- data/lib/slack/smart-bot/commands/on_master/admin_master/set_general_message.rb +38 -0
- data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +49 -0
- data/lib/slack/smart-bot/commands/on_master/create_bot.rb +30 -21
- data/lib/slack/smart-bot/commands.rb +19 -1
- data/lib/slack/smart-bot/listen.rb +7 -8
- data/lib/slack/smart-bot/process.rb +373 -192
- data/lib/slack/smart-bot/process_first.rb +202 -104
- data/lib/slack/smart-bot/treat_message.rb +325 -186
- 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 +83 -30
- data/lib/slack/smart-bot/utils/get_bots_created.rb +4 -1
- data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +1 -7
- data/lib/slack/smart-bot/utils/get_help.rb +87 -35
- data/lib/slack/smart-bot/utils/get_shares.rb +12 -0
- data/lib/slack/smart-bot/utils/has_access.rb +12 -0
- data/lib/slack/smart-bot/utils/save_stats.rb +23 -8
- data/lib/slack/smart-bot/utils/save_status.rb +52 -0
- data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +6 -0
- data/lib/slack/smart-bot/utils.rb +5 -0
- data/lib/slack-smart-bot.rb +88 -47
- data/lib/slack-smart-bot_general_commands.rb +46 -0
- data/lib/slack-smart-bot_general_rules.rb +5 -2
- data/lib/slack-smart-bot_rules.rb +49 -23
- data/whats_new.txt +36 -0
- metadata +44 -13
- data/lib/slack/smart-bot/commands/on_bot/admin_master/bot_stats.rb +0 -195
@@ -7,6 +7,7 @@ class SlackSmartBot
|
|
7
7
|
# helpadmin: NAME: one word to identify the routine
|
8
8
|
# helpadmin: Examples:
|
9
9
|
# helpadmin: _start routine example_
|
10
|
+
# helpadmin: <https://github.com/MarioRuiz/slack-smart-bot#routines|more info>
|
10
11
|
# helpadmin:
|
11
12
|
|
12
13
|
def start_routine(dest, from, name)
|
@@ -3,6 +3,7 @@ class SlackSmartBot
|
|
3
3
|
# helpadmin: ----------------------------------------------
|
4
4
|
# helpadmin: `stop using rules on CHANNEL_NAME`
|
5
5
|
# helpadmin: it will stop using the extended rules on the specified channel.
|
6
|
+
# helpadmin: <https://github.com/MarioRuiz/slack-smart-bot#extending-rules-to-other-channels|more info>
|
6
7
|
# helpadmin:
|
7
8
|
|
8
9
|
def stop_using_rules_on(dest, user, from, channel, typem)
|
@@ -12,6 +13,7 @@ class SlackSmartBot
|
|
12
13
|
respond "Only admins can extend or stop using the rules. Admins on this channel: #{config.admins}", dest
|
13
14
|
else
|
14
15
|
get_bots_created()
|
16
|
+
channel = @channels_name[channel] if @channels_name.key?(channel)
|
15
17
|
if @bots_created[@channel_id][:extended].include?(channel)
|
16
18
|
@bots_created[@channel_id][:extended].delete(channel)
|
17
19
|
update_bots_file()
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
|
3
|
+
# helpadmin: ----------------------------------------------
|
4
|
+
# helpadmin: `react to #CHANNEL_NAME THREAD_ID EMOJIS`
|
5
|
+
# helpadmin: It will send the specified reactions as SmartBot
|
6
|
+
# helpadmin: You can use this command only if you are a Master admin user and if you are in a private conversation with the bot
|
7
|
+
# helpadmin: Examples:
|
8
|
+
# helpadmin: _react to #sales 1622550707.012100 :thumbsup:_
|
9
|
+
# helpadmin: _react to #sales p1622550707012100 :thumbsup:_
|
10
|
+
# helpadmin: _react to #sales p1622550707012100 :thumbsup: :heavy_check_mark: :bathtub:_
|
11
|
+
# helpadmin:
|
12
|
+
def react_to(dest, from, typem, to, thread_ts, emojis)
|
13
|
+
save_stats(__method__)
|
14
|
+
if config.masters.include?(from) and typem==:on_dm #master admin user
|
15
|
+
succs = []
|
16
|
+
emojis.split(' ').each do |emoji|
|
17
|
+
succs << (react emoji, thread_ts, to)
|
18
|
+
end
|
19
|
+
succs.uniq!
|
20
|
+
if succs.size == 1 and succs[0] == true
|
21
|
+
react :heavy_check_mark
|
22
|
+
elsif succs.size == 2
|
23
|
+
react :exclamation
|
24
|
+
else
|
25
|
+
react :x
|
26
|
+
end
|
27
|
+
else
|
28
|
+
respond "Only master admin users on a `pr`ivate conversation with the SmartBot can send reactions as SmartBot.", dest
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
|
3
|
+
# helpadmin: ----------------------------------------------
|
4
|
+
# helpadmin: `send message to @USER_NAME : MESSAGE`
|
5
|
+
# helpadmin: `send message to #CHANNEL_NAME : MESSAGE`
|
6
|
+
# helpadmin: `send message to #CHANNEL_NAME THREAD_ID : MESSAGE`
|
7
|
+
# helpadmin: It will send the specified message as SmartBot
|
8
|
+
# helpadmin: You can use this command only if you are a Master admin user and if you are in a private conversation with the bot
|
9
|
+
# helpadmin:
|
10
|
+
def send_message(dest, from, typem, to, thread_ts, message)
|
11
|
+
save_stats(__method__)
|
12
|
+
if config.masters.include?(from) and typem==:on_dm #master admin user
|
13
|
+
succ = (respond message, to, thread_ts: thread_ts, web_client: true)
|
14
|
+
if succ
|
15
|
+
react :heavy_check_mark
|
16
|
+
else
|
17
|
+
react :x
|
18
|
+
end
|
19
|
+
else
|
20
|
+
respond "Only master admin users on a private conversation with the SmartBot can send messages as SmartBot.", dest
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -3,23 +3,21 @@ 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
|
8
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
|
9
9
|
# help:
|
10
10
|
def delete_repl(dest, user, session_name)
|
11
11
|
#todo: add tests
|
12
12
|
save_stats(__method__)
|
13
|
-
if
|
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
|
13
|
+
if has_access?(__method__, user)
|
17
14
|
if @repls.key?(session_name)
|
18
15
|
Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
|
19
16
|
if config.admins.include?(user.name) or @repls[session_name].creator_name == user.name
|
20
17
|
@repls.delete(session_name)
|
21
18
|
update_repls()
|
22
19
|
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")
|
20
|
+
File.delete("#{config.path}/repl/#{@channel_id}/#{session_name}.output") if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.output")
|
23
21
|
File.delete("#{config.path}/repl/#{@channel_id}/#{session_name}.run") if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
|
24
22
|
respond "REPL #{session_name} deleted"
|
25
23
|
else
|
@@ -2,45 +2,74 @@ 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.
|
8
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#shortcuts|more info>
|
6
9
|
# help:
|
7
10
|
|
8
|
-
def delete_shortcut(dest, user, shortcut, typem, command)
|
11
|
+
def delete_shortcut(dest, user, shortcut, typem, command, global)
|
9
12
|
save_stats(__method__)
|
10
13
|
unless typem == :on_extended
|
11
14
|
from = user.name
|
12
|
-
if
|
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
|
15
|
+
if has_access?(__method__, user)
|
16
16
|
deleted = false
|
17
17
|
|
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)
|
18
|
+
if global
|
19
|
+
if !config.on_master_bot or typem != :on_master
|
20
|
+
respond "It is only possible to delete global shortcuts from Master channel"
|
25
21
|
else
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
22
|
+
if !config.admins.include?(from) and @shortcuts_global[:all].include?(shortcut) and
|
23
|
+
(!@shortcuts_global.key?(from) or !@shortcuts_global[from].include?(shortcut))
|
24
|
+
respond "Only the creator of the shortcut or an admin user can delete it"
|
25
|
+
elsif (@shortcuts_global.key?(from) and @shortcuts_global[from].keys.include?(shortcut)) or
|
26
|
+
(config.admins.include?(from) and @shortcuts_global[:all].include?(shortcut))
|
27
|
+
|
28
|
+
respond "global shortcut deleted!", dest
|
29
|
+
if @shortcuts_global.key?(from) and @shortcuts_global[from].key?(shortcut)
|
30
|
+
respond("#{shortcut}: #{@shortcuts_global[from][shortcut]}", dest)
|
31
|
+
elsif @shortcuts_global.key?(:all) and @shortcuts_global[:all].key?(shortcut)
|
32
|
+
respond("#{shortcut}: #{@shortcuts_global[:all][shortcut]}", dest)
|
33
|
+
end
|
34
|
+
@shortcuts_global[from].delete(shortcut) if @shortcuts_global.key?(from) and @shortcuts_global[from].key?(shortcut)
|
35
|
+
@shortcuts_global[:all].delete(shortcut) if @shortcuts_global.key?(:all) and @shortcuts_global[:all].key?(shortcut)
|
34
36
|
update_shortcuts_file()
|
35
|
-
when /^no/i
|
36
|
-
@questions.delete(from)
|
37
|
-
respond "ok, I won't delete it", dest
|
38
37
|
else
|
39
|
-
|
38
|
+
respond 'shortcut not found'
|
40
39
|
end
|
41
40
|
end
|
42
41
|
else
|
43
|
-
|
42
|
+
if !config.admins.include?(from) and @shortcuts[:all].include?(shortcut) and
|
43
|
+
(!@shortcuts.key?(from) or !@shortcuts[from].include?(shortcut))
|
44
|
+
respond "Only the creator of the shortcut or an admin user can delete it", dest
|
45
|
+
elsif (@shortcuts.keys.include?(from) and @shortcuts[from].keys.include?(shortcut)) or
|
46
|
+
(config.admins.include?(from) and @shortcuts[:all].include?(shortcut))
|
47
|
+
#are you sure? to avoid deleting by mistake
|
48
|
+
if answer.empty?
|
49
|
+
ask("are you sure you want to delete it?", command, from, dest)
|
50
|
+
else
|
51
|
+
case answer
|
52
|
+
when /^(yes|yep)/i
|
53
|
+
answer_delete(from)
|
54
|
+
respond "shortcut deleted!", dest
|
55
|
+
if @shortcuts.key?(from) and @shortcuts[from].key?(shortcut)
|
56
|
+
respond("#{shortcut}: #{@shortcuts[from][shortcut]}", dest)
|
57
|
+
elsif @shortcuts.key?(:all) and @shortcuts[:all].key?(shortcut)
|
58
|
+
respond("#{shortcut}: #{@shortcuts[:all][shortcut]}", dest)
|
59
|
+
end
|
60
|
+
@shortcuts[from].delete(shortcut) if @shortcuts.key?(from) and @shortcuts[from].key?(shortcut)
|
61
|
+
@shortcuts[:all].delete(shortcut) if @shortcuts.key?(:all) and @shortcuts[:all].key?(shortcut)
|
62
|
+
update_shortcuts_file()
|
63
|
+
when /^no/i
|
64
|
+
answer_delete(from)
|
65
|
+
respond "ok, I won't delete it", dest
|
66
|
+
else
|
67
|
+
ask("I don't understand, are you sure you want to delete it? (yes or no)", command, from, dest)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
else
|
71
|
+
respond "shortcut not found", dest
|
72
|
+
end
|
44
73
|
end
|
45
74
|
end
|
46
75
|
end
|
@@ -3,35 +3,33 @@ 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.
|
7
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
|
8
8
|
# help:
|
9
9
|
def get_repl(dest, user, session_name)
|
10
10
|
#todo: add tests
|
11
11
|
save_stats(__method__)
|
12
|
-
if
|
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
|
12
|
+
if has_access?(__method__, user)
|
16
13
|
Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
|
17
14
|
Dir.mkdir("#{config.path}/repl/#{@channel_id}") unless Dir.exist?("#{config.path}/repl/#{@channel_id}")
|
18
15
|
if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
|
19
|
-
if @repls.key?(session_name) and @repls[session_name][:type] == :private and
|
16
|
+
if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
|
20
17
|
@repls[session_name][:creator_name]!=user.name and
|
21
18
|
!config.admins.include?(user.name)
|
22
19
|
respond "The REPL with session name: #{session_name} is private", dest
|
23
20
|
else
|
21
|
+
content = "require 'nice_http'\n"
|
24
22
|
if @repls.key?(session_name)
|
25
23
|
@repls[session_name][:accessed] = Time.now.to_s
|
26
24
|
@repls[session_name][:gets] += 1
|
27
25
|
update_repls()
|
28
26
|
end
|
29
|
-
|
30
|
-
|
31
|
-
if File.exist?("#{project_folder}/.smart-bot-repl")
|
27
|
+
if !@repls.key?(session_name) or
|
28
|
+
(File.exist?("#{project_folder}/.smart-bot-repl") and @repls[session_name][:type] != :private_clean and @repls[session_name][:type] != :public_clean)
|
32
29
|
content += File.read("#{project_folder}/.smart-bot-repl")
|
33
30
|
content += "\n"
|
34
31
|
end
|
32
|
+
|
35
33
|
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
34
|
File.write("#{config.path}/repl/#{@channel_id}/#{session_name}.rb", content, mode: "w+")
|
37
35
|
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,20 +5,21 @@ 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.
|
20
21
|
# help: After 30 minutes of no communication with the Smart Bot the session will be dismissed.
|
21
|
-
# help: If you declare on your rules file a method called
|
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
23
|
# help: By default it will be automatically loaded the gems: string_pattern, nice_hash and nice_http
|
23
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
|
24
25
|
# help: If you want to see the methods of a class or module you created use _ls TheModuleOrClass_
|
@@ -28,14 +29,12 @@ class SlackSmartBot
|
|
28
29
|
# help: _repl CreateCustomer: "It creates a random customer for testing" LOCATION=spain HOST='https://10.30.40.50:8887'_
|
29
30
|
# help: _repl delete_logs_
|
30
31
|
# help: _private repl random-ssn_
|
32
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
|
31
33
|
# help:
|
32
34
|
def repl(dest, user, session_name, env_vars, rules_file, command, description, type)
|
33
35
|
#todo: add more tests
|
34
36
|
from = user.name
|
35
|
-
if
|
36
|
-
(!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
|
37
|
-
respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
|
38
|
-
else
|
37
|
+
if has_access?(__method__, user)
|
39
38
|
if !@repl_sessions.key?(from)
|
40
39
|
save_stats(__method__)
|
41
40
|
Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
|
@@ -78,7 +77,13 @@ class SlackSmartBot
|
|
78
77
|
}
|
79
78
|
update_repls()
|
80
79
|
end
|
81
|
-
|
80
|
+
react :running
|
81
|
+
if Thread.current[:ts].to_s == ''
|
82
|
+
@ts_react = Thread.current[:thread_ts]
|
83
|
+
else
|
84
|
+
@ts_react = Thread.current[:ts]
|
85
|
+
end
|
86
|
+
|
82
87
|
message = "Session name: *#{session_name}*
|
83
88
|
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
89
|
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 +101,19 @@ class SlackSmartBot
|
|
96
101
|
File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.output", "", mode: "a+")
|
97
102
|
File.write("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.run", "", mode: "a+")
|
98
103
|
|
104
|
+
if type != :private_clean and type != :public_clean
|
105
|
+
pre_execute = '
|
106
|
+
if File.exist?(\"./.smart-bot-repl\")
|
107
|
+
begin
|
108
|
+
eval(File.read(\"./.smart-bot-repl\"), bindme' + serialt + ')
|
109
|
+
rescue Exception => resp_repl
|
110
|
+
end
|
111
|
+
end
|
112
|
+
'
|
113
|
+
else
|
114
|
+
pre_execute = ''
|
115
|
+
end
|
116
|
+
|
99
117
|
process_to_run = '
|
100
118
|
ruby -e "' + env_vars.join("\n") + '
|
101
119
|
require \"amazing_print\"
|
@@ -105,17 +123,12 @@ class SlackSmartBot
|
|
105
123
|
(obj.methods - Object.methods)
|
106
124
|
end
|
107
125
|
|
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
|
126
|
+
file_input_repl = File.open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.input\", \"r\")
|
127
|
+
' + pre_execute + '
|
115
128
|
while true do
|
116
129
|
sleep 0.2
|
117
130
|
code_to_run_repl = file_input_repl.read
|
118
|
-
if code_to_run_repl.to_s
|
131
|
+
if code_to_run_repl.to_s!=\"\"
|
119
132
|
add_to_run_repl = true
|
120
133
|
if code_to_run_repl.to_s.match?(/^quit$/i) or
|
121
134
|
code_to_run_repl.to_s.match?(/^exit$/i) or
|
@@ -132,18 +145,18 @@ class SlackSmartBot
|
|
132
145
|
rescue Exception => resp_repl
|
133
146
|
error = true
|
134
147
|
end
|
135
|
-
|
148
|
+
unless error
|
136
149
|
if code_to_run_repl.match?(/^\s*p\s+/i)
|
137
|
-
open(\"' +
|
150
|
+
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
138
151
|
f.puts \"\`\`\`\n#{resp_repl.inspect}\n\`\`\`\"
|
139
152
|
}
|
140
153
|
else
|
141
|
-
open(\"' +
|
154
|
+
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
142
155
|
f.puts \"\`\`\`\n#{resp_repl.ai}\n\`\`\`\"
|
143
156
|
}
|
144
157
|
end
|
145
|
-
unless
|
146
|
-
open(\"' +
|
158
|
+
unless !add_to_run_repl
|
159
|
+
open(\"' + File.expand_path(config.path) + '/repl/' + @channel_id + '/' + session_name + '.run\", \"a+\") {|f|
|
147
160
|
f.puts code_to_run_repl
|
148
161
|
}
|
149
162
|
end
|
@@ -152,7 +165,6 @@ class SlackSmartBot
|
|
152
165
|
end
|
153
166
|
end"
|
154
167
|
'
|
155
|
-
|
156
168
|
unless rules_file.empty? # to get the project_folder
|
157
169
|
begin
|
158
170
|
eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
|
@@ -160,12 +172,12 @@ class SlackSmartBot
|
|
160
172
|
end
|
161
173
|
started = Time.now
|
162
174
|
process_to_run = ("cd #{project_folder} &&" + process_to_run) if defined?(project_folder)
|
163
|
-
|
175
|
+
|
164
176
|
stdin, stdout, stderr, wait_thr = Open3.popen3(process_to_run)
|
165
177
|
timeout = 30 * 60 # 30 minutes
|
166
178
|
|
167
179
|
file_output_repl = File.open("#{config.path}/repl/#{@channel_id}/#{session_name}.output", "r")
|
168
|
-
|
180
|
+
@repl_sessions[from][:pid] = wait_thr.pid
|
169
181
|
while (wait_thr.status == 'run' or wait_thr.status == 'sleep') and @repl_sessions.key?(from)
|
170
182
|
begin
|
171
183
|
if (Time.now-@repl_sessions[from][:finished]) > timeout
|
@@ -173,6 +185,14 @@ class SlackSmartBot
|
|
173
185
|
f.puts 'quit'
|
174
186
|
}
|
175
187
|
respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
|
188
|
+
unreact :running, @ts_react
|
189
|
+
pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
190
|
+
pids.each do |pid|
|
191
|
+
begin
|
192
|
+
Process.kill("KILL", pid)
|
193
|
+
rescue
|
194
|
+
end
|
195
|
+
end
|
176
196
|
@repl_sessions.delete(from)
|
177
197
|
break
|
178
198
|
end
|
@@ -199,10 +219,12 @@ class SlackSmartBot
|
|
199
219
|
code.gsub!("\\r", "\r")
|
200
220
|
# Disabled for the moment since it is deleting lines with '}'
|
201
221
|
#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
|
222
|
+
if code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File.") or
|
203
223
|
code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
|
204
224
|
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/)
|
225
|
+
code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or
|
226
|
+
code.match?(/=\s*File/) or code.match?(/=\s*Dir/) or code.match?(/<\s*File/) or code.match?(/<\s*Dir/) or
|
227
|
+
code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/)
|
206
228
|
respond "Sorry I cannot run this due security reasons", dest
|
207
229
|
else
|
208
230
|
@repl_sessions[from][:input]<<code
|
@@ -212,6 +234,14 @@ class SlackSmartBot
|
|
212
234
|
f.puts code
|
213
235
|
}
|
214
236
|
respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
|
237
|
+
unreact :running, @ts_react
|
238
|
+
pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
239
|
+
pids.each do |pid|
|
240
|
+
begin
|
241
|
+
Process.kill("KILL", pid)
|
242
|
+
rescue
|
243
|
+
end
|
244
|
+
end
|
215
245
|
@repl_sessions.delete(from)
|
216
246
|
when /^\s*-/i
|
217
247
|
#ommit
|
@@ -5,47 +5,70 @@ 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>
|
8
9
|
# help:
|
9
10
|
|
10
11
|
def ruby_code(dest, user, code, rules_file)
|
11
12
|
save_stats(__method__)
|
12
|
-
if
|
13
|
-
|
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
|
16
|
-
unless code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File") or
|
13
|
+
if has_access?(__method__, user)
|
14
|
+
unless code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File.") or
|
17
15
|
code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
|
18
16
|
code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
|
19
|
-
code.include?("ENV") or code.match?(/=\s*IO/)
|
17
|
+
code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or code.match?(/=\s*IO/) or
|
18
|
+
code.match?(/=\s*File/) or code.match?(/=\s*Dir/) or code.match?(/<\s*File/) or code.match?(/<\s*Dir/) or
|
19
|
+
code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/)
|
20
|
+
react :running
|
20
21
|
unless rules_file.empty?
|
21
22
|
begin
|
22
23
|
eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
|
23
24
|
end
|
24
25
|
end
|
25
26
|
|
26
|
-
respond "Running", dest if code.size >
|
27
|
+
respond "Running", dest if code.size > 200
|
27
28
|
|
28
29
|
begin
|
29
30
|
code.gsub!(/^\W*$/, "") #to remove special chars from slack when copy/pasting
|
31
|
+
code.gsub!('$','\$') #to take $ as literal, fex: puts '$lolo' => puts '\$lolo'
|
30
32
|
ruby = "ruby -e \"#{code.gsub('"', '\"')}\""
|
31
33
|
if defined?(project_folder) and project_folder.to_s != "" and Dir.exist?(project_folder)
|
32
34
|
ruby = ("cd #{project_folder} &&" + ruby)
|
33
35
|
else
|
34
36
|
def project_folder() "" end
|
35
37
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
|
39
|
+
stdin, stdout, stderr, wait_thr = Open3.popen3(ruby)
|
40
|
+
timeout = timeoutt = 20
|
41
|
+
procstart = Time.now
|
42
|
+
while (wait_thr.status == 'run' or wait_thr.status == 'sleep') and timeout > 0
|
43
|
+
timeout -= 0.1
|
44
|
+
sleep 0.1
|
45
|
+
end
|
46
|
+
if timeout > 0
|
47
|
+
stdout = stdout.read
|
48
|
+
stderr = stderr.read
|
49
|
+
if stderr == ""
|
50
|
+
if stdout == ""
|
51
|
+
respond "Nothing returned. Remember you need to use p or puts to print", dest
|
52
|
+
else
|
53
|
+
respond stdout, dest
|
54
|
+
end
|
40
55
|
else
|
41
|
-
respond stdout, dest
|
56
|
+
respond "#{stderr}\n#{stdout}", dest
|
42
57
|
end
|
43
58
|
else
|
44
|
-
respond "#{
|
59
|
+
respond "The process didn't finish in #{timeoutt} secs so it was aborted. Timeout!"
|
60
|
+
pids = `pgrep -P #{wait_thr.pid}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
61
|
+
pids.each do |pid|
|
62
|
+
begin
|
63
|
+
Process.kill("KILL", pid)
|
64
|
+
rescue
|
65
|
+
end
|
66
|
+
end
|
45
67
|
end
|
46
68
|
rescue Exception => exc
|
47
69
|
respond exc, dest
|
48
70
|
end
|
71
|
+
unreact :running
|
49
72
|
else
|
50
73
|
respond "Sorry I cannot run this due security reasons", dest
|
51
74
|
end
|