这是一篇来自Telegram的文章(用Tg发博客到Typecho)

· 记录

Telegram to Typecho 快速部署指南

目录


Typecho 设置


登录后台 -> 设置 -> 写作设置 -> 勾选 “开启XML-RPC接口” -> 保存。

Telegram 机器人


  1. @BotFather 对话,使用 /newbot 命令创建机器人。
  2. 保存好你的 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

⬇️ 将以下配置粘贴进去,并修改 Useryour_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

如何使用


管理服务

本文作者: 𝓬𝓸𝓵𝓪 🚀
本文链接: https://bb.bins.fyi/archives/142/
最后修改:
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!