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())