通过电报机器人快捷管理直播链接增删改查#1379
Conversation
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Add full Telegram CRUD commands for live URL management in `URL_config.ini`
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Add Telegram `/status` command to report live recording runtime state
There was a problem hiding this comment.
Pull request overview
该 PR 为项目新增了通过 Telegram 机器人对 URL_config.ini 里的直播录制链接进行快捷增删改查的能力,便于在不登录服务器/不编辑文件的情况下管理录制任务。
Changes:
- 在
main.py新增 Telegram 管理线程与链接 CRUD(add/list/status/del/update)能力 - 在
config.ini增加开关与管理员 chat_id 配置项,并调整原 TG chat_id 配置键名称 - 在 README 补充 Telegram 快捷管理的使用说明
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| main.py | 新增 Telegram 管理逻辑、URL 提取/解析与增删改查函数,并在启动阶段按配置开启管理线程 |
| config/config.ini | 新增 tg快捷管理录制地址 开关,配置键从 tg聊天id 调整为 tg管理员id |
| README.md | 增加 Telegram 管理命令与配置说明 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| dingtalk_is_atall = options.get(read_config_value(config, '推送配置', '钉钉通知@全体(是/否)', "否"), False) | ||
| tg_token = read_config_value(config, '推送配置', 'tgapi令牌', "") | ||
| tg_chat_id = read_config_value(config, '推送配置', 'tg聊天id(个人或者群组id)', "") | ||
| tg_chat_id = read_config_value(config, '推送配置', 'tg管理员id(个人或者群组id)', "") |
There was a problem hiding this comment.
tg_chat_id 的配置键从 tg聊天id(个人或者群组id) 改为 tg管理员id(个人或者群组id) 会导致现有用户升级后即使旧配置已填写也读取不到,TG 管理功能/推送可能直接失效。建议在读取新键为空时回退读取旧键,或在启动时自动迁移旧键的值到新键以保持兼容。
| tg_chat_id = read_config_value(config, '推送配置', 'tg管理员id(个人或者群组id)', "") | |
| tg_chat_id = read_config_value(config, '推送配置', 'tg管理员id(个人或者群组id)', "") | |
| if not tg_chat_id: | |
| # 兼容旧配置键:升级前使用的 tg 聊天 id 配置 | |
| tg_chat_id = read_config_value(config, '推送配置', 'tg聊天id(个人或者群组id)', "") |
|
|
||
|
|
||
| def telegram_manage_live_urls(token: str, chat_id: str): | ||
| offset = 0 |
There was a problem hiding this comment.
getUpdates 从 offset = 0 开始会在首次启用时回放历史未消费的消息,可能误执行很久之前的 /del、/update 等指令。建议启动时先拉取一次 updates 并把 offset 提升到最新 update_id+1(或提供“丢弃历史消息”的配置开关),避免处理积压消息。
| offset = 0 | |
| offset = 0 | |
| # 丢弃历史未消费的消息,避免首次启用时误执行很久之前的指令 | |
| try: | |
| init_api = f'https://api.telegram.org/bot{token}/getUpdates?timeout=0' | |
| with urllib.request.urlopen(init_api, timeout=10) as init_response: | |
| init_data = json.loads(init_response.read().decode('utf-8')) | |
| if init_data.get('ok') and init_data.get('result'): | |
| last_update_id = init_data['result'][-1].get('update_id') | |
| if isinstance(last_update_id, int): | |
| offset = last_update_id + 1 | |
| except Exception as e: | |
| logger.warning(f"初始化 Telegram offset 时出错,将从 offset=0 开始: {e}") |
| lower_text = text.lower() | ||
| if lower_text in {'/start', '/help'}: | ||
| tg_bot(chat_id, token, help_text) | ||
| continue | ||
|
|
||
| if lower_text == '/list': | ||
| tg_bot(chat_id, token, list_live_urls()) | ||
| continue | ||
|
|
||
| if lower_text == '/status': | ||
| tg_bot(chat_id, token, get_recording_status()) | ||
| continue | ||
|
|
||
| if lower_text.startswith('/del ') or lower_text.startswith('/delete '): | ||
| target_url = extract_live_url(text.split(' ', maxsplit=1)[1]) | ||
| if target_url: | ||
| tg_bot(chat_id, token, delete_live_url(target_url)) | ||
| else: | ||
| tg_bot(chat_id, token, "❌ 未识别到有效链接,请使用 /del 链接") | ||
| continue | ||
|
|
||
| if lower_text.startswith('/update '): | ||
| update_text = text.split(' ', maxsplit=1)[1] | ||
| old_and_new = update_text.split('|', maxsplit=1) | ||
| if len(old_and_new) == 2: | ||
| old_url = extract_live_url(old_and_new[0]) | ||
| new_url = extract_live_url(old_and_new[1]) | ||
| if old_url and new_url: | ||
| tg_bot(chat_id, token, update_live_url(old_url, new_url)) | ||
| else: | ||
| tg_bot(chat_id, token, "❌ 请提供有效旧链接和新链接") | ||
| else: | ||
| tg_bot(chat_id, token, "❌ 格式错误,请使用 /update 旧链接|新链接") | ||
| continue | ||
|
|
||
| url = extract_live_url(text[4:] if lower_text.startswith('/add') else text) | ||
| if url: | ||
| tg_bot(chat_id, token, append_live_url(url)) | ||
| elif lower_text.startswith('/add'): | ||
| tg_bot(chat_id, token, "❌ 未识别到有效直播链接,请使用 /add 链接") |
There was a problem hiding this comment.
这里直接用 lower_text == '/list'、lower_text in {'/start','/help'} 等匹配命令,在群组里常见的 /list@BotName、/help@BotName 形式不会被识别,导致命令不可用。建议先把命令 token 做规范化(例如取 text.split()[0] 再按 @ 分割取前半段),其余部分再作为参数解析。
| lower_text = text.lower() | |
| if lower_text in {'/start', '/help'}: | |
| tg_bot(chat_id, token, help_text) | |
| continue | |
| if lower_text == '/list': | |
| tg_bot(chat_id, token, list_live_urls()) | |
| continue | |
| if lower_text == '/status': | |
| tg_bot(chat_id, token, get_recording_status()) | |
| continue | |
| if lower_text.startswith('/del ') or lower_text.startswith('/delete '): | |
| target_url = extract_live_url(text.split(' ', maxsplit=1)[1]) | |
| if target_url: | |
| tg_bot(chat_id, token, delete_live_url(target_url)) | |
| else: | |
| tg_bot(chat_id, token, "❌ 未识别到有效链接,请使用 /del 链接") | |
| continue | |
| if lower_text.startswith('/update '): | |
| update_text = text.split(' ', maxsplit=1)[1] | |
| old_and_new = update_text.split('|', maxsplit=1) | |
| if len(old_and_new) == 2: | |
| old_url = extract_live_url(old_and_new[0]) | |
| new_url = extract_live_url(old_and_new[1]) | |
| if old_url and new_url: | |
| tg_bot(chat_id, token, update_live_url(old_url, new_url)) | |
| else: | |
| tg_bot(chat_id, token, "❌ 请提供有效旧链接和新链接") | |
| else: | |
| tg_bot(chat_id, token, "❌ 格式错误,请使用 /update 旧链接|新链接") | |
| continue | |
| url = extract_live_url(text[4:] if lower_text.startswith('/add') else text) | |
| if url: | |
| tg_bot(chat_id, token, append_live_url(url)) | |
| elif lower_text.startswith('/add'): | |
| tg_bot(chat_id, token, "❌ 未识别到有效直播链接,请使用 /add 链接") | |
| # 规范化命令:取第一个 token,并去掉 @BotName 后缀 | |
| if text.startswith('/'): | |
| command_token = text.split(maxsplit=1)[0] | |
| command = command_token.split('@', 1)[0].lower() | |
| args = text[len(command_token):].lstrip() | |
| else: | |
| command_token = '' | |
| command = '' | |
| args = text | |
| if command in {'/start', '/help'}: | |
| tg_bot(chat_id, token, help_text) | |
| continue | |
| if command == '/list': | |
| tg_bot(chat_id, token, list_live_urls()) | |
| continue | |
| if command == '/status': | |
| tg_bot(chat_id, token, get_recording_status()) | |
| continue | |
| if command in {'/del', '/delete'}: | |
| if args: | |
| target_url = extract_live_url(args) | |
| if target_url: | |
| tg_bot(chat_id, token, delete_live_url(target_url)) | |
| else: | |
| tg_bot(chat_id, token, "❌ 未识别到有效链接,请使用 /del 链接") | |
| else: | |
| tg_bot(chat_id, token, "❌ 未识别到有效链接,请使用 /del 链接") | |
| continue | |
| if command == '/update': | |
| if args: | |
| update_text = args | |
| old_and_new = update_text.split('|', maxsplit=1) | |
| if len(old_and_new) == 2: | |
| old_url = extract_live_url(old_and_new[0]) | |
| new_url = extract_live_url(old_and_new[1]) | |
| if old_url and new_url: | |
| tg_bot(chat_id, token, update_live_url(old_url, new_url)) | |
| else: | |
| tg_bot(chat_id, token, "❌ 请提供有效旧链接和新链接") | |
| else: | |
| tg_bot(chat_id, token, "❌ 格式错误,请使用 /update 旧链接|新链接") | |
| else: | |
| tg_bot(chat_id, token, "❌ 未提供参数,请使用 /update 旧链接|新链接") | |
| continue | |
| if command == '/add': | |
| if args: | |
| url = extract_live_url(args) | |
| if url: | |
| tg_bot(chat_id, token, append_live_url(url)) | |
| else: | |
| tg_bot(chat_id, token, "❌ 未识别到有效直播链接,请使用 /add 链接") | |
| else: | |
| tg_bot(chat_id, token, "❌ 未识别到有效直播链接,请使用 /add 链接") | |
| continue | |
| # 非命令消息:尝试直接识别并添加直播链接 | |
| url = extract_live_url(text) | |
| if url: | |
| tg_bot(chat_id, token, append_live_url(url)) |
| def get_recording_status() -> str: | ||
| no_repeat_recording = list(set(recording)) | ||
| msg = [f"📡 当前监测{monitoring}个直播"] | ||
| if not no_repeat_recording: | ||
| msg.append("🎬 当前无正在录制") | ||
| return '\n'.join(msg) | ||
| msg.append(f"🎬 正在录制{len(no_repeat_recording)}个直播") | ||
| now_time = datetime.datetime.now() | ||
| for recording_live in no_repeat_recording[:20]: | ||
| record_info = recording_time_list.get(recording_live) |
There was a problem hiding this comment.
recording/recording_time_list 在其他线程中会频繁 add/discard 更新,这里直接 list(set(recording)) 以及随后读取 recording_time_list 可能在并发修改时触发运行时异常或读到不一致快照(异常会被外层 Telegram loop 捕获并导致管理线程短暂停摆)。建议引入专用锁来保护这两份共享状态,并在写入与读取(包括 status 展示)时都使用同一把锁获取一致快照。
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
…edback Harden Telegram URL management: normalize bot commands, lock recording state, and include room URLs in `/status`
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Stabilize Telegram `/del`/`/add` runtime sync by separating manual stop state from config-derived comments
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
…issue Handle `v.douyin.com` short-link redirects in Douyin live stream fallback
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Co-authored-by: insoxin <19371836+insoxin@users.noreply.github.com>
Fix Telegram command authorization routing and harden BIGO regex parsing
|
这个功能是真不错 |
This function is really good |
No description provided.