slack-smart-bot 1.10.0 → 1.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +91 -21
  3. data/lib/slack/smart-bot/comm/delete.rb +13 -0
  4. data/lib/slack/smart-bot/comm/dont_understand.rb +2 -2
  5. data/lib/slack/smart-bot/comm/get_channel_members.rb +7 -3
  6. data/lib/slack/smart-bot/comm/get_presence.rb +20 -0
  7. data/lib/slack/smart-bot/comm/get_users.rb +1 -1
  8. data/lib/slack/smart-bot/comm/respond.rb +18 -13
  9. data/lib/slack/smart-bot/comm/send_msg_user.rb +12 -11
  10. data/lib/slack/smart-bot/comm.rb +2 -0
  11. data/lib/slack/smart-bot/commands/general/add_admin.rb +51 -0
  12. data/lib/slack/smart-bot/commands/general/add_announcement.rb +1 -1
  13. data/lib/slack/smart-bot/commands/general/add_team.rb +80 -0
  14. data/lib/slack/smart-bot/commands/general/allow_access.rb +67 -0
  15. data/lib/slack/smart-bot/commands/general/bot_help.rb +20 -11
  16. data/lib/slack/smart-bot/commands/general/delete_announcement.rb +1 -1
  17. data/lib/slack/smart-bot/commands/general/delete_share.rb +1 -1
  18. data/lib/slack/smart-bot/commands/general/delete_team.rb +34 -0
  19. data/lib/slack/smart-bot/commands/general/deny_access.rb +36 -0
  20. data/lib/slack/smart-bot/commands/general/ping_team.rb +100 -0
  21. data/lib/slack/smart-bot/commands/general/poster.rb +116 -0
  22. data/lib/slack/smart-bot/commands/general/remove_admin.rb +58 -0
  23. data/lib/slack/smart-bot/commands/general/see_access.rb +24 -0
  24. data/lib/slack/smart-bot/commands/general/see_admins.rb +33 -0
  25. data/lib/slack/smart-bot/commands/general/see_announcements.rb +6 -4
  26. data/lib/slack/smart-bot/commands/general/see_command_ids.rb +29 -0
  27. data/lib/slack/smart-bot/commands/general/see_favorite_commands.rb +3 -4
  28. data/lib/slack/smart-bot/commands/general/see_statuses.rb +34 -21
  29. data/lib/slack/smart-bot/commands/general/see_teams.rb +252 -0
  30. data/lib/slack/smart-bot/commands/general/share_messages.rb +1 -1
  31. data/lib/slack/smart-bot/commands/general/update_team.rb +109 -0
  32. data/lib/slack/smart-bot/commands/general_bot_commands.rb +271 -10
  33. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +2 -1
  34. data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +2 -1
  35. data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +2 -1
  36. data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +2 -1
  37. data/lib/slack/smart-bot/commands/on_bot/admin/pause_routine.rb +2 -1
  38. data/lib/slack/smart-bot/commands/on_bot/admin/remove_routine.rb +2 -1
  39. data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +3 -2
  40. data/lib/slack/smart-bot/commands/on_bot/admin/see_result_routine.rb +2 -1
  41. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +10 -9
  42. data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +2 -1
  43. data/lib/slack/smart-bot/commands/on_bot/admin/start_routine.rb +2 -1
  44. data/lib/slack/smart-bot/commands/on_bot/admin/stop_using_rules_on.rb +2 -1
  45. data/lib/slack/smart-bot/commands/on_bot/admin_master/delete_message.rb +25 -0
  46. data/lib/slack/smart-bot/commands/on_bot/admin_master/get_bot_logs.rb +1 -0
  47. data/lib/slack/smart-bot/commands/on_bot/admin_master/react_to.rb +3 -1
  48. data/lib/slack/smart-bot/commands/on_bot/admin_master/send_message.rb +15 -2
  49. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +2 -1
  50. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +5 -4
  51. data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +400 -0
  52. data/lib/slack/smart-bot/commands/{general → on_bot/general}/bot_status.rb +1 -0
  53. data/lib/slack/smart-bot/commands/{general → on_bot/general}/leaderboard.rb +1 -0
  54. data/lib/slack/smart-bot/commands/{general → on_bot/general}/stop_using_rules.rb +1 -0
  55. data/lib/slack/smart-bot/commands/{general → on_bot/general}/suggest_command.rb +6 -0
  56. data/lib/slack/smart-bot/commands/{general → on_bot/general}/use_rules.rb +1 -0
  57. data/lib/slack/smart-bot/commands/{general → on_bot/general}/whats_new.rb +2 -1
  58. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +2 -1
  59. data/lib/slack/smart-bot/commands/on_bot/repl.rb +72 -15
  60. data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +1 -0
  61. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +11 -2
  62. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +2 -1
  63. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +3 -2
  64. data/lib/slack/smart-bot/commands/on_master/admin/kill_bot_on_channel.rb +5 -4
  65. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +3 -2
  66. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +2 -1
  67. data/lib/slack/smart-bot/commands/on_master/admin_master/publish_announcements.rb +3 -2
  68. data/lib/slack/smart-bot/commands/on_master/admin_master/set_general_message.rb +2 -1
  69. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +2 -1
  70. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +1 -0
  71. data/lib/slack/smart-bot/commands/on_master/where_smartbot.rb +41 -0
  72. data/lib/slack/smart-bot/commands.rb +22 -7
  73. data/lib/slack/smart-bot/listen.rb +30 -30
  74. data/lib/slack/smart-bot/process.rb +38 -18
  75. data/lib/slack/smart-bot/process_first.rb +2 -2
  76. data/lib/slack/smart-bot/treat_message.rb +13 -17
  77. data/lib/slack/smart-bot/utils/build_help.rb +1 -1
  78. data/lib/slack/smart-bot/utils/create_routine_thread.rb +1 -1
  79. data/lib/slack/smart-bot/utils/get_access_channels.rb +13 -0
  80. data/lib/slack/smart-bot/utils/get_admins_channels.rb +13 -0
  81. data/lib/slack/smart-bot/utils/get_bots_created.rb +27 -10
  82. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +7 -2
  83. data/lib/slack/smart-bot/utils/get_command_ids.rb +84 -0
  84. data/lib/slack/smart-bot/utils/get_help.rb +34 -18
  85. data/lib/slack/smart-bot/utils/get_repls.rb +22 -2
  86. data/lib/slack/smart-bot/utils/get_routines.rb +22 -2
  87. data/lib/slack/smart-bot/utils/get_teams.rb +22 -0
  88. data/lib/slack/smart-bot/utils/has_access.rb +25 -9
  89. data/lib/slack/smart-bot/utils/is_admin.rb +27 -0
  90. data/lib/slack/smart-bot/utils/save_stats.rb +46 -43
  91. data/lib/slack/smart-bot/utils/save_status.rb +21 -6
  92. data/lib/slack/smart-bot/utils/update_access_channels.rb +8 -0
  93. data/lib/slack/smart-bot/utils/update_admins_channels.rb +8 -0
  94. data/lib/slack/smart-bot/utils/update_bots_file.rb +28 -7
  95. data/lib/slack/smart-bot/utils/update_repls.rb +7 -4
  96. data/lib/slack/smart-bot/utils/update_routines.rb +9 -3
  97. data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +13 -6
  98. data/lib/slack/smart-bot/utils/update_teams.rb +16 -0
  99. data/lib/slack/smart-bot/utils.rb +8 -0
  100. data/lib/slack-smart-bot.rb +28 -10
  101. data/lib/slack-smart-bot_general_commands.rb +16 -1
  102. data/whats_new.txt +16 -29
  103. metadata +64 -19
  104. data/lib/slack/smart-bot/commands/general/bot_stats.rb +0 -314
