监听 电报订阅频道消息 telegram,转送到企业微信、wxpusher
先看效果:
telegram频道有新的消息。手机就收到:


安装依赖(Linux)
python3 -m venv /opt/tg-relay-venv
source /opt/tg-relay-venv/bin/activate
pip install --upgrade pip
pip install telethon==1.36.0 requests配置文件 .env,【 cat /opt/tg-relay/.env】
# Telegram MTProto(必须) (填你自己的账号信息,从https://my.telegram.org/apps获取)
TG_API_ID=""
TG_API_HASH=""
TG_SESSION_NAME="tg_relay_session" # session文件名(会生成 tg_relay_session.session)
# 监听频道(必须):逗号分隔,支持 @xxx 或 xxx(公开频道username)
TG_CHANNELS="@MjjShareChannel"
# 推送渠道:WECOM 或 WXPUSHER(二选一)
PUSH_PROVIDER="WXPUSHER"
# 企业微信群机器人 webhook(PUSH_PROVIDER=WECOM 时必填)
WECOM_WEBHOOK="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=123456"
# WxPusher(PUSH_PROVIDER=WXPUSHER 时必填)
WXPUSHER_APP_TOKEN="123456"
# 主题(Topics)
WXPUSHER_TOPIC_IDS="123456"
# 限频(可选)
RATE_LIMIT_PER_MIN=30
# 每分钟最多推送条数,默认 30主程序:tg_to_wechat_telethon.py 【vim /opt/tg-relay/tg_to_wechat_telethon.py】
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import time
import json
import signal
import asyncio
import requests
from datetime import datetime
from typing import List
from telethon import TelegramClient, events
from telethon.tl.types import Message
# ========== 读取 .env ==========
def load_env(path: str):
if not os.path.exists(path):
return
with open(path, "r", encoding="utf-8") as f:
for line in f:
s = line.strip()
if not s or s.startswith("#") or "=" not in s:
continue
k, v = s.split("=", 1)
os.environ.setdefault(k.strip(), v.strip().strip('"').strip("'"))
# ========== 限频器 ==========
class RateLimiter:
def __init__(self, per_min: int):
self.per_min = max(1, per_min)
self.bucket: List[float] = []
async def acquire(self):
now = time.time()
self.bucket = [t for t in self.bucket if t >= now - 60]
if len(self.bucket) >= self.per_min:
await asyncio.sleep((self.bucket[0] + 60) - now)
self.bucket.append(time.time())
# ========== 去重 ==========
class Deduper:
def __init__(self, state_file: str):
self.file = state_file
self.seen = set()
self._load()
def _load(self):
if not os.path.exists(self.file):
return
try:
with open(self.file, "r", encoding="utf-8") as f:
data = json.load(f)
for c, m in data.get("seen", []):
self.seen.add((int(c), int(m)))
except Exception:
pass
def _save(self):
tmp = self.file + ".tmp"
with open(tmp, "w", encoding="utf-8") as f:
json.dump({"seen": list(self.seen)[-5000:]}, f)
os.replace(tmp, self.file)
def is_new(self, chat_id: int, msg_id: int) -> bool:
key = (chat_id, msg_id)
if key in self.seen:
return False
self.seen.add(key)
if len(self.seen) % 50 == 0:
self._save()
return True
# ========== WxPusher HTML 推送(仅 topicIds) ==========
def wxpusher_send_html(app_token: str, topic_ids: List[int], html: str, summary: str = ""):
payload = {
"appToken": app_token,
"content": html,
"summary": summary[:20],
"contentType": 2,
"topicIds": topic_ids,
"verifyPayType": 0
}
r = requests.post(
"https://wxpusher.zjiecode.com/api/send/message",
json=payload,
timeout=20
)
r.raise_for_status()
data = r.json()
if data.get("code") != 1000:
raise RuntimeError(f"WxPusher error: {data}")
# ========== HTML 内容格式化 ==========
def format_html(msg: Message, channel_title: str) -> str:
dt = msg.date.astimezone().strftime("%Y-%m-%d %H:%M:%S")
text = (msg.raw_text or "").strip()
media = "文本"
if msg.photo:
media = "图片"
elif msg.video:
media = "视频"
elif msg.document:
media = "文件"
elif msg.voice:
media = "语音"
html = f"""
<h3> TG频道:{channel_title}</h3>
<p><b>时间:</b>{dt}</p>
<p><b>类型:</b>{media}</p>
<hr/>
<p style="white-space:pre-wrap;">{text}</p>
""".strip()
return html
# ========== 主程序 ==========
async def main():
load_env(os.environ.get("ENV_FILE", "/opt/tg-relay/.env"))
api_id = os.environ.get("TG_API_ID")
api_hash = os.environ.get("TG_API_HASH")
session_name = os.environ.get("TG_SESSION_NAME", "tg_relay_session")
channels = [c.strip() for c in os.environ.get("TG_CHANNELS", "").split(",") if c.strip()]
channels = [c if c.startswith("@") else "@" + c for c in channels]
app_token = os.environ.get("WXPUSHER_APP_TOKEN")
topic_ids = [int(x) for x in os.environ.get("WXPUSHER_TOPIC_IDS", "").split(",") if x.strip().isdigit()]
rate_limit = int(os.environ.get("RATE_LIMIT_PER_MIN", "30"))
if not all([api_id, api_hash, channels, app_token, topic_ids]):
raise SystemExit("❌ .env 配置不完整")
limiter = RateLimiter(rate_limit)
deduper = Deduper("/opt/tg-relay/state_seen.json")
client = TelegramClient(session_name, int(api_id), api_hash)
stop_event = asyncio.Event()
signal.signal(signal.SIGTERM, lambda *_: stop_event.set())
signal.signal(signal.SIGINT, lambda *_: stop_event.set())
await client.start()
me = await client.get_me()
print(f"[OK] 已登录:{me.username or me.id}")
print(f"[OK] 监听频道:{channels}")
print(f"[OK] WxPusher topicIds:{topic_ids}")
@client.on(events.NewMessage(chats=channels))
async def handler(event):
msg = event.message
if not deduper.is_new(event.chat_id, msg.id):
return
await limiter.acquire()
chat = await event.get_chat()
title = getattr(chat, "title", None) or getattr(chat, "username", str(event.chat_id))
html = format_html(msg, title)
summary_text = (msg.raw_text or "").strip()
summary_text = " ".join(summary_text.split()) # 把换行/多空格压成单空格,适合当“标题/摘要”
if not summary_text:
if msg.photo:
summary_text = "【图片】"
elif msg.video:
summary_text = "【视频】"
elif msg.document:
summary_text = "【文件】"
elif msg.voice:
summary_text = "【语音】"
else:
summary_text = "【新消息】"
wxpusher_send_html(
app_token=app_token,
topic_ids=topic_ids,
html=html,
#summary=f"{title} 新消息"
summary=summary_text
)
print(f"[SENT] {title} #{msg.id}")
await stop_event.wait()
await client.disconnect()
if __name__ == "__main__":
loop = asyncio.get_event_loop()
loop.run_until_complete(main())4)首次登录(只需要做一次)
第一次运行必须在一个能交互输入的终端执行一次,生成session等信息(后面 systemd 就能后台跑)。
cd /opt/tg-relay
source /opt/tg-relay-venv/bin/activate
ENV_FILE=/opt/tg-relay/.env python /opt/tg-relay/tg_to_wechat_telethon.py它会提示你输入:
手机号(带国家码)、Telegram 验证码、若开启 2FA,还会要密码
成功后会生成:tg_relay_session.session 文件(非常重要,等于“登录凭证”)
5)systemd 后台守护(长期稳定)
创建:/etc/systemd/system/tg-relay.service
[Unit]
Description=Telegram Channel Relay to WeChat (Telethon)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=/opt/tg-relay
Environment=ENV_FILE=/opt/tg-relay/.env
ExecStart=/opt/tg-relay-venv/bin/python /opt/tg-relay/tg_to_wechat_telethon.py
Restart=always
RestartSec=3
User=root
# 安全加固(可选但推荐)
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target启动:
systemctl daemon-reload
systemctl enable --now tg-relay
#实时看日志
journalctl -u tg-relay -f
重启:
systemctl restart tg-relay
浙公网安备 33010602011771号