slack-smart-bot 1.11.0 → 1.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +44 -3
- data/lib/slack/smart-bot/comm/respond.rb +8 -2
- data/lib/slack/smart-bot/comm/set_status.rb +21 -0
- data/lib/slack/smart-bot/comm.rb +1 -0
- data/lib/slack/smart-bot/commands/general/add_memo_team.rb +117 -0
- data/lib/slack/smart-bot/commands/general/add_vacation.rb +51 -0
- data/lib/slack/smart-bot/commands/general/delete_memo_team.rb +69 -0
- data/lib/slack/smart-bot/commands/general/delete_team.rb +21 -1
- data/lib/slack/smart-bot/commands/general/remove_vacation.rb +27 -0
- data/lib/slack/smart-bot/commands/general/see_announcements.rb +1 -1
- data/lib/slack/smart-bot/commands/general/see_statuses.rb +1 -1
- data/lib/slack/smart-bot/commands/general/see_teams.rb +182 -32
- data/lib/slack/smart-bot/commands/general/see_vacations.rb +58 -0
- data/lib/slack/smart-bot/commands/general/see_vacations_team.rb +136 -0
- data/lib/slack/smart-bot/commands/general/set_memo_status.rb +58 -0
- data/lib/slack/smart-bot/commands/general/update_team.rb +22 -1
- data/lib/slack/smart-bot/commands/general_bot_commands.rb +172 -4
- data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +21 -5
- data/lib/slack/smart-bot/commands/on_bot/kill_repl.rb +32 -0
- data/lib/slack/smart-bot/commands/on_bot/repl.rb +1 -0
- data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +113 -33
- data/lib/slack/smart-bot/commands/on_master/admin_master/publish_announcements.rb +3 -1
- data/lib/slack/smart-bot/commands.rb +8 -0
- data/lib/slack/smart-bot/process.rb +17 -7
- data/lib/slack/smart-bot/treat_message.rb +11 -1
- data/lib/slack/smart-bot/utils/check_vacations.rb +43 -0
- data/lib/slack/smart-bot/utils/get_admins_channels.rb +23 -3
- data/lib/slack/smart-bot/utils/get_command_ids.rb +1 -1
- data/lib/slack/smart-bot/utils/get_help.rb +4 -3
- data/lib/slack/smart-bot/utils/get_vacations.rb +22 -0
- data/lib/slack/smart-bot/utils/save_stats.rb +9 -2
- data/lib/slack/smart-bot/utils/save_status.rb +1 -1
- data/lib/slack/smart-bot/utils/update_admins_channels.rb +20 -3
- data/lib/slack/smart-bot/utils/update_vacations.rb +16 -0
- data/lib/slack/smart-bot/utils.rb +3 -0
- data/lib/slack-smart-bot.rb +22 -2
- data/whats_new.txt +12 -17
- metadata +19 -7
@@ -76,7 +76,7 @@ def general_bot_commands(user, command, dest, files = [])
|
|
76
76
|
# help: command_id: :add_announcement
|
77
77
|
# help:
|
78
78
|
when /\A\s*(add|create)\s+(red\s+|green\s+|white\s+|yellow\s+)?(announcement|statement|declaration|message)\s+(.+)\s*\z/i,
|
79
|
-
/\A\s*(add|create)\s+(
|
79
|
+
/\A\s*(add|create)\s+(:[\w\-]+:)\s+(announcement|statement|declaration|message)\s+(.+)\s*\z/i
|
80
80
|
type = $2.to_s.downcase.strip
|
81
81
|
type = 'white' if type == ''
|
82
82
|
message = $4
|
@@ -122,10 +122,10 @@ def general_bot_commands(user, command, dest, files = [])
|
|
122
122
|
# help: <https://github.com/MarioRuiz/slack-smart-bot#announcements|more info>
|
123
123
|
# help: command_id: :see_announcements
|
124
124
|
# help:
|
125
|
-
when /\A\s*see\s+(red\s+|green\s+|white\s+|yellow\s
|
125
|
+
when /\A\s*see\s+(red\s+|green\s+|white\s+|yellow\s+|:[\w\-]+:\s+)?(announcements|statements|declarations|messages)()\s*\z/i,
|
126
126
|
/\A\s*see\s+(all\s+)?(announcements|statements|declarations|messages)()\s*\z/i,
|
127
|
-
/\A\s*see\s+(red\s+|green\s+|white\s+|yellow\s
|
128
|
-
/\A\s*see\s+(red\s+|green\s+|white\s+|yellow\s
|
127
|
+
/\A\s*see\s+(red\s+|green\s+|white\s+|yellow\s+|:[\w\-]+:\s+)?(announcements|statements|declarations|messages)\s+#([\w\-]+)\s*\z/i,
|
128
|
+
/\A\s*see\s+(red\s+|green\s+|white\s+|yellow\s+|:[\w\-]+:\s+)?(announcements|statements|declarations|messages)\s+<#(\w+)\|.*>\s*\z/i
|
129
129
|
|
130
130
|
type = $1.to_s.downcase.strip
|
131
131
|
channel = $3.to_s
|
@@ -387,6 +387,94 @@ def general_bot_commands(user, command, dest, files = [])
|
|
387
387
|
info = Thread.current[:command_orig].to_s.gsub("\u00A0", " ").scan(/^[^:]+:\s*(.+)\s*$/im).join
|
388
388
|
add_team(user, name, options, info)
|
389
389
|
|
390
|
+
# help: ----------------------------------------------
|
391
|
+
# help: `add TYPE to TEAM_NAME team : MESSAGE`
|
392
|
+
# help: `add private TYPE to TEAM_NAME team : MESSAGE`
|
393
|
+
# help: `add personal TYPE to TEAM_NAME team : MESSAGE`
|
394
|
+
# help: `add TYPE to TEAM_NAME team TOPIC : MESSAGE`
|
395
|
+
# help: `add private TYPE to TEAM_NAME team TOPIC : MESSAGE`
|
396
|
+
# help: `add personal TYPE to TEAM_NAME team TOPIC : MESSAGE`
|
397
|
+
# help: It will add a memo to the team. The memos will be displayed with the team info.
|
398
|
+
# help: Only team members can add a memo.
|
399
|
+
# help: TYPE: memo, note, issue, task, feature, bug, jira, github
|
400
|
+
# help: TOPIC: one word, a-z, A-Z, 0-9, - and _
|
401
|
+
# help: If private then the memo will be only displayed to team members on a DM or the members channel.
|
402
|
+
# help: If personal then the memo will be only displayed to the creator on a DM.
|
403
|
+
# help: In case jira type supplied:
|
404
|
+
# help: The message should be an JQL URL, JQL string or an issue URL.
|
405
|
+
# help: To be able to use it you need to specify on the SmartBot settings the credentials.
|
406
|
+
# help: In case no TOPIC is supplied then it will create automatically the topics from the labels specified on every JIRA issue
|
407
|
+
# help: In case github type supplied:
|
408
|
+
# help: The message should be a github URL. You can filter by state (open/closed/all) and labels
|
409
|
+
# help: To be able to use it you need to specify on the SmartBot settings the github token.
|
410
|
+
# help: In case no TOPIC is supplied then it will create automatically the topics from the labels specified on every Github issue
|
411
|
+
# help: Examples:
|
412
|
+
# help: _add memo to sales team : Add tests for Michigan feature_
|
413
|
+
# help: _add private note to sales team : Bills will need to be deployed before Friday_
|
414
|
+
# help: _add memo to dev team web : Check last version_
|
415
|
+
# help: _add private bug to dev team SRE : Logs should not be accessible from outside VPN_
|
416
|
+
# help: _add memo sales team : Add tests for Michigan feature_
|
417
|
+
# help: _add memo team sales: Add tests for Michigan feature_
|
418
|
+
# help: _add personal memo team sales: Check my vacations_
|
419
|
+
# help: _add jira to sales team : labels = SalesT AND status != Done_
|
420
|
+
# help: _add github to sales team : PeterBale/SalesBoom/issues?state=open&labels=bug_
|
421
|
+
# help: _add github to sales team dev: PeterBale/DevProject/issues/71_
|
422
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#teams|more info>
|
423
|
+
# help: command_id: :add_memo_team
|
424
|
+
# help:
|
425
|
+
when /\A\s*add\s+(private\s+|personal\s+)?(memo|note|issue|task|feature|bug|jira|github)\s+(to\s+)?team\s+([\w\-]+)\s*([^:]+)?\s*:\s+(.+)\s*\z/im,
|
426
|
+
/\A\s*add\s+(private\s+|personal\s+)?(memo|note|issue|task|feature|bug|jira|github)\s+(to\s+)?([\w\-]+)\s+team\s*([^:]+)?\s*:\s+(.+)\s*\z/im
|
427
|
+
privacy = $1.to_s.strip.downcase
|
428
|
+
type = $2.downcase
|
429
|
+
team_name = $4.downcase
|
430
|
+
topic = $5.to_s.strip
|
431
|
+
message = Thread.current[:command_orig].to_s.gsub("\u00A0", " ").scan(/^[^:]+:\s*(.+)\s*$/im).join
|
432
|
+
add_memo_team(user, privacy, team_name, topic, type, message)
|
433
|
+
|
434
|
+
# help: ----------------------------------------------
|
435
|
+
# help: `delete memo ID from TEAM_NAME team`
|
436
|
+
# help: It will delete the supplied memo ID on the team specified.
|
437
|
+
# help: aliases for memo: note, issue, task, feature, bug, jira, github
|
438
|
+
# help: You have to be a member of the team, the creator or a Master admin to be able to delete a memo.
|
439
|
+
# help: Examples:
|
440
|
+
# help: _delete memo 32 from sales team_
|
441
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#teams|more info>
|
442
|
+
# help: command_id: :delete_memo_team
|
443
|
+
# help:
|
444
|
+
when /\A\s*(delete|remove)\s+(memo|note|issue|task|feature|bug|jira|github)\s+(\d+)\s+(from|on)\s+team\s+([\w\-]+)\s*\z/i,
|
445
|
+
/\A\s*(delete|remove)\s+(memo|note|issue|task|feature|bug|jira|github)\s+(\d+)\s+(from|on)\s+([\w\-]+)\s+team\s*\z/i
|
446
|
+
memo_id = $3
|
447
|
+
team_name = $5.downcase
|
448
|
+
delete_memo_team(user, team_name, memo_id)
|
449
|
+
|
450
|
+
# help: ----------------------------------------------
|
451
|
+
# help: `set memo ID on TEAM_NAME team STATUS`
|
452
|
+
# help: `set STATUS on memo ID TEAM_NAME team`
|
453
|
+
# help: It will assign to the ID specified the emoticon status indicated.
|
454
|
+
# help: aliases for memo: note, issue, task, feature, bug
|
455
|
+
# help: This command will be only for memo, note, issue, task, feature, bug. Not for jira or github.
|
456
|
+
# help: You have to be a member of the team, the creator or a Master admin to be able to set a status.
|
457
|
+
# help: Examples:
|
458
|
+
# help: _set memo 32 on sales team :runner:_
|
459
|
+
# help: _set bug 7 on team sales :heavy_check_mark:_
|
460
|
+
# help: _set :runner: on memo 6 sales team_
|
461
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#teams|more info>
|
462
|
+
# help: command_id: :set_memo_status
|
463
|
+
# help:
|
464
|
+
when /\A\s*(set)\s+(memo|note|issue|task|feature|bug)\s+(\d+)\s+on\s+team\s+([\w\-]+)\s+(:[\w\-]+:)\s*\z/i,
|
465
|
+
/\A\s*(set)\s+(memo|note|issue|task|feature|bug)\s+(\d+)\s+on\s+([\w\-]+)\s+team\s+(:[\w\-]+:)\s*\z/i
|
466
|
+
memo_id = $3
|
467
|
+
team_name = $4.downcase
|
468
|
+
status = $5
|
469
|
+
set_memo_status(user, team_name, memo_id, status)
|
470
|
+
|
471
|
+
when /\A\s*(set)\s+(:[\w\-]+:)\s+on\s+(memo|note|issue|task|feature|bug)\s+(\d+)\s+team\s+([\w\-]+)\s*\z/i,
|
472
|
+
/\A\s*(set)\s+(:[\w\-]+:)\s+on\s+(memo|note|issue|task|feature|bug)\s+(\d+)\s+([\w\-]+)\s+team\s*\z/i
|
473
|
+
memo_id = $4
|
474
|
+
team_name = $5.downcase
|
475
|
+
status = $2
|
476
|
+
set_memo_status(user, team_name, memo_id, status)
|
477
|
+
|
390
478
|
# help: ----------------------------------------------
|
391
479
|
# help: `see teams`
|
392
480
|
# help: `see team TEAM_NAME`
|
@@ -488,6 +576,86 @@ def general_bot_commands(user, command, dest, files = [])
|
|
488
576
|
name = $2.downcase
|
489
577
|
delete_team(user, name)
|
490
578
|
|
579
|
+
|
580
|
+
# help: ----------------------------------------------
|
581
|
+
# help: `add vacation from YYYY/MM/DD to YYYY/MM/DD`
|
582
|
+
# help: `add vacation YYYY/MM/DD`
|
583
|
+
# help: `add sick from YYYY/MM/DD to YYYY/MM/DD`
|
584
|
+
# help: `add sick YYYY/MM/DD`
|
585
|
+
# help: `add sick child YYYY/MM/DD`
|
586
|
+
# help: It will add the supplied period to your plan.
|
587
|
+
# help: Instead of YYYY/MM/DD you can use 'today' or 'tomorrow' or 'next week'
|
588
|
+
# help: To see your plan call `see my time off`
|
589
|
+
# help: If you want to see the vacation plan for the team `see team NAME`
|
590
|
+
# help: Also you can see the vacation plan for the team for a specific period: `vacations team NAME YYYY/MM/DD`
|
591
|
+
# help: The SmartBot will automatically set the users status to :palm_tree:, :baby: or :face_with_thermometer: and the expiration date.
|
592
|
+
# help: Examples:
|
593
|
+
# help: _add vacation from 2022/10/01 to 2022/10/22_
|
594
|
+
# help: _add sick 2022/08/22_
|
595
|
+
# help: _add vacation tomorrow_
|
596
|
+
# help: _add sick baby today_
|
597
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#time-off-management|more info>
|
598
|
+
# help: command_id: :add_vacation
|
599
|
+
# help:
|
600
|
+
when /\A\s*add\s+(sick|vacation|sick\s+baby|sick\s+child)\s+from\s+(\d\d\d\d\/\d\d\/\d\d)\s+to\s+(\d\d\d\d\/\d\d\/\d\d)\s*\z/i,
|
601
|
+
/\A\s*add\s+(sick|vacation|sick\s+baby|sick\s+child)\s+from\s+(\d\d\d\d-\d\d-\d\d)\s+to\s+(\d\d\d\d-\d\d-\d\d)\s*\z/i,
|
602
|
+
/\A\s*add\s+(sick|vacation|sick\s+baby|sick\s+child)\s+(\d\d\d\d-\d\d-\d\d)()\s*\z/i,
|
603
|
+
/\A\s*add\s+(sick|vacation|sick\s+baby|sick\s+child)\s+(\d\d\d\d\/\d\d\/\d\d)()\s*\z/i,
|
604
|
+
/\A\s*add\s+(sick|vacation|sick\s+baby|sick\s+child)\s+(today|tomorrow|next\sweek)()\s*\z/i
|
605
|
+
type = $1
|
606
|
+
from = $2.downcase
|
607
|
+
to = $3
|
608
|
+
add_vacation(user, type, from, to)
|
609
|
+
|
610
|
+
# help: ----------------------------------------------
|
611
|
+
# help: `remove vacation ID`
|
612
|
+
# help: `remove vacation period ID`
|
613
|
+
# help: `remove sick period ID`
|
614
|
+
# help: `remove time off period ID`
|
615
|
+
# help: `delete vacation ID`
|
616
|
+
# help: It will remove the specified period from your vacations/sick periods.
|
617
|
+
# help: Examples:
|
618
|
+
# help: _remove vacation 20_
|
619
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#time-off-management|more info>
|
620
|
+
# help: command_id: :remove_vacation
|
621
|
+
# help:
|
622
|
+
when /\A\s*(delete|remove)\s+(vacation|sick|time\s+off)(\s+period)?\s+(\d+)\s*\z/i
|
623
|
+
vacation_id = $4
|
624
|
+
remove_vacation(user, vacation_id)
|
625
|
+
|
626
|
+
# help: ----------------------------------------------
|
627
|
+
# help: `see my vacations`
|
628
|
+
# help: `see my time off`
|
629
|
+
# help: `see vacations @USER`
|
630
|
+
# help: It will display current and past time off.
|
631
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#time-off-management|more info>
|
632
|
+
# help: command_id: :see_vacations
|
633
|
+
# help:
|
634
|
+
when /\A\s*see\s+my\s+vacations\s*()\z/i,
|
635
|
+
/\A\s*see\s+my\s+time\s+off\s*()\z/i,
|
636
|
+
/\A\s*see\s+time\s+off\s+<@(\w+)>\s*\z/i,
|
637
|
+
/\A\s*see\s+vacations\s+<@(\w+)>\s*\z/i
|
638
|
+
from_user = $1
|
639
|
+
see_vacations(user, from_user: from_user)
|
640
|
+
|
641
|
+
# help: ----------------------------------------------
|
642
|
+
# help: `vacations team NAME`
|
643
|
+
# help: `time off team NAME`
|
644
|
+
# help: `vacations team NAME YYYY/MM/DD`
|
645
|
+
# help: `time off team NAME YYYY/MM/DD`
|
646
|
+
# help: It will display the time off plan for the team specified.
|
647
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#time-off-management|more info>
|
648
|
+
# help: command_id: :see_vacations_team
|
649
|
+
# help:
|
650
|
+
when /\A\s*(see\s+)?(vacations|time\s+off)\s+team\s+([\w\-]+)\s*(\d\d\d\d\/\d\d\/\d\d)?\s*\z/i,
|
651
|
+
/\A\s*(see\s+)?(vacations|time\s+off)\s+([\w\-]+)\s+team\s*(\d\d\d\d\/\d\d\/\d\d)?\s*\z/i,
|
652
|
+
/\A\s*(see\s+)?(vacations|time\s+off)\s+team\s+([\w\-]+)\s*(\d\d\d\d-\d\d-\d\d)?\s*\z/i,
|
653
|
+
/\A\s*(see\s+)?(vacations|time\s+off)\s+([\w\-]+)\s+team\s*(\d\d\d\d-\d\d-\d\d)?\s*\z/i
|
654
|
+
team_name = $3.downcase
|
655
|
+
date = $4.to_s
|
656
|
+
date = Date.today.strftime("%Y/%m/%d") if date.empty?
|
657
|
+
see_vacations_team(user, team_name, date)
|
658
|
+
|
491
659
|
else
|
492
660
|
return false
|
493
661
|
end
|
@@ -295,11 +295,27 @@ class SlackSmartBot
|
|
295
295
|
users_attachment = []
|
296
296
|
if user==''
|
297
297
|
users = rows.user_id.uniq.sort
|
298
|
-
rows.
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
298
|
+
if rows[0].key?(:time_zone) #then save_stats is saving the time zone already
|
299
|
+
rows.time_zone.each do |time_zone|
|
300
|
+
unless time_zone == ''
|
301
|
+
tzone_users[time_zone] ||= 0
|
302
|
+
tzone_users[time_zone] += 1
|
303
|
+
end
|
304
|
+
end
|
305
|
+
else
|
306
|
+
rows.user_id.each_with_index do |usr, i|
|
307
|
+
if rows[i].values.size >= 12 #then save_stats is saving the time zone already but not all the data
|
308
|
+
unless rows[i].values[11] == ''
|
309
|
+
tzone_users[rows[i].values[11]] ||= 0
|
310
|
+
tzone_users[rows[i].values[11]] += 1
|
311
|
+
end
|
312
|
+
else
|
313
|
+
user_info = @users.select { |u| u.id == usr or (u.key?(:enterprise_user) and u.enterprise_user.id == usr) }[-1]
|
314
|
+
unless user_info.nil? or user_info.is_app_user or user_info.is_bot
|
315
|
+
tzone_users[user_info.tz_label] ||= 0
|
316
|
+
tzone_users[user_info.tz_label] += 1
|
317
|
+
end
|
318
|
+
end
|
303
319
|
end
|
304
320
|
end
|
305
321
|
if users.size > 10
|
@@ -0,0 +1,32 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
# help: ----------------------------------------------
|
3
|
+
# help: `kill repl RUN_REPL_ID`
|
4
|
+
# help: Will kill a running repl previously executed with 'run repl' command.
|
5
|
+
# help: Only the user that run the repl or a master admin can kill the repl.
|
6
|
+
# help: Example:
|
7
|
+
# help: _kill repl X33JK_
|
8
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
|
9
|
+
# help: command_id: :kill_repl
|
10
|
+
# help:
|
11
|
+
def kill_repl(dest, user, repl_id)
|
12
|
+
#todo: add tests
|
13
|
+
if has_access?(__method__, user)
|
14
|
+
save_stats(__method__)
|
15
|
+
if !@run_repls.key?(repl_id)
|
16
|
+
respond "The run repl with id #{repl_id} doesn't exist"
|
17
|
+
elsif @run_repls[repl_id].user != user.name and !config.masters.include?(user.name)
|
18
|
+
respond "Only #{@run_repls[repl_id].user} or a master admin can kill this repl."
|
19
|
+
else
|
20
|
+
pids = `pgrep -P #{@run_repls[repl_id].pid}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
21
|
+
pids.each do |pd|
|
22
|
+
begin
|
23
|
+
Process.kill("KILL", pd)
|
24
|
+
rescue
|
25
|
+
end
|
26
|
+
end
|
27
|
+
respond "The repl #{@run_repls[repl_id].name} (id: #{repl_id}) has been killed."
|
28
|
+
@run_repls.delete(repl_id)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -2,28 +2,39 @@ class SlackSmartBot
|
|
2
2
|
# help: ----------------------------------------------
|
3
3
|
# help: `run repl SESSION_NAME`
|
4
4
|
# help: `run repl SESSION_NAME ENV_VAR=VALUE ENV_VAR=VALUE`
|
5
|
+
# help: `run repl SESSION_NAME PARAMS`
|
5
6
|
# help: `run live SESSION_NAME`
|
6
7
|
# help: `run irb SESSION_NAME`
|
7
|
-
# help: Will run the repl session specified and return the output.
|
8
|
+
# help: Will run the repl session specified and return the output.
|
8
9
|
# help: You can supply the Environmental Variables you need for the Session
|
10
|
+
# help: PARAMS: Also it is possible to supply code that will be run before the repl code on the same session.
|
9
11
|
# help: It will return only the values that were print out on the repl with puts, print, p or pp
|
10
12
|
# help: Example:
|
11
13
|
# help: _run repl CreateCustomer LOCATION=spain HOST='https://10.30.40.50:8887'_
|
12
14
|
# help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
|
13
15
|
# help: command_id: :run_repl
|
14
16
|
# help:
|
15
|
-
def run_repl(dest, user, session_name, env_vars, rules_file)
|
17
|
+
def run_repl(dest, user, session_name, env_vars, prerun, rules_file)
|
16
18
|
#todo: add tests
|
17
19
|
from = user.name
|
18
20
|
if has_access?(__method__, user)
|
19
21
|
save_stats(__method__)
|
20
22
|
Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
|
21
23
|
Dir.mkdir("#{config.path}/repl/#{@channel_id}") unless Dir.exist?("#{config.path}/repl/#{@channel_id}")
|
24
|
+
code = prerun.join("\n")
|
22
25
|
if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
|
23
|
-
if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
|
24
|
-
|
25
|
-
|
26
|
+
if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
|
27
|
+
@repls[session_name][:creator_name] != user.name and
|
28
|
+
!is_admin?(user.name)
|
26
29
|
respond "The REPL with session name: #{session_name} is private", dest
|
30
|
+
elsif !prerun.empty? and (code.match?(/System/i) or code.match?(/Kernel/i) or code.include?("File.") or
|
31
|
+
code.include?("`") or code.include?("exec") or code.include?("spawn") or code.include?("IO.") or
|
32
|
+
code.match?(/open3/i) or code.match?(/bundle/i) or code.match?(/gemfile/i) or code.include?("%x") or
|
33
|
+
code.include?("ENV") or code.match?(/=\s*IO/) or code.include?("Dir.") or
|
34
|
+
code.match?(/=\s*File/) or code.match?(/=\s*Dir/) or code.match?(/<\s*File/) or code.match?(/<\s*Dir/) or
|
35
|
+
code.match?(/\w+:\s*File/) or code.match?(/\w+:\s*Dir/) or
|
36
|
+
code.match?(/=?\s*(require|load)(\(|\s)/i))
|
37
|
+
respond "Sorry I cannot run this due security reasons", dest
|
27
38
|
else
|
28
39
|
if @repls.key?(session_name) #not temp
|
29
40
|
@repls[session_name][:accessed] = Time.now.to_s
|
@@ -32,55 +43,124 @@ class SlackSmartBot
|
|
32
43
|
else
|
33
44
|
@repls[session_name][:runs_by_others] += 1
|
34
45
|
end
|
35
|
-
update_repls()
|
46
|
+
update_repls()
|
36
47
|
end
|
37
48
|
|
38
49
|
content = env_vars.join("\n")
|
39
50
|
content += "\nrequire 'nice_http'\n"
|
40
51
|
unless rules_file.empty? # to get the project_folder
|
41
52
|
begin
|
42
|
-
eval(File.new(config.path+rules_file).read) if File.exist?(config.path+rules_file)
|
53
|
+
eval(File.new(config.path + rules_file).read) if File.exist?(config.path + rules_file)
|
43
54
|
end
|
44
55
|
end
|
45
|
-
if File.exist?("#{project_folder}/.smart-bot-repl") and
|
46
|
-
|
56
|
+
if File.exist?("#{project_folder}/.smart-bot-repl") and
|
57
|
+
((@repls.key?(session_name) and @repls[session_name][:type] != :private_clean and @repls[session_name][:type] != :public_clean) or !@repls.key?(session_name))
|
47
58
|
content += File.read("#{project_folder}/.smart-bot-repl")
|
48
59
|
content += "\n"
|
49
60
|
end
|
50
|
-
|
61
|
+
unless prerun.empty?
|
62
|
+
content += prerun.join("\n")
|
63
|
+
content += "\n"
|
64
|
+
end
|
65
|
+
content += File.read("#{config.path}/repl/#{@channel_id}/#{session_name}.run").gsub(/^(quit|exit|bye)$/i, "") #todo: remove this gsub, it will never contain it
|
51
66
|
Dir.mkdir("#{project_folder}/tmp") unless Dir.exist?("#{project_folder}/tmp")
|
52
67
|
Dir.mkdir("#{project_folder}/tmp/repl") unless Dir.exist?("#{project_folder}/tmp/repl")
|
53
|
-
|
54
|
-
|
68
|
+
if Thread.current[:on_thread]
|
69
|
+
# to force stdout.each to be performed every 3 seconds
|
70
|
+
content = "Thread.new do
|
71
|
+
while true do
|
72
|
+
puts ''
|
73
|
+
sleep 3
|
74
|
+
end
|
75
|
+
end
|
76
|
+
#{content}
|
77
|
+
"
|
78
|
+
end
|
79
|
+
random = "5:LN&".gen
|
80
|
+
File.write("#{project_folder}/tmp/repl/#{session_name}_#{user.name}_#{random}.rb", content, mode: "w+")
|
81
|
+
process_to_run = "ruby ./tmp/repl/#{session_name}_#{user.name}_#{random}.rb"
|
55
82
|
process_to_run = ("cd #{project_folder} && #{process_to_run}") if defined?(project_folder)
|
56
|
-
respond "Running REPL #{session_name}"
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
83
|
+
respond "Running REPL #{session_name} (id: #{random})"
|
84
|
+
@run_repls[random] = { user: user.name, name: session_name, pid: '' }
|
85
|
+
react :running
|
86
|
+
|
87
|
+
require "pty"
|
88
|
+
timeout = 60 * 60 * 4 # 4 hours
|
89
|
+
|
90
|
+
started = Time.now
|
91
|
+
results = []
|
92
|
+
begin
|
93
|
+
PTY.spawn(process_to_run) do |stdout, stdin, pid|
|
94
|
+
last_result = -1
|
95
|
+
last_time = Time.now
|
96
|
+
@run_repls[random].pid = pid
|
97
|
+
begin
|
98
|
+
stdout.each do |line|
|
99
|
+
if (Time.now - started) > timeout
|
100
|
+
respond "run REPL session finished. Max time reached: #{session_name} (id: #{random})", dest
|
101
|
+
pids = `pgrep -P #{pid}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
102
|
+
pids.each do |pd|
|
103
|
+
begin
|
104
|
+
Process.kill("KILL", pd)
|
105
|
+
rescue
|
106
|
+
end
|
107
|
+
end
|
108
|
+
break
|
67
109
|
else
|
68
|
-
|
110
|
+
results << line
|
111
|
+
if Thread.current[:on_thread]
|
112
|
+
if (Time.now - last_time) > 2
|
113
|
+
if (results.size - last_result) < 60 and results[(last_result + 1)..-1].join.size < 3500
|
114
|
+
output = ""
|
115
|
+
results[(last_result + 1)..-1].each do |li|
|
116
|
+
if li.match?(/^\s*{.+}\s*$/) or li.match?(/^\s*\[.+\]\s*$/)
|
117
|
+
output += "```\n#{li}```\n"
|
118
|
+
else
|
119
|
+
output += li
|
120
|
+
end
|
121
|
+
end
|
122
|
+
respond output
|
123
|
+
else
|
124
|
+
send_file(dest, "", "response.rb", "", "text/plain", "ruby", content: results[(last_result + 1)..-1].join)
|
125
|
+
end
|
126
|
+
last_result = results.size - 1
|
127
|
+
last_time = Time.now
|
128
|
+
end
|
129
|
+
end
|
69
130
|
end
|
70
131
|
end
|
71
|
-
|
132
|
+
rescue Errno::EIO
|
133
|
+
@logger.warn "run_repl PTY Errno:EIO error"
|
134
|
+
end
|
135
|
+
if results.empty?
|
136
|
+
respond "*#{session_name}* (id: #{random}): Nothing returned."
|
72
137
|
else
|
73
|
-
|
138
|
+
if last_result != (results.size - 1)
|
139
|
+
if (results.size - last_result) < 60 and results[(last_result + 1)..-1].join.size < 3500
|
140
|
+
output = ""
|
141
|
+
results[(last_result + 1)..-1].each do |li|
|
142
|
+
if li.match?(/^\s*{.+}\s*$/) or li.match?(/^\s*\[.+\]\s*$/)
|
143
|
+
output += "```\n#{li}```\n"
|
144
|
+
else
|
145
|
+
output += li
|
146
|
+
end
|
147
|
+
end
|
148
|
+
if Thread.current[:on_thread]
|
149
|
+
respond output
|
150
|
+
else
|
151
|
+
respond "*#{session_name}* (id: #{random}):\n#{output}"
|
152
|
+
end
|
153
|
+
else
|
154
|
+
send_file(dest, "", "response.rb", "", "text/plain", "ruby", content: results[(last_result + 1)..-1].join)
|
155
|
+
end
|
156
|
+
end
|
74
157
|
end
|
75
158
|
end
|
76
|
-
|
77
|
-
|
78
|
-
respond "*#{session_name}*: #{stdout} #{stderr}"
|
79
|
-
else
|
80
|
-
send_file(dest, "", 'response.rb', "", 'text/plain', "ruby", content: (stdout.to_s+stderr.to_s))
|
81
|
-
end
|
82
|
-
|
159
|
+
rescue PTY::ChildExited
|
160
|
+
@logger.warn "run_repl PTY The child process exited!"
|
83
161
|
end
|
162
|
+
@run_repls.delete(random) if @run_repls.key?(random)
|
163
|
+
unreact :running
|
84
164
|
end
|
85
165
|
else
|
86
166
|
respond "The REPL with session name: #{session_name} doesn't exist on this Smart Bot Channel", dest
|
@@ -2,6 +2,7 @@ class SlackSmartBot
|
|
2
2
|
# helpmaster: ----------------------------------------------
|
3
3
|
# helpmaster: `publish announcements`
|
4
4
|
# helpmaster: It will publish on all channels the announcements added by using 'add announcement' command.
|
5
|
+
# helpmaster: It won't be published if less than 11 messages published on the channel since last time this command was called.
|
5
6
|
# helpmaster: Only works if you are on Master channel and you are a master admin user
|
6
7
|
# helpmaster: The messages stored on a DM won't be published.
|
7
8
|
# helpmaster: This is very convenient to be called from a *Routine* for example every weekday at 09:00.
|
@@ -16,8 +17,9 @@ class SlackSmartBot
|
|
16
17
|
channels.select! {|i| i[/\.csv$/]}
|
17
18
|
channels.each do |channel|
|
18
19
|
channel.gsub!('.csv','')
|
19
|
-
unless channel[0]== 'D'
|
20
|
+
unless channel[0]== 'D' or (@announcements_activity_after.key?(channel) and @announcements_activity_after[channel] <= 10)
|
20
21
|
see_announcements(user, '', channel, false, true)
|
22
|
+
@announcements_activity_after[channel] = 0
|
21
23
|
sleep 0.5 # to avoid reach ratelimit
|
22
24
|
end
|
23
25
|
end
|
@@ -6,6 +6,7 @@ require_relative "commands/on_bot/ruby_code"
|
|
6
6
|
require_relative "commands/on_bot/repl"
|
7
7
|
require_relative "commands/on_bot/get_repl"
|
8
8
|
require_relative "commands/on_bot/run_repl"
|
9
|
+
require_relative "commands/on_bot/kill_repl"
|
9
10
|
require_relative "commands/on_bot/delete_repl"
|
10
11
|
require_relative "commands/on_bot/see_repls"
|
11
12
|
require_relative "commands/on_bot/general/whats_new"
|
@@ -59,7 +60,14 @@ require_relative "commands/general/see_access"
|
|
59
60
|
require_relative "commands/general/allow_access"
|
60
61
|
require_relative "commands/general/deny_access"
|
61
62
|
require_relative "commands/general/add_team"
|
63
|
+
require_relative "commands/general/add_memo_team"
|
64
|
+
require_relative "commands/general/set_memo_status"
|
65
|
+
require_relative "commands/general/delete_memo_team"
|
62
66
|
require_relative "commands/general/see_teams"
|
63
67
|
require_relative "commands/general/update_team"
|
64
68
|
require_relative "commands/general/ping_team"
|
65
69
|
require_relative "commands/general/delete_team"
|
70
|
+
require_relative "commands/general/add_vacation"
|
71
|
+
require_relative "commands/general/remove_vacation"
|
72
|
+
require_relative "commands/general/see_vacations"
|
73
|
+
require_relative "commands/general/see_vacations_team"
|
@@ -152,7 +152,9 @@ class SlackSmartBot
|
|
152
152
|
see_routines(dest, from, user, all)
|
153
153
|
when /\A\s*get\s+bot\s+logs?\s*$/i
|
154
154
|
get_bot_logs(dest, from, typem)
|
155
|
-
when /\A\s*send\s+message\s+(on|to|in)\s
|
155
|
+
when /\A\s*send\s+message\s+(on|to|in)\s+<(https?:[^:]+)>\s*:\s*(.+)\s*$/im,
|
156
|
+
/\A\s*send\s+message\s+(on|to|in)\s+(https?:[^:]+)\s*:\s*(.+)\s*$/im,
|
157
|
+
/\A\s*send\s+message\s+(on|to|in)\s*([^:]+)\s*:\s*(.+)\s*$/im
|
156
158
|
opts = $2
|
157
159
|
message = $3
|
158
160
|
thread_ts = ''
|
@@ -174,7 +176,7 @@ class SlackSmartBot
|
|
174
176
|
|
175
177
|
thread_ts.gsub!('.','')
|
176
178
|
send_message(dest, from, typem, to, thread_ts, message)
|
177
|
-
when /\A\s*delete\s+message\s+(.+)\s*$/i
|
179
|
+
when /\A\s*delete\s+message\s+(http.+)\s*$/i
|
178
180
|
url = $1
|
179
181
|
delete_message(from, typem, url)
|
180
182
|
when /\A\s*react\s+(on|to|in)\s*([^\s]+)\s+([p\d\.]+)\s+(.+)\s*$/i,
|
@@ -396,11 +398,15 @@ class SlackSmartBot
|
|
396
398
|
when /\A\s*get\s+(repl|irb|live)\s+([\w\-]+)\s*/i
|
397
399
|
session_name = $2
|
398
400
|
get_repl(dest, user, session_name)
|
399
|
-
when /\A\s*run\s+(repl|irb|live)\s+([\w\-]+)()\s
|
400
|
-
/^\s*run\s+(repl|irb|live)\s+([\w\-]+)\s+(.+)\s*$/
|
401
|
+
when /\A\s*run\s+(repl|irb|live)\s+([\w\-]+)()\s*\z/im,
|
402
|
+
/^\s*run\s+(repl|irb|live)\s+([\w\-]+)\s+(.+)\s*$/im
|
401
403
|
session_name = $2
|
402
|
-
|
403
|
-
|
404
|
+
if Thread.current[:command_orig].match(/\s*run\s+(repl|irb|live)\s+([\w\-]+)\s+(.+)\s*$/im)
|
405
|
+
opts = " #{$3}"
|
406
|
+
else
|
407
|
+
opts = ''
|
408
|
+
end
|
409
|
+
env_vars = opts.scan(/\s+[\w\-]+="[^"]+"/i) + opts.scan(/\s+[\w\-]+='[^']+'/i)
|
404
410
|
opts.scan(/\s+[\w\-]+=[^'"\s]+/i).flatten.each do |ev|
|
405
411
|
env_vars << ev.gsub('=',"='") + "'"
|
406
412
|
end
|
@@ -409,12 +415,16 @@ class SlackSmartBot
|
|
409
415
|
ev.lstrip!
|
410
416
|
env_vars[idx] = "ENV['#{ev}"
|
411
417
|
end
|
412
|
-
|
418
|
+
prerun = Thread.current[:command_orig].gsub('```', '`').scan(/\s+`(.+)`/m)
|
419
|
+
run_repl(dest, user, session_name, env_vars.flatten, prerun.flatten, rules_file)
|
413
420
|
when /\A\s*(delete|remove)\s+(repl|irb|live)\s+([\w\-]+)\s*$/i
|
414
421
|
repl_name = $3
|
415
422
|
delete_repl(dest, user, repl_name)
|
416
423
|
when /\A\s*see\s+(repls|repl|irb|irbs)\s*$/i
|
417
424
|
see_repls(dest, user, typem)
|
425
|
+
when /\A\s*(kill)\s+(repl|irb|live)\s+([\w]+)\s*$/i
|
426
|
+
repl_id = $3
|
427
|
+
kill_repl(dest, user, repl_id)
|
418
428
|
else
|
419
429
|
processed2 = false
|
420
430
|
end #of case
|
@@ -74,6 +74,12 @@ class SlackSmartBot
|
|
74
74
|
if !dest.nil? and config.on_master_bot and !data.text.nil? and data.text.match(/^ping from (.+)\s*$/) and data.user == config[:nick_id]
|
75
75
|
@pings << $1
|
76
76
|
end
|
77
|
+
if config.on_master_bot and @vacations_check != Date.today
|
78
|
+
@vacations_check = Date.today
|
79
|
+
t = Thread.new do
|
80
|
+
check_vacations(only_first_day: true)
|
81
|
+
end
|
82
|
+
end
|
77
83
|
typem = :dont_treat
|
78
84
|
if data.nil? or data.user.nil? or data.user.to_s==''
|
79
85
|
user_info = nil
|
@@ -186,7 +192,7 @@ class SlackSmartBot
|
|
186
192
|
end
|
187
193
|
end
|
188
194
|
load "#{config.path}/rules/general_commands.rb" if File.exist?("#{config.path}/rules/general_commands.rb") and @datetime_general_commands != File.mtime("#{config.path}/rules/general_commands.rb")
|
189
|
-
|
195
|
+
eval(File.new(config.path + config.rules_file).read) if !defined?(rules) and File.exist?(config.path+config.rules_file) and !config.rules_file.empty?
|
190
196
|
unless typem == :dont_treat or user_info.nil?
|
191
197
|
if (Time.now - @last_activity_check) > 60 * 30 #every 30 minutes
|
192
198
|
@last_activity_check = Time.now
|
@@ -350,6 +356,10 @@ class SlackSmartBot
|
|
350
356
|
end
|
351
357
|
end
|
352
358
|
end
|
359
|
+
unless data.nil? or data.channel.nil? or data.channel.empty?
|
360
|
+
@announcements_activity_after[data.channel] ||= 0
|
361
|
+
@announcements_activity_after[data.channel] += 1
|
362
|
+
end
|
353
363
|
rescue Exception => stack
|
354
364
|
@logger.fatal stack
|
355
365
|
end
|