slack-smart-bot 0.9.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -1
  3. data/README.md +80 -175
  4. data/lib/slack-smart-bot.rb +95 -1276
  5. data/lib/slack-smart-bot_rules.rb +50 -69
  6. data/lib/slack/smart-bot/comm.rb +197 -0
  7. data/lib/slack/smart-bot/commands/general/bot_help.rb +74 -0
  8. data/lib/slack/smart-bot/commands/general/bot_status.rb +41 -0
  9. data/lib/slack/smart-bot/commands/general/bye_bot.rb +17 -0
  10. data/lib/slack/smart-bot/commands/general/hi_bot.rb +24 -0
  11. data/lib/slack/smart-bot/commands/general/stop_using_rules.rb +44 -0
  12. data/lib/slack/smart-bot/commands/general/use_rules.rb +48 -0
  13. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +64 -0
  14. data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +87 -0
  15. data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +63 -0
  16. data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +21 -0
  17. data/lib/slack/smart-bot/commands/on_bot/admin/pause_routine.rb +26 -0
  18. data/lib/slack/smart-bot/commands/on_bot/admin/remove_routine.rb +28 -0
  19. data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +53 -0
  20. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +57 -0
  21. data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +20 -0
  22. data/lib/slack/smart-bot/commands/on_bot/admin/start_routine.rb +27 -0
  23. data/lib/slack/smart-bot/commands/on_bot/admin/stop_using_rules_on.rb +30 -0
  24. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +41 -0
  25. data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +48 -0
  26. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +32 -0
  27. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +37 -0
  28. data/lib/slack/smart-bot/commands/on_master/admin/kill_bot_on_channel.rb +38 -0
  29. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +42 -0
  30. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +35 -0
  31. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +94 -0
  32. data/lib/slack/smart-bot/listen.rb +36 -0
  33. data/lib/slack/smart-bot/process.rb +169 -0
  34. data/lib/slack/smart-bot/process_first.rb +201 -0
  35. data/lib/slack/smart-bot/treat_message.rb +139 -0
  36. data/lib/slack/smart-bot/utils.rb +299 -0
  37. metadata +71 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f592d79501c1e232639526c3eabca1ecd625987ba4ad206c581d2890290872f
4
- data.tar.gz: 290e448af56d4b77f7ae0cf22c545ee7f5105d104d4a77ab131b4b4df7d80189
3
+ metadata.gz: 2d14d69e55b288d326662672ab780c11801f6210c9b6c312c27192039e05598d
4
+ data.tar.gz: 073cf649b0b011e03fe8ebd0cb40e3163e4fcea38b139e58b0f1e92bb6e98084
5
5
  SHA512:
6
- metadata.gz: 3fcd2060213709eef4d99937f33d116e6a55bb4f23084398bdf7b832211e7849f4e97d6187a12ac3db1f500e31fde3da2ba59cc26402cf1ed70b6fd3c6196464
7
- data.tar.gz: ee2e625db0a043de5fcdecb4ab66b294d6bfc51ef011c62a258132559b75c49190d8b043d0b57896d943254aace9cc1eeeee73e77e84270969bdd649d9cab1fd
6
+ metadata.gz: c2969998ea41b817c4924b1ab850e7f7690d6c65b2e8954768104d19712ce9b649727405b58106d9c6c6828b9eeb23c8bd2359251cbf3c4878856c266d4ddd85
7
+ data.tar.gz: c7ac1ffd674ea068b06e81de18c8176edb416b77d2f1f2ad3729bc4c42c2578c8cca7271d17573f9bcde779e6d5771e4069fea8b0fe8a20526dd5d936a4caa73
data/.yardopts CHANGED
@@ -2,4 +2,8 @@
2
2
  --title 'slack-smart-bot - Create a Slack bot that is smart and easy to expand.'
3
3
  --charset utf-8
4
4
  --markup markdown
