@wirecat/whatsapp-cli 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/LICENSE +204 -0
  2. package/README.md +401 -2
  3. package/dist/adapter/adapter.d.ts +13 -0
  4. package/dist/adapter/adapter.d.ts.map +1 -0
  5. package/dist/adapter/adapter.js +554 -0
  6. package/dist/adapter/adapter.js.map +1 -0
  7. package/dist/adapter/channels.d.ts +43 -0
  8. package/dist/adapter/channels.d.ts.map +1 -0
  9. package/dist/adapter/channels.js +100 -0
  10. package/dist/adapter/channels.js.map +1 -0
  11. package/dist/adapter/checks.d.ts +6 -0
  12. package/dist/adapter/checks.d.ts.map +1 -0
  13. package/dist/adapter/checks.js +2 -0
  14. package/dist/adapter/checks.js.map +1 -0
  15. package/dist/adapter/errors.d.ts +13 -0
  16. package/dist/adapter/errors.d.ts.map +1 -0
  17. package/dist/adapter/errors.js +38 -0
  18. package/dist/adapter/errors.js.map +1 -0
  19. package/dist/adapter/format-markdown.d.ts +8 -0
  20. package/dist/adapter/format-markdown.d.ts.map +1 -0
  21. package/dist/adapter/format-markdown.js +80 -0
  22. package/dist/adapter/format-markdown.js.map +1 -0
  23. package/dist/adapter/groups.d.ts +25 -0
  24. package/dist/adapter/groups.d.ts.map +1 -0
  25. package/dist/adapter/groups.js +252 -0
  26. package/dist/adapter/groups.js.map +1 -0
  27. package/dist/adapter/map.d.ts +23 -0
  28. package/dist/adapter/map.d.ts.map +1 -0
  29. package/dist/adapter/map.js +121 -0
  30. package/dist/adapter/map.js.map +1 -0
  31. package/dist/adapter/pins.d.ts +11 -0
  32. package/dist/adapter/pins.d.ts.map +1 -0
  33. package/dist/adapter/pins.js +10 -0
  34. package/dist/adapter/pins.js.map +1 -0
  35. package/dist/adapter/polls.d.ts +21 -0
  36. package/dist/adapter/polls.d.ts.map +1 -0
  37. package/dist/adapter/polls.js +103 -0
  38. package/dist/adapter/polls.js.map +1 -0
  39. package/dist/adapter/privacy.d.ts +18 -0
  40. package/dist/adapter/privacy.d.ts.map +1 -0
  41. package/dist/adapter/privacy.js +94 -0
  42. package/dist/adapter/privacy.js.map +1 -0
  43. package/dist/adapter/settings.d.ts +21 -0
  44. package/dist/adapter/settings.d.ts.map +1 -0
  45. package/dist/adapter/settings.js +76 -0
  46. package/dist/adapter/settings.js.map +1 -0
  47. package/dist/app.d.ts +6 -0
  48. package/dist/app.d.ts.map +1 -0
  49. package/dist/app.js +16 -0
  50. package/dist/app.js.map +1 -0
  51. package/dist/bin/wa.d.ts +3 -0
  52. package/dist/bin/wa.d.ts.map +1 -0
  53. package/dist/bin/wa.js +14 -0
  54. package/dist/bin/wa.js.map +1 -0
  55. package/dist/commands/session.d.ts +48 -0
  56. package/dist/commands/session.d.ts.map +1 -0
  57. package/dist/commands/session.js +201 -0
  58. package/dist/commands/session.js.map +1 -0
  59. package/dist/commands/setup.d.ts +3 -0
  60. package/dist/commands/setup.d.ts.map +1 -0
  61. package/dist/commands/setup.js +140 -0
  62. package/dist/commands/setup.js.map +1 -0
  63. package/dist/commands/update.d.ts +2 -0
  64. package/dist/commands/update.d.ts.map +1 -0
  65. package/dist/commands/update.js +17 -0
  66. package/dist/commands/update.js.map +1 -0
  67. package/dist/diagnose.d.ts +77 -0
  68. package/dist/diagnose.d.ts.map +1 -0
  69. package/dist/diagnose.js +84 -0
  70. package/dist/diagnose.js.map +1 -0
  71. package/dist/engine/channels.d.ts +100 -0
  72. package/dist/engine/channels.d.ts.map +1 -0
  73. package/dist/engine/channels.js +29 -0
  74. package/dist/engine/channels.js.map +1 -0
  75. package/dist/engine/contract.d.ts +1049 -0
  76. package/dist/engine/contract.d.ts.map +1 -0
  77. package/dist/engine/contract.js +353 -0
  78. package/dist/engine/contract.js.map +1 -0
  79. package/dist/engine/events.d.ts +9 -0
  80. package/dist/engine/events.d.ts.map +1 -0
  81. package/dist/engine/events.js +23 -0
  82. package/dist/engine/events.js.map +1 -0
  83. package/dist/engine/go-engine.d.ts +23 -0
  84. package/dist/engine/go-engine.d.ts.map +1 -0
  85. package/dist/engine/go-engine.js +227 -0
  86. package/dist/engine/go-engine.js.map +1 -0
  87. package/dist/engine/groups.d.ts +171 -0
  88. package/dist/engine/groups.d.ts.map +1 -0
  89. package/dist/engine/groups.js +66 -0
  90. package/dist/engine/groups.js.map +1 -0
  91. package/dist/engine/privacy.d.ts +33 -0
  92. package/dist/engine/privacy.d.ts.map +1 -0
  93. package/dist/engine/privacy.js +23 -0
  94. package/dist/engine/privacy.js.map +1 -0
  95. package/dist/engine/settings.d.ts +45 -0
  96. package/dist/engine/settings.d.ts.map +1 -0
  97. package/dist/engine/settings.js +34 -0
  98. package/dist/engine/settings.js.map +1 -0
  99. package/dist/engine-binary.d.ts +5 -0
  100. package/dist/engine-binary.d.ts.map +1 -0
  101. package/dist/engine-binary.js +18 -0
  102. package/dist/engine-binary.js.map +1 -0
  103. package/dist/install/postinstall.d.ts +8 -0
  104. package/dist/install/postinstall.d.ts.map +1 -0
  105. package/dist/install/postinstall.js +63 -0
  106. package/dist/install/postinstall.js.map +1 -0
  107. package/dist/messenger.d.ts +63 -0
  108. package/dist/messenger.d.ts.map +1 -0
  109. package/dist/messenger.js +186 -0
  110. package/dist/messenger.js.map +1 -0
  111. package/dist/paths.d.ts +5 -0
  112. package/dist/paths.d.ts.map +1 -0
  113. package/dist/paths.js +7 -0
  114. package/dist/paths.js.map +1 -0
  115. package/dist/program.d.ts +12 -0
  116. package/dist/program.d.ts.map +1 -0
  117. package/dist/program.js +86 -0
  118. package/dist/program.js.map +1 -0
  119. package/dist/reasons.d.ts +4 -0
  120. package/dist/reasons.d.ts.map +1 -0
  121. package/dist/reasons.js +21 -0
  122. package/dist/reasons.js.map +1 -0
  123. package/dist/update.d.ts +7 -0
  124. package/dist/update.d.ts.map +1 -0
  125. package/dist/update.js +19 -0
  126. package/dist/update.js.map +1 -0
  127. package/dist/version.d.ts +2 -0
  128. package/dist/version.d.ts.map +1 -0
  129. package/dist/version.js +2 -0
  130. package/dist/version.js.map +1 -0
  131. package/install/postinstall.mjs +3 -0
  132. package/install/windows.ps1 +161 -0
  133. package/package.json +89 -4
  134. package/skills/whatsapp-cli/SKILL.md +325 -0
