morpho-socket 0.2.0 → 0.3.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.
@@ -97,7 +97,7 @@ const g = await contexts.build({ sock, msg, clientId })
97
97
  | who it's about | `await g.who()` — the mentioned person, else the replied-to author |
98
98
  | permissions | `g.isOwner` · `await g.isAdmin()` · `await g.isBotAdmin()` |
99
99
  | to answer | `g.reply(…)` · `g.send(jid, …)` · `g.react("👍")` · `g.edit(sent, …)` |
100
- | group info and actions | `g.group()` · `g.members()` · `g.kick()` · `g.promote()` · … |
100
+ | group info and actions | `g.group()` · `g.members()` · `g.kick()` · `g.promote()` · `g.tagAll(text)` · … (`g.readMore` = the "Read more…" fold) |
101
101
  | media | `g.download()` · `g.media()` |
102
102
 
103
103
  Your bot's rules plug in through `createContext` options and `extend`:
package/package.json CHANGED
@@ -1,11 +1,19 @@
1
1
  {
2
2
  "name": "morpho-socket",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Blocos para bots de WhatsApp sobre o Baileys",
5
5
  "license": "MIT",
6
- "engines": { "node": ">=22.5.0" },
7
- "peerDependencies": { "@whiskeysockets/baileys": "*" },
8
- "files": ["packages", "README.md", "docs"],
6
+ "engines": {
7
+ "node": ">=22.5.0"
8
+ },
9
+ "peerDependencies": {
10
+ "@whiskeysockets/baileys": "*"
11
+ },
12
+ "files": [
13
+ "packages",
14
+ "README.md",
15
+ "docs"
16
+ ],
9
17
  "exports": {
10
18
  "./core": "./packages/core/index.js",
11
19
  "./store-sqlite": "./packages/store-sqlite/index.js",
@@ -23,5 +31,7 @@
23
31
  "./commands": "./packages/commands/index.js",
24
32
  "./kv": "./packages/kv/index.js"
25
33
  },
26
- "scripts": { "test": "node test/run-all.js" }
34
+ "scripts": {
35
+ "test": "node test/run-all.js"
36
+ }
27
37
  }
@@ -58,6 +58,7 @@ if (!r.ok) return g.reply(messages[r.reason] ?? messages.failed)
58
58
  | | |
59
59
  |---|---|
60
60
  | people | `kick` `promote` `demote` `add(number)` `approve` `reject` |
61
+ | tag everyone | `tagAll(text, { quote, admins, bot })` — hidden mention: nobody sees a list of numbers, everybody is notified → `{ ok, action, count, sent }` |
61
62
  | group | `close()` `open()` `lock()` `unlock()` `rename(text)` `setDescription(text)` `leave()` |
62
63
  | invite link | `link()` · `revokeLink()` → `{ code, url }` |
63
64
  | message | `delete()` removes the replied-to message (or the command itself) · `delete("command")` · `delete(sentMessage)` |
@@ -82,6 +83,8 @@ await g.react("🎉"); await g.poll("Pizza?", ["yes", "no"])
82
83
 
83
84
  Media sources: `Buffer` · `"https://…"` · `"/path/to/file"` · `{ url }` (sticker and voice need a Buffer or a file). Options (second argument, or inside the object): `mentions` (anything `g.at` understands), `quote: false | true | msg`, `mimetype` / `fileName`.
84
85
 
86
+ **Read more:** `g.readMore` is a string; put it between two parts of a text and WhatsApp folds the second part behind "Read more…": ``await g.reply(`Title${g.readMore}Long text`)``.
87
+
85
88
  Everything returns the sent message (with `.key`). If quoting fails (e.g. the command came from a button tap) it sends without the quote; if the media fails and there is a caption, it sends the caption. **A command is never left mute.**
86
89
 
87
90
  ## Incoming media
@@ -161,6 +161,30 @@ function createGroupActions({ identity, protect } = {}) {
161
161
  async members() { return Promise.all(((await this.groupMeta())?.participants || []).map(p => describe(this, p, false))) },
162
162
  async admins() { return (await this.members()).filter(m => m.admin) },
163
163
 
164
+ // ----- tag everyone (hidden: the text shows no @numbers, but everybody is notified) -----
165
+ // await g.tagAll("Attention, everyone!") // quotes the command by default
166
+ // await g.tagAll("Hi", { quote: false, admins: true }) // admins: only tag the admins
167
+ // -> { ok: true, action, count, sent } | { ok: false, action, reason }
168
+ async tagAll(text, opts = {}) {
169
+ const blocked = await guard(this, "tagAll", false)
170
+ if (blocked) return blocked
171
+ const t = String(text ?? "")
172
+ if (!t.trim()) return fail("tagAll", "invalid-text")
173
+ try {
174
+ const meta = await this.groupMeta()
175
+ const me = botIds(this.sock)
176
+ const jids = (meta?.participants || [])
177
+ .filter(p => (!opts.admins || isAdminRole(p)) && !(opts.bot === false && participantIds(p).some(x => me.includes(x))))
178
+ .map(p => p.id)
179
+ const content = { text: t, mentions: jids }
180
+ const quoted = opts.quote === false ? null : (opts.quote && opts.quote !== true ? opts.quote : this.msg)
181
+ let sent
182
+ try { sent = await this.sock.sendMessage(this.chatId, content, quoted ? { quoted } : undefined) }
183
+ catch (e) { if (!quoted) throw e; sent = await this.sock.sendMessage(this.chatId, content) } // quote failed (button tap...)? send loose
184
+ return success("tagAll", { count: jids.length, sent })
185
+ } catch (e) { return fail("tagAll", reasonFromError(e), { error: e?.message }) }
186
+ },
187
+
164
188
  kick(user, opts = {}) {
165
189
  return run(this, "kick", "remove", user, opts, {
166
190
  resolve: x => inGroup(this, x),
@@ -46,7 +46,7 @@ function createContext({ identity, media, interactive, getGroupMeta, isOwner, pr
46
46
  get timestamp() { return this.parsed.timestamp },
47
47
  get mentions() { return this.parsed.mentions },
48
48
  get quoted() { return this.parsed.quoted }, // parsed quoted message (text, media, sender...) or null
49
- get readMore() { return "\u200E".repeat(4000) },
49
+ get readMore() { return "\u200E".repeat(4001) }, // `${a}${g.readMore}${b}` -> WhatsApp folds b behind "Read more"
50
50
 
51
51
  // text/caption; a tap on one of OUR buttons/list rows counts as the command it carries (e.g. "/menu games")
52
52
  get text() { return this.parsed.text || this.interactiveReply || null },