Organize directories

This commit is contained in:
Darren Clarke 2023-02-13 13:10:48 +00:00
parent 8a91c9b89b
commit 4898382f78
433 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,9 @@
export * from "./settings";
export * from "./signal/bots";
export * from "./whatsapp/bots";
export * from "./whatsapp/messages";
export * from "./whatsapp/attachments";
export * from "./settings";
export * from "./voice/voice-line";
export * from "./voice/voice-provider";
export * from "./webhooks";

View file

@ -0,0 +1,104 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-unused-vars,@typescript-eslint/no-explicit-any,prefer-destructuring */
import { RepositoryBase, recordInfo, UUID, Flavor } from "common";
export type SettingId = Flavor<UUID, "Setting Id">;
export interface UnsavedSetting<T> {
name: string;
value: T;
}
export interface SavedSetting<T> extends UnsavedSetting<T> {
id: SettingId;
createdAt: Date;
updatedAt: Date;
}
export const SettingRecord = recordInfo<UnsavedSetting<any>, SavedSetting<any>>(
"app_public",
"settings"
);
export class SettingRecordRepository extends RepositoryBase(SettingRecord) {
async findByName<T>(name: string): Promise<SavedSetting<T> | null> {
return this.db.oneOrNone("SELECT * FROM $1 $2:raw LIMIT 1", [
this.schemaTable,
this.where({ name }),
]);
}
async upsert<T>(name: string, value: T): Promise<SavedSetting<T>> {
return this.db.one(
`INSERT INTO $1 ($2:name) VALUES ($2:csv)
ON CONFLICT (name)
DO UPDATE SET value = EXCLUDED.value RETURNING *`,
[this.schemaTable, this.columnize({ name, value })]
);
}
}
// these helpers let us create type safe setting constants
export interface SettingType<T = any> {
_type: T;
}
export interface SettingInfo<T = any> extends SettingType<T> {
name: string;
}
export function castToSettingInfo(
runtimeData: Omit<SettingInfo, "_type">
): SettingInfo {
return runtimeData as SettingInfo;
}
export function settingInfo<T>(name: string): SettingInfo<T>;
// don't use this signature, use the explicit typed signature
export function settingInfo(name: string) {
return castToSettingInfo({
name,
});
}
export interface ISettingsService {
name: string;
lookup<T>(settingInfo: SettingInfo<T>): Promise<T>;
save<T>(settingInfo: SettingInfo<T>, value: T): Promise<T>;
}
export const SettingsService = (
repo: SettingRecordRepository
): ISettingsService => ({
name: "settingService",
lookup: async <T>(settingInfo: SettingInfo<T>): Promise<T> => {
const s = await repo.findByName<T>(settingInfo.name);
return s.value;
},
save: async <T>(settingInfo: SettingInfo<T>, value: T): Promise<T> => {
const s = await repo.upsert(settingInfo.name, value);
return s.value;
},
});
const _test = async () => {
// here is an example of how to use this module
// it also serves as a compile-time test case
const repo = new SettingRecordRepository({} as any);
// create your own custom setting types!
// the value is serialized as json in the database
type Custom = { foo: string; bar: string };
type CustomUnsavedSetting = UnsavedSetting<Custom>;
type CustomSetting = SavedSetting<Custom>;
const s3: CustomSetting = await repo.findByName("test");
const customValue = { foo: "monkeys", bar: "eggplants" };
let customSetting = { name: "custom", value: customValue };
customSetting = await repo.insert(customSetting);
const value: Custom = customSetting.value;
const MySetting = settingInfo<string>("my-setting");
};

View file

