baha-99 commited on
Commit
0fe0ff5
·
verified ·
1 Parent(s): 09a3478

Update bot_telegram.py

Browse files
Files changed (1) hide show
  1. bot_telegram.py +88 -199
bot_telegram.py CHANGED
@@ -1,32 +1,18 @@
1
- import os
2
  import logging
3
- import asyncio
4
  import requests
5
  from telegram import Update
6
  from telegram.ext import Application, CommandHandler, MessageHandler, filters, CallbackContext
7
- import aiohttp
8
 
9
  # Configure logging
10
- logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
11
-
12
- # Load environment variables from Hugging Face Secrets
13
- BOT_TOKEN = os.getenv("BOT_TOKEN")
14
- BASE_URL = os.getenv("BASE_URL")
15
- API_USERNAME = os.getenv("API_USERNAME")
16
- API_PASSWORD = os.getenv("API_PASSWORD")
17
-
18
- # Set the secret password for authentication
19
- SECRET_PASSWORD = "secure123" # Change this to your desired password
20
-
21
- # Dictionary to store authenticated users
22
- AUTHENTICATED_USERS = set()
23
- AWAITING_PASSWORD = set()
24
 
25
  class TelegramBot:
26
- """A Telegram bot with password-based authentication."""
27
 
28
  def __init__(self, bot_token, base_url, username, password):
29
- """Initialize the bot with Telegram API token, API credentials, and authentication."""
30
  self.bot_token = bot_token
31
  self.base_url = base_url
32
  self.username = username
@@ -42,78 +28,98 @@ class TelegramBot:
42
  self.app = Application.builder().token(self.bot_token).build()
43
  self.setup_handlers()
44
 
45
- # Authenticate with API
46
- logging.info("Authenticating with API...")
47
  self.authenticate()
48
 
49
  def authenticate(self):
50
  """Authenticate with the API and retrieve an access token."""
51
- payload = {"username": self.username, "password": self.password}
52
- headers = {"Content-Type": "application/json", "accept": "application/json"}
 
 
 
 
 
 
 
 
53
 
54
  try:
55
  response = requests.post(self.login_url, headers=headers, json=payload)
56
 
57
  if response.status_code == 200:
58
  self.auth_token = response.json().get("access_token")
59
- logging.info("Successfully authenticated with API")
60
  else:
61
- logging.error(f"Authentication failed: {response.status_code} - {response.text}")
62
 
63
  except Exception as e:
64
- logging.error(f"Authentication Error: {e}")
65
 
66
  async def start_command(self, update: Update, context: CallbackContext):
67
- """Handles the /start command and asks for a password if the user is not authenticated."""
68
- user_id = update.message.from_user.id
69
-
70
- if user_id in AUTHENTICATED_USERS:
71
- await update.message.reply_text(" You are already authenticated! You can start chatting.")
72
- else:
73
- AWAITING_PASSWORD.add(user_id)
74
- await update.message.reply_text("🔑 Please enter the secret password to access the bot.")
75
-
76
- async def handle_message(self, update: Update, context: CallbackContext):
77
- """Handles all incoming messages."""
78
- user_id = update.message.from_user.id
79
- user_message = update.message.text.strip()
80
-
81
- # If user is waiting to enter a password, validate it
82
- if user_id in AWAITING_PASSWORD:
83
- await self.check_password(update, context)
84
  return
85
 
86
- # If user is authenticated, process AI request
87
- if user_id in AUTHENTICATED_USERS:
88
- await self.chat_with_ai(update, context)
89
- else:
90
- await update.message.reply_text("❌ You are not authenticated. Please enter the password first.")
91
 
92
- async def check_password(self, update: Update, context: CallbackContext):
93
- """Checks if the password is correct and authenticates the user."""
94
- user_id = update.message.from_user.id
95
- user_message = update.message.text.strip()
96
 
97
- if user_id in AUTHENTICATED_USERS:
98
- await update.message.reply_text("✅ You are already authenticated!")
99
- return
 
 
 
 
 
100
 
101
- if user_message == SECRET_PASSWORD:
102
- AUTHENTICATED_USERS.add(user_id) # Save authentication status
103
- AWAITING_PASSWORD.discard(user_id) # ✅ Remove user from the waiting list
104
- logging.info(f"User {user_id} authenticated successfully.")
105
- await update.message.reply_text("✅ Authentication successful! You can now use the bot.")
106
- else:
107
- await update.message.reply_text("❌ Wrong password. Try again.")
108
 
