aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/session.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/session.js')
-rw-r--r--src/api/session.js77
1 files changed, 77 insertions, 0 deletions
diff --git a/src/api/session.js b/src/api/session.js
new file mode 100644
index 0000000..7def14d
--- /dev/null
+++ b/src/api/session.js
@@ -0,0 +1,77 @@
+// auth.*, daemon.*, events.*: they work even when the session is not ready.
+
+import { WaError } from '../rpc.js';
+
+export const EVENTS = [
+ 'qr', 'code', 'authenticated', 'auth_failure', 'ready', 'disconnected', 'change_state', 'loading_screen',
+ 'message', 'message_create', 'message_ack', 'message_edit', 'message_revoke_everyone', 'message_revoke_me',
+ 'message_reaction', 'media_uploaded', 'unread_count', 'chat_archived', 'chat_removed', 'contact_changed',
+ 'group_join', 'group_leave', 'group_update', 'group_admin_changed', 'group_membership_request',
+ 'call', 'vote_update',
+];
+
+export default {
+ 'auth.status': {
+ description: 'WhatsApp session state (qr, code, authenticated, ready, ...) and linked account.',
+ ready: false,
+ handler: async (ctx) => ctx.status(),
+ },
+
+ 'auth.pair': {
+ description: 'Requests a pairing code by phone number (alternative to the QR). ' +
+ 'Enter it on the phone: Linked devices > Link with phone number instead.',
+ ready: false,
+ params: {
+ phone: { type: 'string', required: true, description: 'Number in international format, digits only (e.g. 5491122334455)' },
+ notify: { type: 'boolean', default: true, description: 'Show a notification on the phone' },
+ },
+ handler: async (ctx, p) => {
+ const st = ctx.status().state;
+ if (st === 'ready' || st === 'authenticated') {
+ throw new WaError('EINVAL', 'the session is already linked');
+ }
+ await ctx.waitState(['qr', 'code'], 60000);
+ const code = await ctx.client.requestPairingCode(p.phone.replace(/\D/g, ''), p.notify);
+ return { code };
+ },
+ },
+
+ 'auth.logout': {
+ description: 'Logs out (unlinks this device) and deletes the local session.',
+ handler: async (ctx) => {
+ await ctx.client.logout();
+ return { ok: true };
+ },
+ },
+
+ 'daemon.status': {
+ description: 'Daemon process information.',
+ ready: false,
+ handler: async (ctx) => ctx.daemonInfo(),
+ },
+
+ 'daemon.stop': {
+ description: 'Stops the daemon (closes the browser; the session stays saved).',
+ ready: false,
+ handler: async (ctx) => {
+ setImmediate(() => ctx.shutdown('daemon.stop'));
+ return { ok: true };
+ },
+ },
+
+ 'events.watch': {
+ description: 'Real-time event stream (NDJSON). Each event: {event, account, time, data}.',
+ ready: false,
+ stream: true,
+ params: {
+ events: { type: 'array', default: ['message'], description: `Eventos a escuchar, o ["all"]. Disponibles: ${EVENTS.join(', ')}` },
+ replay: { type: 'boolean', default: false, description: 'On connect, resend the current state (last qr/code, ready)' },
+ },
+ handler: async (ctx, p, conn) => {
+ const unknown = p.events.filter((e) => e !== 'all' && !EVENTS.includes(e));
+ if (unknown.length) throw new WaError('EINVAL', `eventos desconocidos: ${unknown.join(', ')}`);
+ ctx.subscribe(conn, p.events.includes('all') ? null : new Set(p.events), p.replay);
+ return ctx.STREAM;
+ },
+ },
+};