slack-smart-bot 1.14.2 → 1.15.0

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 +182 -76
  174. data/lib/slack-smart-bot_general_commands.rb +10 -9
  175. data/whats_new.txt +30 -13
  176. metadata +148 -20
@@ -1,3 +1,5 @@
1
+ SP_COMPARE_NUMBERS_AS_STRINGS = false #nice_hash
2
+
1
3
  require "slack-ruby-client"
2
4
  require "async"
3
5
  require "open-uri"
@@ -8,9 +10,12 @@ require "fileutils"
8
10
  require "open3"
9
11
  require "nice_http"
10
12
  require "nice_hash"
11
- require 'cgi'
12
- require 'yaml'
13
+ require "cgi"
14
+ require "yaml"
15
+ require "nokogiri"
16
+ require 'tiktoken_ruby'
13
17
 
18
+ require_relative "slack/smart-bot/config"
14
19
  require_relative "slack/smart-bot/comm"
15
20
  require_relative "slack/smart-bot/listen"
16
21
  require_relative "slack/smart-bot/treat_message"
@@ -21,8 +26,9 @@ require_relative "slack/smart-bot/utils"
21
26
  require_relative "slack/smart-bot/ai"
22
27
 
23
28
  ADMIN_USERS = MASTER_USERS if defined?(MASTER_USERS) # for bg compatibility
29
+
24
30
  class SlackSmartBot
25
- attr_accessor :config, :client, :client_user
31
+ attr_accessor :config, :client, :client_user, :client_granular
26
32
  attr_reader :master_bot_id, :channel_id
27
33
  geml = Gem.loaded_specs.values.select { |x| x.name == "slack-smart-bot" }[0]
28
34
  if geml.nil?
@@ -31,47 +37,33 @@ class SlackSmartBot
31
37
  version = geml.version.to_s
32
38
  end
33
39
  VERSION = version
40
+ TIMEOUT_LISTENING = 60 * 30 # 30 minutes
34
41
 
42
+ # Initializes the Slack Smart Bot with the given configuration.
43
+ # Check the README for more information on the configuration options.
44
+ # and lib/slack/smart-bot/config.rb for the default values.
35
45
  def initialize(config)
36
- if config.key?(:path) and config[:path] != ''
37
- config.path.chop! if config.path[-1]=="/"
38
- else
39
- config[:path] = '.'
46
+ config_defaults = SlackSmartBot::Config.new()
47
+ abort "The config supplied is not correct. You need to supply a hash with the correct keys and values. The keys are: #{config_defaults.to_h.keys.join(", ")}" unless config.is_a?(Hash)
48
+ # when simulate true for testing purposes: client, web_client, nick, nick_id, git
49
+ config_check = config.deep_copy
50
+ [:client, :web_client, :nick, :nick_id, :git, :allow_access, :authorizations].each do |key|
51
+ config_check.delete(key)
40
52
  end
41
- config[:silent] = false unless config.key?(:silent)
42
- config[:testing] = false unless config.key?(:testing)
43
- config[:simulate] = false unless config.key?(:simulate)
44
- config[:stats] = false unless config.key?(:stats)
45
- config[:allow_access] = Hash.new unless config.key?(:allow_access)
46
- config[:on_maintenance] = false unless config.key?(:on_maintenance)
47
- config[:on_maintenance_message] = "Sorry I'm on maintenance so I cannot attend your request." unless config.key?(:on_maintenance_message)
48
- config[:general_message] = "" unless config.key?(:general_message)
49
- config[:logrtm] = false unless config.key?(:logrtm)
50
- config[:status_channel] = 'smartbot-status' unless config.key?(:status_channel)
51
- config[:stats_channel] = 'smartbot-stats' unless config.key?(:stats_channel)
52
-
53
- config[:jira] = { host: '', user: '', password: '' } unless config.key?(:jira) and config[:jira].key?(:host) and config[:jira].key?(:user) and config[:jira].key?(:password)
54
- config[:jira][:host] = "https://#{config[:jira][:host]}" unless config[:jira][:host] == '' or config[:jira][:host].match?(/^http/)
55
- config[:github] = {token: '' } unless config.key?(:github) and config[:github].key?(:token)
56
- config[:github][:host] ||= "https://api.github.com"
57
- config[:github][:host] = "https://#{config[:github][:host]}" unless config[:github][:host] == '' or config[:github][:host].match?(/^http/)
58
- config[:public_holidays] = { api_key: '' } unless config.key?(:public_holidays) and config[:public_holidays].key?(:api_key)
59
- config[:public_holidays][:host] ||= "https://calendarific.com"
60
- config[:public_holidays][:host] = "https://#{config[:public_holidays][:host]}" unless config[:public_holidays][:host] == '' or config[:public_holidays][:host].match?(/^http/)
61
- config[:encrypt] ||= true unless config.key?(:encrypt)
62
- config[:ai] ||= {} unless config.key?(:ai)
63
- config[:ai][:open_ai] ||= {
64
- access_token: '',
65
- organization_id: ''
66
- } unless config[:ai].key?(:open_ai)
67
- config[:ai][:open_ai][:whisper_model] ||= 'whisper-1'
68
- config[:ai][:open_ai][:image_size] ||= '256x256'
69
- config[:ai][:open_ai][:gpt_model] ||= 'gpt-3.5-turbo'
70
-
71
- if config.path.to_s!='' and config.file.to_s==''
53
+ result_config = NiceHash.compare_structure(config_check, config_defaults.to_h)
54
+ abort "The config supplied is not correct. You need to supply a hash with the correct keys and values. The keys are: #{config_defaults.to_h.keys.join(", ")}" unless result_config
55
+
56
+ config = config_defaults.to_h.nice_merge(config)
57
+
58
+ config.path.chop! if config.path[-1] == "/"
59
+ config[:jira][:host] = "https://#{config[:jira][:host]}" unless config[:jira][:host] == "" or config[:jira][:host].match?(/^http/)
60
+ config[:github][:host] = "https://#{config[:github][:host]}" unless config[:github][:host] == "" or config[:github][:host].match?(/^http/)
61
+ config[:public_holidays][:host] = "https://#{config[:public_holidays][:host]}" unless config[:public_holidays][:host] == "" or config[:public_holidays][:host].match?(/^http/)
62
+
63
+ if config.path.to_s != "" and config.file.to_s == ""
72
64
  config.file = File.basename($0)
