slack-smart-bot 1.8.2 → 1.9.1

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +18 -11
  3. data/lib/slack-smart-bot.rb +43 -44
  4. data/lib/slack-smart-bot_rules.rb +6 -6
  5. data/lib/slack/smart-bot/comm.rb +5 -1
  6. data/lib/slack/smart-bot/comm/ask.rb +12 -5
  7. data/lib/slack/smart-bot/comm/dont_understand.rb +1 -1
  8. data/lib/slack/smart-bot/comm/event_hello.rb +30 -0
  9. data/lib/slack/smart-bot/comm/get_channel_members.rb +8 -0
  10. data/lib/slack/smart-bot/comm/get_channels.rb +20 -0
  11. data/lib/slack/smart-bot/comm/get_user_info.rb +16 -0
  12. data/lib/slack/smart-bot/comm/react.rb +21 -8
  13. data/lib/slack/smart-bot/comm/respond.rb +10 -5
  14. data/lib/slack/smart-bot/comm/send_msg_channel.rb +2 -2
  15. data/lib/slack/smart-bot/comm/send_msg_user.rb +4 -4
  16. data/lib/slack/smart-bot/comm/unreact.rb +21 -8
  17. data/lib/slack/smart-bot/commands.rb +3 -1
  18. data/lib/slack/smart-bot/commands/general/bot_help.rb +16 -3
  19. data/lib/slack/smart-bot/commands/general/bot_stats.rb +313 -0
  20. data/lib/slack/smart-bot/commands/general/bot_status.rb +1 -1
  21. data/lib/slack/smart-bot/commands/general/bye_bot.rb +1 -1
  22. data/lib/slack/smart-bot/commands/general/hi_bot.rb +1 -1
  23. data/lib/slack/smart-bot/commands/general/use_rules.rb +2 -6
  24. data/lib/slack/smart-bot/commands/general/whats_new.rb +19 -0
  25. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +65 -33
  26. data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +3 -7
  27. data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +1 -0
  28. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +8 -2
  29. data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +1 -0
  30. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +1 -1
  31. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +52 -21
  32. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +5 -5
  33. data/lib/slack/smart-bot/commands/on_bot/repl.rb +50 -18
  34. data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +34 -9
  35. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +2 -3
  36. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +1 -1
  37. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +27 -9
  38. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +14 -1
  39. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +3 -3
  40. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +1 -1
  41. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +41 -0
  42. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +4 -8
  43. data/lib/slack/smart-bot/listen.rb +6 -5
  44. data/lib/slack/smart-bot/process.rb +227 -188
  45. data/lib/slack/smart-bot/process_first.rb +104 -87
  46. data/lib/slack/smart-bot/treat_message.rb +98 -38
  47. data/lib/slack/smart-bot/utils.rb +2 -0
  48. data/lib/slack/smart-bot/utils/answer.rb +18 -0
  49. data/lib/slack/smart-bot/utils/answer_delete.rb +15 -0
  50. data/lib/slack/smart-bot/utils/build_help.rb +57 -5
  51. data/lib/slack/smart-bot/utils/create_routine_thread.rb +11 -2
  52. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +1 -7
  53. data/lib/slack/smart-bot/utils/get_help.rb +79 -17
  54. data/lib/slack/smart-bot/utils/save_stats.rb +21 -8
  55. data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +6 -0
  56. data/whats_new.txt +18 -0
  57. metadata +21 -12
  58. data/lib/slack/smart-bot/commands/on_bot/admin_master/bot_stats.rb +0 -195
@@ -0,0 +1,16 @@
1
+ class SlackSmartBot
2
+
3
+ def get_user_info(user)
4
+ if user.to_s.length>0
5
+ if config.simulate and config.key?(:client)
6
+ if user[0]=='@' #name
7
+ client.web_client.users_info.select{|k, v| v[:user][:name] == user[1..-1]}.values[-1]
8
+ else #id
9
+ client.web_client.users_info[user.to_sym]
10
+ end
11
+ else
12
+ client.web_client.users_info(user: user)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,16 +1,29 @@
1
1
  class SlackSmartBot
2
2
  # list of available emojis: https://www.webfx.com/tools/emoji-cheat-sheet/
