From 3da103c010fccefb4201ea997654191c3e00295d Mon Sep 17 00:00:00 2001 From: Darren Clarke Date: Thu, 16 May 2024 18:22:10 +0200 Subject: [PATCH] Whatsapp service updates --- apps/bridge-frontend/app/layout.tsx | 2 +- apps/bridge-whatsapp/src/routes.ts | 1 + apps/bridge-whatsapp/src/service.ts | 24 +++++++------ .../whatsapp/receive-whatsapp-message.ts | 14 ++++++-- .../tasks/whatsapp/send-whatsapp-message.ts | 25 +++++++++++-- .../app/_components/MultiProvider.tsx | 2 +- apps/link/app/_components/MultiProvider.tsx | 2 +- packages/bridge-ui/actions/service.ts | 13 +++++++ packages/bridge-ui/components/Detail.tsx | 5 ++- packages/bridge-ui/components/Home.tsx | 7 +++- packages/bridge-ui/components/QRCode.tsx | 16 +++++---- packages/bridge-ui/config/whatsapp.ts | 12 ++++--- packages/bridge-ui/lib/actions.ts | 12 +++++++ packages/bridge-ui/lib/service.ts | 14 ++++++-- packages/bridge-ui/lib/whatsapp.ts | 36 +++++++++++++++++-- packages/ui/index.ts | 2 +- 16 files changed, 151 insertions(+), 36 deletions(-) diff --git a/apps/bridge-frontend/app/layout.tsx b/apps/bridge-frontend/app/layout.tsx index 440c3ce..08546c3 100644 --- a/apps/bridge-frontend/app/layout.tsx +++ b/apps/bridge-frontend/app/layout.tsx @@ -2,7 +2,7 @@ import type { Metadata } from "next"; import { LicenseInfo } from "@mui/x-license"; LicenseInfo.setLicenseKey( - "7c9bf25d9e240f76e77cbf7d2ba58a23Tz02NjU4OCxFPTE3MTU4NjIzMzQ2ODgsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=", + "c787ac6613c5f2aa0494c4285fe3e9f2Tz04OTY1NyxFPTE3NDYzNDE0ODkwMDAsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=", ); export const metadata: Metadata = { diff --git a/apps/bridge-whatsapp/src/routes.ts b/apps/bridge-whatsapp/src/routes.ts index 8351241..8043bdc 100644 --- a/apps/bridge-whatsapp/src/routes.ts +++ b/apps/bridge-whatsapp/src/routes.ts @@ -26,6 +26,7 @@ export const SendMessageRoute = withDefaults({ description: "Send a message", async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) { const { id } = request.params; + console.log({ payload: request.payload }); const { phoneNumber, message } = request.payload as MessageRequest; const whatsappService = getService(request); await whatsappService.send(id, phoneNumber, message as string); diff --git a/apps/bridge-whatsapp/src/service.ts b/apps/bridge-whatsapp/src/service.ts index 8b7fc5f..b25bf30 100644 --- a/apps/bridge-whatsapp/src/service.ts +++ b/apps/bridge-whatsapp/src/service.ts @@ -89,8 +89,8 @@ export default class WhatsappService extends Service { if (qr) { console.log("got qr code"); const botDirectory = this.getBotDirectory(botID); - const qrPath = `${botDirectory}/qr.png`; - fs.writeFileSync(qrPath, qr, "base64"); + const qrPath = `${botDirectory}/qr.txt`; + fs.writeFileSync(qrPath, qr, "utf8"); const verifiedFile = `${botDirectory}/verified`; if (fs.existsSync(verifiedFile)) { fs.rmSync(verifiedFile); @@ -220,13 +220,16 @@ export default class WhatsappService extends Service { whatsappBotId: botID, }; - await fetch(`http://localhost:3000/api/whatsapp/${botID}/receive`, { - method: "POST", - headers: { - "Content-Type": "application/json", + await fetch( + `${process.env.BRIDGE_FRONTEND_URL}/api/whatsapp/bots/${botID}/receive`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(receivedMessage), }, - body: JSON.stringify(receivedMessage), - }); + ); } } } @@ -242,9 +245,9 @@ export default class WhatsappService extends Service { getBot(botID: string): Record { const botDirectory = this.getBotDirectory(botID); - const qrPath = `${botDirectory}/qr.png`; + const qrPath = `${botDirectory}/qr.txt`; const verifiedFile = `${botDirectory}/verified`; - const qr = fs.existsSync(qrPath) ? fs.readFileSync(qrPath, "base64") : null; + const qr = fs.existsSync(qrPath) ? fs.readFileSync(qrPath, "utf8") : null; const verified = fs.existsSync(verifiedFile); return { qr, verified }; @@ -276,6 +279,7 @@ export default class WhatsappService extends Service { _lastReceivedDate: Date, ): Promise { const connection = this.connections[botID]?.socket; + console.log({ connection }); const messages = await connection.loadAllUnreadMessages(); return messages; diff --git a/apps/bridge-worker/tasks/whatsapp/receive-whatsapp-message.ts b/apps/bridge-worker/tasks/whatsapp/receive-whatsapp-message.ts index 90fd1b3..4eee24a 100644 --- a/apps/bridge-worker/tasks/whatsapp/receive-whatsapp-message.ts +++ b/apps/bridge-worker/tasks/whatsapp/receive-whatsapp-message.ts @@ -1,12 +1,22 @@ -// import { db, getWorkerUtils } from "bridge-common"; +import { db, getWorkerUtils } from "bridge-common"; interface ReceiveWhatsappMessageTaskOptions { + token; message: any; } const receiveWhatsappMessageTask = async ({ + token, message, -}: ReceiveWhatsappMessageTaskOptions): Promise => {}; +}: ReceiveWhatsappMessageTaskOptions): Promise => { + const bot = await db + .selectFrom("WhatsappBot") + .selectAll() + .where((eb) => eb.or([eb("token", "=", token), eb("id", "=", token)])) + .executeTakeFirstOrThrow(); + + console.log(bot); +}; export default receiveWhatsappMessageTask; diff --git a/apps/bridge-worker/tasks/whatsapp/send-whatsapp-message.ts b/apps/bridge-worker/tasks/whatsapp/send-whatsapp-message.ts index 0cf2da8..35d12a8 100644 --- a/apps/bridge-worker/tasks/whatsapp/send-whatsapp-message.ts +++ b/apps/bridge-worker/tasks/whatsapp/send-whatsapp-message.ts @@ -1,11 +1,32 @@ -// import { db, getWorkerUtils } from "bridge-common"; +import { db } from "bridge-common"; interface SendWhatsappMessageTaskOptions { + token: string; + recipient: string; message: any; } const sendWhatsappMessageTask = async ({ message, -}: SendWhatsappMessageTaskOptions): Promise => {}; + recipient, + token, +}: SendWhatsappMessageTaskOptions): Promise => { + const bot = await db + .selectFrom("WhatsappBot") + .selectAll() + .where("token", "=", token) + .executeTakeFirstOrThrow(); + + const url = `${process.env.BRIDGE_WHATSAPP_URL}/api/bots/${bot.id}/send`; + const params = { message, phoneNumber: recipient }; + console.log({ params }); + const result = await fetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(params), + }); + + console.log({ result }); +}; export default sendWhatsappMessageTask; diff --git a/apps/leafcutter/app/_components/MultiProvider.tsx b/apps/leafcutter/app/_components/MultiProvider.tsx index 7f4fa40..325cee5 100644 --- a/apps/leafcutter/app/_components/MultiProvider.tsx +++ b/apps/leafcutter/app/_components/MultiProvider.tsx @@ -15,7 +15,7 @@ import fr from "leafcutter-ui/locales/fr.json"; import { LicenseInfo } from "@mui/x-license"; LicenseInfo.setLicenseKey( - "7c9bf25d9e240f76e77cbf7d2ba58a23Tz02NjU4OCxFPTE3MTU4NjIzMzQ2ODgsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=", + "c787ac6613c5f2aa0494c4285fe3e9f2Tz04OTY1NyxFPTE3NDYzNDE0ODkwMDAsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=", ); const messages: any = { en, fr }; diff --git a/apps/link/app/_components/MultiProvider.tsx b/apps/link/app/_components/MultiProvider.tsx index b35e031..a1e7d47 100644 --- a/apps/link/app/_components/MultiProvider.tsx +++ b/apps/link/app/_components/MultiProvider.tsx @@ -14,7 +14,7 @@ import { LicenseInfo } from "@mui/x-license"; import { locales, LeafcutterProvider } from "leafcutter-ui"; LicenseInfo.setLicenseKey( - "7c9bf25d9e240f76e77cbf7d2ba58a23Tz02NjU4OCxFPTE3MTU4NjIzMzQ2ODgsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=", + "c787ac6613c5f2aa0494c4285fe3e9f2Tz04OTY1NyxFPTE3NDYzNDE0ODkwMDAsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=", ); export const MultiProvider: FC = ({ children }) => { diff --git a/packages/bridge-ui/actions/service.ts b/packages/bridge-ui/actions/service.ts index c3a856d..adca808 100644 --- a/packages/bridge-ui/actions/service.ts +++ b/packages/bridge-ui/actions/service.ts @@ -112,3 +112,16 @@ export const deleteAction = async ({ entity, table, id }: DeleteActionArgs) => { export const selectAllAction = async (table: keyof Database) => { return db.selectFrom(table).selectAll().execute(); }; + +type SelectOneArgs = { + table: keyof Database; + id: string; +}; + +export const selectOneAction = async ({ table, id }: SelectOneArgs) => { + return db + .selectFrom(table) + .selectAll() + .where("id", "=", id) + .executeTakeFirst(); +}; diff --git a/packages/bridge-ui/components/Detail.tsx b/packages/bridge-ui/components/Detail.tsx index 0f250f4..19bd66e 100644 --- a/packages/bridge-ui/components/Detail.tsx +++ b/packages/bridge-ui/components/Detail.tsx @@ -22,6 +22,7 @@ export const Detail: FC = ({ service, row }) => { [service]: { entity, table, displayName, displayFields: fields }, } = serviceConfig; const id = row.id as string; + const token = row.token as string; const deleteAction = generateDeleteAction({ entity, table }); const router = useRouter(); const { almostBlack } = colors; @@ -76,7 +77,9 @@ export const Detail: FC = ({ service, row }) => { name={field.name} label={field.label} getValue={field.getValue} - id={row["id"] as string} + refreshInterval={field.refreshInterval} + token={token} + verified={row.verified as boolean} helperText={field.helperText} /> )} diff --git a/packages/bridge-ui/components/Home.tsx b/packages/bridge-ui/components/Home.tsx index f4687da..75788ec 100644 --- a/packages/bridge-ui/components/Home.tsx +++ b/packages/bridge-ui/components/Home.tsx @@ -1,5 +1,10 @@ import { FC } from "react"; +import { Box } from "@mui/material"; export const Home: FC = () => { - return

Home

; + return ( + + Overview + + ); }; diff --git a/packages/bridge-ui/components/QRCode.tsx b/packages/bridge-ui/components/QRCode.tsx index 75b35a1..53db696 100644 --- a/packages/bridge-ui/components/QRCode.tsx +++ b/packages/bridge-ui/components/QRCode.tsx @@ -6,7 +6,8 @@ import { colors } from "../styles/theme"; type QRCodeProps = { name: string; label: string; - id: string; + token: string; + verified: boolean; helperText?: string; getValue?: (id: string) => Promise; refreshInterval?: number; @@ -15,7 +16,8 @@ type QRCodeProps = { export const QRCode: FC = ({ name, label, - id, + token, + verified, helperText, getValue, refreshInterval, @@ -24,19 +26,19 @@ export const QRCode: FC = ({ const { white } = colors; useEffect(() => { - if (getValue && refreshInterval) { + if (!verified && getValue && refreshInterval) { const interval = setInterval(async () => { - const result = await getValue(id); + const result = await getValue(token); setValue(result); - }, refreshInterval); + }, refreshInterval * 1000); return () => clearInterval(interval); } }, [getValue, refreshInterval]); - return ( + return !verified ? ( {helperText} - ); + ) : null; }; diff --git a/packages/bridge-ui/config/whatsapp.ts b/packages/bridge-ui/config/whatsapp.ts index d92c3c0..da612d4 100644 --- a/packages/bridge-ui/config/whatsapp.ts +++ b/packages/bridge-ui/config/whatsapp.ts @@ -1,8 +1,12 @@ import { ServiceConfig } from "../lib/service"; +// import { generateSelectOneAction } from "../lib/actions"; -const getQRCode = async (id: string) => { - console.log("Getting QR code"); - return "xya"; // "2hVSc9OT18wbo60WLKlVrd5KqQqYZWdH+kVlRYlrnZcKbjbzwcL4ybkS1/jGaN5bLafX9ZaR829xyhQ="; +const getQRCode = async (token: string) => { + const url = `/api/whatsapp/bots/${token}`; + const result = await fetch(url, { cache: "no-store" }); + const { qr } = await result.json(); + + return qr ?? ""; }; export const whatsappConfig: ServiceConfig = { @@ -71,7 +75,7 @@ export const whatsappConfig: ServiceConfig = { size: 4, getValue: getQRCode, helperText: "Go ahead, scan it", - refreshInterval: 5, + refreshInterval: 15, }, ], listColumns: [ diff --git a/packages/bridge-ui/lib/actions.ts b/packages/bridge-ui/lib/actions.ts index 597e238..546c404 100644 --- a/packages/bridge-ui/lib/actions.ts +++ b/packages/bridge-ui/lib/actions.ts @@ -4,6 +4,7 @@ import { updateAction, deleteAction, selectAllAction, + selectOneAction, } from "../actions/service"; import { FieldDescription, Entity } from "./service"; @@ -70,3 +71,14 @@ export function generateSelectAllAction(table: keyof Database) { return selectAllAction(table); }; } + +type GenerateSelectOneArgs = { + table: keyof Database; + id: string; +}; + +export function generateSelectOneAction({ table, id }: GenerateSelectOneArgs) { + return async () => { + return selectOneAction({ table, id }); + }; +} diff --git a/packages/bridge-ui/lib/service.ts b/packages/bridge-ui/lib/service.ts index fd75582..75f8a9e 100644 --- a/packages/bridge-ui/lib/service.ts +++ b/packages/bridge-ui/lib/service.ts @@ -23,7 +23,7 @@ export type FieldDescription = { name: string; label: string; kind?: "text" | "phone" | "select" | "multi" | "qrcode"; - getValue?: (id: string) => Promise; + getValue?: (token: string) => Promise; refreshInterval?: number; getOptions?: (formState: any) => Promise; autogenerated?: "token"; @@ -70,15 +70,21 @@ export class Service { return NextResponse.json(row); } + async registerBot({ + params: { service, token }, + }: ServiceParams): Promise { + return NextResponse.error() as any; + } + async sendMessage( req: NextRequest, { params: { service, token } }: ServiceParams, ): Promise { - const message = await req.json(); + const json = await req.json(); const worker = await getWorkerUtils(); await worker.addJob(`${service}/send-${service}-message`, { token, - message, + ...json, }); return NextResponse.json({ response: "ok" }); @@ -88,7 +94,9 @@ export class Service { req: NextRequest, { params: { service, token } }: ServiceParams, ): Promise { + console.log("INTO receiveMessage"); const message = await req.json(); + console.log({ message }); const worker = await getWorkerUtils(); await worker.addJob(`${service}/receive-${service}-message`, { token, diff --git a/packages/bridge-ui/lib/whatsapp.ts b/packages/bridge-ui/lib/whatsapp.ts index 1c1bb90..4e40327 100644 --- a/packages/bridge-ui/lib/whatsapp.ts +++ b/packages/bridge-ui/lib/whatsapp.ts @@ -1,3 +1,35 @@ -import { Service } from "./service"; +import { NextResponse } from "next/server"; +import { db } from "bridge-common"; +import { revalidatePath } from "next/cache"; +import { Service, ServiceParams } from "./service"; -export class Whatsapp extends Service {} +export class Whatsapp extends Service { + async getBot({ params: { token } }: ServiceParams) { + const row = await db + .selectFrom("WhatsappBot") + .selectAll() + .where("token", "=", token as string) + .executeTakeFirstOrThrow(); + const id = row.id; + const url = `${process.env.BRIDGE_WHATSAPP_URL}/api/bots/${id}`; + const result = await fetch(url, { cache: "no-store" }); + console.log({ result1: result }); + const json = await result.json(); + + await db + .updateTable("WhatsappBot") + .set({ verified: json.verified }) + .where("id", "=", id) + .execute(); + + revalidatePath(`/whatsapp/${id}`); + + if (!json.verified) { + const url = `${process.env.BRIDGE_WHATSAPP_URL}/api/bots/${id}/register`; + const result = await fetch(url, { method: "POST", cache: "no-store" }); + console.log({ result2: result }); + } + + return NextResponse.json(json); + } +} diff --git a/packages/ui/index.ts b/packages/ui/index.ts index 4f6a836..2580206 100644 --- a/packages/ui/index.ts +++ b/packages/ui/index.ts @@ -1,7 +1,7 @@ import { LicenseInfo } from "@mui/x-license"; LicenseInfo.setLicenseKey( - "7c9bf25d9e240f76e77cbf7d2ba58a23Tz02NjU4OCxFPTE3MTU4NjIzMzQ2ODgsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=", + "c787ac6613c5f2aa0494c4285fe3e9f2Tz04OTY1NyxFPTE3NDYzNDE0ODkwMDAsUz1wcm8sTE09c3Vic2NyaXB0aW9uLEtWPTI=", ); export { List } from "./components/List";