#20 Telegram Bot Webhook 讯息收发

今天来尝试部署 Cloudflare Workers 并写写看简单的信息收发。

response

如果你仔细看我们昨天写的 main.js,你会发现我们在回传时用 response 函式包起来,但我们并没有写他啊?
没错,所以我们今天要建立一个 response.js 并在 main.js 引用其中的 response 函式。

首先,我们先在 response.js 中建立 response 函式,它的目的是为了要处理 CORS 的问题,帮我们处理好麻烦的 Headers 就不需要每次回传请求都重复加上去了:

// response.js
function response({ data = "", status = 200, headers = {} }) {
    let header = new Headers();
    header.append("Content-Type", "application/json; charset=utf-8");
    header.append("Access-Control-Allow-Origin", "*");
    header.append("Access-Control-Allow-Headers", "*");
    header.append("Access-Control-Allow-Methods", "*");
    header.append("Access-Control-Allow-Credentials", "true");
    header.append("Cache-Control", "max-age=60, s-maxage=60");
    header.append("Cross-Origin-Resource-Policy", "cross-origin");
    for (let h of Object.keys(headers)) header.append(h, headers[h]);

    return new Response(data, {
        status,
        headers: header,
    });
}

export default response;

我们在这里加上一堆 Header 来处理 CORS 的问题,让所有网站都可以请求 (不过在这里其实不是那麽必要啦),并告诉别人我们回传的是 JSON。

接着在 main.js 引入:

import response from "./response";

wrangler

好了,接下来我们来试试 wrangler 的功能吧。

先用 wrangler login 登入。

然後我们在 wrangler.toml 中加上一行:

webpack_config = "webpack.config.js"

这样 wrangler 在 build 的时候就会套用我们 webpack.config.js 中的设定了。

wrnagler dev

接着我们来用用看 wrangler dev,它会在本机建立一个开发预览用的网页,你就可以透过它向你写的 Worker 发送请求了。
在使用 wrangler dev 时,如果你的 worker 没写好发生错误,也可以在 Terminal 看到错误讯息,方便侦错与除错。

wrangler dev 建立的 127.0.0.1:8787 发送请求试试:

