slack-smart-bot 1.14.2 → 1.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +222 -37
  3. data/img/chat_gpt.png +0 -0
  4. data/img/chat_gpt_session.png +0 -0
  5. data/img/chat_gpt_share.png +0 -0
  6. data/img/command_add_sc.png +0 -0
  7. data/img/command_bot_help_echo.png +0 -0
  8. data/img/command_loop.png +0 -0
  9. data/img/command_my_timeoff.png +0 -0
  10. data/img/command_recap.png +0 -0
  11. data/img/command_repl1.png +0 -0
  12. data/img/command_repl2.png +0 -0
  13. data/img/command_ruby.png +0 -0
  14. data/img/command_run_repl.png +0 -0
  15. data/img/command_see_announcements.png +0 -0
  16. data/img/command_see_statuses.png +0 -0
  17. data/img/command_see_team.png +0 -0
  18. data/img/command_summarize.png +0 -0
  19. data/img/commands_inline.png +0 -0
  20. data/img/commands_on_demand.png +0 -0
  21. data/img/commands_on_external_call.png +0 -0
  22. data/img/image_editing.png +0 -0
  23. data/img/image_generation.png +0 -0
  24. data/img/image_variations.png +0 -0
  25. data/img/openai-300.png +0 -0
  26. data/img/openai.png +0 -0
  27. data/img/slack-300.png +0 -0
  28. data/img/slack.png +0 -0
  29. data/img/smart-bot-150.png +0 -0
  30. data/img/smart-bot-profile-pic-2.png +0 -0
  31. data/img/smart-bot-profile-pic.png +0 -0
  32. data/img/smart-bot.png +0 -0
  33. data/img/whisper.png +0 -0
  34. data/lib/slack/smart-bot/ai/open_ai/connect.rb +165 -43
  35. data/lib/slack/smart-bot/ai/open_ai/models.rb +61 -9
  36. data/lib/slack/smart-bot/ai/open_ai/send_gpt_chat.rb +67 -11
  37. data/lib/slack/smart-bot/ai/open_ai/send_image_edit.rb +4 -3
  38. data/lib/slack/smart-bot/ai/open_ai/send_image_generation.rb +4 -4
  39. data/lib/slack/smart-bot/ai/open_ai/send_image_variation.rb +4 -3
  40. data/lib/slack/smart-bot/ai/open_ai/whisper_transcribe.rb +4 -3
  41. data/lib/slack/smart-bot/comm/ask.rb +20 -8
  42. data/lib/slack/smart-bot/comm/dont_understand.rb +2 -2
  43. data/lib/slack/smart-bot/comm/event_hello.rb +30 -1
  44. data/lib/slack/smart-bot/comm/get_channel_members.rb +2 -1
  45. data/lib/slack/smart-bot/comm/get_presence.rb +1 -0
  46. data/lib/slack/smart-bot/comm/get_smartbot_team_info.rb +10 -0
  47. data/lib/slack/smart-bot/comm/get_user_info.rb +45 -6
  48. data/lib/slack/smart-bot/comm/get_users.rb +8 -1
  49. data/lib/slack/smart-bot/comm/respond.rb +225 -196
  50. data/lib/slack/smart-bot/comm/send_msg_channel.rb +2 -2
  51. data/lib/slack/smart-bot/comm/send_msg_user.rb +10 -9
  52. data/lib/slack/smart-bot/comm/unreact.rb +2 -2
  53. data/lib/slack/smart-bot/comm.rb +1 -0
  54. data/lib/slack/smart-bot/commands/general/add_admin.rb +16 -6
  55. data/lib/slack/smart-bot/commands/general/add_announcement.rb +3 -3
  56. data/lib/slack/smart-bot/commands/general/add_vacation.rb +28 -12
  57. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat.rb +272 -23
  58. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_add_collaborator.rb +42 -0
  59. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_copy_session.rb +89 -0
  60. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_delete_session.rb +45 -0
  61. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_get_prompts.rb +41 -0
  62. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_list_sessions.rb +81 -0
  63. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_share_session.rb +52 -0
  64. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_use_model.rb +52 -0
  65. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_edit_image.rb +14 -11
  66. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_generate_image.rb +15 -11
  67. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_models.rb +29 -17
  68. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_variations_image.rb +16 -13
  69. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_whisper.rb +13 -7
  70. data/lib/slack/smart-bot/commands/general/allow_access.rb +8 -4
  71. data/lib/slack/smart-bot/commands/general/bot_help.rb +24 -10
  72. data/lib/slack/smart-bot/commands/general/bye_bot.rb +9 -5
  73. data/lib/slack/smart-bot/commands/general/delete_announcement.rb +2 -1
  74. data/lib/slack/smart-bot/commands/general/delete_share.rb +2 -1
  75. data/lib/slack/smart-bot/commands/general/deny_access.rb +1 -1
  76. data/lib/slack/smart-bot/commands/general/get_smartbot_readme.rb +15 -0
  77. data/lib/slack/smart-bot/commands/general/hi_bot.rb +10 -4
  78. data/lib/slack/smart-bot/commands/general/personal_settings.rb +26 -8
  79. data/lib/slack/smart-bot/commands/general/poster.rb +26 -2
  80. data/lib/slack/smart-bot/commands/general/public_holidays.rb +14 -24
  81. data/lib/slack/smart-bot/commands/general/recap.rb +399 -0
  82. data/lib/slack/smart-bot/commands/general/remove_admin.rb +19 -9
  83. data/lib/slack/smart-bot/commands/general/remove_vacation.rb +23 -6
  84. data/lib/slack/smart-bot/commands/general/see_access.rb +2 -1
  85. data/lib/slack/smart-bot/commands/general/see_admins.rb +8 -4
  86. data/lib/slack/smart-bot/commands/general/see_announcements.rb +5 -5
  87. data/lib/slack/smart-bot/commands/general/see_favorite_commands.rb +4 -4
  88. data/lib/slack/smart-bot/commands/general/see_shares.rb +1 -1
  89. data/lib/slack/smart-bot/commands/general/see_vacations.rb +34 -17
  90. data/lib/slack/smart-bot/commands/general/set_public_holidays.rb +4 -2
  91. data/lib/slack/smart-bot/commands/general/share_messages.rb +3 -3
  92. data/lib/slack/smart-bot/commands/general/summarize.rb +191 -0
  93. data/lib/slack/smart-bot/commands/general/teams/add_team.rb +4 -8
  94. data/lib/slack/smart-bot/commands/general/teams/delete_team.rb +3 -3
  95. data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team.rb +34 -29
  96. data/lib/slack/smart-bot/commands/general/teams/memos/add_memo_team_comment.rb +1 -1
  97. data/lib/slack/smart-bot/commands/general/teams/memos/delete_memo_team.rb +6 -4
  98. data/lib/slack/smart-bot/commands/general/teams/memos/see_memo_team.rb +26 -15
  99. data/lib/slack/smart-bot/commands/general/teams/memos/see_memos_team.rb +33 -24
  100. data/lib/slack/smart-bot/commands/general/teams/memos/set_memo_status.rb +4 -4
  101. data/lib/slack/smart-bot/commands/general/teams/ping_team.rb +10 -8
  102. data/lib/slack/smart-bot/commands/general/teams/see_teams.rb +73 -61
  103. data/lib/slack/smart-bot/commands/general/teams/see_vacations_team.rb +28 -13
  104. data/lib/slack/smart-bot/commands/general/teams/update_team.rb +9 -9
  105. data/lib/slack/smart-bot/commands/general_bot_commands.rb +1152 -839
  106. data/lib/slack/smart-bot/commands/on_bot/add_shortcut.rb +18 -17
  107. data/lib/slack/smart-bot/commands/on_bot/admin/add_routine.rb +11 -9
  108. data/lib/slack/smart-bot/commands/on_bot/admin/remove_routine.rb +2 -0
  109. data/lib/slack/smart-bot/commands/on_bot/admin/run_routine.rb +1 -0
  110. data/lib/slack/smart-bot/commands/on_bot/admin/see_routines.rb +5 -3
  111. data/lib/slack/smart-bot/commands/on_bot/admin_master/delete_message.rb +2 -3
  112. data/lib/slack/smart-bot/commands/on_bot/admin_master/get_bot_logs.rb +2 -3
  113. data/lib/slack/smart-bot/commands/on_bot/admin_master/react_to.rb +2 -3
  114. data/lib/slack/smart-bot/commands/on_bot/admin_master/send_message.rb +7 -6
  115. data/lib/slack/smart-bot/commands/on_bot/admin_master/update_message.rb +2 -3
  116. data/lib/slack/smart-bot/commands/on_bot/delete_repl.rb +1 -1
  117. data/lib/slack/smart-bot/commands/on_bot/delete_shortcut.rb +21 -20
  118. data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +40 -7
  119. data/lib/slack/smart-bot/commands/on_bot/general/bot_status.rb +6 -2
  120. data/lib/slack/smart-bot/commands/on_bot/general/stop_using_rules.rb +7 -6
  121. data/lib/slack/smart-bot/commands/on_bot/general/suggest_command.rb +5 -4
  122. data/lib/slack/smart-bot/commands/on_bot/general/use_rules.rb +4 -3
  123. data/lib/slack/smart-bot/commands/on_bot/get_repl.rb +4 -4
  124. data/lib/slack/smart-bot/commands/on_bot/kill_repl.rb +1 -1
  125. data/lib/slack/smart-bot/commands/on_bot/repl.rb +109 -53
  126. data/lib/slack/smart-bot/commands/on_bot/repl_client.rb +35 -29
  127. data/lib/slack/smart-bot/commands/on_bot/run_repl.rb +5 -5
  128. data/lib/slack/smart-bot/commands/on_bot/see_repls.rb +1 -2
  129. data/lib/slack/smart-bot/commands/on_bot/see_shortcuts.rb +5 -4
  130. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +22 -12
  131. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +12 -7
  132. data/lib/slack/smart-bot/commands/on_master/admin_master/notify_message.rb +2 -2
  133. data/lib/slack/smart-bot/commands/on_master/admin_master/publish_announcements.rb +1 -1
  134. data/lib/slack/smart-bot/commands/on_master/admin_master/set_general_message.rb +5 -5
  135. data/lib/slack/smart-bot/commands/on_master/admin_master/set_maintenance.rb +5 -5
  136. data/lib/slack/smart-bot/commands/on_master/create_bot.rb +3 -3
  137. data/lib/slack/smart-bot/commands.rb +10 -0
  138. data/lib/slack/smart-bot/config.rb +126 -0
  139. data/lib/slack/smart-bot/listen.rb +12 -11
  140. data/lib/slack/smart-bot/process.rb +62 -55
  141. data/lib/slack/smart-bot/process_first.rb +106 -65
  142. data/lib/slack/smart-bot/treat_message.rb +79 -47
  143. data/lib/slack/smart-bot/utils/answer.rb +11 -3
  144. data/lib/slack/smart-bot/utils/answer_delete.rb +11 -3
  145. data/lib/slack/smart-bot/utils/check_vacations.rb +21 -3
  146. data/lib/slack/smart-bot/utils/create_routine_thread.rb +13 -13
  147. data/lib/slack/smart-bot/utils/display_calendar.rb +42 -8
  148. data/lib/slack/smart-bot/utils/encryption/decrypt.rb +16 -9
  149. data/lib/slack/smart-bot/utils/encryption/encrypt.rb +14 -11
  150. data/lib/slack/smart-bot/utils/find_user.rb +71 -0
  151. data/lib/slack/smart-bot/utils/get_access_channels.rb +22 -3
  152. data/lib/slack/smart-bot/utils/get_channels_name_and_id.rb +3 -4
  153. data/lib/slack/smart-bot/utils/get_command_ids.rb +5 -5
  154. data/lib/slack/smart-bot/utils/get_countries_candelarific.rb +18 -0
  155. data/lib/slack/smart-bot/utils/get_help.rb +21 -19
  156. data/lib/slack/smart-bot/utils/get_openai_sessions.rb +47 -0
  157. data/lib/slack/smart-bot/utils/get_personal_settings.rb +29 -3
  158. data/lib/slack/smart-bot/utils/get_rules_imported.rb +27 -6
  159. data/lib/slack/smart-bot/utils/get_shares.rb +1 -1
  160. data/lib/slack/smart-bot/utils/get_team_members.rb +4 -4
  161. data/lib/slack/smart-bot/utils/get_vacations.rb +15 -7
  162. data/lib/slack/smart-bot/utils/has_access.rb +10 -4
  163. data/lib/slack/smart-bot/utils/is_admin.rb +25 -17
  164. data/lib/slack/smart-bot/utils/local_time.rb +29 -0
  165. data/lib/slack/smart-bot/utils/save_stats.rb +5 -3
  166. data/lib/slack/smart-bot/utils/update_access_channels.rb +19 -3
  167. data/lib/slack/smart-bot/utils/update_openai_sessions.rb +42 -0
  168. data/lib/slack/smart-bot/utils/update_personal_settings.rb +11 -3
  169. data/lib/slack/smart-bot/utils/update_rules_imported.rb +18 -3
  170. data/lib/slack/smart-bot/utils/update_vacations.rb +5 -2
  171. data/lib/slack/smart-bot/utils/upgrade_to_use_team_ids.rb +276 -0
  172. data/lib/slack/smart-bot/utils.rb +6 -1
  173. data/lib/slack-smart-bot.rb +181 -76
  174. data/lib/slack-smart-bot_general_commands.rb +10 -9
  175. data/whats_new.txt +30 -13
  176. metadata +128 -20