@@ -2,12 +2,14 @@ class SlackSmartBot
2
2
 
3
3
  # helpadmin: ----------------------------------------------
4
4
  # helpadmin: `react to #CHANNEL_NAME THREAD_ID EMOJIS`
5
+ # helpadmin: `react to URL EMOJIS`
5
6
  # helpadmin: It will send the specified reactions as SmartBot
6
7
  # helpadmin: You can use this command only if you are a Master admin user and if you are in a private conversation with the bot
7
8
  # helpadmin: Examples:
8
9
  # helpadmin: _react to #sales 1622550707.012100 :thumbsup:_
9
10
  # helpadmin: _react to #sales p1622550707012100 :thumbsup:_
10
11
  # helpadmin: _react to #sales p1622550707012100 :thumbsup: :heavy_check_mark: :bathtub:_
12
+ # helpadmin: command_id: :react_to
11
13
  # helpadmin:
12
14
  def react_to(dest, from, typem, to, thread_ts, emojis)
13
15
  save_stats(__method__)
@@ -25,7 +27,7 @@ class SlackSmartBot
25
27
  react :x
26
28
  end
27
29
  else
28
- respond "Only master admin users on a `pr`ivate conversation with the SmartBot can send reactions as SmartBot.", dest
30
+ respond "Only master admin users on a private conversation with the SmartBot can send reactions as SmartBot.", dest
29
31
  end
