chatgpt_assistant 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,277 +1,162 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "helpers/telegram_helper"
4
+
3
5
  module ChatgptAssistant
4
6
  # This class is responsible for the telegram bot features
5
7
  class TelegramBot < ApplicationBot
6
8
  def start
7
9
  bot.listen do |message|
10
+ next if message.chat.type != "private" # disable group and channel messages, we will enable it later
11
+
8
12
  @msg = message
9
- event_callback
13
+ @visitor = telegram_visited?(@msg.chat.id)
14
+ next unless telegram_text_or_audio?
15
+
16
+ text_events if telegram_message_has_text?
17
+ audio_event if telegram_message_has_audio?
10
18
  end
11
- rescue StandardError
19
+ rescue StandardError => e
20
+ Error.create(message: e.message, backtrace: e.backtrace)
12
21
  retry
13
22
  end
14
23
 
15
24
  private
16
25
 
17
- attr_accessor :msg
18
-
19
- def event_callback
20
- return unless text_or_audio?
26
+ include TelegramHelper
27
+
28
+ attr_accessor :msg, :visitor, :chat, :chat_id
29
+
30
+ def text_events
31
+ case msg.text
32
+ when "/start"
33
+ start_event
34
+ when "/help"
35
+ help_event
36
+ when "/hist"
37
+ hist_event
38
+ when "/list"
39
+ list_event
40
+ when "/stop"
41
+ stop_event
42
+ when nil
43
+ nil_event
44
+ else
45
+ action_events
46
+ end
47
+ end
21
48
 
22
- text_events if msg.text.present?
23
- audio_event if msg.audio.present? || msg.voice.present?
24
- end
49
+ def start_event
50
+ register_visitor_action("start", visitor.id) unless visitor.tel_user
51
+ register_user_action("start", visitor.tel_user.id) if visitor.tel_user
52
+ telegram_send_start_message
53
+ end
25
54
 
26
- def text_events
27
- case msg.text
28
- when "/start"
29
- start_event
30
- when "/help"
31
- help_event
32
- when "/list"
33
- list_event
34
- when "/stop"
35
- stop_event
36
- when "/hist"
37
- hist_event
38
- when nil
39
- nil_event
40
- else
41
- action_events
55
+ def help_event
56
+ register_visitor_action("help", visitor.id) unless visitor.tel_user
57
+ register_user_action("help", visitor.tel_user.id) if visitor.tel_user
58
+ help_messages.each { |m| send_message m, msg.chat.id }
42
59
  end
43
- end
44
60
 
45
- def audio_event
46
- return bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:no_chat_selected]) if user.current_chat.nil?
61
+ def hist_event
62
+ return not_logged_in_message unless user
63
+ return no_chat_selected_message unless user.current_chat
64
+ return no_messages_founded_message if user.current_chat.messages.count.zero?
47
65
 
48
- chat = user.current_chat
49
- user_audio = transcribed_file
50
- message = Message.new(content: user_audio["text"], chat_id: chat.id, role: "user")
51
- if message.save
52
- ai_text = chatter.chat(user_audio["text"], chat.id)
53
- ai_voice = audio_synthesis.synthesize_text(ai_text)
54
- send_audio_message(ai_voice, ai_text)
55
- delete_all_voice_files
56
- else
57
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:message_creation_error])
66
+ register_user_action("hist", user.id)
67
+ telegram_user_history.each do |m|
68
+ send_message m, msg.chat.id
69
+ end
58
70
  end
59
- end
60
71
 
61
- def send_audio_message(voice, text)
62
- bot.api.send_voice(chat_id: msg.chat.id, voice: Faraday::UploadIO.new(voice, "audio/mp3"))
63
- bot.api.send_message(chat_id: msg.chat.id, text: text)
64
- end
72
+ def list_event
73
+ return unless valid_for_list_action?
65
74
 
66
- def action_events
67
- return chatter_call unless actions?
68
- return new_chat if msg.text.include?("new_chat/")
69
- return select_chat if msg.text.include?("sl_chat/")
70
- return login if msg.text.include?("login/")
71
- return register if msg.text.include?("register/")
75
+ register_user_action("list", user.id)
76
+ send_message commom_messages[:chat_list], msg.chat.id
77
+ chats_str = ""
78
+ user.chats.each_with_index { |c, i| chats_str += "Chat #{i + 1} - #{c.title}\n" }
79
+ send_message chats_str, msg.chat.id
80
+ end
72
81
 
