diff --git a/README.md b/README.md index aa58adb..1125d15 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# 🤖 Telegram Private Chatbot (v5.3) +# 🤖 Telegram Private Chatbot (v5.4) [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/jikssha/telegram_private_chatbot) ![GitHub stars](https://img.shields.io/github/stars/jikssha/telegram_private_chatbot?style=social) @@ -13,29 +13,22 @@ ---
-📢 v5.1 版本重要更新公告 (2026-01-05) +📢 v5.4 版本更新公告 (2026-05-16) -### 主要修复: -- **自动话题修复**:被删除话题用户不再转发到 General,会自动新建话题。 -- **话题无限循环修复**:针对创建失败添加重试机制,最多重试 3 次。 -- **消息路由规范化**:修复字符串与数字混用问题,统一规范化为 String 类型。 -- **并发验证加固**:添加验证锁机制,彻底杜绝并发绕过漏洞。 -- **数据读取保护**:实现 `safeGetJSON()` 安全读取机制,防止 KV 数据损坏导致崩溃。 -- **验证系统重构**:改用索引方案,完全避免按钮回调截断问题,100% 可用。 +### 新增功能: +- **关键词过滤系统**:新增硬编码+KV动态词库,支持文本及媒体描述 (Caption) 拦截。 +- **动态管理指令**:新增 `/addword`, `/delword`, `/listwords`,无需重启即可实时维护屏蔽词库。 +- **性能优化**:引入内存缓存,保证高并发下的词库查询速度。 -### 更新功能: -**批量清理工具**:/cleanup # 扫描并清理已删除话题的用户数据 - -### ⚠️ 更新指南: -Fork用户可直接点击sync 更新同步,自动更新 -手动部署用户复制worker.js代码到worker,重新部署一次 +### 历史修复: +- 自动话题修复、消息路由规范化、并发验证加固、多媒体转发优化等。
--- ## 📑 目录 (Table of Contents) -* [✨ 核心特性](#-核心特性) +*[✨ 核心特性](#-核心特性) * [🛠️ 管理员指令](#-管理员指令) * [🚀 部署教程](#-部署教程) * [方法一:GitHub 一键连接 (推荐)](#方法一github-一键连接部署-推荐-) @@ -68,14 +61,21 @@ v4.0 版本移除了所有不稳定的外部 API 依赖,专注于**极致的 | 指令 | 作用 | 适用场景 | | :--- | :--- | :--- | -| `/close` | **强制关闭对话**
机器人会提示用户对话已结束,并拒收新消息。 | 工单处理完成,礼貌结束咨询。 | -| `/open` | **重新开启对话**
恢复对该用户的消息转发。 | 误操作关闭,或用户需再次联系。 | -| `/ban` | **封禁用户**
机器人将完全无视该用户的所有消息(无提示)。 | 遇到恶意刷屏、广告机器人。 | -| `/unban` | **解封用户**
恢复该用户的正常通讯权限。 | 给予改过自新的机会。 | -| `/trust` | **永久信任**
该用户将永久免除人机验证(永不过期)。 | 熟人、VIP 客户、长期合作伙伴。 | -| `/reset` | **重置验证**
强制清除该用户的验证状态,下次需重新验证。 | 测试验证流程,或怀疑账号被盗。 | -| `/info` | **查看信息**
显示当前用户的 UID、话题 ID 和链接。 | 查询用户资料。 | -| `/cleanup` | **批量清理**
扫描并清理已删除话题的用户数据。 | 清理失效用户。 | +| `/close` | **强制关闭对话** | 工单处理完成,礼貌结束咨询。 | +| `/open` | **重新开启对话** | 恢复对该用户的消息转发。 | +| `/ban` | **封禁用户** | 遇到恶意刷屏、广告机器人。 | +| `/unban` | **解封用户** | 恢复该用户的正常通讯权限。 | +| `/trust` | **永久信任** | 熟人、VIP 客户、长期合作伙伴。 | +| `/reset` | **重置验证** | 测试验证流程,或怀疑账号被盗。 | +| `/info` | **查看信息** | 查询用户资料。 | +| `/cleanup` | **批量清理** | 扫描并清理已删除话题的用户数据。 | +| `/addword <词>` | **添加屏蔽词** | 将词汇加入动态词库,实时拦截。 | +| `/delword <词>` | **删除屏蔽词** | 从动态词库中移除已添加的屏蔽词。 | +| `/listwords` | **查看词库** | 查看当前生效的全部屏蔽词列表。 | + +#### 🚫 关键词过滤功能说明 +- 本版本新增了关键词智能拦截功能,支持拦截文本及图片/视频描述 (Caption)。 +- 通过命令添加的词汇会存入 KV,修改后 60 秒内全局生效。 --- @@ -87,95 +87,52 @@ v4.0 版本移除了所有不稳定的外部 API 依赖,专注于**极致的 2. **管理员群组**:创建一个 Telegram 群组,并**开启话题功能 (Topics)**。 * 将机器人拉入群组,并设为**管理员**(给予管理话题权限)。 * 获取群组 ID(通常以 `-100` 开头)。 - ``获取 SUPERGROUP_ID 小技巧: -在 Telegram 桌面端右键群内任意消息,复制消息链接;链接里会有一段 -100xxxxxxxxxx 或 xxxxxxxxxx;若只看到纯数字 xxxxxxxxxx,在前面加上 -100,就是完整的 SUPERGROUP_ID(私密频道/群组同理)。`` ### 方法一:GitHub 一键连接部署 (推荐 ★) -这是最简单的自动化部署方式,当您更新 GitHub 仓库时,Cloudflare 会自动重新部署您的 Worker。 - 1. **Fork 本仓库** 到您的 GitHub 账户。 2. 登录 [Cloudflare Dashboard](https://dash.cloudflare.com/)。 3. 导航到 **Workers & Pages** -> **Create Application**。 -4. 点击 **Connect to Git** 标签页。 -5. 授权 Cloudflare 访问您的 GitHub,并选择您刚才 Fork 的 `telegram_private_chatbot` 仓库。 -6. **配置部署设置**: - * 项目名称:`telegram-private-chatbot` (或任意名称)。 - * 生产分支:通常是 `main` 或 `master`。 - * 其余保持默认,点击 **Save and Deploy**。 -7. **⚠️ 关键步骤:绑定数据库与变量** - * 部署完成后,进入该 Worker 的 **Settings** -> **Variables** 页面。 - * **绑定 KV 数据库** (必须): - * 在 Cloudflare 左侧菜单 **KV** 中创建一个新的 Namespace(例如叫 `TOPIC_MAP`)。 - * 回到 Worker 的 Variables 页面,向下滚动到 **KV Namespace Bindings**。 - * 点击 **Add binding**,变量名填写 `TOPIC_MAP` (必须全大写),Namespace 选择刚才创建的那个。 - * **添加环境变量**: - * `BOT_TOKEN`: 你的机器人 Token。 - * `SUPERGROUP_ID`: 你的群组 ID (例如 -100123...)。 -8. **最后一步**:配置完成后,点击页面顶部的 **Deployments** 标签,找到最新的部署记录,点击右侧的 **Retry deployment** (重新部署),让变量生效。 - -### 方法二:手动复制部署 (简单直接) - -如果您不想关联 GitHub,可以直接复制代码。 - -1. 登录 [Cloudflare Dashboard](https://dash.cloudflare.com/)。 -2. 进入 **Workers & Pages** -> **Create Application** -> **Create Worker** ,选择从`hello world`开始。 -3. 命名你的 Worker,点击 **Deploy**。 -4. 点击 **Edit code**,将本项目 `worker.js` 的所有代码复制粘贴进去,覆盖原代码。 -5. 点击右上角 **Deploy** 保存。 -6. **配置 KV 与变量**: - * 去 **Settings** -> **Variables**。 - * 添加 KV 绑定:Variable name 填 `TOPIC_MAP`,并绑定一个 KV 数据库。 - * 添加环境变量:`BOT_TOKEN` 和 `SUPERGROUP_ID`。 - * 点击 **Save and Deploy**。 +4. 点击 **Connect to Git** 标签页,选择刚才 Fork 的 `telegram_private_chatbot` 仓库。 +5. **配置部署设置**:点击 **Save and Deploy**。 +6. **⚠️ 关键步骤:绑定数据库与变量** + * 进入 Worker 的 **Settings** -> **Variables**。 + * **绑定 KV 数据库**:添加 binding,名称填写 `TOPIC_MAP`,Namespace 选择你的 KV 数据库。 + * **添加环境变量**:`BOT_TOKEN` 和 `SUPERGROUP_ID`。 +7. **最后一步**:在 **Deployments** 页面点击 **Retry deployment**。 ---- +### 方法二:手动复制部署 -### 最后一步:激活 Webhook (至关重要) +1. 在 Cloudflare 创建一个 Worker。 +2. 点击 **Edit code**,将 `worker.js` 代码全部粘贴进去。 +3. 点击 **Deploy**。 +4. 在 **Settings** -> **Variables** 中添加 `TOPIC_MAP` 绑定以及 `BOT_TOKEN`、`SUPERGROUP_ID` 变量。 -无论使用哪种部署方式,最后都需要手动告诉 Telegram 你的 Worker 地址。请在浏览器中**严格按顺序**访问以下 URL: +--- - **设置新 Webhook**: - ``` - (https://api.telegram.org/bot)/setWebhook?url= - ``` - *将 `` 替换为机器人 Token,`` 替换为 Worker 的完整域名或者你绑定的自定义的域名 (如 `https://xxx.workers.dev`)。* - - *举例:https://api.telegram.org/bot1234:HUSH2GW/setWebhook?url=https://1234.workers.dev* `前面的bot别删了` +### 最后一步:激活 Webhook (至关重要) -如果返回 `{"ok":true, "result":true, "description":"Webhook was set"}`,即表示部署成功! +在浏览器中访问以下 URL(请替换 `` 和 ``): +`https://api.telegram.org/bot/setWebhook?url=` --- ## ❓ 常见问题 (FAQ) **Q1: 为什么点击验证按钮没有反应?** -A: 请检查 Webhook 是否正确设置。必须确保 Telegram 允许发送 `callback_query` 事件。请务必执行上述“最后一步”中的重置操作。 - -**Q2: 为什么机器人无法在群里创建话题?** -A: 请确保:1. 群组 ID 正确(-100开头);2. 群组已开启 Topics 功能;3. 机器人是群管理员且拥有 "Manage Topics" 权限。 - -**Q3: 为什么人机验证能通过收不到转发的消息?** -A: 请仔细检查所有变量名称和id是否准确,删除webhook再重新激活。 - `(https://api.telegram.org/bot)/deleteWebhook?drop_pending_updates=true` - - 如果依然无法正常转发消息,尝试完成所有步骤后,最后再添加bot的管理员权限。 - -**Q4: 为什么webhook设置失败?** -A: 如果你设置了自定义域名不成功,Webhook 改回 workers.dev 域名再尝试。这种情况是你域名解析失败或者网络环境阻断造成的 - ---- +A: 请确保 Webhook 设置正确,Telegram 必须能向你的 Worker 发送回调请求。 -## 🔒 安全说明 +**Q2: 为什么关键词过滤没有拦截成功?** +A: 请确保在管理员群组的话题内执行指令。系统有 60 秒的内存缓存,添加指令后稍等片刻即可。 -> [!IMPORTANT] -> 请妥善保管您的 Bot API Token ,不要泄露,这些信息关系到您服务的安全性。 +**Q3: 为什么机器人无法在群里创建话题?** +A: 确保机器人拥有群组的 "Manage Topics" 权限,且群组已开启话题功能。 --- ## 📈 Star History -[![Star History Chart](https://api.star-history.com/svg?repos=jikssha/telegram_private_chatbot&type=date&legend=top-left)](https://www.star-history.com/#jikssha/telegram_private_chatbot&type=date&legend=top-left) +[![Star History Chart](https://api.star-history.com/svg?repos=jikssha/telegram_private_chatbot&type=date&legend=top-left)](https://star-history.com/#jikssha/telegram_private_chatbot&type=date&legend=top-left) --- -**如果这个项目对你有帮助,请给个 Star ⭐️ 吧!** +**如果这个项目对你有帮助,请给个 Star ⭐️ 吧!** \ No newline at end of file diff --git a/README_EN.md b/README_EN.md index e8d10bf..099a51e 100644 --- a/README_EN.md +++ b/README_EN.md @@ -1,4 +1,4 @@ -# 🛡️ TeleGuard (v4.0) +# 🛡️ TeleGuard (v5.4) [![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/jikssha/telegram_private_chatbot) ![GitHub stars](https://img.shields.io/github/stars/jikssha/telegram_private_chatbot?style=social) @@ -13,143 +13,113 @@ Deploy a free, enterprise-grade customer service system utilizing Cloudflare's p --- +
+📢 v5.4 Update Highlights (2026-05-16) + +### New Features: +- **Keyword Filtering System**: Added a dual-layer filtering system (hardcoded + KV dynamic word list) supporting text and media caption interception. +- **Dynamic Management**: Added admin commands (`/addword`, `/delword`, `/listwords`) for real-time blacklist maintenance. +- **Performance**: Introduced in-memory caching for high-speed keyword lookups. +
+ +--- + ## 📑 Table of Contents * [✨ Key Features](#-key-features) * [🛠️ Administrator Commands](#-administrator-commands) * [🚀 Deployment Tutorial](#-deployment-tutorial) - * [Method 1: One-Click Deploy via GitHub (Recommended)](#method-1-one-click-deploy-via-github-recommended-) + *[Method 1: One-Click Deploy via GitHub (Recommended)](#method-1-one-click-deploy-via-github-recommended-) * [Method 2: Manual Deployment](#method-2-manual-deployment-simple--direct) * [Final Step: Activate Webhook](#final-step-activate-webhook-crucial) -* [❓ FAQ](#-faq) +*[❓ FAQ](#-faq) * [📈 Star History](#-star-history) --- ## ✨ Key Features -Version 4.0 removes all unstable external API dependencies, focusing on **extreme speed** and **absolute stability**. - | Feature | Description | | :--- | :--- | -| **⚡ Zero-Latency Verification** | Uses a **local curated trivia database**. Verifies instantly, completely eliminating network timeouts and API errors with a 100% success rate. | -| **🛡️ Smart Anti-Spam** | **Short ID mechanism** fixes the Telegram button click failure bug. Provides a **30-day disturbance-free period** after verification, balancing security and user experience. | -| **💬 Topic Group Management** | Utilizes **Telegram Forum Topics** to automatically create a separate topic for each private chat user, isolating messages for organized management. | -| **👮 Invisible Command System** | Automatically **intercepts** commands starting with `/` sent by users to prevent harassment. Admin commands are only effective within the administrator group. | -| **🔒 Permission Control** | Powerful command set: Supports **Ban (/ban)**, **Unban (/unban)**, **Close Ticket (/close)**, and **Trust (/trust)** operations. | -| **☁️ Serverless** | Runs entirely on Cloudflare Workers. **Zero cost**, server-free, maintenance-free, and handles high concurrency. | -| **📸 Multimedia Support** | Perfectly supports two-way forwarding of text, images, videos, files, and other message formats without losing any details. | +| **⚡ Zero-Latency Verification** | Uses a **local curated trivia database**. Verifies instantly, with a 100% success rate. | +| **🛡️ Smart Anti-Spam** | **Short ID mechanism** provides a **30-day disturbance-free period** after verification. | +| **💬 Topic Group Management** | Utilizes **Telegram Forum Topics** to automatically create separate topics for each user for organized management. | +| **👮 Invisible Command System** | Automatically **intercepts** `/` commands sent by users. Admin commands are only effective within the admin group. | +| **🔒 Permission Control** | Powerful command set: Supports **Ban (/ban)**, **Unban (/unban)**, **Close Ticket (/close)**, and **Trust (/trust)**. | +| **☁️ Serverless** | Runs entirely on Cloudflare Workers. **Zero cost**, server-free, and handles high concurrency. | +| **📸 Multimedia Support** | Perfectly supports two-way forwarding of text, images, videos, and files. | --- ## 🛠️ Administrator Commands -> **Note**: The following commands are only effective within **topics in the administrator group**. Commands sent by users in private chats will be silently intercepted and will not disturb administrators. +> **Note**: The following commands are only effective within **topics in the administrator group**. | Command | Action | Scenario | | :--- | :--- | :--- | -| `/close` | **Force Close Chat**
The bot will notify the user that the chat has ended and reject new messages. | Ticket resolved; politely ending the consultation. | -| `/open` | **Reopen Chat**
Resumes message forwarding for the user. | Accidental closure, or the user needs to contact again. | -| `/ban` | **Ban User**
The bot will completely ignore all messages from this user (no notification). | Malicious spamming, ad bots. | -| `/unban` | **Unban User**
Restores the user's normal communication permissions. | Giving a second chance. | -| `/trust` | **Permanent Trust**
The user will be permanently exempt from CAPTCHA verification (never expires). | Acquaintances, VIP clients, long-term partners. | -| `/reset` | **Reset Verification**
Forcibly clears the user's verification status; re-verification required next time. | Testing verification flow, or suspected account compromise. | -| `/info` | **View Info**
Displays the current user's UID, Topic ID, and profile link. | Checking user details. | +| `/close` | **Force Close Chat** | Ticket resolved; politely ending the consultation. | +| `/open` | **Reopen Chat** | Resumes message forwarding for the user. | +| `/ban` | **Ban User** | Malicious spamming, ad bots. | +| `/unban` | **Unban User** | Giving a second chance. | +| `/trust` | **Permanent Trust** | VIP clients or partners (no CAPTCHA). | +| `/reset` | **Reset Verification** | Testing flow or suspected compromise. | +| `/info` | **View Info** | Checking user UID and topic link. | +| `/cleanup` | **Batch Cleanup** | Scans and removes data for deleted topics. | +| `/addword ` | **Add Keyword** | Block new spam/ads in real-time. | +| `/delword ` | **Remove Keyword** | Remove an incorrect or unnecessary block rule. | +| `/listwords` | **List Keywords** | Display all active blocked words (Hardcoded + Dynamic). | + +#### 🚫 Keyword Filtering Description +- Added intelligent keyword interception, supporting text and media `caption` detection. +- Keywords added via commands are stored in KV and take effect globally within 60 seconds. --- ## 🚀 Deployment Tutorial ### Prerequisites -1. **Telegram Bot**: Apply for a bot from [@BotFather](https://t.me/BotFather) and get the `Token`. - * *Important*: Turn off **Group Privacy** in BotFather (`/mybots` > Settings > Group Privacy > Turn off). -2. **Administrator Group**: Create a Telegram group and **enable Topics**. - * Add the bot to the group and set it as an **Administrator** (grant "Manage Topics" permission). - * Get the Group ID (usually starts with `-100`). - > **Tip for getting SUPERGROUP_ID**: In Telegram Desktop, right-click any message in the group and copy the message link. The link will contain a segment like `-100xxxxxxxxxx` or `xxxxxxxxxx`. If you only see numbers `xxxxxxxxxx`, add `-100` in front to get the full `SUPERGROUP_ID` (same applies to private channels/groups). +1. **Telegram Bot**: Apply for a bot from[@BotFather](https://t.me/BotFather) and get the `Token`. *Important: Turn off Group Privacy.* +2. **Administrator Group**: Create a group, enable Topics, add the bot, and grant "Manage Topics" permission. ### Method 1: One-Click Deploy via GitHub (Recommended ★) - -This is the simplest automated deployment method. Cloudflare will automatically redeploy your Worker when you update your GitHub repository. - -1. **Fork this repository** to your GitHub account. -2. Log in to the [Cloudflare Dashboard](https://dash.cloudflare.com/). -3. Navigate to **Workers & Pages** -> **Create Application**. -4. Click the **Connect to Git** tab. -5. Authorize Cloudflare to access your GitHub and select the `telegram_private_chatbot` repository you just forked. -6. **Configure Deployment**: - * Project Name: `telegram-private-chatbot` (or any name). - * Production Branch: Usually `main` or `master`. - * Keep others as default and click **Save and Deploy**. -7. **⚠️ Crucial Step: Bind Database & Variables** - * After deployment, go to the **Settings** -> **Variables** page of the Worker. - * **Bind KV Database** (Required): - * In the Cloudflare sidebar menu **KV**, create a new Namespace (e.g., named `TOPIC_MAP`). - * Go back to the Worker's Variables page, scroll down to **KV Namespace Bindings**. - * Click **Add binding**, set Variable name to `TOPIC_MAP` (must be uppercase), and select the Namespace you just created. - * **Add Environment Variables**: - * `BOT_TOKEN`: Your bot token. - * `SUPERGROUP_ID`: Your group ID (e.g., -100123...). -8. **Final Step**: After configuration, go to the **Deployments** tab at the top, find the latest deployment record, and click **Retry deployment** on the right to apply variables. +1. **Fork this repository** to your GitHub. +2. In Cloudflare Workers & Pages, select **Connect to Git** to link this repository. +3. **Configure Binding & Variables**: + * In **Settings -> Variables**, bind a KV Namespace (Variable name **must** be `TOPIC_MAP`). + * Add Environment Variables `BOT_TOKEN` and `SUPERGROUP_ID` (starts with `-100`). +4. **Final Step**: In the **Deployments** tab, click **Retry deployment**. ### Method 2: Manual Deployment (Simple & Direct) - -If you don't want to link GitHub, you can copy the code directly. - -1. Log in to [Cloudflare Dashboard](https://dash.cloudflare.com/). -2. Go to **Workers & Pages** -> **Create Application** -> **Create Worker**, start from `Hello World`. -3. Name your Worker and click **Deploy**. -4. Click **Edit code**, copy and paste all code from `worker.js` in this project, overwriting the original code. -5. Click **Deploy** in the top right corner. -6. **Configure KV & Variables**: - * Go to **Settings** -> **Variables**. - * Add KV Binding: Variable name `TOPIC_MAP`, bind to a KV database. - * Add Environment Variables: `BOT_TOKEN` and `SUPERGROUP_ID`. - * Click **Save and Deploy**. +1. Create a Worker in Cloudflare. +2. Click **Edit code**, paste the `worker.js` content. +3. Click **Deploy**. +4. In **Settings -> Variables**, add the `TOPIC_MAP` binding and required environment variables. --- ### Final Step: Activate Webhook (Crucial) - -Regardless of the deployment method, you must manually tell Telegram your Worker address. Visit the following URL in your browser **strictly in order**: - - **Set New Webhook**: - ``` - [https://api.telegram.org/bot](https://api.telegram.org/bot)/setWebhook?url= - ``` - *Replace `` with your bot token, and `` with your Worker's full domain or custom domain (e.g., `https://xxx.workers.dev`).* - -If it returns `{"ok":true, "result":true, "description":"Webhook was set"}`, the deployment is successful! +Visit the following URL in your browser (replace Token and Worker URL): +`https://api.telegram.org/bot/setWebhook?url=https://` --- ## ❓ FAQ **Q: Why does clicking the verification button do nothing?** -A: Please check if the Webhook is set correctly. You must ensure Telegram is allowed to send `callback_query` events. Please perform the reset operation in the "Final Step" above. - -**Q: Why can't the bot create topics in the group?** -A: Please ensure: 1. Group ID is correct (starts with -100); 2. Topics are enabled in the group; 3. The bot is an administrator and has "Manage Topics" permission. - ---- +A: Ensure the Webhook is set correctly. You can try deleting the old Webhook first and setting it again. -## 🔒 Security Note - -> [!IMPORTANT] -> Please keep your Bot API Token and Secret Token safe, as this information is critical to the security of your service. +**Q: Why isn't keyword filtering working?** +A: Ensure you sent the command in the admin group topic. The system has a 60-second memory cache, so wait a moment for changes to propagate. -> [!WARNING] -> Do not change the configured Secret Token arbitrarily! After changing it, all registered bots will fail to work because they cannot match the original token. If you need to change it, all bots must be re-registered. - -- Choose a secure and memorable Secret Token during initial setup. -- Avoid using simple or common prefixes. -- Do not share sensitive information with others. +**Q: Why can't the bot create topics?** +A: Ensure the bot has "Manage Topics" permission and that the group has Topics enabled. --- -## 📈 Star History +## 🔒 Security Note -[![Star History Chart](https://api.star-history.com/svg?repos=jikssha/telegram_private_chatbot&type=Date)](https://star-history.com/#jikssha/telegram_private_chatbot&Date) +>[!IMPORTANT] +> Please keep your `BOT_TOKEN` safe. Never share it or commit it to public repositories. --- -**If this project helps you, please give it a Star ⭐️!** +**If this project helps you, please give it a Star ⭐️!** \ No newline at end of file diff --git a/worker.js b/worker.js index 9ea4dca..f14eaba 100644 --- a/worker.js +++ b/worker.js @@ -1,4 +1,4 @@ -// Cloudflare Worker:Telegram 双向机器人 v5.3 +// Cloudflare Worker:Telegram 双向机器人 v5.4 // --- 配置常量 --- const CONFIG = { @@ -50,6 +50,68 @@ const LOCAL_QUESTIONS = [ {"question": "小狗发出的叫声通常是?", "correct_answer": "汪汪", "incorrect_answers": ["喵喵", "咩咩", "呱呱"]} ]; +// --- 屏蔽词列表(硬编码,用户可自行修改此数组) --- +const BLOCKED_WORDS = [ + "赌博", + "色情", + "代开发", + "加微信", + // ↑ 在此添加更多屏蔽词,每行一个,用引号包裹、逗号结尾 +]; + +// 屏蔽词内存缓存(减少 KV 读取频率) +const blockedWordsCache = { data: null, ts: 0, ttl: 60000 }; // 缓存 60 秒 + +/** + * 获取完整屏蔽词列表 = 硬编码 + KV 动态词库(合并去重) + * @param {object} env - Worker 环境 + * @param {boolean} forceRefresh - 是否强制刷新缓存 + * @returns {Promise} + */ +async function getBlockedWords(env, forceRefresh = false) { + const now = Date.now(); + if (!forceRefresh && blockedWordsCache.data && (now - blockedWordsCache.ts < blockedWordsCache.ttl)) { + return blockedWordsCache.data; + } + + // 从 KV 读取动态屏蔽词 + let kvWords = []; + try { + const raw = await env.TOPIC_MAP.get("blocked_words_kv"); + if (raw) { + const parsed = JSON.parse(raw); + if (Array.isArray(parsed)) { + kvWords = parsed.filter(w => typeof w === "string" && w.trim().length > 0); + } + } + } catch (e) { + Logger.warn('blocked_words_kv_parse_error', { error: e.message }); + } + + // 合并去重(硬编码优先,KV 补充) + const merged = [...new Set([...BLOCKED_WORDS, ...kvWords])]; + blockedWordsCache.data = merged; + blockedWordsCache.ts = now; + return merged; +} + +/** + * 检查文本是否包含屏蔽词 + * @param {string} text - 待检查文本 + * @param {string[]} words - 屏蔽词列表 + * @returns {{ hit: boolean, word: string|null }} + */ +function containsBlockedWord(text, words) { + if (!text || !words || words.length === 0) return { hit: false, word: null }; + const lower = text.toLowerCase(); + for (const w of words) { + if (w && lower.includes(w.toLowerCase())) { + return { hit: true, word: w }; + } + } + return { hit: false, word: null }; +} + // --- 辅助工具函数 --- // 结构化日志系统 @@ -482,6 +544,19 @@ async function handlePrivateMessage(msg, env, ctx) { const isBanned = await env.TOPIC_MAP.get(`banned:${userId}`); if (isBanned) return; + // 屏蔽词检查(在验证之前检查,避免带屏蔽词的消息被暂存并转发) + const blockedWords = await getBlockedWords(env); + const textToCheck = [msg.text, msg.caption].filter(Boolean).join(" "); + const blockResult = containsBlockedWord(textToCheck, blockedWords); + if (blockResult.hit) { + Logger.info('message_blocked_by_word', { userId, word: blockResult.word }); + await tgCall(env, "sendMessage", { + chat_id: userId, + text: "🚫 您的消息包含违规内容,已被拦截,请修改后重新发送。" + }); + return; + } + const verified = await env.TOPIC_MAP.get(`verified:${userId}`); if (!verified) { @@ -502,7 +577,7 @@ async function forwardToTopic(msg, userId, key, env, ctx) { return; } - // 【修复 #4】使用安全的 JSON 解析 + // 使用安全的 JSON 解析 let rec = await safeGetJSON(env, key, null); if (rec && rec.closed) { @@ -510,7 +585,7 @@ async function forwardToTopic(msg, userId, key, env, ctx) { return; } - // 【修复 #5】重试计数器,防止无限循环 + // 重试计数器,防止无限循环 const retryKey = `retry:${userId}`; let retryCount = parseInt(await env.TOPIC_MAP.get(retryKey) || "0"); @@ -538,7 +613,7 @@ async function forwardToTopic(msg, userId, key, env, ctx) { } } - // 【修复1】验证话题是否仍然存在(带缓存,降低探测频率) + // 验证话题是否仍然存在(带缓存,降低探测频率) // 当话题被删除后,KV中的thread_id仍然存在,但实际话题已不可用 if (rec && rec.thread_id) { const cacheKey = rec.thread_id; @@ -589,7 +664,22 @@ async function forwardToTopic(msg, userId, key, env, ctx) { } } + // --- 屏蔽词检查(文本 + caption 均检查) --- + const blockedWords = await getBlockedWords(env); + const textToCheck = [msg.text, msg.caption].filter(Boolean).join(" "); + const blockResult = containsBlockedWord(textToCheck, blockedWords); + if (blockResult.hit) { + Logger.info('message_blocked_by_word', { userId, word: blockResult.word }); + await tgCall(env, "sendMessage", { + chat_id: userId, + text: "🚫 您的消息包含违规内容,已被拦截,请修改后重新发送。" + }); + return; + } + if (msg.media_group_id) { + // 媒体组也需要检查 caption + if (blockResult.hit) return; // 上面已拦截,此处冗余保险 await handleMediaGroup(msg, env, ctx, { direction: "p2t", targetChat: env.SUPERGROUP_ID, @@ -668,7 +758,7 @@ async function forwardToTopic(msg, userId, key, env, ctx) { } } - // 【修复2】增强错误处理,双重保险 + // 增强错误处理,双重保险 // 如果上面的测试没有捕获到,这里再次检测 if (!res.ok) { const desc = normalizeTgDescription(res.description); @@ -711,7 +801,7 @@ async function handleAdminReply(msg, env, ctx) { return; } - // 【修复】允许在任何话题执行 /cleanup 命令 + // 允许在任何话题执行 /cleanup 命令 if (text === "/cleanup") { // /cleanup 可能处理较久,使用 waitUntil 防止 webhook 请求超时导致“卡住” ctx.waitUntil(handleCleanupCommand(threadId, env)); @@ -799,6 +889,97 @@ async function handleAdminReply(msg, env, ctx) { return; } + // --- 屏蔽词管理命令 --- + + // /addword 词 — 添加屏蔽词到 KV 动态词库 + if (text.startsWith("/addword ")) { + const word = text.slice(9).trim(); + if (!word) { + await tgCall(env, "sendMessage", { chat_id: env.SUPERGROUP_ID, message_thread_id: threadId, text: "⚠️ 用法: `/addword 屏蔽词`", parse_mode: "Markdown" }); + return; + } + let kvWords = []; + try { + const raw = await env.TOPIC_MAP.get("blocked_words_kv"); + if (raw) kvWords = JSON.parse(raw); + } catch (e) { /* 忽略解析错误,从空数组开始 */ } + if (!Array.isArray(kvWords)) kvWords = []; + + // 检查是否已存在(合并硬编码一起判断) + const allWords = [...new Set([...BLOCKED_WORDS, ...kvWords])]; + if (allWords.map(w => w.toLowerCase()).includes(word.toLowerCase())) { + await tgCall(env, "sendMessage", { chat_id: env.SUPERGROUP_ID, message_thread_id: threadId, text: `⚠️ 屏蔽词「${word}」已存在。`, parse_mode: "Markdown" }); + return; + } + + kvWords.push(word); + await env.TOPIC_MAP.put("blocked_words_kv", JSON.stringify(kvWords)); + // 强制刷新缓存 + blockedWordsCache.data = null; + Logger.info('blocked_word_added', { word, by: senderId }); + await tgCall(env, "sendMessage", { chat_id: env.SUPERGROUP_ID, message_thread_id: threadId, text: `✅ 已添加屏蔽词「${word}」\n当前动态词库共 ${kvWords.length} 个词`, parse_mode: "Markdown" }); + return; + } + + // /delword 词 — 从 KV 动态词库删除屏蔽词(硬编码词无法通过此命令删除,需修改代码) + if (text.startsWith("/delword ")) { + const word = text.slice(9).trim(); + if (!word) { + await tgCall(env, "sendMessage", { chat_id: env.SUPERGROUP_ID, message_thread_id: threadId, text: "⚠️ 用法: `/delword 屏蔽词`", parse_mode: "Markdown" }); + return; + } + + // 检查是否为硬编码词 + if (BLOCKED_WORDS.map(w => w.toLowerCase()).includes(word.toLowerCase())) { + await tgCall(env, "sendMessage", { chat_id: env.SUPERGROUP_ID, message_thread_id: threadId, text: `⚠️「${word}」是硬编码屏蔽词,无法通过命令删除,请直接修改代码中的 BLOCKED_WORDS 数组。`, parse_mode: "Markdown" }); + return; + } + + let kvWords = []; + try { + const raw = await env.TOPIC_MAP.get("blocked_words_kv"); + if (raw) kvWords = JSON.parse(raw); + } catch (e) { /* 忽略 */ } + if (!Array.isArray(kvWords)) kvWords = []; + + const before = kvWords.length; + kvWords = kvWords.filter(w => w.toLowerCase() !== word.toLowerCase()); + + if (kvWords.length === before) { + await tgCall(env, "sendMessage", { chat_id: env.SUPERGROUP_ID, message_thread_id: threadId, text: `⚠️ 屏蔽词「${word}」不存在于动态词库中。`, parse_mode: "Markdown" }); + return; + } + + await env.TOPIC_MAP.put("blocked_words_kv", JSON.stringify(kvWords)); + blockedWordsCache.data = null; // 强制刷新缓存 + Logger.info('blocked_word_removed', { word, by: senderId }); + await tgCall(env, "sendMessage", { chat_id: env.SUPERGROUP_ID, message_thread_id: threadId, text: `✅ 已删除屏蔽词「${word}」\n当前动态词库共 ${kvWords.length} 个词`, parse_mode: "Markdown" }); + return; + } + + // /listwords — 列出所有屏蔽词(硬编码 + 动态) + if (text === "/listwords") { + const allWords = await getBlockedWords(env, true); // 强制刷新 + let kvWords = []; + try { + const raw = await env.TOPIC_MAP.get("blocked_words_kv"); + if (raw) kvWords = JSON.parse(raw); + } catch (e) { /* 忽略 */ } + if (!Array.isArray(kvWords)) kvWords = []; + + const hardcoded = BLOCKED_WORDS; + const dynamic = kvWords.filter(w => !BLOCKED_WORDS.map(h => h.toLowerCase()).includes(w.toLowerCase())); + + let reply = `📝 **屏蔽词列表** (共 ${allWords.length} 个)\n\n`; + reply += `🔧 **硬编码词** (${hardcoded.length} 个,修改需改代码):\n`; + reply += hardcoded.length > 0 ? hardcoded.map(w => ` • ${w}`).join("\n") : " (无)"; + reply += `\n\n💾 **动态词** (${dynamic.length} 个,可通过 /addword /delword 管理):\n`; + reply += dynamic.length > 0 ? dynamic.map(w => ` • ${w}`).join("\n") : " (无)"; + + await tgCall(env, "sendMessage", { chat_id: env.SUPERGROUP_ID, message_thread_id: threadId, text: reply, parse_mode: "Markdown" }); + return; + } + // 转发管理员消息给用户 if (msg.media_group_id) { await handleMediaGroup(msg, env, ctx, { direction: "t2p", targetChat: userId, threadId: undefined }); @@ -1533,4 +1714,4 @@ async function delaySend(env, key, ts) { await env.TOPIC_MAP.delete(key); } -} +} \ No newline at end of file