CognxSafeTrack commited on
Commit ·
fe2f79f
1
Parent(s): fc3b1c4
fix(arch): HF inbound-only + seedDatabase export + DISABLE_WORKER + DISABLE_WHATSAPP_SEND
Browse files
apps/api/src/services/whatsapp.ts
CHANGED
|
@@ -36,71 +36,20 @@ export class WhatsAppService {
|
|
| 36 |
}
|
| 37 |
|
| 38 |
if (normalizedText === 'SEED') {
|
| 39 |
-
|
|
|
|
| 40 |
try {
|
| 41 |
-
|
| 42 |
-
const
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
// Seed FR track
|
| 49 |
-
await prisma.track.create({
|
| 50 |
-
data: {
|
| 51 |
-
title: "Comprendre Son Business (FR)",
|
| 52 |
-
description: "Apprenez à définir, tester et vendre votre projet en 12 leçons.",
|
| 53 |
-
duration: 12,
|
| 54 |
-
language: "FR",
|
| 55 |
-
days: {
|
| 56 |
-
create: [
|
| 57 |
-
{ dayNumber: 1, exerciseType: "AUDIO", lessonText: "Aujourd'hui, on commence simple. Beaucoup de personnes disent : je fais le commerce. Mais ça ne veut rien dire. Dis-moi clairement : Tu aides QUI, à faire QUOI, et comment tu gagnes de l'argent. Exemple : Je vends du jus bissap aux étudiants devant l'université. Maintenant, c'est à toi. Dis ta phrase en 15 secondes.", exercisePrompt: "Envoie-moi un court message vocal (ou texte) avec ta phrase d'activité :" },
|
| 58 |
-
{ dayNumber: 2, exerciseType: "AUDIO", lessonText: "Le client n'achète pas ton produit. Il achète un résultat. Il n'achète pas du savon. Il achète la propreté. Va demander à 2 clients : Pourquoi tu achètes ça ? Écoute bien leurs mots.", exercisePrompt: "Envoie un audio résumant les 2 réponses de tes clients." },
|
| 59 |
-
{ dayNumber: 3, exerciseType: "BUTTON", lessonText: "Si tu vends à tout le monde, tu ne vends à personne. Choisis un seul client principal. Qui est le plus intéressé par ton produit ?", exercisePrompt: "Sélectionne ton client principal ci-dessous :", buttonsJson: [{ id: "jeunes", title: "Jeunes" }, { id: "femmes", title: "Femmes" }, { id: "commercants", title: "Commerçants" }] },
|
| 60 |
-
{ dayNumber: 4, exerciseType: "TEXT", lessonText: "Ton client a un problème. Quel est son plus grand problème ? Parle à 3 personnes aujourd'hui. Pose cette question. Écoute sans expliquer ton produit.", exercisePrompt: "Quel est le problème N°1 que tes clients t'ont partagé ?" },
|
| 61 |
-
{ dayNumber: 5, exerciseType: "BUTTON", lessonText: "À quel moment ton client a ce problème ?", exercisePrompt: "Choisis le moment d'apparition du problème :", buttonsJson: [{ id: "matin_midi", title: "Matin ou Midi" }, { id: "soir", title: "Le Soir" }, { id: "tout_le_temps", title: "Tout le temps" }] },
|
| 62 |
-
{ dayNumber: 6, exerciseType: "TEXT", lessonText: "Avant toi, il faisait comment ?", exercisePrompt: "Donne-moi 2 solutions que ton client utilisait avant de te connaître :" },
|
| 63 |
-
{ dayNumber: 7, exerciseType: "TEXT", lessonText: "Explique ta solution en mots simples. Pas compliqué.", exercisePrompt: "Décris-moi ton offre très simplement en une phrase :" },
|
| 64 |
-
{ dayNumber: 8, exerciseType: "BUTTON", lessonText: "Tu ne peux pas promettre tout. Choisis une seule force.", exercisePrompt: "Quelle est ta promesse principale ?", buttonsJson: [{ id: "rapide", title: "Rapide" }, { id: "moins_cher", title: "Moins cher" }, { id: "fiable_proche", title: "Fiable / Proche" }] },
|
| 65 |
-
{ dayNumber: 9, exerciseType: "TEXT", lessonText: "Parle à 5 personnes. Dis ta phrase. Combien disent OUI ?", exercisePrompt: "Combien t'ont dit OUI ? (Envoie juste un chiffre)" },
|
| 66 |
-
{ dayNumber: 10, exerciseType: "TEXT", lessonText: "Ton prix doit couvrir tes coûts. Note 2 dépenses importantes.", exercisePrompt: "Quelles sont tes 2 plus grosses dépenses pour ce projet ?" },
|
| 67 |
-
{ dayNumber: 11, exerciseType: "BUTTON", lessonText: "Pourquoi toi et pas un autre ?", exercisePrompt: "Quel est ton vrai avantage concurrentiel ?", buttonsJson: [{ id: "qualite", title: "Qualité" }, { id: "rapidite", title: "Rapidité" }, { id: "confiance", title: "Confiance" }] },
|
| 68 |
-
{ dayNumber: 12, exerciseType: "AUDIO", lessonText: "Maintenant tu es prêt. Dis en 30 secondes : Je suis... J'aide... Parce que... Je vends... À...", exercisePrompt: "C'est l'heure du test ! Envoie-moi un audio avec ton Mini Pitch de 30 secondes :" },
|
| 69 |
-
]
|
| 70 |
-
}
|
| 71 |
-
}
|
| 72 |
-
});
|
| 73 |
-
|
| 74 |
-
// Seed WOLOF track
|
| 75 |
-
await prisma.track.create({
|
| 76 |
-
data: {
|
| 77 |
-
title: "Comprendre Son Business (WOLOF)",
|
| 78 |
-
description: "Apprenez à définir, tester et vendre votre projet en 12 leçons.",
|
| 79 |
-
duration: 12,
|
| 80 |
-
language: "WOLOF",
|
| 81 |
-
days: {
|
| 82 |
-
create: [
|
| 83 |
-
{ dayNumber: 1, exerciseType: "AUDIO", lessonText: "Tey, danuy tàmbalee ak lu yomb. Nit ñu bari dañuy wax : dama def commerce. Waaye loolu amul solo. Wax ma leer : Yaay jàppalé KAN, mu def LAN, te naka nga amee xaalis. Misaal : Damaa jaay jus bissap ci taalibe yu université. Léegi sa waxtu la. Wax sa activité ci 15 seconde.", exercisePrompt: "Yónnee ma ab kàddu (audio) walla message bu gatt ngir wax sa mbir :" },
|
| 84 |
-
{ dayNumber: 2, exerciseType: "AUDIO", lessonText: "Kiliifa bi du jënd sa produit rek. Mu jënd ab résultat. Du jënd savon rek. Mu jënd set. Dem laaj 2 kiliifa : Lu tax nga jënd lii ? Déggal bu baax li ñuy wax.", exercisePrompt: "Yónnee ma audio ngir tënk ñaari tontu ya." },
|
| 85 |
-
{ dayNumber: 3, exerciseType: "BUTTON", lessonText: "Su nga jaay ci ñépp, doo jaay ci kenn. Tànnal benn kiliifa bu mag. Kan moo gën a soxla sa produit ?", exercisePrompt: "Tànnal sa kiliifa bu mag ci suuf :", buttonsJson: [{ id: "ndaw_nyi", title: "Ndaw ñi / Jeunes" }, { id: "jigeen_nyi", title: "Jigeen ñi / Femmes" }, { id: "jaaykat_yi", title: "Jaaykat yi / Comms" }] },
|
| 86 |
-
{ dayNumber: 4, exerciseType: "TEXT", lessonText: "Sa kiliifa am na jafe jafe. Lan mooy jafe jafe bu gën a rëy ? Dem waxtaan ak 3 nit. Laaj leen. Bul def publicité.", exercisePrompt: "Lan mooy jafe jafe bu gën a mag bi sa kiliifa yi am ?" },
|
| 87 |
-
{ dayNumber: 5, exerciseType: "BUTTON", lessonText: "Kañ la jafe jafe bi di ñëw ?", exercisePrompt: "Tànnal jamono ji jafe jafe bi di faral di am :", buttonsJson: [{ id: "suba_bëccëg", title: "Suba walla Bëccëg" }, { id: "ngoon", title: "Ngoon / Guddi" }, { id: "saa_su_ne", title: "Saa su nekk" }] },
|
| 88 |
-
{ dayNumber: 6, exerciseType: "TEXT", lessonText: "Balaa yaw, naka la daan def ?", exercisePrompt: "Wax ma ñaari pexe yi kiliifa bi daan jëfandikoo balaa xam sa produit :" },
|
| 89 |
-
{ dayNumber: 7, exerciseType: "TEXT", lessonText: "Wax sa solution ci wax yu yomb.", exercisePrompt: "Tënkal sa solution ci benn phrase bu yomb :" },
|
| 90 |
-
{ dayNumber: 8, exerciseType: "BUTTON", lessonText: "Bul promettre lépp. Tànnal benn doole.", exercisePrompt: "Lan mooy sa dige bu mag ?", buttonsJson: [{ id: "gaaw", title: "Dafa gaaw" }, { id: "yomb", title: "Dafa yomb / Prix" }, { id: "woor", title: "Dafa woor" }] },
|
| 91 |
-
{ dayNumber: 9, exerciseType: "TEXT", lessonText: "Dem waxtaan ak 5 nit. Ñaata ñu wax WAAN ?", exercisePrompt: "Ñaata nit ñoo wax WAAN ? (Bind ma chiffre bi rek)" },
|
| 92 |
-
{ dayNumber: 10, exerciseType: "TEXT", lessonText: "Sa priix war na japp sa dépense. Bind ñaari dépense.", exercisePrompt: "Bind ma ñaari dépense yu gën a rëy ci sa mbir :" },
|
| 93 |
-
{ dayNumber: 11, exerciseType: "BUTTON", lessonText: "Lu tax yaw te du keneen ?", exercisePrompt: "Lan nga gën a mën ci ñeneen ñi ?", buttonsJson: [{ id: "baax", title: "Dafa baax" }, { id: "gaaw", title: "Dafa gaaw" }, { id: "koolute", title: "Kooluté / Confiance" }] },
|
| 94 |
-
{ dayNumber: 12, exerciseType: "AUDIO", lessonText: "Léegi nga hazır. Wax ci 30 seconde : Man ma... Damaa jàppalé... Ndax... Damaa jaay... Ci...", exercisePrompt: "Yónnee ma sa Pitch bu gatt ci 30 seconde :" },
|
| 95 |
-
]
|
| 96 |
-
}
|
| 97 |
-
}
|
| 98 |
-
});
|
| 99 |
-
|
| 100 |
-
await scheduleMessage(user.id, "✅ Seeding terminé ! Les 2 modules (FR + WOLOF) sont en base. Envoie INSCRIPTION pour commencer.");
|
| 101 |
} catch (err: any) {
|
| 102 |
-
console.error('
|
| 103 |
-
|
|
|
|
| 104 |
}
|
| 105 |
return;
|
| 106 |
}
|
|
|
|
| 36 |
}
|
| 37 |
|
| 38 |
if (normalizedText === 'SEED') {
|
| 39 |
+
// Reply immediately so the webhook doesn't time out
|
| 40 |
+
console.log(`[SEED] Triggered by user ${user.id}`);
|
| 41 |
try {
|
| 42 |
+
const { seedDatabase } = await import('@repo/database');
|
| 43 |
+
const result = await seedDatabase(prisma);
|
| 44 |
+
console.log('[SEED] Result:', result.message);
|
| 45 |
+
await scheduleMessage(user.id, result.seeded
|
| 46 |
+
? "✅ Seeding terminé ! Les 2 modules (FR + WOLOF) sont en base.\nEnvoie INSCRIPTION pour commencer."
|
| 47 |
+
: "ℹ️ Les données existent déjà. Envoie INSCRIPTION pour commencer."
|
| 48 |
+
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
} catch (err: any) {
|
| 50 |
+
console.error('[SEED] Error:', err.message);
|
| 51 |
+
// Don't crash — scheduleMessage a user-friendly error
|
| 52 |
+
await scheduleMessage(user.id, `❌ Erreur seed : ${err.message?.substring(0, 200)}`);
|
| 53 |
}
|
| 54 |
return;
|
| 55 |
}
|
apps/whatsapp-worker/src/whatsapp-cloud.ts
CHANGED
|
@@ -32,6 +32,12 @@ function getHeaders(): Record<string, string> {
|
|
| 32 |
* @param text - Message body (supports basic WhatsApp markdown: *bold*, _italic_)
|
| 33 |
*/
|
| 34 |
export async function sendTextMessage(to: string, text: string): Promise<void> {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
const body = {
|
| 36 |
messaging_product: 'whatsapp',
|
| 37 |
recipient_type: 'individual',
|
|
|
|
| 32 |
* @param text - Message body (supports basic WhatsApp markdown: *bold*, _italic_)
|
| 33 |
*/
|
| 34 |
export async function sendTextMessage(to: string, text: string): Promise<void> {
|
| 35 |
+
// Safety guard: HF is inbound-only. Only Railway worker should call this.
|
| 36 |
+
if (process.env.DISABLE_WHATSAPP_SEND === 'true') {
|
| 37 |
+
console.warn(`[WhatsApp] DISABLE_WHATSAPP_SEND=true — Skipping send to ${to}. Message: "${text.substring(0, 60)}..."`);
|
| 38 |
+
return;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
const body = {
|
| 42 |
messaging_product: 'whatsapp',
|
| 43 |
recipient_type: 'individual',
|
packages/database/index.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
| 1 |
export * from '@prisma/client';
|
|
|
|
|
|
| 1 |
export * from '@prisma/client';
|
| 2 |
+
export { seedDatabase } from './src/seed';
|
packages/database/package.json
CHANGED
|
@@ -3,6 +3,10 @@
|
|
| 3 |
"version": "0.0.0",
|
| 4 |
"main": "./index.ts",
|
| 5 |
"types": "./index.ts",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
"scripts": {
|
| 7 |
"db:push": "prisma db push",
|
| 8 |
"db:studio": "prisma studio",
|
|
|
|
| 3 |
"version": "0.0.0",
|
| 4 |
"main": "./index.ts",
|
| 5 |
"types": "./index.ts",
|
| 6 |
+
"exports": {
|
| 7 |
+
".": "./index.ts",
|
| 8 |
+
"./seed": "./src/seed.ts"
|
| 9 |
+
},
|
| 10 |
"scripts": {
|
| 11 |
"db:push": "prisma db push",
|
| 12 |
"db:studio": "prisma studio",
|
packages/database/src/seed.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { PrismaClient } from '@prisma/client';
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Seeds the database with the Module 1 curriculum (FR + WOLOF).
|
| 5 |
+
* Safe to call multiple times — checks for existing data before inserting.
|
| 6 |
+
*/
|
| 7 |
+
export async function seedDatabase(prisma: PrismaClient): Promise<{ seeded: boolean; message: string }> {
|
| 8 |
+
const existingFR = await prisma.track.findFirst({
|
| 9 |
+
where: { title: "Comprendre Son Business (FR)" }
|
| 10 |
+
});
|
| 11 |
+
|
| 12 |
+
if (existingFR) {
|
| 13 |
+
return { seeded: false, message: "Data already exists. No seeding needed." };
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
// ── MODULE 1 – FR ──────────────────────────────────────────────────────────
|
| 17 |
+
await prisma.track.create({
|
| 18 |
+
data: {
|
| 19 |
+
title: "Comprendre Son Business (FR)",
|
| 20 |
+
description: "Apprenez à définir, tester et vendre votre projet en 12 leçons.",
|
| 21 |
+
duration: 12,
|
| 22 |
+
language: "FR",
|
| 23 |
+
days: {
|
| 24 |
+
create: [
|
| 25 |
+
{ dayNumber: 1, exerciseType: "AUDIO", lessonText: "Aujourd'hui, on commence simple. Beaucoup de personnes disent : je fais le commerce. Mais ça ne veut rien dire. Dis-moi clairement : Tu aides QUI, à faire QUOI, et comment tu gagnes de l'argent. Exemple : Je vends du jus bissap aux étudiants devant l'université. Maintenant, c'est à toi. Dis ta phrase en 15 secondes.", exercisePrompt: "Envoie-moi un court message vocal (ou texte) avec ta phrase d'activité :" },
|
| 26 |
+
{ dayNumber: 2, exerciseType: "AUDIO", lessonText: "Le client n'achète pas ton produit. Il achète un résultat. Il n'achète pas du savon. Il achète la propreté. Va demander à 2 clients : Pourquoi tu achètes ça ? Écoute bien leurs mots.", exercisePrompt: "Envoie un audio résumant les 2 réponses de tes clients." },
|
| 27 |
+
{ dayNumber: 3, exerciseType: "BUTTON", lessonText: "Si tu vends à tout le monde, tu ne vends à personne. Choisis un seul client principal. Qui est le plus intéressé par ton produit ?", exercisePrompt: "Sélectionne ton client principal ci-dessous :", buttonsJson: [{ id: "jeunes", title: "Jeunes" }, { id: "femmes", title: "Femmes" }, { id: "commercants", title: "Commerçants" }] },
|
| 28 |
+
{ dayNumber: 4, exerciseType: "TEXT", lessonText: "Ton client a un problème. Quel est son plus grand problème ? Parle à 3 personnes aujourd'hui. Pose cette question. Écoute sans expliquer ton produit.", exercisePrompt: "Quel est le problème N°1 que tes clients t'ont partagé ?" },
|
| 29 |
+
{ dayNumber: 5, exerciseType: "BUTTON", lessonText: "À quel moment ton client a ce problème ?", exercisePrompt: "Choisis le moment d'apparition du problème :", buttonsJson: [{ id: "matin_midi", title: "Matin ou Midi" }, { id: "soir", title: "Le Soir" }, { id: "tout_le_temps", title: "Tout le temps" }] },
|
| 30 |
+
{ dayNumber: 6, exerciseType: "TEXT", lessonText: "Avant toi, il faisait comment ?", exercisePrompt: "Donne-moi 2 solutions que ton client utilisait avant de te connaître :" },
|
| 31 |
+
{ dayNumber: 7, exerciseType: "TEXT", lessonText: "Explique ta solution en mots simples. Pas compliqué.", exercisePrompt: "Décris-moi ton offre très simplement en une phrase :" },
|
| 32 |
+
{ dayNumber: 8, exerciseType: "BUTTON", lessonText: "Tu ne peux pas promettre tout. Choisis une seule force.", exercisePrompt: "Quelle est ta promesse principale ?", buttonsJson: [{ id: "rapide", title: "Rapide" }, { id: "moins_cher", title: "Moins cher" }, { id: "fiable_proche", title: "Fiable / Proche" }] },
|
| 33 |
+
{ dayNumber: 9, exerciseType: "TEXT", lessonText: "Parle à 5 personnes. Dis ta phrase. Combien disent OUI ?", exercisePrompt: "Combien t'ont dit OUI ? (Envoie juste un chiffre)" },
|
| 34 |
+
{ dayNumber: 10, exerciseType: "TEXT", lessonText: "Ton prix doit couvrir tes coûts. Note 2 dépenses importantes.", exercisePrompt: "Quelles sont tes 2 plus grosses dépenses pour ce projet ?" },
|
| 35 |
+
{ dayNumber: 11, exerciseType: "BUTTON", lessonText: "Pourquoi toi et pas un autre ?", exercisePrompt: "Quel est ton vrai avantage concurrentiel ?", buttonsJson: [{ id: "qualite", title: "Qualité" }, { id: "rapidite", title: "Rapidité" }, { id: "confiance", title: "Confiance" }] },
|
| 36 |
+
{ dayNumber: 12, exerciseType: "AUDIO", lessonText: "Maintenant tu es prêt. Dis en 30 secondes : Je suis... J'aide... Parce que... Je vends... À...", exercisePrompt: "C'est l'heure du test ! Envoie-moi un audio avec ton Mini Pitch de 30 secondes :" },
|
| 37 |
+
]
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
});
|
| 41 |
+
|
| 42 |
+
// ── MODULE 1 – WOLOF ───────────────────────────────────────────────────────
|
| 43 |
+
await prisma.track.create({
|
| 44 |
+
data: {
|
| 45 |
+
title: "Comprendre Son Business (WOLOF)",
|
| 46 |
+
description: "Apprenez à définir, tester et vendre votre projet en 12 leçons.",
|
| 47 |
+
duration: 12,
|
| 48 |
+
language: "WOLOF",
|
| 49 |
+
days: {
|
| 50 |
+
create: [
|
| 51 |
+
{ dayNumber: 1, exerciseType: "AUDIO", lessonText: "Tey, danuy tàmbalee ak lu yomb. Nit ñu bari dañuy wax : dama def commerce. Waaye loolu amul solo. Wax ma leer : Yaay jàppalé KAN, mu def LAN, te naka nga amee xaalis. Misaal : Damaa jaay jus bissap ci taalibe yu université. Léegi sa waxtu la. Wax sa activité ci 15 seconde.", exercisePrompt: "Yónnee ma ab kàddu (audio) walla message bu gatt ngir wax sa mbir :" },
|
| 52 |
+
{ dayNumber: 2, exerciseType: "AUDIO", lessonText: "Kiliifa bi du jënd sa produit rek. Mu jënd ab résultat. Du jënd savon rek. Mu jënd set. Dem laaj 2 kiliifa : Lu tax nga jënd lii ? Déggal bu baax li ñuy wax.", exercisePrompt: "Yónnee ma audio ngir tënk ñaari tontu ya." },
|
| 53 |
+
{ dayNumber: 3, exerciseType: "BUTTON", lessonText: "Su nga jaay ci ñépp, doo jaay ci kenn. Tànnal benn kiliifa bu mag. Kan moo gën a soxla sa produit ?", exercisePrompt: "Tànnal sa kiliifa bu mag ci suuf :", buttonsJson: [{ id: "ndaw_nyi", title: "Ndaw ñi / Jeunes" }, { id: "jigeen_nyi", title: "Jigeen ñi / Femmes" }, { id: "jaaykat_yi", title: "Jaaykat yi / Comms" }] },
|
| 54 |
+
{ dayNumber: 4, exerciseType: "TEXT", lessonText: "Sa kiliifa am na jafe jafe. Lan mooy jafe jafe bu gën a rëy ? Dem waxtaan ak 3 nit. Laaj leen. Bul def publicité.", exercisePrompt: "Lan mooy jafe jafe bu gën a mag bi sa kiliifa yi am ?" },
|
| 55 |
+
{ dayNumber: 5, exerciseType: "BUTTON", lessonText: "Kañ la jafe jafe bi di ñëw ?", exercisePrompt: "Tànnal jamono ji jafe jafe bi di faral di am :", buttonsJson: [{ id: "suba_bëccëg", title: "Suba walla Bëccëg" }, { id: "ngoon", title: "Ngoon / Guddi" }, { id: "saa_su_ne", title: "Saa su nekk" }] },
|
| 56 |
+
{ dayNumber: 6, exerciseType: "TEXT", lessonText: "Balaa yaw, naka la daan def ?", exercisePrompt: "Wax ma ñaari pexe yi kiliifa bi daan jëfandikoo balaa xam sa produit :" },
|
| 57 |
+
{ dayNumber: 7, exerciseType: "TEXT", lessonText: "Wax sa solution ci wax yu yomb.", exercisePrompt: "Tënkal sa solution ci benn phrase bu yomb :" },
|
| 58 |
+
{ dayNumber: 8, exerciseType: "BUTTON", lessonText: "Bul promettre lépp. Tànnal benn doole.", exercisePrompt: "Lan mooy sa dige bu mag ?", buttonsJson: [{ id: "gaaw", title: "Dafa gaaw" }, { id: "yomb", title: "Dafa yomb / Prix" }, { id: "woor", title: "Dafa woor" }] },
|
| 59 |
+
{ dayNumber: 9, exerciseType: "TEXT", lessonText: "Dem waxtaan ak 5 nit. Ñaata ñu wax WAAN ?", exercisePrompt: "Ñaata nit ñoo wax WAAN ? (Bind ma chiffre bi rek)" },
|
| 60 |
+
{ dayNumber: 10, exerciseType: "TEXT", lessonText: "Sa priix war na japp sa dépense. Bind ñaari dépense.", exercisePrompt: "Bind ma ñaari dépense yu gën a rëy ci sa mbir :" },
|
| 61 |
+
{ dayNumber: 11, exerciseType: "BUTTON", lessonText: "Lu tax yaw te du keneen ?", exercisePrompt: "Lan nga gën a mën ci ñeneen ñi ?", buttonsJson: [{ id: "baax", title: "Dafa baax" }, { id: "gaaw", title: "Dafa gaaw" }, { id: "koolute", title: "Kooluté / Confiance" }] },
|
| 62 |
+
{ dayNumber: 12, exerciseType: "AUDIO", lessonText: "Léegi nga hazır. Wax ci 30 seconde : Man ma... Damaa jàppalé... Ndax... Damaa jaay... Ci...", exercisePrompt: "Yónnee ma sa Pitch bu gatt ci 30 seconde :" },
|
| 63 |
+
]
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
});
|
| 67 |
+
|
| 68 |
+
return { seeded: true, message: "✅ Module 1 FR + WOLOF seeded successfully (24 lessons)." };
|
| 69 |
+
}
|
scripts/start-backend.sh
CHANGED
|
@@ -4,14 +4,19 @@ set -ex
|
|
| 4 |
echo "Running database migrations..."
|
| 5 |
pnpm --filter @repo/database db:push
|
| 6 |
|
| 7 |
-
|
| 8 |
-
# The
|
| 9 |
-
if [
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
else
|
| 14 |
-
echo "
|
| 15 |
fi
|
| 16 |
|
| 17 |
echo "Starting API..."
|
|
@@ -20,6 +25,6 @@ if [ -f "apps/api/dist/src/index.js" ]; then
|
|
| 20 |
elif [ -f "apps/api/dist/index.js" ]; then
|
| 21 |
exec node apps/api/dist/index.js
|
| 22 |
else
|
| 23 |
-
echo "ERROR: API build output missing
|
| 24 |
exit 1
|
| 25 |
fi
|
|
|
|
| 4 |
echo "Running database migrations..."
|
| 5 |
pnpm --filter @repo/database db:push
|
| 6 |
|
| 7 |
+
# On Hugging Face (DISABLE_WORKER=true), only run the API.
|
| 8 |
+
# The WhatsApp worker runs exclusively on Railway and calls graph.facebook.com from there.
|
| 9 |
+
if [ "${DISABLE_WORKER}" != "true" ]; then
|
| 10 |
+
echo "Starting WhatsApp Worker..."
|
| 11 |
+
if [ -f "apps/whatsapp-worker/dist/src/index.js" ]; then
|
| 12 |
+
node apps/whatsapp-worker/dist/src/index.js &
|
| 13 |
+
elif [ -f "apps/whatsapp-worker/dist/index.js" ]; then
|
| 14 |
+
node apps/whatsapp-worker/dist/index.js &
|
| 15 |
+
else
|
| 16 |
+
echo "WARNING: WhatsApp Worker build output missing — worker not started."
|
| 17 |
+
fi
|
| 18 |
else
|
| 19 |
+
echo "DISABLE_WORKER=true — Skipping worker startup (HF inbound-only mode)."
|
| 20 |
fi
|
| 21 |
|
| 22 |
echo "Starting API..."
|
|
|
|
| 25 |
elif [ -f "apps/api/dist/index.js" ]; then
|
| 26 |
exec node apps/api/dist/index.js
|
| 27 |
else
|
| 28 |
+
echo "ERROR: API build output missing!"
|
| 29 |
exit 1
|
| 30 |
fi
|