Telegram 频道视频自动转存到 115 网盘:部署、排错、测速与迁移全记录

Telegram 频道视频自动转存到 115 网盘:部署、排错、测速与迁移全记录

本文记录一套实际搭建过的 Telegram 频道视频转存方案,并保留搭建过程中遇到的问题、失败链路、诊断方法与迁移思路。文中的公网 IP、频道名称、频道 ID、账号信息、Token、Cookie 和 Session 均使用示例值代替。

请只备份你有权保存的内容,不要将 115 挂载用于多人分发、图床、软件分发或视频外链播放等不规范用途。OpenList 的 115 Open 文档也明确提醒,账号使用方式不规范可能带来封禁风险。

一、目标与最终效果

目标是让服务器长期运行一个自动化任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Telegram 频道发布视频

Telethon 用户会话监听到新消息

只识别视频,忽略文字、图片和普通文件

将视频下载到 VPS 临时目录

上传到 115 指定目录

确认上传命令成功

删除 VPS 本地临时视频

继续监听下一条新视频

本文还会覆盖以下扩展需求:

  • 同时监听多个公开或私有频道;
  • 启动时补传最近若干个历史视频;
  • 批量归档整个频道的历史媒体;
  • VPS 磁盘较小时避免被视频塞满;
  • OpenList 中目录不刷新、没有新建目录按钮;
  • my.telegram.org 创建应用只显示 ERROR
  • 私有频道使用 -100... ID 时 Telethon 无法解析;
  • Telegram 媒体位于其他 DC 时的日志解释;
  • 大文件上传到 115 极慢或卡住;
  • 判断是 OpenList 问题、115 上传问题,还是 VPS 到 115 的线路问题;
  • 将整套服务迁移到香港、日本、国内服务器或家中 NAS。

二、可选链路总览

Telegram 到 115 并不只有一种实现方式。先根据需求选架构,再开始部署。

方案 链路 适合场景 优点 局限
方案 A Telegram 频道 → tg-rclone → rclone → OpenList WebDAV → 115 Open 自动监听频道 组件清晰;OpenList 使用 115 官方开放平台授权;便于浏览与管理 WebDAV 多一层;大文件可能超时、重试或卡住;速度依赖 VPS 到 115 的线路
方案 B Telegram 频道 → Telethon/tg-rclone115cli → 115 自动监听、大文件直传测试 绕过 OpenList WebDAV,链路更短 115cli 是非官方工具;使用 115 Cookie;不同版本的大文件上传能力需要实测
方案 C Telegram 文件手动转发 → TG115 Bot → CloudDrive2 WebDAV → 115 只挑选部分视频保存 不需要监听整个频道;转发给自己的 Bot 后自动处理;有队列与磁盘预算设计 需要手动转发;依赖闭源 CloudDrive2;不等于自动监听频道
方案 D Telegram 频道 → tg-archiver 批量下载 → rclone/115cli 上传 一次性搬运历史频道 断点、进度和元数据更完整;适合老频道 需要较大磁盘;不是实时转存的最佳方案
方案 E Telegram 下载节点 → rsync/SFTP → 115 上传节点 → 115 单台 VPS 无法同时兼顾 Telegram 和 115 可以把 Telegram 线路和 115 线路分别优化 多一台机器,运维更复杂
方案 F Telegram → 自写 115 Open API 上传器 → 115 高级开发者、长期维护 可绕过 WebDAV和 Cookie 需要自行实现 Token 刷新、分片、重试、校验和异常恢复

我的实际结论

如果一台 VPS 能同时高速访问 Telegram 和 115,优先测试:

1
Telethon/tg-rclone → 115cli 直传

如果希望使用官方 115 开放平台授权、并且文件不大,可以先测试:

1
tg-rclone → rclone WebDAV → OpenList → 115 Open

如果只有少量视频需要保存,而不是全频道自动监听,TG115 的“转发给 Bot”模式更省心。

如果海外 VPS 到 115 的线路极差,继续调 --transfers 通常没有意义,应直接换 VPS 或采用双节点架构。


三、本文实测环境与目录约定

实测旧服务器大致为:

1
2
3
4
5
6
系统:Debian/Ubuntu 类 Linux
Docker:26.x
Docker Compose:2.26.x
内存:约 2.4 GiB
系统盘:约 39 GiB
可用空间:约 36 GiB

统一使用以下目录:

1
2
3
4
5
6
7
8
9
10
11
/opt/tg115/
├── docker-compose.yml
├── .env
├── openlist/ # OpenList 数据
├── rclone/ # rclone.conf
├── tg-rclone/ # Telegram 监听项目
├── data/
│ ├── channel-a/ # 频道 A 的状态、日志、下载缓存
│ └── channel-b/ # 频道 B 的状态、日志、下载缓存
├── 115cli-home/ # 115cli 登录状态
└── venv115/ # 115cli Python 虚拟环境

示例变量统一使用:

1
2
3
4
5
VPS IP:203.0.113.10
OpenList 域名:openlist.example.com
私有频道 ID:-1001234567890
公开频道用户名:@example_channel
115 目录:/Telegram/Channel-A

第一部分:OpenList + 115 Open + rclone WebDAV

四、检查服务器环境

1
2
3
4
docker --version
docker compose version
git --version
df -h /

检查 5244 端口:

1
ss -ltnp | grep ':5244' || true

创建目录:

1
2
mkdir -p /opt/tg115/{openlist,rclone,data}
cd /opt/tg115

拉取 tg-rclone

1
git clone https://github.com/EdisonJwa/tg-rclone.git

建议记录当前提交,方便以后知道补丁针对哪个版本:

1
2
cd /opt/tg115/tg-rclone
git rev-parse HEAD

五、部署 OpenList

5.1 初次调试:直接开放 5244

初次搭建为了方便,可以临时公网访问:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
cat > /opt/tg115/docker-compose.yml <<'EOF'
services:
openlist:
image: openlistteam/openlist:latest
container_name: openlist
user: "0:0"
restart: unless-stopped
volumes:
- ./openlist:/opt/openlist/data
ports:
- "5244:5244"
environment:
- UMASK=022
- TZ=Asia/Shanghai

networks:
default:
name: tg115-net
EOF

启动:

1
2
3
4
5
cd /opt/tg115
docker compose pull openlist
docker compose up -d openlist
docker ps
docker logs --tail 50 openlist

浏览器访问:

1
http://203.0.113.10:5244

如果系统启用了 UFW:

1
2
ufw status
ufw allow 5244/tcp

如果云厂商还有安全组,也需要临时放行 TCP 5244。

公网直接暴露 5244 只建议用于调试。正式运行应改为绑定 127.0.0.1,再使用 Nginx 和 HTTPS。

5.2 设置管理员密码

1
docker exec -it openlist ./openlist admin set '请替换为强密码'