30
32
  end
31
33
  end
@@ -3,14 +3,27 @@ class SlackSmartBot
3
3
  # helpadmin: ----------------------------------------------
4
4
  # helpadmin: `send message to @USER_NAME : MESSAGE`
5
5
  # helpadmin: `send message to #CHANNEL_NAME : MESSAGE`
6
- # helpadmin: `send message to #CHANNEL_NAME THREAD_ID : MESSAGE`
6
+ # helpadmin: `send message to THREAD_ID : MESSAGE`
7
+ # helpadmin: `send message to URL : MESSAGE`
8
+ # helpadmin: `send message to @USER1 @USER99 : MESSAGE`
9
+ # helpadmin: `send message to #CHANNEL1 #CHANNEL99 : MESSAGE`
7
10
  # helpadmin: It will send the specified message as SmartBot
8
11
  # helpadmin: You can use this command only if you are a Master admin user and if you are in a private conversation with the bot
12
+ # helpadmin: command_id: :send_message
9
13
  # helpadmin:
10
14
  def send_message(dest, from, typem, to, thread_ts, message)
11
15
  save_stats(__method__)
12
16
  if config.masters.include?(from) and typem==:on_dm #master admin user
13
- succ = (respond message, to, thread_ts: thread_ts, web_client: true)
17
+ unless Thread.current[:command_orig].to_s == ''
18
+ message_orig = Thread.current[:command_orig].to_s.gsub("\u00A0", " ").scan(/[^:]+\s*:\s+(.+)/im).join
19
+ message = message_orig unless message_orig == ''
20
+ end
21
+ succ = true
22
+ to.each do |t|
23
+ unless t.match?(/^\s*$/)
24
+ succ = (respond message, t, thread_ts: thread_ts, web_client: true) && succ
25
+ end
26
+ end
14
27
  if succ
15
28
  react :heavy_check_mark
16
29
  else
@@ -6,6 +6,7 @@ class SlackSmartBot
6
6
  # help: Will delete the specified REPL
7
7
  # help: Only the creator of the REPL or an admin can delete REPLs
8
8
  # help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
9
+ # help: command_id: :delete_repl
9
10
  # help:
10
11
  def delete_repl(dest, user, session_name)
11
12
  #todo: add tests
@@ -13,7 +14,7 @@ class SlackSmartBot
13
14
  if has_access?(__method__, user)
14
15
  if @repls.key?(session_name)
15
16
  Dir.mkdir("#{config.path}/repl") unless Dir.exist?("#{config.path}/repl")
16
- if config.admins.include?(user.name) or @repls[session_name].creator_name == user.name
17
+ if is_admin?(user.name) or @repls[session_name].creator_name == user.name
17
18
  @repls.delete(session_name)
