slack-smart-bot 1.6.7 → 1.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -2
- data/lib/slack-smart-bot.rb +13 -8
- data/lib/slack-smart-bot_general_rules.rb +33 -0
- data/lib/slack-smart-bot_rules.rb +80 -84
- data/lib/slack/smart-bot/comm.rb +2 -1
- data/lib/slack/smart-bot/comm/react.rb +1 -1
- data/lib/slack/smart-bot/comm/respond.rb +1 -1
- data/lib/slack/smart-bot/comm/send_file.rb +28 -12
- data/lib/slack/smart-bot/comm/unreact.rb +16 -0
- data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +33 -9
- data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +1 -1
- data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +1 -0
- data/lib/slack/smart-bot/commands/on_bot/admin_master/bot_stats.rb +88 -15
- data/lib/slack/smart-bot/commands/on_bot/repl.rb +19 -32
- data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +12 -3
- data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +1 -1
- data/lib/slack/smart-bot/commands/on_master/create_bot.rb +5 -0
- data/lib/slack/smart-bot/process.rb +13 -8
- data/lib/slack/smart-bot/treat_message.rb +24 -26
- data/lib/slack/smart-bot/utils/create_routine_thread.rb +37 -6
- data/lib/slack/smart-bot/utils/get_help.rb +5 -1
- metadata +14 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d35053e4911d77ae985b29fbd1564636eb0d078b38106601c2e126a401a03d5b
|
4
|
+
data.tar.gz: d5aadeedbdbe0c7a6528cbeac1f1bb0990d88cf93fdb9f0a085cf9be9ec17c2d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19ed3708f96befda1f755358eb847facba2afaa62f6a92e08e7f68143e51659ef75ab7ef925c0d07e9a2b02072dcd02ddeda89697855363e50e84dc3f1970af6
|
7
|
+
data.tar.gz: d40d1ac18d32074994c2c4ed3b835b74ada4e9ff46bbb5a7122df0f18f1b44eb777a6351ddaad3dd00b8bc129dbb62aee75572b9f3fe625e4fa972add2083107
|
data/README.md
CHANGED
@@ -124,6 +124,7 @@ def rules(user, command, processed, dest)
|
|
124
124
|
respond "zZzzzzzZZZZZZzzzzzzz!"
|
125
125
|
react :sleeping
|
126
126
|
sleep 10
|
127
|
+
unreact :sleeping
|
127
128
|
react :sunny
|
128
129
|
when /no/i, /nope/i, /cancel/i
|
129
130
|
@questions.delete(from)
|
@@ -152,6 +153,8 @@ def rules(user, command, processed, dest)
|
|
152
153
|
else
|
153
154
|
respond "#{user.name}: #{stdout} #{stderr}"
|
154
155
|
end
|
156
|
+
|
157
|
+
unreact :runner
|
155
158
|
|
156
159
|
else
|
157
160
|
unless processed
|
@@ -159,8 +162,11 @@ def rules(user, command, processed, dest)
|
|
159
162
|
end
|
160
163
|
end
|
161
164
|
end
|
162
|
-
|
163
165
|
```
|
166
|
+
|
167
|
+
Also you can add general rules that will be available on all Smart Bot channels to `./rules/general_rules.rb`
|
168
|
+
|
169
|
+
|
164
170
|
### How to access the Smart Bot
|
165
171
|
You can access the bot directly on the MASTER CHANNEL, on a secondary channel where the bot is running and directly by opening a private chat with the bot, in this case the conversation will be just between you and the bot.
|
166
172
|
|
@@ -380,7 +386,7 @@ Example:
|
|
380
386
|
To see available shortcuts: **_`see shortcuts`_** and to delete a particular shortcut: **_`delete shortcut NAME`_**
|
381
387
|
|
382
388
|
### Routines
|
383
|
-
To add specific commands to be run automatically every certain amount of time or a specific time: **_`add routine NAME every NUMBER PERIOD COMMAND`_** or **_`add routine NAME at TIME COMMAND`_
|
389
|
+
To add specific commands to be run automatically every certain amount of time or a specific time: **_`add routine NAME every NUMBER PERIOD COMMAND`_** or **_`add routine NAME at TIME COMMAND`_**. Also just before the command you can supply the channel where you want to publish the results, if not channel supplied then it would be the SmartBot Channel or on the DM if the command is run from there. Remember the SmartBot needs to have access to the channel where you want to publish.
|
384
390
|
|
385
391
|
If you want to hide the routine executions use `add silent routine`. It won't show the routine name when executing.
|
386
392
|
|
@@ -388,6 +394,8 @@ Examples:
|
|
388
394
|
>**_`add routine run_tests every 3h !run tests on customers`_**
|
389
395
|
>**_`add routine clean_db at 17:05 !clean customers temp db`_**
|
390
396
|
>**_`add silent routine clean_db at 17:05 !clean customers temp db`_**
|
397
|
+
>**_`add routine clean_custdb on Mondays at 05:00 !clean customers db`_**
|
398
|
+
>**_`add routine clean_custdb on Tuesdays at 09:00 #SREChannel !clean customers db`_**
|
391
399
|
|
392
400
|
Also instead of adding a Command to be executed, you can attach a file, then the routine will be created and the attached file will be executed on the criteria specified. Only Master Admins are allowed to use it this way.
|
393
401
|
|
data/lib/slack-smart-bot.rb
CHANGED
@@ -36,13 +36,7 @@ class SlackSmartBot
|
|
36
36
|
else
|
37
37
|
config[:path] = '.'
|
38
38
|
end
|
39
|
-
|
40
|
-
config[:silent] = true
|
41
|
-
elsif ENV['BOT_SILENT'].to_s == 'false'
|
42
|
-
config[:silent] = false
|
43
|
-
else
|
44
|
-
config[:silent] = false unless config.key?(:silent)
|
45
|
-
end
|
39
|
+
config[:silent] = false unless config.key?(:silent)
|
46
40
|
config[:testing] = false unless config.key?(:testing)
|
47
41
|
config[:simulate] = false unless config.key?(:simulate)
|
48
42
|
config[:stats] = false unless config.key?(:stats)
|
@@ -183,10 +177,20 @@ class SlackSmartBot
|
|
183
177
|
get_rules_imported()
|
184
178
|
|
185
179
|
begin
|
180
|
+
#todo: take in consideration the case that the value supplied on config.masters and config.admins are the ids and not the user names
|
186
181
|
@admin_users_id = []
|
182
|
+
@master_admin_users_id = []
|
187
183
|
config.admins.each do |au|
|
188
184
|
user_info = client.web_client.users_info(user: "@#{au}")
|
189
185
|
@admin_users_id << user_info.user.id
|
186
|
+
if config.masters.include?(au)
|
187
|
+
@master_admin_users_id << user_info.user.id
|
188
|
+
end
|
189
|
+
sleep 1
|
190
|
+
end
|
191
|
+
(config.masters-config.admins).each do |au|
|
192
|
+
user_info = client.web_client.users_info(user: "@#{au}")
|
193
|
+
@master_admin_users_id << user_info.user.id
|
190
194
|
sleep 1
|
191
195
|
end
|
192
196
|
rescue Slack::Web::Api::Errors::TooManyRequestsError
|
@@ -210,7 +214,8 @@ class SlackSmartBot
|
|
210
214
|
if version_remote != VERSION
|
211
215
|
version_message = ". There is a new available version: #{version_remote}."
|
212
216
|
end
|
213
|
-
|
217
|
+
if !config[:silent] or ENV['BOT_SILENT'].to_s == 'false'
|
218
|
+
ENV['BOT_SILENT'] = 'true' if config[:silent] == 'true' and ENV['BOT_SILENT'].to_s != 'true'
|
214
219
|
respond "Smart Bot started v#{VERSION}#{version_message}\nIf you want to know what I can do for you: `bot help`.\n`bot rules` if you want to display just the specific rules of this channel.\nYou can talk to me privately if you prefer it."
|
215
220
|
end
|
216
221
|
@routines.each do |ch, rout|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# add here the general rules you will be using in all Smart Bots
|
2
|
+
def general_rules(user, command, processed, dest, files = [], rules_file = "")
|
3
|
+
from = user.name
|
4
|
+
display_name = user.profile.display_name
|
5
|
+
|
6
|
+
begin
|
7
|
+
case command
|
8
|
+
|
9
|
+
# help: ----------------------------------------------
|
10
|
+
# help: `echo SOMETHING`
|
11
|
+
# help: `INTEGER echo SOMETHING`
|
12
|
+
# help: repeats SOMETHING. If INTEGER supplied then that number of times.
|
13
|
+
# help: Examples:
|
14
|
+
# help: _echo I am the Smart Bot_
|
15
|
+
# help: _100 echo :heart:_
|
16
|
+
when /^(\d*)\s*echo\s(.+)/i
|
17
|
+
save_stats :echo
|
18
|
+
$1.to_s == '' ? times = 1 : times = $1.to_i
|
19
|
+
respond ($2*times).to_s
|
20
|
+
|
21
|
+
else
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
return true
|
25
|
+
rescue => exception
|
26
|
+
if defined?(@logger)
|
27
|
+
@logger.fatal exception
|
28
|
+
respond "Unexpected error!! Please contact an admin to solve it: <@#{ADMIN_USERS.join(">, <@")}>"
|
29
|
+
else
|
30
|
+
puts exception
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -38,102 +38,98 @@ def rules(user, command, processed, dest, files = [], rules_file = "")
|
|
38
38
|
from = user.name
|
39
39
|
display_name = user.profile.display_name
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
load "#{config.path}/rules/general_rules.rb"
|
42
|
+
|
43
|
+
unless general_rules(user, command, processed, dest, files, rules_file)
|
44
|
+
begin
|
45
|
+
case command
|
43
46
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
react :monkey_face
|
53
|
-
|
54
|
-
# help: ----------------------------------------------
|
55
|
-
# help: `go to sleep`
|
56
|
-
# help: it will sleep the bot for 5 seconds
|
57
|
-
# help:
|
58
|
-
when /^go\sto\ssleep/i
|
59
|
-
save_stats :to_to_sleep
|
60
|
-
unless @questions.keys.include?(from)
|
61
|
-
ask "do you want me to take a siesta?"
|
62
|
-
else
|
63
|
-
case @questions[from]
|
64
|
-
when /yes/i, /yep/i, /sure/i
|
65
|
-
@questions.delete(from)
|
66
|
-
respond "I'll be sleeping for 5 secs... just for you"
|
67
|
-
respond "zZzzzzzZZZZZZzzzzzzz!"
|
68
|
-
react :sleeping
|
69
|
-
sleep 5
|
70
|
-
react :sunny
|
71
|
-
when /no/i, /nope/i, /cancel/i
|
72
|
-
@questions.delete(from)
|
73
|
-
respond "Thanks, I'm happy to be awake"
|
47
|
+
# help: ----------------------------------------------
|
48
|
+
# help: `go to sleep`
|
49
|
+
# help: it will sleep the bot for 5 seconds
|
50
|
+
# help:
|
51
|
+
when /^go\sto\ssleep/i
|
52
|
+
save_stats :go_to_sleep
|
53
|
+
unless @questions.keys.include?(from)
|
54
|
+
ask "do you want me to take a siesta?"
|
74
55
|
else
|
75
|
-
|
76
|
-
|
56
|
+
case @questions[from]
|
57
|
+
when /yes/i, /yep/i, /sure/i
|
58
|
+
@questions.delete(from)
|
59
|
+
respond "I'll be sleeping for 5 secs... just for you"
|
60
|
+
respond "zZzzzzzZZZZZZzzzzzzz!"
|
61
|
+
react :sleeping
|
62
|
+
sleep 5
|
63
|
+
unreact :sleeping
|
64
|
+
react :sunny
|
65
|
+
when /no/i, /nope/i, /cancel/i
|
66
|
+
@questions.delete(from)
|
67
|
+
respond "Thanks, I'm happy to be awake"
|
68
|
+
else
|
69
|
+
respond "I don't understand"
|
70
|
+
ask "are you sure do you want me to sleep? (yes or no)"
|
71
|
+
end
|
77
72
|
end
|
78
|
-
end
|
79
73
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
74
|
+
# help: ----------------------------------------------
|
75
|
+
# help: `run something`
|
76
|
+
# help: It will run the process and report the results when done
|
77
|
+
# help:
|
78
|
+
when /^run something/i
|
79
|
+
save_stats :run_something
|
80
|
+
react :runner
|
87
81
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
if
|
93
|
-
|
82
|
+
process_to_run = "ruby -v"
|
83
|
+
process_to_run = ("cd #{project_folder} &&" + process_to_run) if defined?(project_folder)
|
84
|
+
stdout, stderr, status = Open3.capture3(process_to_run)
|
85
|
+
unreact :runner
|
86
|
+
if stderr == ""
|
87
|
+
if stdout == ""
|
88
|
+
respond "#{display_name}: Nothing returned."
|
89
|
+
else
|
90
|
+
respond "#{display_name}: #{stdout}"
|
91
|
+
end
|
94
92
|
else
|
95
|
-
respond "#{display_name}: #{stdout}"
|
93
|
+
respond "#{display_name}: #{stdout} #{stderr}"
|
96
94
|
end
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
# respond "the message", '@theuser'
|
114
|
-
# # send 'the message' to a specific user id as direct message
|
115
|
-
# respond "the message", 'US3344D3'
|
95
|
+
|
96
|
+
# Emoticons you can use with `react` command https://www.webfx.com/tools/emoji-cheat-sheet/
|
97
|
+
|
98
|
+
# Examples for respond and respond_direct
|
99
|
+
# # send 'the message' to the channel or direct message where the command was written
|
100
|
+
# respond "the message"
|
101
|
+
# # send 'the message' privately as a direct message to the user that sent the command
|
102
|
+
# respond_direct "the message"
|
103
|
+
# # send 'the message' to a specific channel name
|
104
|
+
# respond "the message", 'my_channel'
|
105
|
+
# # send 'the message' to a specific channel id
|
106
|
+
# respond "the message", 'CSU34D33'
|
107
|
+
# # send 'the message' to a specific user as direct message
|
108
|
+
# respond "the message", '@theuser'
|
109
|
+
# # send 'the message' to a specific user id as direct message
|
110
|
+
# respond "the message", 'US3344D3'
|
116
111
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
112
|
+
# Example downloading a file from slack
|
113
|
+
# if !files.nil? and files.size == 1 and files[0].filetype == 'yaml'
|
114
|
+
# require 'nice_http'
|
115
|
+
# http = NiceHttp.new(host: "https://files.slack.com", headers: { "Authorization" => "Bearer #{config[:token]}" })
|
116
|
+
# http.get(files[0].url_private_download, save_data: './tmp/')
|
117
|
+
# end
|
123
118
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
119
|
+
# Examples sending a file to slack:
|
120
|
+
# send_file(to, msg, filepath, title, format, type = "text")
|
121
|
+
# send_file(dest, 'the message', "#{project_folder}/temp/logs_ptBI.log", 'title', 'text/plain', "text")
|
122
|
+
# send_file(dest, 'the message', "#{project_folder}/temp/example.jpeg", 'title', 'image/jpeg', "jpg")
|
128
123
|
|
129
124
|
|
130
|
-
|
131
|
-
|
132
|
-
|
125
|
+
else
|
126
|
+
unless processed
|
127
|
+
dont_understand()
|
128
|
+
end
|
133
129
|
end
|
130
|
+
rescue => exception
|
131
|
+
@logger.fatal exception
|
132
|
+
respond "Unexpected error!! Please contact an admin to solve it: <@#{config.admins.join(">, <@")}>"
|
134
133
|
end
|
135
|
-
rescue => exception
|
136
|
-
@logger.fatal exception
|
137
|
-
respond "Unexpected error!! Please contact an admin to solve it: <@#{config.admins.join(">, <@")}>"
|
138
134
|
end
|
139
135
|
end
|
data/lib/slack/smart-bot/comm.rb
CHANGED
@@ -2,7 +2,7 @@ class SlackSmartBot
|
|
2
2
|
# list of available emojis: https://www.webfx.com/tools/emoji-cheat-sheet/
|
3
3
|
# react(:thumbsup)
|
4
4
|
def react(emoji, parent=false)
|
5
|
-
if parent
|
5
|
+
if parent or Thread.current[:ts].to_s == ''
|
6
6
|
ts = Thread.current[:thread_ts]
|
7
7
|
else
|
8
8
|
ts = Thread.current[:ts]
|
@@ -38,7 +38,7 @@ class SlackSmartBot
|
|
38
38
|
f.puts "|#{dest}|#{config[:nick_id]}|#{msg}"
|
39
39
|
}
|
40
40
|
end
|
41
|
-
elsif dest[0] == "D" or dest[0] == "U" # Direct message
|
41
|
+
elsif dest[0] == "D" or dest[0] == "U" or dest[0] == "W" # Direct message
|
42
42
|
send_msg_user(dest, msg)
|
43
43
|
elsif dest[0] == "@"
|
44
44
|
begin
|
@@ -3,9 +3,12 @@ class SlackSmartBot
|
|
3
3
|
#to send a file to an user or channel
|
4
4
|
#send_file(dest, 'the message', "#{project_folder}/temp/logs_ptBI.log", 'message to be sent', 'text/plain', "text")
|
5
5
|
#send_file(dest, 'the message', "#{project_folder}/temp/example.jpeg", 'message to be sent', 'image/jpeg', "jpg")
|
6
|
-
|
6
|
+
#send_file(dest, 'the message', "", 'message to be sent', 'text/plain', "ruby", content: "the content to be sent when no file supplied")
|
7
|
+
#send_file(dest, 'the message', "myfile.rb", 'message to be sent', 'text/plain', "ruby", content: "the content to be sent when no file supplied")
|
8
|
+
def send_file(to, msg, file, title, format, type = "text", content: '')
|
7
9
|
unless config[:simulate]
|
8
|
-
if
|
10
|
+
file = 'myfile' if file.to_s == '' and content!=''
|
11
|
+
if to[0] == "U" or to[0] == "W" #user
|
9
12
|
im = client.web_client.im_open(user: to)
|
10
13
|
channel = im["channel"]["id"]
|
11
14
|
else
|
@@ -18,16 +21,29 @@ class SlackSmartBot
|
|
18
21
|
ts = nil
|
19
22
|
end
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
if content.to_s == ''
|
25
|
+
client.web_client.files_upload(
|
26
|
+
channels: channel,
|
27
|
+
as_user: true,
|
28
|
+
file: Faraday::UploadIO.new(file, format),
|
29
|
+
title: title,
|
30
|
+
filename: file,
|
31
|
+
filetype: type,
|
32
|
+
initial_comment: msg,
|
33
|
+
thread_ts: ts
|
34
|
+
)
|
35
|
+
else
|
36
|
+
client.web_client.files_upload(
|
37
|
+
channels: channel,
|
38
|
+
as_user: true,
|
39
|
+
content: content,
|
40
|
+
title: title,
|
41
|
+
filename: file,
|
42
|
+
filetype: type,
|
43
|
+
initial_comment: msg,
|
44
|
+
thread_ts: ts
|
45
|
+
)
|
46
|
+
end
|
31
47
|
end
|
32
48
|
end
|
33
49
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
# list of available emojis: https://www.webfx.com/tools/emoji-cheat-sheet/
|
3
|
+
# unreact(:thumbsup)
|
4
|
+
def unreact(emoji, parent=false)
|
5
|
+
if parent
|
6
|
+
ts = Thread.current[:thread_ts]
|
7
|
+
else
|
8
|
+
ts = Thread.current[:ts]
|
9
|
+
end
|
10
|
+
begin
|
11
|
+
client.web_client.reactions_remove(channel: Thread.current[:dest], name: emoji, timestamp: ts)
|
12
|
+
rescue Exception => stack
|
13
|
+
@logger.warn stack
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,10 +1,14 @@
|
|
1
1
|
class SlackSmartBot
|
2
2
|
# helpadmin: ----------------------------------------------
|
3
3
|
# helpadmin: `add routine NAME every NUMBER PERIOD COMMAND`
|
4
|
+
# helpadmin: `add routine NAME every NUMBER PERIOD #CHANNEL COMMAND`
|
4
5
|
# helpadmin: `add routine NAME every NUMBER PERIOD`
|
5
6
|
# helpadmin: `add silent routine NAME every NUMBER PERIOD`
|
6
7
|
# helpadmin: `create routine NAME every NUMBER PERIOD`
|
7
8
|
# helpadmin: `add routine NAME at TIME COMMAND`
|
9
|
+
# helpadmin: `add routine NAME at TIME #CHANNEL COMMAND`
|
10
|
+
# helpadmin: `add routine NAME on DAYWEEK at TIME COMMAND`
|
11
|
+
# helpadmin: `add routine NAME on DAYWEEK at TIME #CHANNEL COMMAND`
|
8
12
|
# helpadmin: `add routine NAME at TIME`
|
9
13
|
# helpadmin: `add silent routine NAME at TIME`
|
10
14
|
# helpadmin: `create routine NAME at TIME`
|
@@ -15,14 +19,18 @@ class SlackSmartBot
|
|
15
19
|
# helpadmin: NUMBER: Integer
|
16
20
|
# helpadmin: PERIOD: days, d, hours, h, minutes, mins, min, m, seconds, secs, sec, s
|
17
21
|
# helpadmin: TIME: time at format HH:MM:SS
|
22
|
+
# helpadmin: DAYWEEK: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday. And their plurals.
|
23
|
+
# helpadmin: #CHANNEL: the destination channel where the results will be published. If not supplied then the bot channel by default or a DM if the command is run from a DM.
|
18
24
|
# helpadmin: COMMAND: any valid smart bot command or rule
|
19
25
|
# helpadmin: Examples:
|
20
26
|
# helpadmin: _add routine example every 30s ruby puts 'a'_
|
21
27
|
# helpadmin: _add routine example every 3 days ruby puts 'a'_
|
22
28
|
# helpadmin: _add routine example at 17:05 ruby puts 'a'_
|
23
|
-
# helpadmin: _create silent routine every 12 hours !Run customer tests_
|
29
|
+
# helpadmin: _create silent routine Example every 12 hours !Run customer tests_
|
30
|
+
# helpadmin: _add routine example on Mondays at 05:00 !run customer tests_
|
31
|
+
# helpadmin: _add routine example on Tuesdays at 09:00 #SREChannel !run db cleanup_
|
24
32
|
# helpadmin:
|
25
|
-
def add_routine(dest, from, user, name, type, number_time, period, command_to_run, files, silent)
|
33
|
+
def add_routine(dest, from, user, name, type, number_time, period, command_to_run, files, silent, channel)
|
26
34
|
save_stats(__method__)
|
27
35
|
if files.nil? or files.size == 0 or (files.size > 0 and config.masters.include?(from))
|
28
36
|
if config.admins.include?(from)
|
@@ -30,13 +38,14 @@ class SlackSmartBot
|
|
30
38
|
respond "I'm sorry but there is already a routine with that name.\nCall `see routines` to see added routines", dest
|
31
39
|
else
|
32
40
|
number_time += ":00" if number_time.split(":").size == 2
|
33
|
-
if (type
|
41
|
+
if (type != "every") && !number_time.match?(/^[01][0-9]:[0-5][0-9]:[0-5][0-9]$/) &&
|
34
42
|
!number_time.match?(/^2[0-3]:[0-5][0-9]:[0-5][0-9]$/)
|
35
43
|
respond "Wrong time specified: *#{number_time}*"
|
36
44
|
else
|
37
45
|
file_path = ""
|
38
46
|
every = ""
|
39
47
|
at = ""
|
48
|
+
dayweek = ''
|
40
49
|
next_run = Time.now
|
41
50
|
case period.downcase
|
42
51
|
when "days", "d"
|
@@ -52,16 +61,31 @@ class SlackSmartBot
|
|
52
61
|
every = "#{number_time} seconds"
|
53
62
|
every_in_seconds = number_time.to_i
|
54
63
|
else # time
|
64
|
+
if type != 'at'
|
65
|
+
dayweek = type.downcase
|
66
|
+
days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']
|
67
|
+
incr = days.index(dayweek) - Time.now.wday
|
68
|
+
if incr < 0
|
69
|
+
incr = (7+incr)*24*60*60
|
70
|
+
else
|
71
|
+
incr = incr * 24 * 60 * 60
|
72
|
+
end
|
73
|
+
days = incr/(24*60*60)
|
74
|
+
every_in_seconds = 7 * 24 * 60 * 60 # one week
|
75
|
+
else
|
76
|
+
days = 0
|
77
|
+
every_in_seconds = 24 * 60 * 60 # one day
|
78
|
+
end
|
79
|
+
|
55
80
|
at = number_time
|
56
|
-
if next_run.strftime("%H:%M:%S") < number_time
|
81
|
+
if next_run.strftime("%H:%M:%S") < number_time and days == 0
|
57
82
|
nt = number_time.split(":")
|
58
83
|
next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
|
59
84
|
else
|
60
|
-
next_run += (24 * 60 * 60) # one more
|
85
|
+
next_run += ((24 * 60 * 60) * days) # one or more days
|
61
86
|
nt = number_time.split(":")
|
62
87
|
next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
|
63
88
|
end
|
64
|
-
every_in_seconds = 24 * 60 * 60
|
65
89
|
end
|
66
90
|
Dir.mkdir("#{config.path}/routines/#{@channel_id}") unless Dir.exist?("#{config.path}/routines/#{@channel_id}")
|
67
91
|
|
@@ -75,12 +99,12 @@ class SlackSmartBot
|
|
75
99
|
http.get(files[0].url_private_download, save_data: file_path)
|
76
100
|
system("chmod +x #{file_path}")
|
77
101
|
end
|
78
|
-
|
102
|
+
channel = dest if channel.to_s == ''
|
79
103
|
@routines[@channel_id] = {} unless @routines.key?(@channel_id)
|
80
104
|
@routines[@channel_id][name] = { channel_name: config.channel, creator: from, creator_id: user.id, status: :on,
|
81
|
-
every: every, every_in_seconds: every_in_seconds, at: at, file_path: file_path,
|
105
|
+
every: every, every_in_seconds: every_in_seconds, at: at, dayweek: dayweek, file_path: file_path,
|
82
106
|
command: command_to_run.to_s.strip, silent: silent,
|
83
|
-
next_run: next_run.to_s, dest:
|
107
|
+
next_run: next_run.to_s, dest: channel, last_run: "", last_elapsed: "",
|
84
108
|
running: false }
|
85
109
|
update_routines
|
86
110
|
respond "Added routine *`#{name}`* to the channel", dest
|
@@ -54,7 +54,7 @@ class SlackSmartBot
|
|
54
54
|
respond "Now the rules from <##{@channel_id}> are available on *<##{@channels_id[channel]}>*", dest
|
55
55
|
end
|
56
56
|
respond "<@#{user.id}> extended the rules from <##{@channel_id}> to this channel so now you can talk to the Smart Bot on demand using those rules.", @channels_id[channel]
|
57
|
-
respond "Use `!` before the command you want to run", @channels_id[channel]
|
57
|
+
respond "Use `!` or `^` or `!!` before the command you want to run", @channels_id[channel]
|
58
58
|
respond "To see the specific rules for this bot on this channel: `!bot rules` or `!bot rules COMMAND`", @channels_id[channel]
|
59
59
|
end
|
60
60
|
end
|
@@ -44,6 +44,7 @@ class SlackSmartBot
|
|
44
44
|
msg << "\tStatus: #{v[:status]}"
|
45
45
|
msg << "\tEvery: #{v[:every]}" unless v[:every] == ""
|
46
46
|
msg << "\tAt: #{v[:at]}" unless v[:at] == ""
|
47
|
+
msg << "\tOn: #{v[:dayweek]}" unless !v.key?(:dayweek) or v[:dayweek].to_s == ""
|
47
48
|
msg << "\tNext Run: #{v[:next_run]}"
|
48
49
|
msg << "\tLast Run: #{v[:last_run]}"
|
49
50
|
msg << "\tTime consumed on last run: #{v[:last_elapsed]}" unless v[:command] !=''
|
@@ -13,6 +13,7 @@ class SlackSmartBot
|
|
13
13
|
# helpadmin: `bot stats CHANNEL exclude masters from YYYY/MM/DD to YYYY/MM/DD`
|
14
14
|
# helpadmin: `bot stats today`
|
15
15
|
# helpadmin: `bot stats exclude COMMAND_ID`
|
16
|
+
# helpadmin: `bot stats monthly`
|
16
17
|
# helpadmin: To see the bot stats
|
17
18
|
# 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
|
18
19
|
# helpadmin: You need to set stats to true to generate the stats when running the bot instance.
|
@@ -21,8 +22,9 @@ class SlackSmartBot
|
|
21
22
|
# helpadmin: _bot stats @peter.wind_
|
22
23
|
# helpadmin: _bot stats #sales from 2019/12/15 to 2019/12/31_
|
23
24
|
# helpadmin: _bot stats #sales today_
|
25
|
+
# helpadmin: _bot stats #sales from 2020-01-01 monthly_
|
24
26
|
# helpadmin:
|
25
|
-
def bot_stats(dest, from_user, typem, channel_id, from, to, user, exclude_masters, exclude_command)
|
27
|
+
def bot_stats(dest, from_user, typem, channel_id, from, to, user, exclude_masters, exclude_command, monthly)
|
26
28
|
require 'csv'
|
27
29
|
if config.stats
|
28
30
|
message = []
|
@@ -30,7 +32,7 @@ class SlackSmartBot
|
|
30
32
|
message = ["You need to set stats to true to generate the stats when running the bot instance."]
|
31
33
|
end
|
32
34
|
save_stats(__method__)
|
33
|
-
if config.masters.include?(from_user) and typem==:on_dm #master admin user
|
35
|
+
if (config.masters.include?(from_user) or @master_admin_users_id.include?(from_user)) and typem==:on_dm #master admin user
|
34
36
|
if !File.exist?("#{config.stats_path}.#{Time.now.strftime("%Y-%m")}.log")
|
35
37
|
message<<'No stats'
|
36
38
|
else
|
@@ -43,17 +45,71 @@ class SlackSmartBot
|
|
43
45
|
from+= " 00:00:00 +0000"
|
44
46
|
to+= " 23:59:59 +0000"
|
45
47
|
rows = []
|
46
|
-
|
48
|
+
rows_month = {}
|
49
|
+
users_month = {}
|
50
|
+
commands_month = {}
|
51
|
+
users_id_name = {}
|
52
|
+
users_name_id = {}
|
53
|
+
|
54
|
+
# to translate global and enterprise users since sometimes was returning different names/ids
|
55
|
+
Dir["#{config.stats_path}.*.log"].sort.each do |file|
|
56
|
+
if file >= "#{config.stats_path}.#{from_file}.log" or file <= "#{config.stats_path}.#{to_file}.log"
|
57
|
+
CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
|
58
|
+
unless users_id_name.key?(row[:user_id])
|
59
|
+
users_id_name[row[:user_id]] = row[:user_name]
|
60
|
+
end
|
61
|
+
unless users_name_id.key?(row[:user_name])
|
62
|
+
users_name_id[row[:user_name]] = row[:user_id]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if user!=''
|
70
|
+
user_info = client.web_client.users_info(user: user)
|
71
|
+
if users_id_name.key?(user_info.user.id)
|
72
|
+
user_name = users_id_name[user_info.user.id]
|
73
|
+
else
|
74
|
+
user_name = user_info.user.name
|
75
|
+
end
|
76
|
+
if users_name_id.key?(user_info.user.name)
|
77
|
+
user_id = users_name_id[user_info.user.name]
|
78
|
+
else
|
79
|
+
user_id = user_info.user.id
|
80
|
+
end
|
81
|
+
end
|
82
|
+
master_admins = config.masters.dup
|
83
|
+
config.masters.each do |u|
|
84
|
+
if users_id_name.key?(u)
|
85
|
+
master_admins << users_id_name[u]
|
86
|
+
elsif users_name_id.key?(u)
|
87
|
+
master_admins << users_name_id[u]
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
47
91
|
Dir["#{config.stats_path}.*.log"].sort.each do |file|
|
48
92
|
if file >= "#{config.stats_path}.#{from_file}.log" or file <= "#{config.stats_path}.#{to_file}.log"
|
49
93
|
CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
|
50
94
|
row[:date] = row[:date].to_s
|
51
|
-
|
52
|
-
|
95
|
+
row[:user_name] = users_id_name[row[:user_id]]
|
96
|
+
row[:user_id] = users_name_id[row[:user_name]]
|
97
|
+
if !exclude_masters or (exclude_masters and !master_admins.include?(row[:user_name]) and
|
98
|
+
!master_admins.include?(row[:user_id]) and
|
99
|
+
!@master_admin_users_id.include?(row[:user_id]))
|
100
|
+
if user=='' or (user!='' and row[:user_name] == user_name) or (user!='' and row[:user_id] == user_id)
|
53
101
|
if exclude_command == '' or (exclude_command!='' and row[:command]!=exclude_command)
|
54
102
|
if row[:bot_channel_id] == channel_id or channel_id == ''
|
55
103
|
if row[:date] >= from and row[:date] <= to
|
56
104
|
rows << row.to_h
|
105
|
+
if monthly
|
106
|
+
rows_month[row[:date][0..6]] = 0 unless rows_month.key?(row[:date][0..6])
|
107
|
+
users_month[row[:date][0..6]] = [] unless users_month.key?(row[:date][0..6])
|
108
|
+
commands_month[row[:date][0..6]] = [] unless commands_month.key?(row[:date][0..6])
|
109
|
+
rows_month[row[:date][0..6]] += 1
|
110
|
+
users_month[row[:date][0..6]] << row[:user_id]
|
111
|
+
commands_month[row[:date][0..6]] << row[:command]
|
112
|
+
end
|
57
113
|
end
|
58
114
|
end
|
59
115
|
end
|
@@ -62,7 +118,6 @@ class SlackSmartBot
|
|
62
118
|
end
|
63
119
|
end
|
64
120
|
end
|
65
|
-
|
66
121
|
total = rows.size
|
67
122
|
if exclude_masters
|
68
123
|
message << 'Excluding master admins'
|
@@ -79,7 +134,22 @@ class SlackSmartBot
|
|
79
134
|
message << "*Total calls <##{channel_id}>*: #{total} from #{from_short} to #{to_short}"
|
80
135
|
end
|
81
136
|
if total > 0
|
82
|
-
|
137
|
+
if monthly
|
138
|
+
message << '*Totals by month / commands / users (%new)*'
|
139
|
+
all_users = []
|
140
|
+
new_users = []
|
141
|
+
rows_month.each do |k,v|
|
142
|
+
if all_users.empty?
|
143
|
+
message_new_users = ''
|
144
|
+
else
|
145
|
+
new_users = (users_month[k]-all_users).uniq
|
146
|
+
message_new_users = "(#{new_users.size*100/users_month[k].uniq.size}%)"
|
147
|
+
end
|
148
|
+
all_users += users_month[k]
|
149
|
+
message << "\t#{k}: #{v} (#{(v.to_f*100/total).round(2)}%) / #{commands_month[k].uniq.size} / #{users_month[k].uniq.size} #{message_new_users}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
83
153
|
if channel_id == ''
|
84
154
|
message << "*Channels*"
|
85
155
|
channels = rows.bot_channel.uniq.sort
|
@@ -89,21 +159,25 @@ class SlackSmartBot
|
|
89
159
|
end
|
90
160
|
end
|
91
161
|
if user==''
|
92
|
-
|
93
|
-
|
162
|
+
users = rows.user_id.uniq.sort
|
163
|
+
message << "*Users* - #{users.size}"
|
164
|
+
count_user = {}
|
94
165
|
users.each do |user|
|
95
|
-
count = rows.count {|h| h.
|
96
|
-
|
166
|
+
count = rows.count {|h| h.user_id==user}
|
167
|
+
count_user[user] = count
|
168
|
+
end
|
169
|
+
count_user.sort_by {|k,v| -v}.each do |user, count|
|
170
|
+
message << "\t#{users_id_name[user]}: #{count} (#{(count.to_f*100/total).round(2)}%)"
|
97
171
|
end
|
98
172
|
end
|
99
|
-
|
100
|
-
message << "*Commands*"
|
173
|
+
|
101
174
|
commands = rows.command.uniq.sort
|
175
|
+
message << "*Commands* - #{commands.size}"
|
102
176
|
commands.each do |command|
|
103
177
|
count = rows.count {|h| h.command==command}
|
104
178
|
message << "\t#{command}: #{count} (#{(count.to_f*100/total).round(2)}%)"
|
105
179
|
end
|
106
|
-
|
180
|
+
|
107
181
|
message << "*Message type*"
|
108
182
|
types = rows.type_message.uniq.sort
|
109
183
|
types.each do |type|
|
@@ -112,7 +186,6 @@ class SlackSmartBot
|
|
112
186
|
end
|
113
187
|
message << "*Last activity*: #{rows[-1].date} #{rows[-1].bot_channel} #{rows[-1].type_message} #{rows[-1].user_name} #{rows[-1].command}"
|
114
188
|
end
|
115
|
-
|
116
189
|
end
|
117
190
|
else
|
118
191
|
message<<"Only Master admin users on a private conversation with the bot can see the bot stats."
|
@@ -82,6 +82,7 @@ class SlackSmartBot
|
|
82
82
|
message = "Session name: *#{session_name}*
|
83
83
|
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
84
|
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`.
|
85
|
+
Use `p` to print a message raw, exacly like it is returned.
|
85
86
|
If you want to avoid a message to be treated by me, start the message with '-'.
|
86
87
|
After 30 minutes of no communication with the Smart Bot the session will be dismissed.
|
87
88
|
If you want to see the methods of a class or module you created use _ls TheModuleOrClass_
|
@@ -100,34 +101,8 @@ class SlackSmartBot
|
|
100
101
|
require \"amazing_print\"
|
101
102
|
bindme' + serialt + ' = binding
|
102
103
|
eval(\"require \'nice_http\'\" , bindme' + serialt + ')
|
103
|
-
def repl_params(met)
|
104
|
-
res = []
|
105
|
-
met.parameters.each do |par|
|
106
|
-
if par[0]==:req
|
107
|
-
res << par[1].to_s
|
108
|
-
elsif par[0]==:keyreq
|
109
|
-
res << par[1].to_s + \":\"
|
110
|
-
elsif par[0]==:key
|
111
|
-
res << \"?\"+par[1].to_s + \":\"
|
112
|
-
elsif par[0]==:opt
|
113
|
-
res << \"?\"+par[1].to_s
|
114
|
-
elsif par[0]==:rest
|
115
|
-
res << \"*\"+par[1].to_s
|
116
|
-
elsif par[0]==:keyrest
|
117
|
-
res << \"**\"+par[1].to_s
|
118
|
-
else
|
119
|
-
res << par[1].to_s
|
120
|
-
end
|
121
|
-
end
|
122
|
-
\"(#{res.join(\", \")})\"
|
123
|
-
end
|
124
104
|
def ls(obj)
|
125
|
-
|
126
|
-
res = []
|
127
|
-
mets.each do |met|
|
128
|
-
res << \"#{met}#{repl_params(obj.method(met))}\"
|
129
|
-
end
|
130
|
-
res.sort
|
105
|
+
(obj.methods - Object.methods)
|
131
106
|
end
|
132
107
|
|
133
108
|
file_input_repl = File.open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.input\", \"r\")
|
@@ -158,9 +133,15 @@ class SlackSmartBot
|
|
158
133
|
error = true
|
159
134
|
end
|
160
135
|
if resp_repl.to_s != \"\"
|
161
|
-
|
162
|
-
|
163
|
-
|
136
|
+
if code_to_run_repl.match?(/^\s*p\s+/i)
|
137
|
+
open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
138
|
+
f.puts \"\`\`\`\n#{resp_repl.inspect}\n\`\`\`\"
|
139
|
+
}
|
140
|
+
else
|
141
|
+
open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.output\", \"a+\") {|f|
|
142
|
+
f.puts \"\`\`\`\n#{resp_repl.ai}\n\`\`\`\"
|
143
|
+
}
|
144
|
+
end
|
164
145
|
unless error or !add_to_run_repl
|
165
146
|
open(\"' + Dir.pwd + '/repl/' + @channel_id + '/' + session_name + '.run\", \"a+\") {|f|
|
166
147
|
f.puts code_to_run_repl
|
@@ -198,7 +179,13 @@ class SlackSmartBot
|
|
198
179
|
sleep 0.2
|
199
180
|
resp_repl = file_output_repl.read
|
200
181
|
if resp_repl.to_s!=''
|
201
|
-
|
182
|
+
if resp_repl.to_s.lines.count < 60 and resp_repl.to_s.size < 3500
|
183
|
+
respond resp_repl, dest
|
184
|
+
else
|
185
|
+
resp_repl.gsub!(/^\s*```/,'')
|
186
|
+
resp_repl.gsub!(/```\s*$/,'')
|
187
|
+
send_file(dest, "", 'response.rb', "", 'text/plain', "ruby", content: resp_repl)
|
188
|
+
end
|
202
189
|
end
|
203
190
|
rescue Exception => excp
|
204
191
|
@logger.fatal excp
|
@@ -210,7 +197,7 @@ class SlackSmartBot
|
|
210
197
|
@repl_sessions[from][:command] = ''
|
211
198
|
code.gsub!("\\n", "\n")
|
212
199
|
code.gsub!("\\r", "\r")
|
213
|
-
# Disabled for the moment
|
200
|
+
# Disabled for the moment since it is deleting lines with '}'
|
214
201
|
#code.gsub!(/^\W*$/, "") #to remove special chars from slack when copy/pasting.
|
215
202
|
if code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File") or
|
216
203
|
code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
|
@@ -52,7 +52,7 @@ class SlackSmartBot
|
|
52
52
|
Dir.mkdir("#{project_folder}/tmp") unless Dir.exist?("#{project_folder}/tmp")
|
53
53
|
Dir.mkdir("#{project_folder}/tmp/repl") unless Dir.exist?("#{project_folder}/tmp/repl")
|
54
54
|
File.write("#{project_folder}/tmp/repl/#{session_name}.rb", content, mode: "w+")
|
55
|
-
process_to_run = "ruby
|
55
|
+
process_to_run = "ruby ./tmp/repl/#{session_name}.rb"
|
56
56
|
process_to_run = ("cd #{project_folder} && #{process_to_run}") if defined?(project_folder)
|
57
57
|
respond "Running REPL #{session_name}"
|
58
58
|
stdout, stderr, status = Open3.capture3(process_to_run)
|
@@ -60,10 +60,19 @@ class SlackSmartBot
|
|
60
60
|
if stdout == ""
|
61
61
|
respond "*#{session_name}*: Nothing returned."
|
62
62
|
else
|
63
|
-
|
63
|
+
if stdout.to_s.lines.count < 60 and stdout.to_s.size < 3500
|
64
|
+
respond "*#{session_name}*: #{stdout}"
|
65
|
+
else
|
66
|
+
send_file(dest, "", 'response.rb', "", 'text/plain', "ruby", content: stdout)
|
67
|
+
end
|
64
68
|
end
|
65
69
|
else
|
66
|
-
|
70
|
+
if (stdout.to_s+stderr.to_s).lines.count < 60
|
71
|
+
respond "*#{session_name}*: #{stdout} #{stderr}"
|
72
|
+
else
|
73
|
+
send_file(dest, "", 'response.rb', "", 'text/plain', "ruby", content: (stdout.to_s+stderr.to_s))
|
74
|
+
end
|
75
|
+
|
67
76
|
end
|
68
77
|
end
|
69
78
|
else
|
@@ -15,7 +15,7 @@ class SlackSmartBot
|
|
15
15
|
message = ""
|
16
16
|
@repls.sort.to_h.each do |session_name, repl|
|
17
17
|
if (repl.creator_name == user.name or repl.type == :public) or (config.admins.include?(user.name) and typem == :on_dm)
|
18
|
-
message += "(#{repl.type}) *#{session_name}*: #{repl.description} / created: #{repl.created} / accessed: #{repl.accessed} / creator:
|
18
|
+
message += "(#{repl.type}) *#{session_name}*: #{repl.description} / created: #{repl.created} / accessed: #{repl.accessed} / creator: #{repl.creator_name} / runs: #{repl.runs_by_creator+repl.runs_by_others} / gets: #{repl.gets} \n"
|
19
19
|
end
|
20
20
|
end
|
21
21
|
message = "No repls created" if message == ''
|
@@ -46,14 +46,19 @@ class SlackSmartBot
|
|
46
46
|
rules_file = "slack-smart-bot_rules_#{channel_id}_#{from.gsub(" ", "_")}.rb"
|
47
47
|
if defined?(RULES_FOLDER)
|
48
48
|
rules_file = RULES_FOLDER + rules_file
|
49
|
+
general_rules_file = RULES_FOLDER + 'general_rules.rb'
|
49
50
|
else
|
50
51
|
Dir.mkdir("#{config.path}/rules") unless Dir.exist?("#{config.path}/rules")
|
51
52
|
Dir.mkdir("#{config.path}/rules/#{channel_id}") unless Dir.exist?("#{config.path}/rules/#{channel_id}")
|
52
53
|
rules_file = "/rules/#{channel_id}/" + rules_file
|
54
|
+
general_rules_file = "/rules/general_rules.rb"
|
53
55
|
end
|
54
56
|
default_rules = (__FILE__).gsub(/slack\/smart-bot\/commands\/on_master\/create_bot\.rb$/, "slack-smart-bot_rules.rb")
|
57
|
+
default_general_rules = (__FILE__).gsub(/slack\/smart-bot\/commands\/on_master\/create_bot\.rb$/, "slack-smart-bot_general_rules.rb")
|
58
|
+
|
55
59
|
File.delete(config.path + rules_file) if File.exist?(config.path + rules_file)
|
56
60
|
FileUtils.copy_file(default_rules, config.path + rules_file) unless File.exist?(config.path + rules_file)
|
61
|
+
FileUtils.copy_file(default_general_rules, config.path + general_rules_file) unless File.exist?(config.path + general_rules_file)
|
57
62
|
admin_users = Array.new()
|
58
63
|
creator_info = client.web_client.users_info(user: channel_found.creator)
|
59
64
|
admin_users = [from, creator_info.user.name] + config.masters
|
@@ -69,15 +69,17 @@ class SlackSmartBot
|
|
69
69
|
when /^\s*kill\sbot\son\s<#C\w+\|(.+)>\s*$/i, /^kill\sbot\son\s(.+)\s*$/i
|
70
70
|
channel = $1
|
71
71
|
kill_bot_on_channel(dest, from, channel)
|
72
|
-
when /^\s*(add|create)\s+(silent\s+)?routine\s+(\w+)\s+(every)\s+(\d+)\s*(days|hours|minutes|seconds|mins|min|secs|sec|d|h|m|s)\s*(\s.+)?\s*$/i,
|
73
|
-
|
72
|
+
when /^\s*(add|create)\s+(silent\s+)?routine\s+(\w+)\s+(every)\s+(\d+)\s*(days|hours|minutes|seconds|mins|min|secs|sec|d|h|m|s)\s*(\s<#(C\w+)\|.+>\s*)?(\s.+)?\s*$/i,
|
73
|
+
/^\s*(add|create)\s+(silent\s+)?routine\s+(\w+)\s+on\s+(monday|tuesday|wednesday|thursday|friday|saturday|sunday)s?\s+at\s+(\d+:\d+:?\d+?)\s*()(\s<#(C\w+)\|.+>\s*)?(\s.+)?\s*$/i,
|
74
|
+
/^\s*(add|create)\s+(silent\s+)?routine\s+(\w+)\s+(at)\s+(\d+:\d+:?\d+?)\s*()(\s<#(C\w+)\|.+>\s*)?(\s.+)?\s*$/i
|
74
75
|
silent = $2.to_s!=''
|
75
76
|
name = $3.downcase
|
76
77
|
type = $4
|
77
78
|
number_time = $5
|
78
79
|
period = $6
|
79
|
-
|
80
|
-
|
80
|
+
channel = $8
|
81
|
+
command_to_run = $9
|
82
|
+
add_routine(dest, from, user, name, type, number_time, period, command_to_run, files, silent, channel)
|
81
83
|
when /^\s*(kill|delete|remove)\s+routine\s+(\w+)\s*$/i
|
82
84
|
name = $2.downcase
|
83
85
|
remove_routine(dest, from, name)
|
@@ -105,12 +107,15 @@ class SlackSmartBot
|
|
105
107
|
st_to = st_to.gsub('.','-').gsub('/','-')
|
106
108
|
st_user = opts.scan(/<@([^>]+)>/).join
|
107
109
|
exclude_masters = opts.match?(/exclude\s+masters?/i)
|
110
|
+
monthly = false
|
108
111
|
if all_opts.include?('today')
|
109
112
|
st_from = st_to = "#{Time.now.strftime("%Y-%m-%d")}"
|
113
|
+
elsif all_opts.include?('monthly')
|
114
|
+
monthly = true
|
110
115
|
end
|
111
116
|
exclude_command = opts.scan(/exclude\s+([^\s]+)/i).join
|
112
117
|
exclude_command = '' if exclude_command == 'masters'
|
113
|
-
bot_stats(dest, from, typem, st_channel, st_from, st_to, st_user, exclude_masters, exclude_command)
|
118
|
+
bot_stats(dest, from, typem, st_channel, st_from, st_to, st_user, exclude_masters, exclude_command, monthly)
|
114
119
|
else
|
115
120
|
processed = false
|
116
121
|
end
|
@@ -166,11 +171,11 @@ class SlackSmartBot
|
|
166
171
|
ruby_code(dest, user, code, rules_file)
|
167
172
|
when /^\s*(private\s+)?(repl|irb|live)\s*()()()$/i,
|
168
173
|
/^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)()()\s*$/i,
|
169
|
-
/^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s
|
170
|
-
/^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s
|
174
|
+
/^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s+"([^"]+)"()\s*$/i,
|
175
|
+
/^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)\s*:\s+"([^"]+)"\s+(.+)\s*$/i,
|
171
176
|
/^\s*(private\s+)?(repl|irb|live)\s+([\w\-]+)()\s+(.+)\s*$/i,
|
172
177
|
/^\s*(private\s+)?(repl|irb|live)()\s+()(.+)\s*$/i
|
173
|
-
|
178
|
+
if $1.to_s!=''
|
174
179
|
type = :private
|
175
180
|
else
|
176
181
|
type = :public
|
@@ -32,6 +32,8 @@ class SlackSmartBot
|
|
32
32
|
data.text = CGI.unescapeHTML(data.text)
|
33
33
|
data.text.gsub!("\u00A0", " ") #to change (asc char 160) into blank space
|
34
34
|
end
|
35
|
+
data.text.gsub!('’', "'")
|
36
|
+
data.text.gsub!('“', '"')
|
35
37
|
rescue
|
36
38
|
@logger.warn "Impossible to unescape or clean format for data.text:#{data.text}"
|
37
39
|
end
|
@@ -40,7 +42,9 @@ class SlackSmartBot
|
|
40
42
|
f.puts "|#{data.channel}|#{data.user}|#{data.text}"
|
41
43
|
}
|
42
44
|
end
|
43
|
-
if data.
|
45
|
+
if data.key?(:dest) and data.dest.to_s!='' # for run routines and publish on different channels
|
46
|
+
dest = data.dest
|
47
|
+
elsif data.channel[0] == "D" or data.channel[0] == "C" or data.channel[0] == "G" #Direct message or Channel or Private Channel
|
44
48
|
dest = data.channel
|
45
49
|
else # not treated
|
46
50
|
dest = nil
|
@@ -63,15 +67,15 @@ class SlackSmartBot
|
|
63
67
|
data.text = $4
|
64
68
|
typem = :on_call
|
65
69
|
end
|
66
|
-
elsif
|
70
|
+
elsif data.channel == @master_bot_id
|
67
71
|
if config.on_master_bot #only to be treated on master bot channel
|
68
72
|
typem = :on_master
|
69
73
|
end
|
70
|
-
elsif @bots_created.key?(
|
71
|
-
if @channel_id ==
|
74
|
+
elsif @bots_created.key?(data.channel)
|
75
|
+
if @channel_id == data.channel #only to be treated by the bot on the channel
|
72
76
|
typem = :on_bot
|
73
77
|
end
|
74
|
-
elsif
|
78
|
+
elsif data.channel[0] == "D" #Direct message
|
75
79
|
get_rules_imported()
|
76
80
|
if @rules_imported.key?(data.user) && @rules_imported[data.user].key?(data.user) and
|
77
81
|
@bots_created.key?(@rules_imported[data.user][data.user])
|
@@ -83,21 +87,21 @@ class SlackSmartBot
|
|
83
87
|
#only to be treated by master bot
|
84
88
|
typem = :on_dm
|
85
89
|
end
|
86
|
-
elsif
|
90
|
+
elsif data.channel[0] == "C" or data.channel[0] == "G"
|
87
91
|
#only to be treated on the channel of the bot. excluding running ruby
|
88
|
-
if !config.on_master_bot and @bots_created.key?(@channel_id) and @bots_created[@channel_id][:extended].include?(@channels_name[
|
92
|
+
if !config.on_master_bot and @bots_created.key?(@channel_id) and @bots_created[@channel_id][:extended].include?(@channels_name[data.channel]) and
|
89
93
|
!data.text.match?(/^!?\s*(ruby|code)\s+/) and !data.text.match?(/^!?!?\s*(ruby|code)\s+/) and !data.text.match?(/^\^?\s*(ruby|code)\s+/)
|
90
94
|
typem = :on_extended
|
91
95
|
elsif config.on_master_bot and (data.text.match?(/^!?\s*(ruby|code)\s+/) or data.text.match?(/^!?!?\s*(ruby|code)\s+/) or data.text.match?(/^\^?\s*(ruby|code)\s+/) )
|
92
96
|
#or in case of running ruby, the master bot
|
93
97
|
@bots_created.each do |k, v|
|
94
|
-
if v.key?(:extended) and v[:extended].include?(@channels_name[
|
98
|
+
if v.key?(:extended) and v[:extended].include?(@channels_name[data.channel])
|
95
99
|
typem = :on_extended
|
96
100
|
break
|
97
101
|
end
|
98
102
|
end
|
99
103
|
end
|
100
|
-
if
|
104
|
+
if data.channel[0] == "G" and config.on_master_bot and typem != :on_extended #private group
|
101
105
|
typem = :on_pg
|
102
106
|
end
|
103
107
|
end
|
@@ -115,6 +119,8 @@ class SlackSmartBot
|
|
115
119
|
begin
|
116
120
|
#todo: when changed @questions user_id then move user_info inside the ifs to avoid calling it when not necessary
|
117
121
|
user_info = client.web_client.users_info(user: data.user)
|
122
|
+
#user_info.user.id = data.user #todo: remove this line when slack issue with Wxxxx Uxxxx fixed
|
123
|
+
data.user = user_info.user.id #todo: remove this line when slack issue with Wxxxx Uxxxx fixed
|
118
124
|
if @questions.key?(user_info.user.name)
|
119
125
|
if data.text.match?(/^\s*(Bye|Bæ|Good\sBye|Adiós|Ciao|Bless|Bless\sBless|Adeu)\s(#{@salutations.join("|")})\s*$/i)
|
120
126
|
@questions.delete(user_info.user.name)
|
@@ -123,16 +129,12 @@ class SlackSmartBot
|
|
123
129
|
command = @questions[user_info.user.name]
|
124
130
|
@questions[user_info.user.name] = data.text
|
125
131
|
end
|
126
|
-
elsif @repl_sessions.key?(user_info.user.name) and
|
132
|
+
elsif @repl_sessions.key?(user_info.user.name) and data.channel==@repl_sessions[user_info.user.name][:dest] and
|
127
133
|
((@repl_sessions[user_info.user.name][:on_thread] and data.thread_ts == @repl_sessions[user_info.user.name][:thread_ts]) or
|
128
134
|
(!@repl_sessions[user_info.user.name][:on_thread] and data.thread_ts.to_s == '' ))
|
129
135
|
|
130
|
-
if data.text.
|
131
|
-
|
132
|
-
@repl_sessions[user_info.user.name][:command] = ""
|
133
|
-
else
|
134
|
-
@repl_sessions[user_info.user.name][:command] = data.text[3..-4]
|
135
|
-
end
|
136
|
+
if data.text.match(/^\s*```(.*)```\s*$/im)
|
137
|
+
@repl_sessions[user_info.user.name][:command] = $1
|
136
138
|
else
|
137
139
|
@repl_sessions[user_info.user.name][:command] = data.text
|
138
140
|
end
|
@@ -142,12 +144,8 @@ class SlackSmartBot
|
|
142
144
|
end
|
143
145
|
|
144
146
|
#when added special characters on the message
|
145
|
-
if command.
|
146
|
-
|
147
|
-
command = ''
|
148
|
-
else
|
149
|
-
command = command[3..-4]
|
150
|
-
end
|
147
|
+
if command.match(/^\s*```(.*)```\s*$/im)
|
148
|
+
command = $1
|
151
149
|
elsif command.size >= 2 and
|
152
150
|
((command[0] == "`" and command[-1] == "`") or (command[0] == "*" and command[-1] == "*") or (command[0] == "_" and command[-1] == "_"))
|
153
151
|
command = command[1..-2]
|
@@ -176,13 +174,13 @@ class SlackSmartBot
|
|
176
174
|
if channel_found.nil?
|
177
175
|
@logger.fatal "Not possible to find the channel #{channel_rules_name}"
|
178
176
|
elsif channel_found.name == config.master_channel
|
179
|
-
respond "You cannot use the rules from Master Channel on any other channel.",
|
177
|
+
respond "You cannot use the rules from Master Channel on any other channel.", data.channel
|
180
178
|
elsif @status != :on
|
181
|
-
respond "The bot in that channel is not :on",
|
179
|
+
respond "The bot in that channel is not :on", data.channel
|
182
180
|
elsif data.user == channel_found.creator or members.include?(data.user)
|
183
181
|
process_first(user_info.user, command, dest, channel_rules, typem, data.files, data.ts, data.thread_ts)
|
184
182
|
else
|
185
|
-
respond "You need to join the channel <##{channel_found.id}> to be able to use the rules.",
|
183
|
+
respond "You need to join the channel <##{channel_found.id}> to be able to use the rules.", data.channel
|
186
184
|
end
|
187
185
|
elsif config.on_master_bot and typem == :on_extended and
|
188
186
|
command.size > 0 and command[0] != "-"
|
@@ -201,7 +199,7 @@ class SlackSmartBot
|
|
201
199
|
@logger.fatal stack
|
202
200
|
end
|
203
201
|
else
|
204
|
-
if !config.on_master_bot and !dest.nil? and (
|
202
|
+
if !config.on_master_bot and !dest.nil? and (data.channel == @master_bot_id or dest[0] == "D") and
|
205
203
|
data.text.match?(/^\s*bot\s+status\s*$/i) and @admin_users_id.include?(data.user)
|
206
204
|
respond "ping from #{config.channel}", dest
|
207
205
|
elsif !config.on_master_bot and !dest.nil? and data.user == config[:nick_id] and dest == @master_bot_id
|
@@ -13,7 +13,6 @@ class SlackSmartBot
|
|
13
13
|
ruby = ""
|
14
14
|
end
|
15
15
|
@routines[@channel_id][name][:silent] = false if !@routines[@channel_id][name].key?(:silent)
|
16
|
-
|
17
16
|
if @routines[@channel_id][name][:at] == "" or
|
18
17
|
(@routines[@channel_id][name][:at] != "" and @routines[@channel_id][name][:running] and
|
19
18
|
@routines[@channel_id][name][:next_run] != "" and Time.now.to_s >= @routines[@channel_id][name][:next_run])
|
@@ -24,7 +23,11 @@ class SlackSmartBot
|
|
24
23
|
stdout, stderr, status = Open3.capture3(process_to_run)
|
25
24
|
if !@routines[@channel_id][name][:silent] or (@routines[@channel_id][name][:silent] and
|
26
25
|
(!stderr.match?(/\A\s*\z/) or !stdout.match?(/\A\s*\z/)))
|
27
|
-
|
26
|
+
if @routines[@channel_id][name][:dest]!=@channel_id
|
27
|
+
respond "routine from <##{@channel_id}> *`#{name}`*: #{@routines[@channel_id][name][:file_path]}", @routines[@channel_id][name][:dest]
|
28
|
+
else
|
29
|
+
respond "routine *`#{name}`*: #{@routines[@channel_id][name][:file_path]}", @routines[@channel_id][name][:dest]
|
30
|
+
end
|
28
31
|
end
|
29
32
|
if stderr == ""
|
30
33
|
unless stdout.match?(/\A\s*\z/)
|
@@ -35,10 +38,15 @@ class SlackSmartBot
|
|
35
38
|
end
|
36
39
|
else #command
|
37
40
|
if !@routines[@channel_id][name][:silent]
|
38
|
-
|
41
|
+
if @routines[@channel_id][name][:dest]!=@channel_id
|
42
|
+
respond "routine from <##{@channel_id}> *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest]
|
43
|
+
else
|
44
|
+
respond "routine *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest]
|
45
|
+
end
|
39
46
|
end
|
40
47
|
started = Time.now
|
41
|
-
data = { channel: @
|
48
|
+
data = { channel: @channel_id,
|
49
|
+
dest: @routines[@channel_id][name][:dest],
|
42
50
|
user: @routines[@channel_id][name][:creator_id],
|
43
51
|
text: @routines[@channel_id][name][:command],
|
44
52
|
files: nil }
|
@@ -55,11 +63,34 @@ class SlackSmartBot
|
|
55
63
|
require "time"
|
56
64
|
every_in_seconds = Time.parse(@routines[@channel_id][name][:next_run]) - Time.now
|
57
65
|
elsif @routines[@channel_id][name][:at] != "" #coming from start after pause for 'at'
|
58
|
-
if
|
66
|
+
if @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!=''
|
67
|
+
day = @routines[@channel_id][name][:dayweek]
|
68
|
+
days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']
|
69
|
+
incr = days.index(day) - Time.now.wday
|
70
|
+
if incr < 0
|
71
|
+
incr = (7+incr)*24*60*60
|
72
|
+
else
|
73
|
+
incr = incr * 24 * 60 * 60
|
74
|
+
end
|
75
|
+
days = incr/(24*60*60)
|
76
|
+
weekly = true
|
77
|
+
else
|
78
|
+
days = 0
|
79
|
+
weekly = false
|
80
|
+
end
|
81
|
+
|
82
|
+
if started.strftime("%H:%M:%S") < @routines[@channel_id][name][:at] and days == 0
|
59
83
|
nt = @routines[@channel_id][name][:at].split(":")
|
60
84
|
next_run = Time.new(started.year, started.month, started.day, nt[0], nt[1], nt[2])
|
61
85
|
else
|
62
|
-
|
86
|
+
if days == 0 and started.strftime("%H:%M:%S") >= @routines[@channel_id][name][:at]
|
87
|
+
if weekly
|
88
|
+
days = 7
|
89
|
+
else
|
90
|
+
days = 1
|
91
|
+
end
|
92
|
+
end
|
93
|
+
next_run = started + (days * 24 * 60 * 60) # one more day/week
|
63
94
|
nt = @routines[@channel_id][name][:at].split(":")
|
64
95
|
next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
|
65
96
|
end
|
@@ -32,7 +32,11 @@ class SlackSmartBot
|
|
32
32
|
help = @help_messages.deep_copy
|
33
33
|
end
|
34
34
|
if rules_file != ""
|
35
|
-
help[:rules_file] =
|
35
|
+
help[:rules_file] = ''
|
36
|
+
help[:rules_file] += IO.readlines(config.path+rules_file).join.scan(/#\s*help\s*\w*:(.*)/i).join("\n") + "\n"
|
37
|
+
if File.exist?(config.path+'/rules/general_rules.rb')
|
38
|
+
help[:rules_file] += IO.readlines(config.path+'/rules/general_rules.rb').join.scan(/#\s*help\s*\w*:(.*)/i).join("\n")
|
39
|
+
end
|
36
40
|
end
|
37
41
|
help = remove_hash_keys(help, :admin_master) unless user_type == :admin_master
|
38
42
|
help = remove_hash_keys(help, :admin) unless user_type == :admin or user_type == :admin_master
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slack-smart-bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mario Ruiz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: slack-ruby-client
|
@@ -16,20 +16,20 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0.
|
19
|
+
version: '0.15'
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: 0.
|
22
|
+
version: 0.15.1
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
27
|
- - "~>"
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version: '0.
|
29
|
+
version: '0.15'
|
30
30
|
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 0.
|
32
|
+
version: 0.15.1
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: nice_http
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -79,6 +79,9 @@ dependencies:
|
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
81
|
version: '1'
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 1.2.1
|
82
85
|
type: :runtime
|
83
86
|
prerelease: false
|
84
87
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -86,6 +89,9 @@ dependencies:
|
|
86
89
|
- - "~>"
|
87
90
|
- !ruby/object:Gem::Version
|
88
91
|
version: '1'
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: 1.2.1
|
89
95
|
- !ruby/object:Gem::Dependency
|
90
96
|
name: rspec
|
91
97
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,6 +125,7 @@ files:
|
|
119
125
|
- LICENSE
|
120
126
|
- README.md
|
121
127
|
- lib/slack-smart-bot.rb
|
128
|
+
- lib/slack-smart-bot_general_rules.rb
|
122
129
|
- lib/slack-smart-bot_rules.rb
|
123
130
|
- lib/slack/smart-bot/comm.rb
|
124
131
|
- lib/slack/smart-bot/comm/ask.rb
|
@@ -129,6 +136,7 @@ files:
|
|
129
136
|
- lib/slack/smart-bot/comm/send_file.rb
|
130
137
|
- lib/slack/smart-bot/comm/send_msg_channel.rb
|
131
138
|
- lib/slack/smart-bot/comm/send_msg_user.rb
|
139
|
+
- lib/slack/smart-bot/comm/unreact.rb
|
132
140
|
- lib/slack/smart-bot/commands.rb
|
133
141
|
- lib/slack/smart-bot/commands/general/bot_help.rb
|
134
142
|
- lib/slack/smart-bot/commands/general/bot_status.rb
|