5
- 'lib/**/*.rb' - '*.md' - 'LICENSE'
5
+ -
6
+ lib/**/*.rb
7
+ lib/slack/smart-bot/*.rb
8
+ *.md
9
+ LICENSE
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Slack Smart Bot
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/slack-smart-bot.svg)](https://rubygems.org/gems/slack-smart-bot)
4
+ [![Build Status](https://travis-ci.com/MarioRuiz/slack-smart-bot.svg?branch=master)](https://github.com/MarioRuiz/slack-smart-bot)
5
+ [![Coverage Status](https://coveralls.io/repos/github/MarioRuiz/slack-smart-bot/badge.svg?branch=master)](https://coveralls.io/github/MarioRuiz/slack-smart-bot?branch=master)
4
6
 
5
7
  Create a Slack bot that is really smart and so easy to expand.
6
8
 
@@ -13,18 +15,22 @@ slack-smart-bot can create bots on demand, create shortcuts, run ruby code... ju
13
15
  - [Installation and configuration](#installation-and-configuration)
14
16
  - [Usage](#usage)
15
17
  * [creating the MASTER BOT](#creating-the-master-bot)
16
- * [How to access the smart bot](#how-to-access-the-smart-bot)
17
- * [Available commands even when the bot is not listening to you](#available-commands-even-when-the-bot-is-not-listening-to-you)
18
- * [Available commands only when listening to you or on demand or in a private conversation with the Smart Bot](#available-commands-only-when-listening-to-you-or-on-demand-or-in-a-private-conversation-with-the-smart-bot)
19
- * [Available commands from channels without a Smart Bot](#available-commands-from-channels-without-a-smart-bot)
20
- * [Creating cloud bots](#creating-cloud-bots)
18
+ * [How to access the Smart Bot](#how-to-access-the-smart-bot)
19
+ * [Bot Help](#bot-help)
20
+ * [Bot Management](#bot-management)
21
+ + [Cloud Bots](#cloud-bots)
22
+ * [Extending rules to other channels](#extending-rules-to-other-channels)
23
+ * [Using rules from other channels](#using-rules-from-other-channels)
24
+ * [Running Ruby code on a conversation](#running-ruby-code-on-a-conversation)
25
+ * [Sending notifications](#sending-notifications)
26
+ * [Shortcuts](#shortcuts)
27
+ * [Routines](#routines)
21
28
  * [Tips](#tips)
22
29
  + [Send a file](#send-a-file)
23
30
  + [Download a file](#download-a-file)
24
31
  - [Contributing](#contributing)
25
32
  - [License](#license)
26
33
 
27
-
28
34
  ## Installation and configuration
29
35
 
30
36
  $ gem install slack-smart-bot
@@ -80,8 +86,9 @@ You can get one by any of these options:
80
86
  ### creating the MASTER BOT
81
87
  Let's guess the file you created was called my_smart_bot.rb so, just run it:
82
88
  ```
83
- ruby my_smart_bot.rb
89
+ nohup ruby my_smart_bot.rb&
84
90
  ```
91
+ nohup will prevent the terminal to send the signal exception: SIGHUP and kill the bot. & will run the process in background. You can use instead: `ruby my_smart_bot.rb & disown`
85
92
 
86
93
  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
87
94
 
@@ -92,7 +99,8 @@ You can add all the rules you want for your bot in the rules file, this is an ex
92
99
  ```ruby
93
100
  def rules(user, command, processed, dest)
94
101
  from = user.name
95
- firstname = from.split(" ").first
102
+ display_name = user.profile.display_name
103
+
96
104
  case command
97
105
 
98
106
  # help: `echo SOMETHING`
@@ -106,7 +114,7 @@ def rules(user, command, processed, dest)
106
114
  # help:
107
115
  when /^go\sto\ssleep/i
108
116
  unless @questions.keys.include?(from)
109
- ask("do you want me to take a siesta?", command, from)
117
+ ask "do you want me to take a siesta?"
110
118
  else
111
119
  case @questions[from]
112
120
  when /yes/i, /yep/i, /sure/i
@@ -119,7 +127,7 @@ def rules(user, command, processed, dest)
119
127
  respond "Thanks, I'm happy to be awake"
120
128
  else
121
129
  respond "I don't understand"
122
- ask("are you sure do you want me to sleep? (yes or no)", "go to sleep", from)
130
+ ask "are you sure do you want me to sleep? (yes or no)"
123
131
  end
124
132
  end
125
133
 
@@ -128,231 +136,128 @@ def rules(user, command, processed, dest)
128
136
  # help: It will run the process and report the results when done
129
137
  # help:
130
138
  when /^run something/i
131
- respond "Running", dest
139
+ respond "Running"
132
140
 
133
141
  process_to_run = "ruby -v"
134
- process_to_run = ("cd #{project_folder} &&" + process_to_run) if defined?(project_folder)
135
142
  stdout, stderr, status = Open3.capture3(process_to_run)
136
143
  if stderr == ""
137
144
  if stdout == ""
138
- respond "#{user.name}: Nothing returned.", dest
145
+ respond "#{user.name}: Nothing returned."
139
146
  else
140
- respond "#{user.name}: #{stdout}", dest
147
+ respond "#{user.name}: #{stdout}"
141
148
  end
142
149
  else
143
- respond "#{user.name}: #{stderr}", dest
150
+ respond "#{user.name}: #{stdout} #{stderr}"
144
151
  end
145
152
 
146
153
  else
147
154
  unless processed
148
- resp = %w{ what huh sorry }.sample
149
- respond "#{firstname}: #{resp}?"
155
+ dont_understand()
150
156
  end
151
157
  end
152
158
  end
153
159
 
154
160
  ```
155
- ### How to access the smart bot
161
+ ### How to access the Smart Bot
156
162
  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.
157
163
 
158
- ### Available commands even when the bot is not listening to you
159
- Some of the commands are available always even when the bot is not listening to you but it is running
160
-
161
- **_`bot help`_**
162
-
163
- **_`bot what can I do?`_**
164
-
165
- >It will display all the commands we can use
166
- >What is displayed by this command is what is written on your rules file like this: #help: THE TEXT TO SHOW
167
-
168
- **_`Hello Bot`_**
169
-
170
- **_`Hello THE_NAME_OF_THE_BOT`_**
171
-
172
- >Also apart of Hello you can use Hallo, Hi, Hola, What's up, Hey, Hæ
173
-
174
- >Bot starts listening to you
175
-
176
- >If you want to avoid a single message to be treated by the smart bot, start the message by -
177
-
178
- **_`Bye Bot`_**
179
-
180
- **_`Bye THE_NAME_OF_THE_BOT`_**
181
-
182
- >Also apart of Bye you can use Bæ, Good Bye, Adiós, Ciao, Bless, Bless Bless, Adeu
183
-
184
- >Bot stops listening to you
185
-
186
- **_`exit bot`_**
187
-
188
- **_`quit bot`_**
189
-
190
- **_`close bot`_**
191
-
192
- >The bot stops running and also stops all the bots created from this master channel
193
-
194
- >You can use this command only if you are an admin user and you are on the master channel
195
-
196
- **_`start bot`_**
197
-
198
- **_`start this bot`_**
199
-
200
- >The bot will start to listen
201
-
202
- >You can use this command only if you are an admin user
203
-
204
- **_`pause bot`_**
205
-
206
- **_`pause this bot`_**
207
-
208
- >The bot will pause so it will listen only to admin commands
209
-
210
- >You can use this command only if you are an admin user
211
-
212
- **_`bot status`_**
213
-
214
- >Displays the status of the bot
215
-
216
- >If on master channel and admin user also it will display info about bots created
217
-
218
- **_`create bot on CHANNEL_NAME`_**
219
-
220
- >Creates a new bot on the channel specified.
221
-
222
- >slack-smart-bot will create a default rules file specific for your channel.
223
- You can edit it and add the rules you want.
224
- As soon as you save the file after editing it will become available on your channel.
225
-
226
- >It will work only if you are on Master channel
227
-
228
- **_`kill bot on CHANNEL_NAME`_**
229
-
230
- >Kills the bot on the specified channel
231
-
232
- >Only works if you are on Master channel and you created that bot or you are an admin user
233
-
234
- **_`notify MESSAGE`_**
235
-
236
- **_`notify all MESSAGE`_**
237
-
238
- >It will send a notificaiton message to all bot channels
239
-
240
- >It will send a notification message to all channels the bot joined and private conversations with the bot
241
-
242
- >Only works if you are on Master channel and you are an admin user
243
-
244
-
245
- ### Available commands only when listening to you or on demand or in a private conversation with the Smart Bot
246
-
247
- 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 or in a private conversation with the Smart Bot.
248
-
249
- For the bot to start listening to you you need to use the "Hi bot" command or one of the aliases
250
-
251
- Also you can call any of these commands on demand by using:
252
-
253
- **_`!THE_COMMAND`_**
254
-
255
- **_`@BOT_NAME THE_COMMAND`_**
256
-
257
- **_`BOT_NAME THE_COMMAND`_**
258
-
259
- Apart of the specific commands you define on the rules file of the channel, you can use:
260
-
261
- **_`ruby RUBY_CODE`_**
262
-
263
- **_`code RUBY_CODE`_**
264
-
265
- >runs the code supplied and returns the output. Also you can send a Ruby file. Examples:
266
-
267
- >code puts (34344/99)*(34+14)
268
-
269
- >ruby require 'json'; res=[]; 20.times {res<<rand(100)}; my_json={result: res}; puts my_json.to_json
270
-
271
- >Also it is possible to attach a Ruby file.
272
-
273
- **_`add shortcut NAME: COMMAND`_**
274
-
275
- **_`add shortcut for all NAME: COMMAND`_**
276
-
277
- **_`shortchut NAME: COMMAND`_**
164
+ On a Smart Bot channel you will be able to run some of the commands just by writing a command, for example: `bot help`
278
165
 
279
- **_`shortchut for all NAME: COMMAND`_**
166
+ Some commands will be only available when the Smart Bot is listening to you. For the Smart Bot to start listening to you just say: `hi bot`. When the Smart Bot is listening to you, you can skip a message to be treated by the bot by starting the message with '-', for example: `- this message won't be treated`. When you want the Smart Bot Stop listening to you: `bye bot`. If you are on a direct conversation with the Smart Bot then it will be on *listening* mode all the time.
280
167
 
281
- >It will add a shortcut that will execute the command we supply.
168
+ All the specific commands of the bot are specified on your rules file and can be added or changed accordingly. We usually call those commands: *rules*. Those rules are only available when the bot is listening to you.
282
169
 
283
- >In case we supply 'for all' then the shorcut will be available for everybody
170
+ Another way to run a command/rule is by asking *on demand*. In this case it is not necessary that the bot is listening to you.
284
171
 
285
- >Example:
286
- >add shortcut for all Spanish account: /code require 'iso/iban'; 10.times {puts ISO::IBAN.random('ES')}
172
+ To run a command on demand:
173
+ `!THE_COMMAND`
174
+ `@NAME_OF_BOT THE_COMMAND`
175
+ `NAME_OF_BOT THE_COMMAND`
287
176
 
288
- >Then to call this shortcut:
177
+ Also you can always call the Smart Bot from any channel, even from channels without a running Smart Bot. You can use the External Call on Demand: `@NAME_OF_BOT on #CHANNEL_NAME COMMAND`. In this case you will call the bot on #CHANNEL_NAME.
289
178
 
290
- >sc spanish account
179
+ ### Bot Help
180
+ To get a full list of all commands and rules for a specific Smart Bot: `bot help`. It will show only the specific available commands for the user requesting.
291
181
 
292
- >spanish account
182
+ If you want to search just for a specific command: `bot help COMMAND`
293
183
 
294
- >shortcut Spanish Account
184
+ To show only the specific rules of the Smart Bot defined on the rules file: `bot rules` or `bot rules COMMAND`
295
185
 
296
- **_`delete shortcut NAME`_**
186
+ ### Bot Management
187
+ To create a new bot on a channel, run on MASTER CHANNEL: `create bot on CHANNEL`. The admins of this new bot on that channel will be the MASTER ADMINS, the creator of the bot and the creator of that channel. It will create a new rules file linked to this new bot.
297
188
 
298
- >It will delete the shortcut with the supplied name
189
+ You can kill any bot running on any channel if you are an admin of that bot: `kill bot on CHANNEL`
299
190
 
300
- **_`see shortcuts`_**
191
+ If you want to pause a bot, from the channel of the bot: `pause bot`. To start it again: `start bot`
301
192
 
302
- >It will display the shortcuts stored for the user and for :all
193
+ To see the status of the bots, on the MASTER CHANNEL: `bot status`
303
194
 
304
- **_`use rules from CHANNEL`_**
195
+ To close the Master Bot, run on MASTER CHANNEL: `exit bot`
305
196
 
306
- >it will use the rules from the specified channel.
197
+ #### Cloud Bots
198
+ If you want to create a bot that will be running on a different machine: `create cloud bot on CHANNEL`. Even though the cloud bots are running on different machines, the management can be done through the MASTER CHANNEL. The new cloud bot will be managed by your Master Bot like the others, closing, pausing...
307
199
 
308
- >you need to be part of that channel to be able to use the rules.
200
+ Cloud Bots are typically used to run commands on specific environments or even different OS or networks.
309
201
 
310
- >only when on a private conversation.
202
+ ### Extending rules to other channels
203
+ If you want to extend the use of your specific rules on a Bot Channel to a third channel you can use the command: `extend rules to CHANNEL`
311
204
 
312
- **_`stop using rules from CHANNEL`_**
205
+ From that moment everybody part of that channel will be able to run the specific rules from the other channel but just on demand, for example: `!run something`
313
206
 
314
- >it will stop using the rules from the specified channel.
207
+ To stop allowing it: `stop using rules on CHANNEL`
315
208
 
316
- >only when on a private conversation.
209
+ ### Using rules from other channels
210
+ To be able to access the rules from other channel, first of all you need to be a member of that channel. Then on a private conversation with the Smart Bot or from another bot channel: `use rules from CHANNEL`
317
211
 
318
- **_`extend rules to CHANNEL_NAME`_**
212
+ When you want to stop using those rules with the bot: `stop using rules from CHANNEL`
319
213
 
320
- **_`use rules on CHANNEL_NAME`_**
214
+ Also you can always call the Smart Bot from any channel, even from channels without a running Smart Bot. You can use the External Call on Demand: `@NAME_OF_BOT on #CHANNEL_NAME COMMAND`. In this case you will call the bot on #CHANNEL_NAME.
321
215
 
322
- >it will allow to use the specific rules from this channel on the CHANNEL_NAME
216
+ ### Running Ruby code on a conversation
217
+ You can run Ruby code by using the command: `ruby THE_CODE`. For example: `!ruby require 'json'; res=[]; 20.times {res<<rand(100)}; my_json={result: res}; puts my_json.to_json`
323
218
 
324
- **_`stop using rules on CHANNEL_NAME`_**
219
+ Also it is possible to attach a Ruby file and the Smart Bot will run and post the output. You need to select Ruby as file format.
325
220
 
326
- >it will stop using the extended rules on the specified channel.
221
+ ### Sending notifications
222
+ You can send notifications from MASTER CHANNEL by using `notify MESSAGE`. All Bot Channels will be notified.
327
223
 
224
+ If you want to send a notification message to all channels the bot joined and direct conversations with the bot: `notify all MESSAGE`
328
225
 
329
- ### Available commands from channels without a Smart Bot
330
- **_`@BOT_NAME on #CHANNEL_NAME COMMAND`_**
226
+ And if you want to send a notification message to the specified channel and to its extended channels: `notify #CHANNEL MESSAGE`
331
227
 
332
- >It will run the supplied command using the rules on the channel supplied.
228
+ ### Shortcuts
229
+ Sometimes your commands or rules are too long and you want to add a shortcut to be executed.
333
230
 
334
- >You need to join the specified channel to be able to use those rules.
231
+ If you have for example a rule like this: `run tests on customers android app` and you want to add a shortcut: `add shortcut run tca: run tests on customers android app`
335
232
 
336
- >Also you can use this command to call another bot from a channel with a running bot.
233
+ From that moment you will be able to run the command: `run tca`
337
234
 
338
- ### Creating cloud bots
235
+ That shortcut will be available for you, in case you want to make it available for everybody on the channel: `add shortcut for all Spanish account: ruby require 'iso/iban'; 10.times {puts ISO::IBAN.random('ES')}`
339
236
 
340
- If you need to run a channel bot from a different machine you can use the command:
237
+ To see available shortcuts: `see shortcuts` and to delete a particular shortcut: `delete shortcut NAME`
341
238
 
342
- **_`create cloud bot on #CHANNEL_NAME`_**
239
+ ### Routines
240
+ 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`
343
241
 
344
- After that you will need to copy your current Smart Bot folder into the the new location. Install all you need: Ruby, slack-smart-bot... and run the command supplied by the Smart Bot after you run the `create cloud` command.
242
+ Examples:
243
+ `add routine run_tests every 3h run tests on customers`
244
+ `add routine clean_db at 17:05 clean customers temp db`
345
245
 
346
- The new cloud bot will be managed by your Master Bot like the others, closing, pausing...
246
+ 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.
347
247
 
348
- This is very useful when you need to run certain processes on a different environment for example on a Windows machine while your Master Bot is on Ubuntu.
248
+ Other routine commands:
249
+ * `pause routine NAME`
250
+ * `start routine NAME`
251
+ * `remove routine NAME`
252
+ * `run routine NAME`
253
+ * `see routines`
349
254
 
350
255
  ### Tips
351
256
 
352
257
  #### Send a file
353
258
 
354
259
  ```ruby
355
- send_file(to, msg, filepath, title, format, type = "text")
260
+ #send_file(to, msg, filepath, title, format, type = "text")
356
261
  send_file(dest, 'the message', "#{project_folder}/temp/logs_ptBI.log", 'title', 'text/plain', "text")
357
262
  send_file(dest, 'the message', "#{project_folder}/temp/example.jpeg", 'title', 'image/jpeg', "jpg")
358
263
  ```
@@ -6,49 +6,90 @@ require "json"
6
6
  require "logger"
7
7
  require "fileutils"
8
8
  require "open3"
9
-
10
- if ARGV.size == 0
11
- CHANNEL = MASTER_CHANNEL
12
- ON_MASTER_BOT = true
13
- ADMIN_USERS = MASTER_USERS
14
- RULES_FILE = "#{$0.gsub(".rb", "_rules.rb")}" unless defined?(RULES_FILE)
15
- unless File.exist?(RULES_FILE)
16
- default_rules = (__FILE__).gsub(/\.rb$/, "_rules.rb")
17
- FileUtils.copy_file(default_rules, RULES_FILE)
9
+ require "nice_http"
10
+ require "nice_hash"
11
+
12
+ require_relative "slack/smart-bot/comm"
13
+ require_relative "slack/smart-bot/listen"
14
+ require_relative "slack/smart-bot/treat_message"
15
+ require_relative "slack/smart-bot/process_first"
16
+ require_relative "slack/smart-bot/process"
17
+ require_relative "slack/smart-bot/utils"
18
+
19
+ TESTING_SLACK_SMART_BOT ||= false
20
+ unless TESTING_SLACK_SMART_BOT
21
+ if ARGV.size == 0
22
+ CHANNEL = MASTER_CHANNEL
23
+ ON_MASTER_BOT = true
24
+ ADMIN_USERS = MASTER_USERS
25
+ RULES_FILE = "#{$0.gsub(".rb", "_rules.rb")}" unless defined?(RULES_FILE)
26
+ unless File.exist?(RULES_FILE)
27
+ default_rules = (__FILE__).gsub(/\.rb$/, "_rules.rb")
28
+ FileUtils.copy_file(default_rules, RULES_FILE)
29
+ end
30
+ STATUS_INIT = :on
31
+ SHORTCUTS_FILE = "slack-smart-bot_shortcuts_#{CHANNEL}.rb".gsub(" ", "_")
32
+ else
33
+ ON_MASTER_BOT = false
34
+ CHANNEL = ARGV[0]
35
+ ADMIN_USERS = ARGV[1].split(",")
36
+ RULES_FILE = ARGV[2]
37
+ STATUS_INIT = ARGV[3].to_sym
38
+ SHORTCUTS_FILE = "slack-smart-bot_shortcuts_#{CHANNEL}.rb".gsub(" ", "_")
18
39
  end
19
- STATUS_INIT = :on
20
- else
21
- ON_MASTER_BOT = false
22
- CHANNEL = ARGV[0]
23
- ADMIN_USERS = ARGV[1].split(",")
24
- RULES_FILE = ARGV[2]
25
- STATUS_INIT = ARGV[3].to_sym
26
40
  end
27
41
 
28
- SHORTCUTS_FILE = "slack-smart-bot_shortcuts_#{CHANNEL}.rb".gsub(" ", "_")
29
-
30
42
  class SlackSmartBot
31
43
  attr_accessor :config, :client
32
44
  attr_reader :master_bot_id, :channel_id
33
- VERSION = Gem.loaded_specs.values.select { |x| x.name == "slack-smart-bot" }[0].version.to_s
45
+ geml = Gem.loaded_specs.values.select { |x| x.name == "slack-smart-bot" }[0]
46
+ if geml.nil?
47
+ version = ""
48
+ else
49
+ version = geml.version.to_s
50
+ end
51
+ VERSION = version
34
52
 
35
53
  def initialize(config)
36
54
  Dir.mkdir("./logs") unless Dir.exist?("./logs")
37
55
  Dir.mkdir("./shortcuts") unless Dir.exist?("./shortcuts")
56
+ Dir.mkdir("./routines") unless Dir.exist?("./routines")
38
57
  logfile = File.basename(RULES_FILE.gsub("_rules_", "_logs_"), ".rb") + ".log"
39
58
  @logger = Logger.new("./logs/#{logfile}")
40
59
  config_log = config.dup
41
60
  config_log.delete(:token)
42
61
  config[:silent] = false unless config.key?(:silent)
62
+ config[:testing] = false unless config.key?(:testing)
43
63
  @logger.info "Initializing bot: #{config_log.inspect}"
44
64
 
65
+ File.new("./buffer.log", "w") if config[:testing]
66
+
45
67
  config[:channel] = CHANNEL
46
68
  self.config = config
47
69
 
48
70
  Slack.configure do |conf|
49
71
  conf.token = config[:token]
50
72
  end
51
- self.client = Slack::RealTime::Client.new(start_method: :rtm_connect)
73
+ restarts = 0
74
+ created = false
75
+ while restarts < 200 and !created
76
+ begin
77
+ @logger.info "Connecting #{config_log.inspect}"
78
+ self.client = Slack::RealTime::Client.new(start_method: :rtm_connect)
79
+ created = true
80
+ rescue Exception => e
81
+ restarts += 1
82
+ if restarts < 200
83
+ @logger.fatal "*" * 50
84
+ @logger.fatal "Rescued on creation: #{e.inspect}"
85
+ @logger.info "Waiting 60 seconds to retry. restarts: #{restarts}"
86
+ puts "#{Time.now}: Not able to create client. Waiting 60 seconds to retry: #{config_log.inspect}"
87
+ sleep 60
88
+ else
89
+ exit!
90
+ end
91
+ end
92
+ end
52
93
 
53
94
  @listening = Array.new
54
95
 
@@ -56,6 +97,7 @@ class SlackSmartBot
56
97
  @shortcuts = Hash.new()
57
98
  @shortcuts[:all] = Hash.new()
58
99
  @rules_imported = Hash.new()
100
+ @routines = Hash.new()
59
101
 
60
102
  if File.exist?("./shortcuts/#{SHORTCUTS_FILE}")
61
103
  file_sc = IO.readlines("./shortcuts/#{SHORTCUTS_FILE}").join
@@ -64,6 +106,8 @@ class SlackSmartBot
64
106
  end
65
107
  end
66
108
 
109
+ get_routines()
110
+
67
111
  if ON_MASTER_BOT and File.exist?($0.gsub(".rb", "_bots.rb"))
68
112
  get_bots_created()
69
113
  if @bots_created.kind_of?(Hash)
@@ -90,11 +134,27 @@ class SlackSmartBot
90
134
  begin
91
135
  user_info = client.web_client.users_info(user: "#{"@" if config[:nick][0] != "@"}#{config[:nick]}")
92
136
  config[:nick_id] = user_info.user.id
137
+ rescue Slack::Web::Api::Errors::TooManyRequestsError
138
+ @logger.fatal "TooManyRequestsError"
139
+ abort("TooManyRequestsError please re run the bot and be sure of executing first: killall ruby")
93
140
  rescue Exception => stack
94
141
  @logger.fatal stack
95
142
  abort("The bot user specified on settings: #{config[:nick]}, doesn't exist on Slack. Execution aborted")
96
143
  end
97
144
 
145
+ begin
146
+ @admin_users_id = []
147
+ ADMIN_USERS.each do |au|
148
+ user_info = client.web_client.users_info(user: "@#{au}")
149
+ @admin_users_id << user_info.user.id
150
+ end
151
+ rescue Slack::Web::Api::Errors::TooManyRequestsError
152
+ @logger.fatal "TooManyRequestsError"
153
+ abort("TooManyRequestsError please re run the bot and be sure of executing first: killall ruby")
154
+ rescue Exception => stack
155
+ abort("The admin user specified on settings: #{ADMIN_USERS.join(", ")}, doesn't exist on Slack. Execution aborted")
156
+ end
157
+
98
158
  client.on :hello do
99
159
  m = "Successfully connected, welcome '#{client.self.name}' to the '#{client.team.name}' team at https://#{client.team.domain}.slack.com."
100
160
  puts m
@@ -108,6 +168,13 @@ class SlackSmartBot
108
168
  unless config[:silent]
109
169
  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."
110
170
  end
171
+ @routines.each do |ch, rout|
172
+ rout.each do |k, v|
173
+ if !v[:running] and v[:channel_name] == CHANNEL
174
+ create_routine_thread(k)
175
+ end
176
+ end
177
+ end
111
178
  end
112
179
 
113
180
  @status = STATUS_INIT
@@ -118,6 +185,14 @@ class SlackSmartBot
118
185
  @channel_id = @channels_id[CHANNEL].dup
119
186
  @master_bot_id = @channels_id[MASTER_CHANNEL].dup
120
187
 
188
+ get_routines()
189
+ if @routines.key?(@channel_id)
190
+ @routines[@channel_id].each do |k, v|
191
+ @routines[@channel_id][k][:running] = false
192
+ end
193
+ end
194
+ update_routines()
195
+
121
196
  client.on :close do |_data|
122
197
  m = "Connection closing, exiting. #{Time.now}"
123
198
  @logger.info m
@@ -133,1261 +208,5 @@ class SlackSmartBot
133
208
  self
134
209
  end
135
210
 
136
- def update_bots_file
137
- file = File.open($0.gsub(".rb", "_bots.rb"), "w")
138
- bots_created = @bots_created.dup
139
- bots_created.each { |k, v| v[:thread] = "" }
140
- file.write bots_created.inspect
141
- file.close
142
- end
143
-
144
- def get_bots_created
145
- if File.exist?($0.gsub(".rb", "_bots.rb"))
146
- if !defined?(@datetime_bots_created) or @datetime_bots_created!=File.mtime($0.gsub(".rb", "_bots.rb"))
147
- file_conf = IO.readlines($0.gsub(".rb", "_bots.rb")).join
148
- if file_conf.to_s() == ""
149
- @bots_created = {}
150
- else
151
- @bots_created = eval(file_conf)
152
- end
153
- @datetime_bots_created = File.mtime($0.gsub(".rb", "_bots.rb"))
154
- @bots_created.each do |k,v| # to be compatible with old versions
155
- v[:extended] = [] unless v.key?(:extended)
156
- end
157
- end
158
- end
159
- end
160
-
161
- def update_shortcuts_file
162
- file = File.open("./shortcuts/#{SHORTCUTS_FILE}", "w")
163
- file.write @shortcuts.inspect
164
- file.close
165
- end
166
-
167
- def update_rules_imported
168
- file = File.open("./rules/rules_imported.rb", "w")
169
- file.write @rules_imported.inspect
170
- file.close
171
- end
172
-
173
- def get_channels_name_and_id
174
- #todo: add pagination for case more than 1000 channels on the workspace
175
- channels = client.web_client.conversations_list(
176
- types: 'private_channel,public_channel',
177
- limit: '1000',
178
- exclude_archived: 'true').channels
179
-
180
- @channels_id = Hash.new()
181
- @channels_name = Hash.new()
182
- channels.each do |ch|
183
- unless ch.is_archived
184
- @channels_id[ch.name] = ch.id
185
- @channels_name[ch.id] = ch.name
186
- end
187
- end
188
- end
189
-
190
- #help: ===================================
191
- #help:
192
- #help: *Commands from Channels without a bot:*
193
- #help:
194
- #help: ----------------------------------------------
195
- #help:
196
- #help: `@BOT_NAME on #CHANNEL_NAME COMMAND`
197
- #help: `@BOT_NAME #CHANNEL_NAME COMMAND`
198
- #help: It will run the supplied command using the rules on the channel supplied.
199
- #help: You need to join the specified channel to be able to use those rules.
200
- #help: Also you can use this command to call another bot from a channel with a running bot.
201
- def listen
202
- @salutations = [config[:nick], config[:nick_id], "bot", "smart"]
203
- get_bots_created()
204
- client.on :message do |data|
205
- if data.channel[0] == "D" or data.channel[0] == "C" or data.channel[0] == "G" #Direct message or Channel or Private Channel
206
- dest = data.channel
207
- else # not treated
208
- dest = nil
209
- end
210
- #todo: sometimes data.user is nil, check the problem.
211
- @logger.warn "!dest is nil. user: #{data.user}, channel: #{data.channel}, message: #{data.text}" if dest.nil?
212
- if !data.files.nil? and data.files.size == 1 and data.text.to_s == '' and data.files[0].filetype == "ruby"
213
- data.text = 'ruby'
214
- end
215
- typem = :dont_treat
216
- if !dest.nil? and !data.text.nil? and !data.text.to_s.match?(/^\s*$/)
217
- if data.text.match(/^<@#{config[:nick_id]}>\s(on\s)?<#(\w+)\|(.+)>\s*:?\s*(.*)/im)
218
- channel_rules = $2
219
- channel_rules_name = $3
220
- # to be treated only on the bot of the requested channel
221
- if @channel_id == channel_rules
222
- data.text = $4
223
- typem = :on_call
224
- end
225
-
226
- elsif dest == @master_bot_id
227
- if ON_MASTER_BOT #only to be treated on master mot channel
228
- typem = :on_master
229
- end
230
- elsif @bots_created.key?(dest)
231
- if @channel_id == dest #only to be treated by the bot on the channel
232
- typem = :on_bot
233
- end
234
- elsif dest[0]=="D" #Direct message
235
- if ON_MASTER_BOT #only to be treated by master bot
236
- typem = :on_dm
237
- end
238
- elsif dest[0]=="G" #private group
239
- if ON_MASTER_BOT #only to be treated by master bot
240
- typem = :on_pg
241
- end
242
- elsif dest[0]=='C'
243
- #only to be treated on the channel of the bot. excluding running ruby
244
- if !ON_MASTER_BOT and @bots_created[@channel_id][:extended].include?(@channels_name[dest]) and
245
- !data.text.match?(/^!?\s*(ruby|code)\s+/)
246
- typem = :on_extended
247
- elsif ON_MASTER_BOT and data.text.match?(/^!?\s*(ruby|code)\s+/) #or in case of running ruby, the master bot
248
- @bots_created.each do |k,v|
249
- if v.key?(:extended) and v[:extended].include?(@channels_name[dest])
250
- typem = :on_extended
251
- break
252
- end
253
- end
254
- end
255
- end
256
- end
257
- unless typem == :dont_treat
258
- begin
259
- command = data.text
260
-
261
- #todo: when changed @questions user_id then move user_info inside the ifs to avoid calling it when not necessary
262
- user_info = client.web_client.users_info(user: data.user)
263
-
264
- #when added special characters on the message
265
- if command.size >= 2 and
266
- ((command[0] == "`" and command[-1] == "`") or (command[0] == "*" and command[-1] == "*") or (command[0] == "_" and command[-1] == "_"))
267
- command = command[1..-2]
268
- end
269
-
270
- #ruby file attached
271
- if !data.files.nil? and data.files.size == 1 and
272
- (command.match?(/^(ruby|code)\s*$/) or (command.match?(/^\s*$/) and data.files[0].filetype == "ruby") or
273
- (typem==:on_call and data.files[0].filetype == "ruby"))
274
- res = Faraday.new("https://files.slack.com", headers: { "Authorization" => "Bearer #{config[:token]}" }).get(data.files[0].url_private)
275
- command += " ruby" if command!='ruby'
276
- command = "#{command} #{res.body.to_s.force_encoding("UTF-8")}"
277
- end
278
-
279
- if typem == :on_call
280
- command = "!" + command unless command[0] == "!" or command.match?(/^\s*$/)
281
-
282
- #todo: add pagination for case more than 1000 channels on the workspace
283
- channels = client.web_client.conversations_list(
284
- types: 'private_channel,public_channel',
285
- limit: '1000',
286
- exclude_archived: 'true').channels
287
- channel_found = channels.detect { |c| c.name == channel_rules_name }
288
- members = client.web_client.conversations_members(channel: @channels_id[channel_rules_name]).members unless channel_found.nil?
289
- if channel_found.nil?
290
- @logger.fatal "Not possible to find the channel #{channel_rules_name}"
291
- elsif channel_found.name == MASTER_CHANNEL
292
- respond "You cannot use the rules from Master Channel on any other channel.", dest
293
- elsif @status != :on
294
- respond "The bot in that channel is not :on", dest
295
- elsif data.user == channel_found.creator or members.include?(data.user)
296
- res = process_first(user_info.user, command, dest, channel_rules, typem, data.files)
297
- else
298
- respond "You need to join the channel <##{channel_found.id}> to be able to use the rules.", dest
299
- end
300
-
301
- elsif @questions.keys.include?(user_info.user.name)
302
- #todo: @questions key should be the id not the name. change it everywhere
303
- dest = data.channel
304
- res = process_first(user_info.user, command, dest, @channel_id, typem, data.files)
305
-
306
- elsif ON_MASTER_BOT and typem ==:on_extended and
307
- command.size > 0 and command[0] != "-"
308
- # to run ruby only from the master bot for the case more than one extended
309
- res = process_first(user_info.user, command, dest, @channel_id, typem, data.files)
310
-
311
- elsif !ON_MASTER_BOT and @bots_created[@channel_id].key?(:extended) and
312
- @bots_created[@channel_id][:extended].include?(@channels_name[data.channel]) and
313
- command.size > 0 and command[0] != "-"
314
- res = process_first(user_info.user, command, dest, @channel_id, typem, data.files)
315
- elsif (dest[0] == "D" or @channel_id == data.channel or data.user == config[:nick_id]) and
316
- command.size > 0 and command[0] != "-"
317
- res = process_first(user_info.user, command, dest, data.channel, typem, data.files)
318
- # if @botname on #channel_rules: do something
319
- end
320
- rescue Exception => stack
321
- @logger.fatal stack
322
- end
323
- end
324
- end
325
-
326
- @logger.info "Bot listening"
327
- client.start!
328
- end
329
-
330
- def process_first(user, text, dest, dchannel, typem, files)
331
- nick = user.name
332
- rules_file = ""
333
-
334
- if typem == :on_call
335
- rules_file = RULES_FILE
336
-
337
- elsif dest[0] == "C" or dest[0] == "G" # on a channel or private channel
338
- rules_file = RULES_FILE
339
-
340
- if @rules_imported.key?(user.id) and @rules_imported[user.id].key?(dchannel)
341
- unless @bots_created.key?(@rules_imported[user.id][dchannel])
342
- get_bots_created()
343
- end
344
- if @bots_created.key?(@rules_imported[user.id][dchannel])
345
- rules_file = @bots_created[@rules_imported[user.id][dchannel]][:rules_file]
346
- end
347
- end
348
- elsif dest[0] == "D" and @rules_imported.key?(user.id) and @rules_imported[user.id].key?(user.id) #direct message
349
- unless @bots_created.key?(@rules_imported[user.id][user.id])
350
- get_bots_created()
351
- end
352
- if @bots_created.key?(@rules_imported[user.id][user.id])
353
- rules_file = @bots_created[@rules_imported[user.id][user.id]][:rules_file]
354
- end
355
- end
356
-
357
- if nick == config[:nick] #if message is coming from the bot
358
- begin
359
- case text
360
- when /^Bot has been (closed|killed) by/i
361
- if CHANNEL == @channels_name[dchannel]
362
- @logger.info "#{nick}: #{text}"
363
- exit!
364
- end
365
- when /^Changed status on (.+) to :(.+)/i
366
- channel_name = $1
367
- status = $2
368
- if ON_MASTER_BOT or CHANNEL == channel_name
369
- @bots_created[@channels_id[channel_name]][:status] = status.to_sym
370
- update_bots_file()
371
- if CHANNEL == channel_name
372
- @logger.info "#{nick}: #{text}"
373
- else #on master bot
374
- @logger.info "Changed status on #{channel_name} to :#{status}"
375
- end
376
- end
377
- when /extended the rules from (.+) to be used on (.+)\.$/i
378
- from_name = $1
379
- to_name = $2
380
- if ON_MASTER_BOT and @bots_created[@channels_id[from_name]][:cloud]
381
- @bots_created[@channels_id[from_name]][:extended] << to_name
382
- @bots_created[@channels_id[from_name]][:extended].uniq!
383
- update_bots_file()
384
- end
385
-
386
- when /removed the access to the rules of (.+) from (.+)\.$/i
387
- from_name = $1
388
- to_name = $2
389
- if ON_MASTER_BOT and @bots_created[@channels_id[from_name]][:cloud]
390
- @bots_created[@channels_id[from_name]][:extended].delete(to_name)
391
- update_bots_file()
392
- end
393
-
394
- end
395
-
396
- return :next #don't continue analyzing
397
- rescue Exception => stack
398
- @logger.fatal stack
399
- return :next
400
- end
401
- end
402
-
403
- #only for shortcuts
404
- if text.match(/^@?(#{config[:nick]}):*\s+(.+)\s*/im) or
405
- text.match(/^()!\s*(.+)\s*/im) or
406
- text.match(/^()<@#{config[:nick_id]}>\s+(.+)\s*/im)
407
- command = $2
408
- addexcl = true
409
- else
410
- addexcl = false
411
- command = text.downcase.lstrip.rstrip
412
- end
413
- if command.scan(/^(shortcut|sc)\s+(.+)/i).any? or
414
- (@shortcuts.keys.include?(:all) and @shortcuts[:all].keys.include?(command)) or
415
- (@shortcuts.keys.include?(nick) and @shortcuts[nick].keys.include?(command))
416
- command = $2.downcase unless $2.nil?
417
- if @shortcuts.keys.include?(nick) and @shortcuts[nick].keys.include?(command)
418
- text = @shortcuts[nick][command].dup
419
- elsif @shortcuts.keys.include?(:all) and @shortcuts[:all].keys.include?(command)
420
- text = @shortcuts[:all][command].dup
421
- else
422
- respond "Shortcut not found", dest unless dest[0]=="C" and dchannel != dest #on extended channel
423
- return :next
424
- end
425
- text = "!" + text if addexcl and text[0] != "!"
426
- end
427
-
428
- if @questions.keys.include?(nick)
429
- command = @questions[nick]
430
- @questions[nick] = text
431
- else
432
- command = text
433
- end
434
- begin
435
- t = Thread.new do
436
- begin
437
- processed = process(user, command, dest, dchannel, rules_file, typem)
438
- @logger.info "command: #{nick}> #{command}" if processed
439
- on_demand = false
440
- if command.match(/^@?(#{config[:nick]}):*\s+(.+)/im) or
441
- command.match(/^()!(.+)/im) or
442
- command.match(/^()<@#{config[:nick_id]}>\s+(.+)/im)
443
- command = $2
444
- on_demand = true
445
- end
446
- if @status == :on and
447
- (@questions.keys.include?(nick) or
448
- (@listening.include?(nick) and typem!=:on_extended) or
449
- dest[0] == "D" or on_demand)
450
- @logger.info "command: #{nick}> #{command}" unless processed
451
- #todo: verify this
452
-
453
- if dest[0] == "C" or dest[0] == "G" or (dest[0]=="D" and typem==:on_call)
454
- if typem!=:on_call and @rules_imported.key?(user.id) and @rules_imported[user.id].key?(dchannel)
455
- if @bots_created.key?(@rules_imported[user.id][dchannel])
456
- if @bots_created[@rules_imported[user.id][dchannel]][:status] != :on
457
- respond "The bot on that channel is not :on", dest
458
- rules_file = ""
459
- end
460
- end
461
- end
462
- unless rules_file.empty?
463
- begin
464
- eval(File.new(rules_file).read) if File.exist?(rules_file)
465
- rescue Exception => stack
466
- @logger.fatal "ERROR ON RULES FILE: #{rules_file}"
467
- @logger.fatal stack
468
- end
469
- if defined?(rules)
470
- command[0] = "" if command[0] == "!"
471
- command.gsub!(/^@\w+:*\s*/, "")
472
- if method(:rules).arity == 4
473
- rules(user, command, processed, dest)
474
- else
475
- rules(user, command, processed, dest, files)
476
- end
477
- else
478
- @logger.warn "It seems like rules method is not defined"
479
- end
480
- end
481
- elsif @rules_imported.key?(user.id) and @rules_imported[user.id].key?(user.id)
482
- if @bots_created.key?(@rules_imported[user.id][user.id])
483
- if @bots_created[@rules_imported[user.id][user.id]][:status] == :on
484
- begin
485
- eval(File.new(rules_file).read) if File.exist?(rules_file)
486
- rescue Exception => stack
487
- @logger.fatal "ERROR ON imported RULES FILE: #{rules_file}"
488
- @logger.fatal stack
489
- end
490
- else
491
- respond "The bot on <##{@rules_imported[user.id][user.id]}|#{@bots_created[@rules_imported[user.id][user.id]][:channel_name]}> is not :on", dest
492
- rules_file = ""
493
- end
494
- end
495
-
496
- unless rules_file.empty?
497
- if defined?(rules)
498
- command[0] = "" if command[0] == "!"
499
- command.gsub!(/^@\w+:*\s*/, "")
500
- if method(:rules).arity == 4
501
- rules(user, command, processed, dest)
502
- else
503
- rules(user, command, processed, dest, files)
504
- end
505
- else
506
- @logger.warn "It seems like rules method is not defined"
507
- end
508
- end
509
- else
510
- @logger.info "it is a direct message with no rules file selected so no rules file executed."
511
- unless processed
512
- resp = ["what", "huh", "sorry", "what do you mean", "I don't understand"].sample
513
- respond "#{resp}?", dest
514
- end
515
- end
516
- end
517
- rescue Exception => stack
518
- @logger.fatal stack
519
- end
520
- end
521
- rescue => e
522
- @logger.error "exception: #{e.inspect}"
523
- end
524
- end
525
-
526
- #help: ===================================
527
- #help:
528
- #help: *General commands:*
529
- #help:
530
- def process(user, command, dest, dchannel, rules_file, typem)
531
- from = user.name
532
- display_name = user.profile.display_name
533
- processed = true
534
-
535
- if typem == :on_master or typem == :on_bot or typem ==:on_pg or typem == :on_dm
536
- case command
537
-
538
- #help: ----------------------------------------------
539
- #help: `Hello Bot`
540
- #help: `Hello Smart`
541
- #help: `Hello THE_NAME_OF_THE_BOT`
542
- #help: Also apart of Hello you can use _Hallo, Hi, Hola, What's up, Hey, Hæ_
543
- #help: Bot starts listening to you
544
- #help: If you want to avoid a single message to be treated by the smart bot, start the message by -
545
- #help:
546
- when /^(Hello|Hallo|Hi|Hola|What's\sup|Hey|Hæ)\s(#{@salutations.join("|")})\s*$/i
547
- if @status == :on
548
- greetings = ["Hello", "Hallo", "Hi", "Hola", "What's up", "Hey", "Hæ"].sample
549
- respond "#{greetings} #{display_name}", dest
550
- if @rules_imported.key?(user.id) and @rules_imported[user.id].key?(user.id) and dest[0] == "D"
551
- respond "You are using specific rules for channel: <##{@rules_imported[user.id][user.id]}>", dest
552
- elsif @rules_imported.key?(user.id) and @rules_imported[user.id].key?(dchannel) and (dest[0] == "C" or dest[0] == "G")
553
- respond "You are using specific rules for channel: <##{@rules_imported[user.id][dchannel]}>", dest
554
- end
555
- @listening << from unless @listening.include?(from)
556
- end
557
-
558
- #help: ----------------------------------------------
559
- #help: `Bye Bot`
560
- #help: `Bye Smart`
561
- #help: `Bye NAME_OF_THE_BOT`
562
- #help: Also apart of Bye you can use _Bæ, Good Bye, Adiós, Ciao, Bless, Bless Bless, Adeu_
563
- #help: Bot stops listening to you
564
- #help:
565
- when /^(Bye|Bæ|Good\sBye|Adiós|Ciao|Bless|Bless\sBless|Adeu)\s(#{@salutations.join("|")})\s*$/i
566
- if @status == :on
567
- bye = ["Bye", "Bæ", "Good Bye", "Adiós", "Ciao", "Bless", "Bless bless", "Adeu"].sample
568
- respond "#{bye} #{display_name}", dest
569
- @listening.delete(from)
570
- end
571
-
572
- #help: ----------------------------------------------
573
- #help: `bot help`
574
- #help: `bot what can I do?`
575
- #help: `bot rules`
576
- #help: it will display this help
577
- #help: `bot rules` will show only the specific rules for this channel.
578
- when /^bot\s+(rules|help)/i, /^bot,? what can I do/i
579
- if $1.to_s.match?(/rules/i)
580
- specific = true
581
- else
582
- specific = false
583
- end
584
- help_message_rules = ''
585
- if !specific
586
- help_message = IO.readlines(__FILE__).join
587
- if ADMIN_USERS.include?(from) #admin user
588
- respond "*Commands for administrators:*\n#{help_message.scan(/#\s*help\s*admin:(.*)/).join("\n")}", dest
589
- end
590
- if ON_MASTER_BOT and (dest[0] == "C" or dest[0] == "G")
591
- respond "*Commands only on Master Channel <##{@master_bot_id}>:*\n#{help_message.scan(/#\s*help\s*master:(.*)/).join("\n")}", dest
592
- end
593
- respond help_message.scan(/#\s*help\s*:(.*)/).join("\n"), dest
594
- end
595
- if dest[0] == "C" or dest[0] == "G" # on a channel
596
- if @rules_imported.key?(user.id) and @rules_imported[user.id].key?(dchannel)
597
- if @bots_created.key?(@rules_imported[user.id][dchannel])
598
- respond "*You are using rules from another channel: <##{@rules_imported[user.id][dchannel]}>. These are the specific commands for that channel:*", dest
599
- end
600
- end
601
- help_message_rules = IO.readlines(rules_file).join
602
- respond help_message_rules.scan(/#\s*help\s*:(.*)/).join("\n"), dest
603
- elsif dest[0] == "D" and @rules_imported.key?(user.id) and @rules_imported[user.id].key?(user.id) #direct message
604
- if @bots_created.key?(@rules_imported[user.id][user.id])
605
- respond "*You are using rules from channel: <##{@rules_imported[user.id][user.id]}>. These are the specific commands for that channel:*", dest
606
- help_message_rules = IO.readlines(rules_file).join
607
- respond help_message_rules.scan(/#\s*help\s*:(.*)/).join("\n"), dest
608
- end
609
- end
610
- if specific
611
- unless rules_file.empty?
612
- begin
613
- eval(File.new(rules_file).read) if File.exist?(rules_file)
614
- end
615
- end
616
- if defined?(git_project) and git_project.to_s!='' and help_message_rules != ''
617
- respond "Git project: #{git_project}", dest
618
- else
619
- def git_project() '' end
620
- def project_folder() '' end
621
- end
622
-
623
- else
624
- respond "Slack Smart Bot Github project: https://github.com/MarioRuiz/slack-smart-bot", dest
625
- end
626
-
627
- #help: ===================================
628
- #help:
629
- #help: *These commands will run only when on a private conversation with the bot:*
630
- #help:
631
- #help: ----------------------------------------------
632
- #help: `use rules from CHANNEL`
633
- #help: it will use the rules from the specified channel.
634
- #help: you need to be part of that channel to be able to use the rules.
635
- #help:
636
- when /^use rules (from\s+)<#C\w+\|(.+)>/i, /^use rules (from\s+)(.+)/i
637
- channel = $2
638
- #todo: add pagination for case more than 1000 channels on the workspace
639
- channels = client.web_client.conversations_list(
640
- types: 'private_channel,public_channel',
641
- limit: '1000',
642
- exclude_archived: 'true').channels
643
-
644
- channel_found = channels.detect { |c| c.name == channel }
645
- members = client.web_client.conversations_members(channel: @channels_id[channel]).members unless channel_found.nil?
646
-
647
- if channel_found.nil?
648
- respond "The channel you are trying to use doesn't exist", dest
649
- elsif channel_found.name == MASTER_CHANNEL
650
- respond "You cannot use the rules from Master Channel on any other channel.", dest
651
- elsif !@bots_created.key?(@channels_id[channel])
652
- respond "There is no bot running on that channel.", dest
653
- elsif @bots_created.key?(@channels_id[channel]) and @bots_created[@channels_id[channel]][:status] != :on
654
- respond "The bot in that channel is not :on", dest
655
- else
656
- if user.id == channel_found.creator or members.include?(user.id)
657
- @rules_imported[user.id] = {} unless @rules_imported.key?(user.id)
658
- if dest[0] == "C" or dest[0] == "G" #todo: take in consideration bots that are not master
659
- @rules_imported[user.id][dchannel] = channel_found.id
660
- else
661
- @rules_imported[user.id][user.id] = channel_found.id
662
- end
663
- update_rules_imported() if ON_MASTER_BOT
664
- respond "I'm using now the rules from <##{channel_found.id}>", dest
665
- def git_project() "" end
666
- def project_folder() "" end
667
- else
668
- respond "You need to join the channel <##{channel_found.id}> to be able to use the rules.", dest
669
- end
670
- end
671
-
672
- #help: ----------------------------------------------
673
- #help: `stop using rules from CHANNEL`
674
- #help: it will stop using the rules from the specified channel.
675
- #help:
676
- when /^stop using rules (from\s+)<#\w+\|(.+)>/i, /^stop using rules (from\s+)(.+)/i
677
- channel = $2
678
- if @channels_id.key?(channel)
679
- channel_id = @channels_id[channel]
680
- else
681
- channel_id = channel
682
- end
683
- if dest[0] == "C" or dest[0] == "G" #channel
684
- if @rules_imported.key?(user.id) and @rules_imported[user.id].key?(dchannel)
685
- if @rules_imported[user.id][dchannel] != channel_id
686
- respond "You are not using those rules.", dest
687
- else
688
- @rules_imported[user.id].delete(dchannel)
689
- update_rules_imported() if ON_MASTER_BOT
690
- respond "You won't be using those rules from now on.", dest
691
- def git_project() "" end
692
- def project_folder() "" end
693
- end
694
- else
695
- respond "You were not using those rules.", dest
696
- end
697
- else #direct message
698
- if @rules_imported.key?(user.id) and @rules_imported[user.id].key?(user.id)
699
- if @rules_imported[user.id][user.id] != channel_id
700
- respond "You are not using those rules.", dest
701
- else
702
- @rules_imported[user.id].delete(user.id)
703
- update_rules_imported() if ON_MASTER_BOT
704
- respond "You won't be using those rules from now on.", dest
705
- def git_project() "" end
706
- def project_folder() "" end
707
- end
708
- else
709
- respond "You were not using those rules.", dest
710
- end
711
- end
712
-
713
- #helpadmin: ----------------------------------------------
714
- #helpadmin: `exit bot`
715
- #helpadmin: `quit bot`
716
- #helpadmin: `close bot`
717
- #helpadmin: The bot stops running and also stops all the bots created from this master channel
718
- #helpadmin: You can use this command only if you are an admin user and you are on the master channel
719
- #helpadmin:
720
- when /^exit\sbot\s*$/i, /^quit\sbot\s*$/i, /^close\sbot\s*$/i
721
- if ON_MASTER_BOT
722
- if ADMIN_USERS.include?(from) #admin user
723
- unless @questions.keys.include?(from)
724
- ask("are you sure?", command, from, dest)
725
- else
726
- case @questions[from]
727
- when /yes/i, /yep/i, /sure/i
728
- respond "Game over!", dest
729
- respond "Ciao #{display_name}!", dest
730
- @bots_created.each { |key, value|
731
- value[:thread] = ""
732
- send_msg_channel(key, "Bot has been closed by #{from}")
733
- sleep 0.5
734
- }
735
- update_bots_file()
736
- sleep 0.5
737
- exit!
738
- when /no/i, /nope/i, /cancel/i
739
- @questions.delete(from)
740
- respond "Thanks, I'm happy to be alive", dest
741
- else
742
- respond "I don't understand", dest
743
- ask("are you sure do you want me to close? (yes or no)", "quit bot", from, dest)
744
- end
745
- end
746
- else
747
- respond "Only admin users can kill me", dest
748
- end
749
- else
750
- respond "To do this you need to be an admin user in the master channel: <##{@master_bot_id}>", dest
751
- end
752
-
753
- #helpadmin: ----------------------------------------------
754
- #helpadmin: `start bot`
755
- #helpadmin: `start this bot`
756
- #helpadmin: the bot will start to listen
757
- #helpadmin: You can use this command only if you are an admin user
758
- #helpadmin:
759
- when /^start\s(this\s)?bot$/i
760
- if ADMIN_USERS.include?(from) #admin user
761
- respond "This bot is running and listening from now on. You can pause again: pause this bot", dest
762
- @status = :on
763
- unless ON_MASTER_BOT
764
- send_msg_channel MASTER_CHANNEL, "Changed status on #{CHANNEL} to :on"
765
- end
766
- else
767
- respond "Only admin users can change my status", dest
768
- end
769
-
770
- #helpadmin: ----------------------------------------------
771
- #helpadmin: `pause bot`
772
- #helpadmin: `pause this bot`
773
- #helpadmin: the bot will pause so it will listen only to admin commands
774
- #helpadmin: You can use this command only if you are an admin user
775
- #helpadmin:
776
- when /^pause\s(this\s)?bot$/i
777
- if ADMIN_USERS.include?(from) #admin user
778
- respond "This bot is paused from now on. You can start it again: start this bot", dest
779
- respond "zZzzzzZzzzzZZZZZZzzzzzzzz", dest
780
- @status = :paused
781
- unless ON_MASTER_BOT
782
- send_msg_channel MASTER_CHANNEL, "Changed status on #{CHANNEL} to :paused"
783
- end
784
- else
785
- respond "Only admin users can put me on pause", dest
786
- end
787
-
788
- #helpadmin: ----------------------------------------------
789
- #helpadmin: `bot status`
790
- #helpadmin: Displays the status of the bot
791
- #helpadmin: If on master channel and admin user also it will display info about bots created
792
- #helpadmin:
793
- when /^bot\sstatus/i
794
- get_bots_created()
795
- gems_remote = `gem list slack-smart-bot --remote`
796
- version_remote = gems_remote.to_s().scan(/slack-smart-bot \((\d+\.\d+\.\d+)/).join
797
- version_message = ""
798
- if version_remote != VERSION
799
- version_message = " There is a new available version: #{version_remote}."
800
- end
801
- require 'socket'
802
- ip_address = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? }.ip_address
803
- respond "*#{Socket.gethostname} (#{ip_address})*\n\tStatus: #{@status}.\n\tVersion: #{VERSION}.#{version_message}\n\tRules file: #{File.basename RULES_FILE} ", dest
804
- if @status == :on
805
- respond "I'm listening to [#{@listening.join(", ")}]", dest
806
- if ON_MASTER_BOT and ADMIN_USERS.include?(from)
807
-
808
- @bots_created.each do |k,v|
809
- msg = []
810
- msg << "`#{v[:channel_name]}` (#{k}):"
811
- msg << "\tcreator: #{v[:creator_name]}"
812
- msg << "\tadmins: #{v[:admins]}"
813
- msg << "\tstatus: #{v[:status]}"
814
- msg << "\tcreated: #{v[:created]}"
815
- msg << "\trules: #{v[:rules_file]}"
816
- msg << "\textended: #{v[:extended]}"
817
- msg << "\tcloud: #{v[:cloud]}"
818
- if ON_MASTER_BOT and v.key?(:cloud) and v[:cloud]
819
- msg << "\trunner: `ruby #{$0} \"#{v[:channel_name]}\" \"#{v[:admins]}\" \"#{v[:rules_file]}\" on&`"
820
- end
821
- respond msg.join("\n"), dest
822
- end
823
- end
824
- end
825
-
826
- #helpmaster: ----------------------------------------------
827
- #helpmaster: `notify MESSAGE`
828
- #helpmaster: `notify all MESSAGE`
829
- #helpmaster: It will send a notificaiton message to all bot channels
830
- #helpmaster: It will send a notification message to all channels the bot joined and private conversations with the bot
831
- #helpmaster: Only works if you are on Master channel and you are an admin user
832
- #helpmaster:
833
- when /^notify\s+(all)?\s*(.+)\s*$/i
834
- if ON_MASTER_BOT
835
- if ADMIN_USERS.include?(from) #admin user
836
- all = $1
837
- message = $2
838
- if all.nil?
839
- @bots_created.each do |k, v|
840
- respond message, k
841
- end
842
- respond "Bot channels have been notified", dest
843
- else
844
- myconv = client.web_client.users_conversations(exclude_archived: true, limit: 100, types: "im, public_channel").channels
845
- myconv.each do |c|
846
- respond message, c.id unless c.name == MASTER_CHANNEL
847
- end
848
- respond "Channels and users have been notified", dest
849
- end
850
- end
851
- end
852
-
853
- #helpmaster: ----------------------------------------------
854
- #helpmaster: `create bot on CHANNEL_NAME`
855
- #helpmaster: `create cloud bot on CHANNEL_NAME`
856
- #helpmaster: creates a new bot on the channel specified
857
- #helpmaster: it will work only if you are on Master channel
858
- #helpmaster: the admins will be the master admins, the creator of the bot and the creator of the channel
859
- #helpmaster: follow the instructions in case creating cloud bots
860
- when /^create\s+(cloud\s+)?bot\s+on\s+<#C\w+\|(.+)>\s*/i, /^create\s+(cloud\s+)?bot\s+on\s+(.+)\s*/i
861
- if ON_MASTER_BOT
862
- cloud = !$1.nil?
863
- channel = $2
864
-
865
- get_channels_name_and_id() unless @channels_name.keys.include?(channel) or @channels_id.keys.include?(channel)
866
- channel_id = nil
867
- if @channels_name.key?(channel) #it is an id
868
- channel_id = channel
869
- channel = @channels_name[channel_id]
870
- elsif @channels_id.key?(channel) #it is a channel name
871
- channel_id = @channels_id[channel]
872
- end
873
- #todo: add pagination for case more than 1000 channels on the workspace
874
- channels = client.web_client.conversations_list(
875
- types: 'private_channel,public_channel',
876
- limit: '1000',
877
- exclude_archived: 'true').channels
878
- channel_found = channels.detect { |c| c.name == channel }
879
- members = client.web_client.conversations_members(channel: @channels_id[channel]).members unless channel_found.nil?
880
-
881
- if channel_id.nil?
882
- respond "There is no channel with that name: #{channel}, please be sure is written exactly the same", dest
883
- elsif channel == MASTER_CHANNEL
884
- respond "There is already a bot in this channel: #{channel}", dest
885
- elsif @bots_created.keys.include?(channel_id)
886
- respond "There is already a bot in this channel: #{channel}, kill it before", dest
887
- elsif config[:nick_id] != channel_found.creator and !members.include?(config[:nick_id])
888
- respond "You need to add first to the channel the smart bot user: #{config[:nick]}", dest
889
- else
890
- if channel_id != config[:channel]
891
- begin
892
- rules_file = "slack-smart-bot_rules_#{channel_id}_#{from.gsub(" ", "_")}.rb"
893
- if defined?(RULES_FOLDER)
894
- rules_file = RULES_FOLDER + rules_file
895
- else
896
- Dir.mkdir("rules") unless Dir.exist?("rules")
897
- Dir.mkdir("rules/#{channel_id}") unless Dir.exist?("rules/#{channel_id}")
898
- rules_file = "./rules/#{channel_id}/" + rules_file
899
- end
900
- default_rules = (__FILE__).gsub(/\.rb$/, "_rules.rb")
901
- File.delete(rules_file) if File.exist?(rules_file)
902
- FileUtils.copy_file(default_rules, rules_file) unless File.exist?(rules_file)
903
- admin_users = Array.new()
904
- creator_info = client.web_client.users_info(user: channel_found.creator)
905
- admin_users = [from, creator_info.user.name] + MASTER_USERS
906
- admin_users.uniq!
907
- @logger.info "ruby #{$0} \"#{channel}\" \"#{admin_users.join(",")}\" \"#{rules_file}\" on"
908
- if cloud
909
- respond "Copy the bot folder to your cloud location and run `ruby #{$0} \"#{channel}\" \"#{admin_users.join(",")}\" \"#{rules_file}\" on&`", dest
910
- else
911
- t = Thread.new do
912
- `ruby #{$0} \"#{channel}\" \"#{admin_users.join(",")}\" \"#{rules_file}\" on`
913
- end
914
- end
915
- @bots_created[channel_id] = {
916
- creator_name: from,
917
- channel_id: channel_id,
918
- channel_name: @channels_name[channel_id],
919
- status: :on,
920
- created: Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z")[0..18],
921
- rules_file: rules_file,
922
- admins: admin_users.join(","),
923
- extended: [],
924
- cloud: cloud,
925
- thread: t,
926
- }
927
- respond "The bot has been created on channel: #{channel}. Rules file: #{File.basename rules_file}. Admins: #{admin_users.join(", ")}", dest
928
- update_bots_file()
929
- rescue Exception => stack
930
- @logger.fatal stack
931
- message = "Problem creating the bot on channel #{channel}. Error: <#{stack}>."
932
- @logger.error message
933
- respond message, dest
934
- end
935
- else
936
- respond "There is already a bot in this channel: #{channel}, and it is the Master Channel!", dest
937
- end
938
- end
939
- else
940
- @logger.info MASTER_CHANNEL
941
- @logger.info @channel_id.inspect
942
- respond "Sorry I cannot create bots from this channel, please visit the master channel: <##{@master_bot_id}>", dest
943
- end
944
-
945
- #helpmaster: ----------------------------------------------
946
- #helpmaster: `kill bot on CHANNEL_NAME`
947
- #helpmaster: kills the bot on the specified channel
948
- #helpmaster: Only works if you are on Master channel and you created that bot or you are an admin user
949
- #helpmaster:
950
- when /^kill\sbot\son\s<#C\w+\|(.+)>\s*$/i, /^kill\sbot\son\s(.+)\s*$/i
951
- if ON_MASTER_BOT
952
- channel = $1
953
-
954
- get_channels_name_and_id() unless @channels_name.keys.include?(channel) or @channels_id.keys.include?(channel)
955
- channel_id = nil
956
- if @channels_name.key?(channel) #it is an id
957
- channel_id = channel
958
- channel = @channels_name[channel_id]
959
- elsif @channels_id.key?(channel) #it is a channel name
960
- channel_id = @channels_id[channel]
961
- end
962
- if channel_id.nil?
963
- respond "There is no channel with that name: #{channel}, please be sure is written exactly the same", dest
964
- elsif @bots_created.keys.include?(channel_id)
965
- if @bots_created[channel_id][:admins].split(",").include?(from)
966
- if @bots_created[channel_id][:thread].kind_of?(Thread) and @bots_created[channel_id][:thread].alive?
967
- @bots_created[channel_id][:thread].kill
968
- end
969
- @bots_created.delete(channel_id)
970
- update_bots_file()
971
- respond "Bot on channel: #{channel}, has been killed and deleted.", dest
972
- send_msg_channel(channel, "Bot has been killed by #{from}")
973
- else
974
- respond "You need to be the creator or an admin of that bot channel", dest
975
- end
976
- else
977
- respond "There is no bot in this channel: #{channel}", dest
978
- end
979
- else
980
- respond "Sorry I cannot kill bots from this channel, please visit the master channel: <##{@master_bot_id}>", dest
981
- end
982
-
983
-
984
-
985
- else
986
- processed = false
987
- end
988
- else
989
- processed = false
990
- end
991
-
992
- on_demand = false
993
- if command.match(/^@?(#{config[:nick]}):*\s+(.+)/im) or
994
- command.match(/^()!(.+)/im) or
995
- command.match(/^()<@#{config[:nick_id]}>\s+(.+)/im)
996
- command = $2
997
- on_demand = true
998
- end
999
-
1000
- #only when :on and (listening or on demand or direct message)
1001
- if @status == :on and
1002
- (@questions.keys.include?(from) or
1003
- (@listening.include?(from) and typem!=:on_extended) or
1004
- typem == :on_dm or typem ==:on_pg or on_demand)
1005
- processed2 = true
1006
-
1007
- #help: ===================================
1008
- #help:
1009
- #help: *These commands will run only when the smart bot is listening to you or on demand or in a private conversation with the Smart Bot*. To run a command on demand:
1010
- #help: `!THE_COMMAND`
1011
- #help: `@NAME_OF_BOT THE_COMMAND`
1012
- #help: `NAME_OF_BOT THE_COMMAND`
1013
- #help:
1014
- case command
1015
-
1016
- when /^bot\s+rules$/i
1017
- if typem == :on_extended or typem == :on_call #for the other cases above.
1018
- help_message_rules = ''
1019
- message = "-\n\n\n===================================\n*Rules from channel #{CHANNEL}*\n"
1020
- if typem == :on_extended
1021
- message += "To run the commands on this extended channel, add `!` before the command.\n"
1022
- end
1023
- help_message_rules = IO.readlines(rules_file).join
1024
- message += help_message_rules.scan(/#\s*help\s*:(.*)/).join("\n")
1025
- respond message, dest
1026
- unless rules_file.empty?
1027
- begin
1028
- eval(File.new(rules_file).read) if File.exist?(rules_file)
1029
- end
1030
- end
1031
- if defined?(git_project) and git_project.to_s!='' and help_message_rules != ''
1032
- respond "Git project: #{git_project}", dest
1033
- else
1034
- def git_project() '' end
1035
- def project_folder() '' end
1036
- end
1037
- end
1038
-
1039
-
1040
-
1041
- #helpadmin: ----------------------------------------------
1042
- #helpadmin: `extend rules to CHANNEL_NAME`
1043
- #helpadmin: `use rules on CHANNEL_NAME`
1044
- #helpadmin: It will allow to use the specific rules from this channel on the CHANNEL_NAME
1045
- #helpadmin:
1046
- when /^extend\s+rules\s+(to\s+)<#C\w+\|(.+)>/i, /^extend\s+rules\s+(to\s+)(.+)/i,
1047
- /^use\s+rules\s+(on\s+)<#C\w+\|(.+)>/i, /^use\s+rules\s+(on\s+)(.+)/i
1048
- unless typem == :on_extended
1049
- if ON_MASTER_BOT
1050
- respond "You cannot use the rules from Master Channel on any other channel.", dest
1051
- elsif !ADMIN_USERS.include?(from) #not admin
1052
- respond "Only admins can extend the rules. Admins on this channel: #{ADMIN_USERS}", dest
1053
- else
1054
- channel = $2
1055
- #todo: add pagination for case more than 1000 channels on the workspace
1056
- channels = client.web_client.conversations_list(
1057
- types: 'private_channel,public_channel',
1058
- limit: '1000',
1059
- exclude_archived: 'true').channels
1060
-
1061
- channel_found = channels.detect { |c| c.name == channel }
1062
- members = client.web_client.conversations_members(channel: @channels_id[channel]).members unless channel_found.nil?
1063
- get_bots_created()
1064
- channels_in_use = []
1065
- @bots_created.each do |k,v|
1066
- if v.key?(:extended) and v[:extended].include?(channel)
1067
- channels_in_use << v[:channel_name]
1068
- end
1069
- end
1070
-
1071
- if channel_found.nil?
1072
- respond "The channel you specified doesn't exist", dest
1073
- elsif @bots_created.key?(@channels_id[channel])
1074
- respond "There is a bot already running on that channel.", dest
1075
- elsif @bots_created[@channel_id][:extended].include?(channel)
1076
- respond "The rules are already extended to that channel.", dest
1077
- elsif !members.include?(config[:nick_id])
1078
- respond "You need to add first to the channel the smart bot user: #{config[:nick]}", dest
1079
- elsif !members.include?(user.id)
1080
- respond "You need to join that channel first", dest
1081
- else
1082
- channels_in_use.each do |channel_in_use|
1083
- respond "The rules from channel <##{@channels_id[channel_in_use]}> are already in use on that channel", dest
1084
- end
1085
- @bots_created[@channel_id][:extended] = [] unless @bots_created[@channel_id].key?(:extended)
1086
- @bots_created[@channel_id][:extended] << channel
1087
- update_bots_file()
1088
- respond "<@#{user.id}> extended the rules from #{CHANNEL} to be used on #{channel}.", @master_bot_id
1089
- respond "Now the rules from <##{@channel_id}> are available on <##{@channels_id[channel]}>", dest
1090
- 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]
1091
- respond "Use `!` before the command you want to run", @channels_id[channel]
1092
- respond "To see the specific rules for this bot on this channel: `!bot rules`", @channels_id[channel]
1093
- end
1094
- end
1095
- end
1096
-
1097
- #helpadmin: ----------------------------------------------
1098
- #helpadmin: `stop using rules on CHANNEL_NAME`
1099
- #helpadmin: it will stop using the extended rules on the specified channel.
1100
- #helpadmin:
1101
- when /^stop using rules (on\s+)<#\w+\|(.+)>/i, /^stop using rules (on\s+)(.+)/i
1102
- unless typem == :on_extended
1103
- if !ADMIN_USERS.include?(from) #not admin
1104
- respond "Only admins can extend or stop using the rules. Admins on this channel: #{ADMIN_USERS}", dest
1105
- else
1106
- channel = $2
1107
- get_bots_created()
1108
- if @bots_created[@channel_id][:extended].include?(channel)
1109
- @bots_created[@channel_id][:extended].delete(channel)
1110
- update_bots_file()
1111
- respond "<@#{user.id}> removed the access to the rules of #{CHANNEL} from #{channel}.", @master_bot_id
1112
- respond "The rules won't be accessible from <##{@channels_id[channel]}> from now on.", dest
1113
- respond "<@#{user.id}> removed the access to the rules of <##{@channel_id}> from this channel.", @channels_id[channel]
1114
- else
1115
- respond "The rules were not accessible from #{@channels_id[channel]}", dest
1116
- end
1117
- end
1118
- end
1119
-
1120
- #help: ----------------------------------------------
1121
- #help: `add shortcut NAME: COMMAND`
1122
- #help: `add sc NAME: COMMAND`
1123
- #help: `add shortcut for all NAME: COMMAND`
1124
- #help: `add sc for all NAME: COMMAND`
1125
- #help: `shortchut NAME: COMMAND`
1126
- #help: `shortchut for all NAME: COMMAND`
1127
- #help: It will add a shortcut that will execute the command we supply.
1128
- #help: In case we supply 'for all' then the shorcut will be available for everybody
1129
- #help: Example:
1130
- #help: _add shortcut for all Spanish account: code require 'iso/iban'; 10.times {puts ISO::IBAN.random('ES')}_
1131
- #help: Then to call this shortcut:
1132
- #help: _sc spanish account_
1133
- #help: _shortcut Spanish Account_
1134
- #help: _Spanish Account_
1135
- #help:
1136
- when /^(add\s)?shortcut\s(for\sall)?\s*(.+)\s*:\s*(.+)/i, /^(add\s)sc\s(for\sall)?\s*(.+)\s*:\s*(.+)/i
1137
- unless typem == :on_extended
1138
- for_all = $2
1139
- shortcut_name = $3.to_s.downcase
1140
- command_to_run = $4
1141
- @shortcuts[from] = Hash.new() unless @shortcuts.keys.include?(from)
1142
-
1143
- if !ADMIN_USERS.include?(from) and @shortcuts[:all].include?(shortcut_name) and !@shortcuts[from].include?(shortcut_name)
1144
- respond "Only the creator of the shortcut or an admin user can modify it", dest
1145
- elsif !@shortcuts[from].include?(shortcut_name)
1146
- #new shortcut
1147
- @shortcuts[from][shortcut_name] = command_to_run
1148
- @shortcuts[:all][shortcut_name] = command_to_run if for_all.to_s != ""
1149
- update_shortcuts_file()
1150
- respond "shortcut added", dest
1151
- else
1152
-
1153
- #are you sure? to avoid overwriting existing
1154
- unless @questions.keys.include?(from)
1155
- ask("The shortcut already exists, are you sure you want to overwrite it?", command, from, dest)
1156
- else
1157
- case @questions[from]
1158
- when /^(yes|yep)/i
1159
- @shortcuts[from][shortcut_name] = command_to_run
1160
- @shortcuts[:all][shortcut_name] = command_to_run if for_all.to_s != ""
1161
- update_shortcuts_file()
1162
- respond "shortcut added", dest
1163
- @questions.delete(from)
1164
- when /^no/i
1165
- respond "ok, I won't add it", dest
1166
- @questions.delete(from)
1167
- else
1168
- respond "I don't understand, yes or no?", dest
1169
- end
1170
- end
1171
- end
1172
- end
1173
-
1174
- #help: ----------------------------------------------
1175
- #help: `delete shortcut NAME`
1176
- #help: `delete sc NAME`
1177
- #help: It will delete the shortcut with the supplied name
1178
- #help:
1179
- when /^delete\s+shortcut\s+(.+)/i, /^delete\s+sc\s+(.+)/i
1180
- unless typem == :on_extended
1181
- shortcut = $1.to_s.downcase
1182
- deleted = false
1183
-
1184
- if !ADMIN_USERS.include?(from) and @shortcuts[:all].include?(shortcut) and !@shortcuts[from].include?(shortcut)
1185
- respond "Only the creator of the shortcut or an admin user can delete it", dest
1186
- elsif (@shortcuts.keys.include?(from) and @shortcuts[from].keys.include?(shortcut)) or
1187
- (ADMIN_USERS.include?(from) and @shortcuts[:all].include?(shortcut))
1188
- #are you sure? to avoid deleting by mistake
1189
- unless @questions.keys.include?(from)
1190
- ask("are you sure you want to delete it?", command, from, dest)
1191
- else
1192
- case @questions[from]
1193
- when /^(yes|yep)/i
1194
- respond "shortcut deleted!", dest
1195
- respond "#{shortcut}: #{@shortcuts[from][shortcut]}", dest
1196
- @shortcuts[from].delete(shortcut)
1197
- @shortcuts[:all].delete(shortcut)
1198
- @questions.delete(from)
1199
- update_shortcuts_file()
1200
- when /^no/i
1201
- respond "ok, I won't delete it", dest
1202
- @questions.delete(from)
1203
- else
1204
- respond "I don't understand, yes or no?", dest
1205
- end
1206
- end
1207
- else
1208
- respond "shortcut not found", dest
1209
- end
1210
- end
1211
-
1212
- #help: ----------------------------------------------
1213
- #help: `see shortcuts`
1214
- #help: `see sc`
1215
- #help: It will display the shortcuts stored for the user and for :all
1216
- #help:
1217
- when /^see\sshortcuts/i, /^see\ssc/i
1218
- unless typem == :on_extended
1219
- msg = ""
1220
- if @shortcuts[:all].keys.size > 0
1221
- msg = "*Available shortcuts for all:*\n"
1222
- @shortcuts[:all].each { |name, value|
1223
- msg += " _#{name}: #{value}_\n"
1224
- }
1225
- respond msg, dest
1226
- end
1227
-
1228
- if @shortcuts.keys.include?(from) and @shortcuts[from].keys.size > 0
1229
- new_hash = @shortcuts[from].dup
1230
- @shortcuts[:all].keys.each { |k| new_hash.delete(k) }
1231
- if new_hash.keys.size > 0
1232
- msg = "*Available shortcuts for #{from}:*\n"
1233
- new_hash.each { |name, value|
1234
- msg += " _#{name}: #{value}_\n"
1235
- }
1236
- respond msg, dest
1237
- end
1238
- end
1239
- respond "No shortcuts found", dest if msg == ""
1240
- end
1241
-
1242
- #help: ----------------------------------------------
1243
- #help: `id channel CHANNEL_NAME`
1244
- #help: shows the id of a channel name
1245
- #help:
1246
- when /^id\schannel\s<#C\w+\|(.+)>\s*/i, /^id channel (.+)/
1247
- unless typem == :on_extended
1248
- channel_name = $1
1249
- get_channels_name_and_id()
1250
- if @channels_id.keys.include?(channel_name)
1251
- respond "the id of #{channel_name} is #{@channels_id[channel_name]}", dest
1252
- else
1253
- respond "channel: #{channel_name} not found", dest
1254
- end
1255
- end
1256
-
1257
- #help: ----------------------------------------------
1258
- #help: `ruby RUBY_CODE`
1259
- #help: `code RUBY_CODE`
1260
- #help: runs the code supplied and returns the output. Also you can send a Ruby file instead. Examples:
1261
- #help: _code puts (34344/99)*(34+14)_
1262
- #help: _ruby require 'json'; res=[]; 20.times {res<<rand(100)}; my_json={result: res}; puts my_json.to_json_
1263
- #help:
1264
- when /^\s*ruby\s(.+)/im, /^\s*code\s(.+)/im
1265
- code = $1
1266
- code.gsub!("\\n", "\n")
1267
- code.gsub!("\\r", "\r")
1268
- @logger.info code
1269
- unless code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File") or
1270
- code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
1271
- code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
1272
- code.include?("ENV") or code.match?(/=\s*IO/)
1273
- unless rules_file.empty?
1274
- begin
1275
- eval(File.new(rules_file).read) if File.exist?(rules_file)
1276
- end
1277
- end
1278
-
1279
- respond "Running", dest if code.size > 100
1280
-
1281
- begin
1282
- code.gsub!(/^\W*$/,'') #to remove special chars from slack when copy/pasting
1283
- ruby = "ruby -e \"#{code.gsub('"', '\"')}\""
1284
- if defined?(project_folder) and project_folder.to_s!='' and Dir.exist?(project_folder)
1285
- ruby = ("cd #{project_folder} &&" + ruby)
1286
- else
1287
- def project_folder() '' end
1288
- end
1289
- stdout, stderr, status = Open3.capture3(ruby)
1290
- if stderr == ""
1291
- if stdout == ""
1292
- respond "Nothing returned. Remember you need to use p or puts to print", dest
1293
- else
1294
- respond stdout, dest
1295
- end
1296
- else
1297
- respond stderr, dest
1298
- end
1299
- rescue Exception => exc
1300
- respond exc, dest
1301
- end
1302
- else
1303
- respond "Sorry I cannot run this due security reasons", dest
1304
- end
1305
-
1306
-
1307
- else
1308
- processed2 = false
1309
- end #of case
1310
-
1311
- processed = true if processed or processed2
1312
- end
1313
-
1314
- return processed
1315
- end
1316
-
1317
- def respond(msg, dest = nil)
1318
- if dest.nil?
1319
- client.message(channel: @channel_id, text: msg, as_user: true)
1320
- elsif dest[0] == "C" or dest[0] == "G" # channel
1321
- client.message(channel: dest, text: msg, as_user: true)
1322
- elsif dest[0] == "D" # Direct message
1323
- send_msg_user(dest, msg)
1324
- else
1325
- @logger.warn("method respond not treated correctly: msg:#{msg} dest:#{dest}")
1326
- end
1327
- end
1328
-
1329
- #context: previous message
1330
- #to: user that should answer
1331
- def ask(question, context, to, dest = nil)
1332
- if dest.nil?
1333
- client.message(channel: @channel_id, text: "#{to}: #{question}", as_user: true)
1334
- elsif dest[0] == "C" or dest[0] == "G" # channel
1335
- client.message(channel: dest, text: "#{to}: #{question}", as_user: true)
1336
- elsif dest[0] == "D" #private message
1337
- send_msg_user(dest, "#{to}: #{question}")
1338
- end
1339
- @questions[to] = context
1340
- end
1341
-
1342
- # to: (String) Channel name or id
1343
- # msg: (String) message to send
1344
- def send_msg_channel(to, msg)
1345
- unless msg == ""
1346
- get_channels_name_and_id() unless @channels_name.key?(to) or @channels_id.key?(to)
1347
- if @channels_name.key?(to) #it is an id
1348
- channel_id = to
1349
- elsif @channels_id.key?(to) #it is a channel name
1350
- channel_id = @channels_id[to]
1351
- else
1352
- @logger.fatal "Channel: #{to} not found. Message: #{msg}"
1353
- end
1354
- client.message(channel: channel_id, text: msg, as_user: true)
1355
- end
1356
- end
1357
-
1358
- #to send messages without listening for a response to users
1359
- def send_msg_user(id_user, msg)
1360
- unless msg == ""
1361
- if id_user[0] == "D"
1362
- client.message(channel: id_user, as_user: true, text: msg)
1363
- else
1364
- im = client.web_client.im_open(user: id_user)
1365
- client.message(channel: im["channel"]["id"], as_user: true, text: msg)
1366
- end
1367
- end
1368
- end
1369
-
1370
- #to send a file to an user or channel
1371
- #send_file(dest, 'the message', "#{project_folder}/temp/logs_ptBI.log", 'message to be sent', 'text/plain', "text")
1372
- #send_file(dest, 'the message', "#{project_folder}/temp/example.jpeg", 'message to be sent', 'image/jpeg', "jpg")
1373
- def send_file(to, msg, file, title, format, type = "text")
1374
- if to[0] == "U" #user
1375
- im = client.web_client.im_open(user: to)
1376
- channel = im["channel"]["id"]
1377
- else
1378
- channel = to
1379
- end
1380
-
1381
- client.web_client.files_upload(
1382
- channels: channel,
1383
- as_user: true,
1384
- file: Faraday::UploadIO.new(file, format),
1385
- title: title,
1386
- filename: file,
1387
- filetype: type,
1388
- initial_comment: msg,
1389
- )
1390
- end
1391
-
1392
211
  private :update_bots_file, :get_bots_created, :get_channels_name_and_id, :update_shortcuts_file
1393
212
  end