235 lines
5.1 KiB
TypeScript
235 lines
5.1 KiB
TypeScript
import type { BridgeRequest } from "../shared/protocol";
|
|
import {
|
|
serialize,
|
|
serializeChat,
|
|
serializeContact,
|
|
serializeGroupMetadata,
|
|
serializeMessage,
|
|
} from "./serializer";
|
|
|
|
let socket: WebSocket | undefined;
|
|
let consts: Record<string, unknown> = {
|
|
DEVICE_ID: String(Date.now()),
|
|
DEVICE_NAME: "",
|
|
};
|
|
|
|
function init() {
|
|
try {
|
|
const _consts = localStorage.getItem("wa-bridge-consts");
|
|
if (!_consts) {
|
|
localStorage.setItem("wa-bridge-consts", JSON.stringify(consts));
|
|
} else {
|
|
consts = JSON.parse(_consts);
|
|
}
|
|
} catch (_) {}
|
|
}
|
|
|
|
function send(message: unknown) {
|
|
if (socket?.readyState !== WebSocket.OPEN) {
|
|
throw new Error("WebSocket is not connected");
|
|
}
|
|
|
|
socket.send(JSON.stringify(message));
|
|
}
|
|
|
|
function reply(id: string, result: unknown) {
|
|
send({
|
|
type: "response",
|
|
id,
|
|
ok: true,
|
|
result: serialize(result),
|
|
});
|
|
}
|
|
|
|
function error(id: string, err: unknown) {
|
|
send({
|
|
type: "response",
|
|
id,
|
|
ok: false,
|
|
error: {
|
|
code: "ERROR",
|
|
message:
|
|
typeof err === "object" && "message" in err!
|
|
? err.message
|
|
: String(err),
|
|
},
|
|
});
|
|
}
|
|
|
|
async function handleRequest(request: BridgeRequest) {
|
|
switch (request.method) {
|
|
case "status":
|
|
return {
|
|
authenticated: WPP.conn.isAuthenticated(),
|
|
ready: WPP.isReady,
|
|
deviceId: consts.DEVICE_ID,
|
|
deviceName: consts.DEVICE_NAME,
|
|
};
|
|
|
|
case "listChats": {
|
|
const chats = await WPP.chat.list({
|
|
...(request.params?.limit != null
|
|
? { count: request.params.limit }
|
|
: {}),
|
|
...(request.params?.groupsOnly ? { onlyGroups: true } : {}),
|
|
...(request.params?.usersOnly ? { onlyUsers: true } : {}),
|
|
});
|
|
|
|
return chats.filter(Boolean).map(serializeChat);
|
|
}
|
|
|
|
case "listContacts": {
|
|
const contacts = await WPP.contact.list();
|
|
|
|
const result = request.params?.limit
|
|
? contacts.slice(0, request.params.limit)
|
|
: contacts;
|
|
|
|
return result.filter(Boolean).map(serializeContact);
|
|
}
|
|
|
|
case "listGroups": {
|
|
const groups = await WPP.group.getAllGroups();
|
|
|
|
return groups
|
|
.filter((i) => i.groupMetadata != null)
|
|
.map((i) => serializeGroupMetadata(i.groupMetadata));
|
|
}
|
|
|
|
case "getMessages": {
|
|
const messages = await WPP.chat.getMessages(request.params.chatId, {
|
|
count: request.params.count ?? 50,
|
|
direction: request.params.direction,
|
|
id: request.params.id,
|
|
});
|
|
|
|
return messages.filter(Boolean).map(serializeMessage);
|
|
}
|
|
|
|
case "sendText": {
|
|
const result = await WPP.chat.sendTextMessage(
|
|
request.params.chatId,
|
|
request.params.text,
|
|
request.params.options,
|
|
);
|
|
|
|
return serialize(result);
|
|
}
|
|
|
|
case "sendFile": {
|
|
const result = await WPP.chat.sendFileMessage(
|
|
request.params.chatId,
|
|
request.params.data,
|
|
request.params.options,
|
|
);
|
|
|
|
return serialize(result);
|
|
}
|
|
|
|
case "getMedia":
|
|
const media = await WPP.chat
|
|
.downloadMedia(request.params)
|
|
.then(WPP.util.blobToBase64);
|
|
return media;
|
|
|
|
case "exec":
|
|
const fn = new Function(request.params);
|
|
return fn();
|
|
|
|
default:
|
|
throw new Error(`Unknown method: ${(request as any).method}`);
|
|
}
|
|
}
|
|
|
|
function connect() {
|
|
if (
|
|
socket &&
|
|
(socket.readyState === WebSocket.OPEN ||
|
|
socket.readyState === WebSocket.CONNECTING)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
socket = new WebSocket(__WS_URL__);
|
|
|
|
socket.onopen = () => {
|
|
console.log("[WA Bridge] connected", __WS_URL__);
|
|
|
|
send({
|
|
type: "event",
|
|
event: "ready",
|
|
data: {
|
|
version: __VERSION__,
|
|
authenticated: WPP.conn.isAuthenticated(),
|
|
deviceId: consts.DEVICE_ID,
|
|
deviceName: consts.DEVICE_NAME,
|
|
},
|
|
});
|
|
};
|
|
|
|
socket.onmessage = async (event) => {
|
|
try {
|
|
const request = JSON.parse(event.data) as BridgeRequest;
|
|
|
|
if (request.type === "ping") {
|
|
send({ type: "pong" });
|
|
}
|
|
|
|
if (request.type !== "request") {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const result = await handleRequest(request);
|
|
|
|
reply(request.id, JSON.stringify(result));
|
|
} catch (err) {
|
|
error(request.id, err);
|
|
}
|
|
} catch (err) {
|
|
console.error("[WA Bridge] invalid message", err);
|
|
}
|
|
};
|
|
|
|
socket.onclose = (event) => {
|
|
console.log("[WA Bridge] disconnected", {
|
|
code: event.code,
|
|
reason: event.reason,
|
|
wasClean: event.wasClean,
|
|
});
|
|
|
|
socket = undefined;
|
|
setTimeout(connect, 3000);
|
|
};
|
|
|
|
socket.onerror = (event) => {
|
|
console.error("[WA Bridge] websocket error", event);
|
|
};
|
|
}
|
|
|
|
function start() {
|
|
WPP.loader.onReady(() => {
|
|
console.log("[WA Bridge] WA-JS ready");
|
|
|
|
connect();
|
|
|
|
/*
|
|
* Forward new-message events to Node.
|
|
*/
|
|
WPP.on("chat.new_message", (message: unknown) => {
|
|
if (socket?.readyState !== WebSocket.OPEN) {
|
|
return;
|
|
}
|
|
|
|
send({
|
|
type: "event",
|
|
event: "message",
|
|
data: serializeMessage(message),
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
init();
|
|
start();
|