slack-smart-bot 1.7.0 → 1.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -12
  3. data/lib/slack-smart-bot.rb +53 -43
  4. data/lib/slack-smart-bot_general_rules.rb +7 -4
  5. data/lib/slack-smart-bot_rules.rb +8 -6
  6. data/lib/slack/smart-bot/comm.rb +6 -1
  7. data/lib/slack/smart-bot/comm/ask.rb +12 -5
  8. data/lib/slack/smart-bot/comm/dont_understand.rb +1 -1
  9. data/lib/slack/smart-bot/comm/event_hello.rb +30 -0
  10. data/lib/slack/smart-bot/comm/get_channel_members.rb +8 -0
  11. data/lib/slack/smart-bot/comm/get_channels.rb +20 -0
  12. data/lib/slack/smart-bot/comm/get_user_info.rb +16 -0
  13. data/lib/slack/smart-bot/comm/react.rb +21 -8
  14. data/lib/slack/smart-bot/comm/respond.rb +38 -12
  15. data/lib/slack/smart-bot/comm/send_file.rb +1 -1
  16. data/lib/slack/smart-bot/comm/send_msg_channel.rb +2 -2
  17. data/lib/slack/smart-bot/comm/send_msg_user.rb +4 -4
  18. data/lib/slack/smart-bot/comm/unreact.rb +29 -0
  19. data/lib/slack/smart-bot/commands.rb +3 -1
  20. data/lib/slack/smart-bot/commands/general/bot_help.rb +16 -3
  21. data/lib/slack/smart-bot/commands/general/bot_stats.rb +313 -0
  22. data/lib/slack/smart-bot/commands/general/bot_status.rb +1 -1
  23. data/lib/slack/smart-bot/commands/general/bye_bot.rb +1 -1
  24. data/lib/slack/smart-bot/commands/general/hi_bot.rb +1 -1
  25. data/lib/slack/smart-bot/commands/general/use_rules.rb +6 -9
  26. data/lib/slack/smart-bot/commands/general/whats_new.rb +19 -0
  27. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +65 -33
  28. data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +42 -9
  29. data/lib/slack/smart-bot/commands/on_bot/admin/extend_rules.rb +3 -7
  30. data/lib/slack/smart-bot/commands/on_bot/admin/pause_bot.rb +1 -0
  31. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +9 -2
  32. data/lib/slack/smart-bot/commands/on_bot/admin/start_bot.rb +1 -0
  33. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +1 -1
  34. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +52 -21
  35. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +5 -5
  36. data/lib/slack/smart-bot/commands/on_bot/repl.rb +50 -18
  37. data/lib/slack/smart-bot/commands/on_bot/ruby_code.rb +34 -9
  38. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +2 -3
  39. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +1 -1
  40. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +27 -9
  41. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +14 -1
  42. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +3 -3
  43. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +1 -1
  44. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +41 -0
  45. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +4 -8
  46. data/lib/slack/smart-bot/listen.rb +6 -5
  47. data/lib/slack/smart-bot/process.rb +230 -186
  48. data/lib/slack/smart-bot/process_first.rb +104 -87
  49. data/lib/slack/smart-bot/treat_message.rb +128 -52
  50. data/lib/slack/smart-bot/utils.rb +2 -0
  51. data/lib/slack/smart-bot/utils/answer.rb +18 -0
  52. data/lib/slack/smart-bot/utils/answer_delete.rb +15 -0
  53. data/lib/slack/smart-bot/utils/build_help.rb +57 -5
  54. data/lib/slack/smart-bot/utils/create_routine_thread.rb +48 -8
  55. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +1 -7
  56. data/lib/slack/smart-bot/utils/get_help.rb +79 -17
  57. data/lib/slack/smart-bot/utils/save_stats.rb +21 -8
  58. data/lib/slack/smart-bot/utils/update_shortcuts_file.rb +6 -0
  59. data/whats_new.txt +24 -0
  60. metadata +23 -13
  61. data/lib/slack/smart-bot/commands/on_bot/admin_master/bot_stats.rb +0 -135