18
19
  update_repls()
19
20
  File.rename("#{config.path}/repl/#{@channel_id}/#{session_name}.input", "#{config.path}/repl/#{@channel_id}/#{session_name}_#{Time.now.strftime("%Y%m%d%H%M%S%N")}.deleted")
@@ -6,6 +6,7 @@ class SlackSmartBot
6
6
  # help: It will delete the shortcut with the supplied name
7
7
  # help: 'global' or 'generic' can only be used on Master channel.
8
8
  # help: <https://github.com/MarioRuiz/slack-smart-bot#shortcuts|more info>
9
+ # help: command_id: :delete_shortcut
9
10
  # help:
10
11
 
11
12
  def delete_shortcut(dest, user, shortcut, typem, command, global)
@@ -19,11 +20,11 @@ class SlackSmartBot
19
20
  if !config.on_master_bot or typem != :on_master
20
21
  respond "It is only possible to delete global shortcuts from Master channel"
21
22
  else
22
- if !config.admins.include?(from) and @shortcuts_global[:all].include?(shortcut) and
23
+ if !is_admin?(from) and @shortcuts_global[:all].include?(shortcut) and
23
24
  (!@shortcuts_global.key?(from) or !@shortcuts_global[from].include?(shortcut))
24
25
  respond "Only the creator of the shortcut or an admin user can delete it"
25
26
  elsif (@shortcuts_global.key?(from) and @shortcuts_global[from].keys.include?(shortcut)) or
26
- (config.admins.include?(from) and @shortcuts_global[:all].include?(shortcut))
27
+ (is_admin?(from) and @shortcuts_global[:all].include?(shortcut))
27
28
 
28
29
  respond "global shortcut deleted!", dest
29
30
  if @shortcuts_global.key?(from) and @shortcuts_global[from].key?(shortcut)
@@ -39,11 +40,11 @@ class SlackSmartBot
39
40
  end
40
41
  end
41
42
  else
42
- if !config.admins.include?(from) and @shortcuts[:all].include?(shortcut) and
43
+ if !is_admin?(from) and @shortcuts[:all].include?(shortcut) and
43
44
  (!@shortcuts.key?(from) or !@shortcuts[from].include?(shortcut))
44
45
  respond "Only the creator of the shortcut or an admin user can delete it", dest
45
46
  elsif (@shortcuts.keys.include?(from) and @shortcuts[from].keys.include?(shortcut)) or
46
- (config.admins.include?(from) and @shortcuts[:all].include?(shortcut))
47
+ (is_admin?(from) and @shortcuts[:all].include?(shortcut))
47
48
  #are you sure? to avoid deleting by mistake
48
49
  if answer.empty?
49
50
  ask("are you sure you want to delete it?", command, from, dest)
@@ -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
@@ -4,6 +4,7 @@ class SlackSmartBot
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
6
  # helpadmin: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
7
+ # helpadmin: command_id: :bot_status
7
8
  # helpadmin:
8
9
  def bot_status(dest, user)
9
10
  save_stats(__method__)
@@ -17,6 +17,7 @@ class SlackSmartBot
17
17
  # help: _leaderboard from 2021/05/01 to 2021/05/31_
18
18
  # help: _ranking today_
19
19
  # help: <https://github.com/MarioRuiz/slack-smart-bot#bot-management|more info>
20
+ # help: command_id: :leaderboard
20
21
  # help:
21
22
 
22
23
  def leaderboard(from, to, period)
@@ -4,6 +4,7 @@ class SlackSmartBot
4
4
  # help: `stop using CHANNEL`
5
5
  # help: it will stop using the rules from the specified channel.
6
6
  # help: <https://github.com/MarioRuiz/slack-smart-bot#using-rules-from-other-channels|more info>
7
+ # help: command_id: :stop_using_rules
7
8
  # help:
8
9
  def stop_using_rules(dest, channel, user, dchannel)
9
10
  save_stats(__method__)
@@ -10,12 +10,17 @@ class SlackSmartBot
10
10
  # help: it will display the help content for a random command.
11
11
  # help: if used 'rule' then it will display a random rule.
12
12
  # help: if used 'command' it will show any kind of command or rule.
13
+ # help: command_id: :suggest_command
13
14
  # help:
14
15
  def suggest_command(from, dest, dchannel, specific, rules_file)
15
16
  save_stats(__method__)
17
+ dont_suggest = []
16
18
  help_message = get_help(rules_file, dest, from, specific, true, descriptions: false, only_normal_user: true)
17
19
  commands = help_message.gsub(/====+/,'-'*30).split(/^\s*-------*$/).flatten
18
20
  commands.reject!{|c| c.match?(/These are specific commands for this bot on this/i) || c.match?(/\A\s*\z/)}
21
+ dont_suggest.each do |ds|
22
+ commands.reject!{|c| c.match?(/:#{ds}\s*$/i)}
23
+ end
19
24
  @last_suggested_commands ||= []
20
25
  @last_suggested_commands.shift if @last_suggested_commands.size >=5
21
26
  command = ''
@@ -23,6 +28,7 @@ class SlackSmartBot
23
28
  command = commands.sample
24
29
  end until !@last_suggested_commands.include?(command) or commands.size <= 5
25
30
  @last_suggested_commands << command
31
+ command.gsub!(/^\s*command_id:\s+:\w+\s*$/,'')
26
32
  message = "*Command suggestion*:\n#{command}"
27
33
  respond message, dest, unfurl_links: false, unfurl_media: false
28
34
  end
@@ -7,6 +7,7 @@ class SlackSmartBot
7
7
  # help: it will use the rules from the specified channel.
8
8
  # help: you need to be part of that channel to be able to use the rules.
9
9
  # help: <https://github.com/MarioRuiz/slack-smart-bot#using-rules-from-other-channels|more info>
10
+ # help: command_id: :use_rules
10
11
  # help:
11
12
  def use_rules(dest, channel, user, dchannel)
12
13
  save_stats(__method__)
@@ -3,11 +3,12 @@ class SlackSmartBot
3
3
  # help: ----------------------------------------------
4
4
  # help: `What's new`
5
5
  # help: It will display the last user changes on Slack Smart Bot
6
+ # help: command_id: :whats_new
6
7
  # help:
7
8
  def whats_new(user, dest, dchannel, from, display_name)
8
9
  if @status == :on
9
10
  save_stats(__method__)
10
- whats_new_file = (__FILE__).gsub(/lib\/slack\/smart-bot\/commands\/general\/whats_new\.rb$/, "whats_new.txt")
11
+ whats_new_file = (__FILE__).gsub(/lib\/slack\/smart-bot\/commands\/on_bot\/general\/whats_new\.rb$/, "whats_new.txt")
11
12
  whats_new = File.read(whats_new_file)
12
13
  whats_new.split(/^\-\-\-\-\-\-+$/).each do |msg|
13
14
  respond msg
@@ -5,6 +5,7 @@ class SlackSmartBot
5
5
  # help: `get live SESSION_NAME`
6
6
  # help: Will get the Ruby commands sent on that SESSION_NAME.
7
7
  # help: <https://github.com/MarioRuiz/slack-smart-bot#repl|more info>
8
+ # help: command_id: :get_repl
8
9
  # help:
9
10
  def get_repl(dest, user, session_name)
10
11
  #todo: add tests
@@ -15,7 +16,7 @@ class SlackSmartBot
15
16
  if File.exist?("#{config.path}/repl/#{@channel_id}/#{session_name}.run")
16
17
  if @repls.key?(session_name) and (@repls[session_name][:type] == :private or @repls[session_name][:type] == :private_clean) and
17
18
  @repls[session_name][:creator_name]!=user.name and
18
- !config.admins.include?(user.name)
19
+ !is_admin?(user.name)
19
20
  respond "The REPL with session name: #{session_name} is private", dest
20
21
  else
21
22
  content = "require 'nice_http'\n"