slack-smart-bot 1.14.1 → 1.15.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 (176) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +237 -48
  3. data/img/chat_gpt.png +0 -0
  4. data/img/chat_gpt_session.png +0 -0
  5. data/img/chat_gpt_share.png +0 -0
  6. data/img/command_add_sc.png +0 -0
  7. data/img/command_bot_help_echo.png +0 -0
  8. data/img/command_loop.png +0 -0
  9. data/img/command_my_timeoff.png +0 -0
  10. data/img/command_recap.png +0 -0
  11. data/img/command_repl1.png +0 -0
  12. data/img/command_repl2.png +0 -0
  13. data/img/command_ruby.png +0 -0
  14. data/img/command_run_repl.png +0 -0
  15. data/img/command_see_announcements.png +0 -0
  16. data/img/command_see_statuses.png +0 -0
  17. data/img/command_see_team.png +0 -0
  18. data/img/command_summarize.png +0 -0
  19. data/img/commands_inline.png +0 -0
  20. data/img/commands_on_demand.png +0 -0
  21. data/img/commands_on_external_call.png +0 -0
  22. data/img/image_editing.png +0 -0
  23. data/img/image_generation.png +0 -0
  24. data/img/image_variations.png +0 -0
  25. data/img/openai-300.png +0 -0
  26. data/img/openai.png +0 -0
  27. data/img/slack-300.png +0 -0
  28. data/img/slack.png +0 -0
  29. data/img/smart-bot-150.png +0 -0
  30. data/img/smart-bot-profile-pic-2.png +0 -0
  31. data/img/smart-bot-profile-pic.png +0 -0
  32. data/img/smart-bot.png +0 -0
  33. data/img/whisper.png +0 -0
  34. data/lib/slack/smart-bot/ai/open_ai/connect.rb +165 -43
  35. data/lib/slack/smart-bot/ai/open_ai/models.rb +61 -9
  36. data/lib/slack/smart-bot/ai/open_ai/send_gpt_chat.rb +67 -11
  37. data/lib/slack/smart-bot/ai/open_ai/send_image_edit.rb +4 -3
  38. data/lib/slack/smart-bot/ai/open_ai/send_image_generation.rb +4 -4
  39. data/lib/slack/smart-bot/ai/open_ai/send_image_variation.rb +4 -3
  40. data/lib/slack/smart-bot/ai/open_ai/whisper_transcribe.rb +4 -3
  41. data/lib/slack/smart-bot/comm/ask.rb +20 -8
  42. data/lib/slack/smart-bot/comm/dont_understand.rb +2 -2
  43. data/lib/slack/smart-bot/comm/event_hello.rb +30 -1
  44. data/lib/slack/smart-bot/comm/get_channel_members.rb +2 -1
  45. data/lib/slack/smart-bot/comm/get_presence.rb +1 -0
  46. data/lib/slack/smart-bot/comm/get_smartbot_team_info.rb +10 -0
  47. data/lib/slack/smart-bot/comm/get_user_info.rb +45 -6
  48. data/lib/slack/smart-bot/comm/get_users.rb +8 -1
  49. data/lib/slack/smart-bot/comm/respond.rb +225 -196
  50. data/lib/slack/smart-bot/comm/send_msg_channel.rb +2 -2
  51. data/lib/slack/smart-bot/comm/send_msg_user.rb +10 -9
  52. data/lib/slack/smart-bot/comm/unreact.rb +2 -2
  53. data/lib/slack/smart-bot/comm.rb +1 -0
  54. data/lib/slack/smart-bot/commands/general/add_admin.rb +16 -6
  55. data/lib/slack/smart-bot/commands/general/add_announcement.rb +3 -3
  56. data/lib/slack/smart-bot/commands/general/add_vacation.rb +28 -12
  57. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat.rb +272 -23
  58. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_add_collaborator.rb +42 -0
  59. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_copy_session.rb +89 -0
  60. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_delete_session.rb +45 -0
  61. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_get_prompts.rb +41 -0
  62. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_list_sessions.rb +81 -0
  63. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_share_session.rb +52 -0
  64. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_use_model.rb +52 -0
  65. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_edit_image.rb +14 -11
  66. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_generate_image.rb +15 -11
  67. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_models.rb +29 -17
  68. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_variations_image.rb +16 -13
  69. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_whisper.rb +13 -7
  70. data/lib/slack/smart-bot/commands/general/allow_access.rb +8 -4
  71. data/lib/slack/smart-bot/commands/general/bot_help.rb +24 -9
  72. data/lib/slack/smart-bot/commands/general/bye_bot.rb +9 -5
  73. data/lib/slack/smart-bot/commands/general/delete_announcement.rb +2 -1
  74. data/lib/slack/smart-bot/commands/general/delete_share.rb +2 -1
  75. data/lib/slack/smart-bot/commands/general/deny_access.rb +1 -1
  76. data/lib/slack/smart-bot/commands/general/get_smartbot_readme.rb +15 -0
  77. data/lib/slack/smart-bot/commands/general/hi_bot.rb +10 -4
  78. data/lib/slack/smart-bot/commands/general/personal_settings.rb +26 -8
  79. data/lib/slack/smart-bot/commands/general/poster.rb +26 -2
  80. data/lib/slack/smart-bot/commands/general/public_holidays.rb +14 -24
  81. data/lib/slack/smart-bot/commands/general/recap.rb +399 -0
  82. data/lib/slack/smart-bot/commands/general/remove_admin.rb +19 -9
  83. data/lib/slack/smart-bot/commands/general/remove_vacation.rb +23 -6
  84. data/lib/slack/smart-bot/commands/general/see_access.rb +2 -1
  85. data/lib/slack/smart-bot/commands/general/see_admins.rb +8 -4
  86. data/lib/slack/smart-bot/commands/general/see_announcements.rb +5 -5
  87. data/lib/slack/smart-bot/commands/general/see_favorite_commands.rb +4 -4
  88. data/lib/slack/smart-bot/commands/general/see_shares.rb +1 -1
  89. data/lib/slack/smart-bot/commands/general/see_vacations.rb +34 -17
  90. data/lib/slack/smart-bot/commands/general/set_public_holidays.rb +4 -2
  91. data/lib/slack/smart-bot/commands/general/share_messages.rb +3 -3
  92. data/lib/slack/smart-bot/commands/general/summarize.rb +191 -0
  93. data/lib/slack/smart-bot/commands/general/teams/add_team.rb +4 -8
  94. data/lib/slack/smart-bot/commands/general/teams/delete_team.rb +3 -3
  95. data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team.rb +34 -29
  96. data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team_comment.rb +1 -1
  97. data/lib/slack/smart-bot/commands/general/teams/memos/delete_memo_team.rb +6 -4
  98. data/lib/slack/smart-bot/commands/general/teams/memos/see_memo_team.rb +26 -15
  99. data/lib/slack/smart-bot/commands/general/teams/memos/see_memos_team.rb +33 -24
  100. data/lib/slack/smart-bot/commands/general/teams/memos/set_memo_status.rb +4 -4
  101. data/lib/slack/smart-bot/commands/general/teams/ping_team.rb +10 -8
  102. data/lib/slack/smart-bot/commands/general/teams/see_teams.rb +73 -61
  103. data/lib/slack/smart-bot/commands/general/teams/see_vacations_team.rb +28 -13
  104. data/lib/slack/smart-bot/commands/general/teams/update_team.rb +9 -9
  105. data/lib/slack/smart-bot/commands/general_bot_commands.rb +1153 -835
  106. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +19 -17
  107. data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +12 -9
  108. data/lib/slack/smart-bot/commands/on_bot/admin/remove_routine.rb +2 -0
  109. data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +1 -0
  110. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +5 -3
  111. data/lib/slack/smart-bot/commands/on_bot/admin_master/delete_message.rb +2 -3
  112. data/lib/slack/smart-bot/commands/on_bot/admin_master/get_bot_logs.rb +2 -3
  113. data/lib/slack/smart-bot/commands/on_bot/admin_master/react_to.rb +2 -3
  114. data/lib/slack/smart-bot/commands/on_bot/admin_master/send_message.rb +7 -6
  115. data/lib/slack/smart-bot/commands/on_bot/admin_master/update_message.rb +2 -3
  116. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +1 -1
  117. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +21 -20
  118. data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +40 -7
  119. data/lib/slack/smart-bot/commands/on_bot/general/bot_status.rb +6 -2
  120. data/lib/slack/smart-bot/commands/on_bot/general/stop_using_rules.rb +7 -6
  121. data/lib/slack/smart-bot/commands/on_bot/general/suggest_command.rb +5 -4
  122. data/lib/slack/smart-bot/commands/on_bot/general/use_rules.rb +4 -3
  123. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +4 -4
  124. data/lib/slack/smart-bot/commands/on_bot/kill_repl.rb +1 -1
  125. data/lib/slack/smart-bot/commands/on_bot/repl.rb +110 -53
  126. data/lib/slack/smart-bot/commands/on_bot/repl_client.rb +35 -29
  127. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +5 -5
  128. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +1 -2
  129. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +5 -4
  130. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +22 -11
  131. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +12 -7
  132. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +2 -2
  133. data/lib/slack/smart-bot/commands/on_master/admin_master/publish_announcements.rb +1 -1
  134. data/lib/slack/smart-bot/commands/on_master/admin_master/set_general_message.rb +5 -5
  135. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +5 -5
  136. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +3 -3
  137. data/lib/slack/smart-bot/commands.rb +10 -0
  138. data/lib/slack/smart-bot/config.rb +126 -0
  139. data/lib/slack/smart-bot/listen.rb +12 -11
  140. data/lib/slack/smart-bot/process.rb +62 -55
  141. data/lib/slack/smart-bot/process_first.rb +106 -65
  142. data/lib/slack/smart-bot/treat_message.rb +79 -47
  143. data/lib/slack/smart-bot/utils/answer.rb +11 -3
  144. data/lib/slack/smart-bot/utils/answer_delete.rb +11 -3
  145. data/lib/slack/smart-bot/utils/check_vacations.rb +21 -3
  146. data/lib/slack/smart-bot/utils/create_routine_thread.rb +13 -13
  147. data/lib/slack/smart-bot/utils/display_calendar.rb +42 -8
  148. data/lib/slack/smart-bot/utils/encryption/decrypt.rb +16 -9
  149. data/lib/slack/smart-bot/utils/encryption/encrypt.rb +14 -11
  150. data/lib/slack/smart-bot/utils/find_user.rb +71 -0
  151. data/lib/slack/smart-bot/utils/get_access_channels.rb +22 -3
  152. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +3 -4
  153. data/lib/slack/smart-bot/utils/get_command_ids.rb +5 -5
  154. data/lib/slack/smart-bot/utils/get_countries_candelarific.rb +18 -0
  155. data/lib/slack/smart-bot/utils/get_help.rb +21 -19
  156. data/lib/slack/smart-bot/utils/get_openai_sessions.rb +47 -0
  157. data/lib/slack/smart-bot/utils/get_personal_settings.rb +29 -3
  158. data/lib/slack/smart-bot/utils/get_rules_imported.rb +27 -6
  159. data/lib/slack/smart-bot/utils/get_shares.rb +1 -1
  160. data/lib/slack/smart-bot/utils/get_team_members.rb +4 -4
  161. data/lib/slack/smart-bot/utils/get_vacations.rb +15 -7
  162. data/lib/slack/smart-bot/utils/has_access.rb +10 -4
  163. data/lib/slack/smart-bot/utils/is_admin.rb +25 -17
  164. data/lib/slack/smart-bot/utils/local_time.rb +29 -0
  165. data/lib/slack/smart-bot/utils/save_stats.rb +5 -3
  166. data/lib/slack/smart-bot/utils/update_access_channels.rb +19 -3
  167. data/lib/slack/smart-bot/utils/update_openai_sessions.rb +42 -0
  168. data/lib/slack/smart-bot/utils/update_personal_settings.rb +11 -3
  169. data/lib/slack/smart-bot/utils/update_rules_imported.rb +18 -3
  170. data/lib/slack/smart-bot/utils/update_vacations.rb +5 -2
  171. data/lib/slack/smart-bot/utils/upgrade_to_use_team_ids.rb +276 -0
  172. data/lib/slack/smart-bot/utils.rb +6 -1
  173. data/lib/slack-smart-bot.rb +182 -76
  174. data/lib/slack-smart-bot_general_commands.rb +10 -9
  175. data/whats_new.txt +30 -13
  176. metadata +152 -18