73
65
  end
74
- if config.key?(:file) and config.file!=''
66
+ if config.key?(:file) and config.file != ""
75
67
  config.file_path = "#{config.path}/#{config.file}"
76
68
  else
77
69
  config.file_path = $0
@@ -91,28 +83,63 @@ class SlackSmartBot
91
83
  Dir.mkdir("#{config.path}/vacations") unless Dir.exist?("#{config.path}/vacations")
92
84
  Dir.mkdir("#{config.path}/teams") unless Dir.exist?("#{config.path}/teams")
93
85
  Dir.mkdir("#{config.path}/personal_settings") unless Dir.exist?("#{config.path}/personal_settings")
86
+ Dir.mkdir("#{config.path}/openai") unless Dir.exist?("#{config.path}/openai")
87
+
94
88
  File.delete("#{config.path}/config_tmp.status") if File.exist?("#{config.path}/config_tmp.status")
95
89
 
96
- config.masters = MASTER_USERS if config.masters.to_s=='' and defined?(MASTER_USERS)
97
- config.master_channel = MASTER_CHANNEL if config.master_channel.to_s=='' and defined?(MASTER_CHANNEL)
90
+ if !config.simulate
91
+ if (!config.key?(:token) or config.token.to_s == "")
92
+ abort "You need to supply a valid token key on the settings. key: :token"
93
+ elsif (!config.key?(:user_token) or config.user_token.to_s == "")
94
+ abort "You need to supply a valid user_token key on the settings. key: :user_token"
95
+ elsif (!config.key?(:granular_token) or config.granular_token.to_s == "")
96
+ abort "You need to supply a valid granular_token key on the settings. key: :granular_token"
97
+ end
98
+ end
99
+
100
+ resp = get_smartbot_team_info(config[:token])
101
+ if resp.key?(:team) and resp[:team].key?(:enterprise_id)
102
+ config.team_id = resp[:team][:enterprise_id]
103
+ else
104
+ config.team_id = resp.team.id
105
+ end
98
106
 
99
- if ARGV.size == 0 or (config.file.to_s!='' and config.file.to_s!=File.basename($0))
100
- config.rules_file = "#{config.file.gsub(".rb", "_rules.rb")}" unless config.rules_file.to_s!=''
101
- unless File.exist?(config.path + '/' + config.rules_file)
107
+ config.masters = MASTER_USERS if config.masters.to_s == "" and defined?(MASTER_USERS)
108
+ config.team_id_masters ||= []
109
+ config.team_id_admins ||= []
110
+
111
+ config.master_channel = MASTER_CHANNEL if config.master_channel.to_s == "" and defined?(MASTER_CHANNEL)
112
+
113
+ if ARGV.size == 0 or (config.file.to_s != "" and config.file.to_s != File.basename($0))
114
+ config.rules_file = "#{config.file.gsub(".rb", "_rules.rb")}" unless config.rules_file.to_s != ""
115
+ unless File.exist?(config.path + "/" + config.rules_file)
102
116
  default_rules = (__FILE__).gsub(/\.rb$/, "_rules.rb")
