feat!: big feature changes

BREAKING CHANGES:
- Heartbeating removed
- `config.consoleLogLevel` -> `config.logLevel`

NEW FEATURES:
- Training messages
- Sequence number system
- WebSocket close codes used instead of disconnect packets

FIXES:
- Improved error handling
- Some performance improvements
- Made code more clean
- Updated dependencies
This commit is contained in:
PalmDevs
2024-03-28 21:41:59 +07:00
parent 77f1a9cb3e
commit b3b7723b4f
33 changed files with 562 additions and 506 deletions

View File

@@ -1,40 +1,40 @@
{
"name": "@revanced/bot-shared",
"type": "module",
"version": "0.1.0",
"description": "🙌🏻 Shared components for bots assisting ReVanced",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "bun bundle && bun types",
"watch": "conc --raw \"bun bundle:watch\" \"bun types:watch\"",
"bundle": "bun build src/index.ts --outdir=dist --sourcemap=external --target=bun --minify",
"bundle:watch": "bun run bundle --watch",
"types": "tsc --declaration --emitDeclarationOnly",
"types:watch": "bun types --watch --preserveWatchOutput",
"types:clean": "bun types --build --clean"
},
"repository": {
"type": "git",
"url": "git+https://github.com/revanced/revanced-helper.git",
"directory": "packages/shared"
},
"author": "Palm <palmpasuthorn@gmail.com> (https://github.com/PalmDevs)",
"contributors": [
"Palm <palmpasuthorn@gmail.com> (https://github.com/PalmDevs)",
"ReVanced <nosupport@revanced.app> (https://github.com/revanced)"
],
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/revanced/revanced-helper/issues"
},
"homepage": "https://github.com/revanced/revanced-helper#readme",
"dependencies": {
"bson": "^6.2.0",
"chalk": "^5.3.0",
"supports-color": "^9.4.0",
"tracer": "^1.3.0",
"valibot": "^0.21.0",
"zod": "^3.22.4"
}
"name": "@revanced/bot-shared",
"type": "module",
"version": "0.1.0",
"description": "🙌🏻 Shared components for bots assisting ReVanced",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "bun bundle && bun types",
"watch": "conc --raw \"bun bundle:watch\" \"bun types:watch\"",
"bundle": "bun build src/index.ts --outdir=dist --sourcemap=external --target=bun --minify",
"bundle:watch": "bun run bundle --watch",
"types": "tsc --declaration --emitDeclarationOnly",
"types:watch": "bun types --watch --preserveWatchOutput",
"types:clean": "bun types --build --clean"
},
"repository": {
"type": "git",
"url": "git+https://github.com/revanced/revanced-helper.git",
"directory": "packages/shared"
},
"author": "Palm <palmpasuthorn@gmail.com> (https://github.com/PalmDevs)",
"contributors": [
"Palm <palmpasuthorn@gmail.com> (https://github.com/PalmDevs)",
"ReVanced <nosupport@revanced.app> (https://github.com/revanced)"
],
"license": "GPL-3.0-or-later",
"bugs": {
"url": "https://github.com/revanced/revanced-helper/issues"
},
"homepage": "https://github.com/revanced/revanced-helper#readme",
"dependencies": {
"bson": "^6.5.0",
"chalk": "^5.3.0",
"supports-color": "^9.4.0",
"tracer": "^1.3.0",
"valibot": "^0.30.0",
"zod": "^3.22.4"
}
}

View File

@@ -3,29 +3,33 @@
*/
enum DisconnectReason {
/**
* Unknown reason
* The client disconnected on its own (**CLIENT-ONLY**)
*/
Generic = 1,
/**
* The client did not respond in time
*/
TimedOut = 2,
PlannedDisconnect = 1000,
/**
* The client sent an invalid packet (unserializable or invalid JSON)
*/
InvalidPacket = 3,
InvalidPacket = 1007,
/**
* The server has encountered an internal error
*/
ServerError = 4,
ServerError = 1011,
/**
* The client had never connected to the server (**CLIENT-ONLY**)
* Unknown reason
*/
NeverConnected = 5,
Generic = 4000,
/**
* The client disconnected on its own (**CLIENT-ONLY**)
* The client did not respond with a heartbeat in time
*/
PlannedDisconnect = 6,
TimedOut = 4001,
/**
* The receiving end didn't have an open socket
*/
NoOpenSocket = 4003,
/**
* The client was not ready in time (**CLIENT-ONLY**)
*/
TooSlow = 4002,
}
export default DisconnectReason

