@manybot/manybot 4.1.0 β†’ 4.1.2

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.
package/README.md CHANGED
@@ -2,17 +2,56 @@
2
2
 
3
3
  ![ManyBot Logo](logo.png)
4
4
 
5
- **Just a cool open source WhatsApp bot**
6
-
7
- [πŸ‡§πŸ‡· PortuguΓͺs](README.md) Β· [πŸ‡ΊπŸ‡Έ English](README_EN.md)
8
-
9
5
  ![Node.js 18+](https://img.shields.io/badge/Node.js-18+-339933?logo=node.js&logoColor=white)
10
6
  ![npm 9+](https://img.shields.io/badge/npm-9+-CB3837?logo=npm&logoColor=white)
11
7
  ![GPL v3](https://img.shields.io/badge/License-GPL--v3-blue.svg)
12
8
  ![Linux](https://img.shields.io/badge/Linux%20%7C%20Windows-lightgrey)
9
+ ![whatsapp-web.js](https://img.shields.io/badge/WhatsApp-25D366?logo=whatsapp&logoColor=white)
10
+
11
+ </div>
13
12
 
14
13
  ---
15
14
 
16
- This repository is dedicated to contributions related to the project.
15
+ ManyBot is free and open-source a messaging automation ecosystem for online businesses and communities.
16
+
17
+ ## Requirements
18
+
19
+ - Node.js >= 18
20
+ - npm
21
+
22
+ ## Getting started
23
+
24
+ ```bash
25
+ npm install -g @manybot/manybot
26
+ npm install -g @manybot/manyplug
27
+ manybot
28
+ ```
29
+
30
+ On first run, a configuration file is created at `~/.manybot/manybot.conf`. Edit it to set up your preferences and list the plugins you want to load.
31
+
32
+ For detailed setup instructions, see the **[documentation](https://manybot.stxerr.dev/docs/)**.
33
+
34
+ ## Plugins
35
+
36
+ ManyBot's functionality comes from plugins. Install them with ManyPlug:
37
+
38
+ ```bash
39
+ manyplug install <plugin-name>
40
+ ```
41
+
42
+ Browse available plugins at **[manybot.stxerr.dev/plugins](https://manybot.stxerr.dev/plugins/)**.
43
+
44
+ Want to build your own? The plugin API lets you add commands and features without touching the bot's core - *documentation coming soon*.
45
+
46
+ ## Contributing
47
+
48
+ All kinds of contributions are welcome:
49
+
50
+ - **Bug reports and feature requests**: open an issue on GitHub or Codeberg
51
+ - **Code**: pull requests are welcome on [GitHub](https://github.com/many-bot/manybot) or [Codeberg](https://codeberg.org/many-bot/manybot); patches by email (`devel+manybot@stxerr.dev`) are also accepted - subscribe to the mailing list [here](https://list.stxerr.dev).
52
+ - **Plugins**: submit your plugin to [manyplug-repo](https://github.com/many-bot/manyplug-repo), which has instructions on how to do it
53
+ - **Anything else**: suggestions, translations, documentation fixes - reach out by email or open an issue
54
+
55
+ ## License
17
56
 
18
- website: https://manybot.stxerr.dev
57
+ ManyBot is distributed under the [GNU General Public License v3.0](LICENSE).
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "SyntaxError!",
6
6
  "email": "me@stxerr.dev"
7
7
  },
8
- "version": "4.1.0",
8
+ "version": "4.1.2",
9
9
  "license": "GPL-3.0-only",
10
10
  "private": false,
11
11
  "engines": {
@@ -16,7 +16,6 @@ import { enqueue } from "#download";
16
16
  import { emptyFolder } from "#utils/file";
17
17
  import { getChatId } from "#utils/getChatId";
18
18
  import pkg from "whatsapp-web.js";
19
- import { client } from "#client/whatsappClient";
20
19
 
21
20
  const { MessageMedia } = pkg;
22
21
 
@@ -112,7 +111,7 @@ function buildPluginsApi(pluginRegistry) {
112
111
  require(name) {
113
112
  const plugin = pluginRegistry.get(name);
114
113
  if (!plugin || plugin.status !== "active") {
115
- throw new Error(`Plugin dependency "${name}" is not active.`);
114
+ throw new Error(`Plugin dependency "${name}" does not exist or is not active.`);
116
115
  }
117
116
  return plugin.exports;
118
117
  },
@@ -181,24 +180,35 @@ function chatIdTarget(client, chatId) {
181
180
  };
182
181
  }
183
182
 
184
- // ── Setup API ────────────────────────────────────────────────────────────────
183
+ // ── Send APIs ────────────────────────────────────────────────────────────────
185
184
 
186
- /**
187
- * Setup API β€” without message context.
188
- * Passed to plugin.setup(ctx) during initialization.
189
- *
190
- * @param {import("whatsapp-web.js").Client} client
191
- * @param {Map<string, any>} pluginRegistry
192
- * @returns {object}
193
- */
194
- export function buildSetupApi(client, pluginRegistry) {
185
+ /** Send to a specific chat by ID. Available in both setup and runtime. */
186
+ function buildSendToApi(client) {
195
187
  return {
196
188
  sendTo: (chatId, text) => client.sendMessage(chatId, text),
197
189
  sendImageTo: (chatId, filePath, caption) => makeSender(chatIdTarget(client, chatId)).image(filePath, caption),
198
190
  sendVideoTo: (chatId, filePath, caption) => makeSender(chatIdTarget(client, chatId)).video(filePath, caption),
199
191
  sendAudioTo: (chatId, filePath) => makeSender(chatIdTarget(client, chatId)).audio(filePath),
200
192
  sendStickerTo: (chatId, source) => makeSender(chatIdTarget(client, chatId)).sticker(source),
193
+ };
194
+ }
201
195
 
196
+ /** Send to the current chat. Only available in runtime. */
197
+ function buildSendApi(chat) {
198
+ const sender = makeSender(chat);
199
+ return {
200
+ send: (text) => sender.text(text),
201
+ sendImage: (filePath, caption) => sender.image(filePath, caption),
202
+ sendVideo: (filePath, caption) => sender.video(filePath, caption),
203
+ sendAudio: (filePath) => sender.audio(filePath),
204
+ sendSticker: (source) => sender.sticker(source),
205
+ };
206
+ }
207
+
208
+ // ── Base API (shared between setup and runtime) ───────────────────────────────
209
+
210
+ function buildBaseApi(client, pluginRegistry) {
211
+ return {
202
212
  log,
203
213
  t,
204
214
  config: buildConfigApi(),
@@ -207,6 +217,23 @@ export function buildSetupApi(client, pluginRegistry) {
207
217
  download: buildDownloadApi(),
208
218
  plugins: buildPluginsApi(pluginRegistry),
209
219
  botId: client.info?.wid?._serialized ?? null,
220
+ ...buildSendToApi(client),
221
+ };
222
+ }
223
+
224
+ // ── Setup API ────────────────────────────────────────────────────────────────
225
+
226
+ /**
227
+ * Setup API β€” without message context.
228
+ * Passed to plugin.setup(ctx) during initialization.
229
+ *
230
+ * @param {import("whatsapp-web.js").Client} client
231
+ * @param {Map<string, any>} pluginRegistry
232
+ * @returns {object}
233
+ */
234
+ export function buildSetupApi(client, pluginRegistry) {
235
+ return {
236
+ ...buildBaseApi(client, pluginRegistry),
210
237
  };
211
238
  }
212
239
 
@@ -224,10 +251,9 @@ export function buildSetupApi(client, pluginRegistry) {
224
251
  * @returns {object} ctx
225
252
  */
226
253
  export function buildApi({ msg, chat, client, pluginRegistry }) {
227
-
228
- const currentSender = makeSender(chat);
229
-
230
254
  return {
255
+ ...buildBaseApi(client, pluginRegistry),
256
+ ...buildSendApi(chat),
231
257
 
232
258
  // ── msg ──────────────────────────────────────────────────
233
259
 
@@ -276,32 +302,5 @@ export function buildApi({ msg, chat, client, pluginRegistry }) {
276
302
  name: chat.name || chat.id.user,
277
303
  isGroup: /@g\.us$/.test(chat.id._serialized),
278
304
  },
279
-
280
- // ── send (current chat) ──────────────────────────────────
281
-
282
- send: (text) => currentSender.text(text),
283
- sendImage: (filePath, caption) => currentSender.image(filePath, caption),
284
- sendVideo: (filePath, caption) => currentSender.video(filePath, caption),
285
- sendAudio: (filePath) => currentSender.audio(filePath),
286
- sendSticker: (source) => currentSender.sticker(source),
287
-
288
- // ── sendTo (specific chat) ───────────────────────────────
289
-
290
- sendTo: (chatId, text) => client.sendMessage(chatId, text),
291
- sendImageTo: (chatId, filePath, caption) => makeSender(chatIdTarget(client, chatId)).image(filePath, caption),
292
- sendVideoTo: (chatId, filePath, caption) => makeSender(chatIdTarget(client, chatId)).video(filePath, caption),
293
- sendAudioTo: (chatId, filePath) => makeSender(chatIdTarget(client, chatId)).audio(filePath),
294
- sendStickerTo: (chatId, source) => makeSender(chatIdTarget(client, chatId)).sticker(source),
295
-
296
- // ── system ───────────────────────────────────────────────
297
-
298
- log,
299
- t,
300
- config: buildConfigApi(),
301
- i18n: buildI18nApi(),
302
- utils: buildUtilsApi(),
303
- download: buildDownloadApi(),
304
- plugins: buildPluginsApi(pluginRegistry),
305
- botId: client.info?.wid?._serialized ?? null,
306
305
  };
307
306
  }
@@ -74,12 +74,32 @@ export async function setupPlugins(api) {
74
74
  }
75
75
  }
76
76
 
77
+ async function findPluginPath(name) {
78
+ // key direto: synt-xerror/figurinha
79
+ const direct = path.join(PLUGINS_DIR, name, "index.js");
80
+ if (fs.existsSync(direct)) return direct;
81
+
82
+ // nome simples em subdir: plugins/synt-xerror/figurinha
83
+ if (!fs.existsSync(PLUGINS_DIR)) return null;
84
+ for (const entry of fs.readdirSync(PLUGINS_DIR, { withFileTypes: true })) {
85
+ if (!entry.isDirectory()) continue;
86
+ const nested = path.join(PLUGINS_DIR, entry.name, name, "index.js");
87
+ if (fs.existsSync(nested)) return nested;
88
+ }
89
+
90
+ return null;
91
+ }
77
92
  /**
78
93
  * Carrega um ΓΊnico plugin pelo nome.
79
94
  * @param {string} name
80
95
  */
81
96
  async function loadPlugin(name) {
82
- const pluginPath = path.join(PLUGINS_DIR, name, "index.js");
97
+ const pluginPath = await findPluginPath(name);
98
+ if (!pluginPath) {
99
+ logger.warn(t("system.pluginNotFound", { name, path: path.join(PLUGINS_DIR, name) }));
100
+ pluginRegistry.set(name, { name, status: "disabled", run: null, exports: null, error: null });
101
+ return;
102
+ }
83
103
 
84
104
  if (!fs.existsSync(pluginPath)) {
85
105
  logger.warn(t("system.pluginNotFound", { name, path: pluginPath }));