-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtelegram_bot.py
More file actions
151 lines (137 loc) · 5.38 KB
/
telegram_bot.py
File metadata and controls
151 lines (137 loc) · 5.38 KB
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
#!/usr/bin/env python3
import os
import sys
import asyncio
import logging
from telegram import BotCommand
from telegram.ext import (
ApplicationBuilder,
CommandHandler,
MessageHandler,
CallbackQueryHandler,
filters,
)
from telegram.error import TimedOut, NetworkError, TelegramError
from bot.config import TOKEN, AUTHORIZED_USER_ID, PROXY_URL
from bot.handlers import (
handle_help,
handle_status,
handle_history,
handle_continue,
handle_delete,
handle_free,
handle_flash,
handle_pro,
handle_new,
handle_stop,
handle_message,
handle_file,
handle_voice_toggle,
handle_restart,
)
from bot.menu import handle_menu, handle_callback, build_main_menu
from bot.state import set_bot_start_time
from bot.scheduler import scheduler_loop
def build_application():
app = (
ApplicationBuilder()
.token(TOKEN)
.proxy(PROXY_URL)
.connect_timeout(30)
.read_timeout(30)
.get_updates_read_timeout(60)
.build()
)
app.add_handler(CommandHandler("help", handle_help))
app.add_handler(CommandHandler("status", handle_status))
app.add_handler(CommandHandler("history", handle_history))
app.add_handler(CommandHandler("continue", handle_continue))
app.add_handler(CommandHandler("delete", handle_delete))
app.add_handler(CommandHandler("free", handle_free))
app.add_handler(CommandHandler("flash", handle_flash))
app.add_handler(CommandHandler("pro", handle_pro))
app.add_handler(CommandHandler("new", handle_new))
app.add_handler(CommandHandler("cancel", handle_stop))
app.add_handler(CommandHandler("voice", handle_voice_toggle))
app.add_handler(CommandHandler("menu", handle_menu))
app.add_handler(CommandHandler("restart", handle_restart))
app.add_handler(CallbackQueryHandler(handle_callback))
app.add_handler(MessageHandler(filters.TEXT & (~filters.COMMAND), handle_message))
app.add_handler(MessageHandler(
filters.Document.ALL | filters.PHOTO | filters.VIDEO | filters.AUDIO | filters.VOICE,
handle_file
))
_consecutive_connection_errors = 0
MAX_CONSECUTIVE_ERRORS = 10
async def error_handler(update, context):
nonlocal _consecutive_connection_errors
error = context.error
if isinstance(error, (TimedOut, NetworkError, ConnectionError, OSError)):
_consecutive_connection_errors += 1
logging.error(f"Connection error ({_consecutive_connection_errors}/{MAX_CONSECUTIVE_ERRORS}): {error}")
if _consecutive_connection_errors >= MAX_CONSECUTIVE_ERRORS:
logging.critical("Too many consecutive connection errors, restarting...")
_consecutive_connection_errors = 0
logging.shutdown()
os.execv(sys.executable, [sys.executable] + sys.argv)
else:
_consecutive_connection_errors = 0
logging.error(f"Exception: {error}")
app.add_error_handler(error_handler)
async def _post_init(app):
from datetime import datetime
set_bot_start_time(datetime.now())
try:
await app.bot.set_my_commands([
BotCommand("menu", "Show interactive menu"),
BotCommand("help", "Show help"),
BotCommand("status", "Show bot health info"),
BotCommand("history", "Show session history"),
BotCommand("continue", "Continue a session"),
BotCommand("delete", "Delete a session"),
BotCommand("new", "New session"),
BotCommand("cancel", "Stop running agent task"),
BotCommand("free", "Use free model"),
BotCommand("flash", "Use deepseek-v4-flash model"),
BotCommand("pro", "Use deepseek-v4-pro model"),
BotCommand("voice", "Toggle voice output"),
BotCommand("restart", "Restart the bot"),
])
except Exception as e:
logging.error(f"Failed to set commands: {e}")
asyncio.create_task(scheduler_loop(app))
await app.bot.send_message(
chat_id=AUTHORIZED_USER_ID,
text="🚀 Agent ready (opencode backend).",
reply_markup=build_main_menu()
)
app.post_init = _post_init
return app
def run_with_health_check():
BASE_DELAY = 5
MAX_DELAY = 300
delay = BASE_DELAY
attempt = 0
while True:
try:
attempt += 1
logging.info(f"Starting bot (attempt {attempt})...")
app = build_application()
app.run_polling(drop_pending_updates=True)
logging.info("Bot exited cleanly.")
break
except (TimedOut, NetworkError, ConnectionError, OSError) as e:
logging.warning(f"[{attempt}] Connection issue: {type(e).__name__}: {e}. Restarting in {delay}s...")
except TelegramError as e:
logging.warning(f"[{attempt}] Telegram API error: {type(e).__name__}: {e}. Restarting in {delay}s...")
except Exception as e:
logging.error(f"[{attempt}] Unexpected error: {type(e).__name__}: {e}. Restarting in {delay}s...")
time.sleep(delay)
delay = min(delay * 2, MAX_DELAY)
def main():
if not TOKEN or not AUTHORIZED_USER_ID:
logging.error("TOKEN and AUTHORIZED_USER_ID must be set")
sys.exit(1)
run_with_health_check()
if __name__ == "__main__":
main()