Compare commits

...

283 Commits

Author SHA1 Message Date
Jacob Nguyen
4722849f56 Merge branch 'main' into fix/dispose 2025-03-10 00:49:21 -05:00
renovate[bot]
513ac8edf4 chore(deps): lock file maintenance (#391)
Some checks failed
NPM / Publish / test-and-publish (push) Has been cancelled
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 00:46:28 -05:00
Jacob Nguyen
3673263f3a s 2025-03-10 00:41:24 -05:00
Jacob Nguyen
24ec4d6ad6 testbot 2025-03-10 00:39:37 -05:00
Jacob Nguyen
27d13f1ea5 Merge branch 'main' into fix/dispose 2025-03-10 00:15:34 -05:00
github-actions[bot]
81a0180d05 chore(main): release 4.2.4 (#396)
Some checks failed
Continuous Delivery / Publishing Dev (push) Has been cancelled
NPM / Publish / test-and-publish (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-05 21:24:41 -06:00
Jacob Nguyen
89d7409536 fix: flat autocomplete (#395)
* first

* fix
2025-03-05 21:22:54 -06:00
github-actions[bot]
aa802f761e chore(main): release 4.2.3 (#394)
Some checks failed
Continuous Delivery / Publishing Dev (push) Has been cancelled
NPM / Publish / test-and-publish (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-03-03 21:48:03 -06:00
Jacob Nguyen
2414992b73 fix: autocomplete sdt.module not present (#393) 2025-03-03 21:45:18 -06:00
kingomes
70c6236802 Update README.md (#392)
Some checks failed
NPM / Publish / test-and-publish (push) Has been cancelled
2025-02-15 11:09:34 -06:00
Jacob Nguyen
ed5bb20c60 asdf 2025-02-07 02:01:40 -06:00
Jacob Nguyen
0d77878dfb Merge branch 'main' into fix/dispose 2025-02-07 01:56:30 -06:00
Jacob Nguyen
8ac7b60b59 Delete yarn.lock 2025-02-07 01:56:10 -06:00
Jacob Nguyen
1f25aa64b9 plock
Some checks failed
Continuous Delivery / Publishing Dev (push) Has been cancelled
NPM / Publish / test-and-publish (push) Has been cancelled
2025-02-07 01:45:57 -06:00
Jacob Nguyen
7cddee30aa lock 2025-02-07 01:44:49 -06:00
renovate[bot]
e7286eee9f chore(deps): lock file maintenance (#331)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-07 01:43:27 -06:00
Jacob Nguyen
a67450328e fuckyarn 2025-02-07 00:50:17 -06:00
jacoobes
926d531863 sdfds 2025-02-03 18:34:26 -06:00
jacoobes
ef3d3d71a0 disposalfixe 2025-02-03 18:33:56 -06:00
Jacob Nguyen
47401f46a3 Update README.md
Some checks failed
NPM / Publish / test-and-publish (push) Has been cancelled
2025-02-03 11:47:49 -06:00
github-actions[bot]
1059065980 chore(main): release 4.2.2 (#388)
Some checks failed
NPM / Publish / test-and-publish (push) Waiting to run
Continuous Delivery / Publishing Dev (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-02-02 19:40:19 -06:00
Jacob Nguyen
974c30fa6c fix: faster autocomplete lookup (#387)
* fix:faster-autocmp

* fixinitializing

* fix

* fixonwindows

* unconsole
2025-02-02 19:37:59 -06:00
Jacob Nguyen
3a569726d8 docs: Sincetagsandmoredocumentation (#385)
Some checks failed
Continuous Delivery / Publishing Dev (push) Has been cancelled
NPM / Publish / test-and-publish (push) Has been cancelled
* commit

* docs
2025-01-27 18:29:20 -06:00
Ararou
1b7f2a49a8 change formatting of example projects (#384)
Some checks failed
NPM / Publish / test-and-publish (push) Has been cancelled
2025-01-25 22:26:31 -06:00
github-actions[bot]
97fa2a2d78 chore(main): release 4.2.1 (#383)
Some checks failed
Continuous Delivery / Publishing Dev (push) Has been cancelled
NPM / Publish / test-and-publish (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-24 16:38:33 -06:00
Jacob Nguyen
a52ad270d8 fix: context-interactions error (#382)
Some checks are pending
Continuous Delivery / Publishing Dev (push) Waiting to run
NPM / Publish / test-and-publish (push) Waiting to run
2025-01-24 10:37:51 -06:00
github-actions[bot]
3f703c17b8 chore(main): release 4.2.0 (#380)
Some checks failed
Continuous Delivery / Publishing Dev (push) Has been cancelled
NPM / Publish / test-and-publish (push) Has been cancelled
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-18 12:00:13 -06:00
Jacob Nguyen
f9e7eaf92d feat: 4.2.0 load multiple directories & handleModuleErrors (#378)
* error-handling-draft

* feat: array based module loading (#379)

Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>

* Update utility.ts

* Update sern.ts

* describesemanticsbetter

---------

Co-authored-by: Duro <davidwright13503@gmail.com>
2025-01-18 11:47:51 -06:00
github-actions[bot]
52e145600d chore(main): release 4.1.1 (#377)
Some checks failed
Continuous Delivery / Publishing Dev (push) Has been cancelled
NPM / Publish / test-and-publish (push) Has been cancelled
* chore(main): release 4.1.1

* Update CHANGELOG.md

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2025-01-13 18:59:14 -06:00
Jacob Nguyen
59d08ef207 fix: remove rxjs (#376)
Some checks are pending
Continuous Delivery / Publishing Dev (push) Waiting to run
NPM / Publish / test-and-publish (push) Waiting to run
* firstcommit

* removerxjs

* document-task

* documentation+clean

* fixregres

* fix+regress

* fix+regres+errorhandling
2025-01-13 10:33:53 -06:00
Jacob Nguyen
7deb79e907 Delete .github/workflows/continuous-integration.yml
Some checks failed
NPM / Publish / test-and-publish (push) Has been cancelled
2025-01-11 13:59:20 -06:00
Jacob Nguyen
f2d4b5bda1 cleanup-tests
Some checks failed
NPM / Publish / test-and-publish (push) Has been cancelled
2025-01-07 17:33:33 -06:00
Glitch
a575b3ed74 update license year (#375)
Some checks failed
NPM / Publish / test-and-publish (push) Waiting to run
Continuous Delivery / Publishing Dev (push) Has been cancelled
2025-01-06 17:16:31 -06:00
github-actions[bot]
2042559b4d chore(main): release 4.1.0 (#374)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-06 17:02:24 -06:00
Jacob Nguyen
220a60ecf8 feat: moduleinfo-in-eventplugins (#373) 2025-01-06 17:00:02 -06:00
Glitch
55715d5659 fix: update github username (#371)
Some checks failed
NPM / Publish / test-and-publish (push) Has been cancelled
2024-11-18 16:34:27 -06:00
github-actions[bot]
d0c3b7469e chore(main): release 4.0.3 (#370)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-10-06 11:53:30 -05:00
Jacob Nguyen
eabfb81819 fix: async presence (#369)
* fix: async presence

* fixes to typings
2024-10-06 11:51:07 -05:00
Duro
1789ccb2f2 fix: fix eventModule typing for Discord events (#368) 2024-08-19 11:18:13 -05:00
github-actions[bot]
25c5891ade chore(main): release 4.0.2 (#367)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-08-12 21:30:44 -05:00
jacob
2106cdc1d0 fix: type issue 2024-08-12 21:28:22 -05:00
Jacob Nguyen
61e82fdc7b refactor: remove ts-results-es (#366)
* remove tsresultses

* remove test since it uses external api

* opt in for simpler

* add more debug information

Signed-off-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>

* add more debug information

Signed-off-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>

* clean up if else

---------

Signed-off-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2024-08-11 11:07:44 -05:00
xxDeveloper
3755b95b1a chore: Update LICENSE year (#365) 2024-08-06 10:56:25 -05:00
Jacob Nguyen
06807ea77f Update README.md 2024-07-19 01:32:22 -05:00
github-actions[bot]
3fd3f1c236 chore(main): release 4.0.1 (#364)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-18 22:55:59 -05:00
Peter-MJ-Parker
92623d2914 fix: add SDT typings to autocomplete commands (#363) 2024-07-18 22:54:23 -05:00
github-actions[bot]
a91f260a86 chore(main): release 4.0.0 (#362)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-07-18 17:01:47 -05:00
Jacob Nguyen
dda0e3395b chore: release 4.0.0
Release-As: 4.0.0
2024-07-18 17:00:33 -05:00
Jacob Nguyen
9a8904f5ae feat: v4 (#361)
* step 1

* Refactorings

* command modules do not depend on anything but itself

* tearing it up

* Remove module store, manager, and Intializable type

* consolidate interfaces in single file

* consolidate default services in single file

* TEAR IT UP

* fix text compile

* the end of sern init??

* Presence namespaced types removed

* internal namespace

* clean up dependencies

* fix test

* fix circular dependency

* still broken but progress

* remove barrel for core/structs

* reffactor

* refactor allat

* more refactoring

* prototyping linking static handler

* cleanup tests, codegen, and importing handler

* some refactor

* generify partition

* for now copy paste new ioc system

* removeiti

* fdsfD

* ensure container is init'd

* fix absPath gen

* working on bun compat

* refactor and clean up and reenter v3 module loading

* dsfsd

* refactor, add cron types, reinstante module loader

* ready handler revamped so much cleaner

* fdssdf

* refactor deps list

* add more tests, polish up ioc

* up to speed with event modules

* i think cron works

* cron works now, poc

* ksdjkldsfld

* updating ioc api, experimenting with cron

* save b4 thunder and lightning

* plugin data reduction & args changes

* freeze module after plugins, updateModule, and more

* simplify plugin args and prepare for reduction among plugins

* add deps to plugin calls and execute

* plugin system loking better, tbd type

* porg

* initplugins inject deps, inconspicuos

* fix faiklling test

* fix initPlugins not reassigning

* parsingParams kinda

* proper mapping

* dynamic customIds

* handling customId params working

* testing n shi

* inlineinignsd

* consolidate fmt

* once on eventModules

* refact,simplf

* readd vitest and Asset fn

* fix typings

* assets fn complete

* more intuitive context.options and Asset typings

* add init hooks not firing

* -file,-updateModule,publish?

* fix: ioc deps not created correctly

* documentation, add json for Asset

* remove asset

* ss

* finish ioc transition

* nvm, now i did

* s

* update locals api, docs, tests

* fix tests

* fix up tests and cleanup

* fix

* Update src/core/functions.ts

Co-authored-by: Evo <85353424+EvolutionX-10@users.noreply.github.com>

* better documentation

* temp fix

* namespace presence types again

* revising cron modules and better error messages

* scheduler ids

* more descriptive errors

* refactor to not type leak and job cancellation

* refactor n better signatures for task scheduler

* documentation

* fix swap not accepting functions

* change task signature

---------

Co-authored-by: Evo <85353424+EvolutionX-10@users.noreply.github.com>
2024-07-18 16:54:55 -05:00
Duro
04c4625bfa docs: change @param wrapper -> @param maybeWrapper for Sern.init (#360)
change `@param wrapper` -> `@param maybeWrapper` for typedoc fix
2024-05-14 21:52:24 -05:00
Jacob Nguyen
91b3768e37 bump tsresults 2024-03-21 10:46:19 -05:00
renovate[bot]
d6f49d1d97 chore(deps): update actions/setup-node digest to 1a4442c (#354)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2024-03-21 10:35:43 -05:00
renovate[bot]
8ecd30cf18 chore(deps): update actions/checkout digest to f43a0e5 (#353)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2024-03-19 10:22:39 -05:00
github-actions[bot]
a19edaf883 chore(main): release 3.3.4 (#359)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-03-18 01:57:13 -05:00
Jacob Nguyen
90e55dfa14 fix: sern emitter err (#358)
* prep for fix

* fix ? (not tested

* fix error event not emitting payload
2024-03-18 01:47:14 -05:00
github-actions[bot]
2106522812 chore(main): release 3.3.3 (#357)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-02-24 18:35:24 -06:00
Jacob Nguyen
ce8c4bf649 fix: typings and cleanup (#356)
* fix typings and cleanup

* import type

* rm unused import
2024-02-18 09:34:53 -06:00
jacob
e89b918390 dfs 2024-02-17 11:40:19 -06:00
jacob
f8b69ae542 stuff 2024-02-17 11:38:37 -06:00
Jacob Nguyen
48f9f6ec16 fix: rm deprecated class modules, clean up, rm indirection (#355)
* refactor: rm deprecations, clean up, rm indirection

* fix: singleton init not being fired when inserting function

* refactor and generic internal add

* deprecate a few things that i impusively added lol
2024-02-17 11:35:53 -06:00
jacob
86ebb221ed deprecate a few things that i impusively added lol 2024-02-15 12:36:00 -06:00
jacob
4efdbb21fb refactor and generic internal add 2024-02-15 12:26:34 -06:00
jacob
07b11b357b fix: singleton init not being fired when inserting function 2024-02-14 15:58:50 -06:00
jacob
ac7f47c590 refactor: rm deprecations, clean up, rm indirection 2024-02-12 21:47:35 -06:00
45cbda7b42 refactor: cleanup (#348)
* some wip code

Co-authored-by: Jacob Nguyen <jacoobes@users.noreply.github.com>

* general idea

* style

* making shrimple truly optional

* got optional localizer working

* proposing api notation?

* prepare for localization map

* add localsFor

* merge some internals

* boss call

* add test for init functionality

* add documentation

* inline and cleanup

* feat: logging for experimental json loading

* loosen typings

* dev workflow and cleaning up comments

* cleaning up a bit more

* rename Localizer -> Localization

* more documentation, change dir for default localizer

* some tests

* "

* move stuff, refactor, deprecate

* yarnb

* Update index.ts

---------

Co-authored-by: Jacob Nguyen <jacoobes@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
Co-authored-by: jacob <jacoobes@sern.dev>
2024-02-09 17:46:16 -06:00
Jacob Nguyen
5cad432589 Update README.md 2024-01-08 14:31:49 -06:00
Jacob Nguyen
044a10dace Update README.md 2024-01-08 14:29:49 -06:00
github-actions[bot]
9d5c6c714f chore(main): release 3.3.2 (#352)
* chore(main): release 3.3.2

* Update CHANGELOG.md

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2024-01-08 12:16:52 -06:00
Jacob Nguyen
4f2387119a fix: presence feature not workign on cjs applications (#351)
* start fix

* fix on unix

* better solution
2024-01-08 12:11:27 -06:00
Jacob Nguyen
a6fa4e3dcb fix docs build 2024-01-07 15:38:22 -06:00
github-actions[bot]
c281832db2 chore(main): release 3.3.1 (#350)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2024-01-07 15:27:43 -06:00
Jacob Nguyen
a359f73fa2 fix: crashing when slash command is used as text command (#349)
* progress on fix

* fix: ids
2024-01-07 15:26:08 -06:00
655bb8d358 revert: the last commit 2024-01-05 20:47:25 +01:00
e8d5029834 chore: update fortnite file 2024-01-05 20:46:38 +01:00
Jacob Nguyen
b0399f9507 refactor: minor (#347)
* some refactoring

* accidental merge

* refactor: ensure all asserts have error message to avoid cryptic messages

* general refactoring

* move controller to create-plugin
2024-01-02 13:04:59 -06:00
renovate[bot]
b962dae36c chore(deps): update actions/setup-node digest to 1a4442c (#314)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-27 11:40:18 -06:00
github-actions[bot]
c73cf96cb2 chore(main): release 3.3.0 (#346)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-12-27 11:13:11 -06:00
Jacob Nguyen
7458befe8a feat: presence (#345)
* presence

* from event presence and refactoring

* refine presence api

* add tests and more comments

* sss

---------

Co-authored-by: SrIzan10 <66965250+SrIzan10@users.noreply.github.com>
2023-12-27 11:11:32 -06:00
Jacob Nguyen
efe49391e8 Update README.md 2023-12-27 01:51:41 -06:00
Jacob Nguyen
3140f80c10 Update README.md 2023-12-27 01:46:55 -06:00
github-actions[bot]
504cdee7b2 chore(main): release 3.2.1 (#344)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-12-21 12:49:42 -06:00
Jacob Nguyen
c7661f272c chore: bump version 2023-12-21 12:47:24 -06:00
Jacob Nguyen
daac37c288 fix: logger swap failing 2023-12-21 12:47:02 -06:00
ysf
a579e272d0 revolutionary (#342) 2023-12-15 17:03:23 -06:00
github-actions[bot]
2051aa1ac0 chore(main): release 3.2.0 (#341)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-12-15 16:23:39 -06:00
Jacob Nguyen
237c8537c6 chore: release 3.2.0
Release-As: 3.2.0
2023-12-15 16:19:38 -06:00
Jacob Nguyen
77fb00d386 feat/abstractiti (#340)
* progress on better error handling

* wiring onError callback through module loader and resolver

* fix error callbacks not being stored

* update onError to be record

* type alias

* wiring

* seems to work

* update error handling contract and wire more

* add command error builder

* fix merge

* progress on error handling

* naive onError handling, not tested

* progres

* proress

* progress on abstracting away iti

* seems to work

* fix tests

* better typings

* add doc

* abstracting iti

* remove onerror for this pr

* feat: better way to add dependencies

* fix tests
2023-12-15 16:09:13 -06:00
renovate[bot]
89f6bbb975 chore(deps): update google-github-actions/release-please-action digest to db8f2c6 (#339)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-21 10:18:07 -06:00
github-actions[bot]
8ef4ee87e9 chore(main): release 3.1.1 (#338)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-11-05 21:25:18 -06:00
Neo
fd39858636 fix: queuing events (#332) @Benzo-Fury (#333)
fix: queuing events

Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-11-05 21:23:27 -06:00
Jacob Nguyen
132b625070 refactor: rm redudant fns and formatting 2023-11-04 16:57:13 -05:00
renovate[bot]
03439fec43 chore(deps): update google-github-actions/release-please-action digest to 4c5670f (#336)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-21 22:30:40 -05:00
Jacob Nguyen
fc87e99ed0 Update README.md 2023-09-09 01:08:16 -05:00
renovate[bot]
a08541a8e7 chore(deps): update actions/checkout digest to f43a0e5 (#329)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-04 17:06:17 -05:00
renovate[bot]
8bd5eb4949 chore(deps): lock file maintenance (#293)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-04 17:05:33 -05:00
github-actions[bot]
e1059f93f7 chore(main): release 3.1.0 (#330)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-09-04 15:43:47 -05:00
renovate[bot]
800531453f chore(deps): pin dependencies (#311)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-22 11:37:22 -05:00
Jacob Nguyen
c9f2d75665 deprecate: mode (#325)
* test: add tests for context

* deprecate: mode

* revert docs for deprecated option
2023-08-19 07:07:47 +05:30
Jacob Nguyen
e59e0b9d40 test: add tests for context (#324) 2023-08-18 10:46:46 -05:00
Jacob Nguyen
26ccd118ff feat: dispose hooks (deprecate useContainerRaw) (#323)
* feat: dispose hooks

* build: unminify, add source map, deprecate useContainerRaw

* fix regression of context and fix tsup build
2023-08-17 12:51:24 -05:00
Jacob Nguyen
4b97d86908 chore: upgrade ts-results-es (#322) 2023-08-13 10:55:39 -05:00
xxDeveloper
b1c82448bd chore: Create FUNDING.yml (#321)
* chore: Create FUNDING.yml

* Rename FUNDING.yml to .github/FUNDING.yml
2023-08-08 10:29:40 +03:00
Jacob Nguyen
d80081384a Update README.md 2023-08-07 17:23:58 -05:00
mina
50253ca322 feat: add guaranteed channelId and userId getters to Context (#320) 2023-08-06 15:28:38 -05:00
github-actions[bot]
215aca2f46 chore(main): release 3.0.2 (#319)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-08-06 10:45:17 -05:00
Jacob Nguyen
a7f5ea269f fix: invalid id for cts, mts, cjs, mjs files, node paths (#318)
* better error messages

* fix: invalid id for cts, mts, cjs, mjs files
2023-08-06 10:43:34 -05:00
Jacob Nguyen
52d6368440 Delete codeql-analysis.yml 2023-08-06 00:36:50 -05:00
Jacob Nguyen
1e723a4154 Update npm-publish.yml 2023-08-06 00:34:43 -05:00
Jacob Nguyen
5fe13f43d2 better npm-publish.yml 2023-08-06 00:33:40 -05:00
Jacob Nguyen
ab9d39306a Create test.yml (#317)
* Create test.yml

* Update test.yml

* Update test.yml
2023-08-06 00:29:43 -05:00
github-actions[bot]
d429f3adbf chore(main): release 3.0.1 (#316)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-08-04 19:05:48 -05:00
Jacob Nguyen
5e011b471e remove warning 2023-08-04 19:01:27 -05:00
Jacob Nguyen
41344608c6 fix: collectors 2023-08-04 19:00:50 -05:00
github-actions[bot]
7a72cc4fe3 chore(main): release 3.0.0 (#315)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-07-29 17:13:42 -05:00
Jacob Nguyen
70cca0dbb0 chore: release 3.0.0
Release-As: 3.0.0
2023-07-29 17:11:51 -05:00
Jacob Nguyen
7798e36458 feat!: v3 (#294)
* refactor: move things to core, imports not fixed yet

* work on strategy and lifted Context

* remove id from lifted Context

* refactor: remove dependence on discord.js for module stoore

* moving and fixing imports

* chore: move operators into core

* chore: fix paths

* add wrapper platform field

* add deprecation warning

* chore:update paths

* chore:remove const function

* chore: remove deprecated symbols

* docs: add documentation to internal function

* chore: remove deprecated support for plugins

* chore: remove dependence on discord.js Awaitable type

* chore: update typings

* lift requiredDependencyKeys out of makeFetcher

* move strategy to index.ts and add adapters

* chore: fix typings

* chore: move command args matrix as binding

* feat: make Context platform specific, CoreContext as Core

* chore: remove extra file

* chore: move prettier into package.json

* chore(core): update imports and operators

* chore(core): add DefaultWrapper as sern classic

* move eslint and prettier configs to json

* chore: remove utils folder in favor of single file

* chore: remove redundant directories for single files

* chore: remove redundant directories for single files

* refactor: move and update things

* chore: move commands into seperate file

* chore: serverless work

* chore: remove redundant directories for single files

* chore: rename, wip refactoring

* chore: redundant directory

* refactor: internalize operators

* feat!: new module resolution algorithm

* chore: refactor and move things

* chore: refactor and add multiplatform typings

* chore: remove leaky import

* chore: add agnostic predicates

* chore: add old context here until i figure out what to do

* chore: update Proccessed typing to ./core

* chore: add tweetnacl

* revert: multiplatform

* revert: multiplatform

* chore: modularize and split typings

* chore: revert multiplatform

* chore: revert multi and mov sernEmitter

* chore: revert multi and clean up code

* refactor: add createGenericHandler

* refactor: remove unneeded signatures and fix imports

* feat: add getPublishableCommands to ModuleManager

* chore: remove bad imports

* style: pretty

* revert: remove AnyDependencies type

* refactor: fold switch case

* docs: specifics

* chore: change all file names to camel case

* refactor: change all files to camelcase and refactor

* revert: remove cloudflare typings

* feat: SernEmitter now captures promise rejections

* chore: fix InitArgs missing

* chore: move typings

* chore: move and clean

* chore: delete plugins dir

* chore: cleanup dispatchers subdirectory for single file

* chore: move context into structures directory

* refactor: cleaning up code and renaming variables

* chore: update name of function to reflect use

* revert: multiple entry points

* revert: readd discordEvent

* refactor: rename, format, move things

* feat: types organization and cleaning up code base

* fix: unaliased modules would throw error

* build: speed up build

* revert: readd module store and add contract

* add separate id for id processing

* chore: progress of globalizing dependencies type

* chore: update container and init hook progress

* style: format & lint

* feat: dev and prod mode

* fix: directories ignoring incorrectly

* refactor: move metadata outside of module declarations

* revert: re export command executable and event executable

* refactor: a lot

* fix: plugins for class modules and module loader

* style: pretty

* fix class based module loading

* feat: globalize dependencies type

* revert: internal name

* feat: add new sern emitter event

* refactor: remove cast

* refactor: add better typings for sern event modules

* test: add tests

* test: add more tests

* feat: change error handling contract

* chore: make changes in codebase after error contract change

* docs: add purpose of d.ts file

* revert removal of crash method and mark deprecated

* fix: typings for options- have access to all properties now

* refactor: npx knip

* 3.0.0-rc1

* chore: fix for version 3 and reexport old types

* fix: reexport payload and button modules

* fix: component commands incorrectly aligned and ordered

* chore: bump version

* test: add id generation testing

* refactor: algorithm for module resolution

* chore: bump vers

* test: add eventDispatcher test

* *.test.ts

* fix: autocomplete nested option

* chore: bump vers

* add npmignore .yarn

* feat: experimental loading sern.config.json

* refactor: simplify build

* chore: bump vers

* chore: add documentation for service api

* add since

* feat: add possible mode option in file loading mode

* refactor: remove two unneeded functions and refactor to throw early

* refactor: clean up handler code

* fix: undefined this binding

* refactor: clean up signatures and types

* refactor: make evident the internal api and move around stuff

* refactor: remove circular dependencies

* fix circulars and imports

* oops, moving around mroe stuff

* refresh lock

* chore: import type and prettier

* style: prettier

* feat: solidify init logic

* fix module-loading.ts

---------

Co-authored-by: jacoobes <jacobnguyend@gmail.com>
2023-07-29 17:10:19 -05:00
Peter-MJ-Parker
9144485c39 My bot uses sern! (#313)
feat: My bot uses sern!
2023-07-03 21:24:05 -05:00
Evo
cf15b67ede chore: bless seren (#310) 2023-06-19 07:20:24 -05:00
jacob
57cc94ff81 Empty-Commit 2023-06-18 09:59:38 -05:00
github-actions[bot]
6a2a5b4565 chore(main): release 2.6.3 (#309)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-06-17 16:21:26 -05:00
Jacob Nguyen
5fdc1eda7f fix: autocomplete nested option and merge main 2023-06-17 16:16:47 -05:00
renovate[bot]
e00d1df32e chore(deps): update dependency @types/node to v18.16.8 (#299)
chore(deps): update dependency @types/node to v18.16.7

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-06-16 00:13:10 -05:00
renovate[bot]
31c221bd5e chore(deps): update actions/checkout digest to c85c95e (#306)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-16 00:12:42 -05:00
renovate[bot]
0aba4a6606 chore(deps): update dependency prettier to v2.8.8 (#289)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-05-19 20:50:36 -05:00
renovate[bot]
e9c7661804 chore(deps): update dependency discord.js to v14.11.0 (#297)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-05-12 21:15:49 -05:00
renovate[bot]
446417bfb9 chore(deps): update dependency @types/node to v18.16.7 (#291)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-05-10 14:47:30 -05:00
renovate[bot]
6b58ef731b chore(deps): update yarn to v3.5.1 (#296)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-10 13:45:24 -05:00
renovate[bot]
b62129bf04 chore(deps): update dependency rxjs to v7.8.1 (#292)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-05-10 13:43:21 -05:00
renovate[bot]
3d121ff01c chore(deps): lock file maintenance 2023-04-25 21:24:03 +00:00
renovate[bot]
d201087d4f chore(deps): update dependency eslint to v8.39.0 (#288)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-25 11:51:12 -05:00
renovate[bot]
1af4a2bed4 chore(deps): update dependency @typescript-eslint/parser to v5.59.1 (#287)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-25 11:50:50 -05:00
renovate[bot]
edcaed083e chore(deps): update dependency @types/node to v18.16.0 (#285)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-04-23 23:49:08 -05:00
renovate[bot]
a4fe2c50df chore(deps): update dependency esbuild to ^0.17.0 (#280)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-04-23 23:48:12 -05:00
renovate[bot]
9ea991626d chore(deps): update actions/checkout digest to 8e5e7e5 (#278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-04-23 23:47:15 -05:00
Jacob Nguyen
64f20f1cf5 Delete non detected license 2023-04-21 16:35:15 -05:00
xxDeveloper
41cc72fe63 chore: README patch (#282)
* docs: Update README.md

It’s better IG

* chore: Update README.md

* chore: Update README.md

* chore; Update README.md

* chore: Update README.md

* chore: Update README.md

* chore: Update README.md

* chore: Update README.md

* chore: Update README.md

* chore: Final updates to README.md
2023-04-18 11:08:20 -05:00
github-actions[bot]
d983f95906 chore(main): release 2.6.2 (#281)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-15 12:42:59 -05:00
Jacob Nguyen
c1f690633c chore: release 2.6.2
Release-As: 2.6.2
2023-04-15 12:40:17 -05:00
Jacob Nguyen
8544d301ef bump version 2023-04-15 12:19:12 -05:00
Jacob Nguyen
52bcba9cfc docs: add deprecation warning 2023-04-15 12:16:35 -05:00
xxDeveloper
21febd2c90 chore: Update SECURITY.md (#276)
Semantic security file

Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-04-11 22:46:02 -05:00
xxDeveloper
11daebb30a chore: Update LICENSE (#275)
We're in 2023
We're sern, not Sern

Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-04-11 22:45:45 -05:00
github-actions[bot]
b817f98c10 style: pretty please (#277)
Co-authored-by: EvolutionX-10 <EvolutionX-10@users.noreply.github.com>
2023-04-11 22:45:30 -05:00
Evo
563f583318 chore: switch to yarn (#273)
* chore: switch to yarn

* chore: pointless limitation

permalink: http://whatthecommit.com/468a491808723d12de48b079d9092b44

* chore: i can't believe it took so long to fix this.

permalink: http://whatthecommit.com/b298fe6d3375ab953abfdb0f1f737826
2023-04-11 12:45:16 -05:00
EvolutionX
e4c7bfe686 chore: ok work pls 2023-04-11 22:32:31 +05:30
EvolutionX
69fa4908c3 chore: refresh lockfile 2023-04-11 22:09:32 +05:30
renovate[bot]
4fa28d605f chore(deps): lock file maintenance (#245)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-11 10:32:46 -05:00
Jacob Nguyen
079b554f8b Update continuous-integration.yml 2023-04-11 10:31:15 -05:00
Jacob Nguyen
dec56335b9 Update codeql-analysis.yml 2023-04-11 10:30:41 -05:00
Jacob Nguyen
50be972d4f Update continuous-integration.yml 2023-04-11 10:29:06 -05:00
Jacob Nguyen
507d183970 Update codeql-analysis.yml 2023-04-11 10:28:29 -05:00
renovate[bot]
90edd4f91e chore(deps): update dependency eslint to v8.38.0 (#180)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-11 10:24:32 -05:00
renovate[bot]
5f11142599 chore(deps): update dependency @typescript-eslint/parser to v5.58.0 (#250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-04-11 10:22:27 -05:00
renovate[bot]
7a635f9978 chore(deps): update actions/checkout digest to 8f4b7f8 (#261)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-04-11 10:21:59 -05:00
renovate[bot]
a17aeac558 chore(deps): update pnpm to v7.32.0 (#262)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-04-11 10:21:39 -05:00
renovate[bot]
af6ebed348 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.58.0 (#249)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-11 10:18:46 -05:00
renovate[bot]
2f96b7634d chore(deps): update dependency prettier to v2.8.7 (#263)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-11 10:18:17 -05:00
EvolutionX
97741faa69 chore: refresh lockfile 2023-04-11 16:02:13 +05:30
Jacob Nguyen
94070d99e8 refactor/decoupling (#265)
* fix npm script for workflows

* filter lazy modules

* lift inline function for readability

* perf: use one instance of operator instead of creating instances

* chore: move fmt closer to call site

* refactor: inline function lifting and readability

* add import payload type

* refactor: remove redundant pipe for single function operators

* refactor: clearer naming for resultResolver

* refactor: no unused variable warning for updateAlive

* style: pretty

* refactor: remove redundant getter

* style: pretty

* fix: typescript needs explicit definition for defineAllFields

* add LazyPaths map

* chore: update tsup and typescript

* chore: revert lazy module work and work on decoupling core

* fix npm script for workflows

* chore: fix typings

* refactor: inline function `defineAllFields`

* docs: add @since annotation

* style: prettier

* docs: add since annotations

* fix: typings

* chore: update dependencies

* chore: remove unused import

* style: pretty

* merge on home pc

* refactor: use dependencies less

---------

Co-authored-by: jacoobes <jacobnguyend@gmail.com>
2023-04-10 22:12:26 -05:00
Jacob Nguyen
473be775f0 Update README.md 2023-03-29 15:12:26 -05:00
Neo
36af102251 docs: removed ALMA (#264)
Not working on it anymore, also not running it.
2023-03-29 12:55:16 -05:00
github-actions[bot]
cee740ea3f style: pretty please (#260)
Co-authored-by: renovate[bot] <renovate[bot]@users.noreply.github.com>
2023-03-17 17:01:20 -05:00
github-actions[bot]
2fd7697300 chore(main): release 2.6.1 (#258)
* chore(main): release 2.6.1

* Update CHANGELOG.md

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-03-17 16:37:20 -05:00
Jacob Nguyen
f9609ce6cd chore: release 2.6.1
Release-As: 2.6.1
2023-03-17 16:33:46 -05:00
Jacob Nguyen
a3064aa915 chore: audit & remove tspattern (#256)
* chore: move import

* build: remove ts pattern

* fix: forgot to convert to switch

* fix workflow

* refactor: lift function out of readyHandler

* refactor: clean up errTap signature

* fix: sern emitter emitting wrong payload

* wa

* style: space

* chore: remove old errTap

* chore:bump discord.js

* chore: eslint format
2023-03-17 16:30:27 -05:00
github-actions[bot]
0a53a48521 style: pretty please (#255)
Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
2023-03-15 21:16:58 -05:00
Jacob Nguyen
05037b5315 build: prettier ignore 2023-03-15 21:15:33 -05:00
Jacob Nguyen
06a3e69210 feat: prettier ignore 2023-03-15 21:13:42 -05:00
Jacob Nguyen
74c4b77d4b build: refactor/building (#252)
* refactor: conditional compilation of loading esm/cjs modules

* refactor: move file loading file

* refactor: add conditional compilation for building modules

* refactor: add conditional compilation for building modules

* perf: decrease build times

* test

* revert: typo and clean code

* build: smaller build

* chore:cleanscripts

* chore:refactor readme

* build:automerge lockfile

* chore: remove build and upgrade readme

* fix: dropdown

* chore: fix

* chore: more docs

---------

Co-authored-by: jacoobes <jacobnguyend@gmail.com>
2023-03-15 21:08:27 -05:00
github-actions[bot]
d381ff568e chore(main): release 2.6.0 (#248)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-09 16:18:06 -06:00
Jacob Nguyen
6db5c71506 chore: update workflow 2023-03-09 16:15:29 -06:00
github-actions[bot]
507c9e7939 style: pretty please (#247)
Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
2023-03-09 16:11:29 -06:00
Jacob Nguyen
09610d0501 refactor: eventhandlers (#246)
* refactor:import

* feat: save progress

* feat:progress

* refactor: event handlers

* fix: merge all subscriptions into event handler

* fix: remove duplicate minify key

* fix: leftover this

* docs: jsdoc

* chore: clean pnpm

---------

Co-authored-by: jacoobes <jacobnguyend@gmail.com>
2023-03-09 16:09:35 -06:00
github-actions[bot]
0862bf92d0 style: pretty please (#244)
Co-authored-by: renovate[bot] <renovate[bot]@users.noreply.github.com>
2023-03-03 09:22:02 -06:00
renovate[bot]
62162f6b8c chore(deps): lock file maintenance (#240)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-03-02 19:03:02 -06:00
renovate[bot]
eb501db09a chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.54.0 (#243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-02 19:01:18 -06:00
renovate[bot]
964848a4e2 chore(deps): update dependency @typescript-eslint/parser to v5.54.0 (#194)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-03-02 18:48:56 -06:00
renovate[bot]
78dead1b49 chore(deps): update pnpm to v7.28.0 (#239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-03-02 18:48:32 -06:00
renovate[bot]
39e6d6d2f9 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.52.0 (#238)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-20 00:30:10 -06:00
renovate[bot]
5aff57ed6d chore(deps): update dependency typescript to v4.9.5 (#208)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-20 00:27:36 -06:00
github-actions[bot]
4095471346 style: pretty please (#237)
Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
2023-02-17 15:47:33 -06:00
Jacob Nguyen
f7b9c52df1 Merge remote-tracking branch 'origin/main' 2023-02-17 15:40:54 -06:00
Jacob Nguyen
d20d01524b feat: adding pure annotation for better tree shaking 2023-02-17 15:40:32 -06:00
renovate[bot]
5684c060bc chore(deps): lock file maintenance (#225)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-02-17 13:13:15 -06:00
github-actions[bot]
ca8b31f280 chore(main): release 2.5.3 (#235)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-16 11:11:32 -06:00
jacoobes
ce9a0831a6 chore: release 2.5.3
Release-As: 2.5.3
2023-02-16 11:09:46 -06:00
jacoobes
3a32968a17 chore: bump version 2023-02-16 10:57:38 -06:00
github-actions[bot]
e549f8bc3e chore(main): release 2.5.2 (#234)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-16 10:52:14 -06:00
jacoobes
3ab73459ad bump version 2023-02-16 10:49:02 -06:00
jacoobes
16af2e996d chore: minifiy build output 2023-02-16 10:44:38 -06:00
Jacob Nguyen
4f9a842b0e update ci 2023-02-15 21:31:18 -06:00
Jacob Nguyen
5dfd1a1fc1 Update README.md 2023-02-15 21:28:48 -06:00
Jacob Nguyen
c45a10c950 Update README.md 2023-02-15 21:28:25 -06:00
Jacob Nguyen
b45ba34f3c Update README.md 2023-02-15 18:22:23 -06:00
Jacob Nguyen
facee79c90 revert: version 2023-02-14 17:54:31 -06:00
Jacob Nguyen
306eee071d chore: bump dependencies 2023-02-14 17:52:23 -06:00
Jacob Nguyen
00d55208a0 Merge remote-tracking branch 'origin/main' 2023-02-14 17:42:26 -06:00
Jacob Nguyen
49fad801a5 chore: bump dependencies 2023-02-14 17:41:54 -06:00
Neo
c9f44ce72b docs: changed name of Benzo-Fury's Bot (#226) 2023-02-13 11:46:28 -06:00
Jacob Nguyen
a9a2528faf Update README.md 2023-02-12 17:07:51 -06:00
github-actions[bot]
529edb7da5 style: pretty please (#224)
Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
2023-02-12 12:59:34 -06:00
github-actions[bot]
f236dc05e2 chore(main): release 2.5.1 (#222)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-12 12:58:00 -06:00
Jacob Nguyen
c78936a225 chore: release 2.5.1
Release-As: 2.5.1
2023-02-12 12:54:18 -06:00
Jacob Nguyen
1860b898f3 fix: autocomplete 2023-02-12 12:51:49 -06:00
renovate[bot]
fd10772a9b chore(deps): lock file maintenance (#213)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-12 11:47:37 -06:00
renovate[bot]
cd36bd8a47 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.51.0 (#187)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-12 11:33:00 -06:00
renovate[bot]
dda7f41231 chore(deps): update dependency prettier to v2.8.4 (#215)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-02-12 11:29:38 -06:00
renovate[bot]
371a57194c chore(deps): update pnpm to v7.27.0 (#216)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-12 11:28:54 -06:00
Neo
bfcc160a39 docs: change protectors WIP mark to its name (#214)
Idk I was bored and saw this was different to mine. Change mine instead or just close this pr, idm.
2023-02-07 20:16:23 -06:00
Neo
86fa531eb6 docs: Adding the WIP to my bot (#212)
feat: Adding the WIP to my bot
2023-02-04 10:37:03 -06:00
Gary
58052e94cb docs: add my bot to the ReadMe (#211) 2023-02-04 01:38:07 -06:00
Neo
96f4281121 feat: Adding my bot to readme (#210) 2023-02-03 23:55:26 -06:00
Jacob Nguyen
f9ae7c003b docs: clarify example 2023-02-03 23:46:19 -06:00
Jacob Nguyen
ec211d5a8d docs: do some updating on readme 2023-02-03 23:14:57 -06:00
github-actions[bot]
3faf83bbf7 style: pretty please (#209)
Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
2023-02-03 23:04:13 -06:00
renovate[bot]
8eed099503 chore(deps): lock file maintenance (#183)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-03 23:00:46 -06:00
renovate[bot]
e2874be4e7 chore(deps): update pnpm to v7.26.3 (#203)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-03 22:56:17 -06:00
github-actions[bot]
1d6751a9cd style: pretty please (#207)
Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
2023-01-30 12:35:37 -06:00
github-actions[bot]
f6afafa352 chore(main): release 2.5.0 (#205)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-30 12:30:28 -06:00
Jacob Nguyen
b4b195dc95 chore: release 2.5.0
Release-As: 2.5.0
2023-01-30 12:27:18 -06:00
Jacob Nguyen
33f14467ec feat!: simpler plugins (#193)
* feat: experimental plugin changes

* more refactors and name changes

* feat: update name usage and update dispatchers.ts

* fix:naming

* feat: slightly safer typings than any[]

* fix: forgot to destructure arguments

* feat: add special function

* fix: typings

* feat: SUPER SIMPLIFY!!!

* refactor: move promisifiedPlugins closer to call site

* refactor: typings

* refactor: typings

* refactor: consolidate resolving initplugins into one function

* refactor: better types

* revert: remove unneeded function

* revert: remove unneeded function

* feat: dispatch work, simplify

* feat: move some observableHandling function to operators for clarity

* feat: simplify and document

* feat: simplifying sern and docs

* fix: typings

* docs: clarity of function name

* docs: add documentation for executeModule

* feat: contextArgs overloads

* docs: found out why

* fix: typings

* feat: shorten operators signature

* refactor: switch to correct convention

* refactor: take(1) -> first()

* refactor: revert

* refactor: safer typings (less any) and more accurate typings

* style: prettier and short type aliases

* fix: typings

* fix: typings

* docs: add deprecations

* refactor: organization and moving stuff

* pretty: prettey

* docs: describe file

* chore: update dependencies and version

* docs: fix link for docasaurus

* refactor: using a more appropriate operator function for closing an observable on crash

* fix!: changing single and many

* refactor: typings and simplifying composeRoot

* fix: re-add logger into handleError

* docs: comment

* docs: new section

* feat: help mitigate breaking changes

* feat: help mitigate breaking changes

* feat: help mitigate breaking changes and function overloads

* feat: deprecate instead of remove

* feat: partial remove and deprecate old symbols

* revert: trying to accommodate old plugins is too difficult

* docs: add many as deprecated

* docs: update

* feat: partial backwards compatability

* refactor: renaming, docs, and exports more clean

* refactor: context got a lot simpler

* refactor: imports

* docs: explain methods
2023-01-26 12:06:04 -06:00
renovate[bot]
cb95105c1c chore(deps): update dependency prettier to v2.8.3 (#196)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-01-17 12:54:57 -06:00
renovate[bot]
845b82feef chore(deps): update actions/checkout digest to ac59398 (#184)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-01-14 22:38:44 -06:00
renovate[bot]
833a323f3c chore(deps): update actions/setup-node digest to 64ed1c7 (#185)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-14 22:36:47 -06:00
renovate[bot]
42e5f20425 chore(deps): update pnpm to v7.25.0 (#195)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-14 22:36:09 -06:00
renovate[bot]
2b25e6bfbb chore(deps): update pnpm to v7.22.0 (#179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-01-07 19:43:59 -06:00
renovate[bot]
1a27341092 chore(deps): update dependency prettier to v2.8.2 (#189)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2023-01-07 16:30:48 -06:00
renovate[bot]
4680e451bb chore(deps): update dependency @typescript-eslint/parser to v5.48.0 (#188)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-07 16:09:49 -06:00
renovate[bot]
917d8b0d1b chore(deps): update dependency iti to ^0.6.0 (#178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-31 14:01:56 -06:00
github-actions[bot]
b8492ee45d chore(main): release 2.1.1 (#181)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-31 13:50:44 -06:00
Jacob Nguyen
08aac1d67a chore: bump version 2022-12-31 13:47:50 -06:00
Jacob Nguyen
a13df6fb42 fix: modals remapping 2022-12-31 13:41:20 -06:00
Jacob Nguyen
7089f5c0dc chore: fix prettier wkflw holy shit 2022-12-30 11:30:38 -06:00
Jacob Nguyen
559c1a7a7b chore: fix prettier wkflw holy shit 2022-12-30 11:28:50 -06:00
Jacob Nguyen
ac27d168e2 chore: fix prettier wkflw holy shit 2022-12-30 11:27:00 -06:00
Jacob Nguyen
d1e6ec4589 chore: fix prettier wkflw holy shit 2022-12-30 11:25:42 -06:00
Jacob Nguyen
ff379d03be chore: fix prettier wkflw holy shit 2022-12-30 11:15:40 -06:00
Jacob Nguyen
1e4e933db2 chore: fix prettier wkflw holy shit 2022-12-30 11:13:38 -06:00
Jacob Nguyen
ce960f4c8d chore: fix prettier wkflw holy shit 2022-12-30 11:11:38 -06:00
Jacob Nguyen
1130456045 chore: fix prettier wkflw holy shit 2022-12-30 11:10:48 -06:00
Jacob Nguyen
1617d2dcc3 chore: fix prettier wkflw 2022-12-30 11:09:14 -06:00
Jacob Nguyen
ddacbd6e38 chore: fix prettier wkflw 2022-12-30 11:06:26 -06:00
Jacob Nguyen
d69819e9fc chore: fix prettier wkflw 2022-12-30 11:03:30 -06:00
github-actions[bot]
49e4ba623f chore(main): release 2.1.0 (#176)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2022-12-30 10:58:57 -06:00
github-actions[bot]
1b6c413fc2 style: pretty please (#175)
Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
2022-12-30 10:56:23 -06:00
Jacob Nguyen
e986535935 fix: multi parameter events 2022-12-30 10:45:02 -06:00
Jacob Nguyen
c30aac476c feat: grammar 2022-12-28 15:37:35 -06:00
github-actions[bot]
f9622d3788 style: pretty please (#173)
Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
2022-12-28 15:34:22 -06:00
renovate[bot]
f286a24686 chore(deps): update dependency prettier to v2.8.1 (#158)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2022-12-28 15:20:45 -06:00
renovate[bot]
166934d749 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.47.1 (#159)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2022-12-28 15:18:51 -06:00
renovate[bot]
01d79177e8 chore(deps): lock file maintenance (#153)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2022-12-28 15:15:12 -06:00
renovate[bot]
50dac7fb46 chore(deps): update dependency @typescript-eslint/parser to v5.47.1 (#160)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2022-12-28 15:10:17 -06:00
renovate[bot]
714d23d401 chore(deps): update actions/checkout digest to 755da8c (#161)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2022-12-28 15:05:10 -06:00
renovate[bot]
565c4fc35a chore(deps): update dependency eslint to v8.30.0 (#152)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-12-28 14:56:44 -06:00
github-actions[bot]
8d18c4b182 style: pretty please (#162)
* style: pretty please

* feat: no package.lock.json anymore

Co-authored-by: jacoobes <jacoobes@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2022-12-28 14:40:19 -06:00
github-actions[bot]
71cec6f142 chore(main): release 2.0.0 (#163)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-12-28 14:30:59 -06:00
Jacob Nguyen
14556223fd feat!:(2.0 global services) (#156)
* style: prettier line feed changes

* style: prettier line feed changes

* feat: prototyping module manager

* feat: removing unused types

* feat: update location of module typings

* revert: port back to original interaction type checkers

* revert: port back to original interaction type checkers

* revert: remove unneeded type predicates

* feat: moving modules to its own contained class

* feat: getting global plugins some typings and adding contracts

* chore: fixing up and cleaning code

* feat: adding ModuleConfiguration helper fn and default module manager

* feat: solidifying contract

* revert: delete files

* chore: saving for later

* feat: prototyping module manager

* feat: removing unused types

* feat: update location of module typings

* revert: port back to original interaction type checkers

* revert: port back to original interaction type checkers

* revert: remove unneeded type predicates

* feat: moving modules to its own contained class

* feat: getting global plugins some typings and adding contracts

* chore: fixing up and cleaning code

* feat: adding ModuleConfiguration helper fn and default module manager

* feat: solidifying contract

* revert: delete files

* chore: saving for later

* feat: more merge conflicts

* feat: starting from scratch, add new typings

* feat: refactor ScopedPlugin typing

* feat: strengthening contracts

* chore: edit eslint

* feat: add addDependencies

* feat: add iti di

* feat: constfn util

* revert: delete old files

* remove: ModuleConfiguration

* feat: add sanity checker

* feat: add new error and update dependency

* feat: add sanity check for iti

* feat: add function helpers for di

* feat: add POC for sern di IOC

* feat: refactor DependenciesMap

* revert: remove old code

* feat: refactor makeDependencies fn

* chore(deps): update dependency @typescript-eslint/parser to v5.36.1 (#127)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.36.1 (#126)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* docs: Fix the code example (#128)

* chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.36.2 (#130)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: xxDeveloper <77380166+Murtatrxx@users.noreply.github.com>

* chore(deps): update dependency @typescript-eslint/parser to v5.36.2 (#131)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: xxDeveloper <77380166+Murtatrxx@users.noreply.github.com>

* feat: allow constructable modules (#133)

* Update readFile.ts

* Update userDefinedEventsHandling.ts

* Update readyHandler.ts

* fix: ts error

Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
Co-authored-by: xxDeveloper <77380166+Murtatrxx@users.noreply.github.com>

* revert: feat of allow constructable modules (#138)

Revert "feat: allow constructable modules (#133)"

This reverts commit 03936eb2ea.

* feat: update CODEOWNERS

* feat: classmodules@arcs (#143)

* feat: add class based commands

* docs: add deprecation warnings

* feat: add deprecation warnings

* feat: add more deprecation warnings

* feat: add prototype ClassModule abstract class

* feat: add EventModuleClass prototype, change names

* feat: more flexible contract

* feat: EventExecutable

* fix: typo

* feat: made abstract classes because of defaults

* fix: typings

* feat: update Context typings, update to djs v15

* chore: update typescript dependency

* chore: bump version

* chore: update ignore

* chore: prettier

* docs: change readme to be docusaurus compliant

* chore(main): release 1.2.0 (#145)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(autocomplete): now support multiple autocomplete options (#147)

* chore(main): release 1.2.1 (#148)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat: update location of module typings

* feat: getting global plugins some typings and adding contracts

* feat: adding ModuleConfiguration helper fn and default module manager

* feat: solidifying contract

* feat: removing unused types

* feat: update location of module typings

* feat: moving modules to its own contained class

* feat: getting global plugins some typings and adding contracts

* feat: solidifying contract

* revert: delete files

* chore: saving for later

* feat: more merge conflicts

* feat: starting from scratch, add new typings

* feat: strengthening contracts

* feat: add sanity check for iti

* feat: refactor DependenciesMap

* feat: adding POC back

* docs: add some deprecation warnings

* feat: remove client and sernEmitter

* feat: rebase global_services

* chore: oops rebase pkgjson fix

* fix: typings

* feat: iti update and getting di working

* feat: adding contracts and default provider

* feat: update typings and change logger

* fix: MapDeps typings and useContainer typings

* feat: moving handlers to 2.0

* feat: injecting crash handler

* feat: alpha logging injection?

* feat: injecting modulemanager in interactionhandler

* fix: typos and fixups

* fix: typos and fixups

* feat: moduleManager injection

* fix: crash on no module

* feat: i think optional dependencies work

* feat: add more optional support

* feat: make exclusion optional

* perf: simplify typings and reduce compile time

* fix: some typings adjustment, deprecating a field

* perf: simplifying plugin typings

* feat: remove addExternal

* feat: more simplifying typings

* perf: allow any and interfacify some types

* revert: remove horrendous Override type

* revert: delete interaction type predicates

* fix: unchecked cast to EventEmitter

* perf: remove unneeded creation of instance members

* feat: init test dir

* refactor: rename fn and short circuit name fn

* refactor: insert function

* feat: starting event module onEvent plugins and log payload

* feat: basic eventmodule event plugins?

* refactor: DRY

* feat: unify warning typing

* fix: typings

* feat: more progress on new eventModules

* feat: event modules with plugins!!?!?

* feat: making sernEmitter create default!!, readjust typings

* feat: inject sernEmitter emits and catch possible errors

* feat: add optionality to Logger dep type

* feat: context upgrade, fix circular emissions, crap ton of shit

* feat!: Module -> AnyModule, Added new SelectMenuInteractions, Changed some CommandType names

* feat: rudimentary onClick handler

* feat!: rename select menu command types, no distinguishing between text and alias

* docs: adding docs

* fix: forgot to change

* fix: logging undefined

* revert: remove spreadparams

* feat: export useContainerRaw function and fix smol bug

* feat: add iti DI disposeAll on crash

* fix: deferred execute

* feat!: rename BasePlugin -> Plugin

* feat: commandplugins for event modules, ill test later

* refactor: rename

* refactor: remove import

* feat: add deprecation warnings

* feat: update documentation comments

* perf: static'ify command plugin and event plugin typings

* refactor: remove redundant assignment

* feat: switch to pnpm

* chore: update pnpm lock

* fix: errors crashing and completing subscription

* feat: update example

* fix: overriding dependencies no matter what

* fix: bad rxjs docs

* refactor: destructuring

* feat: update typings for addDisposer

* feat: update packageManager field in package.json

* feat: grammar

Co-authored-by: jacoobes <jacobnguyend@gmail.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: xxDeveloper <77380166+Murtatrxx@users.noreply.github.com>
Co-authored-by: Arcs <73959934+HighArcs@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Evo <85353424+EvolutionX-10@users.noreply.github.com>
2022-12-28 14:18:36 -06:00
renovate[bot]
59c1c9c6a9 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.44.0 (#154)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-25 16:47:17 -06:00
renovate[bot]
a120136f55 chore(deps): update dependency @typescript-eslint/parser to v5.44.0 (#155)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-25 16:37:43 -06:00
xxDeveloper
9b2d7eea5f chore: Update the old domain (#157) 2022-11-20 18:29:11 +03:00
Jacob Nguyen
4d7aa97b66 docs: update new domain 2022-11-05 09:22:46 -05:00
renovate[bot]
83eadcd2e5 chore(deps): lock file maintenance (#142)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-20 16:30:04 -05:00
renovate[bot]
c0bf346841 chore(deps): update actions/setup-node digest to 8c91899 (#146)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2022-10-20 16:09:07 -05:00
renovate[bot]
73c161fffe chore(deps): update actions/checkout digest to 93ea575 (#151)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
2022-10-20 16:05:03 -05:00
renovate[bot]
ee763301d0 chore(deps): update dependency eslint to v8.25.0 (#141)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-20 15:56:31 -05:00
renovate[bot]
c5f6eb9794 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.40.1 (#139)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-20 15:38:24 -05:00
renovate[bot]
ec8a61a9ee chore(deps): update dependency @typescript-eslint/parser to v5.40.1 (#140)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-10-20 15:33:10 -05:00
Priyanuj09
87c17dbe10 [Fix] Hyperlinks (#149)
* Fixed discord invite link

* Fixed guidelines hyperlink
2022-10-15 14:56:59 -05:00
github-actions[bot]
790ce1681c chore(main): release 1.2.1 (#148)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-10-03 11:53:45 -05:00
Evo
cbad7380e1 fix(autocomplete): now support multiple autocomplete options (#147) 2022-10-03 11:50:49 -05:00
github-actions[bot]
ad36875be2 chore(main): release 1.2.0 (#145)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2022-09-28 11:57:10 -05:00
Jacob Nguyen
50288867a5 feat: classmodules@arcs (#143)
* feat: add class based commands

* docs: add deprecation warnings

* feat: add deprecation warnings

* feat: add more deprecation warnings

* feat: add prototype ClassModule abstract class

* feat: add EventModuleClass prototype, change names

* feat: more flexible contract

* feat: EventExecutable

* fix: typo

* feat: made abstract classes because of defaults

* fix: typings

* feat: update Context typings, update to djs v15

* chore: update typescript dependency

* chore: bump version

* chore: update ignore

* chore: prettier

* docs: change readme to be docusaurus compliant
2022-09-28 11:52:25 -05:00
Jacob Nguyen
6b8995d149 feat: update CODEOWNERS 2022-09-13 14:41:57 -05:00
xxDeveloper
82bbddac8d revert: feat of allow constructable modules (#138)
Revert "feat: allow constructable modules (#133)"

This reverts commit 03936eb2ea.
2022-09-13 14:36:15 -05:00
Arcs
03936eb2ea feat: allow constructable modules (#133)
* Update readFile.ts

* Update userDefinedEventsHandling.ts

* Update readyHandler.ts

* fix: ts error

Co-authored-by: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com>
Co-authored-by: xxDeveloper <77380166+Murtatrxx@users.noreply.github.com>
2022-09-13 21:46:06 +03:00
renovate[bot]
c4019f7a08 chore(deps): lock file maintenance (#136)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-12 20:57:34 +03:00
renovate[bot]
992619f8e5 chore(deps): update dependency @typescript-eslint/parser to v5.36.2 (#131)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: xxDeveloper <77380166+Murtatrxx@users.noreply.github.com>
2022-09-11 14:24:50 +03:00
renovate[bot]
b995560ec6 chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.36.2 (#130)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: xxDeveloper <77380166+Murtatrxx@users.noreply.github.com>
2022-09-11 14:19:40 +03:00
renovate[bot]
f01ef9b86c chore(deps): lock file maintenance (#129)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-11 14:16:19 +03:00
xxDeveloper
702c5750fc docs: Fix the code example (#128) 2022-09-03 14:52:26 +03:00
renovate[bot]
d5d1b4129b chore(deps): update dependency @typescript-eslint/eslint-plugin to v5.36.1 (#126)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-03 11:26:55 +03:00
renovate[bot]
7658d3e3ab chore(deps): update dependency @typescript-eslint/parser to v5.36.1 (#127)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-09-03 11:24:09 +03:00
renovate[bot]
9c1abc6b2e chore(deps): lock file maintenance (#118) 2022-08-29 14:07:45 +03:00
151 changed files with 8926 additions and 4940 deletions

View File

@@ -1,11 +0,0 @@
{
"parser": "@typescript-eslint/parser",
"extends": ["plugin:@typescript-eslint/recommended"],
"parserOptions": { "ecmaVersion": "latest", "sourceType": "script" },
"rules": {
"@typescript-eslint/no-non-null-assertion": "off",
"quotes": [2, "single", { "avoidEscape": true, "allowTemplateLiterals": true }],
"semi": ["error", "always"],
"@typescript-eslint/no-empty-interface": 0
}
}

2
.github/CODEOWNERS vendored
View File

@@ -1 +1 @@
src/* @jacoobes
* @jacoobes

1
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1 @@
open_collective: sern

36
.github/SECURITY.md vendored
View File

@@ -6,13 +6,43 @@ Project is currently under heavy development but you can try out our [npm packag
| Version | Supported |
| ------- | ------------------ |
| 0.1.0 @ dev | :white_check_mark: |
| 2.6.1 | YES |
| 2.6.0 | YES |
| 2.5.3 | YES |
| 2.5.2 | YES |
| 2.5.1 | YES |
| 2.5.0 | YES |
| 2.1.1 | NO |
| 2.1.0 | NO |
| 2.0.0 | NO |
| 1.2.1 | NO |
| 1.2.0 | NO |
| 1.1.0 | NO |
1.0.1 | NO
1.0.0 | NO
1.1.9 @ beta | NO
1.1.8 @ beta | NO
1.1.7 @ beta | NO
1.1.6 @ beta | NO
1.1.5 @ beta | NO
1.1.4 @ beta | NO
1.1.3 @ beta | NO
1.1.2 @ beta | NO
1.1.1 @ beta | NO
1.1.0 @ beta | NO
1.0.4 @ beta | NO
1.0.3 @ beta | NO
1.0.2 @ beta | NO
1.0.1 @ beta | NO
1.0.0 @ beta | NO
0.0.1 @ dev | NO (TRY IT)
* Dev versions might include bugs, use it with your own risk.
* Dev versions might include bugs and not supported use stable versions.
## Reporting a Vulnerability
You can report a vulnerability by opening an issue on the [project's GitHub](https://github.com/SernHandler/Sern/issues) repository.
You can report a vulnerability by opening an issue on the [project's GitHub](https://github.com/sern-handler/handler/issues) repository.
Please provide as much information as possible when reporting a vulnerability. We are looking information for, the affected version, and the steps to reproduce the vulnerability.

View File

@@ -1,37 +0,0 @@
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
schedule:
- cron: '37 20 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'javascript' ]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2

View File

@@ -1,44 +0,0 @@
name: Continuous Integration
on:
# Trigger the workflow on push or pull request or custom
push:
branches:
main
pull_request_target:
branches:
main
workflow_dispatch:
jobs:
Prettier:
name: Run Prettier
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # tag=v3
- name: Set up Node.js
uses: actions/setup-node@2fddd8803e2f5c9604345a0b591c3020ee971a93 # tag=v3
with:
node-version: 17
# Prettier must be in `package.json`
- name: Install Node.js dependencies
run: npm i -g prettier && npm i
- name: Run Prettier
run: prettier --write .
- name: Create Pull Request
id: cpr
uses: peter-evans/create-pull-request@v4
with:
commit-message: "style: pretty please"
branch: prettier
delete-branch: true
branch-suffix: short-commit-hash
title: "style: pretty please"
body: "pretty pretty prettier"
reviewers: EvolutionX-10

View File

@@ -15,6 +15,6 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v3
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
- name: 'Dependency Review'
uses: actions/dependency-review-action@v2
uses: actions/dependency-review-action@0efb1d1d84fc9633afcdaad14c485cbbc90ef46c # v2

34
.github/workflows/npm-publish-dev.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Continuous Delivery
on:
push:
branches:
- main
paths:
- 'src/**'
- 'package.json'
jobs:
Publish:
name: Publishing Dev
runs-on: ubuntu-latest
steps:
- name: Check out Git repository
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
- name: Set up Node.js
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3
with:
node-version: 18
registry-url: 'https://registry.npmjs.org'
- name: Install Node.js dependencies
run: npm i && npm run build:dev
- name: Publish to npm
run: |
npm version premajor --preid "dev.$(git rev-parse --verify --short HEAD)" --git-tag-version=false
npm publish --tag dev
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@@ -1,30 +1,22 @@
name: NPM / Publish
on:
release:
types: [created]
workflow_dispatch:
# We only publish if the version of sern handler is different. workflow automatically cancels if verson is the same
push:
branches:
- 'main'
jobs:
build:
test-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
- uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3
with:
node-version: 16
- run: npm ci
- run: npm test
publish-npm:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
node-version: 18
- run: npm i
- run: npm run build:prod
- uses: JS-DevTools/npm-publish@0f451a94170d1699fd50710966d48fb26194d939 # v1
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm ci
- run: npm publish
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
token: ${{ secrets.NPM_TOKEN }}
access: "public"

View File

@@ -6,7 +6,7 @@ jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/release-please-action@v3
- uses: google-github-actions/release-please-action@db8f2c60ee802b3748b512940dde88eabd7b7e01 # v3
with:
release-type: node
package-name: release-please-action

28
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,28 @@
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
name: Node.js CI
on:
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 19.x, 20.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm install
- run: npm run test

10
.gitignore vendored
View File

@@ -87,3 +87,13 @@ dist
# IntelliJ IDEA Config file
.idea/
# Yarn files
.yarn/install-state.gz
.yarn/build-state.yml
.yalc
yalc.lock
*.svg

View File

@@ -8,6 +8,7 @@ logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.yarn
# Runtime data
pids
*.pid
@@ -52,6 +53,7 @@ typings/
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
@@ -79,9 +81,7 @@ typings/
# FuseBox cache
.fusebox/
# TypeScript build output
dist
test
# VisualStudio Config file
.vs
@@ -108,6 +108,9 @@ tsup.config.js
tsconfig-base.json
tsconfig.cjs.json
tsconfig-cjs.json
tsconfig.esm.json
tsconfig-esm.json
renovate.json
fortnite

View File

@@ -1,2 +1,3 @@
.github/
*.md
*.md
dist/

View File

@@ -1,8 +0,0 @@
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 4,
"arrowParens": "avoid"
}

View File

@@ -1,5 +1,323 @@
# Changelog
## [4.2.4](https://github.com/sern-handler/handler/compare/v4.2.3...v4.2.4) (2025-03-06)
### Bug Fixes
* flat autocomplete ([#395](https://github.com/sern-handler/handler/issues/395)) ([89d7409](https://github.com/sern-handler/handler/commit/89d74095363befddc3222b9e5c89c35e7c6457b9))
## [4.2.3](https://github.com/sern-handler/handler/compare/v4.2.2...v4.2.3) (2025-03-04)
### Bug Fixes
* autocomplete sdt.module not present ([#393](https://github.com/sern-handler/handler/issues/393)) ([2414992](https://github.com/sern-handler/handler/commit/2414992b73a40065464b20f2d53826c78fcd3a5f))
## [4.2.2](https://github.com/sern-handler/handler/compare/v4.2.1...v4.2.2) (2025-02-03)
### Bug Fixes
* faster autocomplete lookup ([#387](https://github.com/sern-handler/handler/issues/387)) ([974c30f](https://github.com/sern-handler/handler/commit/974c30fa6cccaae7b1c2c3246ffa9eecb6bc7bf9))
## [4.2.1](https://github.com/sern-handler/handler/compare/v4.2.0...v4.2.1) (2025-01-24)
### Bug Fixes
* context-interactions error ([#382](https://github.com/sern-handler/handler/issues/382)) ([a52ad27](https://github.com/sern-handler/handler/commit/a52ad270d843e92db5bf2049d07527eed59d428c))
## [4.2.0](https://github.com/sern-handler/handler/compare/v4.1.1...v4.2.0) (2025-01-18)
### Features
* 4.2.0 load multiple directories & `handleModuleErrors` ([#378](https://github.com/sern-handler/handler/issues/378)) ([f9e7eaf](https://github.com/sern-handler/handler/commit/f9e7eaf92d22b76d3d02a1bbe8324ca6813f48f8))
## [4.1.1](https://github.com/sern-handler/handler/compare/v4.1.0...v4.1.1) (2025-01-13)
### Bug Fixes
* remove rxjs ([#376](https://github.com/sern-handler/handler/issues/376)) ([59d08ef](https://github.com/sern-handler/handler/commit/59d08ef207c486ce1cf0aba267e6f862838e0dfb))
* This puts the light back into lightweight (\- 4.1 MB)
## [4.1.0](https://github.com/sern-handler/handler/compare/v4.0.3...v4.1.0) (2025-01-06)
### Features
* moduleinfo-in-eventplugins ([#373](https://github.com/sern-handler/handler/issues/373)) ([220a60e](https://github.com/sern-handler/handler/commit/220a60ecf853df8d288de2533c669562a430c3f9))
### Bug Fixes
* update github username ([#371](https://github.com/sern-handler/handler/issues/371)) ([55715d5](https://github.com/sern-handler/handler/commit/55715d565990fe686159f3c1eda3754d1262c72c))
## [4.0.3](https://github.com/sern-handler/handler/compare/v4.0.2...v4.0.3) (2024-10-06)
### Bug Fixes
* async presence ([#369](https://github.com/sern-handler/handler/issues/369)) ([eabfb81](https://github.com/sern-handler/handler/commit/eabfb81819b53a4656d8eac6e21cfb488b724a42))
* fix eventModule typing for Discord events ([#368](https://github.com/sern-handler/handler/issues/368)) ([1789ccb](https://github.com/sern-handler/handler/commit/1789ccb2f22f502f87538fecdb07106ff7110434))
## [4.0.2](https://github.com/sern-handler/handler/compare/v4.0.1...v4.0.2) (2024-08-13)
### Bug Fixes
* type issue ([2106cdc](https://github.com/sern-handler/handler/commit/2106cdc1d033f88b6ee4ccca6754fe7a595a9328))
## [4.0.1](https://github.com/sern-handler/handler/compare/v4.0.0...v4.0.1) (2024-07-19)
### Bug Fixes
* add SDT typings to autocomplete commands ([#363](https://github.com/sern-handler/handler/issues/363)) ([92623d2](https://github.com/sern-handler/handler/commit/92623d2914fb80e31365f06cf896bb37f36fc814))
## [4.0.0](https://github.com/sern-handler/handler/compare/v3.3.4...v4.0.0) (2024-07-18)
### Features
* v4 ([#361](https://github.com/sern-handler/handler/issues/361)) ([9a8904f](https://github.com/sern-handler/handler/commit/9a8904f5aed4fa36b018ad73bbe58049bae33274))
### Miscellaneous Chores
* release 4.0.0 ([dda0e33](https://github.com/sern-handler/handler/commit/dda0e3395b6704862bfd3fda2a201e2cb9b45d2f))
## [3.3.4](https://github.com/sern-handler/handler/compare/v3.3.3...v3.3.4) (2024-03-18)
### Bug Fixes
* sern emitter err ([#358](https://github.com/sern-handler/handler/issues/358)) ([90e55df](https://github.com/sern-handler/handler/commit/90e55dfa1466c91e5da48922251309331921b1ef))
## [3.3.3](https://github.com/sern-handler/handler/compare/v3.3.2...v3.3.3) (2024-02-25)
### Bug Fixes
* rm deprecated class modules, clean up, rm indirection ([#355](https://github.com/sern-handler/handler/issues/355)) ([48f9f6e](https://github.com/sern-handler/handler/commit/48f9f6ec16e650d574bd24dcbb0ed176933bfe17))
* singleton init not being fired when inserting function ([07b11b3](https://github.com/sern-handler/handler/commit/07b11b357baac0c3c7055c022bc353995c80f766))
* typings and cleanup ([#356](https://github.com/sern-handler/handler/issues/356)) ([ce8c4bf](https://github.com/sern-handler/handler/commit/ce8c4bf6492b9680fb1c1a530d3e0028f214ad2f))
## [3.3.2](https://github.com/sern-handler/handler/compare/v3.3.1...v3.3.2) (2024-01-08)
### Bug Fixes
* presence feature not working on cjs applications ([#351](https://github.com/sern-handler/handler/issues/351)) ([4f23871](https://github.com/sern-handler/handler/commit/4f2387119acfde036d0d1626553e9050f55627d1))
## [3.3.1](https://github.com/sern-handler/handler/compare/v3.3.0...v3.3.1) (2024-01-07)
### Bug Fixes
* crashing when slash command is used as text command ([#349](https://github.com/sern-handler/handler/issues/349)) ([a359f73](https://github.com/sern-handler/handler/commit/a359f73fa24127a4964d411c8c1c0dfea5edc0f1))
### Reverts
* the last commit ([655bb8d](https://github.com/sern-handler/handler/commit/655bb8d35815fe0ce9797d8b169310a07b284ae0))
## [3.3.0](https://github.com/sern-handler/handler/compare/v3.2.1...v3.3.0) (2023-12-27)
### Features
* presence ([#345](https://github.com/sern-handler/handler/issues/345)) ([7458bef](https://github.com/sern-handler/handler/commit/7458befe8a5900480cd71900df02a8364837dc00))
## [3.2.1](https://github.com/sern-handler/handler/compare/v3.2.0...v3.2.1) (2023-12-21)
### Bug Fixes
* logger swap failing ([daac37c](https://github.com/sern-handler/handler/commit/daac37c28858c42b21042bdcb8141239db634e7d))
## [3.2.0](https://github.com/sern-handler/handler/compare/v3.1.1...v3.2.0) (2023-12-15)
### Miscellaneous Chores
* release 3.2.0 ([237c853](https://github.com/sern-handler/handler/commit/237c8537c66052309d7e13a7e6e0a4f7995c2558))
## [3.1.1](https://github.com/sern-handler/handler/compare/v3.1.0...v3.1.1) (2023-11-06)
### Bug Fixes
* queuing events ([fd39858](https://github.com/sern-handler/handler/commit/fd39858636d3038abb6d91021b65c99c488a3d6e))
* queuing events ([#332](https://github.com/sern-handler/handler/issues/332)) @Benzo-Fury ([#333](https://github.com/sern-handler/handler/issues/333)) ([fd39858](https://github.com/sern-handler/handler/commit/fd39858636d3038abb6d91021b65c99c488a3d6e))
## [3.1.0](https://github.com/sern-handler/handler/compare/v3.0.2...v3.1.0) (2023-09-04)
### Features
* add guaranteed `channelId` and `userId` getters to `Context` ([#320](https://github.com/sern-handler/handler/issues/320)) ([50253ca](https://github.com/sern-handler/handler/commit/50253ca322e7d6dbd2313139c0187a1028f71109))
* dispose hooks (deprecate useContainerRaw) ([#323](https://github.com/sern-handler/handler/issues/323)) ([26ccd11](https://github.com/sern-handler/handler/commit/26ccd118ff8cbcde94158a4d09fc0df18da9f254))
## [3.0.2](https://github.com/sern-handler/handler/compare/v3.0.1...v3.0.2) (2023-08-06)
### Bug Fixes
* invalid id for cts, mts, cjs, mjs files, node paths ([#318](https://github.com/sern-handler/handler/issues/318)) ([a7f5ea2](https://github.com/sern-handler/handler/commit/a7f5ea269fb344e221d10dbdc26a1611ffc8138f))
## [3.0.1](https://github.com/sern-handler/handler/compare/v3.0.0...v3.0.1) (2023-08-05)
### Bug Fixes
* collectors ([4134460](https://github.com/sern-handler/handler/commit/41344608c677b6069c46412f5f16e4337182ca7d))
## [3.0.0](https://github.com/sern-handler/handler/compare/v2.6.3...v3.0.0) (2023-07-29)
### ⚠ BREAKING CHANGES
* v3 ([#294](https://github.com/sern-handler/handler/issues/294))
### Features
* v3 ([#294](https://github.com/sern-handler/handler/issues/294)) ([7798e36](https://github.com/sern-handler/handler/commit/7798e36458c7f555d2bcb8a5857a6db47b7211da))
### Miscellaneous Chores
* release 3.0.0 ([70cca0d](https://github.com/sern-handler/handler/commit/70cca0dbb01e70b47a8c899b1fc4f43dee5ed8ed))
## [2.6.3](https://github.com/sern-handler/handler/compare/v2.6.2...v2.6.3) (2023-06-17)
### Bug Fixes
* autocomplete nested option and merge main ([5fdc1ed](https://github.com/sern-handler/handler/commit/5fdc1eda7f4fcc1f94af7eca661660c0edeb3251))
## [2.6.2](https://github.com/sern-handler/handler/compare/v2.6.1...v2.6.2) (2023-04-15)
### Miscellaneous Chores
* release 2.6.2 ([c1f6906](https://github.com/sern-handler/handler/commit/c1f690633c55ba41db1e035b7c16f9e19c70b385))
## [2.6.1](https://github.com/sern-handler/handler/compare/v2.6.0...v2.6.1) (2023-03-17)
### Miscellaneous Chores
* release 2.6.1 ([f9609ce](https://github.com/sern-handler/handler/commit/f9609ce6cd777fa0eb595d8c48d57905bbce5966))
## [2.6.0](https://github.com/sern-handler/handler/compare/v2.5.3...v2.6.0) (2023-03-09)
### Features
* adding pure annotation for better tree shaking ([d20d015](https://github.com/sern-handler/handler/commit/d20d01524b872549da501e21feec147ab204f397))
## [2.5.3](https://github.com/sern-handler/handler/compare/v2.5.2...v2.5.3) (2023-02-16)
### Miscellaneous Chores
* release 2.5.3 ([ce9a083](https://github.com/sern-handler/handler/commit/ce9a0831a6e47dd38648f34653f0bd89b1d2e48e))
## [2.5.2](https://github.com/sern-handler/handler/compare/v2.5.1...v2.5.2) (2023-02-16)
### Reverts
* version ([facee79](https://github.com/sern-handler/handler/commit/facee79c904ad663d3c57ce56fb825419fcc12f9))
## [2.5.1](https://github.com/sern-handler/handler/compare/v2.5.0...v2.5.1) (2023-02-12)
### Features
* Adding my bot to readme ([#210](https://github.com/sern-handler/handler/issues/210)) ([96f4281](https://github.com/sern-handler/handler/commit/96f42811218e4898a47e75a8138ccd452ae2c5c2))
* Adding the WIP to my bot ([86fa531](https://github.com/sern-handler/handler/commit/86fa531eb620d2ac649bad6decb29d5c55a25445))
### Bug Fixes
* autocomplete ([1860b89](https://github.com/sern-handler/handler/commit/1860b898f3a231840e2a8b781e007ef9d6f587a4))
### Miscellaneous Chores
* release 2.5.1 ([c78936a](https://github.com/sern-handler/handler/commit/c78936a22574da4af71826f5b5f72f354a4eb06a))
## [2.5.0](https://github.com/sern-handler/handler/compare/v2.1.1...v2.5.0) (2023-01-30)
### ⚠ BREAKING CHANGES
* simpler plugins ([#193](https://github.com/sern-handler/handler/issues/193))
### Features
* simpler plugins ([#193](https://github.com/sern-handler/handler/issues/193)) ([33f1446](https://github.com/sern-handler/handler/commit/33f14467ec413e003a82503c8a77cb42a6194281))
### Miscellaneous Chores
* release 2.5.0 ([b4b195d](https://github.com/sern-handler/handler/commit/b4b195dc9586736760d0b78caa8589f3d6131f8a))
## [2.1.1](https://github.com/sern-handler/handler/compare/v2.1.0...v2.1.1) (2022-12-31)
### Bug Fixes
* modals remapping ([a13df6f](https://github.com/sern-handler/handler/commit/a13df6fb424d256476284da49024dbe56e82baab))
## [2.1.0](https://github.com/sern-handler/handler/compare/v2.0.0...v2.1.0) (2022-12-30)
### Features
* grammar ([c30aac4](https://github.com/sern-handler/handler/commit/c30aac476cdc2094de34f9f67b5805204cc5e4dd))
### Bug Fixes
* multi parameter events ([e986535](https://github.com/sern-handler/handler/commit/e98653593566ef4635493e0c997bd107a7a3a2a2))
## [2.0.0](https://github.com/sern-handler/handler/compare/v1.2.1...v2.0.0) (2022-12-28)
### ⚠ BREAKING CHANGES
* (2.0 global services) ([#156](https://github.com/sern-handler/handler/issues/156))
### Features
* (2.0 global services) ([#156](https://github.com/sern-handler/handler/issues/156)) ([1455622](https://github.com/sern-handler/handler/commit/14556223fd6f79b797fb2aee03e795d4f4e94a8b))
## [1.2.1](https://github.com/sern-handler/handler/compare/v1.2.0...v1.2.1) (2022-10-03)
### Bug Fixes
* **autocomplete:** now support multiple autocomplete options ([#147](https://github.com/sern-handler/handler/issues/147)) ([cbad738](https://github.com/sern-handler/handler/commit/cbad7380e1993b96c643f365726457f63e4fbd5d))
## [1.2.0](https://github.com/sern-handler/handler/compare/v1.1.0...v1.2.0) (2022-09-28)
### Features
* allow constructable modules ([#133](https://github.com/sern-handler/handler/issues/133)) ([03936eb](https://github.com/sern-handler/handler/commit/03936eb2ea1d1af7cada04d77bb8345d63a5e20f))
* classmodules@arcs ([#143](https://github.com/sern-handler/handler/issues/143)) ([5028886](https://github.com/sern-handler/handler/commit/50288867a5b171511941a1be3877d721694e9f77))
* update CODEOWNERS ([6b8995d](https://github.com/sern-handler/handler/commit/6b8995d149c857558415a6c151a3f575ec373445))
### Reverts
* feat of allow constructable modules ([#138](https://github.com/sern-handler/handler/issues/138)) ([82bbdda](https://github.com/sern-handler/handler/commit/82bbddac8d656b60b3a1fb2471ea03ee5224f5c3))
## [1.1.0](https://github.com/sern-handler/handler/compare/v1.0.0...v1.1.0) (2022-08-29)

View File

@@ -1,6 +1,6 @@
MIT License
**Copyright (c) 2022 Sern**
Copyright (c) 2025 sern
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -19,5 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
End license text.

120
README.md
View File

@@ -1,105 +1,79 @@
<div align="center">
<img src="https://raw.githubusercontent.com/sern-handler/.github/main/banner.png" width="900px">
<img src="https://raw.githubusercontent.com/sern-handler/.github/main/banner.png" width="900px" />
</div>
<h1 align="center">Handlers. Redefined.</h1>
<h4 align="center">A customizable, batteries-included, powerful discord.js framework to streamline bot development.</h4>
<h4 align="center">A complete, customizable, typesafe, & reactive framework for discord bots</h4>
<div align="center" style="margin-top: 10px">
<img src="https://img.shields.io/badge/open-source-brightgreen">
<div align="center" styles="margin-top: 10px">
<img src="https://img.shields.io/badge/open-source-brightgreen" />
<img src="https://img.shields.io/badge/built_with-sern-pink?labelColor=%230C3478&color=%23ed5087&link=https%3A%2F%2Fsern.dev"/>
<a href="https://www.npmjs.com/package/@sern/handler"><img src="https://img.shields.io/npm/v/@sern/handler?maxAge=3600" alt="NPM version" /></a>
<a href="https://www.npmjs.com/package/@sern/handler"><img src="https://img.shields.io/npm/dt/@sern/handler?maxAge=3600" alt="NPM downloads" /></a>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-brightgreen" alt="License MIT"></a>
<a href="https://sern-handler.js.org"><img alt="docs.rs" src="https://img.shields.io/docsrs/docs"></a>
<img alt="Lines of code" src="https://img.shields.io/badge/total%20lines-2k-blue">
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-brightgreen" alt="License MIT" /></a>
<a href="https://sern.dev"><img alt="docs.rs" src="https://img.shields.io/docsrs/docs" /></a>
<img alt="Lines of code" src="https://img.shields.io/badge/total%20lines-2k-blue" />
</div>
## Why?
- For you. A framework that's tailored to your exact needs.
- Lightweight. Does a lot while being small.
- Latest features. Support for discord.js v14 and all of its interactions.
- Start quickly. Plug and play or customize to your liking.
- Works with [bun](https://bun.sh/) and [node](https://nodejs.org/en) out the box!
- Use it with TypeScript or JavaScript. CommonJS and ESM supported.
- Active and growing community, always here to help. [Join us](https://sern.dev/discord)
- Unleash its full potential with a powerful CLI and awesome plugins.
## 📜 Installation
```sh
npm install @sern/handler
```
```sh
yarn add @sern/handler
```
```sh
pnpm add @sern/handler
```
## 👀 Quick Look
* Support for discord.js v14 and all interactions
* Hybrid commands
* lightweight and customizable
* ESM, CommonJS and TypeScript support
* A powerful cli and awesome community-made plugins
[Start here!!](https://sern.dev/v4/reference/getting-started)
## 👶 Basic Usage
<details><summary>ping.ts</summary>
#### ` index.js (CommonJS)`
```js
// Import the discord.js Client and GatewayIntentBits
const { Client, GatewayIntentBits } = require('discord.js');
// Import Sern namespace
const { Sern } = require('@sern/handler');
// Our configuration file
const { defaultPrefix, token } = require('./config.json');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildMessages
]
});
Sern.init({
client,
defaultPrefix,
commands : 'src/commands',
});
client.login(token);
```
#### ` ping.js (CommonJS)`
```js
const { CommandType } = require('@sern/handler');
exports.default = commandModule({
name: 'ping',
description: 'A ping pong command',
```ts
export default commandModule({
type: CommandType.Slash,
//Installed plugin to publish to discord api and allow access to owners only.
plugins: [publish(), ownerOnly()],
description: 'A ping pong command',
execute(ctx) {
ctx.reply('pong!');
ctx.reply('Hello owner of the bot');
}
});
```
</details>
See our [templates](https://github.com/sern-handler/templates) for TypeScript examples and more
# Show off your sern Discord Bot!
## Badge
- Copy this and add it to your [README.md](https://img.shields.io/badge/built_with-sern-pink?labelColor=%230C3478&color=%23ed5087&link=https%3A%2F%2Fsern.dev)
<img src="https://img.shields.io/badge/built_with-sern-pink?labelColor=%230C3478&color=%23ed5087&link=https%3A%2F%2Fsern.dev">
## 🤖 Bots Using sern
- [Community Bot](https://github.com/sern-handler/sern-community) - The community bot for our [Discord server](https://sern.dev/discord).
- [Vinci](https://github.com/SrIzan10/vinci) - The bot for Mara Turing.
- [Bask](https://github.com/baskbotml/bask) - Listen to your favorite artists on Discord.
- [Murayama](https://github.com/murayamabot/murayama) - :pepega:
- [Protector](https://github.com/GlitchApotamus/Protector) - Just a simple bot to help enhance a private Minecraft server.
- [SmokinWeed 💨](https://github.com/Peter-MJ-Parker/sern-bud) - A fun bot for a small, but growing server.
- [Man Nomic](https://github.com/jacoobes/man-nomic) - A simple information bot to provide information to the nomic-ai Discord community.
- [Linear-Discord](https://github.com/sern-handler/linear-discord) - Display and manage a linear dashboard.
- [ZenithBot](https://github.com/CodeCraftersHaven/ZenithBot) - A versatile bot coded in TypeScript, designed to enhance server management and user interaction through its robust features.
## 💻 CLI
It is **highly encouraged** to use the [command line interface](https://github.com/sern-handler/cli) for your project. Don't forget to view it.
## 🔗 Links
- [Official Documentation and Guide](https://sern-handler.js.org)
- [Support Server](https://discord.com/invite/mmyCTnYtbF)
- [Official Documentation and Guide](https://sern.dev)
- [Support Server](https://sern.dev/discord)
## 👋 Contribute
- Read our contribution [guidelines](https://github.com/sern-handler/handler) carefully
- Read our contribution [guidelines](https://github.com/sern-handler/handler/blob/main/.github/CONTRIBUTING.md) carefully
- Pull up on [issues](https://github.com/sern-handler/handler/issues) and report bugs
- All kinds of contributions are welcomed.
## 🚈 Roadmap
You can check our [roadmap](https://github.com/sern-handler/roadmap) to see what's going to be added or patched in the future.

4
bot/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
/node_modules
/dist
.env
.sern

6
bot/README.md Normal file
View File

@@ -0,0 +1,6 @@
# Test bot
## add .env
DISCORD_TOKEN=<token>
NODE_ENV=<production|development>

View File

@@ -0,0 +1,12 @@
{
"command/ping": {
"name": "ping",
"description": "yeth",
"options": {
"asdfs": {
"name": "shidenglish",
"description": "yeah"
}
}
}
}

View File

@@ -0,0 +1,12 @@
{
"command/ping": {
"name": "ping",
"description": "hola",
"options": {
"asdfs": {
"name": "shidspnaol",
"description": "si"
}
}
}
}

1
bot/assets/test.txt Normal file
View File

@@ -0,0 +1 @@
{ "sdfasdfas": "asdf" }

418
bot/package-lock.json generated Normal file
View File

@@ -0,0 +1,418 @@
{
"name": "plugtest",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "plugtest",
"version": "1.0.0",
"license": "UNLICENSED",
"dependencies": {
"@sern/handler": "file:../",
"@sern/localizer": "^1.1.3",
"@sern/publisher": "^1.1.2",
"discord.js": "^14.15.0",
"dotenv": "^16.4.5"
},
"devDependencies": {
"@types/node": "^17.0.25",
"typescript": "latest"
}
},
"..": {
"name": "@sern/handler",
"version": "4.2.4",
"license": "MIT",
"dependencies": {
"@sern/ioc": "^1.1.2",
"callsites": "^3.1.0",
"cron": "^3.1.7",
"deepmerge": "^4.3.1"
},
"devDependencies": {
"@faker-js/faker": "^8.0.1",
"@types/node": "^20.0.0",
"@types/node-cron": "^3.0.11",
"@typescript-eslint/eslint-plugin": "5.58.0",
"@typescript-eslint/parser": "5.59.1",
"discord.js": "^14.14.1",
"eslint": "8.39.0",
"typescript": "5.0.2",
"vitest": "^1.6.0"
},
"engines": {
"node": ">= 20.0.x"
}
},
"../../tools/packages/builder": {
"name": "@sern/builder",
"version": "1.0.0-rc1",
"extraneous": true,
"license": "ISC",
"dependencies": {
"discord-api-types": "latest"
},
"devDependencies": {
"@types/node": "^20.1.0"
}
},
"../handler": {
"name": "@sern/handler",
"version": "4.2.3",
"extraneous": true,
"license": "MIT",
"dependencies": {
"@sern/ioc": "^1.1.2",
"callsites": "^3.1.0",
"cron": "^3.1.7",
"deepmerge": "^4.3.1"
},
"devDependencies": {
"@faker-js/faker": "^8.0.1",
"@types/node": "^20.0.0",
"@types/node-cron": "^3.0.11",
"@typescript-eslint/eslint-plugin": "5.58.0",
"@typescript-eslint/parser": "5.59.1",
"discord.js": "^14.14.1",
"eslint": "8.39.0",
"typescript": "5.0.2",
"vitest": "^1.6.0"
},
"engines": {
"node": ">= 20.0.x"
}
},
"../tools/packages/builder": {
"name": "@sern/builder",
"version": "1.0.0-rc1",
"extraneous": true,
"license": "ISC",
"dependencies": {
"discord-api-types": "latest"
},
"devDependencies": {
"@types/node": "^20.1.0"
}
},
"../tools/packages/localizer": {
"name": "@sern/localizer",
"version": "1.1.1",
"extraneous": true,
"license": "ISC",
"dependencies": {
"shrimple-locales": "^0.2.1"
},
"devDependencies": {
"@sern/handler": "^4.0.0",
"discord.js": "^14.15.3",
"vitest": "^1.2.2"
}
},
"node_modules/@discordjs/builders": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.10.0.tgz",
"integrity": "sha512-ikVZsZP+3shmVJ5S1oM+7SveUCK3L9fTyfA8aJ7uD9cNQlTqF+3Irbk2Y22KXTb3C3RNUahRkSInClJMkHrINg==",
"dependencies": {
"@discordjs/formatters": "^0.6.0",
"@discordjs/util": "^1.1.1",
"@sapphire/shapeshift": "^4.0.0",
"discord-api-types": "^0.37.114",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.3"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/collection": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
"integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/formatters": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.0.tgz",
"integrity": "sha512-YIruKw4UILt/ivO4uISmrGq2GdMY6EkoTtD0oS0GvkJFRZbTSdPhzYiUILbJ/QslsvC9H9nTgGgnarnIl4jMfw==",
"dependencies": {
"discord-api-types": "^0.37.114"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.2.tgz",
"integrity": "sha512-9bOvXYLQd5IBg/kKGuEFq3cstVxAMJ6wMxO2U3wjrgO+lHv8oNCT+BBRpuzVQh7BoXKvk/gpajceGvQUiRoJ8g==",
"dependencies": {
"@discordjs/collection": "^2.1.1",
"@discordjs/util": "^1.1.1",
"@sapphire/async-queue": "^1.5.3",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "^0.37.114",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.19.8"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/util": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz",
"integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.0.tgz",
"integrity": "sha512-QH5CAFe3wHDiedbO+EI3OOiyipwWd+Q6BdoFZUw/Wf2fw5Cv2fgU/9UEtJRmJa9RecI+TAhdGPadMaEIur5yJg==",
"dependencies": {
"@discordjs/collection": "^2.1.0",
"@discordjs/rest": "^2.4.1",
"@discordjs/util": "^1.1.0",
"@sapphire/async-queue": "^1.5.2",
"@types/ws": "^8.5.10",
"@vladfrangu/async_event_emitter": "^2.2.4",
"discord-api-types": "^0.37.114",
"tslib": "^2.6.2",
"ws": "^8.17.0"
},
"engines": {
"node": ">=16.11.0"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@sapphire/async-queue": {
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz",
"integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sapphire/shapeshift": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz",
"integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"lodash": "^4.17.21"
},
"engines": {
"node": ">=v16"
}
},
"node_modules/@sapphire/snowflake": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz",
"integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/@sern/handler": {
"resolved": "..",
"link": true
},
"node_modules/@sern/localizer": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@sern/localizer/-/localizer-1.1.3.tgz",
"integrity": "sha512-hTn0DtiAzIWSuokqMsvnVuFqU+P776p/Yv5etlrq+CWDgw332Hwuj3geyqN1C0yEjwF+ceyXJE/kGu2/inkEyg==",
"dependencies": {
"shrimple-locales": "^0.2.1"
}
},
"node_modules/@sern/publisher": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@sern/publisher/-/publisher-1.1.2.tgz",
"integrity": "sha512-1zh99JZykKUhqHhE75ZXfiLsBtf1WI+NnDCojv8UlpnGBEyzO8xyI1X7PNf6cPKRs4W9XqY3PqTJ+hrqzIsMkg=="
},
"node_modules/@types/node": {
"version": "17.0.45",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
},
"node_modules/@types/ws": {
"version": "8.5.14",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz",
"integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@vladfrangu/async_event_emitter": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz",
"integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/discord-api-types": {
"version": "0.37.119",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.119.tgz",
"integrity": "sha512-WasbGFXEB+VQWXlo6IpW3oUv73Yuau1Ig4AZF/m13tXcTKnMpc/mHjpztIlz4+BM9FG9BHQkEXiPto3bKduQUg=="
},
"node_modules/discord.js": {
"version": "14.17.3",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.17.3.tgz",
"integrity": "sha512-8/j8udc3CU7dz3Eqch64UaSHoJtUT6IXK4da5ixjbav4NAXJicloWswD/iwn1ImZEMoAV3LscsdO0zhBh6H+0Q==",
"dependencies": {
"@discordjs/builders": "^1.10.0",
"@discordjs/collection": "1.5.3",
"@discordjs/formatters": "^0.6.0",
"@discordjs/rest": "^2.4.2",
"@discordjs/util": "^1.1.1",
"@discordjs/ws": "^1.2.0",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "^0.37.114",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"tslib": "^2.6.3",
"undici": "6.19.8"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/dotenv": {
"version": "16.4.7",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
"integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://dotenvx.com"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.snakecase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
},
"node_modules/magic-bytes.js": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
"integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ=="
},
"node_modules/shrimple-locales": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/shrimple-locales/-/shrimple-locales-0.2.1.tgz",
"integrity": "sha512-j2vNBDXJgED3XqGXCD/vqXBSqwlDXP1iGkseVos8mCtZqHp3R+0FImx8xwtjeYufJcYfhjBMkaBTWgsBi8eJZw=="
},
"node_modules/ts-mixer": {
"version": "6.0.4",
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
"integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
"node_modules/typescript": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz",
"integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==",
"engines": {
"node": ">=18.17"
}
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

30
bot/package.json Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "plugtest",
"version": "1.0.0",
"description": "a descriptiuon",
"main": "dist/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "sern build",
"start": "node ./dist/index.js",
"run": "node ./dist/index.js"
},
"keywords": [
"typescript",
"sern",
"discord.js"
],
"license": "UNLICENSED",
"dependencies": {
"@sern/handler": "file:../",
"@sern/localizer": "^1.1.3",
"@sern/publisher": "^1.1.2",
"discord.js": "^14.15.0",
"dotenv": "^16.4.5"
},
"devDependencies": {
"@types/node": "^17.0.25",
"typescript": "latest"
},
"type": "module"
}

11
bot/rm.py Normal file
View File

@@ -0,0 +1,11 @@
import os
for root, dirs, files in os.walk('.'):
for filename in files:
if filename.endswith('.js'):
file_path = os.path.join(root, filename)
try:
os.remove(file_path)
print(f'Successfully deleted: {file_path}')
except OSError as e:
print(f'Error deleting {file_path}: {e.strerror}')

13
bot/sern.config.json Normal file
View File

@@ -0,0 +1,13 @@
{
"language": "typescript",
"defaultPrefix": "!",
"paths": {
"base": "src",
"commands": "commands",
"events": "events"
},
"app": {
"tags": ["Nice ass bot"],
"description": "A bot"
}
}

View File

37
bot/src/commands/add.ts Normal file
View File

@@ -0,0 +1,37 @@
import { CommandType, commandModule } from "@sern/handler";
import { ApplicationCommandOptionType } from "discord.js";
export default commandModule({
name: 'add',
type: CommandType.Slash,
description: 'Adds numbers together',
options: [
{
type: ApplicationCommandOptionType.String,
name: 'numbers',
description: 'Numbers to add together separated by a space.',
required: true,
min_length: 3,
},
],
execute: async (ctx) => {
let numbers = ctx.options.getString('numbers')?.split(' ')!;
numbers = numbers.filter((num) => num !== '');
if (!numbers.every((num) => !isNaN(parseFloat(num)))) {
return ctx.reply({
content: 'You can only input numbers.',
ephemeral: true,
});
}
const sum = numbers.reduce((acc, num) => acc + parseFloat(num), 0);
return ctx.reply({
content: `The sum is ${sum}`,
ephemeral: true,
});
},
});

View File

@@ -0,0 +1,8 @@
import { filter, hasRole } from "../../plugins/filter.js";
import { ownerOnly } from "../../plugins/ownerOnly.js";
import { ADMIN } from '../../constants.js'
export default [
ownerOnly(),
filter({ condition: [hasRole(ADMIN)] })
]

View File

@@ -0,0 +1,11 @@
import { commandModule, CommandType } from '@sern/handler'
export default commandModule({
type: CommandType.Slash,
description: "A",
execute: (ctx, args) => {
}
})

14
bot/src/commands/btn.ts Normal file
View File

@@ -0,0 +1,14 @@
import { CommandType, commandModule } from "@sern/handler";
import { json } from "../plugins/json-params.js";
export default commandModule({
type: CommandType.Button,
plugins: [json],
execute(ctx, args) {
console.log(args.state['json/data'])
//@ts-ignore
ctx.reply(args.state['json/data'].uid)
}
})

View File

@@ -0,0 +1,9 @@
import { CommandType, commandModule } from "@sern/handler";
export default commandModule({
type: CommandType.ChannelSelect,
execute: (s) => {
s.reply('clicked channel');
}
});

View File

@@ -0,0 +1,25 @@
import { CommandType, commandModule } from "@sern/handler";
import { publishConfig } from "@sern/publisher";
import { PermissionFlagsBits } from "discord.js";
export default commandModule({
type: CommandType.Slash,
plugins: [
publishConfig({
integrationTypes: ['User'],
contexts: [0,1,2],
defaultMemberPermissions:
PermissionFlagsBits.Speak
| PermissionFlagsBits.Connect
| PermissionFlagsBits.BanMembers
})
],
description: "yo",
execute:(ctx) => {
ctx.reply("hello");
}
})

View File

@@ -0,0 +1,103 @@
import { CommandType, Context, commandModule } from "@sern/handler";
import { ActionRowBuilder, ButtonBuilder, ButtonStyle } from "discord.js";
export default commandModule ({
type: CommandType.Slash,
description: 'collectors',
execute: async (ctx) => {
//await close(ctx)
await testCollect(ctx)
}
})
const testCollect = async (ctx: Context) => {
const msgcmpt = ctx.interaction.channel?.createMessageComponentCollector()
const buttonRow = new ActionRowBuilder<ButtonBuilder>().addComponents(
new ButtonBuilder().setCustomId("closeyes").setLabel("Yes").setStyle(ButtonStyle.Success),
new ButtonBuilder().setCustomId("closeno").setLabel("No").setStyle(ButtonStyle.Danger)
);
ctx.reply({ components: [buttonRow] })
msgcmpt?.on('collect', async button => {
await button.deferUpdate();
if (button.customId === "closeyes") {
try {
await button.editReply('closing')
} catch (e) {
await button.editReply({ content: "An error has occurred and I could not close the ticket...", components: [] });
}
} else {
await button.editReply({ content: "This ticket will remain open.", components: [] });
msgcmpt.stop();
}
});
}
//export const quiz = async(client: Client, ctx: Context) => {
// try {
// const pokemon = Math.round(Math.random() * 890)
// const question = `https://cdn.dagpi.xyz/wtp/pokemon/${pokemon}q.png`;
// const answer = `https://cdn.dagpi.xyz/wtp/pokemon/${pokemon}a.png`;
//
// const correctPokemon = await (await fetch(`https://pokeapi.co/api/v2/pokemon/${pokemon}`)).json();
// const allPokemon = await (await fetch("https://pokeapi.co/api/v2/pokemon?limit=899")).json();
//
// const options: string[] = [];
// //client.utils.log("WARNING", "INFO", `Correct answer is ${correctPokemon.name}`);
//
// while (options.length < 9) {
// let option = allPokemon.results[pokemon];
// if (options.includes(option.name)) continue;
// options.push(option.name);
// }
//
// if (!options.includes(correctPokemon.name)) {
// options.splice(client.utils.randomRange(0, 10), 0, correctPokemon.name.toLowerCase());
// } else {
// while (options.length < 10) {
// let option = allPokemon.results[client.utils.randomRange(1, 890)];
// if (options.includes(option.name)) continue;
// options.push(option.name);
// }
// }
//
// const msgEmbed = (await client.utils.CustomEmbed({ userID: ctx.user.id })).setTitle("Who's that Pokémon?").setImage(question);
// const row = new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
// new StringSelectMenuBuilder().setCustomId("pokequiz").addOptions(
// options.sort().map((opt) => {
// return { label: client.utils.titleCase(opt), value: opt.toLowerCase() };
// })
// )
// );
//
// const msg = await client.utils.fetchReply(ctx.interaction, { embeds: [msgEmbed], components: [row] });
// const filter = (i: StringSelectMenuInteraction) => i.user.id === ctx.user.id && i.message.id === msg.id;
//
// const collector = msg.createMessageComponentCollector({ filter, componentType: ComponentType.StringSelect, time: 1000 * 20 });
// collector.on("collect", async (i) => {
// const guess = i.values[0].toLowerCase();
//
// msgEmbed.setImage(answer).setTitle(`It's ${client.utils.titleCase(correctPokemon.name)}!`);
//
// if (guess === correctPokemon.name.toLowerCase()) msgEmbed.setColor("Green").setFooter({ text: "You're correct!" });
// else msgEmbed.setColor("Red").setFooter({ text: `You guessed ${client.utils.titleCase(guess)}.` });
//
// await i.update({ embeds: [msgEmbed], components: [] });
// collector.stop("Guessed");
// });
//
// collector.on("end", async (i, reason) => {
// if (reason === "Guessed") return;
//
// msgEmbed
// .setImage(answer)
// .setTitle(`It's ${client.utils.titleCase(correctPokemon.name)}!`)
// .setColor("Red")
// .setFooter({ text: "You did not guess in time." });
//
// await ctx.interaction.editReply({ embeds: [msgEmbed], components: [] });
// });
// } catch (e) {
// client.utils.log("ERROR", __filename, `${e}`);
// return ctx.interaction.reply({ content: "An error has occurred. Please try again.", ephemeral: true });
// }
//};

6
bot/src/commands/dmMe.ts Normal file
View File

@@ -0,0 +1,6 @@
import { CommandType, commandModule } from "@sern/handler";
export default commandModule({
type: CommandType.Modal,
execute: (modal) => modal.reply('thanks')
});

View File

@@ -0,0 +1,24 @@
import { commandModule, CommandType } from "@sern/handler";
import { ApplicationCommandOptionType } from "discord.js";
export default commandModule({
description: "testing",
type: CommandType.Slash,
options: [
{
name: "option",
description: "option desc",
type: ApplicationCommandOptionType.String,
required: true,
autocomplete: true,
command: {
execute: (i) => {
i.respond([{ name: "rah", value: "rah" }]);
},
},
},
],
execute: (ctx) => {
return ctx.reply("rah");
},
});

View File

@@ -0,0 +1,52 @@
import { commandModule, CommandType } from "@sern/handler";
import { ActionRowBuilder, ApplicationCommandOptionType, ModalBuilder, TextInputBuilder, TextInputStyle } from "discord.js";
export default commandModule({
type: CommandType.Slash,
description : 'a ping command',
options: [
{
name: "nest",
description: "testing nested",
type: ApplicationCommandOptionType.SubcommandGroup,
options : [
{
name: "nest",
description: "testing nested",
type: ApplicationCommandOptionType.Subcommand,
options : [
{
name: "sdfasd",
description: "testing autocomplete",
autocomplete: true,
type: ApplicationCommandOptionType.String,
command : {
onEvent : [],
async execute(autocmp, sdt) {
//console.log(autocmp)
const choices = ['butt', 'deez', 'lmao', 'lmfao', 'nuts', 'chicken'];
await autocmp.respond(choices.map((e,i) => ({ name : e, value: i.toString()})));
}
}
}
]
},
]
},
],
async execute ({ interaction }) {
const modal = new ModalBuilder()
.setCustomId('dmMe')
.setTitle('send something to my dm (nothing bad pls)');
const input = new TextInputBuilder()
.setCustomId('message')
.setLabel("Send something to me")
.setStyle(TextInputStyle.Short);
const firstActionRow = new ActionRowBuilder<TextInputBuilder>().addComponents([input]);
modal.addComponents([firstActionRow]);
await interaction.showModal(modal);
}
});

View File

@@ -0,0 +1,8 @@
import { CommandType, commandModule } from '@sern/handler'
export default commandModule({
type: CommandType.CtxMsg,
execute: (i, sdt) => {
i.reply('pong msg')
}
})

View File

@@ -0,0 +1,9 @@
import { CommandType, commandModule } from "@sern/handler";
export default commandModule({
type: CommandType.CtxUser,
execute: (i, sdt) => {
i.reply('pong')
}
})

84
bot/src/commands/ping.ts Normal file
View File

@@ -0,0 +1,84 @@
import {commandModule, CommandType, controller, CommandInitPlugin, CommandControlPlugin } from '@sern/handler';
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ChannelSelectMenuBuilder,
RoleSelectMenuBuilder,
UserSelectMenuBuilder,
} from "discord.js";
import { localize } from '@sern/localizer';
const plugin = CommandControlPlugin(() => {
return controller.next({ a: 'from plugin1' });
});
const plugin2 = CommandControlPlugin(() => {
return controller.next({ a: 'from plugin2' });
})
const updateDescription = (description: string) => {
return CommandInitPlugin(() => {
if(description.length > 100) {
console.error("Description is invalid")
return controller.stop("From updateDescription: description is invalid");
}
return controller.next({ description }); // continue to next plugin
});
};
export default commandModule({
type: CommandType.Slash,
plugins: [localize()],
description: 'A ping command I just updated',
options: [
str(name("asdfs"),
description("sdfds"))
],
execute: async (ctx, sdt) => {
ctx.interaction
const btn = new ButtonBuilder()
.setStyle(ButtonStyle.Link)
.setLabel("Click me")
.setURL('https://www.youtube.com/watch?v=dQw4w9WgXcQ&pp=ygUIcmlja3JvbGw%3D')
const editButton = new ButtonBuilder({
customId: `btn/{"uid":"1061421834341462036"}`,
label: "click me also",
emoji: "🛠",
style: ButtonStyle.Primary,
});
ctx.reply({ components: [
new ActionRowBuilder<ButtonBuilder>().addComponents(btn, editButton),
new ActionRowBuilder<UserSelectMenuBuilder>({
components: [
new UserSelectMenuBuilder({
custom_id: "userselect",
placeholder: "select channel",
minValues: 1,
}),
],
}),
new ActionRowBuilder<ChannelSelectMenuBuilder>().addComponents(
new ChannelSelectMenuBuilder({
custom_id: "channelselect",
placeholder: "select channel",
minValues: 1,
}),
),
new ActionRowBuilder<RoleSelectMenuBuilder>({
components: [
new RoleSelectMenuBuilder({
custom_id: "roleselect",
placeholder: "select role",
minValues: 1,
}),
],
})
]})
},
});

View File

@@ -0,0 +1,9 @@
import { CommandType, commandModule } from "@sern/handler"
export default commandModule( {
type: CommandType.RoleSelect,
execute: (s) => {
s.reply('selected role')
}
})

View File

@@ -0,0 +1,12 @@
import { CommandType, commandModule } from "@sern/handler";
export default commandModule({
type: CommandType.Slash,
description: 'shid',
execute({ interaction }) {
interaction.reply('hello')
}
})

View File

@@ -0,0 +1,62 @@
import { commandModule, CommandType } from '@sern/handler';
import { ApplicationCommandOptionType } from 'discord.js';
export default commandModule({
type: CommandType.Slash,
description: 'A ping command',
options: [
{
name: "art",
type: ApplicationCommandOptionType.Subcommand,
description: "Lists out information about an Animal Crossing artwork.",
options: [
{
name: "name",
description: "The name of the artwork to lookup.",
type: ApplicationCommandOptionType.String,
autocomplete: true,
required: true,
command: {
async execute(ctx) {
await ctx.respond([{ name: 'art', value: 'first' }])
},
},
},
],
},
{
name: "villager",
type: ApplicationCommandOptionType.Subcommand,
description: "Lists out information about an Animal Crossing villager.",
options: [
{
name: "name",
description: "The name of the villager to lookup.",
type: ApplicationCommandOptionType.String,
autocomplete: true,
required: true,
command: {
onEvent: [],
async execute(ctx) {
await ctx.respond([{ name: 'villager', value: 'second' } ])
},
},
},
],
},
],
execute: async (ctx) => {
const command = ctx.options.getSubcommand();
switch (command) {
case "art": {
ctx.reply('art');
break;
}
case "villager": {
ctx.reply('vil');
break;
}
}
},
});

View File

@@ -0,0 +1,40 @@
import { ApplicationCommandOptionType } from "discord.js";
import { Service, commandModule, CommandType } from "@sern/handler";
export const config = {
guildIds: ['941002690211766332']
}
export default commandModule({
type: CommandType.Both,
description: 'tests context',
options: [
{
type: ApplicationCommandOptionType.String,
name: "hello",
description: "wassup",
required: false,
}
],
async execute(ctx) {
const logger = Service('@sern/logger');
if(ctx.isMessage()) {
logger?.info({ message : ctx.message.content })
logger?.info({ message : ctx.prefix })
} else {
logger?.info({ message : ctx.interaction.toString() })
}
logger?.info({ message: ctx.id })
logger?.info({ message: ctx.channel?.toString()! })
logger?.info({ message: ctx.user.toString()! })
logger?.info({ message: ctx.createdTimestamp.toString() })
logger?.info({ message: ctx.guild?.toString() })
logger?.info({ message: ctx.member?.toString() })
logger?.info({ message: ctx.client })
logger?.info({ message: ctx.inGuild })
await ctx.reply("guayin bodishivatta")
}
})

View File

@@ -0,0 +1,33 @@
import { CommandType, commandModule } from "@sern/handler";
import { ActionRowBuilder, ModalActionRowComponentBuilder, ModalBuilder, TextInputBuilder, TextInputStyle } from "discord.js";
const informationRequestModal = new ModalBuilder()
.setCustomId("information-request")
.setTitle("More Information")
.addComponents(
new ActionRowBuilder<ModalActionRowComponentBuilder>().addComponents(
new TextInputBuilder()
.setCustomId("command-name")
.setLabel("Command Name")
.setPlaceholder("The name of the command that this bug occurred on.")
.setStyle(TextInputStyle.Short)
.setMinLength(4)
.setMaxLength(20)
.setRequired(true)));
export default commandModule({
type: CommandType.Slash,
plugins: [],
description: "A random test command.",
execute: async (ctx) => {
await ctx.interaction.showModal(informationRequestModal);
await ctx.interaction
.awaitModalSubmit({ time: 300_000 })
.then(async (modal) => {
modal.reply("thanks brody")
})
.catch(() => null);
},
});

View File

@@ -0,0 +1,8 @@
import { CommandType, commandModule } from "@sern/handler";
export default commandModule( {
type: CommandType.UserSelect,
execute: (s) => {
s.reply('selected user')
}
})

1
bot/src/constants.ts Normal file
View File

@@ -0,0 +1 @@
export const ADMIN = '983754333944434712'

18
bot/src/dependencies.d.ts vendored Normal file
View File

@@ -0,0 +1,18 @@
import type {
Logging,
ErrorHandling,
CoreDependencies
} from '@sern/handler'
import type { Publisher } from '@sern/publisher';
import type { Localizer } from '@sern/localizer';
declare global {
interface Dependencies extends CoreDependencies {
localizer: Localizer;
publisher: Publisher
}
}
export {}

10
bot/src/events/error.ts Normal file
View File

@@ -0,0 +1,10 @@
import { EventType, eventModule } from "@sern/handler";
export default eventModule({
name: 'error',
type: EventType.Sern,
execute: (e) => {
console.log(e)
}
})

View File

@@ -0,0 +1,10 @@
import { discordEvent } from "@sern/handler";
const execute = (...args: any[]) => {
console.log(args[0].content)
}
export default discordEvent({
name: 'messageCreate',
once: true,
execute
})

View File

@@ -0,0 +1,10 @@
import {eventModule, EventType} from "@sern/handler";
export default eventModule({
type: EventType.Sern,
name: 'module.activate',
execute(args) {
}
})

View File

@@ -0,0 +1,9 @@
import { CommandType, EventType, Service, eventModule } from "@sern/handler";
export default eventModule({
type: EventType.Sern,
execute: async () => {
console.log('eventmodule: all loaded');
}
})

View File

@@ -0,0 +1,8 @@
import { discordEvent } from "@sern/handler";
export default discordEvent({
name: 'threadCreate',
execute(thread) {
console.log(thread)
}
})

41
bot/src/index.ts Normal file
View File

@@ -0,0 +1,41 @@
import 'dotenv/config';
import { makeDependencies, Sern, Service } from '@sern/handler'
import { Client, GatewayIntentBits, Partials } from 'discord.js';
import { Publisher } from '@sern/publisher'
import { Localization } from '@sern/localizer'
__DEV__: console.log(1);
const intents = GatewayIntentBits.Guilds |
GatewayIntentBits.GuildMembers |
GatewayIntentBits.GuildMessageReactions |
GatewayIntentBits.GuildMessages |
GatewayIntentBits.DirectMessages |
GatewayIntentBits.MessageContent;
const partials = [
Partials.Channel
];
async function init() {
await makeDependencies(({ add }) => {
add('@sern/client', new Client({ intents, partials }));
add('localizer', Localization());
add('publisher', deps => {
return new Publisher(deps['@sern/modules'],
deps['@sern/emitter'],
deps['@sern/logger']!)
})
})
Sern.init({
commands : "./dist/commands",
events: "./dist/events",
tasks: "./dist/tasks",
defaultPrefix: "!"
})
}
init().then(() => {
Service('@sern/client').login()
})
//View docs for all options

156
bot/src/plugins/args.ts Normal file
View File

@@ -0,0 +1,156 @@
/**
* @author HighArcs
* @version 1.0.0
* @description converts array of argument strings to an object (and maps them)
* @license null
* @example
* ```ts
* import { parsedCommandModule, args } from "../plugins/args";
* import { CommandType } from "@sern/handler";
*
* interface Arg {
* value: number;
* }
*
* export default parsedCommandModule({
* type : CommandType.Text
* plugins: [args({ value: Number })],
* execute: (ctx, args) => {
* console.log(ctx.args.value);
* }
* })
*/
import {
commandModule,
CommandType,
Context, ControlPlugin,
Plugin, CommandControlPlugin, controller
} from "@sern/handler";
import type { Awaitable } from "discord.js";
type Converter<T> = (value?: string) => Awaitable<T>;
type Struct = Record<string, any>;
type ConverterList<T extends Struct> = {
[K in keyof T]: Converter<T[K]>;
};
type Ctx<T> = Context & { _args: T };
interface Err {
key: string;
error: string;
given: string;
index: number;
}
type OnError<T> = (context: Ctx<T>, error: Err) => any;
type SpecialEvt<T> = {
readonly "@@plugin": symbol
} & ControlPlugin<[Ctx<T>, ]>
async function convert<T extends Struct>(
args: Array<string>,
struct: ConverterList<T>
) {
const entries = Object.entries(struct);
const result = {} as T;
for (let i = 0; i < entries.length; i++) {
const value = args[i];
const [key, converter] = entries[i]!;
try {
result[key as keyof T] = await converter(value);
} catch (error) {
throw { key, error: String(error), given: value, index: i };
}
}
return result;
}
interface ParsedInputCommandModule<T extends Struct> {
name?: string;
description: string;
type: CommandType.Both | CommandType.Text | CommandType.Slash;
execute: (context: Ctx<T>, args: Array<string>) => any;
plugins: () =>
| [SpecialEvt<T>, ...Array<Plugin>]
| []
| undefined;
}
export const Structs = {
string: (value: string) => String(value),
number: (value: string) => Number(value),
boolean: (value: string) => value === "true" || value === "1",
date: (value: string) => new Date(value),
integer: (value: string) => Number.parseInt(value),
};
export function parsedCommandModule<T extends Struct>(
a: ParsedInputCommandModule<T>
) {
const plugins = (a.plugins() ?? []);
return commandModule({ ...a, plugins } as never);
}
export namespace Checks {
export function choices<K extends string>(
choices: K[],
value?: string
): asserts value is K {
if (!choices.includes(value as unknown as K)) {
throw "value is not in choices";
}
}
export function required(value?: string): asserts value is string {
if (value === undefined) {
throw "value is required";
}
}
export function limit(min: number, max: number, value?: string) {
required(value);
const val = Structs.number(value);
if (val < min) {
throw `value must be higher than ${min}`;
}
if (val > max) {
throw `value must be lower than ${max}`;
}
return val;
}
}
export function args<T extends Struct>(
struct: ConverterList<T>,
onError?: OnError<T>
): SpecialEvt<T> {
const plugin = CommandControlPlugin<CommandType.Both>(async (ctx, args) => {
switch(args.type) {
case "slash": {
let result: T;
} break;
case "text" : {
let result: T;
try {
result = await convert(args, struct)
} catch (e) {
if (onError) {
onError(ctx as Ctx<T>, e as Err);
}
return controller.stop();
}
//@warn - mutable assignment!
(ctx as Ctx<T>)._args = result;
return controller.next();
}
}
return controller.next()
})
Object.defineProperty(plugin, "@@plugin", { value: Symbol("args") })
return plugin as SpecialEvt<T>;
}

View File

@@ -0,0 +1,57 @@
/**
* This plugin checks the fields of a ModalSubmitInteraction
* with regex or a custom callback
*
* @author @jacoobes [<@182326315813306368>]
* @version 1.0.0
* @example
* ```ts
* export default commandModule({
* type: CommandType.Modal,
* plugins: [
* assertFields({
* fields: {
* // check the modal field "mcUsernameInput" with the regex /a+b+c/
* mcUsernameInput: /a+b+c+/
* },
* failure: (errors, interaction) => {
* interaction.reply(errors.join("\n"))
* }
* }),
* ],
* execute: ctx => {
* ctx.reply("nice!")
* }
* })
* ```
*/
import { CommandControlPlugin, CommandType, controller } from "@sern/handler";
import type { ModalSubmitInteraction } from "discord.js";
type Assertion =
| RegExp
| ((value : string) => boolean);
export function assertFields(config: {
fields: Record<string, Assertion>,
failure: (errors: string[], interaction: ModalSubmitInteraction) => any
}) {
return CommandControlPlugin<CommandType.Modal>(modal => {
const pairs = Object.entries(config.fields);
const errors = [];
for(const [ field, assertion ] of pairs) {
// Keep in mind this doesn't check for typos!
// feel free to add more checks.
const input = modal.fields.getTextInputValue(field)
const resolvedAssertion = assertion instanceof RegExp ? (value: string) => assertion.test(value) : assertion;
if(!resolvedAssertion(input)) {
errors.push(input + " failed to pass assertion " + resolvedAssertion.toString() )
}
}
if(errors.length > 0) {
config.failure(errors, modal);
return controller.stop();
}
return controller.next();
})
}

View File

@@ -0,0 +1,39 @@
/**
* This plugin checks if a channel is the specified type
*
* @author @Benzo-Fury [<@762918086349029386>]
* @version 1.0.0
* @example
* ```ts
* import { channelType } from "../plugins/channelType";
* import { ChannelType } from "discord.js"
* import { commandModule } from "@sern/handler";
* export default commandModule({
* plugins: [ channelType([ChannelType.GuildText], 'This cannot be used here') ],
* execute: (ctx) => {
* //your code here
* }
* })
* ```
*/
import { ChannelType } from "discord.js";
import {CommandControlPlugin, CommandType, controller } from "@sern/handler";
export function channelType(
channelType: ChannelType[],
onFail?: string
){
return CommandControlPlugin<CommandType.Both>(async (ctx) => {
let channel = ctx.channel?.type;
//for some reason the dm channel type was returning undefined at some points
if (channel === undefined) {
channel = ChannelType.DM;
}
if (channelType.includes(channel)) {
return controller.next();
}
if (onFail) {
await ctx.reply(onFail);
}
return controller.stop();
})
}

View File

@@ -0,0 +1,106 @@
/**
* This is buttonConfirmation plugin, it runs confirmation prompt in the form of buttons.
* Note that you need to use edit/editReply in the command itself because we are already replying in the plugin!
* Credits to original plugin of confirmation using reactions and its author!
*
* @author @EvolutionX-10 [<@697795666373640213>]
* @version 1.0.0
* @example
* ```ts
* import { buttonConfirmation } from "../plugins/buttonConfirmation";
* import { commandModule } from "@sern/handler";
* export default commandModule({
* plugins: [ buttonConfirmation() ],
* execute: (ctx) => {
* //your code here
* }
* })
* ```
*/
import {CommandControlPlugin, CommandType, controller} from "@sern/handler";
import {
ActionRowBuilder,
ButtonBuilder,
ButtonStyle,
ComponentType,
} from "discord.js";
export function confirmation(
options?: Partial<ConfirmationOptions>
) {
return CommandControlPlugin<CommandType.Both>(async (ctx, args) => {
options = {
content: "Do you want to proceed?",
denialMessage: "Cancelled",
labels: ["No", "Yes"],
time: 60_000,
wrongUserResponse: "Not for you!",
...options,
};
const buttons = options.labels!.map((l, i) => {
return new ButtonBuilder()
.setCustomId(l)
.setLabel(l)
.setStyle( i === 0 ? ButtonStyle.Danger : ButtonStyle.Success
);
});
const sent = await ctx.reply({
content: options.content,
components: [
new ActionRowBuilder<ButtonBuilder>().setComponents(
buttons
),
],
});
const collector = sent.createMessageComponentCollector({
componentType: ComponentType.Button,
filter: (i) => i.user.id === ctx.user.id,
time: options.time,
});
return new Promise((resolve) => {
collector.on("collect", async (i) => {
await i.update({ components: [] });
collector.stop();
if (i.customId === options!.labels![1]) {
resolve(controller.next());
return;
}
await i.editReply({
content: options?.denialMessage,
});
resolve(controller.stop());
});
collector.on("end", async (c) => {
if (c.size) return;
buttons.forEach((b) => b.setDisabled());
await sent.edit({
components: [
new ActionRowBuilder<ButtonBuilder>().setComponents(
buttons
),
],
});
});
collector.on("ignore", async (i) => {
await i.reply({
content: options?.wrongUserResponse,
ephemeral: true,
});
});
});
});
}
interface ConfirmationOptions {
content: string;
denialMessage: string;
time: number;
labels: [string, string];
wrongUserResponse: string;
}

View File

@@ -0,0 +1,6 @@
import {CommandInitPlugin, controller} from "@sern/handler";
export const correctFile = CommandInitPlugin(() => {
return controller.stop()
})

View File

@@ -0,0 +1,38 @@
// @ts-nocheck
/**
* Disables a command entirely, for whatever reasons you may need.
*
* @author @jacoobes [<@182326315813306368>]
* @version 1.0.0
* @example
* ```ts
* import { disable } from "../plugins/disable";
* import { commandModule } from "@sern/handler";
* export default commandModule({
* plugins: [ disable() ],
* execute: (ctx) => {
* //your code here
* }
* })
* ```
*/
import { CommandType, EventPlugin, PluginType } from "@sern/handler";
import { InteractionReplyOptions, ReplyMessageOptions } from "discord.js";
export function disable(
onFail?:
| string
| Omit<InteractionReplyOptions, "fetchReply">
| ReplyMessageOptions
): EventPlugin<CommandType.Both> {
return {
type: PluginType.Event,
description: "Disables command from responding",
async execute([ctx], controller) {
if (onFail !== undefined) {
await ctx.reply(onFail);
}
return controller.stop();
},
};
}

29
bot/src/plugins/dmOnly.ts Normal file
View File

@@ -0,0 +1,29 @@
/**
* This is dmOnly plugin, it allows commands to be run only in DMs.
* For discord.js you should have the Partials.Channel and DirectMessages intent enabled.
* @author @EvolutionX-10 [<@697795666373640213>]
* @version 1.0.0
* @example
* ```ts
* import { dmOnly } from "../plugins/dmOnly";
* import { commandModule } from "@sern/handler";
* export default commandModule({
* plugins: [dmOnly()],
* execute: (ctx) => {
* //your code here
* }
* })
* ```
*/
import {CommandControlPlugin, CommandType, controller } from "@sern/handler";
export function dmOnly(
content?: string,
ephemeral?: boolean
) {
return CommandControlPlugin<CommandType.Both>(async (ctx, _) => {
if (ctx.channel?.isDMBased()) return controller.next();
if (content) await ctx.reply({ content, ephemeral }); // Change this if you want or remove it for silent deny
return controller.stop();
})
}

637
bot/src/plugins/filter.ts Normal file
View File

@@ -0,0 +1,637 @@
import {
CommandControlPlugin,
type CommandType,
type Context,
controller,
} from "@sern/handler";
import {
GuildMember,
GuildMemberRoleManager,
PermissionResolvable,
PermissionsBitField,
User,
} from "discord.js";
export type Test = (context: Context) => boolean;
export class Criteria {
public constructor(
public readonly name: string,
public readonly execute: Test,
public readonly children: Array<Criteria>
) {}
toString() {
return this.name + ' ' + this.children.map(c => c.name).join(', ')
}
}
export const or = (...filters: Array<FilterImpl>): FilterImpl => {
function execute(context: Context): boolean {
let pass = false;
tests: for (const filter of filters) {
if (filter.test(context)) {
pass = true;
break tests;
}
}
return pass;
}
const children: Array<Criteria> = filters.map((x) => x.criteria);
return new FilterImpl(
new Criteria("or", execute, children),
`or(${filters.map((x) => x.message).join(", ")})`
);
}
export const and = (...filters: Array<FilterImpl>): FilterImpl => {
function execute(context: Context): boolean {
for (const filter of filters) {
if (!filter.test(context)) {
return false;
}
}
return true;
}
const children: Array<Criteria> = filters.map((x) => x.criteria);
return new FilterImpl(
new Criteria("and", execute, children),
`and(${filters.map((x) => x.message).join(", ")})`
);
}
export const not = (filter: FilterImpl): FilterImpl => {
function execute(context: Context): boolean {
return !filter.test(context);
}
return new FilterImpl(
new Criteria("not", execute, [filter.criteria]),
`not(${filter.criteria})`
);
}
export const custom =(execute: Test, message?: string): FilterImpl => {
return new FilterImpl(new Criteria("custom", execute, []), message);
}
export const withCustomMessage = (
filter: FilterImpl,
message?: string
): FilterImpl => {
return new FilterImpl(filter.criteria, message);
}
export const hasGuildPermission = (
permission: PermissionResolvable
): FilterImpl => {
const b = PermissionsBitField.resolve(permission);
const field = Object.entries(PermissionsBitField.Flags).find(
([, v]) => v === b
);
if (field === undefined) {
throw new Error(
`unknown permission \`${permission}\` in filter \`hasGuildPermission\``
);
}
const [name] = field;
function execute(context: Context): boolean {
if (context.member !== null) {
if (typeof context.member.permissions === "string") {
return new PermissionsBitField(BigInt(context.member.permissions)).has(b);
}
return context.member.permissions.has(b);
}
return true;
}
return new FilterImpl(
new Criteria("hasGuildPermission", execute, []),
`has guild permission: ${name}`
);
}
export const hasChannelPermission = (
permission: PermissionResolvable,
channelId?: string
): FilterImpl => {
const b = PermissionsBitField.resolve(permission);
const field = Object.entries(PermissionsBitField.Flags).find(
([, v]) => v === b
);
if (field === undefined) {
throw new Error(
`unknown permission \`${permission}\` in filter \`hasChannelPermission\``
);
}
const [name] = field;
function execute(context: Context): boolean {
if (context.member !== null) {
const channel =
channelId !== undefined
? context.guild?.channels.cache.get(channelId)
: context.channel;
// ?
if (channel == undefined || channel === null) {
return false;
}
if (channel.isDMBased()) {
return true;
}
const field2 = channel.permissionsFor(context.user);
// assume we have no permission overrides
if (field2 === null) {
if (context.member !== null) {
if (typeof context.member.permissions === "string") {
return new PermissionsBitField(
BigInt(context.member.permissions)
).has(b);
}
return context.member.permissions.has(b);
}
return false;
}
return field2.has(b);
}
return true;
}
return new FilterImpl(
new Criteria("hasChannelPermission", execute, []),
channelId !== undefined
? `has channel permission ${name} in <#${channelId}>`
: `has channel permission ${name}`
);
}
export const canAddReactions =(channelId?: string): FilterImpl => {
return hasChannelPermission("AddReactions", channelId);
}
export const canAttachFiles =(channelId?: string): FilterImpl => {
return hasChannelPermission("AttachFiles", channelId);
}
export const canBanMembers = (): FilterImpl => {
return hasGuildPermission("BanMembers");
}
export const canChangeNickname = (): FilterImpl => {
return hasGuildPermission("ChangeNickname");
}
export const canConnect = (channelId?: string): FilterImpl => {
return hasChannelPermission("Connect", channelId);
}
export const canCreateInstantInvite =(channelId?: string): FilterImpl => {
return hasChannelPermission("CreateInstantInvite", channelId);
}
export const canDeafenMembers =(channelId?: string): FilterImpl => {
return hasChannelPermission("DeafenMembers", channelId);
}
export const canEmbedLinks =(channelId?: string): FilterImpl => {
return hasChannelPermission("EmbedLinks", channelId);
}
export const canKickMembers =(): FilterImpl => {
return hasGuildPermission("KickMembers");
}
export const canManageChannelWebhooks =(channelId?: string): FilterImpl => {
return hasChannelPermission("ManageWebhooks", channelId);
}
export const canManageChannels =(channelId?: string): FilterImpl => {
return hasChannelPermission("ManageChannels", channelId);
}
export const canManageEmojisAndStickers =(): FilterImpl => {
return hasGuildPermission("ManageEmojisAndStickers");
}
export const canManageGuild =(): FilterImpl => {
return hasGuildPermission("ManageGuild");
}
export const canManageGuildWebhooks =(): FilterImpl => {
return hasGuildPermission("ManageWebhooks");
}
export const canManageMessages =(channelId?: string): FilterImpl => {
return hasChannelPermission("ManageMessages", channelId);
}
export const canManageNicknames = (): FilterImpl => {
return hasGuildPermission("ManageNicknames");
}
export const canManageRoles = (): FilterImpl => {
return hasGuildPermission("ManageRoles");
}
export const canMentionEveryone = (channelId?: string): FilterImpl => {
return hasChannelPermission("MentionEveryone", channelId);
}
export const canMoveMembers = (channelId?: string): FilterImpl => {
return hasChannelPermission("MoveMembers", channelId);
}
export const canMuteMembers = (channelId?: string): FilterImpl => {
return hasChannelPermission("MuteMembers", channelId);
}
export const canPrioritySpeaker = (channelId?: string): FilterImpl => {
return hasChannelPermission("PrioritySpeaker", channelId);
}
export const canReadMessageHistory = (channelId?: string): FilterImpl => {
return hasChannelPermission("ReadMessageHistory", channelId);
}
export const canViewChannel = (channelId: string): FilterImpl => {
return hasChannelPermission("ViewChannel", channelId);
}
export const canSendMessages = (channelId: string): FilterImpl => {
return hasChannelPermission("SendMessages", channelId);
}
export const canSendTtsMessages = (channelId?: string): FilterImpl => {
return hasChannelPermission("SendTTSMessages", channelId);
}
export const canSpeak = (channelId?: string): FilterImpl => {
return hasChannelPermission("Speak", channelId);
}
export const canStream = (channelId?: string): FilterImpl => {
return hasChannelPermission("Stream", channelId);
}
export const canUseExternalEmojis = (channelId?: string): FilterImpl => {
return hasChannelPermission("UseExternalEmojis", channelId);
}
export const canUseVoiceActivity = (channelId?: string): FilterImpl => {
return hasChannelPermission("UseVAD", channelId);
}
export const canViewAuditLog = (): FilterImpl => {
return hasGuildPermission("ViewAuditLog");
}
export const canViewGuildInsights = (): FilterImpl => {
return hasGuildPermission("ViewGuildInsights");
}
export const channelIdIn = (channelIds: Array<string>): FilterImpl => {
function execute(context: Context): boolean {
return channelIds.includes(
context.isMessage()
? context.message.channelId
: context.interaction.channelId
);
}
return new FilterImpl(
new Criteria("channelIdIn", execute, []),
`channel is one of: ${channelIds.map((v) => `<#${v}>`).join(", ")}`
);
}
export const hasEveryRole = (roles: Array<string>): FilterImpl => {
return withCustomMessage(
and(...roles.map((v) => hasRole(v))),
`has all of: ${roles.map((v) => `<@&${v}>`).join(", ")}`
);
}
export const hasMentionableRole = (): FilterImpl => {
function execute(context: Context): boolean {
if (context.member !== null) {
if (context.member.roles instanceof GuildMemberRoleManager) {
return (
context.member.roles.cache.filter((x) => x.mentionable === true)
.size > 0
);
}
if (context.guild === null) {
return false;
}
return context.member.roles
.map((roleId) => context.guild!.roles.cache.get(roleId))
.filter((x) => x !== undefined)
.some((x) => x!.mentionable);
}
return false;
}
return new FilterImpl(
new Criteria("hasMentionableRole", execute, []),
"has a mentionable role"
);
}
export const hasNickname = (nickname?: string): FilterImpl => {
function execute(context: Context): boolean {
if (context.member !== null) {
if (context.member instanceof GuildMember) {
if (nickname !== null) {
return context.member.nickname === nickname;
}
return context.member.nickname !== null;
}
if (nickname !== null) {
return context.member.nick === nickname;
}
return (
context.member.nick !== null && context.member.nick !== undefined
);
}
// dm members can technically have nicknames but they're per-user, so this should never be true.
return false;
}
return new FilterImpl(new Criteria("hasNickname", execute, []), "has a nickname");
}
export const hasParentId = (parentId: string): FilterImpl => {
function execute(context: Context): boolean {
if (context.channel !== null) {
if (context.channel.isDMBased()) {
return false;
}
return context.channel.parentId === parentId;
}
return false;
}
return new FilterImpl(
new Criteria("hasParentId", execute, []),
`has channel parent <#${parentId}>`
);
}
export const hasRole = (roleId: string): FilterImpl => {
function execute(context: Context): boolean {
if (context.member !== null) {
if (context.member.roles instanceof GuildMemberRoleManager) {
return context.member.roles.cache.has(roleId);
}
if (context.guild === null) {
return false;
}
return context.member.roles.includes(roleId);
}
// assume dm members have every role ever.
return true;
}
return new FilterImpl(
new Criteria("hasRole", execute, []),
`has role <@&${roleId}>`
);
}
export const hasSomeRole = (roles: Array<string>): FilterImpl => {
return withCustomMessage(
or(...roles.map((role) => hasRole(role))),
`has any of: ${roles.map((v) => `<@&${v}>`).join(", ")}`
);
}
export const isAdministator = (): FilterImpl => {
return hasGuildPermission("Administrator");
}
export const isChannelId = (channelId: string): FilterImpl => {
function execute(context: Context): boolean {
if (context.isMessage()) {
return context.message.channelId === channelId;
}
return context.interaction.channelId === channelId;
}
return new FilterImpl(
new Criteria("isChannelId", execute, []),
`is channel <#${channelId}>`
);
}
export const isChannelNsfw = (): FilterImpl => {
function execute(context: Context): boolean {
if (context.channel !== null) {
if (context.channel.isDMBased() || context.channel.isThread()) {
return false;
}
return context.channel.nsfw;
}
return false;
}
return new FilterImpl(
new Criteria("isChannelNsfw", execute, []),
"channel marked as nsfw"
);
}
export const isGuildOwner = (): FilterImpl => {
function execute(context: Context): boolean {
if (context.guild !== null) {
return context.guild.ownerId === context.user.id;
}
return true;
}
return new FilterImpl(
new Criteria("isGuildOwner", execute, []),
"is guild owner"
);
}
export const isBotOwner = (): FilterImpl => {
function execute(context: Context): boolean {
if (context.client.application !== null) {
if (context.client.application.owner !== null) {
if (context.client.application.owner instanceof User) {
return context.user.id === context.client.application.owner.id;
}
return context.client.application.owner.members.has(context.user.id);
}
}
// nope
return false;
}
return new FilterImpl(new Criteria("isBotOwner", execute, []), "is bot owner");
}
export const isUserId = (userId: string): FilterImpl => {
function execute(context: Context): boolean {
return context.user.id === userId;
}
return new FilterImpl(
new Criteria("isUserId", execute, []),
`is user: <@${userId}>`
);
}
export const parentIdIn = (parentIds: Array<string>): FilterImpl => {
return withCustomMessage(
or(...parentIds.map((v) => hasParentId(v))),
`channel parent is one of: ${parentIds.map((v) => `<#${v}>`).join(", ")}`
);
}
export const userIdIn = (userIds: Array<string>): FilterImpl => {
return withCustomMessage(
or(...userIds.map((v) => isUserId(v))),
`user is one of: ${userIds.map((v) => `<@${v}>`).join(", ")}`
);
}
export const isInGuild = (): FilterImpl => {
function execute(context: Context): boolean {
return context.guildId !== null;
}
return new FilterImpl(new Criteria("isInGuild", execute, []), "is in guild");
}
export const isInDm = (): FilterImpl => {
const notInGuild = compose(not, isInGuild);
return withCustomMessage(notInGuild(), "is in dm");
}
export const never = (): FilterImpl => {
function execute(context: Context): boolean {
void context;
return false;
}
return new FilterImpl(new Criteria("never", execute, []), "never");
}
export const always = (): FilterImpl => {
function execute(context: Context): boolean {
void context;
return true;
}
return new FilterImpl(new Criteria("always", execute, []), "always");
}
type CtxMap<T> = (arg: T) => FilterImpl;
/**
* Call FilterImpls in right to left order.
* @example
* import { compose, isUserId, not } from '../plugins/filter'
* const isNotUserId = compose(not, isUserId)
*
*/
export const compose = <T = void>(...funcs: CtxMap<any>[]): CtxMap<T> => {
return (arg: T): FilterImpl =>
//@ts-ignore
funcs.reduceRight((result, func) => func(result), arg);
}
export class FilterImpl {
public readonly test: Test;
public constructor(
public readonly criteria: Criteria,
public message?: string
) {
this.test = this.criteria.execute;
}
}
export type FilterOptions = {
condition: Array<FilterImpl> | FilterImpl,
onFailed?: (context: Context, filters: Array<FilterImpl>) => unknown
};
/**
* Generalized `filter` plugin. revised by jacoobes, all credit to original author.
* Perform declarative conditionals as plugins.
* @author @trueharuu [<@504698587221852172>]
* @version 2.0.0
* @example
* import { filter, not, isGuildOwner, canMentionEveryone } from '../plugins/filter';
* import { commandModule } from '@sern/handler';
*
* export default commandModule({
* plugins: filter({ condition: [not(isGuildOwner()), canMentionEveryone()] }),
* async execute(context) {
* // your code here
* }
* });
*/
export const filter =
(options: FilterOptions) => {
return CommandControlPlugin<CommandType.Both>(async (context) => {
const arrayifiedCondition = Array.isArray(options.condition) ? options.condition : [options.condition]
const value = and(...arrayifiedCondition).test(context);
if (value) {
return controller.next();
}
if (options.onFailed !== undefined) {
await options.onFailed(context, arrayifiedCondition);
} else {
await context.reply({
ephemeral: true,
content: `you do not match the criteria for this command:\n${arrayifiedCondition
.map((x) => x.message)
.filter((x) => x !== undefined)
.join("\n")}`,
allowedMentions: {
repliedUser: false,
parse: [],
},
});
}
return controller.stop();
});
};

View File

@@ -0,0 +1,40 @@
import { PluginType, makePlugin, controller, ControlPlugin } from "@sern/handler";
import type { AutocompleteInteraction } from 'discord.js'
/**
* @plugin
* filters autocomplete interaction that pass the criteria
* @author @jacoobes [<@182326315813306368>]
* @version 1.0.0
* @example
* ```ts
* import { CommandType, commandModule } from "@sern/handler";
* import { filterA } from '../plugins/filterA.js'
* export default commandModule({
* type : CommandType.Slash,
* options: [
* {
* autocomplete: true,
* command : {
* //only accept autocomplete interactions that include 'poo' in the text
* onEvent: [filterA(s => s.includes('poo'))],
* execute: (autocomplete) => {
* let data = [{ name: 'pooba', value: 'first' }, { name: 'pooga', value: 'second' }]
* autocomplete.respond(data)
* }
* }
* }
* ],
* execute: (ctx, args) => {}
* })
* @end
*/
export const filterA = (pred: (value: string) => boolean) => {
return makePlugin(PluginType.Control, (a: AutocompleteInteraction) => {
if(pred(a.options.getFocused())) {
return controller.next();
}
return controller.stop();
}) as ControlPlugin;
}

View File

@@ -0,0 +1,36 @@
//@ts-nocheck
/**
* @plugin
* fromCallback turns a callback into a plugin result.
* if the callback returns truthy value, plugin continues.
* This control plugin works for every command type. The arguments of the callback
* mirror the execute method on the current module.
* @author @jacoobes [<@182326315813306368>]
* @version 1.0.0
* @example
* ```ts
* const myServer = "941002690211766332";
* export default commandModule({
* type: CommandType.Both,
* plugins: [
* fromCallback((ctx, args) => ctx.guildId == myServer)
* ],
* execute: ctx => {
* ctx.reply("I only respond in myServer!");
* }
* })
* ```
* @end
*/
import { PluginType, makePlugin, controller } from "@sern/handler";
export const fromCallback = (cb: (...args: any[]) => boolean) =>
makePlugin(PluginType.Control, (...args) => {
console.log(args)
if(cb.apply(null, args)) {
return controller.next();
}
return controller.stop();
});

View File

@@ -0,0 +1,5 @@
import { CommandControlPlugin, CommandType, controller } from "@sern/handler";
export const json = CommandControlPlugin<CommandType.Button>((ctx, args) => {
return controller.next({ 'json/data': JSON.parse(args.params!) });
})

View File

@@ -0,0 +1,48 @@
/**
* This plugin checks if the channel is nsfw and responds to user with a specified response if not nsfw
*
* @author @Benzo-Fury [<@762918086349029386>]
* @version 1.0.0
* @example
* ```ts
* import { nsfwOnly } from "../plugins/nsfwOnly";
* import { commandModule } from "@sern/handler";
* export default commandModule({
* plugins: [ nsfwOnly('response', true) ],
* execute: (ctx) => {
* //your code here
* }
* })
* ```
*/
import {
ChannelType,
GuildTextBasedChannel,
TextBasedChannel,
TextChannel,
} from "discord.js";
import {CommandControlPlugin, CommandType, controller } from "@sern/handler";
function isGuildText(channel: TextBasedChannel|null): channel is GuildTextBasedChannel {
return (channel?.type == ChannelType.GuildPublicThread ||
channel?.type == ChannelType.GuildPrivateThread);
}
export function nsfwOnly(onFail: string, ephemeral: boolean) {
return CommandControlPlugin<CommandType.Both>(async (ctx, _) => {
if (ctx.guild === null) {
await ctx.reply({ content: onFail, ephemeral });
return controller.stop();
}
//channel is thread (not supported by nsfw)
if (isGuildText(ctx.channel) == true) {
await ctx.reply({ content: onFail, ephemeral });
return controller.stop();
}
if (!(ctx.channel! as TextChannel).nsfw) {
//channel is not nsfw
await ctx.reply({ content: onFail, ephemeral });
return controller.stop();
}
//continues to command if nsfw
return controller.next();
});
}

View File

@@ -0,0 +1,30 @@
// @ts-nocheck
/**
* This is OwnerOnly plugin, it allows only bot owners to run the command, like eval.
*
* @author @EvolutionX-10 [<@697795666373640213>]
* @version 1.2.0
* @example
* ```ts
* import { ownerOnly } from "../plugins/ownerOnly";
* import { commandModule } from "@sern/handler";
* export default commandModule({
* plugins: [ ownerOnly() ], // can also pass array of IDs to override default owner IDs
* execute: (ctx) => {
* //your code here
* }
* })
* ```
*/
import { CommandType, CommandControlPlugin, controller } from "@sern/handler";
const ownerIDs = ["182326315813306368"]; //! Fill your ID
export function ownerOnly(override?: string[]) {
return CommandControlPlugin<CommandType.Both>((ctx) => {
if ((override ?? ownerIDs).includes(ctx.user.id))
return controller.next();
//* If you want to reply when the command fails due to user not being owner, you can use following
// await ctx.reply("Only owner can run it!!!");
return controller.stop(); //! Important: It stops the execution of command!
});
}

View File

@@ -0,0 +1,39 @@
// @ts-nocheck
/**
* @plugin
* This is perm check, it allows users to parse the permission you want and let the plugin do the rest. (check user for that perm).
*
* @author @Benzo-Fury [<@762918086349029386>]
* @version 1.0.1
* @example
* ```ts
* import { permCheck } from "../plugins/permCheck";
* import { commandModule } from "@sern/handler";
* export default commandModule({
* plugins: [ permCheck('permission', 'No permission response') ],
* execute: (ctx) => {
* //your code here
* }
* })
* ```
* @end
*/
import type { GuildMember, PermissionResolvable } from "discord.js";
import { CommandControlPlugin, CommandType, controller } from "@sern/handler";
export function permCheck(perm: PermissionResolvable, response: string) {
return CommandControlPlugin<CommandType.Both>(async (ctx, args) => {
if (ctx.guild === null) {
await ctx.reply("This command cannot be used here");
console.warn(
"PermCheck > A command stopped because we couldn't check a users permissions (was used in dms)",
); //delete this line if you dont want to be notified when a command is used outside of a guild/server
return controller.stop();
}
if (!(ctx.member! as GuildMember).permissions.has(perm)) {
await ctx.reply(response);
return controller.stop();
}
return controller.next();
});
}

215
bot/src/plugins/publish.ts Normal file
View File

@@ -0,0 +1,215 @@
// @ts-nocheck
/**
* @plugin
* [DEPRECATED] It allows you to publish your application commands using the discord.js library with ease.
*
* @author @EvolutionX-10 [<@697795666373640213>]
* @version 2.0.0
* @example
* ```ts
* import { publish } from "../plugins/publish";
* import { commandModule } from "@sern/handler";
* export default commandModule({
* plugins: [ publish() ], // put an object containing permissions, ids for guild commands, boolean for dmPermission
* // plugins: [ publish({ guildIds: ['guildId'], defaultMemberPermissions: 'Administrator'})]
* execute: (ctx) => {
* //your code here
* }
* })
* ```
* @end
*/
import {
CommandInitPlugin,
CommandType,
controller,
SernOptionsData,
SlashCommand,
Service,
} from "@sern/handler";
import {
ApplicationCommandData,
ApplicationCommandType,
ApplicationCommandOptionType,
PermissionResolvable,
} from "discord.js";
export const CommandTypeRaw = {
[CommandType.Both]: ApplicationCommandType.ChatInput,
[CommandType.CtxUser]: ApplicationCommandType.User,
[CommandType.CtxMsg]: ApplicationCommandType.Message,
[CommandType.Slash]: ApplicationCommandType.ChatInput,
} as const;
export function publish<
T extends
| CommandType.Both
| CommandType.Slash
| CommandType.CtxMsg
| CommandType.CtxUser,
>(options?: PublishOptions) {
return CommandInitPlugin<T>(async ({ module }) => {
// Users need to provide their own useContainer function.
let client;
try {
client = (await import("@sern/handler")).Service("@sern/client");
} catch {
const { useContainer } = await import("../index.js");
client = useContainer("@sern/client")[0];
}
const defaultOptions = {
guildIds: [],
dmPermission: undefined,
defaultMemberPermissions: null,
};
options = { ...defaultOptions, ...options } as PublishOptions &
ValidPublishOptions;
let { defaultMemberPermissions, dmPermission, guildIds } =
options as unknown as ValidPublishOptions;
function c(e: unknown) {
console.error("publish command didnt work for", module.name);
console.error(e);
}
const log =
(...message: any[]) =>
() =>
console.log(...message);
const logged = (...message: any[]) => log(message);
/**
* a local function that returns either one value or the other,
* depending on {t}'s CommandType. If the commandtype of
* this module is CommandType.Both or CommandType.Text or CommandType.Slash,
* return 'is', else return 'els'
* @param t
* @returns S | T
*/
const appCmd = <V extends CommandType, S, T>(t: V) => {
return (is: S, els: T) => ((t & CommandType.Both) !== 0 ? is : els);
};
const curAppType = CommandTypeRaw[module.type];
const createCommandData = () => {
const cmd = appCmd(module.type);
return {
name: module.name,
type: curAppType,
description: cmd(module.description, ""),
options: cmd(
optionsTransformer((module as SlashCommand).options ?? []),
[],
),
defaultMemberPermissions,
dmPermission,
} as ApplicationCommandData;
};
try {
const commandData = createCommandData();
if (!guildIds.length) {
const cmd = (await client.application!.commands.fetch()).find(
(c) => c.name === module.name && c.type === curAppType,
);
if (cmd) {
if (!cmd.equals(commandData, true)) {
logged(
`Found differences in global command ${module.name}`,
);
cmd.edit(commandData).then(
log(
`${module.name} updated with new data successfully!`,
),
);
}
return controller.next();
}
client
.application!.commands.create(commandData)
.then(log("Command created", module.name))
.catch(c);
return controller.next();
}
for (const id of guildIds) {
const guild = await client.guilds.fetch(id).catch(c);
if (!guild) continue;
const guildCmd = (await guild.commands.fetch()).find(
(c) => c.name === module.name && c.type === curAppType,
);
if (guildCmd) {
if (!guildCmd.equals(commandData, true)) {
logged(`Found differences in command ${module.name}`);
guildCmd
.edit(commandData)
.then(
log(
`${module.name} updated with new data successfully!`,
),
)
.catch(c);
continue;
}
continue;
}
guild.commands
.create(commandData)
.then(log("Guild Command created", module.name, guild.name))
.catch(c);
}
return controller.next();
} catch (e) {
logged("Command did not register" + module.name);
logged(e);
return controller.stop();
}
});
}
export function optionsTransformer(ops: Array<SernOptionsData>) {
return ops.map((el) => {
switch (el.type) {
case ApplicationCommandOptionType.String:
case ApplicationCommandOptionType.Number:
case ApplicationCommandOptionType.Integer: {
return el.autocomplete && "command" in el
? (({ command, ...el }) => el)(el)
: el;
}
default:
return el;
}
});
}
export type NonEmptyArray<T extends `${number}` = `${number}`> = [T, ...T[]];
export interface ValidPublishOptions {
guildIds: string[];
dmPermission: boolean;
defaultMemberPermissions: PermissionResolvable;
}
interface GuildPublishOptions {
guildIds?: NonEmptyArray;
defaultMemberPermissions?: PermissionResolvable;
dmPermission?: never;
}
interface GlobalPublishOptions {
defaultMemberPermissions?: PermissionResolvable;
dmPermission?: false;
guildIds?: never;
}
type BasePublishOptions = GuildPublishOptions | GlobalPublishOptions;
export type PublishOptions = BasePublishOptions &
(
| Required<Pick<BasePublishOptions, "defaultMemberPermissions">>
| (
| Required<Pick<BasePublishOptions, "dmPermission">>
| Required<Pick<BasePublishOptions, "guildIds">>
)
);

View File

@@ -0,0 +1,95 @@
/**
* This is perm check, it allows users to parse the permission you want and let the plugin do the rest. (check bot or user for that perm).
*
* @author @Benzo-Fury [<@762918086349029386>]
* @author @needhamgary [<@342314924804014081>]
* @version 1.2.0
* @example
* ```ts
* import { requirePermission } from "../plugins/myPermCheck";
* import { commandModule, CommandType } from "@sern/handler";
* export default commandModule({
* plugins: [ requirePermission<CommandType>('target', 'permission', 'No response (optional)') ],
* execute: (ctx) => {
* //your code here
* }
* })
* ```
*/
import type { GuildMember, PermissionResolvable } from "discord.js";
import {
CommandType, CommandControlPlugin, controller,
} from "@sern/handler";
function payload(resp?: string) {
return {
fetchReply: true,
content: resp,
allowedMentions: { repliedUser: false },
} as const;
}
export function requirePermission(
target: "user" | "bot" | "both",
perm: PermissionResolvable[],
response?: string
) {
return CommandControlPlugin<CommandType.Both>(async (ctx, _) => {
if (ctx.guild === null) {
ctx.reply(payload("This command cannot be used here"));
console.warn(
"PermCheck > A command stopped because we couldn't check a users permissions (was used in dms)"
); //delete this line if you dont want to be notified when a command is used outside of a guild/server
return controller.stop();
}
const bot = (await ctx.guild.members.fetchMe({
cache: false,
})!) as GuildMember; const memm = ctx.member! as GuildMember;
switch (target) {
//*********************************************************************************************************************//
case "bot":
if (!bot.permissions.has(perm)) {
if (!response)
response = `I cannot use this command, please give me \`${perm.join(
", "
)}\` permission(s).`;
await ctx.reply(payload(response));
return controller.stop();
}
return controller.next();
//*********************************************************************************************************************//
case "user":
if (!memm.permissions.has(perm)) {
if (!response)
response = `You cannot use this command because you are missing \`${perm.join(
", "
)}\` permission(s).`;
await ctx.reply(payload(response));
return controller.stop();
}
return controller.next();
//*********************************************************************************************************************//
case "both":
if (
!bot.permissions.has(perm) ||
!memm.permissions.has(perm)
) {
if (!response)
response = `Please ensure <@${bot.user.id}> and <@${
memm.user.id
}> both have \`${perm.join(", ")}\` permission(s).`;
await ctx.reply(payload(response));
return controller.stop();
}
return controller.next();
//*********************************************************************************************************************//
default:
console.warn(
"Perm Check >>> You didn't specify user or bot."
);
ctx.reply(payload("User or Bot was not specified."));
return controller.stop();
}
});
}

View File

@@ -0,0 +1,38 @@
/**
* Checks if a command is available in a specific server.
*
* @author @Peter-MJ-Parker [<@1017182455926624316>]
* @version 1.0.0
* @example
* ```ts
* import { commandModule, CommandType } from "@sern/handler";
* import { serverOnly } from "../plugins/serverOnly";
* export default commandModule({
* type: CommandType.Both,
* plugins: [serverOnly(["guildId"], failMessage)], // fail message is the message you will see when the command is ran in the wrong server.
* description: "command description",
* execute: async (ctx, args) => {
* // your code here
* },
* });
* ```
*/
import { CommandType, controller, CommandControlPlugin } from "@sern/handler";
export function serverOnly(
guildId: string[],
failMessage = "This command is not available in this guild. \nFor permission to use in your server, please contact my developer."
) {
return CommandControlPlugin<CommandType.Both>(async (ctx, _) => {
if (!guildId.includes(ctx.guildId!)) {
ctx.reply(failMessage).then(async (m) => {
setTimeout(async () => {
await m.delete();
}, 3000);
});
return controller.stop();
}
return controller.next();
})
}

34
bot/src/presence.ts Normal file
View File

@@ -0,0 +1,34 @@
import { Presence } from '@sern/handler'
import { ActivityType, ClientPresenceStatus } from 'discord.js';
function shuffleArray<T>(array: T[]) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return [...array];
}
const statuses = [[ActivityType.Watching, "the sern community", "online"],
[ActivityType.Listening, "Evo", "dnd"],
[ActivityType.Playing, "with @sern/cli", "idle"],
[ActivityType.Watching, "sern bots", "dnd"],
[ActivityType.Watching, "github stars go brrr", "online"],
[ActivityType.Listening, "Spotify", "dnd"],
[ActivityType.Listening, "what's bofa", "idle"]] satisfies
[ActivityType, string, ClientPresenceStatus][];
export default Presence.module({
execute: () => {
const [type, name, status] = statuses.at(0)!;
return Presence
.of({ activities: [ { type, name } ], status }) //start your presence with this.
.repeated(() => {
const [type, name, status] = [...shuffleArray(statuses)].shift()!;
return {
status,
activities: [{ type, name }]
};
}, 60_000); //repeat and setPresence with returned result every minute
}
})

View File

@@ -0,0 +1,9 @@
import { scheduledTask } from "@sern/handler";
export default scheduledTask({
trigger: "* * * * *",
execute: (args, { deps }) => {
console.log("hello")
}
})

3
bot/tsconfig.json Normal file
View File

@@ -0,0 +1,3 @@
{
"extends": "./.sern/tsconfig.json"
}

1
fortnite Normal file
View File

@@ -0,0 +1 @@

4735
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,23 +1,28 @@
{
"name": "@sern/handler",
"version": "1.1.0",
"description": "A customizable, batteries-included, powerful discord.js framework to automate and streamline bot development.",
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs",
"types": "dist/index.d.ts",
"packageManager": "yarn@3.5.0",
"version": "4.2.4",
"description": "A complete, customizable, typesafe, & reactive framework for discord bots.",
"main": "./dist/index.js",
"module": "./dist/index.js",
"exports": {
".": {
"import": "./dist/esm/index.mjs",
"require": "./dist/cjs/index.cjs"
"import": "./dist/index.js",
"require": "./dist/index.js"
}
},
"scripts": {
"watch": "tsup --watch --dts",
"clean-modules": "rimraf node_modules/ && npm install",
"watch": "tsc --watch",
"lint": "eslint src/**/*.ts",
"format": "eslint src/**/*.ts --fix",
"build": "tsup && node scripts/mkjson.mjs dist/cjs dist/esm && tsup --dts-only --outDir dist",
"publish": "npm run build && npm publish"
"build:dev": "tsc",
"build:prod": "tsc",
"prepare": "tsc",
"pretty": "prettier --write .",
"tdd": "vitest",
"benchmark": "vitest bench",
"test": "vitest --run",
"analyze-imports": "npx depcruise src --include-only \"^src\" --output-type dot | dot -T svg > dependency-graph.svg"
},
"keywords": [
"sern-handler",
@@ -31,24 +36,62 @@
"author": "SernDevs",
"license": "MIT",
"dependencies": {
"rxjs": "^7.5.6",
"ts-pattern": "^4.0.2",
"ts-results-es": "^3.5.0"
"@sern/ioc": "^1.1.2",
"callsites": "^3.1.0",
"cron": "^3.1.7",
"deepmerge": "^4.3.1"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "5.35.1",
"@typescript-eslint/parser": "5.35.1",
"eslint": "8.22.0",
"prettier": "2.7.1",
"tsup": "^6.1.3",
"typescript": "4.7.4"
"@faker-js/faker": "^8.0.1",
"@types/node": "^20.0.0",
"@types/node-cron": "^3.0.11",
"@typescript-eslint/eslint-plugin": "5.58.0",
"@typescript-eslint/parser": "5.59.1",
"discord.js": "^14.14.1",
"eslint": "8.39.0",
"typescript": "5.0.2",
"vitest": "^1.6.0"
},
"peerDependencies": {
"discord.js": "^14.2.x"
"eslintConfig": {
"parser": "@typescript-eslint/parser",
"extends": [
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "script"
},
"rules": {
"@typescript-eslint/no-non-null-assertion": "off",
"quotes": [
2,
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"semi": [
"error",
"always"
],
"@typescript-eslint/no-empty-interface": 0,
"@typescript-eslint/ban-types": 0,
"@typescript-eslint/no-explicit-any": "off"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/sern-handler/handler.git"
},
"homepage": "https://sern-handler.js.org"
"engines": {
"node": ">= 20.0.x"
},
"homepage": "https://sern.dev",
"overrides": {
"ws": "8.17.1"
},
"resolutions": {
"ws": "8.17.1"
}
}

View File

@@ -1,4 +1,9 @@
{
"extends": [
"config:base",
"helpers:pinGitHubActionDigests",
"group:allNonMajor"
],
"major": {
"dependencyDashboardApproval": true,
"reviewers": ["EvolutionX-10", "jacoobes", "Murtatrxx"]
@@ -9,6 +14,6 @@
"schedule": ["every weekend"],
"lockFileMaintenance": {
"enabled": true,
"automerge": false
"automerge": true
}
}

View File

@@ -1,13 +0,0 @@
import { writeFile } from 'fs/promises';
import { join } from 'path';
// A quick script to regenerate package.jsons for each cjs and esm after tsup cleans distributions
const locations = process.argv;
locations.shift();
locations.shift();
for (const loc of locations) {
if (loc.endsWith('cjs')) {
await writeFile(join(loc, 'package.json'), JSON.stringify({ type: 'commonjs' }));
} else {
await writeFile(join(loc, 'package.json'), JSON.stringify({ type: 'module' }));
}
}

111
src/cleanup.ts Normal file
View File

@@ -0,0 +1,111 @@
// It's this package but without default console log / error https://github.com/trevorr/async-cleanup
/** A possibly asynchronous function invoked with the process is about to exit. */
export type CleanupListener = () => void | Promise<void>;
let cleanupListeners: Set<CleanupListener> | undefined;
/** Registers a new cleanup listener. Adding the same listener more than once has no effect. */
export function addCleanupListener(listener: CleanupListener): void {
// Install exit listeners on initial cleanup listener
if (!cleanupListeners) {
installExitListeners();
cleanupListeners = new Set();
}
cleanupListeners.add(listener);
}
/** Removes an existing cleanup listener, and returns whether the listener was registered. */
export function removeCleanupListener(listener: CleanupListener): boolean {
return cleanupListeners != null && cleanupListeners.delete(listener);
}
/** Executes all cleanup listeners and then exits the process. Call this instead of `process.exit` to ensure all listeners are fully executed. */
export async function exitAfterCleanup(code = 0): Promise<never> {
await executeCleanupListeners();
process.exit(code);
}
/** Executes all cleanup listeners and then kills the process with the given signal. */
export async function killAfterCleanup(signal: ExitSignal): Promise<void> {
await executeCleanupListeners();
process.kill(process.pid, signal);
}
async function executeCleanupListeners(): Promise<void> {
if (cleanupListeners) {
// Remove exit listeners to restore normal event handling
uninstallExitListeners();
// Clear cleanup listeners to reset state for testing
const listeners = cleanupListeners;
cleanupListeners = undefined;
// Call listeners in order added with async listeners running concurrently
const promises: Promise<void>[] = [];
for (const listener of listeners) {
try {
const promise = listener();
if (promise) promises.push(promise);
} catch (err) {
// console.error("Uncaught exception during cleanup", err);
}
}
// Wait for all listeners to complete and log any rejections
const results = await Promise.allSettled(promises);
for (const result of results) {
if (result.status === "rejected") {
console.error("Unhandled rejection during cleanup", result.reason);
}
}
}
}
function beforeExitListener(code: number): void {
// console.log(`Exiting with code ${code} due to empty event loop`);
void exitAfterCleanup(code);
}
function uncaughtExceptionListener(error: Error): void {
// console.error("Exiting with code 1 due to uncaught exception", error);
void exitAfterCleanup(1);
}
function signalListener(signal: ExitSignal): void {
// console.log(`Exiting due to signal ${signal}`);
void killAfterCleanup(signal);
}
// Listenable signals that terminate the process by default
// (except SIGQUIT, which generates a core dump and should not trigger cleanup)
// See https://nodejs.org/api/process.html#signal-events
const listenedSignals = [
"SIGBREAK", // Ctrl-Break on Windows
"SIGHUP", // Parent terminal closed
"SIGINT", // Terminal interrupt, usually by Ctrl-C
"SIGTERM", // Graceful termination
"SIGUSR2", // Used by Nodemon
] as const;
/** Signals that can terminate the process. */
export type ExitSignal =
| typeof listenedSignals[number]
| "SIGKILL"
| "SIGQUIT"
| "SIGSTOP";
function installExitListeners(): void {
process.on("beforeExit", beforeExitListener);
process.on("uncaughtException", uncaughtExceptionListener);
listenedSignals.forEach((signal) => process.on(signal, signalListener));
}
function uninstallExitListeners(): void {
process.removeListener("beforeExit", beforeExitListener);
process.removeListener("uncaughtException", uncaughtExceptionListener);
listenedSignals.forEach((signal) =>
process.removeListener(signal, signalListener)
);
}

124
src/core/functions.ts Normal file
View File

@@ -0,0 +1,124 @@
import type { Module, SernAutocompleteData, SernOptionsData } from '../types/core-modules';
import type {
AnySelectMenuInteraction,
ButtonInteraction,
ChatInputCommandInteraction,
MessageContextMenuCommandInteraction,
ModalSubmitInteraction,
UserContextMenuCommandInteraction,
AutocompleteInteraction,
} from 'discord.js';
import { ApplicationCommandOptionType, InteractionType } from 'discord.js';
import { PluginType } from './structures/enums';
import type { Payload, UnpackedDependencies } from '../types/utility';
import path from 'node:path'
export const createSDT = (module: Module, deps: UnpackedDependencies, params: string|undefined) => {
return {
state: {},
deps,
params,
type: module.type,
module: {
name: module.name,
description: module.description,
locals: module.locals,
meta: module.meta
}
}
}
/**
* Removes the first character(s) _[depending on prefix length]_ of the message
* @param msg
* @param prefix The prefix to remove
* @returns The message without the prefix
* @example
* message.content = '!ping';
* console.log(fmt(message.content, '!'));
* // [ 'ping' ]
*/
export function fmt(msg: string, prefix?: string): string[] {
if(!prefix) throw Error("Unable to parse message without prefix");
return msg.slice(prefix.length).trim().split(/\s+/g);
}
export function partitionPlugins<T,V>
(arr: Array<{ type: PluginType }> = []): [T[], V[]] {
const controlPlugins = [];
const initPlugins = [];
for (const el of arr) {
switch (el.type) {
case PluginType.Control: controlPlugins.push(el); break;
case PluginType.Init: initPlugins.push(el); break;
}
}
return [controlPlugins, initPlugins] as [T[], V[]];
}
export const createLookupTable = (options: SernOptionsData[]): Map<string, SernAutocompleteData> => {
const table = new Map<string, SernAutocompleteData>();
_createLookupTable(table, options, "<parent>");
return table;
}
const _createLookupTable = (table: Map<string, SernAutocompleteData>, options: SernOptionsData[], parent: string) => {
for (const opt of options) {
const name = path.posix.join(parent, opt.name)
switch(opt.type) {
case ApplicationCommandOptionType.Subcommand: {
_createLookupTable(table, opt.options ?? [], name);
} break;
case ApplicationCommandOptionType.SubcommandGroup: {
_createLookupTable(table, opt.options ?? [], name);
} break;
default: {
if(Reflect.get(opt, 'autocomplete') === true) {
table.set(name, opt as SernAutocompleteData)
}
} break;
}
}
}
interface InteractionTypable {
type: InteractionType;
}
//discord.js pls fix ur typings or i will >:(
type AnyMessageComponentInteraction = AnySelectMenuInteraction | ButtonInteraction;
type AnyCommandInteraction =
| ChatInputCommandInteraction
| MessageContextMenuCommandInteraction
| UserContextMenuCommandInteraction;
export function isMessageComponent(i: InteractionTypable): i is AnyMessageComponentInteraction {
return i.type === InteractionType.MessageComponent;
}
export function isCommand(i: InteractionTypable): i is AnyCommandInteraction {
return i.type === InteractionType.ApplicationCommand;
}
export function isContextCommand(i: AnyCommandInteraction): i is MessageContextMenuCommandInteraction | UserContextMenuCommandInteraction {
return i.isContextMenuCommand();
}
export function isAutocomplete(i: InteractionTypable): i is AutocompleteInteraction {
return i.type === InteractionType.ApplicationCommandAutocomplete;
}
export function isModal(i: InteractionTypable): i is ModalSubmitInteraction {
return i.type === InteractionType.ModalSubmit;
}
export function resultPayload<T extends 'success'|'warning'|'failure'>
(type: T, module?: Module, reason?: unknown) {
return { type, module, reason } as Payload & { type : T };
}
export function pipe<T>(arg: unknown, firstFn: Function, ...fns: Function[]): T {
let result = firstFn(arg);
for (let fn of fns) {
result = fn(result);
}
return result;
}

74
src/core/id.ts Normal file
View File

@@ -0,0 +1,74 @@
import { ApplicationCommandType, ComponentType, type Interaction, InteractionType } from 'discord.js';
import { CommandType, EventType } from './structures/enums';
const parseParams = (event: { customId: string }, append: string) => {
const hasSlash = event.customId.indexOf('/')
if(hasSlash === -1) {
return { id:event.customId+append };
}
const baseid = event.customId.substring(0, hasSlash);
const params = event.customId.substring(hasSlash+1);
return { id: baseid+append, params }
}
/**
* Construct unique ID for a given interaction object.
* @param event The interaction object for which to create an ID.
* @returns An array of unique string IDs based on the type and properties of the interaction object.
*/
export function reconstruct<T extends Interaction>(event: T) {
switch (event.type) {
case InteractionType.MessageComponent: {
const data = parseParams(event, `_C${event.componentType}`)
return [data];
}
case InteractionType.ApplicationCommand:
case InteractionType.ApplicationCommandAutocomplete:
return [{ id: `${event.commandName}_A${event.commandType}` }, { id: `${event.commandName}_B` }];
//Modal interactions are classified as components for sern
case InteractionType.ModalSubmit: {
const data = parseParams(event, '_M');
return [data];
}
}
}
/**
*
* A magic number to represent any commandtype that is an ApplicationCommand.
*/
const PUBLISHABLE = 0b000000001111;
const TypeMap = new Map<number, number>([[CommandType.Text, 0],
[CommandType.Both, 0],
[CommandType.Slash, ApplicationCommandType.ChatInput],
[CommandType.CtxUser, ApplicationCommandType.User],
[CommandType.CtxMsg, ApplicationCommandType.Message],
[CommandType.Button, ComponentType.Button],
[CommandType.StringSelect, ComponentType.StringSelect],
[CommandType.Modal, InteractionType.ModalSubmit],
[CommandType.UserSelect, ComponentType.UserSelect],
[CommandType.MentionableSelect, ComponentType.MentionableSelect],
[CommandType.RoleSelect, ComponentType.RoleSelect],
[CommandType.ChannelSelect, ComponentType.ChannelSelect]]);
/*
* Generates an id based on name and CommandType.
* A is for any ApplicationCommand. C is for any ComponentCommand
* Then, another number fetched from TypeMap
*/
export function create(name: string, type: CommandType | EventType) {
if(type == CommandType.Text) {
return `${name}_T`;
}
if(type == CommandType.Both) {
return `${name}_B`;
}
if(type == CommandType.Modal) {
return `${name}_M`;
}
const am = (PUBLISHABLE & type) !== 0 ? 'A' : 'C';
return `${name}_${am}${TypeMap.get(type)!}`
}

55
src/core/interfaces.ts Normal file
View File

@@ -0,0 +1,55 @@
import type { AnyFunction } from '../types/utility';
/**
* Represents an initialization contract.
* Let dependencies implement this to initiate some logic.
*/
export interface Init {
init(): unknown;
}
/**
* Represents a Disposable contract.
* Let dependencies implement this to dispose and cleanup.
*/
export interface Disposable {
dispose(): unknown;
}
export interface Emitter {
addListener(eventName: string | symbol, listener: AnyFunction): this;
removeListener(eventName: string | symbol, listener: AnyFunction): this;
emit(eventName: string | symbol, ...payload: any[]): boolean;
}
/**
* @since 2.0.0
*/
export interface ErrorHandling {
/**
* @deprecated
* Version 4 will remove this method
*/
crash(err: Error): never;
/**
* A function that is called on every throw.
* @param error
*/
updateAlive(error: Error): void;
}
/**
* @since 2.0.0
*/
export interface Logging<T = unknown> {
error(payload: LogPayload<T>): void;
warning(payload: LogPayload<T>): void;
info(payload: LogPayload<T>): void;
debug(payload: LogPayload<T>): void;
}
export type LogPayload<T = unknown> = { message: T };

163
src/core/ioc.ts Normal file
View File

@@ -0,0 +1,163 @@
import { Service as $Service, Services as $Services } from '@sern/ioc/global'
import { Container } from '@sern/ioc';
import * as Contracts from './interfaces';
import * as __Services from './structures/default-services';
import type { Logging } from './interfaces';
import { __init_container, useContainerRaw } from '@sern/ioc/global';
import { EventEmitter } from 'node:events';
import { Client } from 'discord.js';
import { Module } from '../types/core-modules';
import { UnpackFunction } from '../types/utility';
export function disposeAll(logger: Logging|undefined) {
useContainerRaw()
?.disposeAll()
.then(() => logger?.info({ message: 'Cleaning container and crashing' }));
}
type Insertable = | ((container: Dependencies) => object)
| object
const dependencyBuilder = (container: Container) => {
return {
/**
* Insert a dependency into your container.
* Supply the correct key and dependency
*/
add(key: keyof Dependencies, v: Insertable) {
if(typeof v !== 'function') {
container.addSingleton(key, v)
} else {
//@ts-ignore
container.addWiredSingleton(key, (cntr) => v(cntr))
}
},
/**
* @param key the key of the dependency
* @param v The dependency to swap out.
* Swap out a preexisting dependency.
*/
swap(key: keyof Dependencies, v: Insertable) {
if(typeof v !== 'function') {
container.swap(key, v);
} else {
container.swap(key, v(container.deps()));
}
},
};
};
type ValidDependencyConfig =
(c: ReturnType<typeof dependencyBuilder>) => any
/**
* makeDependencies constructs a dependency injection container for sern handler to use.
* This is required to start the handler, and is to be called before Sern.init.
* @example
* ```ts
* await makeDependencies(({ add }) => {
* add('@sern/client', new Client({ intents, partials })
* })
* ```
*/
export async function makeDependencies (conf: ValidDependencyConfig) {
const container = await __init_container({ autowire: false });
//We only include logger if it does not exist
const includeLogger = !container.hasKey('@sern/logger');
if(includeLogger) {
container.addSingleton('@sern/logger', new __Services.DefaultLogging);
}
container.addSingleton('@sern/errors', new __Services.DefaultErrorHandling);
container.addSingleton('@sern/modules', new Map);
container.addSingleton('@sern/emitter', new EventEmitter({ captureRejections: true }))
container.addSingleton('@sern/scheduler', new __Services.TaskScheduler)
conf(dependencyBuilder(container));
await container.ready();
}
/**
* The Service api, which allows users to access dependencies in places IOC cannot reach.
* To obtain intellisense, ensure a .d.ts file exists in the root of compilation.
* Our scaffolding tool takes care of this.
* Note: this method only works AFTER your container has been initiated
* @since 3.0.0
* @example
* ```ts
* const client = Service('@sern/client');
* ```
* @param key a key that corresponds to a dependency registered.
* @throws if container is absent or not present
*/
export function Service<const T extends keyof Dependencies>(key: T) {
return $Service(key) as Dependencies[T]
}
/**
* @since 3.0.0
* The plural version of {@link Service}
* @throws if container is absent or not present
* @returns array of dependencies, in the same order of keys provided
*
*/
export function Services<const T extends (keyof Dependencies)[]>(...keys: [...T]) {
return $Services<T, IntoDependencies<T>>(...keys)
}
/**
* @deprecated
* Creates a singleton object.
* @param cb
*/
export function single<T>(cb: () => T) {
console.log('The `single` function is deprecated and has no effect')
return cb();
}
/**
* @deprecated
* @since 2.0.0
* Creates a transient object
* @param cb
*/
export function transient<T>(cb: () => () => T) {
console.log('The `transient` function is deprecated and has no effect')
return cb()();
}
export type DependencyFromKey<T extends keyof Dependencies> = Dependencies[T];
export type IntoDependencies<Tuple extends [...any[]]> = {
[Index in keyof Tuple]: UnpackFunction<NonNullable<DependencyFromKey<Tuple[Index]>>>; //Unpack and make NonNullable
} & { length: Tuple['length'] };
export interface CoreDependencies {
/**
* discord.js client.
*/
'@sern/client': Client;
/**
* sern emitter listens to events that happen throughout
* the handler. some include module.register, module.activate.
*/
'@sern/emitter': Contracts.Emitter;
/**
* An error handler which is the final step before
* the sern process actually crashes.
*/
'@sern/errors': Contracts.ErrorHandling;
/**
* Optional logger. Performs ... logging
*/
'@sern/logger'?: Contracts.Logging;
/**
* Readonly module store. sern stores these
* by module.meta.id -> Module
*/
'@sern/modules': Map<string, Module>;
'@sern/scheduler': __Services.TaskScheduler
}

View File

@@ -0,0 +1,70 @@
import path from 'node:path';
import { existsSync } from 'node:fs';
import { readdir } from 'fs/promises';
import assert from 'node:assert';
import * as Id from './id'
import { Module } from '../types/core-modules';
export const parseCallsite = (site: string) => {
const pathobj = path.posix.parse(site.replace(/file:\\?/, "")
.split(path.sep)
.join(path.posix.sep))
return { name: pathobj.name,
absPath : path.posix.format(pathobj) }
}
export const shouldHandle = (pth: string, filenam: string) => {
const file_name = filenam+path.extname(pth);
let newPath = path.join(path.dirname(pth), file_name)
.replace(/file:\\?/, "");
return { exists: existsSync(newPath),
path: 'file://'+newPath };
}
/**
* Import any module based on the absolute path.
* This can accept four types of exported modules
* commonjs, javascript :
* ```js
* exports = commandModule({ })
* //or
* exports.default = commandModule({ })
* ```
* esm javascript, typescript, and commonjs typescript
* export default commandModule({})
*/
export async function importModule<T>(absPath: string) {
let fileModule = await import(absPath);
let commandModule: Module = fileModule.default;
assert(commandModule , `No default export @ ${absPath}`);
if ('default' in commandModule) {
commandModule = commandModule.default as Module;
}
const p = path.parse(absPath)
commandModule.name ??= p.name; commandModule.description ??= "...";
commandModule.meta = {
id: Id.create(commandModule.name, commandModule.type),
absPath,
};
return { module: commandModule as T };
}
export async function* readRecursive(dir: string): AsyncGenerator<string> {
const files = await readdir(dir, { withFileTypes: true });
for (const file of files) {
const fullPath = path.posix.join(dir, file.name);
if (file.isDirectory()) {
if (!file.name.startsWith('!')) {
yield* readRecursive(fullPath);
}
} else if (!file.name.startsWith('!')) {
yield "file:///"+path.resolve(fullPath);
}
}
}

135
src/core/modules.ts Normal file
View File

@@ -0,0 +1,135 @@
import type { ClientEvents } from 'discord.js';
import { EventType } from '../core/structures/enums';
import type {
InputCommand,
InputEvent,
Module,
ScheduledTask,
} from '../types/core-modules';
import { partitionPlugins } from './functions'
import type { Awaitable } from '../types/utility';
/**
* Creates a command module with standardized structure and plugin support.
*
* @since 1.0.0
* @param {InputCommand} mod - Command module configuration
* @returns {Module} Processed command module ready for registration
*
* @example
* // Basic slash command
* export default commandModule({
* type: CommandType.Slash,
* description: "Ping command",
* execute: async (ctx) => {
* await ctx.reply("Pong! 🏓");
* }
* });
*
* @example
* // Command with component interaction
* export default commandModule({
* type: CommandType.Slash,
* description: "Interactive command",
* execute: async (ctx) => {
* const button = new ButtonBuilder({
* customId: "btn/someData",
* label: "Click me",
* style: ButtonStyle.Primary
* });
* await ctx.reply({
* content: "Interactive message",
* components: [new ActionRowBuilder().addComponents(button)]
* });
* }
* });
*/
export function commandModule(mod: InputCommand): Module {
const [onEvent, plugins] = partitionPlugins(mod.plugins);
return { ...mod,
onEvent,
plugins,
locals: {} } as Module;
}
/**
* Creates an event module for handling Discord.js or custom events.
*
* @since 1.0.0
* @template T - Event name from ClientEvents
* @param {InputEvent<T>} mod - Event module configuration
* @returns {Module} Processed event module ready for registration
* @throws {Error} If ControlPlugins are used in event modules
*
* @example
* // Discord event listener
* export default eventModule({
* type: EventType.Discord,
* execute: async (message) => {
* console.log(`${message.author.tag}: ${message.content}`);
* }
* });
*
* @example
* // Custom sern event
* export default eventModule({
* type: EventType.Sern,
* execute: async (eventData) => {
* // Handle sern-specific event
* }
* });
*/
export function eventModule<T extends keyof ClientEvents = keyof ClientEvents>(mod: InputEvent<T>): Module {
const [onEvent, plugins] = partitionPlugins(mod.plugins);
if(onEvent.length !== 0) throw Error("Event modules cannot have ControlPlugins");
return { ...mod,
plugins,
locals: {} } as Module;
}
/** Create event modules from discord.js client events,
* This was an {@link eventModule} for discord events,
* where typings were bad.
* @deprecated Use {@link eventModule} instead
* @param mod
*/
export function discordEvent<T extends keyof ClientEvents>(mod: {
name: T;
once?: boolean;
execute: (...args: ClientEvents[T]) => Awaitable<unknown>;
}) {
return eventModule({ type: EventType.Discord, ...mod, });
}
/**
* Creates a scheduled task that can be executed at specified intervals using cron patterns
*
* @param {ScheduledTask} ism - The scheduled task configuration object
* @param {string} ism.trigger - A cron pattern that determines when the task should execute
* Format: "* * * * *" (minute hour day month day-of-week)
* @param {Function} ism.execute - The function to execute when the task is triggered
* @param {Object} ism.execute.context - The execution context passed to the task
*
* @returns {ScheduledTask} The configured scheduled task
*
* @example
* // Create a task that runs every minute
* export default scheduledTask({
* trigger: "* * * * *",
* execute: (context) => {
* console.log("Task executed!");
* }
* });
*
* @remarks
* - Tasks must be placed in the 'tasks' directory specified in your config
* - The file name serves as a unique identifier for the task
* - Tasks can be cancelled using deps['@sern/scheduler'].kill(uuid)
*
* @see {@link https://crontab.guru/} for testing and creating cron patterns
*/
export function scheduledTask(ism: ScheduledTask): ScheduledTask {
return ism
}

137
src/core/plugin.ts Normal file
View File

@@ -0,0 +1,137 @@
import { CommandType, PluginType } from './structures/enums';
import type { Plugin, PluginResult, CommandArgs, InitArgs } from '../types/core-plugin';
import { Err, Ok } from './structures/result';
import type { Dictionary } from '../types/utility';
export function makePlugin<V extends unknown[]>(
type: PluginType,
execute: (...args: any[]) => any,
): Plugin<V> {
return { type, execute } as Plugin<V>;
}
/**
* @since 2.5.0
*/
export function EventInitPlugin(execute: (args: InitArgs) => PluginResult) {
return makePlugin(PluginType.Init, execute);
}
/**
* Creates an initialization plugin for command preprocessing and modification
*
* @since 2.5.0
* @template I - Extends CommandType to enforce type safety for command modules
*
* @param {function} execute - Function to execute during command initialization
* @param {InitArgs<T>} execute.args - The initialization arguments
* @param {T} execute.args.module - The command module being initialized
* @param {string} execute.args.absPath - The absolute path to the module file
* @param {Dependencies} execute.args.deps - Dependency injection container
*
* @returns {Plugin} A plugin that runs during command initialization
*
* @example
* // Plugin to update command description
* export const updateDescription = (description: string) => {
* return CommandInitPlugin(({ deps }) => {
* if(description.length > 100) {
* deps.logger?.info({ message: "Invalid description" })
* return controller.stop("From updateDescription: description is invalid");
* }
* module.description = description;
* return controller.next();
* });
* };
*
* @example
* // Plugin to store registration date in module locals
* export const dateRegistered = () => {
* return CommandInitPlugin(({ module }) => {
* module.locals.registered = Date.now()
* return controller.next();
* });
* };
*
* @remarks
* - Init plugins can modify how commands are loaded and perform preprocessing
* - The module.locals object can be used to store custom plugin-specific data
* - Be careful when modifying module fields as multiple plugins may interact with them
* - Use controller.next() to continue to the next plugin
* - Use controller.stop(reason) to halt plugin execution
*/
export function CommandInitPlugin<I extends CommandType>(
execute: (args: InitArgs) => PluginResult
): Plugin {
return makePlugin(PluginType.Init, execute);
}
/**
* Creates a control plugin for command preprocessing, filtering, and state management
*
* @since 2.5.0
* @template I - Extends CommandType to enforce type safety for command modules
*
* @param {function} execute - Function to execute during command control flow
* @param {CommandArgs<I>} execute.args - The command arguments array
* @param {Context} execute.args[0] - The discord context (e.g., guild, channel, user info, interaction)
* @param {SDT} execute.args[1] - The State, Dependencies, Params, Module, and Type object
*
* @returns {Plugin} A plugin that runs during command execution flow
*
* @example
* // Plugin to restrict command to specific guild
* export const inGuild = (guildId: string) => {
* return CommandControlPlugin((ctx, sdt) => {
* if(ctx.guild.id !== guildId) {
* return controller.stop();
* }
* return controller.next();
* });
* };
*
* @example
* // Plugins passing state through the chain
* const plugin1 = CommandControlPlugin((ctx, sdt) => {
* return controller.next({ 'plugin1/data': 'from plugin1' });
* });
*
* const plugin2 = CommandControlPlugin((ctx, sdt) => {
* return controller.next({ 'plugin2/data': ctx.user.id });
* });
*
* export default commandModule({
* type: CommandType.Slash,
* plugins: [plugin1, plugin2],
* execute: (ctx, sdt) => {
* console.log(sdt.state); // Access accumulated state
* }
* });
*
* @remarks
* - Control plugins are executed in order when a discord.js event is emitted
* - Use controller.next() to continue to next plugin or controller.stop() to halt execution
* - State can be passed between plugins using controller.next({ key: value })
* - State keys should be namespaced to avoid collisions (e.g., 'plugin-name/key')
* - Final accumulated state is passed to the command's execute function
* - All plugins must succeed for the command to execute
* - Plugins have access to dependencies through the sdt.deps object
* - Useful for implementing preconditions, filters, and command preprocessing
*/
export function CommandControlPlugin<I extends CommandType>(
execute: (...args: CommandArgs<I>) => PluginResult,
) {
return makePlugin(PluginType.Control, execute);
}
/**
* @since 1.0.0
* The object passed into every plugin to control a command's behavior
*/
export const controller = {
next: (val?: Dictionary) => Ok(val),
stop: (val?: string) => Err(val),
};
export type Controller = typeof controller;

65
src/core/presences.ts Normal file
View File

@@ -0,0 +1,65 @@
import type { ActivitiesOptions } from "discord.js";
import type { IntoDependencies } from "./ioc";
import type { Emitter } from "./interfaces";
import { Awaitable } from "../types/utility";
type Status = 'online' | 'idle' | 'invisible' | 'dnd'
type PresenceReduce = (previous: Presence.Result) => Awaitable<Presence.Result>;
export const Presence = {
/**
* A small wrapper to provide type inference.
* Create a Presence module which **MUST** be put in a file called presence.(language-extension)
* adjacent to the file where **Sern.init** is CALLED.
*/
module : <T extends (keyof Dependencies)[]>(conf: Presence.Config<T>) => conf,
/**
* Create a Presence body which can be either:
* - once, the presence is activated only once.
* - repeated, per cycle or event, the presence can be changed.
*/
of : (root: Omit<Presence.Result, 'repeat' | 'onRepeat'>) => {
return {
/**
* @example
* Presence
* .of({ activities: [{ name: "deez nuts" }] }) //starts presence with "deez nuts".
* .repeated(prev => {
* return {
* afk: true,
* activities: prev.activities?.map(s => ({ ...s, name: s.name+"s" }))
* };
* }, 10000)) //every 10 s, the callback sets the presence to the value returned.
*/
repeated: (onRepeat: PresenceReduce, repeat: number | [Emitter, string]) => {
return { repeat, onRepeat, ...root }
},
/**
* @example
* ```ts
* Presence.of({
* activities: [{ name: "Chilling out" }]
* }).once() // Sets the presence once, with what's provided in '.of()'
* ```
*/
once: () => root
};
}
}
export declare namespace Presence {
export type Config<T extends (keyof Dependencies)[]> = {
inject?: [...T]
execute: (...v: IntoDependencies<T>) => Awaitable<Presence.Result>;
}
export interface Result {
status?: Status;
afk?: boolean;
activities?: ActivitiesOptions[];
shardId?: number[];
repeat?: number | [Emitter, string];
onRepeat?: PresenceReduce
}
}

View File

@@ -0,0 +1,131 @@
import type {
BaseInteraction,
ChatInputCommandInteraction,
Client,
InteractionReplyOptions,
Message,
MessageReplyOptions,
Snowflake,
User,
} from 'discord.js';
import { Result, Ok, Err, val } from './result';
import * as assert from 'assert';
import type { ReplyOptions } from '../../types/utility';
import { fmt } from '../functions'
import { SernError } from './enums';
/**
* @since 1.0.0
* Provides values shared between
* Message and ChatInputCommandInteraction
*/
export class Context {
get options() {
if(this.isMessage()) {
const [, ...rest] = fmt(this.message.content, this.prefix);
return rest;
}
return this.interaction.options;
}
protected constructor(protected ctx: Result<Message, ChatInputCommandInteraction>,
private __prefix?: string) { }
public get prefix() {
return this.__prefix;
}
public get id(): Snowflake {
return val(this.ctx).id
}
public get channel() {
return val(this.ctx).channel;
}
public get channelId(): Snowflake {
return val(this.ctx).channelId;
}
/**
* If context is holding a message, message.author
* else, interaction.user
*/
public get user(): User {
if(this.ctx.ok) {
return this.ctx.value.author;
}
return this.ctx.error.user;
}
public get userId(): Snowflake {
return this.user.id;
}
public get createdTimestamp(): number {
return val(this.ctx).createdTimestamp;
}
public get guild() {
return val(this.ctx).guild;
}
public get guildId() {
return val(this.ctx).guildId;
}
/*
* interactions can return APIGuildMember if the guild it is emitted from is not cached
*/
public get member() {
return val(this.ctx).member;
}
get message(): Message {
if(this.ctx.ok) {
return this.ctx.value;
}
throw Error(SernError.MismatchEvent);
}
public isMessage(): this is Context & { ctx: Result<Message, never> } {
return this.ctx.ok;
}
public isSlash(): this is Context & { ctx: Result<never, ChatInputCommandInteraction> } {
return !this.isMessage();
}
get interaction(): ChatInputCommandInteraction {
if(!this.ctx.ok) {
return this.ctx.error;
}
throw Error(SernError.MismatchEvent);
}
public get client(): Client {
return val(this.ctx).client;
}
public get inGuild(): boolean {
return val(this.ctx).inGuild()
}
public async reply(content: ReplyOptions) {
if(this.ctx.ok) {
return this.ctx.value.reply(content as MessageReplyOptions)
}
interface FetchReply { fetchReply: true };
return this.ctx.error.reply(content as InteractionReplyOptions & FetchReply)
}
static wrap(wrappable: BaseInteraction | Message, prefix?: string): Context {
if ('interaction' in wrappable) {
return new Context(Ok(wrappable), prefix);
}
assert.ok(wrappable.isChatInputCommand(), "Context created with bad interaction.");
return new Context(Err(wrappable), prefix);
}
}

View File

@@ -0,0 +1,89 @@
import { ScheduledTask } from '../../types/core-modules';
import type { LogPayload, Logging, ErrorHandling, Disposable } from '../interfaces';
import { CronJob } from 'cron';
/**
* @internal
* @since 2.0.0
* Version 4.0.0 will internalize this api. Please refrain from using the defaults!
*/
export class DefaultErrorHandling implements ErrorHandling {
crash(err: Error): never {
throw err;
}
updateAlive(err: Error) {
throw err;
}
}
/**
* @internal
* @since 2.0.0
* Version 4.0.0 will internalize this api. Please refrain from using ModuleStore!
*/
export class DefaultLogging implements Logging {
private date() { return new Date() }
debug(payload: LogPayload): void {
console.debug(`DEBUG: ${this.date().toISOString()} -> ${payload.message}`);
}
error(payload: LogPayload): void {
console.error(`ERROR: ${this.date().toISOString()} -> ${payload.message}`);
}
info(payload: LogPayload): void {
console.info(`INFO: ${this.date().toISOString()} -> ${payload.message}`);
}
warning(payload: LogPayload): void {
console.warn(`WARN: ${this.date().toISOString()} -> ${payload.message}`);
}
}
export class TaskScheduler implements Disposable {
private __tasks: Map<string, CronJob<any, any>> = new Map();
schedule(uuid: string, task: ScheduledTask, deps: Dependencies) {
if (this.__tasks.has(uuid)) {
throw Error("while scheduling a task \
found another task of same name. Not scheduling " +
uuid + "again." );
}
try {
const onTick = async function(this: CronJob) {
task.execute({ id: uuid,
lastTimeExecution: this.lastExecution,
nextTimeExecution: this.nextDate().toJSDate() }, { deps })
}
const job = CronJob.from({ cronTime: task.trigger, onTick, timeZone: task.timezone });
job.start();
this.__tasks.set(uuid, job);
} catch (error) {
throw Error(`while scheduling a task ${uuid} ` + error);
}
}
kill(taskName: string): boolean {
const job = this.__tasks.get(taskName);
if (job) {
job.stop();
this.__tasks.delete(taskName);
return true;
}
return false;
}
get tasks(): string[] {
return Array.from(this.__tasks.keys());
}
dispose() {
this.__tasks.forEach((_, id) => {
this.kill(id);
this.__tasks.delete(id);
})
}
}

View File

@@ -0,0 +1,134 @@
/**
* @since 1.0.0
* A bitfield that discriminates command modules
* @enum { number }
* @example
* ```ts
* export default commandModule({
* // highlight-next-line
* type : CommandType.Text,
* name : 'a text command'
* execute(message) {
* console.log(message.content)
* }
* })
* ```
*/
export enum CommandType {
Text = 1 << 0,
Slash = 1 << 1,
Both = 3,
CtxUser = 1 << 2,
CtxMsg = 1 << 3,
Button = 1 << 4,
StringSelect = 1 << 5,
Modal = 1 << 6,
UserSelect = 1 << 7,
RoleSelect = 1 << 8,
MentionableSelect = 1 << 9,
ChannelSelect = 1 << 10,
}
/**
* A bitfield that discriminates event modules
* @enum { number }
* @example
* ```ts
* export default eventModule({
* //highlight-next-line
* type : EventType.Discord,
* name : 'guildMemberAdd'
* execute(member : GuildMember) {
* console.log(member)
* }
* })
* ```
*/
export enum EventType {
/**
* The EventType for handling discord events
*/
Discord,
/**
* The EventType for handling sern events
*/
Sern,
/**
* The EventType for handling external events.
* Could be for example, `process` events, database events
*/
External,
}
/**
* A bitfield that discriminates plugins
* @enum { number }
* @example
* ```ts
* export default function myPlugin() : EventPlugin<CommandType.Text> {
* //highlight-next-line
* type : PluginType.Event,
* execute([ctx, args], controller) {
* return controller.next();
* }
* }
* ```
*/
export enum PluginType {
/**
* The PluginType for InitPlugins
*/
Init = 1,
/**
* The PluginType for EventPlugins
*/
Control = 2,
}
/**
* @deprecated - Use strings 'success' | 'failure' | 'warning'
* @enum { string }
*/
export enum PayloadType {
Success = 'success',
Failure = 'failure',
Warning = 'warning',
}
/**
* @enum { string }
*/
export const enum SernError {
/**
* Throws when registering an invalid module.
* This means it is undefined or an invalid command type was provided
*/
InvalidModuleType = 'Detected an unknown module type',
/**
* Attempted to lookup module in command module store. Nothing was found!
*/
UndefinedModule = `A module could not be detected`,
/**
* Attempted to lookup module in command module store. Nothing was found!
*/
MismatchModule = `A module type mismatched with event emitted!`,
/**
* Unsupported interaction at this moment.
*/
NotSupportedInteraction = `This interaction is not supported.`,
/**
* One plugin called `controller.stop()` (end command execution / loading)
*/
PluginFailure = `A plugin failed to call controller.next()`,
/**
* A crash that occurs when accessing an invalid property of Context
*/
MismatchEvent = `You cannot use message when an interaction fired or vice versa`,
/**
* Unsupported feature attempted to access at this time
*/
NotSupportedYet = `This feature is not supported yet`,
/**
* Required Dependency not found
*/
MissingRequired = `@sern/client is required but was not found`,
}

View File

@@ -0,0 +1,20 @@
export type Result<Ok, Err> =
| { ok: true; value: Ok }
| { ok: false; error: Err };
export const Ok = <Ok>(value: Ok) => ({ ok: true, value } as const);
export const Err = <Err>(error: Err) => ({ ok: false, error } as const);
export const val = <O, E>(r: Result<O, E>) => r.ok ? r.value : r.error;
export const EMPTY_ERR = Err(undefined);
/**
* Wrap an async operation that may throw an Error (`try-catch` style) into checked exception style
* @param op The operation function
*/
export async function wrapAsync<T, E = unknown>(op: () => Promise<T>): Promise<Result<T, E>> {
try { return op()
.then(Ok)
.catch(Err); }
catch (e) { return Promise.resolve(Err(e as E)); }
}

View File

@@ -1,110 +0,0 @@
import type {
BothCommand,
ButtonCommand,
ContextMenuMsg,
ContextMenuUser,
ModalSubmitCommand,
SelectMenuCommand,
SlashCommand,
} from '../structures/module';
import Context from '../structures/context';
import type { SlashOptions } from '../../types/handler';
import { asyncResolveArray } from '../utilities/asyncResolveArray';
import { controller } from '../sern';
import type {
ButtonInteraction,
ModalSubmitInteraction,
SelectMenuInteraction,
AutocompleteInteraction,
ChatInputCommandInteraction,
Interaction,
UserContextMenuCommandInteraction,
MessageContextMenuCommandInteraction,
} from 'discord.js';
import { isAutocomplete } from '../utilities/predicates';
import { SernError } from '../structures/errors';
import treeSearch from '../utilities/treeSearch';
export function applicationCommandDispatcher(interaction: Interaction) {
if (isAutocomplete(interaction)) {
return dispatchAutocomplete(interaction);
} else {
const ctx = Context.wrap(interaction as ChatInputCommandInteraction);
const args: ['slash', SlashOptions] = ['slash', ctx.interaction.options];
return (mod: BothCommand | SlashCommand) => ({
mod,
execute: () => mod.execute(ctx, args),
eventPluginRes: asyncResolveArray(
mod.onEvent.map(plugs => plugs.execute([ctx, args], controller)),
),
});
}
}
export function dispatchAutocomplete(interaction: AutocompleteInteraction) {
return (mod: BothCommand | SlashCommand) => {
const selectedOption = treeSearch(interaction, mod.options);
if (selectedOption !== undefined) {
return {
mod,
execute: () => selectedOption.command.execute(interaction),
eventPluginRes: asyncResolveArray(
selectedOption.command.onEvent.map(e => e.execute(interaction, controller)),
),
};
}
throw Error(
SernError.NotSupportedInteraction + ` There is no autocomplete tag for this option`,
);
};
}
export function modalCommandDispatcher(interaction: ModalSubmitInteraction) {
return (mod: ModalSubmitCommand) => ({
mod,
execute: () => mod.execute(interaction),
eventPluginRes: asyncResolveArray(
mod.onEvent.map(plugs => plugs.execute([interaction], controller)),
),
});
}
export function buttonCommandDispatcher(interaction: ButtonInteraction) {
return (mod: ButtonCommand) => ({
mod,
execute: () => mod.execute(interaction),
eventPluginRes: asyncResolveArray(
mod.onEvent.map(plugs => plugs.execute([interaction], controller)),
),
});
}
export function selectMenuCommandDispatcher(interaction: SelectMenuInteraction) {
return (mod: SelectMenuCommand) => ({
mod,
execute: () => mod.execute(interaction),
eventPluginRes: asyncResolveArray(
mod.onEvent.map(plugs => plugs.execute([interaction], controller)),
),
});
}
export function ctxMenuUserDispatcher(interaction: UserContextMenuCommandInteraction) {
return (mod: ContextMenuUser) => ({
mod,
execute: () => mod.execute(interaction),
eventPluginRes: asyncResolveArray(
mod.onEvent.map(plugs => plugs.execute([interaction], controller)),
),
});
}
export function ctxMenuMsgDispatcher(interaction: MessageContextMenuCommandInteraction) {
return (mod: ContextMenuMsg) => ({
mod,
execute: () => mod.execute(interaction),
eventPluginRes: asyncResolveArray(
mod.onEvent.map(plugs => plugs.execute([interaction], controller)),
),
});
}

View File

@@ -1,10 +0,0 @@
import type Wrapper from '../structures/wrapper';
import { Subject, type Observable } from 'rxjs';
export abstract class EventsHandler<T> {
protected payloadSubject = new Subject<T>();
protected abstract discordEvent: Observable<unknown>;
protected constructor(protected wrapper: Wrapper) {}
protected abstract init(): void;
protected abstract setState(state: T): void;
}

View File

@@ -1,123 +0,0 @@
import type { Interaction } from 'discord.js';
import { catchError, concatMap, from, fromEvent, map, Observable } from 'rxjs';
import type Wrapper from '../structures/wrapper';
import { EventsHandler } from './eventsHandler';
import {
isApplicationCommand,
isAutocomplete,
isMessageComponent,
isModalSubmit,
} from '../utilities/predicates';
import * as Files from '../utilities/readFile';
import type { CommandModule } from '../structures/module';
import { SernError } from '../structures/errors';
import { CommandType, PayloadType } from '../structures/enums';
import { match, P } from 'ts-pattern';
import {
applicationCommandDispatcher,
buttonCommandDispatcher,
ctxMenuMsgDispatcher,
ctxMenuUserDispatcher,
modalCommandDispatcher,
selectMenuCommandDispatcher,
} from './dispatchers';
import type {
ButtonInteraction,
ModalSubmitInteraction,
SelectMenuInteraction,
UserContextMenuCommandInteraction,
MessageContextMenuCommandInteraction,
} from 'discord.js';
import { executeModule } from './observableHandling';
export default class InteractionHandler extends EventsHandler<{
event: Interaction;
mod: CommandModule;
}> {
protected override discordEvent: Observable<Interaction>;
constructor(protected wrapper: Wrapper) {
super(wrapper);
this.discordEvent = <Observable<Interaction>>fromEvent(wrapper.client, 'interactionCreate');
this.init();
this.payloadSubject
.pipe(
map(this.processModules),
concatMap(({ mod, execute, eventPluginRes }) => {
//resolve all the Results from event plugins
return from(eventPluginRes).pipe(map(res => ({ mod, res, execute })));
}),
concatMap(payload => executeModule(wrapper, payload)),
catchError((err, caught) => {
wrapper.sernEmitter?.emit('error', err);
return caught;
}),
)
.subscribe();
}
override init() {
this.discordEvent.subscribe({
next: event => {
if (isMessageComponent(event)) {
const mod = Files.MessageCompCommands[event.componentType].get(event.customId);
this.setState({ event, mod });
} else if (isApplicationCommand(event) || isAutocomplete(event)) {
const mod =
Files.ApplicationCommands[event.commandType].get(event.commandName) ??
Files.BothCommands.get(event.commandName);
this.setState({ event, mod });
} else if (isModalSubmit(event)) {
/**
* maybe move modal submits into message component object maps?
*/
const mod = Files.ModalSubmitCommands.get(event.customId);
this.setState({ event, mod });
} else {
throw Error('This interaction is not supported yet');
}
},
error: reason => {
this.wrapper.sernEmitter?.emit('error', { type: PayloadType.Failure, reason });
},
});
}
protected setState(state: { event: Interaction; mod: CommandModule | undefined }): void {
if (state.mod === undefined) {
this.wrapper?.sernEmitter?.emit('warning', 'Found no module for this interaction');
} else {
//if statement above checks already, safe cast
this.payloadSubject.next(state as { event: Interaction; mod: CommandModule });
}
}
protected processModules({ mod, event }: { event: Interaction; mod: CommandModule }) {
return match(mod)
.with(
{ type: P.union(CommandType.Slash, CommandType.Both) },
applicationCommandDispatcher(event),
)
.with(
{ type: CommandType.Modal },
modalCommandDispatcher(event as ModalSubmitInteraction),
)
.with({ type: CommandType.Button }, buttonCommandDispatcher(event as ButtonInteraction))
.with(
{ type: CommandType.MenuSelect },
selectMenuCommandDispatcher(event as SelectMenuInteraction),
)
.with(
{ type: CommandType.MenuUser },
ctxMenuUserDispatcher(event as UserContextMenuCommandInteraction),
)
.with(
{ type: CommandType.MenuMsg },
ctxMenuMsgDispatcher(event as MessageContextMenuCommandInteraction),
)
.otherwise(() => {
throw Error(SernError.MismatchModule);
});
}
}

View File

@@ -1,81 +0,0 @@
import { EventsHandler } from './eventsHandler';
import { catchError, concatMap, from, fromEvent, map, Observable, of, switchMap } from 'rxjs';
import type Wrapper from '../structures/wrapper';
import type { Message } from 'discord.js';
import { executeModule, ignoreNonBot, isOneOfCorrectModules } from './observableHandling';
import { fmt } from '../utilities/messageHelpers';
import Context from '../structures/context';
import * as Files from '../utilities/readFile';
import type { TextCommand } from '../structures/module';
import { CommandType, PayloadType } from '../structures/enums';
import { asyncResolveArray } from '../utilities/asyncResolveArray';
import { controller } from '../sern';
export default class MessageHandler extends EventsHandler<{
ctx: Context;
args: ['text', string[]];
mod: TextCommand;
}> {
protected discordEvent: Observable<Message>;
public constructor(wrapper: Wrapper) {
super(wrapper);
this.discordEvent = <Observable<Message>>fromEvent(wrapper.client, 'messageCreate');
this.init();
this.payloadSubject
.pipe(
switchMap(({ mod, ctx, args }) => {
const res = asyncResolveArray(
mod.onEvent.map(ePlug => {
return ePlug.execute([ctx, args], controller);
}),
);
const execute = () => {
return mod.execute(ctx, args);
};
//resolves the promise and re-emits it back into source
return from(res).pipe(map(res => ({ mod, execute, res })));
}),
concatMap(payload => executeModule(wrapper, payload)),
catchError((err, caught) => {
wrapper.sernEmitter?.emit('error', err);
return caught;
}),
)
.subscribe();
}
protected init(): void {
if (this.wrapper.defaultPrefix === undefined) return; //for now, just ignore if prefix doesn't exist
const { defaultPrefix } = this.wrapper;
this.discordEvent
.pipe(
ignoreNonBot(this.wrapper.defaultPrefix),
map(message => {
const [prefix, ...rest] = fmt(message, defaultPrefix);
return {
ctx: Context.wrap(message),
args: <['text', string[]]>['text', rest],
mod:
Files.TextCommands.text.get(prefix) ??
Files.BothCommands.get(prefix) ??
Files.TextCommands.aliases.get(prefix),
};
}),
concatMap(element =>
of(element.mod).pipe(
isOneOfCorrectModules(CommandType.Text),
map(mod => ({ ...element, mod })),
),
),
)
.subscribe({
next: value => this.setState(value),
error: reason =>
this.wrapper.sernEmitter?.emit('error', { type: PayloadType.Failure, reason }),
});
}
protected setState(state: { ctx: Context; args: ['text', string[]]; mod: TextCommand }) {
this.payloadSubject.next(state);
}
}

View File

@@ -1,112 +0,0 @@
import type { Message } from 'discord.js';
import { concatMap, from, Observable, of, tap, throwError } from 'rxjs';
import { SernError } from '../structures/errors';
import type { Module, CommandModuleDefs, CommandModule } from '../structures/module';
import { Result } from 'ts-results-es';
import type { CommandType } from '../structures/enums';
import type Wrapper from '../structures/wrapper';
import { PayloadType } from '../structures/enums';
export function ignoreNonBot(prefix: string) {
return (src: Observable<Message>) =>
new Observable<Message>(subscriber => {
return src.subscribe({
next(m) {
const messageFromHumanAndHasPrefix =
!m.author.bot &&
m.content
.slice(0, prefix.length)
.localeCompare(prefix, undefined, { sensitivity: 'accent' }) === 0;
if (messageFromHumanAndHasPrefix) {
subscriber.next(m);
}
},
error: e => subscriber.error(e),
complete: () => subscriber.complete(),
});
});
}
/**
* If the current value in Result stream is an error, calls callback.
* @param cb
*/
export function errTap<T extends Module>(cb: (err: SernError) => void) {
return (src: Observable<Result<{ mod: T; absPath: string }, SernError>>) =>
new Observable<{ mod: T; absPath: string }>(subscriber => {
return src.subscribe({
next(value) {
if (value.err) {
cb(value.val);
} else {
subscriber.next(value.val);
}
},
error: e => subscriber.error(e),
complete: () => subscriber.complete(),
});
});
}
//POG
export function isOneOfCorrectModules<T extends readonly CommandType[]>(...inputs: [...T]) {
return (src: Observable<CommandModule | undefined>) => {
return new Observable<CommandModuleDefs[T[number]]>(subscriber => {
return src.subscribe({
next(mod) {
if (mod === undefined) {
return throwError(() => SernError.UndefinedModule);
}
if (inputs.some(type => (mod.type & type) !== 0)) {
subscriber.next(mod as CommandModuleDefs[T[number]]);
} else {
return throwError(() => SernError.MismatchModule);
}
},
error: e => subscriber.error(e),
complete: () => subscriber.complete(),
});
});
};
}
export function executeModule(
wrapper: Wrapper,
payload: {
mod: CommandModule;
execute: () => unknown;
res: Result<void, void>[];
},
) {
if (payload.res.every(el => el.ok)) {
const executeFn = Result.wrapAsync<unknown, Error | string>(() =>
Promise.resolve(payload.execute()),
);
return from(executeFn).pipe(
concatMap(res => {
if (res.err) {
return throwError(() => ({
type: PayloadType.Failure,
reason: res.val,
module: payload.mod,
}));
}
return of(res.val).pipe(
tap(() =>
wrapper.sernEmitter?.emit('module.activate', {
type: PayloadType.Success,
module: payload.mod,
}),
),
);
}),
);
} else {
wrapper.sernEmitter?.emit('module.activate', {
type: PayloadType.Failure,
module: payload.mod,
reason: SernError.PluginFailure,
});
return of(undefined);
}
}

View File

@@ -1,159 +0,0 @@
import { EventsHandler } from './eventsHandler';
import type Wrapper from '../structures/wrapper';
import { concatMap, fromEvent, Observable, map, take, of, from, toArray, switchMap } from 'rxjs';
import type { CommandModule } from '../structures/module';
import * as Files from '../utilities/readFile';
import { errTap } from './observableHandling';
import type { DefinedCommandModule } from '../../types/handler';
import { basename } from 'path';
import { CommandType, PayloadType, PluginType } from '../structures/enums';
import { processCommandPlugins } from './userDefinedEventsHandling';
import type { Awaitable } from 'discord.js';
import { SernError } from '../structures/errors';
import { match } from 'ts-pattern';
import { type Result, Err, Ok } from 'ts-results-es';
import { ApplicationCommandType, ComponentType } from 'discord.js';
export default class ReadyHandler extends EventsHandler<{
mod: DefinedCommandModule;
absPath: string;
}> {
protected discordEvent!: Observable<{ mod: CommandModule; absPath: string }>;
constructor(wrapper: Wrapper) {
super(wrapper);
const ready$ = fromEvent(this.wrapper.client, 'ready').pipe(take(1));
this.discordEvent = ready$.pipe(
concatMap(() =>
Files.buildData<CommandModule>(this.wrapper.commands).pipe(
errTap(reason =>
wrapper.sernEmitter?.emit('module.register', {
type: PayloadType.Failure,
module: undefined,
reason,
}),
),
),
),
);
this.init();
this.payloadSubject
.pipe(
concatMap(payload => this.processPlugins(payload)),
concatMap(payload => this.resolvePlugins(payload)),
)
.subscribe(payload => {
const allPluginsSuccessful = payload.pluginRes.every(({ execute }) => execute.ok);
if (allPluginsSuccessful) {
const res = registerModule(payload.mod);
if (res.err) {
throw Error(SernError.InvalidModuleType);
}
wrapper.sernEmitter?.emit('module.register', {
type: PayloadType.Success,
module: payload.mod,
});
} else {
wrapper.sernEmitter?.emit('module.register', {
type: PayloadType.Failure,
module: payload.mod,
reason: SernError.PluginFailure,
});
}
});
}
private static intoDefinedModule({ absPath, mod }: { absPath: string; mod: CommandModule }): {
absPath: string;
mod: DefinedCommandModule;
} {
return {
absPath,
mod: {
name: mod?.name ?? Files.fmtFileName(basename(absPath)),
description: mod?.description ?? '...',
...mod,
},
};
}
private resolvePlugins({
mod,
cmdPluginRes,
}: {
mod: DefinedCommandModule;
cmdPluginRes: {
name: string;
description: string;
execute: Awaitable<Result<void, void>>;
type: PluginType.Command;
}[];
}) {
if (mod.plugins.length === 0) {
return of({ mod, pluginRes: [] });
}
// modules with no event plugins are ignored in the previous
return from(cmdPluginRes).pipe(
switchMap(pl =>
from(pl.execute).pipe(
map(execute => ({ ...pl, execute })),
toArray(),
),
),
map(pluginRes => ({ mod, pluginRes })),
);
}
private processPlugins(payload: { mod: DefinedCommandModule; absPath: string }) {
const cmdPluginRes = processCommandPlugins(this.wrapper, payload);
return of({ mod: payload.mod, cmdPluginRes });
}
protected init() {
this.discordEvent.pipe(map(ReadyHandler.intoDefinedModule)).subscribe({
next: value => this.setState(value),
complete: () => this.payloadSubject.unsubscribe(),
});
}
protected setState(state: { absPath: string; mod: DefinedCommandModule }): void {
this.payloadSubject.next(state);
}
}
function registerModule(mod: DefinedCommandModule): Result<void, void> {
const name = mod.name;
return match<DefinedCommandModule>(mod)
.with({ type: CommandType.Text }, mod => {
mod.alias?.forEach(a => Files.TextCommands.aliases.set(a, mod));
Files.TextCommands.text.set(name, mod);
return Ok.EMPTY;
})
.with({ type: CommandType.Slash }, mod => {
Files.ApplicationCommands[ApplicationCommandType.ChatInput].set(name, mod);
return Ok.EMPTY;
})
.with({ type: CommandType.Both }, mod => {
Files.BothCommands.set(name, mod);
mod.alias?.forEach(a => Files.TextCommands.aliases.set(a, mod));
return Ok.EMPTY;
})
.with({ type: CommandType.MenuUser }, mod => {
Files.ApplicationCommands[ApplicationCommandType.User].set(name, mod);
return Ok.EMPTY;
})
.with({ type: CommandType.MenuMsg }, mod => {
Files.ApplicationCommands[ApplicationCommandType.Message].set(name, mod);
return Ok.EMPTY;
})
.with({ type: CommandType.Button }, mod => {
Files.MessageCompCommands[ComponentType.Button].set(name, mod);
return Ok.EMPTY;
})
.with({ type: CommandType.MenuSelect }, mod => {
Files.MessageCompCommands[ComponentType.SelectMenu].set(name, mod);
return Ok.EMPTY;
})
.with({ type: CommandType.Modal }, mod => {
Files.ModalSubmitCommands.set(name, mod);
return Ok.EMPTY;
})
.otherwise(() => Err.EMPTY);
}

View File

@@ -1,87 +0,0 @@
import { from, fromEvent, map } from 'rxjs';
import * as Files from '../utilities/readFile';
import { buildData, ExternalEventEmitters } from '../utilities/readFile';
import { controller } from '../sern';
import type {
DefinedCommandModule,
DefinedEventModule,
EventInput,
SpreadParams,
} from '../../types/handler';
import type { EventModule } from '../structures/module';
import { PayloadType } from '../structures/enums';
import type Wrapper from '../structures/wrapper';
import { basename } from 'path';
import { match } from 'ts-pattern';
import { isDiscordEvent, isSernEvent } from '../utilities/predicates';
import { errTap } from './observableHandling';
/**
* Utility function to process command plugins for all Modules
* @param wrapper
* @param payload
*/
export function processCommandPlugins<T extends DefinedCommandModule>(
wrapper: Wrapper,
payload: { mod: T; absPath: string },
) {
return payload.mod.plugins.map(plug => ({
...plug,
name: plug?.name ?? 'Unnamed Plugin',
description: plug?.description ?? '...',
execute: plug.execute(wrapper, payload, controller),
}));
}
export function processEvents(wrapper: Wrapper, events: EventInput) {
const eventStream$ = eventObservable$(wrapper, events);
const normalize$ = eventStream$.pipe(
map(({ mod, absPath }) => {
return <DefinedEventModule>{
name: mod?.name ?? Files.fmtFileName(basename(absPath)),
description: mod?.description ?? '...',
...mod,
};
}),
);
normalize$.subscribe(e => {
const emitter = isSernEvent(e)
? wrapper?.sernEmitter
: isDiscordEvent(e)
? wrapper.client
: ExternalEventEmitters.get(e.emitter);
if (emitter === undefined) {
throw new Error(`Cannot find event emitter as it is undefined`);
}
//Would add sern event emitter for events loaded, attached onto sern emitter, but could lead to unwanted behavior!
fromEvent(emitter, e.name, e.execute as SpreadParams<typeof e.execute>).subscribe();
});
}
function eventObservable$({ sernEmitter }: Wrapper, events: EventInput) {
return match(events)
.when(Array.isArray, (arr: { mod: EventModule; absPath: string }[]) => {
return from(arr);
})
.when(
e => typeof e === 'string',
(eventsDir: string) => {
return buildData<EventModule>(eventsDir).pipe(
errTap(reason =>
sernEmitter?.emit('module.register', {
type: PayloadType.Failure,
module: undefined,
reason,
}),
),
);
},
)
.when(
e => typeof e === 'function',
(evs: () => { mod: EventModule; absPath: string }[]) => {
return from(evs());
},
)
.run();
}

Some files were not shown because too many files have changed in this diff Show More