slack-smart-bot 1.10.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +134 -23
  3. data/lib/slack/smart-bot/comm/delete.rb +13 -0
  4. data/lib/slack/smart-bot/comm/dont_understand.rb +2 -2
  5. data/lib/slack/smart-bot/comm/get_channel_members.rb +7 -3
  6. data/lib/slack/smart-bot/comm/get_presence.rb +20 -0
  7. data/lib/slack/smart-bot/comm/get_users.rb +1 -1
  8. data/lib/slack/smart-bot/comm/respond.rb +24 -13
  9. data/lib/slack/smart-bot/comm/send_msg_user.rb +12 -11
  10. data/lib/slack/smart-bot/comm/set_status.rb +21 -0
  11. data/lib/slack/smart-bot/comm.rb +3 -0
  12. data/lib/slack/smart-bot/commands/general/add_admin.rb +51 -0
  13. data/lib/slack/smart-bot/commands/general/add_announcement.rb +1 -1
  14. data/lib/slack/smart-bot/commands/general/add_memo_team.rb +117 -0
  15. data/lib/slack/smart-bot/commands/general/add_team.rb +80 -0
  16. data/lib/slack/smart-bot/commands/general/add_vacation.rb +51 -0
  17. data/lib/slack/smart-bot/commands/general/allow_access.rb +67 -0
  18. data/lib/slack/smart-bot/commands/general/bot_help.rb +20 -11
  19. data/lib/slack/smart-bot/commands/general/delete_announcement.rb +1 -1
  20. data/lib/slack/smart-bot/commands/general/delete_memo_team.rb +69 -0
  21. data/lib/slack/smart-bot/commands/general/delete_share.rb +1 -1
  22. data/lib/slack/smart-bot/commands/general/delete_team.rb +54 -0
  23. data/lib/slack/smart-bot/commands/general/deny_access.rb +36 -0
  24. data/lib/slack/smart-bot/commands/general/ping_team.rb +100 -0
  25. data/lib/slack/smart-bot/commands/general/poster.rb +116 -0
  26. data/lib/slack/smart-bot/commands/general/remove_admin.rb +58 -0
  27. data/lib/slack/smart-bot/commands/general/remove_vacation.rb +27 -0
  28. data/lib/slack/smart-bot/commands/general/see_access.rb +24 -0
  29. data/lib/slack/smart-bot/commands/general/see_admins.rb +33 -0
  30. data/lib/slack/smart-bot/commands/general/see_announcements.rb +7 -5
  31. data/lib/slack/smart-bot/commands/general/see_command_ids.rb +29 -0
  32. data/lib/slack/smart-bot/commands/general/see_favorite_commands.rb +3 -4
  33. data/lib/slack/smart-bot/commands/general/see_statuses.rb +34 -21
  34. data/lib/slack/smart-bot/commands/general/see_teams.rb +402 -0
  35. data/lib/slack/smart-bot/commands/general/see_vacations.rb +58 -0
  36. data/lib/slack/smart-bot/commands/general/see_vacations_team.rb +136 -0
  37. data/lib/slack/smart-bot/commands/general/set_memo_status.rb +58 -0
  38. data/lib/slack/smart-bot/commands/general/share_messages.rb +1 -1
  39. data/lib/slack/smart-bot/commands/general/update_team.rb +130 -0
  40. data/lib/slack/smart-bot/commands/general_bot_commands.rb +442 -13
  41. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +2 -1
  42. data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +2 -1
  43. data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +2 -1
  44. data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +2 -1
  45. data/lib/slack/smart-bot/commands/on_bot/admin/pause_routine.rb +2 -1
  46. data/lib/slack/smart-bot/commands/on_bot/admin/remove_routine.rb +2 -1
  47. data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +3 -2
  48. data/lib/slack/smart-bot/commands/on_bot/admin/see_result_routine.rb +2 -1
  49. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +10 -9
  50. data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +2 -1
  51. data/lib/slack/smart-bot/commands/on_bot/admin/start_routine.rb +2 -1
  52. data/lib/slack/smart-bot/commands/on_bot/admin/stop_using_rules_on.rb +2 -1
  53. data/lib/slack/smart-bot/commands/on_bot/admin_master/delete_message.rb +25 -0
  54. data/lib/slack/smart-bot/commands/on_bot/admin_master/get_bot_logs.rb +1 -0
  55. data/lib/slack/smart-bot/commands/on_bot/admin_master/react_to.rb +3 -1
  56. data/lib/slack/smart-bot/commands/on_bot/admin_master/send_message.rb +15 -2
  57. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +2 -1
  58. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +5 -4
  59. data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +416 -0
  60. data/lib/slack/smart-bot/commands/{general → on_bot/general}/bot_status.rb +1 -0
  61. data/lib/slack/smart-bot/commands/{general → on_bot/general}/leaderboard.rb +1 -0
  62. data/lib/slack/smart-bot/commands/{general → on_bot/general}/stop_using_rules.rb +1 -0
  63. data/lib/slack/smart-bot/commands/{general → on_bot/general}/suggest_command.rb +6 -0
  64. data/lib/slack/smart-bot/commands/{general → on_bot/general}/use_rules.rb +1 -0
  65. data/lib/slack/smart-bot/commands/{general → on_bot/general}/whats_new.rb +2 -1
  66. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +2 -1
  67. data/lib/slack/smart-bot/commands/on_bot/kill_repl.rb +32 -0
  68. data/lib/slack/smart-bot/commands/on_bot/repl.rb +73 -15
  69. data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +1 -0
  70. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +117 -28
  71. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +2 -1
  72. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +3 -2
  73. data/lib/slack/smart-bot/commands/on_master/admin/kill_bot_on_channel.rb +5 -4
  74. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +3 -2
  75. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +2 -1
  76. data/lib/slack/smart-bot/commands/on_master/admin_master/publish_announcements.rb +6 -3
  77. data/lib/slack/smart-bot/commands/on_master/admin_master/set_general_message.rb +2 -1
  78. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +2 -1
  79. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +1 -0
  80. data/lib/slack/smart-bot/commands/on_master/where_smartbot.rb +41 -0
  81. data/lib/slack/smart-bot/commands.rb +30 -7
  82. data/lib/slack/smart-bot/listen.rb +30 -30
  83. data/lib/slack/smart-bot/process.rb +53 -23
  84. data/lib/slack/smart-bot/process_first.rb +2 -2
  85. data/lib/slack/smart-bot/treat_message.rb +23 -17
  86. data/lib/slack/smart-bot/utils/build_help.rb +1 -1
  87. data/lib/slack/smart-bot/utils/check_vacations.rb +43 -0
  88. data/lib/slack/smart-bot/utils/create_routine_thread.rb +1 -1
  89. data/lib/slack/smart-bot/utils/get_access_channels.rb +13 -0
  90. data/lib/slack/smart-bot/utils/get_admins_channels.rb +33 -0
  91. data/lib/slack/smart-bot/utils/get_bots_created.rb +27 -10
  92. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +7 -2
  93. data/lib/slack/smart-bot/utils/get_command_ids.rb +84 -0
  94. data/lib/slack/smart-bot/utils/get_help.rb +36 -19
  95. data/lib/slack/smart-bot/utils/get_repls.rb +22 -2
  96. data/lib/slack/smart-bot/utils/get_routines.rb +22 -2
  97. data/lib/slack/smart-bot/utils/get_teams.rb +22 -0
  98. data/lib/slack/smart-bot/utils/get_vacations.rb +22 -0
  99. data/lib/slack/smart-bot/utils/has_access.rb +25 -9
  100. data/lib/slack/smart-bot/utils/is_admin.rb +27 -0
  101. data/lib/slack/smart-bot/utils/save_stats.rb +52 -42
  102. data/lib/slack/smart-bot/utils/save_status.rb +22 -7
  103. data/lib/slack/smart-bot/utils/update_access_channels.rb +8 -0
  104. data/lib/slack/smart-bot/utils/update_admins_channels.rb +25 -0
  105. data/lib/slack/smart-bot/utils/update_bots_file.rb +28 -7
  106. data/lib/slack/smart-bot/utils/update_repls.rb +7 -4
  107. data/lib/slack/smart-bot/utils/update_routines.rb +9 -3
  108. data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +13 -6
  109. data/lib/slack/smart-bot/utils/update_teams.rb +16 -0
  110. data/lib/slack/smart-bot/utils/update_vacations.rb +16 -0
  111. data/lib/slack/smart-bot/utils.rb +11 -0
  112. data/lib/slack-smart-bot.rb +50 -12
  113. data/lib/slack-smart-bot_general_commands.rb +16 -1
  114. data/whats_new.txt +12 -30
  115. metadata +78 -21
  116. data/lib/slack/smart-bot/commands/general/bot_stats.rb +0 -314
