Compare commits

...

49 Commits

Author SHA1 Message Date
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
26 changed files with 2821 additions and 2171 deletions

View File

@@ -5,6 +5,8 @@ on:
push: push:
branches: branches:
main main
paths:
- '**.ts'
pull_request_target: pull_request_target:
branches: branches:
main main

View File

@@ -1,30 +1,23 @@
name: NPM / Publish name: NPM / Publish
on: on:
release: workflow_dispatch:
types: [created]
jobs: jobs:
build: test-and-publish:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: 16 node-version: 17
- run: npm ci - uses: pnpm/action-setup@v2
- run: npm test
publish-npm:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with: with:
node-version: 16 run_install: |
registry-url: https://registry.npmjs.org/ - recursive: true
- run: npm ci args: [--strict-peer-dependencies]
- run: npm publish - run: pnpm install
env: - run: pnpm build
NODE_AUTH_TOKEN: ${{secrets.npm_token}} - uses: JS-DevTools/npm-publish@v1
with:
token: ${{ secrets.NPM_TOKEN }}
access: "public"

View File

@@ -1,5 +1,44 @@
# Changelog # Changelog
## [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) ## [2.5.0](https://github.com/sern-handler/handler/compare/v2.1.1...v2.5.0) (2023-01-30)

View File