73
- invalid_command_error_message
74
- end
82
+ def action_events
83
+ return login_event if msg.text.include?("login/")
84
+ return register_event if msg.text.include?("register/")
85
+ return new_chat_event if msg.text.include?("new_chat/")
86
+ return select_chat_event if msg.text.include?("sl_chat/")
87
+ return telegram_chat_event unless telegram_actions?
75
88
 
76
- def login
77
- user_info = msg.text.split("/").last
78
- email, password = user_info.split(":")
79
- case auth_usertelegram(email, password, msg.chat.id)
80
- when "user not found"
81
- user_not_found_error_message
82
- when "wrong password"
83
- wrong_password_error_message
84
- when find_useremail(email)
85
- user_logged_in_message
89
+ invalid_command_error_message
86
90
  end
87
- end
88
91
 
89
- def register
90
- user_info = msg.text.split("/").last
91
- email, password = user_info.split(":")
92
- if telegram_user_create(msg.chat.id, email, password, msg.from.username || msg.from.first_name)
93
- user_created_message
94
- else
95
- user_creation_error_message
92
+ def login_event
93
+ register_visitor_action("login", visitor.id) unless visitor.tel_user
94
+ user_info = msg.text.split("/").last
95
+ email, password = user_info.split(":")
96
+ case telegram_user_auth(email, password, msg.chat.id)
97
+ when "user not found"
98
+ user_not_found_error_message
99
+ when "wrong password"
100
+ wrong_password_error_message
101
+ when email
102
+ user_logged_in_message
103
+ end
96
104
  end
97
- end
98
-
99
- def audio
100
- msg.audio || msg.voice
101
- end
102
-
103
- def audio_info
104
- bot.api.get_file(file_id: audio.file_id)
105
- end
106
-
107
- def audio_url
108
- "https://api.telegram.org/file/bot#{telegram_token}/#{audio_info["result"]["file_path"]}"
109
- end
110
105
 
111
- def transcribed_file
112
- audio_recognition.transcribe_audio(audio_url)
113
- end
114
-
115
- def start_event
116
- bot.api.send_message(chat_id: msg.chat.id, text: commom_messages[:start])
117
- help_message = help_messages.join("\n").to_s
118
- bot.api.send_message(chat_id: msg.chat.id, text: help_message)
119
- bot.api.send_message(chat_id: msg.chat.id, text: commom_messages[:start_helper])
120
- bot.api.send_message(chat_id: msg.chat.id, text: commom_messages[:start_sec_helper])
121
- end
122
-
123
- def hist_event
124
- return not_logged_in_message unless user
125
- return bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:no_chat_selected]) unless user.current_chat
126
-
127
- if user.current_chat.messages.count.zero?
128
- return bot.api.send_message(chat_id: msg.chat.id,
129
- text: error_messages[:no_messages_founded])
106
+ def register_event
107
+ register_visitor_action("register", visitor.id)
108
+ user_info = msg.text.split("/").last
109
+ email, password = user_info.split(":")
110
+ registered_email = telegram_user_create visitor.id, email, password
111
+ registered_email == email ? user_created_message : user_creation_error_message
130
112
  end
131
113
 
132
- response = chat.messages.last(4).map do |mess|
133
- "#{mess.role}: #{mess.content}\n at: #{mess.created_at}\n\n"
134
- end.join
135
- bot.api.send_message(chat_id: msg.chat.id, text: response)
136
- end
137
-
138
- def select_chat
139
- return not_logged_in_message unless user
140
-
141
- title = msg.text.split("/").last
142
- chat = user.chat_by_title(title)
143
- return chat_not_found_message unless chat
144
-
145
- user.update(current_chat_id: chat.id)
146
- bot.api.send_message(chat_id: msg.chat.id, text: success_messages[:chat_selected])
147
- end
114
+ def new_chat_event
115
+ return not_logged_in_message unless user
148
116
 
149
- def new_chat
150
- user.nil? ? not_logged_in_message : create_chat
151
- end
152
-
153
- def create_chat
154
- text = msg.text
155
- title = text.split("/").last
156
- chat = Chat.new(user_id: user.id, status: 0, title: title)
157
- chat.save ? chat_created_message(chat) : chat_creation_failed_message
158
- end
159
-
160
- def list_event
161
- return unless valid_for_list_action?
162
-
163
- bot.api.send_message(chat_id: msg.chat.id, text: commom_messages[:chat_list])
164
- chats_str = ""
165
- user.chats.each do |chat|
166
- chats_str += "Chat #{chat.id} - #{chat.title}\n"
117
+ register_user_action("new_chat", user.id)
118
+ telegram_create_chat
167
119
  end
