-
Notifications
You must be signed in to change notification settings - Fork 51
fix(instagram): mostrar nombre del contacto y no perder reels/posts/videos en DMs #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,7 +4,7 @@ import type { ChannelAdapter } from "./channels/shared"; | |
| import { telegramAdapter } from "./channels/telegram"; | ||
| import { manychatAdapter } from "./channels/manychat"; | ||
| import { twilioAdapter } from "./channels/twilio"; | ||
| import { parseMetaEvents, verifyMetaSignature } from "./channels/meta"; | ||
| import { parseMetaEvents, verifyMetaSignature, metaProfileName } from "./channels/meta"; | ||
| import { parseWhatsAppEvents, serveWhatsAppMedia } from "./channels/whatsapp"; | ||
| import { adminApp } from "./admin/routes"; | ||
| import { purgeOldMessages } from "./crons/purgeOldMessages"; | ||
|
|
@@ -131,6 +131,11 @@ app.post("/webhooks/meta", async (c) => { | |
| // (si no, cada DM se procesa DOBLE: 2x LLM, 2x respuestas al lead y | ||
| // colisiones de rate limit en ráfagas de historias). | ||
| if (msg.channel === "instagram" && c.env.IG_DM_SOURCE === "manychat") continue; | ||
| // Instagram: el webhook no trae el nombre, así que la conversación se crearía | ||
| // con el id opaco. Lo resolvemos a "Nombre · @usuario" (best-effort, cacheado). | ||
| if (msg.channel === "instagram" && !msg.displayName) { | ||
| msg.displayName = await metaProfileName(c.env, msg.channelUserId); | ||
| } | ||
|
Comment on lines
+134
to
+138
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: rg -n "metaProfileName|displayName|INSTAGRAM_ACCESS_TOKEN|/meta|webhook" test src --glob "*.test.ts"
sed -n '1,180p' test/channels/meta.test.ts
sed -n '120,150p' src/index.tsRepository: santmun/forja Length of output: 5881 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- helper definition/usages ---'
rg -n -C 12 "metaProfileName" src test --glob '*.{ts,tsx,js,jsx}'
printf '%s\n' '--- index route and setup ---'
sed -n '1,170p' src/index.ts
printf '%s\n' '--- test files and likely app invocation ---'
git ls-files 'test/**' 'src/**' | sort
rg -n -C 8 "fetch\\(|webhooks/meta|/webhooks/instagram|app\\.request|new Hono|createExecutionContext|SELF|ingest\\(" test src --glob '*.{test.ts,ts}'
printf '%s\n' '--- package scripts ---'
cat package.jsonRepository: santmun/forja Length of output: 50369 🏁 Script executed: #!/bin/bash
set -e
printf '%s\n' '--- meta helper ---'
sed -n '108,160p' src/channels/meta.ts
printf '%s\n' '--- index test imports/setup and meta references ---'
sed -n '1,90p' test/index.test.ts
rg -n -C 5 "meta|INSTAGRAM|AGENT|fetch|vi\\.mock|spyOn" test/index.test.ts
printf '%s\n' '--- worker export and agent stub ---'
rg -n -C 8 "export default|export const|function getAgentStub|export function getAgentStub" src/index.ts src/agentStub.ts
printf '%s\n' '--- meta test complete ---'
cat -n test/channels/meta.test.tsRepository: santmun/forja Length of output: 13172 Add focused tests for Instagram profile enrichment. The current tests cover only 🤖 Prompt for AI Agents |
||
| await getAgentStub(c.env, `${msg.channel}:${msg.channelUserId}`).ingest(msg); | ||
| } | ||
| return c.text("EVENT_RECEIVED", 200); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| import { describe, it, expect } from "vitest"; | ||
| import { parseMetaEvents } from "../../src/channels/meta"; | ||
|
|
||
| function igBody(attachments: { type: string; payload?: Record<string, unknown> }[], text?: string) { | ||
| return { | ||
| object: "instagram", | ||
| entry: [{ messaging: [{ sender: { id: "17841400000000000" }, message: { mid: "m1", text, attachments } }] }], | ||
| }; | ||
| } | ||
| function fbBody(attachments: { type: string; payload?: Record<string, unknown> }[], text?: string) { | ||
| return { | ||
| object: "page", | ||
| entry: [{ messaging: [{ sender: { id: "PSID123" }, message: { mid: "m1", text, attachments } }] }], | ||
| }; | ||
| } | ||
|
|
||
| describe("parseMetaEvents · Instagram shares (antes se perdían)", () => { | ||
| it("un reel compartido llega como texto con el permalink (no se descarta)", () => { | ||
| const url = "https://www.instagram.com/reel/ABC123/"; | ||
| const [msg] = parseMetaEvents(igBody([{ type: "ig_reel", payload: { url, title: "mira esto" } }])); | ||
| expect(msg).toBeDefined(); | ||
| expect(msg.channel).toBe("instagram"); | ||
| expect(msg.text).toContain("mira esto"); | ||
| expect(msg.text).toContain(url); | ||
| }); | ||
|
|
||
| it("un post compartido muestra su imagen del CDN (sin permalink, es lo único que da Meta)", () => { | ||
| const url = "https://lookaside.fbsbx.com/ig_messaging_cdn/foto.jpg"; | ||
| const [msg] = parseMetaEvents(igBody([{ type: "ig_post", payload: { url } }])); | ||
| expect(msg).toBeDefined(); | ||
| expect(msg.imageUrl).toBe(url); | ||
| expect(msg.text).toContain("Publicación"); | ||
| }); | ||
|
|
||
| it("un video llega como texto con su url", () => { | ||
| const url = "https://cdn.example/video.mp4"; | ||
| const [msg] = parseMetaEvents(igBody([{ type: "video", payload: { url } }])); | ||
| expect(msg).toBeDefined(); | ||
| expect(msg.text).toContain(url); | ||
| }); | ||
|
|
||
| it("imagen y audio siguen mapeando a imageUrl/audioUrl", () => { | ||
| const [img] = parseMetaEvents(igBody([{ type: "image", payload: { url: "https://x/i.jpg" } }])); | ||
| expect(img.imageUrl).toBe("https://x/i.jpg"); | ||
| const [aud] = parseMetaEvents(igBody([{ type: "audio", payload: { url: "https://x/a.mp3" } }])); | ||
| expect(aud.audioUrl).toBe("https://x/a.mp3"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("parseMetaEvents · Messenger sin cambios (solo IG se enriquece)", () => { | ||
| it("un share por Messenger se sigue descartando (no hay contenido de texto/imagen/audio)", () => { | ||
| const out = parseMetaEvents(fbBody([{ type: "ig_reel", payload: { url: "https://instagram.com/reel/X/" } }])); | ||
| expect(out).toHaveLength(0); | ||
| }); | ||
|
|
||
| it("texto por Messenger pasa igual que siempre", () => { | ||
| const [msg] = parseMetaEvents(fbBody([], "hola")); | ||
| expect(msg.channel).toBe("messenger"); | ||
| expect(msg.text).toBe("hola"); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: santmun/forja
Length of output: 9545
🏁 Script executed:
Repository: santmun/forja
Length of output: 7931
No almacene errores de Graph API de forma indefinida.
La condición
!r.okincluye respuestas temporales como 429 y 5xx.igNameCacheguarda"", y las llamadas posteriores retornan ese valor sin otra solicitud mientras viva el isolate. No almacene respuestas no exitosas o use una entrada negativa con expiración corta.🤖 Prompt for AI Agents