File size: 10,894 Bytes
24697df
8d1d329
24697df
8d1d329
 
 
24697df
0fe0ff5
8d1d329
d88c320
24697df
 
 
 
 
d00c9a8
7237f1f
 
24697df
 
 
 
 
 
 
8d1d329
d00c9a8
8d1d329
24697df
8d1d329
 
24697df
8d1d329
 
d363e07
 
 
8d1d329
 
 
 
d88c320
8d1d329
 
 
 
 
24697df
 
d363e07
 
 
 
 
 
7ec3534
d363e07
 
7ec3534
d363e07
 
 
 
 
7ec3534
d363e07
 
8d1d329
 
24697df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
845daf5
 
24697df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8d1d329
24697df
 
 
0fe0ff5
24697df
 
 
8d1d329
7ec3534
 
 
 
 
 
8d1d329
092fc79
8d1d329
d00c9a8
092fc79
d00c9a8
092fc79
 
8d1d329
d00c9a8
24697df
 
8d1d329
092fc79
24697df
 
 
 
 
d00c9a8
 
8d1d329
092fc79
 
 
24697df
7ec3534
092fc79
 
 
d00c9a8
0fe0ff5
8d1d329
092fc79
d00c9a8
092fc79
8d1d329
092fc79
8d1d329
24697df
 
 
 
 
 
 
 
7ec3534
 
 
 
 
 
24697df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d00c9a8
c7582b3
24697df
 
 
 
 
 
d00c9a8
 
 
24697df
 
 
 
c7582b3
 
d00c9a8
c7582b3
24697df
 
c7582b3
 
24697df
 
 
 
 
 
7ec3534
24697df
 
 
 
 
 
 
 
 
8d1d329
 
 
24697df
 
8d1d329
 
 
24697df
0fe0ff5
8d1d329
d00c9a8
8d1d329
 
24697df
 
 
 
8d1d329
 
24697df
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
import os
import logging
import asyncio
import requests
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, CallbackContext
import aiohttp
import io

# Configure logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")

# Load environment variables from Hugging Face Secrets
BOT_TOKEN = os.getenv("BOT_TOKEN")
BASE_URL = os.getenv("BASE_URL")
HF_TOKEN = os.getenv("HF_TOKEN")
API_USERNAME = os.getenv("API_USERNAME")
API_PASSWORD = os.getenv("API_PASSWORD")

# Set the secret password for authentication
SECRET_PASSWORD = "secure123"  # Change this to your desired password

# Dictionary to store authenticated users
AUTHENTICATED_USERS = set()
AWAITING_PASSWORD = set()


class TelegramBot:
    """A Telegram bot with password-based authentication."""

    def __init__(self, bot_token, base_url, username, password):
        """Initialize the bot with Telegram API token, API credentials, and authentication."""
        self.bot_token = bot_token
        self.base_url = base_url
        # self.username = username
        # self.password = password
        # self.auth_token = None

        # API Endpoints
        self.login_url = f"{self.base_url}/api/v1/auth/login"
        self.ai_url = f"{self.base_url}/api/v1/questions/text"
        self.excel_url = f"{self.base_url}/api/v1/questions/excel"

        # Start Telegram Bot
        self.app = Application.builder().token(self.bot_token).build()
        self.setup_handlers()

        # Authenticate with API
        logging.info("Authenticating with API...")
        # self.authenticate()

    # def authenticate(self):
    #     """Authenticate with the API and retrieve an access token."""
    #     payload = {"username": self.username, "password": self.password}
    #     headers = {"Content-Type": "application/json", "accept": "application/json"}
    #
    #     try:
    #         response = requests.post(self.login_url, headers=headers, json=payload)
    #
    #         if response.status_code == 200:
    #             self.auth_token = response.json().get("access_token")
    #             logging.info("Successfully authenticated with API")
    #         else:
    #             logging.error(f"Authentication failed: {response.status_code} - {response.text}")
    #
    #     except Exception as e:
    #         logging.error(f"Authentication Error: {e}")

    async def start_command(self, update: Update, context: CallbackContext):
        """Handles the /start command and asks for a password if the user is not authenticated."""
        user_id = update.message.from_user.id

        if user_id in AUTHENTICATED_USERS:
            await update.message.reply_text(
                "βœ… You are already authenticated!\n\n"
                "You can:\n"
                "1. Send me any question as text\n"
                "2. Send me an Excel file with questions (must have a 'question' column in 'rfp' sheet)\n\n"
                "Note: Excel files must contain no more than 50 questions."
            )
        else:
            AWAITING_PASSWORD.add(user_id)
            await update.message.reply_text("πŸ”‘ Please enter the secret password to access the bot.")

    async def handle_message(self, update: Update, context: CallbackContext):
        """Handles all incoming messages."""
        user_id = update.message.from_user.id
        user_message = update.message.text.strip()

        # If user is waiting to enter a password, validate it
        if user_id in AWAITING_PASSWORD:
            await self.check_password(update, context)
            return

        # If user is authenticated, process AI request
        if user_id in AUTHENTICATED_USERS:
            await self.chat_with_ai(update, context)
        else:
            await update.message.reply_text("❌ You are not authenticated. Please enter the password first.")

    async def check_password(self, update: Update, context: CallbackContext):
        """Checks if the password is correct and authenticates the user."""
        user_id = update.message.from_user.id
        user_message = update.message.text.strip()

        if user_message == SECRET_PASSWORD:
            AUTHENTICATED_USERS.add(user_id)
            AWAITING_PASSWORD.discard(user_id)
            logging.info(f"User {user_id} authenticated successfully.")
            await update.message.reply_text(
                "βœ… Authentication successful!\n\n"
                "You can:\n"
                "1. Send me any question as text\n"
                "2. Send me an Excel file with questions (must have a 'question' column in 'rfp' sheet)\n\n"
                "Note: Excel files must contain no more than 50 questions."
            )
        else:
            await update.message.reply_text("❌ Wrong password. Try again.")

    async def chat_with_ai(self, update: Update, context: CallbackContext):
        """Handles messages and sends them to the AI API."""
        user_id = update.message.from_user.id

        if user_id not in AUTHENTICATED_USERS:
            await update.message.reply_text("❌ You are not authenticated. Please enter the password first.")
            return

        # if not self.auth_token:
        #     self.authenticate()
        #
        # if not self.auth_token:
        #     await update.message.reply_text("Authentication failed. Please try again later.")
        #     return

        user_message = update.message.text

        hf_authorization = "Bearer " + HF_TOKEN
        headers = {
            "Authorization": hf_authorization,
            "accept": "application/json"
        }


        json_payload = {"question": user_message}
        form_payload = {"question": user_message}

        try:
            logging.info(f"Sending payload as JSON: {json_payload}")
            response = requests.post(self.ai_url, headers={**headers, "Content-Type": "application/json"}, json=json_payload)

            if response.status_code == 422:
                logging.warning("JSON format rejected. Retrying with form-data...")
                response = requests.post(self.ai_url, headers={**headers, "Content-Type": "application/x-www-form-urlencoded"},
                                         data=form_payload)

            if response.status_code == 200:
                bot_reply = response.json().get("answer", "I didn't understand that.")
            elif response.status_code == 401:
                logging.warning("Authorization expired. Re-authenticating...")
                # self.authenticate()
                await self.chat_with_ai(update, context)
                return
            else:
                logging.error(f"Error: {response.status_code}")
                bot_reply = f"Error: {response.status_code}"

        except Exception as e:
            logging.error(f"Connection error: {e}")
            bot_reply = f"Connection error: {e}"

        await update.message.reply_text(bot_reply)

    async def handle_excel(self, update: Update, context: CallbackContext):
        """Handles Excel file uploads."""
        user_id = update.message.from_user.id

        if user_id not in AUTHENTICATED_USERS:
            await update.message.reply_text("❌ You are not authenticated. Please enter the password first.")
            return

        # if not self.auth_token:
        #     self.authenticate()
        # 
        # if not self.auth_token:
        #     await update.message.reply_text("Authentication failed. Please try again later.")
        #     return

        document = update.message.document
        if not document.file_name.endswith(('.xls', '.xlsx')):
            await update.message.reply_text("Please send only Excel files (.xls or .xlsx)")
            return

        try:
            # Send initial processing message
            processing_msg = await update.message.reply_text("πŸ“Š Processing your Excel file... This may take a few minutes.")

            # Get file from Telegram
            file = await context.bot.get_file(document.file_id)
            file_bytes = await file.download_as_bytearray()

            headers = {
                "Authorization": f"Bearer {HF_TOKEN}",
                "accept": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
            }

            async with aiohttp.ClientSession() as session:
                # Create form data with the file
                form_data = aiohttp.FormData()
                form_data.add_field('file',
                                    file_bytes,
                                    filename=document.file_name,
                                    content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')

                # Send file to API
                async with session.post(self.excel_url, headers=headers, data=form_data) as response:
                    if response.status == 200:
                        # Get the Excel file content directly
                        file_content = await response.read()

                        # Send the Excel file back to Telegram
                        await context.bot.send_document(
                            chat_id=update.effective_chat.id,
                            document=io.BytesIO(file_content),
                            filename='rfp_responses.xlsx',
                            caption="βœ… Here's your processed Excel file with answers!"
                        )
                        await processing_msg.delete()

                    elif response.status == 401:
                        logging.warning("Authorization expired. Re-authenticating...")
                        # self.authenticate()
                        await self.handle_excel(update, context)
                    else:
                        error_text = await response.text()
                        await processing_msg.edit_text(f"❌ Error processing Excel file: {error_text}")

        except Exception as e:
            logging.error(f"Error handling Excel file: {e}")
            await update.message.reply_text(f"❌ Error processing Excel file: {str(e)}")

    def setup_handlers(self):
        """Set up Telegram command and message handlers."""
        self.app.add_handler(CommandHandler("start", self.start_command))
        self.app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, self.handle_message))
        self.app.add_handler(MessageHandler(filters.Document.FileExtension("xlsx") | filters.Document.FileExtension("xls"), self.handle_excel))

    def run(self):
        """Start the bot and listen for messages."""
        logging.info("Starting Telegram bot...")
        self.app.run_polling()


if __name__ == "__main__":
    bot = TelegramBot(
        bot_token=BOT_TOKEN,
        base_url=BASE_URL,
        username=API_USERNAME,
        password=API_PASSWORD
    )
    bot.run()