main
1#!/venv/bin/python
2# -*- coding: utf-8 -*-
3import argparse
4import asyncio
5import json
6import logging
7import os
8import platform
9import random
10import sys
11from json import JSONDecodeError
12from urllib.parse import urlparse
13
14from apscheduler.schedulers.asyncio import AsyncIOScheduler
15from loguru import logger
16from pyrogram import filters
17from pyrogram.client import Client
18from pyrogram.sync import idle
19from pyrogram.types import LinkPreviewOptions, Message
20
21from ai.chat_summary import daily_summary
22from ai.utils import clean_anthropic_files, clean_gemini_files
23from bridge.chartimg import forward_chartimg_results
24from bridge.ocr import forward_ocr_results
25from bridge.social import forward_social_media_results
26from config import DAILY_MESSAGES, DEVICE_NAME, ENABLE_CRONTAB, PROXY, TOKEN, TZ, cache
27from custom.ai_news import daily_ainews
28from custom.config import ACCOUNT_NAME
29from custom.cyf_greeting import cyf_greeting
30from custom.cyf_quote import fafa_quote
31from custom.cyf_twitter_rss import fafa_twitter_rss
32from custom.d1_daily_backup_msg import daily_backup_history_to_d1
33from custom.del_msg import del_unwanted_message
34from custom.dnkt_attendance import dnkt_attendance
35from custom.dnkt_email import dnkt_email
36from custom.email2md import eml2md
37from custom.events import lottery
38from custom.history_alias import tg_history_alias
39from custom.lilaoshi import handle_lilaoshi, preview_lilaoshi_history_message
40from custom.link_extract import link_extract
41from custom.msg_backup import message_backup
42from custom.readhub import readhub
43from custom.restart import restart_bot
44from custom.rss import update_rss
45from custom.summary_video import summary_videos
46from custom.sync_youtube import sync_youtube
47from custom.tempmail import tempmail
48from danmu.sync import sync_livechats
49from database.r2 import clean_r2_expired
50from emby.checkin import checkin_emby, daily_checkin_emby
51from emby.keepalive import keepalive_emby
52from emby.main import emby_entrypoint
53from emby.register import emby_register
54from history.sync import backup_chat_history, sync_chat_history
55from messages.main import process_message
56from messages.modify import message_modify
57from messages.parser import parse_msg
58from messages.utils import delete_message, startswith_prefix
59from permission import set_permission
60from podcast.main import summary_pods
61from utils import cleanup_old_files, to_int
62
63
64async def main():
65 app = Client(
66 "bot",
67 session_string=session_string,
68 in_memory=True,
69 proxy=proxy,
70 device_model=DEVICE_NAME, # A friendly name can be viewed in "Active Sessions" in Telegram settings
71 app_version=f"{Client.APP_VERSION}, Python {platform.python_version()}",
72 skip_updates=False, # handle messages while client is offline
73 fetch_replies=-1, # fetch all replies
74 link_preview_options=LinkPreviewOptions(is_disabled=True),
75 max_concurrent_transmissions=2,
76 max_business_user_connection_cache_size=100, # reduce memory usage
77 max_message_cache_size=100, # reduce memory usage
78 )
79
80 @app.on_message(filters.group)
81 async def groups(client: Client, message: Message):
82 permission = set_permission(message)
83 if permission["disabled"]:
84 return
85 await process_message(client, message, **permission)
86
87 @app.on_message(filters.channel)
88 async def channels(client: Client, message: Message):
89 permission = set_permission(message)
90 if permission["disabled"]:
91 return
92 await process_message(client, message, **permission)
93
94 @app.on_message(filters.bot)
95 async def bots(client: Client, message: Message):
96 permission = set_permission(message)
97 if permission["disabled"]:
98 return
99 parse_msg(message, verbose=True)
100 await forward_social_media_results(client, message)
101 await forward_ocr_results(client, message)
102 await forward_chartimg_results(client, message)
103 await process_message(client, message, **permission)
104
105 # filters.private = {user chats + bot chats}
106 # so the private handler should be placed after the bot handler
107 @app.on_message(filters.private)
108 async def private(client: Client, message: Message):
109 ctype = message.chat.type.name if message.chat and message.chat.type else ""
110 if ctype == "BOT":
111 await bots(client, message) # handle bot messages
112 return
113 permission = set_permission(message)
114 if permission["disabled"]:
115 return
116 parse_msg(message, verbose=True)
117 await process_message(client, message, **permission)
118
119 @app.on_message(group=1)
120 @app.on_edited_message(group=1)
121 @app.on_deleted_messages(group=1)
122 async def on_changed(client: Client, message: Message | list[Message]):
123 await sync_chat_history(client, message)
124 if isinstance(message, Message):
125 await checkin_emby(client, message)
126
127 @app.on_message(group=2)
128 async def custom(client: Client, message: Message):
129 message = message_modify(message)
130 if ACCOUNT_NAME == "benny":
131 await emby_entrypoint(message)
132 if startswith_prefix(message.content, ["/save"]):
133 await delete_message(message)
134 await tempmail(client, message)
135
136 if ACCOUNT_NAME == "xiaohao":
137 await fafa_quote(client, message)
138 await cyf_greeting(client, message)
139 await handle_lilaoshi(client, message) # 李老师不是你老师
140 # await chenyifa_social_rss(client, message) # CYF社媒追踪
141
142 await restart_bot(message)
143 await lottery(client, message)
144 await emby_register(client, message)
145 await tg_history_alias(client, message)
146 await summary_videos(client, message) # 自动总结视频
147 await message_backup(client, message)
148 await del_unwanted_message(client, message)
149 await eml2md(client, message)
150 await link_extract(client, message)
151
152 if ENABLE_CRONTAB:
153 scheduler = AsyncIOScheduler(timezone=TZ)
154 # scheduler.add_job(cron_secondly, "interval", args=[app], seconds=1)
155 scheduler.add_job(cron_minutely, "interval", args=[app], minutes=1)
156 scheduler.add_job(cron_hourly, "cron", args=[app], minute=0)
157 scheduler.add_job(cron_daily, "cron", args=[app], hour=6, minute=40, jitter=4800)
158 logging.getLogger("apscheduler.scheduler").setLevel(logging.ERROR)
159 scheduler.start()
160
161 await app.start()
162 await idle()
163 await app.stop()
164
165
166# async def cron_secondly(client: Client):
167# pass
168
169
170async def cron_minutely(client: Client):
171 cache.evict() # delete expired cache
172 cleanup_old_files()
173 await backup_chat_history(client)
174 await daily_backup_history_to_d1(client)
175 if ACCOUNT_NAME == "benny":
176 pass
177 elif ACCOUNT_NAME == "xiaohao":
178 await dnkt_attendance(client)
179 await fafa_twitter_rss(client)
180 elif ACCOUNT_NAME == "bot":
181 await dnkt_email(client)
182 await readhub()
183 await sync_youtube(client)
184 await update_rss(client)
185 await keepalive_emby()
186
187
188async def cron_hourly(client: Client):
189 await daily_summary(client)
190 await sync_livechats()
191 await summary_pods(client)
192 if ACCOUNT_NAME == "xiaohao":
193 await preview_lilaoshi_history_message(client) # 解析李老师遗漏的历史消息
194 await clean_r2_expired()
195 await clean_anthropic_files()
196 await clean_gemini_files()
197 if ACCOUNT_NAME == "bot":
198 await daily_ainews()
199
200
201async def cron_daily(client: Client):
202 # send daliy messages
203 await daily_checkin_emby(client)
204 try:
205 daliy = json.loads(DAILY_MESSAGES)
206 for chat_id, msg in daliy.items():
207 logger.info(f"Sending daily message to {chat_id}: {msg}")
208 await client.send_message(to_int(chat_id), msg)
209 await asyncio.sleep(random.randint(3, 8))
210 except (JSONDecodeError, TypeError):
211 logger.warning(f"Invalid DAILY_MESSAGES: {DAILY_MESSAGES}")
212 except Exception as e:
213 logger.warning(f"Error sending daily message: {e}")
214
215
216if __name__ == "__main__":
217 parser = argparse.ArgumentParser()
218 parser.add_argument("--log-level", type=str, help="Log level")
219 parser.add_argument("--session-str", type=str, help="Telegram SESSION STRING")
220 parser.add_argument("--proxy", type=str, help="Telegram proxy (e.g. socks5://127.0.0.1:7890)")
221 args = parser.parse_args()
222
223 logger.remove() # Remove default handler.
224 logger.add(
225 sys.stderr,
226 level=args.log_level.upper() if args.log_level else os.getenv("LOG_LEVEL", "TRACE").upper(),
227 colorize=True,
228 backtrace=True,
229 diagnose=True,
230 format="<green>{time:YYYY-MM-DD HH:mm:ss}</green>| <level>{level: <7}</level> |<cyan>{name: <12}</cyan>:<cyan>{function: ^20}</cyan>:<cyan>{line: >4}</cyan> - <level>{message}</level>",
231 )
232 # settings
233 session_string = args.session_str or TOKEN.SESSION_STRING
234 if not session_string:
235 logger.error("No session string, you should run python scripts/auth.py first")
236 os._exit(1)
237
238 if args.proxy or PROXY.TELEGRAM:
239 info = urlparse(args.proxy) if args.proxy else urlparse(PROXY.TELEGRAM)
240 proxy = {"scheme": info.scheme, "hostname": info.hostname, "port": info.port}
241 logger.warning(f"Using proxy: {proxy}")
242 else:
243 proxy = {}
244
245 asyncio.run(main())