@@ -3,20 +3,37 @@ class SlackSmartBot
3
3
  save_stats(__method__) if add_stats
4
4
 
5
5
  get_vacations()
6
-
6
+ team_id_user = "#{user.team_id}_#{user.name}"
7
+
7
8
  from_user_name = ''
8
- year = Date.today.year if year.to_s == ''
9
9
 
10
10
  if from_user.empty?
11
- from_user_name = user.name
11
+ from_user_name = team_id_user
12
12
  else
13
- @users = get_users() if @users.empty?
14
- user_info = @users.select{|u| u.id == from_user or (u.key?(:enterprise_user) and u.enterprise_user.id == from_user)}[-1]
15
- from_user_name = user_info.name
13
+ user_info = find_user(from_user)
14
+ from_user_name = "#{user_info.team_id}_#{user_info.name}"
16
15
  end
17
- from_user = '' if from_user_name == user.name
16
+
17
+ if @vacations.key?(from_user_name) and @vacations[from_user_name][:public_holidays].to_s != ""
18
+ country_region = @vacations[from_user_name][:public_holidays].downcase
19
+ elsif config[:public_holidays].key?(:default_calendar)
20
+ country_region = config[:public_holidays][:default_calendar].downcase
21
+ else
22
+ country_region = ''
23
+ end
24
+
25
+ local_day_time = local_time(country_region)
26
+ if local_day_time.nil?
27
+ today = Date.today
28
+ else
29
+ today = local_day_time.to_date
30
+ end
31
+ year = today.year.to_s if year.to_s == ''
32
+
33
+ from_user = '' if from_user_name == team_id_user
18
34
  if !@vacations.key?(from_user_name) or !@vacations[from_user_name].key?(:periods) or @vacations[from_user_name].periods.empty?
19
35
  if from_user.empty?
36
+ display_calendar(from_user_name, year) if dest[0] == 'D'
20
37
  respond "You didn't add any time off yet. Use `add vacation from YYYY/MM/DD to YYYY/MM/DD`"
21
38
  else
22
39
  respond "No time off added yet for <@#{from_user}>"
@@ -24,27 +41,27 @@ class SlackSmartBot
24
41
  else
25
42
  messages = []
26
43
  messages << "*Time off <@#{from_user}> #{year}*" if !from_user.empty?
27
-
28
- display_calendar(from_user_name, year) if from_user_name == user.name and dest[0] == 'D'
29
44
 
30
- today = Date.today.strftime("%Y/%m/%d")
45
+ display_calendar(from_user_name, year) if from_user_name == team_id_user and dest[0] == 'D'
46
+
47
+ today_txt = today.strftime("%Y/%m/%d")
31
48
  current_added = false
32
49
  past_added = false
33
50
  @vacations[from_user_name].periods.sort_by { |v| v[:from]}.reverse.each do |vac|
34
- if !current_added and vac.to >= today
35
- messages << "*Current and future periods*"
51
+ if !current_added and vac.to >= today_txt
52
+ messages << "*Current and future periods*"
36
53
  current_added = true
37
54
  end
38
- if !past_added and vac.to < today and from_user.empty? and vac.to[0..3] == year
55
+ if !past_added and vac.to < today_txt and from_user.empty? and vac.to[0..3] == year
39
56
  if dest[0]=='D'
40
- messages << "\n*Past periods #{year}*"
57
+ messages << "\n*Past periods #{year}*"
41
58
  past_added = true
42
59
  else
43
60
  messages << "To see past periods call me from a DM"
44
61
  break
45
62
  end
46
63
  end
47
- unless !from_user.empty? and vac.to < today
64
+ unless !from_user.empty? and vac.to < today_txt
48
65
  if vac.to[0..3] == year
49
66
  if !from_user.empty?
50
67
  icon = ":beach_with_umbrella:"
@@ -63,13 +80,13 @@ class SlackSmartBot
63
80
  end
64
81
  end
65
82
  end
66
- if !past_added and !current_added and dest[0]=='D'
83
+ if !past_added and !current_added and dest[0]=='D'
67
84
  if from_user.empty?
68
85
  messages << "No time off added yet for #{year}"
69
86
  else
70
87
  messages << "Not possible to see past periods for another user"
71
88
  end
72
- elsif !past_added and dest[0]=='D' and !from_user.empty? and from_user_name != user.name
89
+ elsif !past_added and dest[0]=='D' and !from_user.empty? and from_user_name != team_id_user
73
90
  messages << "Not possible to see past periods for another user"
74
91
  end
75
92
  respond messages.join("\n")
@@ -4,6 +4,7 @@ class SlackSmartBot
4
4
 
5
5
  result = public_holidays(country, state, Date.today.year.to_s, '', '', add_stats: false, publish_results: false)
6
6
  if result == true
7
+ team_id_user = "#{user.team_id}_#{user.name}"
7
8
  if state == ""
8
9
  country_region = country
9
10
  else
@@ -15,9 +16,10 @@ class SlackSmartBot
15
16
  respond "Public holidays for *#{country_region}* set."
16
17
  end
17
18
  get_vacations()
18
- @vacations[user.name] ||= {}
19
- @vacations[user.name][:public_holidays] = country_region
19
+ @vacations[team_id_user] ||= {}
20
+ @vacations[team_id_user][:public_holidays] = country_region
20
21
  update_vacations()
22
+ check_vacations(date: nil, team_id: user.team_id, user: user.name, set_status: true, only_first_day: false)
21
23
  else
22
24
  respond "Be sure the country and state are correct. If not displayed available states, try with the country only."
23
25
  end
@@ -25,7 +25,7 @@ class SlackSmartBot
25
25
  type = :reaction
26
26
  end
27
27
  if File.exist?("#{config.path}/shares/#{from_channel}.csv")
28
- t = CSV.table("#{config.path}/shares/#{from_channel}.csv", headers: ['share_id', 'user_deleted', 'user_created', 'date', 'time', 'type', 'to_channel', 'condition'])
28
+ t = CSV.table("#{config.path}/shares/#{from_channel}.csv", headers: ['share_id', 'user_team_id_deleted', 'user_deleted', 'user_team_id_created', 'user_created', 'date', 'time', 'type', 'to_channel', 'condition'])
29
29
  @shares[from_channel] = t
30
30
  if t.size>0
31
31
  num = t[:share_id].max + 1
@@ -34,13 +34,13 @@ class SlackSmartBot
34
34
  end
35
35
  elsif !@shares.key?(from_channel)
36
36
  File.open("#{config.path}/shares/#{from_channel}.csv","w")
37
- t = CSV.table("#{config.path}/shares/#{from_channel}.csv", headers: ['share_id', 'user_deleted', 'user_created', 'date', 'time', 'type', 'to_channel', 'condition'])
37
+ t = CSV.table("#{config.path}/shares/#{from_channel}.csv", headers: ['share_id', 'user_team_id_deleted', 'user_deleted', 'user_team_id_created', 'user_created', 'date', 'time', 'type', 'to_channel', 'condition'])
38
38
  num = 1
39
39
  @shares[from_channel] = t
40
40
  else
41
41
  num = @shares[from_channel][:share_id].max + 1
42
42
  end
43
- values = [num, '', user.name, Time.now.strftime("%Y/%m/%d"), Time.now.strftime("%H:%M"), type.to_s, to_channel, condition]
43
+ values = [num, '', '', user.team_id, user.name, Time.now.strftime("%Y/%m/%d"), Time.now.strftime("%H:%M"), type.to_s, to_channel, condition]
44
44
  @shares[from_channel] << values
45
45
  CSV.open("#{config.path}/shares/#{from_channel}.csv", "a+") do |csv|
46
46
  csv << values
