Skip to content

Instantly share code, notes, and snippets.

@behnamonline
Last active August 30, 2025 16:15
Show Gist options
  • Save behnamonline/03ae7d9f517200a08ba4ce3712b707fe to your computer and use it in GitHub Desktop.
Save behnamonline/03ae7d9f517200a08ba4ce3712b707fe to your computer and use it in GitHub Desktop.
Web Scraper to Telegram Bot
/*
create a KV and bind to your worker -> kv_link
*/
const pageUrl = "https://ledc.ir/%D8%AE%D8%A7%D9%85%D9%88%D8%B4%DB%8C%D9%87%D8%A7%DB%8C-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D8%B1%DB%8C%D8%B2%DB%8C-%D8%B4%D8%AF%D9%87";
const botToken = "2365785233:AAG7afaplinQtnGgLvfgEgsAhbUQERSSYXFo";
const chatId = "@barghmire";
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname == "/bot" || url.pathname.startsWith("/bot")) {
return handleRequest(env, request);
} else {
return new Response(firstpage, { status: 200 , headers:{"Content-type":"text/html"} });
}
},
async scheduled(event, env, ctx) {
ctx.waitUntil(handleRequest(env));
},
};
async function handleRequest(env, request = null) {
const kv = env.kv_link;
let dev = false;
if (request) {
const url = new URL(request.url);
if (url.searchParams.get("dev") === "1") {
dev = true;
}
}
const url = gproxy(pageUrl); // استفاده از گوگل ترسلیت برای لود کردن صفحه
try {
const res = await fetch(url, {
headers: {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "fa-IR,fa;q=0.9,en;q=0.8"
}
});
const html = await res.text();
// شروع پردازش صفحه
const conditions = [
["بروجرد", "جعفری"],
["بروجرد", "بهار"]
];
const tableMatch = html.match(/<table[\s\S]*?<\/table>/i);
const firstTable = tableMatch ? tableMatch[0] : "";
const trMatches = [...firstTable.matchAll(/<tr[\s\S]*?<\/tr>/gi)];
const rows = [];
trMatches.forEach((tr, idx) => {
const trHtml = tr[0];
const text = trHtml.replace(/<[^>]+>/g, " "); // متن خالص
const matched = conditions.find(words =>
words.every(w => text.includes(w))
);
if (matched) {
const tdMatches = [...trHtml.matchAll(/<td[^>]*>([\s\S]*?)<\/td>/gi)];
const third = tdMatches[2] ? tdMatches[2][1].replace(/<[^>]+>/g, "").trim() : null;
rows.push({ label: matched.join(","), value: third });
}
});
const date = extractPersianDate(html) || "none";
const lastDate = await kv.get("last_date");
if (!dev) {
if (date == "none") {
return new Response("try again ...", { status: 200 });
}
if (lastDate === date) {
return new Response("تاریخ مشابه قبلی است، ارسال انجام نشد.", { status: 200 });
}
}
let message = date + "\n\n";
rows.forEach(r => {
message += `<b>${r.label}: ${r.value}</b>\n`;
});
// پایان پردازش صفخه
// ارسال به تلگرام
const telegramUrl = `https://api.telegram.org/bot${botToken}/sendMessage?chat_id=${chatId}&text=${encodeURIComponent(message)}&parse_mode=html`;
await fetch(telegramUrl);
await kv.put("last_date", date);
if (dev) {
return new Response(message+"\n"+html, { headers: { "Content-Type": "text/plain; charset=utf-8" } });
}
return new Response(message, { headers: { "Content-Type": "text/plain; charset=utf-8" } });
} catch (err) {
return new Response("Error: " + err.message, { status: 500 });
}
}
function gproxy(url) {
try {
let u = new URL(url);
u.hostname = u.hostname.replace(/\./g, "-") + ".translate.goog";
["_x_tr_sl=en", "_x_tr_tl=fa", "_x_tr_hl=en", "_x_tr_pto=wapp"]
.forEach(p => { let [k, v] = p.split("="); u.searchParams.set(k, v) });
return u.toString();
} catch (e) { return null }
}
function extractPersianDate(text) {
const match = text.match(/(شنبه|یکشنبه|دوشنبه|سه‌شنبه|سه شنبه|چهارشنبه|پنجشنبه|جمعه)\s+\d{4}\/\d{2}\/\d{2}/);
return match ? match[0] : null;
}
const firstpage = `
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>ربات برق</title>
<meta name="color-scheme" content="dark light" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;800&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0b1220;
--card: rgba(255, 255, 255, 0.06);
--button-start: #ee22c0;
--button-end: #990d74;
--button-text: #f8fafc;
--ring: rgba(59, 130, 246, 0.5);
--button-success: #22c55e;
}
* { box-sizing: border-box; }
html, body { height: 100%; }
body {
margin: 0;
background: radial-gradient(1200px 800px at 70% 20%, #111a32 0%, transparent 60%),
radial-gradient(900px 600px at 20% 80%, #0e1a30 0%, transparent 55%),
var(--bg);
color: #e5e7eb;
font-family: 'Inter', system-ui, -apple-system, Segoe UI, Roboto, 'Helvetica Neue', Arial, "Noto Sans", "Apple Color Emoji", "Segoe UI Emoji";
display: grid;
place-items: center;
min-height: 100svh;
}
.wrap {
width: min(92vw, 700px);
padding: 32px;
border-radius: 24px;
background: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.03));
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255,255,255,0.06);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
display: grid;
gap: 20px;
justify-items: center;
text-align: center;
}
h1 { margin: 0; font-size: clamp(22px, 4vw, 28px); font-weight: 800; }
p { margin: 0; font-size: clamp(13px, 2.8vw, 14px); opacity: 0.7; }
.btn {
--sizeY: clamp(56px, 12vw, 84px);
--sizeX: clamp(200px, 65vw, 380px);
border: 0;
border-radius: 999px;
height: var(--sizeY);
width: var(--sizeX);
font-size: clamp(18px, 5vw, 26px);
font-weight: 800;
color: var(--button-text);
background: linear-gradient(135deg, var(--button-start), var(--button-end));
cursor: pointer;
position: relative;
transition: transform 200ms ease, filter 200ms ease, box-shadow 200ms ease, background 200ms ease;
outline: none;
}
.btn:before {
content: "";
position: absolute;
inset: 2px;
border-radius: 999px;
background: linear-gradient(135deg, rgba(255,255,255,0.16), rgba(255,255,255,0.02));
pointer-events: none;
}
.btn.success {
background: var(--button-success) !important;
}
.btn.loading {
animation: pulseBg 0.5s infinite alternate;
}
@keyframes pulseBg {
from { filter: brightness(0.7); }
to { filter: brightness(1.3); }
}
.pulse { position: absolute; inset: 0; display: grid; place-items: center; pointer-events: none; filter: blur(30px); opacity: 0.35; animation: float 6s ease-in-out infinite; }
.pulse span { width: min(80vw, 420px); height: min(80vw, 420px); border-radius: 50%; background: radial-gradient(circle at 50% 50%, #2563eb, transparent 60%); }
@keyframes float { 0%,100%{transform:translateY(0);} 50%{transform:translateY(6px);} }
</style>
</head>
<body>
<div class="wrap">
<h1>ربات برق</h1>
<img src="https://i.ibb.co/ZzFk08G6/hhh.png"style="width:130px;z-index:999">
<p>برای ارسال روی دکمه زیر بزن.</p>
<div class="pulse" aria-hidden="true"><span></span></div>
<button id="startBtn" class="btn" type="button">...</button>
</div>
<script>
const btn = document.getElementById('startBtn');
btn.textContent = 'بزن';
btn.addEventListener('click', async () => {
btn.textContent = '...';
btn.classList.add('loading');
try {
const res = await fetch('/bot/?dev=1');
if (res.ok) {
btn.classList.remove('loading');
btn.classList.add('success');
btn.textContent = 'ارسال شد';
setTimeout(() => {
btn.classList.remove('success');
btn.textContent = 'بزن';
}, 2000);
} else {
btn.classList.remove('loading');
btn.textContent = 'خطا';
setTimeout(() => { btn.textContent = 'بزن'; }, 1000);
}
} catch (e) {
btn.classList.remove('loading');
btn.textContent = 'خطا';
setTimeout(() => { btn.textContent = 'بزن'; }, 1000);
}
});
</script>
</body>
</html>
`;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment