自建 Telegram 图床机器人 + Cloudflare Pages 免费部署教程

无需服务器 永久免费运行

https://www.xiaoqikeji.com/index.php/archives/63/

🚀 自建 Telegram 图床机器人 + Cloudflare Pages 免费部署教程

本文教你 0 成本 搭建一个 Telegram 图床机器人,实现 图片自动上传 + 自定义域名,全部挂在 Cloudflare Pages 上,无需服务器,一次部署即可永久免费运行。

🧩 项目介绍

这个项目的核心思路:

  1. 用户向 Telegram 机器人发送图片
  2. 机器人将图片转存到免费图床 111666.best
  3. 自动返回可直接外链的图片地址、Markdown 和 BBCode 链接

完全免费:Cloudflare Pages + 111666 图床
无服务器:Worker 脚本直接跑在 Cloudflare 上
可绑定自定义域名:轻松伪装为自己的图床


⚡️ 准备工作

搭建前,你需要:

  • Telegram Bot Token

    • 打开 @BotFather,输入 /newbot 创建机器人
    • 按提示取名后,BotFather 会返回 BOT_TOKEN,形如:

      1234567890:ABCDEFxxxxxxx
  • 图床 AUTH\_TOKEN

  • Cloudflare 账号

    • 用于部署 Pages(免费计划即可)

🔨 部署到 Cloudflare Pages

1️⃣ 创建 Pages 项目

  1. 登录 Cloudflare Dashboard
  2. 选择 Pages → 创建项目 → 直接上传
  3. 项目名称随意,例如:telegram-image-bot
  4. 构建设置:

    • 框架:无
    • 构建命令:留空
    • 输出目录/
  5. 上传一个空文件(例如 index.html),完成初始化部署

2️⃣ 部署机器人代码

Cloudflare Pages 支持 Functions,我们直接写 Worker 脚本。
进入 Functions → 编辑代码,将下列代码粘贴进去。


版本一:默认图床版(无需自定义域名)

将以下代码命名为 functions/index.js,替换 BOT\_TOKENAUTH\_TOKEN 后直接保存即可。
// Created by rocket, improved by ChatGPT
// Original: https://www.nodeseek.com/post-170862-1

const TELEGRAM_BOT_TOKEN = '替换成你的bot_token';
const AUTH_TOKEN = '替换成你的Auth-Token';
const TELEGRAM_API_URL = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}`;
const IMAGE_UPLOAD_URL = 'https://i.111666.best/image';

export default {
  async fetch(request) {
    const url = new URL(request.url);

    // 手动设置 Webhook
    if (url.pathname === '/setWebhook') {
      const webhookUrl = `${url.protocol}//${url.host}/webhook`;
      const webhookResponse = await setWebhook(webhookUrl);
      return webhookResponse.ok
        ? new Response(`Webhook set to ${webhookUrl}`)
        : new Response(`Failed: ${JSON.stringify(webhookResponse)}`, { status: 500 });
    }

    // 接收 Telegram 消息
    if (url.pathname === '/webhook' && request.method === 'POST') {
      try {
        const update = await request.json();
        const chatId = update.message?.chat?.id;

        if (!chatId) return new Response('No chat', { status: 200 });

        // 用户发送文本
        if (update.message.text) {
          await sendMessage(chatId, '请发给我一张图片');
          return new Response('Asked for image', { status: 200 });
        }

        // 用户发送图片
        if (update.message.photo) {
          await sendMessage(chatId, '图片收到,正在上传中...');
          const photoArray = update.message.photo;
          const fileId = photoArray[photoArray.length - 1].file_id;
          const fileUrl = await getFileUrl(fileId);
          const uploadResponse = await uploadImage(fileUrl);

          if (uploadResponse.ok) {
            const imageUrl = `https://i.111666.best${uploadResponse.src}`;
            await sendImageMessage(chatId, imageUrl);
          } else {
            await sendMessage(chatId, '图片上传失败,请稍后再试。');
          }
          return new Response('Processed', { status: 200 });
        }
      } catch (err) {
        console.error("Webhook error:", err);
        return new Response('Error', { status: 500 });
      }
    }

    return new Response('Not Found', { status: 404 });
  }
};

// 发送文本
async function sendMessage(chatId, text) {
  await fetch(`${TELEGRAM_API_URL}/sendMessage`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ chat_id: chatId, text })
  });
}

// 发送图片+链接
async function sendImageMessage(chatId, imageUrl) {
  const markdownLink = `![Image](${imageUrl})`;
  const bbcodeLink = `[img]${imageUrl}[/img]`;

  const message = `<b>Direct Link</b>\n<pre>${imageUrl}</pre>\n\n<b>Markdown</b>\n<pre>${markdownLink}</pre>\n\n<b>BBCode</b>\n<pre>${bbcodeLink}</pre>`;

  await fetch(`${TELEGRAM_API_URL}/sendPhoto`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      chat_id: chatId,
      photo: imageUrl,
      caption: message,
      parse_mode: "HTML"
    })
  });
}

async function getFileUrl(fileId) {
  const res = await fetch(`${TELEGRAM_API_URL}/getFile?file_id=${fileId}`);
  const data = await res.json();
  const path = data.result.file_path;
  return `https://api.telegram.org/file/bot${TELEGRAM_BOT_TOKEN}/${path}`;
}

async function uploadImage(url) {
  const img = await fetch(url);
  const blob = await img.blob();
  const formData = new FormData();
  formData.append('image', blob, 'image.jpg');

  const res = await fetch(IMAGE_UPLOAD_URL, {
    method: 'POST',
    headers: { 'Auth-Token': AUTH_TOKEN },
    body: formData
  });
  const result = await res.json();
  return res.ok && result.ok ? { ok: true, src: result.src } : { ok: false };
}

async function setWebhook(webhookUrl) {
  const res = await fetch(`${TELEGRAM_API_URL}/setWebhook`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ url: webhookUrl })
  });
  return res.json();
}

要先手动访问一次 /setWebhook

3️⃣ 绑定自定义域名(可选)

如果你想让图片链接显示为自己的域名,例如 https://img.xxx.com/image/xxx.jpg,需要 两段代码

(1) Bot Worker(返回自定义域名链接)

与默认版类似,但加上 CUSTOM_DOMAIN

// Bot Worker
const TELEGRAM_BOT_TOKEN = '替换成你的bot_token';
const AUTH_TOKEN = '替换成你的Auth-Token'; 
const TELEGRAM_API_URL = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}`;
const IMAGE_UPLOAD_URL = 'https://i.111666.best/image';
const CUSTOM_DOMAIN = 'https://更改为自己的域名';

export default {
  async fetch(request) {
    const url = new URL(request.url);

    // 设置 Webhook
    if (url.pathname === '/setWebhook') {
      const webhookUrl = `${url.protocol}//${url.host}/webhook`;
      const webhookResponse = await setWebhook(webhookUrl);
      if (webhookResponse.ok) {
        return new Response(`Webhook set successfully to ${webhookUrl}`, { status: 200 });
      } else {
        return new Response(`Failed to set webhook: ${JSON.stringify(webhookResponse)}`, { status: 500 });
      }
    }

    // 接收 Telegram Webhook
    if (url.pathname === '/webhook' && request.method === 'POST') {
      try {
        const update = await request.json();
        if (update.message) {
          const chatId = update.message.chat.id;

          if (update.message.text) {
            await sendMessage(chatId, '请发给我一张图片');
            return new Response('Asked for image', { status: 200 });
          }

          if (update.message.photo) {
            await sendMessage(chatId, '图片收到,正在上传中...');

            const photoArray = update.message.photo;
            const fileId = photoArray[photoArray.length - 1].file_id;

            const fileUrl = await getFileUrl(fileId);
            const uploadResponse = await uploadImage(fileUrl);

            if (uploadResponse.ok) {
              // ✅ 返回自定义域名
              let imageUrl = `${CUSTOM_DOMAIN}${uploadResponse.src}`;

              await sendImageMessage(chatId, imageUrl);
              return new Response('Image processed and uploaded', { status: 200 });
            } else {
              await sendMessage(chatId, '图片上传失败,请稍后再试。');
              return new Response('Upload failed', { status: 500 });
            }
          }
        }

        return new Response('No message found', { status: 200 });
      } catch (err) {
        console.error("Webhook error:", err);
        return new Response('Error processing request', { status: 500 });
      }
    }

    return new Response('Not found', { status: 404 });
  }
};

