Telegram to Typecho 快速部署指南
目录
Typecho 设置
登录后台 -> 设置 -> 写作设置 -> 勾选 “开启XML-RPC接口” -> 保存。
Telegram 机器人
- 与
@BotFather对话,使用/newbot命令创建机器人。 - 保存好你的 API Token。
服务器配置与部署
1. 安装依赖
sudo apt update
sudo apt install python3 python3-pip python3-venv -y
2. 创建项目与环境
# 将 your_user 替换为你的用户名
mkdir -p /home/your_user/typecho_bot
cd /home/your_user/typecho_bot
# 创建并激活虚拟环境
python3 -m venv venv
source venv/bin/activate
# 安装库
pip install python-telegram-bot
3. 放置机器人脚本
创建脚本文件:
nano tg_to_typecho.py
⬇️ 将以下完整代码粘贴进去 ⬇️
#!/usr/bin/env python3
import logging, re, xmlrpc.client
from telegram import Update
from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes, ConversationHandler
from telegram.constants import ParseMode
from xml.parsers.expat import ExpatError
# --- 修改你的配置 ---
TELEGRAM_TOKEN = "在这里填入你的Telegram_Bot_Token"
TYPECHO_URL = "https://你的博客地址/action/xmlrpc"
TYPECHO_USER = "你的Typecho登录用户名"
TYPECHO_PASSWORD = "你的Typecho登录密码"
TYPECHO_BLOG_ID = 1
AUTHORIZED_USER_IDS = [123456789] # 你的Telegram数字ID
# --- 配置结束 ---
logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s", level=logging.INFO)
logger = logging.getLogger(__name__)
GET_TITLE, GET_CATEGORIES, GET_CONTENT = range(3)
def escape_markdown_v2(text: str) -> str:
escape_chars = r'_*[]()~`>#+-=|{}.!'
return re.sub(f'([{re.escape(escape_chars)}])', r'\\\1', text)
def post_to_typecho(title, content, categories=None):
try:
server = xmlrpc.client.ServerProxy(TYPECHO_URL)
post = {'title': title, 'description': content, 'post_status': 'publish', 'custom_fields': [{'key': '__markdown__', 'value': '1'}]}
if categories: post['categories'] = categories
server.metaWeblog.newPost(TYPECHO_BLOG_ID, TYPECHO_USER, TYPECHO_PASSWORD, post, True)
safe_title = escape_markdown_v2(title)
safe_categories = [escape_markdown_v2(cat) for cat in categories] if categories else []
success_msg = f"✅ 文章发布成功!\n\n*标题*: {safe_title}"
if categories: success_msg += f"\n*分类*: {', '.join(safe_categories)}"
return success_msg
except (xmlrpc.client.ProtocolError, ExpatError) as err:
logger.warning(f"捕获到预期的响应错误 ({type(err).__name__}),但按要求视为成功。")
safe_title = escape_markdown_v2(title)
safe_categories = [escape_markdown_v2(cat) for cat in categories] if categories else []
success_msg = f"✅ 文章发布成功!\n\n*标题*: {safe_title}"
if categories: success_msg += f"\n*分类*: {', '.join(safe_categories)}"
return success_msg
except Exception as e:
logger.error(f"捕获到意外错误: {e}")
return f"❌ 发布失败: 发生未知错误,请查看后台日志。"
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
user = update.effective_user
await update.message.reply_text(f"你好, {escape_markdown_v2(user.first_name)}\!\n\n使用 `/newpost` 开始发布。\n使用 `/cancel` 取消操作。", parse_mode=ParseMode.MARKDOWN_V2)
async def new_post(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
if AUTHORIZED_USER_IDS and update.effective_user.id not in AUTHORIZED_USER_IDS:
await update.message.reply_text("抱歉,你没有权限。"); return ConversationHandler.END
await update.message.reply_text("好的,请发送 **文章标题**。", parse_mode=ParseMode.MARKDOWN_V2); return GET_TITLE
async def get_title(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
context.user_data['title'] = update.message.text
await update.message.reply_text(f"标题已设为: “_{escape_markdown_v2(update.message.text)}_”\n\n现在,请输入 **文章分类** (空格隔开,或发 `无`)。", parse_mode=ParseMode.MARKDOWN_V2); return GET_CATEGORIES
async def get_categories(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
text = update.message.text.strip()
if text.lower() in ['无', 'none', '']: categories, cat_text = [], "_无_"
else: categories, cat_text = re.split(r'[\s,]+', text), f"`{escape_markdown_v2(', '.join(re.split(r'[\s,]+', text)))}`"
context.user_data['categories'] = categories
await update.message.reply_text(f"分类已设为: {cat_text}\n\n最后,请发送完整的 **Markdown 正文**。", parse_mode=ParseMode.MARKDOWN_V2); return GET_CONTENT
async def get_content(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
await update.message.reply_text("正在发布...")
result_message = post_to_typecho(context.user_data.get('title'), update.message.text, context.user_data.get('categories'))
await update.message.reply_text(result_message, parse_mode=ParseMode.MARKDOWN_V2); context.user_data.clear(); return ConversationHandler.END
async def cancel(update: Update, context: ContextTypes.DEFAULT_TYPE) -> int:
await update.message.reply_text("操作已取消。"); context.user_data.clear(); return ConversationHandler.END
def main():
application = Application.builder().token(TELEGRAM_TOKEN).build()
conv_handler = ConversationHandler(
entry_points=[CommandHandler("newpost", new_post)],
states={
GET_TITLE: [MessageHandler(filters.TEXT & ~filters.COMMAND, get_title)],
GET_CATEGORIES: [MessageHandler(filters.TEXT & ~filters.COMMAND, get_categories)],
GET_CONTENT: [MessageHandler(filters.TEXT & ~filters.COMMAND, get_content)],
}, fallbacks=[CommandHandler("cancel", cancel)])
application.add_handler(conv_handler); application.add_handler(CommandHandler("start", start))
logger.info("机器人已启动(最终精简版)..."); application.run_polling()
if __name__ == "__main__": main()
保存并退出。然后退出虚拟环境:
deactivate
4. 创建后台服务
创建服务文件:
sudo nano /etc/systemd/system/typecho_bot.service
⬇️ 将以下配置粘贴进去,并修改 User 和 your_user 部分 ⬇️
[Unit]
Description=Telegram to Typecho Bot
After=network.target
[Service]
Type=simple
User=your_user
Group=your_user
WorkingDirectory=/home/your_user/typecho_bot
ExecStart=/home/your_user/typecho_bot/venv/bin/python3 /home/your_user/typecho_bot/tg_to_typecho.py
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
保存并退出。
5. 启动服务
sudo systemctl daemon-reload
sudo systemctl start typecho_bot.service
sudo systemctl enable typecho_bot.service
如何使用
/start: 显示帮助。/newpost: 开始发布新文章。/cancel: 取消当前操作。
管理服务
- 重启:
sudo systemctl restart typecho_bot.service - 停止:
sudo systemctl stop typecho_bot.service - 看日志:
sudo journalctl -u typecho_bot.service -f