109
- async def chat_with_ai(self, update: Update, context: CallbackContext):
110
- """Handles messages and sends them to the AI API."""
111
- user_id = update.message.from_user.id
112
 
113
- if user_id not in AUTHENTICATED_USERS:
114
- await update.message.reply_text("❌ You are not authenticated. Please enter the password first.")
115
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
 
 
117
  if not self.auth_token:
118
  self.authenticate()
119
 
@@ -125,164 +131,47 @@ class TelegramBot:
125
 
126
  headers = {
127
  "Authorization": f"Bearer {self.auth_token}",
 
128
  "accept": "application/json"
129
  }
130
 
131
- json_payload = {"question": user_message}
132
- form_payload = {"question": user_message}
133
 
134
  try:
135
- logging.info(f"Sending payload as JSON: {json_payload}")
136
- response = requests.post(self.ai_url, headers={**headers, "Content-Type": "application/json"}, json=json_payload)
137
-
138
- if response.status_code == 422:
139
- logging.warning("JSON format rejected. Retrying with form-data...")
140
- response = requests.post(self.ai_url, headers={**headers, "Content-Type": "application/x-www-form-urlencoded"}, data=form_payload)
141
-
142
- logging.info(f"Response status: {response.status_code}")
143
- logging.info(f"Response content: {response.text}")
144
 
145
  if response.status_code == 200:
146
  bot_reply = response.json().get("answer", "I didn't understand that.")
147
  elif response.status_code == 401:
148
- logging.warning("Authorization expired. Re-authenticating...")
149
  self.authenticate()
150
  await self.chat_with_ai(update, context)
151
  return
152
- elif response.status_code == 422:
153
- bot_reply = "Error: The API rejected the request. Check payload format."
154
  else:
155
- bot_reply = f"Error: {response.status_code} - {response.text}"
156
 
157
  except Exception as e:
158
  bot_reply = f"Connection error: {e}"
159
 
160
  await update.message.reply_text(bot_reply)
161
 
162
- async def handle_excel(self, update: Update, context: CallbackContext):
163
- """Handles Excel file uploads."""
164
- user_id = update.message.from_user.id
165
-
166
- if user_id not in AUTHENTICATED_USERS:
167
- await update.message.reply_text("❌ You are not authenticated. Please enter the password first.")
168
- return
169
-
170
- if not self.auth_token:
171
- self.authenticate()
172
-
173
- if not self.auth_token:
174
- await update.message.reply_text("Authentication failed. Please try again later.")
175
- return
176
-
177
- try:
178
- # Send initial processing message
179
- processing_msg = await update.message.reply_text("📊 Processing your Excel file... Please wait.")
180
-
181
- # Get file from Telegram
182
- file = await context.bot.get_file(update.message.document.file_id)
183
- file_bytes = await file.download_as_bytearray()
184
-
185
- headers = {
186
- "Authorization": f"Bearer {self.auth_token}",
187
- "accept": "application/json"
188
- }
189
-
190
- async with aiohttp.ClientSession() as session:
191
- # Create form data with the file
192
- form_data = aiohttp.FormData()
193
- form_data.add_field('file',
194
- file_bytes,
195
- filename=update.message.document.file_name,
196
- content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
197
-
198
- # Upload file
199
- async with session.post(self.excel_url, headers=headers, data=form_data) as response:
200
- if response.status == 200:
201
- response_json = await response.json()
202
- job_id = response_json.get('job_id')
203
-
204
- if not job_id:
205
- await update.message.reply_text("❌ Error: No job ID received from server")
206
- return
207
-
208
- await processing_msg.edit_text("✅ Excel file uploaded successfully!\n⏳ Processing questions and preparing email...")
209
-
210
- # Check status periodically
211
- max_retries = 180 # 30 minutes maximum (with 10-second intervals)
212
- retry_count = 0
213
-
214
- while retry_count < max_retries:
215
- try:
216
- status_url = f"{self.excel_url}/status/{job_id}"
217
- async with session.get(status_url, headers=headers) as status_response:
218
- if status_response.status == 200:
219
- status_data = await status_response.json()
220
- current_status = status_data.get('status')
221
-
222
- if current_status == 'completed':
223
- await processing_msg.edit_text("✅ Processing complete!\n📧 Email has been sent successfully! Please check your inbox.")
224
- break
225
- elif current_status == 'failed':
226
- error_msg = status_data.get('error', 'Unknown error')
227
- await processing_msg.edit_text(f"❌ Failed to process the file: {error_msg}")
228
- break
229
- elif current_status == 'processing':
230
- if retry_count % 18 == 0: # Update message every ~3 minutes
231
- await processing_msg.edit_text(
232
- "⏳ Still processing your questions...\n"
233
- "This may take several minutes depending on the number of questions.\n"
234
- "You'll receive an email when it's done."
235
- )
236
- elif status_response.status == 404:
237
- await processing_msg.edit_text("❌ Job not found. Please try uploading again.")
238
- break
239
-
240
- except Exception as e:
241
- logging.error(f"Error checking status: {e}")
242
- await processing_msg.edit_text("❌ Error while checking processing status.")
243
- break
244
-
245
- if retry_count >= max_retries:
246
- await processing_msg.edit_text(
247
- "⏳ Your file is still being processed.\n"
248
- "Don't worry - you'll receive an email when it's complete.\n"
249
- "You can continue using the bot for other questions."
250
- )
251
-
252
- elif response.status == 401:
253
- logging.warning("Authorization expired. Re-authenticating...")
254
- self.authenticate()
255
- await self.handle_excel(update, context)
256
- else:
257
- error_text = await response.text()
258
- await update.message.reply_text(f"❌ Error processing Excel file: {error_text}")
259
-
260
- except Exception as e:
261
- logging.error(f"Error handling Excel file: {e}")
262
- await update.message.reply_text(f"❌ Error processing Excel file: {str(e)}")
263
-
264
  def setup_handlers(self):
265
  """Set up Telegram command and message handlers."""
266
  self.app.add_handler(CommandHandler("start", self.start_command))
267
- self.app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message))
268
- # Add handler for document messages
269
- self.app.add_handler(MessageHandler(filters.Document.FileExtension("xlsx") | filters.Document.FileExtension("xls"), self.handle_excel))
270
 
