slack-smart-bot 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.yardopts +5 -0
- data/LICENSE +21 -0
- data/README.md +248 -0
- data/lib/slack-smart-bot.rb +681 -0
- data/lib/slack-smart-bot_rules.rb +82 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5ad8c9b6d08d27ac0fa0f03b054b39032a2228f25082433b2d58d400dfabbc99
|
4
|
+
data.tar.gz: 7a37e23992b269849aa522a5f25865f8e143a22ced0777b6fb08fc1cec05bc3f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1388d4e8eff5990665fcf8da616eec0164d242b9b87fdb667c018894e465c76bd00cd709aebaf5bf26a0421f29692d81e1d3f80c46388f559c46023176762890
|
7
|
+
data.tar.gz: 00e0ba16a2b50d6bf64582bc3e8443694a473732a6c02175097be935c1ee252984ecbc151fe225aab0ceaa9aaf0b7d3ab286d0c7992cfe4794fcfdaa95267dc9
|
data/.yardopts
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2019 Mario Ruiz
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
# Slack Smart Bot
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/slack-smart-bot.svg)](https://rubygems.org/gems/slack-smart-bot)
|
4
|
+
|
5
|
+
Create a Slack bot that is really smart and so easy to expand.
|
6
|
+
|
7
|
+
The main scope of this ruby gem is to be used internally in your company so teams can create team channels with their own bot to help them on their daily work, almost everything is suitable to be automated!!
|
8
|
+
|
9
|
+
slack-smart-bot can create bots on demand, create shortcuts, run ruby code... just on a chat channel, you can access it just from your mobile phone if you want and run those tests you forgot to run, get the results, restart a server... no limits.
|
10
|
+
|
11
|
+
## Installation and configuration
|
12
|
+
|
13
|
+
$ gem install slack-smart-bot
|
14
|
+
|
15
|
+
After you install it you will need just a couple of things to configure it.
|
16
|
+
|
17
|
+
Create a file like this on the folder you want:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
# the channel that will act like the master channel, main channel
|
21
|
+
MASTER_CHANNEL="my_master_channel"
|
22
|
+
#names of the master users
|
23
|
+
MASTER_USERS=["mario"]
|
24
|
+
|
25
|
+
require 'slack-smart-bot'
|
26
|
+
|
27
|
+
settings = {
|
28
|
+
nick: 'my_smart_bot', # the smart bot name
|
29
|
+
token: 'xxxxxxxxxxxxxxxxxx' # the API Slack token
|
30
|
+
}
|
31
|
+
|
32
|
+
SlackSmartBot.new(settings).listen
|
33
|
+
```
|
34
|
+
|
35
|
+
The MASTER_CHANNEL will be the channel where you will be able to create other bots and will have special treatment.
|
36
|
+
|
37
|
+
The MASTER_USERS will have full access to everything. The names should be written exactly the same like they appear on Slack.
|
38
|
+
|
39
|
+
For the token remember you need to generate a token on the Slack web for the bot user.
|
40
|
+
|
41
|
+
This is something done in Slack, under [integrations](https://my.slack.com/services). Create a [new bot](https://my.slack.com/services/new/bot), and note its API token.
|
42
|
+
|
43
|
+
*Remember to invite the smart bot to the channels where they will be accessible before creating the bot*
|
44
|
+
|
45
|
+
## Usage
|
46
|
+
|
47
|
+
### creating the MASTER BOT
|
48
|
+
Let's guess the file you created was called my_smart_bot.rb so, just run it:
|
49
|
+
```
|
50
|
+
ruby my_smart_bot.rb
|
51
|
+
```
|
52
|
+
|
53
|
+
After the run, it will be generated a rules file with the same name but adding _rules, in this example: my_smart_bot_rules.rb
|
54
|
+
|
55
|
+
The rules file can be edited and will be only affecting this particular bot.
|
56
|
+
|
57
|
+
You can add all the rules you want for your bot in the rules file, this is an example:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
def rules(from, command, processed)
|
61
|
+
firstname = from.split(" ").first
|
62
|
+
case command
|
63
|
+
|
64
|
+
# help: `echo SOMETHING`
|
65
|
+
# help: repeats SOMETHING
|
66
|
+
# help:
|
67
|
+
when /echo\s(.+)/i
|
68
|
+
respond $1
|
69
|
+
|
70
|
+
# help: `go to sleep`
|
71
|
+
# help: it will sleep the bot for 10 seconds
|
72
|
+
# help:
|
73
|
+
when /go\sto\ssleep/i
|
74
|
+
unless @questions.keys.include?(from)
|
75
|
+
ask("do you want me to take a siesta?", command, from)
|
76
|
+
else
|
77
|
+
case @questions[from]
|
78
|
+
when /yes/i, /yep/i, /sure/i
|
79
|
+
respond "zZzzzzzZZZZZZzzzzzzz!"
|
80
|
+
respond "I'll be sleeping for 10 secs... just for you"
|
81
|
+
sleep 10
|
82
|
+
when /no/i, /nope/i, /cancel/i
|
83
|
+
@questions.delete(from)
|
84
|
+
respond "Thanks, I'm happy to be awake"
|
85
|
+
else
|
86
|
+
respond "I don't understand"
|
87
|
+
ask("are you sure do you want me to sleep? (yes or no)", "go to sleep", from)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
else
|
91
|
+
unless processed
|
92
|
+
resp = %w{ what huh sorry }.sample
|
93
|
+
respond "#{firstname}: #{resp}?"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
```
|
99
|
+
### How to access the smart bot
|
100
|
+
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.
|
101
|
+
|
102
|
+
### Available commands even when the bot is not listening to you
|
103
|
+
Some of the commands are available always even when the bot is not listening to you but it is running
|
104
|
+
|
105
|
+
**_`bot help`_**
|
106
|
+
|
107
|
+
**_`bot what can I do?`_**
|
108
|
+
|
109
|
+
>It will display all the commands we can use
|
110
|
+
>What is displayed by this command is what is written on your rules file like this: #help: THE TEXT TO SHOW
|
111
|
+
|
112
|
+
**_`Hello Bot`_**
|
113
|
+
|
114
|
+
**_`Hello THE_NAME_OF_THE_BOT`_**
|
115
|
+
|
116
|
+
>Also apart of Hello you can use Hallo, Hi, Hola, What's up, Hey, Hæ
|
117
|
+
|
118
|
+
>Bot starts listening to you
|
119
|
+
|
120
|
+
**_`Bye Bot`_**
|
121
|
+
|
122
|
+
**_`Bye THE_NAME_OF_THE_BOT`_**
|
123
|
+
|
124
|
+
>Also apart of Bye you can use Bæ, Good Bye, Adiós, Ciao, Bless, Bless Bless, Adeu
|
125
|
+
|
126
|
+
>Bot stops listening to you
|
127
|
+
|
128
|
+
**_`exit bot`_**
|
129
|
+
|
130
|
+
**_`quit bot`_**
|
131
|
+
|
132
|
+
**_`close bot`_**
|
133
|
+
|
134
|
+
>The bot stops running and also stops all the bots created from this master channel
|
135
|
+
|
136
|
+
>You can use this command only if you are an admin user and you are on the master channel
|
137
|
+
|
138
|
+
**_`start bot`_**
|
139
|
+
|
140
|
+
**_`start this bot`_**
|
141
|
+
|
142
|
+
>The bot will start to listen
|
143
|
+
|
144
|
+
>You can use this command only if you are an admin user
|
145
|
+
|
146
|
+
**_`pause bot`_**
|
147
|
+
|
148
|
+
**_`pause this bot`_**
|
149
|
+
|
150
|
+
>The bot will pause so it will listen only to admin commands
|
151
|
+
|
152
|
+
>You can use this command only if you are an admin user
|
153
|
+
|
154
|
+
**_`bot status`_**
|
155
|
+
|
156
|
+
>Displays the status of the bot
|
157
|
+
|
158
|
+
>If on master channel and admin user also it will display info about bots created
|
159
|
+
|
160
|
+
**_`create bot on CHANNEL_NAME`_**
|
161
|
+
|
162
|
+
>Creates a new bot on the channel specified.
|
163
|
+
|
164
|
+
>slack-smart-bot will create a default rules file specific for your channel.
|
165
|
+
You can edit it and add the rules you want.
|
166
|
+
As soon as you save the file after editing it will become available on your channel.
|
167
|
+
|
168
|
+
>It will work only if you are on Master channel
|
169
|
+
|
170
|
+
**_`kill bot on CHANNEL_NAME`_**
|
171
|
+
|
172
|
+
>Kills the bot on the specified channel
|
173
|
+
|
174
|
+
>Only works if you are on Master channel and you created that bot or you are an admin user
|
175
|
+
|
176
|
+
### Available commands only when listening to you or on demand
|
177
|
+
|
178
|
+
All the commands described on here or on your specific Rules file can be used when the bot is listening to you or on demand.
|
179
|
+
|
180
|
+
For the bot to start listening to you you need to use the "Hi bot" command or one of the aliases
|
181
|
+
|
182
|
+
Also you can call any of these commands on demand by using:
|
183
|
+
|
184
|
+
**_`!THE_COMMAND`_**
|
185
|
+
|
186
|
+
**_`@bot THE_COMMAND`_**
|
187
|
+
|
188
|
+
**_`@smart THE_COMMAND`_**
|
189
|
+
|
190
|
+
**_`@BOT_NAME THE_COMMAND`_**
|
191
|
+
|
192
|
+
**_`BOT_NAME THE_COMMAND`_**
|
193
|
+
|
194
|
+
Apart of the specific commands you define on the rules file of the channel, you can use:
|
195
|
+
|
196
|
+
**_`ruby RUBY_CODE`_**
|
197
|
+
|
198
|
+
**_`code RUBY_CODE`_**
|
199
|
+
|
200
|
+
>runs the code supplied and returns the output. Examples:
|
201
|
+
|
202
|
+
>code puts (34344/99)*(34+14)
|
203
|
+
|
204
|
+
>ruby require 'json'; res=[]; 20.times {res<<rand(100)}; my_json={result: res}; puts my_json.to_json
|
205
|
+
|
206
|
+
|
207
|
+
**_`add shortcut NAME: COMMAND`_**
|
208
|
+
|
209
|
+
**_`add shortcut for all NAME: COMMAND`_**
|
210
|
+
|
211
|
+
**_`shortchut NAME: COMMAND`_**
|
212
|
+
|
213
|
+
**_`shortchut for all NAME: COMMAND`_**
|
214
|
+
|
215
|
+
>It will add a shortcut that will execute the command we supply.
|
216
|
+
|
217
|
+
>In case we supply 'for all' then the shorcut will be available for everybody
|
218
|
+
|
219
|
+
>Example:
|
220
|
+
>add shortcut for all Spanish account: /code require 'iso/iban'; 10.times {puts ISO::IBAN.random('ES')}
|
221
|
+
|
222
|
+
>Then to call this shortcut:
|
223
|
+
|
224
|
+
>sc spanish account
|
225
|
+
|
226
|
+
>shortcut Spanish Account
|
227
|
+
|
228
|
+
**_`delete shortcut NAME`_**
|
229
|
+
|
230
|
+
>It will delete the shortcut with the supplied name
|
231
|
+
|
232
|
+
**_`see shortcuts`_**
|
233
|
+
|
234
|
+
>It will display the shortcuts stored for the user and for :all
|
235
|
+
|
236
|
+
**_`id channel CHANNEL_NAME`_**
|
237
|
+
>shows the id of a channel name
|
238
|
+
|
239
|
+
|
240
|
+
## Contributing
|
241
|
+
|
242
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/marioruiz/slack-smart-bot.
|
243
|
+
|
244
|
+
|
245
|
+
## License
|
246
|
+
|
247
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
248
|
+
|
@@ -0,0 +1,681 @@
|
|
1
|
+
require "slack-ruby-client"
|
2
|
+
require "open-uri"
|
3
|
+
require "cgi"
|
4
|
+
require "json"
|
5
|
+
require "logger"
|
6
|
+
require "fileutils"
|
7
|
+
require "open3"
|
8
|
+
|
9
|
+
if ARGV.size == 0
|
10
|
+
CHANNEL = MASTER_CHANNEL
|
11
|
+
ON_MASTER_CHANNEL = true
|
12
|
+
ADMIN_USERS = MASTER_USERS
|
13
|
+
RULES_FILE = "#{$0.gsub(".rb", "_rules.rb")}" unless defined?(RULES_FILE)
|
14
|
+
unless File.exist?(RULES_FILE)
|
15
|
+
default_rules = (__FILE__).gsub(/\.rb$/, "_rules.rb")
|
16
|
+
FileUtils.copy_file(default_rules, RULES_FILE)
|
17
|
+
end
|
18
|
+
STATUS_INIT = :on
|
19
|
+
else
|
20
|
+
ON_MASTER_CHANNEL = false
|
21
|
+
CHANNEL = ARGV[0]
|
22
|
+
ADMIN_USERS = ARGV[1].split(",")
|
23
|
+
RULES_FILE = ARGV[2]
|
24
|
+
STATUS_INIT = ARGV[3].to_sym
|
25
|
+
end
|
26
|
+
|
27
|
+
SHORTCUTS_FILE = "slack-smart-bot_shortcuts_#{CHANNEL}.rb".gsub(" ", "_")
|
28
|
+
|
29
|
+
class SlackSmartBot
|
30
|
+
attr_accessor :config, :client, :wclient
|
31
|
+
|
32
|
+
def initialize(config)
|
33
|
+
Dir.mkdir("./logs") unless Dir.exist?("./logs")
|
34
|
+
Dir.mkdir("./shortcuts") unless Dir.exist?("./shortcuts")
|
35
|
+
logfile = File.basename(RULES_FILE.gsub("_rules_", "_logs_"), ".rb") + ".log"
|
36
|
+
@logger = Logger.new("./logs/#{logfile}")
|
37
|
+
config_log = config.dup
|
38
|
+
config_log.delete(:token)
|
39
|
+
@logger.info "Initializing bot: #{config_log.inspect}"
|
40
|
+
|
41
|
+
config[:channel] = CHANNEL
|
42
|
+
self.config = config
|
43
|
+
|
44
|
+
Slack.configure do |conf|
|
45
|
+
conf.token = config[:token]
|
46
|
+
end
|
47
|
+
self.wclient = Slack::Web::Client.new
|
48
|
+
self.client = Slack::RealTime::Client.new
|
49
|
+
|
50
|
+
@listening = Array.new
|
51
|
+
|
52
|
+
@bots_created = Hash.new()
|
53
|
+
@shortcuts = Hash.new()
|
54
|
+
@shortcuts[:all] = Hash.new()
|
55
|
+
|
56
|
+
if File.exist?("./shortcuts/#{SHORTCUTS_FILE}")
|
57
|
+
file_sc = IO.readlines("./shortcuts/#{SHORTCUTS_FILE}").join
|
58
|
+
unless file_sc.to_s() == ""
|
59
|
+
@shortcuts = eval(file_sc)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if ON_MASTER_CHANNEL and File.exist?($0.gsub(".rb", "_bots.rb"))
|
64
|
+
file_conf = IO.readlines($0.gsub(".rb", "_bots.rb")).join
|
65
|
+
unless file_conf.to_s() == ""
|
66
|
+
@bots_created = eval(file_conf)
|
67
|
+
if @bots_created.kind_of?(Hash)
|
68
|
+
@bots_created.each { |key, value|
|
69
|
+
@logger.info "ruby #{$0} \"#{value[:channel_name]}\" \"#{value[:admins]}\" \"#{value[:rules_file]}\" #{value[:status].to_sym}"
|
70
|
+
t = Thread.new do
|
71
|
+
`ruby #{$0} \"#{value[:channel_name]}\" \"#{value[:admins]}\" \"#{value[:rules_file]}\" #{value[:status].to_sym}`
|
72
|
+
end
|
73
|
+
value[:thread] = t
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
wclient.auth_test
|
80
|
+
|
81
|
+
begin
|
82
|
+
user_info = wclient.users_info(user: "#{"@" if config[:nick][0] != "@"}#{config[:nick]}")
|
83
|
+
config[:nick_id] = user_info.user.id
|
84
|
+
rescue Exception => stack
|
85
|
+
@logger.fatal stack
|
86
|
+
abort("The bot user specified on settings: #{config[:nick]}, doesn't exist on Slack. Execution aborted")
|
87
|
+
end
|
88
|
+
|
89
|
+
client.on :hello do
|
90
|
+
m = "Successfully connected, welcome '#{client.self.name}' to the '#{client.team.name}' team at https://#{client.team.domain}.slack.com."
|
91
|
+
puts m
|
92
|
+
@logger.info m
|
93
|
+
respond "Smart Bot started\nIf you want to know what I can do for you: *`bot help`*\nYou can send me also a direct message."
|
94
|
+
end
|
95
|
+
|
96
|
+
@status = STATUS_INIT
|
97
|
+
@questions = Hash.new()
|
98
|
+
@channels_id = Hash.new()
|
99
|
+
@channels_name = Hash.new()
|
100
|
+
get_channels_name_and_id()
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
def update_bots_file
|
105
|
+
file = File.open($0.gsub(".rb", "_bots.rb"), "w")
|
106
|
+
bots_created = @bots_created.dup
|
107
|
+
bots_created.each { |k, v| v[:thread] = "" }
|
108
|
+
file.write bots_created.inspect
|
109
|
+
file.close
|
110
|
+
end
|
111
|
+
|
112
|
+
def update_shortcuts_file
|
113
|
+
file = File.open("./shortcuts/#{SHORTCUTS_FILE}", "w")
|
114
|
+
file.write @shortcuts.inspect
|
115
|
+
file.close
|
116
|
+
end
|
117
|
+
|
118
|
+
def get_channels_name_and_id
|
119
|
+
channels = wclient.channels_list.channels
|
120
|
+
@channels_id = Hash.new()
|
121
|
+
@channels_name = Hash.new()
|
122
|
+
channels.each do |ch|
|
123
|
+
unless ch.is_archived
|
124
|
+
@channels_id[ch.name] = ch.id
|
125
|
+
@channels_name[ch.id] = ch.name
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def listen
|
131
|
+
@salutations = [config[:nick], config[:nick_id], "bot", "smart"]
|
132
|
+
client.on :message do |data|
|
133
|
+
if data.channel[0] == "D" #Direct message
|
134
|
+
id_user = data.user
|
135
|
+
else
|
136
|
+
id_user = nil
|
137
|
+
end
|
138
|
+
user_info = wclient.users_info(user: data.user)
|
139
|
+
if !id_user.nil? or @channels_id[CHANNEL] == data.channel or user_info.user.name == config[:nick]
|
140
|
+
res = process_first(user_info.user.name, data.text, id_user)
|
141
|
+
next if res.to_s == "next"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
@logger.info "Bot listening"
|
146
|
+
client.start!
|
147
|
+
end
|
148
|
+
|
149
|
+
def process_first(nick, text, id_user)
|
150
|
+
#todo: verify if on slack on anytime nick == config[:nick]
|
151
|
+
if nick == config[:nick] or nick == (config[:nick] + " · Bot") #if message is coming from the bot
|
152
|
+
begin
|
153
|
+
@logger.info "#{nick}: #{text}"
|
154
|
+
case text
|
155
|
+
when /^Bot has been killed by/
|
156
|
+
exit!
|
157
|
+
when /^Changed status on (.+) to :(.+)/i
|
158
|
+
channel = $1
|
159
|
+
status = $2
|
160
|
+
@bots_created[channel][:status] = status.to_sym
|
161
|
+
update_bots_file()
|
162
|
+
end
|
163
|
+
return :next #don't continue analyzing
|
164
|
+
rescue Exception => stack
|
165
|
+
@logger.fatal stack
|
166
|
+
return :next
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
if text.match?(/^!?(shortcut|sc)\s(.+)/i)
|
171
|
+
shortcut = text.scan(/!?\w+\s*(.+)\s*/i).join.downcase
|
172
|
+
if text[0] == "!"
|
173
|
+
addexcl = true
|
174
|
+
else
|
175
|
+
addexcl = false
|
176
|
+
end
|
177
|
+
if @shortcuts.keys.include?(nick) and @shortcuts[nick].keys.include?(shortcut)
|
178
|
+
text = @shortcuts[nick][shortcut].dup
|
179
|
+
elsif @shortcuts.keys.include?(:all) and @shortcuts[:all].keys.include?(shortcut)
|
180
|
+
text = @shortcuts[:all][shortcut].dup
|
181
|
+
else
|
182
|
+
respond "Shortcut not found", id_user
|
183
|
+
return :next
|
184
|
+
end
|
185
|
+
text = "!" + text if addexcl and text[0] != "!"
|
186
|
+
end
|
187
|
+
|
188
|
+
if @questions.keys.include?(nick)
|
189
|
+
command = @questions[nick]
|
190
|
+
@questions[nick] = text
|
191
|
+
else
|
192
|
+
command = text
|
193
|
+
end
|
194
|
+
begin
|
195
|
+
t = Thread.new do
|
196
|
+
begin
|
197
|
+
processed = process(nick, command, id_user)
|
198
|
+
@logger.info "command: #{nick}> #{command}" if processed
|
199
|
+
if @status == :on and
|
200
|
+
((@questions.keys.include?(nick) or
|
201
|
+
@listening.include?(nick) or
|
202
|
+
!id_user.nil? or
|
203
|
+
command.match?(/^@?#{@salutations.join("|")}:*\s+(.+)$/i) or
|
204
|
+
command.match?(/^<@#{@salutations.join("|")}>\s+(.+)$/i) or
|
205
|
+
command.match?(/^!(.+)$/)))
|
206
|
+
@logger.info "command: #{nick}> #{command}" unless processed
|
207
|
+
begin
|
208
|
+
eval(File.new(RULES_FILE).read) if File.exist?(RULES_FILE)
|
209
|
+
rescue Exception => stack
|
210
|
+
@logger.fatal "ERROR ON RULES FILE: #{RULES_FILE}"
|
211
|
+
@logger.fatal stack
|
212
|
+
end
|
213
|
+
if defined?(rules)
|
214
|
+
command[0] = "" if command[0] == "!"
|
215
|
+
command.gsub!(/^@\w+:*\s*/, "")
|
216
|
+
rules(nick, command, processed, id_user)
|
217
|
+
else
|
218
|
+
@logger.warn "It seems like rules method is not defined"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
rescue Exception => stack
|
222
|
+
@logger.fatal stack
|
223
|
+
end
|
224
|
+
end
|
225
|
+
rescue => e
|
226
|
+
@logger.error "exception: #{e.inspect}"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
#help: *Commands you can use*:
|
231
|
+
#help:
|
232
|
+
def process(from, command, id_user)
|
233
|
+
firstname = from.split(/ /).first
|
234
|
+
processed = true
|
235
|
+
|
236
|
+
case command
|
237
|
+
|
238
|
+
#help: `Hello Bot`
|
239
|
+
#help: `Hello Smart`
|
240
|
+
#help: `Hello THE_NAME_OF_THE_BOT`
|
241
|
+
#help: Also apart of Hello you can use _Hallo, Hi, Hola, What's up, Hey, Hæ_
|
242
|
+
#help: Bot starts listening to you
|
243
|
+
#help:
|
244
|
+
when /^(Hello|Hallo|Hi|Hola|What's\sup|Hey|Hæ)\s(#{@salutations.join("|")})\s*$/i
|
245
|
+
if @status == :on
|
246
|
+
greetings = ["Hello", "Hallo", "Hi", "Hola", "What's up", "Hey", "Hæ"].sample
|
247
|
+
respond "#{greetings} #{firstname}", id_user
|
248
|
+
@listening << from unless @listening.include?(from)
|
249
|
+
end
|
250
|
+
|
251
|
+
#help: `Bye Bot`
|
252
|
+
#help: `Bye Smart`
|
253
|
+
#help: `Bye NAME_OF_THE_BOT`
|
254
|
+
#help: Also apart of Bye you can use _Bæ, Good Bye, Adiós, Ciao, Bless, Bless Bless, Adeu_
|
255
|
+
#help: Bot stops listening to you
|
256
|
+
#help:
|
257
|
+
when /^(Bye|Bæ|Good\sBye|Adiós|Ciao|Bless|Bless\sBless|Adeu)\s(#{@salutations.join("|")})\s*$/i
|
258
|
+
if @status == :on
|
259
|
+
bye = ["Bye", "Bæ", "Good Bye", "Adiós", "Ciao", "Bless", "Bless bless", "Adeu"].sample
|
260
|
+
respond "#{bye} #{firstname}", id_user
|
261
|
+
@listening.delete(from)
|
262
|
+
end
|
263
|
+
|
264
|
+
#help: `exit bot`
|
265
|
+
#help: `quit bot`
|
266
|
+
#help: `close bot`
|
267
|
+
#help: The bot stops running and also stops all the bots created from this master channel
|
268
|
+
#help: You can use this command only if you are an admin user and you are on the master channel
|
269
|
+
#help:
|
270
|
+
when /^exit\sbot/i, /^quit\sbot/i, /^close\sbot/i
|
271
|
+
if ON_MASTER_CHANNEL
|
272
|
+
if ADMIN_USERS.include?(from) #admin user
|
273
|
+
unless @questions.keys.include?(from)
|
274
|
+
ask("are you sure?", command, from, id_user)
|
275
|
+
else
|
276
|
+
case @questions[from]
|
277
|
+
when /yes/i, /yep/i, /sure/i
|
278
|
+
respond "Game over!", id_user
|
279
|
+
respond "Ciao #{firstname}!", id_user
|
280
|
+
@bots_created.each { |key, value|
|
281
|
+
value[:thread] = ""
|
282
|
+
send_msg_channel(key, "Bot has been closed by #{from}")
|
283
|
+
sleep 0.5
|
284
|
+
}
|
285
|
+
update_bots_file()
|
286
|
+
sleep 0.5
|
287
|
+
exit!
|
288
|
+
when /no/i, /nope/i, /cancel/i
|
289
|
+
@questions.delete(from)
|
290
|
+
respond "Thanks, I'm happy to be alive", id_user
|
291
|
+
else
|
292
|
+
respond "I don't understand", id_user
|
293
|
+
ask("are you sure do you want me to close? (yes or no)", "quit bot", from, id_user)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
else
|
297
|
+
respond "Only admin users can kill me", id_user
|
298
|
+
end
|
299
|
+
else
|
300
|
+
respond "To do this you need to be an admin user in the master channel", id_user
|
301
|
+
end
|
302
|
+
|
303
|
+
#help: `start bot`
|
304
|
+
#help: `start this bot`
|
305
|
+
#help: the bot will start to listen
|
306
|
+
#help: You can use this command only if you are an admin user
|
307
|
+
#help:
|
308
|
+
when /^start\s(this\s)?bot$/i
|
309
|
+
if ADMIN_USERS.include?(from) #admin user
|
310
|
+
respond "This bot is running and listening from now on. You can pause again: pause this bot", id_user
|
311
|
+
@status = :on
|
312
|
+
unless ON_MASTER_CHANNEL
|
313
|
+
get_channels_name_and_id() unless @channels_name.keys.include?(MASTER_CHANNEL) and @channels_name.keys.include?(CHANNEL)
|
314
|
+
send_msg_channel @channels_name[MASTER_CHANNEL], "Changed status on #{@channels_name[CHANNEL]} to :on"
|
315
|
+
end
|
316
|
+
else
|
317
|
+
respond "Only admin users can change my status", id_user
|
318
|
+
end
|
319
|
+
|
320
|
+
#help: `pause bot`
|
321
|
+
#help: `pause this bot`
|
322
|
+
#help: the bot will pause so it will listen only to admin commands
|
323
|
+
#help: You can use this command only if you are an admin user
|
324
|
+
#help:
|
325
|
+
when /^pause\s(this\s)?bot$/i
|
326
|
+
if ADMIN_USERS.include?(from) #admin user
|
327
|
+
respond "This bot is paused from now on. You can start it again: start this bot", id_user
|
328
|
+
respond "zZzzzzZzzzzZZZZZZzzzzzzzz", id_user
|
329
|
+
@status = :paused
|
330
|
+
unless ON_MASTER_CHANNEL
|
331
|
+
get_channels_name_and_id() unless @channels_name.keys.include?(MASTER_CHANNEL) and @channels_name.keys.include?(CHANNEL)
|
332
|
+
send_msg_channel @channels_name[MASTER_CHANNEL], "Changed status on #{@channels_name[CHANNEL]} to :paused"
|
333
|
+
end
|
334
|
+
else
|
335
|
+
respond "Only admin users can put me on pause", id_user
|
336
|
+
end
|
337
|
+
|
338
|
+
#help: `bot status`
|
339
|
+
#help: Displays the status of the bot
|
340
|
+
#help: If on master channel and admin user also it will display info about bots created
|
341
|
+
#help:
|
342
|
+
when /^bot\sstatus/i
|
343
|
+
respond "Status: #{@status}. Rules file: #{File.basename RULES_FILE} ", id_user
|
344
|
+
if @status == :on
|
345
|
+
respond "I'm listening to [#{@listening.join(", ")}]", id_user
|
346
|
+
if ON_MASTER_CHANNEL and ADMIN_USERS.include?(from)
|
347
|
+
@bots_created.each { |key, value|
|
348
|
+
respond "#{key}: #{value}", id_user
|
349
|
+
}
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
#help: `create bot on CHANNEL_NAME`
|
354
|
+
#help: creates a new bot on the channel specified
|
355
|
+
#help: it will work only if you are on Master channel
|
356
|
+
#help:
|
357
|
+
when /^create\sbot\son\s(.+)\s*/i
|
358
|
+
if ON_MASTER_CHANNEL
|
359
|
+
channel = $1
|
360
|
+
if @bots_created.keys.include?(channel)
|
361
|
+
respond "There is already a bot in this channel: #{channel}, kill it before", id_user
|
362
|
+
else
|
363
|
+
get_channels_name_and_id() unless @channels_name.keys.include?(channel) or @channels_id.keys.include?(channel)
|
364
|
+
channel_id = nil
|
365
|
+
if @channels_name.key?(channel) #it is an id
|
366
|
+
channel_id = channel
|
367
|
+
elsif @channels_id.key?(channel) #it is a channel name
|
368
|
+
channel_id = @channels_id[channel]
|
369
|
+
end
|
370
|
+
|
371
|
+
if !channel_id.nil?
|
372
|
+
if channel_id != config[:channel]
|
373
|
+
begin
|
374
|
+
rules_file = "slack-smart-bot_rules_#{channel_id}_#{from.gsub(" ", "_")}.rb"
|
375
|
+
if defined?(RULES_FOLDER)
|
376
|
+
rules_file = RULES_FOLDER + rules_file
|
377
|
+
else
|
378
|
+
Dir.mkdir("rules") unless Dir.exist?("rules")
|
379
|
+
Dir.mkdir("rules/#{channel_id}") unless Dir.exist?("rules/#{channel_id}")
|
380
|
+
rules_file = "./rules/#{channel_id}/" + rules_file
|
381
|
+
end
|
382
|
+
default_rules = (__FILE__).gsub(/\.rb$/, "_rules.rb")
|
383
|
+
File.delete(rules_file) if File.exist?(rules_file)
|
384
|
+
FileUtils.copy_file(default_rules, rules_file) unless File.exist?(rules_file)
|
385
|
+
admin_users = Array.new()
|
386
|
+
admin_users = [from] + MASTER_USERS
|
387
|
+
admin_users.uniq!
|
388
|
+
@logger.info "ruby #{$0} \"#{channel}\" \"#{admin_users.join(",")}\" \"#{rules_file}\" on"
|
389
|
+
t = Thread.new do
|
390
|
+
`ruby #{$0} \"#{channel}\" \"#{admin_users.join(",")}\" \"#{rules_file}\" on`
|
391
|
+
end
|
392
|
+
@bots_created[channel] = {
|
393
|
+
creator_name: from,
|
394
|
+
channel_id: channel_id,
|
395
|
+
channel_name: @channels_name[channel_id],
|
396
|
+
status: :on,
|
397
|
+
created: Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z")[0..18],
|
398
|
+
rules_file: rules_file,
|
399
|
+
admins: admin_users.join(","),
|
400
|
+
thread: t,
|
401
|
+
}
|
402
|
+
respond "The bot has been created on channel: #{channel}. Rules file: #{File.basename rules_file}", id_user
|
403
|
+
update_bots_file()
|
404
|
+
rescue Exception => stack
|
405
|
+
@logger.fatal stack
|
406
|
+
message = "Problem creating the bot on channel #{channel}. Error: <#{stack}>."
|
407
|
+
@logger.error message
|
408
|
+
respond message, id_user
|
409
|
+
end
|
410
|
+
else
|
411
|
+
respond "There is already a bot in this channel: #{channel}, and it is the Master Channel!", id_user
|
412
|
+
end
|
413
|
+
else
|
414
|
+
respond "There is no channel with that name: #{channel}, please be sure is written exactly the same", id_user
|
415
|
+
end
|
416
|
+
end
|
417
|
+
else
|
418
|
+
respond "Sorry I cannot create bots from this channel, please visit the master channel", id_user
|
419
|
+
end
|
420
|
+
|
421
|
+
#help: `kill bot on CHANNEL_NAME`
|
422
|
+
#help: kills the bot on the specified channel
|
423
|
+
#help: Only works if you are on Master channel and you created that bot or you are an admin user
|
424
|
+
#help:
|
425
|
+
when /^kill\sbot\son\s(.+)\s*/i
|
426
|
+
if ON_MASTER_CHANNEL
|
427
|
+
channel = $1
|
428
|
+
if @bots_created.keys.include?(channel)
|
429
|
+
if @bots_created[channel][:admins].split(",").include?(from)
|
430
|
+
if @bots_created[channel][:thread].kind_of?(Thread) and @bots_created[channel][:thread].alive?
|
431
|
+
@bots_created[channel][:thread].kill
|
432
|
+
end
|
433
|
+
@bots_created.delete(channel)
|
434
|
+
update_bots_file()
|
435
|
+
respond "Bot on channel: #{channel}, has been killed and deleted.", id_user
|
436
|
+
send_msg_channel(channel, "Bot has been killed by #{from}")
|
437
|
+
else
|
438
|
+
respond "You need to be the creator or an admin of that channel", id_user
|
439
|
+
end
|
440
|
+
else
|
441
|
+
respond "There is no bot in this channel: #{channel}", id_user
|
442
|
+
end
|
443
|
+
else
|
444
|
+
respond "Sorry I cannot kill bots from this channel, please visit the master channel", id_user
|
445
|
+
end
|
446
|
+
|
447
|
+
#help: `bot help`
|
448
|
+
#help: `bot what can I do?`
|
449
|
+
#help: it will display this help
|
450
|
+
#help:
|
451
|
+
when /^bot help/i, /^bot,? what can I do/i
|
452
|
+
help_message = IO.readlines(__FILE__).join
|
453
|
+
help_message_rules = IO.readlines(RULES_FILE).join
|
454
|
+
respond help_message.scan(/#\s*help\s*:(.*)/).join("\n"), id_user
|
455
|
+
respond help_message_rules.scan(/#\s*help\s*:(.*)/).join("\n"), id_user
|
456
|
+
else
|
457
|
+
processed = false
|
458
|
+
end
|
459
|
+
|
460
|
+
#only when :on and (listening or on demand or direct message)
|
461
|
+
if @status == :on and
|
462
|
+
((@questions.keys.include?(from) or
|
463
|
+
@listening.include?(from) or
|
464
|
+
!id_user.nil? or
|
465
|
+
command.match?(/^@?#{@salutations.join("|")}:*\s+(.+)$/i) or
|
466
|
+
command.match?(/^!(.+)$/)))
|
467
|
+
processed2 = true
|
468
|
+
|
469
|
+
# help:
|
470
|
+
# help: *These commands will run only when the smart bot is listening to you or on demand*, for example:
|
471
|
+
# help: `!THE_COMMAND`
|
472
|
+
# help: `@bot THE_COMMAND`
|
473
|
+
# help: `@NAME_OF_BOT THE_COMMAND`
|
474
|
+
# help: `NAME_OF_BOT THE_COMMAND`
|
475
|
+
# help:
|
476
|
+
case command
|
477
|
+
|
478
|
+
#help: `add shortcut NAME: COMMAND`
|
479
|
+
#help: `add shortcut for all NAME: COMMAND`
|
480
|
+
#help: `shortchut NAME: COMMAND`
|
481
|
+
#help: `shortchut for all NAME: COMMAND`
|
482
|
+
#help: It will add a shortcut that will execute the command we supply.
|
483
|
+
#help: In case we supply 'for all' then the shorcut will be available for everybody
|
484
|
+
#help: Example:
|
485
|
+
#help: `add shortcut for all Spanish account: code require 'iso/iban'; 10.times {puts ISO::IBAN.random('ES')}`
|
486
|
+
#help: Then to call this shortcut:
|
487
|
+
#help: `sc spanish account`
|
488
|
+
#help: `shortcut Spanish Account`
|
489
|
+
#help:
|
490
|
+
when /(add\s)?shortcut\s(for\sall)?\s*(.+):\s(.+)/i
|
491
|
+
for_all = $2
|
492
|
+
shortcut_name = $3.to_s.downcase
|
493
|
+
command_to_run = $4
|
494
|
+
@shortcuts[from] = Hash.new() unless @shortcuts.keys.include?(from)
|
495
|
+
|
496
|
+
if !ADMIN_USERS.include?(from) and @shortcuts[:all].include?(shortcut_name) and !@shortcuts[from].include?(shortcut_name)
|
497
|
+
respond "Only the creator of the shortcut or an admin user can modify it", id_user
|
498
|
+
elsif !@shortcuts[from].include?(shortcut_name)
|
499
|
+
#new shortcut
|
500
|
+
@shortcuts[from][shortcut_name] = command_to_run
|
501
|
+
@shortcuts[:all][shortcut_name] = command_to_run if for_all.to_s != ""
|
502
|
+
update_shortcuts_file()
|
503
|
+
respond "shortcut added", id_user
|
504
|
+
else
|
505
|
+
|
506
|
+
#are you sure? to avoid overwriting existing
|
507
|
+
unless @questions.keys.include?(from)
|
508
|
+
ask("The shortcut already exists, are you sure you want to overwrite it?", command, from, id_user)
|
509
|
+
else
|
510
|
+
case @questions[from]
|
511
|
+
when /^(yes|yep)/i
|
512
|
+
@shortcuts[from][shortcut_name] = command_to_run
|
513
|
+
@shortcuts[:all][shortcut_name] = command_to_run if for_all.to_s != ""
|
514
|
+
update_shortcuts_file()
|
515
|
+
respond "shortcut added", id_user
|
516
|
+
@questions.delete(from)
|
517
|
+
when /^no/i
|
518
|
+
respond "ok, I won't add it", id_user
|
519
|
+
@questions.delete(from)
|
520
|
+
else
|
521
|
+
respond "I don't understand, yes or no?", id_user
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
#help: `delete shortcut NAME`
|
527
|
+
#help: It will delete the shortcut with the supplied name
|
528
|
+
#help:
|
529
|
+
when /delete\sshortcut\s(.+)/i
|
530
|
+
shortcut = $1.to_s.downcase
|
531
|
+
deleted = false
|
532
|
+
|
533
|
+
if !ADMIN_USERS.include?(from) and @shortcuts[:all].include?(shortcut) and !@shortcuts[from].include?(shortcut)
|
534
|
+
respond "Only the creator of the shortcut or an admin user can delete it", id_user
|
535
|
+
elsif (@shortcuts.keys.include?(from) and @shortcuts[from].keys.include?(shortcut)) or
|
536
|
+
(ADMIN_USERS.include?(from) and @shortcuts[:all].include?(shortcut))
|
537
|
+
#are you sure? to avoid deleting by mistake
|
538
|
+
unless @questions.keys.include?(from)
|
539
|
+
ask("are you sure you want to delete it?", command, from, id_user)
|
540
|
+
else
|
541
|
+
case @questions[from]
|
542
|
+
when /^(yes|yep)/i
|
543
|
+
respond "shortcut deleted!", id_user
|
544
|
+
respond "#{shortcut}: #{@shortcuts[from][shortcut]}", id_user
|
545
|
+
@shortcuts[from].delete(shortcut)
|
546
|
+
@shortcuts[:all].delete(shortcut)
|
547
|
+
@questions.delete(from)
|
548
|
+
update_shortcuts_file()
|
549
|
+
when /^no/i
|
550
|
+
respond "ok, I won't delete it", id_user
|
551
|
+
@questions.delete(from)
|
552
|
+
else
|
553
|
+
respond "I don't understand, yes or no?", id_user
|
554
|
+
end
|
555
|
+
end
|
556
|
+
else
|
557
|
+
respond "shortcut not found", id_user
|
558
|
+
end
|
559
|
+
|
560
|
+
#help: `see shortcuts`
|
561
|
+
#help: It will display the shortcuts stored for the user and for :all
|
562
|
+
#help:
|
563
|
+
when /see\sshortcuts/i
|
564
|
+
msg = ""
|
565
|
+
if @shortcuts[:all].keys.size > 0
|
566
|
+
msg = "*Available shortcuts for all:*\n"
|
567
|
+
@shortcuts[:all].each { |name, value|
|
568
|
+
msg += " _#{name}: #{value}_\n"
|
569
|
+
}
|
570
|
+
respond msg, id_user
|
571
|
+
end
|
572
|
+
|
573
|
+
if @shortcuts.keys.include?(from) and @shortcuts[from].keys.size > 0
|
574
|
+
new_hash = @shortcuts[from].dup
|
575
|
+
@shortcuts[:all].keys.each { |k| new_hash.delete(k) }
|
576
|
+
if new_hash.keys.size > 0
|
577
|
+
msg = "*Available shortcuts for #{from}:*\n"
|
578
|
+
new_hash.each { |name, value|
|
579
|
+
msg += " _#{name}: #{value}_\n"
|
580
|
+
}
|
581
|
+
respond msg, id_user
|
582
|
+
end
|
583
|
+
end
|
584
|
+
respond "No shortcuts found", id_user if msg == ""
|
585
|
+
|
586
|
+
#help: `id channel CHANNEL_NAME`
|
587
|
+
#help: shows the id of a channel name
|
588
|
+
#help:
|
589
|
+
when /id channel (.+)/
|
590
|
+
channel_name = $1
|
591
|
+
get_channels_name_and_id()
|
592
|
+
if @channels_id.keys.include?(channel_name)
|
593
|
+
respond "the id of #{channel_name} is #{@channels_id[channel_name]}", id_user
|
594
|
+
else
|
595
|
+
respond "channel: #{channel_name} not found", id_user
|
596
|
+
end
|
597
|
+
|
598
|
+
# help: `ruby RUBY_CODE`
|
599
|
+
# help: `code RUBY_CODE`
|
600
|
+
# help: runs the code supplied and returns the output. Examples:
|
601
|
+
# help: `code puts (34344/99)*(34+14)`
|
602
|
+
# help: `ruby require 'json'; res=[]; 20.times {res<<rand(100)}; my_json={result: res}; puts my_json.to_json`
|
603
|
+
# help:
|
604
|
+
when /ruby\s(.+)/im, /code\s(.+)/im
|
605
|
+
code = $1
|
606
|
+
code.gsub!("\\n", "\n")
|
607
|
+
unless code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File") or
|
608
|
+
code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO") or
|
609
|
+
code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
|
610
|
+
code.include?("ENV")
|
611
|
+
begin
|
612
|
+
stdout, stderr, status = Open3.capture3("ruby -e \"#{code.gsub('"', '\"')}\"")
|
613
|
+
if stderr == ""
|
614
|
+
if stdout == ""
|
615
|
+
respond "Nothing returned. Remember you need to use p or puts to print", id_user
|
616
|
+
else
|
617
|
+
respond stdout, id_user
|
618
|
+
end
|
619
|
+
else
|
620
|
+
respond stderr, id_user
|
621
|
+
end
|
622
|
+
rescue Exception => exc
|
623
|
+
respond exc, id_user
|
624
|
+
end
|
625
|
+
else
|
626
|
+
respond "Sorry I cannot run this due security issues", id_user
|
627
|
+
end
|
628
|
+
else
|
629
|
+
processed2 = false
|
630
|
+
end
|
631
|
+
processed = true if processed or processed2
|
632
|
+
end
|
633
|
+
|
634
|
+
return processed
|
635
|
+
end
|
636
|
+
|
637
|
+
def respond(msg, id_user = nil)
|
638
|
+
if id_user.nil?
|
639
|
+
client.message(channel: @channels_id[CHANNEL], text: msg, as_user: true)
|
640
|
+
else #private message
|
641
|
+
send_msg_user(id_user, msg)
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
#context: previous message
|
646
|
+
#to: user that should answer
|
647
|
+
def ask(question, context, to, id_user = nil)
|
648
|
+
if id_user.nil?
|
649
|
+
client.message(channel: @channels_id[CHANNEL], text: "#{to}: #{question}", as_user: true)
|
650
|
+
else #private message
|
651
|
+
send_msg_user(id_user, "#{to}: #{question}")
|
652
|
+
end
|
653
|
+
@questions[to] = context
|
654
|
+
end
|
655
|
+
|
656
|
+
# to: (String) Channel name or id
|
657
|
+
# msg: (String) message to send
|
658
|
+
def send_msg_channel(to, msg)
|
659
|
+
unless msg == ""
|
660
|
+
get_channels_name_and_id() unless @channels_name.key?(to) or @channels_id.key?(to)
|
661
|
+
if @channels_name.key?(to) #it is an id
|
662
|
+
channel_id = to
|
663
|
+
elsif @channels_id.key?(to) #it is a channel name
|
664
|
+
channel_id = @channels_id[to]
|
665
|
+
else
|
666
|
+
@logger.fatal "Channel: #{to} not found. Message: #{msg}"
|
667
|
+
end
|
668
|
+
client.message(channel: channel_id, text: msg, as_user: true)
|
669
|
+
end
|
670
|
+
end
|
671
|
+
|
672
|
+
#to send messages without listening for a response to users
|
673
|
+
def send_msg_user(id_user, msg)
|
674
|
+
unless msg == ""
|
675
|
+
im = wclient.im_open(user: id_user)
|
676
|
+
client.message(channel: im["channel"]["id"], as_user: true, text: msg)
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
private :update_bots_file, :get_channels_name_and_id, :update_shortcuts_file
|
681
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
#for the case of testing, just run this file adding in the end a call to rules with the parameters you want
|
2
|
+
if defined?(respond)
|
3
|
+
@testing = false
|
4
|
+
else
|
5
|
+
@testing = true
|
6
|
+
@questions = Hash.new()
|
7
|
+
|
8
|
+
def respond(message, id_user)
|
9
|
+
puts message
|
10
|
+
end
|
11
|
+
|
12
|
+
#context: previous message
|
13
|
+
#to: user that should answer
|
14
|
+
def ask(question, context, to, id_user)
|
15
|
+
puts "Bot: #{question}"
|
16
|
+
@questions[to] = context
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# from: Full name of the person sending the message
|
21
|
+
# command: command to run
|
22
|
+
# processed: in case the command has been already processed on Bot class, by default false
|
23
|
+
# help:
|
24
|
+
# help: *These are specific commands on this bot.*
|
25
|
+
# help: They will be accessible only when the bot is listening to you just writing the command
|
26
|
+
# help: or the bot is not listening to you but requested on demand, for example:
|
27
|
+
# help: `!THE_COMMAND`
|
28
|
+
# help: `@bot THE_COMMAND`
|
29
|
+
# help: `@NAME_OF_BOT THE_COMMAND`
|
30
|
+
# help: `NAME_OF_BOT THE_COMMAND`
|
31
|
+
# help:
|
32
|
+
def rules(from, command, processed, id_user)
|
33
|
+
if @testing
|
34
|
+
puts "#{from}: #{command}"
|
35
|
+
if @questions.keys.include?(from)
|
36
|
+
context = @questions[from]
|
37
|
+
@questions[from] = command
|
38
|
+
command = context
|
39
|
+
end
|
40
|
+
end
|
41
|
+
firstname = from.split(" ").first
|
42
|
+
case command
|
43
|
+
|
44
|
+
# help: `echo SOMETHING`
|
45
|
+
# help: repeats SOMETHING
|
46
|
+
# help:
|
47
|
+
when /echo\s(.+)/i
|
48
|
+
respond $1, id_user
|
49
|
+
|
50
|
+
# help: `go to sleep`
|
51
|
+
# help: it will sleep the bot for 10 seconds
|
52
|
+
# help:
|
53
|
+
when /go\sto\ssleep/i
|
54
|
+
unless @questions.keys.include?(from)
|
55
|
+
ask("do you want me to take a siesta?", command, from, id_user)
|
56
|
+
else
|
57
|
+
case @questions[from]
|
58
|
+
when /yes/i, /yep/i, /sure/i
|
59
|
+
respond "zZzzzzzZZZZZZzzzzzzz!", id_user
|
60
|
+
respond "I'll be sleeping for 10 secs... just for you", id_user
|
61
|
+
sleep 10
|
62
|
+
when /no/i, /nope/i, /cancel/i
|
63
|
+
@questions.delete(from)
|
64
|
+
respond "Thanks, I'm happy to be awake", id_user
|
65
|
+
else
|
66
|
+
respond "I don't understand", id_user
|
67
|
+
ask("are you sure do you want me to sleep? (yes or no)", "go to sleep", from, id_user)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
else
|
71
|
+
unless processed
|
72
|
+
resp = %w{ what huh sorry }.sample
|
73
|
+
respond "#{firstname}: #{resp}?", id_user
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#for the case of testing just running this file, write the dialogue in here:
|
79
|
+
if @testing
|
80
|
+
rules "Peter Johson", "go to sleep, you look tired", false
|
81
|
+
rules "Peter Johson", "yes", false
|
82
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: slack-smart-bot
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mario Ruiz
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-05-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: slack-ruby-client
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.14'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.14.2
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.14'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.14.2
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: celluloid-io
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.17'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.17.3
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0.17'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.17.3
|
53
|
+
description: "Create a Slack bot that is smart and so easy to expand, create new bots
|
54
|
+
on demand, run ruby code on chat, create shortcuts... \n The main scope of this
|
55
|
+
gem is to be used internally in the company so teams can create team channels with
|
56
|
+
their own bot to help them on their daily work, almost everything is suitable to
|
57
|
+
be automated!! \n slack-smart-bot can create bots on demand, create shortcuts,
|
58
|
+
run ruby code... just on a chat channel. \n You can access it just from your mobile
|
59
|
+
phone if you want and run those tests you forgot to run, get the results, restart
|
60
|
+
a server... no limits."
|
61
|
+
email: marioruizs@gmail.com
|
62
|
+
executables: []
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files:
|
65
|
+
- LICENSE
|
66
|
+
- README.md
|
67
|
+
files:
|
68
|
+
- ".yardopts"
|
69
|
+
- LICENSE
|
70
|
+
- README.md
|
71
|
+
- lib/slack-smart-bot.rb
|
72
|
+
- lib/slack-smart-bot_rules.rb
|
73
|
+
homepage: https://github.com/MarioRuiz/slack-smart-bot
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata: {}
|
77
|
+
post_install_message: Thanks for installing! Visit us on https://github.com/MarioRuiz/slack-smart-bot
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '2.4'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 2.7.6
|
94
|
+
signing_key:
|
95
|
+
specification_version: 4
|
96
|
+
summary: Create a Slack bot that is smart and so easy to expand, create new bots on
|
97
|
+
demand, run ruby code on chat, create shortcuts...
|
98
|
+
test_files: []
|