baha-99 commited on
Commit
24697df
Β·
verified Β·
1 Parent(s): 0fe0ff5

Update bot_telegram.py

Browse files
Files changed (1) hide show
  1. bot_telegram.py +170 -88
bot_telegram.py CHANGED
@@ -1,18 +1,33 @@
 
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,98 +43,86 @@ class TelegramBot:
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,19 +134,24 @@ class TelegramBot:
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
@@ -155,23 +163,97 @@ class TelegramBot:
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()
 
 
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
  import io
9
 
10
  # Configure logging
11
+ logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
12
+
13
+ # Load environment variables from Hugging Face Secrets
14
+ BOT_TOKEN = os.getenv("BOT_TOKEN")
15
+ BASE_URL = os.getenv("BASE_URL")
16
+ API_USERNAME = os.getenv("API_USERNAME")
17
+ API_PASSWORD = os.getenv("API_PASSWORD")
18
+
19
+ # Set the secret password for authentication
20
+ SECRET_PASSWORD = "secure123" # Change this to your desired password
21
+
22
+ # Dictionary to store authenticated users
23
+ AUTHENTICATED_USERS = set()
24
+ AWAITING_PASSWORD = set()
25
 
26
  class TelegramBot:
27
+ """A Telegram bot with password-based authentication."""
28
 
29
  def __init__(self, bot_token, base_url, username, password):
30
+ """Initialize the bot with Telegram API token, API credentials, and authentication."""
31
  self.bot_token = bot_token
32
  self.base_url = base_url
33
  self.username = username
 
43
  self.app = Application.builder().token(self.bot_token).build()
44
  self.setup_handlers()
45
 
46
+ # Authenticate with API
47
+ logging.info("Authenticating with API...")
48
  self.authenticate()
49
 
50
  def authenticate(self):
51
  """Authenticate with the API and retrieve an access token."""
52
+ payload = {"username": self.username, "password": self.password}
53
+ headers = {"Content-Type": "application/json", "accept": "application/json"}
 
 
 
 
 
 
 
 
54
 
55
  try:
56
  response = requests.post(self.login_url, headers=headers, json=payload)
57
 
58
  if response.status_code == 200:
59
  self.auth_token = response.json().get("access_token")
60
+ logging.info("Successfully authenticated with API")
61
  else:
62
+ logging.error(f"Authentication failed: {response.status_code} - {response.text}")
63
 
64
  except Exception as e:
65
+ logging.error(f"Authentication Error: {e}")
66
 
67
  async def start_command(self, update: Update, context: CallbackContext):
68
+ """Handles the /start command and asks for a password if the user is not authenticated."""
69
+ user_id = update.message.from_user.id
70
+
71
+ if user_id in AUTHENTICATED_USERS:
72
+ await update.message.reply_text(
73
+ "βœ… You are already authenticated!\n\n"
74
+ "You can:\n"
75
+ "1. Send me any question as text\n"
76
+ "2. Send me an Excel file with questions (must have a 'question' column in 'rfp' sheet)\n\n"
77
+ "Note: Excel files must contain no more than 50 questions."
78
+ )
79
+ else:
80
+ AWAITING_PASSWORD.add(user_id)
81
+ await update.message.reply_text("πŸ”‘ Please enter the secret password to access the bot.")
82
+
83
+ async def handle_message(self, update: Update, context: CallbackContext):
84
+ """Handles all incoming messages."""
85
+ user_id = update.message.from_user.id
86
+ user_message = update.message.text.strip()
87
+
88
+ # If user is waiting to enter a password, validate it
89
+ if user_id in AWAITING_PASSWORD:
90
+ await self.check_password(update, context)
91
  return
92
 
93
+ # If user is authenticated, process AI request
94
+ if user_id in AUTHENTICATED_USERS:
95
+ await self.chat_with_ai(update, context)
96
+ else:
97
+ await update.message.reply_text("❌ You are not authenticated. Please enter the password first.")
98
+
99
+ async def check_password(self, update: Update, context: CallbackContext):
100
+ """Checks if the password is correct and authenticates the user."""
101
+ user_id = update.message.from_user.id
102
+ user_message = update.message.text.strip()
103
+
104
+ if user_message == SECRET_PASSWORD:
105
+ AUTHENTICATED_USERS.add(user_id)
106
+ AWAITING_PASSWORD.discard(user_id)
107
+ logging.info(f"User {user_id} authenticated successfully.")
108
+ await update.message.reply_text(
109
+ "βœ… Authentication successful!\n\n"
110
+ "You can:\n"
111
+ "1. Send me any question as text\n"
112
+ "2. Send me an Excel file with questions (must have a 'question' column in 'rfp' sheet)\n\n"
113
+ "Note: Excel files must contain no more than 50 questions."
114
+ )
115
+ else:
116
+ await update.message.reply_text("❌ Wrong password. Try again.")
117
 
118
+ async def chat_with_ai(self, update: Update, context: CallbackContext):
119
+ """Handles messages and sends them to the AI API."""
120
+ user_id = update.message.from_user.id
 
 
 
 
 
 
 
 
 
 
 
 
 