View File

@@ -4,12 +4,14 @@ import DisconnectReason from './DisconnectReason'
* Humanized disconnect reasons for logs
*/
const HumanizedDisconnectReason = {
[DisconnectReason.InvalidPacket]: 'has sent invalid packet',
[DisconnectReason.Generic]: 'has been disconnected for unknown reasons',
[DisconnectReason.TimedOut]: 'has timed out',
[DisconnectReason.ServerError]: 'has been disconnected due to an internal server error',
[DisconnectReason.NeverConnected]: 'had never connected to the server',
[DisconnectReason.PlannedDisconnect]: 'has disconnected on its own',
} as const satisfies Record<DisconnectReason, string>
[1006]: 'the receiving end had unexpectedly closed the connection',
[DisconnectReason.InvalidPacket]: 'the client has sent invalid packet',
[DisconnectReason.Generic]: '(unknown reason)',
[DisconnectReason.TimedOut]: 'the client did not respond with a heartbeat in time',
[DisconnectReason.ServerError]: 'the server had an internal server error',
[DisconnectReason.TooSlow]: 'the client was not ready in time',
[DisconnectReason.PlannedDisconnect]: 'the client has disconnected on its own',
[DisconnectReason.NoOpenSocket]: 'the receiving end did not have an open socket',
} as const satisfies Record<DisconnectReason | number, string>
export default HumanizedDisconnectReason

View File