不要把密码写入教程、截图、GitHub 或聊天记录。


六、获取 115 Access Token 与 Refresh Token

OpenList 的 115 Open 驱动使用 115 官方开放平台 API。

操作流程:

  1. 打开 OpenList 提供的授权页面;
  2. 下拉框选择“115 验证网盘”;
  3. 某些界面会显示为“115 网盘(OAuth2)跳转登录”;
  4. 勾选“使用 OpenList 提供的参数”;
  5. Client ID 和 Application Secret 留空;
  6. 点击“获取 Token”;
  7. 登录 115 并授权;
  8. 保存 Access Token 与 Refresh Token。

这两个 Token 均属于敏感凭据,不要发给别人。


七、在 OpenList 中挂载 115

进入:

1
2
3
OpenList 管理后台
→ 存储
→ 添加存储

填写:

1
2
3
4
5
驱动:115 开放平台
挂载路径:115
根文件夹 ID:0
Access Token:刚获取的 Access Token
Refresh Token:刚获取的 Refresh Token

说明:

  • 根文件夹 ID 为 0,代表整个 115 根目录;
  • 如果只希望挂载 115 内的某个目录,可进入该目录后查看浏览器 URL 中 cid= 后面的数字;
  • 保存后,OpenList 首页应出现 115

7.1 首页提示 storage not found

如果看到:

1
failed get storage: storage not found; please add a storage first

不是 OpenList 安装失败,只是还没有添加任何存储。

7.2 OpenList 没有“新建目录”按钮

可能原因:

  • 当前会话不是管理员;
  • 用户未开启“创建目录或上传”;
  • WebDAV 管理权限未开启;
  • 当前驱动或前端版本没有显示该入口。

最稳妥的方法是直接在 115 官方网页或 App 中创建:

1
Telegram

再回到 OpenList 刷新。

7.3 在 115 创建目录后,OpenList 刷新仍看不到

浏览器 F5 不一定会清除 OpenList 的存储目录缓存。

正确操作:

1
2
3
进入 /115
→ 右下角“...”
→ 刷新

也可以等待缓存过期,或者在存储设置中临时缩短缓存时间。


八、配置 rclone 连接 OpenList WebDAV

OpenList 与 rclone 将处于同一个 Docker 网络,因此 WebDAV 地址使用容器名,不经过公网:

1
http://openlist:5244/dav/

运行 rclone 配置:

1
2
3
4
docker run --rm -it \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
rclone/rclone:latest config

交互填写:

1
2
3
4
5
6
7
8
9
10
11
n                         # New remote
name> openlist115
Storage> webdav
url> http://openlist:5244/dav/
vendor> other
user> admin
password> OpenList 管理员密码
bearer_token> # 直接回车留空
advanced config> n
keep remote> y
q

生成文件:

1
ls -lah /opt/tg115/rclone

应该看到:

1
rclone.conf

不要公开完整的 rclone.conf

8.1 测试读取

1
2
3
4
5
docker run --rm \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
rclone/rclone:latest \
lsd openlist115:

应看到:

1
115

继续:

1
2
3
4
5
docker run --rm \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
rclone/rclone:latest \
lsd openlist115:115

应列出 115 中的目录。

8.2 创建 Telegram 目录

1
2
3
4
5
docker run --rm \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
rclone/rclone:latest \
mkdir openlist115:115/Telegram/Channel-A

8.3 小文件写入测试

1
echo "Telegram to 115 test $(date)" > /opt/tg115/data/test.txt

上传:

1
2
3
4
5
6
7
8
9
docker run --rm \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
-v /opt/tg115/data:/data \
rclone/rclone:latest \
copyto \
/data/test.txt \
openlist115:115/Telegram/test.txt \
-P

检查:

1
2
3
4
5
docker run --rm \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
rclone/rclone:latest \
lsf openlist115:115/Telegram

只有小文件写入成功以后,才继续配置 Telegram,避免同时排查两端。


第二部分:Telegram API 与 Telethon Session

九、创建 Telegram API 应用

打开:

1
https://my.telegram.org/apps

登录后进入:

1
API development tools

示例:

1
2
3
4
App title:TelegramBackup
Short name:tgbackup2026
Platform:Desktop
Description:Personal Telegram backup application

创建成功后保存:

1
2
App api_id
App api_hash

不要使用网上公开的 API ID/API Hash。Telegram 官方要求第三方客户端获取自己的凭据。

9.1 创建应用只弹出 ERROR

实际遇到过:

1
2
3
表单填写正常
→ 点击 Create application
→ 页面只显示 ERROR

常见处理顺序:

  1. 使用稳定 VPN 节点;
  2. 从登录到创建结束保持同一个出口 IP;
  3. 不要在提交过程中切换国家或节点;
  4. 打开无痕窗口重新登录;
  5. 关闭广告拦截或脚本拦截扩展;
  6. App title 与 Short name 只使用简单英文和数字;
  7. 换浏览器;
  8. 换网络;
  9. 不要短时间连续点击几十次,避免账号或 IP 触发临时限制。

如果账号曾经创建过 API 应用,应先检查原有 API ID,因为 Telegram 官方说明一个号码当前只能关联一个 API ID。


十、生成 TG_SESSION_STRING

先创建临时环境文件:

1
2
3
4
5
6
cat > /opt/tg115/tg-session.env <<'EOF'
API_ID=请替换
API_HASH=请替换
EOF

chmod 600 /opt/tg115/tg-session.env

生成 Session:

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
docker run --rm -it \
--env-file /opt/tg115/tg-session.env \
python:3.11-slim \
sh -lc '
set -e
pip install --no-cache-dir telethon >/dev/null

cat > /tmp/gen_session.py << "PY"
from telethon.sync import TelegramClient
from telethon.sessions import StringSession
from telethon.errors import SessionPasswordNeededError
from getpass import getpass
import os
import sys

api_id = int(os.environ["API_ID"])
api_hash = os.environ["API_HASH"]

try:
sys.stdin = open("/dev/tty")
except Exception:
pass

client = TelegramClient(StringSession(), api_id, api_hash)
client.connect()

try:
if not client.is_user_authorized():
phone = input("Phone, for example +8613812345678: ").strip()
client.send_code_request(phone)
code = input("Login code: ").strip()

try:
client.sign_in(phone=phone, code=code)
except SessionPasswordNeededError:
password = getpass("Telegram 2FA password: ")
client.sign_in(password=password)

print("\n========== COPY THIS ==========")
print("TG_SESSION_STRING=" + client.session.save())
print("================================\n")
finally:
client.disconnect()
PY

python /tmp/gen_session.py
'

交互输入:

1
2
3
手机号:国际格式,例如 +8613812345678
验证码:Telegram 客户端收到的登录验证码
2FA 密码:如果账号启用了两步验证

保存输出的:

