diff --git a/lib/emails/__tests__/getEmailFooter.test.ts b/lib/emails/__tests__/getEmailFooter.test.ts index 0e5890a9..b009b713 100644 --- a/lib/emails/__tests__/getEmailFooter.test.ts +++ b/lib/emails/__tests__/getEmailFooter.test.ts @@ -38,4 +38,20 @@ describe("getEmailFooter", () => { expect(footer).toContain("font-size:12px"); expect(footer).toContain("color:#6b7280"); }); + + it("includes artist workspace when artistName is provided", () => { + const footer = getEmailFooter("room-id", "Taylor Swift"); + expect(footer).toContain("From Taylor Swift's workspace"); + }); + + it("excludes artist line when artistName is not provided", () => { + const footer = getEmailFooter("room-id"); + expect(footer).not.toContain("workspace"); + }); + + it("includes artist workspace without roomId", () => { + const footer = getEmailFooter(undefined, "Drake"); + expect(footer).toContain("From Drake's workspace"); + expect(footer).not.toContain("chat.recoupable.com"); + }); }); diff --git a/lib/emails/getEmailFooter.ts b/lib/emails/getEmailFooter.ts index 2557ef35..6fe173d0 100644 --- a/lib/emails/getEmailFooter.ts +++ b/lib/emails/getEmailFooter.ts @@ -2,9 +2,17 @@ * Generates a standardized email footer HTML. * * @param roomId - Optional room ID for the chat link. If not provided, only the reply note is shown. + * @param artistName - Optional artist name to display in the footer. * @returns HTML string for the email footer. */ -export function getEmailFooter(roomId?: string): string { +export function getEmailFooter(roomId?: string, artistName?: string): string { + const artistLine = artistName + ? ` +

+ From ${artistName}'s workspace +

`.trim() + : ""; + const replyNote = `

Note: you can reply directly to this email to continue the conversation. @@ -22,6 +30,7 @@ export function getEmailFooter(roomId?: string): string { return `


+${artistLine} ${replyNote} ${chatLink}`.trim(); } diff --git a/lib/emails/inbound/generateEmailResponse.ts b/lib/emails/inbound/generateEmailResponse.ts index 95f19c43..59edf811 100644 --- a/lib/emails/inbound/generateEmailResponse.ts +++ b/lib/emails/inbound/generateEmailResponse.ts @@ -3,6 +3,7 @@ import { ChatRequestBody } from "@/lib/chat/validateChatRequest"; import getGeneralAgent from "@/lib/agents/generalAgent/getGeneralAgent"; import { getEmailRoomMessages } from "@/lib/emails/inbound/getEmailRoomMessages"; import { getEmailFooter } from "@/lib/emails/getEmailFooter"; +import { selectRoomWithArtist } from "@/lib/supabase/rooms/selectRoomWithArtist"; /** * Generates the assistant response HTML for an email, including: @@ -29,8 +30,9 @@ export async function generateEmailResponse( const chatResponse = await agent.generate({ messages }); const text = chatResponse.text; + const roomData = await selectRoomWithArtist(roomId); const bodyHtml = marked(text); - const footerHtml = getEmailFooter(roomId); + const footerHtml = getEmailFooter(roomId, roomData?.artist_name || undefined); const html = `${bodyHtml}\n\n${footerHtml}`; return { text, html }; diff --git a/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts b/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts index 509e1aba..84c5344d 100644 --- a/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts +++ b/lib/mcp/tools/__tests__/registerSendEmailTool.test.ts @@ -4,11 +4,16 @@ import { registerSendEmailTool } from "../registerSendEmailTool"; import { NextResponse } from "next/server"; const mockSendEmailWithResend = vi.fn(); +const mockSelectRoomWithArtist = vi.fn(); vi.mock("@/lib/emails/sendEmail", () => ({ sendEmailWithResend: (...args: unknown[]) => mockSendEmailWithResend(...args), })); +vi.mock("@/lib/supabase/rooms/selectRoomWithArtist", () => ({ + selectRoomWithArtist: (...args: unknown[]) => mockSelectRoomWithArtist(...args), +})); + describe("registerSendEmailTool", () => { let mockServer: McpServer; let registeredHandler: (args: unknown) => Promise; diff --git a/lib/mcp/tools/registerSendEmailTool.ts b/lib/mcp/tools/registerSendEmailTool.ts index 3f29eb5d..93d6565a 100644 --- a/lib/mcp/tools/registerSendEmailTool.ts +++ b/lib/mcp/tools/registerSendEmailTool.ts @@ -5,6 +5,7 @@ import { getToolResultSuccess } from "@/lib/mcp/getToolResultSuccess"; import { getToolResultError } from "@/lib/mcp/getToolResultError"; import { RECOUP_FROM_EMAIL } from "@/lib/const"; import { getEmailFooter } from "@/lib/emails/getEmailFooter"; +import { selectRoomWithArtist } from "@/lib/supabase/rooms/selectRoomWithArtist"; import { NextResponse } from "next/server"; import { marked } from "marked"; @@ -24,7 +25,8 @@ export function registerSendEmailTool(server: McpServer): void { async (args: SendEmailInput) => { const { to, cc = [], subject, text, html = "", headers = {}, room_id } = args; - const footer = getEmailFooter(room_id); + const roomData = room_id ? await selectRoomWithArtist(room_id) : null; + const footer = getEmailFooter(room_id, roomData?.artist_name || undefined); const bodyHtml = html || (text ? marked(text) : ""); const htmlWithFooter = `${bodyHtml}\n\n${footer}`; diff --git a/lib/supabase/rooms/selectRoomWithArtist.ts b/lib/supabase/rooms/selectRoomWithArtist.ts new file mode 100644 index 00000000..32f89288 --- /dev/null +++ b/lib/supabase/rooms/selectRoomWithArtist.ts @@ -0,0 +1,31 @@ +import supabase from "../serverClient"; + +type RoomWithArtist = { + id: string; + artist_id: string | null; + artist_name: string | null; +}; + +/** + * Select a room with its associated artist name from accounts table. + * + * @param roomId - The room ID to query. + * @returns The room data with artist name, or null if not found. + */ +export async function selectRoomWithArtist(roomId: string): Promise { + const { data, error } = await supabase + .from("rooms") + .select("id, artist_id, accounts!rooms_artist_id_fkey(name)") + .eq("id", roomId) + .single(); + + if (error || !data) return null; + + const account = Array.isArray(data.accounts) ? data.accounts[0] : data.accounts; + + return { + id: data.id, + artist_id: data.artist_id, + artist_name: account?.name || null, + }; +}