@ -0,0 +1,35 @@
import { RepositoryBase, recordInfo, UUID, Flavor } from "common";
export type SignalBotId = Flavor<UUID, "Signal Bot Id">;
export interface UnsavedSignalBot {
phoneNumber: string;
userId: string;
description: string;
}
export interface SavedSignalBot extends UnsavedSignalBot {
id: SignalBotId;
createdAt: Date;
updatedAt: Date;
token: string;
authInfo: string;
isVerified: boolean;
}
export const SignalBotRecord = recordInfo<UnsavedSignalBot, SavedSignalBot>(
"app_public",
"signal_bots"
);
export class SignalBotRecordRepository extends RepositoryBase(SignalBotRecord) {
async updateAuthInfo(
bot: SavedSignalBot,
authInfo: string | undefined
): Promise<SavedSignalBot> {
return this.db.one(
"UPDATE $1 SET (auth_info, is_verified) = ROW($2, true) WHERE id = $3 RETURNING *",
[this.schemaTable, authInfo, bot.id]
);
}
}

View file

@ -0,0 +1,62 @@
import {
RepositoryBase,
recordInfo,
UUID,
Flavor,
} from "common";
import type { } from "pg-promise";
export type VoiceLineId = Flavor<UUID, "VoiceLine Id">;
export type VoiceLineAudio = {
"audio/webm": string;
"audio/mpeg"?: string;
checksum?: string;
};
export interface UnsavedVoiceLine {
providerId: string;
providerLineSid: string;
number: string;
language: string;
voice: string;
promptText?: string;
promptAudio?: VoiceLineAudio;
audioPromptEnabled: boolean;
audioConvertedAt?: Date;
}
export interface SavedVoiceLine extends UnsavedVoiceLine {
id: VoiceLineId;
createdAt: Date;
updatedAt: Date;
}
export const VoiceLineRecord = recordInfo<UnsavedVoiceLine, SavedVoiceLine>(
"app_public",
"voice_lines"
);
export class VoiceLineRecordRepository extends RepositoryBase(VoiceLineRecord) {
/**
* Fetch all voice lines given the numbers
* @param numbers
*/
async findAllByNumbers(numbers: string[]): Promise<SavedVoiceLine[]> {
return this.db.any(
"SELECT id,provider_id,provider_line_sid,number FROM $1 WHERE number in ($2:csv)",
[this.schemaTable, numbers]
);
}
/**
* Fetch all voice lines given a list of provider line ids
* @param ids
*/
async findAllByProviderLineSids(ids: string[]): Promise<SavedVoiceLine[]> {
return this.db.any(
"SELECT id,provider_id,provider_line_sid,number FROM $1 WHERE provider_line_sid in ($2:csv)",
[this.schemaTable, ids]
);
}
}

View file

@ -0,0 +1,52 @@
import { RepositoryBase, recordInfo, UUID, Flavor } from "common";
/*
* VoiceProvider
*
* A provider is a company that provides incoming voice call services
*/
export type VoiceProviderId = Flavor<UUID, "VoiceProvider Id">;
export enum VoiceProviderKinds {
TWILIO = "TWILIO",
}
export type TwilioCredentials = {
accountSid: string;
apiKeySid: string;
apiKeySecret: string;
};
// expand this type later when we support more providers
export type VoiceProviderCredentials = TwilioCredentials;
export interface UnsavedVoiceProvider {
kind: VoiceProviderKinds;
name: string;
credentials: VoiceProviderCredentials;
}
export interface SavedVoiceProvider extends UnsavedVoiceProvider {
id: VoiceProviderId;
createdAt: Date;
updatedAt: Date;
}
export const VoiceProviderRecord = recordInfo<
UnsavedVoiceProvider,
SavedVoiceProvider
>("app_public", "voice_providers");
export class VoiceProviderRecordRepository extends RepositoryBase(
VoiceProviderRecord
) {
async findByTwilioAccountSid(
accountSid: string
): Promise<SavedVoiceProvider | null> {
return this.db.oneOrNone(
"select * from $1 where credentials->>'accountSid' = $2",
[this.schemaTable, accountSid]
);
}
}

View file