3
3
  # react(:thumbsup)
4
- def react(emoji, parent=false)
5
- if parent or Thread.current[:ts].to_s == ''
6
- ts = Thread.current[:thread_ts]
4
+ # ts: can be true, false or a specific ts
5
+ def react(emoji, ts=false)
6
+ if ts.is_a?(TrueClass) or ts.is_a?(FalseClass)
7
+ parent = ts
8
+ ts = nil
7
9
  else
8
- ts = Thread.current[:ts]
10
+ parent = false
9
11
  end
10
- begin
11
- client.web_client.reactions_add(channel: Thread.current[:dest], name: emoji, timestamp: ts)
12
- rescue Exception => stack
13
- @logger.warn stack
12
+ if ts.nil?
13
+ if parent or Thread.current[:ts].to_s == ''
14
+ ts = Thread.current[:thread_ts]
15
+ else
16
+ ts = Thread.current[:ts]
17
+ end
18
+ end
19
+ if ts.nil?
20
+ @logger.warn 'react method no ts supplied'
21
+ else
22
+ begin
23
+ client.web_client.reactions_add(channel: Thread.current[:dest], name: emoji, timestamp: ts) unless config.simulate
24
+ rescue Exception => stack
25
+ @logger.warn stack
26
+ end
14
27
  end
15
28
  end
16
29
  end
@@ -4,10 +4,14 @@ class SlackSmartBot
4
4
  dest = Thread.current[:dest]
5
5
  end
6
6
  dest = @channels_id[dest] if @channels_id.key?(dest) #it is a name of channel
7
+ if !config.simulate #https://api.slack.com/docs/rate-limits
8
+ msg.to_s.size > 500 ? wait = 0.5 : wait = 0.1
9
+ sleep wait if Time.now <= (@last_respond+wait)
10
+ end
7
11
  if dest.nil?
8
12
  if config[:simulate]
9
13
  open("#{config.path}/buffer_complete.log", "a") { |f|
10
- f.puts "|#{@channel_id}|#{config[:nick_id]}|#{msg}~~~"
14
+ f.puts "|#{@channel_id}|#{config[:nick_id]}|#{config[:nick]}|#{msg}~~~"
11
15
  }
12
16
  else
13
17
  if Thread.current[:on_thread]
@@ -18,13 +22,13 @@ class SlackSmartBot
18
22
  end
19
23
  if config[:testing] and config.on_master_bot
20
24
  open("#{config.path}/buffer.log", "a") { |f|
21
- f.puts "|#{@channel_id}|#{config[:nick_id]}|#{msg}"
25
+ f.puts "|#{@channel_id}|#{config[:nick_id]}|#{config[:nick]}|#{msg}"
22
26
  }
23
27
  end
24
28
  elsif dest[0] == "C" or dest[0] == "G" # channel
25
29
  if config[:simulate]
26
30
  open("#{config.path}/buffer_complete.log", "a") { |f|
27
- f.puts "|#{dest}|#{config[:nick_id]}|#{msg}~~~"
31
+ f.puts "|#{dest}|#{config[:nick_id]}|#{config[:nick]}|#{msg}~~~"
28
32
  }
29
33
  else
30
34
  if Thread.current[:on_thread]
@@ -35,14 +39,14 @@ class SlackSmartBot
35
39
  end
36
40
  if config[:testing] and config.on_master_bot
37
41
  open("#{config.path}/buffer.log", "a") { |f|
38
- f.puts "|#{dest}|#{config[:nick_id]}|#{msg}"
42
+ f.puts "|#{dest}|#{config[:nick_id]}|#{config[:nick]}|#{msg}"
39
43
  }
40
44
  end
41
45
  elsif dest[0] == "D" or dest[0] == "U" or dest[0] == "W" # Direct message
42
46
  send_msg_user(dest, msg)
43
47
  elsif dest[0] == "@"
44
48
  begin
45
- user_info = client.web_client.users_info(user: dest)
49
+ user_info = get_user_info(dest)
46
50
  send_msg_user(user_info.user.id, msg)
47
51
  rescue Exception => stack
48
52
  @logger.warn("user #{dest} not found.")
@@ -54,6 +58,7 @@ class SlackSmartBot
54
58
  else