103
- FileUtils.copy_file(default_rules, config.path + '/' + config.rules_file)
117
+ FileUtils.copy_file(default_rules, config.path + "/" + config.rules_file)
104
118
  end
105
- config.admins = config.masters.dup unless config.admins.to_s!=''
106
- config.channel = config.master_channel unless config.channel.to_s!=''
107
- config.status_init = :on unless config.status_init.to_s!=''
119
+ config.admins = config.masters.dup unless !config.admins.empty?
120
+ config.team_id_admins = config.team_id_masters.dup unless !config.team_id_admins.empty?
121
+ config.channel = config.master_channel unless config.channel.to_s != ""
122
+ config.status_init = :on unless config.status_init.to_s != ""
108
123
  else
109
124
  config.rules_file = ARGV[2]
110
125
  config.admins = ARGV[1].split(",")
111
126
  config.channel = ARGV[0]
112
127
  config.status_init = ARGV[3].to_sym
113
128
  end
114
- config.rules_file[0]='' if config.rules_file[0]=='.'
115
- config.rules_file='/'+config.rules_file if config.rules_file[0]!='/'
129
+ if config.team_id_admins.size != config.admins.size and !config.admins.empty?
130
+ config.admins.each do |name|
131
+ if name.match?(/^[A-Z0-9]{7,11}_/)
132
+ config.team_id_admins << name
133
+ else
134
+ config.team_id_admins << "#{config.team_id}_#{name}"
135
+ end
136
+ end
137
+ end
138
+ config.team_id_admins.uniq!
139
+ config.admins.uniq!
140
+
141
+ config.rules_file[0] = "" if config.rules_file[0] == "."
142
+ config.rules_file = "/" + config.rules_file if config.rules_file[0] != "/"
116
143
 
117
144
  config.shortcuts_file = "slack-smart-bot_shortcuts_#{config.channel}.yaml".gsub(" ", "_")
118
145
  if config.channel == config.master_channel
@@ -122,32 +149,47 @@ class SlackSmartBot
122
149
  config.on_master_bot = false
123
150
  end
124
151
 
125
- if (!config.key?(:token) or config.token.to_s == '') and !config.simulate
126
- abort "You need to supply a valid token key on the settings. key: :token"
127
- elsif !config.key?(:masters) or !config.masters.is_a?(Array) or config.masters.size == 0
128
- abort "You need to supply a masters array on the settings containing the user names of the master admins. key: :masters"
129
- elsif !config.key?(:master_channel) or config.master_channel.to_s == ''
152
+ if (!config.masters.is_a?(Array) or !config.team_id_masters.is_a?(Array)) or
153
+ (config.masters + config.team_id_masters).empty?
154
+ message = "You need to supply a masters array on the settings containing the user names of the master admins, for example: [peter]. key: :masters"
155
+ message += " or a team_id_masters array containing the team_id and user names of the master admins, for example: [TJDFJKD34_peter]. key: :team_id_masters"
156
+ abort message
157
+ elsif !config.key?(:master_channel) or config.master_channel.to_s == ""
130
158
  abort "You need to supply a master_channel on the settings. key: :master_channel"
131
- elsif !config.key?(:channel) or config.channel.to_s == ''
159
+ elsif !config.key?(:channel) or config.channel.to_s == ""
132
160
  abort "You need to supply a bot channel name on the settings. key: :channel"
133
161
  end
134
162
 
135
-
136
-
137
163
  logfile = File.basename(config.rules_file.gsub("_rules_", "_logs_"), ".rb") + ".log"
138
164
  config.log_file = logfile
139
165
  @logger = Logger.new("#{config.path}/logs/#{logfile}")
140
166
  @last_respond = Time.now
141
-
142
- config_log = config.dup
167
+
168
+ #todo: consider putting this on a method
169
+ config_log = config.deep_copy
143
170
  config_log.delete(:token)
144
171
  config_log.delete(:user_token)
