托管:Heroku
我们假设你有关于如何使用 grammY 创建 bot 的基本知识。 如果你还没有准备好,不要犹豫,请查看我们十分友好的 指南 ! 🚀
本教程将指导你如何通过 webhooks 或者 长轮询 将 Telegram bot 部署到 Heroku。 我们还假设你已经有了一个 Heroku 账户。
前提条件
首先,安装一些依赖:
# 创建项目目录。
mkdir grammy-bot
cd grammy-bot
npm init --y
# 安装主要依赖。
npm install grammy express
# 安装开发依赖。
npm install -D typescript @types/express @types/node
# 创建 TypeScript 配置文件。
npx tsc --init
2
3
4
5
6
7
8
9
10
11
12
13
我们将 TypeScript 文件放在 src
文件夹中,编译文件放在 dist
文件夹中。 在项目的根目录下创建文件夹。 然后,在 src
文件夹中创建一个名为 bot
的新文件。 现在,我们的文件夹目录结构应该是这样的:
.
├── node_modules/
├── dist/
├── src/
│ └── bot.ts
├── package.json
├── package-lock.json
└── tsconfig.json
然后,将 tsconfig
修改为如下配置:
{
"compilerOptions": {
"target": "ESNEXT",
"module": "ESNext", // 把 commonjs 改成 esnext
"lib": ["ES2021"],
"outDir": "./dist/",
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src"]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
因为在上面的配置中,我们将 module
选项从 commonjs
设置为了 esnext
,所以我们需要在 package
中添加 "type":
。 package
文件如下所示:
{
"name": "grammy-bot",
"version": "0.0.1",
"description": "",
"main": "dist/app.js",
"type": "module", // 添加 "type": "module"
"scripts": {
"dev-build": "tsc"
},
"author": "",
"license": "ISC",
"dependencies": {
"grammy": "^1.2.0",
"express": "^4.17.1"
},
"devDependencies": {
"typescript": "^4.3.5",
"@types/express": "^4.17.13",
"@types/node": "^16.3.1"
},
"keywords": []
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
上面我们提到了,我们有两个选择来接收 Telegram 的数据,webhooks 和长轮询。 你可以在 这里 了解更多关于两者之间的优缺点,再决定使用哪一个。
Webhooks
如果你决定使用长轮询,你可以跳过本节,跳转到 关于长轮询的章节。🚀
简而言之,与长轮询不同的是,webhook 不会通过持续运行来获取 Telegram 传入的消息。 这会减少服务器负载,并为我们节省大量的 dyno 时间,尤其是当你在使用经济计划时。😁
Okay,让我们继续! 还记得我们之前创建的 bot
吗? 我们不会把所有的代码都扔在里面,而是把 bot 的部分留给你自己。 所以我们将使用 app
来作为我们的主要入口。 这意味着每次 Telegram (或者其他人)访问我们的网站时,express
会决定你的服务器的哪个部分将负责处理请求。 当你在同一个域名下部署网站和机器人时,这会很有用。 此外,通过将代码分割到不同的文件,可以让我们的代码看起来更整洁。✨
Express 和它的中间件
在 src
文件夹中创建 app
,并添加如下代码:
import express from "express";
import { webhookCallback } from "grammy";
import { bot } from "./bot.js";
const domain = String(process.env.DOMAIN);
const secretPath = String(process.env.BOT_TOKEN);
const app = express();
app.use(express.json());
app.use(`/${secretPath}`, webhookCallback(bot, "express"));
app.listen(Number(process.env.PORT), async () => {
// 请确保它是 `https` 而不是 `http`!
await bot.api.setWebhook(`https://${domain}/${secretPath}`);
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
让我们看一下上面的代码:
process
:请记住,千万不要在我们的代码中存储凭证! 关于如何在 Heroku 中创建 环境变量,请前往这个 指南。.env secret
:它可以是我们的Path BOT
或者任何随机字符串。最好的做法是按照 Telegram 的解释 来隐藏我们的 bot 的路径。_TOKEN
⚡ 优化(可选)
第 14 行的 bot
将会在每次 Heroku 再次启动你的服务器时运行。 对于低流量的 bot,每次请求都会触发。 然而,我们并不需要在每次有请求时都运行这段代码。 因此,我们完全可以删除这一部分,只需要手动执行一次 GET
。 在部署我们的 bot 之后,在你的浏览器上打开这个链接:
https://api.telegram.org/bot<bot_token>/setWebhook?url=<webhook_url>
请注意,有些浏览器要求你在传递 webhook
前手动 编码。 举个例子,如果我们有一个 bot token abcd:
和网址 https://
,那么我们的链接应该像这样:
https://api.telegram.org/botabcd:1234/setWebhook?url=https%3A%2F%2Fgrammybot.herokuapp.com%2Fsecret_path
⚡ 优化(可选)
使用 Webhook Reply 以提高效率。
创建 bot.ts
(Webhook)
下一步,前往 bot
:
import { Bot } from "grammy";
const token = process.env.BOT_TOKEN;
if (!token) throw Error("BOT_TOKEN is unset");
export const bot = new Bot(token);
bot.command("start", (ctx) => ctx.reply("Hello there!"));
bot.on("message", (ctx) => ctx.reply("Got another message!"));
2
3
4
5
6
7
8
9
很好! 我们现在完成了主要部分的代码。 但在我们进行部署之前,我们可以对我们的 bot 进行一点优化。 和刚才一样,这是可选的。
⚡ 优化(可选)
每次你的服务器启动时,grammY 会向 Telegram 请求 bot 的信息,以便在 ctx
下的 上下文对象 提供 bot 的信息。 我们可以设置 bot 的信息 以防止过多的 get
调用。
在你最喜欢的浏览器中打开这个链接
https://
,推荐使用 Firefox,因为它能格式化显示api .telegram .org /bot<bot _token> /get Me json
数据。根据
get
的结果来修改我们上面第 4 行的代码:Me tsconst token = process.env.BOT_TOKEN; if (!token) throw new Error("BOT_TOKEN is unset"); export const bot = new Bot(token, { botInfo: { id: 111111111, is_bot: true, first_name: "xxxxxxxxx", username: "xxxxxxbot", can_join_groups: true, can_read_all_group_messages: false, supports_inline_queries: false, }, });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
酷!现在是时候准备我们的部署环境了! 可以直接跳到 部署部分 !💪
长轮询
在使用长轮询时,你的脚本会持续运行。
除非你知道如何处理这种行为,否则确保你有足够的 dyno 时间。
考虑使用 webhooks 吗? 跳转到 webhooks 章节。🚀
在你的服务器上使用长轮询并不总是一个坏主意。 有些时候它适用于不需要快速响应和处理大量数据的数据收集 bot。 你可以通过长轮询的方式来每个小时运行一次来获取消息。 这是你没办法用 webhook 控制的。 如果你的 bot 收到大量的消息,你会看到大量的 webhook 请求,但是你可以使用长轮询更轻松地控制处理 update 的速度。
创建 bot.ts
(长轮询)
让我们打开我们之前创建的 bot
文件。 添加如下几行代码:
import { Bot } from "grammy";
const token = process.env.BOT_TOKEN;
if (!token) throw new Error("BOT_TOKEN unset");
const bot = new Bot(token);
bot.command(
"start",
(ctx) => ctx.reply("I'm running on Heroku using long polling!"),
);
bot.start();
2
3
4
5
6
7
8
9
10
11
12
13
就是这样! 我们已经准备好去部署它了。 看起来是不是非常简单?😃 如果你觉得这太简单了,可以查看我们的 部署清单!🚀
部署
等一下…我们的 Rocket Bot 还没有准备好发射。 请先完成下面这些步骤!
编译文件
在你的终端中运行这段命令,将 TypeScript 文件编译为 JavaScript 文件:
npx tsc
如果运行成功且没有任何报错,我们的编译文件应该在 dist
文件夹中,扩展名为 .js
。
设置 Procfile
目前 Heroku
有好几种 dynos 类型。 其中两个是:
Web dynos:
Web dynos 是接收来自路由器的 HTTP 流量的 “web” 进程。 这种类型的 dyno 在执行代码时有 30 秒的超时限制。 另外,如果在 30 分钟内没有需要处理的请求,它就会休眠。 这种类型的 dyno 非常适合 webhooks。
Worker dynos:
Worker dynos 通常用于后台工作。 它没有超时限制,而且如果它不处理任何请求,它也不会休眠。 它适合 长轮询。
在项目根目录下创建没有扩展名的 Procfile
文件。 也就是说,Procfile
和 procfile
是无效的。 然后以下面这个格式写入单行代码:
<dynos type>: <command for executing our main entry file>
对于我们的情况来说,应该是这样:
web: node dist/app.js
worker: node dist/bot.js
设置 Git
我们将使用 Git 和 Heroku Cli 来部署我们的 bot。 这里是安装的链接:
假设你已经在你的机器里安装了它们,并且你在项目的根目录下打开了一个终端。 现在在终端中运行这段代码来初始化本地 git 仓库:
git init
接下来,我们需要避免不必要的文件上传到我们的生产服务器(在这篇教程里生产服务器指的是 Heroku
)。 在项目的根目录下创建一个名为 .gitignore
的文件。 然后添加下列内容:
node_modules/
src/
tsconfig.json
2
3
最终,我们的文件夹结构看起来应该是这样的:
.
├── .git/
├── node_modules/
├── dist/
│ ├── bot.js
│ └── app.js
├── src/
│ ├── bot.ts
│ └── app.ts
├── package.json
├── package-lock.json
├── tsconfig.json
├── Procfile
└── .gitignore
.
├── .git/
├── node_modules/
├── dist/
│ └── bot.js
├── src/
│ └── bot.ts
├── package.json
├── package-lock.json
├── tsconfig.json
├── Procfile
└── .gitignore
将文件提交到我们的 git 仓库:
git add .
git commit -m "My first commit"
2
设置 Heroku Remote
如果你已经创建了 Heroku 应用,在下面的 <my
中传入你的 已存在应用
的名称,然后运行代码。 否则,请运行 新应用
heroku create
git remote -v
2
heroku git:remote -a <myApp>
部署代码
最后,按下 红色按钮,升空!🚀
git push heroku main
如果失败了,可能我们的 git 分支是 master
而不是 main
。 请按下这个 蓝色按钮:
git push heroku master