@@ -0,0 +1,402 @@
1
+ class SlackSmartBot
2
+ def see_teams(user, team_name, search = "", 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
+ users_link = (Thread.current[:dest][0] == "D")
13
+ filter = (search != "")
14
+ react :runner
15
+ @users = get_users() if add_stats
16
+
17
+ messages = []
18
+ search_members = []
19
+ search_channels = []
20
+ search_info = []
21
+ if filter
22
+ search.split(" ").each do |s|
23
+ if s.match(/<@(\w+)>/i)
24
+ m = $1
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]
26
+ search_members << user_info.name unless user_info.nil?
27
+ elsif s.match(/<#(\w+)\|[^>]*>/i)
28
+ c = $1.upcase
29
+ search_channels << @channels_name[c] if @channels_name.key?(c)
30
+ else
31
+ search_info << s
32
+ end
33
+ end
34
+ end
35
+ if team_name.to_s == "" and search.to_s == ""
36
+ dest = :on_thread
37
+ messages.unshift("Since there are many lines returned the results are returned on a thread by default.")
38
+ else
39
+ dest = Thread.current[:dest]
40
+ end
41
+
42
+ teams.each do |name, team|
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("-", "").gsub("_", "") == team_name.to_s)
45
+ message = []
46
+ message << "*#{name.capitalize}*"
47
+
48
+ if filter and search_info.size > 0
49
+ all_info = true
50
+ search_info.each do |s|
51
+ if (team.members.keys.find { |e| /#{s}/i =~ e })
52
+ add = true
53
+ break
54
+ end
55
+ if !name.match?(/#{s}/i)
56
+ all_info = false
57
+ break
58
+ end
59
+ end
60
+ add = true if all_info
61
+ end
62
+
63
+ message << " > *_members_*"
64
+
65
+ assigned_members = team.members.values.flatten
66
+ assigned_members.uniq!
67
+ assigned_members.dup.each do |m|
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
+ assigned_members.delete(m) if user_info.nil? or user_info.deleted
70
+ end
71
+ channels_members = []
72
+ all_team_members = assigned_members.dup
73
+ if team.channels.key?("members")
74
+ team_members = []
75
+ team.channels["members"].each do |ch|
76
+ get_channels_name_and_id() unless @channels_id.key?(ch)
77
+ tm = get_channel_members(@channels_id[ch])
78
+ if tm.nil?
79
+ respond ":exclamation: Add the Smart Bot to *##{ch}* channel to be able to get the list of members.", dest
80
+ else
81
+ channels_members << @channels_id[ch]
82
+ tm.each do |m|
83
+ user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
84
+ team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
85
+ end
86
+ end
87
+ end
88
+ team_members.flatten!
89
+ team_members.uniq!
90
+ unassigned_members = team_members - assigned_members
91
+ unassigned_members.delete(config.nick)
92
+ not_on_team_channel = assigned_members - team_members
93
+ all_team_members += team_members
94
+ else
95
+ unassigned_members = []
96
+ not_on_team_channel = []
97
+ end
98
+ unless unassigned_members.empty?
99
+ um = unassigned_members.dup
100
+ um.each do |m|
101
+ user_info = @users.select { |u| u.name == m or (u.key?(:enterprise_user) and u.enterprise_user.name == m) }[-1]
102
+ unless user_info.nil? or user_info.profile.title.to_s == ""
103
+ team.members[user_info.profile.title.to_snake_case] ||= []
104
+ team.members[user_info.profile.title.to_snake_case] << m
105
+ unassigned_members.delete(m)
106
+ end
107
+ end
108
+ unless unassigned_members.empty?
109
+ team.members["unassigned"] ||= []
110
+ team.members["unassigned"] += unassigned_members
111
+ team.members["unassigned"].sort!
112
+ end
113
+ end
114
+ unless not_on_team_channel.empty?
115
+ team.members["not on members channel"] = not_on_team_channel
116
+ team.members["not on members channel"].sort!
117
+ end
118
+ add = true if (team.members.values.flatten & search_members).size > 0
119
+ add = true if (team.channels.values.flatten & search_channels).size > 0
120
+ if filter and search_info.size > 0
121
+ all_info = true
122
+ search_info.each do |s|
123
+ if (team.members.keys.find { |e| /#{s}/i =~ e })
124
+ add = true
125
+ break
126
+ end
127
+ if !team.info.match?(/#{s}/i)
128
+ all_info = false
129
+ break
130
+ end
131
+ end
132
+ add = true if all_info
133
+ end
134
+
135
+ if add
136
+ if team_name.to_s != ""
137
+ team.members.each do |type, members|
138
+ message << " _`#{type}`_: "
139
+ members.each do |member|
140
+ types = [":palm_tree:", ":spiral_calendar_pad:", ":face_with_thermometer:", ":baby:"]
141
+ member_info = @users.select { |u| u.name == member }[-1]
142
+ if !member_info.nil? and !member_info.deleted
143
+ member_id = member_info.id
144
+ info = get_user_info(member_id)
145
+ emoji = info.user.profile.status_emoji
146
+ if types.include?(emoji)
147
+ status = emoji
148
+ else
149
+ active = (get_presence(member_id).presence.to_s == "active")
150
+ if active
151
+ user_info = @users.select { |u| u.id == member_id or (u.key?(:enterprise_user) and u.enterprise_user.name == member_id) }[-1]
152
+ if (user_info.tz_offset - user.tz_offset).abs <= (4 * 3600)
153
+ status = ":large_green_circle:"
154
+ else
155
+ status = ":large_yellow_circle:"
156
+ end
157
+ else
158
+ status = ":white_circle:"
159
+ end
160
+ end
161
+ else
162
+ status = ":exclamation:"
163
+ end
164
+ unless status == ":exclamation:"
165
+ if users_link
166
+ message[-1] += " #{status}<@#{member}>, "
167
+ else
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
174
+ end
175
+ message[-1] += " #{status} #{name}, "
176
+ end
177
+ end
178
+ end
179
+ end
180
+ message[-1].chop!
181
+ message[-1].chop!
182
+ end
183
+ else
184
+ team.members.each do |type, members|
185
+ if users_link
186
+ message << " _`#{type}`_: <@#{members.join(">, <@")}>"
187
+ else
188
+ membersn = []
189
+ members.each do |m|
190
+ user_info = @users.select { |u| u.name == m or (u.key?(:enterprise_user) and u.enterprise_user.name == m) }[-1]
191
+ unless user_info.nil? or user_info.deleted
192
+ if user_info.profile.display_name == ""
193
+ name = user_info.name
194
+ else
195
+ name = user_info.profile.display_name
196
+ end
197
+ membersn << name
198
+ end
199
+ end
200
+ message << " _`#{type}`_: #{membersn.join(" / ")}"
201
+ end
202
+ end
203
+ end
204
+ end
205
+
206
+ if add
207
+ message << " > *_channels_*"
208
+ team.channels.each do |type, channels|
209
+ channel_ids = []
210
+ channels.each do |ch|
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)))
213
+ channel_ids << @channels_id[ch]
214
+ end
215
+ end
216
+ message << " _`#{type}`_: <##{channel_ids.join("> <#")}>" unless channel_ids.empty?
217
+ end
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
+
364
+ unless team.info.empty?
365
+ team.info.split("\n").each do |m|
366
+ message << ">#{m}"
367
+ end
368
+ message << "> "
369
+ message << "> "
370
+ end
371
+ messages << message.join("\n")
372
+ end
373
+ end
374
+ end
375
+ unreact :runner
376
+ if messages.empty?
377
+ if filter
378
+ respond "It seems like we didn't find any team with the criteria supplied. Call `see teams` for a full list of teams."
379
+ else
380
+ respond "It seems like there are no teams added.\nUse `add team TEAM_NAME PROPERTIES` to add one. Call `bot help add team` for extended info."
381
+ end
382
+ else
383
+ if team_name.to_s != ""
384
+ message = "\n\n:palm_tree: On vacation / "
385
+ message += ":spiral_calendar_pad: In a meeting / "
386
+ message += ":face_with_thermometer: :baby: Sick leave / "
387
+ message += ":white_circle: Away / "
388
+ message += ":large_yellow_circle: Available in remote timezone / "
389
+ message += ":large_green_circle: Available"
390
+ messages[-1] << message
391
+ messages[-1] << "\n:information_source: Remote Time zone is >4h away from your current (#{user.tz_label})"
392
+ end
393
+ messages.each do |msg|
394
+ respond msg, dest, unfurl_links: false, unfurl_media: false
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
399
+ end
400
+ end
401
+ end
402
+ 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
@@ -24,7 +24,7 @@ class SlackSmartBot
24
24
  else
25
25
  type = :reaction
26
26
  end
27
- if File.exists?("#{config.path}/shares/#{from_channel}.csv")
27
+ if File.exist?("#{config.path}/shares/#{from_channel}.csv")
28
28
  t = CSV.table("#{config.path}/shares/#{from_channel}.csv", headers: ['share_id', 'user_deleted', 'user_created', 'date', 'time', 'type', 'to_channel', 'condition'])
29
29
  @shares[from_channel] = t
30
30
  if t.size>0