AVRIL_START_JANCOKALIVEAVRIL_END_JANCOK Interactive Terminal

Command Executor

remove_duplicates – town
town
    Uncategorized 尚无浏览 3 分钟阅读

    remove_duplicates

    import asyncio
    import os
    import sys
    from telethon import TelegramClient, errors
    from telethon.tl.types import MessageMediaDocument, DocumentAttributeFilename
    from dotenv import load_dotenv
    
    # ================== 1. 读取配置  频道视频去重==================
    load_dotenv()
    
    try:
        api_id = int(os.getenv("API_ID"))
        api_hash = os.getenv("API_HASH")
        PHONE_NUMBER = os.getenv("PHONE_NUMBER")
        TWO_STEP_PASSWORD = os.getenv("TWO_STEP_PASSWORD") or None
        
        # --- 修复:自动判断频道 ID 是数字还是用户名 ---
        raw_target = os.getenv("TARGET_CHANNEL")
        try:
            # 如果是数字(如 -100xxx),转换为整数
            TARGET_CHANNEL = int(raw_target)
        except (ValueError, TypeError):
            # 如果不是数字(如 @username),保持字符串
            TARGET_CHANNEL = raw_target
    
        # 扫描限制:设置为想要扫描的【视频数量】
        scan_env = os.getenv("SCAN_LIMIT", "21000")
        TARGET_VIDEO_COUNT = int(scan_env)
        
    except Exception as e:
        print(f"❌ 配置错误: {e}")
        sys.exit(1)
    
    client = TelegramClient("session/user_session", api_id, api_hash)
    
    # ================== 2. 工具函数 ==================
    def get_video_info(message):
        """
        解析视频信息
        返回: (is_video, file_id, file_name)
        """
        if not message.media or not isinstance(message.media, MessageMediaDocument):
            return False, None, None
    
        doc = message.media.document
        
        # 判定是否为视频 mime 类型
        if not (doc.mime_type and doc.mime_type.startswith("video/")):
            return False, None, None
    
        # 获取文件名(仅用于显示,不用于判重)
        file_name = "未知文件名"
        for attr in doc.attributes:
            if isinstance(attr, DocumentAttributeFilename):
                file_name = attr.file_name
                break
    
        # Telethon document.id 是该文件在 TG 系统内的唯一标识
        return True, doc.id, file_name
    
    # ================== 3. 主逻辑 ==================
    async def main():
        print("🔐 正在登录 Telegram...")
        # 自动处理登录,如果是第一次运行,控制台会要求输入验证码
        await client.start(phone=PHONE_NUMBER, password=TWO_STEP_PASSWORD)
        print("✅ 登录成功")
    
        try:
            # 获取频道实体对象
            target = await client.get_entity(TARGET_CHANNEL)
            target_name = getattr(target, "title", TARGET_CHANNEL)
        except Exception as e:
            print(f"❌ 无法获取频道信息: {TARGET_CHANNEL}")
            print(f"   原因: {e}")
            print("   提示: 请确保你已经加入了该频道,且 ID 填写正确(ID必须是整数,不带引号)。")
            return
    
        # 显示当前任务模式
        mode_str = "无限 (直到扫描完所有历史)" if TARGET_VIDEO_COUNT == 0 else f"最近 {TARGET_VIDEO_COUNT} 个视频"
        print(f"\n📺 目标频道:{target_name}")
        print(f"🎯 扫描目标:{mode_str}")
        print(f"⚙️ 判重策略:保留【最新】发布的视频,删除旧的重复项")
        print("-" * 40)
    
        seen_keys = set()    # 记录已出现的视频 ID
        duplicates = []      # 存储待删除的消息 [(msg_id, file_name), ...]
    
        scanned_msgs = 0     # 扫描过的消息总数(含文字/图片)
        found_videos = 0     # 找到的视频数
    
        print("⏳ 正在扫描消息 (顺序:从新 -> 旧)...")
    
        # limit=None 表示如果不手动 break,就一直扫描下去
        async for msg in client.iter_messages(target, limit=None):
            scanned_msgs += 1
            
            is_vid, file_id, file_name = get_video_info(msg)
    
            if not is_vid:
                continue
    
            # 找到一个视频
            found_videos += 1
            
            # 核心判重逻辑
            if file_id in seen_keys:
                # 已经在 seen_keys 里,说明之前扫描到了(即更新的消息里有这个视频)
                # 所以当前这条较旧的消息是重复的
                duplicates.append((msg.id, file_name))
            else:
                seen_keys.add(file_id)
    
            # 打印进度条
            if found_videos % 20 == 0:
                 print(f"   已检索 {found_videos} 个视频 (总扫描消息 {scanned_msgs} 条)...")
    
            # 达到数量限制,退出循环
            if TARGET_VIDEO_COUNT != 0 and found_videos >= TARGET_VIDEO_COUNT:
                print(f"✅ 已达到设定的 {TARGET_VIDEO_COUNT} 个视频目标,停止扫描。")
                break
    
        print("-" * 40)
        print("📊 扫描结果统计")
        print(f"   总扫描消息数:{scanned_msgs}")
        print(f"   检索视频总数:{found_videos}")
        print(f"   发现重复视频:{len(duplicates)}")
    
        if not duplicates:
            print("✅ 没有发现需要删除的重复视频")
            return
    
        print(f"\n⚠️ 即将删除 {len(duplicates)} 条【旧的重复】视频")
        # 等待用户确认
        confirm = input("❓ 确认删除?(输入 y 确认,其他键取消): ").strip().lower()
    
        if confirm != "y":
            print("🚫 已取消操作")
            return
    
        print("🗑️ 开始执行删除任务...")
        
        # 提取所有要删除的消息 ID
        delete_ids = [d[0] for d in duplicates]
        batch_size = 50 # 每次删除 50 条,防止请求过大
    
        for i in range(0, len(delete_ids), batch_size):
            batch = delete_ids[i:i + batch_size]
            try:
                await client.delete_messages(target, batch)
                print(f"   已删除 {min(i + batch_size, len(delete_ids))}/{len(delete_ids)}")
                # 适当延时,保护账号安全
                await asyncio.sleep(1.5)
            except errors.FloodWaitError as e:
                print(f"⏳ 触发 Telegram 流控 (FloodWait),需等待 {e.seconds} 秒...")
                await asyncio.sleep(e.seconds + 2)
            except errors.MessageIdInvalidError:
                print(f"⚠️ 某些消息可能已经被删除,跳过该批次")
            except Exception as e:
                print(f"❌ 删除出错: {e}")
    
        print(f"\n✅ 清理完成!")
    
    # ================== 4. 程序入口 ==================
    if __name__ == "__main__":
        # 使用 with 语法自动管理连接和断开
        with client:
            client.loop.run_until_complete(main())

    0

    1. 此文章尚无评论

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注