@@ -0,0 +1,399 @@
1
+ class SlackSmartBot
2
+
3
+ #todo: add tests
4
+ def recap(user, dest, channel, date_start, date_end, my_recap)
5
+ save_stats(__method__)
6
+
7
+ require "date"
8
+ require "time"
9
+ react :runner
10
+ @history_still_running ||= false
11
+
12
+ if channel[0] == "D"
13
+ messages = []
14
+ messages << "Sorry, `recap` command is not available in direct messages."
15
+ messages << "Specify a channel instead or call it from a channel."
16
+ messages << "For example: `my recap 2023 #channel`"
17
+ messages << ""
18
+ messages << "You can also call from a DM:"
19
+ messages << "\t\t`bot stats last year`"
20
+ messages << "\t\t`bot stats last year monthly graph`"
21
+ messages << "\t\t`bot stats`"
22
+ messages << ""
23
+ messages << "Call `bot help bot stats` for more information."
24
+ respond messages.join("\n")
25
+ elsif @history_still_running
26
+ respond "Due to Slack API rate limit, `recap` command is limited. Waiting for other `recap` command to finish."
27
+ num_times = 0
28
+ while @history_still_running and num_times < 30
29
+ num_times += 1
30
+ sleep 1
31
+ end
32
+ if @history_still_running
33
+ respond "Sorry, Another `recap` command is still running after 30 seconds. Please try again later."
34
+ end
35
+ end
36
+
37
+ if !@history_still_running and channel[0] != "D"
38
+ @history_still_running = true
39
+ begin
40
+ date_start = date_start.gsub(".", "/").gsub("-", "/")
41
+ date_end = date_end.gsub(".", "/").gsub("-", "/")
42
+ if date_start.to_s == ""
43
+ date_start = Date.today.strftime("%Y/01/01")
44
+ elsif date_start.match(/^\d\d\d\d$/)
45
+ date_start = "#{date_start}/01/01"
46
+ end
47
+ if date_end.to_s == ""
48
+ date_end = Date.parse(date_start).strftime("%Y/12/31")
49
+ end
50
+ if date_end < date_start
51
+ respond "Sorry, `recap` command requires `date_start` to be before `date_end`."
52
+ else
53
+ # if more than one year respond max one year
54
+ num_days = (Date.parse(date_end) - Date.parse(date_start)).to_i
55
+ if num_days > 365
56
+ respond "Sorry, `recap` command is limited to one year."
57
+ else
58
+ messages = []
59
+ #if date_start is 1st of January and date_end is 31st of December, then use the year only
60
+ if Date.parse(date_start).strftime("%m/%d") == "01/01" and Date.parse(date_end).strftime("%m/%d") == "12/31"
61
+ messages << "*#{Date.parse(date_start).strftime("%Y")} Recap <##{channel}>*"
62
+ else
63
+ messages << "*Recap for <##{channel}> from #{date_start} to #{date_end}*"
64
+ end
65
+ date_start = date_start + " 00:00:00"
66
+ date_start = Time.strptime(date_start, "%Y/%m/%d %H:%M:%S")
67
+ date_end = date_end + " 23:59:59"
68
+ date_end = Time.strptime(date_end, "%Y/%m/%d %H:%M:%S")
69
+
70
+ # get all messages from the channel
71
+ hist = []
72
+
73
+ begin
74
+ members = get_channel_members(channel)
75
+ rescue
76
+ members = []
77
+ end
78
+ if !members.include?(config.nick_id_granular) or !members.include?(config.nick_id) #monitor-smartbot #smartbot
79
+ respond "Sorry, `recap` command requires <@#{config.nick_id_granular}> and <@#{config.nick_id}> to be in the channel. Please add them and try again."
80
+ elsif !members.include?(user.id)
81
+ respond "Sorry, `recap` command requires you to be in the channel. Please join the channel and try again."
82
+ else
83
+
84
+ client_granular.conversations_history(channel: channel, limit: 200, oldest: date_start.to_f, latest: date_end.to_f) do |response|
85
+ hist << response.messages
86
+ end
87
+ hist_replies = hist.deep_copy()
88
+ hist.flatten!
89
+
90
+ # sort hist by reply_count from highest to lowest, reply_count is not always, return a hash
91
+ hist.each do |message|
92
+ if !message.key?("reply_count")
93
+ message[:reply_count] = 0
94
+ end
95
+ end
96
+ num_posts = hist.length
97
+ num_threads = hist.select { |msg| msg.key?("thread_ts") }.length
98
+
99
+ hist_replies_count = hist.sort_by { |k| k[:reply_count] }
100
+ # get the top 3 threads
101
+ top_three_threads = []
102
+ #hist_replies_count is not always 3, so we need to check if the index exists
103
+ top_three_threads << hist_replies_count[-1] if hist_replies_count[-1] and hist_replies_count[-1].key?("thread_ts")
104
+ top_three_threads << hist_replies_count[-2] if hist_replies_count[-2] and hist_replies_count[-2].key?("thread_ts")
105
+ top_three_threads << hist_replies_count[-3] if hist_replies_count[-3] and hist_replies_count[-3].key?("thread_ts")
106
+
107
+ num_messages_skipped = 0
108
+ num_messages_not_skipped = 0
109
+ hist.each do |message|
110
+ if Time.at(message[:ts].to_f) >= date_start and Time.at(message[:ts].to_f) <= date_end
111
+ if message.key?("thread_ts")
112
+ if message.reply_users_count == 1 and message.reply_users[0] == config.nick_id
113
+ #if the thread has only one replier and it is from smartbot, then we don't need to get the replies from the thread
114
+ num_messages_skipped += 1
115
+ message[:reply_count].times do
116
+ hist_replies << { user: config.nick_id, ts: message.thread_ts, text: "" }
117
+ end
118
+ else
119
+ num_messages_not_skipped += 1
120
+ thread_ts = message.thread_ts
121
+ passed = false
122
+ num_tries = 0
123
+ while !passed and num_tries < 30
124
+ begin
125
+ num_tries += 1
126
+ replies = client_granular.conversations_replies(channel: channel, ts: thread_ts)
127
+ passed = true
128
+ rescue => e
129
+ @logger.info "recap command: threads num_tries: #{num_tries}"
130
+ @logger.fatal "recap command: threads error: #{e.message}"
131
+ sleep 1
132
+ passed = false
133
+ end
134
+ end
135
+ if !passed
136
+ respond "Sorry, `recap` command failed. <@#{config.admins.join(">,<@")}> please check the logs."
137
+ @history_still_running = false
138
+ unreact :runner
139
+ return
140
+ end
141
+ hist_replies << replies.messages
142
+ sleep 0.5 #to avoid rate limit Tier 3 (50 requests per minute)
143
+ end
144
+ end
145
+ end
146
+ end
147
+ hist_replies.flatten!
148
+
149
+ hist_user = {}
150
+ hist_replies.each do |msg|
151
+ hist_user[msg.user] ||= 0
152
+ hist_user[msg.user] += 1
153
+ end
154
+ #sort hist_user by value from highest to lowest, return a hash
155
+ hist_user = hist_user.sort_by { |k, v| v }.reverse.to_h
156
+ #top_three_users excluding smartbot
157
+ hist_user_wo_smartbot = hist_user.reject { |k, v| k == config.nick_id }
158
+
159
+ top_three_users = hist_user_wo_smartbot.first(3).to_h
160
+ messages << "\t :bar_chart: Totals:"
161
+ messages << "\t\t\t messages: *#{hist_replies.length}*"
162
+ messages << "\t\t\t SmartBot messages: *#{hist_user[config.nick_id]}* (#{hist_user[config.nick_id] * 100 / hist_replies.length} %)" if hist_user.key?(config.nick_id)
163
+ messages << "\t\t\t posts: *#{num_posts}*"
164
+ messages << "\t\t\t threads: *#{num_threads}*"
165
+
166
+ num_replies = hist_replies.length - num_posts
167
+ messages << "\t\t\t replies: *#{num_replies}*"
168
+
169
+ messages << "\t\t\t users: *#{hist_user.length}*"
170
+ if top_three_users.length > 0
171
+ messages << "\n\t :boom: Top 3 users:"
172
+ top_three_users.each do |user, num|
173
+ messages << "\t\t\t<@#{user}>: *#{num}*"
174
+ end
175
+ end
176
+
177
+ #top 3 users posting excluding smartbot
178
+ hist_user_posts = {}
179
+ hist.each do |msg|
180
+ hist_user_posts[msg.user] ||= 0
181
+ hist_user_posts[msg.user] += 1
182
+ end
183
+ hist_user_posts = hist_user_posts.reject { |k, v| k == config.nick_id }
184
+ hist_user_posts = hist_user_posts.sort_by { |k, v| v }.reverse.to_h
185
+ top_three_users_posts = hist_user_posts.first(3).to_h
186
+ if top_three_users_posts.length > 0
187
+ messages << "\n\t :bust_in_silhouette: Top 3 users posting:"
188
+ top_three_users_posts.each do |user, num|
189
+ messages << "\t\t\t<@#{user}>: *#{num}*"
190
+ end
191
+ end
192
+
193
+ if top_three_threads.length > 0
194
+ messages << "\n\t :star: Top 3 threads:"
195
+ top_three_threads.each do |thread|
196
+ messages << "\t\t\t<@#{thread.user}>: *#{thread.reply_count}* replies. *<https://greenqloud.slack.com/archives/#{channel}/p#{thread.ts.gsub(".", "")}|#{thread.text[0..50].gsub(/[^a-zA-Z0-9\s]/, "").gsub("\n", "").strip}>*"
197
+ end
198
+ end
199
+
200
+ #get the stats from the files, they are like smart-bot.stats.2020-01.log, smart-bot.stats.2020-02.log, etc.
201
+ smartbot_stats = []
202
+ Dir["#{config.stats_path}.*.log"].sort.each do |file|
203
+ #read only the files that are in the range of date_start and date_end
204
+ if file.match(/#{config.stats_path}\.(\d\d\d\d-\d\d)\.log/)
205
+ if $1 >= date_start.strftime("%Y-%m") and $1 <= date_end.strftime("%Y-%m")
206
+ smartbot_stats_file = CSV.read(file, headers: true, converters: :numeric)
207
+ #convert smartbot_stats_file to array of hashes
208
+ smartbot_stats_file = smartbot_stats_file.map(&:to_hash)
209
+ smartbot_stats << smartbot_stats_file
210
+ end
211
+ end
212
+ end
213
+ smartbot_stats.flatten!
214
+
215
+ #filter by bot_channel_id==channel and dest_channel_id!=channel and type_message==on_dm
216
+ smartbot_stats_on_dm = smartbot_stats.deep_copy
217
+ smartbot_stats_on_dm = smartbot_stats_on_dm.select { |msg| msg["bot_channel_id"] == channel and msg["dest_channel_id"] != channel and msg["type_message"] == "on_dm" }
218
+ #filter by date_start and date_end. We store the date on "date" field like this: 2020-01-14 16:34:16
219
+ smartbot_stats_on_dm = smartbot_stats_on_dm.select { |msg| Date.parse(msg["date"]) >= Date.parse(date_start.strftime("%Y-%m-%d")) and Date.parse(msg["date"]) <= Date.parse(date_end.strftime("%Y-%m-%d")) }
220
+
221
+ #filter by bot_channel_id==channel and dest_channel_id!=channel and type_message!=on_dm
222
+ smartbot_stats_external = smartbot_stats.deep_copy
223
+ smartbot_stats_external = smartbot_stats_external.select { |msg| msg["bot_channel_id"] == channel and msg["dest_channel_id"] != channel and msg["type_message"] != "on_dm" }
224
+ #filter by date_start and date_end. We store the date on "date" field like this: 2020-01-14 16:34:16
225
+ smartbot_stats_external = smartbot_stats_external.select { |msg| Date.parse(msg["date"]) >= Date.parse(date_start.strftime("%Y-%m-%d")) and Date.parse(msg["date"]) <= Date.parse(date_end.strftime("%Y-%m-%d")) }
226
+
227
+ # filter by dest_channel_id
228
+ smartbot_stats = smartbot_stats.select { |msg| msg["dest_channel_id"] == channel }
229
+ # filter by date_start and date_end. We store the date on "date" field like this: 2020-01-14 16:34:16
230
+ smartbot_stats = smartbot_stats.select { |msg| Date.parse(msg["date"]) >= Date.parse(date_start.strftime("%Y-%m-%d")) and Date.parse(msg["date"]) <= Date.parse(date_end.strftime("%Y-%m-%d")) }
231
+
232
+ #get the number of times SmartBot was called by a user, if field 'user_name' is not including 'routine/'
233
+ smartbot_stats_user = {}
234
+ smartbot_stats.each do |msg|
235
+ if !msg["user_name"].include?("routine/")
236
+ smartbot_stats_user[msg["user_name"]] ||= 0
237
+ smartbot_stats_user[msg["user_name"]] += 1
238
+ end
239
+ end
240
+
241
+ smartbot_stats_total = smartbot_stats_user.values.sum
242
+ if smartbot_stats_total > 0
243
+ messages << "\t :robot_face: *SmartBot* Stats:"
244
+ #total of times SmartBot was called
245
+ messages << "\t\t\t called by a user: *#{smartbot_stats_total}*"
246
+ #total of times SmartBot was called by any user including routines
247
+ smartbot_stats_total_all = smartbot_stats.length
248
+ messages << "\t\t\t called on this channel: *#{smartbot_stats_total_all}*" if smartbot_stats_total != smartbot_stats_total_all
249
+ #total of times SmartBot was called using this channel on a DM
250
+ messages << "\t\t\t called on a DM using this channel: *#{smartbot_stats_on_dm.length}*" if smartbot_stats_on_dm.length > 0
251
+ #total of times SmartBot was called using this channel from another channel
252
+ messages << "\t\t\t called from another channel using this channel: *#{smartbot_stats_external.length}*" if smartbot_stats_external.length > 0
253
+
254
+ #total of times SmartBot was used
255
+ total_sb_calls = smartbot_stats_total_all + smartbot_stats_on_dm.length + smartbot_stats_external.length
256
+ messages << "\t\t\t Total times SmartBot was used: *#{total_sb_calls}*" if total_sb_calls != smartbot_stats_total_all
257
+
258
+ #get the three most used commands. The command is stored on "command" field
259
+ smartbot_stats_commands = {}
260
+ smartbot_stats.each do |msg|
261
+ smartbot_stats_commands[msg["command"]] ||= 0
262
+ smartbot_stats_commands[msg["command"]] += 1
263
+ end
264
+
265
+ #total number of different commands
266
+ smartbot_stats_total_commands = smartbot_stats_commands.keys.length
267
+ messages << "\t\t\t Total different commands used: *#{smartbot_stats_total_commands}*"
268
+
269
+ #top three commands
270
+ smartbot_stats_commands = smartbot_stats_commands.sort_by { |k, v| v }.reverse.to_h
271
+ smartbot_stats_commands = smartbot_stats_commands.first(3).to_h
272
+ messages << "\t\t\t :speech_balloon: Top 3 commands:"
273
+ smartbot_stats_commands.each do |command, num|
274
+ messages << "\t\t\t\t*#{command}*: *#{num}*"
275
+ end
276
+
277
+ #top three commands excluding routines only if the top 3 is different from including routines
278
+ smartbot_stats_commands_include_routines = smartbot_stats_commands.deep_copy
279
+
280
+ smartbot_stats_commands = {}
281
+ smartbot_stats.each do |msg|
282
+ if !msg["user_name"].include?("routine/")
283
+ smartbot_stats_commands[msg["command"]] ||= 0
284
+ smartbot_stats_commands[msg["command"]] += 1
285
+ end
286
+ end
287
+ smartbot_stats_commands = smartbot_stats_commands.sort_by { |k, v| v }.reverse.to_h
288
+ smartbot_stats_commands = smartbot_stats_commands.first(3).to_h
289
+ if smartbot_stats_commands != smartbot_stats_commands_include_routines
290
+ messages << "\t\t\t :speech_balloon: Top 3 commands excluding routines:"
291
+ smartbot_stats_commands.each do |command, num|
292
+ messages << "\t\t\t\t*#{command}*: *#{num}*"
293
+ end
294
+ end
295
+
296
+ #get the three most used users. The user is stored on "user_name" field
297
+ smartbot_stats_user = smartbot_stats_user.sort_by { |k, v| v }.reverse.to_h
298
+ smartbot_stats_user = smartbot_stats_user.first(3).to_h
299
+ messages << "\t\t\t :bust_in_silhouette: Top 3 users calling SmartBot:"
300
+ smartbot_stats_user.each do |user, num|
301
+ messages << "\t\t\t\t<@#{user}>: *#{num}*"
302
+ end
303
+ end
304
+
305
+ #number of messages by month
306
+ hist_month = {}
307
+ hist_replies.each do |msg|
308
+ if Time.at(msg[:ts].to_f) >= date_start and Time.at(msg[:ts].to_f) <= date_end
309
+ date = Time.at(msg[:ts].to_f).strftime("%Y/%m")
310
+ hist_month[date] ||= 0
311
+ hist_month[date] += 1
312
+ end
313
+ end
314
+ hist_month = hist_month.sort_by { |k, v| k }.to_h
315
+ respond messages.join("\n")
316
+ messages = []
317
+ messages << "\t :memo: Number of messages by month:" if hist_month.size > 0
318
+ hist_month.each do |date, num|
319
+ graph = ":large_yellow_square: " * (num.to_f * (10 * hist_month.size) / hist_replies.size).round(2)
320
+ messages << "\t\t\t#{date}: #{graph} #{num} (#{num * 100 / hist_replies.size}%)"
321
+ end
322
+
323
+ #number of users by month
324
+ hist_month_user = {}
325
+ hist_replies.each do |msg|
326
+ if Time.at(msg[:ts].to_f) >= date_start and Time.at(msg[:ts].to_f) <= date_end
327
+ date = Time.at(msg[:ts].to_f).strftime("%Y/%m")
328
+ hist_month_user[date] ||= []
329
+ hist_month_user[date] << msg.user
330
+ end
331
+ end
332
+ hist_month_user = hist_month_user.sort_by { |k, v| k }.to_h
333
+ respond messages.join("\n")
334
+ messages = []
335
+ messages << "\t :person_with_crown: Number of users by month:" if hist_month_user.size > 0
336
+ total_users_by_month = 0
337
+ hist_month_user.each do |date, users|
338
+ total_users_by_month += users.uniq.length
339
+ end
340
+ hist_month_user.each do |date, users|
341
+ graph = ":large_orange_square: " * (users.uniq.length.to_f * (10 * hist_month_user.size) / total_users_by_month).round(2)
342
+ messages << "\t\t\t#{date}: #{graph} #{users.uniq.length} "
343
+ end
344
+ if my_recap
345
+ respond messages.join("\n")
346
+ messages = []
347
+ messages << "\t :medal: *Recap for <@#{user[:id]}>*:"
348
+ messages << "\t\t\tTotal messages: *#{hist_user[user[:id]]}*"
349
+ # top three posts of user.name
350
+ hist_user_posts = hist.select { |msg| msg.user == user[:id] }
351
+ hist_user_posts = hist_user_posts.sort_by { |k| k[:reply_count] }
352
+ top_three_posts = [hist_user_posts[-1], hist_user_posts[-2], hist_user_posts[-3]]
353
+ messages << "\t\t\tTop 3 threads:"
354
+ top_three_posts.each do |post|
355
+ if !post.nil?
356
+ messages << "\t\t\t\t *#{post.reply_count}* replies: *<https://greenqloud.slack.com/archives/#{channel}/p#{post.ts.gsub(".", "")}|#{post.text[0..50].gsub(/[^a-zA-Z0-9\s]/, "").gsub("\n", "").strip}>*"
357
+ end
358
+ end
359
+
360
+ #Add also stats from SmartBot commands for this user
361
+ #filter stats by user
362
+ smartbot_stats_user = smartbot_stats.select { |msg| msg["user_id"] == user[:id] }
363
+ #get the number of times SmartBot was called by this user
364
+ smartbot_stats_user_total = smartbot_stats_user.length
365
+ messages << "\t\t\t SmartBot calls: *#{smartbot_stats_user_total}*"
366
+ #get the three most used commands by this user
367
+ smartbot_stats_user_commands = {}
368
+ smartbot_stats_user.each do |msg|
369
+ smartbot_stats_user_commands[msg["command"]] ||= 0
370
+ smartbot_stats_user_commands[msg["command"]] += 1
371
+ end
372
+ if smartbot_stats_user_total > 0
373
+ #total number of different commands
374
+ smartbot_stats_user_total_commands = smartbot_stats_user_commands.keys.length
375
+ messages << "\t\t\t Different commands: *#{smartbot_stats_user_total_commands}*"
376
+ #top three commands
377
+ smartbot_stats_user_commands = smartbot_stats_user_commands.sort_by { |k, v| v }.reverse.to_h
378
+ smartbot_stats_user_commands = smartbot_stats_user_commands.first(3).to_h
379
+ messages << "\t\t\t :speech_balloon: Top 3 commands:"
380
+ smartbot_stats_user_commands.each do |command, num|
381
+ messages << "\t\t\t\t\t*#{command}*: *#{num}*"
382
+ end
383
+ end
384
+ end
385
+
386
+ respond messages.join("\n")
387
+ end
388
+ end
389
+ end
390
+ rescue => e
391
+ @logger.fatal "recap command failed: #{e.message}"
392
+ @logger.fatal e.backtrace.inspect
393
+ respond "Sorry, `recap` command failed. <@#{config.admins.join(">,<@")}> please check the logs."
394
+ end
395
+ @history_still_running = false
396
+ end
397
+ unreact :runner
398
+ end
399
+ end
@@ -14,37 +14,47 @@ class SlackSmartBot
14
14
  end
15
15
  messages = []
16
16
  admins = config.masters.dup
17
+ team_id_admins = config.team_id_masters.dup
17
18
  channels = get_channels()
18
19
  channel_found = channels.detect { |c| c.id == channel }
19
20
  if !channel_found.nil? and channel_found.creator.to_s != ''
20
- creator_info = @users.select{|u| u.id == channel_found.creator or (u.key?(:enterprise_user) and u.enterprise_user.id == channel_found.creator)}[-1]
21
+ creator_info = find_user(channel_found.creator)
21
22
  admins << creator_info.name
23
+ team_id_admins << "#{creator_info.team_id}_#{creator_info.name}"
22
24
  end
23
25
  if Thread.current[:typem] == :on_bot or Thread.current[:typem] == :on_master
24
26
  admins << config.admins.dup
27
+ team_id_admins << config.team_id_admins.dup
25
28
  end
26
29
  if @admins_channels.key?(channel) and @admins_channels[channel].size > 0
27
- admins << @admins_channels[channel]
30
+ team_id_admins << @admins_channels[channel]
31
+ #remove the team_id from the names on @admins_channels
32
+ admins << @admins_channels[channel].map { |a| a.split('_')[1..-1].join('_') }
28
33
  end
29
34
  admins.flatten!
30
35
  admins.uniq!
31
36
  admins.delete(nil)
32
- if admins.include?(user.name)
33
- admin_info = @users.select{|u| u.id == admin_user or (u.key?(:enterprise_user) and u.enterprise_user.id == admin_user)}[-1]
34
- if creator_info.name == admin_info.name
37
+ team_id_admins.flatten!
38
+ team_id_admins.uniq!
39
+ team_id_admins.delete(nil)
40
+
41
+ if team_id_admins.include?("#{user.team_id}_#{user.name}")
42
+ admin_info = find_user(admin_user)
43
+ if creator_info.name == admin_info.name and creator_info.team_id == admin_info.team_id
35
44
  messages << "This user created the channel and cannot be removed as an admin."
36
- elsif config.masters.include?(admin_info.name) or config.masters.include?(admin_user)
45
+ elsif config.team_id_masters.include?("#{admin_info.team_id}_#{admin_info.name}") or config.team_id_masters.include?(admin_user)
37
46
  messages << "Master admins cannot be removed as admins of this channel."
38
- elsif config.admins.include?(admin_info.name) or config.admins.include?(admin_user)
47
+ elsif config.team_id_admins.include?("#{admin_info.team_id}_#{admin_info.name}") or config.team_id_admins.include?(admin_user)
39
48
  messages << "This user is a defaulted admin for this channel and cannot be removed using this command."
40
- elsif !admins.include?(admin_info.name)
49
+ elsif !team_id_admins.include?("#{admin_info.team_id}_#{admin_info.name}")
41
50
  messages << "This user is not an admin of this channel."
42
51
  else
43
52
  @admins_channels[channel] ||= []
44
- @admins_channels[channel].delete(admin_info.name)
53
+ @admins_channels[channel].delete("#{admin_info.team_id}_#{admin_info.name}")
45
54
  update_admins_channels()
46
55
  messages << "The user is not an admin of this channel from now on."
47
56
  admins.delete(admin_info.name)
57
+ team_id_admins.delete("#{admin_info.team_id}_#{admin_info.name}")
48
58
  end
49
59
  messages << "*Admins*: <@#{admins.join('>, <@')}>"
50
60
  else
@@ -3,24 +3,41 @@ class SlackSmartBot
3
3
  save_stats(__method__)
4
4
 
5
5
  get_vacations()
6
- if !@vacations.key?(user.name)
6
+ team_id_user = "#{user.team_id}_#{user.name}"
7
+
8
+ if !@vacations.key?(team_id_user)
7
9
  respond "It seems like you don't have any time off added."
8
- elsif @vacations[user.name].periods.empty? or !@vacations[user.name].periods.vacation_id.include?(vacation_id)
10
+ elsif @vacations[team_id_user].periods.empty? or !@vacations[team_id_user].periods.vacation_id.include?(vacation_id)
9
11
  respond "It seems like the ID supplied doesn't exist. Please call `see my time off` and check the ID."
10
12
  else
11
- vacations = @vacations[user.name].deep_copy
13
+ if @vacations.key?(team_id_user) and @vacations[team_id_user][:public_holidays].to_s != ""
14
+ country_region = @vacations[team_id_user][:public_holidays].downcase
15
+ elsif config[:public_holidays].key?(:default_calendar)
16
+ country_region = config[:public_holidays][:default_calendar].downcase
17
+ else
18
+ country_region = ''
19
+ end
20
+
21
+ local_day_time = local_time(country_region)
22
+ if local_day_time.nil?
23
+ today = Date.today
24
+ else
25
+ today = local_day_time.to_date
26
+ end
27
+
28
+ vacations = @vacations[team_id_user].deep_copy
12
29
  vacation = vacations.periods.select {|v| v.vacation_id == vacation_id }[-1]
13
30
  vacations.periods.delete_if {|v| v.vacation_id == vacation_id }
14
- update_vacations({user.name => vacations})
31
+ update_vacations({team_id_user => vacations})
15
32
  respond "Your time off has been removed."
16
- if vacation.from <= Date.today.strftime("%Y/%m/%d") and vacation.to >= Date.today.strftime("%Y/%m/%d")
33
+ if vacation.from <= today.strftime("%Y/%m/%d") and vacation.to >= today.strftime("%Y/%m/%d")
17
34
  info = get_user_info(vacations.user_id)
18
35
  emoji = info.user.profile.status_emoji
19
36
  if (vacation.type == 'vacation' and emoji == ':palm_tree:') or (vacation.type == 'sick' and emoji == ':face_with_thermometer:') or
20
37
  (vacation.type == 'sick child' and emoji == ':baby:')
21
38
  set_status(vacations.user_id, status: '', expiration: '', message: '')
22
39
  end
23
- check_vacations(date: Date.today, user: user.name, set_status: true, only_first_day: false)
40
+ check_vacations(date: today, team_id: user.team_id, user: user.name, set_status: true, only_first_day: false)
24
41
  end
25
42
  end
26
43
  end
@@ -11,7 +11,8 @@ class SlackSmartBot
11
11
  command_ids = get_command_ids()
12
12
  if command_ids.values.flatten.include?(command_id)
13
13
  if @access_channels.key?(channel) and @access_channels[channel].key?(command_id) and @access_channels[channel][command_id].size > 0
14
- respond "Only these users have access to `#{command_id}` in this channel: <@#{@access_channels[channel][command_id].join(">, <@")}>"
14
+ names = @access_channels[channel][command_id].map { |a| a.split('_')[1..-1].join('_') }
15
+ respond "Only these users have access to `#{command_id}` in this channel: <@#{names.join(">, <@")}>"
15
16
  elsif @access_channels.key?(channel) and @access_channels[channel].key?(command_id) and @access_channels[channel][command_id].empty?
16
17
  respond "`#{command_id}` is not possible to be used in this channel. Please contact an admin if you want to use it."
17
18
  else
@@ -11,22 +11,26 @@ class SlackSmartBot
11
11
 
12
12
  messages = []
13
13
  admins = []
14
+ team_id_admins = []
14
15
  channels = get_channels()
15
16
  channel_found = channels.detect { |c| c.id == channel }
16
17
  if !channel_found.nil? and channel_found.creator.to_s != ''
17
- messages << "*Channel creator*: <@#{channel_found.creator}>"
18
- creator_info = @users.select{|u| u.id == channel_found.creator or (u.key?(:enterprise_user) and u.enterprise_user.id == channel_found.creator)}[-1]
18
+ messages << "*Channel creator*: <@#{channel_found.creator}>"
19
+ creator_info = find_user(channel_found.creator)
19
20
  else
20
- creator_info = {name: []}
21
+ creator_info = {name: [], team_id: []}
21
22
  end
22
23
  messages << "*Master admins*: <@#{config.masters.join('>, <@')}>"
23
24
  if Thread.current[:typem] == :on_bot or Thread.current[:typem] == :on_master
24
25
  admins = config.admins.dup
26
+ team_id_admins = config.team_id_admins.dup
25
27
  end
26
28
  if @admins_channels.key?(channel) and @admins_channels[channel].size > 0
27
- admins = (@admins_channels[channel] + admins).uniq
29
+ team_id_admins = (@admins_channels[channel] + team_id_admins).uniq
30
+ admins = (@admins_channels[channel].map { |a| a.split('_')[1..-1].join('_') } + admins).uniq
28
31
  end
29
32
  admins = admins - config.masters - [creator_info.name]
33
+ team_id_admins = team_id_admins - config.team_id_masters - ["#{creator_info.team_id}_#{creator_info.name}"]
30
34
  messages << "*Admins*: <@#{admins.join('>, <@')}>" unless admins.empty?
31
35
  respond messages.join("\n")
32
36
  end
@@ -16,9 +16,9 @@ class SlackSmartBot
16
16
  else
17
17
  dest = Thread.current[:dest]
18
18
  end
19
-
19
+
20
20
  if type == 'all'
21
- if config.masters.include?(user.name) and typem==:on_dm
21
+ if config.team_id_masters.include?("#{user.team_id}_#{user.name}") and typem==:on_dm
22
22
  channels = Dir.entries("#{config.path}/announcements/")
23
23
  channels.select! {|i| i[/\.csv$/]}
24
24
  else
@@ -45,14 +45,14 @@ class SlackSmartBot
45
45
  end
46
46
  end
47
47
  if has_access?(__method__, user)
48
- if (channel_id!=Thread.current[:dest] and config.masters.include?(user.name) and typem==:on_dm) or publish
48
+ if (channel_id!=Thread.current[:dest] and config.team_id_masters.include?("#{user.team_id}_#{user.name}") and typem==:on_dm) or publish
49
49
  see_announcements_on_demand = true
50
50
  else
51
51
  see_announcements_on_demand = false
52
52
  end
53
53
  if channel_id == Thread.current[:dest] or see_announcements_on_demand or publish #master admin user or publish_announcements
54
54
  if File.exist?("#{config.path}/announcements/#{channel_id}.csv") and (!@announcements.key?(channel_id) or see_announcements_on_demand) # to force to have the last version that maybe was updated by other SmartBot in case of demand
55
- t = CSV.table("#{config.path}/announcements/#{channel_id}.csv", headers: ['message_id', 'user_deleted', 'user_created', 'date', 'time', 'type', 'message'])
55
+ t = CSV.table("#{config.path}/announcements/#{channel_id}.csv", headers: ['message_id', 'user_team_id_deleted', 'user_deleted', 'user_team_id_created', 'user_created', 'date', 'time', 'type', 'message'])
56
56
  @announcements[channel_id] = t
57
57
  end
58
58
  if @announcements.key?(channel_id)
@@ -70,7 +70,7 @@ class SlackSmartBot
70
70
  user_created = "<@#{m[:user_created]}>"
71
71
  else
72
72
  user_created = m[:user_created]
73
- user_info = @users.select { |u| u.name == user_created or (u.key?(:enterprise_user) and u.enterprise_user.name == user_created) }[-1]
73
+ user_info = find_user(user_created)
74
74
  user_created = user_info.profile.display_name unless user_info.nil?
75
75
  end
76
76
  if type == 'all' and channel_id[0]=='D'
@@ -9,18 +9,18 @@ class SlackSmartBot
9
9
  else
10
10
  channel = Thread.current[:using_channel]
11
11
  end
12
-
12
+
13
13
  files = Dir["#{config.stats_path}.*.log"].sort.reverse[0..1]
14
14
  if files.empty?
15
15
  respond "There is no data stored."
16
16
  else
17
17
  count_commands = {}
18
-
18
+
19
19
  files.each do |file|
20
20
  CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
21
21
  row[:dest_channel_id] = row[:bot_channel_id] if row[:dest_channel_id].to_s[0] == "D"
22
- if ((only_mine and row[:user_name]==user.name) or (!only_mine and !config.masters.include?(row[:user_name]))) and
23
- row[:dest_channel_id] == channel and !row[:user_name].include?('routine/') and
22
+ if ((only_mine and row[:user_name]==user.name) or (!only_mine and !config.team_id_masters.include?("#{row[:team_id]}_#{row[:user_name]}"))) and
23
+ row[:dest_channel_id] == channel and !row[:user_name].include?('routine/') and
24
24
  row[:command] != 'dont_understand'
25
25
  row[:command] = 'bot_help' if row[:command] == 'bot_rules'
26
26
  count_commands[row[:command]] ||= 0
@@ -12,7 +12,7 @@ class SlackSmartBot
12
12
 
13
13
  general_message = "\nRelated commands `share messages /RegExp/ on #CHANNEL`, `share messages \"TEXT\" on #CHANNEL`, `delete share ID`"
14
14
  if File.exist?("#{config.path}/shares/#{@channels_name[channel]}.csv")
15
- t = CSV.table("#{config.path}/shares/#{@channels_name[channel]}.csv", headers: ['share_id', 'user_deleted', 'user_created', 'date', 'time', 'type', 'to_channel', 'condition'])
15
+ t = CSV.table("#{config.path}/shares/#{@channels_name[channel]}.csv", headers: ['share_id', 'user_team_id_deleted', 'user_deleted', 'user_team_id_created', 'user_created', 'date', 'time', 'type', 'to_channel', 'condition'])
16
16
  message =[]
17
17
  t.each do |m|
18
18
  if m[:user_deleted] == ''