@@ -0,0 +1,23 @@
1
+ import type { Account, Attachment, Chat, ChatEvent, ChatUpdate, Member, Message } from "@wirecat/cli-core";
2
+ import { type WaAccount, type WaAttachment, type WaChat, type WaChatEvent, type WaContact, type WaMediaRef, type WaMessage } from "../engine/contract.js";
3
+ /** A pushed chat as the store saves it: what the engine left out keeps the stored value. */
4
+ export declare const toChatUpdate: (chat: WaChat) => ChatUpdate;
5
+ export declare const toChat: (chat: WaChat) => Chat;
6
+ export declare const toAccount: (account: WaAccount) => Account;
7
+ export declare const toMember: (contact: WaContact) => Member;
8
+ export declare const toChatEvent: ({ id, timestamp, event, by, byName, people, title }: WaChatEvent) => ChatEvent;
9
+ export declare const toAttachment: ({ kind, url, width, height, title, name, size, mime, duration, ref }: WaAttachment) => Attachment;
10
+ export declare const toMessage: (message: WaMessage) => Message;
11
+ /** Whether an attachment holds a file's keys, not a place or a contact card. */
12
+ export declare const isFile: (attachment: WaAttachment | undefined) => attachment is WaAttachment & {
13
+ ref: WaMediaRef;
14
+ };
15
+ /**
16
+ * Whether two refs name one file, wherever each says it is: the phone keeps a file's key when it uploads it again, but
17
+ * may encrypt it again, which changes both checksums. A keyless file (a channel's) is never uploaded again, and
18
+ * without keys two of them cannot be told apart.
19
+ */
20
+ export declare const sameFile: (one: unknown, other: unknown) => boolean;
21
+ /** A stored attachment as the engine takes it back, or `undefined` when it is not one this engine wrote. */
22
+ export declare const toWaAttachment: ({ providerRef, ...attachment }: Attachment) => WaAttachment | undefined;
23
+ //# sourceMappingURL=map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../src/adapter/map.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAY,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAEpH,OAAO,EACL,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,MAAM,EACX,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,SAAS,EAGf,MAAM,uBAAuB,CAAA;AAU9B,4FAA4F;AAC5F,eAAO,MAAM,YAAY,SAAU,MAAM,KAAG,UAS1C,CAAA;AAEF,eAAO,MAAM,MAAM,SAAU,MAAM,KAAG,IAQpC,CAAA;AAWF,eAAO,MAAM,SAAS,YAAa,SAAS,KAAG,OAAoE,CAAA;AAEnH,eAAO,MAAM,QAAQ,YAAa,SAAS,KAAG,MAAmE,CAAA;AAEjH,eAAO,MAAM,WAAW,wDAAyD,WAAW,KAAG,SAO7F,CAAA;AAGF,eAAO,MAAM,YAAY,yEAA0E,YAAY,eAY9G,CAAA;AAED,eAAO,MAAM,SAAS,YAAa,SAAS,KAAG,OAyB7C,CAAA;AAEF,gFAAgF;AAChF,eAAO,MAAM,MAAM,eAAgB,YAAY,GAAG,SAAS,KAAG,UAAU,IAAI,YAAY,GAAG;IAAE,GAAG,EAAE,UAAU,CAAA;CAC3C,CAAA;AAEjE;;;;GAIG;AACH,eAAO,MAAM,QAAQ,QAAS,OAAO,SAAS,OAAO,KAAG,OAIvD,CAAA;AAED,4GAA4G;AAC5G,eAAO,MAAM,cAAc,mCAAoC,UAAU,KAAG,YAAY,GAAG,SAW1F,CAAA"}
@@ -0,0 +1,121 @@
1
+ import * as v from "valibot";
2
+ import { waAttachment, waMediaRef, } from "../engine/contract.js";
3
+ const KINDS = {
4
+ dm: "dialog",
5
+ self: "saved",
6
+ group: "group",
7
+ newsletter: "channel",
8
+ broadcast: "unknown",
9
+ };
10
+ /** A pushed chat as the store saves it: what the engine left out keeps the stored value. */
11
+ export const toChatUpdate = (chat) => ({
12
+ id: chat.jid,
13
+ ...(chat.name === undefined ? {} : { title: chat.name }),
14
+ kind: KINDS[chat.kind],
15
+ ...(chat.unread === undefined ? {} : { unreadCount: chat.unread }),
16
+ ...(chat.lastMessageAt === undefined ? {} : { lastMessageAt: chat.lastMessageAt }),
17
+ ...(chat.participants === undefined ? {} : { participantsCount: chat.participants }),
18
+ ...(chat.membershipState === undefined ? {} : { membershipState: chat.membershipState }),
19
+ ...rest(chat),
20
+ });
21
+ export const toChat = (chat) => ({
22
+ id: chat.jid,
23
+ title: chat.name ?? null,
24
+ kind: KINDS[chat.kind],
25
+ unreadCount: chat.unread ?? null,
26
+ lastMessageAt: chat.lastMessageAt ?? null,
27
+ participantsCount: chat.participants ?? null,
28
+ ...rest(chat),
29
+ });
30
+ const rest = (chat) => ({
31
+ ...(chat.archived === undefined ? {} : { archived: chat.archived }),
32
+ ...(chat.muted === undefined ? {} : { muted: chat.muted }),
33
+ // cli-core has a parent chat but no kind of chat that holds groups, so a community is a group marked as one.
34
+ ...(chat.parent === undefined ? {} : { parentChatId: chat.parent }),
35
+ ...(chat.community ? { providerMetadata: { community: true } } : {}),
36
+ });
37
+ // WhatsApp has no public handle, and the engine never answers the phone number.
38
+ export const toAccount = (account) => ({ id: account.jid, name: account.name, username: null });
39
+ export const toMember = (contact) => ({ id: contact.jid, name: contact.name, username: null });
40
+ export const toChatEvent = ({ id, timestamp, event, by, byName, people, title }) => ({
41
+ messageId: id,
42
+ timestamp,
43
+ event,
44
+ by: { id: by, name: byName },
45
+ people: people.map(({ jid, name }) => ({ id: jid, name })),
46
+ ...(title === undefined ? {} : { title }),
47
+ });
48
+ // In the domain type's order, so an answer from the store prints byte for byte the same.
49
+ export const toAttachment = ({ kind, url, width, height, title, name, size, mime, duration, ref }) => {
50
+ const attachment = { kind };
51
+ if (url !== undefined)
52
+ attachment.url = url;
53
+ if (width !== undefined)
54
+ attachment.width = width;
55
+ if (height !== undefined)
56
+ attachment.height = height;
57
+ if (title !== undefined)
58
+ attachment.title = title;
59
+ if (name !== undefined)
60
+ attachment.name = name;
61
+ if (size !== undefined)
62
+ attachment.size = size;
63
+ if (mime !== undefined)
64
+ attachment.mime = mime;
65
+ if (duration !== undefined)
66
+ attachment.duration = duration;
67
+ if (ref !== undefined)
68
+ attachment.providerRef = ref;
69
+ return attachment;
70
+ };
71
+ export const toMessage = (message) => ({
72
+ id: message.id,
73
+ chatId: message.chat,
74
+ senderId: message.sender,
75
+ senderName: message.senderName,
76
+ timestamp: message.timestamp,
77
+ editedAt: message.editedAt,
78
+ text: message.text,
79
+ outgoing: message.fromMe,
80
+ attachments: message.attachments?.map(toAttachment) ?? [],
81
+ replyTo: null,
82
+ ...(message.replyTo === null ? {} : { replyToId: message.replyTo }),
83
+ forwardedFrom: null,
84
+ reactions: null,
85
+ // Kept with the message in the store: a vote names answers by text, and only the poll's message has them; a
86
+ // channel's history is read back from a post's server id.
87
+ ...(message.poll === undefined && message.serverId === undefined
88
+ ? {}
89
+ : {
90
+ providerMetadata: {
91
+ ...(message.poll === undefined ? {} : { poll: message.poll }),
92
+ ...(message.serverId === undefined ? {} : { serverId: message.serverId }),
93
+ },
94
+ }),
95
+ ...(message.starred ? { starred: true } : {}),
96
+ });
97
+ /** Whether an attachment holds a file's keys, not a place or a contact card. */
98
+ export const isFile = (attachment) => attachment?.ref !== undefined && "directPath" in attachment.ref;
99
+ /**
100
+ * Whether two refs name one file, wherever each says it is: the phone keeps a file's key when it uploads it again, but
101
+ * may encrypt it again, which changes both checksums. A keyless file (a channel's) is never uploaded again, and
102
+ * without keys two of them cannot be told apart.
103
+ */
104
+ export const sameFile = (one, other) => {
105
+ const [a, b] = [v.safeParse(waMediaRef, one), v.safeParse(waMediaRef, other)];
106
+ if (!a.success || !b.success || a.output.mediaKey === undefined)
107
+ return false;
108
+ return a.output.media === b.output.media && a.output.mediaKey === b.output.mediaKey;
109
+ };
110
+ /** A stored attachment as the engine takes it back, or `undefined` when it is not one this engine wrote. */
111
+ export const toWaAttachment = ({ providerRef, ...attachment }) => {
112
+ const parsed = v.safeParse(waAttachment, {
113
+ kind: attachment.kind,
114
+ ...Object.fromEntries(["url", "width", "height", "title", "name", "size", "mime", "duration"]
115
+ .filter((field) => attachment[field] !== undefined && attachment[field] !== null)
116
+ .map((field) => [field, attachment[field]])),
117
+ ...(providerRef ? { ref: providerRef } : {}),
118
+ });
119
+ return parsed.success ? parsed.output : undefined;
120
+ };
121
+ //# sourceMappingURL=map.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"map.js","sourceRoot":"","sources":["../../src/adapter/map.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Message } from "@wirecat/cli-core";
2
+ import type { MessagePins } from "@wirecat/cli-core/cli";
3
+ import type { WaKey, WhatsAppEngine } from "../engine/contract.js";
4
+ /** The message is addressed by its key in the store, like an edit or a reaction. */
5
+ export declare const pinAdapter: ({ engine, held, keyOf, translated, }: {
6
+ engine: WhatsAppEngine;
7
+ held: (chatId: string, messageId: string) => Promise<Message>;
8
+ keyOf: (chatId: string, message: Message) => WaKey;
9
+ translated: <T>(work: Promise<T>) => Promise<T>;
10
+ }) => MessagePins;
11
+ //# sourceMappingURL=pins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pins.d.ts","sourceRoot":"","sources":["../../src/adapter/pins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAElE,oFAAoF;AACpF,eAAO,MAAM,UAAU,yCAKpB;IACD,MAAM,EAAE,cAAc,CAAA;IACtB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7D,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,CAAA;IAClD,UAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;CAChD,KAAG,WAQF,CAAA"}
@@ -0,0 +1,10 @@
1
+ /** The message is addressed by its key in the store, like an edit or a reaction. */
2
+ export const pinAdapter = ({ engine, held, keyOf, translated, }) => ({
3
+ pin: async (chatId, messageId, { duration }) => {
4
+ await translated(engine.pin(keyOf(chatId, await held(chatId, messageId)), duration));
5
+ },
6
+ unpin: async (chatId, messageId) => {
7
+ await translated(engine.unpin(keyOf(chatId, await held(chatId, messageId))));
8
+ },
9
+ });
10
+ //# sourceMappingURL=pins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pins.js","sourceRoot":"","sources":["../../src/adapter/pins.ts"],"names":[],"mappings":"AAIA,oFAAoF;AACpF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,EACzB,MAAM,EACN,IAAI,EACJ,KAAK,EACL,UAAU,GAMX,EAAe,EAAE,CAAC,CAAC;IAClB,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC7C,MAAM,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAA;IACtF,CAAC;IAED,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE;QACjC,MAAM,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAA;IAC9E,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { type Message, type PollVoteEvent } from "@wirecat/cli-core";
2
+ import type { HistoryVote, MessagePolls, StoredMessages } from "@wirecat/cli-core/cli";
3
+ import { type WaKey, type WaVote, type WhatsAppEngine } from "../engine/contract.js";
4
+ export type PollAdapter = Omit<MessagePolls, "closePoll">;
5
+ /** A live vote, for a poll the store holds; one for a poll it does not hold is dropped. */
6
+ export declare const toVoteEvent: (vote: WaVote, stored: StoredMessages) => Promise<PollVoteEvent | undefined>;
7
+ /** A vote from a history push, counted by the poll it carries. */
8
+ export declare const toHistoryVote: (vote: WaVote) => HistoryVote | undefined;
9
+ /**
10
+ * Polls as WhatsApp has them: everyone in the chat sees who voted for what, a vote can always be changed, and a poll
11
+ * neither closes nor is a quiz. A poll is read from the store, which holds the message that carries it and each
12
+ * voter's newest vote; WhatsApp has no request for either.
13
+ */
14
+ export declare const pollAdapter: ({ engine, stored, held, keyOf, translated, }: {
15
+ engine: WhatsAppEngine;
16
+ stored: StoredMessages;
17
+ held: (chatId: string, messageId: string) => Promise<Message>;
18
+ keyOf: (chatId: string, message: Message) => WaKey;
19
+ translated: <T>(work: Promise<T>) => Promise<T>;
20
+ }) => PollAdapter;
21
+ //# sourceMappingURL=polls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polls.d.ts","sourceRoot":"","sources":["../../src/adapter/polls.ts"],"names":[],"mappings":"AACA,OAAO,EAAe,KAAK,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAC3G,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEtF,OAAO,EAAE,KAAK,KAAK,EAAe,KAAK,MAAM,EAAE,KAAK,cAAc,EAAU,MAAM,uBAAuB,CAAA;AAIzG,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAA;AAwCzD,2FAA2F;AAC3F,eAAO,MAAM,WAAW,SAAgB,MAAM,UAAU,cAAc,KAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAIzG,CAAA;AAED,kEAAkE;AAClE,eAAO,MAAM,aAAa,SAAU,MAAM,KAAG,WAAW,GAAG,SAG1D,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,WAAW,iDAMrB;IACD,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,cAAc,CAAA;IACtB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7D,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,CAAA;IAClD,UAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;CAChD,KAAG,WA+DF,CAAA"}
@@ -0,0 +1,103 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ import { countedPoll } from "@wirecat/cli-core";
3
+ import * as v from "valibot";
4
+ import { waPoll } from "../engine/contract.js";
5
+ import { toMessage } from "./map.js";
6
+ const pollIn = (message) => {
7
+ const parsed = v.safeParse(waPoll, message?.providerMetadata?.poll);
8
+ return parsed.success ? parsed.output : undefined;
9
+ };
10
+ const pollOf = (message) => {
11
+ const poll = pollIn(message);
12
+ if (!poll)
13
+ throw new CliError("not_found", `message ${message.id} carries no poll`);
14
+ return poll;
15
+ };
16
+ /** The poll as its message carries it, before the votes the store keeps are counted in. */
17
+ const toPoll = (chatId, message, poll) => ({
18
+ chatId,
19
+ messageId: message.id,
20
+ question: poll.question,
21
+ answers: poll.answers.map(({ id, text }) => ({ id, text, voters: null, chosen: false })),
22
+ closed: false,
23
+ multiple: poll.multiple,
24
+ anonymous: false,
25
+ voters: null,
26
+ quiz: false,
27
+ revote: true,
28
+ creator: message.outgoing === true,
29
+ });
30
+ /**
31
+ * A vote as cli-core keeps it. WhatsApp names each chosen answer by the full hash of its text, an answer id is the
32
+ * start of it. A vote whose answers this poll does not have is dropped rather than kept as a vote taken back.
33
+ */
34
+ const toPollVote = (vote, poll) => {
35
+ const answers = poll.answers
36
+ .filter((answer) => vote.selected.some((hash) => hash.startsWith(answer.id)))
37
+ .map((answer) => answer.id);
38
+ if (answers.length === 0 && vote.selected.length > 0)
39
+ return undefined;
40
+ return { person: { id: vote.voter, name: vote.voterName, username: null }, answers, votedAt: vote.votedAt };
41
+ };
42
+ /** A live vote, for a poll the store holds; one for a poll it does not hold is dropped. */
43
+ export const toVoteEvent = async (vote, stored) => {
44
+ const poll = pollIn(await stored.message(vote.chat, vote.message).catch(() => undefined));
45
+ const counted = poll && toPollVote(vote, poll);
46
+ return counted && { event: "vote", chatId: vote.chat, chatTitle: null, messageId: vote.message, vote: counted };
47
+ };
48
+ /** A vote from a history push, counted by the poll it carries. */
49
+ export const toHistoryVote = (vote) => {
50
+ const counted = vote.poll && toPollVote(vote, vote.poll);
51
+ return counted && { chatId: vote.chat, messageId: vote.message, vote: counted };
52
+ };
53
+ /**
54
+ * Polls as WhatsApp has them: everyone in the chat sees who voted for what, a vote can always be changed, and a poll
55
+ * neither closes nor is a quiz. A poll is read from the store, which holds the message that carries it and each
56
+ * voter's newest vote; WhatsApp has no request for either.
57
+ */
58
+ export const pollAdapter = ({ engine, stored, held, keyOf, translated, }) => ({
59
+ poll: async (chatId, messageId) => {
60
+ const message = await held(chatId, messageId);
61
+ return countedPoll(toPoll(chatId, message, pollOf(message)), await stored.pollVotes(chatId, messageId), engine.self());
62
+ },
63
+ // cli-core keeps this vote only after it returns, so the answer counts it in itself.
64
+ vote: async (chatId, messageId, answerIds) => {
65
+ const message = await held(chatId, messageId);
66
+ const poll = pollOf(message);
67
+ const known = new Map(poll.answers.map((answer) => [answer.id, answer.text]));
68
+ const unknown = answerIds.filter((answer) => !known.has(answer));
69
+ if (unknown.length > 0)
70
+ throw new CliError("validation_error", `${unknown.join(", ")} ${unknown.length === 1 ? "is" : "are"} not an answer of this poll — its answers are ${[...known.keys()].join(", ")}`);
71
+ if (answerIds.length > 1 && !poll.multiple)
72
+ throw new CliError("validation_error", "this poll takes one answer; give one id");
73
+ await translated(engine.vote(keyOf(chatId, message), answerIds.map((answer) => known.get(answer))));
74
+ const self = engine.self();
75
+ const others = (await stored.pollVotes(chatId, messageId)).filter((vote) => vote.person.id !== self);
76
+ const mine = self === null
77
+ ? []
78
+ : [{ person: { id: self, name: null, username: null }, answers: answerIds, votedAt: new Date().toISOString() }];
79
+ return countedPoll(toPoll(chatId, message, poll), [...mine, ...others], self);
80
+ },
81
+ pollVoters: async (chatId, messageId, { limit, answerId }) => {
82
+ await held(chatId, messageId);
83
+ const voters = (await stored.pollVotes(chatId, messageId)).filter((vote) => vote.answers.length > 0 && (answerId === undefined || vote.answers.includes(answerId)));
84
+ return { items: voters.slice(0, limit), hasMore: voters.length > limit, total: voters.length };
85
+ },
86
+ createPoll: async (chatId, poll, { sendId, threadId }) => {
87
+ if (poll.quiz !== undefined || poll.closeAfter !== undefined)
88
+ throw new CliError("validation_error", "WhatsApp polls are neither quizzes nor close by themselves");
89
+ if (threadId !== undefined)
90
+ throw new CliError("validation_error", "WhatsApp sends a poll without a topic");
91
+ if (new Set(poll.answers).size < poll.answers.length)
92
+ throw new CliError("validation_error", "each answer needs text of its own: WhatsApp counts a vote by it");
93
+ const message = await translated(engine.createPoll({
94
+ chat: chatId,
95
+ id: sendId,
96
+ question: poll.question,
97
+ answers: poll.answers,
98
+ multiple: poll.multiple,
99
+ }));
100
+ return { message: toMessage(message), sendId };
101
+ },
102
+ });
103
+ //# sourceMappingURL=polls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"polls.js","sourceRoot":"","sources":["../../src/adapter/polls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { PrivacyKey, PrivacySettings } from "@wirecat/cli-core";
2
+ import type { MessengerAdapter } from "@wirecat/cli-core/cli";
3
+ import type { WhatsAppEngine } from "../engine/contract.js";
4
+ import type { WaPrivacy } from "../engine/privacy.js";
5
+ export type PrivacyAdapter = Required<Pick<MessengerAdapter, "privacy" | "updatePrivacy">>;
6
+ /** WhatsApp's settings, in the order its phone app lists them; `findByPhone` and `phoneNumber` it has not. */
7
+ export declare const PRIVACY_SETTINGS: readonly PrivacyKey[];
8
+ /**
9
+ * What WhatsApp has of cli-core's settings. Who may add the account has no "contacts except" in cli-core, so such a
10
+ * value is left out there, never rounded to the nearest; calls have none in WhatsApp. Hidden online means last seen
11
+ * to nobody and online only to those who see last seen.
12
+ */
13
+ export declare const toPrivacy: (privacy: WaPrivacy) => PrivacySettings;
14
+ export declare const privacyAdapter: ({ engine, translated, }: {
15
+ engine: WhatsAppEngine;
16
+ translated: <T>(work: Promise<T>) => Promise<T>;
17
+ }) => PrivacyAdapter;
18
+ //# sourceMappingURL=privacy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/adapter/privacy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAY,UAAU,EAAE,eAAe,EAAiB,MAAM,mBAAmB,CAAA;AAC7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,sBAAsB,CAAA;AAEtE,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,eAAe,CAAC,CAAC,CAAA;AAE1F,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,EAAE,SAAS,UAAU,EAQjD,CAAA;AA0BD;;;;GAIG;AACH,eAAO,MAAM,SAAS,YAAa,SAAS,KAAG,eAS9C,CAAA;AAsCD,eAAO,MAAM,cAAc,4BAGxB;IACD,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;CAChD,KAAG,cAUF,CAAA"}
@@ -0,0 +1,94 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ /** WhatsApp's settings, in the order its phone app lists them; `findByPhone` and `phoneNumber` it has not. */
3
+ export const PRIVACY_SETTINGS = [
4
+ "lastSeen",
5
+ "hideOnline",
6
+ "profilePhoto",
7
+ "about",
8
+ "readReceipts",
9
+ "chatInvites",
10
+ "calls",
11
+ ];
12
+ const SHOWN = {
13
+ everyone: "all",
14
+ contacts: "contacts",
15
+ contacts_except: "contact_blacklist",
16
+ nobody: "none",
17
+ };
18
+ // whatsmeow lists groupadd none, but Baileys dropped it as refused by WhatsApp (WhiskeySockets/Baileys#966).
19
+ const GROUP_ADD = { everyone: "all", contacts: "contacts" };
20
+ // Baileys' "mute unknown calls" (WhiskeySockets/Baileys#872), the phone's "silence unknown callers"; not checked live.
21
+ const CALL_ADD = { everyone: "all", contacts: "known" };
22
+ const AUDIENCES = [
23
+ ["lastSeen", "last"],
24
+ ["profilePhoto", "profile"],
25
+ ["about", "status"],
26
+ ];
27
+ const audienceOf = (values, value) => Object.keys(values).find((audience) => values[audience] === value);
28
+ const hidden = (privacy) => privacy.last === "none" && privacy.online === "match_last_seen";
29
+ /**
30
+ * What WhatsApp has of cli-core's settings. Who may add the account has no "contacts except" in cli-core, so such a
31
+ * value is left out there, never rounded to the nearest; calls have none in WhatsApp. Hidden online means last seen
32
+ * to nobody and online only to those who see last seen.
33
+ */
34
+ export const toPrivacy = (privacy) => {
35
+ const settings = {
36
+ calls: audienceOf(CALL_ADD, privacy.calladd),
37
+ chatInvites: audienceOf(GROUP_ADD, privacy.groupadd),
38
+ hideOnline: privacy.last === "" || privacy.online === "" ? undefined : hidden(privacy),
39
+ readReceipts: privacy.readreceipts === "" ? undefined : privacy.readreceipts === "all",
40
+ };
41
+ for (const [key, name] of AUDIENCES)
42
+ settings[key] = audienceOf(SHOWN, privacy[name]);
43
+ return Object.fromEntries(Object.entries(settings).filter(([, value]) => value !== undefined));
44
+ };
45
+ const refusedUpFront = (change) => {
46
+ const missing = Object.keys(change).find((key) => !PRIVACY_SETTINGS.includes(key));
47
+ if (missing)
48
+ return `WhatsApp has no ${missing} privacy setting`;
49
+ if (change.chatInvites === "nobody")
50
+ return "WhatsApp lets everyone or only contacts add the account to groups, not nobody";
51
+ if (change.calls === "nobody")
52
+ return "WhatsApp lets everyone call, or silences callers the account does not know (contacts), not nobody";
53
+ if (change.hideOnline !== undefined && change.lastSeen !== undefined)
54
+ return "--hide-online sets who sees last seen too; give --hide-online or --last-seen, not both";
55
+ for (const [key] of AUDIENCES)
56
+ if (change[key] === "contacts_except")
57
+ return "contacts_except is kept on the phone with its list of exceptions; wa only shows it";
58
+ return undefined;
59
+ };
60
+ /**
61
+ * The settings to send, only those that differ from now. Showing online again sets last seen to contacts: the
62
+ * audience before it was hidden is not kept anywhere, and contacts shows the least of what "not hidden" can mean.
63
+ */
64
+ const changesFor = (change, now) => {
65
+ const wanted = [];
66
+ for (const [key, name] of AUDIENCES) {
67
+ const audience = change[key];
68
+ if (audience !== undefined)
69
+ wanted.push({ name, value: SHOWN[audience] });
70
+ }
71
+ if (change.readReceipts !== undefined)
72
+ wanted.push({ name: "readreceipts", value: change.readReceipts ? "all" : "none" });
73
+ if (change.chatInvites !== undefined)
74
+ wanted.push({ name: "groupadd", value: GROUP_ADD[change.chatInvites] });
75
+ if (change.calls !== undefined)
76
+ wanted.push({ name: "calladd", value: CALL_ADD[change.calls] });
77
+ if (change.hideOnline === true)
78
+ wanted.push({ name: "last", value: "none" }, { name: "online", value: "match_last_seen" });
79
+ if (change.hideOnline === false && hidden(now))
80
+ wanted.push({ name: "last", value: "contacts" });
81
+ return wanted.filter(({ name, value }) => now[name] !== value);
82
+ };
83
+ export const privacyAdapter = ({ engine, translated, }) => ({
84
+ privacy: async () => toPrivacy(await translated(engine.privacy())),
85
+ updatePrivacy: async (change) => {
86
+ const refused = refusedUpFront(change);
87
+ if (refused)
88
+ throw new CliError("validation_error", refused);
89
+ const now = await translated(engine.privacy());
90
+ const changes = changesFor(change, now);
91
+ return toPrivacy(changes.length === 0 ? now : await translated(engine.setPrivacy(changes)));
92
+ },
93
+ });
94
+ //# sourceMappingURL=privacy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/adapter/privacy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAQ5C,8GAA8G;AAC9G,MAAM,CAAC,MAAM,gBAAgB,GAA0B;IACrD,UAAU;IACV,YAAY;IACZ,cAAc;IACd,OAAO;IACP,cAAc;IACd,aAAa;IACb,OAAO;CACR,CAAA;AAID,MAAM,KAAK,GAA0B;IACnC,QAAQ,EAAE,KAAK;IACf,QAAQ,EAAE,UAAU;IACpB,eAAe,EAAE,mBAAmB;IACpC,MAAM,EAAE,MAAM;CACf,CAAA;AACD,6GAA6G;AAC7G,MAAM,SAAS,GAAqB,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAA;AAC7E,uHAAuH;AACvH,MAAM,QAAQ,GAAqB,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAA;AAEzE,MAAM,SAAS,GAAG;IAChB,CAAC,UAAU,EAAE,MAAM,CAAC;IACpB,CAAC,cAAc,EAAE,SAAS,CAAC;IAC3B,CAAC,OAAO,EAAE,QAAQ,CAAC;CACX,CAAA;AAEV,MAAM,UAAU,GAAG,CAAmB,MAAiB,EAAE,KAAa,EAAiB,EAAE,CACtF,MAAM,CAAC,IAAI,CAAC,MAAM,CAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC,CAAA;AAE7E,MAAM,MAAM,GAAG,CAAC,OAAkB,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM,IAAI,OAAO,CAAC,MAAM,KAAK,iBAAiB,CAAA;AAEtG;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAkB,EAAmB,EAAE;IAC/D,MAAM,QAAQ,GAAoB;QAChC,KAAK,EAAE,UAAU,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC;QAC5C,WAAW,EAAE,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC,QAAQ,CAAC;QACpD,UAAU,EAAE,OAAO,CAAC,IAAI,KAAK,EAAE,IAAI,OAAO,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;QACtF,YAAY,EAAE,OAAO,CAAC,YAAY,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,KAAK,KAAK;KACvF,CAAA;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,SAAS;QAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACrF,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAA;AAChG,CAAC,CAAA;AAED,MAAM,cAAc,GAAG,CAAC,MAAuB,EAAsB,EAAE;IACrE,MAAM,OAAO,GAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAkB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAA;IACpG,IAAI,OAAO;QAAE,OAAO,mBAAmB,OAAO,kBAAkB,CAAA;IAChE,IAAI,MAAM,CAAC,WAAW,KAAK,QAAQ;QACjC,OAAO,+EAA+E,CAAA;IACxF,IAAI,MAAM,CAAC,KAAK,KAAK,QAAQ;QAC3B,OAAO,mGAAmG,CAAA;IAC5G,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,KAAK,SAAS;QAClE,OAAO,wFAAwF,CAAA;IACjG,KAAK,MAAM,CAAC,GAAG,CAAC,IAAI,SAAS;QAC3B,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,iBAAiB;YACnC,OAAO,oFAAoF,CAAA;IAC/F,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,UAAU,GAAG,CAAC,MAAuB,EAAE,GAAc,EAAqB,EAAE;IAChF,MAAM,MAAM,GAAsB,EAAE,CAAA;IACpC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAA;QAC5B,IAAI,QAAQ,KAAK,SAAS;YAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAW,EAAE,CAAC,CAAA;IACrF,CAAC;IACD,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS;QACnC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;IACpF,IAAI,MAAM,CAAC,WAAW,KAAK,SAAS;QAClC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,WAAW,CAAW,EAAE,CAAC,CAAA;IACnF,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS;QAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAW,EAAE,CAAC,CAAA;IACzG,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI;QAC5B,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAA;IAC5F,IAAI,MAAM,CAAC,UAAU,KAAK,KAAK,IAAI,MAAM,CAAC,GAAG,CAAC;QAAE,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAA;IAChG,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAA;AAChE,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAC7B,MAAM,EACN,UAAU,GAIX,EAAkB,EAAE,CAAC,CAAC;IACrB,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC,SAAS,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAElE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAC9B,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CAAC,CAAA;QACtC,IAAI,OAAO;YAAE,MAAM,IAAI,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAA;QAC5D,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAA;QAC9C,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;QACvC,OAAO,SAAS,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7F,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,21 @@
1
+ import type { Message } from "@wirecat/cli-core";
2
+ import type { MessengerAdapter, StoredMessages } from "@wirecat/cli-core/cli";
3
+ import type { UploadSource } from "@wirecat/cli-core/sends";
4
+ import { type WaKey, type WhatsAppEngine } from "../engine/contract.js";
5
+ import type { Words } from "./checks.js";
6
+ /** The account's own settings, which WhatsApp syncs to its other devices, and the profile everyone sees. */
7
+ export type SettingsAdapter = Required<Pick<MessengerAdapter, "mute" | "updateProfile" | "deleteChat" | "clearHistory" | "archiveChat" | "pinChat" | "markChatUnread">>;
8
+ export interface SettingsDeps {
9
+ engine: WhatsAppEngine;
10
+ translated: <T>(work: Promise<T>) => Promise<T>;
11
+ words: Words;
12
+ stored: StoredMessages;
13
+ keyOf: (chatId: string, message: Message) => WaKey;
14
+ /** An upload as a file the engine can read. */
15
+ onDisk: <T>(source: UploadSource, work: (path: string) => Promise<T>) => Promise<T>;
16
+ }
17
+ export declare const AFTER_EVERY_MESSAGE = "9999-12-31T23:59:59.999Z";
18
+ /** Translates a write to the synced settings, which needs the settings keys WhatsApp sends after linking. */
19
+ export declare const settingsWrite: (translated: SettingsDeps["translated"], words: Words) => <T>(work: Promise<T>) => Promise<T>;
20
+ export declare const settingsAdapter: ({ engine, translated, words, stored, keyOf, onDisk, }: SettingsDeps) => SettingsAdapter;
21
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/adapter/settings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAiB,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC5F,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAA8B,KAAK,KAAK,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAEnG,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAA;AAIxC,4GAA4G;AAC5G,MAAM,MAAM,eAAe,GAAG,QAAQ,CACpC,IAAI,CACF,gBAAgB,EAChB,MAAM,GAAG,eAAe,GAAG,YAAY,GAAG,cAAc,GAAG,aAAa,GAAG,SAAS,GAAG,gBAAgB,CACxG,CACF,CAAA;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,cAAc,CAAA;IACtB,UAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IAC/C,KAAK,EAAE,KAAK,CAAA;IACZ,MAAM,EAAE,cAAc,CAAA;IACtB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,CAAA;IAClD,+CAA+C;IAC/C,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;CACpF;AAGD,eAAO,MAAM,mBAAmB,6BAA6B,CAAA;AAE7D,6GAA6G;AAC7G,eAAO,MAAM,aAAa,eACX,YAAY,CAAC,YAAY,CAAC,SAAS,KAAK,MACpD,CAAC,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAY7B,CAAA;AAEL,eAAO,MAAM,eAAe,0DAOzB,YAAY,KAAG,eA+CjB,CAAA"}
@@ -0,0 +1,76 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ import { EngineError } from "../engine/contract.js";
3
+ import { isJpeg } from "./groups.js";
4
+ import { toAccount } from "./map.js";
5
+ // `anchor` gives the newest message sent at a time or before; this one is later than any message's.
6
+ export const AFTER_EVERY_MESSAGE = "9999-12-31T23:59:59.999Z";
7
+ /** Translates a write to the synced settings, which needs the settings keys WhatsApp sends after linking. */
8
+ export const settingsWrite = (translated, words) => (work) =>
9
+ // The engine cannot name the profile, so the command that brings the keys is named here. whatsmeow refuses before
10
+ // anything leaves, so a retry is safe (WC-OUT-20).
11
+ translated(work.catch((error) => {
12
+ if (!(error instanceof EngineError) || error.reason !== "settings.keys-missing")
13
+ throw error;
14
+ throw new CliError(error.code, `WhatsApp has not sent the settings keys yet; keep ${words("serve")} running a few minutes and try again`, { reason: error.reason, sent: false });
15
+ }));
16
+ export const settingsAdapter = ({ engine, translated, words, stored, keyOf, onDisk, }) => {
17
+ const keyed = settingsWrite(translated, words);
18
+ // WhatsApp applies the patch up to a message it names, either side's; a guessed one makes the phone drop the patch
19
+ // without a word, so a chat the store holds nothing of is refused.
20
+ const newest = async (chatId, purpose) => {
21
+ const last = await stored.anchor(chatId, AFTER_EVERY_MESSAGE);
22
+ if (!last)
23
+ throw new CliError("not_found", `nothing of this chat is in the local store, and WhatsApp needs its newest message to know ${purpose}; ` +
24
+ `${words("serve")} keeps the store filled`);
25
+ return { ...keyOf(chatId, last), timestamp: last.timestamp };
26
+ };
27
+ return {
28
+ mute: async (chatId, until) => {
29
+ await keyed(engine.mute(chatId, until));
30
+ },
31
+ deleteChat: async (chatId) => {
32
+ await keyed(engine.deleteChat(await newest(chatId, "what to delete")));
33
+ },
34
+ clearHistory: async (chatId) => {
35
+ await keyed(engine.clearChat(await newest(chatId, "what to delete")));
36
+ },
37
+ archiveChat: async (chatId, archived) => {
38
+ await keyed(engine.archiveChat(await newest(chatId, "what the archive covers"), archived));
39
+ },
40
+ pinChat: async (chatId, pinned) => {
41
+ await keyed(engine.pinChat(chatId, pinned));
42
+ },
43
+ markChatUnread: async (chatId) => {
44
+ await keyed(engine.markChatUnread(await newest(chatId, "what is unread")));
45
+ },
46
+ updateProfile: async (change) => {
47
+ const profile = toWaProfile(change);
48
+ const { photo } = change;
49
+ if (photo === undefined)
50
+ return toAccount(await keyed(engine.updateProfile(profile)));
51
+ if (!(await isJpeg(photo.source)))
52
+ throw new CliError("validation_error", `${photo.name} is not a JPEG — WhatsApp takes a profile photo only as one`);
53
+ return toAccount(await keyed(onDisk(photo.source, (path) => engine.updateProfile({ ...profile, path }))));
54
+ },
55
+ };
56
+ };
57
+ /** WhatsApp keeps one name, so a first and a last name are joined into it; a last name alone has nothing to join. */
58
+ const toWaProfile = ({ firstName, lastName, description }) => {
59
+ if (firstName === undefined && lastName !== undefined)
60
+ throw new CliError("validation_error", "WhatsApp has one name — pass --first-name, and --last-name is added to it after a space");
61
+ if (description !== undefined && description.trim() === "")
62
+ throw new CliError("validation_error", "a WhatsApp about cannot be empty");
63
+ const pushName = firstName === undefined
64
+ ? undefined
65
+ : [firstName, lastName]
66
+ .map((part) => part?.trim())
67
+ .filter(Boolean)
68
+ .join(" ");
69
+ if (pushName === "")
70
+ throw new CliError("validation_error", "a WhatsApp name cannot be empty");
71
+ return {
72
+ ...(pushName === undefined ? {} : { pushName }),
73
+ ...(description === undefined ? {} : { about: description }),
74
+ };
75
+ };
76
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/adapter/settings.ts"],"names":[],"mappings":"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"}
package/dist/app.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ import type { AppIdentity } from "@wirecat/cli-core/cli";
2
+ import { type Configuration } from "@wirecat/cli-core/cli";
3
+ export declare const WA: AppIdentity;
4
+ export declare const CONFIG: Configuration;
5
+ export declare const resolveSettings: (flags?: import("@wirecat/cli-core/cli").GlobalFlags, options?: import("@wirecat/cli-core/cli").ResolveOptions) => import("@wirecat/cli-core/cli").Settings & object;
6
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,KAAK,aAAa,EAAe,MAAM,uBAAuB,CAAA;AAIvE,eAAO,MAAM,EAAE,EAAE,WAShB,CAAA;AAED,eAAO,MAAM,MAAM,EAAE,aAA+B,CAAA;AACpD,eAAO,MAAQ,eAAe,sKAAW,CAAA"}
package/dist/app.js ADDED
@@ -0,0 +1,16 @@
1
+ import { settingsFor } from "@wirecat/cli-core/cli";
2
+ import { REASONS } from "./reasons.js";
3
+ import { VERSION } from "./version.js";
4
+ export const WA = {
5
+ command: "wa",
6
+ appName: "whatsapp-cli",
7
+ envPrefix: "WA",
8
+ description: "A personal WhatsApp account from the command line, as a linked device, for agents and scripts",
9
+ version: VERSION,
10
+ issues: "https://github.com/WireCatLabs/whatsapp-cli/issues/new",
11
+ locale: "en-GB",
12
+ errors: { reasons: REASONS },
13
+ };
14
+ export const CONFIG = settingsFor(WA);
15
+ export const { resolveSettings } = CONFIG;
16
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAEtC,MAAM,CAAC,MAAM,EAAE,GAAgB;IAC7B,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,cAAc;IACvB,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,+FAA+F;IAC5G,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,wDAAwD;IAChE,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE;CAC7B,CAAA;AAED,MAAM,CAAC,MAAM,MAAM,GAAkB,WAAW,CAAC,EAAE,CAAC,CAAA;AACpD,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,CAAA"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=wa.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wa.d.ts","sourceRoot":"","sources":["../../src/bin/wa.ts"],"names":[],"mappings":""}