168
- bot.api.send_message(chat_id: msg.chat.id, text: chats_str)
169
- end
170
-
171
- def valid_for_list_action?
172
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:not_logged_in]) if user.nil?
173
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:no_chats_founded]) if user.chats.count.zero?
174
- !user.nil? && user.chats.count.positive?
175
- end
176
-
177
- def chatter_call
178
- user ? chat_if_exists : not_logged_in_message
179
- end
180
120
 
181
- def chat_if_exists
182
- chat = Chat.find_by(user_id: user.id, id: user.current_chat_id)
183
- chat ? chat_success(chat.id) : no_chat_selected
184
- end
121
+ def select_chat_event
122
+ return not_logged_in_message unless user
185
123
 
186
- def chat_success(chat_id)
187
- Message.create(chat_id: chat_id, content: msg.text, role: "user")
188
- bot.api.send_message(chat_id: msg.chat.id, text: chatter.chat(msg.text, chat_id))
189
- end
124
+ register_user_action("select_chat", user.id)
125
+ title = msg.text.split("/").last
126
+ chat = user.chat_by_title(title)
127
+ return chat_not_found_message unless chat
190
128
 
191
- def help_event
192
- help_messages.each do |message|
193
- bot.api.send_message(chat_id: msg.chat.id, text: message)
129
+ user.update(current_chat_id: chat.id)
130
+ send_message success_messages[:chat_selected], msg.chat.id
194
131
  end
195
- end
196
-
197
- def stop_event
198
- bot.api.send_message(chat_id: msg.chat.id, text: commom_messages[:stop])
199
- bot.api.leave_chat(chat_id: msg.chat.id)
200
- end
201
-
202
- def nil_event
203
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:nil])
204
- end
205
-
206
- def user_logged_message
207
- user.update(telegram_id: msg.chat.id)
208
- bot.api.send_message(chat_id: msg.chat.id, text: success_messages[:user_logged_in])
209
- end
210
-
211
- def invalid_command_error_message
212
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:invalid_command])
213
- end
214
-
215
- def wrong_password_error_message
216
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:password])
217
- end
218
-
219
- def user_not_found_error_message
220
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:user_not_found])
221
- end
222
-
223
- def not_logged_in_message
224
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:user_not_logged_in])
225
- end
226
-
227
- def user_created_message
228
- bot.api.send_message(chat_id: msg.chat.id, text: success_messages[:user_created])
229
- end
230
132
 
231
- def user_creation_error_message
232
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:user_creation])
233
- end
234
-
235
- def chat_created_message(chat)
236
- user.update(current_chat_id: chat.id)
237
- bot.api.send_message(chat_id: msg.chat.id, text: success_messages[:chat_created])
238
- end
239
-
240
- def chat_creation_failed_message
241
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:chat_creation_failed])
242
- end
243
-
244
- def no_chat_selected
245
- bot.api.send_message(chat_id: msg.chat.id,
246
- text: error_messages[:no_chat_selected])
247
- end
248
-
249
- def chat_not_found_message
250
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:chat_not_found])
251
- end
252
-
253
- def error_log(err)
254
- if err.message.to_s.include?("Bad Request: message is too long")
255
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:message_history_too_long])
256
- else
257
- bot.api.send_message(chat_id: msg.chat.id, text: error_messages[:something_went_wrong])
133
+ def stop_event
134
+ register_event_action("stop", visitor.id) unless visitor.tel_user
135
+ register_user_action("stop", visitor.tel_user.id) if visitor.tel_user
136
+ send_message commom_messages[:stop], msg.chat.id
137
+ bot.api.leave_chat(chat_id: msg.chat.id)
258
138
  end
259
- end
260
-
261
- def user
262
- @user ||= User.find_by(telegram_id: msg.chat.id)
263
- end
264
-
265
- def bot
266
- @bot ||= Telegram::Bot::Client.new(telegram_token)
267
- end
268
139
 
269
- def text_or_audio?
270
- msg.respond_to?(:text) || msg.respond_to?(:audio) || msg.respond_to?(:voice)
271
- end
140
+ def nil_event
141
+ register_event_action("nil", visitor.id) unless visitor.tel_user
142
+ register_user_action("nil", visitor.tel_user.id) if visitor.tel_user
143
+ send_message error_messages[:nil], msg.chat.id
144
+ end
272
145
 