145
- @logger.info "Initializing bot: #{config_log.inspect}"
172
+ config_log.delete(:granular_token)
173
+ config_log.delete(:authorizations)
174
+ config_log.jira.password = "********" if config_log.key?(:jira) and config_log.jira.key?(:password)
175
+ config_log.github.token = "********" if config_log.key?(:github) and config_log.github.key?(:token)
176
+ config_log.public_holidays.api_key = "********" if config_log.key?(:public_holidays) and config_log.public_holidays.key?(:api_key)
177
+ config_log.encryption[:key] = "********" if config_log.key?(:encryption) and config_log.encryption[:key].to_s != ""
178
+ config_log.encryption.iv = "********" if config_log.key?(:encryption) and config_log.encryption.iv.to_s != ""
179
+ config_log.ai.open_ai.access_token = "********" if config_log.key?(:ai) and config_log.ai.key?(:open_ai) and config_log.ai.open_ai.key?(:access_token)
180
+ config_log.ai.open_ai.chat_gpt.access_token = "********" if config_log.key?(:ai) and config_log.ai.key?(:open_ai) and config_log.ai.open_ai.key?(:chat_gpt) and config_log.ai.open_ai.chat_gpt.key?(:access_token)
181
+ config_log.ai.open_ai.dall_e.access_token = "********" if config_log.key?(:ai) and config_log.ai.key?(:open_ai) and config_log.ai.open_ai.key?(:dall_e) and config_log.ai.open_ai.dall_e.key?(:access_token)
182
+ config_log.ai.open_ai.whisper.access_token = "********" if config_log.key?(:ai) and config_log.ai.key?(:open_ai) and config_log.ai.open_ai.key?(:whisper) and config_log.ai.open_ai.whisper.key?(:access_token)
183
+ config_log.ai.open_ai.models.access_token = "********" if config_log.key?(:ai) and config_log.ai.key?(:open_ai) and config_log.ai.open_ai.key?(:models) and config_log.ai.open_ai.models.key?(:access_token)
184
+ config_log.ldap.auth.password = "********" if config_log.key?(:ldap) and config_log.ldap.key?(:auth) and config_log.ldap.auth.key?(:password)
185
+ @config_log = config_log.deep_copy
186
+ @logger.info "Initializing bot: #{@config_log.inspect}"
146
187
 
147
188
  File.new("#{config.path}/buffer.log", "w") if config[:testing] and config.on_master_bot
148
189
  File.new("#{config.path}/buffer_complete.log", "w") if config[:simulate] and config.on_master_bot
149
190
 
150
191
  self.config = config
192
+
151
193
  save_status :off, :initializing, "Initializing bot: #{config_log.inspect}"
152
194
 
153
195
  unless config.simulate and config.key?(:client)
@@ -163,10 +205,26 @@ class SlackSmartBot
163
205
  @logger.fatal "*" * 50
164
206
  @logger.fatal "Rescued on creation client_user: #{e.inspect}"
165
207
  self.client_user = nil
208
+ abort "Rescued on creation client_user. You need to supply a valid user_token key on the settings. key: :user_token.\n#{e.inspect}"
166
209
  end
167
210
  else
168
211
  self.client_user = nil
169
212
  end
213
+
214
+ unless (config.simulate and config.key?(:client)) or config.granular_token.nil? or config.granular_token.empty?
215
+ begin
216
+ self.client_granular = Slack::Web::Client.new(token: config.granular_token)
217
+ self.client_granular.auth_test
218
+ rescue Exception => e
219
+ @logger.fatal "*" * 50
220
+ @logger.fatal "Rescued on creation client_granular: #{e.inspect}"
221
+ self.client_granular = nil
222
+ abort "Rescued on creation client_granular. You need to supply a valid granular_token key on the settings. key: :granular_token.\n#{e.inspect}"
223
+ end
224
+ else
225
+ self.client_granular = nil
226
+ end
227
+
170
228
  restarts = 0
171
229
  created = false
172
230
  while restarts < 200 and !created
@@ -202,7 +260,25 @@ class SlackSmartBot
202
260
  end
203
261
  end
204
262
 
263
+ if config.team_id_masters.empty?
264
+ config.masters.each_with_index do |name, i|
265
+ if name.match?(/^[A-Z0-9]{7,11}_/)
266
+ config.team_id_masters << name
267
+ config.masters[i] = name.split("_")[1..-1].join("_")
268
+ else
269
+ config.team_id_masters << "#{config.team_id}_#{name}"
270
+ end
271
+ end
272
+ else
273
+ config.masters = []
274
+ config.team_id_masters.each_with_index do |tid_name, i|
275
+ name = tid_name.split("_")[1..-1].join("_")
276
+ config.masters << name
277
+ end
278
+ end
279
+
205
280
  @listening = Hash.new()
281
+ @listening[:threads] = Hash.new() #[thread_ts] => channel_id
206
282
 
207
283
  @bots_created = Hash.new()
208
284
  @shortcuts = Hash.new()
@@ -217,30 +293,57 @@ class SlackSmartBot
217
293
  @announcements = Hash.new()
218
294
  @shares = Hash.new()
219
295
  @last_status_change = Time.now
220
- @vacations_check = (Date.today - 1)
296
+ @vacations_check = (Time.now - 3600).strftime("%Y%m%d%H")
221
297
  @announcements_activity_after = Hash.new()
222
298
  @public_holidays = Hash.new()
223
299
  @loops = Hash.new()