@@ -2,33 +2,28 @@
* Client operation codes for the gateway
*/
export enum ClientOperation {
/**
* Client's heartbeat (to check if the connection is dead or not)
*/
Heartbeat = 100,
/**
* Client's request to parse text
*/
ParseText = 110,
ParseText = 100,
/**
* Client's request to parse image
*/
ParseImage = 111,
ParseImage = 101,
/**
* Client's request to train a message
*/
TrainMessage = 102,
}
/**
* Server operation codes for the gateway
*/
export enum ServerOperation {
/**
* Server's acknowledgement of a client's heartbeat
*/
HeartbeatAck = 1,
/**
* Server's initial response to a client's connection
*/
Hello = 2,
Hello = 1,
/**
* Server's response to client's request to parse text
@@ -46,6 +41,14 @@ export enum ServerOperation {
* Server's failure response to client's request to parse image
*/
ParseImageFailed = 13,
/**
* Server's response to client's request to train a message
*/
TrainedMessage = 14,
/**
* Server's failure response to client's request to train a message
*/
TrainMessageFailed = 15,
/**
* Server's disconnect message

View File

@@ -1,3 +1,3 @@
export * from './constants/index'
export * from './schemas/index'
export * from './utils/index'
export * from './constants'
export * from './schemas'
export * from './utils'

View File

@@ -1,13 +1,12 @@
import {
url,
AnySchema,
NullSchema,
ObjectSchema,
Output,
type AnySchema,
type NullSchema,
type ObjectSchema,
type Output,
array,
enum_,
null_,
number,
object,
parse,
special,
@@ -30,6 +29,8 @@ export const PacketSchema = special<Packet>(input => {
'd' in input &&
typeof input.d === 'object'
) {
if (input.op in ServerOperation && !('s' in input && typeof input.s === 'number')) return false
try {
parse(PacketDataSchemas[input.op as Operation], input.d)
return true
@@ -44,14 +45,8 @@ export const PacketSchema = special<Packet>(input => {
* Schema to validate packet data for each possible operations
*/
export const PacketDataSchemas = {
[ServerOperation.Hello]: object({
heartbeatInterval: number(),
}),
[ServerOperation.HeartbeatAck]: object({
nextHeartbeat: number(),
}),
[ServerOperation.Hello]: null_(),
[ServerOperation.ParsedText]: object({
id: string(),
labels: array(
object({
name: string(),
@@ -60,35 +55,38 @@ export const PacketDataSchemas = {
),
}),
[ServerOperation.ParsedImage]: object({
id: string(),
text: string(),
}),
[ServerOperation.ParseTextFailed]: object({
id: string(),
}),
[ServerOperation.ParseImageFailed]: object({
id: string(),
}),
[ServerOperation.ParseTextFailed]: null_(),
[ServerOperation.ParseImageFailed]: null_(),
[ServerOperation.Disconnect]: object({
reason: enum_(DisconnectReason),
}),
[ServerOperation.TrainedMessage]: null_(),
[ServerOperation.TrainMessageFailed]: null_(),
[ClientOperation.Heartbeat]: null_(),
[ClientOperation.ParseText]: object({
id: string(),
text: string(),
}),
[ClientOperation.ParseImage]: object({
id: string(),
image_url: string([url()]),
}),
[ClientOperation.TrainMessage]: object({
text: string(),
label: string(),
}),
} as const satisfies Record<
Operation,
// biome-ignore lint/suspicious/noExplicitAny: This is a schema, it's not possible to type it
ObjectSchema<any> | AnySchema | NullSchema
>
export type Packet<TOp extends Operation = Operation> = {
export type Packet<TOp extends Operation = Operation> = TOp extends ServerOperation
? PacketWithSequenceNumber<TOp>
: Omit<PacketWithSequenceNumber<TOp>, 's'>
type PacketWithSequenceNumber<TOp extends Operation> = {
op: TOp
d: Output<(typeof PacketDataSchemas)[TOp]>
s: number
}

View File

@@ -1,5 +1,5 @@
import { ClientOperation, Operation, ServerOperation } from '../constants/Operation'
import { Packet } from '../schemas/Packet'
import { ClientOperation, type Operation, ServerOperation } from '../constants/Operation'
import type { Packet } from '../schemas/Packet'
/**
* Checks whether a packet is trying to do the given operation
@@ -21,7 +21,7 @@ export function isClientPacket(packet: Packet): packet is Packet<ClientOperation
}
/**
* Checks whether this packet is a server packet **(this does NOT validate the data)**
* Checks whether this packet is a server packet **(this does NOT validate the data or the sequence number)**
* @param packet A packet
* @returns Whether this packet is a server packet
*/

View File

@@ -1,5 +1,5 @@
import { Chalk, supportsColor, supportsColorStderr } from 'chalk'
import { console as uncoloredConsole, Tracer, colorConsole } from 'tracer'
import { type Tracer, colorConsole, console as uncoloredConsole } from 'tracer'
const chalk = new Chalk()
const DefaultConfig = {
@@ -8,19 +8,16 @@ const DefaultConfig = {
'{{message}}',
{
error: `${chalk.bgRedBright.whiteBright(' ERROR ')} {{message}}\n${chalk.gray('{{stack}}')}`,
debug: chalk.gray('DEBUG: {{message}}\n{{stack}}'),
warn: `${chalk.bgYellowBright.whiteBright(' WARN ')} ${chalk.yellowBright('{{message}}')}\n${chalk.gray(
'{{stack}}',
)}`,
debug: chalk.gray('DEBUG: {{message}}'),
warn: `${chalk.bgYellowBright.whiteBright(' WARN ')} ${chalk.yellowBright('{{message}}')}`,
info: `${chalk.bgBlueBright.whiteBright(' INFO ')} ${chalk.cyanBright('{{message}}')}`,
fatal: `${chalk.bgRedBright.whiteBright(' FATAL ')} ${chalk.redBright('{{message}}')}\n${chalk.white(
'{{stack}}',
)}`,
log: '{{message}}',
trace: chalk.gray('[{{timestamp}}] TRACE: {{message}}\n{{stack}}'),
},
],
methods: ['debug', 'trace', 'log', 'info', 'warn', 'error', 'fatal'],
methods: ['debug', 'log', 'info', 'warn', 'error', 'fatal'],
filters: [],
} satisfies Tracer.LoggerConfig

View File

@@ -1,7 +1,7 @@
import * as BSON from 'bson'
import { parse } from 'valibot'
import { Operation } from '../constants/index'
import { Packet, PacketSchema } from '../schemas/index'
import type { Operation } from '../constants'
import { type Packet, PacketSchema } from '../schemas'
/**
* Compresses a packet into a buffer

View File

@@ -1,12 +1,12 @@
{
"extends": "../../tsconfig.packages.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "./src",
"outDir": "dist",
"module": "ESNext",
"composite": true,
"noEmit": false
},
"exclude": ["node_modules", "dist"]
"extends": "../../tsconfig.packages.json",
"compilerOptions": {
"baseUrl": ".",
"rootDir": "./src",
"outDir": "dist",
"module": "ESNext",
"composite": true,
"noEmit": false
},
"exclude": ["node_modules", "dist"]
}