slack-smart-bot 1.9.1 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +184 -16
  3. data/lib/slack/smart-bot/comm/ask.rb +55 -49
  4. data/lib/slack/smart-bot/comm/delete.rb +13 -0
  5. data/lib/slack/smart-bot/comm/dont_understand.rb +3 -3
  6. data/lib/slack/smart-bot/comm/event_hello.rb +8 -4
  7. data/lib/slack/smart-bot/comm/get_channel_members.rb +13 -4
  8. data/lib/slack/smart-bot/comm/get_channels.rb +31 -16
  9. data/lib/slack/smart-bot/comm/get_presence.rb +20 -0
  10. data/lib/slack/smart-bot/comm/get_user_info.rb +12 -8
  11. data/lib/slack/smart-bot/comm/get_users.rb +24 -0
  12. data/lib/slack/smart-bot/comm/react.rb +19 -2
  13. data/lib/slack/smart-bot/comm/respond.rb +224 -53
  14. data/lib/slack/smart-bot/comm/respond_direct.rb +2 -3
  15. data/lib/slack/smart-bot/comm/respond_thread.rb +5 -0
  16. data/lib/slack/smart-bot/comm/send_file.rb +38 -34
  17. data/lib/slack/smart-bot/comm/send_msg_channel.rb +27 -22
  18. data/lib/slack/smart-bot/comm/send_msg_user.rb +59 -33
  19. data/lib/slack/smart-bot/comm/unreact.rb +22 -18
  20. data/lib/slack/smart-bot/comm.rb +4 -0
  21. data/lib/slack/smart-bot/commands/general/add_admin.rb +51 -0
  22. data/lib/slack/smart-bot/commands/general/add_announcement.rb +32 -0
  23. data/lib/slack/smart-bot/commands/general/add_team.rb +80 -0
  24. data/lib/slack/smart-bot/commands/general/allow_access.rb +67 -0
  25. data/lib/slack/smart-bot/commands/general/bot_help.rb +69 -33
  26. data/lib/slack/smart-bot/commands/general/bye_bot.rb +0 -7
  27. data/lib/slack/smart-bot/commands/general/delete_announcement.rb +34 -0
  28. data/lib/slack/smart-bot/commands/general/delete_share.rb +34 -0
  29. data/lib/slack/smart-bot/commands/general/delete_team.rb +34 -0
  30. data/lib/slack/smart-bot/commands/general/deny_access.rb +36 -0
  31. data/lib/slack/smart-bot/commands/general/hi_bot.rb +16 -11
  32. data/lib/slack/smart-bot/commands/general/ping_team.rb +100 -0
  33. data/lib/slack/smart-bot/commands/general/poster.rb +116 -0
  34. data/lib/slack/smart-bot/commands/general/remove_admin.rb +58 -0
  35. data/lib/slack/smart-bot/commands/general/see_access.rb +24 -0
  36. data/lib/slack/smart-bot/commands/general/see_admins.rb +33 -0
  37. data/lib/slack/smart-bot/commands/general/see_announcements.rb +115 -0
  38. data/lib/slack/smart-bot/commands/general/see_command_ids.rb +29 -0
  39. data/lib/slack/smart-bot/commands/general/see_favorite_commands.rb +53 -0
  40. data/lib/slack/smart-bot/commands/general/see_shares.rb +41 -0
  41. data/lib/slack/smart-bot/commands/general/see_statuses.rb +91 -0
  42. data/lib/slack/smart-bot/commands/general/see_teams.rb +252 -0
  43. data/lib/slack/smart-bot/commands/general/share_messages.rb +58 -0
  44. data/lib/slack/smart-bot/commands/general/update_team.rb +109 -0
  45. data/lib/slack/smart-bot/commands/general_bot_commands.rb +504 -0
  46. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +4 -6
  47. data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +45 -14
  48. data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +4 -1
  49. data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +6 -3
  50. data/lib/slack/smart-bot/commands/on_bot/admin/pause_routine.rb +3 -1
  51. data/lib/slack/smart-bot/commands/on_bot/admin/remove_routine.rb +4 -4
  52. data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +8 -2
  53. data/lib/slack/smart-bot/commands/on_bot/admin/see_result_routine.rb +33 -0
  54. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +13 -10
  55. data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +6 -3
  56. data/lib/slack/smart-bot/commands/on_bot/admin/start_routine.rb +3 -1
  57. data/lib/slack/smart-bot/commands/on_bot/admin/stop_using_rules_on.rb +4 -1
  58. data/lib/slack/smart-bot/commands/on_bot/admin_master/delete_message.rb +25 -0
  59. data/lib/slack/smart-bot/commands/on_bot/admin_master/get_bot_logs.rb +1 -0
  60. data/lib/slack/smart-bot/commands/on_bot/admin_master/react_to.rb +34 -0
  61. data/lib/slack/smart-bot/commands/on_bot/admin_master/send_message.rb +37 -0
  62. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +4 -5
  63. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +7 -8
  64. data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +400 -0
  65. data/lib/slack/smart-bot/commands/{general → on_bot/general}/bot_status.rb +3 -4
  66. data/lib/slack/smart-bot/commands/on_bot/general/leaderboard.rb +201 -0
  67. data/lib/slack/smart-bot/commands/{general → on_bot/general}/stop_using_rules.rb +12 -6
  68. data/lib/slack/smart-bot/commands/on_bot/general/suggest_command.rb +36 -0
  69. data/lib/slack/smart-bot/commands/{general → on_bot/general}/use_rules.rb +13 -11
  70. data/lib/slack/smart-bot/commands/{general → on_bot/general}/whats_new.rb +2 -1
  71. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +4 -5
  72. data/lib/slack/smart-bot/commands/on_bot/repl.rb +76 -21
  73. data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +3 -4
  74. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +15 -7
  75. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +5 -6
  76. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +5 -6
  77. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +45 -12
  78. data/lib/slack/smart-bot/commands/on_master/admin/kill_bot_on_channel.rb +7 -3
  79. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +4 -1
  80. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +3 -1
  81. data/lib/slack/smart-bot/commands/on_master/admin_master/publish_announcements.rb +33 -0
  82. data/lib/slack/smart-bot/commands/on_master/admin_master/set_general_message.rb +39 -0
  83. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +10 -1
  84. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +28 -14
  85. data/lib/slack/smart-bot/commands/on_master/where_smartbot.rb +41 -0
  86. data/lib/slack/smart-bot/commands.rb +36 -5
  87. data/lib/slack/smart-bot/listen.rb +31 -33
  88. data/lib/slack/smart-bot/process.rb +234 -73
  89. data/lib/slack/smart-bot/process_first.rb +119 -38
  90. data/lib/slack/smart-bot/treat_message.rb +310 -237
  91. data/lib/slack/smart-bot/utils/build_help.rb +2 -2
  92. data/lib/slack/smart-bot/utils/create_routine_thread.rb +81 -46
  93. data/lib/slack/smart-bot/utils/get_access_channels.rb +13 -0
  94. data/lib/slack/smart-bot/utils/get_admins_channels.rb +13 -0
  95. data/lib/slack/smart-bot/utils/get_bots_created.rb +28 -8
  96. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +7 -2
  97. data/lib/slack/smart-bot/utils/get_command_ids.rb +84 -0
  98. data/lib/slack/smart-bot/utils/get_help.rb +79 -73
  99. data/lib/slack/smart-bot/utils/get_repls.rb +22 -2
  100. data/lib/slack/smart-bot/utils/get_routines.rb +22 -2
  101. data/lib/slack/smart-bot/utils/get_shares.rb +12 -0
  102. data/lib/slack/smart-bot/utils/get_teams.rb +22 -0
  103. data/lib/slack/smart-bot/utils/has_access.rb +28 -0
  104. data/lib/slack/smart-bot/utils/is_admin.rb +27 -0
  105. data/lib/slack/smart-bot/utils/save_stats.rb +46 -41
  106. data/lib/slack/smart-bot/utils/save_status.rb +67 -0
  107. data/lib/slack/smart-bot/utils/update_access_channels.rb +8 -0
  108. data/lib/slack/smart-bot/utils/update_admins_channels.rb +8 -0
  109. data/lib/slack/smart-bot/utils/update_bots_file.rb +28 -7
  110. data/lib/slack/smart-bot/utils/update_repls.rb +7 -4
  111. data/lib/slack/smart-bot/utils/update_routines.rb +9 -3
  112. data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +13 -6
  113. data/lib/slack/smart-bot/utils/update_teams.rb +16 -0
  114. data/lib/slack/smart-bot/utils.rb +11 -0
  115. data/lib/slack-smart-bot.rb +72 -12
  116. data/lib/slack-smart-bot_general_commands.rb +61 -0
  117. data/lib/slack-smart-bot_general_rules.rb +5 -2
  118. data/lib/slack-smart-bot_rules.rb +43 -17
  119. data/whats_new.txt +20 -15
  120. metadata +76 -9
  121. data/lib/slack/smart-bot/commands/general/bot_stats.rb +0 -313
