@amatiscorp/disguard 1.4.0 → 1.6.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 (83) hide show
  1. package/README.md +309 -14
  2. package/dist/AntiSpam.d.ts +9 -1
  3. package/dist/AntiSpam.d.ts.map +1 -1
  4. package/dist/AntiSpam.js +155 -2
  5. package/dist/AntiSpam.js.map +1 -1
  6. package/dist/defaults.d.ts.map +1 -1
  7. package/dist/defaults.js +95 -1
  8. package/dist/defaults.js.map +1 -1
  9. package/dist/detectors/blank.d.ts +3 -0
  10. package/dist/detectors/blank.d.ts.map +1 -0
  11. package/dist/detectors/blank.js +25 -0
  12. package/dist/detectors/blank.js.map +1 -0
  13. package/dist/detectors/embed.d.ts +3 -0
  14. package/dist/detectors/embed.d.ts.map +1 -0
  15. package/dist/detectors/embed.js +25 -0
  16. package/dist/detectors/embed.js.map +1 -0
  17. package/dist/detectors/emojis.d.ts.map +1 -1
  18. package/dist/detectors/emojis.js +19 -1
  19. package/dist/detectors/emojis.js.map +1 -1
  20. package/dist/detectors/files.d.ts.map +1 -1
  21. package/dist/detectors/files.js +29 -1
  22. package/dist/detectors/files.js.map +1 -1
  23. package/dist/detectors/forward.d.ts +3 -0
  24. package/dist/detectors/forward.d.ts.map +1 -0
  25. package/dist/detectors/forward.js +35 -0
  26. package/dist/detectors/forward.js.map +1 -0
  27. package/dist/detectors/ghost.d.ts.map +1 -1
  28. package/dist/detectors/ghost.js +7 -0
  29. package/dist/detectors/ghost.js.map +1 -1
  30. package/dist/detectors/impersonate.d.ts +3 -0
  31. package/dist/detectors/impersonate.d.ts.map +1 -0
  32. package/dist/detectors/impersonate.js +86 -0
  33. package/dist/detectors/impersonate.js.map +1 -0
  34. package/dist/detectors/joins.d.ts +4 -0
  35. package/dist/detectors/joins.d.ts.map +1 -0
  36. package/dist/detectors/joins.js +30 -0
  37. package/dist/detectors/joins.js.map +1 -0
  38. package/dist/detectors/polls.d.ts +3 -0
  39. package/dist/detectors/polls.d.ts.map +1 -0
  40. package/dist/detectors/polls.js +35 -0
  41. package/dist/detectors/polls.js.map +1 -0
  42. package/dist/detectors/raid.d.ts +3 -0
  43. package/dist/detectors/raid.d.ts.map +1 -0
  44. package/dist/detectors/raid.js +23 -0
  45. package/dist/detectors/raid.js.map +1 -0
  46. package/dist/detectors/replies.d.ts +3 -0
  47. package/dist/detectors/replies.d.ts.map +1 -0
  48. package/dist/detectors/replies.js +32 -0
  49. package/dist/detectors/replies.js.map +1 -0
  50. package/dist/detectors/stickers.d.ts +3 -0
  51. package/dist/detectors/stickers.d.ts.map +1 -0
  52. package/dist/detectors/stickers.js +60 -0
  53. package/dist/detectors/stickers.js.map +1 -0
  54. package/dist/detectors/voice.d.ts +3 -0
  55. package/dist/detectors/voice.d.ts.map +1 -0
  56. package/dist/detectors/voice.js +35 -0
  57. package/dist/detectors/voice.js.map +1 -0
  58. package/dist/detectors/words.d.ts.map +1 -1
  59. package/dist/detectors/words.js +27 -3
  60. package/dist/detectors/words.js.map +1 -1
  61. package/dist/enforcement.d.ts +2 -1
  62. package/dist/enforcement.d.ts.map +1 -1
  63. package/dist/enforcement.js +100 -5
  64. package/dist/enforcement.js.map +1 -1
  65. package/dist/index.d.ts +5 -3
  66. package/dist/index.d.ts.map +1 -1
  67. package/dist/index.js +9 -1
  68. package/dist/index.js.map +1 -1
  69. package/dist/store/MemoryStore.d.ts +6 -0
  70. package/dist/store/MemoryStore.d.ts.map +1 -1
  71. package/dist/store/MemoryStore.js +44 -0
  72. package/dist/store/MemoryStore.js.map +1 -1
  73. package/dist/types.d.ts +131 -2
  74. package/dist/types.d.ts.map +1 -1
  75. package/dist/utils/flags.d.ts +5 -0
  76. package/dist/utils/flags.d.ts.map +1 -0
  77. package/dist/utils/flags.js +41 -0
  78. package/dist/utils/flags.js.map +1 -0
  79. package/dist/utils/normalize.d.ts +2 -0
  80. package/dist/utils/normalize.d.ts.map +1 -1
  81. package/dist/utils/normalize.js +46 -0
  82. package/dist/utils/normalize.js.map +1 -1
  83. package/package.json +2 -2
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  **Configurable antispam for discord.js v14**
6
6
 
