From b60494f2d90fd7d26a7f49a2b47e9ba62a1628dc Mon Sep 17 00:00:00 2001 From: Moli-X <327660509@qq.com> Date: Wed, 5 Aug 2026 12:10:18 +0800 Subject: [PATCH] Require QuantX channel membership --- Bot.js | 52 +++++++++++++++++++++++++++++++++++++++++ Config/Script/Worker.js | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/Bot.js b/Bot.js index b049001..2a6b3e0 100644 --- a/Bot.js +++ b/Bot.js @@ -2,6 +2,8 @@ const TOKEN = ENV_BOT_TOKEN; const WEBHOOK = '/endpoint'; const SECRET = ENV_BOT_SECRET; const ADMIN_UID = ENV_ADMIN_UID; +const REQUIRED_CHANNEL = '@QuantX'; +const REQUIRED_CHANNEL_URL = 'https://t.me/QuantX'; const KV_NAMESPACE = telegrambot; const LAST_USER_KEY = 'last_user'; const USER_MESSAGES_KEY_PREFIX = 'user_message_'; @@ -41,6 +43,14 @@ async function onMessage(message) { const chatId = message.chat.id; const userName = message.from.username ? `@${message.from.username}` : message.from.first_name; + if (chatId != ADMIN_UID) { + const membership = await getRequiredChannelMembership(message.from.id); + if (membership !== true) { + await sendChannelRequirement(chatId, membership === null); + return; + } + } + if (chatId == ADMIN_UID) { // 管理员消息处理 let userChatId; @@ -153,6 +163,48 @@ function apiUrl(methodName) { return `https://api.telegram.org/bot${TOKEN}/${methodName}`; } +function hasActiveChannelMembership(chatMember) { + return ['creator', 'administrator', 'member'].includes(chatMember.status) || + (chatMember.status === 'restricted' && chatMember.is_member === true); +} + +async function getRequiredChannelMembership(userId) { + try { + const response = await fetch(apiUrl('getChatMember'), { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ chat_id: REQUIRED_CHANNEL, user_id: userId }) + }); + const result = await response.json(); + if (!result.ok) { + console.error('getChatMember failed:', JSON.stringify(result)); + return null; + } + return hasActiveChannelMembership(result.result); + } catch (error) { + console.error('getChatMember request failed:', error); + return null; + } +} + +async function sendChannelRequirement(chatId, verificationFailed = false) { + const text = verificationFailed + ? '暂时无法验证频道关注状态,请稍后重新发送消息。' + : '请先关注频道 @QuantX,关注完成后返回这里重新发送消息。'; + const response = await fetch(apiUrl('sendMessage'), { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + chat_id: chatId, + text, + reply_markup: { + inline_keyboard: [[{ text: '📢 关注 @QuantX', url: REQUIRED_CHANNEL_URL }]] + } + }) + }); + return response.json(); +} + async function sendPlainText(chatId, text) { const response = await fetch(apiUrl('sendMessage'), { method: 'POST', diff --git a/Config/Script/Worker.js b/Config/Script/Worker.js index 05031f4..fc052a5 100644 --- a/Config/Script/Worker.js +++ b/Config/Script/Worker.js @@ -7,6 +7,8 @@ const TOKEN = ENV_BOT_TOKEN // Get it from @BotFather const WEBHOOK = '/endpoint' const SECRET = ENV_BOT_SECRET // A-Z, a-z, 0-9, _ and - const ADMIN_UID = ENV_ADMIN_UID // your user id, get it from https://t.me/username_to_id_bot +const REQUIRED_CHANNEL = '@QuantX' +const REQUIRED_CHANNEL_URL = 'https://t.me/QuantX' const NOTIFY_INTERVAL = 3600 * 1000; const fraudDb = 'https://github.com/Moli-X/TelegramBot/raw/main/Date/Fraud.db'; @@ -106,6 +108,13 @@ async function onUpdate (update) { * https://core.telegram.org/bots/api#message */ async function onMessage (message) { + if(message.chat.id.toString() !== ADMIN_UID){ + const membership = await getRequiredChannelMembership(message.from.id) + if(membership !== true){ + return sendChannelRequirement(message.chat.id, membership === null) + } + } + if(message.text === '/start'){ let startMsg = await fetch(startMsgUrl).then(r => r.text()) return sendMessage({ @@ -190,6 +199,40 @@ async function onMessage (message) { return handleGuestMessage(message) } +function hasActiveChannelMembership(chatMember){ + return ['creator', 'administrator', 'member'].includes(chatMember.status) || + (chatMember.status === 'restricted' && chatMember.is_member === true) +} + +async function getRequiredChannelMembership(userId){ + try { + const result = await requestTelegram('getChatMember', makeReqBody({ + chat_id: REQUIRED_CHANNEL, + user_id: userId + })) + if(!result.ok){ + console.error('getChatMember failed:', JSON.stringify(result)) + return null + } + return hasActiveChannelMembership(result.result) + } catch(error) { + console.error('getChatMember request failed:', error) + return null + } +} + +function sendChannelRequirement(chatId, verificationFailed = false){ + return sendMessage({ + chat_id: chatId, + text: verificationFailed + ? '暂时无法验证频道关注状态,请稍后重新发送消息。' + : '请先关注频道 @QuantX,关注完成后返回这里重新发送消息。', + reply_markup: { + inline_keyboard: [[{ text: '📢 关注 @QuantX', url: REQUIRED_CHANNEL_URL }]] + } + }) +} + async function handleGuestMessage(message){ let chatId = message.chat.id; let isblocked = await nfd.get('isblocked-' + chatId, { type: "json" })