move queues
This commit is contained in:
@ -1,38 +0,0 @@
|
||||
import { Queue } from "quirrel/blitz";
|
||||
import twilio from "twilio";
|
||||
|
||||
import db from "../../../db";
|
||||
import insertCallsQueue from "./insert-calls";
|
||||
|
||||
type Payload = {
|
||||
customerId: string;
|
||||
};
|
||||
|
||||
const fetchCallsQueue = Queue<Payload>("api/queue/fetch-calls", async ({ customerId }) => {
|
||||
const customer = await db.customer.findFirst({ where: { id: customerId } });
|
||||
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId } });
|
||||
|
||||
const [callsSent, callsReceived] = await Promise.all([
|
||||
twilio(customer!.accountSid!, customer!.authToken!).calls.list({
|
||||
from: phoneNumber!.phoneNumber,
|
||||
}),
|
||||
twilio(customer!.accountSid!, customer!.authToken!).calls.list({
|
||||
to: phoneNumber!.phoneNumber,
|
||||
}),
|
||||
]);
|
||||
const calls = [...callsSent, ...callsReceived].sort(
|
||||
(a, b) => a.dateCreated.getTime() - b.dateCreated.getTime()
|
||||
);
|
||||
|
||||
await insertCallsQueue.enqueue(
|
||||
{
|
||||
customerId,
|
||||
calls,
|
||||
},
|
||||
{
|
||||
id: `insert-calls-${customerId}`,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export default fetchCallsQueue;
|
@ -1,38 +0,0 @@
|
||||
import { Queue } from "quirrel/blitz";
|
||||
import twilio from "twilio";
|
||||
|
||||
import db from "../../../db";
|
||||
import insertMessagesQueue from "./insert-messages";
|
||||
|
||||
type Payload = {
|
||||
customerId: string;
|
||||
};
|
||||
|
||||
const fetchMessagesQueue = Queue<Payload>("api/queue/fetch-messages", async ({ customerId }) => {
|
||||
const customer = await db.customer.findFirst({ where: { id: customerId } });
|
||||
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId } });
|
||||
|
||||
const [messagesSent, messagesReceived] = await Promise.all([
|
||||
twilio(customer!.accountSid!, customer!.authToken!).messages.list({
|
||||
from: phoneNumber!.phoneNumber,
|
||||
}),
|
||||
twilio(customer!.accountSid!, customer!.authToken!).messages.list({
|
||||
to: phoneNumber!.phoneNumber,
|
||||
}),
|
||||
]);
|
||||
const messages = [...messagesSent, ...messagesReceived].sort(
|
||||
(a, b) => a.dateCreated.getTime() - b.dateCreated.getTime()
|
||||
);
|
||||
|
||||
await insertMessagesQueue.enqueue(
|
||||
{
|
||||
customerId,
|
||||
messages,
|
||||
},
|
||||
{
|
||||
id: `insert-messages-${customerId}`,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
export default fetchMessagesQueue;
|
@ -1,59 +0,0 @@
|
||||
import { Queue } from "quirrel/blitz";
|
||||
import type { CallInstance } from "twilio/lib/rest/api/v2010/account/call";
|
||||
|
||||
import db, { Direction, CallStatus } from "../../../db";
|
||||
|
||||
type Payload = {
|
||||
customerId: string;
|
||||
calls: CallInstance[];
|
||||
};
|
||||
|
||||
const insertCallsQueue = Queue<Payload>("api/queue/insert-calls", async ({ calls, customerId }) => {
|
||||
const phoneCalls = calls
|
||||
.map((call) => ({
|
||||
customerId,
|
||||
twilioSid: call.sid,
|
||||
from: call.from,
|
||||
to: call.to,
|
||||
direction: translateDirection(call.direction),
|
||||
status: translateStatus(call.status),
|
||||
duration: call.duration,
|
||||
createdAt: new Date(call.dateCreated),
|
||||
}))
|
||||
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
|
||||
|
||||
await db.phoneCall.createMany({ data: phoneCalls });
|
||||
});
|
||||
|
||||
export default insertCallsQueue;
|
||||
|
||||
function translateDirection(direction: CallInstance["direction"]): Direction {
|
||||
switch (direction) {
|
||||
case "inbound":
|
||||
return Direction.Inbound;
|
||||
case "outbound":
|
||||
default:
|
||||
return Direction.Outbound;
|
||||
}
|
||||
}
|
||||
|
||||
function translateStatus(status: CallInstance["status"]): CallStatus {
|
||||
switch (status) {
|
||||
case "busy":
|
||||
return CallStatus.Busy;
|
||||
case "canceled":
|
||||
return CallStatus.Canceled;
|
||||
case "completed":
|
||||
return CallStatus.Completed;
|
||||
case "failed":
|
||||
return CallStatus.Failed;
|
||||
case "in-progress":
|
||||
return CallStatus.InProgress;
|
||||
case "no-answer":
|
||||
return CallStatus.NoAnswer;
|
||||
case "queued":
|
||||
return CallStatus.Queued;
|
||||
case "ringing":
|
||||
return CallStatus.Ringing;
|
||||
}
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
import { Queue } from "quirrel/blitz";
|
||||
import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message";
|
||||
|
||||
import db, { MessageStatus, Direction, Message } from "../../../db";
|
||||
import { encrypt } from "../../../db/_encryption";
|
||||
|
||||
type Payload = {
|
||||
customerId: string;
|
||||
messages: MessageInstance[];
|
||||
};
|
||||
|
||||
const insertMessagesQueue = Queue<Payload>(
|
||||
"api/queue/insert-messages",
|
||||
async ({ messages, customerId }) => {
|
||||
const customer = await db.customer.findFirst({ where: { id: customerId } });
|
||||
const encryptionKey = customer!.encryptionKey;
|
||||
|
||||
const sms = messages
|
||||
.map<Omit<Message, "id">>((message) => ({
|
||||
customerId,
|
||||
content: encrypt(message.body, encryptionKey),
|
||||
from: message.from,
|
||||
to: message.to,
|
||||
status: translateStatus(message.status),
|
||||
direction: translateDirection(message.direction),
|
||||
twilioSid: message.sid,
|
||||
sentAt: new Date(message.dateCreated),
|
||||
}))
|
||||
.sort((a, b) => a.sentAt.getTime() - b.sentAt.getTime());
|
||||
|
||||
await db.message.createMany({ data: sms });
|
||||
}
|
||||
);
|
||||
|
||||
export default insertMessagesQueue;
|
||||
|
||||
function translateDirection(direction: MessageInstance["direction"]): Direction {
|
||||
switch (direction) {
|
||||
case "inbound":
|
||||
return Direction.Inbound;
|
||||
case "outbound-api":
|
||||
case "outbound-call":
|
||||
case "outbound-reply":
|
||||
default:
|
||||
return Direction.Outbound;
|
||||
}
|
||||
}
|
||||
|
||||
function translateStatus(status: MessageInstance["status"]): MessageStatus {
|
||||
switch (status) {
|
||||
case "accepted":
|
||||
return MessageStatus.Accepted;
|
||||
case "canceled":
|
||||
return MessageStatus.Canceled;
|
||||
case "delivered":
|
||||
return MessageStatus.Delivered;
|
||||
case "failed":
|
||||
return MessageStatus.Failed;
|
||||
case "partially_delivered":
|
||||
return MessageStatus.PartiallyDelivered;
|
||||
case "queued":
|
||||
return MessageStatus.Queued;
|
||||
case "read":
|
||||
return MessageStatus.Read;
|
||||
case "received":
|
||||
return MessageStatus.Received;
|
||||
case "receiving":
|
||||
return MessageStatus.Receiving;
|
||||
case "scheduled":
|
||||
return MessageStatus.Scheduled;
|
||||
case "sending":
|
||||
return MessageStatus.Sending;
|
||||
case "sent":
|
||||
return MessageStatus.Sent;
|
||||
case "undelivered":
|
||||
return MessageStatus.Undelivered;
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
import { Queue } from "quirrel/blitz";
|
||||
import twilio from "twilio";
|
||||
|
||||
import db from "../../../db";
|
||||
|
||||
type Payload = {
|
||||
id: string;
|
||||
customerId: string;
|
||||
to: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
const sendMessageQueue = Queue<Payload>(
|
||||
"api/queue/send-message",
|
||||
async ({ id, customerId, to, content }) => {
|
||||
const customer = await db.customer.findFirst({ where: { id: customerId } });
|
||||
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId } });
|
||||
|
||||
try {
|
||||
const message = await twilio(
|
||||
customer!.accountSid!,
|
||||
customer!.authToken!
|
||||
).messages.create({
|
||||
body: content,
|
||||
to,
|
||||
from: phoneNumber!.phoneNumber,
|
||||
});
|
||||
await db.message.update({
|
||||
where: { id },
|
||||
data: { twilioSid: message.sid },
|
||||
});
|
||||
} catch (error) {
|
||||
// TODO: handle twilio error
|
||||
console.log(error.code); // 21211
|
||||
console.log(error.moreInfo); // https://www.twilio.com/docs/errors/21211
|
||||
}
|
||||
},
|
||||
{
|
||||
retry: ["1min"],
|
||||
}
|
||||
);
|
||||
|
||||
export default sendMessageQueue;
|
@ -1,43 +0,0 @@
|
||||
import { Queue } from "quirrel/blitz";
|
||||
import twilio from "twilio";
|
||||
|
||||
import db from "../../../db";
|
||||
|
||||
type Payload = {
|
||||
customerId: string;
|
||||
};
|
||||
|
||||
const setTwilioWebhooks = Queue<Payload>(
|
||||
"api/queue/set-twilio-webhooks",
|
||||
async ({ customerId }) => {
|
||||
const customer = await db.customer.findFirst({ where: { id: customerId } });
|
||||
const twimlApp = customer!.twimlAppSid
|
||||
? await twilio(customer!.accountSid!, customer!.authToken!)
|
||||
.applications.get(customer!.twimlAppSid)
|
||||
.fetch()
|
||||
: await twilio(customer!.accountSid!, customer!.authToken!).applications.create({
|
||||
friendlyName: "Virtual Phone",
|
||||
smsUrl: "https://phone.mokhtar.dev/api/webhook/incoming-message",
|
||||
smsMethod: "POST",
|
||||
voiceUrl: "https://phone.mokhtar.dev/api/webhook/incoming-call",
|
||||
voiceMethod: "POST",
|
||||
});
|
||||
const twimlAppSid = twimlApp.sid;
|
||||
const phoneNumber = await db.phoneNumber.findFirst({ where: { customerId } });
|
||||
|
||||
await Promise.all([
|
||||
db.customer.update({
|
||||
where: { id: customerId },
|
||||
data: { twimlAppSid },
|
||||
}),
|
||||
twilio(customer!.accountSid!, customer!.authToken!)
|
||||
.incomingPhoneNumbers.get(phoneNumber!.phoneNumberSid)
|
||||
.update({
|
||||
smsApplicationSid: twimlAppSid,
|
||||
voiceApplicationSid: twimlAppSid,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
export default setTwilioWebhooks;
|
Reference in New Issue
Block a user