271
  def run(self):
272
  """Start the bot and listen for messages."""
273
- logging.info("Starting Telegram bot...")
274
- asyncio.set_event_loop(asyncio.new_event_loop())
275
- try:
276
- self.app.run_polling()
277
- except Exception as e:
278
- logging.error(f"Bot failed to start: {e}")
279
 
 
280
  if __name__ == "__main__":
281
  bot = TelegramBot(
282
- bot_token=BOT_TOKEN,
283
- base_url=BASE_URL,
284
- username=API_USERNAME,
285
- password=API_PASSWORD
286
  )
287
  bot.run()
288
-
 
 
1
  import logging
 
2
  import requests
3
  from telegram import Update
4
  from telegram.ext import Application, CommandHandler, MessageHandler, filters, CallbackContext
5
+ import io
6
 
7
  # Configure logging
8
+ logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO)
9
+ logger = logging.getLogger(__name__)
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  class TelegramBot:
12
+ """A Telegram bot that communicates with an AI model via API."""
13
 
14
  def __init__(self, bot_token, base_url, username, password):
15
+ """Initialize the bot with Telegram API token, API credentials, and endpoints."""
16
  self.bot_token = bot_token
17
  self.base_url = base_url
18
  self.username = username
 
28
  self.app = Application.builder().token(self.bot_token).build()
29
  self.setup_handlers()
30
 
31
+ # Authenticate on startup
 
32
  self.authenticate()
33
 
34
  def authenticate(self):
35
  """Authenticate with the API and retrieve an access token."""
36
+ payload = {
37
+ "username": self.username,
38
+ "password": self.password,
39
+ "grant_type": "password"
40
+ }
41
+
42
+ headers = {
43
+ "Content-Type": "application/json",
44
+ "accept": "application/json"
45
+ }
46
 
47
  try:
48
  response = requests.post(self.login_url, headers=headers, json=payload)
49
 
50
  if response.status_code == 200:
51
  self.auth_token = response.json().get("access_token")
52
+ logger.info("Successfully authenticated with API!")
53
  else:
54
+ logger.error(f"Authentication failed: {response.status_code} - {response.text}")
55
 
56
  except Exception as e:
57
+ logger.error(f"Authentication Error: {e}")
58
 
59
  async def start_command(self, update: Update, context: CallbackContext):
