mirror of
https://github.com/dreamhunter2333/cloudflare_temp_email.git
synced 2026-06-25 17:35:07 +08:00
feat: telegram bot (#238)
This commit is contained in:
@@ -17,6 +17,7 @@ import MailsUnknow from './admin/MailsUnknow.vue';
|
||||
import About from './common/About.vue';
|
||||
import Maintenance from './admin/Maintenance.vue';
|
||||
import Appearance from './common/Appearance.vue';
|
||||
import Telegram from './admin/Telegram.vue';
|
||||
|
||||
const {
|
||||
localeCache, adminAuth, showAdminAuth, adminTab, loading, globalTabplacement
|
||||
@@ -46,6 +47,7 @@ const { t } = useI18n({
|
||||
unknow: 'Mails with unknow receiver',
|
||||
senderAccess: 'Sender Access Control',
|
||||
sendBox: 'Send Box',
|
||||
telegram: 'Telegram Bot',
|
||||
statistics: 'Statistics',
|
||||
maintenance: 'Maintenance',
|
||||
appearance: 'Appearance',
|
||||
@@ -64,6 +66,7 @@ const { t } = useI18n({
|
||||
unknow: '无收件人邮件',
|
||||
senderAccess: '发件权限控制',
|
||||
sendBox: '发件箱',
|
||||
telegram: '电报机器人',
|
||||
statistics: '统计',
|
||||
maintenance: '维护',
|
||||
appearance: '外观',
|
||||
@@ -121,6 +124,9 @@ onMounted(async () => {
|
||||
<n-tab-pane name="sendBox" :tab="t('sendBox')">
|
||||
<SendBox />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="telegram" :tab="t('telegram')">
|
||||
<Telegram />
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="statistics" :tab="t('statistics')">
|
||||
<Statistics />
|
||||
</n-tab-pane>
|
||||
|
||||
77
frontend/src/views/admin/Telegram.vue
Normal file
77
frontend/src/views/admin/Telegram.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup>
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import { useGlobalState } from '../../store'
|
||||
import { api } from '../../api'
|
||||
|
||||
const { localeCache } = useGlobalState()
|
||||
const message = useMessage()
|
||||
|
||||
const { t } = useI18n({
|
||||
locale: localeCache.value || 'zh',
|
||||
messages: {
|
||||
en: {
|
||||
init: 'Init',
|
||||
successTip: 'Success',
|
||||
status: 'Check Status',
|
||||
},
|
||||
zh: {
|
||||
init: '初始化',
|
||||
successTip: '成功',
|
||||
status: '查看状态',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const status = ref({
|
||||
fetched: false,
|
||||
})
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await api.fetch(`/admin/telegram/status`)
|
||||
Object.assign(status.value, res)
|
||||
status.value.fetched = true
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
}
|
||||
|
||||
const save = async () => {
|
||||
try {
|
||||
await api.fetch(`/admin/telegram/init`, {
|
||||
method: 'POST',
|
||||
})
|
||||
message.success(t('successTip'))
|
||||
} catch (error) {
|
||||
message.error(error.message || "error");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card style="max-width: 800px; overflow: auto;">
|
||||
<n-button @click="save" type="primary" block>
|
||||
{{ t('init') }}
|
||||
</n-button>
|
||||
<n-button @click="fetchData" secondary block>
|
||||
{{ t('status') }}
|
||||
</n-button>
|
||||
<pre v-if="status.fetched">{{ JSON.stringify(status, null, 2) }}</pre>
|
||||
</n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.center {
|
||||
display: flex;
|
||||
text-align: left;
|
||||
place-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.n-button {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -5,22 +5,19 @@ import { GithubAlt, Discord, Telegram } from '@vicons/fa'
|
||||
<template>
|
||||
<div class="center">
|
||||
<n-card>
|
||||
<n-button tag="a" target="_blank" href="https://github.com/dreamhunter2333/cloudflare_temp_email" secondary
|
||||
block strong>
|
||||
<n-button tag="a" target="_blank" href="https://github.com/dreamhunter2333/cloudflare_temp_email">
|
||||
<template #icon>
|
||||
<n-icon :component="GithubAlt" />
|
||||
</template>
|
||||
Github
|
||||
</n-button>
|
||||
<n-button tag="a" target="_blank" href="https://discord.gg/dQEwTWhA6Q" secondary
|
||||
block strong>
|
||||
<n-button tag="a" target="_blank" href="https://discord.gg/dQEwTWhA6Q">
|
||||
<template #icon>
|
||||
<n-icon :component="Discord" />
|
||||
</template>
|
||||
Discord
|
||||
</n-button>
|
||||
<n-button tag="a" target="_blank" href="https://t.me/cloudflare_temp_email" secondary
|
||||
block strong>
|
||||
<n-button tag="a" target="_blank" href="https://t.me/cloudflare_temp_email">
|
||||
<template #icon>
|
||||
<n-icon :component="Telegram" />
|
||||
</template>
|
||||
@@ -38,10 +35,10 @@ import { GithubAlt, Discord, Telegram } from '@vicons/fa'
|
||||
|
||||
.n-card {
|
||||
max-width: 800px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.n-button {
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -95,6 +95,8 @@ ENABLE_AUTO_REPLY = false
|
||||
# dkim config
|
||||
# DKIM_SELECTOR = "mailchannels" # Refer to the DKIM section mailchannels._domainkey for mailchannels
|
||||
# DKIM_PRIVATE_KEY = "" # Refer to the contents of priv_key.txt in the DKIM section
|
||||
# telegram bot
|
||||
# TG_MAX_ACCOUNTS = 5
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
|
||||
@@ -12,6 +12,7 @@ cp wrangler.toml.template wrangler.toml
|
||||
|
||||
> [!NOTE]
|
||||
> 如果你要启用注册用户功能,并需要发送邮件验证,则需要创建 `KV` 缓存, 不需要可跳过此步骤
|
||||
> 如果需要 Telegram Bot,需要创建 `KV` 缓存,不需要可跳过此步骤
|
||||
|
||||
通过命令行创建 KV 缓存,或者在 Cloudflare 控制台创建,然后复制对应配置到 `wrangler.toml` 文件中
|
||||
|
||||
@@ -62,6 +63,8 @@ ENABLE_AUTO_REPLY = false
|
||||
# dkim config
|
||||
# DKIM_SELECTOR = "mailchannels" # 参考 DKIM 部分 mailchannels._domainkey 的 mailchannels
|
||||
# DKIM_PRIVATE_KEY = "" # 参考 DKIM 部分 priv_key.txt 的内容
|
||||
# telegram bot 最多绑定邮箱数量
|
||||
# TG_MAX_ACCOUNTS = 5
|
||||
|
||||
# D1 数据库的名称和 ID 可以在 cloudflare 控制台查看
|
||||
[[d1_databases]]
|
||||
@@ -83,6 +86,17 @@ database_id = "xxx" # D1 数据库 ID
|
||||
# simple = { limit = 10, period = 60 }
|
||||
```
|
||||
|
||||
## Telegram Bot 配置
|
||||
|
||||
> [!NOTE]
|
||||
> 如果不需要 Telegram Bot, 可跳过此步骤
|
||||
|
||||
请先创建一个 Telegram Bot,然后获取 `token`,然后执行下面的命令,将 `token` 添加到 secrets 中
|
||||
|
||||
```bash
|
||||
pnpm wrangler secret put TELEGRAM_BOT_TOKEN
|
||||
```
|
||||
|
||||
## 部署
|
||||
|
||||
第一次部署会提示创建项目, `production` 分支请填写 `production`
|
||||
|
||||
@@ -28,6 +28,8 @@ docker-compose up -d
|
||||
|
||||
修改 docker-compose.yaml 中的环境变量, 注意选择合适的 `tag`
|
||||
|
||||
`proxy_url` 为 `worker` 的 URL 地址
|
||||
|
||||
```yaml
|
||||
services:
|
||||
smtp_proxy_server:
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
```python
|
||||
limit = 10
|
||||
offset = 0
|
||||
res = requests.post(
|
||||
res = requests.get(
|
||||
f"http://localhost:8787/api/mails?limit={limit}&offset={offset}`;",
|
||||
json=send_body, headers={
|
||||
headers={
|
||||
"Authorization": f"Bearer {你的JWT密码}",
|
||||
# "x-custom-auth": "<你的网站密码>", # 如果启用了自定义密码
|
||||
"Content-Type": "application/json"
|
||||
|
||||
@@ -45,3 +45,10 @@
|
||||
|
||||

|
||||

|
||||
|
||||
9. Telegram Bot 配置
|
||||
|
||||
> [!NOTE]
|
||||
> 如果不需要 Telegram Bot, 可跳过此步骤
|
||||
|
||||
请先创建一个 Telegram Bot,然后获取 `token`,然后执行下面的命令,将 `token` 添加到 `Variables` 中, Name: `TELEGRAM_BOT_TOKEN`
|
||||
|
||||
1
worker/.gitignore
vendored
1
worker/.gitignore
vendored
@@ -131,3 +131,4 @@ dist
|
||||
|
||||
.wrangler
|
||||
wrangler.toml
|
||||
.dev.vars
|
||||
|
||||
@@ -5,15 +5,23 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "wrangler dev",
|
||||
"deploy": "wrangler deploy",
|
||||
"deploy": "wrangler deploy --minify",
|
||||
"start": "wrangler dev",
|
||||
"build": "wrangler deploy src/worker.js --dry-run --outdir dist --minify"
|
||||
},
|
||||
"devDependencies": {
|
||||
"wrangler": "^3.53.1"
|
||||
"@cloudflare/workers-types": "^4.20240512.0",
|
||||
"wrangler": "^3.55.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"hono": "^4.3.0",
|
||||
"mimetext": "^3.0.24"
|
||||
"hono": "^4.3.6",
|
||||
"mimetext": "^3.0.24",
|
||||
"postal-mime": "^2.2.5",
|
||||
"telegraf": "4.16.3"
|
||||
},
|
||||
"pnpm": {
|
||||
"patchedDependencies": {
|
||||
"telegraf@4.16.3": "patches/telegraf@4.16.3.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
163
worker/patches/telegraf@4.16.3.patch
Normal file
163
worker/patches/telegraf@4.16.3.patch
Normal file
@@ -0,0 +1,163 @@
|
||||
diff --git a/lib/core/network/client.js b/lib/core/network/client.js
|
||||
index 25fbbbb47c7f88e83ae26f629e5ae1a0c141725c..209d4a6bf05352f44eeb082eb327581d698de5ce 100644
|
||||
--- a/lib/core/network/client.js
|
||||
+++ b/lib/core/network/client.js
|
||||
@@ -1,18 +1,18 @@
|
||||
"use strict";
|
||||
-var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function (o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
- desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
+ desc = { enumerable: true, get: function () { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
-}) : (function(o, m, k, k2) {
|
||||
+}) : (function (o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
-var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function (o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
-}) : function(o, v) {
|
||||
+}) : function (o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
@@ -29,8 +29,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
/* eslint @typescript-eslint/restrict-template-expressions: [ "error", { "allowNumber": true, "allowBoolean": true } ] */
|
||||
const crypto = __importStar(require("crypto"));
|
||||
const fs = __importStar(require("fs"));
|
||||
-const promises_1 = require("fs/promises");
|
||||
-const https = __importStar(require("https"));
|
||||
+// const promises_1 = require("fs/promises");
|
||||
+// const https = __importStar(require("https"));
|
||||
const path = __importStar(require("path"));
|
||||
const node_fetch_1 = __importDefault(require("node-fetch"));
|
||||
const check_1 = require("../helpers/check");
|
||||
@@ -61,10 +61,10 @@ const DEFAULT_OPTIONS = {
|
||||
apiRoot: 'https://api.telegram.org',
|
||||
apiMode: 'bot',
|
||||
webhookReply: true,
|
||||
- agent: new https.Agent({
|
||||
- keepAlive: true,
|
||||
- keepAliveMsecs: 10000,
|
||||
- }),
|
||||
+ // agent: new https.Agent({
|
||||
+ // keepAlive: true,
|
||||
+ // keepAliveMsecs: 10000,
|
||||
+ // }),
|
||||
attachmentAgent: undefined,
|
||||
testEnv: false,
|
||||
};
|
||||
@@ -112,9 +112,9 @@ async function buildFormDataConfig(payload, agent) {
|
||||
}
|
||||
const boundary = crypto.randomBytes(32).toString('hex');
|
||||
const formData = new multipart_stream_1.default(boundary);
|
||||
- await Promise.all(Object.keys(payload).map((key) =>
|
||||
- // @ts-expect-error payload[key] can obviously index payload, but TS doesn't trust us
|
||||
- attachFormValue(formData, key, payload[key], agent)));
|
||||
+ await Promise.all(Object.keys(payload).map((key) =>
|
||||
+ // @ts-expect-error payload[key] can obviously index payload, but TS doesn't trust us
|
||||
+ attachFormValue(formData, key, payload[key], agent)));
|
||||
return {
|
||||
method: 'POST',
|
||||
compress: true,
|
||||
@@ -205,14 +205,15 @@ async function attachFormMedia(form, media, id, agent) {
|
||||
if ('source' in media && media.source) {
|
||||
let mediaSource = media.source;
|
||||
if (typeof media.source === 'string') {
|
||||
- const source = await (0, promises_1.realpath)(media.source);
|
||||
- if ((await (0, promises_1.stat)(source)).isFile()) {
|
||||
- fileName = (_c = media.filename) !== null && _c !== void 0 ? _c : path.basename(media.source);
|
||||
- mediaSource = await fs.createReadStream(media.source);
|
||||
- }
|
||||
- else {
|
||||
- throw new TypeError(`Unable to upload '${media.source}', not a file`);
|
||||
- }
|
||||
+ throw new TypeError(`Unable to upload '${media.source}', not a file`);
|
||||
+ // const source = await (0, promises_1.realpath)(media.source);
|
||||
+ // if ((await (0, promises_1.stat)(source)).isFile()) {
|
||||
+ // fileName = (_c = media.filename) !== null && _c !== void 0 ? _c : path.basename(media.source);
|
||||
+ // mediaSource = await fs.createReadStream(media.source);
|
||||
+ // }
|
||||
+ // else {
|
||||
+ // throw new TypeError(`Unable to upload '${media.source}', not a file`);
|
||||
+ // }
|
||||
}
|
||||
if (isStream(mediaSource) || Buffer.isBuffer(mediaSource)) {
|
||||
form.addPart({
|
||||
diff --git a/lib/core/network/polling.js b/lib/core/network/polling.js
|
||||
index 42f20a5090304c56d0970da56eeaaacaa518ca92..0ae889c32d46e33440c62ad6d27a290c0fe3dda2 100644
|
||||
--- a/lib/core/network/polling.js
|
||||
+++ b/lib/core/network/polling.js
|
||||
@@ -6,10 +6,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Polling = void 0;
|
||||
const abort_controller_1 = __importDefault(require("abort-controller"));
|
||||
const debug_1 = __importDefault(require("debug"));
|
||||
-const util_1 = require("util");
|
||||
+// const util_1 = require("util");
|
||||
const error_1 = require("./error");
|
||||
const debug = (0, debug_1.default)('telegraf:polling');
|
||||
-const wait = (0, util_1.promisify)(setTimeout);
|
||||
+// const wait = (0, util_1.promisify)(setTimeout);
|
||||
function always(x) {
|
||||
return () => x;
|
||||
}
|
||||
@@ -47,7 +47,8 @@ class Polling {
|
||||
(err instanceof error_1.TelegramError && err.code >= 500)) {
|
||||
const retryAfter = (_b = (_a = err.parameters) === null || _a === void 0 ? void 0 : _a.retry_after) !== null && _b !== void 0 ? _b : 5;
|
||||
debug('Failed to fetch updates, retrying after %ds.', retryAfter, err);
|
||||
- await wait(retryAfter * 1000);
|
||||
+ // await wait(retryAfter * 1000);
|
||||
+ await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
|
||||
continue;
|
||||
}
|
||||
if (err instanceof error_1.TelegramError &&
|
||||
diff --git a/lib/telegraf.js b/lib/telegraf.js
|
||||
index 23d021c3d5f98493bd714a2114ec8fa853560e5c..90094d18316138b7e12eab42f722e69ccc9b6c1f 100644
|
||||
--- a/lib/telegraf.js
|
||||
+++ b/lib/telegraf.js
|
||||
@@ -28,8 +28,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Telegraf = void 0;
|
||||
const crypto = __importStar(require("crypto"));
|
||||
-const http = __importStar(require("http"));
|
||||
-const https = __importStar(require("https"));
|
||||
+// const http = __importStar(require("http"));
|
||||
+// const https = __importStar(require("https"));
|
||||
const composer_1 = require("./composer");
|
||||
const compact_1 = require("./core/helpers/compact");
|
||||
const context_1 = __importDefault(require("./context"));
|
||||
@@ -157,13 +157,13 @@ class Telegraf extends composer_1.Composer {
|
||||
const callback = typeof cb === 'function'
|
||||
? (req, res) => webhookCb(req, res, () => cb(req, res))
|
||||
: webhookCb;
|
||||
- this.webhookServer =
|
||||
- tlsOptions != null
|
||||
- ? https.createServer(tlsOptions, callback)
|
||||
- : http.createServer(callback);
|
||||
- this.webhookServer.listen(port, host, () => {
|
||||
- debug('Webhook listening on port: %s', port);
|
||||
- });
|
||||
+ // this.webhookServer =
|
||||
+ // tlsOptions != null
|
||||
+ // ? https.createServer(tlsOptions, callback)
|
||||
+ // : http.createServer(callback);
|
||||
+ // this.webhookServer.listen(port, host, () => {
|
||||
+ // debug('Webhook listening on port: %s', port);
|
||||
+ // });
|
||||
return this;
|
||||
}
|
||||
secretPathComponent() {
|
||||
@@ -176,7 +176,7 @@ class Telegraf extends composer_1.Composer {
|
||||
/**
|
||||
* @see https://github.com/telegraf/telegraf/discussions/1344#discussioncomment-335700
|
||||
*/
|
||||
- async launch(config = {},
|
||||
+ async launch(config = {},
|
||||
/** @experimental */
|
||||
onLaunch) {
|
||||
var _a, _b;
|
||||
189
worker/pnpm-lock.yaml
generated
189
worker/pnpm-lock.yaml
generated
@@ -4,20 +4,34 @@ settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
patchedDependencies:
|
||||
telegraf@4.16.3:
|
||||
hash: pnqp5pf7vetkijrc5uah4r5w6m
|
||||
path: patches/telegraf@4.16.3.patch
|
||||
|
||||
importers:
|
||||
|
||||
.:
|
||||
dependencies:
|
||||
hono:
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.0
|
||||
specifier: ^4.3.6
|
||||
version: 4.3.6
|
||||
mimetext:
|
||||
specifier: ^3.0.24
|
||||
version: 3.0.24
|
||||
postal-mime:
|
||||
specifier: ^2.2.5
|
||||
version: 2.2.5
|
||||
telegraf:
|
||||
specifier: 4.16.3
|
||||
version: 4.16.3(patch_hash=pnqp5pf7vetkijrc5uah4r5w6m)
|
||||
devDependencies:
|
||||
'@cloudflare/workers-types':
|
||||
specifier: ^4.20240512.0
|
||||
version: 4.20240512.0
|
||||
wrangler:
|
||||
specifier: ^3.53.1
|
||||
version: 3.53.1
|
||||
specifier: ^3.55.0
|
||||
version: 3.55.0(@cloudflare/workers-types@4.20240512.0)
|
||||
|
||||
packages:
|
||||
|
||||
@@ -63,6 +77,9 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@cloudflare/workers-types@4.20240512.0':
|
||||
resolution: {integrity: sha512-o2yTEWg+YK/I1t/Me+dA0oarO0aCbjibp6wSeaw52DSE9tDyKJ7S+Qdyw/XsMrKn4t8kF6f/YOba+9O4MJfW9w==}
|
||||
|
||||
'@cspotcode/source-map-support@0.8.1':
|
||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -223,11 +240,18 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.9':
|
||||
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
|
||||
|
||||
'@telegraf/types@7.1.0':
|
||||
resolution: {integrity: sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==}
|
||||
|
||||
'@types/node-forge@1.3.11':
|
||||
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
||||
|
||||
'@types/node@20.12.8':
|
||||
resolution: {integrity: sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==}
|
||||
'@types/node@20.12.12':
|
||||
resolution: {integrity: sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==}
|
||||
|
||||
abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
engines: {node: '>=6.5'}
|
||||
|
||||
acorn-walk@8.3.2:
|
||||
resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
|
||||
@@ -256,6 +280,15 @@ packages:
|
||||
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
buffer-alloc-unsafe@1.1.0:
|
||||
resolution: {integrity: sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==}
|
||||
|
||||
buffer-alloc@1.2.0:
|
||||
resolution: {integrity: sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==}
|
||||
|
||||
buffer-fill@1.0.0:
|
||||
resolution: {integrity: sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==}
|
||||
|
||||
capnp-ts@0.7.0:
|
||||
resolution: {integrity: sha512-XKxXAC3HVPv7r674zP0VC3RTXz+/JKhfyw94ljvF80yynK6VkTnqE3jMuN8b3dUVmmc43TjyxjW4KTsmB3c86g==}
|
||||
|
||||
@@ -267,8 +300,8 @@ packages:
|
||||
resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
core-js-pure@3.37.0:
|
||||
resolution: {integrity: sha512-d3BrpyFr5eD4KcbRvQ3FTUx/KWmaDesr7+a3+1+P46IUnNoEt+oiLijPINZMEon7w9oGkIINWxrBAU9DEciwFQ==}
|
||||
core-js-pure@3.37.1:
|
||||
resolution: {integrity: sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==}
|
||||
|
||||
data-uri-to-buffer@2.0.2:
|
||||
resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==}
|
||||
@@ -294,6 +327,10 @@ packages:
|
||||
estree-walker@0.6.1:
|
||||
resolution: {integrity: sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==}
|
||||
|
||||
event-target-shim@5.0.1:
|
||||
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
exit-hook@2.2.1:
|
||||
resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -324,8 +361,8 @@ packages:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hono@4.3.0:
|
||||
resolution: {integrity: sha512-rf9142VLQNMVBj+BjVLISgDWDxnJGUIuX39dvqcdySwr2gTsPfsqW1twWDUjfwQNWm9hEn40MpDu9RFGUN+e8A==}
|
||||
hono@4.3.6:
|
||||
resolution: {integrity: sha512-2IqXwrxWF4tG2AR7b5tMYn+KEnWK8UvdC/NUSbOKWj/Kj11OJqel58FxyiXLK5CcKLiL8aGtTe4lkBKXyaHMBQ==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
@@ -369,11 +406,15 @@ packages:
|
||||
mimetext@3.0.24:
|
||||
resolution: {integrity: sha512-UdG1KVfcxeEfo6el91lzFG2WLLTm8DxSK/rosxx5H2Pjla50+DSsjTgr9BRAfAkbQWaxvzcaTO+bHK5ZrdKdfA==}
|
||||
|
||||
miniflare@3.20240419.0:
|
||||
resolution: {integrity: sha512-fIev1PP4H+fQp5FtvzHqRY2v5s+jxh/a0xAhvM5fBNXvxWX7Zod1OatXfXwYbse3hqO3KeVMhb0osVtrW0NwJg==}
|
||||
miniflare@3.20240419.1:
|
||||
resolution: {integrity: sha512-Q9n0W07uUD/u0c/b03E4iogeXOAMjZnE3P7B5Yi8sPaZAx6TYWwjurGBja+Pg2yILN2iMaliEobfVyAKss33cA==}
|
||||
engines: {node: '>=16.13'}
|
||||
hasBin: true
|
||||
|
||||
mri@1.2.0:
|
||||
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
|
||||
@@ -386,6 +427,15 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
node-fetch@2.7.0:
|
||||
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
peerDependencies:
|
||||
encoding: ^0.1.0
|
||||
peerDependenciesMeta:
|
||||
encoding:
|
||||
optional: true
|
||||
|
||||
node-forge@1.3.1:
|
||||
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
||||
engines: {node: '>= 6.13.0'}
|
||||
@@ -394,6 +444,10 @@ packages:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
p-timeout@4.1.0:
|
||||
resolution: {integrity: sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
path-parse@1.0.7:
|
||||
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
|
||||
|
||||
@@ -404,6 +458,9 @@ packages:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
postal-mime@2.2.5:
|
||||
resolution: {integrity: sha512-6eTJf+B47JMdDuLF/4MBiGpTinxl0W8bA9CzrSoiQrNVRqK8Vhe59VrS6sXh2lG/lgo0bxpZFcWOF4Dv1FaSfg==}
|
||||
|
||||
printable-characters@1.0.42:
|
||||
resolution: {integrity: sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==}
|
||||
|
||||
@@ -432,6 +489,13 @@ packages:
|
||||
rollup-pluginutils@2.8.2:
|
||||
resolution: {integrity: sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==}
|
||||
|
||||
safe-compare@1.1.4:
|
||||
resolution: {integrity: sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==}
|
||||
|
||||
sandwich-stream@2.0.2:
|
||||
resolution: {integrity: sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
selfsigned@2.4.1:
|
||||
resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -455,10 +519,18 @@ packages:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
telegraf@4.16.3:
|
||||
resolution: {integrity: sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==}
|
||||
engines: {node: ^12.20.0 || >=14.13.1}
|
||||
hasBin: true
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
|
||||
tr46@0.0.3:
|
||||
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
|
||||
|
||||
tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
|
||||
@@ -469,13 +541,19 @@ packages:
|
||||
resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==}
|
||||
engines: {node: '>=14.0'}
|
||||
|
||||
webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||
|
||||
workerd@1.20240419.0:
|
||||
resolution: {integrity: sha512-9yV98KpkQgG+bdEsKEW8i1AYZgxns6NVSfdOVEB2Ue1pTMtIEYfUyqUE+O2amisRrfaC3Pw4EvjtTmVaoetfeg==}
|
||||
engines: {node: '>=16'}
|
||||
hasBin: true
|
||||
|
||||
wrangler@3.53.1:
|
||||
resolution: {integrity: sha512-bdMRQdHYdvowIwOhEMFkARIZUh56aDw7HLUZ/2JreBjj760osXE4Fc4L1TCkfRRBWgB6/LKF5LA4OcvORMYmHg==}
|
||||
wrangler@3.55.0:
|
||||
resolution: {integrity: sha512-VhtCioKxOdVqkHa8jQ6C6bX3by2Ko0uM0DKzrA+6lBZvfDUlGDWSOPiG+1fOHBHj2JTVBntxWCztXP6L+Udr8w==}
|
||||
engines: {node: '>=16.17.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -502,14 +580,14 @@ packages:
|
||||
youch@3.3.3:
|
||||
resolution: {integrity: sha512-qSFXUk3UZBLfggAW3dJKg0BMblG5biqSF8M34E06o5CSsZtH92u9Hqmj2RzGiHDi64fhe83+4tENFP2DB6t6ZA==}
|
||||
|
||||
zod@3.23.6:
|
||||
resolution: {integrity: sha512-RTHJlZhsRbuA8Hmp/iNL7jnfc4nZishjsanDAfEY1QpDQZCahUp3xDzl+zfweE9BklxMUcgBgS1b7Lvie/ZVwA==}
|
||||
zod@3.23.8:
|
||||
resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@babel/runtime-corejs3@7.24.5':
|
||||
dependencies:
|
||||
core-js-pure: 3.37.0
|
||||
core-js-pure: 3.37.1
|
||||
regenerator-runtime: 0.14.1
|
||||
|
||||
'@babel/runtime@7.24.5':
|
||||
@@ -535,6 +613,8 @@ snapshots:
|
||||
'@cloudflare/workerd-windows-64@1.20240419.0':
|
||||
optional: true
|
||||
|
||||
'@cloudflare/workers-types@4.20240512.0': {}
|
||||
|
||||
'@cspotcode/source-map-support@0.8.1':
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
@@ -626,14 +706,20 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
|
||||
'@telegraf/types@7.1.0': {}
|
||||
|
||||
'@types/node-forge@1.3.11':
|
||||
dependencies:
|
||||
'@types/node': 20.12.8
|
||||
'@types/node': 20.12.12
|
||||
|
||||
'@types/node@20.12.8':
|
||||
'@types/node@20.12.12':
|
||||
dependencies:
|
||||
undici-types: 5.26.5
|
||||
|
||||
abort-controller@3.0.0:
|
||||
dependencies:
|
||||
event-target-shim: 5.0.1
|
||||
|
||||
acorn-walk@8.3.2: {}
|
||||
|
||||
acorn@8.11.3: {}
|
||||
@@ -655,6 +741,15 @@ snapshots:
|
||||
dependencies:
|
||||
fill-range: 7.0.1
|
||||
|
||||
buffer-alloc-unsafe@1.1.0: {}
|
||||
|
||||
buffer-alloc@1.2.0:
|
||||
dependencies:
|
||||
buffer-alloc-unsafe: 1.1.0
|
||||
buffer-fill: 1.0.0
|
||||
|
||||
buffer-fill@1.0.0: {}
|
||||
|
||||
capnp-ts@0.7.0:
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
@@ -676,7 +771,7 @@ snapshots:
|
||||
|
||||
cookie@0.5.0: {}
|
||||
|
||||
core-js-pure@3.37.0: {}
|
||||
core-js-pure@3.37.1: {}
|
||||
|
||||
data-uri-to-buffer@2.0.2: {}
|
||||
|
||||
@@ -713,6 +808,8 @@ snapshots:
|
||||
|
||||
estree-walker@0.6.1: {}
|
||||
|
||||
event-target-shim@5.0.1: {}
|
||||
|
||||
exit-hook@2.2.1: {}
|
||||
|
||||
fill-range@7.0.1:
|
||||
@@ -739,7 +836,7 @@ snapshots:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
||||
hono@4.3.0: {}
|
||||
hono@4.3.6: {}
|
||||
|
||||
is-binary-path@2.1.0:
|
||||
dependencies:
|
||||
@@ -778,7 +875,7 @@ snapshots:
|
||||
js-base64: 3.7.7
|
||||
mime-types: 2.1.35
|
||||
|
||||
miniflare@3.20240419.0:
|
||||
miniflare@3.20240419.1:
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
acorn: 8.11.3
|
||||
@@ -791,28 +888,38 @@ snapshots:
|
||||
workerd: 1.20240419.0
|
||||
ws: 8.17.0
|
||||
youch: 3.3.3
|
||||
zod: 3.23.6
|
||||
zod: 3.23.8
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
mri@1.2.0: {}
|
||||
|
||||
ms@2.1.2: {}
|
||||
|
||||
mustache@4.2.0: {}
|
||||
|
||||
nanoid@3.3.7: {}
|
||||
|
||||
node-fetch@2.7.0:
|
||||
dependencies:
|
||||
whatwg-url: 5.0.0
|
||||
|
||||
node-forge@1.3.1: {}
|
||||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
p-timeout@4.1.0: {}
|
||||
|
||||
path-parse@1.0.7: {}
|
||||
|
||||
path-to-regexp@6.2.2: {}
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
||||
postal-mime@2.2.5: {}
|
||||
|
||||
printable-characters@1.0.42: {}
|
||||
|
||||
readdirp@3.6.0:
|
||||
@@ -843,6 +950,12 @@ snapshots:
|
||||
dependencies:
|
||||
estree-walker: 0.6.1
|
||||
|
||||
safe-compare@1.1.4:
|
||||
dependencies:
|
||||
buffer-alloc: 1.2.0
|
||||
|
||||
sandwich-stream@2.0.2: {}
|
||||
|
||||
selfsigned@2.4.1:
|
||||
dependencies:
|
||||
'@types/node-forge': 1.3.11
|
||||
@@ -861,10 +974,26 @@ snapshots:
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
telegraf@4.16.3(patch_hash=pnqp5pf7vetkijrc5uah4r5w6m):
|
||||
dependencies:
|
||||
'@telegraf/types': 7.1.0
|
||||
abort-controller: 3.0.0
|
||||
debug: 4.3.4
|
||||
mri: 1.2.0
|
||||
node-fetch: 2.7.0
|
||||
p-timeout: 4.1.0
|
||||
safe-compare: 1.1.4
|
||||
sandwich-stream: 2.0.2
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
|
||||
tr46@0.0.3: {}
|
||||
|
||||
tslib@2.6.2: {}
|
||||
|
||||
undici-types@5.26.5: {}
|
||||
@@ -873,6 +1002,13 @@ snapshots:
|
||||
dependencies:
|
||||
'@fastify/busboy': 2.1.1
|
||||
|
||||
webidl-conversions@3.0.1: {}
|
||||
|
||||
whatwg-url@5.0.0:
|
||||
dependencies:
|
||||
tr46: 0.0.3
|
||||
webidl-conversions: 3.0.1
|
||||
|
||||
workerd@1.20240419.0:
|
||||
optionalDependencies:
|
||||
'@cloudflare/workerd-darwin-64': 1.20240419.0
|
||||
@@ -881,7 +1017,7 @@ snapshots:
|
||||
'@cloudflare/workerd-linux-arm64': 1.20240419.0
|
||||
'@cloudflare/workerd-windows-64': 1.20240419.0
|
||||
|
||||
wrangler@3.53.1:
|
||||
wrangler@3.55.0(@cloudflare/workers-types@4.20240512.0):
|
||||
dependencies:
|
||||
'@cloudflare/kv-asset-handler': 0.3.2
|
||||
'@esbuild-plugins/node-globals-polyfill': 0.2.3(esbuild@0.17.19)
|
||||
@@ -889,7 +1025,7 @@ snapshots:
|
||||
blake3-wasm: 2.1.5
|
||||
chokidar: 3.6.0
|
||||
esbuild: 0.17.19
|
||||
miniflare: 3.20240419.0
|
||||
miniflare: 3.20240419.1
|
||||
nanoid: 3.3.7
|
||||
path-to-regexp: 6.2.2
|
||||
resolve: 1.22.8
|
||||
@@ -898,6 +1034,7 @@ snapshots:
|
||||
source-map: 0.6.1
|
||||
xxhash-wasm: 1.0.2
|
||||
optionalDependencies:
|
||||
'@cloudflare/workers-types': 4.20240512.0
|
||||
fsevents: 2.3.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
@@ -914,4 +1051,4 @@ snapshots:
|
||||
mustache: 4.2.0
|
||||
stacktracey: 2.1.8
|
||||
|
||||
zod@3.23.6: {}
|
||||
zod@3.23.8: {}
|
||||
|
||||
@@ -36,7 +36,12 @@ api.post('/admin/new_address', async (c) => {
|
||||
if (!name) {
|
||||
return c.text("Please provide a name", 400)
|
||||
}
|
||||
return newAddress(c, name, domain, enablePrefix);
|
||||
try {
|
||||
const res = await newAddress(c, name, domain, enablePrefix);
|
||||
return c.json(res);
|
||||
} catch (e) {
|
||||
return c.text(`Failed create address: ${e.message}`, 400)
|
||||
}
|
||||
})
|
||||
|
||||
api.delete('/admin/delete_address/:id', async (c) => {
|
||||
|
||||
@@ -7,14 +7,14 @@ export const newAddress = async (c, name, domain, enablePrefix) => {
|
||||
name = name.replace(/[^a-zA-Z0-9.]/g, '')
|
||||
// check name length
|
||||
if (name.length < 0) {
|
||||
return c.text("Name too short", 400)
|
||||
throw new Error("Name too short")
|
||||
}
|
||||
// create address
|
||||
if (enablePrefix) {
|
||||
name = getStringValue(c.env.PREFIX) + name;
|
||||
}
|
||||
if (name.length >= 30) {
|
||||
return c.text("Name too long (max 30)", 400)
|
||||
throw new Error("Name too long (max 30)")
|
||||
}
|
||||
// check domain, generate random domain
|
||||
const domains = getDomains(c);
|
||||
@@ -28,30 +28,27 @@ export const newAddress = async (c, name, domain, enablePrefix) => {
|
||||
`INSERT INTO address(name) VALUES(?)`
|
||||
).bind(name).run();
|
||||
if (!success) {
|
||||
return c.text("Failed to create address", 500)
|
||||
throw new Error("Failed to create address")
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.message && e.message.includes("UNIQUE")) {
|
||||
return c.text("Address already exists, please retry a new address", 400)
|
||||
throw new Error("Address already exists")
|
||||
}
|
||||
return c.text("Failed to create address", 500)
|
||||
throw new Error("Failed to create address")
|
||||
}
|
||||
let address_id = 0;
|
||||
try {
|
||||
address_id = await c.env.DB.prepare(
|
||||
`SELECT id FROM address where name = ?`
|
||||
).bind(name).first("id");
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
address_id = await c.env.DB.prepare(
|
||||
`SELECT id FROM address where name = ?`
|
||||
).bind(name).first("id");
|
||||
// create jwt
|
||||
const jwt = await Jwt.sign({
|
||||
address: name,
|
||||
address_id: address_id
|
||||
}, c.env.JWT_SECRET, "HS256")
|
||||
return c.json({
|
||||
jwt: jwt
|
||||
})
|
||||
return {
|
||||
jwt: jwt,
|
||||
address: name,
|
||||
}
|
||||
}
|
||||
|
||||
export const cleanup = async (c, cleanType, cleanDays) => {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
export const CONSTANTS = {
|
||||
VERSION: 'v0.4.2',
|
||||
|
||||
// DB settings
|
||||
ADDRESS_BLOCK_LIST_KEY: 'address_block_list',
|
||||
SEND_BLOCK_LIST_KEY: 'send_block_list',
|
||||
AUTO_CLEANUP_KEY: 'auto_cleanup',
|
||||
USER_SETTINGS_KEY: 'user_settings',
|
||||
|
||||
// KV
|
||||
TG_KV_PREFIX: "temp-mail-telegram"
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createMimeMessage } from "mimetext";
|
||||
import { getBooleanValue } from "./utils";
|
||||
import { sendMailToTelegram } from "./telegram_api";
|
||||
|
||||
async function email(message, env, ctx) {
|
||||
if (env.BLACK_LIST && env.BLACK_LIST.split(",").some(word => message.from.includes(word))) {
|
||||
@@ -20,6 +21,15 @@ async function email(message, env, ctx) {
|
||||
console.log(`Failed save message from ${message.from} to ${message.to}`);
|
||||
}
|
||||
|
||||
// send email to telegram
|
||||
try {
|
||||
await sendMailToTelegram({
|
||||
env: env,
|
||||
}, message.to, rawEmail);
|
||||
} catch (error) {
|
||||
console.log("send mail to telegram error", error);
|
||||
}
|
||||
|
||||
// auto reply email
|
||||
if (getBooleanValue(env.ENABLE_AUTO_REPLY)) {
|
||||
try {
|
||||
|
||||
@@ -106,7 +106,12 @@ api.post('/api/new_address', async (c) => {
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
return newAddress(c, name, domain, true);
|
||||
try {
|
||||
const res = await newAddress(c, name, domain, true);
|
||||
return c.json(res);
|
||||
} catch (e) {
|
||||
return c.text(`Failed create address: ${e.message}`, 400)
|
||||
}
|
||||
})
|
||||
|
||||
api.delete('/api/delete_address', async (c) => {
|
||||
|
||||
49
worker/src/telegram_api/index.ts
Normal file
49
worker/src/telegram_api/index.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { Hono, Context } from 'hono'
|
||||
import { ServerResponse } from 'node:http'
|
||||
import { Writable } from 'node:stream'
|
||||
import { newTelegramBot, initTelegramBotCommands, sendMailToTelegram } from './telegram'
|
||||
|
||||
export const api = new Hono()
|
||||
export { sendMailToTelegram }
|
||||
|
||||
api.post("/telegram/webhook", async (c: Context) => {
|
||||
const token = c.env.TELEGRAM_BOT_TOKEN;
|
||||
const bot = newTelegramBot(c, token);
|
||||
let body = null;
|
||||
const res = new Writable();
|
||||
Object.assign(res, {
|
||||
headersSent: false,
|
||||
setHeader: (name: string, value: string) => c.header(name, value),
|
||||
end: (data: any) => body = data,
|
||||
});
|
||||
const reqJson = await c.req.json();
|
||||
await bot.handleUpdate(reqJson, res as ServerResponse);
|
||||
return c.body(body);
|
||||
});
|
||||
|
||||
api.post("/admin/telegram/init", async (c: Context) => {
|
||||
if (!c.env.TELEGRAM_BOT_TOKEN || !c.env.KV) {
|
||||
return c.text("TELEGRAM_BOT_TOKEN and KV are required", 400);
|
||||
}
|
||||
const domain = new URL(c.req.url).host;
|
||||
const token = c.env.TELEGRAM_BOT_TOKEN;
|
||||
const webhookUrl = `https://${domain}/telegram/webhook`;
|
||||
console.log(`setting webhook to ${webhookUrl}`);
|
||||
const bot = newTelegramBot(c, token);
|
||||
await bot.telegram.setWebhook(webhookUrl)
|
||||
await initTelegramBotCommands(bot);
|
||||
return c.json({
|
||||
message: "webhook set successfully",
|
||||
});
|
||||
});
|
||||
|
||||
api.get("/admin/telegram/status", async (c: Context) => {
|
||||
if (!c.env.TELEGRAM_BOT_TOKEN || !c.env.KV) {
|
||||
return c.text("TELEGRAM_BOT_TOKEN and KV are required", 400);
|
||||
}
|
||||
const token = c.env.TELEGRAM_BOT_TOKEN;
|
||||
const bot = newTelegramBot(c, token);
|
||||
const info = await bot.telegram.getWebhookInfo()
|
||||
const commands = await bot.telegram.getMyCommands()
|
||||
return c.json({ info, commands });
|
||||
});
|
||||
269
worker/src/telegram_api/telegram.ts
Normal file
269
worker/src/telegram_api/telegram.ts
Normal file
@@ -0,0 +1,269 @@
|
||||
|
||||
import { Context } from "hono";
|
||||
import { Jwt } from 'hono/utils/jwt'
|
||||
import { Telegraf, Context as TgContext, Markup } from "telegraf";
|
||||
import { callbackQuery } from "telegraf/filters";
|
||||
import PostalMime from 'postal-mime';
|
||||
|
||||
import { CONSTANTS } from "../constants";
|
||||
// @ts-ignore
|
||||
import { getIntValue, getDomains, getStringValue } from '../utils';
|
||||
// @ts-ignore
|
||||
import { newAddress } from '../common'
|
||||
|
||||
const COMMANDS = [
|
||||
{
|
||||
command: "start",
|
||||
description: "开始使用"
|
||||
},
|
||||
{
|
||||
command: "new",
|
||||
description: "新建邮箱地址, 如果要自定义邮箱地址, 请输入 /new <name>@<domain>, name [a-zA-Z0-9.] 有效"
|
||||
},
|
||||
{
|
||||
command: "address",
|
||||
description: "查看邮箱地址列表"
|
||||
},
|
||||
{
|
||||
command: "bind",
|
||||
description: "绑定邮箱地址, 请输入 /bind <邮箱地址凭证>"
|
||||
},
|
||||
{
|
||||
command: "mails",
|
||||
description: "查看邮件, 请输入 /mails <邮箱地址>, 不输入地址默认查看第一个地址"
|
||||
},
|
||||
]
|
||||
|
||||
export function newTelegramBot(c: Context, token: string): Telegraf {
|
||||
const bot = new Telegraf(token);
|
||||
bot.command("start", async (ctx: TgContext) => {
|
||||
if (ctx.chat?.type !== "private") {
|
||||
return await ctx.reply("请在私聊中使用");
|
||||
}
|
||||
const prefix = getStringValue(c.env.PREFIX)
|
||||
const domains = getDomains(c);
|
||||
return await ctx.reply(
|
||||
"欢迎使用本机器人\n\n"
|
||||
+ (prefix ? `当前已启用前缀: ${prefix}\n` : '')
|
||||
+ "新建邮箱地址, 如果要自定义邮箱地址, "
|
||||
+ "请输入 /new <name>@<domain>, name [a-zA-Z0-9.] 有效\n"
|
||||
+ `当前可用域名: ${JSON.stringify(domains)}\n`
|
||||
+ "请使用以下命令:\n"
|
||||
+ COMMANDS.map(c => `/${c.command}: ${c.description}`).join("\n")
|
||||
);
|
||||
});
|
||||
bot.command("new", async (ctx: TgContext) => {
|
||||
if (ctx.chat?.type !== "private") {
|
||||
return await ctx.reply("请在私聊中使用");
|
||||
}
|
||||
const userId = ctx?.message?.from?.id;
|
||||
if (!userId) {
|
||||
return await ctx.reply("无法获取用户信息");
|
||||
}
|
||||
try {
|
||||
// @ts-ignore
|
||||
const address = ctx?.message?.text.slice("/new".length).trim();
|
||||
if (!address) {
|
||||
return await ctx.reply("请输入邮箱地址");
|
||||
}
|
||||
const [name, domain] = address.includes("@") ? address.split("@") : [address, null];
|
||||
const jwtList = await c.env.KV.get(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, { type: 'json' }) || [];
|
||||
if (jwtList.length >= getIntValue(c.env.TG_MAX_ADDRESS, 5)) {
|
||||
return await ctx.reply("绑定地址数量已达上限");
|
||||
}
|
||||
const res = await newAddress(c, name, domain, true);
|
||||
// for mail push to telegram
|
||||
await c.env.KV.put(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, JSON.stringify([...jwtList, res.jwt]));
|
||||
await c.env.KV.put(`${CONSTANTS.TG_KV_PREFIX}:${res.address}`, userId);
|
||||
return await ctx.reply(`创建地址成功:\n`
|
||||
+ `地址: ${res.address}\n`
|
||||
+ `凭证: ${res.jwt}\n`
|
||||
);
|
||||
} catch (e) {
|
||||
return await ctx.reply(`创建地址失败: ${(e as Error).message}`);
|
||||
}
|
||||
});
|
||||
|
||||
bot.command("bind", async (ctx: TgContext) => {
|
||||
if (ctx.chat?.type !== "private") {
|
||||
return await ctx.reply("请在私聊中使用");
|
||||
}
|
||||
const userId = ctx?.message?.from?.id;
|
||||
if (!userId) {
|
||||
return await ctx.reply("无法获取用户信息");
|
||||
}
|
||||
try {
|
||||
// @ts-ignore
|
||||
const jwt = ctx?.message?.text.slice("/bind".length).trim();
|
||||
if (!jwt) {
|
||||
return await ctx.reply("请输入凭证");
|
||||
}
|
||||
const { address } = await Jwt.verify(jwt, c.env.JWT_SECRET, "HS256");
|
||||
if (!address) {
|
||||
return await ctx.reply("凭证无效");
|
||||
}
|
||||
const jwtList = await c.env.KV.get(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, { type: 'json' }) || [];
|
||||
if (jwtList.length >= getIntValue(c.env.TG_MAX_ADDRESS, 5)) {
|
||||
return await ctx.reply("绑定地址数量已达上限");
|
||||
}
|
||||
await c.env.KV.put(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, JSON.stringify([...jwtList, jwt]));
|
||||
// for mail push to telegram
|
||||
await c.env.KV.put(`${CONSTANTS.TG_KV_PREFIX}:${address}`, userId);
|
||||
return await ctx.reply(`绑定成功:\n`
|
||||
+ `地址: ${address}`
|
||||
);
|
||||
}
|
||||
catch (e) {
|
||||
return await ctx.reply(`绑定失败: ${(e as Error).message}`);
|
||||
}
|
||||
});
|
||||
|
||||
bot.command("address", async (ctx: TgContext) => {
|
||||
if (ctx.chat?.type !== "private") {
|
||||
return await ctx.reply("请在私聊中使用");
|
||||
}
|
||||
const userId = ctx?.message?.from?.id;
|
||||
if (!userId) {
|
||||
return await ctx.reply("无法获取用户信息");
|
||||
}
|
||||
try {
|
||||
const jwtList = await c.env.KV.get(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, { type: 'json' }) || [];
|
||||
const addressList = [];
|
||||
for (const jwt of jwtList) {
|
||||
try {
|
||||
const { address } = await Jwt.verify(jwt, c.env.JWT_SECRET, "HS256");
|
||||
addressList.push(address);
|
||||
} catch (e) {
|
||||
addressList.push("此凭证无效");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return await ctx.reply(`地址列表:\n\n`
|
||||
+ addressList.map(a => `地址: ${a}`).join("\n")
|
||||
);
|
||||
} catch (e) {
|
||||
return await ctx.reply(`获取地址列表失败: ${(e as Error).message}`);
|
||||
}
|
||||
});
|
||||
|
||||
const queryMail = async (ctx: TgContext, queryAddress: string, mailIndex: number, edit: boolean) => {
|
||||
const userId = ctx?.message?.from?.id || ctx.callbackQuery?.message?.chat?.id;
|
||||
if (!userId) {
|
||||
return await ctx.reply("无法获取用户信息");
|
||||
}
|
||||
const jwtList = await c.env.KV.get(`${CONSTANTS.TG_KV_PREFIX}:${userId}`, { type: 'json' }) || [];
|
||||
const addressList = [];
|
||||
for (const jwt of jwtList) {
|
||||
try {
|
||||
const { address } = await Jwt.verify(jwt, c.env.JWT_SECRET, "HS256");
|
||||
addressList.push(address);
|
||||
} catch (e) {
|
||||
addressList.push("此凭证无效");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!queryAddress && addressList.length > 0) {
|
||||
queryAddress = addressList[0];
|
||||
}
|
||||
if (!addressList.includes(queryAddress)) {
|
||||
return await ctx.reply(`未绑定此地址 ${queryAddress}`);
|
||||
}
|
||||
const raw = await c.env.DB.prepare(
|
||||
`SELECT * FROM raw_mails where address = ? `
|
||||
+ ` order by id desc limit 1 offset ?`
|
||||
).bind(
|
||||
queryAddress, mailIndex
|
||||
).first("raw");
|
||||
const { mail } = await parseMail(raw);
|
||||
if (edit) {
|
||||
return await ctx.editMessageText(mail || "无邮件",
|
||||
{
|
||||
...Markup.inlineKeyboard([
|
||||
Markup.button.callback("上一条", `mail_${queryAddress}_${mailIndex - 1}`, mailIndex <= 0),
|
||||
Markup.button.callback("下一条", `mail_${queryAddress}_${mailIndex + 1}`, !raw),
|
||||
])
|
||||
},
|
||||
);
|
||||
}
|
||||
return await ctx.reply(mail || "无邮件",
|
||||
{
|
||||
...Markup.inlineKeyboard([
|
||||
Markup.button.callback("上一条", `mail_${queryAddress}_${mailIndex - 1}`, mailIndex <= 0),
|
||||
Markup.button.callback("下一条", `mail_${queryAddress}_${mailIndex + 1}`, !raw),
|
||||
])
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
bot.command("mails", async ctx => {
|
||||
try {
|
||||
const queryAddress = ctx?.message?.text.slice("/mails".length).trim();
|
||||
return await queryMail(ctx, queryAddress, 0, false);
|
||||
} catch (e) {
|
||||
return await ctx.reply(`获取邮件失败: ${(e as Error).message}`);
|
||||
}
|
||||
});
|
||||
|
||||
bot.on(callbackQuery("data"), async ctx => {
|
||||
// Use ctx.callbackQuery.data
|
||||
try {
|
||||
const data = ctx.callbackQuery.data;
|
||||
if (data && data.startsWith("mail_") && data.split("_").length === 3) {
|
||||
const [_, queryAddress, mailIndex] = data.split("_");
|
||||
await queryMail(ctx, queryAddress, parseInt(mailIndex), true);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log(`获取邮件失败: ${(e as Error).message}`, e);
|
||||
return await ctx.answerCbQuery(`获取邮件失败: ${(e as Error).message}`);
|
||||
}
|
||||
await ctx.answerCbQuery();
|
||||
});
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
|
||||
export async function initTelegramBotCommands(bot: Telegraf) {
|
||||
bot.telegram.sendMessage
|
||||
await bot.telegram.setMyCommands(COMMANDS);
|
||||
}
|
||||
|
||||
const parseMail = async (raw_mail: string) => {
|
||||
if (!raw_mail) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
const parsedEmail = await PostalMime.parse(raw_mail);
|
||||
return {
|
||||
isHtml: false,
|
||||
mail: `From: ${parsedEmail.from ? `${parsedEmail.from.name}[${parsedEmail.from.address}]` : "无发件人"}\n`
|
||||
+ `To: ${parsedEmail.to?.map(t => `${t.name}[${t.address}]`).join(" ")}\n`
|
||||
+ `Subject: ${parsedEmail.subject}\n`
|
||||
+ `Date: ${parsedEmail.date}\n`
|
||||
+ `Content:\n${parsedEmail.text?.substring(0, 100) || "解析失败"}`
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
isHtml: false,
|
||||
mail: `解析邮件失败: ${(e as Error).message}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function sendMailToTelegram(c: Context, address: string, raw_mail: string) {
|
||||
if (!c.env.TELEGRAM_BOT_TOKEN || !c.env.KV) {
|
||||
return;
|
||||
}
|
||||
const userId = await c.env.KV.get(`${CONSTANTS.TG_KV_PREFIX}:${address}`);
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
const { mail } = await parseMail(raw_mail);
|
||||
if (!mail) {
|
||||
return;
|
||||
}
|
||||
const bot = newTelegramBot(c, c.env.TELEGRAM_BOT_TOKEN);
|
||||
await bot.telegram.sendMessage(userId, mail);
|
||||
}
|
||||
@@ -47,10 +47,24 @@ export const getBooleanValue = (value) => {
|
||||
if (typeof value === "string") {
|
||||
return value === "true";
|
||||
}
|
||||
console.error("Invalid boolean value", value);
|
||||
console.error(`Failed to parse boolean value: ${value}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
export const getIntValue = (value, defaultValue = 0) => {
|
||||
if (typeof value === "number") {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
try {
|
||||
return parseInt(value);
|
||||
} catch (e) {
|
||||
console.error(`Failed to parse int value: ${value}`);
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
export const getDomains = (c) => {
|
||||
if (!c.env.DOMAINS) {
|
||||
return [];
|
||||
|
||||
@@ -9,6 +9,7 @@ import { api as userApi } from './user_api';
|
||||
import { api as adminApi } from './admin_api';
|
||||
import { api as apiV1 } from './deprecated';
|
||||
import { api as apiSendMail } from './mails_api/send_mail_api'
|
||||
import { api as telegramApi } from './telegram_api'
|
||||
|
||||
import { email } from './email';
|
||||
import { scheduled } from './scheduled';
|
||||
@@ -107,6 +108,7 @@ app.route('/', userApi)
|
||||
app.route('/', adminApi)
|
||||
app.route('/', apiV1)
|
||||
app.route('/', apiSendMail)
|
||||
app.route('/', telegramApi)
|
||||
|
||||
app.get('/', async c => c.text("OK"))
|
||||
app.get('/health_check', async c => c.text("OK"))
|
||||
|
||||
15
worker/tsconfig.json
Normal file
15
worker/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"lib": [
|
||||
"ESNext"
|
||||
],
|
||||
"types": [
|
||||
"@cloudflare/workers-types"
|
||||
]
|
||||
},
|
||||
}
|
||||
@@ -38,6 +38,8 @@ ENABLE_AUTO_REPLY = false
|
||||
# dkim config
|
||||
# DKIM_SELECTOR = ""
|
||||
# DKIM_PRIVATE_KEY = ""
|
||||
# telegram bot
|
||||
# TG_MAX_ACCOUNTS = 5
|
||||
|
||||
[[d1_databases]]
|
||||
binding = "DB"
|
||||
|
||||
Reference in New Issue
Block a user