121
 
122
+ if user_id not in AUTHENTICATED_USERS:
123
+ await update.message.reply_text("❌ You are not authenticated. Please enter the password first.")
124
+ return
125
 
 
 
126
  if not self.auth_token:
127
  self.authenticate()
128
 
 
134
 
135
  headers = {
136
  "Authorization": f"Bearer {self.auth_token}",
 
137
  "accept": "application/json"
138
  }
139
 
140
+ json_payload = {"question": user_message}
141
+ form_payload = {"question": user_message}
142
 
143
  try:
144
+ logging.info(f"Sending payload as JSON: {json_payload}")
145
+ response = requests.post(self.ai_url, headers={**headers, "Content-Type": "application/json"}, json=json_payload)
146
+
147
+ if response.status_code == 422:
148
+ logging.warning("JSON format rejected. Retrying with form-data...")
149
+ response = requests.post(self.ai_url, headers={**headers, "Content-Type": "application/x-www-form-urlencoded"}, data=form_payload)
150
 
151
  if response.status_code == 200:
152
  bot_reply = response.json().get("answer", "I didn't understand that.")
153
  elif response.status_code == 401:
154
+ logging.warning("Authorization expired. Re-authenticating...")
155
  self.authenticate()
156
  await self.chat_with_ai(update, context)
157
  return
 
163
 
164
  await update.message.reply_text(bot_reply)
165
 
166
+ async def handle_excel(self, update: Update, context: CallbackContext):
167
+ """Handles Excel file uploads."""
168
+ user_id = update.message.from_user.id
169
+
170
+ if user_id not in AUTHENTICATED_USERS:
171
+ await update.message.reply_text("❌ You are not authenticated. Please enter the password first.")
172
+ return
173
+
174
+ if not self.auth_token:
175
+ self.authenticate()
176
+
177
+ if not self.auth_token:
178
+ await update.message.reply_text("Authentication failed. Please try again later.")
179
+ return
180
+
181
+ document = update.message.document
182
+ if not document.file_name.endswith(('.xls', '.xlsx')):
183
+ await update.message.reply_text("Please send only Excel files (.xls or .xlsx)")
184
+ return
185
+
186
+ try:
187
+ # Send initial processing message
188
+ processing_msg = await update.message.reply_text("πŸ“Š Processing your Excel file... This may take a few minutes.")
189
+
190
+ # Get file from Telegram
191
+ file = await context.bot.get_file(document.file_id)
192
+ file_bytes = await file.download_as_bytearray()
193
+
194
+ headers = {
195
+ "Authorization": f"Bearer {self.auth_token}",
196
+ "accept": "application/json"
197
+ }
198
+
199
+ async with aiohttp.ClientSession() as session:
200
+ # Create form data with the file
201
+ form_data = aiohttp.FormData()
202
+ form_data.add_field('file',
203
+ file_bytes,
204
+ filename=document.file_name,
205
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
206
+
207
+ # Send file to API
208
+ async with session.post(self.excel_url, headers=headers, data=form_data) as response:
209
+ if response.status == 200:
210
+ response_json = await response.json()
211
+ file_content = response_json.get('file_content')
212
+ filename = response_json.get('filename', 'rfp_responses.xlsx')
213
+
214
+ if not file_content:
215
+ await processing_msg.edit_text("❌ Error: No file content received from server")
216
+ return
217
+
218
+ # Convert base64 string to bytes and send back to Telegram
219
+ file_bytes = bytes(file_content)
220
+ await context.bot.send_document(
221
+ chat_id=update.effective_chat.id,
222
+ document=io.BytesIO(file_bytes),
223
+ filename=filename,
224
+ caption="βœ… Here's your processed Excel file with answers!"
225
+ )
226
+ await processing_msg.delete()
227
+
228
+ elif response.status == 401:
229
+ logging.warning("Authorization expired. Re-authenticating...")
230
+ self.authenticate()
231
+ await self.handle_excel(update, context)
232
+ else:
233
+ error_text = await response.text()
234
+ await processing_msg.edit_text(f"❌ Error processing Excel file: {error_text}")
235
+
236
+ except Exception as e:
237
+ logging.error(f"Error handling Excel file: {e}")
238
+ await update.message.reply_text(f"❌ Error processing Excel file: {str(e)}")
239
+
240
  def setup_handlers(self):
241
  """Set up Telegram command and message handlers."""
242
  self.app.add_handler(CommandHandler("start", self.start_command))
243
+ self.app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message))
244
+ self.app.add_handler(MessageHandler(filters.Document.FileExtension("xlsx") | filters.Document.FileExtension("xls"), self.handle_excel))
245
 
246
  def run(self):
247
  """Start the bot and listen for messages."""
248
+ logging.info("Starting Telegram bot...")
249
  self.app.run_polling()
250
 
 
251
  if __name__ == "__main__":
252
  bot = TelegramBot(
253
+ bot_token=BOT_TOKEN,
254
+ base_url=BASE_URL,
255
+ username=API_USERNAME,
256
+ password=API_PASSWORD
257
  )
258
  bot.run()
259
+