55
59
  @logger.warn("method respond not treated correctly: msg:#{msg} dest:#{dest}")
56
60
  end
61
+ @last_respond = Time.now
57
62
  end
58
63
 
59
64
  end
@@ -14,7 +14,7 @@ class SlackSmartBot
14
14
  end
15
15
  if config[:simulate]
16
16
  open("#{config.path}/buffer_complete.log", "a") { |f|
17
- f.puts "|#{channel_id}|#{config[:nick_id]}|#{msg}~~~"
17
+ f.puts "|#{channel_id}|#{config[:nick_id]}|#{config[:nick]}|#{msg}~~~"
18
18
  }
19
19
  else
20
20
  if Thread.current[:on_thread]
@@ -25,7 +25,7 @@ class SlackSmartBot
25
25
  end
26
26
  if config[:testing] and config.on_master_bot
27
27
  open("#{config.path}/buffer.log", "a") { |f|
28
- f.puts "|#{channel_id}|#{config[:nick_id]}|#{msg}"
28
+ f.puts "|#{channel_id}|#{config[:nick_id]}|#{config[:nick]}|#{msg}"
29
29
  }
30
30
  end
31
31
  end
@@ -6,7 +6,7 @@ class SlackSmartBot
6
6
  if id_user[0] == "D"
7
7
  if config[:simulate]
8
8
  open("#{config.path}/buffer_complete.log", "a") { |f|
9
- f.puts "|#{id_user}|#{config[:nick_id]}|#{msg}~~~"
9
+ f.puts "|#{id_user}|#{config[:nick_id]}|#{config[:nick]}|#{msg}~~~"
10
10
  }
11
11
  else
12
12
  if Thread.current[:on_thread]
@@ -17,14 +17,14 @@ class SlackSmartBot
17
17
  end
18
18
  if config[:testing] and config.on_master_bot
19
19
  open("#{config.path}/buffer.log", "a") { |f|
20
- f.puts "|#{id_user}|#{config[:nick_id]}|#{msg}"
20
+ f.puts "|#{id_user}|#{config[:nick_id]}|#{config[:nick]}|#{msg}"
21
21
  }
22
22
  end
23
23
  else
24
24
  im = client.web_client.im_open(user: id_user)
25
25
  if config[:simulate]
26
26
  open("#{config.path}/buffer_complete.log", "a") { |f|
27
- f.puts "|#{im["channel"]["id"]}|#{config[:nick_id]}|#{msg}~~~"
27
+ f.puts "|#{im["channel"]["id"]}|#{config[:nick_id]}|#{config[:nick]}|#{msg}~~~"
28
28
  }
29
29
  else
30
30
  if Thread.current[:on_thread]
@@ -35,7 +35,7 @@ class SlackSmartBot
35
35
  end
36
36
  if config[:testing] and config.on_master_bot
37
37
  open("#{config.path}/buffer.log", "a") { |f|
38
- f.puts "|#{im["channel"]["id"]}|#{config[:nick_id]}|#{msg}"
38
+ f.puts "|#{im["channel"]["id"]}|#{config[:nick_id]}|#{config[:nick]}|#{msg}"
39
39
  }
40
40
  end
41
41
  end
@@ -1,16 +1,29 @@
1
1
  class SlackSmartBot
2
2
  # list of available emojis: https://www.webfx.com/tools/emoji-cheat-sheet/
3
3
  # unreact(:thumbsup)
4
- def unreact(emoji, parent=false)
5
- if parent
6
- ts = Thread.current[:thread_ts]
4
+ # ts: can be true, false or a specific ts
5
+ def unreact(emoji, ts=false)
6
+ if ts.is_a?(TrueClass) or ts.is_a?(FalseClass)
7
+ parent = ts
8
+ ts = nil
7
9
  else
8
- ts = Thread.current[:ts]
10
+ parent = false
9
11
  end