1
TG_SESSION_STRING=一长串字符

TG_SESSION_STRING 相当于长期登录凭据。Telethon 文档明确提醒,拿到 Session 的人可能登录该账号。泄露后应立即在 Telegram“设置 → 设备”中终止对应会话。

写入正式 .env

1
nano /opt/tg115/.env

内容:

1
2
3
API_ID=12345678
API_HASH=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TG_SESSION_STRING=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

设置权限:

1
chmod 600 /opt/tg115/.env

检查变量是否存在,但不要打印值:

1
2
3
4
5
6
7
for k in API_ID API_HASH TG_SESSION_STRING; do
if grep -q "^${k}=.\+" /opt/tg115/.env; then
echo "$k: OK"
else
echo "$k: MISSING"
fi
done

十一、列出账号加入的 Telegram 频道

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
docker run --rm -it \
--env-file /opt/tg115/.env \
python:3.11-slim \
sh -lc '
set -e
pip install --no-cache-dir telethon >/dev/null

python - << "PY"
import os
from telethon.sync import TelegramClient
from telethon.sessions import StringSession

client = TelegramClient(
StringSession(os.environ["TG_SESSION_STRING"]),
int(os.environ["API_ID"]),
os.environ["API_HASH"],
)

client.connect()

if not client.is_user_authorized():
raise SystemExit("TG_SESSION_STRING 无效或已失效")

print("========== Telegram Channels ==========")

for dialog in client.iter_dialogs():
if dialog.is_channel:
username = getattr(dialog.entity, "username", None)
username = f"@{username}" if username else "-"
print(f"ID: {dialog.id} | {username} | {dialog.name}")

print("=======================================")
client.disconnect()
PY
'

示例输出:

1
2
ID: -1001234567890 | - | 私有频道 A
ID: -1009876543210 | @channel_b | 公开频道 B

使用规则:

  • 公开频道可使用 @channel_b
  • 私有频道建议使用 -100... 数字 ID;
  • .env 中不要给数字 ID 添加多余空格;
  • YAML 中最好加引号,避免被当作普通数字处理。

第三部分:修改 tg-rclone,只转存视频

十二、为什么不能直接使用原版

tg-rclone 原版功能是:

  • 监听 Telegram 用户账号可访问的频道/群组;
  • 支持私有频道;
  • 处理文字、照片、视频、文档、音频、语音、贴纸、位置等;
  • 每个文件调用 rclone copyto
  • 上传成功后删除本地临时文件;
  • 上传失败时保留本地文件,重启后重新入队。

我们的需求是“只转存视频”,因此要修改:

  1. 忽略文字、图片、音频和普通文档;
  2. 为下载文件保留 .mp4 等扩展名;
  3. HISTORY_LIMIT=1 应表示最近 1 个视频,而不是最近 1 条消息;
  4. 私有频道使用数字 ID 时,需要从 dialogs 中解析 input_entity/access_hash

十三、备份原文件并应用补丁

1
2
cd /opt/tg115/tg-rclone
cp monitor.py monitor.original.py

下面的补丁适用于本文记录时的项目结构。仓库更新后如果匹配失败,应对照当前源码手工修改。

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
python3 - <<'PY'
from pathlib import Path

p = Path("/opt/tg115/tg-rclone/monitor.py")
s = p.read_text(encoding="utf-8")

start = s.index("async def process_message(msg):")
end = s.index("# --- Event handler", start)

new_process = '''async def process_message(msg):
mid = msg.id

if mid in state.processed_ids:
return

is_video = False

if isinstance(msg.media, MessageMediaDocument):
try:
mime = msg.media.document.mime_type or ""
is_video = mime.startswith("video/")
except Exception:
pass

if is_video:
file_obj = getattr(msg, "file", None)
ext = (getattr(file_obj, "ext", None) or ".mp4").lstrip(".")
p = gen_path(msg.id, msg.grouped_id, ext=ext)
got = await handle_expired_and_download(msg, p)

if got:
state.update_stats("video")
await enqueue_upload(got)
else:
state.update_stats("failed")
append_failed(msg, "video download failed")

state.mark_seen(mid)
await save_state()

'''

s = s[:start] + new_process + s[end:]

start = s.index("async def backfill_history():")
end = s.index("async def periodic_stats():", start)

new_backfill = '''async def backfill_history():
if HISTORY_LIMIT <= 0:
logger.info("Skip backfill; listen-only mode.")
return

scan_max = int(os.getenv("HISTORY_SCAN_MAX", "500"))
found = 0

logger.info(
f"Scanning up to {scan_max} messages for latest "
f"{HISTORY_LIMIT} video(s)"
)

target_entity = None

# StringSession 不保存完整的实体缓存。
# 对没有 @username 的私有频道,要从 dialogs 中取得 input_entity。
if TARGET_CHAT_ID is not None:
logger.info(
f"Resolving Telegram entity from dialogs: {TARGET_CHAT_ID}"
)

async for dialog in client.iter_dialogs():
if dialog.id == TARGET_CHAT_ID:
target_entity = dialog.input_entity
logger.info(
f"Resolved target dialog: {dialog.name} ({dialog.id})"
)
break

if target_entity is None:
try:
target_entity = await client.get_input_entity(CHANNEL)
except Exception as e:
raise RuntimeError(
f"Unable to resolve target channel {CHANNEL}: {e}"
)

async for m in client.iter_messages(target_entity, limit=scan_max):
is_video = False

if isinstance(m.media, MessageMediaDocument):
try:
mime = m.media.document.mime_type or ""
is_video = mime.startswith("video/")
except Exception:
pass

if not is_video:
continue

await process_message(m)
found += 1

if found >= HISTORY_LIMIT:
break

logger.info(f"Video backfill finished: found={found}")

'''

s = s[:start] + new_backfill + s[end:]

p.write_text(s, encoding="utf-8")
print("monitor.py patched successfully")
PY

检查语法:

1
2
python3 -m py_compile /opt/tg115/tg-rclone/monitor.py \
&& echo "Python syntax: OK"

没有报错才继续构建。

检查关键段落:

1
2
3
grep -n -A45 -B5 \
"async def process_message" \
/opt/tg115/tg-rclone/monitor.py
1
2
3
grep -n -A50 \
"Resolving Telegram entity" \
/opt/tg115/tg-rclone/monitor.py

13.1 终端粘贴大段代码被破坏

实际遇到过 heredoc 粘贴后终端显示异常字符。

处理原则:

  1. 立即停止,不要构建;
  2. monitor.original.py 恢复;
  3. 使用短补丁脚本,而不是重新粘贴 400 行完整文件;
  4. 每次都运行 python3 -m py_compile
  5. 使用 grepsed 检查关键区域。

恢复命令:

1
2
cp -f /opt/tg115/tg-rclone/monitor.original.py \
/opt/tg115/tg-rclone/monitor.py