273
- def actions?
274
- msg.text.include?("new_chat/") || msg.text.include?("sl_chat/") || msg.text.include?("login/") || msg.text.include?("register/")
275
- end
146
+ def audio_event
147
+ return not_logged_in_message unless user
148
+ return no_chat_selected_message if user.current_chat_id.nil?
149
+
150
+ register_user_action("audio", user.id)
151
+ user_audio = transcribe_file(telegram_audio_url)
152
+ message = Message.new(content: user_audio[:text], chat_id: user.current_chat_id, role: "user")
153
+ if message.save
154
+ ai_response = telegram_process_ai_voice(user_audio[:file])
155
+ telegram_send_voice_message(voice: ai_response[:voice], text: ai_response[:text])
156
+ delete_file ai_response[:voice]
157
+ else
158
+ send_message error_messages[:message_creation_error], msg.chat.id
159
+ end
160
+ end
276
161
  end
277
162
  end
@@ -15,38 +15,7 @@ module ChatgptAssistant
15
15
  @openai_api_key = openai_api_key
16
16
  end
17
17
 
18
- def download_audio(audio_url)
19
- audio_conn = Faraday.new(url: audio_url)
20
- File.open(dl_file_name, "wb") do |file|
21
- file.write(audio_conn.get.body)
22
- end
23
- FFMPEG::Movie.new(dl_file_name).transcode(file_name)
24
- File.delete(dl_file_name)
25
- end
26
-
27
- def header
28
- {
29
- "Content-Type": "multipart/form-data",
30
- "Authorization": "Bearer #{openai_api_key}"
31
- }
32
- end
33
-
34
- def payload
35
- {
36
- "file": Faraday::UploadIO.new(file_name, "audio/mp3"),
37
- "model": "whisper-1"
38
- }
39
- end
40
-
41
- def transcribed_file_json
42
- {
43
- file_name: file_name,
44
- text: JSON.parse(response.body)["text"]
45
- }.to_json
46
- end
47
-
48
18
  def transcribe_audio(audio_url)
49
- @audio_url = audio_url
50
19
  download_audio(audio_url)
51
20
  @response = conn.post("v1/audio/transcriptions", payload, header)
52
21
  transcribed_file_json
@@ -54,6 +23,36 @@ module ChatgptAssistant
54
23
 
55
24
  private
56
25
 
57
- attr_reader :conn, :openai_api_key, :audio_url, :time, :ibm_api_key, :ibm_url, :response, :dl_file_name, :file_name
26
+ attr_reader :conn, :time,
27
+ :response, :dl_file_name, :file_name,
28
+ :ibm_api_key, :ibm_url, :openai_api_key
29
+
30
+ def download_audio(audio_url)
31
+ audio_conn = Faraday.new(url: audio_url)
32
+ File.binwrite(dl_file_name, audio_conn.get.body)
33
+ FFMPEG::Movie.new(dl_file_name).transcode(file_name)
34
+ File.delete(dl_file_name)
35
+ end
36
+
37
+ def header
38
+ {
39
+ "Content-Type": "multipart/form-data",
40
+ Authorization: "Bearer #{openai_api_key}"
41
+ }
42
+ end
43
+
44
+ def payload
45
+ {
46
+ file: Faraday::UploadIO.new(file_name, "audio/mp3"),
47
+ model: "whisper-1"
48
+ }
49
+ end
50
+
51
+ def transcribed_file_json
52
+ {
53
+ file: file_name,
54
+ text: JSON.parse(response.body)["text"]
55
+ }
56
+ end
58
57
  end
59
58
  end
@@ -22,98 +22,94 @@ module ChatgptAssistant
22
22
 
23
23
  private
24
24
 
25
- attr_reader :openai_api_key, :ibm_api_key, :ibm_url, :aws_access_key_id, :aws_secret_access_key, :aws_region,
26
- :config, :language, :voice
27
-
28
- def faraday_instance
29
- Faraday.new(url: "https://api.openai.com/") do |faraday|
30
- faraday.request :multipart
31
- faraday.request :url_encoded
32
- faraday.adapter Faraday.default_adapter
25
+ attr_reader :openai_api_key, :ibm_api_key, :ibm_url, :aws_access_key_id, :aws_secret_access_key, :aws_region,
26
+ :config, :language, :voice
27
+
28
+ def faraday_instance
29
+ Faraday.new(url: "https://api.openai.com/") do |faraday|
30
+ faraday.request :multipart
31
+ faraday.request :url_encoded
32
+ faraday.adapter Faraday.default_adapter
33
+ end
33
34
  end
34
- end
35
35
 
