Last active
September 8, 2025 20:54
-
-
Save behnamonline/1f622de01c1581776a0b4e608b8ff32e to your computer and use it in GitHub Desktop.
GhoreKeshi
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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