10
- begin
11
- client.web_client.reactions_remove(channel: Thread.current[:dest], name: emoji, timestamp: ts)
12
- rescue Exception => stack
13
- @logger.warn stack
12
+ if ts.nil?
13
+ if parent or Thread.current[:ts].to_s == ''
14
+ ts = Thread.current[:thread_ts]
15
+ else
16
+ ts = Thread.current[:ts]
17
+ end
18
+ end
19
+ if ts.nil?
20
+ @logger.warn 'unreact method no ts supplied'
21
+ else
22
+ begin
23
+ client.web_client.reactions_remove(channel: Thread.current[:dest], name: emoji, timestamp: ts) unless config.simulate
24
+ rescue Exception => stack
25
+ @logger.warn stack
26
+ end
14
27
  end
15
28
  end
16
29
  end
@@ -1,3 +1,4 @@
1
+ require_relative "commands/general/whats_new"
1
2
  require_relative "commands/general/hi_bot"
2
3
  require_relative "commands/general/bye_bot"
3
4
  require_relative "commands/general/bot_help"
@@ -29,4 +30,5 @@ require_relative "commands/on_bot/delete_shortcut"
29
30
  require_relative "commands/on_bot/see_shortcuts"
30
31
  require_relative "commands/on_extended/bot_rules"
31
32
  require_relative "commands/on_bot/admin_master/get_bot_logs"
32
- require_relative "commands/on_bot/admin_master/bot_stats"
33
+ require_relative "commands/general/bot_stats"
34
+ require_relative "commands/on_master/admin_master/set_maintenance"
@@ -5,9 +5,12 @@ class SlackSmartBot
5
5
  # help: `bot help COMMAND`
6
6
  # help: `bot rules`
7
7
  # help: `bot rules COMMAND`
8
+ # help: `bot help expanded`
9
+ # help: `bot rules expanded`
8
10
  # help: `bot what can I do?`
9
- # help: it will display this help
11
+ # help: it will display this help. For a more detailed help call `bot help expanded` or `bot rules expanded`.
10
12
  # help: if COMMAND supplied just help for that command
13
+ # help: you can use the option 'expanded' or the alias 'extended'
11
14
  # help: `bot rules` will show only the specific rules for this channel.
12
15
  # help:
13
16
  def bot_help(user, from, dest, dchannel, specific, help_command, rules_file)
@@ -19,8 +22,17 @@ class SlackSmartBot
19
22
  help_found = false
20
23
 
21
24
  message = ""
25
+ if help_command.to_s != ''
26
+ help_command = '' if help_command.to_s.match?(/^\s*expanded\s*$/i) or help_command.to_s.match?(/^\s*extended\s*$/i)
27
+ expanded = true
28
+ message_not_expanded = ''
29
+ else
30
+ expanded = false
31
+ message_not_expanded = "*If you want to see the expanded version of `bot help` or `bot rules`, please call `bot help expanded` or `bot rules expanded`*\n"
32
+ message_not_expanded += "*Also to get specific expanded help for a specific command or rule call `bot help COMMAND`*\n"
33
+ end
22
34
 
23
- help_message = get_help(rules_file, dest, from, specific)
35
+ help_message = get_help(rules_file, dest, from, specific, expanded)
24
36
 
25
37
  if help_command.to_s != ""
26
38
  help_message.gsub(/====+/,'-'*30).split(/^\s*-------*$/).each do |h|
@@ -31,7 +43,7 @@ class SlackSmartBot
31
43
  end
32
44
  else
33
45
  if Thread.current[:using_channel]!=''
34
- message = "*You are using rules from another channel: <##{Thread.current[:using_channel]}>. These are the specific commands for that channel:*"
46
+ message += "*You are using rules from another channel: <##{Thread.current[:using_channel]}>. These are the specific commands for that channel:*"
35
47
  end
36
48
  respond message, dest
37
49
  end
@@ -70,6 +82,7 @@ class SlackSmartBot
70
82
  elsif help_command.to_s == ""
71
83
  respond "Slack Smart Bot Github project: https://github.com/MarioRuiz/slack-smart-bot", dest
72
84
  end
85
+ respond message_not_expanded unless expanded
73
86
  end
74
87
  end
75
88
  end
