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
@@ -5,12 +5,12 @@ class SlackSmartBot
|
|
5
5
|
get_teams()
|
6
6
|
teams = @teams.deep_copy
|
7
7
|
if teams.empty?
|
8
|
-
respond "There are no teams added yet. Use `add team` command to add a team."
|
9
|
-
elsif team_name.to_s != "" and !teams.key?(team_name.to_sym) and (teams.keys.select {|t| (t.to_s.gsub(
|
8
|
+
respond "There are no teams added yet. Use `add team` command to add a team."
|
9
|
+
elsif team_name.to_s != "" and !teams.key?(team_name.to_sym) and (teams.keys.select { |t| (t.to_s.gsub("-", "").gsub("_", "") == team_name.to_s) }).empty?
|
10
10
|
respond "It seems like the team *#{team_name}* doesn't exist.\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
|
11
11
|
else
|
12
12
|
users_link = (Thread.current[:dest][0] == "D")
|
13
|
-
filter = (search !=
|
13
|
+
filter = (search != "")
|
14
14
|
react :runner
|
15
15
|
@users = get_users() if add_stats
|
16
16
|
|
@@ -19,7 +19,7 @@ class SlackSmartBot
|
|
19
19
|
search_channels = []
|
20
20
|
search_info = []
|
21
21
|
if filter
|
22
|
-
search.split(
|
22
|
+
search.split(" ").each do |s|
|
23
23
|
if s.match(/<@(\w+)>/i)
|
24
24
|
m = $1
|
25
25
|
user_info = @users.select { |u| u.id.downcase == m.downcase or (u.key?(:enterprise_user) and u.enterprise_user.id.downcase == m.downcase) }[-1]
|
@@ -29,28 +29,28 @@ class SlackSmartBot
|
|
29
29
|
search_channels << @channels_name[c] if @channels_name.key?(c)
|
30
30
|
else
|
31
31
|
search_info << s
|
32
|
-
end
|
32
|
+
end
|
33
33
|
end
|
34
34
|
end
|
35
|
-
if team_name.to_s ==
|
35
|
+
if team_name.to_s == "" and search.to_s == ""
|
36
36
|
dest = :on_thread
|
37
|
-
messages.unshift(
|
37
|
+
messages.unshift("Since there are many lines returned the results are returned on a thread by default.")
|
38
38
|
else
|
39
39
|
dest = Thread.current[:dest]
|
40
40
|
end
|
41
41
|
|
42
42
|
teams.each do |name, team|
|
43
43
|
filter ? add = false : add = true
|
44
|
-
if team_name.to_s == "" or (team_name.to_s == name.to_s) or (name.to_s.gsub(
|
44
|
+
if team_name.to_s == "" or (team_name.to_s == name.to_s) or (name.to_s.gsub("-", "").gsub("_", "") == team_name.to_s)
|
45
45
|
message = []
|
46
46
|
message << "*#{name.capitalize}*"
|
47
47
|
|
48
48
|
if filter and search_info.size > 0
|
49
49
|
all_info = true
|
50
50
|
search_info.each do |s|
|
51
|
-
if (team.members.keys.find { |e| /#{s}/i =~ e})
|
52
|
-
add = true
|
53
|
-
break
|
51
|
+
if (team.members.keys.find { |e| /#{s}/i =~ e })
|
52
|
+
add = true
|
53
|
+
break
|
54
54
|
end
|
55
55
|
if !name.match?(/#{s}/i)
|
56
56
|
all_info = false
|
@@ -68,7 +68,8 @@ class SlackSmartBot
|
|
68
68
|
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]
|
69
69
|
assigned_members.delete(m) if user_info.nil? or user_info.deleted
|
70
70
|
end
|
71
|
-
|
71
|
+
channels_members = []
|
72
|
+
all_team_members = assigned_members.dup
|
72
73
|
if team.channels.key?("members")
|
73
74
|
team_members = []
|
74
75
|
team.channels["members"].each do |ch|
|
@@ -77,6 +78,7 @@ class SlackSmartBot
|
|
77
78
|
if tm.nil?
|
78
79
|
respond ":exclamation: Add the Smart Bot to *##{ch}* channel to be able to get the list of members.", dest
|
79
80
|
else
|
81
|
+
channels_members << @channels_id[ch]
|
80
82
|
tm.each do |m|
|
81
83
|
user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
|
82
84
|
team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
|
@@ -88,6 +90,7 @@ class SlackSmartBot
|
|
88
90
|
unassigned_members = team_members - assigned_members
|
89
91
|
unassigned_members.delete(config.nick)
|
90
92
|
not_on_team_channel = assigned_members - team_members
|
93
|
+
all_team_members += team_members
|
91
94
|
else
|
92
95
|
unassigned_members = []
|
93
96
|
not_on_team_channel = []
|
@@ -96,7 +99,7 @@ class SlackSmartBot
|
|
96
99
|
um = unassigned_members.dup
|
97
100
|
um.each do |m|
|
98
101
|
user_info = @users.select { |u| u.name == m or (u.key?(:enterprise_user) and u.enterprise_user.name == m) }[-1]
|
99
|
-
unless user_info.nil? or user_info.profile.title.to_s==
|
102
|
+
unless user_info.nil? or user_info.profile.title.to_s == ""
|
100
103
|
team.members[user_info.profile.title.to_snake_case] ||= []
|
101
104
|
team.members[user_info.profile.title.to_snake_case] << m
|
102
105
|
unassigned_members.delete(m)
|
@@ -117,9 +120,9 @@ class SlackSmartBot
|
|
117
120
|
if filter and search_info.size > 0
|
118
121
|
all_info = true
|
119
122
|
search_info.each do |s|
|
120
|
-
if (team.members.keys.find { |e| /#{s}/i =~ e})
|
121
|
-
add = true
|
122
|
-
break
|
123
|
+
if (team.members.keys.find { |e| /#{s}/i =~ e })
|
124
|
+
add = true
|
125
|
+
break
|
123
126
|
end
|
124
127
|
if !team.info.match?(/#{s}/i)
|
125
128
|
all_info = false
|
@@ -134,7 +137,7 @@ class SlackSmartBot
|
|
134
137
|
team.members.each do |type, members|
|
135
138
|
message << " _`#{type}`_: "
|
136
139
|
members.each do |member|
|
137
|
-
types = [":palm_tree:", ":spiral_calendar_pad:", ":face_with_thermometer:"]
|
140
|
+
types = [":palm_tree:", ":spiral_calendar_pad:", ":face_with_thermometer:", ":baby:"]
|
138
141
|
member_info = @users.select { |u| u.name == member }[-1]
|
139
142
|
if !member_info.nil? and !member_info.deleted
|
140
143
|
member_id = member_info.id
|
@@ -146,7 +149,7 @@ class SlackSmartBot
|
|
146
149
|
active = (get_presence(member_id).presence.to_s == "active")
|
147
150
|
if active
|
148
151
|
user_info = @users.select { |u| u.id == member_id or (u.key?(:enterprise_user) and u.enterprise_user.name == member_id) }[-1]
|
149
|
-
if (user_info.tz_offset-user.tz_offset).abs <= (4*3600)
|
152
|
+
if (user_info.tz_offset - user.tz_offset).abs <= (4 * 3600)
|
150
153
|
status = ":large_green_circle:"
|
151
154
|
else
|
152
155
|
status = ":large_yellow_circle:"
|
@@ -158,19 +161,19 @@ class SlackSmartBot
|
|
158
161
|
else
|
159
162
|
status = ":exclamation:"
|
160
163
|
end
|
161
|
-
unless status ==
|
164
|
+
unless status == ":exclamation:"
|
162
165
|
if users_link
|
163
166
|
message[-1] += " #{status}<@#{member}>, "
|
164
167
|
else
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
172
|
-
message[-1] += " #{status} #{name}, "
|
168
|
+
user_info = @users.select { |u| u.name == member or (u.key?(:enterprise_user) and u.enterprise_user.name == member) }[-1]
|
169
|
+
unless user_info.nil?
|
170
|
+
if user_info.profile.display_name == ""
|
171
|
+
name = user_info.name
|
172
|
+
else
|
173
|
+
name = user_info.profile.display_name
|
173
174
|
end
|
175
|
+
message[-1] += " #{status} #{name}, "
|
176
|
+
end
|
174
177
|
end
|
175
178
|
end
|
176
179
|
end
|
@@ -186,7 +189,7 @@ class SlackSmartBot
|
|
186
189
|
members.each do |m|
|
187
190
|
user_info = @users.select { |u| u.name == m or (u.key?(:enterprise_user) and u.enterprise_user.name == m) }[-1]
|
188
191
|
unless user_info.nil? or user_info.deleted
|
189
|
-
if user_info.profile.display_name ==
|
192
|
+
if user_info.profile.display_name == ""
|
190
193
|
name = user_info.name
|
191
194
|
else
|
192
195
|
name = user_info.profile.display_name
|
@@ -200,20 +203,164 @@ class SlackSmartBot
|
|
200
203
|
end
|
201
204
|
end
|
202
205
|
|
203
|
-
|
204
206
|
if add
|
205
207
|
message << " > *_channels_*"
|
206
208
|
team.channels.each do |type, channels|
|
207
209
|
channel_ids = []
|
208
210
|
channels.each do |ch|
|
209
|
-
channel_info = @channels_list.select { |c| c.name.to_s.downcase == ch.to_s.downcase}[-1]
|
210
|
-
if @channels_id.key?(ch) and (!channel_info.is_private or (channel_info.is_private and (team.members.values+[team.creator]).flatten.include?(user.name)))
|
211
|
+
channel_info = @channels_list.select { |c| c.name.to_s.downcase == ch.to_s.downcase }[-1]
|
212
|
+
if @channels_id.key?(ch) and (!channel_info.is_private or (channel_info.is_private and (team.members.values + [team.creator]).flatten.include?(user.name)))
|
211
213
|
channel_ids << @channels_id[ch]
|
212
214
|
end
|
213
215
|
end
|
214
216
|
message << " _`#{type}`_: <##{channel_ids.join("> <#")}>" unless channel_ids.empty?
|
215
217
|
end
|
216
218
|
|
219
|
+
unless !team.key?(:memos) or team.memos.empty? or (team_name.to_s == "" and search.to_s == "")
|
220
|
+
all_memos = {}
|
221
|
+
team.memos.each do |memo|
|
222
|
+
if memo.privacy.empty? or
|
223
|
+
(memo.privacy == "private" and (all_team_members.include?(user.name) and (users_link or channels_members.include?(Thread.current[:dest])))) or
|
224
|
+
(memo.privacy == "personal" and memo.user == user.name and users_link)
|
225
|
+
if memo.type == "jira" and config.jira.host != ""
|
226
|
+
http = NiceHttp.new(config.jira.host)
|
227
|
+
http.headers.authorization = NiceHttpUtils.basic_authentication(user: config.jira.user, password: config.jira.password)
|
228
|
+
if memo.message.match?(/^\w+\-\d+$/)
|
229
|
+
resp = http.get("/rest/api/latest/issue/#{memo.message}")
|
230
|
+
issues = [resp.data.json] if resp.code == 200
|
231
|
+
else
|
232
|
+
resp = http.get("/rest/api/latest/search/?jql=#{memo.message}")
|
233
|
+
issues = resp.data.json().issues if resp.code == 200
|
234
|
+
end
|
235
|
+
if resp.code == 200
|
236
|
+
unless issues.empty?
|
237
|
+
issues.each do |issue|
|
238
|
+
jira_memo = { jira: true, github: false, status: "", memo_id: memo.memo_id, topic: memo.topic, privacy: memo.privacy, user: memo.user, date: memo.date, message: "", type: memo.type }
|
239
|
+
jira_memo.message = issue.fields.summary
|
240
|
+
jira_memo.user = issue.fields.reporter.name
|
241
|
+
jira_memo.date = issue.fields.created
|
242
|
+
if memo.topic == :no_topic and !issue.fields.labels.empty?
|
243
|
+
jira_memo.topic = issue.fields.labels.sort.join("_").split(" ").join("_")
|
244
|
+
end
|
245
|
+
case issue.fields.issuetype.name
|
246
|
+
when "Story"; jira_memo.type = ":abc:"
|
247
|
+
when "Bug"; jira_memo.type = ":bug:"
|
248
|
+
when "Task"; jira_memo.type = ":clock1:"
|
249
|
+
when "New Feature", "Improvement"; jira_memo.type = ":sunny:"
|
250
|
+
else jira_memo.type = ":memo:"
|
251
|
+
end
|
252
|
+
case issue.fields.status.statusCategory.name
|
253
|
+
when "Done"; jira_memo.status = ":heavy_check_mark:"
|
254
|
+
when "To Do"; jira_memo.status = ":new:"
|
255
|
+
when "In Progress"; jira_memo.status = ":runner:"
|
256
|
+
else jira_memo.status = ":heavy_minus_sign:"
|
257
|
+
end
|
258
|
+
#todo: check if possible to add link to status instead of jira issue
|
259
|
+
#jira_memo.status = " <#{config.jira.host}/browse/#{issue[:key]}|#{jira_memo.status}> "
|
260
|
+
jira_memo.status += " <#{config.jira.host}/browse/#{issue[:key]}|#{issue[:key]}>"
|
261
|
+
|
262
|
+
all_memos[jira_memo.topic] ||= []
|
263
|
+
all_memos[jira_memo.topic] << jira_memo
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
http.close
|
268
|
+
elsif memo.type == "github" and config.github.host != ""
|
269
|
+
http = NiceHttp.new(config.github.host)
|
270
|
+
http.headers.authorization = "token #{config.github.token}"
|
271
|
+
memo.message+="?" unless memo.message.include?('?')
|
272
|
+
memo.message+="&per_page=50"
|
273
|
+
|
274
|
+
resp = http.get("/repos/#{memo.message}")
|
275
|
+
issues = resp.data.json()
|
276
|
+
issues = [issues] unless issues.is_a?(Array)
|
277
|
+
if resp.code == 200
|
278
|
+
unless issues.empty?
|
279
|
+
issues.each do |issue|
|
280
|
+
github_memo = { jira: false, github: true, status: "", memo_id: memo.memo_id, topic: memo.topic, privacy: memo.privacy, user: memo.user, date: memo.date, message: "", type: memo.type }
|
281
|
+
github_memo.message = issue.title
|
282
|
+
github_memo.user = issue.user.login
|
283
|
+
github_memo.date = issue.created_at
|
284
|
+
if issue.labels.empty?
|
285
|
+
labels = ''
|
286
|
+
else
|
287
|
+
labels = issue.labels.name.sort.join("_").split(" ").join("_")
|
288
|
+
end
|
289
|
+
if memo.topic == :no_topic and !issue.labels.empty?
|
290
|
+
github_memo.topic = labels
|
291
|
+
end
|
292
|
+
case labels
|
293
|
+
when /bug/i; github_memo.type = ":bug:"
|
294
|
+
when /docum/i; github_memo.type = ":abc:"
|
295
|
+
when /task/i; github_memo.type = ":clock1:"
|
296
|
+
when /enhancem/i, /improvement/i; github_memo.type = ":sunny:"
|
297
|
+
else github_memo.type = ":memo:"
|
298
|
+
end
|
299
|
+
if issue.key?(:events_url)
|
300
|
+
resp_events = http.get(issue.events_url)
|
301
|
+
events = resp_events.data.json(:event)
|
302
|
+
issue.state = "in progress" if events.include?('referenced')
|
303
|
+
end
|
304
|
+
case issue.state
|
305
|
+
when "closed"; github_memo.status = ":heavy_check_mark:"
|
306
|
+
when "open"; github_memo.status = ":new:"
|
307
|
+
when "in progress"; github_memo.status = ":runner:"
|
308
|
+
else github_memo.status = ":heavy_minus_sign:"
|
309
|
+
end
|
310
|
+
#todo: check if possible to add link to status instead of github issue
|
311
|
+
github_memo.status += " <#{issue.html_url}|##{issue.number}>"
|
312
|
+
|
313
|
+
all_memos[github_memo.topic] ||= []
|
314
|
+
all_memos[github_memo.topic] << github_memo
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
http.close
|
319
|
+
else
|
320
|
+
memo.jira = false
|
321
|
+
memo.github = false
|
322
|
+
all_memos[memo.topic] ||= []
|
323
|
+
case memo.type
|
324
|
+
when "memo"; memo.type = ":memo:"
|
325
|
+
when "note"; memo.type = ":abc:"
|
326
|
+
when "bug"; memo.type = ":bug:"
|
327
|
+
when "task"; memo.type = ":clock1:"
|
328
|
+
when "feature"; memo.type = ":sunny:"
|
329
|
+
when "issue"; memo.type = ":hammer:"
|
330
|
+
else memo.type = ":heavy_minus_sign:"
|
331
|
+
end
|
332
|
+
all_memos[memo.topic] << memo
|
333
|
+
end
|
334
|
+
end
|
335
|
+
end
|
336
|
+
message << " > *_memos_*" unless all_memos.empty?
|
337
|
+
|
338
|
+
if all_memos.key?(:no_topic)
|
339
|
+
all_memos[:no_topic].sort_by { |memo| memo[:date] }.each do |memo|
|
340
|
+
case memo.privacy
|
341
|
+
when "private"; priv = " `private`"
|
342
|
+
when "personal"; priv = " `personal`"
|
343
|
+
else priv = ""
|
344
|
+
end
|
345
|
+
message << " #{memo.type} #{memo.date.gsub("-", "/")[0..9]}: #{memo.status} #{memo.message} (#{memo.user} #{memo.memo_id})#{priv}"
|
346
|
+
end
|
347
|
+
end
|
348
|
+
all_memos[:no_topic] = []
|
349
|
+
all_memos.each do |topic, mems|
|
350
|
+
unless mems.empty?
|
351
|
+
message << " _`#{topic}`_:"
|
352
|
+
mems.sort_by { |m| m[:date] }.each do |memo|
|
353
|
+
case memo.privacy
|
354
|
+
when "private"; priv = " `private`"
|
355
|
+
when "personal"; priv = " `personal`"
|
356
|
+
else priv = ""
|
357
|
+
end
|
358
|
+
message << " #{memo.type} #{memo.date.gsub("-", "/")[0..9]}: #{memo.status} #{memo.message} (#{memo.user} #{memo.memo_id})#{priv}"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
217
364
|
unless team.info.empty?
|
218
365
|
team.info.split("\n").each do |m|
|
219
366
|
message << ">#{m}"
|
@@ -236,7 +383,7 @@ class SlackSmartBot
|
|
236
383
|
if team_name.to_s != ""
|
237
384
|
message = "\n\n:palm_tree: On vacation / "
|
238
385
|
message += ":spiral_calendar_pad: In a meeting / "
|
239
|
-
message += ":face_with_thermometer: Sick leave / "
|
386
|
+
message += ":face_with_thermometer: :baby: Sick leave / "
|
240
387
|
message += ":white_circle: Away / "
|
241
388
|
message += ":large_yellow_circle: Available in remote timezone / "
|
242
389
|
message += ":large_green_circle: Available"
|
@@ -246,6 +393,9 @@ class SlackSmartBot
|
|
246
393
|
messages.each do |msg|
|
247
394
|
respond msg, dest, unfurl_links: false, unfurl_media: false
|
248
395
|
end
|
396
|
+
unless team_name.to_s.empty?
|
397
|
+
see_vacations_team(user, team_name, Date.today.strftime("%Y/%m/%d"), add_stats: false)
|
398
|
+
end
|
249
399
|
end
|
250
400
|
end
|
251
401
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
def see_vacations(user, from_user: '', add_stats: true)
|
3
|
+
save_stats(__method__) if add_stats
|
4
|
+
|
5
|
+
get_vacations()
|
6
|
+
|
7
|
+
from_user_name = ''
|
8
|
+
|
9
|
+
if from_user.empty?
|
10
|
+
from_user_name = user.name
|
11
|
+
else
|
12
|
+
@users = get_users() if @users.empty?
|
13
|
+
user_info = @users.select{|u| u.id == from_user or (u.key?(:enterprise_user) and u.enterprise_user.id == from_user)}[-1]
|
14
|
+
from_user_name = user_info.name
|
15
|
+
end
|
16
|
+
|
17
|
+
if !@vacations.key?(from_user_name) or @vacations[from_user_name].periods.empty?
|
18
|
+
if from_user.empty?
|
19
|
+
respond "You didn't add any time off yet. Use `add vacation from YYYY/MM/DD to YYYY/MM/DD`"
|
20
|
+
else
|
21
|
+
respond "No time off added yet for <@#{from_user}>"
|
22
|
+
end
|
23
|
+
else
|
24
|
+
messages = []
|
25
|
+
messages << "*Time off <@#{from_user}>*" if !from_user.empty?
|
26
|
+
today = Date.today.strftime("%Y/%m/%d")
|
27
|
+
current_added = false
|
28
|
+
past_added = false
|
29
|
+
@vacations[from_user_name].periods.sort_by { |v| v[:from]}.reverse.each do |vac|
|
30
|
+
if !current_added and vac.to >= today
|
31
|
+
messages << "*Current and future periods*"
|
32
|
+
current_added = true
|
33
|
+
end
|
34
|
+
if !past_added and vac.to < today and from_user.empty?
|
35
|
+
messages << "\n*Past periods*"
|
36
|
+
past_added = true
|
37
|
+
end
|
38
|
+
unless !from_user.empty? and vac.to < today
|
39
|
+
if !from_user.empty?
|
40
|
+
icon = ":beach_with_umbrella:"
|
41
|
+
elsif vac.type == 'vacation'
|
42
|
+
icon = ':palm_tree:'
|
43
|
+
elsif vac.type == 'sick'
|
44
|
+
icon = ':face_with_thermometer:'
|
45
|
+
elsif vac.type == 'sick child'
|
46
|
+
icon = ':baby:'
|
47
|
+
end
|
48
|
+
if vac.from == vac.to
|
49
|
+
messages << " #{icon} #{vac.from} ##{vac.vacation_id}"
|
50
|
+
else
|
51
|
+
messages << " #{icon} #{vac.from} -> #{vac.to} ##{vac.vacation_id}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
respond messages.join("\n")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
def see_vacations_team(user, team_name, date, add_stats: true)
|
3
|
+
save_stats(__method__) if add_stats
|
4
|
+
|
5
|
+
get_teams()
|
6
|
+
teams = @teams.deep_copy
|
7
|
+
if teams.empty?
|
8
|
+
respond "There are no teams added yet. Use `add team` command to add a team."
|
9
|
+
elsif team_name.to_s != "" and !teams.key?(team_name.to_sym) and (teams.keys.select { |t| (t.to_s.gsub("-", "").gsub("_", "") == team_name.to_s) }).empty?
|
10
|
+
respond "It seems like the team *#{team_name}* doesn't exist.\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
|
11
|
+
else
|
12
|
+
teams.each do |name, team|
|
13
|
+
if team_name==name.to_s or (name.to_s.gsub("-", "").gsub("_", "") == team_name.to_s)
|
14
|
+
team_name = name.to_s
|
15
|
+
break
|
16
|
+
end
|
17
|
+
end
|
18
|
+
date.gsub!('-','/')
|
19
|
+
get_vacations()
|
20
|
+
team = teams[team_name.to_sym]
|
21
|
+
assigned_members = team.members.values.flatten
|
22
|
+
assigned_members.uniq!
|
23
|
+
assigned_members.dup.each do |m|
|
24
|
+
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]
|
25
|
+
assigned_members.delete(m) if user_info.nil? or user_info.deleted
|
26
|
+
end
|
27
|
+
|
28
|
+
channels_members = []
|
29
|
+
all_team_members = assigned_members.dup
|
30
|
+
if team.channels.key?("members")
|
31
|
+
team_members = []
|
32
|
+
team.channels["members"].each do |ch|
|
33
|
+
get_channels_name_and_id() unless @channels_id.key?(ch)
|
34
|
+
tm = get_channel_members(@channels_id[ch])
|
35
|
+
if tm.nil?
|
36
|
+
respond ":exclamation: Add the Smart Bot to *##{ch}* channel to be able to get the list of members.", dest
|
37
|
+
else
|
38
|
+
channels_members << @channels_id[ch]
|
39
|
+
tm.each do |m|
|
40
|
+
user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
|
41
|
+
team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
team_members.flatten!
|
46
|
+
all_team_members += team_members
|
47
|
+
all_team_members.uniq!
|
48
|
+
end
|
49
|
+
unless all_team_members.empty?
|
50
|
+
blocks_header =
|
51
|
+
{
|
52
|
+
"type": "context",
|
53
|
+
elements: [
|
54
|
+
{
|
55
|
+
type: "mrkdwn",
|
56
|
+
text: "*Time Off #{team_name} team* from #{date} ",
|
57
|
+
},
|
58
|
+
],
|
59
|
+
}
|
60
|
+
|
61
|
+
from = Date.parse(date, "%Y/%m/%d")
|
62
|
+
blocks = []
|
63
|
+
all_team_members.each do |m|
|
64
|
+
@users = get_users() if @users.empty?
|
65
|
+
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]
|
66
|
+
unless info.nil?
|
67
|
+
info = get_user_info(info.id)
|
68
|
+
if @vacations.key?(m)
|
69
|
+
v = ""
|
70
|
+
(from..(from+20)).each do |d|
|
71
|
+
v+="#{d.strftime("%d")} " if d.wday==1 or d==from
|
72
|
+
on_vacation = false
|
73
|
+
@vacations[m].periods.each do |p|
|
74
|
+
if p.from <= d.strftime("%Y/%m/%d") and p.to >= d.strftime("%Y/%m/%d")
|
75
|
+
if d.wday == 0 or d.wday == 6
|
76
|
+
v+=":large_orange_square: "
|
77
|
+
else
|
78
|
+
v+=":large_red_square: "
|
79
|
+
end
|
80
|
+
on_vacation=true
|
81
|
+
break
|
82
|
+
end
|
83
|
+
end
|
84
|
+
unless on_vacation
|
85
|
+
if d.wday == 0 or d.wday == 6
|
86
|
+
v += ":large_yellow_square: "
|
87
|
+
else
|
88
|
+
v+= ":white_square: "
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
v = ""
|
94
|
+
(from..(from+20)).each do |d|
|
95
|
+
if d.wday==1 or d==from
|
96
|
+
v += "#{d.strftime("%d")} "
|
97
|
+
end
|
98
|
+
if d.wday == 0 or d.wday == 6
|
99
|
+
v += ":large_yellow_square: "
|
100
|
+
else
|
101
|
+
v += ":white_square: "
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
blocks << {
|
107
|
+
type: "context",
|
108
|
+
elements: [
|
109
|
+
{
|
110
|
+
type: "image",
|
111
|
+
image_url: info.user.profile.image_24,
|
112
|
+
alt_text: info.user.name,
|
113
|
+
},
|
114
|
+
{
|
115
|
+
type: "plain_text",
|
116
|
+
text: v
|
117
|
+
}
|
118
|
+
],
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
first = true
|
123
|
+
blocks.each_slice(10).each do |b|
|
124
|
+
if first
|
125
|
+
b.unshift(blocks_header)
|
126
|
+
first = false
|
127
|
+
end
|
128
|
+
respond blocks: b
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class SlackSmartBot
|
2
|
+
def set_memo_status(user, team_name, memo_id, status)
|
3
|
+
save_stats(__method__) if answer.empty?
|
4
|
+
|
5
|
+
get_teams()
|
6
|
+
if @teams.key?(team_name.to_sym)
|
7
|
+
assigned_members = @teams[team_name.to_sym].members.values.flatten
|
8
|
+
assigned_members.uniq!
|
9
|
+
all_team_members = assigned_members.dup
|
10
|
+
team_members = []
|
11
|
+
if @teams[team_name.to_sym].channels.key?("members")
|
12
|
+
@teams[team_name.to_sym].channels["members"].each do |ch|
|
13
|
+
get_channels_name_and_id() unless @channels_id.key?(ch)
|
14
|
+
tm = get_channel_members(@channels_id[ch])
|
15
|
+
tm.each do |m|
|
16
|
+
user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
|
17
|
+
team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
team_members.flatten!
|
22
|
+
team_members.uniq!
|
23
|
+
all_team_members += team_members
|
24
|
+
all_team_members.uniq!
|
25
|
+
end
|
26
|
+
|
27
|
+
if !@teams.key?(team_name.to_sym)
|
28
|
+
respond "It seems like the team *#{team_name}* doesn't exist.\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
|
29
|
+
elsif !(all_team_members + config.masters).flatten.include?(user.name)
|
30
|
+
respond "You have to be a member of the team or a Master admin to be able to set the status of a memo."
|
31
|
+
elsif !@teams[team_name.to_sym].key?(:memos) or @teams[team_name.to_sym][:memos].empty? or !@teams[team_name.to_sym][:memos].memo_id.include?(memo_id.to_i)
|
32
|
+
respond "It seems like there is no memo with id #{memo_id}"
|
33
|
+
elsif @teams[team_name.to_sym][:memos].memo_id.include?(memo_id.to_i)
|
34
|
+
memo_selected = @teams[team_name.to_sym][:memos].select { |m| m.memo_id == memo_id.to_i }[-1]
|
35
|
+
if memo_selected.type == 'jira' or memo_selected.type == 'github'
|
36
|
+
#todo: add tests for jira and github
|
37
|
+
respond "The memo specified is a #{memo_selected.type} and the status in those cases are linked to the specific issues in #{memo_selected.type}."
|
38
|
+
elsif memo_selected.privacy == "personal" and memo_selected.user != user.name
|
39
|
+
respond "Only the creator can set the status of a personal memo."
|
40
|
+
else
|
41
|
+
answer_delete
|
42
|
+
memos = []
|
43
|
+
message = ""
|
44
|
+
get_teams()
|
45
|
+
@teams[team_name.to_sym][:memos].each do |memo|
|
46
|
+
if memo.memo_id == memo_id.to_i
|
47
|
+
memo.status = status
|
48
|
+
message = memo.message
|
49
|
+
end
|
50
|
+
memos << memo
|
51
|
+
end
|
52
|
+
@teams[team_name.to_sym][:memos] = memos
|
53
|
+
update_teams()
|
54
|
+
respond "The memo status has been updated on team #{team_name}: #{message}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -2,9 +2,29 @@ class SlackSmartBot
|
|
2
2
|
def update_team(user, team_name, new_name: "", new_info: "", delete_opts: "", add_opts: "")
|
3
3
|
save_stats(__method__)
|
4
4
|
get_teams()
|
5
|
+
if @teams.key?(team_name.to_sym)
|
6
|
+
assigned_members = @teams[team_name.to_sym].members.values.flatten
|
7
|
+
assigned_members.uniq!
|
8
|
+
all_team_members = assigned_members.dup
|
9
|
+
team_members = []
|
10
|
+
if @teams[team_name.to_sym].channels.key?("members")
|
11
|
+
@teams[team_name.to_sym].channels["members"].each do |ch|
|
12
|
+
get_channels_name_and_id() unless @channels_id.key?(ch)
|
13
|
+
tm = get_channel_members(@channels_id[ch])
|
14
|
+
tm.each do |m|
|
15
|
+
user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
|
16
|
+
team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
team_members.flatten!
|
21
|
+
team_members.uniq!
|
22
|
+
all_team_members += team_members
|
23
|
+
all_team_members.uniq!
|
24
|
+
end
|
5
25
|
if !@teams.key?(team_name.to_sym)
|
6
26
|
respond "It seems like the team *#{team_name}* doesn't exist.\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
|
7
|
-
elsif !(
|
27
|
+
elsif !(all_team_members + [@teams[team_name.to_sym].creator] + config.masters).flatten.include?(user.name)
|
8
28
|
respond "You have to be a member of the team, the creator or a Master admin to be able to update this team."
|
9
29
|
else
|
10
30
|
wrong = false
|
@@ -69,6 +89,7 @@ class SlackSmartBot
|
|
69
89
|
last_type = opt
|
70
90
|
elsif opt.match(/<@(\w+)>/i)
|
71
91
|
member_id = $1
|
92
|
+
last_type = 'no_type' if last_type.nil?
|
72
93
|
member_info = @users.select { |u| u.id == member_id or (u.key?(:enterprise_user) and u.enterprise_user.id == member_id) }[-1]
|
73
94
|
@teams[team_name.to_sym].members[last_type] ||= []
|
74
95
|
@teams[team_name.to_sym].members[last_type] << member_info.name
|