@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 @@
1
+ {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../../src/adapter/channels.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAA;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3E,OAAO,EAAoB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAE7C,2EAA2E;AAC3E,eAAO,MAAM,SAAS,OAAQ,MAAM,YAA+B,CAAA;AAEnE,eAAO,MAAM,YAAY,QAAwE,CAAA;AAEjG,qHAAqH;AACrH,eAAO,MAAM,aAAa,YAAa,SAAS,KAAG,SAejD,CAAA;AAOF;;;GAGG;AACH,eAAO,MAAM,cAAc,WACjB,cAAc,UACd,cAAc,UACd,MAAM,qBACK;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,KACpD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAcvB,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,WACpB,cAAc,cACV,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,UACvC,MAAM,oBACI;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,KAClD,OAAO,CAAC,IAAI,CAMd,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,cAAc,CAAA;IACtB,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7C,UAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IAC/C,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,CAAA;CAC7C;AAED,iHAAiH;AACjH,eAAO,MAAM,cAAc,6CAKxB,WAAW,KAAG,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAUlD,CAAA;AAEF,KAAK,aAAa,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,YAAY,GAAG,MAAM,CAAC,CAAA;AAKnH;;;GAGG;AACH,eAAO,MAAM,aAAa,wDAAyD,aAAa,KAAG,aAqBjG,CAAA"}
@@ -0,0 +1,100 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ import { CHANNEL_PAGE_MAX } from "../engine/channels.js";
3
+ import { toChat, toMessage } from "./map.js";
4
+ /** WhatsApp channels are whatsmeow's newsletters, and their ids end so. */
5
+ export const isChannel = (id) => id.endsWith("@newsletter");
6
+ export const CHANNEL_LINK = /^\s*(?:https?:\/\/)?(?:www\.)?whatsapp\.com\/channel\/[A-Za-z0-9]+/i;
7
+ /** A channel as `chats join` answers it: only its admins post, and WhatsApp has none of a group's other switches. */
8
+ export const toChannelCard = (channel) => ({
9
+ ...toChat(channel.chat),
10
+ description: channel.description,
11
+ link: channel.link,
12
+ settings: {
13
+ allCanPin: null,
14
+ onlyAdminsAdd: null,
15
+ onlyAdminsCall: null,
16
+ onlyOwnerEditsInfo: null,
17
+ membersSeeLink: null,
18
+ joinApproval: null,
19
+ onlyAdminsSend: true,
20
+ onlyAdminsEditInfo: null,
21
+ disappearingSeconds: null,
22
+ },
23
+ });
24
+ const serverIdOf = (message) => {
25
+ const id = message?.providerMetadata?.serverId;
26
+ return typeof id === "number" ? id : undefined;
27
+ };
28
+ /**
29
+ * A channel's posts live on WhatsApp's servers, so `messages sync` reads them there: the newest first, then back
30
+ * from the server id of the stored post the last page reached. Nothing stored is needed to start.
31
+ */
32
+ export const channelHistory = async (engine, stored, chatId, { limit, before }) => {
33
+ let from;
34
+ if (before !== undefined) {
35
+ const anchor = await stored.anchor(chatId, before);
36
+ from = serverIdOf(anchor);
37
+ if (from === undefined)
38
+ throw new CliError("not_found", "no stored post of this channel has a server id to count back from, so WhatsApp cannot be asked for older ones");
39
+ }
40
+ const page = await engine.channelMessages(chatId, Math.min(limit, CHANNEL_PAGE_MAX), from);
41
+ const items = page.messages.map(toMessage).sort((one, other) => one.timestamp.localeCompare(other.timestamp));
42
+ return { items, hasMore: page.more };
43
+ };
44
+ /**
45
+ * Only a channel's owner and admins post, and WhatsApp says the account's role only when asked, so a post asks first.
46
+ * The role is asked before anything is uploaded. One file per post: whatsmeow's newsletter send has no album handling.
47
+ */
48
+ export const refuseChannelPost = async (engine, translated, chatId, { files, reply }) => {
49
+ if (files > 1)
50
+ throw new CliError("validation_error", "wa posts one file at a time to a WhatsApp channel");
51
+ if (reply)
52
+ throw new CliError("validation_error", "a WhatsApp channel post cannot be a reply");
53
+ const { role } = await translated(engine.channel(chatId));
54
+ if (role !== "owner" && role !== "admin")
55
+ throw new CliError("permission_error", "only a channel's owner and admins may post in it");
56
+ };
57
+ /** `chats join` and `chats leave` follow and unfollow a channel; a group's link or id goes on to the group's. */
58
+ export const channelAdapter = ({ engine, resolve, translated, groups, }) => ({
59
+ join: async (link) => CHANNEL_LINK.test(link) ? toChannelCard(await translated(engine.followChannel(link))) : groups.join(link),
60
+ leave: async (chat) => {
61
+ const { id } = await resolve(chat);
62
+ if (!isChannel(id))
63
+ return groups.leave(chat);
64
+ await translated(engine.unfollowChannel(id));
65
+ return { chatId: id };
66
+ },
67
+ });
68
+ const NOT_IN_CHANNELS = () => new CliError("validation_error", "wa does not do that in a WhatsApp channel yet; it posts text and single files only");
69
+ /**
70
+ * The writes wa has not built for a channel are refused before anything is sent: whatsmeow would send each of them
71
+ * to a channel as a post, and WhatsApp addresses a post's edit, deletion and votes by its server id.
72
+ */
73
+ export const channelWrites = ({ edit, delete: remove, forward, createPoll, vote }) => ({
74
+ edit: async (chatId, ...rest) => {
75
+ if (isChannel(chatId))
76
+ throw NOT_IN_CHANNELS();
77
+ return edit(chatId, ...rest);
78
+ },
79
+ delete: async (chatId, ...rest) => {
80
+ if (isChannel(chatId))
81
+ throw NOT_IN_CHANNELS();
82
+ return remove(chatId, ...rest);
83
+ },
84
+ forward: async (fromChatId, messageId, toChatId, options) => {
85
+ if (isChannel(toChatId))
86
+ throw NOT_IN_CHANNELS();
87
+ return forward(fromChatId, messageId, toChatId, options);
88
+ },
89
+ createPoll: async (chatId, ...rest) => {
90
+ if (isChannel(chatId))
91
+ throw NOT_IN_CHANNELS();
92
+ return createPoll(chatId, ...rest);
93
+ },
94
+ vote: async (chatId, ...rest) => {
95
+ if (isChannel(chatId))
96
+ throw NOT_IN_CHANNELS();
97
+ return vote(chatId, ...rest);
98
+ },
99
+ });
100
+ //# sourceMappingURL=channels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.js","sourceRoot":"","sources":["../../src/adapter/channels.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { CheckWords } from "@wirecat/cli-core";
2
+ /** The commands a message sends a person to; the command layer names them in its own words. */
3
+ export type Check = "chats.list" | "serve" | "session.start";
4
+ /** A command as the person types it, in one pair of backticks. */
5
+ export type Words = CheckWords<Check>;
6
+ //# sourceMappingURL=checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/adapter/checks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD,+FAA+F;AAC/F,MAAM,MAAM,KAAK,GAAG,YAAY,GAAG,OAAO,GAAG,eAAe,CAAA;AAE5D,kEAAkE;AAClE,MAAM,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/adapter/checks.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ import type { EngineError } from "../engine/contract.js";
3
+ /**
4
+ * WhatsApp's 429 says neither how long nor whether reads count, so writes wait this long and reads go on
5
+ * (WC-PERM-18). The owner's to change: docs/dev/BACKLOG.md.
6
+ */
7
+ export declare const RATE_LIMIT_HOLD_MS: number;
8
+ /**
9
+ * An engine refusal in cli-core's terms, its reason kept. A rate limit and a ban carry `standing`, which cli-core turns
10
+ * into a hold on the profile's writes.
11
+ */
12
+ export declare const fromEngine: (error: EngineError, now?: number) => CliError;
13
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/adapter/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAExD;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAc,CAAA;AAI7C;;;GAGG;AACH,eAAO,MAAM,UAAU,UAAW,WAAW,mBAAqB,QAyBjE,CAAA"}
@@ -0,0 +1,38 @@
1
+ import { CliError } from "@wirecat/cli-base";
2
+ /**
3
+ * WhatsApp's 429 says neither how long nor whether reads count, so writes wait this long and reads go on
4
+ * (WC-PERM-18). The owner's to change: docs/dev/BACKLOG.md.
5
+ */
6
+ export const RATE_LIMIT_HOLD_MS = 15 * 60_000;
7
+ const at = (ms) => new Date(ms).toISOString();
8
+ /**
9
+ * An engine refusal in cli-core's terms, its reason kept. A rate limit and a ban carry `standing`, which cli-core turns
10
+ * into a hold on the profile's writes.
11
+ */
12
+ export const fromEngine = (error, now = Date.now()) => {
13
+ const reason = error.reason === undefined ? {} : { reason: error.reason };
14
+ if (error.reason === "account.rate-limited")
15
+ return new CliError(error.code, `${error.message}; this profile holds its writes for 15 minutes`, {
16
+ ...reason,
17
+ retry_after_ms: RATE_LIMIT_HOLD_MS,
18
+ standing: {
19
+ state: "limited",
20
+ hint: "WhatsApp asked this account to slow down",
21
+ until: at(now + RATE_LIMIT_HOLD_MS),
22
+ },
23
+ });
24
+ if (error.reason === "account.banned") {
25
+ const ends = error.until === undefined ? Number.NaN : Date.parse(error.until);
26
+ const dated = ends > now;
27
+ return new CliError(error.code, dated ? `${error.message}; the ban ends ${at(ends)}` : error.message, {
28
+ ...reason,
29
+ standing: {
30
+ state: "banned",
31
+ hint: "WhatsApp banned or restricted this account",
32
+ ...(dated ? { until: at(ends) } : {}),
33
+ },
34
+ });
35
+ }
36
+ return new CliError(error.code, error.message, reason);
37
+ };
38
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/adapter/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAI5C;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,GAAG,MAAM,CAAA;AAE7C,MAAM,EAAE,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;AAErD;;;GAGG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAkB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,EAAY,EAAE;IAC3E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,CAAA;IACzE,IAAI,KAAK,CAAC,MAAM,KAAK,sBAAsB;QACzC,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,KAAK,CAAC,OAAO,gDAAgD,EAAE;YAChG,GAAG,MAAM;YACT,cAAc,EAAE,kBAAkB;YAClC,QAAQ,EAAE;gBACR,KAAK,EAAE,SAAS;gBAChB,IAAI,EAAE,0CAA0C;gBAChD,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,kBAAkB,CAAC;aACV;SAC5B,CAAC,CAAA;IACJ,IAAI,KAAK,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QAC7E,MAAM,KAAK,GAAG,IAAI,GAAG,GAAG,CAAA;QACxB,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,kBAAkB,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE;YACpG,GAAG,MAAM;YACT,QAAQ,EAAE;gBACR,KAAK,EAAE,QAAQ;gBACf,IAAI,EAAE,4CAA4C;gBAClD,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACZ;SAC5B,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;AACxD,CAAC,CAAA"}
@@ -0,0 +1,8 @@
1
+ import type { TextSpan } from "@wirecat/cli-core";
2
+ /**
3
+ * Text and its spans in WhatsApp's marks: `*bold*`, `_italic_`, `~strike~`, `` `code` ``, ```` ``` ```` blocks and
4
+ * `> ` quotes. A link is its label and then its address, which WhatsApp makes clickable. Underline, spoiler and
5
+ * highlight have no mark there and stay plain text, and a span that crosses another without nesting is dropped.
6
+ */
7
+ export declare const toWhatsApp: (text: string, spans: readonly TextSpan[]) => string;
8
+ //# sourceMappingURL=format-markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-markdown.d.ts","sourceRoot":"","sources":["../../src/adapter/format-markdown.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAiDjD;;;;GAIG;AACH,eAAO,MAAM,UAAU,SAAU,MAAM,SAAS,SAAS,QAAQ,EAAE,KAAG,MA6BrE,CAAA"}
@@ -0,0 +1,80 @@
1
+ const MARKS = {
2
+ bold: "*",
3
+ heading: "*",
4
+ italic: "_",
5
+ strike: "~",
6
+ code: "`",
7
+ pre: "```",
8
+ };
9
+ // WhatsApp draws these on one line only.
10
+ const LINE_ONLY = new Set(["bold", "heading", "italic", "strike", "code"]);
11
+ /** WhatsApp ignores a mark with a space just inside it, so the space moves outside. */
12
+ const trimmed = (text, span, from, to) => {
13
+ // A code block's indentation is part of the code.
14
+ while (from < to && span.type !== "pre" && /\s/u.test(text[from] ?? ""))
15
+ from++;
16
+ while (to > from && /\s/u.test(text[to - 1] ?? ""))
17
+ to--;
18
+ return to > from ? { ...span, from, length: to - from } : undefined;
19
+ };
20
+ const perLine = (text, span) => {
21
+ const end = span.from + span.length;
22
+ if (!LINE_ONLY.has(span.type)) {
23
+ const whole = trimmed(text, span, span.from, end);
24
+ return whole ? [whole] : [];
25
+ }
26
+ const pieces = [];
27
+ for (let start = span.from; start < end;) {
28
+ const newline = text.indexOf("\n", start);
29
+ const stop = newline === -1 || newline >= end ? end : newline;
30
+ const piece = trimmed(text, span, start, stop);
31
+ if (piece)
32
+ pieces.push(piece);
33
+ start = stop + 1;
34
+ }
35
+ return pieces;
36
+ };
37
+ const contains = (outer, inner) => outer.from <= inner.from && inner.from + inner.length <= outer.from + outer.length;
38
+ const apart = (a, b) => a.from + a.length <= b.from || b.from + b.length <= a.from;
39
+ const linkTail = (text, span) => {
40
+ const label = text.slice(span.from, span.from + span.length);
41
+ return span.url === undefined || span.url === label || span.url === `mailto:${label}` ? "" : ` (${span.url})`;
42
+ };
43
+ /**
44
+ * Text and its spans in WhatsApp's marks: `*bold*`, `_italic_`, `~strike~`, `` `code` ``, ```` ``` ```` blocks and
45
+ * `> ` quotes. A link is its label and then its address, which WhatsApp makes clickable. Underline, spoiler and
46
+ * highlight have no mark there and stay plain text, and a span that crosses another without nesting is dropped.
47
+ */
48
+ export const toWhatsApp = (text, spans) => {
49
+ const kept = [];
50
+ const ordered = spans
51
+ .flatMap((span) => perLine(text, span))
52
+ .sort((a, b) => a.from - b.from || b.length - a.length || a.type.localeCompare(b.type));
53
+ for (const span of ordered)
54
+ if (kept.every((one) => contains(one, span) || apart(one, span)))
55
+ kept.push(span);
56
+ let out = "";
57
+ const open = [];
58
+ let next = 0;
59
+ for (let at = 0; at <= text.length; at++) {
60
+ for (let top = open.at(-1); top && top.from + top.length === at; top = open.at(-1)) {
61
+ open.pop();
62
+ out += top.type === "link" ? linkTail(text, top) : (MARKS[top.type] ?? "");
63
+ }
64
+ for (; kept[next]?.from === at; next++) {
65
+ const span = kept[next];
66
+ open.push(span);
67
+ out += span.type === "blockquote" ? "> " : (MARKS[span.type] ?? "");
68
+ }
69
+ if (at === text.length)
70
+ break;
71
+ out += text[at];
72
+ if (text[at] === "\n")
73
+ out += open
74
+ .filter((span) => span.type === "blockquote")
75
+ .map(() => "> ")
76
+ .join("");
77
+ }
78
+ return out;
79
+ };
80
+ //# sourceMappingURL=format-markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-markdown.js","sourceRoot":"","sources":["../../src/adapter/format-markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { Chat, GroupCard } from "@wirecat/cli-core";
2
+ import type { MessengerAdapter } from "@wirecat/cli-core/cli";
3
+ import type { UploadSource } from "@wirecat/cli-core/sends";
4
+ import type { WhatsAppEngine } from "../engine/contract.js";
5
+ import type { WaGroup } from "../engine/groups.js";
6
+ /** Group moderation and admin, join requests, profiles, blocking, number lookup and the account's health. */
7
+ export type GroupAdapter = Required<Pick<MessengerAdapter, "members" | "admins" | "people" | "createGroup" | "join" | "leave" | "group" | "updateGroup" | "resetInviteLink" | "addMembers" | "removeMembers" | "addAdmin" | "removeAdmin" | "joinRequests" | "answerJoinRequest" | "profile" | "block" | "unblock" | "lookup" | "addressBook" | "health">>;
8
+ /** What a WhatsApp admin may do. WhatsApp has one kind of admin, with all of these at once. */
9
+ export declare const ADMIN_RIGHTS: ["members", "admins", "info", "pin", "link", "post", "delete"];
10
+ /** The switches of cli-core's `chats update` that WhatsApp has. */
11
+ export declare const GROUP_SETTINGS: readonly ["onlyAdminsAdd", "joinApproval", "onlyAdminsSend", "onlyAdminsEditInfo", "disappearingSeconds"];
12
+ /** A group as `chats update` and `chats create` answer it. */
13
+ export declare const toGroupCard: (group: WaGroup) => GroupCard;
14
+ export interface GroupDeps {
15
+ engine: WhatsAppEngine;
16
+ resolve: (reference: string) => Promise<Chat>;
17
+ translated: <T>(work: Promise<T>) => Promise<T>;
18
+ /** Whether the owner has written to this person; from cli-core's store. */
19
+ wroteTo: (personId: string) => Promise<boolean>;
20
+ /** An upload as a file the engine can read. */
21
+ onDisk: <T>(source: UploadSource, work: (path: string) => Promise<T>) => Promise<T>;
22
+ }
23
+ export declare const isJpeg: (source: UploadSource) => Promise<boolean>;
24
+ export declare const groupAdapter: ({ engine, resolve, translated, wroteTo, onDisk }: GroupDeps) => GroupAdapter;
25
+ //# sourceMappingURL=groups.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.d.ts","sourceRoot":"","sources":["../../src/adapter/groups.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAc,IAAI,EAAE,SAAS,EAA6B,MAAM,mBAAmB,CAAA;AAC/F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAiB,MAAM,qBAAqB,CAAA;AAGjE,6GAA6G;AAC7G,MAAM,MAAM,YAAY,GAAG,QAAQ,CACjC,IAAI,CACF,gBAAgB,EACd,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,aAAa,GACb,MAAM,GACN,OAAO,GACP,OAAO,GACP,aAAa,GACb,iBAAiB,GACjB,YAAY,GACZ,eAAe,GACf,UAAU,GACV,aAAa,GACb,cAAc,GACd,mBAAmB,GACnB,SAAS,GACT,OAAO,GACP,SAAS,GACT,QAAQ,GACR,aAAa,GACb,QAAQ,CACX,CACF,CAAA;AAED,+FAA+F;AAC/F,eAAO,MAAM,YAAY,gEAQQ,CAAA;AAEjC,mEAAmE;AACnE,eAAO,MAAM,cAAc,YACzB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,CACb,CAAA;AAUV,8DAA8D;AAC9D,eAAO,MAAM,WAAW,UAAW,OAAO,KAAG,SAoB5C,CAAA;AASD,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,cAAc,CAAA;IACtB,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7C,UAAU,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAA;IAC/C,2EAA2E;IAC3E,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;IAC/C,+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,MAAM,WAAkB,YAAY,KAAG,OAAO,CAAC,OAAO,CAYlE,CAAA;AAED,eAAO,MAAM,YAAY,qDAAsD,SAAS,KAAG,YA4M1F,CAAA"}
@@ -0,0 +1,252 @@
1
+ import { open } from "node:fs/promises";
2
+ import { CliError, isCliError } from "@wirecat/cli-base";
3
+ import { toChat, toMember } from "./map.js";
4
+ /** What a WhatsApp admin may do. WhatsApp has one kind of admin, with all of these at once. */
5
+ export const ADMIN_RIGHTS = [
6
+ "members",
7
+ "admins",
8
+ "info",
9
+ "pin",
10
+ "link",
11
+ "post",
12
+ "delete",
13
+ ];
14
+ /** The switches of cli-core's `chats update` that WhatsApp has. */
15
+ export const GROUP_SETTINGS = [
16
+ "onlyAdminsAdd",
17
+ "joinApproval",
18
+ "onlyAdminsSend",
19
+ "onlyAdminsEditInfo",
20
+ "disappearingSeconds",
21
+ ];
22
+ /** The timers WhatsApp's apps offer, in seconds; its server refuses any other in a group. */
23
+ const DISAPPEARING_SECONDS = [0, 86_400, 604_800, 7_776_000];
24
+ /** WhatsApp refuses a longer group name; the engine's `groupNameLimit`. */
25
+ const GROUP_NAME_LIMIT = 25;
26
+ const isPerson = (id) => id.endsWith("@lid") || id.endsWith("@s.whatsapp.net");
27
+ /** A group as `chats update` and `chats create` answer it. */
28
+ export const toGroupCard = (group) => {
29
+ // A null parent tells the store to clear it; an answer leaves it out, as the store's own answer does.
30
+ const { parentChatId, ...chat } = toChat(group.chat);
31
+ return {
32
+ ...chat,
33
+ ...(parentChatId == null ? {} : { parentChatId }),
34
+ description: group.description,
35
+ link: group.link,
36
+ settings: {
37
+ allCanPin: null,
38
+ onlyAdminsAdd: group.addMode === "" ? null : group.addMode === "admin_add",
39
+ onlyAdminsCall: null,
40
+ onlyOwnerEditsInfo: null,
41
+ membersSeeLink: null,
42
+ joinApproval: group.approval,
43
+ onlyAdminsSend: group.announce,
44
+ onlyAdminsEditInfo: group.locked,
45
+ disappearingSeconds: group.ephemeral,
46
+ },
47
+ };
48
+ };
49
+ const toGroupMember = ({ jid, name, role }) => ({
50
+ id: jid,
51
+ name,
52
+ username: null,
53
+ role,
54
+ });
55
+ // A JPEG starts with a start-of-image marker and then another marker; the file's name says nothing.
56
+ export const isJpeg = async (source) => {
57
+ const head = Buffer.alloc(3);
58
+ if (source.kind === "bytes")
59
+ head.set(source.bytes.subarray(0, 3));
60
+ else {
61
+ const file = await open(source.path);
62
+ try {
63
+ await file.read(head, 0, 3, 0);
64
+ }
65
+ finally {
66
+ await file.close();
67
+ }
68
+ }
69
+ return head[0] === 0xff && head[1] === 0xd8 && head[2] === 0xff;
70
+ };
71
+ export const groupAdapter = ({ engine, resolve, translated, wroteTo, onDisk }) => {
72
+ const groupId = async (reference) => {
73
+ const { id } = await resolve(reference);
74
+ if (!id.endsWith("@g.us"))
75
+ throw new CliError("validation_error", "that chat is not a WhatsApp group");
76
+ return id;
77
+ };
78
+ const personId = async (reference) => {
79
+ const { id } = await resolve(reference);
80
+ if (!isPerson(id))
81
+ throw new CliError("validation_error", "that is a chat, not a person");
82
+ return id;
83
+ };
84
+ // WhatsApp bans accounts that add strangers to groups, so only someone in the address book or written to before.
85
+ const refuseStrangers = async (people) => {
86
+ const book = new Set((await translated(engine.contacts())).map((contact) => contact.jid));
87
+ for (const person of people) {
88
+ if (book.has(person) || (await wroteTo(person)))
89
+ continue;
90
+ throw new CliError("permission_error", "wa adds only people in your address book or that you have written to — WhatsApp bans accounts that add strangers");
91
+ }
92
+ };
93
+ return {
94
+ members: async (chat, { limit, offset }) => {
95
+ const group = await translated(engine.group(await groupId(chat)));
96
+ const end = limit === undefined ? group.members.length : offset + limit;
97
+ return {
98
+ chatId: group.chat.jid,
99
+ items: group.members.slice(offset, end).map(toGroupMember),
100
+ hasMore: group.members.length > end,
101
+ participantsCount: group.members.length,
102
+ };
103
+ },
104
+ admins: async (chat) => {
105
+ const group = await translated(engine.group(await groupId(chat)));
106
+ return group.members.filter((member) => member.role !== "member").map((member) => member.jid);
107
+ },
108
+ people: async (references) => {
109
+ const chats = await translated(engine.directory());
110
+ return references.map((reference) => {
111
+ const typed = reference.trim();
112
+ if (typed.includes("@")) {
113
+ if (!isPerson(typed))
114
+ throw new CliError("validation_error", "that is a chat, not a person");
115
+ return typed;
116
+ }
117
+ const wanted = typed.toLowerCase();
118
+ const people = chats.filter((chat) => chat.kind === "dm" && isPerson(chat.jid) && chat.name !== null);
119
+ const exact = people.filter((chat) => chat.name?.toLowerCase() === wanted);
120
+ const found = exact.length > 0 ? exact : people.filter((chat) => chat.name?.toLowerCase().includes(wanted));
121
+ if (found.length === 1 && found[0])
122
+ return found[0].jid;
123
+ if (found.length > 1)
124
+ throw new CliError("validation_error", `${found.length} people match that name — name one by their id (…@lid)`);
125
+ throw new CliError("not_found", "nobody by that name in your WhatsApp contacts; name them by their id (…@lid)");
126
+ });
127
+ },
128
+ createGroup: async (title, people, { channel }) => {
129
+ if (channel)
130
+ throw new CliError("validation_error", "wa makes groups only; WhatsApp channels are made in the app");
131
+ await refuseStrangers(people);
132
+ const { group, notAdded } = await translated(engine.createGroup(title, people));
133
+ const card = toGroupCard(group);
134
+ return notAdded.length === 0 ? card : { ...card, providerMetadata: { ...card.providerMetadata, notAdded } };
135
+ },
136
+ join: async (link) => {
137
+ const joined = await translated(engine.join(link));
138
+ return joined.group ? toGroupCard(joined.group) : { requested: true };
139
+ },
140
+ leave: async (chat) => {
141
+ const chatId = await groupId(chat);
142
+ await translated(engine.leave(chatId));
143
+ return { chatId };
144
+ },
145
+ group: async (chat) => toGroupCard(await translated(engine.group(await groupId(chat), { withLink: true }))),
146
+ updateGroup: async (chatId, { title, description, settings = {}, photo, removePhoto }) => {
147
+ if (photo !== undefined && !(await isJpeg(photo.source)))
148
+ throw new CliError("validation_error", `${photo.name} is not a JPEG — WhatsApp takes a group photo only as one`);
149
+ const missing = Object.keys(settings).filter((key) => !GROUP_SETTINGS.includes(key));
150
+ if (missing.length > 0)
151
+ throw new CliError("validation_error", `WhatsApp has no group setting ${missing.join(", ")}`);
152
+ const { onlyAdminsAdd, joinApproval, onlyAdminsSend, onlyAdminsEditInfo, disappearingSeconds } = settings;
153
+ if (typeof disappearingSeconds === "number" && !DISAPPEARING_SECONDS.includes(disappearingSeconds))
154
+ throw new CliError("validation_error", "WhatsApp makes messages disappear after 24h, 7d or 90d; off keeps them");
155
+ const change = {
156
+ ...(title === undefined ? {} : { title }),
157
+ ...(description === undefined ? {} : { description }),
158
+ ...(typeof onlyAdminsAdd === "boolean" ? { addMode: onlyAdminsAdd ? "admin_add" : "all_member_add" } : {}),
159
+ ...(typeof joinApproval === "boolean" ? { approval: joinApproval } : {}),
160
+ ...(typeof onlyAdminsSend === "boolean" ? { announce: onlyAdminsSend } : {}),
161
+ ...(typeof onlyAdminsEditInfo === "boolean" ? { locked: onlyAdminsEditInfo } : {}),
162
+ ...(typeof disappearingSeconds === "number" ? { ephemeral: disappearingSeconds } : {}),
163
+ };
164
+ // The engine checks the name too, but only after the photo is set: a refusal then would hide that it changed.
165
+ if (title !== undefined && [...title].length > GROUP_NAME_LIMIT)
166
+ throw new CliError("validation_error", "WhatsApp takes a group name of 1 to 25 characters");
167
+ const jid = await groupId(chatId);
168
+ // The photo goes first: the engine refuses one WhatsApp would not take, and then nothing else has changed.
169
+ const pictured = removePhoto === true
170
+ ? await translated(engine.removeGroupPhoto(jid))
171
+ : photo === undefined
172
+ ? undefined
173
+ : await translated(onDisk(photo.source, (path) => engine.setGroupPhoto(jid, path)));
174
+ if (pictured === undefined)
175
+ return toGroupCard(await translated(engine.updateGroup(jid, change)));
176
+ if (Object.keys(change).length === 0)
177
+ return toGroupCard(pictured);
178
+ try {
179
+ return toGroupCard(await translated(engine.updateGroup(jid, change)));
180
+ }
181
+ catch (error) {
182
+ if (!isCliError(error))
183
+ throw error;
184
+ throw new CliError(error.code, `the photo was ${removePhoto === true ? "removed" : "set"}, the rest of the change was not: ${error.message}`, error.details);
185
+ }
186
+ },
187
+ resetInviteLink: async (chatId) => toGroupCard(await translated(engine.resetInviteLink(await groupId(chatId)))),
188
+ addMembers: async (chatId, people, { history }) => {
189
+ if (history)
190
+ throw new CliError("validation_error", "WhatsApp does not show people added the messages from before");
191
+ await refuseStrangers(people);
192
+ return translated(engine.participants(await groupId(chatId), "add", people));
193
+ },
194
+ removeMembers: async (chatId, people) => {
195
+ await translated(engine.participants(await groupId(chatId), "remove", people));
196
+ },
197
+ addAdmin: async (chatId, person) => {
198
+ await translated(engine.participants(await groupId(chatId), "promote", [person]));
199
+ },
200
+ removeAdmin: async (chatId, person) => {
201
+ await translated(engine.participants(await groupId(chatId), "demote", [person]));
202
+ },
203
+ joinRequests: async (chatId, { limit, search, link }) => {
204
+ if (link !== undefined)
205
+ throw new CliError("validation_error", "a WhatsApp group has one invite link; leave out --link");
206
+ const wanted = search?.toLowerCase();
207
+ const found = (await translated(engine.joinRequests(await groupId(chatId))))
208
+ .filter((one) => wanted === undefined || (one.name?.toLowerCase().includes(wanted) ?? false))
209
+ .sort((a, b) => (b.requestedAt ?? "").localeCompare(a.requestedAt ?? ""));
210
+ return {
211
+ items: found.slice(0, limit).map((one) => ({
212
+ person: { id: one.jid, name: one.name, username: null },
213
+ requestedAt: one.requestedAt,
214
+ })),
215
+ hasMore: found.length > limit,
216
+ total: found.length,
217
+ };
218
+ },
219
+ answerJoinRequest: async (chatId, person, accept) => {
220
+ await translated(engine.answerJoinRequest(await groupId(chatId), person, accept));
221
+ return { already: false };
222
+ },
223
+ profile: async (reference) => {
224
+ const found = await translated(engine.profile(await personId(reference)));
225
+ return {
226
+ id: found.jid,
227
+ name: found.name,
228
+ usernames: [],
229
+ bio: found.about,
230
+ flags: found.verified ? { verified: true } : {},
231
+ contact: found.contact,
232
+ ...(found.photo === null ? {} : { hasPhoto: found.photo }),
233
+ chats: [],
234
+ };
235
+ },
236
+ block: async (person) => {
237
+ await translated(engine.block(person, true));
238
+ },
239
+ unblock: async (person) => {
240
+ await translated(engine.block(person, false));
241
+ },
242
+ lookup: async (phone) => toMember(await translated(engine.lookup(phone))),
243
+ addressBook: async () => (await translated(engine.contacts())).map(toMember),
244
+ // Connecting is the check: WhatsApp refuses a banned account's connection, and the engine answers with the ban,
245
+ // whose `standing` holds the profile's writes. A connection lifts that hold (`doctor --online`, WC-PERM-18).
246
+ health: async () => {
247
+ await translated(engine.ready(new AbortController().signal));
248
+ return { standingChecked: true };
249
+ },
250
+ };
251
+ };
252
+ //# sourceMappingURL=groups.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"groups.js","sourceRoot":"","sources":["../../src/adapter/groups.ts"],"names":[],"mappings":"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"}