十四、配置一个频道做端到端测试

先测试一个频道,不要一开始同时开多个容器。

清理旧配置项:

1
2
3
4
5
6
7
8
9
cd /opt/tg115

sed -i \
-e '/^CHANNEL=/d' \
-e '/^RCLONE_DEST=/d' \
-e '/^HISTORY_LIMIT=/d' \
-e '/^HISTORY_SCAN_MAX=/d' \
-e '/^UPLOAD_WORKERS=/d' \
.env

追加:

1
2
3
4
5
6
7
cat >> /opt/tg115/.env <<'EOF'
CHANNEL=-1001234567890
RCLONE_DEST=openlist115:115/Telegram/Channel-A
HISTORY_LIMIT=1
HISTORY_SCAN_MAX=500
UPLOAD_WORKERS=1
EOF

检查非敏感变量:

1
2
3
grep -E \
'^(CHANNEL|RCLONE_DEST|HISTORY_LIMIT|HISTORY_SCAN_MAX|UPLOAD_WORKERS)=' \
/opt/tg115/.env

创建独立数据目录:

1
mkdir -p /opt/tg115/data/channel-a

十五、Docker Compose:单频道测试版

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
cat > /opt/tg115/docker-compose.yml <<'EOF'
services:
openlist:
image: openlistteam/openlist:latest
container_name: openlist
user: "0:0"
restart: unless-stopped
volumes:
- ./openlist:/opt/openlist/data
ports:
- "5244:5244"
environment:
- UMASK=022
- TZ=Asia/Shanghai

tg-channel-a:
build:
context: ./tg-rclone
image: tg-rclone-video:local
container_name: tg-channel-a
restart: unless-stopped
depends_on:
- openlist
env_file:
- ./.env
volumes:
- ./rclone:/root/.config/rclone:ro
- ./data/channel-a:/data

networks:
default:
name: tg115-net
EOF

构建:

1
2
cd /opt/tg115
docker compose build --no-cache tg-channel-a

启动:

1
2
docker compose up -d tg-channel-a
docker ps --filter "name=tg-channel-a"

日志:

1
docker logs -f --tail 100 tg-channel-a

正常日志过程:

1
2
3
4
5
6
7
8
9
Starting tg-rclone
TARGET_CHAT_ID => -1001234567890
Scanning up to 500 messages for latest 1 video(s)
Resolving Telegram entity from dialogs: -1001234567890
Resolved target dialog: Channel A (-1001234567890)
Starting direct file download...
Video backfill finished: found=1
Listening for new messages...
[uploader-0] uploaded & cleaned: 12345.mp4

注意:上传 worker 是异步运行的,因此可能先出现:

1
2
Video backfill finished
Listening for new messages

随后才出现:

1
uploaded & cleaned

十六、验证是否真正成功

16.1 查看容器是否运行

1
docker ps --filter "name=tg-channel-a"

16.2 查看日志

1
docker logs --tail 50 tg-channel-a

真正成功的关键标志:

1
uploaded & cleaned

16.3 查看 115 目录

1
2
3
4
5
docker run --rm \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
rclone/rclone:latest \
lsf openlist115:115/Telegram/Channel-A

16.4 查看本地是否已删除

1
find /opt/tg115/data/channel-a -type f -ls

上传成功后,大视频应消失,只留下:

1
2
3
state.json 或 video_state.json
日志文件
失败记录

16.5 查看下载文件是否仍在增长

1
2
3
4
5
6
7
watch -n 3 '
du -sh /opt/tg115/data/channel-a
echo
find /opt/tg115/data/channel-a \
-type f \
-printf "%12s bytes %p\n"
'

Ctrl+C 只会退出观察,不会停止容器。


十七、Telegram 日志中的“其他 DC”不是错误

日志可能出现:

1
2
3
File lives in another DC
Connecting to 91.108.x.x:443
Exporting auth for new borrowed sender

这是 Telegram 媒体文件位于另一个数据中心,Telethon 正在借用新的 sender 下载媒体,属于正常流程。

只要:

  • 容器仍为 Up
  • 下载文件大小继续增长;
  • 没有 Traceback;

就不要重启。


十八、私有频道报 Cannot find any entity

典型报错:

1
ValueError: Cannot find any entity corresponding to "-1001234567890"

原因不是频道 ID 一定错误,而是:

  • CHANNEL 是一个字符串形式的数字 ID;
  • Telethon 访问私有频道历史消息还需要 access_hash
  • StringSession 主要保存授权信息,不像 SQLite Session 那样持久保存完整实体缓存;
  • 直接 iter_messages("-100...") 可能无法构造正确的 InputPeer。

解决方法就是本文补丁中的:

1
2
3
async for dialog in client.iter_dialogs():
if dialog.id == TARGET_CHAT_ID:
target_entity = dialog.input_entity

然后使用:

1
client.iter_messages(target_entity)

十九、FloodWait 与快速重启循环

如果容器因代码错误反复退出,而 Compose 设置了:

1
restart: unless-stopped

会形成:

1
2
3
4
5
6
启动
→ 登录 Telegram
→ 解析频道
→ 崩溃
→ 自动重启
→ 再请求 Telegram

随后可能出现:

1
2
Sleeping for 22s on GetContactsRequest flood wait
FloodWait

正确做法:

1
2
cd /opt/tg115
docker compose stop tg-channel-a

先修复代码并重新构建,再启动。不要连续 restart


二十、从测试模式切换为长期监听

测试时:

1
HISTORY_LIMIT=1

表示启动时找最近 1 个视频。

确认成功后,改成:

1
HISTORY_LIMIT=0

只监听以后发布的新视频:

1
2
3
4
5
6
sed -i \
's/^HISTORY_LIMIT=.*/HISTORY_LIMIT=0/' \
/opt/tg115/.env

cd /opt/tg115
docker compose up -d --force-recreate tg-channel-a

查看:

1
docker logs --tail 50 tg-channel-a

应出现:

1
2
Skip backfill; listen-only mode.
Listening for new messages...

二十一、批量补传历史视频

不要一开始设置很大的值,例如:

1
HISTORY_LIMIT=10000

因为可能造成:

  • Telegram FloodWait;
  • VPS 磁盘被同时下载的文件占满;
  • 115 上传队列长期堆积;
  • 失败文件持续保留;
  • 容器内存和网络压力过大。

推荐分批:

1
2
3
HISTORY_LIMIT=5
HISTORY_SCAN_MAX=1000
UPLOAD_WORKERS=1

每批确认完成后再增加。

检查磁盘:

