chatgpt_assistant 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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_usertelegram(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,38 @@ 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.open(dl_file_name, "wb") do |file|
33
+ file.write(audio_conn.get.body)
34
+ end
35
+ FFMPEG::Movie.new(dl_file_name).transcode(file_name)
36
+ File.delete(dl_file_name)
37
+ end
38
+
39
+ def header
40
+ {
41
+ "Content-Type": "multipart/form-data",
42
+ "Authorization": "Bearer #{openai_api_key}"
43
+ }
44
+ end
45
+
46
+ def payload
47
+ {
48
+ "file": Faraday::UploadIO.new(file_name, "audio/mp3"),
49
+ "model": "whisper-1"
50
+ }
51
+ end
52
+
53
+ def transcribed_file_json
54
+ {
55
+ file: file_name,
56
+ text: JSON.parse(response.body)["text"]
57
+ }
58
+ end
58
59
  end
59
60
  end
@@ -22,98 +22,98 @@ 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.open("voice/aws-#{time}.mp3", "wb") do |file|
65
+ file.write(response.audio_stream.read)
66
+ end
67
+ "voice/aws-#{time}.mp3"
66
68
  end
67
- "voice/aws-#{time}.mp3"
68
- end
69
-
70
- def synthesize_text_ibm(text)
71
- time = Time.now.to_i
72
- authenticator = IBMWatson::Authenticators::IamAuthenticator.new(
73
- apikey: ibm_api_key
74
- )
75
69
 
76
- text_to_speech = IBMWatson::TextToSpeechV1.new(
77
- authenticator: authenticator
78
- )
79
-
80
- text_to_speech.service_url = ibm_url
81
-
82
- audio_format = "audio/mp3"
83
- audio = text_to_speech.synthesize(
84
- text: text,
85
- accept: audio_format,
86
- voice: voice
87
- ).result
88
-
89
- File.open("voice/ibm-#{time}.mp3", "wb") do |audio_file|
90
- audio_file.write(audio)
70
+ def synthesize_text_ibm(text)
71
+ time = Time.now.to_i
72
+ authenticator = IBMWatson::Authenticators::IamAuthenticator.new(
73
+ apikey: ibm_api_key
74
+ )
75
+
76
+ text_to_speech = IBMWatson::TextToSpeechV1.new(
77
+ authenticator: authenticator
78
+ )
79
+
80
+ text_to_speech.service_url = ibm_url
81
+
82
+ audio_format = "audio/mp3"
83
+ audio = text_to_speech.synthesize(
84
+ text: text,
85
+ accept: audio_format,
86
+ voice: voice
87
+ ).result
88
+
89
+ File.open("voice/ibm-#{time}.mp3", "wb") do |audio_file|
90
+ audio_file.write(audio)
91
+ end
92
+ "voice/ibm-#{time}.mp3"
91
93
  end
92
- "voice/ibm-#{time}.mp3"
93
- end
94
94
 
95
- def pt_aws_voice
96
- "Vitoria"
97
- end
95
+ def pt_aws_voice
96
+ "Vitoria"
97
+ end
98
98
 
99
- def en_aws_voice
100
- "Joanna"
101
- end
99
+ def en_aws_voice
100
+ "Joanna"
101
+ end
102
102
 
103
- def pt_ibm_voice
104
- "pt-BR_IsabelaV3Voice"
105
- end
103
+ def pt_ibm_voice
104
+ "pt-BR_IsabelaV3Voice"
105
+ end
106
106
 
107
- def en_ibm_voice
108
- "en-US_AllisonV3Voice"
109
- end
107
+ def en_ibm_voice
108
+ "en-US_AllisonV3Voice"
109
+ end
110
110
 
111
- def ibm_mode?
112
- config.mode == "ibm"
113
- end
111
+ def ibm_mode?
112
+ config.mode == "ibm"
113
+ end
114
114
 
115
- def aws_mode?
116
- config.mode == "aws"
117
- end
115
+ def aws_mode?
116
+ config.mode == "aws"
117
+ end
118
118
  end
119
119
  end