telegram双向机器人Turnstile 版
源码
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
// Turnstile相关常量
const TURNSTILE_SITE_KEY = ENV_TURNSTILE_SITE_KEY // 从Cloudflare获取的站点密钥
const TURNSTILE_SECRET_KEY = ENV_TURNSTILE_SECRET_KEY // 从Cloudflare获取的密钥
const NOTIFY_INTERVAL = 3600 * 1000;
const fraudDb = 'https://raw.githubusercontent.com/LloydAsp/nfd/main/data/fraud.db';
const notificationUrl = 'https://raw.githubusercontent.com/LloydAsp/nfd/main/data/notification.txt'
const startMsgUrl = 'https://raw.githubusercontent.com/LloydAsp/nfd/main/data/startMessage.md';
const enable_notification = false
/**
* Return url to telegram api, optionally with parameters added
*/
function apiUrl (methodName, params = null) {
let query = ''
if (params) {
query = '?' + new URLSearchParams(params).toString()
}
return `https://api.telegram.org/bot${TOKEN}/${methodName}${query}`
}
function requestTelegram(methodName, body, params = null){
return fetch(apiUrl(methodName, params), body)
.then(r => r.json())
}
function makeReqBody(body){
return {
method:'POST',
headers:{
'content-type':'application/json'
},
body:JSON.stringify(body)
}
}
function sendMessage(msg = {}){
return requestTelegram('sendMessage', makeReqBody(msg))
}
function answerCallbackQuery(callbackQueryId, text = null, showAlert = false) {
return requestTelegram('answerCallbackQuery', makeReqBody({
callback_query_id: callbackQueryId,
text: text,
show_alert: showAlert
}))
}
function copyMessage(msg = {}){
return requestTelegram('copyMessage', makeReqBody(msg))
}
function forwardMessage(msg){
return requestTelegram('forwardMessage', makeReqBody(msg))
}
// 验证Turnstile token的函数
async function verifyTurnstileToken(token) {
const formData = new FormData();
formData.append('secret', TURNSTILE_SECRET_KEY);
formData.append('response', token);
const result = await fetch('https://challenges.cloudflare.com/turnstile/v0/siteverify', {
method: 'POST',
body: formData
}).then(response => response.json());
return result.success;
}
/**
* Wait for requests to the worker
*/
addEventListener('fetch', event => {
const url = new URL(event.request.url)
if (url.pathname === WEBHOOK) {
event.respondWith(handleWebhook(event))
} else if (url.pathname === '/registerWebhook') {
event.respondWith(registerWebhook(event, url, WEBHOOK, SECRET))
} else if (url.pathname === '/unRegisterWebhook') {
event.respondWith(unRegisterWebhook(event))
} else if (url.pathname === '/verify') {
// 添加验证页面路由
event.respondWith(handleVerificationPage(event))
} else {
event.respondWith(new Response('No handler for this request'))
}
})
/**
* Handle requests to WEBHOOK
* https://core.telegram.org/bots/api#update
*/
async function handleWebhook (event) {
// Check secret
if (event.request.headers.get('X-Telegram-Bot-Api-Secret-Token') !== SECRET) {
return new Response('Unauthorized', { status: 403 })
}
// Read request body synchronously
const update = await event.request.json()
// Deal with response asynchronously
event.waitUntil(onUpdate(update))
return new Response('Ok')
}
/**
* Handle incoming Update
* https://core.telegram.org/bots/api#update
*/
async function onUpdate (update) {
if ('message' in update) {
await onMessage(update.message)
} else if ('callback_query' in update) {
// 处理回调查询,用于Turnstile验证
await onCallbackQuery(update.callback_query)
}
}
/**
* Handle incoming Callback Query
* https://core.telegram.org/bots/api#callbackquery
*/
async function onCallbackQuery(callbackQuery) {
const data = callbackQuery.data;
const chatId = callbackQuery.message.chat.id;
const messageId = callbackQuery.message.message_id;
if (data === 'verify') {
// 生成唯一验证ID
const verifyId = 'verify_' + Date.now() + '_' + Math.random().toString(36).substring(2, 15);
// 存储验证ID与用户ID的映射
await nfd.put('verify-' + verifyId, chatId.toString());
// 创建验证页面URL - 替换为您的实际Worker URL
const verifyUrl = `https://xx.xx.worker.dev/verify?id=${verifyId}`;
// 使用web_app类型的按钮,确保在Telegram内嵌浏览器中打开
return requestTelegram('editMessageText', makeReqBody({
chat_id: chatId,
message_id: messageId,
text: "为了保护机器人免受滥用,请点击下方按钮完成验证:",
reply_markup: {
inline_keyboard: [[{
text: "🔒 完成验证",
web_app: { url: verifyUrl }
}]]
}
}));
}
// 回答回调查询
return answerCallbackQuery(callbackQuery.id);
}
/**
* Handle verification page
*/
async function handleVerificationPage(event) {
const url = new URL(event.request.url);
const verifyId = url.searchParams.get('id');
// 如果是POST请求,处理验证
if (event.request.method === 'POST') {
try {
const { token, verifyId } = await event.request.json();
// 验证Turnstile token
const isValid = await verifyTurnstileToken(token);
if (isValid) {
// 获取用户ID
const chatId = await nfd.get('verify-' + verifyId);
if (chatId) {
// 标记用户已验证
await nfd.put('verified-' + chatId, 'true');
// 通知用户验证成功
sendMessage({
chat_id: parseInt(chatId),
text: "✅ 验证成功!您现在可以使用机器人了。"
});
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' }
});
}
}
return new Response(JSON.stringify({ success: false }), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({ success: false }), {
headers: { 'Content-Type': 'application/json' }
});
}
}
if (!verifyId) {
return new Response('Invalid request', { status: 400 });
}
// 获取用户ID
const chatId = await nfd.get('verify-' + verifyId);
if (!chatId) {
return new Response('Verification link expired', { status: 400 });
}
// 返回包含Turnstile的HTML页面,完全适配Telegram内嵌浏览器
const html = `
<!DOCTYPE html>
<html>
<head>
<title>机器人验证</title>
<script src="https://telegram.org/js/telegram-web-app.js"></script>
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
background-color: var(--tg-theme-bg-color, #f5f5f5);
color: var(--tg-theme-text-color, #333);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
line-height: 1.5;
}
.container {
background-color: var(--tg-theme-secondary-bg-color, white);
border-radius: 16px;
padding: 24px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 100%;
max-width: 400px;
text-align: center;
}
.icon {
font-size: 48px;
margin-bottom: 16px;
}
h1 {
font-size: 24px;
margin-bottom: 16px;
color: var(--tg-theme-text-color, #333);
}
p {
color: var(--tg-theme-hint-color, #666);
margin-bottom: 24px;
}
.turnstile-container {
margin: 20px 0;
display: flex;
justify-content: center;
}
#status {
margin-top: 20px;
padding: 12px;
border-radius: 8px;
font-weight: 500;
transition: all 0.3s ease;
}
.success {
background-color: rgba(40, 167, 69, 0.1);
color: #28a745;
}
.error {
background-color: rgba(220, 53, 69, 0.1);
color: #dc3545;
}
.loading {
background-color: rgba(0, 123, 255, 0.1);
color: #007bff;
}
.progress {
width: 100%;
height: 4px;
background-color: var(--tg-theme-secondary-bg-color, #e9ecef);
border-radius: 2px;
overflow: hidden;
margin-top: 16px;
}
.progress-bar {
height: 100%;
background-color: var(--tg-theme-button-color, #007bff);
width: 0%;
transition: width 0.3s ease;
}
.instructions {
background-color: var(--tg-theme-secondary-bg-color, #f8f9fa);
padding: 12px;
border-radius: 8px;
margin-bottom: 20px;
font-size: 14px;
color: var(--tg-theme-hint-color, #666);
}
</style>
</head>
<body>
<div class="container">
<div class="icon">🤖</div>
<h1>机器人验证</h1>
<p>请完成下面的验证以继续使用机器人</p>
<div class="instructions">
点击下方验证按钮,完成人机验证后即可继续使用
</div>
<div class="turnstile-container">
<div class="cf-turnstile" data-sitekey="${TURNSTILE_SITE_KEY}" data-callback="onSuccess" data-error-callback="onError"></div>
</div>
<div id="status"></div>
<div class="progress">
<div class="progress-bar" id="progress"></div>
</div>
</div>
<script>
// 初始化Telegram Web App
window.Telegram.WebApp.ready();
window.Telegram.WebApp.expand();
window.Telegram.WebApp.setHeaderColor('#ffffff');
// 设置主按钮
window.Telegram.WebApp.MainButton.setText('关闭').onClick(() => {
window.Telegram.WebApp.close();
}).show();
// 进度条动画
function updateProgress(percent) {
document.getElementById('progress').style.width = percent + '%';
}
function onSuccess(token) {
const statusEl = document.getElementById('status');
statusEl.textContent = "验证中...";
statusEl.className = "loading";
updateProgress(30);
fetch('/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
token: token,
verifyId: '${verifyId}'
})
})
.then(response => {
updateProgress(60);
return response.json();
})
.then(data => {
updateProgress(90);
if (data.success) {
statusEl.textContent = "✅ 验证成功!正在返回...";
statusEl.className = "success";
updateProgress(100);
// 显示成功提示
window.Telegram.WebApp.showPopup({
title: "验证成功",
message: "您现在可以使用机器人了",
buttons: [{
text: "确定"
}]
});
// 1.5秒后关闭Web App
setTimeout(() => {
window.Telegram.WebApp.close();
}, 1500);
} else {
statusEl.textContent = "❌ 验证失败,请重试。";
statusEl.className = "error";
updateProgress(0);
window.Telegram.WebApp.showPopup({
title: "验证失败",
message: "请重试验证",
buttons: [{
text: "确定"
}]
});
}
})
.catch(error => {
statusEl.textContent = "❌ 发生错误,请重试。";
statusEl.className = "error";
updateProgress(0);
window.Telegram.WebApp.showPopup({
title: "错误",
message: "发生错误,请重试",
buttons: [{
text: "确定"
}]
});
console.error('Error:', error);
});
}
function onError() {
const statusEl = document.getElementById('status');
statusEl.textContent = "❌ 验证出错,请刷新页面重试";
statusEl.className = "error";
updateProgress(0);
}
// 监听Web App关闭事件
window.Telegram.WebApp.onEvent('viewportChanged', () => {
window.Telegram.WebApp.expand();
});
</script>
</body>
</html>
`;
return new Response(html, {
headers: {
'Content-Type': 'text/html',
'Access-Control-Allow-Origin': '*'
}
});
}
/**
* Handle incoming Message
* https://core.telegram.org/bots/api#message
*/
async function onMessage (message) {
if(message.text === '/start'){
// 检查用户是否已验证
const chatId = message.chat.id;
const isVerified = await nfd.get('verified-' + chatId);
if (!isVerified) {
return sendMessage({
chat_id: chatId,
text: "👋 欢迎使用龘龘的私聊机器人!\n\n为了保护机器人免受滥用,请先完成验证:",
reply_markup: {
inline_keyboard: [[{
text: "🔒 验证",
callback_data: "verify"
}]]
}
});
}
return sendMessage({
chat_id: chatId,
text: "👋 欢迎使用龘龘的私聊机器人",
});
}
// 对于其他消息,也检查验证状态
const chatId = message.chat.id;
const isVerified = await nfd.get('verified-' + chatId);
if (!isVerified && chatId.toString() !== ADMIN_UID) {
return sendMessage({
chat_id: chatId,
text: "⚠️ 请先完成验证才能使用机器人功能。",
reply_markup: {
inline_keyboard: [[{
text: "🔒 验证",
callback_data: "verify"
}]]
}
});
}
if(message.chat.id.toString() === ADMIN_UID){
if(!message?.reply_to_message?.chat){
return sendMessage({
chat_id:ADMIN_UID,
text:'使用方法,回复转发的消息,并发送回复消息,或者`/block`、`/unblock`、`/checkblock`等指令'
})
}
if(/^\/block$/.exec(message.text)){
return handleBlock(message)
}
if(/^\/unblock$/.exec(message.text)){
return handleUnBlock(message)
}
if(/^\/checkblock$/.exec(message.text)){
return checkBlock(message)
}
let guestChantId = await nfd.get('msg-map-' + message?.reply_to_message.message_id,
{ type: "json" })
return copyMessage({
chat_id: guestChantId,
from_chat_id:message.chat.id,
message_id:message.message_id,
})
}
return handleGuestMessage(message)
}
async function handleGuestMessage(message){
let chatId = message.chat.id;
let isblocked = await nfd.get('isblocked-' + chatId, { type: "json" })
if(isblocked){
return sendMessage({
chat_id: chatId,
text:'Your are blocked'
})
}
let forwardReq = await forwardMessage({
chat_id:ADMIN_UID,
from_chat_id:message.chat.id,
message_id:message.message_id
})
console.log(JSON.stringify(forwardReq))
if(forwardReq.ok){
await nfd.put('msg-map-' + forwardReq.result.message_id, chatId)
}
return handleNotify(message)
}
async function handleNotify(message){
// 先判断是否是诈骗人员,如果是,则直接提醒
// 如果不是,则根据时间间隔提醒:用户id,交易注意点等
let chatId = message.chat.id;
if(await isFraud(chatId)){
return sendMessage({
chat_id: ADMIN_UID,
text:`检测到骗子,UID${chatId}`
})
}
if(enable_notification){
let lastMsgTime = await nfd.get('lastmsg-' + chatId, { type: "json" })
if(!lastMsgTime || Date.now() - lastMsgTime > NOTIFY_INTERVAL){
await nfd.put('lastmsg-' + chatId, Date.now())
return sendMessage({
chat_id: ADMIN_UID,
text:await fetch(notificationUrl).then(r => r.text())
})
}
}
}
async function handleBlock(message){
let guestChantId = await nfd.get('msg-map-' + message.reply_to_message.message_id,
{ type: "json" })
if(guestChantId === ADMIN_UID){
return sendMessage({
chat_id: ADMIN_UID,
text:'不能屏蔽自己'
})
}
await nfd.put('isblocked-' + guestChantId, true)
return sendMessage({
chat_id: ADMIN_UID,
text: `UID:${guestChantId}屏蔽成功`,
})
}
async function handleUnBlock(message){
let guestChantId = await nfd.get('msg-map-' + message.reply_to_message.message_id,
{ type: "json" })
await nfd.put('isblocked-' + guestChantId, false)
return sendMessage({
chat_id: ADMIN_UID,
text:`UID:${guestChantId}解除屏蔽成功`,
})
}
async function checkBlock(message){
let guestChantId = await nfd.get('msg-map-' + message.reply_to_message.message_id,
{ type: "json" })
let blocked = await nfd.get('isblocked-' + guestChantId, { type: "json" })
return sendMessage({
chat_id: ADMIN_UID,
text: `UID:${guestChantId}` + (blocked ? '被屏蔽' : '没有被屏蔽')
})
}
/**
* Send plain text message
* https://core.telegram.org/bots/api#sendmessage
*/
async function sendPlainText (chatId, text) {
return sendMessage({
chat_id: chatId,
text
})
}
/**
* Set webhook to this worker's url
* https://core.telegram.org/bots/api#setwebhook
*/
async function registerWebhook (event, requestUrl, suffix, secret) {
// https://core.telegram.org/bots/api#setwebhook
const webhookUrl = `${requestUrl.protocol}//${requestUrl.hostname}${suffix}`
const r = await (await fetch(apiUrl('setWebhook', { url: webhookUrl, secret_token: secret }))).json()
return new Response('ok' in r && r.ok ? 'Ok' : JSON.stringify(r, null, 2))
}
/**
* Remove webhook
* https://core.telegram.org/bots/api#setwebhook
*/
async function unRegisterWebhook (event) {
const r = await (await fetch(apiUrl('setWebhook', { url: '' }))).json()
return new Response('ok' in r && r.ok ? 'Ok' : JSON.stringify(r, null, 2))
}
async function isFraud(id){
id = id.toString()
let db = await fetch(fraudDb).then(r => r.text())
let arr = db.split('\n').filter(v => v)
console.log(JSON.stringify(arr))
let flag = arr.filter(v => v === id).length !== 0
console.log(flag)
return flag
}
环境变量设置
ENV_ADMIN_UID #管理员id 可在@myidbot获取
ENV_BOT_SECRET #随机加密字符串
ENV_BOT_TOKEN # 机器人token
ENV_TURNSTILE_SECRET_KEY #turnstile 密钥
ENV_TURNSTILE_SITE_KEY #turnstile站点密钥




https://xx.xx.worker.dev 代码中要替换成你真实的地址