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 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
| import asyncio import random import os import json from telethon import TelegramClient, events from telethon.errors import SessionPasswordNeededError from telethon.tl.types import MessageMediaDocument
# === 配置区 === MIN_FILE_SIZE_MB = 500 MIN_FILE_SIZE = MIN_FILE_SIZE_MB * 1024 * 1024 delay_between_forwards = (5, 8)
# === 全局去重文件 === GLOBAL_KEYS_FILE = "global_video_keys.json" GLOBAL_SEEN_FILE = "global_seen_ids.json"
# === 加载全局去重数据 === if os.path.exists(GLOBAL_KEYS_FILE): with open(GLOBAL_KEYS_FILE, "r", encoding="utf-8") as f: video_keys_global = set(json.load(f)) else: video_keys_global = set()
if os.path.exists(GLOBAL_SEEN_FILE): with open(GLOBAL_SEEN_FILE, "r", encoding="utf-8") as f: seen_ids_global = set(json.load(f)) else: seen_ids_global = set()
# === 账号1 === account1 = { "session": "user1_session", "api_id": xx, "api_hash": "xx", "phone": "+xx", "two_step": "xx", "source_channel": "@xx", "target_channel": "@xx" }
# === 账号2 === account2 = { "session": "user2_session", "api_id": xx, "api_hash": "xx", "phone": "+xx", "two_step": "xx", "source_channel": "@xx", "target_channel": "@xx" }
# 保存全局去重数据 def save_global_data(): with open(GLOBAL_KEYS_FILE, "w", encoding="utf-8") as f: json.dump(list(video_keys_global), f) with open(GLOBAL_SEEN_FILE, "w", encoding="utf-8") as f: json.dump(list(seen_ids_global), f)
async def run_account(acc): print(f"📏 当前设置:账号 {acc['phone']} 只转发 ≥ {MIN_FILE_SIZE_MB} MB 的视频")
client = TelegramClient(acc["session"], acc["api_id"], acc["api_hash"]) seen_file = f"seen_ids_{acc['phone']}.json"
# 加载账号本地已转发 ID if os.path.exists(seen_file): with open(seen_file, "r", encoding="utf-8") as f: seen_ids_local = set(json.load(f)) else: seen_ids_local = set()
async def login(): await client.connect() if not await client.is_user_authorized(): print(f'🔐 使用手机号登录: {acc["phone"]}') await client.send_code_request(acc["phone"]) code = input(f"[{acc['phone']}] 请输入验证码: ") try: await client.sign_in(acc["phone"], code) except SessionPasswordNeededError: print(f"[{acc['phone']}] 🔑 输入两步验证密码...") await client.sign_in(password=acc["two_step"]) print(f"[{acc['phone']}] ✅ 登录成功")
async def load_existing_videos(): print(f"[{acc['phone']}] 🔍 扫描目标频道已有视频...") target = await client.get_entity(acc["target_channel"]) async for msg in client.iter_messages(target): if msg.media and isinstance(msg.media, MessageMediaDocument): size = msg.media.document.size filename = next((attr.file_name for attr in msg.media.document.attributes if hasattr(attr, 'file_name')), "unknown") key = f"{filename}_{size}" video_keys_global.add(key) save_global_data() print(f"[{acc['phone']}] 📂 全局视频记录数: {len(video_keys_global)}")
def get_video_key(message): size = message.media.document.size filename = next((attr.file_name for attr in message.media.document.attributes if hasattr(attr, 'file_name')), "video.mp4") return f"{filename}_{size}"
async def forward_video(message, video_key): """通用转发(带全局持久化去重)""" if video_key in video_keys_global: print(f"[{acc['phone']}] ⚠️ 跳过重复视频(全局): {video_key}") return try: await client.send_file( acc["target_channel"], file=message.media, caption=f"{message.message or ''}\n📁 原文件名: {video_key.split('_')[0]}", file_name=video_key.split('_')[0] ) video_keys_global.add(video_key) seen_ids_global.add(message.id) seen_ids_local.add(message.id) save_global_data() with open(seen_file, "w", encoding="utf-8") as f: json.dump(list(seen_ids_local), f) print(f"[{acc['phone']}] ✅ 转发成功: {video_key}") except Exception as e: print(f"[{acc['phone']}] ❌ 转发失败: {e}")
async def batch_forward(): print(f"[{acc['phone']}] 📦 批量转发历史视频...") source = await client.get_entity(acc["source_channel"]) count = 0 async for message in client.iter_messages(source, reverse=True): if message.id in seen_ids_local or message.id in seen_ids_global: continue if (message.video or isinstance(message.media, MessageMediaDocument)) and \ message.media and hasattr(message.media, "document") and \ message.media.document.size >= MIN_FILE_SIZE: video_key = get_video_key(message) await forward_video(message, video_key) count += 1 await asyncio.sleep(random.uniform(*delay_between_forwards)) print(f"[{acc['phone']}] 📁 历史转发完成,共转发 {count} 个视频")
@client.on(events.NewMessage(chats=acc["source_channel"])) async def live_forward(event): if event.message.id in seen_ids_local or event.message.id in seen_ids_global: return if (event.video or isinstance(event.media, MessageMediaDocument)) and \ event.media and hasattr(event.media, "document") and \ event.media.document.size >= MIN_FILE_SIZE: video_key = get_video_key(event.message) await forward_video(event.message, video_key)
async with client: await login() await load_existing_videos() await batch_forward() print(f"[{acc['phone']}] ⌛ 开始监听 {acc['source_channel']} ...") await client.run_until_disconnected()
async def main(): await asyncio.gather( run_account(account1), run_account(account2) )
if __name__ == "__main__": asyncio.run(main())
|