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
@@ -21,8 +21,8 @@ class SlackSmartBot
21
21
  elsif el.type == 'user'
22
22
  data_text += "<@#{el.user_id}>"
23
23
  elsif el.type == 'channel'
24
- tch = data.text.scan(/(<##{el.channel_id}\|[^\>]*>)/).join
25
- data_text += tch
24
+ tch = data.text.scan(/(<##{el.channel_id}\|[^\>]*>)/).flatten.first
25
+ data_text += tch.to_s
26
26
  else
27
27
  data_text += el.url
28
28
  end
@@ -41,22 +41,22 @@ class SlackSmartBot
41
41
  end
42
42
  data.text.gsub!('‘', "'")
43
43
  data.text.gsub!('’', "'")
44
- data.text.gsub!('“', '"')
44
+ data.text.gsub!('“', '"')
45
45
  data.text.gsub!('”', '"')
46
46
  rescue Exception => exc
47
47
  @logger.warn "Impossible to unescape or clean format for data.text:#{data.text}"
48
48
  @logger.warn exc.inspect
49
49
  end
50
-
50
+
51
51
  unless data.key?(:routine)
52
- data.routine = false
52
+ data.routine = false
53
53
  data.routine_name = ''
54
54
  data.routine_type = ''
55
55
  end
56
56
  if config[:testing] and config.on_master_bot and !@buffered
57
57
  @buffered = true
58
58
  open("#{config.path}/buffer.log", "a") { |f|
59
- f.puts "|#{data.channel}|#{data.user}|#{data.user_name}|#{data.text}"
59
+ f.puts "|#{data.channel}|#{data.thread_ts}|#{data.user}|#{data.user_name}|#{data.text}"
60
60
  }
61
61
  end
62
62
  if data.key?(:dest) and data.dest.to_s!='' # for run routines and publish on different channels
@@ -80,38 +80,51 @@ class SlackSmartBot
80
80
  end
81
81
  end
82
82
  end
83
-
84
83
  if !dest.nil? and config.on_master_bot and !data.text.nil? and data.text.match(/^ping from (.+)\s*$/) and data.user == config[:nick_id]
85
84
  @pings << $1
86
85
  end
87
- if config.on_master_bot and @vacations_check != Date.today
88
- @vacations_check = Date.today
86
+ if config.on_master_bot and @vacations_check != Time.now.strftime("%Y%m%d%H") #every hour since depends on user's time zone
87
+ @vacations_check = Time.now.strftime("%Y%m%d%H")
89
88
  t = Thread.new do
90
- check_vacations(only_first_day: true)
89
+ check_vacations(date: nil, only_first_day: true)
91
90
  end
92
91
  end
93
92
  typem = :dont_treat
93
+
94
+ @users = get_users() if @users.empty?
95
+ if data.key?(:bot_id) and data.bot_id.to_s!=''
96
+ if @slack_bots.key?(data.bot_id) #bot or workflow
97
+ data.user = @slack_bots[data.bot_id]
98
+ else
99
+ bot_info = get_user_info(data.bot_id, is_bot: true)
100
+ unless bot_info.nil? or bot_info.empty?
101
+ @slack_bots[data.bot_id] = bot_info.user.id
102
+ data.user = bot_info.user.id
103
+ @users << bot_info.user unless bot_info.nil? or bot_info.empty?
104
+ end
105
+ end
106
+ end
94
107
  if data.nil? or data.user.nil? or data.user.to_s==''
95
108
  user_info = nil
96
- @users = get_users() if @users.empty?
97
109
  else
98
- #todo: when changed @questions user_id then move user_info inside the ifs to avoid calling it when not necessary
99
- user_info = @users.select{|u| u.id == data.user or (u.key?(:enterprise_user) and u.enterprise_user.id == data.user)}[-1]
100
- if user_info.nil? or user_info.empty?
101
- @users = get_users()
102
- user_info = @users.select{|u| u.id == data.user or (u.key?(:enterprise_user) and u.enterprise_user.id == data.user)}[-1]
110
+ user_info = find_user(data.user, get_sso_user_name: true)
111
+ if user_info.nil?
112
+ @logger.warn "User not found on users with id #{data.user} and user_team #{data.user_team}"
113
+ elsif !user_info.key?(:team_id) or user_info.team_id.to_s.empty?
114
+ @logger.warn "User with id #{data.user} and user_team #{data.user_team} has no team_id. User_info: #{user_info.inspect}"
103
115
  end
104
116
  end
105
117
  if !dest.nil? and !data.text.nil? and !data.text.to_s.match?(/\A\s*\z/)
118
+
106
119
  get_bots_created()
107
- if data.channel[0] == "D" and !data.text.to_s.match?(/^\s*<@#{config[:nick_id]}>\s+/) and
120
+ if data.channel[0] == "D" and !data.text.to_s.match?(/^\s*<@#{config[:nick_id]}>\s+/) and
108
121
  (data.text.to_s.match?(/^\s*(on)?\s*<#\w+\|[^>]*>/i) or data.text.to_s.match?(/^\s*(on)?\s*#\w+/i))
109
122
  data.text = "<@#{config[:nick_id]}> " + data.text.to_s
110
123
  end
111
124
  #todo: we need to add mixed channels: @smart-bot on private1 #bot1cm <#CXDDFRDDF|bot2cu>: echo A
112
125
  if data.text.match(/\A\^\^+/) # to open a thread it will be only when starting by single ^
113
126
  typem = :dont_treat
114
- elsif data.text.match(/^\s*<@#{config[:nick_id]}>\s+(on\s+)?((<#\w+\|[^>]*>\s*)+)\s*:?\s*(.*)/im) or
127
+ elsif data.text.match(/^\s*<@#{config[:nick_id]}>\s+(on\s+)?((<#\w+\|[^>]*>\s*)+)\s*:?\s*(.*)/im) or
115
128
  data.text.match(/^\s*<@#{config[:nick_id]}>\s+(on\s+)?((#[a-zA-Z0-9\-\_]+\s*)+)\s*:?\s*(.*)/im) or
116
129
  data.text.match(/^\s*<@#{config[:nick_id]}>\s+(on\s+)?(([a-zA-Z0-9\-\_]+\s*)+)\s*:\s*(.*)/im)
117
130
  channels_rules = $2 #multiple channels @smart-bot on #channel1 #channel2 echo AAA
@@ -132,10 +145,10 @@ class SlackSmartBot
132
145
  else
133
146
  channels_arr.each do |row|
134
147
  row[0] = @channels_id[row[1]] if row[0] == ''
135
- row[1] = @channels_name[row[0]] if row[1] == ''
148
+ row[1] = @channels_name[row[0]] if row[1] == ''
136
149
  end
137
150
  end
138
-
151
+
139
152
  # to be treated only on the bots of the requested channels
140
153
  channels_arr.each do |tcid, tcname|
141
154
  if @channel_id == tcid
@@ -163,9 +176,9 @@ class SlackSmartBot
163
176
  end
164
177
  elsif data.channel[0] == "D" #Direct message
165
178
  get_rules_imported()
166
- if @rules_imported.key?(user_info.name) && @rules_imported[user_info.name].key?(user_info.name) and
167
- @bots_created.key?(@rules_imported[user_info.name][user_info.name])
168
- if @channel_id == @rules_imported[user_info.name][user_info.name]
179
+ if @rules_imported.key?("#{user_info.team_id}_#{user_info.name}") && @rules_imported["#{user_info.team_id}_#{user_info.name}"].key?(user_info.name) and
180
+ @bots_created.key?(@rules_imported["#{user_info.team_id}_#{user_info.name}"][user_info.name])
181
+ if @channel_id == @rules_imported["#{user_info.team_id}_#{user_info.name}"][user_info.name]
169
182
  #only to be treated by the channel we are 'using'
170
183
  typem = :on_dm
171
184
  end
@@ -204,46 +217,61 @@ class SlackSmartBot
204
217
  load "#{config.path}/rules/general_commands.rb" if File.exist?("#{config.path}/rules/general_commands.rb") and @datetime_general_commands != File.mtime("#{config.path}/rules/general_commands.rb")
205
218
  eval(File.new(config.path + config.rules_file).read) if !defined?(rules) and File.exist?(config.path+config.rules_file) and !config.rules_file.empty?
206
219
  unless typem == :dont_treat or user_info.nil?
207
- if (Time.now - @last_activity_check) > 60 * 30 #every 30 minutes
220
+ if (Time.now - @last_activity_check) > TIMEOUT_LISTENING #every 30 minutes
208
221
  @last_activity_check = Time.now
209
222
  @listening.each do |k,v|
210
- v.each do |kk, vv|
211
- @listening[k].delete(kk) if (Time.now - vv) > 60 * 30
223
+ unless k == :threads
224
+ v.each do |kk, vv|
225
+ if (Time.now - vv) > TIMEOUT_LISTENING
226
+ if @listening[:threads].key?(kk) && @active_chat_gpt_sessions.key?(k) &&
227
+ @active_chat_gpt_sessions[k].key?(kk)
228
+ unreact :running, kk, channel: @listening[:threads][kk]
229
+ session_name = @active_chat_gpt_sessions[k][kk]
230
+ chatgpt_message = "ChatGPT session has been terminated due to inactivity."
231
+ if !session_name.to_s.empty?
232
+ chatgpt_message += "\n\nIf you want to start it again on this thread call `chatgpt #{session_name}`"
233
+ end
234
+ respond chatgpt_message, @listening[:threads][kk], thread_ts: kk
235
+ @listening[:threads].delete(kk)
236
+ end
237
+ @listening[k].delete(kk)
238
+ end
239
+ end
240
+ @listening.delete(k) if @listening[k].empty?
212
241
  end
213
- @listening.delete(k) if @listening[k].empty?
214
242
  end
215
243
  end
216
244
  begin
217
245
  #user_info.id = data.user #todo: remove this line when slack issue with Wxxxx Uxxxx fixed
218
246
  data.user = user_info.id #todo: remove this line when slack issue with Wxxxx Uxxxx fixed
219
- if data.thread_ts.nil?
247
+ team_id_user = "#{user_info.team_id}_#{user_info.name}"
248
+ if data.thread_ts.to_s.empty?
220
249
  qdest = dest
221
250
  else
222
251
  qdest = data.thread_ts
223
252
  end
224
- if !answer(user_info.name, qdest).empty?
253
+ if !answer(user_info, qdest).empty?
225
254
  if data.text.match?(/\A\s*(Bye|Bæ|Good\sBye|Adiós|Ciao|Bless|Bless\sBless|Adeu)\s(#{@salutations.join("|")})\s*$/i)
226
- answer_delete(user_info.name, qdest)
255
+ answer_delete(user_info, qdest)
227
256
  command = data.text
228
257
  else
229
- command = answer(user_info.name, qdest)
230
- @answer[user_info.name][qdest] = data.text
231
- @questions[user_info.name] = data.text # to be backwards compatible #todo remove it when 2.0
258
+ command = answer(user_info, qdest)
259
+ @answer[team_id_user][qdest] = data.text
260
+ @questions[team_id_user] = data.text # to be backwards compatible #todo remove it when 2.0
232
261
  end
233
- elsif @repl_sessions.key?(user_info.name) and data.channel==@repl_sessions[user_info.name][:dest] and
234
- ((@repl_sessions[user_info.name][:on_thread] and data.thread_ts == @repl_sessions[user_info.name][:thread_ts]) or
235
- (!@repl_sessions[user_info.name][:on_thread] and data.thread_ts.to_s == '' ))
236
-
262
+ elsif @repl_sessions.key?(team_id_user) and data.channel==@repl_sessions[team_id_user][:dest] and
263
+ ((@repl_sessions[team_id_user][:on_thread] and data.thread_ts == @repl_sessions[team_id_user][:thread_ts]) or
264
+ (!@repl_sessions[team_id_user][:on_thread] and data.thread_ts.to_s == '' ))
265
+
237
266
  if data.text.match(/^\s*```(.*)```\s*$/im)
238
- @repl_sessions[user_info.name][:command] = $1
239
- else
240
- @repl_sessions[user_info.name][:command] = data.text
267
+ @repl_sessions[team_id_user][:command] = $1
268
+ else
269
+ @repl_sessions[team_id_user][:command] = data.text
241
270
  end
242
271
  command = 'repl'
243
272
  else
244
273
  command = data.text
245
274
  end
246
-
247
275
  #when added special characters on the message
248
276
  if command.match(/\A\s*```(.*)```\s*\z/im)
249
277
  command = $1
@@ -291,16 +319,16 @@ class SlackSmartBot
291
319
  command.size > 0 and command[0] != "-"
292
320
  process_first(user_info, command, dest, data.channel, typem, data.files, data.ts, data.thread_ts, data.routine, data.routine_name, data.routine_type, command_orig)
293
321
  # if @botname on #channel_rules: do something
294
- elsif typem == :on_pub or typem == :on_pg
322
+ elsif (typem == :on_pub or typem == :on_pg) and command.size > 0 and command[0] != "-"
295
323
  process_first(user_info, command, dest, channel_rules, typem, data.files, data.ts, data.thread_ts, data.routine, data.routine_name, data.routine_type, command_orig)
296
324
  end
297
-
298
325
  rescue Exception => stack
299
326
  @logger.fatal stack
300
327
  end
301
-
302
328
  else
303
- @logger.warn "Pay attention there is no user on users with id #{data.user}" if user_info.nil? and data.user.to_s!=''
329
+ if user_info.nil? and data.user.to_s!=''
330
+ @logger.warn "Pay attention there is no user on users with id #{data.user}"
331
+ end
304
332
  if !config.on_master_bot and !dest.nil? and (data.channel == @master_bot_id or dest[0] == "D") and
305
333
  data.text.match?(/^\s*(!|!!|\^)?\s*bot\s+status\s*$/i) and @admin_users_id.include?(data.user)
306
334
  respond "ping from #{config.channel}", dest
@@ -342,7 +370,7 @@ class SlackSmartBot
342
370
  end
343
371
  end
344
372
  end
345
-
373
+
346
374
  when /^Bot has been (closed|killed) by/i
347
375
  sleep 2
348
376
  get_bots_created()
@@ -379,6 +407,10 @@ class SlackSmartBot
379
407
  end
380
408
  end
381
409
  if @status == :exit
410
+ @listening[:threads].each do |thread_ts, channel_thread|
411
+ unreact :running, thread_ts, channel: channel_thread
412
+ respond "ChatGPT session closed since SmartBot is going to be closed", channel_thread, thread_ts: thread_ts
413
+ end
382
414
  @logger.info 'Game over!'
383
415
  sleep 3
384
416
  exit!
@@ -388,7 +420,7 @@ class SlackSmartBot
388
420
  end
389
421
  unless data.nil? or data.channel.nil? or data.channel.empty?
390
422
  @announcements_activity_after[data.channel] ||= 0
391
- @announcements_activity_after[data.channel] += 1
423
+ @announcements_activity_after[data.channel] += 1
392
424
  end
393
425
  rescue Exception => stack
394
426
  @logger.fatal stack
@@ -1,5 +1,14 @@
1
1
  class SlackSmartBot
2
- def answer(from = Thread.current[:user].name, dest = Thread.current[:dest])
2
+ def answer(user = Thread.current[:user], dest = Thread.current[:dest])
3
+ if user.is_a?(String)
4
+ if user.match?(/^[A-Z0-9]{7,11}_/)
5
+ from = user
6
+ else
7
+ from = "#{config.team_id}_#{user}"
8
+ end
9
+ else
10
+ from = "#{user.team_id}_#{user.name}"
11
+ end
3
12
  if @answer.key?(from)
4
13
  if Thread.current[:on_thread]
5
14
  dest = Thread.current[:thread_ts]
@@ -13,6 +22,5 @@ class SlackSmartBot
13
22
  return ''
14
23
  end
15
24
  end
16
-
25
+
17
26
  end
18
-
@@ -1,5 +1,14 @@
1
1
  class SlackSmartBot
2
- def answer_delete(from = Thread.current[:user].name, dest = Thread.current[:dest])
2
+ def answer_delete(user = Thread.current[:user], dest = Thread.current[:dest])
3
+ if user.is_a?(String)
4
+ if user.match?(/^[A-Z0-9]{7,11}_/)
5
+ from = user
6
+ else
7
+ from = "#{config.team_id}_#{user}"
8
+ end
9
+ else
10
+ from = "#{user.team_id}_#{user.name}"
11
+ end
3
12
  if @answer.key?(from)
4
13
  if Thread.current[:on_thread]
5
14
  dest = Thread.current[:thread_ts]
@@ -10,6 +19,5 @@ class SlackSmartBot
10
19
  @questions.delete(from) # to be backwards compatible #todo: remove when 2.0
11
20
  end
12
21
  end
13
-
22
+
14
23
  end
15
-
@@ -1,15 +1,33 @@
1
1
  class SlackSmartBot
2
- def check_vacations(date: Date.today, user: nil, set_status: true, only_first_day: true)
2
+ def check_vacations(date: Date.today, team_id: nil, user: nil, set_status: true, only_first_day: true)
3
3
  get_vacations()
4
4
  if user.nil?
5
5
  users = @vacations.keys
6
6
  else
7
- users = [user]
7
+ users = ["#{team_id}_#{user}"]
8
8
  end
9
- on_vacation = []
9
+ on_vacation = []
10
+
10
11
  users.each do |user|
11
12
  type = nil
12
13
  expiration = nil
14
+
15
+ if date.nil?
16
+ if @vacations.key?(user) and @vacations[user][:public_holidays].to_s != ""
17
+ country_region = @vacations[user][:public_holidays].downcase
18
+ elsif config[:public_holidays].key?(:default_calendar)
19
+ country_region = config[:public_holidays][:default_calendar].downcase
20
+ else
21
+ country_region = ''
22
+ end
23
+
24
+ local_day_time = local_time(country_region)
25
+ if local_day_time.nil?
26
+ date = Date.today
27
+ else
28
+ date = local_day_time.to_date
29
+ end
30
+ end
13
31
  @vacations[user].periods ||= []
14
32
  @vacations[user].periods.each do |p|
15
33
  if only_first_day and p.from == date.strftime("%Y/%m/%d")
@@ -7,7 +7,7 @@ class SlackSmartBot
7
7
  started = Time.now
8
8
  if @status == :on and @routines[@channel_id][name][:status] == :on
9
9
  if !@routines[@channel_id][name].key?(:creator_id) or @routines[@channel_id][name][:creator_id].to_s == ''
10
- user_info = @users.select{|u| u.name == @routines[@channel_id][name][:creator]}[-1]
10
+ user_info = find_user(@routines[@channel_id][name][:creator])
11
11
  @routines[@channel_id][name][:creator_id] = user_info.id unless user_info.nil? or user_info.empty?
12
12
  end
13
13
  @logger.info "Routine #{name}: #{@routines[@channel_id][name].inspect}"
@@ -20,11 +20,11 @@ class SlackSmartBot
20
20
  if @routines[@channel_id][name][:at] == "" or
21
21
  (@routines[@channel_id][name][:at] != "" and @routines[@channel_id][name][:running] and
22
22
  @routines[@channel_id][name][:next_run] != "" and Time.now.to_s >= @routines[@channel_id][name][:next_run])
23
-
24
- if !@routines[@channel_id][name].key?(:dayweek) or
23
+
24
+ if !@routines[@channel_id][name].key?(:dayweek) or
25
25
  (@routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!='weekday' and @routines[@channel_id][name][:dayweek].to_s!='weekend') or
26
26
  (@routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s=='weekday' and Date.today.wday>=1 and Date.today.wday<=5) or
27
- (@routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s=='weekend' and (Date.today.wday==6 or Date.today.wday==0))
27
+ (@routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s=='weekend' and (Date.today.wday==6 or Date.today.wday==0))
28
28
  File.delete "#{config.path}/routines/#{@channel_id}/#{name}_output.txt" if File.exist?("#{config.path}/routines/#{@channel_id}/#{name}_output.txt")
29
29
  if @routines[@channel_id][name][:file_path] != ""
30
30
  process_to_run = "#{ruby}#{Dir.pwd}#{@routines[@channel_id][name][:file_path][1..-1]}"
@@ -90,7 +90,7 @@ class SlackSmartBot
90
90
  end
91
91
  @routines[@channel_id][name][:last_run] = started.to_s
92
92
  elsif (@routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s=='weekday' and (Date.today.wday==6 or Date.today.wday==0)) or
93
- (@routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s=='weekend' and Date.today.wday>=1 and Date.today.wday<=5)
93
+ (@routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s=='weekend' and Date.today.wday>=1 and Date.today.wday<=5)
94
94
  @routines[@channel_id][name][:last_run] = started.to_s
95
95
  end
96
96
  end
@@ -116,27 +116,27 @@ class SlackSmartBot
116
116
  end
117
117
  days = (next_time - Date.today).to_i
118
118
  every_in_seconds = Time.parse(@routines[@channel_id][name][:next_run]) - Time.now
119
- elsif @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!='' and
119
+ elsif @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!='' and
120
120
  @routines[@channel_id][name][:dayweek].to_s!='weekend' and @routines[@channel_id][name][:dayweek].to_s!='weekday'
121
-
121
+
122
122
  day = @routines[@channel_id][name][:dayweek]
123
123
  days = ['sunday','monday','tuesday','wednesday','thursday','friday','saturday']
124
124
  incr = days.index(day) - Time.now.wday
125
- if incr < 0
125
+ if incr < 0
126
126
  incr = (7+incr)*24*60*60
127
127
  else
128
128
  incr = incr * 24 * 60 * 60
129
129
  end
130
130
  days = incr/(24*60*60)
131
131
  weekly = true
132
- elsif @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!='' and
132
+ elsif @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!='' and
133
133
  @routines[@channel_id][name][:dayweek].to_s=='weekend'
134
-
134
+
135
135
  weekly = false
136
136
  days = 0
137
- elsif @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!='' and
137
+ elsif @routines[@channel_id][name].key?(:dayweek) and @routines[@channel_id][name][:dayweek].to_s!='' and
138
138
  @routines[@channel_id][name][:dayweek].to_s=='weekday'
139
-
139
+
140
140
  weekly = false
141
141
  days = 0
142
142
  else
@@ -147,7 +147,7 @@ class SlackSmartBot
147
147
  if started.strftime("%H:%M:%S") < @routines[@channel_id][name][:at] and days == 0
148
148
  nt = @routines[@channel_id][name][:at].split(":")
149
149
  next_run = Time.new(started.year, started.month, started.day, nt[0], nt[1], nt[2])
150
- else
150
+ else
151
151
  if days == 0 and started.strftime("%H:%M:%S") >= @routines[@channel_id][name][:at]
152
152
  if weekly
153
153
  days = 7
@@ -5,8 +5,13 @@ class SlackSmartBot
5
5
  elsif config[:public_holidays].key?(:default_calendar) and country_region.empty?
6
6
  country_region = config[:public_holidays][:default_calendar].downcase
7
7
  end
8
-
9
8
  country, location = country_region.split("/")
9
+ local_day_time = local_time(country_region)
10
+ if local_day_time.nil?
11
+ today = Date.today
12
+ else
13
+ today = local_day_time.to_date
14
+ end
10
15
  public_holidays(country.to_s, location.to_s, year, "", "", add_stats: false, publish_results: false)
11
16
  if from_user_name.empty?
12
17
  messages = ["*Calendar #{year} #{country_region}*"]
@@ -39,7 +44,11 @@ class SlackSmartBot
39
44
  vacations_set = false
40
45
  public_holiday_set = false
41
46
  if phd.include?(date_text)
42
- month_line += ":large_red_square: "
47
+ if date == today
48
+ month_line += ":large_red_circle: "
49
+ else
50
+ month_line += ":large_red_square: "
51
+ end
43
52
  public_holiday_set = true
44
53
  end
45
54
  if !public_holiday_set
@@ -47,11 +56,23 @@ class SlackSmartBot
47
56
  @vacations[from_user_name][:periods].each do |period|
48
57
  if date >= Date.parse(period[:from]) and date <= Date.parse(period[:to])
49
58
  if period[:type] == "sick"
50
- month_line += ":face_with_thermometer: "
59
+ if date == today
60
+ month_line += ":thermometer: "
61
+ else
62
+ month_line += ":face_with_thermometer: "
63
+ end
51
64
  elsif period[:type] == "sick child"
52
- month_line += ":baby: "
65
+ if date == today
66
+ month_line += ":baby_bottle: "
67
+ else
68
+ month_line += ":baby: "
69
+ end
53
70
  elsif period[:type] == "vacation"
54
- month_line += ":palm_tree: "
71
+ if date == today
72
+ month_line += ":evergreen_tree: "
73
+ else
74
+ month_line += ":palm_tree: "
75
+ end
55
76
  if wday <= 5
56
77
  days_of_vacations += 1
57
78
  end
@@ -63,9 +84,17 @@ class SlackSmartBot
63
84
  end
64
85
  if !vacations_set
65
86
  if wday == 6 || wday == 7
66
- month_line += ":large_yellow_square: "
87
+ if date == today
88
+ month_line += ":large_yellow_circle: "
89
+ else
90
+ month_line += ":large_yellow_square: "
91
+ end
67
92
  else
68
- month_line += ":white_square: "
93
+ if date == today
94
+ month_line += ":white_circle: "
95
+ else
96
+ month_line += ":white_square: "
97
+ end
69
98
  end
70
99
  end
71
100
  end
@@ -80,7 +109,12 @@ class SlackSmartBot
80
109
  if !from_user_name.empty?
81
110
  messages << "\n\n:large_yellow_square: weekend / :white_square: weekday / :white_small_square: not in month / :large_red_square: Public Holiday / :palm_tree: Vacation / :face_with_thermometer: Sick / :baby: Sick child"
82
111
  if country_region != ""
83
- messages << "Your public holidays are set for #{country_region.downcase}. Call `set public holidays to COUNTRY/REGION` if you want to change it.\n"
112
+ if local_day_time.nil?
113
+ local_str = "local time not found"
114
+ else
115
+ local_str = local_day_time.strftime("%Y-%m-%d %H:%M")
116
+ end
117
+ messages << "Your public holidays are set for #{country_region.downcase} (#{local_str}). Call `set public holidays to COUNTRY/REGION` if you want to change it.\n"
84
118
  else
85
119
  messages << "Your public holidays are not set. Call `set public holidays to COUNTRY/REGION` to set it.\n"
86
120
  end
@@ -2,17 +2,24 @@ class SlackSmartBot
2
2
  module Utils
3
3
  module Encryption
4
4
  def self.decrypt(data, config)
5
- if config.encrypt
5
+ if config.encrypt or (config.key?(:recover_encrypted) and config[:recover_encrypted])
6
6
  require "openssl"
7
7
  require "base64"
8
-
9
- key, iv = Utils::Encryption.encryption_get_key_iv(config)
10
- encrypted = Base64.decode64(data)
11
- cipher = OpenSSL::Cipher.new("AES-256-CBC")
12
- cipher.decrypt
13
- cipher.key = key
14
- cipher.iv = iv
15
- plain = cipher.update(encrypted) + cipher.final
8
+ if data == ''
9
+ plain = ''
10
+ else
11
+ begin
12
+ key, iv = Utils::Encryption.encryption_get_key_iv(config)
13
+ encrypted = Base64.decode64(data)
14
+ cipher = OpenSSL::Cipher.new("AES-256-CBC")
15
+ cipher.decrypt
16
+ cipher.key = key
17
+ cipher.iv = iv
18
+ plain = cipher.update(encrypted) + cipher.final
19
+ rescue Exception => stack
20
+ return data
21
+ end
22
+ end
16
23
  return plain
17
24
  else
18
25
  return data
@@ -5,17 +5,20 @@ class SlackSmartBot
5
5
  if config.encrypt
6
6
  require "openssl"
7
7
  require "base64"
8
-
9
- key, iv = Utils::Encryption.encryption_get_key_iv(config)
10
- cipher = OpenSSL::Cipher::Cipher.new "AES-256-CBC"
11
- cipher.encrypt
12
- cipher.key = key
13
- cipher.iv = iv
14
- encrypted = cipher.update(data) + cipher.final
15
- encrypted = Base64.encode64(encrypted)
16
- if defined?(Thread.current)
17
- Thread.current[:encrypted] ||= []
18
- Thread.current[:encrypted] << data
8
+ if data == ''
9
+ encrypted = ''
10
+ else
11
+ key, iv = Utils::Encryption.encryption_get_key_iv(config)
12
+ cipher = OpenSSL::Cipher::Cipher.new "AES-256-CBC"
13
+ cipher.encrypt
14
+ cipher.key = key
15
+ cipher.iv = iv
16
+ encrypted = cipher.update(data) + cipher.final
17
+ encrypted = Base64.encode64(encrypted)
18
+ if defined?(Thread.current)
19
+ Thread.current[:encrypted] ||= []
20
+ Thread.current[:encrypted] << data
21
+ end
19
22
  end
20
23
  return encrypted
21
24
  else
@@ -0,0 +1,71 @@
1
+ class SlackSmartBot
2
+ def find_user(user, get_sso_user_name: false)
3
+ @users = get_users() if @users.empty?
4
+ userh = { id: "", name: "" }
5
+ user_info = nil
6
+ if user.to_s.length > 0 and user != "@"
7
+ if user[0] == "@" #name
8
+ user = user[1..-1]
9
+ is_name = true
10
+ else
11
+ is_name = false
12
+ end
13
+ if user.match?(/^[A-Z0-9]{7,11}_/) #team_id_user_name
14
+ team_id = user.split("_")[0]
15
+ user = user.split("_")[1..-1].join("_")
16
+ is_name = true
17
+ else
18
+ team_id = config.team_id
19
+ is_name = false
20
+ end
21
+
22
+ if is_name
23
+ userh[:name] = user
24
+ elsif user.match?(/[a-z]/)
25
+ # if not is name and user contains any downcase letter, then we guess it is a name
26
+ userh[:name] = user
27
+ else
28
+ userh[:id] = user
29
+ end
30
+ user_info = @users.select { |u|
31
+ # for user id we don't check team_id as for the moment according to Slack API, user id is unique
32
+ ((userh[:id].to_s != "" and u.id == userh[:id]) or (u.key?(:enterprise_user) and u.enterprise_user.id == userh[:id])) or
33
+ ((userh[:name].to_s != "" and u.name == userh[:name] and u.team_id == team_id) or (u.key?(:enterprise_user) and u.enterprise_user.name == userh[:name] and u.enterprise_user.enterprise_id == team_id))
34
+ }[-1]
35
+
36
+ if user_info.nil? #other workspace
37
+ user_info = get_user_info(user)
38
+ unless user_info.nil? or user_info.empty?
39
+ @users << user_info.user
40
+ user_info = @users.select { |u|
41
+ # for user id we don't check team_id as for the moment according to Slack API, user id is unique
42
+ ((userh[:id].to_s != "" and u.id == userh[:id]) or (u.key?(:enterprise_user) and u.enterprise_user.id == userh[:id])) or
43
+ ((userh[:name].to_s != "" and u.name == userh[:name] and u.team_id == team_id) or (u.key?(:enterprise_user) and u.enterprise_user.name == userh[:name] and u.enterprise_user.enterprise_id == team_id))
44
+ }[-1]
45
+ end
46
+ end
47
+ end
48
+ if get_sso_user_name and defined?(@ldap) and !@ldap.nil? and !user_info.nil? and user_info[:sso_user_name].to_s.empty? and !user_info[:profile].email.to_s.empty?
49
+ begin
50
+ if @ldap.bind
51
+ email = user_info[:profile].email
52
+ filter1 = Net::LDAP::Filter.eq("mail", email)
53
+ filter2 = Net::LDAP::Filter.eq("mailAlternateAddress", email)
54
+ filter3 = Net::LDAP::Filter.eq("mail", email.gsub(/@.+$/, ""))
55
+ filter4 = Net::LDAP::Filter.eq("mailAlternateAddress", email.gsub(/@.+$/, ""))
56
+ filter = filter1 | filter2 | filter3 | filter4
57
+ @ldap.search(:base => config.ldap.treebase, :filter => filter) do |entry|
58
+ user_info[:sso_user_name] = entry.uid[0]
59
+ end
60
+ end
61
+ rescue => exception
62
+ if defined?(@logger)
63
+ @logger.fatal exception
64
+ else
65
+ puts exception
66
+ end
67
+ end
68
+ end
69
+ return user_info
70
+ end
71
+ end