Skip to content

Instantly share code, notes, and snippets.

@behnamonline
Last active September 8, 2025 20:54
Show Gist options
  • Select an option

  • Save behnamonline/1f622de01c1581776a0b4e608b8ff32e to your computer and use it in GitHub Desktop.

Select an option

Save behnamonline/1f622de01c1581776a0b4e608b8ff32e to your computer and use it in GitHub Desktop.
GhoreKeshi
// bind a D1 database to the worker , name it: dblink
// run your worker https://.....workers.dev/init to set webhook and create users table
const BOT_TOKEN = '7492366949:AAENq_vvsIGOKFJC-2x2TW8A2JpJ2gACLAKC'
const HOOK = BOT_TOKEN.split(":")[0]
export default {
async fetch(request, env, ctx) {
const botDB = env.dblink;
async function postReq(url, fields) {
const tgFormData = new FormData();
fields.forEach(obj => {
for (let key in obj) {
tgFormData.append(key, obj[key]);
}
});
const telegramResponse = await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/${url}`, {
method: 'POST',
body: tgFormData,
});
return await telegramResponse;
}
const tg = await (await fetch("https://telegram.org/js/telegram-web-app.js?56")).text();
const tw = await (await fetch("https://cdn.tailwindcss.com")).text();
const html = `<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8">
<title>قرعه‌کشی</title>
<script>
${tw}
</script>
<script>
${tg}
</script>
<style>
@font-face {
font-family: 'MyFont';
src: url('https://www.behnam-online.ir/font.woff') format('woff');
font-weight: normal;
font-style: normal;
font-display: swap;
}
html,
body {
font-family: 'MyFont', sans-serif;
padding-bottom: 20rem;
}
</style>
<script>
const info = {}
</script>
</head>
<body class="bg-gray-900 ">
<div id="q" class="transition-all duration-800 flex flex-col items-center min-h-screen text-white">
<h1 class="text-4xl font-bold mb-1 text-cyan-400">قرعه‌کشی بزرگ</h1>
<div data-section="tavalod"
class="selectitem transition-all duration-500 w-full max-w-3xl bg-gray-800 p-6 rounded-2xl shadow-lg mb-6 ">
<div class="m-2 mb-10 text-2xl text-center">متولد کدوم ماهی؟</div>
<div class="grid grid-cols-3 gap-4">
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">فروردین</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">اردیبهشت</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">خرداد</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">تیر</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">مرداد</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">شهریور</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">مهر</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">آبان</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">آذر</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">دی</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">بهمن</button>
<button
class="bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">اسفند</button>
</div>
</div>
<div data-section="city"
class="selectitem transition-all duration-500 opacity-0 w-full max-w-3xl bg-gray-800 p-6 rounded-2xl shadow-lg mb-6 ">
<div class="m-3 text-2xl text-center">کدوم شهری ؟</div>
<div class="grid grid-cols-3 sm:grid-cols-4 gap-4">
<button
class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">بروجرد</button>
<button
class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">لندن</button>
<button
class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">اصفهان</button>
<button
class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">تهران</button>
<button
class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">شیراز</button>
<button
class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">آبادان</button>
<button
class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">کرمانشاه</button>
<button
class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">مشهد</button>
<button
class="bg-gradient-to-r from-cyan-500 to-blue-500 text-white font-bold text-xl py-4 rounded-xl shadow-lg hover:scale-105 transition">اردبیل</button>
</div>
</div>
<div data-section="animal"
class="selectitem transition-all duration-500 opacity-0 w-full max-w-3xl bg-gray-800 p-6 rounded-2xl shadow-lg mb-6 ">
<div class="m-3 text-2xl text-center">حیون مورد علاقه‌ات چیه؟</div>
<div class="grid grid-cols-2 sm:grid-cols-4 gap-4 animals">
<button
class="flex flex-col items-center bg-gradient-to-r from-rose-500 to-pink-600 text-white font-bold text-xl rounded-xl shadow-lg hover:scale-105 transition overflow-hidden">
<img src="https://gorbekeshi.behnamonline.workers.dev/img-boar.png"
alt="Golzar" class="w-full h-48 object-cover">
<span class="py-3">گراز</span>
</button>
<button
class="flex flex-col items-center bg-gradient-to-r from-rose-500 to-pink-600 text-white font-bold text-xl rounded-xl shadow-lg hover:scale-105 transition overflow-hidden">
<img src="https://gorbekeshi.behnamonline.workers.dev/img-cat.png"
alt="Golzar" class="w-full h-48 object-cover">
<span class="py-3">گربه</span>
</button>
</div>
</div>
<div data-section="singer" data-isFinal="yes"
class="selectitem transition-all duration-500 opacity-0 w-full max-w-3xl bg-gray-800 p-6 rounded-2xl shadow-lg mb-6 ">
<div class="m-3 text-2xl text-center">خواننده مورد علاقت کیه؟</div>
<div class="grid grid-cols-2 gap-4 singers">
<button
class="flex flex-col items-center bg-gradient-to-r from-rose-500 to-pink-600 text-white font-bold text-xl rounded-xl shadow-lg hover:scale-105 transition overflow-hidden">
<img src="https://gorbekeshi.behnamonline.workers.dev/img-golzar.png"
alt="Eric Clapton" class="w-full h-48 object-cover">
<span class="py-3">گلزار</span>
</button>
<button
class="flex flex-col items-center bg-gradient-to-r from-rose-500 to-pink-600 text-white font-bold text-xl rounded-xl shadow-lg hover:scale-105 transition overflow-hidden">
<img src="https://gorbekeshi.behnamonline.workers.dev/img-clapton.png"
alt="Golzar" class="w-full h-48 object-cover">
<span class="py-3"> کلپتون</span>
</button>
</div>
</div>
<div class="hidden text-lg pt-5" id="wait">چند لحظه صبر کنید ...</div>
<script>
document.querySelectorAll('.selectitem').forEach(section => {
const buttons = section.querySelectorAll('button');
const sectionname = section.getAttribute("data-section");
const nextDiv = section.nextElementSibling;
const isFinal = section.getAttribute("data-isFinal");
buttons.forEach(btn => {
btn.style.transition = "transform 0.2s , opacity 0.4s "
btn.addEventListener('click', () => {
info[sectionname] = btn.innerText;
setTimeout(() => {
nextDiv.scrollIntoView({ behavior: "smooth" });
section.style.opacity = "0"
setTimeout(() => {
nextDiv.classList.remove("hidden")
nextDiv.classList.add("opacity-100")
}, 500)
}, 1000)
buttons.forEach(b => {
if (b === btn) {
b.style.transform = "scale(1.5)";
b.style.opacity = "1";
b.style.zIndex = "9"
} else {
b.style.transform = "scale(1)";
b.style.opacity = "0.25";
b.style.filter = "grayscale(100%)";
}
});
if (isFinal == "yes") {
setTimeout(() => {
const waitElem = document.getElementById("wait");
waitElem.classList.remove("hidden")
waitElem.scrollIntoView({ behavior: "smooth" });
fetch('/send', {
method: 'POST',
body: JSON.stringify({ id: Telegram.WebApp.initDataUnsafe.user.id, info: info })
})
.then(res => res.json())
.then(data => {
try {
Telegram.WebApp.close()
window.TelegramWebviewProxy.postEvent('web_app_close');
} catch (error) {
console.error('Error posting event to Telegram Webview:', error);
}
})
.catch(err => console.error(err));
}, 1500);
}
});
});
});
</script>
</div>
</body>
</html>`
const url = new URL(request.url);
if (url.pathname === "/init") {
const lines = [];
// 1) ست‌کردن وبهوک
try {
const setWebhook = await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/setWebhook`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
url: `${url.protocol}//${url.hostname}/${HOOK}`,
drop_pending_updates: true
})
});
const webhookRes = await setWebhook.json().catch(() => null);
if (webhookRes && typeof webhookRes === "object") {
lines.push(`setWebhook.ok: ${webhookRes.ok === true ? "موفق" : "ناموفق"}`);
if (webhookRes.description) lines.push(`setWebhook.description: ${webhookRes.description}`);
if (webhookRes.result && webhookRes.result.url) lines.push(`setWebhook.url: ${webhookRes.result.url}`);
} else {
lines.push("setWebhook: پاسخ غیرمنتظره یا parse نشد");
}
} catch (err) {
lines.push(`setWebhook error: ${err.message}`);
}
// 2) ساخت جدول (اگر موجود باشد با IF NOT EXISTS خطا نمیدهد)
try {
await botDB.prepare(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
user_id TEXT,
user_info TEXT,
phone_number TEXT,
code TEXT
);
`).run();
lines.push("createTable: انجام شد");
} catch (err) {
lines.push(`createTable error: ${err.message}`);
}
// پاسخ نهایی — متن ساده چندخطی
return new Response(lines.join("\n"), {
status: 200,
headers: { "Content-Type": "text/plain; charset=utf-8" }
});
}
function normalizePhone(number) {
if (typeof number !== "string") return number;
return number.startsWith("+98")
? "0" + number.slice(3) // بعد از +98، عددها رو نگه می‌داریم
: number;
}
if (url.pathname === "/export") {
const users = await botDB.prepare(`SELECT * FROM users`).all()
const tableRows = users.results.map(user => {
const info = JSON.parse(user.user_info)
return `
<tr>
<td>${user.user_id}</td>
<td>${info.first_name} ${info.last_name}</td>
<td>${info.username}</td>
<td>${info.tavalod}</td>
<td>${info.animal}</td>
<td>${info.singer}</td>
<td>${info.city}</td>
<td>${user.phone_number}</td>
<td>${user.code}</td>
</tr>
`
}).join("")
const html = `
<!DOCTYPE html>
<html>
<head>
<title>Users Table</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 20px; }
table { border-collapse: collapse; margin: 0 auto; width: 90%; }
th, td { border: 1px solid #ddd; padding: 8px; }
th { background-color: #f2f2f2; }
button {
background-color: #28a745; /* سبز */
color: white;
font-size: 20px; /* فونت بزرگ */
padding: 12px 30px;
margin-top: 20px;
border: none;
border-radius: 8px;
cursor: pointer;
}
button:hover {
background-color: #218838;
}
form { display: inline-block; margin-top: 20px; }
</style>
</head>
<body>
<h1>Users</h1>
<table>
<thead>
<tr>
<th>User ID</th>
<th>Name</th>
<th>Username</th>
<th>Tavalod</th>
<th>Animal</th>
<th>Singer</th>
<th>City</th>
<th>Phone</th>
<th>Code</th>
</tr>
</thead>
<tbody>
${tableRows}
</tbody>
</table>
<form method="get" action="/download">
<button type="submit">دانلود اکسل (CSV)</button>
</form>
</body>
</html>
`
return new Response(html, { headers: { "Content-Type": "text/html;charset=UTF-8" } })
}
if (url.pathname === "/download") {
const csv = await generateCSV()
return new Response(csv, {
headers: {
"Content-Type": "text/csv",
"Content-Disposition": "attachment; filename=users.csv"
}
})
}
async function generateCSV() {
const users = await botDB.prepare(`SELECT * FROM users`).all()
const header = ["user_id", "first_name", "last_name", "username", "tavalod", "animal", "singer", "city", "phone_number", "code"]
const rows = users.results.map(user => {
const info = JSON.parse(user.user_info)
return [
user.user_id,
info.first_name,
info.last_name,
info.username,
info.tavalod,
info.animal.replace(/\n/g, ''),
info.singer.replace(/\n/g, ''), // حذف خط جدید از singer
info.city,
`'${user.phone_number}`,
user.code
].join(",")
})
return [header.join(","), ...rows].join("\n")
}
if (url.pathname === "/" + HOOK) {
const body = await request.json();
if (body.message.contact) {
let user = {
first_name: body.message.from?.first_name || "", // اگر موجود نبود خالی بذار
last_name: body.message.from?.last_name || "",
username: body.message.from?.username || "",
};
let code = Math.floor(10000 + Math.random() * 90000);
const updateuser = await botDB.prepare(`
UPDATE users
SET
phone_number = ?1,
code = ?2,
user_info = json_patch(user_info, ?3)
WHERE user_id = ?4
`).bind(
String(normalizePhone(body.message.contact.phone_number)), // ?1
String(code), // ?2
JSON.stringify(user), // ?3
String(body.message.from.id) // ?4
).run();
await postReq("sendMessage", [
{ "chat_id": body.message.from.id },
{ "text": "کد به شمارتون sms شد" }
])
// ارسال sms
const sms = {
username: "<username>",
password: "<pass>",
to: body.message.contact.phone_number,
from: "<5000....>",
text: "کد: "+code,
isFlash: false,
};
const response = await fetch("https://rest.payamak-panel.com/api/SendSMS/SendSMS", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(sms),
});
} else {
await postReq("sendMessage", [
{ "chat_id": body.message.from.id },
{ "text": "برای باز کردن اپ کلیک کن 👇" },
{
"reply_markup": JSON.stringify({
"inline_keyboard":
[
[
{
"text": "🚀 باز کردن اپ",
"web_app": {
"url": url.protocol + "//" + url.hostname + "/"
}
}
]
]
})
}
])
}
return new Response("ok", {
status: 200,
});
}
if (url.pathname === "/send") {
const user = await request.json();
const insertuser = await botDB.prepare(`INSERT INTO users (user_id, user_info) VALUES (?1, ?2)`).bind(
String(user.id),
String(JSON.stringify(user.info)),
).run();
const tgFormData = new FormData();
tgFormData.append("chat_id", user.id);
tgFormData.append("text", "برای دریافت کد قرعه کشی روی دکمه ی زیر بزنید");
tgFormData.append("reply_markup", JSON.stringify({
"keyboard": [
[
{
"text": "دریافت کد قرعه کشی",
"request_contact": true
}
]
],
"resize_keyboard": true
}));
await fetch(`https://api.telegram.org/bot${BOT_TOKEN}/sendMessage`, {
method: 'POST',
body: tgFormData,
});
return new Response("{}", {
status: 200,
headers: {
"content-type": "application/json; charset=UTF-8"
}
});
}
const imgs = {
"boar.png": "https://i.ibb.co/8gKHc107/boar.png",
"cat.png": "https://i.ibb.co/Fb1q9Kcs/cat.png",
"clapton.png": "https://i.ibb.co/LzqzFWh6/clapton.png",
"golzar.png": "https://i.ibb.co/CKykCWGg/golzar.png"
}
// مسیر باید مثل /img-aaa.png باشه
const match = url.pathname.match(/^\/img-(.+)$/)
if (match) {
const fileName = match[1]
if (imgs[fileName]) {
const resp = await fetch(imgs[fileName])
if (!resp.ok) {
return new Response("Image fetch failed", { status: 502 })
}
// تعیین Content-Type بر اساس پسوند
let contentType = "application/octet-stream"
if (fileName.endsWith(".png")) contentType = "image/png"
else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) contentType = "image/jpeg"
else if (fileName.endsWith(".gif")) contentType = "image/gif"
else if (fileName.endsWith(".webp")) contentType = "image/webp"
else if (fileName.endsWith(".svg")) contentType = "image/svg+xml"
return new Response(resp.body, {
headers: {
"Content-Type": contentType
}
})
} else {
return new Response("Image not found", { status: 404 })
}
}
return new Response(html, {
status: 200,
headers: {
"content-type": "text/html; charset=UTF-8"
}
});
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment