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("user_id", "uuid")
.addColumn("name", "text") .addColumn("name", "text")
.addColumn("description", "text") .addColumn("description", "text")
.addColumn("auth_info", "text") .addColumn("qr_code", "text")
.addColumn("is_verified", "boolean", (col) => .addColumn("verified", "boolean", (col) => col.notNull().defaultTo(false))
col.notNull().defaultTo(false),
)
.addColumn("created_at", "timestamptz", (col) => .addColumn("created_at", "timestamptz", (col) =>
col.notNull().defaultTo(sql`now()`), col.notNull().defaultTo(sql`now()`),
) )

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,22 +1,7 @@
{ {
"extends": "@link-stack/typescript-config", "extends": "@link-stack/typescript-config/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"outDir": "build/main", "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"
}
}, },
"include": ["**/*.ts", "**/.*.ts"], "include": ["**/*.ts", "**/.*.ts"],
"exclude": ["node_modules", "build"] "exclude": ["node_modules", "build"]

View file

@ -109,7 +109,9 @@ export interface Database {
name: string; name: string;
description: string; description: string;
phoneNumber: string; phoneNumber: string;
createdBy: string; qrCode: string;
token: string;
verified: boolean;
createdAt: Date; createdAt: Date;
updatedAt: Date; updatedAt: Date;
}; };

View file

@ -82,7 +82,7 @@ export const Detail: FC<DetailProps> = ({ service, row }) => {
<QRCode <QRCode
name={field.name} name={field.name}
label={field.label} label={field.label}
getValue={field.getValue} getValue={field.getQRCode}
refreshInterval={field.refreshInterval} refreshInterval={field.refreshInterval}
token={token} token={token}
verified={row.verified as boolean} verified={row.verified as boolean}

View file

@ -9,7 +9,7 @@ type QRCodeProps = {
token: string; token: string;
verified: boolean; verified: boolean;
helperText?: string; helperText?: string;
getValue?: (id: string) => Promise<string>; getValue?: (id: string) => Promise<Record<string, string>>;
refreshInterval?: number; refreshInterval?: number;
}; };
@ -23,13 +23,16 @@ export const QRCode: FC<QRCodeProps> = ({
refreshInterval, refreshInterval,
}) => { }) => {
const [value, setValue] = useState(""); const [value, setValue] = useState("");
const [kind, setKind] = useState("data");
const { white } = colors; const { white } = colors;
useEffect(() => { useEffect(() => {
if (!verified && getValue && refreshInterval) { if (!verified && getValue && refreshInterval) {
const interval = setInterval(async () => { const interval = setInterval(async () => {
const result = await getValue(token); const { qr, kind } = await getValue(token);
setValue(result); console.log({ kind });
setValue(qr);
setKind(kind);
}, refreshInterval * 1000); }, refreshInterval * 1000);
return () => clearInterval(interval); return () => clearInterval(interval);
} }
@ -37,7 +40,11 @@ export const QRCode: FC<QRCodeProps> = ({
return !verified ? ( return !verified ? (
<Box sx={{ backgroundColor: white, m: 2 }}> <Box sx={{ backgroundColor: white, m: 2 }}>
{kind === "data" ? (
<QRCodeInternal value={value} /> <QRCodeInternal value={value} />
) : (
<img src={value} alt={name} />
)}
<Box>{helperText}</Box> <Box>{helperText}</Box>
</Box> </Box>
) : null; ) : null;

View file

@ -1,5 +1,13 @@
import { ServiceConfig } from "../lib/service"; import { ServiceConfig } from "../lib/service";
const getQRCode = async (token: string): Promise<Record<string, string>> => {
const url = `/api/signal/bots/${token}`;
const result = await fetch(url, { cache: "no-store" });
const { qr } = await result.json();
return { qr, kind: "image" };
};
export const signalConfig: ServiceConfig = { export const signalConfig: ServiceConfig = {
entity: "signal", entity: "signal",
table: "SignalBot", table: "SignalBot",
@ -59,6 +67,15 @@ export const signalConfig: ServiceConfig = {
label: "Token", label: "Token",
copyable: true, copyable: true,
}, },
{
name: "qrcode",
label: "QR Code",
kind: "qrcode",
size: 4,
getQRCode,
helperText: "Go to link devices in the app, then scan the code",
refreshInterval: 15,
},
], ],
listColumns: [ listColumns: [
{ {

View file

@ -6,7 +6,7 @@ const getQRCode = async (token: string) => {
const result = await fetch(url, { cache: "no-store" }); const result = await fetch(url, { cache: "no-store" });
const { qr } = await result.json(); const { qr } = await result.json();
return qr ?? ""; return { qr, kind: "data" };
}; };
export const whatsappConfig: ServiceConfig = { export const whatsappConfig: ServiceConfig = {
@ -73,8 +73,8 @@ export const whatsappConfig: ServiceConfig = {
label: "QR Code", label: "QR Code",
kind: "qrcode", kind: "qrcode",
size: 4, size: 4,
getValue: getQRCode, getQRCode,
helperText: "Go ahead, scan it", helperText: "Go to link devices in the app, then scan the code",
refreshInterval: 15, refreshInterval: 15,
}, },
], ],

View file

@ -24,6 +24,7 @@ export type FieldDescription = {
label: string; label: string;
kind?: "text" | "phone" | "select" | "multi" | "qrcode"; kind?: "text" | "phone" | "select" | "multi" | "qrcode";
getValue?: (token: string) => Promise<string>; getValue?: (token: string) => Promise<string>;
getQRCode?: (token: string) => Promise<Record<string, string>>;
refreshInterval?: number; refreshInterval?: number;
getOptions?: (formState: any) => Promise<SelectOption[]>; getOptions?: (formState: any) => Promise<SelectOption[]>;
autogenerated?: "token"; autogenerated?: "token";

View file

@ -1,3 +1,45 @@
import { Service } from "./service"; import { NextResponse } from "next/server";
import { db } from "@link-stack/bridge-common";
import { Configuration, DevicesApi } from "@link-stack/signal-api";
// import { revalidatePath } from "next/cache";
import { Service, ServiceParams } from "./service";
export class Signal extends Service {} const fetchNoCache = async (url: string, options = {}) => {
// @ts-ignore
options.cache = options.cache || "no-store";
return fetch(url, options);
};
export class Signal extends Service {
async getBot({ params: { token } }: ServiceParams) {
const row = await db
.selectFrom("SignalBot")
.selectAll()
.where("token", "=", token as string)
.executeTakeFirstOrThrow();
const { name } = row;
if (!row.verified) {
const config = new Configuration({
basePath: process.env.BRIDGE_SIGNAL_URL,
fetchApi: fetchNoCache,
});
const devicesClient = new DevicesApi(config);
const blob: Blob = await devicesClient.v1QrcodelinkGet({
deviceName: name.replaceAll(" ", "_"),
});
const arrayBuffer = await blob.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const qrString = buffer.toString("base64");
const qr = `data:${blob.type};base64,${qrString}`;
const finalRow = {
...row,
qr,
};
return NextResponse.json(finalRow);
} else {
return NextResponse.json(row);
}
}
}

View file

@ -12,15 +12,14 @@
* Do not edit the class manually. * Do not edit the class manually.
*/ */
import * as runtime from "../runtime";
import * as runtime from '../runtime';
import type { import type {
ApiAddDeviceRequest, ApiAddDeviceRequest,
ApiError, ApiError,
ApiRegisterNumberRequest, ApiRegisterNumberRequest,
ApiUnregisterNumberRequest, ApiUnregisterNumberRequest,
ApiVerifyNumberSettings, ApiVerifyNumberSettings,
} from '../models/index'; } from "../models/index";
import { import {
ApiAddDeviceRequestFromJSON, ApiAddDeviceRequestFromJSON,
ApiAddDeviceRequestToJSON, ApiAddDeviceRequestToJSON,
@ -32,7 +31,7 @@ import {
ApiUnregisterNumberRequestToJSON, ApiUnregisterNumberRequestToJSON,
ApiVerifyNumberSettingsFromJSON, ApiVerifyNumberSettingsFromJSON,
ApiVerifyNumberSettingsToJSON, ApiVerifyNumberSettingsToJSON,
} from '../models/index'; } from "../models/index";
export interface V1DevicesNumberPostRequest { export interface V1DevicesNumberPostRequest {
number: string; number: string;
@ -64,23 +63,25 @@ export interface V1UnregisterNumberPostRequest {
* *
*/ */
export class DevicesApi extends runtime.BaseAPI { export class DevicesApi extends runtime.BaseAPI {
/** /**
* Links another device to this device. Only works, if this is the master device. * Links another device to this device. Only works, if this is the master device.
* Links another device to this device. * Links another device to this device.
*/ */
async v1DevicesNumberPostRaw(requestParameters: V1DevicesNumberPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> { async v1DevicesNumberPostRaw(
if (requestParameters['number'] == null) { requestParameters: V1DevicesNumberPostRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<runtime.ApiResponse<void>> {
if (requestParameters["number"] == null) {
throw new runtime.RequiredError( throw new runtime.RequiredError(
'number', "number",
'Required parameter "number" was null or undefined when calling v1DevicesNumberPost().' 'Required parameter "number" was null or undefined when calling v1DevicesNumberPost().',
); );
} }
if (requestParameters['data'] == null) { if (requestParameters["data"] == null) {
throw new runtime.RequiredError( throw new runtime.RequiredError(
'data', "data",
'Required parameter "data" was null or undefined when calling v1DevicesNumberPost().' 'Required parameter "data" was null or undefined when calling v1DevicesNumberPost().',
); );
} }
@ -88,15 +89,21 @@ export class DevicesApi extends runtime.BaseAPI {
const headerParameters: runtime.HTTPHeaders = {}; const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json'; headerParameters["Content-Type"] = "application/json";
const response = await this.request({ const response = await this.request(
path: `/v1/devices/{number}`.replace(`{${"number"}}`, encodeURIComponent(String(requestParameters['number']))), {
method: 'POST', path: `/v1/devices/{number}`.replace(
`{${"number"}}`,
encodeURIComponent(String(requestParameters["number"])),
),
method: "POST",
headers: headerParameters, headers: headerParameters,
query: queryParameters, query: queryParameters,
body: ApiAddDeviceRequestToJSON(requestParameters['data']), body: ApiAddDeviceRequestToJSON(requestParameters["data"]),
}, initOverrides); },
initOverrides,
);
return new runtime.VoidApiResponse(response); return new runtime.VoidApiResponse(response);
} }
@ -105,7 +112,10 @@ export class DevicesApi extends runtime.BaseAPI {
* Links another device to this device. Only works, if this is the master device. * Links another device to this device. Only works, if this is the master device.
* Links another device to this device. * Links another device to this device.
*/ */
async v1DevicesNumberPost(requestParameters: V1DevicesNumberPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> { async v1DevicesNumberPost(
requestParameters: V1DevicesNumberPostRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<void> {
await this.v1DevicesNumberPostRaw(requestParameters, initOverrides); await this.v1DevicesNumberPostRaw(requestParameters, initOverrides);
} }
@ -113,46 +123,54 @@ export class DevicesApi extends runtime.BaseAPI {
* Link device and generate QR code * Link device and generate QR code
* Link device and generate QR code. * Link device and generate QR code.
*/ */
async v1QrcodelinkGetRaw(requestParameters: V1QrcodelinkGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<string>> { async v1QrcodelinkGetRaw(
if (requestParameters['deviceName'] == null) { requestParameters: V1QrcodelinkGetRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<runtime.ApiResponse<Blob>> {
if (requestParameters["deviceName"] == null) {
throw new runtime.RequiredError( throw new runtime.RequiredError(
'deviceName', "deviceName",
'Required parameter "deviceName" was null or undefined when calling v1QrcodelinkGet().' 'Required parameter "deviceName" was null or undefined when calling v1QrcodelinkGet().',
); );
} }
const queryParameters: any = {}; const queryParameters: any = {};
if (requestParameters['deviceName'] != null) { if (requestParameters["deviceName"] != null) {
queryParameters['device_name'] = requestParameters['deviceName']; queryParameters["device_name"] = requestParameters["deviceName"];
} }
if (requestParameters['qrcodeVersion'] != null) { if (requestParameters["qrcodeVersion"] != null) {
queryParameters['qrcode_version'] = requestParameters['qrcodeVersion']; queryParameters["qrcode_version"] = requestParameters["qrcodeVersion"];
} }
const headerParameters: runtime.HTTPHeaders = {}; const headerParameters: runtime.HTTPHeaders = {};
const response = await this.request({ const response = await this.request(
{
path: `/v1/qrcodelink`, path: `/v1/qrcodelink`,
method: 'GET', method: "GET",
headers: headerParameters, headers: headerParameters,
query: queryParameters, query: queryParameters,
}, initOverrides); },
initOverrides,
);
if (this.isJsonMime(response.headers.get('content-type'))) { return new runtime.BlobApiResponse(response) as any;
return new runtime.JSONApiResponse<string>(response);
} else {
return new runtime.TextApiResponse(response) as any;
}
} }
/** /**
* Link device and generate QR code * Link device and generate QR code
* Link device and generate QR code. * Link device and generate QR code.
*/ */
async v1QrcodelinkGet(requestParameters: V1QrcodelinkGetRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<string> { async v1QrcodelinkGet(
const response = await this.v1QrcodelinkGetRaw(requestParameters, initOverrides); requestParameters: V1QrcodelinkGetRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<Blob> {
const response = await this.v1QrcodelinkGetRaw(
requestParameters,
initOverrides,
);
return await response.value(); return await response.value();
} }
@ -160,11 +178,14 @@ export class DevicesApi extends runtime.BaseAPI {
* Register a phone number with the signal network. * Register a phone number with the signal network.
* Register a phone number. * Register a phone number.
*/ */
async v1RegisterNumberPostRaw(requestParameters: V1RegisterNumberPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> { async v1RegisterNumberPostRaw(
if (requestParameters['number'] == null) { requestParameters: V1RegisterNumberPostRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<runtime.ApiResponse<void>> {
if (requestParameters["number"] == null) {
throw new runtime.RequiredError( throw new runtime.RequiredError(
'number', "number",
'Required parameter "number" was null or undefined when calling v1RegisterNumberPost().' 'Required parameter "number" was null or undefined when calling v1RegisterNumberPost().',
); );
} }
@ -172,15 +193,21 @@ export class DevicesApi extends runtime.BaseAPI {
const headerParameters: runtime.HTTPHeaders = {}; const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json'; headerParameters["Content-Type"] = "application/json";
const response = await this.request({ const response = await this.request(
path: `/v1/register/{number}`.replace(`{${"number"}}`, encodeURIComponent(String(requestParameters['number']))), {
method: 'POST', path: `/v1/register/{number}`.replace(
`{${"number"}}`,
encodeURIComponent(String(requestParameters["number"])),
),
method: "POST",
headers: headerParameters, headers: headerParameters,
query: queryParameters, query: queryParameters,
body: ApiRegisterNumberRequestToJSON(requestParameters['data']), body: ApiRegisterNumberRequestToJSON(requestParameters["data"]),
}, initOverrides); },
initOverrides,
);
return new runtime.VoidApiResponse(response); return new runtime.VoidApiResponse(response);
} }
@ -189,7 +216,10 @@ export class DevicesApi extends runtime.BaseAPI {
* Register a phone number with the signal network. * Register a phone number with the signal network.
* Register a phone number. * Register a phone number.
*/ */
async v1RegisterNumberPost(requestParameters: V1RegisterNumberPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> { async v1RegisterNumberPost(
requestParameters: V1RegisterNumberPostRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<void> {
await this.v1RegisterNumberPostRaw(requestParameters, initOverrides); await this.v1RegisterNumberPostRaw(requestParameters, initOverrides);
} }
@ -197,18 +227,21 @@ export class DevicesApi extends runtime.BaseAPI {
* Verify a registered phone number with the signal network. * Verify a registered phone number with the signal network.
* Verify a registered phone number. * Verify a registered phone number.
*/ */
async v1RegisterNumberVerifyTokenPostRaw(requestParameters: V1RegisterNumberVerifyTokenPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<string>> { async v1RegisterNumberVerifyTokenPostRaw(
if (requestParameters['number'] == null) { requestParameters: V1RegisterNumberVerifyTokenPostRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<runtime.ApiResponse<string>> {
if (requestParameters["number"] == null) {
throw new runtime.RequiredError( throw new runtime.RequiredError(
'number', "number",
'Required parameter "number" was null or undefined when calling v1RegisterNumberVerifyTokenPost().' 'Required parameter "number" was null or undefined when calling v1RegisterNumberVerifyTokenPost().',
); );
} }
if (requestParameters['token'] == null) { if (requestParameters["token"] == null) {
throw new runtime.RequiredError( throw new runtime.RequiredError(
'token', "token",
'Required parameter "token" was null or undefined when calling v1RegisterNumberVerifyTokenPost().' 'Required parameter "token" was null or undefined when calling v1RegisterNumberVerifyTokenPost().',
); );
} }
@ -216,17 +249,28 @@ export class DevicesApi extends runtime.BaseAPI {
const headerParameters: runtime.HTTPHeaders = {}; const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json'; headerParameters["Content-Type"] = "application/json";
const response = await this.request({ const response = await this.request(
path: `/v1/register/{number}/verify/{token}`.replace(`{${"number"}}`, encodeURIComponent(String(requestParameters['number']))).replace(`{${"token"}}`, encodeURIComponent(String(requestParameters['token']))), {
method: 'POST', path: `/v1/register/{number}/verify/{token}`
.replace(
`{${"number"}}`,
encodeURIComponent(String(requestParameters["number"])),
)
.replace(
`{${"token"}}`,
encodeURIComponent(String(requestParameters["token"])),
),
method: "POST",
headers: headerParameters, headers: headerParameters,
query: queryParameters, query: queryParameters,
body: ApiVerifyNumberSettingsToJSON(requestParameters['data']), body: ApiVerifyNumberSettingsToJSON(requestParameters["data"]),
}, initOverrides); },
initOverrides,
);
if (this.isJsonMime(response.headers.get('content-type'))) { if (this.isJsonMime(response.headers.get("content-type"))) {
return new runtime.JSONApiResponse<string>(response); return new runtime.JSONApiResponse<string>(response);
} else { } else {
return new runtime.TextApiResponse(response) as any; return new runtime.TextApiResponse(response) as any;
@ -237,8 +281,14 @@ export class DevicesApi extends runtime.BaseAPI {
* Verify a registered phone number with the signal network. * Verify a registered phone number with the signal network.
* Verify a registered phone number. * Verify a registered phone number.
*/ */
async v1RegisterNumberVerifyTokenPost(requestParameters: V1RegisterNumberVerifyTokenPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<string> { async v1RegisterNumberVerifyTokenPost(
const response = await this.v1RegisterNumberVerifyTokenPostRaw(requestParameters, initOverrides); requestParameters: V1RegisterNumberVerifyTokenPostRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<string> {
const response = await this.v1RegisterNumberVerifyTokenPostRaw(
requestParameters,
initOverrides,
);
return await response.value(); return await response.value();
} }
@ -246,11 +296,14 @@ export class DevicesApi extends runtime.BaseAPI {
* Disables push support for this device. **WARNING:** If *delete_account* is set to *true*, the account will be deleted from the Signal Server. This cannot be undone without loss. * Disables push support for this device. **WARNING:** If *delete_account* is set to *true*, the account will be deleted from the Signal Server. This cannot be undone without loss.
* Unregister a phone number. * Unregister a phone number.
*/ */
async v1UnregisterNumberPostRaw(requestParameters: V1UnregisterNumberPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<void>> { async v1UnregisterNumberPostRaw(
if (requestParameters['number'] == null) { requestParameters: V1UnregisterNumberPostRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<runtime.ApiResponse<void>> {
if (requestParameters["number"] == null) {
throw new runtime.RequiredError( throw new runtime.RequiredError(
'number', "number",
'Required parameter "number" was null or undefined when calling v1UnregisterNumberPost().' 'Required parameter "number" was null or undefined when calling v1UnregisterNumberPost().',
); );
} }
@ -258,15 +311,21 @@ export class DevicesApi extends runtime.BaseAPI {
const headerParameters: runtime.HTTPHeaders = {}; const headerParameters: runtime.HTTPHeaders = {};
headerParameters['Content-Type'] = 'application/json'; headerParameters["Content-Type"] = "application/json";
const response = await this.request({ const response = await this.request(
path: `/v1/unregister/{number}`.replace(`{${"number"}}`, encodeURIComponent(String(requestParameters['number']))), {
method: 'POST', path: `/v1/unregister/{number}`.replace(
`{${"number"}}`,
encodeURIComponent(String(requestParameters["number"])),
),
method: "POST",
headers: headerParameters, headers: headerParameters,
query: queryParameters, query: queryParameters,
body: ApiUnregisterNumberRequestToJSON(requestParameters['data']), body: ApiUnregisterNumberRequestToJSON(requestParameters["data"]),
}, initOverrides); },
initOverrides,
);
return new runtime.VoidApiResponse(response); return new runtime.VoidApiResponse(response);
} }
@ -275,8 +334,10 @@ export class DevicesApi extends runtime.BaseAPI {
* Disables push support for this device. **WARNING:** If *delete_account* is set to *true*, the account will be deleted from the Signal Server. This cannot be undone without loss. * Disables push support for this device. **WARNING:** If *delete_account* is set to *true*, the account will be deleted from the Signal Server. This cannot be undone without loss.
* Unregister a phone number. * Unregister a phone number.
*/ */
async v1UnregisterNumberPost(requestParameters: V1UnregisterNumberPostRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<void> { async v1UnregisterNumberPost(
requestParameters: V1UnregisterNumberPostRequest,
initOverrides?: RequestInit | runtime.InitOverrideFunction,
): Promise<void> {
await this.v1UnregisterNumberPostRaw(requestParameters, initOverrides); await this.v1UnregisterNumberPostRaw(requestParameters, initOverrides);
} }
} }

View file

@ -9,13 +9,9 @@
"node": ">=20" "node": ">=20"
}, },
"files": [ "files": [
"tsconfig.json" "tsconfig.json",
], "tsconfig.next.json",
"keywords": [ "tsconfig.node.json"
"tsconfig",
"typescript",
"ts",
"config"
], ],
"scripts": { "scripts": {
"lint": "echo no lint", "lint": "echo no lint",

View file

@ -1,24 +1,15 @@
{ {
"compilerOptions": { "compilerOptions": {
"incremental": true, "target": "esnext",
"target": "es2020", "skipLibCheck": true,
"lib": ["es2020"], "strict": true,
"module": "ES2020", "allowJs": true,
"moduleResolution": "node", "forceConsistentCasingInFileNames": true,
"declaration": true,
"inlineSourceMap": true,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"noUnusedLocals": false, "isolatedModules": true,
"noUnusedParameters": false, "incremental": true
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"traceResolution": false,
"listEmittedFiles": false,
"listFiles": false,
"pretty": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"types": ["node"]
} }
} }

View file

@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"noEmit": true,
"moduleResolution": "bundler",
"plugins": [
{
"name": "next"
},
{
"name": "typescript-eslint-language-service"
}
]
}
}

View file

@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./dist",
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": false,
"baseUrl": ".",
"sourceMap": true,
"inlineSources": true
}
}