slack-smart-bot 1.12.9 → 1.13.1
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/event_hello.rb +3 -1
- 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 +6 -4
- 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 +45 -5
- 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
@@ -10,7 +10,7 @@ class SlackSmartBot
|
|
10
10
|
user_info = @users.select{|u| u.name == @routines[@channel_id][name][:creator]}[-1]
|
11
11
|
@routines[@channel_id][name][:creator_id] = user_info.id unless user_info.nil? or user_info.empty?
|
12
12
|
end
|
13
|
-
@logger.info "Routine: #{@routines[@channel_id][name].inspect}"
|
13
|
+
@logger.info "Routine #{name}: #{@routines[@channel_id][name].inspect}"
|
14
14
|
if @routines[@channel_id][name][:file_path].match?(/\.rb$/i)
|
15
15
|
ruby = "ruby "
|
16
16
|
else
|
@@ -62,11 +62,12 @@ class SlackSmartBot
|
|
62
62
|
File.write("#{config.path}/routines/#{@channel_id}/#{name}_output.txt", stdout.to_s+stderr.to_s, mode: "a+")
|
63
63
|
end
|
64
64
|
else #command
|
65
|
+
message = nil
|
65
66
|
if !@routines[@channel_id][name][:silent] and !config.on_maintenance
|
66
67
|
if @routines[@channel_id][name][:dest]!=@channel_id
|
67
|
-
respond "routine from <##{@channel_id}> *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest]
|
68
|
+
message = respond "routine from <##{@channel_id}> *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest], return_message: true
|
68
69
|
else
|
69
|
-
respond "routine *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest]
|
70
|
+
message = respond "routine *`#{name}`*: #{@routines[@channel_id][name][:command]}", @routines[@channel_id][name][:dest], return_message: true
|
70
71
|
end
|
71
72
|
end
|
72
73
|
started = Time.now
|
@@ -78,6 +79,9 @@ class SlackSmartBot
|
|
78
79
|
routine: true,
|
79
80
|
routine_name: name,
|
80
81
|
routine_type: hroutine[:routine_type] }
|
82
|
+
if !message.nil? and (@routines[@channel_id][name][:command].match?(/^!!/) or @routines[@channel_id][name][:command].match?(/^\^/))
|
83
|
+
data[:ts] = message.ts
|
84
|
+
end
|
81
85
|
treat_message(data)
|
82
86
|
end
|
83
87
|
# in case the routine was deleted while running the process
|
@@ -95,7 +99,24 @@ class SlackSmartBot
|
|
95
99
|
require "time"
|
96
100
|
every_in_seconds = Time.parse(@routines[@channel_id][name][:next_run]) - Time.now
|
97
101
|
elsif @routines[@channel_id][name][:at] != "" #coming from start after pause for 'at'
|
98
|
-
if @routines[@channel_id][name].key?(:
|
102
|
+
if @routines[@channel_id][name].key?(:daymonth) and @routines[@channel_id][name][:daymonth].to_s!='' # day of month
|
103
|
+
weekly = false
|
104
|
+
daymonth = @routines[@channel_id][name][:daymonth]
|
105
|
+
day = daymonth.to_i
|
106
|
+
if Date.today.day > day
|
107
|
+
next_month = Date.new(Date.today.year, Date.today.month, 1) >> 1
|
108
|
+
else
|
109
|
+
next_month = Date.new(Date.today.year, Date.today.month, 1)
|
110
|
+
end
|
111
|
+
next_month_last_day = Date.new(next_month.year, next_month.month, -1)
|
112
|
+
if day > next_month_last_day.day
|
113
|
+
next_time = Date.new(next_month.year, next_month.month, next_month_last_day.day)
|
114
|
+
else
|
115
|
+
next_time = Date.new(next_month.year, next_month.month, day)
|
116
|
+
end
|
117
|
+
days = (next_time - Date.today).to_i
|
118
|
+
every_in_seconds = Time.parse(@routines[@channel_id][name][:next_run]) - Time.now
|
119
|
+
elsif @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!='' and
|
99
120
|
@routines[@channel_id][name][:dayweek].to_s!='weekend' and @routines[@channel_id][name][:dayweek].to_s!='weekday'
|
100
121
|
|
101
122
|
day = @routines[@channel_id][name][:dayweek]
|
@@ -126,10 +147,25 @@ class SlackSmartBot
|
|
126
147
|
if started.strftime("%H:%M:%S") < @routines[@channel_id][name][:at] and days == 0
|
127
148
|
nt = @routines[@channel_id][name][:at].split(":")
|
128
149
|
next_run = Time.new(started.year, started.month, started.day, nt[0], nt[1], nt[2])
|
129
|
-
else
|
150
|
+
else
|
130
151
|
if days == 0 and started.strftime("%H:%M:%S") >= @routines[@channel_id][name][:at]
|
131
152
|
if weekly
|
132
153
|
days = 7
|
154
|
+
elsif @routines[@channel_id][name].key?(:daymonth) and @routines[@channel_id][name][:daymonth].to_s!=''
|
155
|
+
daymonth = @routines[@channel_id][name][:daymonth]
|
156
|
+
day = daymonth.to_i
|
157
|
+
if Date.today.day >= day
|
158
|
+
next_month = Date.new(Date.today.year, Date.today.month, 1) >> 1
|
159
|
+
else
|
160
|
+
next_month = Date.new(Date.today.year, Date.today.month, 1)
|
161
|
+
end
|
162
|
+
next_month_last_day = Date.new(next_month.year, next_month.month, -1)
|
163
|
+
if day > next_month_last_day.day
|
164
|
+
next_time = Date.new(next_month.year, next_month.month, next_month_last_day.day)
|
165
|
+
else
|
166
|
+
next_time = Date.new(next_month.year, next_month.month, day)
|
167
|
+
end
|
168
|
+
days = (next_time - Date.today).to_i
|
133
169
|
else
|
134
170
|
days = 1
|
135
171
|
end
|
@@ -152,6 +188,10 @@ class SlackSmartBot
|
|
152
188
|
update_routines()
|
153
189
|
sleep(@routines[@channel_id][name][:sleeping]) unless elapsed > every_in_seconds
|
154
190
|
else
|
191
|
+
if !@routines[@channel_id][name][:running]
|
192
|
+
@routines[@channel_id][name][:running] = true
|
193
|
+
update_routines()
|
194
|
+
end
|
155
195
|
sleep 30
|
156
196
|
end
|
157
197
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
def decrypt(data)
|
3
|
+
require "openssl"
|
4
|
+
require "base64"
|
5
|
+
|
6
|
+
key, iv = encryption_get_key_iv()
|
7
|
+
encrypted = Base64.decode64(data)
|
8
|
+
cipher = OpenSSL::Cipher.new("AES-256-CBC")
|
9
|
+
cipher.decrypt
|
10
|
+
cipher.key = key
|
11
|
+
cipher.iv = iv
|
12
|
+
plain = cipher.update(encrypted) + cipher.final
|
13
|
+
return plain
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
def display_calendar(from_user_name, year)
|
3
|
+
if @vacations.key?(from_user_name) and @vacations[from_user_name][:public_holidays].to_s != ""
|
4
|
+
country_region = @vacations[from_user_name][:public_holidays].downcase
|
5
|
+
elsif config[:public_holidays].key?(:default_calendar)
|
6
|
+
country_region = config[:public_holidays][:default_calendar].downcase
|
7
|
+
else
|
8
|
+
country_region = ""
|
9
|
+
end
|
10
|
+
country, location = country_region.split("/")
|
11
|
+
public_holidays(country.to_s, location.to_s, year, "", "", add_stats: false, publish_results: false)
|
12
|
+
messages = ["*Time off #{year}*"]
|
13
|
+
days_of_vacations = 0
|
14
|
+
(1..12).each do |m|
|
15
|
+
date = Date.parse("#{year}/#{m}/1")
|
16
|
+
month_name = date.strftime("%B")
|
17
|
+
month_line = ""
|
18
|
+
(1..6).each do |w|
|
19
|
+
if date.month == m
|
20
|
+
month_line += "#{date.strftime("%d")} "
|
21
|
+
else
|
22
|
+
month_line += ":white_small_square: "
|
23
|
+
end
|
24
|
+
|
25
|
+
if @public_holidays.key?(country_region) and @public_holidays[country_region].key?(year.to_s)
|
26
|
+
phd = @public_holidays[country_region][year.to_s].date.iso
|
27
|
+
else
|
28
|
+
phd = []
|
29
|
+
end
|
30
|
+
(1..7).each do |d|
|
31
|
+
wday = date.wday
|
32
|
+
wday = 7 if wday == 0
|
33
|
+
break if d >= 3 and w == 6 # week 6 cannot be more than wednesday
|
34
|
+
date_text = date.strftime("%Y-%m-%d")
|
35
|
+
if wday == d and date.month == m
|
36
|
+
vacations_set = false
|
37
|
+
public_holiday_set = false
|
38
|
+
if phd.include?(date_text)
|
39
|
+
month_line += ":large_red_square: "
|
40
|
+
public_holiday_set = true
|
41
|
+
end
|
42
|
+
if !public_holiday_set
|
43
|
+
if @vacations.key?(from_user_name) and @vacations[from_user_name].key?(:periods)
|
44
|
+
@vacations[from_user_name][:periods].each do |period|
|
45
|
+
if date >= Date.parse(period[:from]) and date <= Date.parse(period[:to])
|
46
|
+
if period[:type] == "sick"
|
47
|
+
month_line += ":face_with_thermometer: "
|
48
|
+
elsif period[:type] == "sick child"
|
49
|
+
month_line += ":baby: "
|
50
|
+
elsif period[:type] == "vacation"
|
51
|
+
month_line += ":palm_tree: "
|
52
|
+
if wday <= 5
|
53
|
+
days_of_vacations += 1
|
54
|
+
end
|
55
|
+
end
|
56
|
+
vacations_set = true
|
57
|
+
break
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
if !vacations_set
|
62
|
+
if wday == 6 || wday == 7
|
63
|
+
month_line += ":large_yellow_square: "
|
64
|
+
else
|
65
|
+
month_line += ":white_square: "
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
date += 1
|
70
|
+
else
|
71
|
+
month_line += ":white_small_square: "
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
messages << "#{month_line} #{month_name}\n"
|
76
|
+
end
|
77
|
+
messages << "\n\n:large_yellow_square: weekend / :white_square: weekday / :white_small_square: not in month / :large_red_square: Public Holiday / :palm_tree: Vacation / :face_with_thermometer: Sick / :baby: Sick child"
|
78
|
+
if country_region != ""
|
79
|
+
messages << "Your public holidays are set for #{country_region.downcase}. Call `set public holidays to COUNTRY/REGION` if you want to change it.\n"
|
80
|
+
else
|
81
|
+
messages << "Your public holidays are not set. Call `set public holidays to COUNTRY/REGION` to set it.\n"
|
82
|
+
end
|
83
|
+
messages << "You have spent #{days_of_vacations} days of vacations in #{year} considering only weekdays.\n\n"
|
84
|
+
respond messages.join("\n")
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
def encrypt(data)
|
3
|
+
require "openssl"
|
4
|
+
require "base64"
|
5
|
+
|
6
|
+
key, iv = encryption_get_key_iv()
|
7
|
+
cipher = OpenSSL::Cipher::Cipher.new "AES-256-CBC"
|
8
|
+
cipher.encrypt
|
9
|
+
cipher.key = key
|
10
|
+
cipher.iv = iv
|
11
|
+
encrypted = cipher.update(data) + cipher.final
|
12
|
+
encrypted = Base64.encode64(encrypted)
|
13
|
+
return encrypted
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
def encryption_get_key_iv
|
3
|
+
if defined?(@encryption_key_built)
|
4
|
+
key = @encryption_key_built
|
5
|
+
iv = @encryption_iv_built
|
6
|
+
else
|
7
|
+
if config.key?(:encryption) and config.encryption.key?(:key) and config.encryption.key?(:iv)
|
8
|
+
key = config[:encryption][:key]
|
9
|
+
iv = config[:encryption][:iv]
|
10
|
+
else
|
11
|
+
key = (Socket.gethostname + config.token.reverse)[0..49]
|
12
|
+
iv = config.token[0..15]
|
13
|
+
end
|
14
|
+
|
15
|
+
#Convert from hex to raw bytes:
|
16
|
+
key = [key].pack('H*')
|
17
|
+
#Pad with zero bytes to correct length:
|
18
|
+
key << ("\x00" * (32 - key.length))
|
19
|
+
|
20
|
+
#Convert from hex to raw bytes:
|
21
|
+
iv = [iv].pack('H*')
|
22
|
+
#Pad with zero bytes to correct length:
|
23
|
+
iv << ("\x00" * (16 - iv.length))
|
24
|
+
@encryption_key_built = key
|
25
|
+
@encryption_iv_built = iv
|
26
|
+
end
|
27
|
+
return key, iv
|
28
|
+
end
|
29
|
+
end
|
@@ -4,7 +4,7 @@ class SlackSmartBot
|
|
4
4
|
general: [:bot_help, :hi_bot, :bye_bot, :add_admin, :remove_admin, :see_admins, :poster, :add_announcement, :delete_announcement,
|
5
5
|
:see_announcements, :see_command_ids, :share_messages, :see_shares, :delete_share, :see_favorite_commands, :see_statuses,
|
6
6
|
:allow_access, :see_access, :deny_access, :add_team, :add_memo_team, :delete_memo_team, :set_memo_status, :see_teams, :update_team, :ping_team, :delete_team,
|
7
|
-
|
7
|
+
:see_memos_team, :add_vacation, :remove_vacation, :see_vacations, :see_vacations_team, :public_holidays, :create_loop, :quit_loop],
|
8
8
|
on_bot_general: [:whats_new, :suggest_command, :bot_status, :use_rules, :stop_using_rules, :bot_stats, :leaderboard],
|
9
9
|
on_bot: [:ruby_code, :repl, :get_repl, :run_repl, :delete_repl, :see_repls, :kill_repl, :add_shortcut, :delete_shortcut, :see_shortcuts],
|
10
10
|
on_bot_admin: [:extend_rules, :stop_using_rules_on, :start_bot, :pause_bot, :add_routine,
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
def get_team_members(team)
|
3
|
+
assigned_members = team.members.values.flatten
|
4
|
+
assigned_members.uniq!
|
5
|
+
assigned_members.dup.each do |m|
|
6
|
+
user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) or u.name == m or (u.key?(:enterprise_user) and u.enterprise_user.name == m) }[-1]
|
7
|
+
assigned_members.delete(m) if user_info.nil? or user_info.deleted
|
8
|
+
end
|
9
|
+
channels_members = []
|
10
|
+
all_team_members = assigned_members.dup
|
11
|
+
if team.channels.key?("members")
|
12
|
+
team_members = []
|
13
|
+
team.channels["members"].each do |ch|
|
14
|
+
get_channels_name_and_id() unless @channels_id.key?(ch)
|
15
|
+
tm = get_channel_members(@channels_id[ch])
|
16
|
+
if tm.nil?
|
17
|
+
respond ":exclamation: Add the Smart Bot to *##{ch}* channel to be able to get the list of members.", dest
|
18
|
+
else
|
19
|
+
channels_members << @channels_id[ch]
|
20
|
+
tm.each do |m|
|
21
|
+
user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
|
22
|
+
team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
team_members.flatten!
|
27
|
+
team_members.uniq!
|
28
|
+
unassigned_members = team_members - assigned_members
|
29
|
+
unassigned_members.delete(config.nick)
|
30
|
+
not_on_team_channel = assigned_members - team_members
|
31
|
+
all_team_members += team_members
|
32
|
+
else
|
33
|
+
unassigned_members = []
|
34
|
+
not_on_team_channel = []
|
35
|
+
end
|
36
|
+
|
37
|
+
return assigned_members, unassigned_members, not_on_team_channel, channels_members, all_team_members
|
38
|
+
end
|
39
|
+
end
|
@@ -1,22 +1,28 @@
|
|
1
1
|
class SlackSmartBot
|
2
2
|
def get_teams
|
3
3
|
@teams ||= {}
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
4
|
+
old_teams_file = config.file_path.gsub(".rb", "_teams.yaml") #to be backward compatible
|
5
|
+
require 'yaml'
|
6
|
+
if File.exist?(old_teams_file)
|
7
|
+
@logger.info 'Migrating teams to new format'
|
8
|
+
teams = YAML.load(File.read(old_teams_file))
|
9
|
+
@logger.info "@teams: #{teams.inspect}}"
|
10
|
+
teams.each do |key, value|
|
11
|
+
File.write(File.join(config.path, "teams", "t_#{key}.yaml"), encrypt(value.to_yaml))
|
12
|
+
end
|
13
|
+
@logger.info "Deleting old_teams_file: #{old_teams_file}"
|
14
|
+
File.delete(old_teams_file)
|
15
|
+
end
|
16
|
+
files = Dir.glob(File.join(config.path, "teams", "t_*.yaml"))
|
17
|
+
@datetime_teams_file ||= {}
|
18
|
+
files.each do |file|
|
19
|
+
if !defined?(@datetime_teams_file) or !@datetime_teams_file.key?(file) or @datetime_teams_file[file] != File.mtime(file)
|
20
|
+
teams_team = YAML.load(decrypt(File.read(file)))
|
21
|
+
team_name = File.basename(file).gsub("t_","").gsub(".yaml","")
|
22
|
+
teams_team[:name] = team_name unless teams_team.key?(:name) #to be backward compatible
|
23
|
+
@teams[team_name.to_sym] = teams_team
|
24
|
+
@datetime_teams_file[file] = File.mtime(file)
|
19
25
|
end
|
20
26
|
end
|
21
27
|
end
|
22
|
-
end
|
28
|
+
end
|
@@ -1,21 +1,26 @@
|
|
1
1
|
class SlackSmartBot
|
2
2
|
def get_vacations
|
3
3
|
@vacations ||= {}
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
4
|
+
old_vacations_file = config.file_path.gsub(".rb", "_vacations.yaml") #to be backward compatible
|
5
|
+
require 'yaml'
|
6
|
+
if File.exist?(old_vacations_file)
|
7
|
+
@logger.info 'Migrating vacations to new format'
|
8
|
+
vacations = @vacations
|
9
|
+
vacations = YAML.load(File.read(old_vacations_file))
|
10
|
+
@vacations = vacations unless vacations.is_a?(FalseClass)
|
11
|
+
@vacations.each do |key, value|
|
12
|
+
File.write(File.join(config.path, "vacations", "v_#{key}.yaml"), encrypt(value.to_yaml))
|
13
|
+
end
|
14
|
+
@logger.info "Deleting old_vacations_file: #{old_vacations_file}"
|
15
|
+
File.delete(old_vacations_file)
|
16
|
+
end
|
17
|
+
files = Dir.glob(File.join(config.path, "vacations", "v_*.yaml"))
|
18
|
+
@datetime_vacations_file ||= {}
|
19
|
+
files.each do |file|
|
20
|
+
if !defined?(@datetime_vacations_file) or !@datetime_vacations_file.key?(file) or @datetime_vacations_file[file] != File.mtime(file)
|
21
|
+
vacations_user = YAML.load(decrypt(File.read(file)))
|
22
|
+
@vacations[File.basename(file).gsub("v_","").gsub(".yaml","")] = vacations_user
|
23
|
+
@datetime_vacations_file[file] = File.mtime(file)
|
19
24
|
end
|
20
25
|
end
|
21
26
|
end
|
@@ -1,16 +1,22 @@
|
|
1
1
|
class SlackSmartBot
|
2
2
|
def update_teams(team=nil)
|
3
3
|
require 'yaml'
|
4
|
-
|
4
|
+
if team.nil?
|
5
|
+
teams = @teams.keys
|
6
|
+
else
|
5
7
|
get_teams()
|
6
8
|
@teams.merge!(team)
|
9
|
+
teams = team.keys
|
10
|
+
end
|
11
|
+
|
12
|
+
teams.each do |team|
|
13
|
+
team_file = File.join(config.path, "teams", "t_#{team}.yaml")
|
14
|
+
File.open(team_file, 'w') {|file|
|
15
|
+
file.flock(File::LOCK_EX)
|
16
|
+
file.write(encrypt(@teams[team].to_yaml))
|
17
|
+
file.flock(File::LOCK_UN)
|
18
|
+
}
|
19
|
+
@datetime_teams_file[team_file] = File.mtime(team_file)
|
7
20
|
end
|
8
|
-
teams_file = config.file_path.gsub(".rb", "_teams.yaml")
|
9
|
-
File.open(teams_file, 'w') {|file|
|
10
|
-
file.flock(File::LOCK_EX)
|
11
|
-
file.write(@teams.to_yaml)
|
12
|
-
file.flock(File::LOCK_UN)
|
13
|
-
}
|
14
|
-
@datetime_teams_file = File.mtime(teams_file)
|
15
21
|
end
|
16
|
-
end
|
22
|
+
end
|
@@ -5,12 +5,14 @@ class SlackSmartBot
|
|
5
5
|
get_vacations()
|
6
6
|
@vacations.merge!(vacation)
|
7
7
|
end
|
8
|
-
|
8
|
+
user = Thread.current[:user]
|
9
|
+
vacations_file = File.join(config.path, "vacations", "v_#{user.name}.yaml")
|
10
|
+
|
9
11
|
File.open(vacations_file, 'w') {|file|
|
10
12
|
file.flock(File::LOCK_EX)
|
11
|
-
file.write(@vacations.to_yaml)
|
13
|
+
file.write(encrypt(@vacations[user.name].to_yaml))
|
12
14
|
file.flock(File::LOCK_UN)
|
13
15
|
}
|
14
|
-
@datetime_vacations_file = File.mtime(vacations_file)
|
16
|
+
@datetime_vacations_file[vacations_file] = File.mtime(vacations_file)
|
15
17
|
end
|
16
18
|
end
|
@@ -29,4 +29,9 @@ require_relative 'utils/get_vacations'
|
|
29
29
|
require_relative 'utils/update_teams'
|
30
30
|
require_relative 'utils/update_vacations'
|
31
31
|
require_relative 'utils/check_vacations'
|
32
|
+
require_relative 'utils/display_calendar'
|
33
|
+
require_relative 'utils/encryption_get_key_iv'
|
34
|
+
require_relative 'utils/encrypt'
|
35
|
+
require_relative 'utils/decrypt'
|
36
|
+
require_relative 'utils/get_team_members'
|
32
37
|
|
data/lib/slack-smart-bot.rb
CHANGED
@@ -54,6 +54,10 @@ class SlackSmartBot
|
|
54
54
|
config[:github] = {token: '' } unless config.key?(:github) and config[:github].key?(:token)
|
55
55
|
config[:github][:host] ||= "https://api.github.com"
|
56
56
|
config[:github][:host] = "https://#{config[:github][:host]}" unless config[:github][:host] == '' or config[:github][:host].match?(/^http/)
|
57
|
+
config[:public_holidays] = { api_key: '' } unless config.key?(:public_holidays) and config[:public_holidays].key?(:api_key)
|
58
|
+
config[:public_holidays][:host] ||= "https://calendarific.com"
|
59
|
+
config[:public_holidays][:host] = "https://#{config[:public_holidays][:host]}" unless config[:public_holidays][:host] == '' or config[:public_holidays][:host].match?(/^http/)
|
60
|
+
|
57
61
|
if config.path.to_s!='' and config.file.to_s==''
|
58
62
|
config.file = File.basename($0)
|
59
63
|
end
|
@@ -74,6 +78,8 @@ class SlackSmartBot
|
|
74
78
|
Dir.mkdir("#{config.path}/announcements") unless Dir.exist?("#{config.path}/announcements")
|
75
79
|
Dir.mkdir("#{config.path}/shares") unless Dir.exist?("#{config.path}/shares")
|
76
80
|
Dir.mkdir("#{config.path}/rules") unless Dir.exist?("#{config.path}/rules")
|
81
|
+
Dir.mkdir("#{config.path}/vacations") unless Dir.exist?("#{config.path}/vacations")
|
82
|
+
Dir.mkdir("#{config.path}/teams") unless Dir.exist?("#{config.path}/teams")
|
77
83
|
File.delete("#{config.path}/config_tmp.status") if File.exist?("#{config.path}/config_tmp.status")
|
78
84
|
|
79
85
|
config.masters = MASTER_USERS if config.masters.to_s=='' and defined?(MASTER_USERS)
|
@@ -202,6 +208,8 @@ class SlackSmartBot
|
|
202
208
|
@last_status_change = Time.now
|
203
209
|
@vacations_check = (Date.today - 1)
|
204
210
|
@announcements_activity_after = Hash.new()
|
211
|
+
@public_holidays = Hash.new()
|
212
|
+
@loops = Hash.new()
|
205
213
|
|
206
214
|
if File.exist?("#{config.path}/shortcuts/#{config.shortcuts_file}".gsub('.yaml','.rb')) #backwards compatible
|
207
215
|
file_conf = IO.readlines("#{config.path}/shortcuts/#{config.shortcuts_file}".gsub('.yaml','.rb')).join
|
@@ -317,6 +325,7 @@ class SlackSmartBot
|
|
317
325
|
get_shares()
|
318
326
|
get_admins_channels()
|
319
327
|
get_access_channels()
|
328
|
+
get_vacations()
|
320
329
|
|
321
330
|
if @routines.key?(@channel_id)
|
322
331
|
@routines[@channel_id].each do |k, v|
|
@@ -19,6 +19,39 @@ def general_commands(user, command, dest, files = [])
|
|
19
19
|
lines = 200 if lines > 200
|
20
20
|
respond (">#{"\n"*lines}<")
|
21
21
|
|
22
|
+
# help: ----------------------------------------------
|
23
|
+
# help: `blink TEXT`
|
24
|
+
# help: `INTEGER blink TEXT`
|
25
|
+
# help: It will blink the text supplied. One or more lines of text. Emoticons or text format are allowed.
|
26
|
+
# help: INTEGER (optional): number of times. Default 50. Max 200.
|
27
|
+
# help: Examples:
|
28
|
+
# help: blink Hello World!
|
29
|
+
# help: blink :moneybag: Pay attention! *Sales* are published!
|
30
|
+
# help: 100 blink :new: *Party is about to start* :i_love_you_hand_sign:
|
31
|
+
# help: command_id: :blink
|
32
|
+
# help:
|
33
|
+
when /\A\s*(\d+)?\s*blink\s+(.+)\s*\z/im
|
34
|
+
save_stats :blink
|
35
|
+
num_times = $1.to_s == '' ? 50 : $1.to_i
|
36
|
+
text = $2
|
37
|
+
@blinking ||= []
|
38
|
+
if num_times > 200 or num_times < 1
|
39
|
+
respond "The number of times must be between 1 and 200"
|
40
|
+
elsif @blinking.include?(user.name)
|
41
|
+
respond "I'm already blinking something for you. Please wait until I finish"
|
42
|
+
elsif @blinking.size >= 3 # rate limit in theory update can be done only once per second
|
43
|
+
respond "I'm already blinking something for too many people. Please wait until I finish at least one of them."
|
44
|
+
else
|
45
|
+
@blinking << user.name
|
46
|
+
msg = respond(text, return_message: true)
|
47
|
+
num_times.times do
|
48
|
+
sleep 2
|
49
|
+
update(dest, msg.ts, ' ')
|
50
|
+
sleep 0.5
|
51
|
+
update(dest, msg.ts, text)
|
52
|
+
end
|
53
|
+
@blinking.delete(user.name)
|
54
|
+
end
|
22
55
|
|
23
56
|
# this is a hidden command that it is not listed when calling bot help
|
24
57
|
when /\s*(that's\s+)?(thanks|thank\s+you|I\s+love\s+you|nice|cool)\s+(#{@salutations.join("|")})\s*!*\s*$/i
|
@@ -8,8 +8,8 @@ def general_rules(user, command, processed, dest, files = [], rules_file = "")
|
|
8
8
|
|
9
9
|
# help: ----------------------------------------------
|
10
10
|
# help: `echo SOMETHING`
|
11
|
-
# help: `
|
12
|
-
# help: repeats SOMETHING. If
|
11
|
+
# help: `NUMBER echo SOMETHING`
|
12
|
+
# help: repeats SOMETHING. If NUMBER supplied then that number of times.
|
13
13
|
# help: Examples:
|
14
14
|
# help: _echo I am the Smart Bot_
|
15
15
|
# help: _100 echo :heart:_
|
data/whats_new.txt
CHANGED
@@ -1,23 +1,21 @@
|
|
1
|
-
*Version 1.
|
1
|
+
*Version 1.13.0* Released 2023-Mar-01
|
2
2
|
|
3
3
|
*For General users*
|
4
|
-
-
|
5
|
-
-
|
6
|
-
-
|
7
|
-
-
|
8
|
-
- `
|
9
|
-
-
|
10
|
-
-
|
11
|
-
- Added 'Time off' (Vacation/Sick/Sick Child) management. The SmartBot will automatically set your status on the periods specified. It will be possible to see the 'Time off' plan for a specific team and period. (<https://github.com/MarioRuiz/slack-smart-bot/issues/73|#73>)
|
12
|
-
- More natural language for 'Time off' command: (<https://github.com/MarioRuiz/slack-smart-bot/issues/80|#80>)
|
4
|
+
- It is possible to run any command on a loop: `for 3 times every 5 minutes !ruby puts Time.now` (<https://github.com/MarioRuiz/slack-smart-bot/issues/83|#83>).
|
5
|
+
- You can add collaborators to your REPL by sending `add collaborator @USER` to the session. (<https://github.com/MarioRuiz/slack-smart-bot/issues/85|#85>).
|
6
|
+
- If using the `monthly` option when calling `bot stats` it will show a graph (<https://github.com/MarioRuiz/slack-smart-bot/issues/86|#86>).
|
7
|
+
- New `public holidays COUNTRY/REGION` command (<https://github.com/MarioRuiz/slack-smart-bot/issues/88|#88>)
|
8
|
+
- When calling `see my vacations` on a DM will display a calendar of the year with the days off, including public holidays (<https://github.com/MarioRuiz/slack-smart-bot/issues/89|#89>)
|
9
|
+
- `set public holidays to COUNTRY/REGION` will set the public holidays for the country and region specified. (<https://github.com/MarioRuiz/slack-smart-bot/issues/90|#90>)
|
10
|
+
- `see MEMO_TYPE from TEAM_NAME team` will show the memos of the team. (<https://github.com/MarioRuiz/slack-smart-bot/issues/91|#91>)
|
13
11
|
|
14
12
|
*For Admin users*
|
15
|
-
-
|
16
|
-
-
|
17
|
-
-
|
18
|
-
-
|
19
|
-
-
|
20
|
-
|
13
|
+
- It is possible to see routines filtering the HEADER supplied using the given REGEXP: `see routines HEADER /REGEXP/` (<https://github.com/MarioRuiz/slack-smart-bot/issues/84|#84>).
|
14
|
+
- `send message to users from YYYY/MM/DD to YYYY/MM/DD #CHANNEL COMMAND_ID: MESSAGE` 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 are optional filters. (<https://github.com/MarioRuiz/slack-smart-bot/issues/87|#87>)
|
15
|
+
- `update message URL TEXT` It will update the SmartBot message supplied. You can use this command only if you are a Master admin user and if you are in a private conversation with the bot. (<https://github.com/MarioRuiz/slack-smart-bot/issues/33|#33>)
|
16
|
+
- Routines now can create threads (<https://github.com/MarioRuiz/slack-smart-bot/issues/36|#36>)
|
17
|
+
- `add routine NAME on the DAY_OF_MONTH at TIME COMMAND` (<https://github.com/MarioRuiz/slack-smart-bot/issues/82|#82>)
|
18
|
+
|
21
19
|
------------------------------
|
22
20
|
|
23
|
-
*Previous*: <https://github.com/MarioRuiz/slack-smart-bot/blob/
|
21
|
+
*Previous*: <https://github.com/MarioRuiz/slack-smart-bot/blob/2b93317d7556ceaeb724494f53cfa9e8907c3e52/whats_new.txt|1.12.9>
|