1
2
3
df -h /
du -sh /opt/tg115/data/*
find /opt/tg115/data -type f -size +1G -ls

如果 VPS 只有 40GB 左右,至少保留 10~20GB 安全空间。


二十二、多个频道的正确部署方式

原版 tg-rclone 是单 CHANNEL、单 RCLONE_DEST

有两种办法:

方法 1:每个频道一个容器

优点:

  • 配置简单;
  • 状态与下载目录互不干扰;
  • 某个频道失败不影响其他频道。

建议每个并发容器生成一个独立的 TG_SESSION_STRING。API_ID 和 API_HASH 可以相同,但 Session 独立。

目录:

1
2
3
4
5
6
/opt/tg115/
├── .env.channel-a
├── .env.channel-b
└── data/
├── channel-a/
└── channel-b/

.env.channel-a

1
2
3
4
5
6
7
8
API_ID=12345678
API_HASH=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TG_SESSION_STRING=独立SessionA
CHANNEL=-1001234567890
RCLONE_DEST=openlist115:115/Telegram/Channel-A
HISTORY_LIMIT=0
HISTORY_SCAN_MAX=500
UPLOAD_WORKERS=1

.env.channel-b

1
2
3
4
5
6
7
8
API_ID=12345678
API_HASH=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TG_SESSION_STRING=独立SessionB
CHANNEL=@example_channel
RCLONE_DEST=openlist115:115/Telegram/Channel-B
HISTORY_LIMIT=0
HISTORY_SCAN_MAX=500
UPLOAD_WORKERS=1

权限:

1
2
chmod 600 /opt/tg115/.env.channel-a
chmod 600 /opt/tg115/.env.channel-b

Compose:

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
services:
openlist:
image: openlistteam/openlist:latest
container_name: openlist
user: "0:0"
restart: unless-stopped
volumes:
- ./openlist:/opt/openlist/data
ports:
- "127.0.0.1:5244:5244"
environment:
- TZ=Asia/Shanghai

tg-channel-a:
build:
context: ./tg-rclone
image: tg-rclone-video:local
container_name: tg-channel-a
restart: unless-stopped
depends_on:
- openlist
env_file:
- ./.env.channel-a
volumes:
- ./rclone:/root/.config/rclone:ro
- ./data/channel-a:/data

tg-channel-b:
image: tg-rclone-video:local
container_name: tg-channel-b
restart: unless-stopped
depends_on:
- openlist
env_file:
- ./.env.channel-b
volumes:
- ./rclone:/root/.config/rclone:ro
- ./data/channel-b:/data

networks:
default:
name: tg115-net

方法 2:自行改成一个进程监听多个频道

优点是只需要一个 Session 和一个容器,但需要:

  • 维护 频道 ID → 115 目录 映射;
  • 为每个频道维护独立状态;
  • 防止多个视频同时下载把磁盘占满;
  • 在事件处理器中根据 event.chat_id 分流。

除非准备长期维护代码,否则每频道一个容器更容易排错。


第四部分:大文件上传慢或卡住的诊断

二十三、一个真实故障样本

实测某视频:

1
本地大小:1,119,128,862 bytes,约 1.04 GiB

Telegram 下载正常完成,但 115 目录一直为空。

docker top 显示:

1
2
3
4
5
6
7
rclone copyto /data/downloads/3513.mp4 \
openlist115:115/Telegram/Channel-A/3513.mp4 \
--transfers=4 \
--checkers=8 \
--contimeout=30s \
--low-level-retries=5 \
--retries=3

docker stats 累计值显示:

1
2
3
tg 容器接收约 1.15GB
tg 容器发送约 3.37GB
OpenList 接收约 3.36GB

/proc/<PID>/io 中的读取量约为视频大小的 3 倍,并且 10 秒内不再增加。

这强烈暗示:

1
2
3
4
同一个 1.1GB 文件发生了多轮 PUT/重试
→ rclone 不再读取本地文件
→ 进程正在等待 OpenList 或 115 返回
→ 远端目录仍没有完成文件

这不是普通的“速度稍慢”。


二十四、诊断命令

24.1 看容器累计网络与内存

1
docker stats --no-stream tg-channel-a openlist

注意:

  • NET I/O 是容器启动以来的累计值,不是实时速度;
  • 适合判断是否发生了大量重复传输;
  • OpenList 内存异常增大也值得关注。

24.2 看 rclone 子进程

1
docker top tg-channel-a

如果看到 rclone copyto,说明上传任务仍未结束。

24.3 看 rclone 是否还在读取本地文件

1
2
3
4
5
6
7
8
PID=$(docker top tg-channel-a -eo pid,cmd \
| awk '/rclone copyto/ {print $1; exit}')

echo "rclone PID=$PID"

grep -E 'rchar|read_bytes' /proc/$PID/io
sleep 10
grep -E 'rchar|read_bytes' /proc/$PID/io

判断:

  • 数值持续增长:仍在读取或重新读取本地文件;
  • 10 秒以上完全不变:可能在等待远端响应;
  • 读取量接近文件大小的 2 倍、3 倍:可能发生整文件重试。

这只是诊断线索,不是所有系统上的绝对字节计费。

24.4 看进程等待点

1
ps -o pid,ppid,stat,etime,wchan:32,cmd -p "$PID"

例如:

1
2
STAT=Sl
WCHAN=do_epoll_wait

说明进程正在等待网络或事件,不代表上传成功。

24.5 查看远端目录

1
2
3
4
5
docker run --rm \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
rclone/rclone:latest \
lsl openlist115:115/Telegram/Channel-A

24.6 查看 OpenList 日志

1
docker logs --since 60m openlist 2>&1 | tail -n 300

OpenList 没有输出日志并不等于没有问题,仍需结合进程、远端目录和网络统计判断。

24.7 停止任务但保留本地文件

1
2
cd /opt/tg115
docker compose stop tg-channel-a

原项目只有在 rclone 返回成功时才删除本地文件。停止后检查:

1
ls -lh /opt/tg115/data/channel-a/downloads/

二十五、为什么 --transfers=4 没让单个视频快四倍

--transfers 主要控制同时传输多少个文件,不等于把一个大视频自动拆成四份。

对于:

1
一个 1.1GB 的 MP4

WebDAV 后端通常仍表现为一个主要的 PUT 流。单文件多线程是否有效取决于后端和协议支持,不能仅靠提高 --transfers 解决。

因此:

  • 多个小文件时,增加 transfers 可能有效;
  • 单个大文件时,瓶颈往往在后端、线路、分片实现或服务器响应;
  • 盲目提高并发可能增加重试、内存和风控风险。

二十六、OpenList WebDAV 大文件的已知风险

OpenList 的 115 Open 使用官方开放平台 API,但速度和稳定性仍与:

  • OpenList 所在服务器网络;
  • 115 服务器网络;
  • 服务器性能;
  • 临时上传凭证有效期;
  • WebDAV 客户端行为;
  • 文件大小和上传耗时;

有关。

OpenList 项目 Issue 中也出现过:

  • 约 1.8GB 文件通过 WebDAV 写入时锁定或 context canceled;
  • 15GB 文件上传时临时安全 Token 过期;
  • 失败后客户端再次 PUT,产生重复传输。

因此大文件正式生产前必须做真实验收,不能只用几十字节的测试文件判断。


第五部分:115cli 直传方案

二十七、为什么测试 115cli

为了判断瓶颈是否来自 OpenList WebDAV,可以绕过 OpenList:

1
2
3
本地视频
→ 115cli
→ 115

如果 115cli 很快而 OpenList 很慢,说明问题更偏向 WebDAV/OpenList。

如果 115cli 也很慢,则更可能是:

1
VPS → 115 上传节点

的线路问题。

115cli 是非官方项目,支持 Cookie 登录、目录浏览、上传与秒传等操作。请阅读项目免责声明,并以当前版本 README 为准。

需要特别注意:不同版本对大文件分片上传的实现状态可能不同。本文记录时,项目 README 将部分大文件能力列在后续计划中,因此不要在未验收前把它当成绝对稳定的大文件上传器。


二十八、安装 115cli

1
2
3
4
5
6
7
8
9
10
11
cd /opt/tg115

apt-get update
apt-get install -y python3-venv

python3 -m venv /opt/tg115/venv115

/opt/tg115/venv115/bin/pip install -U pip
/opt/tg115/venv115/bin/pip install -U 115cli

/opt/tg115/venv115/bin/115cli --help

创建独立 HOME:

1
2
mkdir -p /opt/tg115/115cli-home
chmod 700 /opt/tg115/115cli-home

二十九、安全登录 115cli

115cli 当前可使用以下 Cookie 字段:

1
2
3
4
UID
CID
SEID
KID

浏览器登录 115 后,在开发者工具中查看 115 域名 Cookie。

不要直接把 Cookie 写进命令行历史:

1
2
read -rsp "Paste 115 Cookie: " PAN115_COOKIE
echo

登录:

1
2
3
HOME=/opt/tg115/115cli-home \
/opt/tg115/venv115/bin/115cli \
login cookie "$PAN115_COOKIE"

立即清理变量:

1
unset PAN115_COOKIE

验证:

1
2
HOME=/opt/tg115/115cli-home \
/opt/tg115/venv115/bin/115cli account

列目录:

1
2
HOME=/opt/tg115/115cli-home \
/opt/tg115/venv115/bin/115cli ls /
1
2
HOME=/opt/tg115/115cli-home \
/opt/tg115/venv115/bin/115cli ls /Telegram

三十、115cli 上传测速

对现有视频测试:

1
2
3
4
time HOME=/opt/tg115/115cli-home \
/opt/tg115/venv115/bin/115cli upload \
/opt/tg115/data/channel-a/downloads/12345.mp4 \
/Telegram/Channel-A/12345.mp4

某次实测显示:

1
2
约 18 kB/s
预计十几个小时

此时可以基本排除“只有 OpenList WebDAV 慢”的判断,因为直传链路也慢。

结论更偏向:

1
当前海外 VPS 到 115 上传节点的路由很差

这种情况下继续调 Python、rclone 或 OpenList 参数意义不大,应该换上传出口。

Ctrl+C 停止测试后,确认本地文件仍在:

1
ls -lh /opt/tg115/data/channel-a/downloads/

三十一、如何把 tg-rclone 改成 115cli 上传

逻辑上可以把原来的:

1
rclone copyto local remote

替换为:

1
115cli upload local /Telegram/Channel-A/filename

但正式使用前必须处理:

  • 115 Cookie 失效;
  • 远端同名文件;
  • 上传成功后的真实性校验;
  • 秒传与真实上传的区别;
  • 进程退出码;
  • 中断后的残留文件;
  • 失败重试次数;
  • 超大文件是否分片;
  • Cookie 或 WAF 风控;
  • 多频道并发。

因此更稳妥的做法是:

  1. 在新 VPS 上先进行 100MB 和 1GB 实测;
  2. 确认速度与稳定性;
  3. 再将上传 worker 替换为 115cli;
  4. 保留“成功后删除、失败则保留”的原则;
  5. 不要只根据命令退出码判断,最好再执行 statls -l 校验远端大小。

第六部分:另外几种链路

三十二、TG115:手动挑选视频转存

TG115 的设计是:

1
2
3
4
5
6
在 Telegram 中看到文件
→ 转发给自己的 Telegram Bot
→ VPS 排队
→ 下载
→ CloudDrive2 WebDAV
→ 115

适合:

  • 不想监听整个频道;
  • 只挑选部分视频;
  • 希望通过 Bot 查看任务状态;
  • 需要队列、磁盘预算和大文件流式策略。

其正式支持范围主要是本人和 Bot 的一对一私聊。

局限:

  • 需要手动转发;
  • 依赖 CloudDrive2;
  • CloudDrive2 是闭源软件;
  • FUSE/特权容器需要额外安全评估;
  • “WebDAV 接收完成”与“115 官方端最终入库完成”应分开确认。

三十三、tg-archiver:批量归档整个历史频道

tg-archiver 更适合一次性历史归档:

  • 公开或私有频道;
  • 群组;
  • 保存媒体;
  • 保存 Caption、消息 ID、时间等元数据;
  • 记录进度;
  • 中断后恢复;
  • 跳过已下载内容。

典型链路:

1
2
3
4
5
Telegram 历史频道
→ tg-archiver
→ /data/files
→ 115cli 或 rclone
→ 115

适合几百或几千个历史视频,但要提前估算磁盘。

建议:

  • 下载与上传串行或小并发;
  • 每完成一个文件立即上传;
  • 上传成功后再清理;
  • 定期备份 progress 和 metadata;
  • 不要把全部历史视频一次性落盘后再上传。

三十四、双节点架构

当一台海外 VPS 访问 Telegram 很快,但访问 115 极慢,可以拆分:

1
2
3
4
5
6
7
8
9
节点 A:Telegram 下载节点

rsync / SFTP / rclone SFTP

节点 B:115 上传节点

115cli 或 OpenList

115

推荐节点 B:

  • 中国大陆服务器;
  • 家中 NAS 或电脑;
  • 香港 CMI/CN2;
  • 日本或新加坡优质中国方向线路。

不要只看“1Gbps 端口”,必须测试实际到 115 的上传速度。

双节点的优点:

  • Telegram 与 115 分别选择最合适线路;
  • 节点 A 不保存长期数据;
  • 节点 B 可位于家中或国内网络。

缺点:

  • 多一次传输;
  • 需要 SSH 密钥;
  • 需要处理节点 B 不在线的情况;
  • 两边都要监控磁盘。

第七部分:整套服务迁移到新 VPS

三十五、先测试新 VPS,再迁移

不要购买后立即搬整套数据。

新 VPS 先安装最小环境:

1
2
3
4
5
6
7
8
9
10
11
apt update
apt install -y \
docker.io \
docker-compose-plugin \
git \
python3 \
python3-venv \
rsync \
curl

systemctl enable --now docker

检查:

1
2
3
4
docker --version
docker compose version
python3 --version
df -h /

35.1 先做 115 上传测速

安装 115cli并登录后,创建一个不可压缩的 100MB 随机文件:

1
2
3
4
5
dd if=/dev/urandom \
of=/tmp/115-test-100m.bin \
bs=1M \
count=100 \
status=progress

上传:

1
2
3
4
time HOME=/opt/tg115/115cli-home \
/opt/tg115/venv115/bin/115cli upload \
/tmp/115-test-100m.bin \
/Telegram/SpeedTest/115-test-100m.bin

建议标准:

1
2
3
4
低于 1 MB/s:不建议迁移
1~5 MB/s:勉强可用
5~10 MB/s:基本可用
10 MB/s 以上:较适合视频转存

这些只是经验阈值,应按视频大小和数量调整。

通用 Speedtest 不能替代真实 115 上传测试。即使 VPS 对公共测速节点很快,也可能到 115 很慢。


三十六、停止旧 VPS 的写入任务

在旧 VPS:

1
2
cd /opt/tg115
docker compose stop tg-channel-a tg-channel-b 2>/dev/null || true

确认没有 rclone 或 115cli 上传进程:

1
ps aux | grep -E 'rclone copyto|115cli upload' | grep -v grep

不要同时在旧、新 VPS 上使用同一个 Session 长期监听同一个频道。


三十七、迁移方式一:只迁移配置和状态

在旧 VPS:

1
2
3
4
5
rsync -avh --progress \
--exclude 'venv115/' \
--exclude 'data/*/downloads/*' \
/opt/tg115/ \
root@新VPS_IP:/opt/tg115/

如果 SSH 不是 22 端口:

1
2
3
4
5
6
rsync -avh --progress \
-e 'ssh -p 2222' \
--exclude 'venv115/' \
--exclude 'data/*/downloads/*' \
/opt/tg115/ \
root@新VPS_IP:/opt/tg115/

这种方式迁移:

  • .env
  • Telegram Session;
  • 代码;
  • Compose;
  • OpenList 数据;
  • rclone 配置;
  • 状态与日志;

但不迁移未上传的大视频。


三十八、迁移方式二:连未上传视频一起迁移

先迁移配置,再单独传大文件:

1
2
3
rsync -avh --progress \
/opt/tg115/data/channel-a/downloads/12345.mp4 \
root@新VPS_IP:/opt/tg115/data/channel-a/downloads/

如果旧 VPS 到新 VPS 的线路较好,这通常比重新从 Telegram 下载更省时间。

注意目标目录:

1
2
ssh root@新VPS_IP \
'mkdir -p /opt/tg115/data/channel-a/downloads'

三十九、为什么不要复制 Python venv

venv115 中包含:

  • Python 解释器路径;
  • 软链接;
  • 本机安装路径;
  • 可能与系统架构或 Python 小版本相关的包。

因此在新 VPS 重建:

1
2
3
4
5
rm -rf /opt/tg115/venv115

python3 -m venv /opt/tg115/venv115
/opt/tg115/venv115/bin/pip install -U pip
/opt/tg115/venv115/bin/pip install -U 115cli

四十、新 VPS 上验证敏感配置

权限:

1
2
3
chmod 600 /opt/tg115/.env*
chmod 700 /opt/tg115/115cli-home
chmod 600 /opt/tg115/rclone/rclone.conf

检查变量是否存在:

1
2
3
4
5
6
7
8
for f in /opt/tg115/.env*; do
echo "===== $f ====="
for k in API_ID API_HASH TG_SESSION_STRING CHANNEL; do
grep -q "^${k}=.\+" "$f" \
&& echo "$k: OK" \
|| echo "$k: MISSING"
done
done

不要执行:

1
cat .env

后再截图公开。


四十一、验证 115cli 登录状态

复制 115cli-home 后,登录状态不一定始终有效。

1
2
HOME=/opt/tg115/115cli-home \
/opt/tg115/venv115/bin/115cli account

失败则重新使用 Cookie 登录。

不要把 Cookie 写入脚本、Compose 或 GitHub。


四十二、验证 rclone 和 OpenList

启动 OpenList:

1
2
cd /opt/tg115
docker compose up -d openlist

测试:

1
2
3
4
5
docker run --rm \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
rclone/rclone:latest \
lsd openlist115:115

如果 OpenList OAuth Token 与新 IP 相关的刷新逻辑异常,可重新授权并更新 Access Token/Refresh Token。


四十三、重新构建 Telegram 镜像

不要依赖旧服务器构建好的本地镜像。

1
2
cd /opt/tg115
docker compose build --no-cache tg-channel-a

先只启动一个频道:

1
2
docker compose up -d tg-channel-a
docker logs -f --tail 100 tg-channel-a

验证完成后再启动其他频道。


四十四、迁移验收清单

新 VPS 必须全部通过:

  • Docker 与 Compose 正常;
  • OpenList 可以读取 115;
  • rclone 能列出 115 目录;
  • 115cli 能显示账号信息;
  • 100MB 上传速度符合预期;
  • Telegram Session 有效;
  • 能解析私有频道;
  • 能下载 1 个视频;
  • 能上传 1 个视频;
  • 115 官方客户端能看到文件;
  • 本地文件上传成功后被删除;
  • 容器重启后不会重复下载;
  • VPS 重启后服务自动恢复;
  • 磁盘空间保持在安全线之上。

完成后再清理旧 VPS。


四十五、回滚方案

新 VPS 失败时:

  1. 保留旧 VPS /opt/tg115
  2. 不删除旧 VPS 的待上传视频;
  3. 停止新 VPS 容器;
  4. 确保只有一端运行 Telegram Session;
  5. 重新启动旧端:
1
2
cd /opt/tg115
docker compose up -d

第八部分:正式运行的安全与维护

四十六、OpenList 正式环境改为本地监听

Compose:

1
2
ports:
- "127.0.0.1:5244:5244"

Nginx 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 80;
server_name openlist.example.com;

location / {
proxy_pass http://127.0.0.1:5244;
proxy_http_version 1.1;

proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
client_max_body_size 0;
}
}

申请证书:

1
2
3
4
5
6
certbot --nginx \
-d openlist.example.com \
--email [email protected] \
--agree-tos \
--no-eff-email \
--redirect

如果 OpenList 仅用于后台管理,还可以进一步限制来源 IP。


四十七、敏感信息清单

以下内容均不能公开:

1
2
3
4
5
6
7
8
9
10
Telegram API_HASH
TG_SESSION_STRING
Telegram 验证码
Telegram 2FA 密码
115 Access Token
115 Refresh Token
115 Cookie:UID/CID/SEID/KID
OpenList 管理员密码
rclone.conf 中的认证信息
SSH 私钥

权限:

1
2
3
chmod 600 /opt/tg115/.env*
chmod 600 /opt/tg115/rclone/rclone.conf
chmod 700 /opt/tg115/115cli-home

.gitignore

1
2
3
4
5
6
7
8
.env
.env.*
rclone/
openlist/
115cli-home/
data/
*.session
*.log

四十八、Session 泄露后的处理

Telegram:

1
2
3
设置
→ 设备
→ 终止对应会话

然后重新生成 TG_SESSION_STRING

115 Cookie 泄露:

  • 在 115 官方端退出相关登录;
  • 修改密码;
  • 重新获取 Cookie;
  • 删除旧 115cli-home 登录状态。

OpenList Token 泄露:

  • 重新授权;
  • 更新 Token;
  • 检查账号异常访问。

四十九、常用运维命令

查看所有容器:

1
docker ps

查看日志:

1
2
docker logs --tail 100 tg-channel-a
docker logs -f tg-channel-a

重启单个频道:

1
docker compose restart tg-channel-a

停止单个频道:

1
docker compose stop tg-channel-a

重新构建:

1
2
docker compose build --no-cache tg-channel-a
docker compose up -d --force-recreate tg-channel-a

查看磁盘:

1
2
df -h /
du -sh /opt/tg115/data/*

查找大文件:

1
2
3
4
find /opt/tg115/data \
-type f \
-size +500M \
-printf '%12s %p\n'

查看上传进程:

1
docker top tg-channel-a

查看 115 目录:

1
2
3
4
5
docker run --rm \
--network tg115-net \
-v /opt/tg115/rclone:/config/rclone \
rclone/rclone:latest \
lsf openlist115:115/Telegram/Channel-A

五十、确认 Docker 开机自启

1
systemctl is-enabled docker

如果不是 enabled

1
systemctl enable docker

Compose 服务设置:

1
restart: unless-stopped

VPS 重启后检查:

1
2
docker ps
docker logs --tail 30 tg-channel-a

五十一、备份配置

不备份临时视频:

1
2
3
4
tar \
--exclude='/opt/tg115/data/*/downloads/*' \
-czf /root/tg115-config-$(date +%F).tar.gz \
/opt/tg115

更严格的备份可以排除 venv115

1
2
3
4
5
tar \
--exclude='/opt/tg115/venv115' \
--exclude='/opt/tg115/data/*/downloads/*' \
-czf /root/tg115-config-$(date +%F).tar.gz \
/opt/tg115

备份文件本身包含敏感凭据,不能公开。


第九部分:问题速查表

现象 可能原因 处理
storage not found OpenList 未添加存储 添加 115 开放平台
115 新目录在 OpenList 看不到 目录缓存 在 OpenList 目录内点“刷新”,不要只按 F5
OpenList 没有新建目录 权限或前端入口 在 115 官方端创建;检查管理员写入/WebDAV权限
SSH 22 端口拒绝 SSH 不是 22、服务未启动或防火墙 sshd -Tss -ltnp;也可临时公网访问 OpenList
创建 Telegram API 应用只显示 ERROR VPN/IP/浏览器会话或 Telegram 风控 固定节点、无痕、重新登录、稍后再试
TG_SESSION_STRING 无效 Session 撤销、粘贴错误 重新生成并检查 .env
私有频道 -100... 无法解析 缺少实体 access_hash iter_dialogs() 取得 dialog.input_entity
FloodWait 启动太频繁或历史扫描太快 停止重启循环,让程序等待,减小历史批次
File lives in another DC 媒体在其他 Telegram 数据中心 正常现象,等待连接完成
下载文件在增长但日志不更新 Telethon 正在下载大文件 findduwatch 观察
日志显示 Listening,但文件仍在本地 异步 uploader 正在上传 docker top 检查 rclone 子进程
rclone 读取量是文件的 3 倍 可能发生整文件重试 查看 --retries、远端目录、OpenList日志
read_bytes 不再增长且 epoll_wait 在等待远端响应或卡住 停止容器保留本地,改用直传测试
OpenList 上传慢,115cli 也慢 VPS 到 115 的线路差 换 VPS、国内/NAS上传节点或双节点
磁盘持续增长 上传失败文件被保留 查看日志与 leftovers;先解决上传端
两个频道状态混乱 共享同一 /data 每个频道独立挂载数据目录
大段代码粘贴后语法损坏 终端 heredoc 混入字符 恢复备份,短补丁,运行 py_compile

第十部分:最终选型建议

只偶尔保存几个文件

1
TG115 + CloudDrive2

把文件转发给自己的 Bot,操作最简单。

自动监听一两个频道,文件不算太大

1
2
3
4
tg-rclone
→ rclone
→ OpenList WebDAV
→ 115 Open

先用 1GB 级真实视频测试,不能只测 test.txt

希望缩短上传链路

1
2
3
Telethon/tg-rclone
→ 115cli
→ 115

需要接受非官方 Cookie 登录方式,并对大文件、Cookie 失效和风控做实测。

整个历史频道搬家

1
2
3
tg-archiver
→ 分批上传
→ 115

重点是断点、磁盘预算和上传成功后的清理。

海外 VPS 到 115 极慢

1
迁移整套服务到更合适的 VPS

或者:

1
2
3
4
Telegram 海外下载节点
→ rsync
→ 国内/香港/NAS 上传节点
→ 115

不要继续依赖参数微调掩盖线路问题。


十一、参考项目与官方资料


十二、结语

这次部署最重要的经验不是“找到一条命令”,而是把链路拆开验收:

1
2
3
4
5
6
7
8
Telegram 登录
→ 频道实体解析
→ 视频下载
→ 本地文件完整
→ 上传客户端
→ OpenList/115
→ 远端文件确认
→ 本地清理

每一段都单独验证,才能快速判断问题在哪。

最终实际遇到的关键问题是:

1
2
3
4
Telegram 下载正常
OpenList 小文件上传正常
1GB 大文件经过 WebDAV 卡住并疑似重复上传
115cli 直传也只有几十 kB/s

因此根因更可能是旧 VPS 到 115 上传节点的线路,而不是 Telegram 下载程序。正确处理方式不是继续提高并发,而是先测试新的上传出口,再迁移整套服务。

对于长期自动化任务,稳定性优先于“看起来能跑”。真正的生产验收必须包括:

1
2
3
4
5
6
7
8
真实大文件
真实目标目录
真实 VPS 线路
失败保留
重启恢复
磁盘安全
远端可见
本地清理

只有全部通过,才能称为 Telegram 频道视频自动转存到 115 的完整链路。