feat(GRO-984): persist outbound SMS to messages table
Wire sendSms() to find/create the conversation by sender/recipient and insert an outbound message row with the Telnyx message_id. Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
import { Telnyx } from "telnyx";
|
import { Telnyx } from "telnyx";
|
||||||
import { createHmac } from "crypto";
|
import { createHmac } from "crypto";
|
||||||
|
import { v4 as uuidv4 } from "uuid";
|
||||||
|
import { getDb, conversations, messages, businessSettings, eq, and } from "@groombook/db";
|
||||||
|
|
||||||
export interface SmsProvider {
|
export interface SmsProvider {
|
||||||
sendSms(to: string, body: string, mediaUrls?: string[]): Promise<{ messageId: string; status: string }>;
|
sendSms(to: string, body: string, mediaUrls?: string[]): Promise<{ messageId: string; status: string }>;
|
||||||
@@ -32,6 +34,48 @@ function isE164(phone: string): boolean {
|
|||||||
return /^\+[1-9]\d{7,14}$/.test(phone);
|
return /^\+[1-9]\d{7,14}$/.test(phone);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function findOrCreateConversationForOutbound(
|
||||||
|
businessId: string,
|
||||||
|
clientPhone: string,
|
||||||
|
businessNumber: string
|
||||||
|
): Promise<{ id: string; clientId: string }> {
|
||||||
|
const db = getDb();
|
||||||
|
|
||||||
|
const [existing] = await db
|
||||||
|
.select({ id: conversations.id, clientId: conversations.clientId })
|
||||||
|
.from(conversations)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(conversations.businessId, businessId),
|
||||||
|
eq(conversations.externalNumber, clientPhone),
|
||||||
|
eq(conversations.businessNumber, businessNumber)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
return { id: existing.id, clientId: existing.clientId };
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientId = uuidv4();
|
||||||
|
|
||||||
|
const [created] = await db
|
||||||
|
.insert(conversations)
|
||||||
|
.values({
|
||||||
|
id: uuidv4(),
|
||||||
|
businessId,
|
||||||
|
clientId,
|
||||||
|
channel: "sms",
|
||||||
|
externalNumber: clientPhone,
|
||||||
|
businessNumber,
|
||||||
|
lastMessageAt: new Date(),
|
||||||
|
status: "active",
|
||||||
|
})
|
||||||
|
.returning({ id: conversations.id, clientId: conversations.clientId });
|
||||||
|
|
||||||
|
return { id: created.id, clientId: created.clientId };
|
||||||
|
}
|
||||||
|
|
||||||
export async function sendSms(
|
export async function sendSms(
|
||||||
to: string,
|
to: string,
|
||||||
body: string,
|
body: string,
|
||||||
@@ -58,6 +102,38 @@ export async function sendSms(
|
|||||||
|
|
||||||
const result = await client.messages.create(payload as Record<string, string | string[]>);
|
const result = await client.messages.create(payload as Record<string, string | string[]>);
|
||||||
const smsResult = result.data as unknown as TelnyxSmsResult;
|
const smsResult = result.data as unknown as TelnyxSmsResult;
|
||||||
|
|
||||||
|
const db = getDb();
|
||||||
|
const [settings] = await db
|
||||||
|
.select({ id: businessSettings.id })
|
||||||
|
.from(businessSettings)
|
||||||
|
.where(eq(businessSettings.messagingPhoneNumber, from))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (settings?.id) {
|
||||||
|
const { id: conversationId } = await findOrCreateConversationForOutbound(
|
||||||
|
settings.id,
|
||||||
|
to,
|
||||||
|
from
|
||||||
|
);
|
||||||
|
|
||||||
|
await db
|
||||||
|
.update(conversations)
|
||||||
|
.set({ lastMessageAt: new Date(), updatedAt: new Date() })
|
||||||
|
.where(eq(conversations.id, conversationId));
|
||||||
|
|
||||||
|
await db
|
||||||
|
.insert(messages)
|
||||||
|
.values({
|
||||||
|
id: uuidv4(),
|
||||||
|
conversationId,
|
||||||
|
direction: "outbound",
|
||||||
|
body,
|
||||||
|
status: "sent",
|
||||||
|
providerMessageId: smsResult.message_id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
messageId: smsResult.message_id,
|
messageId: smsResult.message_id,
|
||||||
status: smsResult.status,
|
status: smsResult.status,
|
||||||
@@ -139,4 +215,4 @@ export async function smsSend(
|
|||||||
|
|
||||||
await provider.sendSms(to, body, mediaUrls);
|
await provider.sendSms(to, body, mediaUrls);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user