@@ -10,7 +10,7 @@ class SlackSmartBot
10
10
  if typem==:on_extended
11
11
  get_bots_created()
12
12
  end
13
- text = get_help(rules_file, dest, user.name, typem==:on_extended)
13
+ text = get_help(rules_file, dest, user.name, typem==:on_extended, true)
14
14
 
15
15
  ff = text.scan(/\s*`\s*([^`]+)\s*`\s*/i).flatten
16
16
  ff.delete("!THE_COMMAND")
@@ -0,0 +1,30 @@
1
+ class SlackSmartBot
2
+ def event_hello()
3
+ unless config.simulate
4
+ m = "Successfully connected, welcome '#{client.self.name}' to the '#{client.team.name}' team at https://#{client.team.domain}.slack.com."
5
+ puts m
6
+ @logger.info m
7
+ config.nick = client.self.name
8
+ config.nick_id = client.self.id
9
+ end
10
+ @salutations = [config[:nick], "<@#{config[:nick_id]}>", "bot", "smart"]
11
+
12
+ gems_remote = `gem list slack-smart-bot --remote`
13
+ version_remote = gems_remote.to_s().scan(/slack-smart-bot \((\d+\.\d+\.\d+)/).join
14
+ version_message = ""
15
+ if Gem::Version.new(version_remote) > Gem::Version.new(VERSION)
16
+ version_message = ". There is a new available version: #{version_remote}."
17
+ end
18
+ if (!config[:silent] or ENV['BOT_SILENT'].to_s == 'false') and !config.simulate
19
+ ENV['BOT_SILENT'] = 'true' if config[:silent] and ENV['BOT_SILENT'].to_s != 'true'
20
+ respond "Smart Bot started v#{VERSION}#{version_message}\nIf you want to know what I can do for you: `bot help`.\n`bot rules` if you want to display just the specific rules of this channel.\nYou can talk to me privately if you prefer it."
21
+ end
22
+ @routines.each do |ch, rout|
23
+ rout.each do |k, v|
24
+ if !v[:running] and v[:channel_name] == config.channel
25
+ create_routine_thread(k)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ def get_channel_members(channel_id)
2
+ if config.simulate and config.key?(:client)
3
+ client.web_client.conversations_members[channel_id.to_sym].members
4
+ else
5
+ client.web_client.conversations_members(channel: channel_id).members
6
+ end
7
+
8
+ end
@@ -0,0 +1,20 @@
1
+ def get_channels(bot_is_in: false)
2
+ if config.simulate and config.key?(:client)
3
+ if bot_is_in
4
+ client.web_client.conversations_members.reject {|r,v| !v.members.include?(config.nick_id)}.values
5
+ else
6
+ client.web_client.conversations_members.values
7
+ end
8
+ else
9
+ if bot_is_in
10
+ client.web_client.users_conversations(exclude_archived: true, limit: 100, types: "im, public_channel,private_channel").channels
11
+ else
12
+ #todo: add pagination for case more than 1000 channels on the workspace
13
+ client.web_client.conversations_list(
14
+ types: "private_channel,public_channel",
15
+ limit: "1000",
16
+ exclude_archived: "true",
17
+ ).channels
18
+ end
19
+ end
20
+ end
@@ -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
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,46 +4,71 @@ 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
+ else
11
+ wait = 0
12
+ end
13
+ msgs = msg.chars.each_slice(4000).map(&:join) # max of 4000 characters per message
7
14
  if dest.nil?
8
15
  if config[:simulate]
9
16
  open("#{config.path}/buffer_complete.log", "a") { |f|
10
- f.puts "|#{@channel_id}|#{config[:nick_id]}|#{msg}~~~"
17
+ f.puts "|#{@channel_id}|#{config[:nick_id]}|#{config[:nick]}|#{msg}~~~"
11
18
  }
12
19
  else
13
20
  if Thread.current[:on_thread]
14
- client.message(channel: @channel_id, text: msg, as_user: true, thread_ts: Thread.current[:thread_ts])
21
+ msgs.each do |msg|
22
+ client.message(channel: @channel_id, text: msg, as_user: true, thread_ts: Thread.current[:thread_ts])
23
+ sleep wait
24
+ end
15
25
  else
16
- client.message(channel: @channel_id, text: msg, as_user: true)
26
+ msgs.each do |msg|
27
+ client.message(channel: @channel_id, text: msg, as_user: true)
28
+ sleep wait
29
+ end
17
30
  end
18
31
  end
19
32
  if config[:testing] and config.on_master_bot
20
33
  open("#{config.path}/buffer.log", "a") { |f|
21
- f.puts "|#{@channel_id}|#{config[:nick_id]}|#{msg}"
34
+ f.puts "|#{@channel_id}|#{config[:nick_id]}|#{config[:nick]}|#{msg}"
22
35
  }
23
36
  end
24
37
  elsif dest[0] == "C" or dest[0] == "G" # channel
25
38
  if config[:simulate]
26
39
  open("#{config.path}/buffer_complete.log", "a") { |f|
27
- f.puts "|#{dest}|#{config[:nick_id]}|#{msg}~~~"
40
+ f.puts "|#{dest}|#{config[:nick_id]}|#{config[:nick]}|#{msg}~~~"
28
41
  }
29
42
  else
30
43
  if Thread.current[:on_thread]
31
- client.message(channel: dest, text: msg, as_user: true, thread_ts: Thread.current[:thread_ts])
44
+ msgs.each do |msg|
45
+ client.message(channel: dest, text: msg, as_user: true, thread_ts: Thread.current[:thread_ts])
46
+ sleep wait
47
+ end
32
48
  else
33
- client.message(channel: dest, text: msg, as_user: true)
49
+ msgs.each do |msg|
50
+ client.message(channel: dest, text: msg, as_user: true)
51
+ sleep wait
52
+ end
34
53
  end
35
54
  end
36
55
  if config[:testing] and config.on_master_bot
37
56
  open("#{config.path}/buffer.log", "a") { |f|
38
- f.puts "|#{dest}|#{config[:nick_id]}|#{msg}"
57
+ f.puts "|#{dest}|#{config[:nick_id]}|#{config[:nick]}|#{msg}"
39
58
  }
40
59
  end
41
- elsif dest[0] == "D" or dest[0] == "U" # Direct message
42
- send_msg_user(dest, msg)
60
+ elsif dest[0] == "D" or dest[0] == "U" or dest[0] == "W" # Direct message
61
+ msgs.each do |msg|
62
+ send_msg_user(dest, msg)
63
+ sleep wait
64
+ end
43
65
  elsif dest[0] == "@"
44
66
  begin
45
- user_info = client.web_client.users_info(user: dest)
46
- send_msg_user(user_info.user.id, msg)
67
+ user_info = get_user_info(dest)
68
+ msgs.each do |msg|
69
+ send_msg_user(user_info.user.id, msg)
70
+ sleep wait
71
+ end
47
72
  rescue Exception => stack
48
73
  @logger.warn("user #{dest} not found.")
49
74
  @logger.warn stack
@@ -54,6 +79,7 @@ class SlackSmartBot
54
79
  else
55
80
  @logger.warn("method respond not treated correctly: msg:#{msg} dest:#{dest}")
56
81
  end
82
+ @last_respond = Time.now
57
83
  end
58
84
 
59
85
  end
@@ -8,7 +8,7 @@ class SlackSmartBot
8
8
  def send_file(to, msg, file, title, format, type = "text", content: '')
9
9
  unless config[:simulate]
10
10
  file = 'myfile' if file.to_s == '' and content!=''
11
- if to[0] == "U" #user
11
+ if to[0] == "U" or to[0] == "W" #user
12
12
  im = client.web_client.im_open(user: to)
13
13
  channel = im["channel"]["id"]
14
14
  else
@@ -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
@@ -0,0 +1,29 @@
1
+ class SlackSmartBot
2
+ # list of available emojis: https://www.webfx.com/tools/emoji-cheat-sheet/
3
+ # unreact(:thumbsup)
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
9
+ else
10
+ parent = false
11
+ end
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
27
+ end
28
+ end
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