@dongdev/fca-unofficial 3.0.0 → 3.0.3

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/CHANGELOG.md CHANGED
@@ -134,3 +134,6 @@ Too lazy to write changelog, sorry! (will write changelog in the next release, t
134
134
 
135
135
  ## v2.0.32 - 2025-10-30
136
136
  - Hotfix / auto bump
137
+
138
+ ## v3.0.2 - 2025-11-27
139
+ - Hotfix / auto bump
package/DOCS.md CHANGED
@@ -321,10 +321,13 @@ const stopListening = api.listenMqtt((err, event) => {
321
321
  messageID: "mid.xxx",
322
322
  senderID: "100012345678901",
323
323
  body: "Message content",
324
+ args: ["Message", "content"], // Array of words from body (split by whitespace)
324
325
  attachments: [], // Array of attachments
325
326
  mentions: {}, // Object of mentions
326
327
  timestamp: 1234567890000,
327
- isGroup: false // true if group chat
328
+ isGroup: false, // true if group chat
329
+ isUnread: false, // Whether message is unread
330
+ participantIDs: ["100012345678901"] // Array of participant IDs
328
331
  }
329
332
 
330
333
  // Event type: "event"
@@ -355,7 +358,241 @@ const stopListening = api.listenMqtt((err, event) => {
355
358
 
356
359
  ---
357
360
 
358
- ### 3.3. getUserInfo - Get User Information
361
+ ### 3.3. Middleware System - Filter and Process Events
362
+
363
+ The middleware system allows you to intercept, filter, and modify events before they are emitted to your callback. This is useful for logging, rate limiting, message filtering, auto-replies, and more.
364
+
365
+ #### Syntax:
366
+ ```javascript
367
+ // Add middleware
368
+ const removeMiddleware = api.useMiddleware(middlewareFunction);
369
+ const removeMiddleware = api.useMiddleware("middlewareName", middlewareFunction);
370
+
371
+ // Remove middleware
372
+ api.removeMiddleware(identifier); // identifier can be name (string) or function
373
+
374
+ // Clear all middleware
375
+ api.clearMiddleware();
376
+
377
+ // List middleware
378
+ const names = api.listMiddleware();
379
+
380
+ // Enable/disable middleware
381
+ api.setMiddlewareEnabled("middlewareName", true); // enable
382
+ api.setMiddlewareEnabled("middlewareName", false); // disable
383
+
384
+ // Get middleware count
385
+ const count = api.middlewareCount;
386
+ ```
387
+
388
+ #### Middleware Function Signature:
389
+ ```javascript
390
+ function middleware(event, next) {
391
+ // event: The event object (can be modified)
392
+ // next: Function to continue to next middleware
393
+ // - next() - continue to next middleware
394
+ // - next(false) or next(null) - stop processing, don't emit event
395
+ // - next(error) - emit error instead
396
+
397
+ // Your logic here
398
+
399
+ next(); // Continue to next middleware
400
+ }
401
+ ```
402
+
403
+ #### Examples:
404
+
405
+ **1. Message Filtering - Block messages from specific users:**
406
+ ```javascript
407
+ api.useMiddleware("blockUsers", (event, next) => {
408
+ if (event.type === "message") {
409
+ const blockedUsers = ["100012345678901", "100012345678902"];
410
+ if (blockedUsers.includes(event.senderID)) {
411
+ // Block this message
412
+ return next(false);
413
+ }
414
+ }
415
+ next(); // Continue processing
416
+ });
417
+ ```
418
+
419
+ **2. Logging Middleware:**
420
+ ```javascript
421
+ api.useMiddleware("logger", (event, next) => {
422
+ if (event.type === "message") {
423
+ console.log(`[${new Date().toISOString()}] Message from ${event.senderID}: ${event.body}`);
424
+ }
425
+ next(); // Continue to next middleware
426
+ });
427
+ ```
428
+
429
+ **3. Auto-Reply Middleware:**
430
+ ```javascript
431
+ api.useMiddleware("autoReply", (event, next) => {
432
+ if (event.type === "message" && event.body.toLowerCase() === "hello") {
433
+ api.sendMessage("Hi there! How can I help you?", event.threadID);
434
+ }
435
+ next(); // Continue processing
436
+ });
437
+ ```
438
+
439
+ **4. Rate Limiting Middleware:**
440
+ ```javascript
441
+ const messageCounts = {};
442
+ const RATE_LIMIT = 10; // messages per minute
443
+ const RATE_WINDOW = 60000; // 1 minute
444
+
445
+ api.useMiddleware("rateLimit", (event, next) => {
446
+ if (event.type === "message") {
447
+ const now = Date.now();
448
+ const senderID = event.senderID;
449
+
450
+ // Clean old entries
451
+ if (messageCounts[senderID] && messageCounts[senderID].timestamp < now - RATE_WINDOW) {
452
+ delete messageCounts[senderID];
453
+ }
454
+
455
+ // Initialize or increment
456
+ if (!messageCounts[senderID]) {
457
+ messageCounts[senderID] = { count: 0, timestamp: now };
458
+ }
459
+ messageCounts[senderID].count++;
460
+
461
+ // Check rate limit
462
+ if (messageCounts[senderID].count > RATE_LIMIT) {
463
+ console.log(`Rate limit exceeded for user ${senderID}`);
464
+ return next(false); // Block message
465
+ }
466
+ }
467
+ next();
468
+ });
469
+ ```
470
+
471
+ **5. Message Transformation:**
472
+ ```javascript
473
+ api.useMiddleware("transform", (event, next) => {
474
+ if (event.type === "message") {
475
+ // Add custom property
476
+ event.customProperty = "customValue";
477
+
478
+ // Transform message body
479
+ if (event.body) {
480
+ event.body = event.body.toUpperCase();
481
+ }
482
+ }
483
+ next();
484
+ });
485
+ ```
486
+
487
+ **6. Async Middleware (Promise-based):**
488
+ ```javascript
489
+ api.useMiddleware("asyncMiddleware", async (event, next) => {
490
+ if (event.type === "message") {
491
+ // Do async operation
492
+ const userInfo = await api.getUserInfo(event.senderID);
493
+ event.senderName = userInfo[event.senderID].name;
494
+ }
495
+ next(); // Continue
496
+ });
497
+ ```
498
+
499
+ **7. Conditional Middleware:**
500
+ ```javascript
501
+ // Only process messages in group chats
502
+ api.useMiddleware("groupOnly", (event, next) => {
503
+ if (event.type === "message" && !event.isGroup) {
504
+ return next(false); // Skip non-group messages
505
+ }
506
+ next();
507
+ });
508
+
509
+ // Only process messages containing specific keywords
510
+ api.useMiddleware("keywordFilter", (event, next) => {
511
+ if (event.type === "message") {
512
+ const keywords = ["help", "support", "info"];
513
+ const hasKeyword = keywords.some(keyword =>
514
+ event.body.toLowerCase().includes(keyword)
515
+ );
516
+ if (!hasKeyword) {
517
+ return next(false); // Skip messages without keywords
518
+ }
519
+ }
520
+ next();
521
+ });
522
+ ```
523
+
524
+ **8. Remove Middleware:**
525
+ ```javascript
526
+ // Remove by name
527
+ api.removeMiddleware("logger");
528
+
529
+ // Remove by function reference
530
+ const myMiddleware = (event, next) => { /* ... */ };
531
+ api.useMiddleware("myMiddleware", myMiddleware);
532
+ // Later...
533
+ api.removeMiddleware(myMiddleware);
534
+ ```
535
+
536
+ **9. Complete Example - Bot with Multiple Middleware:**
537
+ ```javascript
538
+ const login = require("@dongdev/fca-unofficial");
539
+
540
+ login({ appState: [] }, (err, api) => {
541
+ if (err) return console.error(err);
542
+
543
+ // 1. Logging middleware
544
+ api.useMiddleware("logger", (event, next) => {
545
+ if (event.type === "message") {
546
+ console.log(`Message: ${event.body}`);
547
+ }
548
+ next();
549
+ });
550
+
551
+ // 2. Block spam users
552
+ const spamUsers = ["100012345678901"];
553
+ api.useMiddleware("spamFilter", (event, next) => {
554
+ if (event.type === "message" && spamUsers.includes(event.senderID)) {
555
+ return next(false);
556
+ }
557
+ next();
558
+ });
559
+
560
+ // 3. Auto-reply to greetings
561
+ api.useMiddleware("autoReply", (event, next) => {
562
+ if (event.type === "message") {
563
+ const greetings = ["hi", "hello", "hey"];
564
+ if (greetings.includes(event.body.toLowerCase())) {
565
+ api.sendMessage("Hello! How can I help?", event.threadID);
566
+ }
567
+ }
568
+ next();
569
+ });
570
+
571
+ // 4. Listen for messages (middleware will process them first)
572
+ api.listenMqtt((err, event) => {
573
+ if (err) return console.error(err);
574
+
575
+ // This callback receives events AFTER middleware processing
576
+ if (event.type === "message") {
577
+ console.log("Received message:", event.body);
578
+ }
579
+ });
580
+ });
581
+ ```
582
+
583
+ #### Middleware Execution Order:
584
+ Middleware functions are executed in the order they are added. If a middleware calls `next(false)` or `next(null)`, the event will be blocked and not emitted to your callback.
585
+
586
+ #### Notes:
587
+ - Middleware only processes events, not errors (errors bypass middleware)
588
+ - You can modify the event object in middleware (it will be passed to the next middleware and callback)
589
+ - Middleware can be async (return a Promise)
590
+ - Middleware can be enabled/disabled without removing them
591
+ - The middleware system is persistent across reconnections
592
+
593
+ ---
594
+
595
+ ### 3.4. getUserInfo - Get User Information
359
596
 
360
597
  Get detailed information about one or more users.
361
598
 
@@ -402,7 +639,268 @@ api.getUserInfo(["100012345678901", "100012345678902"], (err, userInfo) => {
402
639
 
403
640
  ---
404
641
 
405
- ### 3.4. getThreadInfo - Get Thread Information
642
+ ### 3.4. Message Scheduler - Schedule Messages
643
+
644
+ Schedule messages to be sent at a specific time in the future. Useful for reminders, scheduled announcements, and automated messages.
645
+
646
+ #### Syntax:
647
+ ```javascript
648
+ // Schedule a message
649
+ const id = api.scheduler.scheduleMessage(message, threadID, when, options);
650
+
651
+ // Cancel a scheduled message
652
+ api.scheduler.cancelScheduledMessage(id);
653
+
654
+ // Get scheduled message info
655
+ const scheduled = api.scheduler.getScheduledMessage(id);
656
+
657
+ // List all scheduled messages
658
+ const list = api.scheduler.listScheduledMessages();
659
+
660
+ // Cancel all scheduled messages
661
+ const count = api.scheduler.cancelAllScheduledMessages();
662
+
663
+ // Get count of scheduled messages
664
+ const count = api.scheduler.getScheduledCount();
665
+ ```
666
+
667
+ #### Parameters:
668
+ - `message`: Message content (string or MessageObject)
669
+ - `threadID`: Target thread ID(s) (string or array)
670
+ - `when`: When to send - can be:
671
+ - `Date` object
672
+ - `number` (Unix timestamp in milliseconds)
673
+ - `string` (ISO date string)
674
+ - `options`: Optional object with:
675
+ - `replyMessageID`: Message ID to reply to
676
+ - `isGroup`: Whether it's a group chat
677
+ - `callback`: Callback function when message is sent
678
+
679
+ #### Examples:
680
+
681
+ **1. Schedule message for specific time:**
682
+ ```javascript
683
+ // Schedule for 1 hour from now
684
+ const oneHourLater = Date.now() + (60 * 60 * 1000);
685
+ const id = api.scheduler.scheduleMessage(
686
+ "This is a scheduled message!",
687
+ "100012345678901",
688
+ oneHourLater
689
+ );
690
+ console.log(`Message scheduled with ID: ${id}`);
691
+ ```
692
+
693
+ **2. Schedule using Date object:**
694
+ ```javascript
695
+ // Schedule for tomorrow at 9:00 AM
696
+ const tomorrow = new Date();
697
+ tomorrow.setDate(tomorrow.getDate() + 1);
698
+ tomorrow.setHours(9, 0, 0, 0);
699
+
700
+ const id = api.scheduler.scheduleMessage(
701
+ "Good morning! ☀️",
702
+ "100012345678901",
703
+ tomorrow
704
+ );
705
+ ```
706
+
707
+ **3. Schedule using ISO string:**
708
+ ```javascript
709
+ // Schedule for specific date/time
710
+ const id = api.scheduler.scheduleMessage(
711
+ "Meeting reminder!",
712
+ "100012345678901",
713
+ "2024-12-25T10:00:00Z"
714
+ );
715
+ ```
716
+
717
+ **4. Schedule with options:**
718
+ ```javascript
719
+ const id = api.scheduler.scheduleMessage(
720
+ "Reply to your message",
721
+ "100012345678901",
722
+ Date.now() + 30000, // 30 seconds from now
723
+ {
724
+ replyMessageID: "mid.xxx",
725
+ isGroup: false,
726
+ callback: (err) => {
727
+ if (err) {
728
+ console.error("Scheduled message failed:", err);
729
+ } else {
730
+ console.log("Scheduled message sent!");
731
+ }
732
+ }
733
+ }
734
+ );
735
+ ```
736
+
737
+ **5. Cancel scheduled message:**
738
+ ```javascript
739
+ const id = api.scheduler.scheduleMessage("Test", threadID, Date.now() + 60000);
740
+
741
+ // Cancel it
742
+ if (api.scheduler.cancelScheduledMessage(id)) {
743
+ console.log("Message cancelled");
744
+ } else {
745
+ console.log("Message not found or already sent");
746
+ }
747
+ ```
748
+
749
+ **6. List all scheduled messages:**
750
+ ```javascript
751
+ const scheduled = api.scheduler.listScheduledMessages();
752
+
753
+ scheduled.forEach(msg => {
754
+ const timeUntil = Math.round(msg.timeUntilSend / 1000 / 60); // minutes
755
+ console.log(`ID: ${msg.id}, Sends in ${timeUntil} minutes`);
756
+ });
757
+ ```
758
+
759
+ **7. Get scheduled message info:**
760
+ ```javascript
761
+ const scheduled = api.scheduler.getScheduledMessage(id);
762
+ if (scheduled) {
763
+ console.log("Message:", scheduled.message);
764
+ console.log("Scheduled for:", new Date(scheduled.timestamp));
765
+ console.log("Time until send:", scheduled.timeUntilSend, "ms");
766
+ }
767
+ ```
768
+
769
+ **8. Complete example - Reminder bot:**
770
+ ```javascript
771
+ const login = require("@dongdev/fca-unofficial");
772
+
773
+ login({ appState: [] }, (err, api) => {
774
+ if (err) return console.error(err);
775
+
776
+ api.listenMqtt((err, event) => {
777
+ if (err) return console.error(err);
778
+
779
+ if (event.type === "message" && event.body.startsWith("/remind")) {
780
+ const args = event.body.split(" ");
781
+ if (args.length < 3) {
782
+ api.sendMessage("Usage: /remind <minutes> <message>", event.threadID);
783
+ return;
784
+ }
785
+
786
+ const minutes = parseInt(args[1]);
787
+ const message = args.slice(2).join(" ");
788
+ const when = Date.now() + (minutes * 60 * 1000);
789
+
790
+ const id = api.scheduler.scheduleMessage(
791
+ message,
792
+ event.threadID,
793
+ when
794
+ );
795
+
796
+ api.sendMessage(
797
+ `Reminder scheduled! I'll remind you in ${minutes} minutes.`,
798
+ event.threadID
799
+ );
800
+ }
801
+ });
802
+ });
803
+ ```
804
+
805
+ #### Notes:
806
+ - Scheduled messages are stored in memory and will be lost if the bot restarts
807
+ - Messages are sent automatically at the scheduled time
808
+ - You can cancel messages before they are sent
809
+ - The scheduler automatically cleans up expired messages
810
+
811
+ ---
812
+
813
+ ### 3.5. Auto-save AppState
814
+
815
+ Automatically save AppState to a file at regular intervals to prevent session loss.
816
+
817
+ #### Syntax:
818
+ ```javascript
819
+ // Enable auto-save
820
+ const disable = api.enableAutoSaveAppState(options);
821
+
822
+ // Disable auto-save
823
+ disable();
824
+ ```
825
+
826
+ #### Parameters:
827
+ - `options`: Optional object with:
828
+ - `filePath`: Path to save AppState file (default: "appstate.json")
829
+ - `interval`: Save interval in milliseconds (default: 10 minutes)
830
+ - `saveOnLogin`: Save immediately on login (default: true)
831
+
832
+ #### Examples:
833
+
834
+ **1. Basic auto-save:**
835
+ ```javascript
836
+ const login = require("@dongdev/fca-unofficial");
837
+
838
+ login({ appState: [] }, (err, api) => {
839
+ if (err) return console.error(err);
840
+
841
+ // Enable auto-save (saves every 10 minutes by default)
842
+ api.enableAutoSaveAppState();
843
+ });
844
+ ```
845
+
846
+ **2. Custom file path and interval:**
847
+ ```javascript
848
+ // Save to custom location every 5 minutes
849
+ const disable = api.enableAutoSaveAppState({
850
+ filePath: "./data/appstate.json",
851
+ interval: 5 * 60 * 1000, // 5 minutes
852
+ saveOnLogin: true
853
+ });
854
+
855
+ // Later, disable it
856
+ // disable();
857
+ ```
858
+
859
+ **3. Save only on login:**
860
+ ```javascript
861
+ // Save only once on login, not periodically
862
+ const disable = api.enableAutoSaveAppState({
863
+ interval: Infinity, // Never save periodically
864
+ saveOnLogin: true
865
+ });
866
+ ```
867
+
868
+ **4. Complete example:**
869
+ ```javascript
870
+ const fs = require("fs");
871
+ const login = require("@dongdev/fca-unofficial");
872
+
873
+ // Try to load existing AppState
874
+ let appState = [];
875
+ try {
876
+ appState = JSON.parse(fs.readFileSync("appstate.json", "utf8"));
877
+ } catch (e) {
878
+ console.log("No existing AppState found");
879
+ }
880
+
881
+ login({ appState }, (err, api) => {
882
+ if (err) return console.error(err);
883
+
884
+ // Enable auto-save
885
+ api.enableAutoSaveAppState({
886
+ filePath: "appstate.json",
887
+ interval: 10 * 60 * 1000, // 10 minutes
888
+ saveOnLogin: true
889
+ });
890
+
891
+ console.log("Bot started with auto-save enabled!");
892
+ });
893
+ ```
894
+
895
+ #### Notes:
896
+ - AppState is saved automatically at the specified interval
897
+ - Saves immediately on login if `saveOnLogin` is true
898
+ - The save function checks if AppState is valid before saving
899
+ - Multiple auto-save instances can be enabled with different settings
900
+
901
+ ---
902
+
903
+ ### 3.6. getThreadInfo - Get Thread Information
406
904
 
407
905
  Get information about conversation/group chat.
408
906