@@ -0,0 +1,191 @@
1
+ class SlackSmartBot
2
+
3
+ #todo: add tests
4
+ def summarize(user, dest, channel, from, thread_ts)
5
+ save_stats(__method__)
6
+
7
+ ai_conn, message = SlackSmartBot::AI::OpenAI.connect({}, config, {}, service: :chat_gpt)
8
+ if message.empty?
9
+ ai_models_conn, message = SlackSmartBot::AI::OpenAI.connect({}, config, {}, service: :models)
10
+ end
11
+ if !message.empty? #error connecting
12
+ respond message
13
+ else
14
+ channels_bot_is_in = get_channels(bot_is_in: true)
15
+ if dest[0] == "D" and channel == dest
16
+ respond "Sorry, I can't summarize a direct message. Please use this command in a channel or supply the channel you want to summarize."
17
+ elsif !channels_bot_is_in.id.include?(channel) or !get_channel_members(channel).include?(config.nick_id_granular)
18
+ respond "Sorry, I can't summarize a channel where <@#{config.nick_id_granular}> and <@#{config.nick_id}> are not members. Please invite them to the channel."
19
+ elsif !get_channel_members(channel).include?(user.id)
20
+ respond "Sorry, I can't summarize a channel where you are not a member."
21
+ else
22
+ if (from == "" and channel == dest and Thread.current[:on_thread]) or thread_ts != ""
23
+ summarize_thread = true
24
+ if thread_ts == ""
25
+ thread_ts = Thread.current[:thread_ts]
26
+ else
27
+ if thread_ts.include?(".")
28
+ thread_ts = thread_ts
29
+ else
30
+ thread_ts = thread_ts.scan(/(\d+)/).join
31
+ thread_ts = "#{thread_ts[0..9]}.#{thread_ts[10..-1]}"
32
+ end
33
+ end
34
+ else
35
+ summarize_thread = false
36
+ end
37
+ from_time_off = false
38
+ if from == ""
39
+ from = (Time.now - (60 * 60) * 24 * 30).to_s
40
+ get_vacations()
41
+ if @vacations.key?(user.team_id_user) and @vacations[user.team_id_user].key?(:periods)
42
+ @vacations[user.team_id_user].periods.each do |p|
43
+ #get the last from date
44
+ if p.from > from[0..9].gsub("-", "/")
45
+ from = p.from
46
+ from_time_off = true
47
+ end
48
+ end
49
+ #from will be the day before the last time off
50
+ if from_time_off
51
+ from = (Time.strptime(from, "%Y/%m/%d") - (60 * 60) * 24).to_s
52
+ end
53
+ end
54
+ end
55
+
56
+ from.gsub!("-", "/")
57
+ if from.length == 10
58
+ from = from + " 00:00:00"
59
+ elsif from.length == 16
60
+ from = from + ":00"
61
+ end
62
+ from = Time.strptime(from, "%Y/%m/%d %H:%M:%S")
63
+ if summarize_thread
64
+ last_msg = respond("I'm going to summarize the thread messages", return_message: true)
65
+ elsif from_time_off
66
+ last_msg = respond("I'm going to summarize the messages since the day before your last time off #{from.strftime("%Y/%m/%d")} in <##{channel}>. This may take a while.", return_message: true)
67
+ else
68
+ last_msg = respond("I'm going to summarize the messages since #{from.strftime("%Y/%m/%d %H:%M:%S")} in <##{channel}>. This may take a while.", return_message: true)
69
+ end
70
+
71
+ @history_still_running ||= false
72
+ if @history_still_running
73
+ respond "Due to Slack API rate limit, `summarize` command is limited. Waiting for other `summarize` command to finish."
74
+ num_times = 0
75
+ while @history_still_running and num_times < 30
76
+ num_times += 1
77
+ sleep 1
78
+ end
79
+ if @history_still_running
80
+ respond "Sorry, Another `summarize` command is still running after 30 seconds. Please try again later."
81
+ end
82
+ end
83
+ unless @history_still_running
84
+ @history_still_running = true
85
+ react :running
86
+ if summarize_thread
87
+ hist = client_granular.conversations_history(channel: channel, oldest: thread_ts, inclusive: true, limit: 1)
88
+ else
89
+ hist = client_granular.conversations_history(channel: channel)
90
+ end
91
+ messages = {} # store the messages by year/month
92
+ act_users = {}
93
+ act_threads = {}
94
+ hist.messages.each do |message|
95
+ if Time.at(message.ts.to_f) >= from or summarize_thread
96
+ year_month = Time.at(message.ts.to_f).strftime("%Y/%m")
97
+ messages[year_month] ||= []
98
+ if message.key?("thread_ts")
99
+ thread_ts_message = message.thread_ts
100
+ replies = client_granular.conversations_replies(channel: channel, ts: thread_ts_message, latest: last_msg.ts)
101
+ sleep 0.5 #to avoid rate limit Tier 3 (50 requests per minute)
102
+ messages_replies = ["Thread Started about last message:"]
103
+ act_threads[message.ts] = replies.messages.size
104
+ replies.messages.each_with_index do |msgrepl, i|
105
+ act_users[msgrepl.user] ||= 0
106
+ act_users[msgrepl.user] += 1
107
+ messages_replies << "<@#{msgrepl.user}> (#{Time.at(msgrepl.ts.to_f)}) wrote:> #{msgrepl.text}" if i > 0
108
+ end
109
+ messages_replies << "Thread ended."
110
+ messages[year_month] += messages_replies.reverse # the order on repls is from older to newer
111
+ end
112
+ act_users[message.user] ||= 0
113
+ act_users[message.user] += 1
114
+ url_to_message = "https://#{client.team.domain}.slack.com/archives/#{channel}/#{message.ts}"
115
+ messages[year_month] << "<@#{message.user}> (#{Time.at(message.ts.to_f)}) (link to the message: #{url_to_message}) wrote:> #{message.text}"
116
+ end
117
+ end
118
+ messages.each do |year_month, msgs|
119
+ messages[year_month] = msgs.reverse # the order on history is from newer to older
120
+ end
121
+ @history_still_running = false
122
+ unreact :running
123
+ if messages.empty?
124
+ respond "There are no Slack Messages since #{from}"
125
+ else
126
+ react :speech_balloon
127
+ chatgpt = ai_conn[user.team_id_user].chat_gpt
128
+ models = ai_models_conn[user.team_id_user].models
129
+
130
+ prompt_orig = "Could you please provide a summary of the given conversation, including all key points and supporting details? The summary should be comprehensive and accurately reflect the main message and arguments presented in the original text, while also being concise and easy to understand. To ensure accuracy, please read the text carefully and pay attention to any nuances or complexities in the language. Please also add the most important conversations in the summary. Additionally, the summary should avoid any personal biases or interpretations and remain objective and factual throughout.\n"
131
+ prompt_orig += "If you name an user remember to name it as <@user_id> so it is not replaced by the user name.\n"
132
+ prompt_orig += "Add the link to the message so it is easy to find it. The links added need to follow this <LINK|message>\n"
133
+ prompt_orig += "For example <https://#{client.team.domain}.slack.com/archives/C111JG4V4DZ/1610231016.950299|message>\n"
134
+ prompt_orig += "Add also the date of the message for relevant conversations.\n"
135
+ prompt_orig += "This is the conversation:\n"
136
+ #sort by year/month from older to newer
137
+ messages = messages.sort_by { |k, v| k }.to_h
138
+
139
+ @open_ai_model_info ||= {}
140
+ @open_ai_model_info[chatgpt.smartbot_model] ||= SlackSmartBot::AI::OpenAI.models(models.client, models, chatgpt.smartbot_model, return_response: true)
141
+ if @open_ai_model_info[chatgpt.smartbot_model].key?(:max_input_tokens)
142
+ max_num_tokens = @open_ai_model_info[chatgpt.smartbot_model][:max_input_tokens].to_i
143
+ elsif @open_ai_model_info[chatgpt.smartbot_model].key?(:max_tokens)
144
+ max_num_tokens = @open_ai_model_info[chatgpt.smartbot_model][:max_tokens].to_i
145
+ else
146
+ max_num_tokens = 8000
147
+ end
148
+ num_tokens = OpenAI.rough_token_count(prompt_orig + messages.values.flatten.join)
149
+ respond ":information_source: ChatGPT model: *#{chatgpt.smartbot_model}*. Max tokens: *#{max_num_tokens}*. Characters: #{messages.values.flatten.join.size}. Messages: #{messages.values.flatten.size}. Threads: #{act_threads.size}. Users: #{act_users.size}. Chatgpt tokens: *#{num_tokens}*"
150
+
151
+ prompts = []
152
+ i = 0
153
+ messages.each do |year_month, msgs|
154
+ msgs.each do |msg|
155
+ num_tokens = OpenAI.rough_token_count(prompts[i].to_s + msg)
156
+ i += 1 if num_tokens > max_num_tokens
157
+ prompts[i] ||= prompt_orig
158
+ prompts[i] += "#{msg}\n"
159
+ end
160
+ end
161
+ prompts.each_with_index do |prompt, i|
162
+ num_tokens = OpenAI.rough_token_count(prompt)
163
+ respond ":information_source: The total number of chatgpt tokens is more than the max allowed for this chatgpt model. *Part #{i + 1} of #{prompts.size}*.\n" if prompts.size > 1
164
+ success, res = SlackSmartBot::AI::OpenAI.send_gpt_chat(chatgpt.client, chatgpt.smartbot_model, prompt, chatgpt)
165
+ result_messages = []
166
+ if success
167
+ result_messages << "*ChatGPT:*\n#{res}"
168
+ else
169
+ result_messages << "*ChatGPT:*\nI'm sorry, I couldn't summarize the conversation. This is the issue: #{res}"
170
+ end
171
+ if i == prompts.size - 1
172
+ act_users.delete(config.nick_id_granular)
173
+ act_users.delete(config.nick_id)
174
+
175
+ act_users = act_users.sort_by { |k, v| v }.reverse
176
+
177
+ result_messages << "\n\t:runner: Most active users: #{act_users[0..2].map { |k, v| "<@#{k}> (#{v})" }.join(", ")}"
178
+ if act_threads.size > 0 and !summarize_thread
179
+ act_threads = act_threads.sort_by { |k, v| v }.reverse
180
+ result_messages << "\t:fire: Most active threads: #{act_threads[0..2].map { |k, v| "<https://#{client.team.domain}.slack.com/archives/#{channel}/#{k}|#{v - 1} replies>" }.join(", ")}"
181
+ end
182
+ end
183
+ respond result_messages.join("\n").gsub("**", "*")
184
+ end
185
+ unreact :speech_balloon
186
+ end
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -28,12 +28,8 @@ class SlackSmartBot
28
28
  break
