fix: bind Telegram pairing to expiring single-use account challenges

This commit is contained in:
2026-09-06 14:50:11 +03:00
parent aca1f5c3d1
commit 283bfe5990
9 changed files with 137 additions and 23 deletions
+11 -9
View File
@@ -2,7 +2,7 @@
* Telegram bildirim kanalı — Bot API, bağımlılık yok.
*
* KURULUM (kullanıcı/yönetici): BotFather'da bot oluşturulur, tokenı
* TELEGRAM_BOT_TOKEN env'ine girilir. Her kullanıcı uygulamadan aldığı 6 haneli
* TELEGRAM_BOT_TOKEN env'ine girilir. Her kullanıcı uygulamadan aldığı süreli
* kodu bota yazar; sunucu getUpdates ile kodu görüp chat id'yi kullanıcıya
* bağlar. Token bizde tutulmaz, sohbet kimliği kullanıcı verisinde durur.
*/
@@ -48,22 +48,24 @@ export async function sendTelegram(chatId, text, { parseMode = 'HTML', returnRec
}
/**
* Kullanıcının bota yazdığı 6 haneli kodu son güncellemelerde arar.
* Kullanıcının bota yazdığı süreli bağlama kodunu son güncellemelerde arar.
* Bulursa { chatId, ad } döner. Long-polling yok; kullanıcı "doğrula" dediğinde
* tek getUpdates çağrısı yeterli (Telegram son 24 saati tutar).
*/
export async function kodIleBagla(kod) {
export async function kodIleBagla(kod, {issuedAt,expiresAt,now=Date.now()} = {}) {
if (!telegramConfigured()) throw new Error('Sunucuda TELEGRAM_BOT_TOKEN tanımlı değil');
if(!/^[a-f0-9]{48}$/.test(String(kod))||!Number.isFinite(issuedAt)||!Number.isFinite(expiresAt)||now>=expiresAt)return null;
const r = await fetch(`${API()}/getUpdates?limit=100`, { signal: AbortSignal.timeout(15000) });
const d = await r.json().catch(() => ({}));
if (!d.ok) throw new Error(d.description || 'getUpdates başarısız');
const aranan = String(kod).trim();
const aranan = String(kod);
for (const u of [...(d.result || [])].reverse()) {
const m = u.message || u.edited_message;
if (!m?.text) continue;
if (m.text.replace(/\D/g, '').includes(aranan)) {
return { chatId: String(m.chat.id), ad: [m.chat.first_name, m.chat.last_name].filter(Boolean).join(' ') || m.chat.username || null };
}
const m = u.message;
if(typeof m?.text!=='string'||m.chat?.type!=='private'||m.from?.is_bot!==false||
!Number.isSafeInteger(m.chat.id)||m.chat.id<=0||m.from.id!==m.chat.id||
!Number.isFinite(m.date)||m.date<Math.floor(issuedAt/1000)||m.date>Math.floor(now/1000))continue;
if(m.text.trim()===aranan || m.text.trim()===`/start ${aranan}`)
return {chatId:String(m.chat.id),ad:[m.chat.first_name,m.chat.last_name].filter(Boolean).join(' ')||m.chat.username||null};
}
return null;
}