Compare commits

...

74 Commits

Author SHA1 Message Date
semantic-release-bot
0a5a9c3e27 chore(release): 1.4.1 [skip ci]
## @revanced/discord-bot [1.4.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.4.0...@revanced/discord-bot@1.4.1) (2025-09-13)

### Bug Fixes

* **bots/discord/database:** use non-deprecated way to create indexes ([8cc2377](8cc2377cbf))
* **bots/discord:** allow partial users ([dd3e7d2](dd3e7d2ee0))
* **bots/discord:** error in cache `keepOverLimit` comparison ([567c5d2](567c5d2c7f))
2025-09-13 12:28:35 +00:00
PalmDevs
50a205c430 chore: update dependencies 2025-09-13 19:27:45 +07:00
PalmDevs
dd3e7d2ee0 fix(bots/discord): allow partial users 2025-09-13 19:27:43 +07:00
PalmDevs
567c5d2c7f fix(bots/discord): error in cache keepOverLimit comparison 2025-09-13 19:27:42 +07:00
PalmDevs
8cc2377cbf fix(bots/discord/database): use non-deprecated way to create indexes 2025-09-13 19:27:41 +07:00
semantic-release-bot
f3c199d573 chore(release): 1.4.0 [skip ci]
# @revanced/discord-bot [1.4.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.3.0...@revanced/discord-bot@1.4.0) (2025-07-11)

### Bug Fixes

* **bots/discord:** pass non-empty out of scope label to discord ([fbd9480](fbd9480036))

### Features

* **bots/discord:** react to label classified response ([96a9b83](96a9b83c48))
2025-07-11 18:23:24 +00:00
PalmDevs
42c0facef1 chore: format 2025-07-12 01:22:30 +07:00
PalmDevs
96a9b83c48 feat(bots/discord): react to label classified response 2025-07-12 01:22:28 +07:00
PalmDevs
fbd9480036 fix(bots/discord): pass non-empty out of scope label to discord 2025-07-12 01:22:27 +07:00
semantic-release-bot
cc02c0a775 chore(release): 1.3.0 [skip ci]
# @revanced/discord-bot [1.3.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.3...@revanced/discord-bot@1.3.0) (2025-07-11)

### Features

* **bots/discord:** support training without label ([c68cfd1](c68cfd1c01))
2025-07-11 18:08:35 +00:00
semantic-release-bot
c83e219088 chore(release): 1.1.0 [skip ci]
# @revanced/bot-websocket-api [1.1.0](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.2...@revanced/bot-websocket-api@1.1.0) (2025-07-11)

### Features