29
29
  else
30
30
  member_id = $1
31
- member_info = @users.select { |u| u.id == member_id or (u.key?(:enterprise_user) and u.enterprise_user.id == member_id) }[-1]
32
- if member_info.nil?
33
- @users = get_users()
34
- member_info = @users.select { |u| u.id == member_id or (u.key?(:enterprise_user) and u.enterprise_user.id == member_id) }[-1]
35
- end
36
- team[:members][last_type] << member_info.name
31
+ member_info = find_user(member_id)
32
+ team[:members][last_type] << "#{member_info.team_id}_#{member_info.name}"
37
33
  end
38
34
  elsif opt.match(/<#(\w+)\|[^>]*>/i)
39
35
  team[:channels][last_type] ||= []
@@ -69,8 +65,8 @@ class SlackSmartBot
69
65
  get_teams()
70
66
  team[:info] = info
71
67
  team[:status] = :added
72
- team[:user] = user.name
73
- team[:creator] = user.name
68
+ team[:user] = "#{user.team_id}_#{user.name}"
69
+ team[:creator] = "#{user.team_id}_#{user.name}"
74
70
  team[:date] = Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z")[0..18]
75
71
  new_team = {}
76
72
  team[:name] = name
@@ -19,8 +19,8 @@ class SlackSmartBot
19
19
  get_channels_name_and_id() unless @channels_id.key?(ch)
20
20
  tm = get_channel_members(@channels_id[ch])
21
21
  tm.each do |m|
22
- user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
23
- team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
22
+ user_info = find_user(m)
23
+ team_members << "#{user_info.team_id}_#{user_info.name}" unless user_info.nil? or user_info.is_app_user or user_info.is_bot
24
24
  end
25
25
  end
26
26
  end
@@ -31,7 +31,7 @@ class SlackSmartBot
31
31
  end
32
32
  if !@teams.key?(team_name.to_sym)
33
33
  respond "It seems like the team *#{team_name}* doesn't exist.\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
34
- elsif !(all_team_members + [@teams[team_name.to_sym].creator] + config.masters).flatten.include?(user.name)
34
+ elsif !(all_team_members + [@teams[team_name.to_sym].creator] + config.team_id_masters).flatten.include?("#{user.team_id}_#{user.name}")
35
35
  respond "You have to be a member of the team, the creator or a Master admin to be able to delete this team."
36
36
  else
37
37
  if answer.empty?
@@ -17,8 +17,8 @@ class SlackSmartBot
17
17
  get_channels_name_and_id() unless @channels_id.key?(ch)
18
18
  tm = get_channel_members(@channels_id[ch])
19
19
  tm.each do |m|
20
- user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
21
- team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
20
+ user_info = find_user(m)
21
+ team_members << "#{user_info.team_id}_#{user_info.name}" unless user_info.is_app_user or user_info.is_bot
22
22
  end
23
23
  end
24
24
  end
@@ -31,34 +31,39 @@ class SlackSmartBot
31
31
  if type == "jira"
32
32
  able_to_connect_jira = false
33
33
  begin
34
- http = NiceHttp.new(config.jira.host)
35
- http.headers.authorization = NiceHttpUtils.basic_authentication(user: config.jira.user, password: config.jira.password)
36
- message.gsub!(/^\s*</, "")
37
- message.gsub!(/\>$/, "")
38
- message.gsub!(/\|.+$/, "")
39
- message.gsub!(/^#{config.jira.host}/, "")
40
- if message.include?("/browse/")
41
- message = message.scan(/\/browse\/(.+)/).join
42
- resp = http.get("/rest/api/latest/issue/#{message}")
43
- else
44
- message.gsub!(/^\/issues\/\?jql=/, "")
45
- message.gsub!(" ", "%20")
46
- resp = http.get("/rest/api/latest/search/?jql=#{message}")
47
- search = true
48
- end
49
- if resp.code == 200
50
- able_to_connect_jira = true
34
+ if config.jira.host == "" or config.jira.user == "" or config.jira.password == ""
35
+ respond "You need to supply the correct credentials for JIRA on the SmartBot settings: `jira: { host: HOST, user: USER, password: PASSWORD }`"
51
36
  else
52
- error_code = resp.code
53
- if resp.code == 400
54
- error_message = resp.data.json(:errorMessages)[-1]
37
+ http = NiceHttp.new(config.jira.host)
38
+ http.headers.authorization = NiceHttpUtils.basic_authentication(user: config.jira.user, password: config.jira.password)
39
+ message.gsub!(/^\s*</, "")
40
+ message.gsub!(/\>$/, "")
41
+ message.gsub!(/\|.+$/, "")
42
+ message.gsub!(/^#{config.jira.host}/, "")
43
+ if message.include?("/browse/")
44
+ message = message.scan(/\/browse\/(.+)/).join
45
+ resp = http.get("/rest/api/latest/issue/#{message}")
55
46
  else
56
- error_message = ""
47
+ message.gsub!(/^\/issues\/\?jql=/, "")
48
+ message.gsub!(" ", "%20")
49
+ resp = http.get("/rest/api/latest/search/?jql=#{message}")
50
+ search = true
51
+ end
52
+ if resp.code.to_s == '200'
53
+ able_to_connect_jira = true
54
+ else
55
+ error_code = resp.code.to_s
56
+ if resp.code.to_s == '400'
57
+ error_message = resp.data.json(:errorMessages)[-1]
58
+ else
59
+ error_message = ""
60
+ end
57
61
  end
62
+ http.close
58
63
  end
59
- http.close
60
64
  rescue => exception
61
65
  @logger.fatal exception
66
+ respond "There was an error trying to connect to JIRA. Please ask the admin to check the logs."
62
67
  end
63
68
  elsif type == "github"
64
69
  able_to_connect_github = false
@@ -73,11 +78,11 @@ class SlackSmartBot
73
78
  message.gsub!("https://github.com", "")
74
79
  message.slice!(0) if message[0] == "/"
75
80
  resp = http.get("/repos/#{message}")
76
- if resp.code == 200
81
+ if resp.code.to_s == '200'
77
82
  able_to_connect_github = true
78
83
  else
79
- error_code = resp.code
80
- if resp.code == 401
84
+ error_code = resp.code.to_s
85
+ if resp.code.to_s == '401'
81
86
  error_message = resp.data.json(:message)[-1]
82
87
  else
83
88
  error_message = ""
@@ -91,7 +96,7 @@ class SlackSmartBot
91
96
 
92
97
  if !@teams.key?(team_name.to_sym)
93
98
  respond "It seems like the team *#{team_name}* doesn't exist\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
94
- elsif !(all_team_members + config.masters).flatten.include?(user.name)
99
+ elsif !(all_team_members + config.team_id_masters).flatten.include?("#{user.team_id}_#{user.name}")
95
100
  respond "You have to be a member of the team or a Master admin to be able to add a memo to the team."
96
101
  elsif type == "jira" and !able_to_connect_jira
97
102
  if error_message == ""
@@ -117,7 +122,7 @@ class SlackSmartBot
117
122
  topic: topic,
118
123
  type: type,
119
124
  privacy: privacy,
120
- user: user.name,
125
+ user: "#{user.team_id}_#{user.name}",
121
126
  date: Time.now.strftime("%Y-%m-%dT%H:%M:%S.000Z")[0..18],
122
127
  message: message,
123
128
  status: ":new: ",
@@ -13,7 +13,7 @@ class SlackSmartBot
13
13
  memo = @teams[team_name].memos.select { |m| m.memo_id == memo_id.to_i }[-1]
14
14
  if memo
15
15
  memo.comments ||= []
16
- memo.comments << { user_name: user.name, user_id: user.id, message: message, time: Time.now.to_s }
16
+ memo.comments << { user_name: "#{user.team_id}_#{user.name}", user_id: user.id, message: message, time: Time.now.to_s }
17
17
  update_teams()
18
18
  if config.simulate
19
19
  respond "Comment added to memo #{memo_id} in team #{team_name}"
@@ -7,6 +7,8 @@ class SlackSmartBot
7
7
  save_stats(__method__) if answer.empty?
8
8
 
9
9
  get_teams()
10
+ team_id_user = "#{user.team_id}_#{user.name}"
11
+
10
12
  if @teams.key?(team_name.to_sym)
11
13
  assigned_members = @teams[team_name.to_sym].members.values.flatten
12
14
  assigned_members.uniq!
@@ -17,8 +19,8 @@ class SlackSmartBot
17
19
  get_channels_name_and_id() unless @channels_id.key?(ch)
18
20
  tm = get_channel_members(@channels_id[ch])
19
21
  tm.each do |m|
20
- user_info = @users.select { |u| u.id == m or (u.key?(:enterprise_user) and u.enterprise_user.id == m) }[-1]
21
- team_members << user_info.name unless user_info.is_app_user or user_info.is_bot
22
+ user_info = find_user(m)
23
+ team_members << "#{user_info.team_id}_#{user_info.name}" unless user_info.is_app_user or user_info.is_bot
22
24
  end
23
25
  end
24
26
  end
@@ -30,13 +32,13 @@ class SlackSmartBot
30
32
 
31
33
  if !@teams.key?(team_name.to_sym)
32
34
  respond "It seems like the team *#{team_name}* doesn't exist.\nRelated commands `add team TEAM_NAME PROPERTIES`, `see team TEAM_NAME`, `see teams`"
33
- elsif !(all_team_members + config.masters).flatten.include?(user.name)
35
+ elsif !(all_team_members + config.team_id_masters).flatten.include?(team_id_user)
34
36
  respond "You have to be a member of the team or a Master admin to be able to delete a memo of the team."
35
37
  elsif !@teams[team_name.to_sym].key?(:memos) or @teams[team_name.to_sym][:memos].empty? or !@teams[team_name.to_sym][:memos].memo_id.include?(memo_id.to_i)
36
38
  respond "It seems like there is no memo with id #{memo_id}"
37
39
  elsif @teams[team_name.to_sym][:memos].memo_id.include?(memo_id.to_i)
38
40
  memo_selected = @teams[team_name.to_sym][:memos].select { |m| m.memo_id == memo_id.to_i }[-1]
39
- if memo_selected.privacy == "personal" and memo_selected.user != user.name
41
+ if memo_selected.privacy == "personal" and memo_selected.user != team_id_user
40
42
  respond "Only the creator can delete a personal memo."
41
43
  else
42
44
  if answer.empty?
@@ -24,8 +24,9 @@ class SlackSmartBot
24
24
  end
25
25
  if memo
26
26
  if memo_deleted
27
- messages = ["This memo was deleted from the team #{team_name}.\nOnly the creator (#{memo.user}) of the memo can get access to it."]
28
- if memo.user == user.name
27
+ uname = memo.user.split("_")[1..-1].join("_")
28
+ messages = ["This memo was deleted from the team #{team_name}.\nOnly the creator (#{uname}) of the memo can get access to it."]
29
+ if memo.user == "#{user.team_id}_#{user.name}"
29
30
  messages << "Memo #{memo.memo_id} (#{memo.type}): #{memo.message}"
30
31
  end
31
32
  else
@@ -39,24 +40,33 @@ class SlackSmartBot
39
40
  else
40
41
  if memo.type == "jira" and !memo_deleted
41
42
  require "time"
42
- http = NiceHttp.new(config.jira.host)
43
- http.headers.authorization = NiceHttpUtils.basic_authentication(user: config.jira.user, password: config.jira.password)
44
- resp = http.get("/rest/api/2/issue/#{memo.message}/comment")
45
- if resp.code == 200
46
- jira_comments = resp.data.json.comments
47
- if !jira_comments.nil? and !jira_comments.empty?
48
- messages << "\n_*JIRA Comments:*_"
49
- jira_comments.each do |comment|
50
- messages << " *#{comment.author.displayName}* > #{comment.body} _(#{Time.parse(comment.created).strftime("%Y/%m/%d %H:%M")})_"
43
+ if config.jira.host == "" or config.jira.user == "" or config.jira.password == ""
44
+ respond "You need to supply the correct credentials for JIRA on the SmartBot settings: `jira: { host: HOST, user: USER, password: PASSWORD }`"
45
+ else
46
+ begin
47
+ http = NiceHttp.new(config.jira.host)
48
+ http.headers.authorization = NiceHttpUtils.basic_authentication(user: config.jira.user, password: config.jira.password)
49
+ resp = http.get("/rest/api/2/issue/#{memo.message}/comment")
50
+ if resp.code.to_s == '200'
51
+ jira_comments = resp.data.json.comments
52
+ if !jira_comments.nil? and !jira_comments.empty?
53
+ messages << "\n_*JIRA Comments:*_"
54
+ jira_comments.each do |comment|
55
+ messages << " *#{comment.author.displayName}* > #{comment.body} _(#{Time.parse(comment.created).strftime("%Y/%m/%d %H:%M")})_"
56
+ end
57
+ end
51
58
  end
59
+ http.close
60
+ rescue => exception
61
+ @logger.fatal exception
62
+ respond "There was an error trying to connect to JIRA. Please ask the admin to check the logs."
52
63
  end
53
64
  end
54
- http.close
55
65
  elsif memo.type == "github" and !memo_deleted
56
66
  http = NiceHttp.new(config.github.host)
57
67
  http.headers.authorization = "token #{config.github.token}"
58
68
  resp = http.get("/repos/#{memo.message}/comments")
59
- if resp.code == 200
69
+ if resp.code.to_s == '200'
60
70
  github_comments = resp.data.json
61
71
  if !github_comments.nil? and !github_comments.empty?
62
72
  messages << "\n_*GitHub Comments:*_"
@@ -67,10 +77,11 @@ class SlackSmartBot
67
77
  end
68
78
  http.close
69
79
  end
70
- if memo.key?(:comments) and !memo.comments.empty? and (!memo_deleted or (memo_deleted and memo.user == user.name))
80
+ if memo.key?(:comments) and !memo.comments.empty? and (!memo_deleted or (memo_deleted and memo.user == "#{user.team_id}_#{user.name}"))
71
81
  messages << "\n_*Comments:*_"
72
82
  memo.comments.each do |comment|
73
- messages << " *#{comment[:user_name]}* > #{comment[:message]} _(#{comment[:time][0..15]})_"
83
+ uname = comment[:user_name].split("_")[1..-1].join("_")
84
+ messages << " *#{uname}* > #{comment[:message]} _(#{comment[:time][0..15]})_"
74
85
  end
75
86
  end
76
87