import { A1BaseAPI, type WhatsAppIncomingData } from "a1base-node";
import express, { Express, Request, Response } from "express";
import OpenAI from "openai";
const AGENT_NUMBER = "YOUR_AGENT_NUMBER";
const API_KEY = "YOUR_A1BASE_API_KEY";
const API_SECRET = "YOUR_A1BASE_API_SECRET";
const ACCOUNT_ID = "YOUR_A1BASE_ACCOUNT_ID";
const OPENAI_API_KEY = "YOUR_OPENAI_API_KEY";
interface ThreadMessage {
message_id: string;
content: string;
sender_number: string;
sender_name: string;
timestamp: string;
}
const openai = new OpenAI({
apiKey: OPENAI_API_KEY,
});
const credentials = {
apiKey: API_KEY,
apiSecret: API_SECRET,
};
const client = new A1BaseAPI({
credentials
});
const messagesByThread = new Map<string, ThreadMessage[]>();
const getSystemPrompt = (userName: string) => `
You are a friendly AI chat assistant. Keep your responses concise and engaging.
The user's name is ${userName}.
You should address them by name in the first message, and in future messages where appropriate (but not too often, keep it natural).
Keep messages relatively short as this conversation is occurring over WhatsApp.
Try to mirror their mannerisms. If they use lowercase, you should too. Etc.
`;
async function generateAgentResponse(threadId: string): Promise<string> {
const threadMessages = messagesByThread.get(threadId) || [];
const messages = threadMessages.map((msg) => ({
role:
msg.sender_number === AGENT_NUMBER
? ("assistant" as const)
: ("user" as const),
content: msg.content,
}));
const userName =
[...threadMessages]
.reverse()
.find((msg: ThreadMessage) => msg.sender_number !== AGENT_NUMBER)
?.sender_name || "";
console.log(messages);
const completion = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: getSystemPrompt(userName) },
...messages,
],
});
return (
completion.choices[0].message.content ||
"Sorry, I couldn't generate a response"
);
}
const saveMessage = (threadId: string, message: ThreadMessage) => {
const threadMessages = messagesByThread.get(threadId) || [];
threadMessages.push(message);
messagesByThread.set(threadId, threadMessages);
console.log(`[Save] Stored new message in thread:`, threadId, message);
};
const app: Express = express();
const port = 3000;
app.use(express.json());
app.post(
"/whatsapp/incoming",
async (req: Request, res: Response): Promise<any> => {
console.log("[Receive] Incoming message:", req.body);
let {
thread_id,
message_id,
thread_type,
content,
sender_number,
sender_name,
a1_account_id,
timestamp,
} = req.body as WhatsAppIncomingData;
sender_number = "+" + sender_number;
if (thread_type === "group" && sender_number === "+") {
sender_number = AGENT_NUMBER;
}
saveMessage(thread_id, {
message_id,
content,
sender_number,
sender_name,
timestamp,
});
if (sender_number === AGENT_NUMBER) {
console.log("[Receive] Message from agent, not generating response");
return res.json({ success: true });
}
const aiResponse = await generateAgentResponse(thread_id);
if (thread_type === "group") {
try {
await client.sendGroupMessage(ACCOUNT_ID, {
content: aiResponse,
from: AGENT_NUMBER,
thread_id: thread_id,
service: "whatsapp",
});
} catch (error) {
console.error("[Chat] Error generating/sending AI response:", error);
await client.sendGroupMessage(ACCOUNT_ID, {
content: "Sorry, I encountered an error processing your message",
from: AGENT_NUMBER,
thread_id: thread_id,
service: "whatsapp",
});
}
} else {
try {
await client.sendIndividualMessage(ACCOUNT_ID, {
content: aiResponse,
from: AGENT_NUMBER,
to: sender_number,
service: "whatsapp",
});
} catch (error) {
console.error("[Chat] Error generating/sending AI response:", error);
await client.sendIndividualMessage(ACCOUNT_ID, {
content: "Sorry, I encountered an error processing your message",
from: AGENT_NUMBER,
to: sender_number,
service: "whatsapp",
});
}
}
return res.json({ success: true });
},
);
app.listen(port, () => {
console.log(`[Server] My bot running at http://localhost:${port}`);
if (!API_KEY || !API_SECRET || !ACCOUNT_ID || !AGENT_NUMBER) {
console.warn(
"[Server] Warning: Missing environment variables for WhatsApp integration",
);
}
});