@ -0,0 +1,50 @@
import {
RepositoryBase,
recordInfo,
UUID,
Flavor,
} from "common";
/*
* Webhook
*
* A webhook allows external services to be notified when a recorded call is available
*/
export type WebhookId = Flavor<UUID, "Webhook Id">;
export interface HttpHeaders {
header: string;
value: string;
}
export interface UnsavedWebhook {
name: string;
voiceLineId: string;
endpointUrl: string;
httpMethod: "post" | "put";
headers?: HttpHeaders[];
}
export interface SavedWebhook extends UnsavedWebhook {
id: WebhookId;
createdAt: Date;
updatedAt: Date;
}
export const WebhookRecord = recordInfo<UnsavedWebhook, SavedWebhook>(
"app_public",
"webhooks"
);
export class WebhookRecordRepository extends RepositoryBase(WebhookRecord) {
async findAllByBackendId(
backendType: string,
backendId: string
): Promise<SavedWebhook[]> {
return this.db.any(
"select * from $1 where backend_type = $2 and backend_id = $3",
[this.schemaTable, backendType, backendId]
);
}
}

View file

@ -0,0 +1,24 @@
import { RepositoryBase, recordInfo, UUID, Flavor } from "common";
export type WhatsappAttachmentId = Flavor<UUID, "Whatsapp Attachment Id">;
export interface UnsavedWhatsappAttachment {
whatsappBotId: string;
whatsappMessageId: string;
attachment: Buffer;
}
export interface SavedWhatsappAttachment extends UnsavedWhatsappAttachment {
id: WhatsappAttachmentId;
createdAt: Date;
updatedAt: Date;
}
export const WhatsappAttachmentRecord = recordInfo<
UnsavedWhatsappAttachment,
SavedWhatsappAttachment
>("app_public", "whatsapp_attachments");
export class WhatsappAttachmentRecordRepository extends RepositoryBase(
WhatsappAttachmentRecord
) { }

View file

@ -0,0 +1,48 @@
import { RepositoryBase, recordInfo, UUID, Flavor } from "common";
export type WhatsappBotId = Flavor<UUID, "Whatsapp Bot Id">;
export interface UnsavedWhatsappBot {
phoneNumber: string;
userId: string;
description: string;
}
export interface SavedWhatsappBot extends UnsavedWhatsappBot {
id: WhatsappBotId;
createdAt: Date;
updatedAt: Date;
token: string;
authInfo: string;
qrCode: string;
isVerified: boolean;
}
export const WhatsappBotRecord = recordInfo<
UnsavedWhatsappBot,
SavedWhatsappBot
>("app_public", "whatsapp_bots");
export class WhatsappBotRecordRepository extends RepositoryBase(
WhatsappBotRecord
) {
async updateQR(
bot: SavedWhatsappBot,
qrCode: string | undefined
): Promise<SavedWhatsappBot> {
return this.db.one(
"UPDATE $1 SET (qr_code) = ROW($2) WHERE id = $3 RETURNING *",
[this.schemaTable, qrCode, bot.id]
);
}
async updateAuthInfo(
bot: SavedWhatsappBot,
authInfo: string | undefined
): Promise<SavedWhatsappBot> {
return this.db.one(
"UPDATE $1 SET (auth_info, is_verified) = ROW($2, true) WHERE id = $3 RETURNING *",
[this.schemaTable, authInfo, bot.id]
);
}
}

View file

@ -0,0 +1,26 @@
import { RepositoryBase, recordInfo, UUID, Flavor } from "common";
export type WhatsappMessageId = Flavor<UUID, "Whatsapp Message Id">;
export interface UnsavedWhatsappMessage {
whatsappBotId: string;
waMessageId: string;
waTimestamp: Date;
waMessage: string;
attachments?: string[];
}
export interface SavedWhatsappMessage extends UnsavedWhatsappMessage {
id: WhatsappMessageId;
createdAt: Date;
updatedAt: Date;
}
export const WhatsappMessageRecord = recordInfo<
UnsavedWhatsappMessage,
SavedWhatsappMessage
>("app_public", "whatsapp_messages");
export class WhatsappMessageRecordRepository extends RepositoryBase(
WhatsappMessageRecord
) { }