* **apis/websocket:** support training without label ([670cc70](670cc70057))
2025-07-11 18:07:46 +00:00
PalmDevs
6164e6c1a5 chore: format 2025-07-12 01:06:45 +07:00
PalmDevs
47d2f8c015 chore: update dependencies and remove outdated patches 2025-07-12 01:06:43 +07:00
PalmDevs
c68cfd1c01 feat(bots/discord): support training without label 2025-07-12 01:06:41 +07:00
PalmDevs
2b1928e116 feat(packages/api): support training without label 2025-07-12 01:06:39 +07:00
PalmDevs
670cc70057 feat(apis/websocket): support training without label 2025-07-12 01:06:37 +07:00
Palm
439f301872 chore: merge dev to main 2025-07-11 21:05:10 +07:00
Pun Butrach
aeccf12906 ci: Use default fetch-depth of 1 for checkout step (#47) 2025-07-08 18:14:54 +02:00
semantic-release-bot
b4ac8433f4 chore(release): 1.2.3 [skip ci]
## @revanced/discord-bot [1.2.3](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.2...@revanced/discord-bot@1.2.3) (2025-06-23)

### Bug Fixes

* **bots/discord:** disable unneeded cache, enable message cache sweeping ([3a0f0fe](3a0f0fe786))
2025-06-23 13:33:41 +00:00
semantic-release-bot
b01a4accee chore(release): 1.0.2 [skip ci]
## @revanced/bot-websocket-api [1.0.2](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.1...@revanced/bot-websocket-api@1.0.2) (2025-06-23)
2025-06-23 13:32:58 +00:00
PalmDevs
858e989bdc build(Needs bump): update dependencies 2025-06-23 20:32:18 +07:00
PalmDevs
3a0f0fe786 fix(bots/discord): disable unneeded cache, enable message cache sweeping 2025-06-23 20:30:28 +07:00
semantic-release-bot
409e850768 chore(release): 1.2.2 [skip ci]
## @revanced/discord-bot [1.2.2](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.1...@revanced/discord-bot@1.2.2) (2025-06-10)

### Bug Fixes

* **bots/discord:** use intervals for checking expired presets ([6e89b87](6e89b874cd))
2025-06-10 17:40:44 +00:00
PalmDevs
6e89b874cd fix(bots/discord): use intervals for checking expired presets 2025-06-11 00:40:08 +07:00
semantic-release-bot
b4a5c62549 chore(release): 1.2.1 [skip ci]
## @revanced/discord-bot [1.2.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.0...@revanced/discord-bot@1.2.1) (2025-05-02)

### Bug Fixes

* **bots/discord:** fix timeout overflow check for role presets ([495f686](495f686292))
2025-05-02 16:11:20 +00:00
PalmDevs
495f686292 fix(bots/discord): fix timeout overflow check for role presets 2025-05-02 23:01:37 +07:00
semantic-release-bot
71eb11b67f chore(release): 1.2.0 [skip ci]
# @revanced/discord-bot [1.2.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.1.2...@revanced/discord-bot@1.2.0) (2025-05-02)

### Features

* **bots/discord:** switch duration parser to `@sapphire/duration` ([04ce825](04ce8252c0))
2025-05-02 15:58:00 +00:00
PalmDevs
04ce8252c0 feat(bots/discord): switch duration parser to @sapphire/duration 2025-05-02 22:57:13 +07:00
semantic-release-bot
27e06db1d9 chore(release): 1.1.2 [skip ci]
## @revanced/discord-bot [1.1.2](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.1.1...@revanced/discord-bot@1.1.2) (2025-04-16)

### Bug Fixes

* **bots/discord/commands/admin/reload:** fix type error ([3908854](3908854fe0))
* **bots/discord/commands/moderation:** check if timeout amount is safe in role-preset commands ([0c1382c](0c1382c558))
* **bots/discord:** replace use of deprecated `options.ephemeral` in replies ([31e5cf7](31e5cf7fc5))
2025-04-16 13:41:16 +00:00
PalmDevs
3908854fe0 fix(bots/discord/commands/admin/reload): fix type error 2025-04-16 20:40:16 +07:00
PalmDevs
0c1382c558 fix(bots/discord/commands/moderation): check if timeout amount is safe in role-preset commands 2025-04-16 20:40:14 +07:00
PalmDevs
410d289297 ci(release): hash new lockfile for dependency caching 2025-04-16 20:40:12 +07:00
PalmDevs
31e5cf7fc5 fix(bots/discord): replace use of deprecated options.ephemeral in replies 2025-04-16 20:40:11 +07:00
semantic-release-bot
4e797a2cfd chore(release): 1.1.1 [skip ci]
## @revanced/discord-bot [1.1.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.1.0...@revanced/discord-bot@1.1.1) (2025-04-14)

### Bug Fixes

* **bots/discord:** fix sticky msg force timer always starting, add more logging ([cb4dc42](cb4dc42dfa))
2025-04-14 14:09:38 +00:00
PalmDevs
cb4dc42dfa fix(bots/discord): fix sticky msg force timer always starting, add more logging 2025-04-14 21:08:47 +07:00
semantic-release-bot
33ba5b1f61 chore(release): 1.1.0 [skip ci]
# @revanced/discord-bot [1.1.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.4...@revanced/discord-bot@1.1.0) (2025-04-14)

### Features

* **bots/discord:** delete and send sticky msg concurrently, add more logging ([247a00f](247a00f57f))
2025-04-14 14:01:23 +00:00
PalmDevs
247a00f57f feat(bots/discord): delete and send sticky msg concurrently, add more logging 2025-04-14 21:00:24 +07:00
semantic-release-bot
0da3c989cd chore(release): 1.0.4 [skip ci]
## @revanced/discord-bot [1.0.4](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.3...@revanced/discord-bot@1.0.4) (2025-04-14)

### Bug Fixes

* **bots/discord:** fix sticky messages logic again ([aa7501c](aa7501c309))
* **bots/discord:** remove expired presets from db if unapplying is not possible ([9d705e5](9d705e580c))
2025-04-14 12:54:56 +00:00
PalmDevs
16d97f409c chore: format 2025-04-14 19:53:58 +07:00
PalmDevs
539025f2d4 chore(bots/discord): separate discord ready event handlers 2025-04-14 19:53:56 +07:00
PalmDevs
9d705e580c fix(bots/discord): remove expired presets from db if unapplying is not possible 2025-04-14 19:53:54 +07:00
PalmDevs
aa7501c309 fix(bots/discord): fix sticky messages logic again 2025-04-14 19:53:53 +07:00
semantic-release-bot
00118b4a1b chore(release): 1.0.3 [skip ci]
## @revanced/discord-bot [1.0.3](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.2...@revanced/discord-bot@1.0.3) (2025-04-09)

### Bug Fixes

* **bots/discord:** attempt to fix sticky messages one last time ([65288ec](65288ec424))
2025-04-09 14:06:27 +00:00
PalmDevs
65288ec424 fix(bots/discord): attempt to fix sticky messages one last time 2025-04-09 21:05:37 +07:00
PalmDevs
a5067889b2 chore: update monorepo README 2025-04-09 21:05:35 +07:00
semantic-release-bot
8efa9091a4 chore(release): 1.0.2 [skip ci]
## @revanced/discord-bot [1.0.2](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.1...@revanced/discord-bot@1.0.2) (2025-04-09)
2025-04-09 13:48:21 +00:00
semantic-release-bot
0e44bb5ffe chore(release): 1.0.1 [skip ci]
## @revanced/bot-websocket-api [1.0.1](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0...@revanced/bot-websocket-api@1.0.1) (2025-04-09)

### Bug Fixes

* **bots/discord:** attempt to fix sticky messages again ([7564b1a](7564b1a8f0))
2025-04-09 13:47:36 +00:00
PalmDevs
8942d27453 build(Needs bump): fix webhook not triggering 2025-04-09 20:46:53 +07:00
semantic-release-bot
ca3ac64390 chore(release): 1.0.1 [skip ci]
## @revanced/discord-bot [1.0.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.0...@revanced/discord-bot@1.0.1) (2025-04-09)

### Bug Fixes

* **bots/discord:** attempt to fix sticky messages again ([7564b1a](7564b1a8f0))
2025-04-09 13:35:59 +00:00
semantic-release-bot
5c2429aed7 chore(release): 1.0.1 [skip ci]
## @revanced/bot-websocket-api [1.0.1](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0...@revanced/bot-websocket-api@1.0.1) (2025-04-09)

### Bug Fixes

* **bots/discord:** attempt to fix sticky messages again ([7564b1a](7564b1a8f0))
2025-04-09 13:35:20 +00:00
PalmDevs
7564b1a8f0 fix(bots/discord): attempt to fix sticky messages again 2025-04-09 20:14:32 +07:00
Palm
5c9f4c6638 ci(release): specify correct permissions for job 2025-04-09 19:53:47 +07:00
Palm
69de750e6a ci(release): use secrets.GITHUB_TOKEN 2025-04-09 19:41:49 +07:00
semantic-release-bot
83f9780f9c chore(release): 1.0.0 [skip ci]
# @revanced/discord-bot 1.0.0 (2025-04-04)

### Bug Fixes

* **bot/discord:** start remove preset timeout for `role-preset` command ([cbf9116](cbf91162e2))
* **bots/discord/commands/eval:** evaluate in current context ([5925d90](5925d90209))
* **bots/discord/commands/mute:** use existing `parseDuration` util function' ([3e07429](3e07429664))
* **bots/discord/commands/unmute:** fix option description and embeds ([7fdf8c0](7fdf8c0dc7))
* **bots/discord/commands:** minor issues ([3b2596e](3b2596e748))
* **bots/discord/commands:** refactor and add checks ([a2bf3ea](a2bf3eade9))
* **bots/discord/database:** fix schema for role presets ([4aa138a](4aa138a9af))
* **bots/discord/scripts/build:** check if dist dir exists before cleaning ([c06033e](c06033e573))
* **bots/discord/scripts:** unintentional escaping on windows ([09dc706](09dc70632d))
* **bots/discord/utils/discord/embeds:** set thumbnail on moderation embeds ([b056291](b056291ad0))
* **bots/discord/utils/discord/rolePresets:** correct property access for presets ([4c6ad11](4c6ad11be3))
* **bots/discord/utils/discord:** add `moderation` module related functions ([7e8270f](7e8270f7d2))
* **bots/discord/utils/duration:** fix empty string returning with duration is 0 ([83c314e](83c314ef5f))
* **bots/discord/utils/duration:** fix specified default unit not working ([d138af4](d138af46d1))
* **bots/discord/utils/duration:** make second the default unit ([5d1af3c](5d1af3c31c))
* **bots/discord:** add GuildMember partial ([8e3946a](8e3946a666))
* **bots/discord:** allow access to `context` in `/eval` and await result ([99f65f0](99f65f07f5))
* **bots/discord:** always true check causing no messages to be scanned ([98ec37b](98ec37b5d1))
* **bots/discord:** apply active role presets if members rejoin ([f50b26b](f50b26b82d))
* **bots/discord:** attempt to fix stuck sticky message timeouts ([3ed5bd1](3ed5bd11ac))
* **bots/discord:** await when putting entries into db ([4da6175](4da6175cf5))
* **bots/discord:** broken regex when prefix set to special characters ([ab62e55](ab62e55e76))
* **bots/discord:** check token before connecting to bot api ([f3e4408](f3e4408aa2))
* **bots/discord:** ci issues causing database to not be auto generated ([673aa18](673aa189be))
* **bots/discord:** clear role presets after they expire ([faa81f4](faa81f4d88))
* **bots/discord:** connect to discord API even if initial bot API connection fails ([6658b58](6658b582db))
* **bots/discord:** contextify object before sandboxing ([062735f](062735f6d5))
* **bots/discord:** correct permission check logic ([dd8872c](dd8872c027))
* **bots/discord:** correct sticky messages logic ([de8bef6](de8bef6520))
* **bots/discord:** correct timer active condition for sticky messages ([96065ff](96065ff175))
* **bots/discord:** correct whitelist logic ([49c29be](49c29bebfb))
* **bots/discord:** cross-device link build errors ([38c0699](38c06997b4))
* **bots/discord:** decrease length of an option in `ban` command ([22d3eea](22d3eea88d))
* **bots/discord:** delete expired appliedPresets entries after unapplying ([14c98e8](14c98e87df))
* **bots/discord:** deployment runtime errors due to minification ([a60c60c](a60c60c0f9))
* **bots/discord:** do decancer after resetting nickname ([0303fe3](0303fe3e36))
* **bots/discord:** do not remove unrelated reactions ([031fd26](031fd26b26))
* **bots/discord:** don't refresh timer if force timer is active for sticky messages ([4abac0c](4abac0c890))
* **bots/discord:** filter out text triggers correctly from image-only scans ([8c0dd67](8c0dd67d03))
* **bots/discord:** fix freeze on prod builds ([8efb549](8efb549453))
* **bots/discord:** fix get response logic ([3261294](3261294822))
* **bots/discord:** fix reload not working ([11582d5](11582d5034))
* **bots/discord:** follow-up if reply is already sent when error ([f75060b](f75060bc9c))
* **bots/discord:** give only removed roles for role presets ([522ad28](522ad28fd8))
* **bots/discord:** hanging process when disconnecting from API too many times ([d31616e](d31616ebcb))
* **bots/discord:** import `config` from context ([763ef25](763ef253f9))
* **bots/discord:** make `/eval` work ([eaa25f2](eaa25f2eb5))
* **bots/discord:** messed up file name for `unmute` command ([399dca7](399dca7153))
* **bots/discord:** only check for member permissions when specified while correcting responses ([b79a1c7](b79a1c7575))
* **bots/discord:** open database as read-write ([c366840](c36684091d))
* **bots/discord:** owners cannot bypass checks on some commands ([39cba97](39cba97341))
* **bots/discord:** parse larger units of durations, fix wrong timestamp in mod embed ([6c8dce0](6c8dce0593))
* **bots/discord:** persist changes in context for eval command ([5b4965d](5b4965dcc7))
* **bots/discord:** provide discord token for `reload` command ([dd21a5a](dd21a5abad))
* **bots/discord:** remove auto-generated files ([fb8af00](fb8af00866))
* **bots/discord:** remove bad text channel checks ([f5939e2](f5939e2528))
* **bots/discord:** remove redundant footer for response embeds ([412e003](412e00317d))
* **bots/discord:** remove usage of macros ([7f27c56](7f27c5607c))
* **bots/discord:** remove useless feature ([d830e48](d830e48bc2))
* **bots/discord:** replace duration parser with a library ([94c4fed](94c4fedc06))
* **bots/discord:** reset counter when reconnected to api, redo message scan filter logic ([d234d79](d234d79310))
* **bots/discord:** revert dist denesting, fixes config not found ([0d4898d](0d4898dae8))
* **bots/discord:** send right response for after regexes ([a7688fa](a7688fa9b9))
* **bots/discord:** set the `label` property correctly for message scans ([6d463df](6d463df586))
* **bots/discord:** set timeout for eligible mutes to unmute faster ([1f5c5a9](1f5c5a92a6))
* **bots/discord:** some configuration values not applying after running `/reload` ([a976dd2](a976dd2acc))
* **bots/discord:** use `APIEmbed` for response config ([35b9448](35b944800a))
* **bots/discord:** use env for initializing database ([af3759c](af3759caf4))
* **bots/discord:** wrong command file path being imported ([fa0159c](fa0159c3a8))
* **bots/discord:** wrong database schema path ([875bd20](875bd209b2))
* config file not being read ([474a8be](474a8be4af))
* **discord-bot:** also execute slash commands ([f0d45b2](f0d45b2c92))
* **discord-bot:** check for role position ([d332043](d332043b1a))
* **discord-bot:** check if the member has the role ([9bff68c](9bff68c8c4))
* **discord-bot:** not executing slash commands ([aa08087](aa0808768b))
* **discord-bot:** only send lowercased text ([7803758](78037580dc))
* dislike button not working properly ([85eba55](85eba55424))
* fix deprecation ([4373ede](4373ede855))
* fix the fiter for the interaction collector ([a9ff003](a9ff00394a))
* fix typings and formatting ([479812e](479812e199))
* ignore message if there's no content ([3cbebc2](3cbebc2842))
* move modules to `/bots` ([cd7156e](cd7156e792))
* other small issues ([bc437a5](bc437a5ec7))
* run projects with `--bun` ([bb2182e](bb2182e707))
* trainAI not using the bin location ([bd29943](bd2994388b))
* update repo url ([a21aa34](a21aa348d7))

### chore

* fix more build issues ([77fefb9](77fefb9bef))

### Features

* add wit.ai support ([1909e2c](1909e2c421))
* **bots/discord/commands/reply:** send stacktrace when failed ([9f1ac37](9f1ac37927))
* **bots/discord/commands:** add `ban` and `unban` commands ([dc4863d](dc4863dc20))
* **bots/discord/commands:** add `eval` command ([e64d1da](e64d1da00c))
* **bots/discord/commands:** add `mute` and `unmute` commands ([c0fa2fe](c0fa2fe1c3))
* **bots/discord/commands:** add `purge` and `role-preset` commands ([fb01ce5](fb01ce5740))
* **bots/discord/commands:** add `reload` command ([6875b32](6875b32fd0))
* **bots/discord/commands:** allow process exception in `exception-test` ([ca47535](ca475356ad))
* **bots/discord/utils/discord/embeds:** expose `applyCommonEmbedStyles` fn ([2d794ed](2d794ede7d))
* **bots/discord/utils/embeds:** make title parameter nullable ([ee885ca](ee885ca758))
* **bots/discord/utils/fs:** use `recursive` option for listing files ([da21e1a](da21e1a6f7))
* **bots/discord/utils:** add duration utility ([a9add9e](a9add9ea9a))
* **bots/discord/utils:** add functions for role presets ([fb32a04](fb32a04ad3))
* **bots/discord/utils:** allow loading commands from custom dir ([8b690b8](8b690b879b))
* **bots/discord:** add `api.disconnectRetryInterval` config ([2f86586](2f86586179))
* **bots/discord:** add `moderation.roles` config to be used in `moderation` commands ([39d5b3a](39d5b3a479))
* **bots/discord:** add `ocrTriggers` resp config, embed footer scan mode ([744a56a](744a56a4fd))
* **bots/discord:** add `replyToReplied` option in response config ([27662ed](27662ed91a))
* **bots/discord:** add `train` commands ([ee90ef2](ee90ef247b))
* **bots/discord:** add a better way to manage databases ([a68d726](a68d726875))
* **bots/discord:** add code to actually scan text files correctly ([80aeb19](80aeb19020))
* **bots/discord:** add default durations for moderation commands ([27d3b39](27d3b39209))
* **bots/discord:** add more fallbacks for decancering ([2e1e009](2e1e009b42))
* **bots/discord:** add more month aliases to duration parser ([c2009ca](c2009ca6d4))
* **bots/discord:** add more options for curing, fix default regex ([1a4ec1e](1a4ec1ece8))
* **bots/discord:** add source ([f9d50a0](f9d50a0a6b))
* **bots/discord:** add sticky messages ([bf66155](bf661556e1))
* **bots/discord:** add trigger to context for eval ([b5f4097](b5f4097538))
* **bots/discord:** allow admins to bypass permission checks ([620f933](620f9339f0))
* **bots/discord:** blacklist and whitelist for filters ([cdb6001](cdb6001955))
* **bots/discord:** cure on every event ([8ff6086](8ff6086028))
* **bots/discord:** don't nest builds in src directory, autogen db when missing ([4834685](4834685186))
* **bots/discord:** framework changes and new features ([646ec8d](646ec8da87))
* **bots/discord:** improve admin commands ([0346741](0346741188))
* **bots/discord:** improve logs ([6abb740](6abb740994))
* **bots/discord:** sanitize `BasicDatabase` inputs ([fd76e0a](fd76e0af72))
* **bots/discord:** support nickname decancering ([1723e8c](1723e8cacf))
* **bots/discord:** switch to `drizzle-orm` ([e204b7b](e204b7b756))
* **bots/discord:** update config ([197d2ac](197d2acea8))
* **bots/discord:** update example config file ([bc9951c](bc9951c9b5))
* **bots/discord:** update to newer command definition framework ([97f2795](97f2795df4))
* discord bot scanning messages ([d1bd3b2](d1bd3b2b7e))
* **discord-bot:** a way to train AI ([355a508](355a50803a))
* **discord-bot:** command handler and train cmd ([6aee8a4](6aee8a4c63))
* **discord-bot:** event handler ([0ad5ece](0ad5ece085))
* GODEL AI ([0ba525c](0ba525c4a5))
* initalize discord bot ([bb4a5a7](bb4a5a77ee))
* initialize helper client ([7f9ca77](7f9ca77e03))
* message buttons for training ([6551ca9](6551ca9dad))
* platform specific responses ([18e57b0](18e57b0c32))
* prettier and eslint ([1c27ccb](1c27ccb17c))
* refactor and new features ([#7](https://github.com/revanced/revanced-bots/issues/7)) ([8b9f45d](8b9f45dc22))
* run bots in one process ([d26d533](d26d533174))
* training and replies changed ([715aa91](715aa918cf))
* **utils/discord/embeds:** allow adding extra fields for moderation embeds ([49ce9a7](49ce9a7ca3))

### BREAKING CHANGES

* In `@revanced/discord-bot`, its environment variable
                 `DATABASE_URL` has been renamed to `DATABASE_PATH`
                 and the `file:` prefix is no longer needed
2025-04-04 17:24:04 +00:00
semantic-release-bot
5d862401a0 chore(release): 1.0.0 [skip ci]
# @revanced/bot-websocket-api 1.0.0 (2025-04-04)

### Bug Fixes

* **apis/websocket:** also include tesseract core files in build ([7dfbf6c](7dfbf6c92c))
* **apis/websocket:** attempt to fix missing remote address ([9b2888b](9b2888b944))
* **apis/websocket:** build and runtime issues ([89d8ab1](89d8ab1ee5))
* **apis/websocket:** builds not working due to dynamic import requirement ([fc7be22](fc7be22c6c))
* **apis/websocket:** don't bundle `tesseract.js` ([51a6fb6](51a6fb65f0))
* **apis/websocket:** fix forever stuck Promise ([168f40d](168f40def6))
* **apis/websocket:** fix undefined error ([2f03800](2f03800c61))
* **apis/websocket:** hardcoded paths in tesseract worker builds ([38e00eb](38e00eb4e5))
* **apis/websocket:** improve logging and error handling ([b6cbe9d](b6cbe9d64c))
* **bots/discord:** hanging process when disconnecting from API too many times ([d31616e](d31616ebcb))
* fix typings and formatting ([479812e](479812e199))
* other small issues ([bc437a5](bc437a5ec7))
* remove error cb handling for `socket.send()` calls ([29544d4](29544d4e01))
* run projects with `--bun` ([bb2182e](bb2182e707))
* **types:** fix issues with typings ([669e24c](669e24ca81))
* update repo url ([a21aa34](a21aa348d7))

### chore

* fix more build issues ([77fefb9](77fefb9bef))

### Features

* **apis/websocket:** clear old client sessions and instances ([43bd0a0](43bd0a021c))
* **apis/websocket:** return `true` for data on a `TrainedMessage` packet ([65add4d](65add4dfee))
* **packages/shared:** add logger factory ([17c6be7](17c6be7bee))

### BREAKING CHANGES

* In `@revanced/discord-bot`, its environment variable
                 `DATABASE_URL` has been renamed to `DATABASE_PATH`
                 and the `file:` prefix is no longer needed
2025-04-04 17:23:29 +00:00
Palm
91bac934ab chore: merge dev to main 2025-04-05 00:22:22 +07:00
semantic-release-bot
d74fba4092 chore(release): 1.0.0-dev.38 [skip ci]
# @revanced/discord-bot [1.0.0-dev.38](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.0-dev.37...@revanced/discord-bot@1.0.0-dev.38) (2025-04-04)

### Bug Fixes

* run projects with `--bun` ([bb2182e](bb2182e707))
2025-04-04 16:56:56 +00:00
semantic-release-bot
ce76c5f08e chore(release): 1.0.0-dev.11 [skip ci]
# @revanced/bot-websocket-api [1.0.0-dev.11](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0-dev.10...@revanced/bot-websocket-api@1.0.0-dev.11) (2025-04-04)

### Bug Fixes

* **apis/websocket:** attempt to fix missing remote address ([9b2888b](9b2888b944))
* run projects with `--bun` ([bb2182e](bb2182e707))
2025-04-04 16:56:18 +00:00
PalmDevs
bb2182e707 fix: run projects with --bun 2025-04-04 23:55:06 +07:00
PalmDevs
9b2888b944 fix(apis/websocket): attempt to fix missing remote address 2025-04-04 23:55:04 +07:00
PalmDevs
ba1a467e20 chore: update deps 2025-04-04 23:55:02 +07:00
semantic-release-bot
c97fffb32f chore(release): 1.0.0-dev.37 [skip ci]
# @revanced/discord-bot [1.0.0-dev.37](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.0-dev.36...@revanced/discord-bot@1.0.0-dev.37) (2025-03-08)

### Bug Fixes

* **bots/discord/utils/duration:** fix specified default unit not working ([d138af4](d138af46d1))
2025-03-08 23:34:30 +00:00
PalmDevs
d138af46d1 fix(bots/discord/utils/duration): fix specified default unit not working 2025-03-09 06:33:21 +07:00
semantic-release-bot
75a57b0e16 chore(release): 1.0.0-dev.36 [skip ci]
# @revanced/discord-bot [1.0.0-dev.36](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.0-dev.35...@revanced/discord-bot@1.0.0-dev.36) (2025-03-03)

### Bug Fixes

* **bots/discord/scripts/build:** check if dist dir exists before cleaning ([c06033e](c06033e573))
* **bots/discord/utils/duration:** make second the default unit ([5d1af3c](5d1af3c31c))
* **bots/discord:** add GuildMember partial ([8e3946a](8e3946a666))
* **bots/discord:** decrease length of an option in `ban` command ([22d3eea](22d3eea88d))
* **bots/discord:** delete expired appliedPresets entries after unapplying ([14c98e8](14c98e87df))
* fix typings and formatting ([479812e](479812e199))
* update repo url ([a21aa34](a21aa348d7))

### Features

* **bots/discord:** add more month aliases to duration parser ([c2009ca](c2009ca6d4))
2025-03-03 19:40:39 +00:00
PalmDevs
c06033e573 fix(bots/discord/scripts/build): check if dist dir exists before cleaning 2025-03-04 02:39:08 +07:00
semantic-release-bot
d290417ff3 chore(release): 1.0.0-dev.10 [skip ci]
# @revanced/bot-websocket-api [1.0.0-dev.10](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0-dev.9...@revanced/bot-websocket-api@1.0.0-dev.10) (2025-03-03)

### Bug Fixes

* fix typings and formatting ([479812e](479812e199))
* update repo url ([a21aa34](a21aa348d7))
2025-03-03 19:36:29 +00:00
PalmDevs
a21aa348d7 fix: update repo url 2025-03-04 02:35:19 +07:00
PalmDevs
479812e199 fix: fix typings and formatting 2025-03-04 02:27:48 +07:00
PalmDevs
f6119946f8 chore: update deps 2025-03-04 02:19:06 +07:00
PalmDevs
5d1af3c31c fix(bots/discord/utils/duration): make second the default unit 2025-03-04 02:17:59 +07:00
PalmDevs
14c98e87df fix(bots/discord): delete expired appliedPresets entries after unapplying 2025-03-04 02:15:19 +07:00
PalmDevs
8e3946a666 fix(bots/discord): add GuildMember partial 2025-03-04 02:09:59 +07:00
PalmDevs
c2009ca6d4 feat(bots/discord): add more month aliases to duration parser 2024-10-30 18:56:54 +07:00
PalmDevs
22d3eea88d fix(bots/discord): decrease length of an option in ban command 2024-10-30 18:50:22 +07:00
77 changed files with 3417 additions and 564 deletions

View File

@@ -12,23 +12,20 @@ jobs:
name: Release
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
contents: write # to be able to publish a GitHub release
issues: write # to be able to comment on released issues
pull-requests: write # to be able to comment on released pull requests
packages: write # to be able to publish GitHub packages
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Make sure the release step uses its own credentials:
# https://github.com/cycjimmy/semantic-release-action#private-packages
persist-credentials: false
fetch-depth: 0
- name: Cache dependencies
uses: actions/cache@v4
with:
path: '**/node_modules'
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
restore-keys: ${{ runner.os }}-bun-
- name: Setup Bun
@@ -48,22 +45,11 @@ jobs:
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and release
- name: Build, release, publish
env:
RELEASE_WORKFLOW_STEP: release
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCKER_REGISTRY_USER: ${{ github.repository_owner }}
DOCKER_REGISTRY_PASSWORD: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
DEBUG: semantic-release:*
run: bunx multi-semantic-release --debug
# We call multi-semantic-release twice to publish in a different step
# An environment variable determines which plugins in the config to run
- name: Trigger Portainer webhooks
if: github.ref == 'refs/heads/main'
env:
RELEASE_WORKFLOW_STEP: publish
GITHUB_TOKEN: ${{ secrets.REPOSITORY_PUSH_ACCESS }}
DOCKER_REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
WEBSOCKET_API_PORTAINER_WEBHOOK_URL: ${{ secrets.WEBSOCKET_API_PORTAINER_WEBHOOK_URL }}
DISCORD_BOT_PORTAINER_WEBHOOK_URL: ${{ secrets.DISCORD_BOT_PORTAINER_WEBHOOK_URL }}
run: bunx multi-semantic-release

View File

@@ -58,7 +58,7 @@
Continuing the legacy of Vanced
</p>
# 🤖 ReVanced Helper
# 🤖 ReVanced Bots
Bots assisting ReVanced on multiple platforms.
@@ -67,4 +67,4 @@ Bots assisting ReVanced on multiple platforms.
Thank you for considering! This project is made in TypeScript and we use the [Bun](https://bun.sh) JavaScript runtime.
Please refer to the [documentation](./docs) for more information.
You can find projects under the [`apis`](./apis/), [`bots`](./bots), and [`packages`](./packages/) directories.
You can find projects under the [`apis`](./apis/), [`bots`](./bots), and [`packages`](./packages/) directories.

View File

@@ -1,32 +1,28 @@
import defineSubprojectReleaseConfig from '../../semantic-release-config.js'
export default defineSubprojectReleaseConfig({
plugins:
process.env.RELEASE_WORKFLOW_STEP === 'publish'
? [
[
'@semantic-release/exec',
{
publishCmd: 'bun run scripts/trigger-portainer-webhook.ts',
},
],
]
: [
[
'@codedependant/semantic-release-docker',
{
dockerImage: 'revanced-bot-websocket-api',
dockerRegistry: 'ghcr.io',
dockerProject: 'revanced',
dockerContext: '../..',
dockerPlatform: ['linux/amd64', 'linux/arm64'],
dockerBuildQuiet: false,
dockerTags: [
'{{#if prerelease.[0]}}dev{{else}}main{{/if}}',
'{{#unless prerelease.[0]}}latest{{/unless}}',
'{{version}}',
],
},
],
],
plugins: [
[
'@codedependant/semantic-release-docker',
{
dockerImage: 'revanced-bot-websocket-api',
dockerRegistry: 'ghcr.io',
dockerProject: 'revanced',
dockerContext: '../..',
dockerPlatform: ['linux/amd64', 'linux/arm64'],
dockerBuildQuiet: false,
dockerTags: [
'{{#if prerelease.[0]}}dev{{else}}main{{/if}}',
'{{#unless prerelease.[0]}}latest{{/unless}}',
'{{version}}',
],
},
],
[
'@semantic-release/exec',
{
successCmd: 'bun run scripts/trigger-portainer-webhook.ts',
},
],
],
})

View File

@@ -1,3 +1,83 @@
# @revanced/bot-websocket-api [1.1.0](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.2...@revanced/bot-websocket-api@1.1.0) (2025-07-11)
### Features
* **apis/websocket:** support training without label ([670cc70](https://github.com/revanced/revanced-bots/commit/670cc700570b32924738c3f24acb9f5312d2dcdb))
## @revanced/bot-websocket-api [1.0.2](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.1...@revanced/bot-websocket-api@1.0.2) (2025-06-23)
## @revanced/bot-websocket-api [1.0.1](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0...@revanced/bot-websocket-api@1.0.1) (2025-04-09)
### Bug Fixes
* **bots/discord:** attempt to fix sticky messages again ([7564b1a](https://github.com/revanced/revanced-bots/commit/7564b1a8f066183df390887ddfd4d73e0baa87ac))
## @revanced/bot-websocket-api [1.0.1](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0...@revanced/bot-websocket-api@1.0.1) (2025-04-09)
### Bug Fixes
* **bots/discord:** attempt to fix sticky messages again ([7564b1a](https://github.com/revanced/revanced-bots/commit/7564b1a8f066183df390887ddfd4d73e0baa87ac))
# @revanced/bot-websocket-api 1.0.0 (2025-04-04)
### Bug Fixes
* **apis/websocket:** also include tesseract core files in build ([7dfbf6c](https://github.com/revanced/revanced-bots/commit/7dfbf6c92c49100954fa4aca471dce4ab9fd9565))
* **apis/websocket:** attempt to fix missing remote address ([9b2888b](https://github.com/revanced/revanced-bots/commit/9b2888b944ea1d61d31aa5df3536768e9a2dadf8))
* **apis/websocket:** build and runtime issues ([89d8ab1](https://github.com/revanced/revanced-bots/commit/89d8ab1ee58278a9a96cdc31c679d0a0a0d865af))
* **apis/websocket:** builds not working due to dynamic import requirement ([fc7be22](https://github.com/revanced/revanced-bots/commit/fc7be22c6c15974c7394790e93de2a23a6627153))
* **apis/websocket:** don't bundle `tesseract.js` ([51a6fb6](https://github.com/revanced/revanced-bots/commit/51a6fb65f0df3409eacffb297430840a0e326989))
* **apis/websocket:** fix forever stuck Promise ([168f40d](https://github.com/revanced/revanced-bots/commit/168f40def64ca213cd2b549f4bafed4c0e1e3695))
* **apis/websocket:** fix undefined error ([2f03800](https://github.com/revanced/revanced-bots/commit/2f03800c61c00e59e512567d273a195e605d6736))
* **apis/websocket:** hardcoded paths in tesseract worker builds ([38e00eb](https://github.com/revanced/revanced-bots/commit/38e00eb4e59c763bd74d27b9b9b482ea66e4dcf4))
* **apis/websocket:** improve logging and error handling ([b6cbe9d](https://github.com/revanced/revanced-bots/commit/b6cbe9d64c01ff11feab8351fb801bc1aee48325))
* **bots/discord:** hanging process when disconnecting from API too many times ([d31616e](https://github.com/revanced/revanced-bots/commit/d31616ebcba6f1dcd8bde183bcb8d1adb1501b61))
* fix typings and formatting ([479812e](https://github.com/revanced/revanced-bots/commit/479812e199b52cdb295a5746e0767306afab3413))
* other small issues ([bc437a5](https://github.com/revanced/revanced-bots/commit/bc437a5ec7ce1d339094d608e2a61ac5f460c163))
* remove error cb handling for `socket.send()` calls ([29544d4](https://github.com/revanced/revanced-bots/commit/29544d4e0127173465796b7e3c62161f4db59c8b))
* run projects with `--bun` ([bb2182e](https://github.com/revanced/revanced-bots/commit/bb2182e707fa40c555d56138972eeea28f1b3cf9))
* **types:** fix issues with typings ([669e24c](https://github.com/revanced/revanced-bots/commit/669e24ca8103ea051b4e61160dd0f978e36707ea))
* update repo url ([a21aa34](https://github.com/revanced/revanced-bots/commit/a21aa348d7f32cd0ee65b371e9594520c0a9d3f1))
### chore
* fix more build issues ([77fefb9](https://github.com/revanced/revanced-bots/commit/77fefb9bef286a22f40a4d76b79c64fcc5a2467f))
### Features
* **apis/websocket:** clear old client sessions and instances ([43bd0a0](https://github.com/revanced/revanced-bots/commit/43bd0a021cd885a3d74a1f307ec2935e81d17458))
* **apis/websocket:** return `true` for data on a `TrainedMessage` packet ([65add4d](https://github.com/revanced/revanced-bots/commit/65add4dfeed2fa067c2c8e2377f7d01d505ade54))
* **packages/shared:** add logger factory ([17c6be7](https://github.com/revanced/revanced-bots/commit/17c6be7bee5b5c24fd4a5279e73374b0bb7a6229))
### BREAKING CHANGES
* In `@revanced/discord-bot`, its environment variable
`DATABASE_URL` has been renamed to `DATABASE_PATH`
and the `file:` prefix is no longer needed
# @revanced/bot-websocket-api [1.0.0-dev.11](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0-dev.10...@revanced/bot-websocket-api@1.0.0-dev.11) (2025-04-04)
### Bug Fixes
* **apis/websocket:** attempt to fix missing remote address ([9b2888b](https://github.com/revanced/revanced-bots/commit/9b2888b944ea1d61d31aa5df3536768e9a2dadf8))
* run projects with `--bun` ([bb2182e](https://github.com/revanced/revanced-bots/commit/bb2182e707fa40c555d56138972eeea28f1b3cf9))
# @revanced/bot-websocket-api [1.0.0-dev.10](https://github.com/revanced/revanced-bots/compare/@revanced/bot-websocket-api@1.0.0-dev.9...@revanced/bot-websocket-api@1.0.0-dev.10) (2025-03-03)
### Bug Fixes
* fix typings and formatting ([479812e](https://github.com/revanced/revanced-bots/commit/479812e199b52cdb295a5746e0767306afab3413))
* update repo url ([a21aa34](https://github.com/revanced/revanced-bots/commit/a21aa348d7f32cd0ee65b371e9594520c0a9d3f1))
# @revanced/bot-websocket-api [1.0.0-dev.9](https://github.com/revanced/revanced-helper/compare/@revanced/bot-websocket-api@1.0.0-dev.8...@revanced/bot-websocket-api@1.0.0-dev.9) (2024-08-03)

View File

@@ -14,4 +14,4 @@ WORKDIR /app
COPY --from=build /build/apis/websocket/dist /app
USER 1000:1000
ENTRYPOINT [ "bun", "run", "index.js" ]
ENTRYPOINT [ "bun", "--bun", "run", "index.js" ]

View File

@@ -2,7 +2,7 @@
"name": "@revanced/bot-websocket-api",
"type": "module",
"private": true,
"version": "1.0.0-dev.9",
"version": "1.1.0",
"description": "🧦 WebSocket API server for bots assisting ReVanced",
"main": "dist/index.js",
"scripts": {
@@ -13,7 +13,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/revanced/revanced-helper.git",
"url": "git+https://github.com/revanced/revanced-bots.git",
"directory": "apis/websocket"
},
"author": "Palm <contact@palmdevs.me> (https://palmdevs.me)",
@@ -23,18 +23,18 @@
],
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/revanced/revanced-helper/issues"
"url": "https://github.com/revanced/revanced-bots/issues"
},
"homepage": "https://github.com/revanced/revanced-helper#readme",
"homepage": "https://github.com/revanced/revanced-bots#readme",
"dependencies": {
"@revanced/bot-shared": "workspace:*",
"@sapphire/async-queue": "^1.5.3",
"chalk": "^5.3.0",
"tesseract.js": "^5.1.1",
"ws": "^8.18.0"
"@sapphire/async-queue": "^1.5.5",
"chalk": "^5.6.2",
"tesseract.js": "^6.0.1",
"ws": "^8.18.3"
},
"devDependencies": {
"@types/ws": "^8.5.12",
"@types/ws": "^8.18.1",
"typed-emitter": "^2.1.0"
}
}

View File

@@ -1,10 +1,10 @@
import { createLogger } from '@revanced/bot-shared'
import { cp, rm } from 'fs/promises'
import { cp, exists, rm } from 'fs/promises'
const logger = createLogger()
logger.info('Cleaning previous build...')
await rm('./dist', { recursive: true })
if (await exists('./dist')) await rm('./dist', { recursive: true })
logger.info('Building WebSocket API...')
await Bun.build({

View File

@@ -1,15 +1,14 @@
import { EventEmitter } from 'events'
import {
ClientOperation,
DisconnectReason,
type Packet,
ServerOperation,
deserializePacket,
isClientPacket,
type Packet,
ServerOperation,
serializePacket,
uncapitalize,
} from '@revanced/bot-shared'
import { EventEmitter } from 'events'
import type TypedEmitter from 'typed-emitter'
import type { RawData, WebSocket } from 'ws'
@@ -100,7 +99,7 @@ export default class Client {
// @ts-expect-error TypeScript doesn't know that the above line will negate the type enough
packet,
)
} catch (e) {
} catch (_e) {
// TODO: add error fields to sent packet so we can log what went wrong
this.disconnect(DisconnectReason.InvalidPacket)
}
@@ -110,7 +109,7 @@ export default class Client {
protected _toBuffer(data: RawData) {
if (data instanceof Buffer) return data
if (data instanceof ArrayBuffer) return Buffer.from(data)
return Buffer.concat(data)
return Buffer.concat(data as Uint8Array[])
}
}

View File

@@ -1,8 +1,7 @@
import { OEM, createWorker as createTesseractWorker } from 'tesseract.js'
import { join as joinPath } from 'path'
import { createLogger } from '@revanced/bot-shared'
import { exists as pathExists } from 'fs/promises'
import { join as joinPath } from 'path'
import { createWorker as createTesseractWorker, OEM } from 'tesseract.js'
import { getConfig } from './utils/config'
export const config = getConfig()
@@ -24,12 +23,12 @@ export const wit = {
if (!res.ok) throw new Error(`Failed to fetch from Wit.ai: ${res.statusText} (${res.status})`)
return await res.json()
return (await res.json()) as WitMessageResponse
},
message(text: string) {
return this.fetch(`/message?q=${encodeURIComponent(text)}&n=8`) as Promise<WitMessageResponse>
},
async train(text: string, label: string) {
async train(text: string, label?: string) {
await this.fetch('/utterances', {
body: JSON.stringify([
{
@@ -42,7 +41,14 @@ export const wit = {
method: 'POST',
})
},
} as const
} satisfies Wit
export interface Wit {
token: string
fetch(route: string, options?: RequestInit): Promise<WitMessageResponse>
message(text: string): Promise<WitMessageResponse>
train(text: string, label?: string): Promise<void>
}
export interface WitMessageResponse {
text: string

View File

@@ -1,12 +1,11 @@
import type { ClientOperation } from '@revanced/bot-shared'
import type { Logger } from '@revanced/bot-shared'
import type { ClientOperation, Logger } from '@revanced/bot-shared'
import type { Worker as TesseractWorker } from 'tesseract.js'
import type { ClientPacketObject } from '../classes/Client'
import type { WitMessageResponse } from '../context'
import type { Wit } from '../context'
import type { Config } from '../utils/config'
export { default as parseTextEventHandler } from './parseText'
export { default as parseImageEventHandler } from './parseImage'
export { default as parseTextEventHandler } from './parseText'
export { default as trainMessageEventHandler } from './trainMessage'
export type EventHandler<POp extends ClientOperation> = (
@@ -15,10 +14,7 @@ export type EventHandler<POp extends ClientOperation> = (
) => void | Promise<void>
export type EventContext = {
wit: {
train(text: string, label: string): Promise<void>
message(text: string): Promise<WitMessageResponse>
}
wit: Wit
tesseract: TesseractWorker
logger: Logger
config: Config

View File

@@ -1,6 +1,5 @@
import { type ClientOperation, ServerOperation } from '@revanced/bot-shared'
import { AsyncQueue } from '@sapphire/async-queue'
import type { EventHandler } from '.'
const queue = new AsyncQueue()

View File

@@ -1,5 +1,4 @@
import { type ClientOperation, ServerOperation } from '@revanced/bot-shared'
import type { EventHandler } from '.'
const parseTextEventHandler: EventHandler<ClientOperation.ParseText> = async (packet, { wit, logger }) => {

View File

@@ -1,5 +1,4 @@
import { type ClientOperation, ServerOperation } from '@revanced/bot-shared'
import type { EventHandler } from '.'
const trainMessageEventHandler: EventHandler<ClientOperation.TrainMessage> = async (packet, { wit, logger }) => {
@@ -11,7 +10,7 @@ const trainMessageEventHandler: EventHandler<ClientOperation.TrainMessage> = asy
const nextSeq = client.currentSequence++
const actualText = text.slice(0, 279)
logger.debug(`${client.id} requested to train label ${label} (${nextSeq}) with:`, actualText)
logger.debug(`${client.id} requested to train label ${label ?? '<out of scope>'} (${nextSeq}) with:`, actualText)
try {
await wit.train(actualText, label)

View File

@@ -1,14 +1,10 @@
import { inspect as inspectObject } from 'util'
import Client from './classes/Client'
import { type EventContext, parseImageEventHandler, parseTextEventHandler, trainMessageEventHandler } from './events'
import { DisconnectReason, HumanizedDisconnectReason } from '@revanced/bot-shared'
import { createServer } from 'http'
import { inspect as inspectObject } from 'util'
import { type WebSocket, WebSocketServer } from 'ws'
import Client from './classes/Client'
import { config, logger, tesseract, wit } from './context'
import { type EventContext, parseImageEventHandler, parseTextEventHandler, trainMessageEventHandler } from './events'
// Load config, init logger, check environment
@@ -56,12 +52,13 @@ const wss = new WebSocketServer({
wss.on('connection', async (socket, request) => {
try {
if (!request.socket.remoteAddress) {
const addrInfo = request.socket.address()
if (!('address' in addrInfo)) {
socket.close()
return logger.warn('Connection failed because client is missing remote address')
return logger.warn('Connection failed because client is missing remote address. addrInfo =', addrInfo)
}
const id = `${request.socket.remoteAddress}:${request.socket.remotePort}`
const id = `${addrInfo.address}:${addrInfo.port}`
if (clientIds.has(id)) {
logger.warn(`Client ${id} already connected, disconnecting old session`)

View File

@@ -1,6 +1,20 @@
{
"$schema": "https://biomejs.dev/schemas/1.8.2/schema.json",
"organizeImports": {
"$schema": "https://biomejs.dev/schemas/2.0.5/schema.json",
"assist": {
"actions": {
"source": {
"organizeImports": {
"level": "on",
"options": {
"groups": [
{
"type": false
}
]
}
}
}
},
"enabled": true
},
"linter": {
@@ -24,7 +38,15 @@
},
"useNumberNamespace": {
"level": "off"
}
},
"noParameterAssign": "error",
"useAsConstAssertion": "error",
"useDefaultParameterLast": "error",
"useSelfClosingElements": "error",
"useSingleVarDeclarator": "error",
"noUnusedTemplateLiteral": "error",
"noInferrableTypes": "error",
"noUselessElse": "error"
}
}
},
@@ -54,7 +76,12 @@
},
"files": {
"ignoreUnknown": true,
"include": ["*.js", "*.json", "*.ts"],
"ignore": ["dist/**/*", "node_modules/**/*"]
"includes": [
"**/*.js",
"**/*.json",
"**/*.ts",
"!**/dist/**/*",
"!**/node_modules/**/*"
]
}
}

View File

@@ -1,32 +1,28 @@
import defineSubprojectReleaseConfig from '../../semantic-release-config.js'
export default defineSubprojectReleaseConfig({
plugins:
process.env.RELEASE_WORKFLOW_STEP === 'publish'
? [
[
'@semantic-release/exec',
{
publishCmd: 'bun run scripts/trigger-portainer-webhook.ts',
},
],
]
: [
[
'@codedependant/semantic-release-docker',
{
dockerImage: 'revanced-bot-discord',
dockerRegistry: 'ghcr.io',
dockerProject: 'revanced',
dockerContext: '../..',
dockerPlatform: ['linux/amd64', 'linux/arm64'],
dockerBuildQuiet: false,
dockerTags: [
'{{#if prerelease.[0]}}dev{{else}}main{{/if}}',
'{{#unless prerelease.[0]}}latest{{/unless}}',
'{{version}}',
],
},
],
],
plugins: [
[
'@codedependant/semantic-release-docker',
{
dockerImage: 'revanced-bot-discord',
dockerRegistry: 'ghcr.io',
dockerProject: 'revanced',
dockerContext: '../..',
dockerPlatform: ['linux/amd64', 'linux/arm64'],
dockerBuildQuiet: false,
dockerTags: [
'{{#if prerelease.[0]}}dev{{else}}main{{/if}}',
'{{#unless prerelease.[0]}}latest{{/unless}}',
'{{version}}',
],
},
],
[
'@semantic-release/exec',
{
successCmd: 'bun run scripts/trigger-portainer-webhook.ts',
},
],
],
})

View File

@@ -1,3 +1,301 @@
## @revanced/discord-bot [1.4.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.4.0...@revanced/discord-bot@1.4.1) (2025-09-13)
### Bug Fixes
* **bots/discord/database:** use non-deprecated way to create indexes ([8cc2377](https://github.com/revanced/revanced-bots/commit/8cc2377cbfcc74c2c3228ed18da2495b4efd45aa))
* **bots/discord:** allow partial users ([dd3e7d2](https://github.com/revanced/revanced-bots/commit/dd3e7d2ee0cdf40e083af7f6db35ac3508ddf763))
* **bots/discord:** error in cache `keepOverLimit` comparison ([567c5d2](https://github.com/revanced/revanced-bots/commit/567c5d2c7f41dae7d7fec9946a7dd2ac1b10cc2a))
# @revanced/discord-bot [1.4.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.3.0...@revanced/discord-bot@1.4.0) (2025-07-11)
### Bug Fixes
* **bots/discord:** pass non-empty out of scope label to discord ([fbd9480](https://github.com/revanced/revanced-bots/commit/fbd948003631b48a1914eb7b2551ead4b05089b7))
### Features
* **bots/discord:** react to label classified response ([96a9b83](https://github.com/revanced/revanced-bots/commit/96a9b83c486fdc6e78f4c59e197fa6c1dab09161))
# @revanced/discord-bot [1.3.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.3...@revanced/discord-bot@1.3.0) (2025-07-11)
### Features
* **bots/discord:** support training without label ([c68cfd1](https://github.com/revanced/revanced-bots/commit/c68cfd1c01703fad17f233ad4e13cf81913afbb6))
## @revanced/discord-bot [1.2.3](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.2...@revanced/discord-bot@1.2.3) (2025-06-23)
### Bug Fixes
* **bots/discord:** disable unneeded cache, enable message cache sweeping ([3a0f0fe](https://github.com/revanced/revanced-bots/commit/3a0f0fe7861d73a4d81ecaba0e12bd60c06f8eb8))
## @revanced/discord-bot [1.2.2](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.1...@revanced/discord-bot@1.2.2) (2025-06-10)
### Bug Fixes
* **bots/discord:** use intervals for checking expired presets ([6e89b87](https://github.com/revanced/revanced-bots/commit/6e89b874cdfee8a1c215559271c741f43ba578ce))
## @revanced/discord-bot [1.2.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.2.0...@revanced/discord-bot@1.2.1) (2025-05-02)
### Bug Fixes
* **bots/discord:** fix timeout overflow check for role presets ([495f686](https://github.com/revanced/revanced-bots/commit/495f686292ebdcf51902c1dc75ac1510d7fdbd9c))
# @revanced/discord-bot [1.2.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.1.2...@revanced/discord-bot@1.2.0) (2025-05-02)
### Features
* **bots/discord:** switch duration parser to `@sapphire/duration` ([04ce825](https://github.com/revanced/revanced-bots/commit/04ce8252c05a23dbb4a91fded4f1a3d63b5c8a64))
## @revanced/discord-bot [1.1.2](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.1.1...@revanced/discord-bot@1.1.2) (2025-04-16)
### Bug Fixes
* **bots/discord/commands/admin/reload:** fix type error ([3908854](https://github.com/revanced/revanced-bots/commit/3908854fe090dda67b0d90225ab76f75e95db4c0))
* **bots/discord/commands/moderation:** check if timeout amount is safe in role-preset commands ([0c1382c](https://github.com/revanced/revanced-bots/commit/0c1382c55856ed1e54c9e53dbb37e9297c5da37c))
* **bots/discord:** replace use of deprecated `options.ephemeral` in replies ([31e5cf7](https://github.com/revanced/revanced-bots/commit/31e5cf7fc5c7cd0c6ca3b1f3b9410a88b95d8273))
## @revanced/discord-bot [1.1.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.1.0...@revanced/discord-bot@1.1.1) (2025-04-14)
### Bug Fixes
* **bots/discord:** fix sticky msg force timer always starting, add more logging ([cb4dc42](https://github.com/revanced/revanced-bots/commit/cb4dc42dfab8cf9821b03316cf56b405abd497ae))
# @revanced/discord-bot [1.1.0](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.4...@revanced/discord-bot@1.1.0) (2025-04-14)
### Features
* **bots/discord:** delete and send sticky msg concurrently, add more logging ([247a00f](https://github.com/revanced/revanced-bots/commit/247a00f57fc2a45fe828cc41e6f0e38e67e83a20))
## @revanced/discord-bot [1.0.4](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.3...@revanced/discord-bot@1.0.4) (2025-04-14)
### Bug Fixes
* **bots/discord:** fix sticky messages logic again ([aa7501c](https://github.com/revanced/revanced-bots/commit/aa7501c3097a790265e4ea624d07c4a9c3c1b908))
* **bots/discord:** remove expired presets from db if unapplying is not possible ([9d705e5](https://github.com/revanced/revanced-bots/commit/9d705e580c05d8b25df6f845d3aac747adaca116))
## @revanced/discord-bot [1.0.3](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.2...@revanced/discord-bot@1.0.3) (2025-04-09)
### Bug Fixes
* **bots/discord:** attempt to fix sticky messages one last time ([65288ec](https://github.com/revanced/revanced-bots/commit/65288ec4242b32d0b5e213b3d7af602bb9a829ca))
## @revanced/discord-bot [1.0.2](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.1...@revanced/discord-bot@1.0.2) (2025-04-09)
## @revanced/discord-bot [1.0.1](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.0...@revanced/discord-bot@1.0.1) (2025-04-09)
### Bug Fixes
* **bots/discord:** attempt to fix sticky messages again ([7564b1a](https://github.com/revanced/revanced-bots/commit/7564b1a8f066183df390887ddfd4d73e0baa87ac))
# @revanced/discord-bot 1.0.0 (2025-04-04)
### Bug Fixes
* **bot/discord:** start remove preset timeout for `role-preset` command ([cbf9116](https://github.com/revanced/revanced-bots/commit/cbf91162e27dd4c1ecb976927ab708f1d882abca))
* **bots/discord/commands/eval:** evaluate in current context ([5925d90](https://github.com/revanced/revanced-bots/commit/5925d902095acef5f6396ca03583a9cbb0862498))
* **bots/discord/commands/mute:** use existing `parseDuration` util function' ([3e07429](https://github.com/revanced/revanced-bots/commit/3e07429664f7dbb6ce653083e0adb1a232737fde))
* **bots/discord/commands/unmute:** fix option description and embeds ([7fdf8c0](https://github.com/revanced/revanced-bots/commit/7fdf8c0dc722e21fe5a3ad6ef8d3a306ef85f532))
* **bots/discord/commands:** minor issues ([3b2596e](https://github.com/revanced/revanced-bots/commit/3b2596e748cf2cde1500ef2ded55f0faabc2c272))
* **bots/discord/commands:** refactor and add checks ([a2bf3ea](https://github.com/revanced/revanced-bots/commit/a2bf3eade99b46f9ffb55d45b8caf1bcf3d22a9b))
* **bots/discord/database:** fix schema for role presets ([4aa138a](https://github.com/revanced/revanced-bots/commit/4aa138a9af8db7093ef637470fcfdea1f5341236))
* **bots/discord/scripts/build:** check if dist dir exists before cleaning ([c06033e](https://github.com/revanced/revanced-bots/commit/c06033e5730f82438e8052b9b519a8f8e2d25437))
* **bots/discord/scripts:** unintentional escaping on windows ([09dc706](https://github.com/revanced/revanced-bots/commit/09dc70632da0597fdb26677acee3f6fccbb2b9b5))
* **bots/discord/utils/discord/embeds:** set thumbnail on moderation embeds ([b056291](https://github.com/revanced/revanced-bots/commit/b056291ad0f2e2eac5eec8aa71f15dbc769aa0f9))
* **bots/discord/utils/discord/rolePresets:** correct property access for presets ([4c6ad11](https://github.com/revanced/revanced-bots/commit/4c6ad11be30c1d6af97c4ae40fc62d05fa7bdd57))
* **bots/discord/utils/discord:** add `moderation` module related functions ([7e8270f](https://github.com/revanced/revanced-bots/commit/7e8270f7d260322e1950e058b221ab088bd595d0))
* **bots/discord/utils/duration:** fix empty string returning with duration is 0 ([83c314e](https://github.com/revanced/revanced-bots/commit/83c314ef5f721abc355272db0e4c182dcfe5d943))
* **bots/discord/utils/duration:** fix specified default unit not working ([d138af4](https://github.com/revanced/revanced-bots/commit/d138af46d1f25a11b6f8ab3790ecaa70b1d716a9))
* **bots/discord/utils/duration:** make second the default unit ([5d1af3c](https://github.com/revanced/revanced-bots/commit/5d1af3c31c3379b6a13684dfb07583737908c8aa))
* **bots/discord:** add GuildMember partial ([8e3946a](https://github.com/revanced/revanced-bots/commit/8e3946a66602838715787090008c7bfaf72b67e9))
* **bots/discord:** allow access to `context` in `/eval` and await result ([99f65f0](https://github.com/revanced/revanced-bots/commit/99f65f07f5f8830c6e8ea4ae171e986af4d3f1f6))
* **bots/discord:** always true check causing no messages to be scanned ([98ec37b](https://github.com/revanced/revanced-bots/commit/98ec37b5d18cade85270ab83b0ed0abe41244dd9))
* **bots/discord:** apply active role presets if members rejoin ([f50b26b](https://github.com/revanced/revanced-bots/commit/f50b26b82d66c88fd1dbb8c07d77c177c0e781df))
* **bots/discord:** attempt to fix stuck sticky message timeouts ([3ed5bd1](https://github.com/revanced/revanced-bots/commit/3ed5bd11acc3b4fbd57b0d632c68eb9f77365b8a))
* **bots/discord:** await when putting entries into db ([4da6175](https://github.com/revanced/revanced-bots/commit/4da6175cf58b1fa6144bdc71ec806766d32c1025))
* **bots/discord:** broken regex when prefix set to special characters ([ab62e55](https://github.com/revanced/revanced-bots/commit/ab62e55e76005f5999d7413d1158e54053f28d1f))
* **bots/discord:** check token before connecting to bot api ([f3e4408](https://github.com/revanced/revanced-bots/commit/f3e4408aa28fb6a9d21365af8c1bea3d07b481de))
* **bots/discord:** ci issues causing database to not be auto generated ([673aa18](https://github.com/revanced/revanced-bots/commit/673aa189bef1009a3e32ba3b1291a5ee84f2def3))
* **bots/discord:** clear role presets after they expire ([faa81f4](https://github.com/revanced/revanced-bots/commit/faa81f4d887eaeae809651f5b68187d033a260f2))
* **bots/discord:** connect to discord API even if initial bot API connection fails ([6658b58](https://github.com/revanced/revanced-bots/commit/6658b582dbeba7e072a7a04c4efa255e7f634aef))
* **bots/discord:** contextify object before sandboxing ([062735f](https://github.com/revanced/revanced-bots/commit/062735f6d552890404d6192244c51a11b0709580))
* **bots/discord:** correct permission check logic ([dd8872c](https://github.com/revanced/revanced-bots/commit/dd8872c027c7e7e1a00f38d659b4d6e79274238c))
* **bots/discord:** correct sticky messages logic ([de8bef6](https://github.com/revanced/revanced-bots/commit/de8bef6520d53a1299f0478458320a7eb75c5e1d))
* **bots/discord:** correct timer active condition for sticky messages ([96065ff](https://github.com/revanced/revanced-bots/commit/96065ff17584ff99a56ca5008327863ca5a7852b))
* **bots/discord:** correct whitelist logic ([49c29be](https://github.com/revanced/revanced-bots/commit/49c29bebfbe348ae4e2cc1b3a83bfa41eb26ccd1))
* **bots/discord:** cross-device link build errors ([38c0699](https://github.com/revanced/revanced-bots/commit/38c06997b4d0f7bb3f1e62618a5e3f088c522e30))
* **bots/discord:** decrease length of an option in `ban` command ([22d3eea](https://github.com/revanced/revanced-bots/commit/22d3eea88d532792c1237d1a1ab18bc02e57816a))
* **bots/discord:** delete expired appliedPresets entries after unapplying ([14c98e8](https://github.com/revanced/revanced-bots/commit/14c98e87df1ec4fd762bbc48ca4c06470cb110a2))
* **bots/discord:** deployment runtime errors due to minification ([a60c60c](https://github.com/revanced/revanced-bots/commit/a60c60c0f994a4c256b7d0582e99a1731209cf49))
* **bots/discord:** do decancer after resetting nickname ([0303fe3](https://github.com/revanced/revanced-bots/commit/0303fe3e367c07e92f831365d5548ca5b03435b2))
* **bots/discord:** do not remove unrelated reactions ([031fd26](https://github.com/revanced/revanced-bots/commit/031fd26b2619ecafeff3964e50accacb87de6108))
* **bots/discord:** don't refresh timer if force timer is active for sticky messages ([4abac0c](https://github.com/revanced/revanced-bots/commit/4abac0c890c0548e14cb56723cae919353a8e726))
* **bots/discord:** filter out text triggers correctly from image-only scans ([8c0dd67](https://github.com/revanced/revanced-bots/commit/8c0dd67d03d5a1747993da08a5bf82a39de43789))
* **bots/discord:** fix freeze on prod builds ([8efb549](https://github.com/revanced/revanced-bots/commit/8efb549453a04fab1ac6414a7f7f8bf702df3c93))
* **bots/discord:** fix get response logic ([3261294](https://github.com/revanced/revanced-bots/commit/3261294822b0a9faec094536ed5be2d3e1d5e17b))
* **bots/discord:** fix reload not working ([11582d5](https://github.com/revanced/revanced-bots/commit/11582d50345cae9fb645a65ca4e621596de6a408))
* **bots/discord:** follow-up if reply is already sent when error ([f75060b](https://github.com/revanced/revanced-bots/commit/f75060bc9cda44902cf872def73c116a6df039d7))
* **bots/discord:** give only removed roles for role presets ([522ad28](https://github.com/revanced/revanced-bots/commit/522ad28fd83565e9ca411dbce86c8447574288fd))
* **bots/discord:** hanging process when disconnecting from API too many times ([d31616e](https://github.com/revanced/revanced-bots/commit/d31616ebcba6f1dcd8bde183bcb8d1adb1501b61))
* **bots/discord:** import `config` from context ([763ef25](https://github.com/revanced/revanced-bots/commit/763ef253f9d4ff70a8b79969a7f4f41cba7f3c59))
* **bots/discord:** make `/eval` work ([eaa25f2](https://github.com/revanced/revanced-bots/commit/eaa25f2eb58a9e2d25bb98633ad668485e099714))
* **bots/discord:** messed up file name for `unmute` command ([399dca7](https://github.com/revanced/revanced-bots/commit/399dca71538fe5c8831977694a97058254a17578))
* **bots/discord:** only check for member permissions when specified while correcting responses ([b79a1c7](https://github.com/revanced/revanced-bots/commit/b79a1c7575e94c3e62654c87775cac497be4a50a))
* **bots/discord:** open database as read-write ([c366840](https://github.com/revanced/revanced-bots/commit/c36684091dddf67880505dc459e4334a8a5492f4))
* **bots/discord:** owners cannot bypass checks on some commands ([39cba97](https://github.com/revanced/revanced-bots/commit/39cba973418027ba6ed67e1ae5ab5c6458807562))
* **bots/discord:** parse larger units of durations, fix wrong timestamp in mod embed ([6c8dce0](https://github.com/revanced/revanced-bots/commit/6c8dce059366a6ef85f5b8b1794c056515b9f5b6))
* **bots/discord:** persist changes in context for eval command ([5b4965d](https://github.com/revanced/revanced-bots/commit/5b4965dcc7285676b2b3b6756c249bd56eaf8485))
* **bots/discord:** provide discord token for `reload` command ([dd21a5a](https://github.com/revanced/revanced-bots/commit/dd21a5abad560f3d00b8c58912786d4b6bd520e9))
* **bots/discord:** remove auto-generated files ([fb8af00](https://github.com/revanced/revanced-bots/commit/fb8af008661bf37389e01cba19d64a8b4fc82139))
* **bots/discord:** remove bad text channel checks ([f5939e2](https://github.com/revanced/revanced-bots/commit/f5939e25288fea2022fdeec9085ecb9ffada6111))
* **bots/discord:** remove redundant footer for response embeds ([412e003](https://github.com/revanced/revanced-bots/commit/412e00317d1eaca23e9c1375e16f94a5f2fa8d86))
* **bots/discord:** remove usage of macros ([7f27c56](https://github.com/revanced/revanced-bots/commit/7f27c5607ceeeef56d67097e88f68caa1b8791b3))
* **bots/discord:** remove useless feature ([d830e48](https://github.com/revanced/revanced-bots/commit/d830e48bc2de7aa457eab3a5f96ae652a93178f9))
* **bots/discord:** replace duration parser with a library ([94c4fed](https://github.com/revanced/revanced-bots/commit/94c4fedc06e20051e4123508e3134b97eb84782a))
* **bots/discord:** reset counter when reconnected to api, redo message scan filter logic ([d234d79](https://github.com/revanced/revanced-bots/commit/d234d79310caed9c43e14a905f9ef46a110e071d))
* **bots/discord:** revert dist denesting, fixes config not found ([0d4898d](https://github.com/revanced/revanced-bots/commit/0d4898dae8b26f8466d3f6b8f62875866f581644))
* **bots/discord:** send right response for after regexes ([a7688fa](https://github.com/revanced/revanced-bots/commit/a7688fa9b91919a87f74071b502cd0a87cd1c1fa))
* **bots/discord:** set the `label` property correctly for message scans ([6d463df](https://github.com/revanced/revanced-bots/commit/6d463df586dee5dd8fe8d6cff1c5316f7809b32a))
* **bots/discord:** set timeout for eligible mutes to unmute faster ([1f5c5a9](https://github.com/revanced/revanced-bots/commit/1f5c5a92a639973b83a1204355538936e69a4454))
* **bots/discord:** some configuration values not applying after running `/reload` ([a976dd2](https://github.com/revanced/revanced-bots/commit/a976dd2accc4b74914651245acde0979c30c92f5))
* **bots/discord:** use `APIEmbed` for response config ([35b9448](https://github.com/revanced/revanced-bots/commit/35b944800a3943c187d5b0e0d3e465ad7d2056fe))
* **bots/discord:** use env for initializing database ([af3759c](https://github.com/revanced/revanced-bots/commit/af3759caf428fada3b3f4a51852543d6fb280018))
* **bots/discord:** wrong command file path being imported ([fa0159c](https://github.com/revanced/revanced-bots/commit/fa0159c3a8dd4dad8778ccdb75b9e7c02ebbb64f))
* **bots/discord:** wrong database schema path ([875bd20](https://github.com/revanced/revanced-bots/commit/875bd209b252566414bf89349839cabc01697e1c))
* config file not being read ([474a8be](https://github.com/revanced/revanced-bots/commit/474a8be4af4eb2bae6e80a893439d846ad4f7503))
* **discord-bot:** also execute slash commands ([f0d45b2](https://github.com/revanced/revanced-bots/commit/f0d45b2c926ed753e2d21f2e06e24d7e6c43880a))
* **discord-bot:** check for role position ([d332043](https://github.com/revanced/revanced-bots/commit/d332043b1a4bb7ac9698a2fc912832e184130b4b))
* **discord-bot:** check if the member has the role ([9bff68c](https://github.com/revanced/revanced-bots/commit/9bff68c8c40c692764e4dec15a058e35059efbc9))
* **discord-bot:** not executing slash commands ([aa08087](https://github.com/revanced/revanced-bots/commit/aa0808768b90844c5fbd3e75d9f2d01c723b0151))
* **discord-bot:** only send lowercased text ([7803758](https://github.com/revanced/revanced-bots/commit/78037580dc92883f5ca21157e45268850cb5db90))
* dislike button not working properly ([85eba55](https://github.com/revanced/revanced-bots/commit/85eba554247738066af72a8efd0de215ec1164dc))
* fix deprecation ([4373ede](https://github.com/revanced/revanced-bots/commit/4373ede855333f209676551162a525238656e1f8))
* fix the fiter for the interaction collector ([a9ff003](https://github.com/revanced/revanced-bots/commit/a9ff00394a73f68a6793c2b35ff184675ee5a72c))
* fix typings and formatting ([479812e](https://github.com/revanced/revanced-bots/commit/479812e199b52cdb295a5746e0767306afab3413))
* ignore message if there's no content ([3cbebc2](https://github.com/revanced/revanced-bots/commit/3cbebc284277808495e64cf0fb47c555924ad9c5))
* move modules to `/bots` ([cd7156e](https://github.com/revanced/revanced-bots/commit/cd7156e792e65777ad1ab5a6f5d828b9ef6a9754))
* other small issues ([bc437a5](https://github.com/revanced/revanced-bots/commit/bc437a5ec7ce1d339094d608e2a61ac5f460c163))
* run projects with `--bun` ([bb2182e](https://github.com/revanced/revanced-bots/commit/bb2182e707fa40c555d56138972eeea28f1b3cf9))
* trainAI not using the bin location ([bd29943](https://github.com/revanced/revanced-bots/commit/bd2994388bc65f720120ef49edb6ba8163260309))
* update repo url ([a21aa34](https://github.com/revanced/revanced-bots/commit/a21aa348d7f32cd0ee65b371e9594520c0a9d3f1))
### chore
* fix more build issues ([77fefb9](https://github.com/revanced/revanced-bots/commit/77fefb9bef286a22f40a4d76b79c64fcc5a2467f))
### Features
* add wit.ai support ([1909e2c](https://github.com/revanced/revanced-bots/commit/1909e2c42148d635dcd045c738d88f65c8be16e3))
* **bots/discord/commands/reply:** send stacktrace when failed ([9f1ac37](https://github.com/revanced/revanced-bots/commit/9f1ac379276c11da65235577a9c6717e01cb02eb))
* **bots/discord/commands:** add `ban` and `unban` commands ([dc4863d](https://github.com/revanced/revanced-bots/commit/dc4863dc208b3fede4d4def323306ab58daffe04))
* **bots/discord/commands:** add `eval` command ([e64d1da](https://github.com/revanced/revanced-bots/commit/e64d1da00cc2ba718da5a4b0da141fe86a0e48d2))
* **bots/discord/commands:** add `mute` and `unmute` commands ([c0fa2fe](https://github.com/revanced/revanced-bots/commit/c0fa2fe1c36acdc7c52cde277aa7da867065f55e))
* **bots/discord/commands:** add `purge` and `role-preset` commands ([fb01ce5](https://github.com/revanced/revanced-bots/commit/fb01ce57400130c93751a11573eb444c0ba103eb))
* **bots/discord/commands:** add `reload` command ([6875b32](https://github.com/revanced/revanced-bots/commit/6875b32fd0c6ce3034da9dc6c704d425afb26f2e))
* **bots/discord/commands:** allow process exception in `exception-test` ([ca47535](https://github.com/revanced/revanced-bots/commit/ca475356ad95fec86e8e8b5bf4bbf17b70add5fe))
* **bots/discord/utils/discord/embeds:** expose `applyCommonEmbedStyles` fn ([2d794ed](https://github.com/revanced/revanced-bots/commit/2d794ede7d7a208bd3616c45e8e6d2a2cd83e9ed))
* **bots/discord/utils/embeds:** make title parameter nullable ([ee885ca](https://github.com/revanced/revanced-bots/commit/ee885ca7585a55fdc31e137ae29dc13a37ce2fb2))
* **bots/discord/utils/fs:** use `recursive` option for listing files ([da21e1a](https://github.com/revanced/revanced-bots/commit/da21e1a6f76deaeb477203b04263bd170863825b))
* **bots/discord/utils:** add duration utility ([a9add9e](https://github.com/revanced/revanced-bots/commit/a9add9ea9affb42bdfcb17cf4b268feec5729854))
* **bots/discord/utils:** add functions for role presets ([fb32a04](https://github.com/revanced/revanced-bots/commit/fb32a04ad38be8d0836dc99259b6ef05a0825830))
* **bots/discord/utils:** allow loading commands from custom dir ([8b690b8](https://github.com/revanced/revanced-bots/commit/8b690b879bb5c6023c8fc863afbd9fd1d02719bb))
* **bots/discord:** add `api.disconnectRetryInterval` config ([2f86586](https://github.com/revanced/revanced-bots/commit/2f8658617923c07f6847cbf1fdfc5f5379d95b6c))
* **bots/discord:** add `moderation.roles` config to be used in `moderation` commands ([39d5b3a](https://github.com/revanced/revanced-bots/commit/39d5b3a479b4d856aabe12cc31177c24f88ae23e))
* **bots/discord:** add `ocrTriggers` resp config, embed footer scan mode ([744a56a](https://github.com/revanced/revanced-bots/commit/744a56a4fdc8844e37959a88bcf81ee39fe726ef))
* **bots/discord:** add `replyToReplied` option in response config ([27662ed](https://github.com/revanced/revanced-bots/commit/27662ed91a79bfac7d3f091834e859a7b57366ce))
* **bots/discord:** add `train` commands ([ee90ef2](https://github.com/revanced/revanced-bots/commit/ee90ef247b4bf2b3c0698606b947116f2dc1b868))
* **bots/discord:** add a better way to manage databases ([a68d726](https://github.com/revanced/revanced-bots/commit/a68d72687584332587455962b0202a306288057d))
* **bots/discord:** add code to actually scan text files correctly ([80aeb19](https://github.com/revanced/revanced-bots/commit/80aeb1902063140a2e78cfaed9424e5101ab03f1))
* **bots/discord:** add default durations for moderation commands ([27d3b39](https://github.com/revanced/revanced-bots/commit/27d3b392092141a1e3b4b0298131ff7817458dc1))
* **bots/discord:** add more fallbacks for decancering ([2e1e009](https://github.com/revanced/revanced-bots/commit/2e1e009b4272495798313bd3bd61f258875c62e1))
* **bots/discord:** add more month aliases to duration parser ([c2009ca](https://github.com/revanced/revanced-bots/commit/c2009ca6d42e4387bc5f375d76ecf72991b7fe32))
* **bots/discord:** add more options for curing, fix default regex ([1a4ec1e](https://github.com/revanced/revanced-bots/commit/1a4ec1ece80becd9156582cc490f6681cb2a1f39))
* **bots/discord:** add source ([f9d50a0](https://github.com/revanced/revanced-bots/commit/f9d50a0a6bef8beaa428a0a555bfa4f879f685f1))
* **bots/discord:** add sticky messages ([bf66155](https://github.com/revanced/revanced-bots/commit/bf661556e131bf0ef24e47f658fbcd701960e312))
* **bots/discord:** add trigger to context for eval ([b5f4097](https://github.com/revanced/revanced-bots/commit/b5f40975386677ffff343c42f8ffac21f847a0b7))
* **bots/discord:** allow admins to bypass permission checks ([620f933](https://github.com/revanced/revanced-bots/commit/620f9339f0737b79d72c66d90ffa42ea3f987710))
* **bots/discord:** blacklist and whitelist for filters ([cdb6001](https://github.com/revanced/revanced-bots/commit/cdb600195520dba33110c40841629259e317055e))
* **bots/discord:** cure on every event ([8ff6086](https://github.com/revanced/revanced-bots/commit/8ff6086028132cc4b49ee60846e8d6ef909f5a89))
* **bots/discord:** don't nest builds in src directory, autogen db when missing ([4834685](https://github.com/revanced/revanced-bots/commit/48346851864c4d4b6276388644dd24ce16222b3e))
* **bots/discord:** framework changes and new features ([646ec8d](https://github.com/revanced/revanced-bots/commit/646ec8da87617e6c8f48a89e8054e2cba91da549))
* **bots/discord:** improve admin commands ([0346741](https://github.com/revanced/revanced-bots/commit/03467411882b8598e2c06f389a09ef2e201bb43f))
* **bots/discord:** improve logs ([6abb740](https://github.com/revanced/revanced-bots/commit/6abb7409945c10bd3af451fb45ef4b4d4ebe9489))
* **bots/discord:** sanitize `BasicDatabase` inputs ([fd76e0a](https://github.com/revanced/revanced-bots/commit/fd76e0af72fe28b414ae3b5e8d3886e58561e57e))
* **bots/discord:** support nickname decancering ([1723e8c](https://github.com/revanced/revanced-bots/commit/1723e8cacf96e8c6bdee22cfd30e89524fdcef74))
* **bots/discord:** switch to `drizzle-orm` ([e204b7b](https://github.com/revanced/revanced-bots/commit/e204b7b7566fd7fa423baef32977a8575d44a9e0))
* **bots/discord:** update config ([197d2ac](https://github.com/revanced/revanced-bots/commit/197d2acea89c38e43858d52736508d449152e804))
* **bots/discord:** update example config file ([bc9951c](https://github.com/revanced/revanced-bots/commit/bc9951c9b5e007c3e1b3076aa0966ccf29bb18bc))
* **bots/discord:** update to newer command definition framework ([97f2795](https://github.com/revanced/revanced-bots/commit/97f2795df4ede4d12a08193dba453c1bc765a4c2))
* discord bot scanning messages ([d1bd3b2](https://github.com/revanced/revanced-bots/commit/d1bd3b2b7e4985a64e9b070ab006cc6f3508c46e))
* **discord-bot:** a way to train AI ([355a508](https://github.com/revanced/revanced-bots/commit/355a50803adc85b5579155b55ddbba4fa0449237))
* **discord-bot:** command handler and train cmd ([6aee8a4](https://github.com/revanced/revanced-bots/commit/6aee8a4c63eb108800fcb0a23ca61f200d8f1f2a))
* **discord-bot:** event handler ([0ad5ece](https://github.com/revanced/revanced-bots/commit/0ad5ece08593c0db111fa4a592b42c6e0348fd1c))
* GODEL AI ([0ba525c](https://github.com/revanced/revanced-bots/commit/0ba525c4a5802106d582c75f713728accf2f151a))
* initalize discord bot ([bb4a5a7](https://github.com/revanced/revanced-bots/commit/bb4a5a77eefbc7ac88536f73a111df1050b235e7))
* initialize helper client ([7f9ca77](https://github.com/revanced/revanced-bots/commit/7f9ca77e0331ec143160ee51ed7c3aa9e4e70b9c))
* message buttons for training ([6551ca9](https://github.com/revanced/revanced-bots/commit/6551ca9dadc2e3ddfe98875e80ed61f7d71a1651))
* platform specific responses ([18e57b0](https://github.com/revanced/revanced-bots/commit/18e57b0c320732a937bb60db11c5d6794ed11522))
* prettier and eslint ([1c27ccb](https://github.com/revanced/revanced-bots/commit/1c27ccb17c85f0f6982db45de426181d2c231d0e))
* refactor and new features ([#7](https://github.com/revanced/revanced-bots/issues/7)) ([8b9f45d](https://github.com/revanced/revanced-bots/commit/8b9f45dc22de29dc2ccb1cfab9a026db00457e25))
* run bots in one process ([d26d533](https://github.com/revanced/revanced-bots/commit/d26d53317440c64fb775cea609a87d29be6c8b40))
* training and replies changed ([715aa91](https://github.com/revanced/revanced-bots/commit/715aa918cf84213c9b19591a398d7532eb3f232a))
* **utils/discord/embeds:** allow adding extra fields for moderation embeds ([49ce9a7](https://github.com/revanced/revanced-bots/commit/49ce9a7ca3d8558b73a9b94dfe7a01d809db6fff))
### BREAKING CHANGES
* In `@revanced/discord-bot`, its environment variable
`DATABASE_URL` has been renamed to `DATABASE_PATH`
and the `file:` prefix is no longer needed
# @revanced/discord-bot [1.0.0-dev.38](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.0-dev.37...@revanced/discord-bot@1.0.0-dev.38) (2025-04-04)
### Bug Fixes
* run projects with `--bun` ([bb2182e](https://github.com/revanced/revanced-bots/commit/bb2182e707fa40c555d56138972eeea28f1b3cf9))
# @revanced/discord-bot [1.0.0-dev.37](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.0-dev.36...@revanced/discord-bot@1.0.0-dev.37) (2025-03-08)
### Bug Fixes
* **bots/discord/utils/duration:** fix specified default unit not working ([d138af4](https://github.com/revanced/revanced-bots/commit/d138af46d1f25a11b6f8ab3790ecaa70b1d716a9))
# @revanced/discord-bot [1.0.0-dev.36](https://github.com/revanced/revanced-bots/compare/@revanced/discord-bot@1.0.0-dev.35...@revanced/discord-bot@1.0.0-dev.36) (2025-03-03)
### Bug Fixes
* **bots/discord/scripts/build:** check if dist dir exists before cleaning ([c06033e](https://github.com/revanced/revanced-bots/commit/c06033e5730f82438e8052b9b519a8f8e2d25437))
* **bots/discord/utils/duration:** make second the default unit ([5d1af3c](https://github.com/revanced/revanced-bots/commit/5d1af3c31c3379b6a13684dfb07583737908c8aa))
* **bots/discord:** add GuildMember partial ([8e3946a](https://github.com/revanced/revanced-bots/commit/8e3946a66602838715787090008c7bfaf72b67e9))
* **bots/discord:** decrease length of an option in `ban` command ([22d3eea](https://github.com/revanced/revanced-bots/commit/22d3eea88d532792c1237d1a1ab18bc02e57816a))
* **bots/discord:** delete expired appliedPresets entries after unapplying ([14c98e8](https://github.com/revanced/revanced-bots/commit/14c98e87df1ec4fd762bbc48ca4c06470cb110a2))
* fix typings and formatting ([479812e](https://github.com/revanced/revanced-bots/commit/479812e199b52cdb295a5746e0767306afab3413))
* update repo url ([a21aa34](https://github.com/revanced/revanced-bots/commit/a21aa348d7f32cd0ee65b371e9594520c0a9d3f1))
### Features
* **bots/discord:** add more month aliases to duration parser ([c2009ca](https://github.com/revanced/revanced-bots/commit/c2009ca6d42e4387bc5f375d76ecf72991b7fe32))
# @revanced/discord-bot [1.0.0-dev.35](https://github.com/revanced/revanced-helper/compare/@revanced/discord-bot@1.0.0-dev.34...@revanced/discord-bot@1.0.0-dev.35) (2024-10-17)

View File

@@ -15,4 +15,4 @@ COPY --from=build /build/bots/discord/dist /app
USER 1000:1000
ENTRYPOINT [ "bun", "run", "src/index.js" ]
ENTRYPOINT [ "bun", "--bun", "run", "src/index.js" ]

View File

@@ -19,8 +19,8 @@ export default {
},
timeout: 60000,
forceSendTimeout: 300000,
}
}
},
},
},
moderation: {
cure: {
@@ -66,7 +66,6 @@ export default {
},
},
humanCorrections: {
falsePositiveLabel: 'false_positive',
allow: {
members: {
permissions: 8n,
@@ -77,7 +76,7 @@ export default {
attachments: {
scanAttachments: true,
allowedMimeTypes: ['image/jpeg', 'image/png', 'image/webp', 'text/plain'],
maxTextFileSize: 512000
maxTextFileSize: 512000,
},
responses: [
{

View File

@@ -36,7 +36,6 @@ export type Config = {
blacklist?: Filter
}
humanCorrections: {
falsePositiveLabel: string
allow?: {
users?: string[]
members?: {
@@ -72,7 +71,7 @@ export type ConfigMessageScanResponse = {
image?: Array<RegExp>
}
filterOverride?: NonNullable<Config['messageScan']>['filter']
response: ConfigMessageScanResponseMessage | null
response: ConfigMessageScanResponseMessage
respondToReply?: boolean
}

View File

@@ -2,7 +2,7 @@
"name": "@revanced/discord-bot",
"type": "module",
"private": true,
"version": "1.0.0-dev.35",
"version": "1.4.1",
"description": "🤖 Discord bot assisting ReVanced",
"main": "src/index.ts",
"scripts": {
@@ -14,7 +14,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/revanced/revanced-helper.git",
"url": "git+https://github.com/revanced/revanced-bots.git",
"directory": "bots/discord"
},
"author": "Palm <contact@palmdevs.me> (https://palmdevs.me)",
@@ -24,23 +24,23 @@
],
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/revanced/revanced-helper/issues"
"url": "https://github.com/revanced/revanced-bots/issues"
},
"homepage": "https://github.com/revanced/revanced-helper#readme",
"homepage": "https://github.com/revanced/revanced-bots#readme",
"dependencies": {
"@discordjs/builders": "^1.9.0",
"@discordjs/rest": "^2.4.0",
"@discordjs/builders": "^1.11.3",
"@discordjs/rest": "^2.6.0",
"@revanced/bot-api": "workspace:*",
"@revanced/bot-shared": "workspace:*",
"chalk": "^5.3.0",
"decancer": "^3.2.4",
"discord.js": "^14.16.3",
"drizzle-orm": "^0.31.4",
"parse-duration": "^1.1.0"
"@sapphire/duration": "^1.2.0",
"chalk": "^5.6.2",
"decancer": "^3.3.3",
"discord.js": "^14.22.1",
"drizzle-orm": "^0.44.5"
},
"devDependencies": {
"@libsql/client": "^0.7.0",
"discord-api-types": "^0.37.102",
"drizzle-kit": "^0.22.8"
"@libsql/client": "^0.15.15",
"discord-api-types": "^0.38.24",
"drizzle-kit": "^0.31.4"
}
}

View File

@@ -1,10 +1,10 @@
import { createLogger } from '@revanced/bot-shared'
import { cp, rm } from 'fs/promises'
import { cp, exists, rm } from 'fs/promises'
const logger = createLogger()
logger.warn('Cleaning previous build...')
await rm('./dist', { recursive: true })
if (await exists('./dist')) await rm('./dist', { recursive: true })
logger.info('Building bot...')
await Bun.build({

View File

@@ -1,8 +1,7 @@
import { ApplicationCommandOptionType, ApplicationCommandType } from 'discord.js'
import { config } from '../context'
import { isAdmin } from '../utils/discord/permissions'
import CommandError, { CommandErrorType } from './CommandError'
import type {
APIApplicationCommandChannelOption,
CacheType,
@@ -20,7 +19,6 @@ import type {
UserContextMenuCommandInteraction,
UserResolvable,
} from 'discord.js'
import { config } from '../context'
export enum CommandType {
ChatGlobal = 1,

View File

@@ -1,9 +1,8 @@
import { ApplicationCommandOptionType, MessageFlags } from 'discord.js'
import { unlinkSync, writeFileSync } from 'fs'
import { join } from 'path'
import { inspect } from 'util'
import { createContext, runInContext } from 'vm'
import { ApplicationCommandOptionType } from 'discord.js'
import { AdminCommand } from '$/classes/Command'
import { createSuccessEmbed } from '$/utils/discord/embeds'
import { parseDuration } from '$/utils/duration'
@@ -85,8 +84,8 @@ export default new AdminCommand({
})
await trigger.reply({
ephemeral: true,
embeds: [embed],
flags: MessageFlags.Ephemeral,
files,
})

View File

@@ -1,5 +1,4 @@
import { ApplicationCommandOptionType } from 'discord.js'
import { AdminCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'

View File

@@ -1,3 +1,4 @@
import { type CommandInteraction, MessageFlags } from 'discord.js'
import { AdminCommand } from '$/classes/Command'
export default new AdminCommand({
@@ -10,7 +11,8 @@ export default new AdminCommand({
logger.debug('Invalidating previous config...')
context.config.invalidate()
if ('deferReply' in trigger) await trigger.deferReply({ ephemeral: true })
if ((trigger as CommandInteraction).deferReply)
await (trigger as CommandInteraction).deferReply({ flags: MessageFlags.Ephemeral })
logger.info('Reinitializing API client to reload configuration...')
await api.client.ws.setOptions(

View File

@@ -1,8 +1,6 @@
import { ApplicationCommandOptionType, Routes } from 'discord.js'
import { AdminCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { createSuccessEmbed } from '$/utils/discord/embeds'
const SubcommandOptions = {

View File

@@ -1,3 +1,4 @@
import { MessageFlags } from 'discord.js'
import { AdminCommand } from '$/classes/Command'
export default new AdminCommand({
@@ -9,7 +10,7 @@ export default new AdminCommand({
logger.fatal('Stopping bot...')
trigger.reply({
content: 'Stopping... (I will go offline once done)',
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
if (!api.client.disconnected) api.client.disconnect()

View File

@@ -1,5 +1,4 @@
import { EmbedBuilder } from 'discord.js'
import { EmbedBuilder, MessageFlags } from 'discord.js'
import Command from '$/classes/Command'
import { applyCommonEmbedStyles } from '$/utils/discord/embeds'
@@ -18,6 +17,7 @@ export default new Command({
const reply = await trigger
.reply({
embeds: [embed.toJSON()],
flags: MessageFlags.Ephemeral,
})
.then(it => it.fetch())

View File

@@ -1,5 +1,5 @@
import { ApplicationCommandOptionType, Message, MessageFlags } from 'discord.js'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { ApplicationCommandOptionType, Message } from 'discord.js'
import { ModerationCommand } from '../../classes/Command'
export default new ModerationCommand({
@@ -40,7 +40,7 @@ export default new ModerationCommand({
await trigger.reply({
content: 'OK!',
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
},
})

View File

@@ -20,7 +20,7 @@ export default new ModerationCommand({
},
dmt: {
description:
'Time duration to delete messages (default time unit is days, must be from 0s to 7d, default value is 0s)',
'Time duration to delete messages (default time unit is days, must be from 0s to 7d, default is 0s)',
required: false,
type: ModerationCommand.OptionType.String,
},

View File

@@ -1,3 +1,4 @@
import { MessageFlags } from 'discord.js'
import { ModerationCommand } from '$/classes/Command'
import { createSuccessEmbed } from '$/utils/discord/embeds'
import { cureNickname } from '$/utils/discord/moderation'
@@ -18,7 +19,7 @@ export default new ModerationCommand({
await cureNickname(member)
await interaction.reply({
embeds: [createSuccessEmbed(null, `Cured nickname for ${member.toString()}`)],
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
},
})

View File

@@ -3,7 +3,7 @@ import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { createModerationActionEmbed } from '$/utils/discord/embeds'
import { sendModerationReplyAndLogs } from '$/utils/discord/moderation'
import { applyRolePreset, removeRolePreset } from '$/utils/discord/rolePresets'
import { parseDuration } from '$/utils/duration'
import { isSafeTimeoutDuration, parseDuration } from '$/utils/duration'
export default new ModerationCommand({
name: 'mute',
@@ -63,7 +63,7 @@ export default new ModerationCommand({
createModerationActionEmbed('Muted', user, executor.user, reason, Math.ceil(expires / 1000)),
)
if (duration)
if (isSafeTimeoutDuration(duration))
setTimeout(() => {
removeRolePreset(member, 'mute')
}, duration)

View File

@@ -1,5 +1,4 @@
import { EmbedBuilder } from 'discord.js'
import { ModerationCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { applyCommonEmbedStyles } from '$/utils/discord/embeds'

View File

@@ -2,7 +2,7 @@ import { ModerationCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { sendPresetReplyAndLogs } from '$/utils/discord/moderation'
import { applyRolePreset, removeRolePreset } from '$/utils/discord/rolePresets'
import { parseDuration } from '$/utils/duration'
import { isSafeTimeoutDuration, parseDuration } from '$/utils/duration'
const SubcommandOptions = {
member: {
@@ -78,7 +78,7 @@ export default new ModerationCommand({
)
}
if (expires)
if (expires && isSafeTimeoutDuration(expires))
setTimeout(() => {
removeRolePreset(member, preset)
}, expires)

View File

@@ -1,9 +1,8 @@
import { createSuccessEmbed } from '$/utils/discord/embeds'
import { durationToString, parseDuration } from '$/utils/duration'
import { ChannelType } from 'discord.js'
import { ModerationCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { ChannelType } from 'discord.js'
import { createSuccessEmbed } from '$/utils/discord/embeds'
import { durationToString, parseDuration } from '$/utils/duration'
export default new ModerationCommand({
name: 'slowmode',

View File

@@ -1,10 +1,10 @@
import { and, eq } from 'drizzle-orm'
import { ModerationCommand } from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { appliedPresets } from '$/database/schemas'
import { createModerationActionEmbed } from '$/utils/discord/embeds'
import { sendModerationReplyAndLogs } from '$/utils/discord/moderation'
import { removeRolePreset } from '$/utils/discord/rolePresets'
import { and, eq } from 'drizzle-orm'
export default new ModerationCommand({
name: 'unmute',

View File

@@ -1,15 +1,15 @@
import { type FetchMessageOptions, MessageFlags, type MessageResolvable } from 'discord.js'
import Command from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { config } from '../../../context'
import type { FetchMessageOptions, MessageResolvable } from 'discord.js'
import type { ConfigMessageScanResponseLabelConfig } from 'config.schema'
import { createSuccessEmbed } from '$/utils/discord/embeds'
import { config } from '../../../context'
import type { ConfigMessageScanResponseLabelConfig } from 'config.schema'
const msRcConfig = config.messageScan?.humanCorrections?.allow
export default new Command({
name: 'train',
description: 'Train a specific message or text to a specific label',
description: 'Train a specific message to a specific label',
type: Command.Type.ChatGuild,
requirements: {
users: msRcConfig?.users,
@@ -26,9 +26,9 @@ export default new Command({
required: true,
},
label: {
description: 'The label to train the message as',
description: 'The label to train the message as (leave empty for out of scope)',
type: Command.OptionType.String,
required: true,
required: false,
},
},
allowMessageCommand: true,
@@ -49,7 +49,7 @@ export default new Command({
'This command can only be used in or on text channels',
)
if (!labels.includes(label))
if (label && !labels.includes(label))
throw new CommandError(
CommandErrorType.InvalidArgument,
`The provided label is invalid.\nValid labels are:${labels.map(l => `\n- \`${l}\``).join('')}`,
@@ -60,17 +60,17 @@ export default new Command({
)
if (!refMsg) throw new CommandError(CommandErrorType.InvalidArgument, 'The provided message does not exist.')
logger.debug(`User ${context.executor.id} is training message ${refMsg?.id} as ${label}`)
logger.debug(`User ${context.executor.id} is training message ${refMsg?.id} as ${label ?? 'out of scope'}`)
await context.api.client.trainMessage(refMsg.content, label)
await trigger.reply({
embeds: [
createSuccessEmbed(
'Message trained',
`The provided message has been trained as \`${label}\`. Thank you for your contribution!`,
`The provided message has been trained as ${label ? `\`${label}\`` : 'out of scope'}. Thank you for your contribution!`,
),
],
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
},
})

View File

@@ -1,7 +1,7 @@
import { type APIStringSelectComponent, ComponentType, MessageFlags } from 'discord.js'
import Command from '$/classes/Command'
import CommandError, { CommandErrorType } from '$/classes/CommandError'
import { config } from '../../../context'
import { type APIStringSelectComponent, ComponentType } from 'discord.js'
import type { ConfigMessageScanResponseLabelConfig } from 'config.schema'
const msRcConfig = config.messageScan?.humanCorrections?.allow
@@ -37,14 +37,19 @@ export default new Command({
components: [
{
custom_id: `tr_${trigger.targetMessage.channelId}_${trigger.targetId}`,
options: labels.map(label => ({ label, value: label })),
options: [
...labels.map(label => ({ label, value: label })),
{ label: 'Out of scope', value: OutOfScopeLabel, emoji: { name: '❌' } },
],
type: ComponentType.StringSelect,
} satisfies APIStringSelectComponent,
],
type: ComponentType.ActionRow,
},
],
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
},
})
export const OutOfScopeLabel = '<out of scope>'

View File

@@ -1,16 +1,13 @@
import { Database } from 'bun:sqlite'
import { existsSync, readFileSync, readdirSync } from 'fs'
import { join } from 'path'
import { Client as APIClient } from '@revanced/bot-api'
import { createLogger } from '@revanced/bot-shared'
import { Client as DiscordClient, type Message, Partials } from 'discord.js'
import { Client as DiscordClient, type Message, Options, Partials } from 'discord.js'
import { drizzle } from 'drizzle-orm/bun-sqlite'
import * as schemas from './database/schemas'
import type { default as Command, CommandOptionsOptions, CommandType } from './classes/Command'
import { existsSync, readdirSync, readFileSync } from 'fs'
import { join } from 'path'
import { __getConfig, config } from './config'
import * as schemas from './database/schemas'
import type { default as Command, CommandOptionsOptions, CommandType } from './classes/Command'
export { config, __getConfig }
export const logger = createLogger({
@@ -80,7 +77,52 @@ export const discord = {
parse: ['users'],
repliedUser: true,
},
partials: [Partials.Message, Partials.Reaction],
sweepers: {
...Options.DefaultSweeperSettings,
messages: {
interval: 1_800, // Every 30m
lifetime: 3_600, // Remove messages older than 1h
},
},
makeCache: Options.cacheWithLimits({
...Options.DefaultMakeCacheSettings,
UserManager: 50,
GuildMemberManager: {
maxSize: 50,
// Always keep client guild member in cache
keepOverLimit: member => member.id === member.client.user.id,
},
ThreadManager: {
maxSize: 0,
// Always keep threads that are used for moderation logging
keepOverLimit: thread => config.moderation?.log?.thread === thread.id,
},
GuildMessageManager: {
maxSize: 0,
// Always keep messages posted by the client in cache
keepOverLimit: message => message.author?.id === message.client.user.id,
},
// Unneeded cache
MessageManager: 0,
ReactionManager: 0,
VoiceStateManager: 0,
ThreadMemberManager: 0,
StageInstanceManager: 0,
ReactionUserManager: 0,
PresenceManager: 0,
GuildTextThreadManager: 0,
GuildStickerManager: 0,
DMMessageManager: 0,
GuildEmojiManager: 0,
GuildBanManager: 0,
GuildScheduledEventManager: 0,
EntitlementManager: 0,
AutoModerationRuleManager: 0,
GuildForumThreadManager: 0,
BaseGuildEmojiManager: 0,
GuildInviteManager: 0,
}),
partials: [Partials.Message, Partials.Reaction, Partials.GuildMember, Partials.User],
}),
commands: Object.fromEntries(Object.values(commands).map(cmd => [cmd.name, cmd])) as Record<
string,
@@ -101,7 +143,7 @@ export const discord = {
timerActive: boolean
timerMs: number
forceTimerMs?: number
send: (forced?: boolean) => Promise<void>
send: () => Promise<void>
currentMessage?: Message<true>
timer?: NodeJS.Timeout
forceTimer?: NodeJS.Timeout

View File

@@ -1,5 +1,5 @@
import type { InferSelectModel } from 'drizzle-orm'
import { integer, sqliteTable, text, uniqueIndex } from 'drizzle-orm/sqlite-core'
import type { InferSelectModel } from 'drizzle-orm'
export const responses = sqliteTable('responses', {
replyId: text('reply').primaryKey().notNull(),
@@ -20,9 +20,7 @@ export const appliedPresets = sqliteTable(
preset: text('preset').notNull(),
until: integer('until'),
},
table => ({
uniqueComposite: uniqueIndex('unique_composite').on(table.memberId, table.preset, table.guildId),
}),
table => [uniqueIndex('unique_composite').on(table.memberId, table.preset, table.guildId)],
)
export type Response = InferSelectModel<typeof responses>

View File

@@ -1,5 +1,5 @@
import { on, withContext } from '$utils/api/events'
import { DisconnectReason, HumanizedDisconnectReason } from '@revanced/bot-shared'
import { on, withContext } from '$utils/api/events'
withContext(on, 'disconnect', ({ api, config, logger }, reason, msg) => {
if (reason === DisconnectReason.PlannedDisconnect && api.intentionallyDisconnecting) return

View File

@@ -1,7 +1,7 @@
import { and, eq, gt } from 'drizzle-orm'
import { appliedPresets } from '$/database/schemas'
import { on, withContext } from '$/utils/discord/events'
import { applyRolesUsingPreset } from '$/utils/discord/rolePresets'
import { and, eq, gt } from 'drizzle-orm'
withContext(on, 'guildMemberAdd', async ({ database }, member) => {
const applieds = await database.query.appliedPresets.findMany({

View File

@@ -1,3 +1,4 @@
import { MessageFlags } from 'discord.js'
import CommandError from '$/classes/CommandError'
import { createStackTraceEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events'
@@ -9,8 +10,7 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
const command = discord.commands[interaction.commandName]
logger.debug(`Command ${interaction.commandName} being invoked by ${interaction.user.tag} via chat`)
if (!command)
return void logger.error(`Chat command ${interaction.commandName} not implemented but registered!!!`)
if (!command) return void logger.error(`Chat command ${interaction.commandName} not implemented but registered!!!`)
try {
logger.debug(`Command ${interaction.commandName} being executed via chat`)
@@ -21,7 +21,7 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
await interaction[interaction.replied ? 'followUp' : 'reply']({
embeds: [err instanceof CommandError ? err.toEmbed() : createStackTraceEmbed(err)],
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
// 100 and up are user errors

View File

@@ -1,3 +1,4 @@
import { MessageFlags } from 'discord.js'
import CommandError from '$/classes/CommandError'
import { createStackTraceEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events'
@@ -20,7 +21,7 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
logger.error(`Error while executing command ${interaction.commandName}:`, err)
await interaction[interaction.replied ? 'followUp' : 'reply']({
embeds: [err instanceof CommandError ? err.toEmbed() : createStackTraceEmbed(err)],
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
}
})

View File

@@ -1,11 +1,15 @@
import {
type ButtonInteraction,
MessageFlags,
type StringSelectMenuInteraction,
type TextBasedChannel,
} from 'discord.js'
import { eq } from 'drizzle-orm'
import { responses } from '$/database/schemas'
import { handleUserResponseCorrection } from '$/utils/discord/messageScan'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events'
import type { ButtonInteraction, StringSelectMenuInteraction, TextBasedChannel } from 'discord.js'
import { eq } from 'drizzle-orm'
// No permission check required as it is already done when the user reacts to a bot response
withContext(on, 'interactionCreate', async (context, interaction) => {
const {
@@ -26,7 +30,7 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
if (!response)
return void (await interaction.reply({
content: "I don't recall having sent this response, so I cannot correct it.",
ephemeral: true,
flags: MessageFlags.Ephemeral,
}))
try {
@@ -54,8 +58,8 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
const editMessage = (content: string, description?: string) =>
editInteractionMessage(interaction, msg.url, content, description)
const handleCorrection = (label: string) =>
handleUserResponseCorrection(context, response, msg, label, interaction.user)
const handleCorrection = (label?: string) =>
handleUserResponseCorrection(context, response, msg, interaction.user, label)
if (response.correctedById)
return await editMessage(
@@ -78,7 +82,7 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
await editMessage('Canceled', 'You canceled this interaction. 😞')
break
case 'delete':
await handleCorrection(msConfig.humanCorrections.falsePositiveLabel)
await handleCorrection()
await editMessage(
'Marked as false positive',
'The response has been deleted and marked as a false positive. Thank you for your feedback. 🎉',
@@ -91,7 +95,7 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
logger.error('Failed to handle correct response interaction:', e)
await interaction.reply({
embeds: [createStackTraceEmbed(e)],
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
}
})

View File

@@ -1,8 +1,8 @@
import { MessageFlags, type TextBasedChannel } from 'discord.js'
import { OutOfScopeLabel } from '$/commands/support/train/context-menu'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$utils/discord/embeds'
import { on, withContext } from '$utils/discord/events'
import type { TextBasedChannel } from 'discord.js'
withContext(on, 'interactionCreate', async (context, interaction) => {
const {
logger,
@@ -28,11 +28,14 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
'Thank you for your contribution! Unfortunately, the message could not be found.',
),
],
ephemeral: true,
flags: MessageFlags.Ephemeral,
}))
const selectedLabel = interaction.values[0]!
await context.api.client.trainMessage(msg.content, selectedLabel)
const selectedLabel = interaction.values[0]
await context.api.client.trainMessage(
msg.content,
selectedLabel === OutOfScopeLabel ? undefined : selectedLabel,
)
await interaction.reply({
embeds: [
createSuccessEmbed(
@@ -40,13 +43,13 @@ withContext(on, 'interactionCreate', async (context, interaction) => {
`Thank you for your contribution! The selected message is being trained as \`${selectedLabel}\`. 🎉`,
),
],
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
} catch (e) {
logger.error('Failed to handle train message interaction:', e)
await interaction.reply({
embeds: [createStackTraceEmbed(e)],
ephemeral: true,
flags: MessageFlags.Ephemeral,
})
}
})

View File

@@ -1,3 +1,4 @@
import { MessageScanLabeledResponseReactions } from '$/constants'
import { responses } from '$/database/schemas'
import { getResponseFromText, messageMatchesFilter } from '$/utils/discord/messageScan'
import { createMessageScanResponseEmbed } from '$utils/discord/embeds'
@@ -47,6 +48,8 @@ withContext(on, 'messageCreate', async (context, msg) => {
label,
content: msg.content,
})
await Promise.all(Object.values(MessageScanLabeledResponseReactions).map(name => reply.react(name)))
}
}
} catch (e) {

View File

@@ -7,30 +7,22 @@ withContext(on, 'messageCreate', async ({ discord, logger }, msg) => {
const store = discord.stickyMessages[msg.guildId]?.[msg.channelId]
if (!store) return
if (store.timerActive) {
if (!store.forceTimerActive && store.forceTimerMs) {
logger.debug(
`Channel ${msg.channelId} in guild ${msg.guildId} is active, starting force send timer and clearing existing timer`,
)
// Timer is already active from previous event, and force timer isn't active, so we start the latter
if (store.timerActive && store.forceTimerMs && !store.forceTimerActive) {
logger.debug(
`Channel ${msg.channelId} in guild ${msg.guildId} is very active, starting sticky message force timer`,
)
// Clear the timer
clearTimeout(store.timer)
store.timerActive = false
// (Re)start the force timer
store.forceTimerActive = true
if (!store.forceTimer)
store.forceTimer = setTimeout(
() =>
store.send(true).then(() => {
store.forceTimerActive = false
}),
store.forceTimerMs,
) as NodeJS.Timeout
else store.forceTimer.refresh()
}
} else if (!store.forceTimerActive) {
store.timerActive = true
if (!store.timer) store.timer = setTimeout(store.send, store.timerMs) as NodeJS.Timeout
// (Re)start the force timer
store.forceTimerActive = true
if (store.forceTimer) store.forceTimer.refresh()
else store.forceTimer = setTimeout(store.send, store.forceTimerMs)
}
logger.debug(`Channel ${msg.channelId} in guild ${msg.guildId} is active, starting sticky message timer`)
// (Re)start the timer
store.timerActive = true
if (store.timer) store.timer.refresh()
else store.timer = setTimeout(store.send, store.timerMs) as NodeJS.Timeout
})

View File

@@ -1,7 +1,3 @@
import { MessageScanLabeledResponseReactions as Reactions } from '$/constants'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$/utils/discord/embeds'
import { on, withContext } from '$/utils/discord/events'
import {
ActionRowBuilder,
ButtonBuilder,
@@ -9,21 +5,23 @@ import {
StringSelectMenuBuilder,
StringSelectMenuOptionBuilder,
} from 'discord.js'
import type { ConfigMessageScanResponseLabelConfig } from '$/../config.schema'
import { eq } from 'drizzle-orm'
import { MessageScanLabeledResponseReactions as Reactions } from '$/constants'
import { responses } from '$/database/schemas'
import { createErrorEmbed, createStackTraceEmbed, createSuccessEmbed } from '$/utils/discord/embeds'
import { on, withContext } from '$/utils/discord/events'
import { handleUserResponseCorrection } from '$/utils/discord/messageScan'
import { isAdmin } from '$/utils/discord/permissions'
import { eq } from 'drizzle-orm'
import type { ConfigMessageScanResponseLabelConfig } from '$/../config.schema'
const PossibleReactions = Object.values(Reactions) as string[]
withContext(on, 'messageReactionAdd', async (context, rct, user) => {
if (user.bot) return
const { database: db, logger, config } = context
const { messageScan: msConfig } = config
// If there's no config, we can't do anything
if (!msConfig?.humanCorrections) return
@@ -65,8 +63,8 @@ withContext(on, 'messageReactionAdd', async (context, rct, user) => {
logger.debug(`User ${user.id} is trying to correct the response ${rct.message.id}`)
const handleCorrection = (label: string) =>
handleUserResponseCorrection(context, response, reactionMessage, label, user)
const handleCorrection = (label?: string) =>
handleUserResponseCorrection(context, response, reactionMessage, user, label)
try {
if (reaction.emoji.name === Reactions.train) {
@@ -108,7 +106,7 @@ withContext(on, 'messageReactionAdd', async (context, rct, user) => {
.setCustomId(`${componentPrefix}_cancel`),
new ButtonBuilder()
.setEmoji(Reactions.delete)
.setLabel('Delete (mark as false positive)')
.setLabel('Delete (mark as out of scope)')
.setStyle(ButtonStyle.Danger)
.setCustomId(`${componentPrefix}_delete`),
),
@@ -119,8 +117,8 @@ withContext(on, 'messageReactionAdd', async (context, rct, user) => {
components: rows,
})
} else if (reaction.emoji.name === Reactions.delete) {
await handleCorrection(msConfig.humanCorrections.falsePositiveLabel)
await user.send({ content: 'The response has been deleted and marked as a false positive.' })
await handleCorrection()
await user.send({ content: 'The response has been deleted and marked as out of scope.' })
}
} catch (e) {
logger.error('Failed to correct response:', e)

View File

@@ -1,103 +0,0 @@
import { database, logger } from '$/context'
import { appliedPresets } from '$/database/schemas'
import { applyCommonEmbedStyles } from '$/utils/discord/embeds'
import { on, withContext } from '$/utils/discord/events'
import { removeRolePreset } from '$/utils/discord/rolePresets'
import { lt } from 'drizzle-orm'
import type { Client } from 'discord.js'
export default withContext(on, 'ready', async ({ config, discord, logger }, client) => {
logger.info(`Connected to Discord API, logged in as ${client.user.displayName} (@${client.user.tag})!`)
logger.info(`Bot is in ${client.guilds.cache.size} guilds`)
if (config.stickyMessages)
for (const [guildId, channels] of Object.entries(config.stickyMessages)) {
const guild = await client.guilds.fetch(guildId)
// In case of configuration refresh, this will not be nullable
const oldStore = discord.stickyMessages[guildId]
discord.stickyMessages[guildId] = {}
for (const [channelId, { message, timeout, forceSendTimeout }] of Object.entries(channels)) {
const channel = await guild.channels.fetch(channelId)
if (!channel?.isTextBased())
return void logger.warn(
`Channel ${channelId} in guild ${guildId} is not a text channel, sticky messages will not be sent`,
)
const send = async (forced = false) => {
try {
const msg = await channel.send({
...message,
embeds: message.embeds?.map(it => applyCommonEmbedStyles(it, true, true, true)),
})
const store = discord.stickyMessages[guildId]![channelId]
if (!store) return
await store.currentMessage?.delete().catch()
store.currentMessage = msg
// Clear any remaining timers
clearTimeout(store.timer)
clearTimeout(store.forceTimer)
store.forceTimerActive = store.timerActive = false
if (!forced)
logger.debug(
`Timeout ended for sticky message in channel ${channelId} in guild ${guildId}, channel is inactive`,
)
else
logger.debug(
`Forced send timeout for sticky message in channel ${channelId} in guild ${guildId} ended, channel is too active`,
)
logger.debug(`Sent sticky message to channel ${channelId} in guild ${guildId}`)
} catch (e) {
logger.error(
`Error while sending sticky message to channel ${channelId} in guild ${guildId}:`,
e,
)
}
}
// Set up the store
discord.stickyMessages[guildId]![channelId] = {
forceTimerActive: false,
timerActive: false,
forceTimerMs: forceSendTimeout,
timerMs: timeout,
send,
// If the store exists before the configuration refresh, take its current message
currentMessage: oldStore?.[channelId]?.currentMessage,
}
// Send a new sticky message immediately, as well as deleting the old/outdated message, if it exists
await send()
}
}
if (config.rolePresets) {
removeExpiredPresets(client)
setTimeout(() => removeExpiredPresets(client), config.rolePresets.checkExpiredEvery)
}
})
const removeExpiredPresets = async (client: Client) => {
logger.debug('Checking for expired role presets...')
const expireds = await database.query.appliedPresets.findMany({
where: lt(appliedPresets.until, Math.floor(Date.now() / 1000)),
})
for (const expired of expireds)
try {
const guild = await client.guilds.fetch(expired.guildId)
const member = await guild.members.fetch(expired.memberId)
logger.debug(`Removing role preset for ${expired.memberId} in ${expired.guildId}`)
await removeRolePreset(member, expired.preset)
} catch (e) {
logger.error(`Error while removing role preset for ${expired.memberId} in ${expired.guildId}: ${e}`)
}
}

View File

@@ -0,0 +1,6 @@
import { on, withContext } from '$/utils/discord/events'
export default withContext(on, 'ready', async ({ logger }, client) => {
logger.info(`Connected to Discord API, logged in as ${client.user.displayName} (@${client.user.tag})!`)
logger.info(`Bot is in ${client.guilds.cache.size} guilds`)
})

View File

@@ -0,0 +1,42 @@
import { type Client, DiscordAPIError } from 'discord.js'
import { and, eq, lt } from 'drizzle-orm'
import { database, logger } from '$/context'
import { appliedPresets } from '$/database/schemas'
import { on, withContext } from '$/utils/discord/events'
import { removeRolePreset } from '$/utils/discord/rolePresets'
export default withContext(on, 'ready', async ({ config }, client) => {
if (config.rolePresets) {
removeExpiredPresets(client)
setInterval(() => removeExpiredPresets(client), config.rolePresets.checkExpiredEvery)
}
})
async function removeExpiredPresets(client: Client) {
logger.debug('Checking for expired role presets...')
const expireds = await database.query.appliedPresets.findMany({
where: lt(appliedPresets.until, Math.floor(Date.now() / 1000)),
})
for (const expired of expireds) {
try {
logger.debug(`Removing role preset for ${expired.memberId} in ${expired.guildId}`)
const guild = await client.guilds.fetch(expired.guildId)
const member = await guild.members.fetch(expired.memberId)
await removeRolePreset(member, expired.preset)
} catch (e) {
// Unknown Member: https://discord.com/developers/docs/topics/opcodes-and-status-codes#json
if (!(e instanceof DiscordAPIError) || e.code !== 10007) {
logger.error(`Error while removing role preset for ${expired.memberId} in ${expired.guildId}: ${e}`)
continue
}
}
await database
.delete(appliedPresets)
.where(and(eq(appliedPresets.guildId, expired.guildId), eq(appliedPresets.memberId, expired.memberId)))
}
}

View File

@@ -0,0 +1,68 @@
import { applyCommonEmbedStyles } from '$/utils/discord/embeds'
import { on, withContext } from '$/utils/discord/events'
export default withContext(on, 'ready', async ({ config, discord, logger }, client) => {
if (config.stickyMessages)
for (const [guildId, channels] of Object.entries(config.stickyMessages)) {
const guild = await client.guilds.fetch(guildId)
// In case of configuration refresh, this will not be nullable
const oldStore = discord.stickyMessages[guildId]
discord.stickyMessages[guildId] = {}
for (const [channelId, { message, timeout, forceSendTimeout }] of Object.entries(channels)) {
const channel = await guild.channels.fetch(channelId)
if (!channel?.isTextBased())
return void logger.warn(
`Channel ${channelId} in guild ${guildId} is not a text channel, sticky messages will not be sent`,
)
// Set up the store
// biome-ignore lint/suspicious/noAssignInExpressions: don't care
const store = (discord.stickyMessages[guildId]![channelId] = {
forceTimerActive: false,
timerActive: false,
forceTimerMs: forceSendTimeout,
timerMs: timeout,
async send() {
try {
await Promise.all([
channel
.send({
...message,
embeds: message.embeds?.map(it => applyCommonEmbedStyles(it, true, true, true)),
})
.then(msg => {
this.currentMessage = msg
logger.debug(`Sent sticky message to channel ${channelId} in guild ${guildId}`)
}),
this.currentMessage
?.delete()
?.then(() =>
logger.debug(
`Deleted old sticky message from channel ${channelId} in guild ${guildId}`,
),
),
])
} catch (e) {
logger.error(
`Error while managing sticky message of channel ${channelId} in guild ${guildId}:`,
e,
)
} finally {
// Clear any remaining timers
clearTimeout(this.timer)
clearTimeout(this.forceTimer)
this.forceTimerActive = this.timerActive = false
logger.debug(`Cleared sticky message timer for channel ${channelId} in guild ${guildId}`)
}
},
// If the store exists before the configuration refresh, take its current message
currentMessage: oldStore?.[channelId]?.currentMessage,
})
// Send a new sticky message immediately, as well as deleting the old/outdated message, if it exists
await store.send()
}
}
})

View File

@@ -1,5 +1,5 @@
import { api, discord, logger } from '$/context'
import { getMissingEnvironmentVariables } from '@revanced/bot-shared'
import { api, discord, logger } from '$/context'
import './events/register'

View File

@@ -1,5 +1,5 @@
import type { ClientWebSocketEvents } from '@revanced/bot-api'
import * as context from '../../context'
import type { ClientWebSocketEvents } from '@revanced/bot-api'
const { client } = context.api

View File

@@ -1,5 +1,5 @@
import { DefaultEmbedColor, ReVancedLogoURL } from '$/constants'
import { type APIEmbed, EmbedBuilder, type EmbedField, type JSONEncodable, type User } from 'discord.js'
import { DefaultEmbedColor, ReVancedLogoURL } from '$/constants'
import type { ConfigMessageScanResponseMessage } from '../../../config.schema'
export const createErrorEmbed = (title: string | null, description?: string) =>

View File

@@ -1,9 +1,9 @@
import { type Response, responses } from '$/database/schemas'
import type { Config, ConfigMessageScanResponse, ConfigMessageScanResponseLabelConfig } from 'config.schema'
import { ButtonStyle, ComponentType } from 'discord.js'
import type { APIActionRowComponent, APIButtonComponent, Message, PartialUser, User } from 'discord.js'
import { eq } from 'drizzle-orm'
import { type Response, responses } from '$/database/schemas'
import { createMessageScanResponseEmbed } from './embeds'
import type { Config, ConfigMessageScanResponse, ConfigMessageScanResponseLabelConfig } from 'config.schema'
import type { APIActionRowComponent, APIButtonComponent, Message, PartialUser, User } from 'discord.js'
export const getResponseFromText = async (
content: string,
@@ -17,7 +17,7 @@ export const getResponseFromText = async (
type ResponseConfig = Awaited<ReturnType<typeof getResponseFromText>>
let responseConfig: Omit<ResponseConfig, 'triggers'> & { triggers?: ResponseConfig['triggers'] } = {
triggers: undefined,
response: null,
response: null!,
}
const firstLabelIndexes: number[] = []
@@ -143,15 +143,22 @@ export const handleUserResponseCorrection = async (
{ api, database: db, config: { messageScan: msConfig }, logger }: typeof import('$/context'),
response: Response,
reply: Message,
label: string,
user: User | PartialUser,
label?: string,
) => {
if (!label) {
await Promise.all([reply.delete(), api.client.trainMessage(response.content, label)]).finally(() =>
logger.debug(`User ${user.id} trained message ${response.replyId} as out of scope`),
)
return
}
const correctLabelResponse = msConfig!.responses!.find(r =>
r.triggers.text!.some(t => 'label' in t && t.label === label),
)
if (!correctLabelResponse) throw new Error('Cannot find label config for the selected label')
if (!correctLabelResponse.response) return void (await reply.delete())
if (response.label !== label) {
db.update(responses)
@@ -168,12 +175,12 @@ export const handleUserResponseCorrection = async (
}))
}
await api.client.trainMessage(response.content, label)
logger.debug(`User ${user.id} trained message ${response.replyId} as ${label} (positive)`)
await reply.edit({
components: [],
})
await Promise.all([
api.client.trainMessage(response.content, label),
reply.edit({
components: [],
}),
]).finally(() => logger.debug(`User ${user.id} trained message ${response.replyId} as ${label}`))
}
export const createMessageScanResponseComponents = (reply: Message<true>) => [

View File

@@ -1,7 +1,7 @@
import { config, logger } from '$/context'
import decancer from 'decancer'
import type { CommandInteraction, EmbedBuilder, Guild, GuildMember, Message, User } from 'discord.js'
import { config, logger } from '$/context'
import { applyReferenceToModerationActionEmbed, createModerationActionEmbed } from './embeds'
import type { CommandInteraction, EmbedBuilder, Guild, GuildMember, Message, User } from 'discord.js'
const PresetLogAction = {
apply: 'Applied role preset to',
@@ -57,7 +57,7 @@ export const cureNickname = async (member: GuildMember) => {
cured =
member.user.username.length >= 3
? member.user.username
: config.moderation?.cure?.defaultName ?? 'Server member'
: (config.moderation?.cure?.defaultName ?? 'Server member')
if (cured.toLowerCase() === name.toLowerCase()) return

View File

@@ -1,7 +1,7 @@
import { and, eq } from 'drizzle-orm'
import { config, database } from '$/context'
import { appliedPresets } from '$/database/schemas'
import type { GuildMember } from 'discord.js'
import { and, eq } from 'drizzle-orm'
// TODO: Fix this type
type PresetKey = string

View File

@@ -1,25 +1,44 @@
import parse from 'parse-duration'
import { Duration, DurationFormatter } from '@sapphire/duration'
const defaultUnitValue = parse['']!
const fmt = new DurationFormatter({
year: {
DEFAULT: 'y',
},
month: {
DEFAULT: 'M',
},
week: {
DEFAULT: 'w',
},
day: {
DEFAULT: 'd',
},
hour: {
DEFAULT: 'h',
},
minute: {
DEFAULT: 'm',
},
second: {
DEFAULT: 's',
},
})
export const parseDuration = (duration: string, defaultUnit?: parse.Units) => {
if (defaultUnit) parse[''] = parse[defaultUnit]!
return (
// biome-ignore lint/suspicious/noAssignInExpressions: Expression is ignored
// biome-ignore lint/style/noCommaOperator: The last expression (parse call) is returned, it is not confusing
(parse[''] = defaultUnitValue), parse(duration, 'ms') ?? Number.NaN
)
export const parseDuration = (duration: string, defaultUnit = 's') => {
// adds default unit to the end of the string if it doesn't have a unit
// 100 -> 100s
// 10m100 -> 10m100s
// biome-ignore lint/style/noParameterAssign: this is fine
if (/\d$/.test(duration)) duration += defaultUnit
return new Duration(duration).offset
}
export const durationToString = (duration: number) => {
if (duration === 0) return '0s'
const days = Math.floor(duration / (24 * 60 * 60 * 1000))
const hours = Math.floor((duration % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000))
const minutes = Math.floor((duration % (60 * 60 * 1000)) / (60 * 1000))
const seconds = Math.floor((duration % (60 * 1000)) / 1000)
return `${days ? `${days}d` : ''}${hours ? `${hours}h` : ''}${minutes ? `${minutes}m` : ''}${
seconds ? `${seconds}s` : ''
}`
return fmt.format(duration, undefined, {
left: '',
})
}
export function isSafeTimeoutDuration(duration: number) {
return duration > 0 && duration < 2 ** 31 - 1
}

2446
bun.lock Normal file

File diff suppressed because it is too large Load Diff

BIN
bun.lockb

Binary file not shown.

View File

@@ -1,7 +1,7 @@
# 🏗️ Setting up the development environment
> [!IMPORTANT]
> **This project uses [Bun](https://bun.sh) to run and bundle the code.**
> [!IMPORTANT]
> **This project uses [Bun](https://bun.sh) to run and bundle the code.**
> Compatibility with other runtimes (Node.js, Deno, ...) are not guaranteed and most package scripts won't work.
To start developing, you'll need to set up the development environment first.
@@ -11,8 +11,8 @@ To start developing, you'll need to set up the development environment first.
2. Clone the mono-repository
```sh
git clone https://github.com/revanced/revanced-helper.git &&
cd revanced-helper
git clone https://github.com/revanced/revanced-bots.git &&
cd revanced-bots
```
3. Install dependencies

View File

@@ -1,12 +1,16 @@
{
"name": "revanced-helper",
"name": "revanced-bots",
"description": "🤖 Bots assisting ReVanced on multiple platforms",
"private": true,
"version": "0.0.0",
"license": "GPL-3.0-or-later",
"type": "module",
"author": "Palm <contact@palmdevs.me> (https://palmdevs.me)",
"workspaces": ["packages/*", "apis/*", "bots/*"],
"workspaces": [
"packages/*",
"apis/*",
"bots/*"
],
"scripts": {
"build:all": "turbo run build",
"build:packages": "turbo build --filter=\"./packages/*\"",
@@ -15,37 +19,37 @@
"flint:check": "biome check .",
"clint": "commitlint --edit"
},
"homepage": "https://github.com/revanced/revanced-helper#readme",
"homepage": "https://github.com/revanced/revanced-bots#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/revanced/revanced-helper.git"
"url": "git+https://github.com/revanced/revanced-bots.git"
},
"bugs": {
"url": "https://github.com/revanced/revanced-helper/issues"
"url": "https://github.com/revanced/revanced-bots/issues"
},
"contributors": [
"Palm <contact@palmdevs.me> (https://palmdevs.me)",
"ReVanced <nosupport@revanced.app> (https://revanced.app)"
],
"packageManager": "bun@1.1.20",
"packageManager": "bun@1.2.17",
"devDependencies": {
"@anolilab/multi-semantic-release": "^1.1.3",
"@biomejs/biome": "^1.9.3",
"@codedependant/semantic-release-docker": "^5.0.3",
"@commitlint/cli": "^19.5.0",
"@commitlint/config-conventional": "^19.5.0",
"@anolilab/multi-semantic-release": "^2.0.3",
"@biomejs/biome": "^2.2.4",
"@codedependant/semantic-release-docker": "^5.1.1",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@saithodev/semantic-release-backmerge": "^4.0.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/exec": "^7.1.0",
"@semantic-release/git": "^10.0.1",
"@tsconfig/strictest": "^2.0.5",
"@types/bun": "^1.1.11",
"conventional-changelog-conventionalcommits": "^7.0.2",
"lefthook": "^1.7.21",
"@types/bun": "^1.2.21",
"conventional-changelog-conventionalcommits": "^9.1.0",
"lefthook": "^1.13.0",
"portainer-service-webhook": "https://github.com/newarifrh/portainer-service-webhook#v1",
"semantic-release": "^24.1.2",
"turbo": "^2.1.3",
"typescript": "^5.6.3"
"semantic-release": "^24.2.8",
"turbo": "^2.5.6",
"typescript": "^5.9.2"
},
"trustedDependencies": [
"@biomejs/biome",
@@ -54,8 +58,6 @@
"lefthook"
],
"patchedDependencies": {
"@semantic-release/npm@12.0.1": "patches/@semantic-release%2Fnpm@12.0.1.patch",
"drizzle-kit@0.22.8": "patches/drizzle-kit@0.22.8.patch",
"decancer@3.2.4": "patches/decancer@3.2.4.patch"
"@semantic-release/npm@12.0.2": "patches/@semantic-release%2Fnpm@12.0.2.patch"
}
}

View File

@@ -13,7 +13,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/revanced/revanced-helper.git",
"url": "git+https://github.com/revanced/revanced-bots.git",
"directory": "packages/api"
},
"author": "Palm <contact@palmdevs.me> (https://palmdevs.me)",
@@ -23,15 +23,15 @@
],
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/revanced/revanced-helper/issues"
"url": "https://github.com/revanced/revanced-bots/issues"
},
"homepage": "https://github.com/revanced/revanced-helper#readme",
"homepage": "https://github.com/revanced/revanced-bots#readme",
"dependencies": {
"@revanced/bot-shared": "workspace:*",
"ws": "^8.18.0"
"ws": "^8.18.3"
},
"devDependencies": {
"@types/ws": "^8.5.12",
"@types/ws": "^8.18.1",
"typed-emitter": "^2.1.0"
}
}

View File

@@ -101,7 +101,7 @@ export default class Client {
return packet
}
async trainMessage(text: string, label: string) {
async trainMessage(text: string, label?: string) {
this.#throwIfNotReady()
this.ws.send({

View File

@@ -1,16 +1,16 @@
import { EventEmitter } from 'events'
import {
type ClientOperation,
DisconnectReason,
type Packet,
ServerOperation,
deserializePacket,
isServerPacket,
type Packet,
ServerOperation,
serializePacket,
uncapitalize,
} from '@revanced/bot-shared'
import type TypedEmitter from 'typed-emitter'
import { EventEmitter } from 'events'
import { type RawData, WebSocket } from 'ws'
import type TypedEmitter from 'typed-emitter'
/**
* The class that handles the WebSocket connection to the server.
@@ -207,7 +207,7 @@ export class ClientWebSocketManager {
protected _toBuffer(data: RawData) {
if (data instanceof Buffer) return data
if (data instanceof ArrayBuffer) return Buffer.from(data)
return Buffer.concat(data)
return Buffer.concat(data as Uint8Array[])
}
}

View File

@@ -1,3 +1,3 @@
export { default as Client } from './Client'
export * from './Client'
export { default as Client } from './Client'
export * from './ClientWebSocket'

View File

@@ -16,7 +16,7 @@
},
"repository": {
"type": "git",
"url": "git+https://github.com/revanced/revanced-helper.git",
"url": "git+https://github.com/revanced/revanced-bots.git",
"directory": "packages/shared"
},
"author": "Palm <contact@palmdevs.me> (https://palmdevs.me)",
@@ -26,13 +26,13 @@
],
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/revanced/revanced-helper/issues"
"url": "https://github.com/revanced/revanced-bots/issues"
},
"homepage": "https://github.com/revanced/revanced-helper#readme",
"homepage": "https://github.com/revanced/revanced-bots#readme",
"dependencies": {
"bson": "^6.8.0",
"chalk": "^5.3.0",
"bson": "^6.10.4",
"chalk": "^5.6.2",
"tracer": "^1.3.0",
"valibot": "^0.30.0"
"valibot": "^1.1.0"
}
}

View File

@@ -1,18 +1,16 @@
import {
url,
type AnySchema,
type BooleanSchema,
type NullSchema,
type ObjectSchema,
type Output,
array,
boolean,
custom,
enum_,
type InferOutput,
null_,
object,
optional,
parse,
special,
pipe,
string,
url,
// merge
} from 'valibot'
import DisconnectReason from '../constants/DisconnectReason'
@@ -21,7 +19,7 @@ import { ClientOperation, Operation, ServerOperation } from '../constants/Operat
/**
* Schema to validate packets
*/
export const PacketSchema = special<Packet>(input => {
export const PacketSchema = custom<Packet>(input => {
if (
typeof input === 'object' &&
input &&
@@ -51,7 +49,7 @@ export const PacketDataSchemas = {
labels: array(
object({
name: string(),
confidence: special<number>(input => typeof input === 'number' && input >= 0 && input <= 1),
confidence: custom<number>(input => typeof input === 'number' && input >= 0 && input <= 1),
}),
),
}),
@@ -70,17 +68,13 @@ export const PacketDataSchemas = {
text: string(),
}),
[ClientOperation.ParseImage]: object({
image_url: string([url()]),
image_url: pipe(string(), url()),
}),
[ClientOperation.TrainMessage]: object({
text: string(),
label: string(),
label: optional(string()),
}),
} as const satisfies Record<
Operation,
// biome-ignore lint/suspicious/noExplicitAny: This is a schema, it's not possible to type it
ObjectSchema<any> | AnySchema | NullSchema | BooleanSchema
>
} as const
export type Packet<TOp extends Operation = Operation> = TOp extends ServerOperation
? PacketWithSequenceNumber<TOp>
@@ -88,6 +82,6 @@ export type Packet<TOp extends Operation = Operation> = TOp extends ServerOperat
type PacketWithSequenceNumber<TOp extends Operation> = {
op: TOp
d: Output<(typeof PacketDataSchemas)[TOp]>
d: InferOutput<(typeof PacketDataSchemas)[TOp]>
s: number
}

View File

@@ -1,5 +1,5 @@
import { Chalk, supportsColor, supportsColorStderr } from 'chalk'
import { type Tracer, colorConsole, console as uncoloredConsole } from 'tracer'
import { colorConsole, type Tracer, console as uncoloredConsole } from 'tracer'
const chalk = new Chalk()
const DefaultConfig = {

View File

@@ -1,7 +1,7 @@
import * as BSON from 'bson'
import { parse } from 'valibot'
import type { Operation } from '../constants'
import { type Packet, PacketSchema } from '../schemas'
import type { Operation } from '../constants'
/**
* Compresses a packet into a buffer
@@ -18,6 +18,6 @@ export function serializePacket<TOp extends Operation>(packet: Packet<TOp>) {
* @returns A packet
*/
export function deserializePacket(buffer: Buffer) {
const data = BSON.deserialize(buffer)
const data = BSON.deserialize(buffer as Uint8Array)
return parse(PacketSchema, data) as Packet
}

View File

@@ -1,12 +1,3 @@
diff --git a/node_modules/@semantic-release/npm/.bun-tag-3853154e196b7721 b/.bun-tag-3853154e196b7721
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/node_modules/@semantic-release/npm/.bun-tag-550461f23a8ec245 b/.bun-tag-550461f23a8ec245
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/node_modules/@semantic-release/npm/.bun-tag-c9c8130945517add b/.bun-tag-c9c8130945517add
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/lib/prepare.js b/lib/prepare.js
index 3e76bec44cf595a1b4141728336bed904d4d518d..4b25ca64879bbee2a600f2b23b738c86136ad9c6 100644
--- a/lib/prepare.js

View File

@@ -1,13 +0,0 @@
diff --git a/src/lib.js b/src/lib.js
index de45d7dbe82975b09eff3742d0718accae2107fc..0575daa03dfabdd5c96928458ff4270cb8f7188a 100644
--- a/src/lib.js
+++ b/src/lib.js
@@ -42,7 +42,7 @@ function isMusl() {
}
function getBinding(name) {
- const path = join(__dirname, '..', `decancer.${name}.node`)
+ const path = join(import.meta.dir, '..', `decancer.${name}.node`)
return require(existsSync(path) ? path : `@vierofernando/decancer-${name}`)
}

View File

@@ -1,21 +0,0 @@
diff --git a/bin.cjs b/bin.cjs
index 142ed9c20f28dc1080bebfb52325fa308c6cb771..9d3bea0787f6c05df11567c6821bc85743286340 100644
--- a/bin.cjs
+++ b/bin.cjs
@@ -22053,7 +22053,7 @@ var init_sqliteImports = __esm({
const { unregister } = await safeRegister();
for (let i2 = 0; i2 < imports.length; i2++) {
const it = imports[i2];
- const i0 = require(`${it}`);
+ const i0 = await import(`${it}`);
const prepared = prepareFromExports3(i0);
tables.push(...prepared.tables);
}
@@ -129572,6 +129572,7 @@ var generateCommand = new Command("generate").option("--dialect <dialect>", "Dat
} else {
assertUnreachable(dialect7);
}
+ process.exit(0);
});
var migrateCommand = new Command("migrate").option(
"--config <config>",

View File

@@ -11,55 +11,52 @@ const Options = {
prerelease: true,
},
],
plugins:
process.env['RELEASE_WORKFLOW_STEP'] !== 'publish'
? [
[
'@semantic-release/commit-analyzer',
{
releaseRules: [{ type: 'build', scope: 'Needs bump', release: 'patch' }],
},
],
'@semantic-release/release-notes-generator',
'@semantic-release/changelog',
[
'@semantic-release/npm',
{
npmPublish: false,
},
],
[
'@semantic-release/git',
{
assets: ['CHANGELOG.md', 'package.json', '../../bun.lockb'],
},
],
[
'@semantic-release/github',
{
assets: [
{
path: 'dist/*',
},
],
successComment: false,
},
],
// This unfortunately has to run multiple times, even though it needs to run only once.
[
'@saithodev/semantic-release-backmerge',
{
backmergeBranches: [
{
from: 'main',
to: 'dev',
},
],
clearWorkspace: true,
},
],
]
: [],
plugins: [
[
'@semantic-release/commit-analyzer',
{
releaseRules: [{ type: 'build', scope: 'Needs bump', release: 'patch' }],
},
],
'@semantic-release/release-notes-generator',
'@semantic-release/changelog',
[
'@semantic-release/npm',
{
npmPublish: false,
},
],
[
'@semantic-release/git',
{
assets: ['CHANGELOG.md', 'package.json', '../../bun.lockb'],
},
],
[
'@semantic-release/github',
{
assets: [
{
path: 'dist/*',
},
],
successComment: false,
},
],
// This unfortunately has to run multiple times, even though it needs to run only once.
[
'@saithodev/semantic-release-backmerge',
{
backmergeBranches: [
{
from: 'main',
to: 'dev',
},
],
clearWorkspace: true,
},
],
],
}
/**
@@ -70,6 +67,6 @@ export default function defineSubprojectReleaseConfig(subprojectOptions) {
return {
...Options,
...subprojectOptions,
plugins: [...(subprojectOptions.plugins || []), ...(Options.plugins || [])],
plugins: [...(Options.plugins || []), ...(subprojectOptions.plugins || [])],
}
}