60
+ """Handles the /start command."""
61
+ await update.message.reply_text(
62
+ "Hello! You can:\n"
63
+ "1. Send me any question as text\n"
64
+ "2. Send me an Excel file with questions (must have a 'question' column in 'rfp' sheet)"
65
+ )
66
+
67
+ async def handle_document(self, update: Update, context: CallbackContext):
68
+ """Handles Excel file uploads."""
69
+ if not self.auth_token:
70
+ self.authenticate()
71
+
72
+ if not self.auth_token:
73
+ await update.message.reply_text("Authentication failed. Please try again later.")
 
 
 
74
  return
75
 
76
+ document = update.message.document
77
+ if not document.file_name.endswith(('.xls', '.xlsx')):
78
+ await update.message.reply_text("Please send only Excel files (.xls or .xlsx)")
79
+ return
 
80
 
81
+ try:
82
+ # Download the file from Telegram
83
+ file = await context.bot.get_file(document.file_id)
84
+ file_content = await file.download_as_bytearray()
85
 
86
+ # Prepare the file for upload to our API
87
+ files = {
88
+ 'file': (document.file_name, io.BytesIO(file_content), 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
89
+ }
90
+ headers = {
91
+ "Authorization": f"Bearer {self.auth_token}",
92
+ "accept": "application/json"
93
+ }
94
 
95
+ # Send processing message
96
+ status_message = await update.message.reply_text("Processing your Excel file... This may take a few minutes.")
 
 
 
 
 
97
 
98
+ # Send to our API
99
+ response = requests.post(self.excel_url, headers=headers, files=files)
 
100
 
101
+ if response.status_code == 200:
102
+ # Get the processed Excel file from the response
103
+ result = response.json()
104
+ file_content = result.get("file_content")
105
+ filename = result.get("filename")
106
+
107
+ # Send the processed Excel file back to Telegram
108
+ await context.bot.send_document(
109
+ chat_id=update.effective_chat.id,
110
+ document=io.BytesIO(bytes(file_content)),
111
+ filename=filename
112
+ )
113
+ await status_message.delete()
114
+ else:
115
+ error_message = f"Error processing file: {response.text}"
116
+ await status_message.edit_text(error_message)
117
+
118
+ except Exception as e:
119
+ await update.message.reply_text(f"Error processing file: {str(e)}")
120
 
121
+ async def chat_with_ai(self, update: Update, context: CallbackContext):
122
+ """Handles text messages and sends them to the AI API."""
123
  if not self.auth_token:
124
  self.authenticate()
125
 
 
131
 
132
  headers = {
133
  "Authorization": f"Bearer {self.auth_token}",
134
+ "Content-Type": "application/x-www-form-urlencoded",
135
  "accept": "application/json"
136
  }
137
 
138
+ payload = {"question": user_message}
 
139
 
140
  try:
141
+ response = requests.post(self.ai_url, headers=headers, data=payload)
 
 
 
 
 
 
 
 
142
 
143
  if response.status_code == 200:
144
  bot_reply = response.json().get("answer", "I didn't understand that.")
145
  elif response.status_code == 401:
146
+ logger.warning("Authorization expired. Re-authenticating...")
147
  self.authenticate()
148
  await self.chat_with_ai(update, context)
149
  return
 
 
150
  else:
151
+ bot_reply = f"Error: {response.status_code}"
152
 
153
  except Exception as e:
154
  bot_reply = f"Connection error: {e}"
155
 
156
  await update.message.reply_text(bot_reply)
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  def setup_handlers(self):
159
  """Set up Telegram command and message handlers."""
160
  self.app.add_handler(CommandHandler("start", self.start_command))
161
+ self.app.add_handler(MessageHandler(filters.DOCUMENT, self.handle_document))
162
+ self.app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.chat_with_ai))
 
163
 
164
  def run(self):
165
  """Start the bot and listen for messages."""
166
+ logger.info("Bot is now listening for messages...")
167
+ self.app.run_polling()
 
 
 
 
168
 
169
+ # --- Initialize and Run Bot ---
170
  if __name__ == "__main__":
171
  bot = TelegramBot(
172
+ bot_token="7218319671:AAF6XyrAJrsent3fMOjQQus9XnZJxRH99BQ",
173
+ base_url="https://22ab-86-19-236-226.ngrok-free.app",
174
+ username="admin",
175
+ password="admin"
176
  )
177
  bot.run()