slack-smart-bot 1.15.1 → 1.15.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -1
  3. data/img/chat_gpt_attach_image.png +0 -0
  4. data/lib/slack/smart-bot/ai/open_ai/models.rb +19 -10
  5. data/lib/slack/smart-bot/ai/open_ai/send_gpt_chat.rb +14 -10
  6. data/lib/slack/smart-bot/comm/dont_understand.rb +23 -6
  7. data/lib/slack/smart-bot/comm/get_user_info.rb +9 -10
  8. data/lib/slack/smart-bot/comm/respond.rb +56 -28
  9. data/lib/slack/smart-bot/comm/send_file.rb +17 -6
  10. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat.rb +885 -129
  11. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_add_collaborator.rb +3 -3
  12. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_copy_session.rb +132 -15
  13. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_delete_session.rb +1 -1
  14. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_get_prompts.rb +50 -12
  15. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_list_sessions.rb +99 -34
  16. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_share_session.rb +12 -2
  17. data/lib/slack/smart-bot/commands/general/ai/open_ai/open_ai_chat_use_model.rb +36 -25
  18. data/lib/slack/smart-bot/commands/general/bot_help.rb +29 -24
  19. data/lib/slack/smart-bot/commands/general/poster.rb +0 -1
  20. data/lib/slack/smart-bot/commands/general/see_announcements.rb +1 -1
  21. data/lib/slack/smart-bot/commands/general/summarize.rb +22 -8
  22. data/lib/slack/smart-bot/commands/general_bot_commands.rb +156 -55
  23. data/lib/slack/smart-bot/commands/on_bot/general/bot_stats.rb +13 -11
  24. data/lib/slack/smart-bot/commands/on_extended/bot_rules.rb +21 -17
  25. data/lib/slack/smart-bot/commands/on_master/admin_master/exit_bot.rb +2 -2
  26. data/lib/slack/smart-bot/commands.rb +19 -19
  27. data/lib/slack/smart-bot/process_first.rb +38 -35
  28. data/lib/slack/smart-bot/treat_message.rb +57 -56
  29. data/lib/slack/smart-bot/utils/download_http_content.rb +91 -0
  30. data/lib/slack/smart-bot/utils/get_authorizations.rb +41 -0
  31. data/lib/slack/smart-bot/utils/get_keywords.rb +33 -0
  32. data/lib/slack/smart-bot/utils/get_openai_sessions.rb +46 -6
  33. data/lib/slack/smart-bot/utils/get_teams.rb +9 -1
  34. data/lib/slack/smart-bot/utils/save_stats.rb +13 -5
  35. data/lib/slack/smart-bot/utils/transform_to_slack_markdown.rb +36 -0
  36. data/lib/slack/smart-bot/utils/update_openai_sessions.rb +9 -4
  37. data/lib/slack/smart-bot/utils.rb +48 -44
  38. data/lib/slack-smart-bot.rb +10 -9
  39. data/whats_new.txt +27 -1
  40. metadata +63 -2
@@ -1,46 +1,45 @@
1
1
  class SlackSmartBot
2
-
3
- def bot_help(user, from, dest, dchannel, specific, help_command, rules_file, savestats: true, strict: false, send_to_file: false)
2
+ def bot_help(user, from, dest, dchannel, specific, help_command, rules_file, savestats: true, strict: false, send_to_file: false, return_output: false)
4
3
  save_stats(__method__) if savestats
5
4
  output = []
6
5
  if has_access?(__method__, user)
7
6
  help_found = false
8
7
  message = ""
9
- if help_command.to_s != ''
10
- help_command = '' if help_command.to_s.match?(/^\s*expanded\s*$/i) or help_command.to_s.match?(/^\s*extended\s*$/i)
8
+ if help_command.to_s != ""
9
+ help_command = "" if help_command.to_s.match?(/^\s*expanded\s*$/i) or help_command.to_s.match?(/^\s*extended\s*$/i)
11
10
  expanded = true
12
- message_not_expanded = ''
11
+ message_not_expanded = ""
13
12
  else
14
13
  expanded = false
15
14
  message_not_expanded = "If you want to see the *expanded* version of *`bot help`* or *`bot rules`*, please call *`bot help expanded`* or *`bot rules expanded`*\n"
16
15
  message_not_expanded += "Also to get specific *expanded* help for a specific command or rule call *`bot help COMMAND`*\n"
17
16
  end
18
- expanded = true if Thread.current[:prompt].to_s != ''
17
+ expanded = true if Thread.current[:prompt].to_s != ""
19
18
  if send_to_file
20
- expanded = true
21
- message_not_expanded = ''
19
+ expanded = true
20
+ message_not_expanded = ""
22
21
  end
23
22
  help_message = get_help(rules_file, dest, user, specific, expanded)
24
23
  commands = []
25
24
  commands_search = []
26
25
  if help_command.to_s != ""
27
26
  help_command.gsub!("?", "\\?") # for open ai commands
28
- help_message.gsub(/====+/,'-'*30).split(/^\s*-------*$/).each do |h|
27
+ help_message.gsub(/====+/, "-" * 30).split(/^\s*-------*$/).each do |h|
29
28
  if strict
30
- if h.match?(/`#{help_command}`/i) or h.match?(/^\s*command_id:\s+:#{help_command.gsub(' ', '_')}\s*$/)
29
+ if h.match?(/`#{help_command}`/i) or h.match?(/^\s*command_id:\s+:#{help_command.gsub(" ", "_")}\s*$/)
31
30
  output << h
32
31
  help_found = true
33
32
  commands << h
34
33
  break
35
34
  end
36
35
  else
37
- if h.match?(/[`_]#{help_command}/i) or h.match?(/^\s*command_id:\s+:#{help_command.gsub(' ', '_')}\s*$/)
36
+ if h.match?(/[`_]#{help_command}/i) or h.match?(/^\s*command_id:\s+:#{help_command.gsub(" ", "_")}\s*$/)
38
37
  output << h
39
38
  help_found = true
40
39
  commands << h
41
40
  elsif !h.match?(/\A\s*\*/) and !h.match?(/\A\s*=+/) #to avoid general messages for bot help *General commands...*
42
41
  all_found = true
43
- help_command.to_s.split(' ') do |hc|
42
+ help_command.to_s.split(" ") do |hc|
44
43
  unless hc.match?(/^\s*\z/)
45
44
  if !h.match?(/#{hc}/i)
46
45
  all_found = false
@@ -52,7 +51,7 @@ class SlackSmartBot
52
51
  end
53
52
  end
54
53
  else
55
- if Thread.current[:using_channel]!=''
54
+ if Thread.current[:using_channel] != ""
56
55
  message += "*You are using rules from another channel: <##{Thread.current[:using_channel]}>. These are the specific commands for that channel:*"
57
56
  end
58
57
  output << message
@@ -65,16 +64,16 @@ class SlackSmartBot
65
64
  end
66
65
  end
67
66
  if Thread.current[:typem] == :on_pg or Thread.current[:typem] == :on_pub
68
- if @bots_created.size>0
69
- txt = "\nThese are the *SmartBots* running on this Slack workspace: *<##{@master_bot_id}>, <##{@bots_created.keys.join('>, <#')}>*\n"
67
+ if @bots_created.size > 0
68
+ txt = "\nThese are the *SmartBots* running on this Slack workspace: *<##{@master_bot_id}>, <##{@bots_created.keys.join(">, <#")}>*\n"
70
69
  txt += "Join one channel and call *`bot rules`* to see specific commands for that channel or *`bot help`* to see all commands for that channel.\n"
71
70
  output << txt
72
71
  end
73
72
  end
74
73
  else
75
- if commands.size < 10 and help_command.to_s!='' and commands_search.size > 0
74
+ if commands.size < 10 and help_command.to_s != "" and commands_search.size > 0
76
75
  commands_search.shuffle!
77
- (10-commands.size).times do |n|
76
+ (10 - commands.size).times do |n|
78
77
  unless commands_search[n].nil?
79
78
  output << commands_search[n]
80
79
  help_found = true
@@ -114,22 +113,28 @@ class SlackSmartBot
114
113
  output << message_not_expanded
115
114
  end
116
115
  end
117
- if output.join("\n").lines.count > 50 and dest[0]!='D' and !send_to_file
116
+ if output.join("\n").lines.count > 50 and dest[0] != "D" and !send_to_file
118
117
  dest = :on_thread
119
- output.unshift('Since there are many lines returned the results are returned on a thread by default.')
118
+ output.unshift("Since there are many lines returned the results are returned on a thread by default.")
120
119
  end
121
120
  if send_to_file
122
121
  content = output.join("\n\n")
123
122
  content.gsub!(/\*<([^>]*)\|([^>]*)>\*/, '## [\2](\1)')
124
123
  content.gsub!(/^\s*(\*.+\*)\s*$/, '# \1')
125
- content.gsub!(/command_id:\s+:/, '### :')
126
- content = content.gsub("\n", " \n").gsub(/\|[\w\s]*>/i,">").gsub(/^\s*\-\-\-\-\-\-/, "\n------")
124
+ content.gsub!(/command_id:\s+:/, "### :")
125
+ content = content.gsub("\n", " \n").gsub(/\|[\w\s]*>/i, ">").gsub(/^\s*\-\-\-\-\-\-/, "\n------")
127
126
  dest == :on_thread ? dest_file = dchannel : dest_file = dest
128
- send_file(dest_file, "SmartBot Help", "", 'smartbot_help.md', "text/markdown", "markdown", content: content)
127
+ send_file(dest_file, "SmartBot Help", "", "smartbot_help.md", "text/markdown", "markdown", content: content)
128
+ elsif return_output
129
+ output.each do |h|
130
+ h.gsub!(/^\s*command_id:\s+:\w+\s*$/, "")
131
+ h.gsub!(/^\s*>.+$/, "") if help_command.to_s != ""
132
+ end
133
+ return output
129
134
  else
130
135
  output.each do |h|
131
- msg = h.gsub(/^\s*command_id:\s+:\w+\s*$/,'')
132
- msg.gsub!(/^\s*>.+$/,'') if help_command.to_s != ''
136
+ msg = h.gsub(/^\s*command_id:\s+:\w+\s*$/, "")
137
+ msg.gsub!(/^\s*>.+$/, "") if help_command.to_s != ""
133
138
  unless msg.match?(/\A\s*\z/)
134
139
  respond msg, dest, unfurl_links: false, unfurl_media: false
135
140
  end
@@ -1,5 +1,4 @@
1
1
  class SlackSmartBot
2
-
3
2
  def poster(permanent, emoticon_text, emoticon_bg, string, minutes)
4
3
  chars = { a: ["0000", "1111", "1001", "1111", "1001", "1001", "0000"],
5
4
  b: ["0000", "1111", "1001", "1110", "1001", "1111", "0000"],
@@ -100,7 +100,7 @@ class SlackSmartBot
100
100
  end
101
101
  end
102
102
  else
103
- if typem == :on_dm and channel_id[0]=='D'
103
+ if typem == :on_dm and !channel_id.nil? and channel_id[0]=='D'
104
104
  respond("There are no announcements#{general_message}", dest) unless type == 'all'
105
105
  else
106
106
  respond("There are no announcements for <##{channel_id}>#{general_message}", dest) unless publish or type == 'all' or (typem==:on_dm and channel_id[0]!='D' and !see_announcements_on_demand)
@@ -41,7 +41,7 @@ class SlackSmartBot
41
41
  if @vacations.key?(user.team_id_user) and @vacations[user.team_id_user].key?(:periods)
42
42
  @vacations[user.team_id_user].periods.each do |p|
43
43
  #get the last from date
44
- if p.from > from[0..9].gsub("-", "/")
44
+ if p.from > from[0..9].gsub("-", "/") and p.from < Time.now.to_s[0..9].gsub("-", "/")
45
45
  from = p.from
46
46
  from_time_off = true
47
47
  end
@@ -127,12 +127,16 @@ class SlackSmartBot
127
127
  chatgpt = ai_conn[user.team_id_user].chat_gpt
128
128
  models = ai_models_conn[user.team_id_user].models
129
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"
130
+ prompt_orig = "Could you please provide a summary of the given conversation, including all key points and supporting details?\n"
131
+ prompt_orig += "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.\n"
132
+ prompt_orig += "To ensure accuracy, please read the text carefully and pay attention to any nuances or complexities in the language.\n"
133
+ prompt_orig += "Please also add the most important conversations in the summary.\n"
134
+ prompt_orig += "Additionally, the summary should avoid any personal biases or interpretations and remain objective and factual throughout.\n"
131
135
  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"
136
+ prompt_orig += "Add the link to the message so it is easy to find it.\n"
134
137
  prompt_orig += "Add also the date of the message for relevant conversations.\n"
135
138
  prompt_orig += "This is the conversation:\n"
139
+
136
140
  #sort by year/month from older to newer
137
141
  messages = messages.sort_by { |k, v| k }.to_h
138
142
 
@@ -145,26 +149,36 @@ class SlackSmartBot
145
149
  else
146
150
  max_num_tokens = 8000
147
151
  end
148
- num_tokens = OpenAI.rough_token_count(prompt_orig + messages.values.flatten.join)
152
+ #num_tokens = OpenAI.rough_token_count(prompt_orig + messages.values.flatten.join) #jal
153
+ #enc = Tiktoken.encoding_for_model(chatgpt.smartbot_model)
154
+ enc = Tiktoken.encoding_for_model("gpt-4") #jal todo: fixed value since version 0.0.8 and 0.0.9 failed to install on SmartBot VM. Revert when fixed.
155
+ num_tokens = enc.encode(prompt_orig + messages.values.flatten.join).length
156
+
149
157
  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
158
 
151
159
  prompts = []
152
160
  i = 0
153
161
  messages.each do |year_month, msgs|
154
162
  msgs.each do |msg|
155
- num_tokens = OpenAI.rough_token_count(prompts[i].to_s + msg)
163
+ #num_tokens = OpenAI.rough_token_count(prompts[i].to_s + msg) #jal
164
+ #enc = Tiktoken.encoding_for_model(chatgpt.smartbot_model)
165
+ enc = Tiktoken.encoding_for_model("gpt-4") #jal todo: fixed value since version 0.0.8 and 0.0.9 failed to install on SmartBot VM. Revert when fixed.
166
+ num_tokens = enc.encode(prompts[i].to_s + msg).length
156
167
  i += 1 if num_tokens > max_num_tokens
157
168
  prompts[i] ||= prompt_orig
158
169
  prompts[i] += "#{msg}\n"
159
170
  end
160
171
  end
161
172
  prompts.each_with_index do |prompt, i|
162
- num_tokens = OpenAI.rough_token_count(prompt)
173
+ #num_tokens = OpenAI.rough_token_count(prompt)
174
+ #enc = Tiktoken.encoding_for_model(chatgpt.smartbot_model)
175
+ enc = Tiktoken.encoding_for_model("gpt-4") #jal todo: fixed value since version 0.0.8 and 0.0.9 failed to install on SmartBot VM. Revert when fixed.
176
+ num_tokens = enc.encode(prompt).length
163
177
  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
178
  success, res = SlackSmartBot::AI::OpenAI.send_gpt_chat(chatgpt.client, chatgpt.smartbot_model, prompt, chatgpt)
165
179
  result_messages = []
166
180
  if success
167
- result_messages << "*ChatGPT:*\n#{res}"
181
+ result_messages << "*ChatGPT:*\n#{transform_to_slack_markdown(res.to_s.strip)}"
168
182
  else
169
183
  result_messages << "*ChatGPT:*\nI'm sorry, I couldn't summarize the conversation. This is the issue: #{res}"
170
184
  end
@@ -45,48 +45,48 @@ class SlackSmartBot
45
45
  when /^\A*(get\s+)?smartbot\s+readme\s*\z/i, /^\A*readme\s+smartbot\s*\z/i
46
46
  get_smartbot_readme(dest)
47
47
 
48
- # help: ----------------------------------------------
49
- # help: `for NUMBER times every NUMBER minutes COMMAND`
50
- # help: `for NUMBER times every NUMBER seconds COMMAND`
51
- # help: `NUMBER times every NUMBER minutes COMMAND`
52
- # help: `NUMBER times every NUMBER seconds COMMAND`
53
- # help: It will run the command every NUMBER minutes or seconds for NUMBER times.
54
- # help: max 24 times. min every 10 seconds. max every 60 minutes.
55
- # help: Call `quit loop LOOP_ID` to stop the loop.
56
- # help: aliases for minutes: m, minute, minutes
57
- # help: aliases for seconds: s, sc, second, seconds
58
- # help: Examples:
59
- # help: _for 5 times every 1 minute ^ruby puts Time.now_
60
- # help: _10 times every 30s !ruby puts Time.now_
61
- # help: _24 times every 60m !get sales today_
62
- # help: <https://github.com/MarioRuiz/slack-smart-bot#loops|more info>
63
- # help: command_id: :create_loop
64
- # help:
48
+ # help: ----------------------------------------------
49
+ # help: `for NUMBER times every NUMBER minutes COMMAND`
50
+ # help: `for NUMBER times every NUMBER seconds COMMAND`
51
+ # help: `NUMBER times every NUMBER minutes COMMAND`
52
+ # help: `NUMBER times every NUMBER seconds COMMAND`
53
+ # help: It will run the command every NUMBER minutes or seconds for NUMBER times.
54
+ # help: max 24 times. min every 10 seconds. max every 60 minutes.
55
+ # help: Call `quit loop LOOP_ID` to stop the loop.
56
+ # help: aliases for minutes: m, minute, minutes
57
+ # help: aliases for seconds: s, sc, second, seconds
58
+ # help: Examples:
59
+ # help: _for 5 times every 1 minute ^ruby puts Time.now_
60
+ # help: _10 times every 30s !ruby puts Time.now_
61
+ # help: _24 times every 60m !get sales today_
62
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#loops|more info>
63
+ # help: command_id: :create_loop
64
+ # help:
65
65
 
66
- # help: ----------------------------------------------
67
- # help: `quit loop LOOP_ID`
68
- # help: It will stop the loop with the id LOOP_ID.
69
- # help: Only the user who created the loop or an admin can stop it.
70
- # help: aliases for loop: iterator, iteration
71
- # help: aliases for quit: stop, exit, kill
72
- # help: Examples:
73
- # help: _quit loop 1_
74
- # help: _stop iterator 12_
75
- # help: <https://github.com/MarioRuiz/slack-smart-bot#loops|more info>
76
- # help: command_id: :quit_loop
77
- # help:
66
+ # help: ----------------------------------------------
67
+ # help: `quit loop LOOP_ID`
68
+ # help: It will stop the loop with the id LOOP_ID.
69
+ # help: Only the user who created the loop or an admin can stop it.
70
+ # help: aliases for loop: iterator, iteration
71
+ # help: aliases for quit: stop, exit, kill
72
+ # help: Examples:
73
+ # help: _quit loop 1_
74
+ # help: _stop iterator 12_
75
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#loops|more info>
76
+ # help: command_id: :quit_loop
77
+ # help:
78
78
 
79
- # help: ----------------------------------------------
80
- # help: `Hi Bot`
81
- # help: `Hi Smart`
82
- # help: `Hello Bot` `Hola Bot` `Hallo Bot` `What's up Bot` `Hey Bot` `Hæ Bot`
83
- # help: `Hello THE_NAME_OF_THE_BOT`
84
- # help: Bot starts listening to you if you are on a Bot channel
85
- # help: After that if you want to avoid a single message to be treated by the smart bot, start the message by -
86
- # help: Also apart of Hello you can use _Hallo, Hi, Hola, What's up, Hey, Hæ_
87
- # help: <https://github.com/MarioRuiz/slack-smart-bot#how-to-access-the-smart-bot|more info>
88
- # help: command_id: :hi_bot
89
- # help:
79
+ # help: ----------------------------------------------
80
+ # help: `Hi Bot`
81
+ # help: `Hi Smart`
82
+ # help: `Hello Bot` `Hola Bot` `Hallo Bot` `What's up Bot` `Hey Bot` `Hæ Bot`
83
+ # help: `Hello THE_NAME_OF_THE_BOT`
84
+ # help: Bot starts listening to you if you are on a Bot channel
85
+ # help: After that if you want to avoid a single message to be treated by the smart bot, start the message by -
86
+ # help: Also apart of Hello you can use _Hallo, Hi, Hola, What's up, Hey, Hæ_
87
+ # help: <https://github.com/MarioRuiz/slack-smart-bot#how-to-access-the-smart-bot|more info>
88
+ # help: command_id: :hi_bot
89
+ # help:
90
90
  when /\A\s*(Hello|Hallo|Hi|Hola|What's\sup|Hey|Hæ)\s+(#{@salutations.join("|")})\s*$/i
91
91
  hi_bot(user, dest, user.name, display_name)
92
92
 
@@ -122,7 +122,7 @@ class SlackSmartBot
122
122
  # help: command_id: :poster
123
123
  # help:
124
124
  when /\A()poster\s+(\d+m\s+)?(:[^:]+:)\s+(:[^:]+:)(.+)\s*\z/i, /\A()poster\s+(\d+m\s+)?(:.+:)\s+()(.+)\s*\z/i, /\A()poster\s+(\d+m\s+)?()()(.+)\s*\z/i,
125
- /\A(p)poster\s+()(:[^:]+:)\s+(:[^:]+:)(.+)\s*\z/i, /\A(p)poster\s+()(:.+:)\s+()(.+)\s*\z/i, /\A(p)poster\s+()()()(.+)\s*\z/i
125
+ /\A(p)poster\s+()(:[^:]+:)\s+(:[^:]+:)(.+)\s*\z/i, /\A(p)poster\s+()(:.+:)\s+()(.+)\s*\z/i, /\A(p)poster\s+()()()(.+)\s*\z/i
126
126
  permanent = $1.to_s != ""
127
127
  minutes = $2.to_s
128
128
  emoticon_text = $3
@@ -957,6 +957,15 @@ class SlackSmartBot
957
957
  # help: If ?? is used, it will start from zero the temporary session. If not all the previous prompts from the session will be used to generate the response.
958
958
  # help: You can share a message and use it as input for the supplied PROMPT.
959
959
  # help: If you include in the prompt !URL then it will download and use the content of the URL as input for the prompt.
960
+ # help: If the URL is a Google Drive link, it will download the content of the file.
961
+ # help: If the URL is a PDF, DOCX or text file (txt, json, yaml...) SmartBot will extract the content of the file and use it as input for the prompt.
962
+ # help: Add static content to your session by using `add static content URL1 URL99`. The content will be downloaded and added to your session only once.
963
+ # help: Add live content to your session by using `add live content URL1 URL99`. The content will be downloaded and added to your session every time you send a new prompt.
964
+ # help: Add messages from a channel to your session by using `add history #CHANNEL_NAME`.
965
+ # help: You can add authorizations on a specific ChatGPT session: `add authorization HOST HEADER VALUE`, for example: `add authorization api.example.com Authorization Bearer 123456`.
966
+ # help: You can attach any image to the message and it will be used as input for the prompt.
967
+ # help: If you want to delete the last ChatGPT response and send again last prompt, you can use `resend prompt`.
968
+ # help: You can set the context of your ChatGPT session: `set context CONTEXT`. Example: `set context You are a funny comedian who tells dad jokes. The output should be in JSON format.`
960
969
  # help: MODEL_NAME can be a substring of the model name, SmartBot will search the model name that contains the substring supplied.
961
970
  # help: When "DESCRIPTION" is used it will be used as a description for the session.
962
971
  # help: If a previous DESCRIPTION for the session exists it will be replaced.
@@ -975,6 +984,14 @@ class SlackSmartBot
975
984
  # help: command_id: :open_ai_chat
976
985
  # help: command_id: :open_ai_chat_add_collaborator
977
986
  # help: command_id: :open_ai_chat_use_model
987
+ # help: command_id: :open_ai_chat_add_live_content
988
+ # help: command_id: :open_ai_chat_add_static_content
989
+ # help: command_id: :open_ai_chat_add_authorization
990
+ # help: command_id: :open_ai_chat_resend_prompt
991
+ # help: command_id: :open_ai_chat_set_context
992
+ # help: command_id: :open_ai_chat_delete_live_content
993
+ # help: command_id: :open_ai_chat_delete_static_content
994
+ # help: command_id: :open_ai_chat_delete_authorization
978
995
  # help:
979
996
 
980
997
  # help: ----------------------------------------------
@@ -997,6 +1014,14 @@ class SlackSmartBot
997
1014
  # help: command_id: :open_ai_chat_delete_session
998
1015
  # help:
999
1016
 
1017
+ # help: ----------------------------------------------
1018
+ # help: `chatGPT clean SESSION_NAME`
1019
+ # help: OpenAI: SmartBot will delete all the prompts from the ChatGPT session indicated.
1020
+ # help: Examples:
1021
+ # help: _chatgpt clean SpanishTeacher_
1022
+ # help: command_id: :open_ai_chat_clean_session
1023
+ # help:
1024
+
1000
1025
  # help: ----------------------------------------------
1001
1026
  # help: `chatGPT share SESSION_NAME`
1002
1027
  # help: `chatGPT share SESSION_NAME #CHANNEL`
@@ -1040,15 +1065,23 @@ class SlackSmartBot
1040
1065
  # help: `chatGPT copy temporary session NEW_SESSION_NAME`
1041
1066
  # help: `chatGPT use USER_NAME SESSION_NAME`
1042
1067
  # help: `chatGPT use USER_NAME SESSION_NAME NEW_SESSION_NAME`
1068
+ # help: `?? use SESSION_NAME`
1069
+ # help: `?? use SESSION_NAME NEW_PROMPT`
1070
+ # help: `?? use USER_NAME SESSION_NAME`
1071
+ # help: `?? use USER_NAME SESSION_NAME NEW_PROMPT`
1043
1072
  # help: OpenAI: SmartBot will copy the ChatGPT session indicated.
1044
1073
  # help: If NEW_SESSION_NAME is supplied it will be used as the name for the new session.
1045
1074
  # help: If USER_NAME is supplied it will copy the session from that user on shared sessions.
1046
1075
  # help: In case no NEW_SESSION_NAME is supplied it will use the same name as the original session plus a number.
1076
+ # help: If ?? is used it will start a new temporary session with the prompts from the session indicated.
1047
1077
  # help: Examples:
1048
1078
  # help: _chatgpt copy SpanishTeacher_
1049
1079
  # help: _chatgpt copy SpanishTeacher spanish_lessons_
1050
1080
  # help: _chatgpt use peterw SpanishTeacher_
1051
1081
  # help: _chatgpt use susanssd dataAnalysis DataSales_
1082
+ # help: _?? use peterw SpanishTeacher_
1083
+ # help: _?? use stats_sales can you project the sales for next month?_
1084
+ # help: _?? use susanssd dataAnalysis How much is the total?_
1052
1085
  # help: command_id: :open_ai_chat_copy_session
1053
1086
  # help: command_id: :open_ai_chat_copy_session_from_user
1054
1087
  # help:
@@ -1115,22 +1148,85 @@ class SlackSmartBot
1115
1148
  end
1116
1149
  open_ai_chat_copy_session(user_team_id, user_name, session_name, new_session_name)
1117
1150
 
1151
+ #?? use model
1152
+ when /\A\s*(\?\?)()\s+use\s+model\s+([\w\-0-9\.]+)()()\s*\z/im,
1153
+ /\A\s*\?\s+(use)\s+model\s+()([\w\-0-9\.]+)()()\s*\z/im
1154
+ model = $3.to_s
1155
+ if $1.to_s == "??"
1156
+ open_ai_chat("", true, :temporary, model: model)
1157
+ else
1158
+ open_ai_chat_use_model(model)
1159
+ end
1160
+
1161
+ #chatgpt use session for temporary session
1162
+ when /\A\s*\?\?\s*use\s+(session\s+)?([\w\-0-9]+)()()\s*\z/im, #use SESSION_NAME
1163
+ /\A\s*\?\?\s*use\s+(session\s+)?([\w\-0-9]+)\s+([\w\-0-9]+)()\s*\z/im, #use USER_NAME SESSION_NAME
1164
+ /\A\s*\?\?\s*use\s+(session\s+)?([\w\-0-9]+)\s+([\w\-0-9]+)\s+(.+)\s*\z/im, #use USER_NAME SESSION_NAME NEW_PROMPT
1165
+ /\A\s*\?\?\s*use\s+(session\s+)?([\w\-0-9]+)\s+(.+)()\s*\z/im #use SESSION_NAME NEW_PROMPT
1166
+ user_name = $2.to_s
1167
+ session_name = $3.to_s
1168
+ new_prompt = $4.to_s
1169
+ session_found = false
1170
+ more_than_one = false
1171
+
1172
+ if !@open_ai.keys.any? { |team_id_user| team_id_user.match?(/\A[A-Z0-9]{7,11}_#{user_name}$/) }
1173
+ session_name += " " + new_prompt
1174
+ new_prompt = ""
1175
+ end
1176
+ short_command = false
1177
+ if new_prompt == "" #use SESSION_NAME or use SESSION_NAME PROMPT
1178
+ short_command = true
1179
+ get_openai_sessions()
1180
+ new_prompt = session_name
1181
+ session_name = user_name
1182
+ @open_ai.keys.each do |team_id_user|
1183
+ if @open_ai[team_id_user][:chat_gpt].key?(:sessions) and
1184
+ @open_ai[team_id_user][:chat_gpt][:sessions].key?(session_name)
1185
+ if @open_ai[team_id_user][:chat_gpt][:sessions][session_name][:shared].size > 0
1186
+ members_shared = []
1187
+ @open_ai[team_id_user][:chat_gpt][:sessions][session_name][:shared].each do |shared|
1188
+ members_shared += get_channel_members(shared)
1189
+ end
1190
+ members_shared.uniq!
1191
+ else
1192
+ members_shared = []
1193
+ end
1194
+
1195
+ if team_id_user == user.team_id_user or
1196
+ @open_ai[team_id_user][:chat_gpt][:sessions][session_name][:public] or
1197
+ @open_ai[team_id_user][:chat_gpt][:sessions][session_name][:shared].include?(dest) or
1198
+ members_shared.include?(user.id)
1199
+ user_name = @open_ai[team_id_user][:chat_gpt][:sessions][session_name][:user_creator]
1200
+ more_than_one = true if session_found
1201
+ session_found = true
1202
+ end
1203
+ break if more_than_one
1204
+ end
1205
+ end
1206
+ end
1207
+ if more_than_one
1208
+ respond "There is more than one session with that name. Please specify the user name."
1209
+ save_stats(:open_ai_chat_copy_session_from_user)
1210
+ elsif !session_found and short_command
1211
+ respond ":exclamation: Session not found.\nCall `chatGPT sessions` to see the list of your sessions\n`chatGPT public sessions` to see the public sessions.\n`chatGPT shared sessions` to see the shared sessions."
1212
+ else
1213
+ success = open_ai_chat_copy_session("", user_name, session_name, "", temporary_session: true)
1214
+ open_ai_chat(new_prompt, false, :temporary) if new_prompt != "" and success
1215
+ end
1216
+
1118
1217
  # chatgpt chat
1119
1218
  when /\A\s*\?\s+(add\s+collaborator)\s+<@(\w+)>()()()\s*\z/im,
1120
- /\A\s*\?\s+(use)\s+model\s+()([\w\-0-9\.]+)()()\s*\z/im,
1121
- /\A\s*(\?\?)()\s+use\s+model\s+([\w\-0-9\.]+)()()\s*\z/im,
1122
1219
  /\A\s*\?\?(s|c)\s+([\w\-0-9]+)()()()\s*\z/im,
1123
- /\A\s*chatgpt\s+(start|continue|load)\s+([\w\-0-9]+)()()()\s*\z/im,
1220
+ /\A\s*chatgpt\s+(start|continue|load|clean)\s+([\w\-0-9]+)()()()\s*\z/im,
1124
1221
  /\A\s*(chatgpt)\s+([\w\-0-9]+)()()()\s*\z/im, #chatgpt session_name
1125
1222
  /\A\s*(chatgpt)\s+([\w\-0-9]+)()\s+>([\w\-0-9]+)()\s*\z/im, #chatgpt session_name >tag_name
1126
1223
  /\A\s*(chatgpt)\s+([\w\-0-9]+)\s+([\w\-0-9\.]+)()()\s*\z/im, #chatgpt session_name model_name
1127
1224
  /\A\s*(chatgpt)\s+([\w\-0-9]+)\s+([\w\-0-9\.]+)\s+>([\w\-0-9\.]+)()\s*\z/im, #chatgpt session_name model_name >tag_name
1128
1225
  /\A\s*(chatgpt)\s+([\w\-0-9]+)()\s+>([\w\-0-9]+)\s+(".+")\s*\z/im, #chatgpt session_name >tag_name "description"
1129
1226
  /\A\s*(chatgpt)\s+([\w\-0-9]+)()()\s+(".+")\s*\z/im, #chatgpt session_name "description"
1130
- /\A\s*(chatgpt)\s+([\w\-0-9]+)()\s+>([\w\-0-9]+)\s+(".+")\s*\z/im, #chatgpt session_name >tag_name "description"
1131
1227
  /\A\s*(chatgpt)\s+([\w\-0-9]+)\s+([\w\-0-9\.]+)()\s+(".+")\s*\z/im, #chatgpt session_name model_name "description"
1132
1228
  /\A\s*(chatgpt)\s+([\w\-0-9]+)\s+([\w\-0-9\.]+)\s+>([\w\-0-9]+)\s+(".+")\s*\z/im, #chatgpt session_name model_name >tag_name "description"
1133
- /\A\s*(\?\?)\s*()()()()\z/im, /\A\s*(\?\?)\s*(.+)()\s*()()\z/im, /\A\s*()\?\s*(.+)()\s*()()\z/im
1229
+ /\A\s*(\?\?)\s*()()()()\z/im, /\A\s*(\?\?)\s*(.+)()\s*()()\z/im, /\A\s*()(\?\s*.+)()\s*()()\z/im
1134
1230
  type = $1.to_s.downcase
1135
1231
  text = $2.to_s
1136
1232
  model = $3.to_s
@@ -1142,21 +1238,26 @@ class SlackSmartBot
1142
1238
  type = :start
1143
1239
  elsif type == "c" or type == "continue" or type == "load"
1144
1240
  type = :continue
1241
+ elsif type == "clean"
1242
+ type = :clean
1243
+ delete_history = true
1145
1244
  elsif type.match?(/add\s+collaborator/)
1146
1245
  type = :add_collaborator
1147
- elsif type == "use"
1148
- type = :use_model
1149
1246
  else
1150
- type = :temporary
1151
- if text == "??"
1152
- delete_history = true
1153
- text = ""
1247
+ if text.match?(/\A\?[^\?]/im) and !text.match?(/\A\?\s+\?\?/im)
1248
+ type = :continue_session
1249
+ #remove first ? from the prompt
1250
+ text = text[1..-1]
1251
+ else
1252
+ type = :temporary
1253
+ if text == "??" or text.match?(/\A\?\s+\?\?/)
1254
+ delete_history = true
1255
+ text = ""
1256
+ end
1154
1257
  end
1155
1258
  end
1156
1259
  if type == :add_collaborator
1157
1260
  open_ai_chat_add_collaborator(text)
1158
- elsif type == :use_model
1159
- open_ai_chat_use_model(model)
1160
1261
  else
1161
1262
  open_ai_chat(text, delete_history, type, model: model, tag: tag, description: description, files: files)
1162
1263
  end
@@ -1231,7 +1332,7 @@ class SlackSmartBot
1231
1332
  channel = dest if channel == ""
1232
1333
  summarize(user, dest, channel, from, thread_ts)
1233
1334
 
1234
- # help: >*<|OTHER GENERAL COMMANDS>*
1335
+ # help: >*<|OTHER GENERAL COMMANDS>*
1235
1336
 
1236
1337
  else
1237
1338
  return false
@@ -83,7 +83,7 @@ class SlackSmartBot
83
83
  get_channels_name_and_id() unless @channels_id.keys.include?(channel_members)
84
84
 
85
85
  tm = get_channel_members(channel_members)
86
- if tm.nil?
86
+ if tm.nil? or tm.size == 0
87
87
  message << ":exclamation: Add the Smart Bot to *<##{channel_members}>* channel first."
88
88
  wrong = true
89
89
  else
@@ -93,7 +93,6 @@ class SlackSmartBot
93
93
  end
94
94
  end
95
95
  end
96
-
97
96
  if header.size > 0
98
97
  headers = ["date", "bot_channel", "bot_channel_id", "dest_channel", "dest_channel_id", "type_message", "user_name", "user_id", "text", "command", "files", "time_zone", "job_title", "team_id"]
99
98
  header.each do |h|
@@ -195,8 +194,9 @@ class SlackSmartBot
195
194
  Dir["#{config.stats_path}.*.log"].sort.each do |file|
196
195
  if file >= "#{config.stats_path}.#{from_file}.log" and file <= "#{config.stats_path}.#{to_file}.log"
197
196
  CSV.foreach(file, headers: true, header_converters: :symbol, converters: :numeric) do |row|
198
- if (include_channel_members and members_list.include?(row[:user_name])) or
199
- (exclude_channel_members and !members_list.include?(row[:user_name])) or
197
+ clean_user_name = row[:user_name].gsub('routine/','')
198
+ if (include_channel_members and members_list.include?(clean_user_name)) or
199
+ (exclude_channel_members and !members_list.include?(clean_user_name)) or
200
200
  (!include_channel_members and !exclude_channel_members)
201
201
  row[:date] = row[:date].to_s
202
202
  row[:team_id] = config.team_id if row[:team_id].to_s == ''
@@ -385,9 +385,10 @@ class SlackSmartBot
385
385
 
386
386
  #print users by team
387
387
  if users_by_team.size > 0
388
+ total_users = rows.user_id.uniq.size
388
389
  message << "*Users by Space*"
389
390
  users_by_team.each do |team_name, users|
390
- message << "\t#{team_name}: #{users.size} (#{(users.size.to_f * 100 / users_id_name.size).round(2)}%)"
391
+ message << "\t#{team_name}: #{users.size} (#{(users.size.to_f * 100 / total_users).round(2)}%)"
391
392
  end
392
393
  end
393
394
 
@@ -404,8 +405,8 @@ class SlackSmartBot
404
405
  if user == ""
405
406
  users = rows.user_id.uniq.sort
406
407
  if rows[0].key?(:time_zone) #then save_stats is saving the time zone already
407
- rows.time_zone.each do |time_zone|
408
- unless time_zone == ""
408
+ rows.time_zone.each_with_index do |time_zone, idx|
409
+ unless time_zone == "" or rows.user_name[idx].match?(/^routine\//)
409
410
  tzone_users[time_zone] ||= 0
410
411
  tzone_users[time_zone] += 1
411
412
  end
@@ -413,13 +414,13 @@ class SlackSmartBot
413
414
  else
414
415
  rows.user_id.each_with_index do |usr, i|
415
416
  if rows[i].values.size >= 12 #then save_stats is saving the time zone already but not all the data
416
- unless rows[i].values[11] == ""
417
+ unless rows[i].values[11] == "" or rows[i].values[11].match?(/^routine\//)
417
418
  tzone_users[rows[i].values[11]] ||= 0
418
419
  tzone_users[rows[i].values[11]] += 1
419
420
  end
420
421
  else
421
422
  user_info = find_user(usr)
422
- unless user_info.nil? or user_info.is_app_user or user_info.is_bot
423
+ unless user_info.nil? or user_info.is_app_user or user_info.is_bot or user_info.tz_label.to_s == "" or rows.user_name[i].match?(/^routine\//)
423
424
  tzone_users[user_info.tz_label] ||= 0
424
425
  tzone_users[user_info.tz_label] += 1
425
426
  end
@@ -428,7 +429,7 @@ class SlackSmartBot
428
429
  end
429
430
  if rows[0].key?(:job_title) #then save_stats is saving the job title already
430
431
  rows.job_title.each_with_index do |job_title, idx|
431
- unless job_title.to_s == ""
432
+ unless job_title.to_s == "" or rows.user_name[idx].match?(/^routine\//)
432
433
  unless job_title_users.key?(job_title)
433
434
  job_title = job_title.to_s.split.map { |x| x[0].upcase + x[1..-1] }.join(" ")
434
435
  job_title_users[job_title] ||= 0
@@ -543,10 +544,11 @@ class SlackSmartBot
543
544
  message << "*Num Users by Job Title*"
544
545
  end
545
546
  i = 0
547
+ users_size_without_routines = users.delete_if { |u| u.include?("routine/") }.size
546
548
  users_by_job_title.sort_by { |k, v| -v.size }.each do |jtitle, usersj|
547
549
  i += 1
548
550
  if i <= 10
549
- message << "\t#{jtitle}: #{usersj.size} (#{(usersj.size.to_f * 100 / users.size).round(2)}%)"
551
+ message << "\t#{jtitle}: #{usersj.size} (#{(usersj.size.to_f * 100 / users_size_without_routines).round(2)}%)"
550
552
  end
551
553
  end
552
554
  end