300
+ @ai_gpt ||= {}
301
+ @active_chat_gpt_sessions = Hash.new()
302
+ @chat_gpt_collaborating = Hash.new()
303
+ @open_ai = Hash.new()
304
+ @open_ai_models = []
305
+ @slack_bots = Hash.new()
306
+
307
+ @ldap = nil
308
+ begin
309
+ if config.ldap.key?(:host) and config.ldap[:host].to_s != ""
310
+ require 'net/ldap'
311
+ if config.ldap.key?(:auth) and config.ldap[:auth].key?(:user) and config.ldap[:auth][:user].to_s != ""
312
+ @ldap = Net::LDAP.new(host: config.ldap.host, port: config.ldap.port, auth: config.ldap.auth)
313
+ else
314
+ @ldap = Net::LDAP.new(host: config.ldap.host, port: config.ldap.port)
315
+ end
316
+ end
317
+ rescue Exception => e
318
+ @logger.fatal "Rescued on creation ldap: #{e.inspect}"
319
+ @ldap = nil
320
+ end
321
+
322
+ if (config.on_master_bot and !File.exist?("#{config.path}/status/version.txt")) or
323
+ (config.on_master_bot and File.exist?("#{config.path}/status/version.txt") and
324
+ Gem::Version.new(File.read("#{config.path}/status/version.txt").to_s) <= Gem::Version.new("1.15.0"))
325
+ upgrade_to_use_team_ids()
326
+ end
224
327
 
225
- if File.exist?("#{config.path}/shortcuts/#{config.shortcuts_file}".gsub('.yaml','.rb')) #backwards compatible
226
- file_conf = IO.readlines("#{config.path}/shortcuts/#{config.shortcuts_file}".gsub('.yaml','.rb')).join
328
+ if File.exist?("#{config.path}/shortcuts/#{config.shortcuts_file}".gsub(".yaml", ".rb")) #backwards compatible
329
+ file_conf = IO.readlines("#{config.path}/shortcuts/#{config.shortcuts_file}".gsub(".yaml", ".rb")).join
227
330
  if file_conf.to_s() == ""
228
331
  @shortcuts = {}
229
332
  else
230
333
  @shortcuts = eval(file_conf)
231
334
  end
232
- File.open("#{config.path}/shortcuts/#{config.shortcuts_file}", 'w') {|file| file.write(@shortcuts.to_yaml) }
233
- File.delete("#{config.path}/shortcuts/#{config.shortcuts_file}".gsub('.yaml','.rb'))
335
+ File.open("#{config.path}/shortcuts/#{config.shortcuts_file}", "w") { |file| file.write(@shortcuts.to_yaml) }
336
+ File.delete("#{config.path}/shortcuts/#{config.shortcuts_file}".gsub(".yaml", ".rb"))
234
337
  elsif File.exist?("#{config.path}/shortcuts/#{config.shortcuts_file}")
235
338
  @shortcuts = YAML.load(File.read("#{config.path}/shortcuts/#{config.shortcuts_file}"))
236
339
  end
237
- if File.exist?("#{config.path}/shortcuts/shortcuts_global.rb") #backwards compatible
340
+ if File.exist?("#{config.path}/shortcuts/shortcuts_global.rb") #backwards compatible
238
341
  file_sc = IO.readlines("#{config.path}/shortcuts/shortcuts_global.rb").join
239
342
  @shortcuts_global = {}
240
343
  unless file_sc.to_s() == ""
241
344
  @shortcuts_global = eval(file_sc)
242
345
  end
243
- File.open("#{config.path}/shortcuts/shortcuts_global.yaml", 'w') {|file| file.write(@shortcuts_global.to_yaml) }
346
+ File.open("#{config.path}/shortcuts/shortcuts_global.yaml", "w") { |file| file.write(@shortcuts_global.to_yaml) }
244
347
  File.delete("#{config.path}/shortcuts/shortcuts_global.rb")
245
348
  elsif File.exist?("#{config.path}/shortcuts/shortcuts_global.yaml")
246
349
  @shortcuts_global = YAML.load(File.read("#{config.path}/shortcuts/shortcuts_global.yaml"))
@@ -249,12 +352,12 @@ class SlackSmartBot
249
352
  get_routines()
250
353
  get_repls()
251
354
 
252
- if config.on_master_bot and (File.exist?(config.file_path.gsub(".rb", "_bots.rb")) or File.exist?(config.file_path.gsub('.rb', '_bots.yaml')))
355
+ if config.on_master_bot and (File.exist?(config.file_path.gsub(".rb", "_bots.rb")) or File.exist?(config.file_path.gsub(".rb", "_bots.yaml")))
253
356
  get_bots_created()
254
357
  if @bots_created.kind_of?(Hash) and config.start_bots
