Ürünereklam
مكتملالصق رابط منتج، واحصل على إعلانات فيديو وصور جاهزة بالتركية والإنجليزية، يقيّم نموذج تنبؤ كل واحد منها قبل إنفاق أي مال. بدأ المشروع في BTK Akademi Hackathon عام 2026.
- دوري
- التصميم والتطوير، بمفردي
- السنة
- 2026
- التقنيات المستخدمة
- Next.js · Inngest · Supabase · Gemini · Veo · ElevenLabs · Remotion · PyTorch on Modal · Polar
مرّر أفقيًا لرؤية المخطط كاملًا.
المشكلة
الإعلانات بلغتين، وبالحجم الذي تحتاجه المتاجر الإلكترونية، تعني عادةً وكالة تتقاضى مبلغًا شهريًا ثابتًا. وحتى في هذه الحالة، لا أحد يعرف أي نسخة ستنجح قبل أن تُنفق الميزانية.
كيف بُني
تطبيق Next.js ينسّق مهام Inngest دائمة (durable). يقرأ scraper مكتوب بـ Python صفحة المنتج، مع مسار جلب محصّن للمتاجر الصعبة. يكتب Gemini الاستراتيجية والنصوص الإعلانية والإطارات، ويُنتج Veo أو Seedance الفيديو، ويضيف ElevenLabs الصوت، ويضيف Remotion نصوصًا على الشاشة متزامنة مع الكلمات، والعلامة المائية، ودعوة لاتخاذ إجراء. وينتج استوديو منفصل إعلانات بصور ثابتة ونصوصًا إعلانية. ثم تمر كل نسخة عبر نموذج مستضاف ذاتيًا يتنبأ بكيفية تفاعل الناس معها (TRIBE v2) على وحدات GPU في Modal، يُشغَّل ثم يُستعلم عن حالته دوريًا، لأن الـ render يستمر أطول من مهلة الـ edge function. ويحتفظ Supabase بالحالة خلف row-level security، مع روابط URL موقّعة قصيرة العمر للوسائط. وتُسجَّل الأرصدة في دفتر حسابات append-only مع خصم واسترداد ذريّين (atomic)، ويتم الدفع عبر Polar بأسعار بالليرة والدولار.
القرارات المهمة
الخطوة التي تفشل تعيد الرصيد بنفسها
يتكوّن الـ pipeline من مهام دائمة (durable)، ولكل خطوة failure handler خاص بها. والخطوة التي تتعطل تعيد أرصدتها مرة واحدة بالضبط، وتحرّر التشغيل بدلًا من تركه عالقًا.
الوسائط خاصة افتراضيًا
تُحفظ ملفات الـ render الخام في bucket خاص، وصلاحية SELECT على مستوى الأعمدة مسحوبة، وروابط الوسائط روابط URL موقّعة قصيرة العمر. لا يمكن تخمين عنوان أي ملف وسائط، بما في ذلك الباقة المجانية.
كل إعلان يُقيَّم قبل أن يدفع أحد مقابله
لا يخرج أي إعلان من الاستوديو دون تقييم. يرتّب النموذج النسخ قبل أن يضع أحد ميزانية خلف أي منها.
كود من المستودع
async function safeRefund(
opts: Parameters<typeof refundCredits>[0],
): Promise<void> {
try {
await refundCredits(opts);
} catch (err) {
console.error(
`[billing][RECONCILE-NEEDED] refund failed user=${opts.userId} ` +1
`amount=${opts.amount} ref=${opts.refType}:${opts.refId} ` +
`key=${opts.idempotencyKey ?? "-"}`,
err,
);
}
}
// …
await safeRefund({
supa,
userId: campaign.user_id,
amount: campaignCreditCost({
videoCount: brief.videoCount ?? 1,
videoModel: brief.videoModel ?? "seedance",
durationSec: brief.durationSec,
}),
refType: "campaign",
refId: campaignId,
idempotencyKey: `refund:campaign-all:${campaignId}`,2
description: "Kampanya başlatılamadı — otomatik kredi iadesi",3
});
السطر 8. عند فشل الاسترداد، تُسجَّل علامة RECONCILE-NEEDED بدلًا من رمي استثناء، فلا يضيع أي رصيد بصمت، ويحرّر الـ pipeline التشغيل رغم ذلك.
السطر 27. مفتاح idempotency واحد لكل حدث: قد يعمل الـ failure handler مرة أخرى، لكن دفتر الحسابات يسجّل الاسترداد مرة واحدة فقط.
السطر 28. سبب الاسترداد مكتوب بالتركية لأن المنتج ثنائي اللغة من البداية إلى النهاية.
أين وصل الآن
تطوّر من عرض تجريبي في هاكاثون إلى منتج ثنائي اللغة مع تقييم يعمل على GPU. وهو الآن مغلق المصدر، ويجري الاستعداد لإطلاقه كخدمة مدفوعة.