2024-08-19 11:31:06 -05:00
import os
2024-08-19 11:35:29 -05:00
import logging
2024-08-20 12:37:35 -05:00
import sys
2024-08-20 14:11:19 -05:00
import asyncio
2024-08-20 14:21:41 -05:00
import time
2024-08-19 11:31:06 -05:00
from telegram import Update , InlineKeyboardButton , InlineKeyboardMarkup
from telegram . ext import Application , CommandHandler , MessageHandler , filters , ContextTypes , CallbackQueryHandler
2024-08-20 16:52:09 -05:00
from browse_command import browse_command , button_callback
2024-08-19 11:31:06 -05:00
class TelegramHelper :
2025-06-02 14:55:30 -05:00
CLAUDE_REBOOT_TARGET = ' claude '
HTML_QUOTE_BLOCK_START = ' <blockquote expandable><b>Thinking...</b> '
HTML_QUOTE_BLOCK_END = ' </blockquote> '
2025-06-02 16:36:51 -05:00
DEFAULT_REBOOT_CLAUDE_FILE = ' .reboot_claude '
DEFAULT_REBOOT_FILE = ' .doreboot '
2025-06-02 14:55:30 -05:00
2025-06-02 16:36:51 -05:00
def __init__ ( self , bot , reboot_claude_file_path : str | None = None , reboot_file_path : str | None = None ) :
2024-08-19 11:31:06 -05:00
self . bot = bot
2024-08-19 11:35:29 -05:00
self . telegram_bot_token = os . getenv ( ' TELEGRAM_BOT_TOKEN ' )
2024-08-20 14:21:41 -05:00
self . start_time = time . time ( )
2025-06-02 16:35:52 -05:00
self . reboot_claude_file = reboot_claude_file_path or self . DEFAULT_REBOOT_CLAUDE_FILE
self . reboot_file = reboot_file_path or self . DEFAULT_REBOOT_FILE
2024-08-19 11:31:06 -05:00
2025-06-02 16:36:51 -05:00
# --- Start Command ---
async def _start_logic ( self ) - > str : # New logic method
2024-08-19 11:31:06 -05:00
await self . bot . start ( )
2025-06-02 16:36:51 -05:00
return " Hello! I ' m your AI assistant. How can I help you today? "
2024-08-19 11:31:06 -05:00
2025-06-02 16:36:51 -05:00
async def start ( self , update : Update , context : ContextTypes . DEFAULT_TYPE ) - > None : # Modified
response_message = await self . _start_logic ( )
await update . message . reply_text ( response_message )
# --- Clear Command ---
async def _clear_logic ( self , user_id : int ) - > str : # New logic method
2025-06-02 15:23:20 -05:00
self . bot . clear_conversation_history ( user_id )
2025-06-02 16:36:51 -05:00
return " Conversation history cleared. Let ' s start fresh! "
async def clear ( self , update : Update , context : ContextTypes . DEFAULT_TYPE ) - > None : # Modified
user_id = update . effective_user . id
2025-06-02 16:38:11 -05:00
response_message = await self . _clear_logic ( user_id )
await update . message . reply_text ( response_message )
2025-06-02 16:36:51 -05:00
# --- Status Command ---
async def _status_logic ( self ) - > str : # New logic method
return await self . bot . get_bot_status ( )
2024-08-19 11:31:06 -05:00
2025-06-02 16:36:51 -05:00
async def status ( self , update : Update , context : ContextTypes . DEFAULT_TYPE ) - > None : # Modified
response_message = await self . _status_logic ( )
await update . message . reply_text ( response_message )
2024-08-19 11:31:06 -05:00
2025-06-02 16:36:51 -05:00
# --- Switch Command ---
async def _switch_logic ( self ) - > str : # New logic method
2024-08-19 11:35:29 -05:00
if hasattr ( self . bot , ' switch_model ' ) :
2025-06-02 16:36:51 -05:00
return await self . bot . switch_model ( )
2024-08-19 11:35:29 -05:00
else :
2025-06-02 16:36:51 -05:00
return " Model switching is not supported for this bot. "
async def switch ( self , update : Update , context : ContextTypes . DEFAULT_TYPE ) - > None : # Modified
response_message = await self . _switch_logic ( )
await update . message . reply_text ( response_message )
2024-08-19 11:31:06 -05:00
async def update_status_message ( self , context : ContextTypes . DEFAULT_TYPE , chat_id : int , message_id : int , status : str ) :
keyboard = [
[ InlineKeyboardButton ( " Abort " , callback_data = ' abort ' ) ]
]
reply_markup = InlineKeyboardMarkup ( keyboard )
await context . bot . edit_message_text (
chat_id = chat_id ,
message_id = message_id ,
text = f " Current status: { status } " ,
reply_markup = reply_markup
)
2024-08-19 11:35:29 -05:00
async def handle_message ( self , update : Update , context : ContextTypes . DEFAULT_TYPE ) - > None :
try :
user_id = update . effective_user . id
user_message = update . message . text
logging . info ( f " Message from user { user_id } : { user_message } " )
2025-06-02 16:36:51 -05:00
status_message_obj = await update . message . reply_text ( " Processing your request... " , reply_markup = InlineKeyboardMarkup ( [ [ InlineKeyboardButton ( " Abort " , callback_data = ' abort ' ) ] ] ) )
self . bot . set_processing_status ( user_id , status_message_obj . message_id )
2024-08-19 11:35:29 -05:00
response = await self . bot . handle_message ( user_id , user_message )
2025-06-02 16:36:51 -05:00
await context . bot . delete_message ( chat_id = update . effective_chat . id , message_id = status_message_obj . message_id )
2025-06-02 15:23:20 -05:00
self . bot . clear_processing_status ( user_id )
2025-06-02 14:55:30 -05:00
response = response . replace ( " <think> " , self . HTML_QUOTE_BLOCK_START ) . replace ( " </think> " , self . HTML_QUOTE_BLOCK_END )
2025-06-02 13:23:02 -05:00
if len ( response ) > 4096 :
chunks = [ response [ i : i + 4096 ] for i in range ( 0 , len ( response ) , 4096 ) ]
for chunk in chunks :
await update . message . reply_text ( chunk )
await asyncio . sleep ( 0.1 )
2025-06-02 14:55:30 -05:00
else :
2025-06-02 13:23:02 -05:00
await update . message . reply_text ( response )
2024-08-19 11:35:29 -05:00
except Exception as e :
logging . error ( f " An error occurred: { str ( e ) } " )
await update . message . reply_text ( " Sorry, an error occurred while processing your request. " )
2025-06-02 16:38:11 -05:00
# --- Abort Processing (Callback) ---
async def _abort_processing_logic ( self , user_id : int ) - > str : # New logic method
return await self . bot . abort_processing ( user_id )
async def abort_processing ( self , update : Update , context : ContextTypes . DEFAULT_TYPE ) - > None : # Modified
2024-08-19 11:35:29 -05:00
query = update . callback_query
2025-06-02 16:38:11 -05:00
await query . answer ( ) # Telegram specific interaction
2025-06-02 14:55:30 -05:00
2024-08-19 11:35:29 -05:00
user_id = query . from_user . id
2025-06-02 16:38:11 -05:00
response_text = await self . _abort_processing_logic ( user_id ) # Call logic method
await query . edit_message_text ( text = response_text ) # Telegram specific interaction
2024-08-19 11:35:29 -05:00
2024-08-20 12:37:35 -05:00
async def reboot ( self , update : Update , context : ContextTypes . DEFAULT_TYPE ) - > None :
2025-06-02 14:55:30 -05:00
user_message = update . message . text . split ( )
if len ( user_message ) > 1 and user_message [ 1 ] . lower ( ) == self . CLAUDE_REBOOT_TARGET :
2025-06-02 16:35:52 -05:00
open ( self . reboot_claude_file , ' w ' ) . close ( )
2024-08-21 17:07:00 -05:00
2024-08-20 14:28:51 -05:00
if update :
await update . message . reply_text ( " Rebooting the bot... " )
2024-08-20 13:11:24 -05:00
logging . info ( " Received reboot command. Exiting process... " )
2025-06-02 16:36:51 -05:00
2025-06-02 16:35:52 -05:00
reboot_f_path = self . reboot_file
if not os . path . exists ( reboot_f_path ) :
with open ( reboot_f_path , ' w ' ) as f :
2025-06-02 16:36:51 -05:00
chat_id_to_write = str ( update . effective_chat . id ) if update and update . effective_chat else " "
f . write ( chat_id_to_write )
2024-08-20 13:11:24 -05:00
sys . exit ( 0 )
2024-08-20 12:37:35 -05:00
2024-08-20 14:09:12 -05:00
async def check_doreboot_file ( self , application : Application ) :
2025-06-02 16:35:52 -05:00
reboot_f_path = self . reboot_file
if os . path . exists ( reboot_f_path ) :
with open ( reboot_f_path , ' r ' ) as f :
2024-08-20 14:09:12 -05:00
chat_id = f . read ( ) . strip ( )
2024-08-20 14:28:51 -05:00
if chat_id :
2025-06-02 16:36:51 -05:00
try :
await application . bot . send_message ( chat_id = chat_id , text = " The application has finished initializing. " )
except Exception as e :
logging . error ( f " Failed to send reboot notification to chat_id { chat_id } : { e } " )
2025-06-02 16:35:52 -05:00
os . remove ( reboot_f_path )
2024-08-20 14:09:12 -05:00
2024-08-20 16:52:09 -05:00
async def browse ( self , update : Update , context : ContextTypes . DEFAULT_TYPE ) - > None :
2024-08-21 13:26:34 -05:00
await browse_command ( update , context , self . bot )
2024-08-20 16:52:09 -05:00
2024-08-19 11:31:06 -05:00
def run ( self ) :
2024-08-19 11:35:29 -05:00
application = Application . builder ( ) . token ( self . telegram_bot_token ) . build ( )
2024-08-19 11:31:06 -05:00
application . add_handler ( CommandHandler ( " start " , self . start ) )
application . add_handler ( CommandHandler ( " clear " , self . clear ) )
2024-08-19 11:35:29 -05:00
application . add_handler ( CommandHandler ( " switch " , self . switch ) )
2024-08-19 11:31:06 -05:00
application . add_handler ( CommandHandler ( " status " , self . status ) )
2024-08-20 12:37:35 -05:00
application . add_handler ( CommandHandler ( " reboot " , self . reboot ) )
2024-08-20 16:52:09 -05:00
application . add_handler ( CommandHandler ( " browse " , self . browse ) )
2025-06-02 15:23:20 -05:00
application . add_handler ( MessageHandler ( filters . TEXT & ~ filters . COMMAND , self . handle_message ) )
2024-08-19 11:31:06 -05:00
application . add_handler ( CallbackQueryHandler ( self . abort_processing , pattern = ' ^abort$ ' ) )
2024-08-20 16:52:09 -05:00
application . add_handler ( CallbackQueryHandler ( button_callback , pattern = ' ^(browse|file): ' ) )
2025-06-02 14:55:30 -05:00
2024-08-19 11:35:29 -05:00
logging . info ( " Bot is running... " )
2024-08-20 13:52:32 -05:00
2025-06-02 16:36:51 -05:00
loop = asyncio . get_event_loop ( )
if loop . is_running ( ) :
loop . create_task ( self . check_doreboot_file ( application ) )
else :
2025-06-02 16:38:11 -05:00
asyncio . run ( self . check_doreboot_file ( application ) )
2025-06-02 14:55:30 -05:00
application . run_polling ( )