import asyncio import logging import os from pyrogram import Client, utils logger = logging.getLogger("pyrogram_clients") # ====================== 重要修復:Pyrogram PeerIdInvalid Bug ====================== # 針對大型 supergroup/channel ID(如 -10025xxxxxxxx)經常出現的問題 def get_peer_type_new(peer_id: int) -> str: """強制正確識別 -100 開頭的 chat_id 為 channel/supergroup""" peer_id_str = str(peer_id) if not peer_id_str.startswith("-"): return "user" elif peer_id_str.startswith("-100"): return "channel" else: return "chat" # 立即套用 patch(必須在任何 Pyrogram 操作前執行) utils.get_peer_type = get_peer_type_new logger.info("[PYRO-FIX] Monkey patch applied successfully: get_peer_type fixed for large supergroups/channels") # =============================================================================== async def perform_pyrogram_upload(client, file_path, file_name, chat_id, mime_type, size, thumbnail_path=None, task_id=None): """ 執行 Pyrogram 上傳(已加入 PeerIdInvalid 修復與進度回調) """ import time from database import db from models import UploadTask start_time = time.time() last_update_time = 0 async def progress_callback(current, total): nonlocal last_update_time now = time.time() # 每秒更新一次數據庫,避免頻繁寫入 if now - last_update_time >= 1.0 and task_id: elapsed = now - start_time speed_mb = (current / 1024 / 1024) / elapsed if elapsed > 0 else 0 speed_str = f"{speed_mb:.2f} MB/s" # 使用獨立的 session 更新,避免干擾主線程 try: task = UploadTask.query.get(task_id) if task: task.uploaded_bytes = current task.speed = speed_str db.session.commit() last_update_time = now except: db.session.rollback() try: if not os.path.exists(file_path): logger.error(f"[PYRO-FLOW] File not found: {file_path}") return [None] * 6 target_chat = int(chat_id) if isinstance(chat_id, str) else chat_id logger.info(f"[PYRO-FLOW] Starting upload: {file_name} → chat {target_chat} | mime: {mime_type}") # 嘗試預先獲取 chat(更新 access hash),失敗也不中斷 try: chat_obj = await client.get_chat(target_chat) logger.info(f"[PYRO-FLOW] get_chat success: {chat_obj.title} (type: {chat_obj.type})") except Exception as e: logger.warning(f"[PYRO-FLOW] get_chat failed (common with large IDs): {e}. Continuing with upload...") message = None if mime_type.startswith("video/"): message = await client.send_video( chat_id=target_chat, video=file_path, file_name=file_name, thumb=thumbnail_path, supports_streaming=True, progress=progress_callback ) else: message = await client.send_document( chat_id=target_chat, document=file_path, file_name=file_name, thumb=thumbnail_path, progress=progress_callback ) if message: # 兼容 video / document / audio 等 item = getattr(message, 'video', None) or \ getattr(message, 'document', None) or \ getattr(message, 'audio', None) or \ getattr(message, 'photo', None) if not item: logger.error("[PYRO-FLOW] Upload succeeded but could not extract file details.") return [None] * 6 file_id = item.file_id final_thumb_id = None if hasattr(item, 'thumbs') and item.thumbs: final_thumb_id = item.thumbs[-1].file_id elif hasattr(item, 'thumbnail') and item.thumbnail: final_thumb_id = item.thumbnail.file_id logger.info(f"[PYRO-FLOW] Upload SUCCESS! file_id: {file_id[:25]}... | msg_id: {message.id}") return file_id, file_name, mime_type, item.file_size, final_thumb_id, message.id else: logger.error("[PYRO-FLOW] send_video/send_document returned None") return [None] * 6 except Exception as e: logger.error(f"[PYRO-FLOW] Critical error during upload: {type(e).__name__}: {e}") # 如果還是出現 PeerIdInvalid,顯示更清楚的提示 if "Peer id invalid" in str(e) or "PEER_ID_INVALID" in str(e).upper(): logger.error("[PYRO-FLOW] PeerIdInvalid still occurred. Please check: " "1. Bot is added to the group as admin " "2. Chat ID is correct " "3. Try making the group public briefly then private again.") return [None] * 6