fix: align server deadlines and follow consumers with preparation state

This commit is contained in:
2026-09-06 09:09:10 +03:00
parent 7ad5fb4f50
commit 144724484c
8 changed files with 124 additions and 33 deletions
+21 -15
View File
@@ -6,6 +6,8 @@
// Claude Desktop (mcp-remote) araçları: search_tenders, get_tender,
// search_results, search_items, upcoming_deadlines. Bağımlılık yok.
import * as store from './user-store.mjs';
import { deadlineInfo, tenderDateKey } from '../src/services/tenderCalendar.js';
import { applyTenderStates, tenderStateKey } from '../src/services/tenderState.js';
import { readHistory, resultsByIkn } from './history-store.mjs';
import * as kalemStore from './kalem-store.mjs';
@@ -25,17 +27,17 @@ export function rssUret({ title, link, description, items }) {
/** dd.mm.yyyy → Date (ihaleist tarih biçimi). */
export function trTarih(s) {
const m = /(\d{1,2})[./](\d{1,2})[./](\d{4})/.exec(String(s ?? ''));
return m ? new Date(Number(m[3]), Number(m[2]) - 1, Number(m[1])) : null;
const m = /^(\d{1,2})[./](\d{1,2})[./](\d{4})$/.exec(String(s ?? '').trim());
const key = m && tenderDateKey({ tarih: `${m[1].padStart(2, '0')}.${m[2].padStart(2, '0')}.${m[3]}` });
return key ? new Date(key + 'T00:00:00+03:00') : null;
}
/** Liste filtresi — RSS ve MCP ortak. */
export function ihaleFiltrele(tenders, { q, il, sektor, sadeceAcik = true, limit = 50 } = {}) {
export function ihaleFiltrele(tenders, { q, il, sektor, sadeceAcik = true, limit = 50 } = {}, now = new Date()) {
const qq = norm(q); const ill = norm(il); const sek = norm(sektor);
const bugun = new Date(); bugun.setHours(0, 0, 0, 0);
return tenders
.filter((t) => !sadeceAcik || !t.kazananFirma)
.filter((t) => !sadeceAcik || !trTarih(t.tarih) || trTarih(t.tarih) >= bugun)
.filter((t) => !sadeceAcik || deadlineInfo(t, now).expired !== true)
.filter((t) => !ill || norm(t.sehir).includes(ill))
.filter((t) => !sek || norm(t.sektor).includes(sek))
.filter((t) => !qq || norm(t.baslik).includes(qq) || norm(t.kurum).includes(qq) || String(t.ikn ?? '').includes(qq) ||
@@ -99,16 +101,20 @@ export const TOOLS = [
const hata = (id, code, message) => ({ jsonrpc: '2.0', id: id ?? null, error: { code, message } });
const sonuc = (id, result) => ({ jsonrpc: '2.0', id: id ?? null, result });
export function yaklasanIhaleler(email, tenders, gun = 14) {
const takip = new Set((store.getData(email, 'takip') || []).map(String));
const bugun = new Date(); bugun.setHours(0, 0, 0, 0);
const sinir = new Date(bugun.getTime() + gun * 86400000);
return tenders
.filter((t) => t.ikn && takip.has(String(t.ikn)))
.map((t) => ({ t, d: trTarih(t.tarih) }))
.filter(({ d }) => d && d >= bugun && d <= sinir)
.sort((a, b) => a.d - b.d)
.map(({ t, d }) => ({ ikn: t.ikn, baslik: t.baslik, kurum: t.kurum, sehir: t.sehir, tarih: t.tarih, saat: t.saat, kalan_gun: Math.round((d - bugun) / 86400000), kaynakUrl: t.kaynakUrl }));
export function yaklasanIhaleler(email, tenders, gun = 14, now = new Date()) {
const states = store.getTenderStates(email);
const takip = new Set(store.getTrackedTenderKeys(email, states));
const days = Math.min(90, Math.max(1, Math.floor(Number(gun) || 14)));
return applyTenderStates(tenders, states)
.filter(t => !t.kazananFirma && takip.has(tenderStateKey(t)))
.map(t => ({ t, info: deadlineInfo(t, now) }))
.filter(({ info }) => info.days !== null && info.days >= 0 && info.days <= days && info.expired !== true)
.sort((a, b) => a.info.days - b.info.days ||
(a.info.deadline?.getTime() ?? Infinity) - (b.info.deadline?.getTime() ?? Infinity))
.map(({ t, info }) => ({ ikn: t.ikn, baslik: t.baslik, kurum: t.kurum, sehir: t.sehir,
tarih: t.tarih, saat: t.saat, kalan_gun: info.days,
son_teklif_zamani: info.deadline?.toISOString() ?? null,
saat_dogrulanmali: !info.deadline, kaynakUrl: t.kaynakUrl }));
}
export async function aracCalistir(name, args, user, ctx) {