// ------------------- Helper Functions -------------------
async function sendMessage(chatId, text) {
  await fetch(`${TELEGRAM_API_URL}/sendMessage`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ chat_id: chatId, text })
  });
}

async function sendImageMessage(chatId, imageUrl) {
  const markdownLink = `![Image](${imageUrl})`;
  const bbcodeLink = `[img]${imageUrl}[/img]`;

  const messageText = `<b>Direct Link</b>\n<pre>${imageUrl}</pre>\n\n<b>Markdown</b>\n<pre>${markdownLink}</pre>\n\n<b>BBCode</b>\n<pre>${bbcodeLink}</pre>`;

  await fetch(`${TELEGRAM_API_URL}/sendPhoto`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      chat_id: chatId,
      photo: imageUrl,
      caption: messageText,
      parse_mode: "HTML"
    })
  });
}

async function getFileUrl(fileId) {
  const res = await fetch(`${TELEGRAM_API_URL}/getFile?file_id=${fileId}`);
  const data = await res.json();
  if (!data.ok) throw new Error("Failed to get file path");
  const filePath = data.result.file_path;
  return `https://api.telegram.org/file/bot${TELEGRAM_BOT_TOKEN}/${filePath}`;
}

async function uploadImage(imageUrl) {
  const imageResponse = await fetch(imageUrl);
  const buffer = await imageResponse.arrayBuffer();
  const blob = new Blob([buffer]);

  const formData = new FormData();
  formData.append('image', blob, 'image.jpg');

  const res = await fetch(IMAGE_UPLOAD_URL, {
    method: 'POST',
    headers: { 'Auth-Token': AUTH_TOKEN },
    body: formData
  });

  const result = await res.json();
  if (res.ok && result.ok) {
    return { ok: true, src: result.src };
  } else {
    console.error("Upload failed:", result);
    return { ok: false };
  }
}

async function setWebhook(webhookUrl) {
  const res = await fetch(`${TELEGRAM_API_URL}/setWebhook`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ url: webhookUrl })
  });
  return res.json();
}

(2) 反代 Worker(转发图片请求)

将下列代码部署到同一个 Pages 项目,用来代理 111666.best 图片:

export default {
  async fetch(request) {
    const url = new URL(request.url);

    if (url.pathname.startsWith("/image/")) {
      const target = `https://i.111666.best${url.pathname}`;

      const res = await fetch(target, {
        headers: {
          "Referer": "https://i.111666.best/",
          "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36"
        }
      });

      const buffer = await res.arrayBuffer();

      return new Response(buffer, {
        status: res.status,
        headers: {
          "Content-Type": res.headers.get("Content-Type") || "image/jpeg",
          "Cache-Control": "public, max-age=86400",
          "Content-Disposition": "inline",
          "Access-Control-Allow-Origin": "*"
        }
      });
    }

    return new Response("Not Found", { status: 404 });
  }
}

然后在 Cloudflare → DNS 中:

  • 添加 CNAME

    img   CNAME   xxx.pages.dev
  • 打开 SSL/TLS → 选择 灵活模式

🕹 启动机器人

部署完成后,在浏览器访问一次:

https://你的Pages域名/setWebhook

返回 Webhook set successfully 表示绑定成功。

之后,直接给机器人发送图片,就能自动获得:

  • Direct Link:图片直链
  • Markdown:可用于博客、GitHub
  • BBCode:适用于论坛发帖

💡 实际效果

功能说明
速度CF 全球加速,访问飞快
存储依托 111666 图床,图片长期稳定
成本0 元搭建,0 元维护

已构建机器人:https://t.me/xiaoqituchuangbot

通过 Cloudflare Pages + Telegram Bot + 免费图床,你可以 零成本 搭建一个全球加速的个人图床,且支持 自定义域名,完美适配博客/论坛/笔记场景。

如果对你有帮助,不妨收藏/分享,让更多人告别付费图床! 🚀

添加新评论