@@ -0,0 +1,400 @@
1
+ class SlackSmartBot
2
+ # help: ----------------------------------------------
3
+ # help: `bot stats`
4
+ # helpmaster: `bot stats USER_NAME`
5
+ # help: `bot stats exclude masters`
6
+ # help: `bot stats exclude routines`
7
+ # help: `bot stats from YYYY/MM/DD`
8
+ # help: `bot stats from YYYY/MM/DD to YYYY/MM/DD`
9
+ # help: `bot stats CHANNEL`
10
+ # help: `bot stats CHANNEL from YYYY/MM/DD`
11
+ # help: `bot stats CHANNEL from YYYY/MM/DD to YYYY/MM/DD`
12
+ # help: `bot stats command COMMAND`
13
+ # helpmaster: `bot stats USER_NAME from YYYY/MM/DD to YYYY/MM/DD`
14
+ # helpmaster: `bot stats CHANNEL USER_NAME from YYYY/MM/DD to YYYY/MM/DD`
15
+ # help: `bot stats CHANNEL exclude masters from YYYY/MM/DD to YYYY/MM/DD`
16
+ # help: `bot stats members #CHANNEL`
17
+ # help: `bot stats exclude members #CHANNEL`
18
+ # help: `bot stats today`
19
+ # help: `bot stats exclude COMMAND_ID`
20
+ # help: `bot stats monthly`
21
+ # help: `bot stats alldata`
22
+ # help: To see the bot stats
23
+ # helpmaster: You can use this command only if you are a Master admin user and if you are in a private conversation with the bot
24
+ # helpmaster: You need to set stats to true to generate the stats when running the bot instance.
25
+ # help: members #CHANNEL will return stats for only members of the channel supplied
26
+ # help: exclude members #CHANNEL will return stats for only members that are not members of the channel supplied
27
+ # help: If alldata option supplied then it will be attached files including all data and not only the top 10.
28
+ # help: Examples:
29
+ # help: _bot stats #sales_
30
+ # helpmaster: _bot stats @peter.wind_
31
+ # help: _bot stats #sales from 2019/12/15 to 2019/12/31_
32
+ # help: _bot stats #sales today_
33
+ # help: _bot stats #sales from 2020-01-01 monthly_
34
+ # help: _bot stats exclude routines masters from 2021/01/01 monthly_
35
+ # help: _bot stats members #development from 2022/01/01 to 2022/01/31_
36
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
37
+ # help: command_id: :bot_stats
38
+ # help:
39
+ def bot_stats(dest, from_user, typem, channel_id, from, to, user, st_command, exclude_masters, exclude_routines, exclude_command, monthly, all_data, members_channel, exclude_members_channel)
40
+ require 'csv'
41
+ if config.stats
42
+ message = []
43
+ else
44
+ message = ["You need to set stats to true to generate the stats when running the bot instance."]
45
+ end
46
+ save_stats(__method__)
47
+ if (from_user.id != user and (config.masters.include?(from_user.name) or @master_admin_users_id.include?(from_user.id)) and (typem==:on_dm or dest[0]=='D'))
48
+ on_dm_master = true #master admin user
49
+ else
50
+ on_dm_master = false
51
+ end
52
+ wrong = false
53
+ exclude_channel_members = false
54
+ include_channel_members = false
55
+ members_list = []
56
+ if exclude_members_channel!='' or members_channel!=''
57
+ if members_channel!=''
58
+ channel_members = members_channel
59
+ include_channel_members = true
60
+ else
61
+ channel_members = exclude_members_channel
62
+ exclude_channel_members = true
63
+ end
64
+ get_channels_name_and_id() unless @channels_id.keys.include?(channel_members)
65
+
66
+ tm = get_channel_members(channel_members)
67
+ if tm.nil?
68
+ message << ":exclamation: Add the Smart Bot to *<##{channel_members}>* channel first."
69
+ wrong = true
70
+ else
71
+ tm.each do |m|
72
+ user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
73
+ members_list << user_info.name unless user_info.is_app_user or user_info.is_bot
74
+ end
75
+ end
76
+ end
77
+ tzone_users = {}
78
+
79
+ unless wrong
80
+ if on_dm_master or (from_user.id == user) # normal user can only see own stats
81
+ if !File.exist?("#{config.stats_path}.#{Time.now.strftime("%Y-%m")}.log")
82
+ message<<'No stats'
83
+ else
84
+ from = "#{Time.now.strftime("%Y-%m")}-01" if from == ''
85
+ to = "#{Time.now.strftime("%Y-%m-%d")}" if to == ''
86
+ from_short = from
87
+ to_short = to
88
+ from_file = from[0..3] + '-' + from[5..6]
89
+ to_file = to[0..3] + '-' + to[5..6]
90
+ from+= " 00:00:00 +0000"
91
+ to+= " 23:59:59 +0000"
92
+ rows = []
93
+ rows_month = {}
94
+ users_month = {}
95
+ commands_month = {}
96
+ users_id_name = {}
97
+ users_name_id = {}
98
+ count_users = {}
99
+ count_channels_dest = {}
100
+
101
+ # to translate global and enterprise users since sometimes was returning different names/ids
102
+ #if from[0..3]=='2020' # this was an issue only on that period
103
+ Dir["#{config.stats_path}.*.log"].sort.each do |file|
104
+ if file >= "#{config.stats_path}.#{from_file}.log" and file <= "#{config.stats_path}.#{to_file}.log"
105
+ CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
106
+ unless users_id_name.key?(row[:user_id])
107
+ users_id_name[row[:user_id]] = row[:user_name]
108
+ users_name_id[row[:user_name]] = row[:user_id]
109
+ end
110
+ end
111
+ end
112
+ end
113
+ #end
114
+
115
+ if user!=''
116
+ user_info = @users.select{|u| u.id == user or (u.key?(:enterprise_user) and u.enterprise_user.id == user)}[-1]
117
+ if user_info.nil? # for the case the user is populated from outside of slack
118
+ user_name = user
119
+ user_id = user
120
+ else
121
+ if users_id_name.key?(user_info.id)
122
+ user_name = users_id_name[user_info.id]
123
+ else
124
+ user_name = user_info.name
125
+ end
126
+ if users_name_id.key?(user_info.name)
127
+ user_id = users_name_id[user_info.name]
128
+ else
129
+ user_id = user_info.id
130
+ end
131
+ end
132
+ end
133
+ master_admins = config.masters.dup
134
+ if users_id_name.size > 0
135
+ config.masters.each do |u|
136
+ if users_id_name.key?(u)
137
+ master_admins << users_id_name[u]
138
+ elsif users_name_id.key?(u)
139
+ master_admins << users_name_id[u]
140
+ end
141
+ end
142
+ end
143
+
144
+ Dir["#{config.stats_path}.*.log"].sort.each do |file|
145
+ if file >= "#{config.stats_path}.#{from_file}.log" and file <= "#{config.stats_path}.#{to_file}.log"
146
+ CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
147
+ if (include_channel_members and members_list.include?(row[:user_name])) or
148
+ (exclude_channel_members and !members_list.include?(row[:user_name])) or
149
+ (!include_channel_members and !exclude_channel_members)
150
+
151
+ row[:date] = row[:date].to_s
152
+ if row[:dest_channel_id].to_s[0]=='D'
153
+ row[:dest_channel] = 'DM'
154
+ elsif row[:dest_channel].to_s == ''
155
+ row[:dest_channel] = row[:dest_channel_id]
156
+ end
157
+ if users_name_id.size > 0
158
+ row[:user_name] = users_id_name[row[:user_id]]
159
+ row[:user_id] = users_name_id[row[:user_name]]
160
+ else
161
+ users_id_name[row[:user_id]] ||= row[:user_name]
162
+ end
163
+ if !exclude_masters or (exclude_masters and !master_admins.include?(row[:user_name]) and
164
+ !master_admins.include?(row[:user_id]) and
165
+ !@master_admin_users_id.include?(row[:user_id]))
166
+ if !exclude_routines or (exclude_routines and !row[:user_name].match?(/^routine\//) )
167
+ if exclude_command == '' or (exclude_command!='' and row[:command]!=exclude_command)
168
+ if st_command == '' or (st_command != '' and row[:command] == st_command)
169
+ if row[:bot_channel_id] == channel_id or channel_id == '' or row[:dest_channel_id] == channel_id
170
+ if row[:date] >= from and row[:date] <= to
171
+ count_users[row[:user_id]] ||= 0
172
+ count_users[row[:user_id]] += 1
173
+ if user=='' or (user!='' and row[:user_name] == user_name) or (user!='' and row[:user_id] == user_id)
174
+ rows << row.to_h
175
+ count_channels_dest[row[:dest_channel]] ||= 0
176
+ count_channels_dest[row[:dest_channel]] += 1
177
+ if monthly
178
+ rows_month[row[:date][0..6]] = 0 unless rows_month.key?(row[:date][0..6])
179
+ users_month[row[:date][0..6]] = [] unless users_month.key?(row[:date][0..6])
180
+ commands_month[row[:date][0..6]] = [] unless commands_month.key?(row[:date][0..6])
181
+ rows_month[row[:date][0..6]] += 1
182
+ users_month[row[:date][0..6]] << row[:user_id]
183
+ commands_month[row[:date][0..6]] << row[:command]
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ total = rows.size
197
+ if exclude_masters
198
+ message << 'Excluding master admins'
199
+ end
200
+ if exclude_routines
201
+ message << 'Excluding routines'
202
+ end
203
+ if exclude_command != ''
204
+ message << "Excluding command #{exclude_command}"
205
+ end
206
+ if st_command != ''
207
+ message << "Including only command #{st_command}"
208
+ end
209
+ if include_channel_members
210
+ message << "Including only members of <##{members_channel}>"
211
+ end
212
+ if exclude_channel_members
213
+ message << "Including only members that are not members of <##{exclude_members_channel}>"
214
+ end
215
+ if user!=''
216
+ if user==from_user.id
217
+ message << "Bot stats for <@#{user}>"
218
+ else
219
+ message << "Showing only user <@#{user}>"
220
+ end
221
+ end
222
+ if channel_id == ''
223
+ message << "*Total calls*: #{total} from #{from_short} to #{to_short}"
224
+ else
225
+ message << "*Total calls <##{channel_id}>*: #{total} from #{from_short} to #{to_short}"
226
+ end
227
+ unless count_users.size == 0 or total == 0 or user == ''
228
+ my_place = (count_users.sort_by(&:last).reverse.to_h.keys.index(user_id)+1)
229
+ message <<"\tYou are the *\# #{my_place}* of *#{count_users.size}* users"
230
+ end
231
+ if total > 0
232
+ if monthly
233
+ if on_dm_master
234
+ message << '*Totals by month / commands / users (%new)*'
235
+ else
236
+ message << '*Totals by month / commands*'
237
+ end
238
+
239
+ all_users = []
240
+ new_users = []
241
+ rows_month.each do |k,v|
242
+ if all_users.empty?
243
+ message_new_users = ''
244
+ else
245
+ new_users = (users_month[k]-all_users).uniq
246
+ message_new_users = "(#{new_users.size*100/users_month[k].uniq.size}%)"
247
+ end
248
+ all_users += users_month[k]
249
+ if on_dm_master
250
+ message << "\t#{k}: #{v} (#{(v.to_f*100/total).round(2)}%) / #{commands_month[k].uniq.size} / #{users_month[k].uniq.size} #{message_new_users}"
251
+ else
252
+ message << "\t#{k}: #{v} (#{(v.to_f*100/total).round(2)}%) / #{commands_month[k].uniq.size}"
253
+ end
254
+ end
255
+ end
256
+
257
+ if channel_id == ''
258
+ message << "*SmartBots*"
259
+ channels = rows.bot_channel.uniq.sort
260
+ channels.each do |channel|
261
+ count = rows.count {|h| h.bot_channel==channel}
262
+ channel_info = @channels_list.select { |c| c.name.to_s.downcase == channel.to_s.downcase}[-1]
263
+ if @channels_id.key?(channel) and !channel_info.is_private
264
+ c = "<##{@channels_id[channel]}>"
265
+ else
266
+ c = channel
267
+ end
268
+ message << "\t#{c}: #{count} (#{(count.to_f*100/total).round(2)}%)"
269
+ end
270
+ end
271
+ channels_dest_attachment = []
272
+ count_channels_dest = count_channels_dest.sort_by(&:last).reverse.to_h
273
+ if count_channels_dest.size > 10
274
+ message << "*From Channel* - #{count_channels_dest.size} (Top 10)"
275
+ else
276
+ message << "*From Channel* - #{count_channels_dest.size}"
277
+ end
278
+
279
+ count_channels_dest.keys[0..9].each do |ch|
280
+ channel_info = @channels_list.select { |c| c.name.to_s.downcase == ch.to_s.downcase}[-1]
281
+ if @channels_id.key?(ch) and !channel_info.is_private
282
+ c = "<##{@channels_id[ch]}>"
283
+ else
284
+ c = ch
285
+ end
286
+ message << "\t#{c}: #{count_channels_dest[ch]} (#{(count_channels_dest[ch].to_f*100/total).round(2)}%)"
287
+ end
288
+ if count_channels_dest.size > 10 and all_data
289
+ count_channels_dest.each do |ch, value|
290
+ channel_info = @channels_list.select { |c| c.name.to_s.downcase == ch.to_s.downcase}[-1]
291
+ channels_dest_attachment << "\t##{ch}: #{value} (#{(value.to_f*100/total).round(2)}%)"
292
+ end
293
+ end
294
+
295
+ users_attachment = []
296
+ if user==''
297
+ users = rows.user_id.uniq.sort
298
+ rows.user_id.each do |usr|
299
+ user_info = @users.select { |u| u.id == usr or (u.key?(:enterprise_user) and u.enterprise_user.id == usr) }[-1]
300
+ unless user_info.nil? or user_info.is_app_user or user_info.is_bot
301
+ tzone_users[user_info.tz_label] ||= 0
302
+ tzone_users[user_info.tz_label] += 1
303
+ end
304
+ end
305
+ if users.size > 10
306
+ message << "*Users* - #{users.size} (Top 10)"
307
+ else
308
+ message << "*Users* - #{users.size}"
309
+ end
310
+ count_user = {}
311
+ users.each do |user|
312
+ count = rows.count {|h| h.user_id==user}
313
+ count_user[user] = count
314
+ end
315
+ i = 0
316
+ total_without_routines = total
317
+ count_user.sort_by {|k,v| -v}.each do |user, count|
318
+ i+=1
319
+ if user.include?('routine/')
320
+ user_link = users_id_name[user]
321
+ total_without_routines -= count
322
+ else
323
+ user_link = "<@#{user}>"
324
+ end
325
+ if i <= 10
326
+ message << "\t#{user_link}: #{count} (#{(count.to_f*100/total).round(2)}%)"
327
+ end
328
+ if users.size > 10 and all_data
329
+ users_attachment << "\t#{users_id_name[user]}: #{count} (#{(count.to_f*100/total).round(2)}%)"
330
+ end
331
+ end
332
+
333
+ if tzone_users.size > 0
334
+ message << "*Time Zones*"
335
+ total_known = 0
336
+ tzone_users.each do |tzone, num|
337
+ message << "\t#{tzone}: #{num} (#{(num.to_f*100/total_without_routines).round(2)}%)"
338
+ total_known+=num
339
+ end
340
+ total_unknown = total_without_routines - total_known
341
+ message << "\tUnknown: #{total_unknown} (#{(total_unknown.to_f*100/total_without_routines).round(2)}%)" if total_unknown > 0
342
+ end
343
+
344
+ end
345
+ commands_attachment = []
346
+
347
+ if st_command == ''
348
+ commands = rows.command.uniq.sort
349
+ count_command = {}
350
+ commands.each do |command|
351
+ count = rows.count {|h| h.command==command}
352
+ count_command[command] = count
353
+ end
354
+
355
+ if commands.size > 10
356
+ message << "*Commands* - #{commands.size} (Top 10)"
357
+ else
358
+ message << "*Commands* - #{commands.size}"
359
+ end
360
+
361
+ i = 0
362
+ count_command.sort_by {|k,v| -v}.each do |command, count|
363
+ i+=1
364
+ if i <= 10
365
+ message << "\t#{command}: #{count} (#{(count.to_f*100/total).round(2)}%)"
366
+ end
367
+ if commands.size > 10 and all_data
368
+ commands_attachment << "\t#{command}: #{count} (#{(count.to_f*100/total).round(2)}%)"
369
+ end
370
+ end
371
+ end
372
+
373
+ message << "*Message type*"
374
+ types = rows.type_message.uniq.sort
375
+ types.each do |type|
376
+ count = rows.count {|h| h.type_message==type}
377
+ message << "\t#{type}: #{count} (#{(count.to_f*100/total).round(2)}%)"
378
+ end
379
+
380
+ if on_dm_master
381
+ message << "*Last activity*: #{rows[-1].date} #{rows[-1].bot_channel} #{rows[-1].type_message} #{rows[-1].user_name} #{rows[-1].command}"
382
+ end
383
+ if users_attachment.size>0
384
+ send_file(dest, "", 'users.txt', "", 'text/plain', "text", content: users_attachment.join("\n"))
385
+ end
386
+ if commands_attachment.size>0
387
+ send_file(dest, "", 'commands.txt', "", 'text/plain', "text", content: commands_attachment.join("\n"))
388
+ end
389
+ if channels_dest_attachment.size>0
390
+ send_file(dest, "", 'channels_dest.txt', "", 'text/plain', "text", content: channels_dest_attachment.join("\n"))
391
+ end
392
+ end
393
+ end
394
+ else
395
+ message<<"Only Master admin users on a private conversation with the bot can see this kind of bot stats."
396
+ end
397
+ end
398
+ respond "#{message.join("\n")}", dest
399
+ end
400
+ end
@@ -3,14 +3,13 @@ class SlackSmartBot
3
3
  # helpadmin: `bot status`
4
4
  # helpadmin: Displays the status of the bot
5
5
  # helpadmin: If on master channel and admin user also it will display info about bots created
6
+ # helpadmin: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
7
+ # helpadmin: command_id: :bot_status
6
8
  # helpadmin:
7
9
  def bot_status(dest, user)
8
10
  save_stats(__method__)
9
11
  get_bots_created()
10
- if config[:allow_access].key?(__method__) and !config[:allow_access][__method__].include?(user.name) and !config[:allow_access][__method__].include?(user.id) and
11
- (!user.key?(:enterprise_user) or ( user.key?(:enterprise_user) and !config[:allow_access][__method__].include?(user[:enterprise_user].id)))
12
- respond "You don't have access to use this command, please contact an Admin to be able to use it: <@#{config.admins.join(">, <@")}>"
13
- else
12
+ if has_access?(__method__, user)
14
13
  gems_remote = `gem list slack-smart-bot --remote`
15
14
  version_remote = gems_remote.to_s().scan(/slack-smart-bot \((\d+\.\d+\.\d+)/).join
16
15
  version_message = ""
@@ -0,0 +1,201 @@
1
+ class SlackSmartBot
2
+ # help: ----------------------------------------------
3
+ # help: `leaderboard`
4
+ # help: `ranking`
5
+ # help: `podium`
6
+ # help: `leaderboard from YYYY/MM/DD`
7
+ # help: `leaderboard from YYYY/MM/DD to YYYY/MM/DD`
8
+ # help: `leaderboard PERIOD`
9
+ # help: It will present some useful information about the use of the SmartBot in the period specified.
10
+ # help: If no 'from' and 'to' specified then it will be considered 'last week'
11
+ # help: PERIOD: today, yesterday, last week, this week, last month, this month, last year, this year
12
+ # help: The results will exclude master admins and routines.
13
+ # help: For a more detailed data use the command `bot stats`
14
+ # help: Examples:
15
+ # help: _leaderboard_
16
+ # help: _podium from 2021/05/01_
17
+ # help: _leaderboard from 2021/05/01 to 2021/05/31_
18
+ # help: _ranking today_
19
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
20
+ # help: command_id: :leaderboard
21
+ # help:
22
+
23
+ def leaderboard(from, to, period)
24
+ exclude_masters = true
25
+ exclude_routines = true
26
+ require 'csv'
27
+ if config.stats
28
+ message = []
29
+ else
30
+ message = ["You need to set stats to true to generate the stats when running the bot instance."]
31
+ end
32
+ save_stats(__method__)
33
+ if !File.exist?("#{config.stats_path}.#{Time.now.strftime("%Y-%m")}.log")
34
+ message<<'No stats'
35
+ else
36
+ if period == ''
37
+ message << "*Leaderboard SmartBot <##@channel_id> from #{from} to #{to}*\n"
38
+ else
39
+ message << "*Leaderboard SmartBot <##@channel_id> #{period}* (#{from} -> #{to})\n"
40
+ end
41
+ from_short = from
42
+ to_short = to
43
+ from_file = from[0..3] + '-' + from[5..6]
44
+ to_file = to[0..3] + '-' + to[5..6]
45
+ from+= " 00:00:00 +0000"
46
+ to+= " 23:59:59 +0000"
47
+ rows = []
48
+ users_id_name = {}
49
+ users_name_id = {}
50
+ count_users = {}
51
+ count_channels_dest = {}
52
+ count_commands_uniq_user = {}
53
+
54
+ # to translate global and enterprise users since sometimes was returning different names/ids
55
+ if from[0..3]=='2020' # this was an issue only on that period
56
+ Dir["#{config.stats_path}.*.log"].sort.each do |file|
57
+ if file >= "#{config.stats_path}.#{from_file}.log" or file <= "#{config.stats_path}.#{to_file}.log"
58
+ CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
59
+ unless users_id_name.key?(row[:user_id])
60
+ users_id_name[row[:user_id]] = row[:user_name]
61
+ end
62
+ unless users_name_id.key?(row[:user_name])
63
+ users_name_id[row[:user_name]] = row[:user_id]
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ master_admins = config.masters.dup
72
+ if users_id_name.size > 0
73
+ config.masters.each do |u|
74
+ if users_id_name.key?(u)
75
+ master_admins << users_id_name[u]
76
+ elsif users_name_id.key?(u)
77
+ master_admins << users_name_id[u]
78
+ end
79
+ end
80
+ end
81
+
82
+ Dir["#{config.stats_path}.*.log"].sort.each do |file|
83
+ if file >= "#{config.stats_path}.#{from_file}.log" and file <= "#{config.stats_path}.#{to_file}.log"
84
+ CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
85
+ row[:date] = row[:date].to_s
86
+ if row[:dest_channel_id].to_s[0]=='D'
87
+ row[:dest_channel] = 'DM'
88
+ elsif row[:dest_channel].to_s == ''
89
+ row[:dest_channel] = row[:dest_channel_id]
90
+ end
91
+ if users_name_id.size > 0
92
+ row[:user_name] = users_id_name[row[:user_id]]
93
+ row[:user_id] = users_name_id[row[:user_name]]
94
+ else
95
+ users_id_name[row[:user_id]] ||= row[:user_name]
96
+ end
97
+ if !exclude_masters or (exclude_masters and !master_admins.include?(row[:user_name]) and
98
+ !master_admins.include?(row[:user_id]) and
99
+ !@master_admin_users_id.include?(row[:user_id]))
100
+ if !exclude_routines or (exclude_routines and !row[:user_name].match?(/^routine\//) )
101
+ if row[:bot_channel_id] == @channel_id
102
+ if row[:date] >= from and row[:date] <= to
103
+ count_users[row[:user_id]] ||= 0
104
+ count_users[row[:user_id]] += 1
105
+ rows << row.to_h
106
+ count_channels_dest[row[:dest_channel]] ||= 0
107
+ count_channels_dest[row[:dest_channel]] += 1
108
+ count_commands_uniq_user[row[:user_id]] ||= []
109
+ count_commands_uniq_user[row[:user_id]] << row[:command] unless count_commands_uniq_user[row[:user_id]].include?(row[:command])
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+
118
+ total = rows.size
119
+
120
+ if total > 0
121
+
122
+ users = rows.user_id.uniq.sort
123
+ count_user = {}
124
+ users.each do |user|
125
+ count = rows.count {|h| h.user_id==user}
126
+ count_user[user] = count
127
+ end
128
+ mtc = nil
129
+ mtu = []
130
+ i = 0
131
+ count_user.sort_by {|k,v| -v}.each do |user, count|
132
+ if i >= 3
133
+ break
134
+ elsif mtc.nil? or mtc == count or i < 3
135
+ mtu << "*<@#{users_id_name[user]}>* (#{count})"
136
+ mtc = count
137
+ else
138
+ break
139
+ end
140
+ i+=1
141
+ end
142
+ message << "\t :boom: Users that called more commands: \n\t\t\t\t#{mtu.join("\n\t\t\t\t")}"
143
+
144
+ mtc = nil
145
+ mtu = []
146
+ i = 0
147
+ count_commands_uniq_user.sort_by {|k,v| -v.size}.each do |user, cmds|
148
+ if i >= 3
149
+ break
150
+ elsif mtc.nil? or mtc == cmds.size or i < 3
151
+ mtu << "*<@#{users_id_name[user]}>* (#{cmds.size})"
152
+ mtc = cmds.size
153
+ else
154
+ break
155
+ end
156
+ i+=1
157
+ end
158
+ message << "\t :stethoscope: Users that called more different commands: \n\t\t\t\t#{mtu.join("\n\t\t\t\t")}"
159
+
160
+ commands_attachment = []
161
+
162
+ commands = rows.command.uniq.sort
163
+ count_command = {}
164
+ commands.each do |command|
165
+ count = rows.count {|h| h.command==command}
166
+ count_command[command] = count
167
+ end
168
+
169
+ mtu = []
170
+ count_command.sort_by {|k,v| -v}[0..2].each do |command, count|
171
+ mtu << "*`#{command.gsub('_',' ')}`* (#{count})"
172
+ end
173
+ message << "\t :four_leaf_clover: Most used commands: \n\t\t\t\t#{mtu.join("\n\t\t\t\t")}"
174
+
175
+ count_channels_dest = count_channels_dest.sort_by(&:last).reverse.to_h
176
+ count_channels_dest.keys[0..0].each do |ch|
177
+ if ch=='DM'
178
+ message << "\t :star: Most used channel: *DM* (#{(count_channels_dest[ch].to_f*100/total).round(2)}%)"
179
+ else
180
+ message << "\t :star: Most used channel: *<##{@channels_id[ch]}>* (#{(count_channels_dest[ch].to_f*100/total).round(2)}%)"
181
+ end
182
+ end
183
+
184
+ types = rows.type_message.uniq.sort
185
+ count_type = {}
186
+ types.each do |type|
187
+ count = rows.count {|h| h.type_message==type}
188
+ count_type[type] = count
189
+ end
190
+
191
+ count_type.sort_by {|k,v| -v}[0..0].each do |type, count|
192
+ message << "\t :house_with_garden: Most calls came from *#{type}* (#{(count.to_f*100/total).round(2)}%)"
193
+ end
194
+ else
195
+ message << 'No data yet'
196
+ end
197
+ end
198
+ respond "#{message.join("\n")}"
199
+
200
+ end
201
+ end
@@ -1,10 +1,14 @@
1
1
  class SlackSmartBot
2
2
  # help: ----------------------------------------------
3
3
  # help: `stop using rules from CHANNEL`
4
+ # help: `stop using CHANNEL`
4
5
  # help: it will stop using the rules from the specified channel.
6
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#using-rules-from-other-channels|more info>
7
+ # help: command_id: :stop_using_rules
5
8
  # help:
6
9
  def stop_using_rules(dest, channel, user, dchannel)
7
10
  save_stats(__method__)
11
+ channel.gsub!('#','') # for the case the channel name is in plain text including #
8
12
  if @channels_id.key?(channel)
9
13
  channel_id = @channels_id[channel]
10
14
  else
@@ -12,11 +16,12 @@ class SlackSmartBot
12
16
  end
13
17
 
14
18
  if dest[0] == "C" or dest[0] == "G" #channel
15
- if @rules_imported.key?(user.id) and @rules_imported[user.id].key?(dchannel)
16
- if @rules_imported[user.id][dchannel] != channel_id
19
+ if @rules_imported.key?(user.name) and @rules_imported[user.name].key?(dchannel)
20
+ if @rules_imported[user.name][dchannel] != channel_id
17
21
  respond "You are not using those rules.", dest
18
22
  else
19
- @rules_imported[user.id].delete(dchannel)
23
+ @rules_imported[user.name].delete(dchannel)
24
+ sleep 0.5
20
25
  update_rules_imported()
21
26
  respond "You won't be using those rules from now on.", dest
22
27
 
@@ -27,11 +32,12 @@ class SlackSmartBot
27
32
  respond "You were not using those rules.", dest
28
33
  end
29
34
  else #direct message
30
- if @rules_imported.key?(user.id) and @rules_imported[user.id].key?(user.id)
31
- if @rules_imported[user.id][user.id] != channel_id
35
+ if @rules_imported.key?(user.name) and @rules_imported[user.name].key?(user.name)
36
+ if @rules_imported[user.name][user.name] != channel_id
32
37
  respond "You are not using those rules.", dest
33
38
  else
34
- @rules_imported[user.id].delete(user.id)
39
+ @rules_imported[user.name].delete(user.name)
40
+ sleep 0.5
35
41
  update_rules_imported()
36
42
  respond "You won't be using those rules from now on.", dest
37
43