slack-smart-bot 1.12.8 → 1.13.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 +85 -12
- data/lib/slack/smart-bot/comm/respond.rb +1 -0
- data/lib/slack/smart-bot/comm/update.rb +13 -0
- data/lib/slack/smart-bot/comm.rb +1 -0
- data/lib/slack/smart-bot/commands/general/add_team.rb +1 -0
- data/lib/slack/smart-bot/commands/general/add_vacation.rb +5 -0
- data/lib/slack/smart-bot/commands/general/allow_access.rb +1 -1
- data/lib/slack/smart-bot/commands/general/delete_team.rb +1 -0
- data/lib/slack/smart-bot/commands/general/deny_access.rb +1 -1
- data/lib/slack/smart-bot/commands/general/public_holidays.rb +144 -0
- data/lib/slack/smart-bot/commands/general/see_announcements.rb +2 -2
- data/lib/slack/smart-bot/commands/general/see_memos_team.rb +202 -0
- data/lib/slack/smart-bot/commands/general/see_teams.rb +3 -175
- data/lib/slack/smart-bot/commands/general/see_vacations.rb +41 -21
- data/lib/slack/smart-bot/commands/general/set_public_holidays.rb +21 -0
- data/lib/slack/smart-bot/commands/general/update_team.rb +1 -0
- data/lib/slack/smart-bot/commands/general_bot_commands.rb +100 -8
- data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +27 -3
- data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +12 -8
- data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +33 -4
- data/lib/slack/smart-bot/commands/on_bot/admin/start_routine.rb +22 -1
- data/lib/slack/smart-bot/commands/on_bot/admin_master/send_message.rb +50 -4
- data/lib/slack/smart-bot/commands/on_bot/admin_master/update_message.rb +25 -0
- data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +8 -6
- data/lib/slack/smart-bot/commands/on_bot/repl.rb +55 -15
- data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +2 -1
- data/lib/slack/smart-bot/commands.rb +4 -0
- data/lib/slack/smart-bot/listen.rb +1 -1
- data/lib/slack/smart-bot/process.rb +36 -6
- data/lib/slack/smart-bot/process_first.rb +250 -188
- data/lib/slack/smart-bot/treat_message.rb +1 -1
- data/lib/slack/smart-bot/utils/build_help.rb +1 -1
- data/lib/slack/smart-bot/utils/create_routine_thread.rb +40 -4
- data/lib/slack/smart-bot/utils/decrypt.rb +15 -0
- data/lib/slack/smart-bot/utils/display_calendar.rb +86 -0
- data/lib/slack/smart-bot/utils/encrypt.rb +15 -0
- data/lib/slack/smart-bot/utils/encryption_get_key_iv.rb +29 -0
- data/lib/slack/smart-bot/utils/get_help.rb +1 -1
- data/lib/slack/smart-bot/utils/get_team_members.rb +39 -0
- data/lib/slack/smart-bot/utils/get_teams.rb +22 -16
- data/lib/slack/smart-bot/utils/get_vacations.rb +20 -15
- data/lib/slack/smart-bot/utils/save_stats.rb +2 -2
- data/lib/slack/smart-bot/utils/update_teams.rb +15 -9
- data/lib/slack/smart-bot/utils/update_vacations.rb +5 -3
- data/lib/slack/smart-bot/utils.rb +5 -0
- data/lib/slack-smart-bot.rb +9 -0
- data/lib/slack-smart-bot_general_commands.rb +33 -0
- data/lib/slack-smart-bot_general_rules.rb +2 -2
- data/whats_new.txt +15 -17
- metadata +27 -11
@@ -11,8 +11,6 @@ def general_bot_commands(user, command, dest, files = [])
|
|
11
11
|
display_name = user.profile.display_name
|
12
12
|
end
|
13
13
|
case command
|
14
|
-
|
15
|
-
|
16
14
|
# help: ----------------------------------------------
|
17
15
|
# help: `bot help`
|
18
16
|
# help: `bot help COMMAND`
|
@@ -29,6 +27,35 @@ def general_bot_commands(user, command, dest, files = [])
|
|
29
27
|
# help: command_id: :bot_help
|
30
28
|
# help:
|
31
29
|
|
30
|
+
# help: ----------------------------------------------
|
31
|
+
# help: `for NUMBER times every NUMBER minutes COMMAND`
|
32
|
+
# help: `for NUMBER times every NUMBER seconds COMMAND`
|
33
|
+
# help: `NUMBER times every NUMBER minutes COMMAND`
|
34
|
+
# help: `NUMBER times every NUMBER seconds COMMAND`
|
35
|
+
# help: It will run the command every NUMBER minutes or seconds for NUMBER times.
|
36
|
+
# help: max 24 times. min every 10 seconds. max every 60 minutes.
|
37
|
+
# help: Call `quit loop LOOP_ID` to stop the loop.
|
38
|
+
# help: aliases for minutes: m, minute, minutes
|
39
|
+
# help: aliases for seconds: s, sc, second, seconds
|
40
|
+
# help: Examples:
|
41
|
+
# help: _for 5 times every 1 minute ^ruby puts Time.now_
|
42
|
+
# help: _10 times every 30s !ruby puts Time.now_
|
43
|
+
# help: _24 times every 60m !get sales today_
|
44
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#loops|more info>
|
45
|
+
# help: command_id: :create_loop
|
46
|
+
# help:
|
47
|
+
|
48
|
+
# help: ----------------------------------------------
|
49
|
+
# help: `quit loop LOOP_ID`
|
50
|
+
# help: It will stop the loop with the id LOOP_ID.
|
51
|
+
# help: Only the user who created the loop or an admin can stop it.
|
52
|
+
# help: aliases for loop: iterator, iteration
|
53
|
+
# help: aliases for quit: stop, exit, kill
|
54
|
+
# help: Examples:
|
55
|
+
# help: _quit loop 1_
|
56
|
+
# help: _stop iterator 12_
|
57
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#loops|more info>
|
58
|
+
# help: command_id: :quit_loop
|
32
59
|
|
33
60
|
# help: ----------------------------------------------
|
34
61
|
# help: `Hi Bot`
|
@@ -575,6 +602,27 @@ def general_bot_commands(user, command, dest, files = [])
|
|
575
602
|
name = $2.downcase
|
576
603
|
delete_team(user, name)
|
577
604
|
|
605
|
+
# help: ----------------------------------------------
|
606
|
+
# help: `see MEMO_TYPE from TEAM_NAME team`
|
607
|
+
# help: `see MEMO_TYPE from TEAM_NAME team TOPIC`
|
608
|
+
# help: `see all memos from TEAM_NAME team`
|
609
|
+
# help: `see all memos from TEAM_NAME team TOPIC`
|
610
|
+
# help: It will show the memos of the team.
|
611
|
+
# help: If TOPIC is supplied it will show the memos of the topic.
|
612
|
+
# help: MEMO_TYPE: memos, notes, issues, tasks, features, bugs, jira, github. In case of 'all memos' will display all of any type.
|
613
|
+
# help: Examples:
|
614
|
+
# help: _see memos from sales team_
|
615
|
+
# help: _see bugs from sales team_
|
616
|
+
# help: _see all memos from sales team webdev_
|
617
|
+
# help: <https://github.com/MarioRuiz/slack-smart-bot#teams|more info>
|
618
|
+
# help: command_id: :see_memos_team
|
619
|
+
# help:
|
620
|
+
when /\A\s*see\s+(memo|note|issue|task|feature|bug|jira|github|all\s+memo)s?\s+(from\s+)?([\w\-]+)\s+team(.*)\s*\z/i,
|
621
|
+
/\A\s*see\s+(memo|note|issue|task|feature|bug|jira|github|all\s+memo)s?\s+(from\s+)?team\s+([\w\-]+)(.*)\s*\z/i
|
622
|
+
type = $1.downcase.to_sym
|
623
|
+
name = $3.downcase
|
624
|
+
topic = $4.strip
|
625
|
+
see_memos_team(user, type: type, name: name, topic: topic)
|
578
626
|
|
579
627
|
# help: ----------------------------------------------
|
580
628
|
# help: `add vacation from YYYY/MM/DD to YYYY/MM/DD`
|
@@ -631,16 +679,19 @@ def general_bot_commands(user, command, dest, files = [])
|
|
631
679
|
# help: `see my vacations`
|
632
680
|
# help: `see my time off`
|
633
681
|
# help: `see vacations @USER`
|
682
|
+
# help: `see my vacations YEAR`
|
634
683
|
# help: It will display current and past time off.
|
684
|
+
# help: If you call this command on a DM, it will show your vacations for the year on a calendar.
|
635
685
|
# help: <https://github.com/MarioRuiz/slack-smart-bot#time-off-management|more info>
|
636
686
|
# help: command_id: :see_vacations
|
637
687
|
# help:
|
638
|
-
when /\A\s*see\s+my\s+vacations\s*()\z/i,
|
639
|
-
/\A\s*see\s+my\s+time\s+off\s*()\z/i,
|
640
|
-
/\A\s*see\s+time\s+off\s+<@(\w+)>\s*\z/i,
|
641
|
-
/\A\s*see\s+vacations\s+<@(\w+)>\s*\z/i
|
688
|
+
when /\A\s*see\s+my\s+vacations\s*()\s*(\d{4})?\s*\z/i,
|
689
|
+
/\A\s*see\s+my\s+time\s+off\s*()\s*(\d{4})?\s*\z/i,
|
690
|
+
/\A\s*see\s+time\s+off\s+<@(\w+)>\s*\s*(\d{4})?\s*\z/i,
|
691
|
+
/\A\s*see\s+vacations\s+<@(\w+)>\s*(\d{4})?\s*\z/i
|
642
692
|
from_user = $1
|
643
|
-
|
693
|
+
year = $2
|
694
|
+
see_vacations(user, dest, from_user: from_user, year: year)
|
644
695
|
|
645
696
|
# help: ----------------------------------------------
|
646
697
|
# help: `vacations team NAME`
|
@@ -659,7 +710,48 @@ def general_bot_commands(user, command, dest, files = [])
|
|
659
710
|
date = $4.to_s
|
660
711
|
date = Date.today.strftime("%Y/%m/%d") if date.empty?
|
661
712
|
see_vacations_team(user, team_name, date)
|
662
|
-
|
713
|
+
|
714
|
+
|
715
|
+
# help: ----------------------------------------------
|
716
|
+
# help: `public holidays COUNTRY`
|
717
|
+
# help: `public holidays COUNTRY/STATE DATE`
|
718
|
+
# help: STATE: optional. If not specified, it will return all the holidays for the country.
|
719
|
+
# help: DATE: optional. It can be supplied as YYYY or YYYY-MM or YYYY-MM-DD. If not specified, it will return all the holidays for current year.
|
720
|
+
# help: Examples:
|
721
|
+
# help: _public holidays United States_
|
722
|
+
# help: _public holidays United States/California_
|
723
|
+
# help: _public holidays United States/California 2023_
|
724
|
+
# help: _public holidays Iceland 2023-12_
|
725
|
+
# help: _public holidays India 2023-12-25_
|
726
|
+
# help: command_id: :public_holidays
|
727
|
+
# help:
|
728
|
+
when /\A\s*public\s+(holiday?|vacation)s?\s+(in\s+|on\s+)?([a-zA-Z\s]+)()()()()\s*\z/i,
|
729
|
+
/\A\s*public\s+(holiday?|vacation)s?\s+(in\s+|on\s+)?([a-zA-Z\s]+)\/([a-zA-Z\s]+)()()()\s*\z/i,
|
730
|
+
/\A\s*public\s+(holiday?|vacation)s?\s+(in\s+|on\s+)?([a-zA-Z\s]+)\/([a-zA-Z\s]+)\s+(\d{4})[\/\-]?(\d\d)?[\/\-]?(\d\d)?\s*\z/i,
|
731
|
+
/\A\s*public\s+(holiday?|vacation)s?\s+(in\s+|on\s+)?([a-zA-Z\s]+)()\s+(\d{4})[\/\-]?(\d\d)?[\/\-]?(\d\d)?\s*\z/i
|
732
|
+
country = $3
|
733
|
+
state = $4.to_s
|
734
|
+
year = $5.to_s
|
735
|
+
month = $6.to_s
|
736
|
+
day = $7.to_s
|
737
|
+
year = Date.today.year if year.to_s == ''
|
738
|
+
public_holidays(country, state, year, month, day)
|
739
|
+
|
740
|
+
# help: ----------------------------------------------
|
741
|
+
# help: `set public holidays to COUNTRY/STATE`
|
742
|
+
# help: It will set the public holidays for the country and state specified.
|
743
|
+
# help: If STATE is not specified, it will set the public holidays for the country.
|
744
|
+
# help: Examples:
|
745
|
+
# help: _set public holidays to Iceland_
|
746
|
+
# help: _set public holidays to United States/California_
|
747
|
+
# help: command_id: :set_public_holidays
|
748
|
+
# help:
|
749
|
+
when /\A\s*set\s+public\s+(holiday?|vacation)s?\s+to\s+([^\/]+)\/([^\/]+)\s*\z/i,
|
750
|
+
/\A\s*set\s+public\s+(holiday?|vacation)s?\s+to\s+([^\/]+)\s*\z/i
|
751
|
+
country = $2
|
752
|
+
state = $3.to_s
|
753
|
+
set_public_holidays(country, state, user)
|
754
|
+
|
663
755
|
else
|
664
756
|
return false
|
665
757
|
end
|
@@ -11,6 +11,7 @@ class SlackSmartBot
|
|
11
11
|
# helpadmin: `add routine NAME at TIME #CHANNEL COMMAND`
|
12
12
|
# helpadmin: `add routine NAME on DAYWEEK at TIME COMMAND`
|
13
13
|
# helpadmin: `add routine NAME on DAYWEEK at TIME #CHANNEL COMMAND`
|
14
|
+
# helpadmin: `add routine NAME on the DAY_OF_MONTH at TIME COMMAND`
|
14
15
|
# helpadmin: `add routine NAME at TIME`
|
15
16
|
# helpadmin: `add silent routine NAME at TIME`
|
16
17
|
# helpadmin: `create routine NAME at TIME`
|
@@ -34,6 +35,7 @@ class SlackSmartBot
|
|
34
35
|
# helpadmin: _add bgroutine example on Mondays at 05:00 !run customer tests_
|
35
36
|
# helpadmin: _add routine example on Tuesdays at 09:00 #SREChannel !run db cleanup_
|
36
37
|
# helpadmin: _add routine example on weekdays at 22:00 suggest command_
|
38
|
+
# helpadmin: _add routine example on the 5th at 22:00 suggest command_
|
37
39
|
# helpadmin: <https://github.com/MarioRuiz/slack-smart-bot#routines|more info>
|
38
40
|
# helpadmin: command_id: :add_routine
|
39
41
|
# helpadmin:
|
@@ -53,6 +55,7 @@ class SlackSmartBot
|
|
53
55
|
every = ""
|
54
56
|
at = ""
|
55
57
|
dayweek = ''
|
58
|
+
daymonth = ''
|
56
59
|
next_run = Time.now
|
57
60
|
case period.downcase
|
58
61
|
when "days", "d"
|
@@ -68,7 +71,28 @@ class SlackSmartBot
|
|
68
71
|
every = "#{number_time} seconds"
|
69
72
|
every_in_seconds = number_time.to_i
|
70
73
|
else # time
|
71
|
-
if type != 'at' and type
|
74
|
+
if type != 'at' and type.match?(/^\d+$/) # day of month
|
75
|
+
day = type.to_i
|
76
|
+
daymonth = type
|
77
|
+
if day > 31
|
78
|
+
respond "Wrong day of month specified: *#{day}*", dest
|
79
|
+
return
|
80
|
+
end
|
81
|
+
if Date.today.day > day
|
82
|
+
next_month = Date.new(Date.today.year, Date.today.month, 1) >> 1
|
83
|
+
else
|
84
|
+
next_month = Date.new(Date.today.year, Date.today.month, 1)
|
85
|
+
end
|
86
|
+
next_month_last_day = Date.new(next_month.year, next_month.month, -1)
|
87
|
+
if day > next_month_last_day.day
|
88
|
+
next_time = Date.new(next_month.year, next_month.month, next_month_last_day.day)
|
89
|
+
else
|
90
|
+
next_time = Date.new(next_month.year, next_month.month, day)
|
91
|
+
end
|
92
|
+
days = (next_time - Date.today).to_i
|
93
|
+
every_in_seconds = days * 24 * 60 * 60 # one day
|
94
|
+
|
95
|
+
elsif type != 'at' and type!='weekday' and type!='weekend'
|
72
96
|
dayweek = type.downcase
|
73
97
|
|
74
98
|
days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']
|
@@ -133,11 +157,11 @@ class SlackSmartBot
|
|
133
157
|
channel_id = dest if channel_id.to_s == ''
|
134
158
|
@routines[@channel_id] = {} unless @routines.key?(@channel_id)
|
135
159
|
@routines[@channel_id][name] = { channel_name: config.channel, creator: from, creator_id: user.id, status: :on,
|
136
|
-
every: every, every_in_seconds: every_in_seconds, at: at, dayweek: dayweek, file_path: file_path,
|
160
|
+
every: every, every_in_seconds: every_in_seconds, at: at, dayweek: dayweek, daymonth: daymonth, file_path: file_path,
|
137
161
|
command: command_to_run.to_s.strip, silent: silent,
|
138
162
|
next_run: next_run.to_s, dest: channel_id, last_run: "", last_elapsed: "",
|
139
163
|
running: false, routine_type: routine_type}
|
140
|
-
update_routines
|
164
|
+
update_routines()
|
141
165
|
respond "Added routine *`#{name}`* to the channel", dest
|
142
166
|
create_routine_thread(name, @routines[@channel_id][name])
|
143
167
|
end
|
@@ -38,15 +38,19 @@ class SlackSmartBot
|
|
38
38
|
respond "routine *`#{name}`*: #{stdout} #{stderr}", @routines[@channel_id][name][:dest]
|
39
39
|
end
|
40
40
|
else #command
|
41
|
-
respond "routine *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest]
|
41
|
+
message = respond "routine *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest], return_message: true
|
42
42
|
started = Time.now
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
43
|
+
data = { channel: @routines[@channel_id][name][:dest],
|
44
|
+
user: @routines[@channel_id][name][:creator_id],
|
45
|
+
text: @routines[@channel_id][name][:command],
|
46
|
+
files: nil,
|
47
|
+
routine_name: name,
|
48
|
+
routine_type: @routines[@channel_id][name][:routine_type],
|
49
|
+
routine: true }
|
50
|
+
if @routines[@channel_id][name][:command].match?(/^!!/) or @routines[@channel_id][name][:command].match?(/^\^/)
|
51
|
+
data[:ts] = message.ts
|
52
|
+
end
|
53
|
+
treat_message(data)
|
50
54
|
end
|
51
55
|
@routines[@channel_id][name][:last_elapsed] = (Time.now - started)
|
52
56
|
@routines[@channel_id][name][:last_run] = started.to_s
|
@@ -1,14 +1,17 @@
|
|
1
1
|
class SlackSmartBot
|
2
2
|
# helpadmin: ----------------------------------------------
|
3
3
|
# helpadmin: `see routines`
|
4
|
+
# helpadmin: `see routines HEADER /REGEXP/`
|
4
5
|
# helpadmin: `see all routines`
|
6
|
+
# helpadmin: `see all routines HEADER /REGEXP/`
|
5
7
|
# helpadmin: It will show the routines of the channel
|
6
|
-
# helpadmin: In case of
|
8
|
+
# helpadmin: In case of 'all' and on the master channel, it will show all the routines from all channels
|
9
|
+
# helpadmin: If you use HEADER it will show only the routines that match the REGEXP on the header. Available headers: name, creator, status, next_run, last_run, command
|
7
10
|
# helpadmin: You can use this command only if you are an admin user
|
8
11
|
# helpadmin: <https://github.com/MarioRuiz/slack-smart-bot#routines|more info>
|
9
12
|
# helpadmin: command_id: :see_routines
|
10
13
|
# helpadmin:
|
11
|
-
def see_routines(dest, from, user, all)
|
14
|
+
def see_routines(dest, from, user, all, header, regexp)
|
12
15
|
save_stats(__method__)
|
13
16
|
if is_admin?
|
14
17
|
if all
|
@@ -33,11 +36,37 @@ class SlackSmartBot
|
|
33
36
|
end
|
34
37
|
end
|
35
38
|
|
39
|
+
if header != ''
|
40
|
+
routines_filtered = {}
|
41
|
+
|
42
|
+
routines.each do |ch, rout_ch|
|
43
|
+
routines_filtered[ch] = rout_ch.dup
|
44
|
+
rout_ch.each do |k, v|
|
45
|
+
if header == 'name'
|
46
|
+
if k.match(/#{regexp}/i).nil?
|
47
|
+
routines_filtered[ch].delete(k)
|
48
|
+
end
|
49
|
+
elsif v[header.to_sym].to_s.match(/#{regexp}/i).nil?
|
50
|
+
routines_filtered[ch].delete(k)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
routines = routines_filtered
|
55
|
+
end
|
56
|
+
|
36
57
|
if routines.get_values(:channel_name).size == 0
|
37
|
-
|
58
|
+
if header != ''
|
59
|
+
respond "There are no routines added that match the header *#{header}* and the regexp *#{regexp}*.", dest
|
60
|
+
else
|
61
|
+
respond "There are no routines added.", dest
|
62
|
+
end
|
38
63
|
else
|
39
64
|
routines.each do |ch, rout_ch|
|
40
|
-
|
65
|
+
if header != ''
|
66
|
+
respond "Routines on channel *#{rout_ch.get_values(:channel_name).values.flatten.uniq[0]}* that match the header *#{header}* and the regexp *#{regexp}*", dest
|
67
|
+
else
|
68
|
+
respond "Routines on channel *#{rout_ch.get_values(:channel_name).values.flatten.uniq[0]}*", dest
|
69
|
+
end
|
41
70
|
rout_ch.each do |k, v|
|
42
71
|
msg = []
|
43
72
|
if v[:dest][0] == 'D'
|
@@ -18,7 +18,28 @@ class SlackSmartBot
|
|
18
18
|
respond "It's only possible to start routines from MASTER channel from a direct message with the bot.", dest
|
19
19
|
elsif @routines.key?(@channel_id) and @routines[@channel_id].key?(name)
|
20
20
|
@routines[@channel_id][name][:status] = :on
|
21
|
-
if @routines[@channel_id][name][:
|
21
|
+
if @routines[@channel_id][name].key?(:daymonth) and @routines[@channel_id][name][:daymonth] != ''
|
22
|
+
started = Time.now
|
23
|
+
daymonth = @routines[@channel_id][name][:daymonth]
|
24
|
+
day = daymonth.to_i
|
25
|
+
nt = @routines[@channel_id][name][:at].split(":")
|
26
|
+
if Time.now > Time.new(Time.now.year, Time.now.month, day, nt[0], nt[1], nt[2])
|
27
|
+
next_month = Date.new(Date.today.year, Date.today.month, 1) >> 1
|
28
|
+
else
|
29
|
+
next_month = Date.new(Date.today.year, Date.today.month, 1)
|
30
|
+
end
|
31
|
+
next_month_last_day = Date.new(next_month.year, next_month.month, -1)
|
32
|
+
if day > next_month_last_day.day
|
33
|
+
next_time = Date.new(next_month.year, next_month.month, next_month_last_day.day)
|
34
|
+
else
|
35
|
+
next_time = Date.new(next_month.year, next_month.month, day)
|
36
|
+
end
|
37
|
+
days = (next_time - Date.today).to_i
|
38
|
+
next_run = started + (days * 24 * 60 * 60) # one more day/week
|
39
|
+
next_run = Time.new(next_run.year, next_run.month, next_run.day, nt[0], nt[1], nt[2])
|
40
|
+
@routines[@channel_id][name][:next_run] = next_run.to_s
|
41
|
+
@routines[@channel_id][name][:sleeping] = (next_run - started).ceil
|
42
|
+
elsif @routines[@channel_id][name][:at]!=''
|
22
43
|
started = Time.now
|
23
44
|
if started.strftime("%H:%M:%S") < @routines[@channel_id][name][:at]
|
24
45
|
nt = @routines[@channel_id][name][:at].split(":")
|
@@ -7,23 +7,69 @@ class SlackSmartBot
|
|
7
7
|
# helpadmin: `send message to URL : MESSAGE`
|
8
8
|
# helpadmin: `send message to @USER1 @USER99 : MESSAGE`
|
9
9
|
# helpadmin: `send message to #CHANNEL1 #CHANNEL99 : MESSAGE`
|
10
|
+
# helpadmin: `send message to users from YYYY/MM/DD to YYYY/MM/DD #CHANNEL COMMAND_ID: MESSAGE`
|
10
11
|
# helpadmin: It will send the specified message as SmartBot
|
11
12
|
# helpadmin: You can use this command only if you are a Master admin user and if you are in a private conversation with the bot
|
13
|
+
# helpadmin: In case from and to specified will send a DM to all users that have been using the SmartBot according to the SmartBot Stats. One message every 5sc. #CHANNEL and COMMAND_ID are optional filters.
|
12
14
|
# helpadmin: command_id: :send_message
|
13
15
|
# helpadmin:
|
14
|
-
def send_message(dest, from, typem, to, thread_ts, message)
|
16
|
+
def send_message(dest, from, typem, to, thread_ts, stats_from, stats_to, stats_channel_filter, stats_command_filter, message)
|
15
17
|
save_stats(__method__)
|
16
18
|
if config.masters.include?(from) and typem==:on_dm #master admin user
|
19
|
+
react :runner
|
17
20
|
unless Thread.current[:command_orig].to_s == ''
|
18
21
|
message_orig = Thread.current[:command_orig].to_s.gsub("\u00A0", " ").scan(/[^:]+\s*:\s+(.+)/im).join
|
19
22
|
message = message_orig unless message_orig == ''
|
20
23
|
end
|
21
24
|
succ = true
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
+
if stats_from!='' and stats_to!=''
|
26
|
+
users = []
|
27
|
+
user_ids = []
|
28
|
+
stats_from.gsub!('/', '-')
|
29
|
+
stats_to.gsub!('/', '-')
|
30
|
+
stats_from += " 00:00:00 +0000"
|
31
|
+
stats_to += " 23:59:59 +0000"
|
32
|
+
Dir["#{config.stats_path}.*.log"].sort.each do |file|
|
33
|
+
if file >= "#{config.stats_path}.#{stats_from[0..6]}.log" and file <= "#{config.stats_path}.#{stats_to[0..6]}.log"
|
34
|
+
CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
|
35
|
+
if row[:date] >= stats_from and row[:date] <= stats_to and !users.include?(row[:user_name])
|
36
|
+
if (stats_channel_filter=='' and stats_command_filter=='') or
|
37
|
+
(stats_channel_filter!='' and stats_command_filter=='' and (row[:bot_channel_id]==stats_channel_filter or row[:dest_channel_id]==stats_channel_filter)) or
|
38
|
+
(stats_command_filter!='' and stats_channel_filter=='' and row[:command]==stats_command_filter) or
|
39
|
+
(stats_channel_filter!='' and stats_command_filter!='' and ((row[:bot_channel_id]==stats_channel_filter or row[:dest_channel_id]==stats_channel_filter) and row[:command]==stats_command_filter))
|
40
|
+
|
41
|
+
user_ids << row[:user_id]
|
42
|
+
users << row[:user_name]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
users_success = []
|
50
|
+
users_failed = []
|
51
|
+
|
52
|
+
user_ids.each do |u|
|
53
|
+
succ = (respond message, u, thread_ts: thread_ts, web_client: true)
|
54
|
+
if succ
|
55
|
+
users_success << u
|
56
|
+
else
|
57
|
+
users_failed << u
|
58
|
+
end
|
59
|
+
sleep 5
|
60
|
+
end
|
61
|
+
respond "Users that received the message (#{users_success.size}): <@#{users_success.join('>, <@')}>", dest if users_success.size > 0
|
62
|
+
respond "Users that didn't receive the message (#{users_failed.size}): <@#{users_failed.join('>, <@')}>", dest if users_failed.size > 0
|
63
|
+
respond "No users selected to send the message.", dest if users_success.size == 0 and users_failed.size == 0
|
64
|
+
succ = false if users_failed.size > 0
|
65
|
+
else
|
66
|
+
to.each do |t|
|
67
|
+
unless t.match?(/^\s*$/)
|
68
|
+
succ = (respond message, t, thread_ts: thread_ts, web_client: true) && succ
|
69
|
+
end
|
25
70
|
end
|
26
71
|
end
|
72
|
+
unreact :runner
|
27
73
|
if succ
|
28
74
|
react :heavy_check_mark
|
29
75
|
else
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
|
3
|
+
# helpadmin: ----------------------------------------------
|
4
|
+
# helpadmin: `update message URL TEXT`
|
5
|
+
# helpadmin: It will update the SmartBot message supplied
|
6
|
+
# helpadmin: You can use this command only if you are a Master admin user and if you are in a private conversation with the bot
|
7
|
+
# helpadmin: command_id: :update_message
|
8
|
+
# helpadmin:
|
9
|
+
def update_message(from, typem, url, text)
|
10
|
+
save_stats(__method__)
|
11
|
+
channel, ts = url.scan(/\/archives\/(\w+)\/(\w\d+)/)[0]
|
12
|
+
if config.masters.include?(from) and typem==:on_dm and !channel.nil? #master admin user
|
13
|
+
ts = "#{ts[0..-7]}.#{ts[-6..-1]}"
|
14
|
+
succ = update(channel, ts, text)
|
15
|
+
if succ
|
16
|
+
react :heavy_check_mark
|
17
|
+
else
|
18
|
+
react :x
|
19
|
+
end
|
20
|
+
else
|
21
|
+
respond "Only master admin users on a private conversation with the SmartBot can update SmartBot messages"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -39,7 +39,7 @@ class SlackSmartBot
|
|
39
39
|
# help: _bot stats #sales today_
|
40
40
|
# help: _bot stats #sales from 2020-01-01 monthly_
|
41
41
|
# help: _bot stats exclude routines masters from 2021/01/01 monthly_
|
42
|
-
# help: _bot stats members #development from 2022/01/01 to 2022/01/31_
|
42
|
+
# help: _bot stats members #development from 2022/01/01 to 2022/01/31_
|
43
43
|
# help: _bot stats type_message /(on_pub|on_pg)/_
|
44
44
|
# help: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
|
45
45
|
# help: command_id: :bot_stats
|
@@ -60,7 +60,7 @@ class SlackSmartBot
|
|
60
60
|
user = "" # for the case we are on the stats channel
|
61
61
|
end
|
62
62
|
if (from_user.id != user and
|
63
|
-
(config.masters.include?(from_user.name) or master_admin_users_id.include?(from_user.id) or dest == @channels_id[config.stats_channel]) and
|
63
|
+
(config.masters.include?(from_user.name) or master_admin_users_id.include?(from_user.id) or dest == @channels_id[config.stats_channel]) and
|
64
64
|
(typem == :on_dm or dest[0] == "D" or dest == @channels_id[config.stats_channel]))
|
65
65
|
on_dm_master = true #master admin user
|
66
66
|
else
|
@@ -202,7 +202,7 @@ class SlackSmartBot
|
|
202
202
|
unless header.empty?
|
203
203
|
add = true
|
204
204
|
header.each_with_index do |h, i|
|
205
|
-
if row[h.downcase.to_sym].to_s.match?(
|
205
|
+
if !row[h.downcase.to_sym].to_s.match?(/#{regexp[i]}/i)
|
206
206
|
add = false
|
207
207
|
break
|
208
208
|
end
|
@@ -297,10 +297,11 @@ class SlackSmartBot
|
|
297
297
|
message_new_users = "(#{new_users.size * 100 / users_month[k].uniq.size}%)"
|
298
298
|
end
|
299
299
|
all_users += users_month[k]
|
300
|
+
graph = ":large_yellow_square: " * (v.to_f * (10*rows_month.size) / total).round(2)
|
300
301
|
if on_dm_master
|
301
|
-
message << "\t#{k}: #{v} (#{(v.to_f * 100 / total).round(2)}%) / #{commands_month[k].uniq.size} / #{users_month[k].uniq.size} #{message_new_users}"
|
302
|
+
message << "\t#{k}: #{graph} #{v} (#{(v.to_f * 100 / total).round(2)}%) / #{commands_month[k].uniq.size} / #{users_month[k].uniq.size} #{message_new_users}"
|
302
303
|
else
|
303
|
-
message << "\t#{k}: #{v} (#{(v.to_f * 100 / total).round(2)}%) / #{commands_month[k].uniq.size}"
|
304
|
+
message << "\t#{k}: #{graph} #{v} (#{(v.to_f * 100 / total).round(2)}%) / #{commands_month[k].uniq.size}"
|
304
305
|
end
|
305
306
|
end
|
306
307
|
end
|
@@ -450,7 +451,8 @@ class SlackSmartBot
|
|
450
451
|
total_known = 0
|
451
452
|
tzone_users.each do |tzone, num|
|
452
453
|
unless tzone.to_s == ""
|
453
|
-
|
454
|
+
abb_tzone = tzone.split.map{|i| i[0,1].upcase}.join
|
455
|
+
message << "\t#{abb_tzone} _#{tzone}_: #{num} (#{(num.to_f * 100 / total_without_routines).round(2)}%)"
|
454
456
|
total_known += num
|
455
457
|
end
|
456
458
|
end
|
@@ -24,6 +24,7 @@ class SlackSmartBot
|
|
24
24
|
# help: To pre-execute some ruby when starting the session add the code to .smart-bot-repl file on the project root folder defined on project_folder
|
25
25
|
# help: If you want to see the methods of a class or module you created use _ls TheModuleOrClass_
|
26
26
|
# help: You can supply the Environmental Variables you need for the Session
|
27
|
+
# help: You can add collaborators by sending _add collaborator @USER_ to the session.
|
27
28
|
# help: Examples:
|
28
29
|
# help: _repl CreateCustomer LOCATION=spain HOST='https://10.30.40.50:8887'_
|
29
30
|
# help: _repl CreateCustomer: "It creates a random customer for testing" LOCATION=spain HOST='https://10.30.40.50:8887'_
|
@@ -61,7 +62,10 @@ class SlackSmartBot
|
|
61
62
|
finished: Time.now,
|
62
63
|
input: [],
|
63
64
|
on_thread: Thread.current[:on_thread],
|
64
|
-
thread_ts: Thread.current[:thread_ts]
|
65
|
+
thread_ts: Thread.current[:thread_ts],
|
66
|
+
collaborators: [],
|
67
|
+
user_type: :creator,
|
68
|
+
user_creator: from
|
65
69
|
}
|
66
70
|
|
67
71
|
unless temp_repl
|
@@ -90,6 +94,7 @@ class SlackSmartBot
|
|
90
94
|
|
91
95
|
message = "Session name: *#{session_name}*
|
92
96
|
From now on I will execute all you write as a Ruby command and I will keep the session open until you send `quit` or `bye` or `exit`.
|
97
|
+
In case you need someone to help you with the session you can add collaborators by sending `add collaborator @USER` to the session.
|
93
98
|
I will respond with the result so it is not necessary you send `print`, `puts`, `p` or `pp` unless you want it as the output when calling `run repl`.
|
94
99
|
Use `p` to print a message raw, exacly like it is returned.
|
95
100
|
If you want to avoid a message to be treated by me, start the message with '-'.
|
@@ -239,6 +244,9 @@ class SlackSmartBot
|
|
239
244
|
rescue
|
240
245
|
end
|
241
246
|
end
|
247
|
+
@repl_sessions[from][:collaborators].each do |collaborator|
|
248
|
+
@repl_sessions.delete(collaborator)
|
249
|
+
end
|
242
250
|
@repl_sessions.delete(from)
|
243
251
|
break
|
244
252
|
end
|
@@ -280,24 +288,56 @@ class SlackSmartBot
|
|
280
288
|
code.match?(/=?\s*(require|load)(\(|\s)/i)
|
281
289
|
|
282
290
|
respond "Sorry I cannot run this due security reasons", dest
|
291
|
+
elsif code.match(/\A\s*add\s+collaborator\s+<@(\w+)>\s*\z/i)
|
292
|
+
collaborator = $1
|
293
|
+
user_info = @users.select{|u| u.id == collaborator or (u.key?(:enterprise_user) and u.enterprise_user.id == collaborator)}[-1]
|
294
|
+
collaborator_name = user_info.name
|
295
|
+
if @repl_sessions.key?(collaborator_name)
|
296
|
+
respond "Sorry, <@#{collaborator}> is already in a repl. Please ask her/him to quit it first.", dest
|
297
|
+
else
|
298
|
+
respond "Collaborator added. Now <@#{collaborator}> can interact with this repl.", dest
|
299
|
+
creator = @repl_sessions[from][:user_creator]
|
300
|
+
@repl_sessions[creator][:collaborators] << collaborator_name
|
301
|
+
@repl_sessions[collaborator_name] = {
|
302
|
+
name: @repl_sessions[from][:name],
|
303
|
+
dest: dest,
|
304
|
+
on_thread: Thread.current[:on_thread],
|
305
|
+
thread_ts: Thread.current[:thread_ts],
|
306
|
+
user_type: :collaborator,
|
307
|
+
user_creator: creator
|
308
|
+
}
|
309
|
+
end
|
283
310
|
else
|
284
|
-
@repl_sessions[from][:
|
311
|
+
if @repl_sessions[from][:user_type] == :collaborator
|
312
|
+
@repl_sessions[@repl_sessions[from][:user_creator]][:input] << code
|
313
|
+
else
|
314
|
+
@repl_sessions[from][:input] << code
|
315
|
+
end
|
285
316
|
case code
|
286
|
-
when /^\s*(quit|exit|bye|bye
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
317
|
+
when /^\s*(quit|exit|bye|bye\s+bot)\s*$/i
|
318
|
+
if @repl_sessions[from][:user_type] == :collaborator
|
319
|
+
respond "Collaborator <@#{user.id}> removed.", dest
|
320
|
+
@repl_sessions[@repl_sessions[from][:user_creator]][:collaborators].delete(from)
|
321
|
+
@repl_sessions.delete(from)
|
322
|
+
else
|
323
|
+
open("#{config.path}/repl/#{@channel_id}/#{@repl_sessions[from][:name]}.input", 'a+') {|f|
|
324
|
+
f.puts code
|
325
|
+
}
|
326
|
+
respond "REPL session finished: #{@repl_sessions[from][:name]}", dest
|
327
|
+
unreact :running, @ts_react[@repl_sessions[from].name]
|
328
|
+
pids = `pgrep -P #{@repl_sessions[from][:pid]}`.split("\n").map(&:to_i) #todo: it needs to be adapted for Windows
|
329
|
+
pids.each do |pid|
|
330
|
+
begin
|
331
|
+
Process.kill("KILL", pid)
|
332
|
+
rescue
|
333
|
+
end
|
334
|
+
end
|
335
|
+
@repl_sessions[from][:collaborators].each do |collaborator|
|
336
|
+
@repl_sessions.delete(collaborator)
|
297
337
|
end
|
338
|
+
@repl_sessions.delete(from)
|
298
339
|
end
|
299
|
-
|
300
|
-
when /^\s*-/i
|
340
|
+
when /\A\s*-/i
|
301
341
|
#ommit
|
302
342
|
else
|
303
343
|
if @ts_repl[@repl_sessions[from].name].to_s == ''
|
@@ -28,7 +28,8 @@ class SlackSmartBot
|
|
28
28
|
respond "Running", dest if code.size > 200
|
29
29
|
|
30
30
|
begin
|
31
|
-
code
|
31
|
+
#todo: check. Commented next line because it was causing problems with the code, for the case the line was for example any of these chars: { } [ ] ( ) < > | & ; $ ` " ' \ * ? ~ # = ! ^ -
|
32
|
+
#code.gsub!(/^\W*$/, "") #to remove special chars from slack when copy/pasting
|
32
33
|
code.gsub!('$','\$') #to take $ as literal, fex: puts '$lolo' => puts '\$lolo'
|
33
34
|
ruby = "ruby -e \"#{code.gsub('"', '\"')}\""
|
34
35
|
if defined?(project_folder) and project_folder.to_s != "" and Dir.exist?(project_folder)
|
@@ -36,6 +36,7 @@ require_relative "commands/on_extended/bot_rules"
|
|
36
36
|
require_relative "commands/on_bot/admin_master/get_bot_logs"
|
37
37
|
require_relative "commands/on_bot/admin_master/send_message"
|
38
38
|
require_relative "commands/on_bot/admin_master/delete_message"
|
39
|
+
require_relative "commands/on_bot/admin_master/update_message"
|
39
40
|
require_relative "commands/on_bot/admin_master/react_to"
|
40
41
|
require_relative "commands/on_bot/general/bot_stats"
|
41
42
|
require_relative "commands/on_bot/general/leaderboard"
|
@@ -63,6 +64,7 @@ require_relative "commands/general/add_team"
|
|
63
64
|
require_relative "commands/general/add_memo_team"
|
64
65
|
require_relative "commands/general/set_memo_status"
|
65
66
|
require_relative "commands/general/delete_memo_team"
|
67
|
+
require_relative "commands/general/see_memos_team"
|
66
68
|
require_relative "commands/general/see_teams"
|
67
69
|
require_relative "commands/general/update_team"
|
68
70
|
require_relative "commands/general/ping_team"
|
@@ -71,3 +73,5 @@ require_relative "commands/general/add_vacation"
|
|
71
73
|
require_relative "commands/general/remove_vacation"
|
72
74
|
require_relative "commands/general/see_vacations"
|
73
75
|
require_relative "commands/general/see_vacations_team"
|
76
|
+
require_relative "commands/general/public_holidays"
|
77
|
+
require_relative "commands/general/set_public_holidays"
|
@@ -5,7 +5,7 @@ class SlackSmartBot
|
|
5
5
|
@last_activity_check = Time.now
|
6
6
|
get_bots_created()
|
7
7
|
@buffer_complete = [] unless defined?(@buffer_complete)
|
8
|
-
b = File.read("#{config.path}/buffer_complete.log")
|
8
|
+
b = File.read("#{config.path}/buffer_complete.log", encoding: "UTF-8")
|
9
9
|
result = b.scan(/^\|(\w+)\|(\w+)\|(\w+)\|([^~]+)~~~/m)
|
10
10
|
result.delete(nil)
|
11
11
|
new_messages = result[@buffer_complete.size..-1]
|