7
- Flood · phishing · leaked secrets · channel hop · ghost pings · TypeScript
7
+ Flood · phishing · forwards · impersonation · join raids · leaked secrets · TypeScript
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/@amatiscorp/disguard.svg?style=flat-square)](https://www.npmjs.com/package/@amatiscorp/disguard)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
@@ -22,7 +22,7 @@ Configurable antispam for [discord.js](https://discord.js.org) v14 bots.
22
22
 
23
23
  It is **not** a bot. You plug it into your existing `Client` and decide every threshold, allowlist, and punishment.
24
24
 
25
- Detects flood, leaked tokens/webhooks, channel hopping, blocked words, phishing, duplicate images, mention spam, ghost pings, and more.
25
+ Detects flood, leaked tokens/webhooks, channel hopping, blocked words, phishing, duplicate images, mention spam, ghost pings, forwarded-message dumps, bot impersonation, join raids, and more.
26
26
 
27
27
  **Languages**
28
28
 
@@ -33,6 +33,86 @@ Detects flood, leaked tokens/webhooks, channel hopping, blocked words, phishing,
33
33
 
34
34
  # English
35
35
 
36
+ ## What's new in 1.6.0
37
+
38
+ Forward dumps, sticker floods, bot/staff impersonation, join raids, voice-note spam, poll spam, and harder-to-dodge word/file filters. Kick/ban stay **off**. `joins` is **off** in `balanced` so a busy invite night does not false-positive.
39
+
40
+ | Option | Default | What it does |
41
+ | --- | --- | --- |
42
+ | `forwards` | **on**, 4 / 10s | Too many Discord **forwarded** messages (HasSnapshot) in a short window. |
43
+ | `stickers` | **on**, 3 per message, 8 / 10s | Sticker dumps. `blockOnly` (off) flags sticker-only messages. |
44
+ | `impersonate` | **on**, match bot at 0.84 | Nick / username / display name too close to the **bot** (or `names`). |
45
+ | `joins` | **off** (`strict`: on, 6 / 8s) | Too many **distinct accounts joining** the guild at once. Needs `guildMemberAdd`. |
46
+ | `voice` | **on**, 3 / 15s | Too many **voice messages** in a window. |
47
+ | `polls` | **on**, 2 / 20s | Too many **polls** in a window. |
48
+ | `files.blockDoubleExtensions` | `true` | Flags `virus.exe.png` (blocked ext hidden behind a safe one). |
49
+ | `words.scanEmbeds` | `true` | Word list also reads embed text. |
50
+ | `words.normalizeHomoglyphs` | `true` | Folds Cyrillic / Greek / fullwidth lookalikes (`rаid` → `raid`). |
51
+ | `ignoreBoosters` | `false` | Skip server boosters. |
52
+ | `punishment.slowmodeSeconds` | `0` | On `raid` / `join` hits, set channel slowmode (needs **Manage Channels**). Sticky until a mod changes it. |
53
+ | `getRecentIncidents()` | — | Last 50 live hits (not `analyze()`). |
54
+ | `setDetectorEnabled(type, bool)` | — | Toggle one detector without rewriting `disabledDetectors`. |
55
+ | `onJoin` | — | Callback for join-raid incidents (no message). |
56
+
57
+ ```js
58
+ const antispam = new AntiSpam(client, {
59
+ preset: "balanced",
60
+ forwards: { maxForwards: 3, windowMs: 8_000 },
61
+ stickers: { maxStickers: 2, maxInWindow: 6 },
62
+ impersonate: { names: ["Admin", "Moderador", "Staff"] },
63
+ joins: { enabled: true, maxJoins: 8, windowMs: 10_000 },
64
+ voice: { maxVoice: 3 },
65
+ polls: { maxPolls: 2 },
66
+ ignoreBoosters: true,
67
+ words: { enabled: true, list: ["raid-now"], normalizeHomoglyphs: true },
68
+ punishment: { slowmodeSeconds: 10 },
69
+ onJoin(incident) {
70
+ console.log("join raid", incident.details);
71
+ },
72
+ });
73
+
74
+ antispam.setDetectorEnabled("poll", false);
75
+ console.log(antispam.getRecentIncidents(10));
76
+ ```
77
+
78
+ Turn `joins` on only if you want a burst of accounts joining at once to timeout the newest member. Pair it with `accounts.minAgeDays` if the raid is all-new alts. `impersonate.names` is for **staff/brand** clones; `matchBot` already covers the bot's own name.
79
+
80
+ `graceMessages` now also runs **impersonate** and **forward** during the hello window (still skips flood).
81
+
82
+ ## What's new in 1.5.0
83
+
84
+ Channel raids, reply spam, empty messages, embed walls, file allowlists, and per-user overlays. Kick/ban stay **off**. `raid` is **off** in `balanced` so busy servers do not false-positive.
85
+
86
+ | Option | Default | What it does |
87
+ | --- | --- | --- |
88
+ | `replies` | **on**, 6 / 8s | Too many Discord **replies** in a short window (reply ping / bump spam). |
89
+ | `blank` | **on** | Empty or whitespace-only messages (no files/stickers/embeds). |
90
+ | `embeds` | **on**, 6 | Too many embeds in one message. |
91
+ | `raid` | **off** (`strict`: on, 6 users / 5s) | Too many **distinct users** posting in the **same channel** at once. |
92
+ | `files.allowedExtensions` | `[]` | If set, only those extensions are allowed (after the blocklist). |
93
+ | `emojis.maxInWindow` | `0` (off) | Cap emojis+stickers across several messages. |
94
+ | `ignoreNsfw` | `false` | Skip NSFW channels. |
95
+ | `graceMessages` | `0` | First N messages from a user only run file/secret/link/word (say hi without flood). |
96
+ | `userOverrides` / `setUserConfig` | `{}` | Per-user config overlay (after roles, before channel). |
97
+
98
+ ```js
99
+ const antispam = new AntiSpam(client, {
100
+ preset: "balanced",
101
+ replies: { maxReplies: 5, windowMs: 8_000 },
102
+ blank: { enabled: true },
103
+ embeds: { maxEmbeds: 6 },
104
+ raid: { enabled: true, maxUsers: 8, windowMs: 6_000 },
105
+ files: { allowedExtensions: ["png", "jpg", "webp", "gif"] },
106
+ graceMessages: 2,
107
+ ignoreNsfw: true,
108
+ emojis: { maxInWindow: 30, windowMs: 10_000 },
109
+ });
110
+
111
+ antispam.setUserConfig("KNOWN_SPAMMER_ID", { flood: { maxMessages: 2 } });
112
+ ```
113
+
114
+ Turn `raid` on only if you want a busy general chat to trip when many people post at once (join raids). For a meme channel, leave it off or raise `maxUsers`.
115
+
36
116
  ## What's new in 1.4.0
37
117
 
38
118
  Secret scanning, cross-channel copy-paste, invisible unicode, attachment floods, and cleaner logs. Kick/ban stay **off**.
@@ -208,6 +288,8 @@ console.log(antispam.getStats());
208
288
 
209
289
  ## Table of contents
210
290
 
291
+ - [What's new in 1.6.0](#whats-new-in-160)
292
+ - [What's new in 1.5.0](#whats-new-in-150)
211
293
  - [What's new in 1.4.0](#whats-new-in-140)
212
294
  - [What's new in 1.3.0](#whats-new-in-130)
213
295
  - [What's new in 1.2.0](#whats-new-in-120)
@@ -254,6 +336,16 @@ console.log(antispam.getStats());
254
336
  | **echo** | Same text pasted in several channels |
255
337
  | **invisible** | Zero-width / bidi obfuscation |
256
338
  | **attach** | Attachment floods over a sliding window |
339
+ | **reply** | Too many replies in a short window |
340
+ | **blank** | Empty / whitespace-only messages |
341
+ | **embed** | Too many embeds in one message |
342
+ | **raid** | Many distinct users posting in one channel (off by default) |
343
+ | **forward** | Too many Discord forwarded messages in a window |
344
+ | **sticker** | Sticker dumps / optional sticker-only messages |
345
+ | **impersonate** | Nick/username too close to the bot or protected names |
346
+ | **join** | Mass joins in a short window (off by default) |
347
+ | **voice** | Too many voice messages in a window |
348
+ | **poll** | Too many polls in a window |
257
349
 
258
350
  Also included:
259
351
 
@@ -261,7 +353,7 @@ Also included:
261
353
  - Ignore lists for users, roles, channels, categories, guilds, and command prefixes
262
354
  - Per-guild, per-channel, and per-role overlays
263
355
  - Strike system with decay
264
- - Optional delete / warn / timeout / kick / ban / addRole / removeRole (kick and ban are **off** by default)
356
+ - Optional delete / warn / timeout / kick / ban / addRole / removeRole / slowmode (kick and ban are **off** by default)
265
357
  - `dryRun` to tune rules without punishing anyone
266
358
  - Edit scanning (links, mentions, and words)
267
359
  - Zero extra runtime dependencies (peer: `discord.js`)
@@ -379,6 +471,8 @@ npm run dev
379
471
  | Log embeds | **Embed Links** |
380
472
  | Kick / ban (opt-in) | **Kick Members** / **Ban Members** |
381
473
  | Mute / add / remove roles (opt-in) | **Manage Roles** |
474
+ | Slowmode on raid/join (opt-in) | **Manage Channels** |
475
+ | Join-raid detector | **Server Members Intent** (`guildMemberAdd`) |
382
476
 
383
477
  The bot will not sanction the guild owner or anyone above it in the role hierarchy. Those actions are skipped and reported in `onAction`.
384
478
 
@@ -388,10 +482,11 @@ Enable **Message Content Intent** in the [Discord Developer Portal](https://disc
388
482
 
389
483
  1. Ignores bots, webhooks, system messages, the owner, administrators, command prefixes, and anything in your ignore lists.
390
484
  2. Keeps a short **in-memory** history per user and guild (no database).
391
- 3. Runs detectors in this order: files → secrets → words → flood → hop → echo → duplicates → attachments → links → images → mentions → zalgo → newlines → punctuation → spoilers → invisible → accounts → length → caps → emojis. The first match wins.
485
+ 3. Runs detectors in this order: files → secrets → words → impersonate → flood → hop → echo → duplicates → attachments → raid → replies → blank → embeds → forwards → voice → polls → stickers → links → images → mentions → zalgo → newlines → punctuation → spoilers → invisible → accounts → length → caps → emojis. The first match wins.
392
486
  4. Adds one strike (with optional decay) and applies the configured punishment.
393
487
  5. Message edits re-check **links**, **mentions**, **words**, **secrets**, and **invisible**.
394
488
  6. Deletes can trigger **ghost** pings if `ghostPing.enabled` is true.
489
+ 7. Joins can trigger **join** raids if `joins.enabled` is true (`guildMemberAdd`).
395
490
 
396
491
  Call `antispam.stop()` on shutdown, hot reload, or plugin unload.
397
492
 
@@ -423,6 +518,7 @@ new AntiSpam(client, {
423
518
  | Block shorteners | no | yes | yes |
424
519
  | Channel hop | off | 5 / 8s | 3 / 6s |
425
520
  | Ghost pings | off | off | on |
521
+ | Join raids | off | off | on |
426
522
  | Auto timeout | off | 60s from 2 strikes | 5 min from 1 strike |
427
523
 
428
524
  ## Full configuration
@@ -441,8 +537,10 @@ Every option is optional. Missing fields fall back to the preset (or `balanced`
441
537
  | `ignoreAdministrators` | `boolean` | `true` | Skip members with Administrator. |
442
538
  | `checkEdits` | `boolean` | `true` | Re-scan edits for links, mentions, and words. |
443
539
  | `checkDeletes` | `boolean` | `true` | Listen for `messageDelete` (ghost pings). |
540
+ | `checkJoins` | `boolean` | `true` | Listen for `guildMemberAdd` (join raids). |
444
541
  | `ignoreThreads` | `boolean` | `false` | Skip messages in threads. |
445
542
  | `ignoreSystem` | `boolean` | `true` | Skip Discord system messages. |
543
+ | `ignoreBoosters` | `boolean` | `false` | Skip server boosters. |
446
544
  | `cleanupIntervalMs` | `number` | `60000` | How often the memory store is pruned. |
447
545
  | `locale` | `"en"` \| `"es"` | `"en"` | Language for warnings and log embeds. |
448
546
  | `ignorePermissions` | `string[]` | `[]` | Permission names that bypass checks. |
@@ -620,6 +718,7 @@ files: {
620
718
  enabled: true,
621
719
  blockedExtensions: ["exe", "bat", "cmd", "com", "scr", "dll", "msi", "vbs", "ps1", "jar", "apk"],
622
720
  maxBytes: 0, // 0 = unlimited
721
+ blockDoubleExtensions: true, // virus.exe.png
623
722
  severity: "critical",
624
723
  }
625
724
 
@@ -645,6 +744,8 @@ words: {
645
744
  regex: ["n[i1]tro\\s+free"],
646
745
  ignoreCase: true,
647
746
  matchWholeWord: true,
747
+ scanEmbeds: true,
748
+ normalizeHomoglyphs: true, // rаid-now (Cyrillic a) still matches
648
749
  severity: "high",
649
750
  }
650
751
 
@@ -687,6 +788,83 @@ checkDeletes: true
687
788
 
688
789
  Disable with `ghostPing: { enabled: false }` or `disabledDetectors: ["ghost"]`.
689
790
 
791
+ ### Forwards, stickers, voice, polls
792
+
793
+ ```js
794
+ forwards: {
795
+ enabled: true,
796
+ maxForwards: 4, // 4th forward in the window triggers
797
+ windowMs: 10_000,
798
+ severity: "medium",
799
+ }
800
+
801
+ stickers: {
802
+ enabled: true,
803
+ maxStickers: 3, // per message
804
+ maxInWindow: 8, // 0 = off
805
+ windowMs: 10_000,
806
+ blockOnly: false, // true = sticker-only messages (no text)
807
+ severity: "low",
808
+ }
809
+
810
+ voice: {
811
+ enabled: true,
812
+ maxVoice: 3,
813
+ windowMs: 15_000,
814
+ severity: "medium",
815
+ }
816
+
817
+ polls: {
818
+ enabled: true,
819
+ maxPolls: 2,
820
+ windowMs: 20_000,
821
+ severity: "medium",
822
+ }
823
+ ```
824
+
825
+ `lenient` turns forwards, voice, and polls off. `strict` tightens the windows and sets `stickers.blockOnly: true`.
826
+
827
+ ### Impersonation
828
+
829
+ Compares nick / username / display name to the bot (`matchBot`) and any extra `names`. Text is folded (homoglyphs stripped, alphanumeric only) before similarity.
830
+
831
+ ```js
832
+ impersonate: {
833
+ enabled: true,
834
+ matchBot: true,
835
+ names: ["Admin", "Moderador", "Staff"],
836
+ minSimilarity: 0.84,
837
+ checkNickname: true,
838
+ checkUsername: true,
839
+ checkDisplayName: true,
840
+ severity: "high",
841
+ }
842
+ ```
843
+
844
+ Skip the bot itself. Short names under 3 letters after folding are ignored.
845
+
846
+ ### Join raids
847
+
848
+ Off in `balanced` / `lenient`. On in `strict`. Needs `checkJoins: true` (default) and **Server Members Intent**. There is no message to delete: Disguard timeouts/kicks the newest joiner if those punishments are enabled.
849
+
850
+ ```js
851
+ joins: {
852
+ enabled: false,
853
+ maxJoins: 8, // 8th distinct join in the window triggers
854
+ windowMs: 10_000,
855
+ minAccountAgeDays: 0, // e.g. 1 = only count accounts younger than 1 day
856
+ severity: "critical",
857
+ }
858
+
859
+ checkJoins: true
860
+ ```
861
+
862
+ ```js
863
+ onJoin(incident) {
864
+ // no Message — use incident.userId / guildId
865
+ }
866
+ ```
867
+
690
868
  ### Punishment
691
869
 
692
870
  Kick and ban stay **disabled** on purpose. Turn them on only if you really want that.
@@ -709,6 +887,7 @@ punishment: {
709
887
  addRoleIds: [], // e.g. ["MUTE_ROLE_ID"] — needs Manage Roles
710
888
  removeRoleIds: [],
711
889
  minStrikesForRoles: 1,
890
+ slowmodeSeconds: 0, // e.g. 10 — on raid/join, needs Manage Channels
712
891
  }
713
892
 
714
893
  // timeout.scale: "none" | "linear" (base * strikes) | "exponential" (base * 2^n)
@@ -718,7 +897,7 @@ punishment: {
718
897
 
719
898
  `warnMessage` placeholders: `{user}` `{reason}` `{type}` `{strikes}`.
720
899
 
721
- With `escalate: true`, Disguard applies **one** hard action (timeout, or kick, or ban — whichever threshold you hit). Delete and warn can still run together. `addRole` / `removeRole` run if those ID lists are non-empty and strikes ≥ `minStrikesForRoles`.
900
+ With `escalate: true`, Disguard applies **one** hard action (timeout, or kick, or ban — whichever threshold you hit). Delete and warn can still run together. `addRole` / `removeRole` run if those ID lists are non-empty and strikes ≥ `minStrikesForRoles`. `slowmodeSeconds` only runs on **raid** and **join** hits; it stays until a moderator changes it.
722
901
 
723
902
  If the bot lacks a permission, or the target is higher in the hierarchy, that action is skipped and listed in `result.skipped`.
724
903
 
@@ -739,6 +918,9 @@ new AntiSpam(client, {
739
918
  onCooldown(message) {
740
919
  // Extra spam during punishment.cooldownMs (already deleted if deleteDuringCooldown).
741
920
  },
921
+ onJoin(incident) {
922
+ // Join raid. No message object.
923
+ },
742
924
  });
743
925
  ```
744
926
 
@@ -749,7 +931,8 @@ new AntiSpam(client, {
749
931
  type: "flood" | "duplicate" | "link" | "image" | "mention" | "caps" | "emoji"
750
932
  | "file" | "zalgo" | "newline" | "account" | "length"
751
933
  | "word" | "hop" | "punctuation" | "spoiler" | "ghost"
752
- | "secret" | "echo" | "invisible" | "attach",
934
+ | "secret" | "echo" | "invisible" | "attach" | "reply" | "blank" | "embed" | "raid"
935
+ | "forward" | "sticker" | "impersonate" | "join" | "voice" | "poll",
753
936
  severity: "low" | "medium" | "high" | "critical",
754
937
  userId: string,
755
938
  guildId: string,
@@ -757,7 +940,7 @@ new AntiSpam(client, {
757
940
  messageId: string,
758
941
  reason: string,
759
942
  details: Record<string, unknown>,
760
- recommendedActions: Array<"delete" | "warn" | "timeout" | "kick" | "ban" | "addRole" | "removeRole" | "purge">,
943
+ recommendedActions: Array<"delete" | "warn" | "timeout" | "kick" | "ban" | "addRole" | "removeRole" | "purge" | "slowmode">,
761
944
  timestamp: number,
762
945
  }
763
946
  ```
@@ -804,11 +987,15 @@ antispam.setChannelConfig(guildId, channelId, { images: { maxRepeats: 8 } });
804
987
  antispam.getChannelConfig(guildId, channelId);
805
988
  antispam.clearChannelConfig(guildId, channelId);
806
989
  antispam.setRoleConfig(roleId, { flood: { maxMessages: 12 } });
990
+ antispam.setUserConfig(userId, { flood: { maxMessages: 2 } });
991
+ antispam.clearUserConfig(userId);
807
992
  antispam.clearRoleConfig(roleId);
808
993
 
809
994
  antispam.use(customDetector);
810
995
  antispam.getStats();
811
996
  antispam.resetStats();
997
+ antispam.getRecentIncidents(20);
998
+ antispam.setDetectorEnabled("poll", false);
812
999
 
813
1000
  // Analyze only — no punishment, no callbacks
814
1001
  const incident = await antispam.analyze(message);
@@ -823,7 +1010,9 @@ Helpers you can import for your own tools:
823
1010
  import {
824
1011
  extractUrls,
825
1012
  normalizeText,
1013
+ foldHomoglyphs,
826
1014
  similarity,
1015
+ isForwardedMessage,
827
1016
  DEFAULT_PHISHING_KEYWORDS,
828
1017
  DEFAULT_SHORTENERS,
829
1018
  OFFICIAL_BRANDS,
@@ -964,9 +1153,21 @@ Wait a few seconds between categories so flood does not eat the next test.
964
1153
  | Echo | Same long sentence in 3 channels within 12s |
965
1154
  | Invisible | A message padded with many zero-width spaces |
966
1155
  | Attachments | 8+ files in 10 seconds |
1156
+ | Replies | Reply to someone 6 times in 8s |
1157
+ | Blank | A message with only spaces |
1158
+ | Embeds | A webhook-style dump with 7+ embeds |
1159
+ | Raid | Enable `raid` and have 8 people post in the same channel in 6s |
1160
+ | Forwards | Forward the same / any messages 4 times in 10s |
1161
+ | Stickers | Send 4 stickers in one message |
1162
+ | Impersonate | Set your nick to the bot's username (or a name in `impersonate.names`) |
1163
+ | Voice | Send 3 voice messages in 15s |
1164
+ | Polls | Post 2 polls in 20s |
1165
+ | Double ext | Upload `foto.exe.png` |
1166
+ | Homoglyph word | Enable `words` with `raid-now`, send `rаid-now` (Cyrillic **а**) |
1167
+ | Joins | Enable `joins` and have 8 accounts join within 10s |
967
1168
  | Edit | Send `hello`, then edit it to `Free Nitro https://bit.ly/test` |
968
1169
 
969
- Watch the terminal: `[detect] flood|duplicate|link|image|mention|caps|emoji|file|zalgo|newline|word|hop|punctuation|spoiler|ghost|secret|echo|invisible|attach`.
1170
+ Watch the terminal: `[detect] flood|duplicate|link|image|mention|caps|emoji|file|zalgo|newline|word|hop|punctuation|spoiler|ghost|secret|echo|invisible|attach|forward|sticker|impersonate|voice|poll|join`.
970
1171
 
971
1172
  The second strike applies a 1 minute timeout with the default example config. Strikes decay after 15 minutes, or call `resetUser`.
972
1173
 
@@ -1007,6 +1208,70 @@ MIT
1007
1208
 
1008
1209
  # Español
1009
1210
 
1211
+ ## Novedades de 1.6.0
1212
+
1213
+ Reenvíos, dumps de stickers, impersonación del bot/staff, raids de joins, mensajes de voz, encuestas y filtros de palabras/archivos más difíciles de esquivar. Kick/ban siguen **apagados**. `joins` va **apagado** en `balanced`.
1214
+
1215
+ | Opción | Default | Qué hace |
1216
+ | --- | --- | --- |
1217
+ | `forwards` | **on**, 4 / 10s | Demasiados mensajes **reenviados** de Discord. |
1218
+ | `stickers` | **on**, 3 por mensaje, 8 / 10s | Dump de stickers. `blockOnly` (off) caza mensajes solo-sticker. |
1219
+ | `impersonate` | **on**, bot a 0.84 | Nick / username / display name demasiado parecido al **bot** (o a `names`). |
1220
+ | `joins` | **off** (`strict`: on, 6 / 8s) | Demasiadas **cuentas distintas entrando** al servidor. Hace falta `guildMemberAdd`. |
1221
+ | `voice` | **on**, 3 / 15s | Demasiados **mensajes de voz**. |
1222
+ | `polls` | **on**, 2 / 20s | Demasiadas **encuestas**. |
1223
+ | `files.blockDoubleExtensions` | `true` | Caza `virus.exe.png`. |
1224
+ | `words.scanEmbeds` | `true` | La lista de palabras también lee embeds. |
1225
+ | `words.normalizeHomoglyphs` | `true` | Pliega lookalikes cirílicos / griegos / fullwidth. |
1226
+ | `ignoreBoosters` | `false` | Ignora boosters. |
1227
+ | `punishment.slowmodeSeconds` | `0` | En hits `raid` / `join`, pone slowmode (hace falta **Manage Channels**). |
1228
+ | `getRecentIncidents()` | — | Últimos 50 hits en vivo (no `analyze()`). |
1229
+ | `setDetectorEnabled(type, bool)` | — | Enciende/apaga un detector. |
1230
+ | `onJoin` | — | Callback de raid de joins (sin mensaje). |
1231
+
1232
+ ```js
1233
+ new AntiSpam(client, {
1234
+ forwards: { maxForwards: 3 },
1235
+ impersonate: { names: ["Admin", "Moderador"] },
1236
+ joins: { enabled: true, maxJoins: 8 },
1237
+ ignoreBoosters: true,
1238
+ words: { enabled: true, list: ["raid-now"], normalizeHomoglyphs: true },
1239
+ punishment: { slowmodeSeconds: 10 },
1240
+ onJoin(incident) {
1241
+ console.log("join raid", incident.details);
1242
+ },
1243
+ });
1244
+ antispam.setDetectorEnabled("poll", false);
1245
+ ```
1246
+
1247
+ Activa `joins` solo si quieres que un burst de cuentas nuevas dispare timeout al último en entrar.
1248
+
1249
+ ## Novedades de 1.5.0
1250
+
1251
+ Raids de canal, spam de replies, mensajes vacíos, paredes de embeds, allowlist de archivos y config por usuario. Kick/ban siguen **apagados**. `raid` va **apagado** en `balanced`.
1252
+
1253
+ | Opción | Default | Qué hace |
1254
+ | --- | --- | --- |
1255
+ | `replies` | **on**, 6 / 8s | Demasiadas **respuestas** seguidas. |
1256
+ | `blank` | **on** | Mensajes vacíos o solo espacios. |
1257
+ | `embeds` | **on**, 6 | Demasiados embeds en un mensaje. |
1258
+ | `raid` | **off** (`strict`: on) | Demasiados **usuarios distintos** escribiendo en el **mismo canal**. |
1259
+ | `files.allowedExtensions` | `[]` | Si lo rellenas, solo esas extensiones pasan. |
1260
+ | `emojis.maxInWindow` | `0` (off) | Tope de emojis+stickers entre varios mensajes. |
1261
+ | `ignoreNsfw` | `false` | Ignora canales NSFW. |
1262
+ | `graceMessages` | `0` | Los primeros N mensajes solo miran file/secret/link/word. |
1263
+ | `setUserConfig` | — | Overlay por usuario. |
1264
+
1265
+ ```js
1266
+ new AntiSpam(client, {
1267
+ raid: { enabled: true, maxUsers: 8 },
1268
+ files: { allowedExtensions: ["png", "jpg", "webp"] },
1269
+ graceMessages: 2,
1270
+ ignoreNsfw: true,
1271
+ });
1272
+ antispam.setUserConfig("ID_USER", { flood: { maxMessages: 2 } });
1273
+ ```
1274
+
1010
1275
  ## Novedades de 1.4.0
1011
1276
 
1012
1277
  Escaneo de secretos, copypasta entre canales, unicode invisible, flood de adjuntos y logs por webhook. Kick/ban siguen **apagados**.
@@ -1124,6 +1389,8 @@ new AntiSpam(client, {
1124
1389
 
1125
1390
  ## Tabla de contenidos
1126
1391
 
1392
+ - [Novedades de 1.6.0](#novedades-de-160)
1393
+ - [Novedades de 1.5.0](#novedades-de-150)
1127
1394
  - [Novedades de 1.4.0](#novedades-de-140)
1128
1395
  - [Novedades de 1.3.0](#novedades-de-130)
1129
1396
  - [Novedades de 1.2.0](#novedades-de-120)
@@ -1149,7 +1416,7 @@ new AntiSpam(client, {
1149
1416
 
1150
1417
  **Disguard** es una librería antispam para bots de discord.js v14. No es un bot: la enchufas a tu `Client` y tú decides umbrales, listas y castigos.
1151
1418
 
1152
- Detecta flood, salto de canales, palabras bloqueadas, phishing / enlaces no deseados (también en embeds), imágenes repetidas, spam de menciones, ghost pings, mayúsculas, emojis, archivos peligrosos, zalgo y paredes de líneas.
1419
+ Detecta flood, salto de canales, palabras bloqueadas, phishing / enlaces no deseados (también en embeds), imágenes repetidas, spam de menciones, ghost pings, reenvíos, impersonación, raids de joins, mayúsculas, emojis, archivos peligrosos, zalgo y paredes de líneas.
1153
1420
 
1154
1421
  ## Requisitos
1155
1422
 
@@ -1236,6 +1503,8 @@ npm run dev
1236
1503
  | Embed de logs | **Embed Links** |
1237
1504
  | Kick / ban (opt-in) | **Kick Members** / **Ban Members** |
1238
1505
  | Mute / add / remove roles (opt-in) | **Manage Roles** |
1506
+ | Slowmode en raid/join (opt-in) | **Manage Channels** |
1507
+ | Raid de joins | **Server Members Intent** (`guildMemberAdd`) |
1239
1508
 
1240
1509
  No puede sancionar al dueño del servidor ni a quien esté por encima del bot en la jerarquía.
1241
1510
 
@@ -1245,10 +1514,11 @@ Activa **Message Content Intent** en el [Portal de Discord](https://discord.com/
1245
1514
 
1246
1515
  1. Ignora bots, webhooks, mensajes de sistema, el dueño, administradores, prefijos de comando y tus listas de ignore.
1247
1516
  2. Guarda en **memoria** un historial corto por usuario y servidor. Sin base de datos.
1248
- 3. Pasa el mensaje por: archivos → secretos → palabras → flood → hop → echo → duplicados → adjuntos → enlaces → imágenes → menciones → zalgo → líneas → puntuación → spoilers → invisibles → cuentas → longitud → caps → emojis. El primero que dispare gana.
1517
+ 3. Pasa el mensaje por: archivos → secretos → palabras → impersonate → flood → hop → echo → duplicados → adjuntos → raid → replies → vacíos → embeds → reenvíos → voz → encuestas → stickers → enlaces → imágenes → menciones → zalgo → líneas → puntuación → spoilers → invisibles → cuentas → longitud → caps → emojis. El primero que dispare gana.
1249
1518
  4. Suma un strike (con caducidad) y aplica el castigo configurado.
1250
1519
  5. Las ediciones revisan **enlaces**, **menciones** y **palabras**.
1251
1520
  6. Los borrados pueden disparar **ghost** ping si `ghostPing.enabled` es true.
1521
+ 7. Los joins pueden disparar **join** si `joins.enabled` es true.
1252
1522
 
1253
1523
  Llama `antispam.stop()` al apagar el proceso.
1254
1524
 
@@ -1283,8 +1553,10 @@ Todo es opcional. Lo que no pongas usa el preset.
1283
1553
  | `ignoreAdministrators` | `boolean` | `true` | Ignora quien tenga Administrator. |
1284
1554
  | `checkEdits` | `boolean` | `true` | Revisa ediciones (enlaces, menciones y palabras). |
1285
1555
  | `checkDeletes` | `boolean` | `true` | Escucha `messageDelete` (ghost pings). |
1556
+ | `checkJoins` | `boolean` | `true` | Escucha `guildMemberAdd` (raids de joins). |
1286
1557
  | `ignoreThreads` | `boolean` | `false` | Ignora hilos. |
1287
1558
  | `ignoreSystem` | `boolean` | `true` | Ignora mensajes de sistema. |
1559
+ | `ignoreBoosters` | `boolean` | `false` | Ignora boosters. |
1288
1560
  | `cleanupIntervalMs` | `number` | `60000` | Limpieza de memoria. |
1289
1561
  | `roleOverrides` | `object` | `{}` | Parches por rol. |
1290
1562
 
@@ -1383,7 +1655,7 @@ emojis: { enabled: true, maxEmojis: 12, maxStickers: 3, severity: "low" },
1383
1655
  ### Archivos, zalgo, saltos de línea
1384
1656
 
1385
1657
  ```js
1386
- files: { enabled: true, blockedExtensions: ["exe", "bat", "cmd", "dll", "msi"], maxBytes: 0, severity: "critical" },
1658
+ files: { enabled: true, blockedExtensions: ["exe", "bat", "cmd", "dll", "msi"], maxBytes: 0, blockDoubleExtensions: true, severity: "critical" },
1387
1659
  zalgo: { enabled: true, maxCombining: 20, severity: "medium" },
1388
1660
  newlines: { enabled: true, maxNewlines: 15, severity: "low" },
1389
1661
  ```
@@ -1391,14 +1663,20 @@ newlines: { enabled: true, maxNewlines: 15, severity: "low" },
1391
1663
  ### Palabras, hop, puntuación, spoilers, ghost ping
1392
1664
 
1393
1665
  ```js
1394
- words: { enabled: false, list: ["raid-now"], regex: [], ignoreCase: true, matchWholeWord: true, severity: "high" },
1666
+ words: { enabled: false, list: ["raid-now"], regex: [], ignoreCase: true, matchWholeWord: true, scanEmbeds: true, normalizeHomoglyphs: true, severity: "high" },
1395
1667
  hop: { enabled: true, maxChannels: 5, windowMs: 8_000, severity: "high" },
1396
1668
  punctuation: { enabled: true, maxRepeated: 10, severity: "low" },
1397
1669
  spoilers: { enabled: true, maxSpoilers: 8, severity: "low" },
1398
1670
  ghostPing: { enabled: false, minMentions: 1, maxAgeMs: 15_000, severity: "high" },
1671
+ forwards: { enabled: true, maxForwards: 4, windowMs: 10_000, severity: "medium" },
1672
+ stickers: { enabled: true, maxStickers: 3, maxInWindow: 8, blockOnly: false, severity: "low" },
1673
+ impersonate: { enabled: true, matchBot: true, names: ["Admin", "Moderador"], minSimilarity: 0.84, severity: "high" },
1674
+ joins: { enabled: false, maxJoins: 8, windowMs: 10_000, severity: "critical" },
1675
+ voice: { enabled: true, maxVoice: 3, windowMs: 15_000, severity: "medium" },
1676
+ polls: { enabled: true, maxPolls: 2, windowMs: 20_000, severity: "medium" },
1399
1677
  ```
1400
1678
 
1401
- `lenient` apaga hop y puntuación. `strict` enciende ghost ping y aprieta hop a 3 canales / 6s.
1679
+ `lenient` apaga hop, puntuación, reenvíos, voz y encuestas. `strict` enciende ghost ping, joins y aprieta umbrales.
1402
1680
 
1403
1681
  ### Castigos
1404
1682
 
@@ -1421,6 +1699,7 @@ punishment: {
1421
1699
  addRoleIds: [],
1422
1700
  removeRoleIds: [],
1423
1701
  minStrikesForRoles: 1,
1702
+ slowmodeSeconds: 0,
1424
1703
  }
1425
1704
  ```
1426
1705
 
@@ -1439,6 +1718,7 @@ new AntiSpam(client, {
1439
1718
  console.error(context, error);
1440
1719
  },
1441
1720
  onCooldown(message) {},
1721
+ onJoin(incident) {},
1442
1722
  });
1443
1723
  ```
1444
1724
 
@@ -1461,7 +1741,10 @@ antispam.resetUser(guildId, userId);
1461
1741
  antispam.setGuildConfig(guildId, { flood: { maxMessages: 3 } });
1462
1742
  antispam.setChannelConfig(guildId, channelId, { flood: { maxMessages: 12 } });
1463
1743
  antispam.setRoleConfig(roleId, { emojis: { maxEmojis: 40 } });
1744
+ antispam.setUserConfig(userId, { flood: { maxMessages: 2 } });
1464
1745
  antispam.getStats();
1746
+ antispam.getRecentIncidents(10);
1747
+ antispam.setDetectorEnabled("poll", false);
1465
1748
 
1466
1749
  const incident = await antispam.analyze(message);
1467
1750
  const editado = await antispam.analyze(message, { isEdit: true });
@@ -1540,9 +1823,21 @@ Espera unos segundos entre categorías.
1540
1823
  | Echo | La misma frase larga en 3 canales en 12s |
1541
1824
  | Invisible | Un mensaje con muchos espacios de ancho cero |
1542
1825
  | Adjuntos | 8+ archivos en 10 segundos |
1826
+ | Replies | Responder 6 veces en 8s |
1827
+ | Vacío | Un mensaje solo con espacios |
1828
+ | Embeds | 7+ embeds en un mensaje |
1829
+ | Raid | Activa `raid` y que 8 personas escriban en el mismo canal |
1830
+ | Reenvíos | Reenvía 4 mensajes en 10s |
1831
+ | Stickers | 4 stickers en un mensaje |
1832
+ | Impersonar | Pon tu nick igual al del bot (o un nombre de `impersonate.names`) |
1833
+ | Voz | 3 mensajes de voz en 15s |
1834
+ | Encuestas | 2 polls en 20s |
1835
+ | Doble ext | Sube `foto.exe.png` |
1836
+ | Homoglyph | Activa `words` con `raid-now` y manda `rаid-now` (а cirílica) |
1837
+ | Joins | Activa `joins` y que 8 cuentas entren en 10s |
1543
1838
  | Edición | Manda `hola` y edítalo a `Free Nitro https://bit.ly/test` |
1544
1839
 
1545
- En la terminal: `[detect] flood|duplicate|link|image|mention|caps|emoji|file|zalgo|newline|word|hop|punctuation|spoiler|ghost|secret|echo|invisible|attach`.
1840
+ En la terminal: `[detect] flood|duplicate|link|image|mention|caps|emoji|file|zalgo|newline|word|hop|punctuation|spoiler|ghost|secret|echo|invisible|attach|forward|sticker|impersonate|voice|poll|join`.
1546
1841
 
1547
1842
  Al segundo strike el ejemplo mete timeout de 1 minuto. Los strikes caducan a los 15 minutos, o usa `resetUser`.
1548
1843
 
@@ -1,6 +1,6 @@
1
1
  import { type Client, type Message } from "discord.js";
2
2
  import { MemoryStore } from "./store/MemoryStore";
3
- import type { AntiSpamOptions, AntiSpamStats, DeepPartial, Detector, Incident, ResolvedConfig } from "./types";
3
+ import type { AntiSpamOptions, AntiSpamStats, DeepPartial, Detector, DetectorType, Incident, ResolvedConfig } from "./types";
4
4
  export declare class AntiSpam {
5
5
  readonly client: Client;
6
6
  readonly store: MemoryStore;
@@ -10,12 +10,14 @@ export declare class AntiSpam {
10
10
  private readonly guildConfigs;
11
11
  private readonly channelConfigs;
12
12
  private stats;
13
+ private readonly recentIncidents;
13
14
  private started;
14
15
  private paused;
15
16
  private cleanupTimer;
16
17
  private readonly onMessage;
17
18
  private readonly onEdit;
18
19
  private readonly onDelete;
20
+ private readonly onJoin;
19
21
  constructor(client: Client, options?: AntiSpamOptions);
20
22
  /** Starts listening to `messageCreate` (and `messageUpdate` / `messageDelete` if enabled). */
21
23
  start(): this;
@@ -40,10 +42,14 @@ export declare class AntiSpam {
40
42
  clearChannelConfig(guildId: string, channelId: string): void;
41
43
  setRoleConfig(roleId: string, patch: DeepPartial<ResolvedConfig>): void;
42
44
  clearRoleConfig(roleId: string): void;
45
+ setUserConfig(userId: string, patch: DeepPartial<ResolvedConfig>): void;
46
+ clearUserConfig(userId: string): void;
43
47
  getStrikes(guildId: string, userId: string): number;
44
48
  isCoolingDown(guildId: string, userId: string): boolean;
45
49
  resetUser(guildId: string, userId: string): void;
46
50
  getStats(): AntiSpamStats;
51
+ getRecentIncidents(limit?: number): Incident[];
52
+ setDetectorEnabled(type: DetectorType, enabled: boolean): this;
47
53
  resetStats(): void;
48
54
  /**
49
55
  * Analyzes a message without punishing.
@@ -54,10 +60,12 @@ export declare class AntiSpam {
54
60
  }): Promise<Incident | null>;
55
61
  private handle;
56
62
  private handleDelete;
63
+ private handleJoin;
57
64
  shouldIgnore(message: Message): boolean;
58
65
  private configFor;
59
66
  private orderedDetectors;
60
67
  private toSnapshot;
68
+ private pushRecent;
61
69
  private maxRetentionMs;
62
70
  }
63
71
  export declare function createAntiSpam(client: Client, options?: AntiSpamOptions): AntiSpam;
@@ -1 +1 @@
1
- {"version":3,"file":"AntiSpam.d.ts","sourceRoot":"","sources":["../src/AntiSpam.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AntiSpam.d.ts","sourceRoot":"","sources":["../src/AntiSpam.ts"],"names":[],"mappings":"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"}