slack-smart-bot 1.9.1 → 1.11.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 (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