@@ -0,0 +1,313 @@
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 today`
17
+ # help: `bot stats exclude COMMAND_ID`
18
+ # help: `bot stats monthly`
19
+ # help: `bot stats alldata`
20
+ # help: To see the bot stats
21
+ # 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
22
+ # helpmaster: You need to set stats to true to generate the stats when running the bot instance.
23
+ # help: If alldata option supplied then it will be attached files including all data and not only the top 10.
24
+ # help: Examples:
25
+ # help: _bot stats #sales_
26
+ # helpmaster: _bot stats @peter.wind_
27
+ # help: _bot stats #sales from 2019/12/15 to 2019/12/31_
28
+ # help: _bot stats #sales today_
29
+ # help: _bot stats #sales from 2020-01-01 monthly_
30
+ # help: _bot stats exclude routines masters from 2021/01/01 monthly_
31
+ # help:
32
+ def bot_stats(dest, from_user, typem, channel_id, from, to, user, st_command, exclude_masters, exclude_routines, exclude_command, monthly, all_data)
33
+ require 'csv'
34
+ if config.stats
35
+ message = []
36
+ else
37
+ message = ["You need to set stats to true to generate the stats when running the bot instance."]
38
+ end
39
+ save_stats(__method__)
40
+ 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'))
41
+ on_dm_master = true #master admin user
42
+ else
43
+ on_dm_master = false
44
+ end
45
+ if on_dm_master or (from_user.id == user) # normal user can only see own stats
46
+ if !File.exist?("#{config.stats_path}.#{Time.now.strftime("%Y-%m")}.log")
47
+ message<<'No stats'
48
+ else
49
+ from = "#{Time.now.strftime("%Y-%m")}-01" if from == ''
50
+ to = "#{Time.now.strftime("%Y-%m-%d")}" if to == ''
51
+ from_short = from
52
+ to_short = to
53
+ from_file = from[0..3] + '-' + from[5..6]
54
+ to_file = to[0..3] + '-' + to[5..6]
55
+ from+= " 00:00:00 +0000"
56
+ to+= " 23:59:59 +0000"
57
+ rows = []
58
+ rows_month = {}
59
+ users_month = {}
60
+ commands_month = {}
61
+ users_id_name = {}
62
+ users_name_id = {}
63
+ count_users = {}
64
+ count_channels_dest = {}
65
+
66
+ # to translate global and enterprise users since sometimes was returning different names/ids
67
+ if from[0..3]=='2020' # this was an issue only on that period
68
+ Dir["#{config.stats_path}.*.log"].sort.each do |file|
69
+ if file >= "#{config.stats_path}.#{from_file}.log" or file <= "#{config.stats_path}.#{to_file}.log"
70
+ CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
71
+ unless users_id_name.key?(row[:user_id])
72
+ users_id_name[row[:user_id]] = row[:user_name]
73
+ end
74
+ unless users_name_id.key?(row[:user_name])
75
+ users_name_id[row[:user_name]] = row[:user_id]
76
+ end
77
+
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ if user!=''
84
+ user_info = get_user_info(user)
85
+ if users_id_name.key?(user_info.user.id)
86
+ user_name = users_id_name[user_info.user.id]
87
+ else
88
+ user_name = user_info.user.name
89
+ end
90
+ if users_name_id.key?(user_info.user.name)
91
+ user_id = users_name_id[user_info.user.name]
92
+ else
93
+ user_id = user_info.user.id
94
+ end
95
+ end
96
+ master_admins = config.masters.dup
97
+ if users_id_name.size > 0
98
+ config.masters.each do |u|
99
+ if users_id_name.key?(u)
100
+ master_admins << users_id_name[u]
101
+ elsif users_name_id.key?(u)
102
+ master_admins << users_name_id[u]
103
+ end
104
+ end
105
+ end
106
+
107
+ Dir["#{config.stats_path}.*.log"].sort.each do |file|
108
+ if file >= "#{config.stats_path}.#{from_file}.log" or file <= "#{config.stats_path}.#{to_file}.log"
109
+ CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
110
+ row[:date] = row[:date].to_s
111
+ if row[:dest_channel_id].to_s[0]=='D'
112
+ row[:dest_channel] = 'DM'
113
+ elsif row[:dest_channel].to_s == ''
114
+ row[:dest_channel] = row[:dest_channel_id]
115
+ end
116
+ if users_name_id.size > 0
117
+ row[:user_name] = users_id_name[row[:user_id]]
118
+ row[:user_id] = users_name_id[row[:user_name]]
119
+ else
120
+ users_id_name[row[:user_id]] ||= row[:user_name]
121
+ end
122
+ if !exclude_masters or (exclude_masters and !master_admins.include?(row[:user_name]) and
123
+ !master_admins.include?(row[:user_id]) and
124
+ !@master_admin_users_id.include?(row[:user_id]))
125
+ if !exclude_routines or (exclude_routines and !row[:user_name].match?(/^routine\//) )
126
+ if exclude_command == '' or (exclude_command!='' and row[:command]!=exclude_command)
127
+ if st_command == '' or (st_command != '' and row[:command] == st_command)
128
+ if row[:bot_channel_id] == channel_id or channel_id == ''
129
+ if row[:date] >= from and row[:date] <= to
130
+ count_users[row[:user_id]] ||= 0
131
+ count_users[row[:user_id]] += 1
132
+ if user=='' or (user!='' and row[:user_name] == user_name) or (user!='' and row[:user_id] == user_id)
133
+ rows << row.to_h
134
+ count_channels_dest[row[:dest_channel]] ||= 0
135
+ count_channels_dest[row[:dest_channel]] += 1
136
+ if monthly
137
+ rows_month[row[:date][0..6]] = 0 unless rows_month.key?(row[:date][0..6])
138
+ users_month[row[:date][0..6]] = [] unless users_month.key?(row[:date][0..6])
139
+ commands_month[row[:date][0..6]] = [] unless commands_month.key?(row[:date][0..6])
140
+ rows_month[row[:date][0..6]] += 1
141
+ users_month[row[:date][0..6]] << row[:user_id]
142
+ commands_month[row[:date][0..6]] << row[:command]
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ total = rows.size
155
+ if exclude_masters
156
+ message << 'Excluding master admins'
157
+ end
158
+ if exclude_routines
159
+ message << 'Excluding routines'
160
+ end
161
+ if exclude_command != ''
162
+ message << "Excluding command #{exclude_command}"
163
+ end
164
+ if st_command != ''
165
+ message << "Including only command #{st_command}"
166
+ end
167
+ if user!=''
168
+ if user==from_user.id
169
+ message << "Bot stats for <@#{user}>"
170
+ else
171
+ message << "Showing only user <@#{user}>"
172
+ end
173
+ end
174
+ if channel_id == ''
175
+ message << "*Total calls*: #{total} from #{from_short} to #{to_short}"
176
+ else
177
+ message << "*Total calls <##{channel_id}>*: #{total} from #{from_short} to #{to_short}"
178
+ end
179
+ unless count_users.size == 0 or total == 0 or user == ''
180
+ my_place = (count_users.sort_by(&:last).reverse.to_h.keys.index(user_id)+1)
181
+ message <<"\tYou are the *\# #{my_place}* of *#{count_users.size}* users"
182
+ end
183
+ if total > 0
184
+ if monthly
185
+ if on_dm_master
186
+ message << '*Totals by month / commands / users (%new)*'
187
+ else
188
+ message << '*Totals by month / commands*'
189
+ end
190
+
191
+ all_users = []
192
+ new_users = []
193
+ rows_month.each do |k,v|
194
+ if all_users.empty?
195
+ message_new_users = ''
196
+ else
197
+ new_users = (users_month[k]-all_users).uniq
198
+ message_new_users = "(#{new_users.size*100/users_month[k].uniq.size}%)"
199
+ end
200
+ all_users += users_month[k]
201
+ if on_dm_master
202
+ message << "\t#{k}: #{v} (#{(v.to_f*100/total).round(2)}%) / #{commands_month[k].uniq.size} / #{users_month[k].uniq.size} #{message_new_users}"
203
+ else
204
+ message << "\t#{k}: #{v} (#{(v.to_f*100/total).round(2)}%) / #{commands_month[k].uniq.size}"
205
+ end
206
+ end
207
+ end
208
+
209
+ if channel_id == ''
210
+ message << "*SmartBots*"
211
+ channels = rows.bot_channel.uniq.sort
212
+ channels.each do |channel|
213
+ count = rows.count {|h| h.bot_channel==channel}
214
+ message << "\t#{channel}: #{count} (#{(count.to_f*100/total).round(2)}%)"
215
+ end
216
+ end
217
+ channels_dest_attachment = []
218
+ count_channels_dest = count_channels_dest.sort_by(&:last).reverse.to_h
219
+ if count_channels_dest.size > 10
220
+ message << "*From Channel* - #{count_channels_dest.size} (Top 10)"
221
+ else
222
+ message << "*From Channel* - #{count_channels_dest.size}"
223
+ end
224
+
225
+ count_channels_dest.keys[0..9].each do |ch|
226
+ message << "\t#{ch}: #{count_channels_dest[ch]} (#{(count_channels_dest[ch].to_f*100/total).round(2)}%)"
227
+ end
228
+ if count_channels_dest.size > 10 and all_data
229
+ count_channels_dest.each do |ch, value|
230
+ channels_dest_attachment << "\t#{ch}: #{value} (#{(value.to_f*100/total).round(2)}%)"
231
+ end
232
+ end
233
+
234
+
235
+ users_attachment = []
236
+ if user==''
237
+ users = rows.user_id.uniq.sort
238
+ if users.size > 10
239
+ message << "*Users* - #{users.size} (Top 10)"
240
+ else
241
+ message << "*Users* - #{users.size}"
242
+ end
243
+ count_user = {}
244
+ users.each do |user|
245
+ count = rows.count {|h| h.user_id==user}
246
+ count_user[user] = count
247
+ end
248
+ i = 0
249
+ count_user.sort_by {|k,v| -v}.each do |user, count|
250
+ i+=1
251
+ if i <= 10
252
+ message << "\t#{users_id_name[user]}: #{count} (#{(count.to_f*100/total).round(2)}%)"
253
+ end
254
+ if users.size > 10 and all_data
255
+ users_attachment << "\t#{users_id_name[user]}: #{count} (#{(count.to_f*100/total).round(2)}%)"
256
+ end
257
+ end
258
+ end
259
+ commands_attachment = []
260
+
261
+ if st_command == ''
262
+ commands = rows.command.uniq.sort
263
+ count_command = {}
264
+ commands.each do |command|
265
+ count = rows.count {|h| h.command==command}
266
+ count_command[command] = count
267
+ end
268
+
269
+ if commands.size > 10
270
+ message << "*Commands* - #{commands.size} (Top 10)"
271
+ else
272
+ message << "*Commands* - #{commands.size}"
273
+ end
274
+
275
+ i = 0
276
+ count_command.sort_by {|k,v| -v}.each do |command, count|
277
+ i+=1
278
+ if i <= 10
279
+ message << "\t#{command}: #{count} (#{(count.to_f*100/total).round(2)}%)"
280
+ end
281
+ if commands.size > 10 and all_data
282
+ commands_attachment << "\t#{command}: #{count} (#{(count.to_f*100/total).round(2)}%)"
283
+ end
284
+ end
285
+ end
286
+
287
+ message << "*Message type*"
288
+ types = rows.type_message.uniq.sort
289
+ types.each do |type|
290
+ count = rows.count {|h| h.type_message==type}
291
+ message << "\t#{type}: #{count} (#{(count.to_f*100/total).round(2)}%)"
292
+ end
293
+
294
+ if on_dm_master
295
+ message << "*Last activity*: #{rows[-1].date} #{rows[-1].bot_channel} #{rows[-1].type_message} #{rows[-1].user_name} #{rows[-1].command}"
296
+ end
297
+ if users_attachment.size>0
298
+ send_file(dest, "", 'users.txt', "", 'text/plain', "text", content: users_attachment.join("\n"))
299
+ end
300
+ if commands_attachment.size>0
301
+ send_file(dest, "", 'commands.txt', "", 'text/plain', "text", content: commands_attachment.join("\n"))
302
+ end
303
+ if channels_dest_attachment.size>0
304
+ send_file(dest, "", 'channels_dest.txt', "", 'text/plain', "text", content: channels_dest_attachment.join("\n"))
305
+ end
306
+ end
307
+ end
308
+ else
309
+ message<<"Only Master admin users on a private conversation with the bot can see this kind of bot stats."
310
+ end
311
+ respond "#{message.join("\n")}", dest
312
+ end
313
+ end