{
  "cf": {
    "clientTcpRtt": 1,
    "longitude": "121.53240",
    "latitude": "25.05040",
    "tlsCipher": "ECDHE-ECDSA-AES128-GCM-SHA256",
    "continent": "AS",
    "asn": 3462,
    "clientAcceptEncoding": "gzip",
    "country": "TW",
    ...
}

这串 JSON 就是你对 Worker 发送的请求所包含的资讯,以及 Worker 的资讯,像是由何处的 Worker 回应的。

wrangler publish

当觉得 Worker 写到一个段落要部署到 Edge 上时,就使用 wrangler publish,如果你有在 wrangler.toml 设定 KV 或是 Cron,它也会在这时一并设定。

执行完後它就会告诉你你的 Worker 位置在哪里了。

建立 Telegram Bot

首先我们得先建立一个 Telegram Bot。
建立的方法很奇特,就是在 Telegram 里向 @BotFather 传指令讯息。

注意,其中的 Token 是用来控制 Bot 的,不能外流。 如果不小心外流可以用 /revoke 来更换一个新的 Token。

这样就建立好 Bot 了,那要怎麽设定 Webhook 呢?
我们可以用 Telegram 的 REST API 设定 Webhook:

URL = `https://api.telegram.org/bot${token}/setWebhook?url=${webhook_url}`

我们在这里将 webhook_url 设为我们 Worker Root + /webhook
发送讯息後,如果 Telegram 回传这个就代表设定成功:

{"ok":true,"result":true,"description":"Webhook was set"}

然後我们回到我们的 main.js 新增程序来接收 webhook 收到的讯息:

// 我们会收到 Telegram 的 POST 请求
router.post("/webhook", async (request) => {
    return response({ status: 200 });
});

记住要写在 router.all("*", ... 前面会优先套用规则。
然後一定要回传 HTTP CODE 200,要不然 Telegram 会以为你没收到一直戳你。
Telegram 的文件中都有说明它会透过 webhook 传的格式:文件#update
我们在意的是 Update 中的 Message,文件中也有写到格式:文件#message

我们这样写来收发讯息:

router.post("/webhook", async (request) => {
    // 拿到 Webhook 的 JSON 格式资料
    const body = await request.json();

    // 因为不只有 Telegram 可以透过该网址传讯息,所以要确认讯息格式是否正确
    if (body.message && body.message.chat && body.message.chat.id && body.message.text) {
        // 我们在乎的是讯息内容及讯息来源
        const text = body.message.text;
        const chat_id = body.message.chat.id;

        // 回覆讯息,POST
        await fetch(`https://api.telegram.org/bot${TOKEN}/sendMessage`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            // 用 Markdown 格式回传颠倒的讯息
            body: JSON.stringify({ chat_id, parse_mode: "MarkdownV2", text: text.split("").reverse().join("") }),
        });
    }

    // 告诉 Telegram 我们收到了
    return response({ status: 200 });
});

在我们收到讯息後会回传覆相反的文字。
在上方程序中我们有一个 TOKEN 要用环境变数带入,在 wrangler.toml 最下方新增:

[vars]
TOKEN = "BOT_TOKEN"

接着让我们 wrangler publish 上去!

成功!

接下来

接下来让我们的 Automation 在有空位的时候通知我们!


每日铁人赛热门 Top 10 (1003)

以 10/03 20:00 ~ 10/04 20:00 文章观看数增加值排名

  1. +281 JS 07 - 原型方法:欲达则必速
    • 作者: Felix
    • 系列:JavaScript 从 50% 开始,打造函式库不是问题!
  2. +172 [Day28] 戏弄老板! 教你用Machine Learning将老板玩弄於股掌之间!
    • 作者: lulu_meat
    • 系列:奇怪的知识增加了!原来程序还可以这样用?!
  3. +158 Day 22网路程序设计
    • 作者: weiting_0826
    • 系列:一起学Flutter,和我变Better!
  4. +150 Proxmox VE 客体机磁碟迁移
    • 作者: Jason Cheng (节省哥)
    • 系列:突破困境:企业开源虚拟化管理平台
  5. +146 Day 1 无限手套 AWS 版:掌控一切的 5 + 1 云端必学主题
    • 作者: 用图片高效学程序
    • 系列:无限手套 AWS 版:掌控一切的 5 + 1 云端必学主题
  6. +138 Android学习笔记25
    • 作者: blossomgp3
    • 系列:Android kotlin &MVVM
  7. +123 从零开始的8-bit迷宫探险【Level 26】这游戏没有华佗,不能补血啊!Game Over 场景切换
    • 作者: 雪花冰
    • 系列:从零开始的8-bit迷宫探险!Swift SpriteKit 游戏开发实战
  8. +118 终章 - 资安碎碎念与心得
    • 作者: John酱
    • 系列:网路奇妙物语 - IT&Security
  9. +115 爬虫怎麽爬 从零开始的爬虫自学 DAY20 网路爬虫开爬-3抓取整页标题
    • 作者: 早安您好
    • 系列:爬虫怎麽爬 从零开始的爬虫自学
  10. +112 [面试][资料库]关连式资料库要如何设计避免超卖?
    • 作者: 宝宝出头天
    • 系列:全端工程师生存笔记

还剩 10 天!!


<<:  EP 26: MockData come back by (a little bit) DI design

>>:  【在厨房想30天的演算法】Day 19 演算法 : 图形搜寻 graph search 广度搜寻、深度搜寻

Day 30 完赛了...然後呢?

最後一天~~~~~ 到最後一天,其实也不免俗的想发表一下完赛感想, 说真的,很开心参加这次的铁人赛,...

Buy Store Location Data: Best Store Database Provider 2021

Store location data is information about the geogr...

【没钱买ps,PyQt自己写】Day 10 - 以 QFileDialog 读取系统的档案、资料夹

看完这篇文章你会得到的成果图 前言 我们接下来的讨论,会基於读者已经先读过我 day5 文章 的架构...

21 "准备完成" 用 PubSub 同步更新网页

拉出 component Component 除了在同一个 module 用之外也能拉出来放 我们来...

【从实作学习ASP.NET Core】Day04 | View 视图

昨天成功建立了 Controller 也得到了回传值,但它终究只是字串,所以今天要让他正式传入网页...