feat: frontend draft

This commit is contained in:
2026-09-15 17:55:48 +08:00
parent a13e4d2d99
commit 5329bb2ed9
28 changed files with 4865 additions and 67 deletions
-42
View File
@@ -1,42 +0,0 @@
import { WhatsAppClient } from "./whatsapp";
const wa = new WhatsAppClient("wss://wa-bridge.rul.sh/ws");
await wa.connect();
const chats = await wa.listChats({
limit: 10,
groupsOnly: true,
// usersOnly: true,
});
console.log(
chats.map((i) => ({
id: typeof i.id === "object" ? i.id?._serialized : i.id,
lid: i.accountLid,
name: i.formattedTitle,
})),
);
const messages = await wa.getMessages({
chatId: "[email protected]",
count: 5,
});
console.log(
messages.map((i) => ({
id: i.id.id,
from: i.from,
text: i.body,
})),
);
// const result = await wa.searchMessages({
// query: "pasien",
// chatId: "[email protected]",
// limit: 20,
// });
// await wa.sendText({
// chatId: "[email protected]",
// text: "Hello from Node!",
// });
+25 -10
View File
@@ -1,5 +1,3 @@
import WebSocket from "ws";
import type {
GetMessagesParams,
ListChatsParams,
@@ -14,8 +12,9 @@ import type {
} from "../shared/dto";
export class WhatsAppClient {
isConnected: boolean = false;
private url: string = "";
private isConnected: boolean = false;
private socket: WebSocket | null = null;
private pending = new Map<
@@ -27,25 +26,28 @@ export class WhatsAppClient {
}
>();
private listeners: Map<string, (...args: any[]) => void> = new Map();
constructor(url = "ws://127.0.0.1:8787") {
this.url = url;
}
async connect() {
return new Promise<void>((resolve, reject) => {
return new Promise<void>((resolve) => {
if (this.socket) return;
this.socket = new WebSocket(this.url);
this.emit("connecting");
this.socket = new WebSocket(this.url + "?client=1");
this.socket.on("open", () => {
this.socket.addEventListener("open", () => {
console.log("Connected to bridge");
this.isConnected = true;
this.emit("status", true);
resolve();
});
this.socket.on("message", (data) => {
const message = JSON.parse(data.toString());
this.socket.addEventListener("message", (data) => {
const message = JSON.parse(data.data);
if (message.type !== "response") {
return;
}
@@ -66,9 +68,10 @@ export class WhatsAppClient {
}
});
this.socket.on("close", () => {
this.socket.addEventListener("close", () => {
this.socket = null;
this.isConnected = false;
this.emit("status", false);
setTimeout(() => this.connect(), 3000);
});
});
@@ -105,7 +108,19 @@ export class WhatsAppClient {
});
}
on(event: string, callback: (...args: any[]) => void) {
this.listeners.set(event, callback);
}
private emit(event: string, ...args: any[]) {
this.listeners.get(event)?.(...args);
}
status() {
if (!this.isConnected) {
return null;
}
return this.request<{
authenticated: boolean;
ready: boolean;
+15 -11
View File
@@ -36,15 +36,19 @@ export function initDb() {
}
export function insertMessage(message: Message): void {
db.query(
`INSERT INTO messages (id, chat_id, sender, receiver, from_me, data)
VALUES ($id, $chatId, $from, $to, $fromMe, $data)`,
).run({
$id: message.id,
$chatId: message.chatId,
$from: message.from,
$to: message.to,
$fromMe: message.fromMe ? 1 : 0,
$data: JSON.stringify(message.data),
});
try {
db.query(
`INSERT INTO messages (id, chat_id, sender, receiver, from_me, data)
VALUES ($id, $chatId, $from, $to, $fromMe, $data) ON CONFLICT(id) DO UPDATE SET data = $data;`,
).run({
$id: message.id,
$chatId: message.chatId,
$from: message.from,
$to: message.to,
$fromMe: message.fromMe ? 1 : 0,
$data: JSON.stringify(message.data),
});
} catch (err) {
console.error(err);
}
}
+1 -1
View File
@@ -115,7 +115,7 @@ wss.on("connection", (ws) => {
* First connection from Tampermonkey is the
* WhatsApp bridge.
*/
if (!whatsapp) {
if (!whatsapp && !ws.url.includes("?client=1")) {
whatsapp = ws;
console.log("[WS] WhatsApp bridge attached");
+1 -1
View File
@@ -267,7 +267,7 @@ export function serializeMessage(message: any): MessageDTO {
from: serializeId(message.from),
to: serializeId(message.to),
author: serializeId(message.author),
fromMe: message.fromMe ?? false,
fromMe: message.id?.fromMe ?? message.fromMe ?? false,
body: message.body ?? null,
caption: message.caption ?? null,
type: message.type ?? null,