@@ -3,7 +3,7 @@
</div> </div>
<h1 align="center">Handlers. Redefined.</h1> <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" styles="margin-top: 10px"> <div align="center" styles="margin-top: 10px">
<img src="https://img.shields.io/badge/open-source-brightgreen" /> <img src="https://img.shields.io/badge/open-source-brightgreen" />
@@ -14,20 +14,6 @@
<img alt="Lines of code" src="https://img.shields.io/badge/total%20lines-2k-blue" /> <img alt="Lines of code" src="https://img.shields.io/badge/total%20lines-2k-blue" />
</div> </div>
## 📜 Installation
```sh
npm install @sern/handler
```
```sh
yarn add @sern/handler
```
```sh
pnpm add @sern/handler
```
## Why? ## Why?
- Most handlers don't support discord.js 14.7+ - Most handlers don't support discord.js 14.7+
- Customizable commands - Customizable commands
@@ -43,6 +29,20 @@ pnpm add @sern/handler
* ESM, CommonJS and TypeScript support * ESM, CommonJS and TypeScript support
* A powerful CLI and awesome community-made plugins * A powerful CLI and awesome community-made plugins
## 📜 Installation
```sh
npm install @sern/handler
```
```sh
yarn add @sern/handler
```
```sh
pnpm add @sern/handler
```
## 👶 Basic Usage ## 👶 Basic Usage
#### ` index.js (CommonJS)` #### ` index.js (CommonJS)`
@@ -52,10 +52,7 @@ pnpm add @sern/handler
const { Client, GatewayIntentBits } = require('discord.js'); const { Client, GatewayIntentBits } = require('discord.js');
// Import Sern namespace // Import Sern namespace
const { Sern } = require('@sern/handler'); const { Sern, single } = require('@sern/handler');
// Our configuration file
const { defaultPrefix, token } = require('./config.json');
const client = new Client({ const client = new Client({
intents: [ intents: [
@@ -66,8 +63,8 @@ const client = new Client({
}); });
export const useContainer = Sern.makeDependencies({ export const useContainer = Sern.makeDependencies({
build: root => root build: root => root
.add({ '@sern/client': single(client) }) .add({ '@sern/client': single(() => client) })
.add({ '@sern/logger': single(new DefaultLogging()) }) .upsert({ '@sern/logger': single(() => new DefaultLogging()) })
}); });
//View docs for all options //View docs for all options
@@ -80,7 +77,7 @@ Sern.init({
} }
}); });
client.login(token); client.login("YOUR_BOT_TOKEN_HERE");
``` ```
#### ` ping.js (CommonJS)` #### ` ping.js (CommonJS)`
@@ -89,16 +86,21 @@ client.login(token);
const { CommandType, commandModule } = require('@sern/handler'); const { CommandType, commandModule } = require('@sern/handler');
exports.default = commandModule({ exports.default = commandModule({
name: 'ping',
description: 'A ping pong command',
type: CommandType.Slash, type: CommandType.Slash,
description: 'A ping pong command',
execute(ctx) { execute(ctx) {
ctx.reply('pong!'); ctx.reply('pong!');
} }
}); });
``` ```
See our [templates](https://github.com/sern-handler/templates) for TypeScript examples and more. ## 🤖 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 your favorite artists on Discord.
- [ava](https://github.com/SrIzan10/ava), A discord bot that plays KNGI and Gensokyo Radio.
- [ALMA (WIP)](https://github.com/Benzo-Fury/ALMA), Using AI to unleash the power in your server.
- [Protector (WIP)](https://github.com/needhamgary/Protector), Just a simple bot to help enhance a private minecraft server.
## 💻 CLI ## 💻 CLI
@@ -115,6 +117,4 @@ It is **highly encouraged** to use the [command line interface](https://github.c
- Pull up on [issues](https://github.com/sern-handler/handler/issues) and report bugs - Pull up on [issues](https://github.com/sern-handler/handler/issues) and report bugs
- All kinds of contributions are welcomed. - 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.

View File

@@ -1,7 +1,7 @@
{ {
"name": "@sern/handler", "name": "@sern/handler",
"packageManager": "pnpm@7.25.0", "packageManager": "pnpm@7.28.0",
"version": "2.5.0", "version": "2.6.0",
"description": "A customizable, batteries-included, powerful discord.js framework to automate and streamline bot development.", "description": "A customizable, batteries-included, powerful discord.js framework to automate and streamline bot development.",
"main": "dist/cjs/index.cjs", "main": "dist/cjs/index.cjs",
"module": "dist/esm/index.mjs", "module": "dist/esm/index.mjs",
@@ -13,7 +13,7 @@
} }
}, },
"scripts": { "scripts": {
"watch": "tsup --watch --dts", "watch": "tsup --dts --watch",
"clean-modules": "rimraf node_modules/ && npm install", "clean-modules": "rimraf node_modules/ && npm install",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"format": "eslint src/**/*.ts --fix", "format": "eslint src/**/*.ts --fix",
@@ -34,18 +34,18 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"iti": "^0.6.0", "iti": "^0.6.0",
"rxjs": "^7.5.6", "rxjs": "^7.8.0",
"ts-pattern": "^4.0.6", "ts-pattern": "^4.1.4",
"ts-results-es": "^3.5.0" "ts-results-es": "^3.5.0"
}, },
"devDependencies": { "devDependencies": {
"@typescript-eslint/eslint-plugin": "5.47.1", "@typescript-eslint/eslint-plugin": "5.54.0",
"@typescript-eslint/parser": "5.48.0", "@typescript-eslint/parser": "5.54.0",
"discord.js": ">= ^14.7.x", "discord.js": "^14.7.1",
"eslint": "8.30.0", "eslint": "8.30.0",
"prettier": "2.8.3", "prettier": "2.8.4",
"tsup": "^6.1.3", "tsup": "^6.6.3",
"typescript": "4.9.4" "typescript": "4.9.5"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

2073
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ import type { Processed } from '../../types/handler';
export interface ModuleManager { export interface ModuleManager {
get<T extends CommandType>( get<T extends CommandType>(
strat: (ms: ModuleStore) => Processed<CommandModuleDefs[T]> | undefined, strat: (ms: ModuleStore) => Processed<CommandModuleDefs[T]> | undefined,
): CommandModuleDefs[T] | undefined; ): Processed<CommandModuleDefs[T]> | undefined;
set(strat: (ms: ModuleStore) => void): void; set(strat: (ms: ModuleStore) => void): void;
} }

View File

@@ -1,27 +1,35 @@
import { _const } from '../utilities/functions'; import { _const } from '../utilities/functions';
type NotFunction = string | number | boolean | null | undefined | bigint | type NotFunction =
readonly any[] | { apply?: never, [k: string]: any } | | string
{ call?: never, [k: string]: any }; | number
| boolean
| null
| undefined
| bigint
| readonly any[]
| { apply?: never; [k: string]: any }
| { call?: never; [k: string]: any };
/** /**
* @deprecated * @deprecated
* @param cb * @param cb
*/ */
export function single<T extends NotFunction>(cb: T) : () => T; export function single<T extends NotFunction>(cb: T): () => T;
/** /**
* New signature * New signature
* @param cb * @param cb
*/ */
export function single<T extends () => unknown>(cb: T) : T; export function single<T extends () => unknown>(cb: T): T;
/** /**
* @__PURE__
* Please note that on intellij, the deprecation is for all signatures, which is unintended behavior (and * Please note that on intellij, the deprecation is for all signatures, which is unintended behavior (and
* very annoying). * very annoying).
* For future versions, ensure that single is being passed as a **callback!!** * For future versions, ensure that single is being passed as a **callback!!**
* @param cb * @param cb
*/ */
export function single<T>(cb: T) { export function single<T>(cb: T) {
if(typeof cb === 'function') return cb; if (typeof cb === 'function') return cb;
return () => cb; return () => cb;
} }
/** /**
@@ -29,19 +37,21 @@ export function single<T>(cb: T) {
* @param cb * @param cb
* Deprecated signature * Deprecated signature
*/ */
export function transient<T extends NotFunction>(cb: T) : () => () => T export function transient<T extends NotFunction>(cb: T): () => () => T;
export function transient<T extends () => () => unknown>(cb: T) : T; export function transient<T extends () => () => unknown>(cb: T): T;
/** /**
* @__PURE__
* Following iti's singleton and transient implementation, * Following iti's singleton and transient implementation,
* use transient if you want a new dependency every time your container getter is called * use transient if you want a new dependency every time your container getter is called
* @param cb * @param cb
*/ */
export function transient<T>(cb: (() => () => T) | T) { export function transient<T>(cb: (() => () => T) | T) {
if(typeof cb !== 'function') return () => () => cb; if (typeof cb !== 'function') return () => () => cb;
return cb; return cb;
} }
/** /**
* @__PURE__
* @deprecated * @deprecated
* @param value * @param value
* Please use the transient function instead * Please use the transient function instead

View File

@@ -1,12 +1,11 @@
import type { Container } from 'iti'; import type { Container } from 'iti';
import { SernError } from '../structures/errors';
import type { Dependencies, DependencyConfiguration, MapDeps } from '../../types/handler'; import type { Dependencies, DependencyConfiguration, MapDeps } from '../../types/handler';
import SernEmitter from '../sernEmitter'; import SernEmitter from '../sernEmitter';
import { DefaultErrorHandling, DefaultLogging, DefaultModuleManager } from '../contracts'; import { DefaultErrorHandling, DefaultLogging, DefaultModuleManager } from '../contracts';
import { ModuleStore } from '../structures/moduleStore';
import { Result } from 'ts-results-es'; import { Result } from 'ts-results-es';
import { BehaviorSubject } from 'rxjs'; import { BehaviorSubject } from 'rxjs';
import { createContainer } from 'iti'; import { createContainer } from 'iti';
import { type Wrapper, ModuleStore, SernError } from '../structures';
export const containerSubject = new BehaviorSubject(defaultContainer()); export const containerSubject = new BehaviorSubject(defaultContainer());
@@ -20,20 +19,18 @@ export function composeRoot<T extends Dependencies>(conf: DependencyConfiguratio
//Get the current container. This should have no client or possible logger yet. //Get the current container. This should have no client or possible logger yet.
const currentContainer = containerSubject.getValue(); const currentContainer = containerSubject.getValue();
const excludeLogger = conf.exclude?.has('@sern/logger'); const excludeLogger = conf.exclude?.has('@sern/logger');
if(!excludeLogger) { if (!excludeLogger) {
currentContainer.add({ currentContainer.add({
'@sern/logger' : () => new DefaultLogging() '@sern/logger': () => new DefaultLogging(),
}); });
} }
//Build the container based on the callback provided by the user //Build the container based on the callback provided by the user
const container = conf.build(currentContainer); const container = conf.build(currentContainer);
//Check if the built container contains @sern/client or throw //Check if the built container contains @sern/client or throw
// a runtime exception // a runtime exception
Result Result.wrap(() => container.get('@sern/client')).expect(SernError.MissingRequired);
.wrap(() => container.get('@sern/client'))
.expect(SernError.MissingRequired);
if(!excludeLogger) { if (!excludeLogger) {
container.get('@sern/logger')?.info({ message: 'All dependencies loaded successfully.' }); container.get('@sern/logger')?.info({ message: 'All dependencies loaded successfully.' });
} }
//I'm sorry little one //I'm sorry little one
@@ -61,12 +58,29 @@ export function useContainerRaw<T extends Dependencies>() {
*/ */
function defaultContainer() { function defaultContainer() {
return createContainer() return createContainer()
.add({ '@sern/errors': () => new DefaultErrorHandling()}) .add({ '@sern/errors': () => new DefaultErrorHandling() })
.add({ '@sern/store' : () => new ModuleStore()}) .add({ '@sern/store': () => new ModuleStore() })
.add(ctx => { .add(ctx => {
return { return {
'@sern/modules': () => new DefaultModuleManager(ctx['@sern/store']) '@sern/modules': () => new DefaultModuleManager(ctx['@sern/store']),
}; };
}) })
.add({ '@sern/emitter': () => new SernEmitter()}) as Container<Omit<Dependencies, '@sern/client' | '@sern/logger'>, {}>; .add({ '@sern/emitter': () => new SernEmitter() }) as Container<
Omit<Dependencies, '@sern/client' | '@sern/logger'>,
{}
>;
}
export function makeFetcher(wrapper: Wrapper) {
const requiredDependencyKeys = [
'@sern/emitter',
'@sern/client',
'@sern/errors',
'@sern/logger',
] as ['@sern/emitter', '@sern/client', '@sern/errors', '@sern/logger'];
return <Keys extends (keyof Dependencies)[]>(otherKeys: [...Keys]) =>
wrapper.containerConfig.get(...requiredDependencyKeys, ...otherKeys) as MapDeps<
Dependencies,
[...typeof requiredDependencyKeys, ...Keys]
>;
} }

View File

@@ -54,7 +54,7 @@ export function dispatchAutocomplete(
const option = treeSearch(interaction, module.options); const option = treeSearch(interaction, module.options);
if (option !== undefined) { if (option !== undefined) {
return { return {
module, module: option.command as Processed<Module>, //autocomplete is not a true "module" warning cast!
args: [interaction], args: [interaction],
}; };
} }

View File

@@ -1,34 +0,0 @@
import type Wrapper from '../structures/wrapper';
import { Subject, type Observable } from 'rxjs';
import type { EventEmitter } from 'events';
import type SernEmitter from '../sernEmitter';
import type { ErrorHandling, Logging, ModuleManager } from '../contracts';
/**
* why did I make this, definitely going to be changed in the future
*/
export abstract class EventsHandler<T> {
protected payloadSubject = new Subject<T>();
protected abstract discordEvent: Observable<unknown>;
protected client: EventEmitter;
protected emitter: SernEmitter;
protected crashHandler: ErrorHandling;
protected logger?: Logging;
protected modules: ModuleManager;
protected constructor({ containerConfig }: Wrapper) {
const [client, emitter, crash, modules, logger] = containerConfig.get(
'@sern/client',
'@sern/emitter',
'@sern/errors',
'@sern/modules',
'@sern/logger',
);
this.logger = logger as Logging | undefined;
this.modules = modules as ModuleManager;
this.client = client as EventEmitter;
this.emitter = emitter as SernEmitter;
this.crashHandler = crash as ErrorHandling;
}
protected abstract init(): void;
protected abstract setState(state: T): void;
}

View File

@@ -1,117 +1,105 @@
import type { Interaction } from 'discord.js'; import type { Interaction } from 'discord.js';
import { catchError, concatMap, finalize, fromEvent, map, Observable } from 'rxjs'; import {
import type Wrapper from '../structures/wrapper'; catchError,
import { EventsHandler } from './eventsHandler'; concatMap,
import { CommandType, SernError, type ModuleStore } from '../structures'; EMPTY,
filter,
finalize,
fromEvent,
map,
Observable,
of,
OperatorFunction,
pipe,
} from 'rxjs';
import { CommandType, type ModuleStore, SernError } from '../structures';
import { match, P } from 'ts-pattern'; import { match, P } from 'ts-pattern';
import { contextArgs, interactionArg, dispatchAutocomplete, dispatchCommand } from './dispatchers'; import { contextArgs, dispatchAutocomplete, dispatchCommand, interactionArg } from './dispatchers';
import { executeModule, makeModuleExecutor } from './observableHandling'; import { executeModule, makeModuleExecutor } from './observableHandling';
import type { CommandModule } from '../../types/module'; import type { CommandModule } from '../../types/module';
import { handleError } from '../contracts/errorHandling'; import { ErrorHandling, handleError } from '../contracts/errorHandling';
import SernEmitter from '../sernEmitter'; import SernEmitter from '../sernEmitter';
import type { Processed } from '../../types/handler'; import type { Processed } from '../../types/handler';
import { useContainerRaw } from '../dependencies'; import { useContainerRaw } from '../dependencies';
import type { Logging, ModuleManager } from '../contracts';
import type { EventEmitter } from 'node:events';
export default class InteractionHandler extends EventsHandler<{ function makeInteractionProcessor(
event: Interaction; modules: ModuleManager,
module: Processed<CommandModule>; ): OperatorFunction<Interaction, { module: Processed<CommandModule>; event: Interaction }> {
}> {
protected override discordEvent: Observable<Interaction>;
constructor(wrapper: Wrapper) {
super(wrapper);
this.discordEvent = <Observable<Interaction>>fromEvent(this.client, 'interactionCreate');
this.init();
this.payloadSubject
.pipe(
map(this.createDispatcher),
makeModuleExecutor(module => {
this.emitter.emit(
'module.activate',
SernEmitter.failure(module, SernError.PluginFailure),
);
}),
concatMap(payload => executeModule(this.emitter, payload)),
catchError(handleError(this.crashHandler, this.logger)),
finalize(() => {
this.logger?.info({ message: 'interactionCreate stream closed or reached end of lifetime'});
useContainerRaw()
?.disposeAll()
.then(() => {
this.logger?.info({ message: 'Cleaning container and crashing' });
});
})
)
.subscribe();
}
override init() {
const get = (cb: (ms: ModuleStore) => Processed<CommandModule> | undefined) => { const get = (cb: (ms: ModuleStore) => Processed<CommandModule> | undefined) => {
return this.modules.get(cb); return modules.get(cb);
}; };
/** return pipe(
* Module retrieval: concatMap(event => {
* ModuleStores are mapped by Discord API values and modules mapped
* by customId or command name.
*/
this.discordEvent.subscribe({
next: event => {
if (event.isMessageComponent()) { if (event.isMessageComponent()) {
const module = get(ms => const module = get(ms =>
ms.InteractionHandlers[event.componentType].get(event.customId), ms.InteractionHandlers[event.componentType].get(event.customId),
); );
this.setState({ event, module }); return of({ module, event });
} else if (event.isCommand() || event.isAutocomplete()) { } else if (event.isCommand() || event.isAutocomplete()) {
const module = get( const module = get(
ms => ms =>
/** /**
* try to fetch from ApplicationCommands, if nothing, try BothCommands * try to fetch from ApplicationCommands, if nothing, try BothCommands
* map. If nothing again,this means a slash command
* exists on the API but not sern * exists on the API but not sern
*/ */
ms.ApplicationCommands[event.commandType].get(event.commandName) ?? ms.ApplicationCommands[event.commandType].get(event.commandName) ??
ms.BothCommands.get(event.commandName), ms.BothCommands.get(event.commandName),
); );
this.setState({ event, module }); return of({ module, event });
} else if (event.isModalSubmit()) { } else if (event.isModalSubmit()) {
const module = get(ms => ms.ModalSubmit.get(event.customId)); const module = get(ms => ms.ModalSubmit.get(event.customId));
this.setState({ event, module }); return of({ module, event });
} else { } else return EMPTY;
throw Error('This interaction is not supported yet'); }),
} filter(m => m.module !== undefined),
}, ) as OperatorFunction<Interaction, { module: Processed<CommandModule>; event: Interaction }>;
error: reason => { }
this.emitter.emit('error', SernEmitter.failure(undefined, reason));
}, export function makeInteractionCreate([s, client, err, log, modules]: [
SernEmitter,
EventEmitter,
ErrorHandling,
Logging | undefined,
ModuleManager,
]) {
//map. If nothing again,this means a slash command
const interactionStream$ = fromEvent(client, 'interactionCreate') as Observable<Interaction>;
const interactionProcessor = makeInteractionProcessor(modules);
return interactionStream$
.pipe(
interactionProcessor,
map(createDispatcher),
makeModuleExecutor(module => {
s.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure));
}),
concatMap(module => executeModule(s, module)),
catchError(handleError(err, log)),
finalize(() => {
log?.info({
message: 'interactionCreate stream closed or reached end of lifetime',
}); });
} useContainerRaw()
?.disposeAll()
.then(() => log?.info({ message: 'Cleaning container and crashing' }));
}),
)
.subscribe();
}
protected setState(state: { event: Interaction; module: CommandModule | undefined }): void { function createDispatcher({
if (state.module === undefined) {
this.emitter.emit(
'warning',
SernEmitter.warning('Found no module for this interaction'),
);
} else {
//if statement above checks already, safe cast
this.payloadSubject.next(
state as { event: Interaction; module: Processed<CommandModule> },
);
}
}
protected createDispatcher({
module, module,
event, event,
}: { }: {
event: Interaction; event: Interaction;
module: Processed<CommandModule>; module: Processed<CommandModule>;
}) { }) {
return ( return (
match(module) match(module)
.with({ type: CommandType.Text }, () => .with({ type: CommandType.Text }, () => {
this.crashHandler.crash(Error(SernError.MismatchEvent)), throw Error(SernError.MismatchEvent);
) })
//P.union = either CommandType.Slash or CommandType.Both //P.union = either CommandType.Slash or CommandType.Both
.with({ type: P.union(CommandType.Slash, CommandType.Both) }, module => { .with({ type: P.union(CommandType.Slash, CommandType.Both) }, module => {
if (event.isAutocomplete()) { if (event.isAutocomplete()) {
@@ -131,5 +119,4 @@ export default class InteractionHandler extends EventsHandler<{
*/ */
.otherwise(mod => dispatchCommand(mod, interactionArg(event))) .otherwise(mod => dispatchCommand(mod, interactionArg(event)))
); );
}
} }

View File

@@ -1,64 +1,35 @@
import { EventsHandler } from './eventsHandler'; import { catchError, concatMap, EMPTY, finalize, fromEvent, map, of, pipe } from 'rxjs';
import { catchError, concatMap, EMPTY, finalize, fromEvent, map, Observable, of } from 'rxjs'; import { type ModuleStore, SernError } from '../structures';
import { type Wrapper, type ModuleStore, SernError } from '../structures';
import type { Message } from 'discord.js'; import type { Message } from 'discord.js';
import { executeModule, ignoreNonBot, makeModuleExecutor } from './observableHandling'; import { executeModule, ignoreNonBot, makeModuleExecutor } from './observableHandling';
import { fmt } from '../utilities/messageHelpers'; import { fmt } from '../utilities/messageHelpers';
import type { CommandModule, Module, TextCommand } from '../../types/module'; import type { CommandModule, TextCommand } from '../../types/module';
import { handleError } from '../contracts/errorHandling'; import { ErrorHandling, handleError } from '../contracts/errorHandling';
import { contextArgs, dispatchCommand } from './dispatchers'; import { contextArgs, dispatchCommand } from './dispatchers';
import SernEmitter from '../sernEmitter'; import SernEmitter from '../sernEmitter';
import type { Processed } from '../../types/handler'; import type { Processed } from '../../types/handler';
import { useContainerRaw } from '../dependencies'; import { useContainerRaw } from '../dependencies';
import type { Logging, ModuleManager } from '../contracts';
import type { EventEmitter } from 'node:events';
export default class MessageHandler extends EventsHandler<{ /**
module: Processed<Module>; * An operator function that processes a message to fetch a command module and prepares context payload.
args: unknown[]; * @param defaultPrefix
}> { * @param get
protected discordEvent: Observable<Message>; */
const createMessageProcessor = (
public constructor(protected wrapper: Wrapper) { defaultPrefix: string,
super(wrapper); get: (
this.discordEvent = <Observable<Message>>fromEvent(this.client, 'messageCreate'); cb: (ms: ModuleStore) => Processed<CommandModule> | undefined,
this.init(); ) => CommandModule | undefined,
this.payloadSubject ) =>
.pipe( pipe(
makeModuleExecutor(module => { ignoreNonBot(defaultPrefix),
this.emitter.emit(
'module.activate',
SernEmitter.failure(module, SernError.PluginFailure),
);
}),
concatMap(payload => executeModule(this.emitter, payload)),
catchError(handleError(this.crashHandler, this.logger)),
finalize(() => {
this.logger?.info({ message: 'messageCreate stream closed or reached end of lifetime'});
useContainerRaw()
?.disposeAll()
.then(() => {
this.logger?.info({ message: 'Cleaning container and crashing' });
});
})
)
.subscribe();
}
protected init(): void {
if (this.wrapper.defaultPrefix === undefined) return; //for now, just ignore if prefix doesn't exist
const { defaultPrefix } = this.wrapper;
const get = (cb: (ms: ModuleStore) => Processed<CommandModule> | undefined) => {
return this.modules.get(cb);
};
this.discordEvent
.pipe(
ignoreNonBot(this.wrapper.defaultPrefix),
//This concatMap checks if module is undefined, and if it is, do not continue. //This concatMap checks if module is undefined, and if it is, do not continue.
// Synonymous to filterMap, but I haven't thought of a generic implementation for filterMap yet // Synonymous to filterMap, but I haven't thought of a generic implementation for filterMap yet
concatMap(message => { concatMap(message => {
const [prefix, ...rest] = fmt(message, defaultPrefix); const [prefix, ...rest] = fmt(message, defaultPrefix);
const module = get( const module = get(ms => ms.TextCommands.get(prefix) ?? ms.BothCommands.get(prefix));
ms => ms.TextCommands.get(prefix) ?? ms.BothCommands.get(prefix),
);
if (module === undefined) { if (module === undefined) {
return EMPTY; return EMPTY;
} }
@@ -69,14 +40,40 @@ export default class MessageHandler extends EventsHandler<{
return of(payload); return of(payload);
}), }),
map(({ args, module }) => dispatchCommand(module as Processed<TextCommand>, args)), map(({ args, module }) => dispatchCommand(module as Processed<TextCommand>, args)),
) );
.subscribe({
next: value => this.setState(value),
error: reason => this.emitter.emit('error', SernEmitter.failure(reason)),
});
}
protected setState(state: { module: Processed<Module>; args: unknown[] }) { export function makeMessageCreate(
this.payloadSubject.next(state); [s, client, err, log, modules]: [
SernEmitter,
EventEmitter,
ErrorHandling,
Logging | undefined,
ModuleManager,
],
defaultPrefix?: string,
) {
if (!defaultPrefix) {
return EMPTY.subscribe();
} }
const get = (cb: (ms: ModuleStore) => Processed<CommandModule> | undefined) => {
return modules.get(cb);
};
const messageStream$ = fromEvent<Message>(client, 'messageCreate');
const messageProcessor = createMessageProcessor(defaultPrefix, get);
return messageStream$
.pipe(
messageProcessor,
makeModuleExecutor(module => {
s.emit('module.activate', SernEmitter.failure(module, SernError.PluginFailure));
}),
concatMap(payload => executeModule(s, payload)),
catchError(handleError(err, log)),
finalize(() => {
log?.info({ message: 'messageCreate stream closed or reached end of lifetime' });
useContainerRaw()
?.disposeAll()
.then(() => log?.info({ message: 'Cleaning container and crashing' }));
}),
)
.subscribe();
} }

View File

@@ -7,7 +7,7 @@ import { _const as i } from '../utilities/functions';
import SernEmitter from '../sernEmitter'; import SernEmitter from '../sernEmitter';
import { callPlugin, everyPluginOk, filterMapTo } from './operators'; import { callPlugin, everyPluginOk, filterMapTo } from './operators';
import type { Processed } from '../../types/handler'; import type { Processed } from '../../types/handler';
import type { VoidResult } from '../../types/plugin'; import type { ControlPlugin, VoidResult } from '../../types/plugin';
/** /**
* Ignores messages from any person / bot except itself * Ignores messages from any person / bot except itself
@@ -94,7 +94,7 @@ export function executeModule(
* @returns receiver function for flattening a stream of data * @returns receiver function for flattening a stream of data
*/ */
export function createResultResolver< export function createResultResolver<
T extends Processed<Module>, T extends { execute: (...args: any[]) => any; onEvent: ControlPlugin[] },
Args extends { module: T; [key: string]: unknown }, Args extends { module: T; [key: string]: unknown },
Output, Output,
>(config: { >(config: {

View File

@@ -32,7 +32,7 @@ export function callPlugin(args: unknown): OperatorFunction<
return pipe( return pipe(
concatMap(async plugin => { concatMap(async plugin => {
const isNewPlugin = Reflect.has(plugin, guayin); const isNewPlugin = Reflect.has(plugin, guayin);
if(isNewPlugin) { if (isNewPlugin) {
if (Array.isArray(args)) { if (Array.isArray(args)) {
return plugin.execute(...args); return plugin.execute(...args);
} }

View File

@@ -1,72 +1,58 @@
import { EventsHandler } from './eventsHandler'; import { fromEvent, pipe, switchMap, take } from 'rxjs';
import type Wrapper from '../structures/wrapper';
import { concatMap, fromEvent, type Observable, take } from 'rxjs';
import * as Files from '../utilities/readFile'; import * as Files from '../utilities/readFile';
import { errTap, scanModule } from './observableHandling'; import { errTap, scanModule } from './observableHandling';
import { CommandType, SernError, type ModuleStore } from '../structures'; import { CommandType, type ModuleStore, SernError } from '../structures';
import { match } from 'ts-pattern'; import { match } from 'ts-pattern';
import { Result } from 'ts-results-es'; import { Result } from 'ts-results-es';
import { ApplicationCommandType, ComponentType } from 'discord.js'; import { ApplicationCommandType, ComponentType } from 'discord.js';
import type { CommandModule } from '../../types/module'; import type { CommandModule } from '../../types/module';
import type { Processed } from '../../types/handler'; import type { Processed } from '../../types/handler';
import type { ModuleManager } from '../contracts'; import type { ErrorHandling, Logging, ModuleManager } from '../contracts';
import { _const, err, ok } from '../utilities/functions'; import { _const, err, ok } from '../utilities/functions';
import { defineAllFields } from './operators'; import { defineAllFields } from './operators';
import SernEmitter from '../sernEmitter'; import SernEmitter from '../sernEmitter';
import type { EventEmitter } from 'node:events';
export default class ReadyHandler extends EventsHandler<{ export function makeReadyEvent(
module: Processed<CommandModule>; [sEmitter, client, errorHandler, , moduleManager]: [
absPath: string; SernEmitter,
}> { EventEmitter,
protected discordEvent!: Observable<{ module: CommandModule; absPath: string }>; ErrorHandling,
constructor(wrapper: Wrapper) { Logging | undefined,
super(wrapper); ModuleManager,
const ready$ = fromEvent(this.client, 'ready').pipe(take(1)); ],
this.discordEvent = ready$.pipe( commandDir: string,
concatMap(() => ) {
Files.buildData<CommandModule>(wrapper.commands).pipe( const readyOnce$ = fromEvent(client, 'ready').pipe(take(1));
errTap(reason => { const parseCommandModules = pipe(
this.emitter.emit( switchMap(() => Files.buildData<CommandModule>(commandDir)),
'module.register', errTap(error => {
SernEmitter.failure(undefined, reason), sEmitter.emit('module.register', SernEmitter.failure(undefined, error));
);
}), }),
), defineAllFields(),
),
); );
this.init(); return readyOnce$
this.payloadSubject
.pipe( .pipe(
parseCommandModules,
scanModule({ scanModule({
onFailure: module => { onFailure: module => {
this.emitter.emit( sEmitter.emit(
'module.register', 'module.register',
SernEmitter.failure(module, SernError.PluginFailure), SernEmitter.failure(module, SernError.PluginFailure),
); );
}, },
onSuccess: ({ module }) => { onSuccess: ({ module }) => {
this.emitter.emit('module.register', SernEmitter.success(module)); sEmitter.emit('module.register', SernEmitter.success(module));
return module; return module;
}, },
}), }),
) )
.subscribe(module => { .subscribe(module => {
const res = registerModule(this.modules, module as Processed<CommandModule>); const result = registerModule(moduleManager, module as Processed<CommandModule>);
if (res.err) { if (result.err) {
this.crashHandler.crash(Error(SernError.InvalidModuleType)); errorHandler.crash(Error(SernError.InvalidModuleType));
} }
}); });
}
protected init() {
this.discordEvent.pipe(defineAllFields()).subscribe({
next: value => this.setState(value),
complete: () => this.payloadSubject.unsubscribe(),
});
}
protected setState(state: { absPath: string; module: Processed<CommandModule> }): void {
this.payloadSubject.next(state);
}
} }
function registerModule<T extends Processed<CommandModule>>( function registerModule<T extends Processed<CommandModule>>(

View File

@@ -1,4 +1,4 @@
import { catchError, finalize, map, tap } from 'rxjs'; import { catchError, finalize, map, tap, mergeAll } from 'rxjs';
import { buildData } from '../utilities/readFile'; import { buildData } from '../utilities/readFile';
import type { Dependencies, Processed } from '../../types/handler'; import type { Dependencies, Processed } from '../../types/handler';
import { errTap, scanModule } from './observableHandling'; import { errTap, scanModule } from './observableHandling';
@@ -13,57 +13,51 @@ import { handleError } from '../contracts/errorHandling';
import { defineAllFields } from './operators'; import { defineAllFields } from './operators';
import { useContainerRaw } from '../dependencies'; import { useContainerRaw } from '../dependencies';
export function processEvents({ containerConfig, events }: Wrapper) { export function makeEventsHandler(
const [client, errorHandling, sernEmitter, logger] = containerConfig.get( [s, client, err, log]: [SernEmitter, EventEmitter, ErrorHandling, Logging | undefined],
'@sern/client', eventsPath: string,
'@sern/errors', containerGetter: Wrapper['containerConfig'],
'@sern/emitter', ) {
'@sern/logger', const lazy = (k: string) => containerGetter.get(k as keyof Dependencies)[0];
) as [EventEmitter, ErrorHandling, SernEmitter, Logging?]; const eventStream$ = eventObservable(eventsPath, s);
const lazy = (k: string) => containerConfig.get(k as keyof Dependencies)[0];
const eventStream$ = eventObservable$(events!, sernEmitter);
const eventCreation$ = eventStream$.pipe( const eventCreation$ = eventStream$.pipe(
defineAllFields(), defineAllFields(),
scanModule({ scanModule({
onFailure: module => sernEmitter.emit('module.register', SernEmitter.success(module)), onFailure: module => s.emit('module.register', SernEmitter.success(module)),
onSuccess: ({ module }) => { onSuccess: ({ module }) => {
sernEmitter.emit( s.emit('module.register', SernEmitter.failure(module, SernError.PluginFailure));
'module.register',
SernEmitter.failure(module, SernError.PluginFailure),
);
return module; return module;
}, },
}), }),
); );
const intoDispatcher = (e: Processed<EventModule | CommandModule>) => const intoDispatcher = (e: Processed<EventModule | CommandModule>) =>
match(e) match(e)
.with({ type: EventType.Sern }, m => eventDispatcher(m, sernEmitter)) .with({ type: EventType.Sern }, m => eventDispatcher(m, s))
.with({ type: EventType.Discord }, m => eventDispatcher(m, client)) .with({ type: EventType.Discord }, m => eventDispatcher(m, client))
.with({ type: EventType.External }, m => eventDispatcher(m, lazy(m.emitter))) .with({ type: EventType.External }, m => eventDispatcher(m, lazy(m.emitter)))
.otherwise(() => errorHandling.crash(Error(SernError.InvalidModuleType))); .otherwise(() => err.crash(Error(SernError.InvalidModuleType)));
eventCreation$ eventCreation$
.pipe( .pipe(
map(intoDispatcher), map(intoDispatcher),
/** /**
* Where all events are turned on * Where all events are turned on
*/ */
tap(dispatcher => dispatcher.subscribe()), mergeAll(),
catchError(handleError(errorHandling, logger)), catchError(handleError(err, log)),
finalize(() => { finalize(() => {
logger?.info({ message: 'an event module reached end of lifetime'}); log?.info({ message: 'an event module reached end of lifetime' });
useContainerRaw() useContainerRaw()
?.disposeAll() ?.disposeAll()
.then(() => { .then(() => {
logger?.info({ message: 'Cleaning container and crashing' }); log?.info({ message: 'Cleaning container and crashing' });
}); });
}) }),
) )
.subscribe(); .subscribe();
} }
function eventObservable$(events: string, emitter: SernEmitter) { function eventObservable(events: string, emitter: SernEmitter) {
return buildData<EventModule>(events).pipe( return buildData<EventModule>(events).pipe(
errTap(reason => { errTap(reason => {
emitter.emit('module.register', SernEmitter.failure(undefined, reason)); emitter.emit('module.register', SernEmitter.failure(undefined, reason));

View File

@@ -16,7 +16,8 @@ import type {
StringSelectCommand, StringSelectCommand,
TextCommand, TextCommand,
UserSelectCommand, UserSelectCommand,
ContextMenuMsg, Module, ContextMenuMsg,
Module,
} from '../../types/module'; } from '../../types/module';
import type { Args, Payload, Processed, SlashOptions } from '../../types/handler'; import type { Args, Payload, Processed, SlashOptions } from '../../types/handler';
import type Context from '../structures/context'; import type Context from '../structures/context';

View File

@@ -1,4 +1,4 @@
import { CommandType, EventType, PluginType } from '../structures/enums'; import { CommandType, EventType, PluginType } from '../structures';
import type { Plugin, PluginResult } from '../../types/plugin'; import type { Plugin, PluginResult } from '../../types/plugin';
import type { CommandArgs, EventArgs } from './args'; import type { CommandArgs, EventArgs } from './args';
import type { ClientEvents } from 'discord.js'; import type { ClientEvents } from 'discord.js';
@@ -10,7 +10,7 @@ export function makePlugin<V extends unknown[]>(
return { return {
type, type,
execute, execute,
[guayin]: undefined [guayin]: undefined,
} as Plugin<V>; } as Plugin<V>;
} }

View File

@@ -1,10 +1,10 @@
import type Wrapper from './structures/wrapper'; import type Wrapper from './structures/wrapper';
import { processEvents } from './events/userDefinedEventsHandling'; import { makeEventsHandler } from './events/userDefinedEventsHandling';
import { CommandType, EventType, PluginType } from './structures/enums'; import { CommandType, EventType, PluginType } from './structures/enums';
import type { AnyEventPlugin, ControlPlugin, InitPlugin, Plugin } from '../types/plugin'; import type { AnyEventPlugin, ControlPlugin, InitPlugin, Plugin } from '../types/plugin';
import InteractionHandler from './events/interactionHandler'; import { makeInteractionCreate } from './events/interactionHandler';
import ReadyHandler from './events/readyHandler'; import { makeReadyEvent } from './events/readyHandler';
import MessageHandler from './events/messageHandler'; import { makeMessageCreate } from './events/messageHandler';
import type { import type {
CommandModule, CommandModule,
CommandModuleDefs, CommandModuleDefs,
@@ -14,7 +14,7 @@ import type {
InputEvent, InputEvent,
} from '../types/module'; } from '../types/module';
import type { Dependencies, DependencyConfiguration } from '../types/handler'; import type { Dependencies, DependencyConfiguration } from '../types/handler';
import { composeRoot, useContainer } from './dependencies/provider'; import { composeRoot, makeFetcher, useContainer } from './dependencies/provider';
import type { Logging } from './contracts'; import type { Logging } from './contracts';
import { err, ok, partition } from './utilities/functions'; import { err, ok, partition } from './utilities/functions';
import type { Awaitable, ClientEvents } from 'discord.js'; import type { Awaitable, ClientEvents } from 'discord.js';
@@ -37,14 +37,15 @@ import type { Awaitable, ClientEvents } from 'discord.js';
*/ */
export function init(wrapper: Wrapper) { export function init(wrapper: Wrapper) {
const logger = wrapper.containerConfig.get('@sern/logger')[0] as Logging | undefined; const logger = wrapper.containerConfig.get('@sern/logger')[0] as Logging | undefined;
const requiredDependenciesAnd = makeFetcher(wrapper);
const startTime = performance.now(); const startTime = performance.now();
const { events } = wrapper; const { events } = wrapper;
if (events !== undefined) { if (events !== undefined) {
processEvents(wrapper); makeEventsHandler(requiredDependenciesAnd([]), events, wrapper.containerConfig);
} }
new ReadyHandler(wrapper); makeReadyEvent(requiredDependenciesAnd(['@sern/modules']), wrapper.commands);
new MessageHandler(wrapper); makeMessageCreate(requiredDependenciesAnd(['@sern/modules']), wrapper.defaultPrefix);
new InteractionHandler(wrapper); makeInteractionCreate(requiredDependenciesAnd(['@sern/modules']));
const endTime = performance.now(); const endTime = performance.now();
logger?.info({ message: `sern : ${(endTime - startTime).toFixed(2)} ms` }); logger?.info({ message: `sern : ${(endTime - startTime).toFixed(2)} ms` });
} }

View File

@@ -5,6 +5,7 @@ import { Err, Ok } from 'ts-results-es';
* A function that returns whatever value is provided. * A function that returns whatever value is provided.
* Warning: this evaluates { @param value }. It does not defer a value. * Warning: this evaluates { @param value }. It does not defer a value.
* @param value * @param value
* @__PURE__
*/ */
// prettier-ignore // prettier-ignore
export const _const = <T>(value: T) => () => value; export const _const = <T>(value: T) => () => value;

View File

@@ -62,8 +62,8 @@ export type MapDeps<Deps extends Dependencies, T extends readonly unknown[]> = T
//Basically, '@sern/client' | '@sern/store' | '@sern/modules' | '@sern/error' | '@sern/emitter' will be provided defaults, and you can exclude the rest //Basically, '@sern/client' | '@sern/store' | '@sern/modules' | '@sern/error' | '@sern/emitter' will be provided defaults, and you can exclude the rest
export type OptionalDependencies = '@sern/logger'; export type OptionalDependencies = '@sern/logger';
export type Processed<T> = T & { name: string; description: string }; export type Processed<T> = T & { name: string; description: string };
export type Deprecated<Message extends string> = [never, Message] export type Deprecated<Message extends string> = [never, Message];
export interface DependencyConfiguration<T extends Dependencies> { export interface DependencyConfiguration<T extends Dependencies> {
exclude?: Set<OptionalDependencies>; exclude?: Set<OptionalDependencies>;
build: (root: Container<Omit<Dependencies, '@sern/client'>, {}>) => Container<T, {}> build: (root: Container<Omit<Dependencies, '@sern/client'>, {}>) => Container<T, {}>;
} }

View File

@@ -22,8 +22,8 @@ export type PluginResult = Awaitable<VoidResult>;
export type VoidResult = Result<void, void>; export type VoidResult = Result<void, void>;
export interface Controller { export interface Controller {
next: () => Ok<void> next: () => Ok<void>;
stop: () => Err<void> stop: () => Err<void>;
} }
export interface Plugin<Args extends any[] = any[]> { export interface Plugin<Args extends any[] = any[]> {
type: PluginType; type: PluginType;
@@ -50,7 +50,10 @@ export interface CommandPlugin<T extends CommandType = CommandType> {
name?: string; name?: string;
description?: string; description?: string;
type: PluginType.Command; type: PluginType.Command;
execute: (m: InitArgs<Processed<CommandModule>>, controller?: Deprecated<'Please import controller instead'>) => PluginResult; execute: (
m: InitArgs<Processed<CommandModule>>,
controller?: Deprecated<'Please import controller instead'>,
) => PluginResult;
} }
/** /**
* @deprecated * @deprecated
@@ -60,7 +63,7 @@ export interface EventPlugin<T extends CommandType> {
name?: string; name?: string;
description?: string; description?: string;
type: PluginType.Event; type: PluginType.Event;
execute: (args : CommandArgs<T, PluginType.Event>, controller?: Controller) => PluginResult execute: (args: CommandArgs<T, PluginType.Event>, controller?: Controller) => PluginResult;
} }
export type DiscordEmitterPlugin = Deprecated<'Please view alternatives: '>; export type DiscordEmitterPlugin = Deprecated<'Please view alternatives: '>;
export type ExternalEmitterPlugin = Deprecated<'Please view alternatives: '>; export type ExternalEmitterPlugin = Deprecated<'Please view alternatives: '>;

View File

@@ -5,6 +5,7 @@ const shared = {
platform: 'node', platform: 'node',
clean: true, clean: true,
sourcemap: false, sourcemap: false,
minify: true,
}; };
export default defineConfig([ export default defineConfig([
{ {
@@ -12,7 +13,6 @@ export default defineConfig([
target: 'node16', target: 'node16',
tsconfig: './tsconfig-esm.json', tsconfig: './tsconfig-esm.json',
outDir: './dist/esm', outDir: './dist/esm',
external: ['discord.js'],
treeshake: true, treeshake: true,
outExtension() { outExtension() {
return { return {
@@ -24,7 +24,6 @@ export default defineConfig([
{ {
format: 'cjs', format: 'cjs',
splitting: false, splitting: false,
external: ['discord.js'],
target: 'node16', target: 'node16',
tsconfig: './tsconfig-cjs.json', tsconfig: './tsconfig-cjs.json',
outDir: './dist/cjs', outDir: './dist/cjs',