36
- def classify_mode
37
- if ibm_mode?
38
- @ibm_api_key = config.ibm_api_key
39
- @ibm_url = config.ibm_url
40
- @voice = send("#{language}_ibm_voice")
41
- elsif aws_mode?
42
- @aws_access_key_id = config.aws_access_key_id
43
- @aws_secret_access_key = config.aws_secret_access_key
44
- @aws_region = config.aws_region
45
- @voice = send("#{language}_aws_voice")
36
+ def classify_mode
37
+ if ibm_mode?
38
+ @ibm_api_key = config.ibm_api_key
39
+ @ibm_url = config.ibm_url
40
+ @voice = send("#{language}_ibm_voice")
41
+ elsif aws_mode?
42
+ @aws_access_key_id = config.aws_access_key_id
43
+ @aws_secret_access_key = config.aws_secret_access_key
44
+ @aws_region = config.aws_region
45
+ @voice = send("#{language}_aws_voice")
46
+ end
46
47
  end
47
- end
48
48
 
49
- def synthesize_text_aws(text)
50
- time = Time.now.to_i
51
- @time = Time.now.to_i
52
- polly_client = Aws::Polly::Client.new(
53
- access_key_id: aws_access_key_id,
54
- secret_access_key: aws_secret_access_key,
55
- region: aws_region
56
- )
57
- response = polly_client.synthesize_speech(
58
- output_format: "mp3",
59
- text: text,
60
- voice_id: voice,
61
- engine: "neural"
62
- )
63
-
64
- File.open("voice/aws-#{time}.mp3", "wb") do |file|
65
- file.write(response.audio_stream.read)
49
+ def synthesize_text_aws(text)
50
+ time = Time.now.to_i
51
+ @time = Time.now.to_i
52
+ polly_client = Aws::Polly::Client.new(
53
+ access_key_id: aws_access_key_id,
54
+ secret_access_key: aws_secret_access_key,
55
+ region: aws_region
56
+ )
57
+ response = polly_client.synthesize_speech(
58
+ output_format: "mp3",
59
+ text: text,
60
+ voice_id: voice,
61
+ engine: "neural"
62
+ )
63
+
64
+ File.binwrite("voice/aws-#{time}.mp3", response.audio_stream.read)
65
+ "voice/aws-#{time}.mp3"
66
66
  end
67
- "voice/aws-#{time}.mp3"
68
- end
69
67
 
70
- def synthesize_text_ibm(text)
71
- time = Time.now.to_i
72
- authenticator = IBMWatson::Authenticators::IamAuthenticator.new(
73
- apikey: ibm_api_key
74
- )
68
+ def synthesize_text_ibm(text)
69
+ time = Time.now.to_i
70
+ authenticator = IBMWatson::Authenticators::IamAuthenticator.new(
71
+ apikey: ibm_api_key
72
+ )
75
73
 
76
- text_to_speech = IBMWatson::TextToSpeechV1.new(
77
- authenticator: authenticator
78
- )
74
+ text_to_speech = IBMWatson::TextToSpeechV1.new(
75
+ authenticator: authenticator
76
+ )
79
77
 
80
- text_to_speech.service_url = ibm_url
78
+ text_to_speech.service_url = ibm_url
81
79
 
82
- audio_format = "audio/mp3"
83
- audio = text_to_speech.synthesize(
84
- text: text,
85
- accept: audio_format,
86
- voice: voice
87
- ).result
80
+ audio_format = "audio/mp3"
81
+ audio = text_to_speech.synthesize(
82
+ text: text,
83
+ accept: audio_format,
84
+ voice: voice
85
+ ).result
88
86
 
89
- File.open("voice/ibm-#{time}.mp3", "wb") do |audio_file|
90
- audio_file.write(audio)
87
+ File.binwrite("voice/ibm-#{time}.mp3", audio)
88
+ "voice/ibm-#{time}.mp3"
91
89
  end
92
- "voice/ibm-#{time}.mp3"
93
- end
94
90
 
95
- def pt_aws_voice
96
- "Vitoria"
97
- end
91
+ def pt_aws_voice
92
+ "Vitoria"
93
+ end
98
94
 
99
- def en_aws_voice
100
- "Joanna"
101
- end
95
+ def en_aws_voice
96
+ "Joanna"
97
+ end
102
98
 
103
- def pt_ibm_voice
104
- "pt-BR_IsabelaV3Voice"
105
- end
99
+ def pt_ibm_voice
100
+ "pt-BR_IsabelaV3Voice"
101
+ end
106
102
 
107
- def en_ibm_voice
108
- "en-US_AllisonV3Voice"
109
- end
103
+ def en_ibm_voice
104
+ "en-US_AllisonV3Voice"
105
+ end
110
106
 
111
- def ibm_mode?
112
- config.mode == "ibm"
113
- end
107
+ def ibm_mode?
108
+ config.mode == "ibm"
109
+ end
114
110
 
115
- def aws_mode?
116
- config.mode == "aws"
117
- end
111
+ def aws_mode?
112
+ config.mode == "aws"
113
+ end
118
114
  end
119
115
  end