255
358
  @bots_created.each { |key, value|
256
359
  if !value.key?(:cloud) or (value.key?(:cloud) and value[:cloud] == false)
257
- if value.key?(:silent) and value.silent!=config.silent
360
+ if value.key?(:silent) and value.silent != config.silent
258
361
  silent = value.silent
259
362
  else
260
363
  silent = config.silent
@@ -280,24 +383,26 @@ class SlackSmartBot
280
383
  FileUtils.copy_file(default_general_commands, config.path + general_commands_file) unless File.exist?(config.path + general_commands_file)
281
384
 
282
385
  get_rules_imported()
283
-
284
386
  begin
285
387
  #todo: take in consideration the case that the value supplied on config.masters and config.admins are the ids and not the user names
286
388
  @admin_users_id = []
287
389
  @master_admin_users_id = []
288
- config.admins.each do |au|
390
+ config.team_id_admins.each do |au|
289
391
  user_info = get_user_info("@#{au}")
290
392
  @admin_users_id << user_info.user.id
291
- if config.masters.include?(au)
393
+ if config.team_id_masters.include?(au)
292
394
  @master_admin_users_id << user_info.user.id
293
395
  end
294
396
  sleep 1
295
397
  end
296
- (config.masters-config.admins).each do |au|
398
+ (config.team_id_masters - config.team_id_admins).each do |au|
297
399
  user_info = get_user_info("@#{au}")
298
- @master_admin_users_id << user_info.user.id
400
+ @master_admin_users_id << user_info.user.id unless user_info.nil?
299
401
  sleep 1
300
402
  end
403
+ @admin_users_id.uniq!
404
+ @master_admin_users_id.uniq!
405
+
301
406
  rescue Slack::Web::Api::Errors::TooManyRequestsError
302
407
  @logger.fatal "TooManyRequestsError"
303
408
  save_status :off, :TooManyRequestsError, "TooManyRequestsError please re run the bot and be sure of executing first: killall ruby"
@@ -337,6 +442,7 @@ class SlackSmartBot
337
442
  get_admins_channels()
338
443
  get_access_channels()
339
444
  get_vacations()
445
+ get_openai_sessions()
340
446
  get_personal_settings()
341
447
 
342
448
  if @routines.key?(@channel_id)
@@ -361,7 +467,7 @@ class SlackSmartBot
361
467
  @logger.info _data
362
468
  #save_status :off, :closing, "Connection closing, exiting." #todo: don't notify for the moment, remove when checked
363
469
  end
364
-
470
+
365
471
  client.on :closed do |_data|
366
472
  m = "Connection has been disconnected. #{Time.now}"
367
473
  @logger.info m
@@ -1,7 +1,8 @@
1
1
  # add here the general commands you will be using in any channel where The SmartBot is part of. Not necessary to use ! or ^, it will answer directly.
2
2
  def general_commands(user, command, dest, files = [])
3
-
3
+
4
4
  begin
5
+ team_id_user = "#{user.team_id}_#{user.name}"
5
6
  case command
6
7
 
7
8
  # help: ----------------------------------------------
@@ -12,7 +13,7 @@ def general_commands(user, command, dest, files = [])
12
13
  # help: It will send a big empty message.
13
14
  # help: NUMBER (optional): number of lines. Default 100. Max 200.
14
15
  # help: command_id: :cls
15
- # help:
16
+ # help:
16
17
  when /\A\s*(\d*)\s*(clear|cls|clear\s+screen)\s*\z/i
17
18
  save_stats :cls
18
19
  $1.to_s == '' ? lines = 100 : lines = $1.to_i
@@ -24,12 +25,12 @@ def general_commands(user, command, dest, files = [])
24
25
  # help: `INTEGER blink TEXT`
25
26
  # help: It will blink the text supplied. One or more lines of text. Emoticons or text format are allowed.
26
27
  # help: INTEGER (optional): number of times. Default 50. Max 200.
27
- # help: Examples:
28
+ # help: Examples:
28
29
  # help: blink Hello World!
29
30
  # help: blink :moneybag: Pay attention! *Sales* are published!
30
31
  # help: 100 blink :new: *Party is about to start* :i_love_you_hand_sign:
31
32
  # help: command_id: :blink
32
- # help:
33
+ # help:
33
34
  when /\A\s*(\d+)?\s*blink\s+(.+)\s*\z/im
34
35
  save_stats :blink
35
36
  num_times = $1.to_s == '' ? 50 : $1.to_i
@@ -37,12 +38,12 @@ def general_commands(user, command, dest, files = [])
37
38
  @blinking ||= []
38
39
  if num_times > 200 or num_times < 1
39
40
  respond "The number of times must be between 1 and 200"
40
- elsif @blinking.include?(user.name)
41
+ elsif @blinking.include?(team_id_user)
41
42
  respond "I'm already blinking something for you. Please wait until I finish"
42
43
  elsif @blinking.size >= 3 # rate limit in theory update can be done only once per second
43
44
  respond "I'm already blinking something for too many people. Please wait until I finish at least one of them."
44
45
  else
45
- @blinking << user.name
46
+ @blinking << team_id_user
46
47
  msg = respond(text, return_message: true)
47
48
  num_times.times do
48
49
  sleep 2
@@ -50,7 +51,7 @@ def general_commands(user, command, dest, files = [])
50
51
  sleep 0.5
51
52
  update(dest, msg.ts, text)
52
53
  end
53
- @blinking.delete(user.name)
54
+ @blinking.delete(team_id_user)
54
55
  end
55
56
 
56
57
  # this is a hidden command that it is not listed when calling bot help
@@ -58,7 +59,7 @@ def general_commands(user, command, dest, files = [])
58
59
  save_stats :thanks
59
60
  reactions = [:heart, :heart_eyes, :blush, :relaxed, :simple_smile, :smiley, :two_hearts, :heartbeat, :green_heart ]
60
61
  reactions.sample(rand(3)+1).each {|rt| react rt }
61
- responses = ['Thank YOU', "You're welcome", "You're very welcome", 'No problem', 'No worries', "Don't mention it", 'My pleasure',
62
+ responses = ['Thank YOU', "You're welcome", "You're very welcome", 'No problem', 'No worries', "Don't mention it", 'My pleasure',
62
63
  'Anytime', 'It was the least I could do', 'Glad to help', 'Sure', 'Pleasure', 'The pleasure is mine', 'It was nothing', 'Much obliged', "I'm happy to help",
63
64
  'Það var ekkert', 'De nada', 'No hay de qué', 'De rien', 'Bitte', 'Prego', 'मेरा सौभाग्य है', '不客氣', 'Παρακαλώ']
64
65
  respond "#{responses.sample}#{'!'*rand(4)}"
@@ -91,4 +92,4 @@ def general_commands(user, command, dest, files = [])
91
92
  end
92
93
  return false
93
94
  end
94
- end
95
+ end
data/whats_new.txt CHANGED
@@ -1,20 +1,37 @@
1
- *Version 1.14.1* Released 2023-Apr-13
1
+ *Version 1.15.0* Released 2024-Apr-04
2
2
 
3
3
  *For General users*
4
- - `team TEAM_NAME memo ID MESSAGE`, `TEAM_NAME team memo ID MESSAGE` It will add a comment to the memo ID specified on the team specified. (<https://github.com/MarioRuiz/slack-smart-bot/issues/95|#95>).
5
- - `team TEAM_NAME memo ID`, `TEAM_NAME team memo ID` It will show the memo ID specified on the team specified along with all the comments. (<https://github.com/MarioRuiz/slack-smart-bot/issues/96|#96>)
6
- - When on a repl it is possible to get the documentation and source code of any method: `doc METHOD`, `source METHOD`, `code METHOD`. Examples: `doc Operations.list`, `code World::API::Time.get` (<https://github.com/MarioRuiz/slack-smart-bot/issues/98|#98>)
7
- - Deleted memos will be accessible by the creator (<https://github.com/MarioRuiz/slack-smart-bot/issues/100|#100>)
8
- - `calendar COUNTRY`, `calendar COUNTRY/STATE`, `calendar COUNTRY/STATE DATE` will display the calendar of public holidays. (<https://github.com/MarioRuiz/slack-smart-bot/issues/102|#102>)
9
- - Display public holidays for every member on vacation team calendar (<https://github.com/MarioRuiz/slack-smart-bot/issues/103|#103>)
10
- - OpenAI integration for personal results. ChatGPT, Whisper, Image generation, Image Variations and Image Edition (<https://github.com/MarioRuiz/slack-smart-bot/issues/101|#101>)
11
- - New `set personal settings` command (<https://github.com/MarioRuiz/slack-smart-bot/issues/105|#105>)
4
+ - OpenAI adds specific hosts and access tokens for services. (<https://github.com/MarioRuiz/slack-smart-bot/issues/107|#107>).
5
+ - ChatGPT command improvements. (<https://github.com/MarioRuiz/slack-smart-bot/issues/108|#108>)
6
+ - ChatGPT sharing sessions. (<https://github.com/MarioRuiz/slack-smart-bot/issues/110|#110>)
7
+ - ChatGPT tags for sessions. (<https://github.com/MarioRuiz/slack-smart-bot/issues/111|#111>)
8
+ - ChatGPT can be called inside a REPL to help you create scripts. (<https://github.com/MarioRuiz/slack-smart-bot/issues/112|#112>)
9
+ - ChatGPT has access to internet. If a prompt includes `!URL`, SmartBot will download the supplied website text and add it to the prompt. (<https://github.com/MarioRuiz/slack-smart-bot/issues/113|#113>)
10
+ - ChatGPT deletes all prompts from the session when on a Thread and sending `??` (<https://github.com/MarioRuiz/slack-smart-bot/issues/115|#115>)
11
+ - ChatGPT to use a different model when starting a temporary session: `^?? use model MODEL_NAME` (<https://github.com/MarioRuiz/slack-smart-bot/issues/114|#114>)
12
+ - ChatGPT when on a session if we want to change the model send `use model MODEL_NAME` (<https://github.com/MarioRuiz/slack-smart-bot/issues/114|#114>)
13
+ - ChatGPT To send the results of a SmartBot command as input for a ChatGPT session, use `COMMAND ?? PROMPT`. Example: `bot help ?? how can I use the time off commands`. (<https://github.com/MarioRuiz/slack-smart-bot/issues/116|#116>)
14
+ - ChatGPT To copy the prompts from the temporary session: `chatgpt copy temporary session NEW_SESSION_NAME` (<https://github.com/MarioRuiz/slack-smart-bot/issues/125|#125>)
15
+ - Time Off shows the day of today differently from other days. (<https://github.com/MarioRuiz/slack-smart-bot/issues/118|#118>)
16
+ - Teams It is possible to filter by topic, for example: `sales team dev` (<https://github.com/MarioRuiz/slack-smart-bot/issues/118|#124>)
17
+ - All users in the channel will be able to use SmartBot even though they are not workspace members. (<https://github.com/MarioRuiz/slack-smart-bot/issues/123|#123>)
18
+ - SmartBot can be called from Slack Workflows or Slack apps (<https://github.com/MarioRuiz/slack-smart-bot/issues/122|#122>)
19
+ - New `recap` command. It will show a recap of the channel. If channel not supplied, it will show the recap of the current channel (<https://github.com/MarioRuiz/slack-smart-bot/issues/126|#126>)
20
+ - New `summarize` command. It will summarize using ChatGPT the messages in the channel since the date specified (<https://github.com/MarioRuiz/slack-smart-bot/issues/127|#127>)
21
+ - Call `get smartbot readme` to get the README for SmartBot. (<https://github.com/MarioRuiz/slack-smart-bot/issues/128|#128>)
22
+ - You can call `<bot help` or `<bot rules`. SmartBot will send a Markdown file with extended help commands. Open the file in a new window. You can install this extension in Google Chrome to view it: https://chromewebstore.google.com/detail/medapdbncneneejhbgcjceippjlfkmkg (<https://github.com/MarioRuiz/slack-smart-bot/issues/129|#129>)
23
+ - It is possible to specify authorizations tokens for any website you want to reach the content using ChatGPT command. When adding to your prompt !URL SmartBot will verify if the domain specified have set an authorization header and add it. To add personal authorization tokens you can do it calling on a DM the commands: `add personal settings authorizations.example.host example.com`, `add personal settings authorizations.example.authorization Xdddj33a_SDFBBBS33Ab` (<https://github.com/MarioRuiz/slack-smart-bot/issues/130|#130>)
12
24
 
13
25
  *For Admin users*
14
- - New 'silent' option for command `exit bot silent`. It won't notify the SmartBot channels. (<https://github.com/MarioRuiz/slack-smart-bot/issues/93|#93>).
15
- - Show graph for `bot stats` when specifying option `graph` and grouping by daily, weekly, monthly or yearly. (<https://github.com/MarioRuiz/slack-smart-bot/issues/97|#97>).
16
- - config.encrypt (Boolean) By default will be set to true. (<https://github.com/MarioRuiz/slack-smart-bot/issues/99|#99>)
26
+ - ChatGPT now supports Azure. (<https://github.com/MarioRuiz/slack-smart-bot/issues/109|#109>)
27
+ - Bot Stats will store also the team of the user.
28
+ - Added granular_token to SmartBot settings. Supply the Slack token of your granular App.
29
+ - New config added to lib/slack/smart-bot/config.rb
30
+ - It is possible now to use LLM proxy for ChatGPT and also specify a concrete end point to get the models.
31
+ - Added LDAP settings in config. (<https://github.com/MarioRuiz/slack-smart-bot/issues/131|#131>)
32
+ - Added authorizations settings in config. (<https://github.com/MarioRuiz/slack-smart-bot/issues/130|#130>)
17
33
 
18
34
  ------------------------------
19
35
 
20
- *Previous*: <https://github.com/MarioRuiz/slack-smart-bot/blob/644c0882dec62281d5961d7565f6aba001be8d91/whats_new.txt|1.13.2>
36
+ *Previous*: <https://github.com/MarioRuiz/slack-smart-bot/blob/3a4b369d6d32316690e873309eb0758511a2c513/whats_new.txt|1.14.2>
37
+