Signal API updates

This commit is contained in:
Darren Clarke 2024-06-05 15:12:48 +02:00
parent 83653ef23b
commit c729a46a0c
25 changed files with 501 additions and 279 deletions

View file

@ -11,10 +11,8 @@ export async function up(db: Kysely<any>): Promise<void> {
.addColumn("user_id", "uuid")
.addColumn("name", "text")
.addColumn("description", "text")
.addColumn("auth_info", "text")
.addColumn("is_verified", "boolean", (col) =>
col.notNull().defaultTo(false),
)
.addColumn("qr_code", "text")
.addColumn("verified", "boolean", (col) => col.notNull().defaultTo(false))
.addColumn("created_at", "timestamptz", (col) =>
col.notNull().defaultTo(sql`now()`),
)

View file

@ -107,7 +107,7 @@ export default class WhatsappService extends Service {
} else if (connectionState === "open") {
console.log("opened connection");
} else if (connectionState === "close") {
console.log("connection closed due to ", lastDisconnect.error);
console.log("connection closed due to ", lastDisconnect?.error);
const disconnectStatusCode = (lastDisconnect?.error as any)?.output
?.statusCode;
@ -182,12 +182,12 @@ export default class WhatsappService extends Service {
const messageContent = Object.values(message)[0];
let messageType: MediaType;
let attachment: string;
let filename: string;
let mimetype: string;
let filename: string | null | undefined;
let mimetype: string | null | undefined;
if (isMediaMessage) {
if (audioMessage) {
messageType = "audio";
filename = id + "." + audioMessage.mimetype.split("/").pop();
filename = id + "." + audioMessage.mimetype?.split("/").pop();
mimetype = audioMessage.mimetype;
} else if (documentMessage) {
messageType = "document";
@ -195,16 +195,17 @@ export default class WhatsappService extends Service {
mimetype = documentMessage.mimetype;
} else if (imageMessage) {
messageType = "image";
filename = id + "." + imageMessage.mimetype.split("/").pop();
filename = id + "." + imageMessage.mimetype?.split("/").pop();
mimetype = imageMessage.mimetype;
} else if (videoMessage) {
messageType = "video";
filename = id + "." + videoMessage.mimetype.split("/").pop();
filename = id + "." + videoMessage.mimetype?.split("/").pop();
mimetype = videoMessage.mimetype;
}
const stream = await downloadContentFromMessage(
messageContent,
// @ts-ignore
messageType,
);
let buffer = Buffer.from([]);
@ -214,11 +215,13 @@ export default class WhatsappService extends Service {
attachment = buffer.toString("base64");
}
// @ts-ignore
if (messageContent || attachment) {
const receivedMessage = {
waMessageId: id,
waMessage: JSON.stringify(webMessageInfo),
waTimestamp: new Date((messageTimestamp as number) * 1000),
// @ts-ignore
attachment,
filename,
mimetype,

View file

@ -1,5 +1,5 @@
{
"extends": "@link-stack/typescript-config",
"extends": "@link-stack/typescript-config/tsconfig.node.json",
"compilerOptions": {
"module": "commonjs",
"target": "es2018",

View file

@ -6,6 +6,8 @@ const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const startWorker = async () => {
console.log("Starting worker...");
console.log(process.env);
await run({
connectionString: process.env.DATABASE_URL,
concurrency: 10,

View file

@ -7,7 +7,7 @@
"license": "AGPL-3.0-or-later",
"scripts": {
"build": "tsc -p tsconfig.json",
"dev": "dotenv -- graphile-worker",
"dev": "graphile-worker",
"start": "node build/main/index.js"
},
"dependencies": {

View file

@ -1,4 +1,5 @@
/* eslint-disable camelcase */
/*
import { convert } from "html-to-text";
import { URLSearchParams } from "url";
import { withDb, AppDatabase } from "../../lib/db.js";
@ -181,8 +182,10 @@ const sendToLabelStudio = async (tickets: FormattedZammadTicket[]) => {
console.log(JSON.stringify(importResult, undefined, 2));
}
};
*/
const importLabelStudioTask = async (): Promise<void> => {
/*
withDb(async (db: AppDatabase) => {
const {
leafcutter: { contributorName },
@ -204,6 +207,7 @@ const importLabelStudioTask = async (): Promise<void> => {
});
}
});
*/
};
export default importLabelStudioTask;

View file

@ -1,4 +1,5 @@
/* eslint-disable camelcase */
/*
import { URLSearchParams } from "url";
import { withDb, AppDatabase } from "../../lib/db.js";
// import { loadConfig } from "@digiresilience/bridge-config";
@ -143,8 +144,9 @@ const sendToLeafcutter = async (tickets: LabelStudioTicket[]) => {
});
console.log({ result });
};
*/
const importLeafcutterTask = async (): Promise<void> => {
/*
withDb(async (db: AppDatabase) => {
const {
leafcutter: { contributorName },
@ -169,6 +171,7 @@ const importLeafcutterTask = async (): Promise<void> => {
minUpdatedTimestamp: newLastTimestamp,
});
});
*/
};
export default importLeafcutterTask;

View file

@ -0,0 +1,32 @@
import { db, getWorkerUtils } from "@link-stack/bridge-common";
import * as signalApi from "@link-stack/signal-api";
const { Configuration, MessagesApi } = signalApi;
const fetchSignalMessagesTask = async (): Promise<void> => {
const worker = await getWorkerUtils();
const rows = await db.selectFrom("SignalBot").selectAll().execute();
const config = new Configuration({
basePath: process.env.BRIDGE_SIGNAL_URL,
});
const messagesClient = new MessagesApi(config);
for (const row of rows) {
const { id, phoneNumber: number } = row;
const messages = await messagesClient.v1ReceiveNumberGet({ number });
for (const msg of messages) {
const { envelope } = msg as any;
const { source } = envelope;
const message = envelope?.dataMessage?.message;
if (source !== number && message) {
await worker.addJob("signal/receive-signal-message", {
token: id,
sender: source,
message,
});
}
}
}
};
export default fetchSignalMessagesTask;

View file

@ -1,11 +1,33 @@
// import { db, getWorkerUtils } from "@link-stack/bridge-common";
import { db, getWorkerUtils } from "@link-stack/bridge-common";
interface ReceiveSignalMessageTaskOptions {
message: any;
token: string;
sender: string;
message: string;
}
const receiveSignalMessageTask = async ({
token,
sender,
message,
}: ReceiveSignalMessageTaskOptions): Promise<void> => {};
}: ReceiveSignalMessageTaskOptions): Promise<void> => {
console.log({ token, sender, message });
const worker = await getWorkerUtils();
const row = await db
.selectFrom("SignalBot")
.selectAll()
.where("id", "=", token)
.executeTakeFirstOrThrow();
console.log(row);
const backendId = row.id;
const payload = {
message,
recipient: sender,
};
await worker.addJob("common/notify-webhooks", { backendId, payload });
};
export default receiveSignalMessageTask;

View file

@ -1,11 +1,38 @@
// import { db, getWorkerUtils } from "@link-stack/bridge-common";
import { db } from "@link-stack/bridge-common";
import * as signalApi from "@link-stack/signal-api";
const { Configuration, MessagesApi } = signalApi;
interface SendSignalMessageTaskOptions {
token: string;
recipient: string;
message: any;
}
const sendSignalMessageTask = async ({
message,
}: SendSignalMessageTaskOptions): Promise<void> => {};
recipient,
token,
}: SendSignalMessageTaskOptions): Promise<void> => {
const bot = await db
.selectFrom("SignalBot")
.selectAll()
.where("token", "=", token)
.executeTakeFirstOrThrow();
const { phoneNumber: number } = bot;
const config = new Configuration({
basePath: process.env.BRIDGE_SIGNAL_URL,
});
const messagesClient = new MessagesApi(config);
const response = await messagesClient.v2SendPost({
data: {
number,
recipients: [recipient],
message,
},
});
console.log({ response });
};
export default sendSignalMessageTask;

View file

@ -61,7 +61,7 @@ const notifyWebhooks = async (
return;
}
webhooks.forEach(({ id }) => {
webhooks.forEach(({ id }: any) => {
const payload = formatPayload(messageInfo);
// logger.debug(
// { payload },

View file

@ -50,7 +50,7 @@ const notifyWebhooks = async (
const webhooks = await db.webhooks.findAllByBackendId("voice", voiceLineId);
if (webhooks && webhooks.length === 0) return;
webhooks.forEach(({ id }) => {
webhooks.forEach(({ id }: any) => {
const payload = formatPayload(call, recording);
workerUtils.addJob(
"notify-webhook",

View file

@ -1,22 +1,7 @@
{
"extends": "@link-stack/typescript-config",
"extends": "@link-stack/typescript-config/tsconfig.json",
"compilerOptions": {
"outDir": "build/main",
"module": "esnext",
"target": "esnext",
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "node"
},
"ts-node": {
"esm": true,
"experimentalSpecifierResolution": "node",
"transpileOnly": true,
"compilerOptions": {
"module": "esNext",
"target": "esNext",
"moduleResolution": "node"
}
"outDir": "build/main"
},
"include": ["**/*.ts", "**/.*.ts"],
"exclude": ["node_modules", "build"]