aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/session.js
blob: 7def14d2e8cd5051155f2e664fec2b4087ea7146 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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;
        },
    },
};