mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-02 04:56:07 +00:00
feat: guild bans, formatter and minor functionality changes. (#203)
* fix(Colors#bold): bold returning weird values * chore: testing something * fix(Parsers): optionsParser & argsParser were being overriden by default options * revert(colors): mistakenly pushed a unplanned change * feat(Formatter): add a formatter class to add support to discord markdown, unix and etc * chore: set exports for formatter class * feat(AllGuildVoiceChannels): new type for all guild based voice channels * feat(VoiceChannel#members): returns a list of members in the voice channel * chore: small patch to voice channel methods * feat(GuildBans): addition of bans into seyfert * chore: request changes - incomplete * fix(BanShorter#bulkCreate): fix method return, bulk-bans returns stats * feat(GuildBan#methods): all methods added on the guild ban structure * chore: missed some, here pushed them * chore: requested change --------- Co-authored-by: NotAditya69 <90441096+NotAditya69@users.noreply.github.com>
This commit is contained in:
parent
37dfaef407
commit
390953082b
@ -43,7 +43,8 @@
|
||||
"noUselessConstructor": "off",
|
||||
"noThisInStatic": "off",
|
||||
"noExcessiveCognitiveComplexity": "off",
|
||||
"noVoid": "off"
|
||||
"noVoid": "off",
|
||||
"noStaticOnlyClass": "off"
|
||||
},
|
||||
"a11y": {
|
||||
"all": false
|
||||
|
@ -84,6 +84,8 @@ import type {
|
||||
RESTPatchAPIGuildWidgetSettingsResult,
|
||||
RESTPostAPIAutoModerationRuleJSONBody,
|
||||
RESTPostAPIAutoModerationRuleResult,
|
||||
RESTPostAPIGuildBulkBanJSONBody,
|
||||
RESTPostAPIGuildBulkBanResult,
|
||||
RESTPostAPIGuildChannelJSONBody,
|
||||
RESTPostAPIGuildChannelResult,
|
||||
RESTPostAPIGuildEmojiJSONBody,
|
||||
@ -233,6 +235,11 @@ export interface GuildRoutes {
|
||||
delete(args?: RestArguments<ProxyRequestMethod.Delete>): Promise<RESTDeleteAPIGuildBanResult>;
|
||||
};
|
||||
};
|
||||
'bulk-bans': {
|
||||
post(
|
||||
args: RestArguments<ProxyRequestMethod.Post, RESTPostAPIGuildBulkBanJSONBody>,
|
||||
): Promise<RESTPostAPIGuildBulkBanResult>;
|
||||
};
|
||||
mfa: {
|
||||
post(
|
||||
args: RestArguments<ProxyRequestMethod.Post, RESTPostAPIGuildsMFAJSONBody>,
|
||||
|
24
src/cache/index.ts
vendored
24
src/cache/index.ts
vendored
@ -14,6 +14,7 @@ import { StageInstances } from './resources/stage-instances';
|
||||
import { Stickers } from './resources/stickers';
|
||||
import { Threads } from './resources/threads';
|
||||
import { VoiceStates } from './resources/voice-states';
|
||||
import { Bans } from './resources/bans';
|
||||
|
||||
import { ChannelType, GatewayIntentBits, type GatewayDispatchPayload } from 'discord-api-types/v10';
|
||||
import type { InternalOptions, UsingClient } from '../commands';
|
||||
@ -36,7 +37,8 @@ export type GuildRelated =
|
||||
| 'presences'
|
||||
| 'stageInstances'
|
||||
| 'overwrites'
|
||||
| 'messages';
|
||||
| 'messages'
|
||||
| 'bans';
|
||||
|
||||
// ClientBased
|
||||
export type NonGuildBased = 'users' | 'guilds';
|
||||
@ -58,6 +60,8 @@ export type CachedEvents =
|
||||
| 'GUILD_ROLE_CREATE'
|
||||
| 'GUILD_ROLE_UPDATE'
|
||||
| 'GUILD_ROLE_DELETE'
|
||||
| 'GUILD_BAN_ADD'
|
||||
| 'GUILD_BAN_REMOVE'
|
||||
| 'GUILD_EMOJIS_UPDATE'
|
||||
| 'GUILD_STICKERS_UPDATE'
|
||||
| 'GUILD_MEMBER_ADD'
|
||||
@ -93,6 +97,7 @@ export class Cache {
|
||||
presences?: Presences;
|
||||
stageInstances?: StageInstances;
|
||||
messages?: Messages;
|
||||
bans?: Bans;
|
||||
|
||||
constructor(
|
||||
public intents: number,
|
||||
@ -144,6 +149,9 @@ export class Cache {
|
||||
if (!this.disabledCache.includes('messages')) {
|
||||
this.messages = new Messages(this, client);
|
||||
}
|
||||
if (!this.disabledCache.includes('bans')) {
|
||||
this.bans = new Bans(this, client);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@ -163,6 +171,7 @@ export class Cache {
|
||||
this.threads?.__setClient(client);
|
||||
this.stageInstances?.__setClient(client);
|
||||
this.messages?.__setClient(client);
|
||||
this.bans?.__setClient(client);
|
||||
}
|
||||
|
||||
flush(): ReturnCache<void> {
|
||||
@ -206,6 +215,10 @@ export class Cache {
|
||||
return this.hasIntent('DirectMessages');
|
||||
}
|
||||
|
||||
get hasBansIntent() {
|
||||
return this.hasIntent('GuildBans');
|
||||
}
|
||||
|
||||
async bulkGet(
|
||||
keys: (
|
||||
| readonly [
|
||||
@ -246,6 +259,7 @@ export class Cache {
|
||||
case 'users':
|
||||
case 'guilds':
|
||||
case 'overwrites':
|
||||
case 'bans':
|
||||
case 'messages':
|
||||
{
|
||||
if (!allData[type]) {
|
||||
@ -313,6 +327,7 @@ export class Cache {
|
||||
case 'stageInstances':
|
||||
case 'emojis':
|
||||
case 'overwrites':
|
||||
case 'bans':
|
||||
case 'messages':
|
||||
{
|
||||
if (!this[type]?.filter(data, id, guildId)) continue;
|
||||
@ -404,6 +419,7 @@ export class Cache {
|
||||
case 'stageInstances':
|
||||
case 'emojis':
|
||||
case 'overwrites':
|
||||
case 'bans':
|
||||
case 'messages':
|
||||
{
|
||||
if (!this[type]?.filter(data, id, guildId)) continue;
|
||||
@ -500,6 +516,12 @@ export class Cache {
|
||||
case 'GUILD_ROLE_DELETE':
|
||||
await this.roles?.remove(event.d.role_id, event.d.guild_id);
|
||||
break;
|
||||
case 'GUILD_BAN_ADD':
|
||||
await this.bans?.set(event.d.user.id, event.d.guild_id, event.d);
|
||||
break;
|
||||
case 'GUILD_BAN_REMOVE':
|
||||
await this.bans?.remove(event.d.user.id, event.d.guild_id);
|
||||
break;
|
||||
case 'GUILD_EMOJIS_UPDATE':
|
||||
await this.emojis?.remove(await this.emojis?.keys(event.d.guild_id), event.d.guild_id);
|
||||
await this.emojis?.set(
|
||||
|
46
src/cache/resources/bans.ts
vendored
Normal file
46
src/cache/resources/bans.ts
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
import type { APIBan } from 'discord-api-types/v10';
|
||||
import type { ReturnCache } from '../..';
|
||||
import { fakePromise } from '../../common';
|
||||
import { GuildBasedResource } from './default/guild-based';
|
||||
import { GuildBan } from '../../structures/GuildBan';
|
||||
export class Bans extends GuildBasedResource {
|
||||
namespace = 'ban';
|
||||
|
||||
//@ts-expect-error
|
||||
filter(data: APIBan, id: string, guild_id: string) {
|
||||
return true;
|
||||
}
|
||||
|
||||
override parse(data: any, key: string, guild_id: string) {
|
||||
const { user, ...rest } = super.parse(data, data.user?.id ?? key, guild_id);
|
||||
return rest;
|
||||
}
|
||||
|
||||
override get(id: string, guild: string): ReturnCache<GuildBan | undefined> {
|
||||
return fakePromise(super.get(id, guild)).then(rawBan =>
|
||||
rawBan ? new GuildBan(this.client, rawBan, guild) : undefined,
|
||||
);
|
||||
}
|
||||
|
||||
override bulk(ids: string[], guild: string): ReturnCache<GuildBan[]> {
|
||||
return fakePromise(super.bulk(ids, guild)).then(
|
||||
bans =>
|
||||
bans
|
||||
.map(rawBan => {
|
||||
return rawBan ? new GuildBan(this.client, rawBan, guild) : undefined;
|
||||
})
|
||||
.filter(Boolean) as GuildBan[],
|
||||
);
|
||||
}
|
||||
|
||||
override values(guild: string): ReturnCache<GuildBan[]> {
|
||||
return fakePromise(super.values(guild)).then(
|
||||
bans =>
|
||||
bans
|
||||
.map(rawBan => {
|
||||
return rawBan ? new GuildBan(this.client, rawBan, guild) : undefined;
|
||||
})
|
||||
.filter(Boolean) as GuildBan[],
|
||||
);
|
||||
}
|
||||
}
|
@ -10,7 +10,11 @@ import type {
|
||||
RegisteredMiddlewares,
|
||||
UsingClient,
|
||||
} from '../commands';
|
||||
import { IgnoreCommand, type InferWithPrefix, type MiddlewareContext } from '../commands/applications/shared';
|
||||
import {
|
||||
IgnoreCommand,
|
||||
type InferWithPrefix,
|
||||
type MiddlewareContext,
|
||||
} from '../commands/applications/shared';
|
||||
import { CommandHandler } from '../commands/handler';
|
||||
import {
|
||||
ChannelShorter,
|
||||
@ -33,8 +37,18 @@ import {
|
||||
type MakeRequired,
|
||||
} from '../common';
|
||||
|
||||
import type { LocaleString, RESTPostAPIChannelMessageJSONBody } from 'discord-api-types/rest/v10';
|
||||
import type { Awaitable, DeepPartial, IntentStrings, OmitInsert, PermissionStrings, When } from '../common/types/util';
|
||||
import type {
|
||||
LocaleString,
|
||||
RESTPostAPIChannelMessageJSONBody,
|
||||
} from 'discord-api-types/rest/v10';
|
||||
import type {
|
||||
Awaitable,
|
||||
DeepPartial,
|
||||
IntentStrings,
|
||||
OmitInsert,
|
||||
PermissionStrings,
|
||||
When,
|
||||
} from '../common/types/util';
|
||||
import { ComponentHandler } from '../components/handler';
|
||||
import { LangsHandler } from '../langs/handler';
|
||||
import type {
|
||||
@ -45,8 +59,14 @@ import type {
|
||||
ModalSubmitInteraction,
|
||||
UserCommandInteraction,
|
||||
} from '../structures';
|
||||
import type { ComponentCommand, ComponentContext, ModalCommand, ModalContext } from '../components';
|
||||
import type {
|
||||
ComponentCommand,
|
||||
ComponentContext,
|
||||
ModalCommand,
|
||||
ModalContext,
|
||||
} from '../components';
|
||||
import { promises } from 'node:fs';
|
||||
import { BanShorter } from '../common/shorters/bans';
|
||||
|
||||
export class BaseClient {
|
||||
rest!: ApiHandler;
|
||||
@ -63,6 +83,7 @@ export class BaseClient {
|
||||
reactions = new ReactionShorter(this);
|
||||
emojis = new EmojiShorter(this);
|
||||
threads = new ThreadShorter(this);
|
||||
bans = new BanShorter(this);
|
||||
interactions = new InteractionShorter(this);
|
||||
|
||||
debugger?: Logger;
|
||||
@ -79,7 +100,10 @@ export class BaseClient {
|
||||
private _botId?: string;
|
||||
middlewares?: Record<string, MiddlewareContext>;
|
||||
|
||||
protected static assertString(value: unknown, message?: string): asserts value is string {
|
||||
protected static assertString(
|
||||
value: unknown,
|
||||
message?: string
|
||||
): asserts value is string {
|
||||
if (!(typeof value === 'string' && value !== '')) {
|
||||
throw new Error(message ?? 'Value is not a string');
|
||||
}
|
||||
@ -99,43 +123,94 @@ export class BaseClient {
|
||||
{
|
||||
commands: {
|
||||
defaults: {
|
||||
onRunError(context: CommandContext<any>, error: unknown): any {
|
||||
context.client.logger.fatal(`${context.command.name}.<onRunError>`, context.author.id, error);
|
||||
onRunError(
|
||||
context: CommandContext<any>,
|
||||
error: unknown
|
||||
): any {
|
||||
context.client.logger.fatal(
|
||||
`${context.command.name}.<onRunError>`,
|
||||
context.author.id,
|
||||
error
|
||||
);
|
||||
},
|
||||
onOptionsError(context: CommandContext<{}, never>, metadata: OnOptionsReturnObject): any {
|
||||
context.client.logger.fatal(`${context.command.name}.<onOptionsError>`, context.author.id, metadata);
|
||||
onOptionsError(
|
||||
context: CommandContext<{}, never>,
|
||||
metadata: OnOptionsReturnObject
|
||||
): any {
|
||||
context.client.logger.fatal(
|
||||
`${context.command.name}.<onOptionsError>`,
|
||||
context.author.id,
|
||||
metadata
|
||||
);
|
||||
},
|
||||
onMiddlewaresError(context: CommandContext<{}, never>, error: string): any {
|
||||
context.client.logger.fatal(`${context.command.name}.<onMiddlewaresError>`, context.author.id, error);
|
||||
onMiddlewaresError(
|
||||
context: CommandContext<{}, never>,
|
||||
error: string
|
||||
): any {
|
||||
context.client.logger.fatal(
|
||||
`${context.command.name}.<onMiddlewaresError>`,
|
||||
context.author.id,
|
||||
error
|
||||
);
|
||||
},
|
||||
onBotPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any {
|
||||
onBotPermissionsFail(
|
||||
context: CommandContext<{}, never>,
|
||||
permissions: PermissionStrings
|
||||
): any {
|
||||
context.client.logger.fatal(
|
||||
`${context.command.name}.<onBotPermissionsFail>`,
|
||||
context.author.id,
|
||||
permissions,
|
||||
permissions
|
||||
);
|
||||
},
|
||||
onPermissionsFail(context: CommandContext<{}, never>, permissions: PermissionStrings): any {
|
||||
onPermissionsFail(
|
||||
context: CommandContext<{}, never>,
|
||||
permissions: PermissionStrings
|
||||
): any {
|
||||
context.client.logger.fatal(
|
||||
`${context.command.name}.<onPermissionsFail>`,
|
||||
context.author.id,
|
||||
permissions,
|
||||
permissions
|
||||
);
|
||||
},
|
||||
onInternalError(client: UsingClient, command: Command, error?: unknown): any {
|
||||
client.logger.fatal(`${command.name}.<onInternalError>`, error);
|
||||
onInternalError(
|
||||
client: UsingClient,
|
||||
command: Command,
|
||||
error?: unknown
|
||||
): any {
|
||||
client.logger.fatal(
|
||||
`${command.name}.<onInternalError>`,
|
||||
error
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
defaults: {
|
||||
onRunError(context: ComponentContext, error: unknown): any {
|
||||
context.client.logger.fatal('ComponentCommand.<onRunError>', context.author.id, error);
|
||||
onRunError(
|
||||
context: ComponentContext,
|
||||
error: unknown
|
||||
): any {
|
||||
context.client.logger.fatal(
|
||||
'ComponentCommand.<onRunError>',
|
||||
context.author.id,
|
||||
error
|
||||
);
|
||||
},
|
||||
onMiddlewaresError(context: ComponentContext, error: string): any {
|
||||
context.client.logger.fatal('ComponentCommand.<onMiddlewaresError>', context.author.id, error);
|
||||
onMiddlewaresError(
|
||||
context: ComponentContext,
|
||||
error: string
|
||||
): any {
|
||||
context.client.logger.fatal(
|
||||
'ComponentCommand.<onMiddlewaresError>',
|
||||
context.author.id,
|
||||
error
|
||||
);
|
||||
},
|
||||
onInternalError(client: UsingClient, error?: unknown): any {
|
||||
onInternalError(
|
||||
client: UsingClient,
|
||||
error?: unknown
|
||||
): any {
|
||||
client.logger.fatal(error);
|
||||
},
|
||||
},
|
||||
@ -143,18 +218,32 @@ export class BaseClient {
|
||||
modals: {
|
||||
defaults: {
|
||||
onRunError(context: ModalContext, error: unknown): any {
|
||||
context.client.logger.fatal('ComponentCommand.<onRunError>', context.author.id, error);
|
||||
context.client.logger.fatal(
|
||||
'ComponentCommand.<onRunError>',
|
||||
context.author.id,
|
||||
error
|
||||
);
|
||||
},
|
||||
onMiddlewaresError(context: ModalContext, error: string): any {
|
||||
context.client.logger.fatal('ComponentCommand.<onMiddlewaresError>', context.author.id, error);
|
||||
onMiddlewaresError(
|
||||
context: ModalContext,
|
||||
error: string
|
||||
): any {
|
||||
context.client.logger.fatal(
|
||||
'ComponentCommand.<onMiddlewaresError>',
|
||||
context.author.id,
|
||||
error
|
||||
);
|
||||
},
|
||||
onInternalError(client: UsingClient, error?: unknown): any {
|
||||
onInternalError(
|
||||
client: UsingClient,
|
||||
error?: unknown
|
||||
): any {
|
||||
client.logger.fatal(error);
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies BaseClientOptions,
|
||||
options,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
@ -163,7 +252,9 @@ export class BaseClient {
|
||||
}
|
||||
|
||||
get botId() {
|
||||
return this._botId ?? BaseClient.getBotIdFromToken(this.rest.options.token);
|
||||
return (
|
||||
this._botId ?? BaseClient.getBotIdFromToken(this.rest.options.token)
|
||||
);
|
||||
}
|
||||
|
||||
set applicationId(id: string) {
|
||||
@ -178,7 +269,13 @@ export class BaseClient {
|
||||
return new Router(this.rest).createProxy();
|
||||
}
|
||||
|
||||
setServices({ rest, cache, langs, middlewares, handlers }: ServicesOptions) {
|
||||
setServices({
|
||||
rest,
|
||||
cache,
|
||||
langs,
|
||||
middlewares,
|
||||
handlers,
|
||||
}: ServicesOptions) {
|
||||
if (rest) {
|
||||
this.rest = rest;
|
||||
}
|
||||
@ -187,7 +284,7 @@ export class BaseClient {
|
||||
this.cache?.intents ?? 0,
|
||||
cache?.adapter ?? this.cache?.adapter ?? new MemoryAdapter(),
|
||||
cache.disabledCache ?? this.cache?.disabledCache ?? [],
|
||||
this,
|
||||
this
|
||||
);
|
||||
}
|
||||
if (middlewares) {
|
||||
@ -199,7 +296,9 @@ export class BaseClient {
|
||||
this.components = undefined;
|
||||
} else if (typeof handlers.components === 'function') {
|
||||
this.components ??= new ComponentHandler(this.logger, this);
|
||||
this.components.setHandlers({ callback: handlers.components });
|
||||
this.components.setHandlers({
|
||||
callback: handlers.components,
|
||||
});
|
||||
} else {
|
||||
this.components = handlers.components;
|
||||
}
|
||||
@ -227,7 +326,8 @@ export class BaseClient {
|
||||
}
|
||||
if (langs) {
|
||||
if (langs.default) this.langs!.defaultLang = langs.default;
|
||||
if (langs.aliases) this.langs!.aliases = Object.entries(langs.aliases);
|
||||
if (langs.aliases)
|
||||
this.langs!.aliases = Object.entries(langs.aliases);
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,13 +341,20 @@ export class BaseClient {
|
||||
}
|
||||
|
||||
async start(
|
||||
options: Pick<DeepPartial<StartOptions>, 'langsDir' | 'commandsDir' | 'connection' | 'token' | 'componentsDir'> = {
|
||||
options: Pick<
|
||||
DeepPartial<StartOptions>,
|
||||
| 'langsDir'
|
||||
| 'commandsDir'
|
||||
| 'connection'
|
||||
| 'token'
|
||||
| 'componentsDir'
|
||||
> = {
|
||||
token: undefined,
|
||||
langsDir: undefined,
|
||||
commandsDir: undefined,
|
||||
connection: undefined,
|
||||
componentsDir: undefined,
|
||||
},
|
||||
}
|
||||
) {
|
||||
await this.loadLangs(options.langsDir);
|
||||
await this.loadCommands(options.commandsDir);
|
||||
@ -278,21 +385,32 @@ export class BaseClient {
|
||||
}
|
||||
|
||||
shouldUploadCommands(cachePath: string) {
|
||||
return this.commands!.shouldUpload(cachePath).then(async should => {
|
||||
if (should) await promises.writeFile(cachePath, JSON.stringify(this.commands!.values.map(x => x.toJSON())));
|
||||
return this.commands!.shouldUpload(cachePath).then(async (should) => {
|
||||
if (should)
|
||||
await promises.writeFile(
|
||||
cachePath,
|
||||
JSON.stringify(this.commands!.values.map((x) => x.toJSON()))
|
||||
);
|
||||
return should;
|
||||
});
|
||||
}
|
||||
|
||||
async uploadCommands(applicationId?: string) {
|
||||
applicationId ??= await this.getRC().then(x => x.applicationId ?? this.applicationId);
|
||||
applicationId ??= await this.getRC().then(
|
||||
(x) => x.applicationId ?? this.applicationId
|
||||
);
|
||||
BaseClient.assertString(applicationId, 'applicationId is not a string');
|
||||
|
||||
const commands = this.commands!.values;
|
||||
const filter = filterSplit(commands, command => !command.guildId);
|
||||
const filter = filterSplit(commands, (command) => !command.guildId);
|
||||
|
||||
await this.proxy.applications(applicationId).commands.put({
|
||||
body: filter.expect.filter(cmd => !('ignore' in cmd) || cmd.ignore !== IgnoreCommand.Slash).map(x => x.toJSON()),
|
||||
body: filter.expect
|
||||
.filter(
|
||||
(cmd) =>
|
||||
!('ignore' in cmd) || cmd.ignore !== IgnoreCommand.Slash
|
||||
)
|
||||
.map((x) => x.toJSON()),
|
||||
});
|
||||
|
||||
const guilds = new Set<string>();
|
||||
@ -309,14 +427,19 @@ export class BaseClient {
|
||||
.guilds(guild)
|
||||
.commands.put({
|
||||
body: filter.never
|
||||
.filter(cmd => cmd.guildId?.includes(guild) && (!('ignore' in cmd) || cmd.ignore !== IgnoreCommand.Slash))
|
||||
.map(x => x.toJSON()),
|
||||
.filter(
|
||||
(cmd) =>
|
||||
cmd.guildId?.includes(guild) &&
|
||||
(!('ignore' in cmd) ||
|
||||
cmd.ignore !== IgnoreCommand.Slash)
|
||||
)
|
||||
.map((x) => x.toJSON()),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async loadCommands(dir?: string) {
|
||||
dir ??= await this.getRC().then(x => x.commands);
|
||||
dir ??= await this.getRC().then((x) => x.commands);
|
||||
if (dir && this.commands) {
|
||||
await this.commands.load(dir, this);
|
||||
this.logger.info('CommandHandler loaded');
|
||||
@ -324,7 +447,7 @@ export class BaseClient {
|
||||
}
|
||||
|
||||
async loadComponents(dir?: string) {
|
||||
dir ??= await this.getRC().then(x => x.components);
|
||||
dir ??= await this.getRC().then((x) => x.components);
|
||||
if (dir && this.components) {
|
||||
await this.components.load(dir);
|
||||
this.logger.info('ComponentHandler loaded');
|
||||
@ -332,7 +455,7 @@ export class BaseClient {
|
||||
}
|
||||
|
||||
async loadLangs(dir?: string) {
|
||||
dir ??= await this.getRC().then(x => x.langs);
|
||||
dir ??= await this.getRC().then((x) => x.langs);
|
||||
if (dir && this.langs) {
|
||||
await this.langs.load(dir);
|
||||
this.logger.info('LangsHandler loaded');
|
||||
@ -344,23 +467,37 @@ export class BaseClient {
|
||||
}
|
||||
|
||||
async getRC<
|
||||
T extends InternalRuntimeConfigHTTP | InternalRuntimeConfig = InternalRuntimeConfigHTTP | InternalRuntimeConfig,
|
||||
T extends InternalRuntimeConfigHTTP | InternalRuntimeConfig =
|
||||
| InternalRuntimeConfigHTTP
|
||||
| InternalRuntimeConfig
|
||||
>() {
|
||||
const seyfertConfig = (BaseClient._seyfertConfig ||
|
||||
(await this.options.getRC?.()) ||
|
||||
(await magicImport(join(process.cwd(), 'seyfert.config.js')).then(x => x.default ?? x))) as T;
|
||||
(await magicImport(join(process.cwd(), 'seyfert.config.js')).then(
|
||||
(x) => x.default ?? x
|
||||
))) as T;
|
||||
|
||||
const { locations, debug, ...env } = seyfertConfig;
|
||||
|
||||
const obj = {
|
||||
debug: !!debug,
|
||||
...env,
|
||||
templates: locations.templates ? join(process.cwd(), locations.base, locations.templates) : undefined,
|
||||
langs: locations.langs ? join(process.cwd(), locations.output, locations.langs) : undefined,
|
||||
templates: locations.templates
|
||||
? join(process.cwd(), locations.base, locations.templates)
|
||||
: undefined,
|
||||
langs: locations.langs
|
||||
? join(process.cwd(), locations.output, locations.langs)
|
||||
: undefined,
|
||||
events:
|
||||
'events' in locations && locations.events ? join(process.cwd(), locations.output, locations.events) : undefined,
|
||||
components: locations.components ? join(process.cwd(), locations.output, locations.components) : undefined,
|
||||
commands: locations.commands ? join(process.cwd(), locations.output, locations.commands) : undefined,
|
||||
'events' in locations && locations.events
|
||||
? join(process.cwd(), locations.output, locations.events)
|
||||
: undefined,
|
||||
components: locations.components
|
||||
? join(process.cwd(), locations.output, locations.components)
|
||||
: undefined,
|
||||
commands: locations.commands
|
||||
? join(process.cwd(), locations.output, locations.commands)
|
||||
: undefined,
|
||||
base: join(process.cwd(), locations.base),
|
||||
output: join(process.cwd(), locations.output),
|
||||
};
|
||||
@ -379,7 +516,7 @@ export interface BaseClientOptions {
|
||||
| MessageCommandInteraction<boolean>
|
||||
| ComponentInteraction
|
||||
| ModalSubmitInteraction
|
||||
| When<InferWithPrefix, Message, never>,
|
||||
| When<InferWithPrefix, Message, never>
|
||||
) => {};
|
||||
globalMiddlewares?: readonly (keyof RegisteredMiddlewares)[];
|
||||
commands?: {
|
||||
@ -410,7 +547,10 @@ export interface BaseClientOptions {
|
||||
onAfterRun?: ModalCommand['onAfterRun'];
|
||||
};
|
||||
};
|
||||
allowedMentions?: Omit<NonNullable<RESTPostAPIChannelMessageJSONBody['allowed_mentions']>, 'parse'> & {
|
||||
allowedMentions?: Omit<
|
||||
NonNullable<RESTPostAPIChannelMessageJSONBody['allowed_mentions']>,
|
||||
'parse'
|
||||
> & {
|
||||
parse?: ('everyone' | 'roles' | 'users')[]; //nice types, d-api
|
||||
};
|
||||
getRC?(): Awaitable<InternalRuntimeConfig | InternalRuntimeConfigHTTP>;
|
||||
@ -455,11 +595,17 @@ export type InternalRuntimeConfigHTTP = Omit<
|
||||
MakeRequired<RC, 'publicKey' | 'port' | 'applicationId'>,
|
||||
'intents' | 'locations'
|
||||
> & { locations: Omit<RC['locations'], 'events'> };
|
||||
export type RuntimeConfigHTTP = Omit<MakeRequired<RC, 'publicKey' | 'applicationId'>, 'intents' | 'locations'> & {
|
||||
export type RuntimeConfigHTTP = Omit<
|
||||
MakeRequired<RC, 'publicKey' | 'applicationId'>,
|
||||
'intents' | 'locations'
|
||||
> & {
|
||||
locations: Omit<RC['locations'], 'events'>;
|
||||
};
|
||||
|
||||
export type InternalRuntimeConfig = Omit<MakeRequired<RC, 'intents'>, 'publicKey' | 'port'>;
|
||||
export type InternalRuntimeConfig = Omit<
|
||||
MakeRequired<RC, 'intents'>,
|
||||
'publicKey' | 'port'
|
||||
>;
|
||||
export type RuntimeConfig = OmitInsert<
|
||||
InternalRuntimeConfig,
|
||||
'intents',
|
||||
@ -476,7 +622,9 @@ export interface ServicesOptions {
|
||||
middlewares?: Record<string, MiddlewareContext>;
|
||||
handlers?: {
|
||||
components?: ComponentHandler | ComponentHandler['callback'];
|
||||
commands?: CommandHandler | Parameters<CommandHandler['setHandlers']>[0];
|
||||
commands?:
|
||||
| CommandHandler
|
||||
| Parameters<CommandHandler['setHandlers']>[0];
|
||||
langs?: LangsHandler | LangsHandler['callback'];
|
||||
};
|
||||
}
|
@ -26,13 +26,26 @@ import {
|
||||
} from '../common';
|
||||
import { EventHandler } from '../events';
|
||||
import { ClientUser } from '../structures';
|
||||
import { ShardManager, properties, type ShardManagerOptions } from '../websocket';
|
||||
import {
|
||||
ShardManager,
|
||||
properties,
|
||||
type ShardManagerOptions,
|
||||
} from '../websocket';
|
||||
import { MemberUpdateHandler } from '../websocket/discord/events/memberUpdate';
|
||||
import { PresenceUpdateHandler } from '../websocket/discord/events/presenceUpdate';
|
||||
import type { BaseClientOptions, InternalRuntimeConfig, ServicesOptions, StartOptions } from './base';
|
||||
import type {
|
||||
BaseClientOptions,
|
||||
InternalRuntimeConfig,
|
||||
ServicesOptions,
|
||||
StartOptions,
|
||||
} from './base';
|
||||
import { BaseClient } from './base';
|
||||
import { onInteractionCreate } from './oninteractioncreate';
|
||||
import { defaultArgsParser, defaultOptionsParser, onMessageCreate } from './onmessagecreate';
|
||||
import {
|
||||
defaultArgsParser,
|
||||
defaultOptionsParser,
|
||||
onMessageCreate,
|
||||
} from './onmessagecreate';
|
||||
import { Collectors } from './collectors';
|
||||
|
||||
let parentPort: import('node:worker_threads').MessagePort;
|
||||
@ -42,7 +55,10 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
gateway!: ShardManager;
|
||||
me!: If<Ready, ClientUser>;
|
||||
declare options: Omit<ClientOptions, 'commands'> & {
|
||||
commands: MakeRequired<NonNullable<ClientOptions['commands']>, 'argsParser' | 'optionsParser'>;
|
||||
commands: MakeRequired<
|
||||
NonNullable<ClientOptions['commands']>,
|
||||
'argsParser' | 'optionsParser'
|
||||
>;
|
||||
};
|
||||
memberUpdateHandler = new MemberUpdateHandler();
|
||||
presenceUpdateHandler = new PresenceUpdateHandler();
|
||||
@ -54,11 +70,14 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
this.options = MergeOptions(
|
||||
{
|
||||
commands: {
|
||||
argsParser: defaultArgsParser,
|
||||
optionsParser: defaultOptionsParser,
|
||||
argsParser:
|
||||
options?.commands?.argsParser ?? defaultArgsParser,
|
||||
optionsParser:
|
||||
options?.commands?.optionsParser ??
|
||||
defaultOptionsParser,
|
||||
},
|
||||
} satisfies ClientOptions,
|
||||
this.options,
|
||||
this.options
|
||||
);
|
||||
}
|
||||
|
||||
@ -96,43 +115,60 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
}
|
||||
|
||||
async loadEvents(dir?: string) {
|
||||
dir ??= await this.getRC().then(x => x.events);
|
||||
dir ??= await this.getRC().then((x) => x.events);
|
||||
if (dir && this.events) {
|
||||
await this.events.load(dir);
|
||||
this.logger.info('EventHandler loaded');
|
||||
}
|
||||
}
|
||||
|
||||
protected async execute(options: { token?: string; intents?: number } = {}) {
|
||||
protected async execute(
|
||||
options: { token?: string; intents?: number } = {}
|
||||
) {
|
||||
await super.execute(options);
|
||||
|
||||
const worker_threads = lazyLoadPackage<typeof import('node:worker_threads')>('node:worker_threads');
|
||||
const worker_threads = lazyLoadPackage<
|
||||
typeof import('node:worker_threads')
|
||||
>('node:worker_threads');
|
||||
|
||||
if (worker_threads?.parentPort) {
|
||||
parentPort = worker_threads.parentPort;
|
||||
}
|
||||
|
||||
if (worker_threads?.workerData?.__USING_WATCHER__) {
|
||||
parentPort?.on('message', (data: WatcherPayload | WatcherSendToShard) => {
|
||||
switch (data.type) {
|
||||
case 'PAYLOAD':
|
||||
this.gateway.options.handlePayload(data.shardId, data.payload);
|
||||
break;
|
||||
case 'SEND_TO_SHARD':
|
||||
this.gateway.send(data.shardId, data.payload);
|
||||
break;
|
||||
parentPort?.on(
|
||||
'message',
|
||||
(data: WatcherPayload | WatcherSendToShard) => {
|
||||
switch (data.type) {
|
||||
case 'PAYLOAD':
|
||||
this.gateway.options.handlePayload(
|
||||
data.shardId,
|
||||
data.payload
|
||||
);
|
||||
break;
|
||||
case 'SEND_TO_SHARD':
|
||||
this.gateway.send(data.shardId, data.payload);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
} else {
|
||||
await this.gateway.spawnShards();
|
||||
}
|
||||
}
|
||||
|
||||
async start(options: Omit<DeepPartial<StartOptions>, 'httpConnection'> = {}, execute = true) {
|
||||
async start(
|
||||
options: Omit<DeepPartial<StartOptions>, 'httpConnection'> = {},
|
||||
execute = true
|
||||
) {
|
||||
await super.start(options);
|
||||
await this.loadEvents(options.eventsDir);
|
||||
|
||||
const { token: tokenRC, intents: intentsRC, debug: debugRC } = await this.getRC<InternalRuntimeConfig>();
|
||||
const {
|
||||
token: tokenRC,
|
||||
intents: intentsRC,
|
||||
debug: debugRC,
|
||||
} = await this.getRC<InternalRuntimeConfig>();
|
||||
const token = options?.token ?? tokenRC;
|
||||
const intents = options?.connection?.intents ?? intentsRC;
|
||||
|
||||
@ -149,9 +185,14 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
presence: this.options?.presence,
|
||||
debug: debugRC,
|
||||
shardStart: this.options?.shards?.start,
|
||||
shardEnd: this.options?.shards?.end ?? this.options?.shards?.total,
|
||||
totalShards: this.options?.shards?.total ?? this.options?.shards?.end,
|
||||
properties: { ...properties, ...this.options?.gateway?.properties },
|
||||
shardEnd:
|
||||
this.options?.shards?.end ?? this.options?.shards?.total,
|
||||
totalShards:
|
||||
this.options?.shards?.total ?? this.options?.shards?.end,
|
||||
properties: {
|
||||
...properties,
|
||||
...this.options?.gateway?.properties,
|
||||
},
|
||||
compress: this.options?.gateway?.compress,
|
||||
});
|
||||
}
|
||||
@ -173,29 +214,59 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
if (!this.memberUpdateHandler.check(packet.d)) {
|
||||
return;
|
||||
}
|
||||
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
|
||||
await this.events?.execute(
|
||||
packet.t,
|
||||
packet,
|
||||
this as Client<true>,
|
||||
shardId
|
||||
);
|
||||
break;
|
||||
case 'PRESENCE_UPDATE':
|
||||
if (!this.presenceUpdateHandler.check(packet.d as any)) {
|
||||
return;
|
||||
}
|
||||
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
|
||||
await this.events?.execute(
|
||||
packet.t,
|
||||
packet,
|
||||
this as Client<true>,
|
||||
shardId
|
||||
);
|
||||
break;
|
||||
case 'GUILD_CREATE': {
|
||||
if (this.__handleGuilds?.has(packet.d.id)) {
|
||||
this.__handleGuilds.delete(packet.d.id);
|
||||
if (!this.__handleGuilds.size && [...this.gateway.values()].every(shard => shard.data.session_id)) {
|
||||
await this.events?.runEvent('BOT_READY', this, this.me, -1);
|
||||
if (
|
||||
!this.__handleGuilds.size &&
|
||||
[...this.gateway.values()].every(
|
||||
(shard) => shard.data.session_id
|
||||
)
|
||||
) {
|
||||
await this.events?.runEvent(
|
||||
'BOT_READY',
|
||||
this,
|
||||
this.me,
|
||||
-1
|
||||
);
|
||||
}
|
||||
if (!this.__handleGuilds.size) delete this.__handleGuilds;
|
||||
return this.cache.onPacket(packet);
|
||||
}
|
||||
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
|
||||
await this.events?.execute(
|
||||
packet.t,
|
||||
packet,
|
||||
this as Client<true>,
|
||||
shardId
|
||||
);
|
||||
break;
|
||||
}
|
||||
//rest of the events
|
||||
default: {
|
||||
await this.events?.execute(packet.t, packet, this as Client<true>, shardId);
|
||||
await this.events?.execute(
|
||||
packet.t,
|
||||
packet,
|
||||
this as Client<true>,
|
||||
shardId
|
||||
);
|
||||
switch (packet.t) {
|
||||
case 'INTERACTION_CREATE':
|
||||
await onInteractionCreate(this, packet.d, shardId);
|
||||
@ -209,19 +280,36 @@ export class Client<Ready extends boolean = boolean> extends BaseClient {
|
||||
}
|
||||
this.botId = packet.d.user.id;
|
||||
this.applicationId = packet.d.application.id;
|
||||
this.me = new ClientUser(this, packet.d.user, packet.d.application) as never;
|
||||
this.me = new ClientUser(
|
||||
this,
|
||||
packet.d.user,
|
||||
packet.d.application
|
||||
) as never;
|
||||
if (
|
||||
!(
|
||||
this.__handleGuilds?.size &&
|
||||
(this.gateway.options.intents & GatewayIntentBits.Guilds) === GatewayIntentBits.Guilds
|
||||
(this.gateway.options.intents &
|
||||
GatewayIntentBits.Guilds) ===
|
||||
GatewayIntentBits.Guilds
|
||||
)
|
||||
) {
|
||||
if ([...this.gateway.values()].every(shard => shard.data.session_id)) {
|
||||
await this.events?.runEvent('BOT_READY', this, this.me, -1);
|
||||
if (
|
||||
[...this.gateway.values()].every(
|
||||
(shard) => shard.data.session_id
|
||||
)
|
||||
) {
|
||||
await this.events?.runEvent(
|
||||
'BOT_READY',
|
||||
this,
|
||||
this.me,
|
||||
-1
|
||||
);
|
||||
}
|
||||
delete this.__handleGuilds;
|
||||
}
|
||||
this.debugger?.debug(`#${shardId}[${packet.d.user.username}](${this.botId}) is online...`);
|
||||
this.debugger?.debug(
|
||||
`#${shardId}[${packet.d.user.username}](${this.botId}) is online...`
|
||||
);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -243,15 +331,21 @@ export interface ClientOptions extends BaseClientOptions {
|
||||
};
|
||||
commands?: BaseClientOptions['commands'] & {
|
||||
prefix?: (message: Message) => Promise<string[]> | string[];
|
||||
deferReplyResponse?: (ctx: CommandContext) => Parameters<Message['write']>[0];
|
||||
deferReplyResponse?: (
|
||||
ctx: CommandContext
|
||||
) => Parameters<Message['write']>[0];
|
||||
reply?: (ctx: CommandContext) => boolean;
|
||||
argsParser?: (content: string, command: SubCommand | Command, message: Message) => Record<string, string>;
|
||||
argsParser?: (
|
||||
content: string,
|
||||
command: SubCommand | Command,
|
||||
message: Message
|
||||
) => Record<string, string>;
|
||||
optionsParser?: (
|
||||
self: UsingClient,
|
||||
command: Command | SubCommand,
|
||||
message: GatewayMessageCreateDispatchData,
|
||||
args: Partial<Record<string, string>>,
|
||||
resolved: MakeRequired<ContextOptionsResolved>,
|
||||
resolved: MakeRequired<ContextOptionsResolved>
|
||||
) => Awaitable<{
|
||||
errors: {
|
||||
name: string;
|
||||
|
@ -1,8 +1,9 @@
|
||||
export * from './it/constants';
|
||||
export * from './it/utils';
|
||||
//
|
||||
export * from './it/colors';
|
||||
export * from './it/logger';
|
||||
export * from './it/formatter';
|
||||
//
|
||||
export * from './shorters/channels';
|
||||
export * from './shorters/emojis';
|
||||
export * from './shorters/guilds';
|
||||
@ -15,6 +16,7 @@ export * from './shorters/users';
|
||||
export * from './shorters/threads';
|
||||
export * from './shorters/webhook';
|
||||
export * from './shorters/interaction';
|
||||
//
|
||||
export * from './types/options';
|
||||
export * from './types/resolvables';
|
||||
export * from './types/util';
|
||||
|
203
src/common/it/formatter.ts
Normal file
203
src/common/it/formatter.ts
Normal file
@ -0,0 +1,203 @@
|
||||
/**
|
||||
* Represents heading levels.
|
||||
*/
|
||||
export enum HeadingLevel {
|
||||
/**
|
||||
* Represents a level 1 heading. (#)
|
||||
*/
|
||||
H1 = 1,
|
||||
/**
|
||||
* Represents a level 2 heading. (##)
|
||||
*/
|
||||
H2 = 2,
|
||||
/**
|
||||
* Represents a level 3 heading. (###)
|
||||
*/
|
||||
H3 = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents timestamp styles.
|
||||
*/
|
||||
export enum TimestampStyle {
|
||||
/**
|
||||
* Represents a short timestamp style.
|
||||
*/
|
||||
ShortTime = 't',
|
||||
/**
|
||||
* Represents a long timestamp style.
|
||||
*/
|
||||
LongTime = 'T',
|
||||
/**
|
||||
* Represents a short date style.
|
||||
*/
|
||||
ShortDate = 'd',
|
||||
/**
|
||||
* Represents a long date style.
|
||||
*/
|
||||
LongDate = 'D',
|
||||
/**
|
||||
* Represents a short time style.
|
||||
*/
|
||||
ShortDateTime = 'f',
|
||||
/**
|
||||
* Represents a long time style.
|
||||
*/
|
||||
LongDateTime = 'F',
|
||||
/**
|
||||
* Represents a relative time style.
|
||||
*/
|
||||
RelativeTime = 'R',
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a message link.
|
||||
*/
|
||||
type MessageLink = `https://discord.com/channels/${string}/${string}/${string}`;
|
||||
|
||||
/**
|
||||
* Represents a timestamp.
|
||||
*/
|
||||
type Timestamp = `<t:${number}:${TimestampStyle}>`;
|
||||
|
||||
/**
|
||||
* Represents a formatter utility for formatting content.
|
||||
*/
|
||||
export class Formatter {
|
||||
/**
|
||||
* Formats a code block.
|
||||
* @param content The content of the code block.
|
||||
* @param language The language of the code block. Defaults to 'txt'.
|
||||
* @returns The formatted code block.
|
||||
*/
|
||||
static codeBlock(content: string, language = 'txt'): string {
|
||||
return `\`\`\`${language}\n${content}\n\`\`\``;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats content into inline code.
|
||||
* @param content The content to format.
|
||||
* @returns The formatted content.
|
||||
*/
|
||||
static inlineCode(content: string): `\`${string}\`` {
|
||||
return `\`${content}\``;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats content into bold text.
|
||||
* @param content The content to format.
|
||||
* @returns The formatted content.
|
||||
*/
|
||||
static bold(content: string): `**${string}**` {
|
||||
return `**${content}**`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats content into italic text.
|
||||
* @param content The content to format.
|
||||
* @returns The formatted content.
|
||||
*/
|
||||
static italic(content: string): `*${string}*` {
|
||||
return `*${content}*`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats content into underlined text.
|
||||
* @param content The content to format.
|
||||
* @returns The formatted content.
|
||||
*/
|
||||
static underline(content: string): `__${string}__` {
|
||||
return `__${content}__`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats content into strikethrough text.
|
||||
* @param content The content to format.
|
||||
* @returns The formatted content.
|
||||
*/
|
||||
static strikeThrough(content: string): `~~${string}~~` {
|
||||
return `~~${content}~~`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats content into a hyperlink.
|
||||
* @param content The content to format.
|
||||
* @param url The URL to hyperlink to.
|
||||
* @returns The formatted content.
|
||||
*/
|
||||
static hyperlink(content: string, url: string): `[${string}](${string})` {
|
||||
return `[${content}](${url})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats content into a spoiler.
|
||||
* @param content The content to format.
|
||||
* @returns The formatted content.
|
||||
*/
|
||||
static spoiler(content: string): `||${string}||` {
|
||||
return `||${content}||`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats content into a quote.
|
||||
* @param content The content to format.
|
||||
* @returns The formatted content.
|
||||
*/
|
||||
static blockQuote(content: string): string {
|
||||
return `>>> ${content}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats content into a quote.
|
||||
* @param content The content to format.
|
||||
* @returns The formatted content.
|
||||
*/
|
||||
static quote(content: string): string {
|
||||
return `> ${content}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a message link.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param channelId The ID of the channel.
|
||||
* @param messageId The ID of the message.
|
||||
* @returns The formatted message link.
|
||||
*/
|
||||
static messageLink(guildId: string, channelId: string, messageId: string): MessageLink {
|
||||
return `https://discord.com/channels/${guildId}/${channelId}/${messageId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a header.
|
||||
* @param content The content of the header.
|
||||
* @param level The level of the header. Defaults to 1.
|
||||
* @returns The formatted header.
|
||||
*/
|
||||
static header(content: string, level: HeadingLevel = HeadingLevel.H1): string {
|
||||
return `${'#'.repeat(level)} ${content}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a list.
|
||||
* @param items The items of the list.
|
||||
* @param ordered Whether the list is ordered. Defaults to false.
|
||||
* @returns The formatted list.
|
||||
*/
|
||||
static list(items: string[], ordered = false): string {
|
||||
return items
|
||||
.map((item, index) => {
|
||||
return (ordered ? `${index + 1}. ` : '- ') + item;
|
||||
})
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given timestamp into discord unix timestamp format.
|
||||
* @param timestamp The timestamp to format.
|
||||
* @param style The style of the timestamp. Defaults to 't'.
|
||||
* @returns The formatted timestamp.
|
||||
*/
|
||||
static timestamp(timestamp: Date, style: TimestampStyle = TimestampStyle.RelativeTime): Timestamp {
|
||||
return `<t:${Math.floor(timestamp.getTime() / 1000)}:${style}>`;
|
||||
}
|
||||
}
|
86
src/common/shorters/bans.ts
Normal file
86
src/common/shorters/bans.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import type {
|
||||
APIBan,
|
||||
RESTGetAPIGuildBansQuery,
|
||||
RESTPostAPIGuildBulkBanJSONBody,
|
||||
RESTPutAPIGuildBanJSONBody,
|
||||
} from 'discord-api-types/v10';
|
||||
import { BaseShorter } from './base';
|
||||
import { GuildBan } from '../../structures/GuildBan';
|
||||
|
||||
export class BanShorter extends BaseShorter {
|
||||
/**
|
||||
* Bulk creates bans in the guild.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param body The request body for bulk banning members.
|
||||
* @param reason The reason for bulk banning members.
|
||||
*/
|
||||
async bulkCreate(guildId: string, body: RESTPostAPIGuildBulkBanJSONBody, reason?: string) {
|
||||
const bans = await this.client.proxy.guilds(guildId)['bulk-bans'].post({ reason, body });
|
||||
for (const id of bans.banned_users) this.client.cache.members?.removeIfNI('GuildBans', id, guildId);
|
||||
return bans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unbans a member from the guild.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param memberId The ID of the member to unban.
|
||||
* @param reason The reason for unbanning the member.
|
||||
*/
|
||||
async remove(guildId: string, memberId: string, reason?: string) {
|
||||
await this.client.proxy.guilds(guildId).bans(memberId).delete({ reason });
|
||||
}
|
||||
|
||||
/**
|
||||
* Bans a member from the guild.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param memberId The ID of the member to ban.
|
||||
* @param body The request body for banning the member.
|
||||
* @param reason The reason for banning the member.
|
||||
*/
|
||||
async create(guildId: string, memberId: string, body?: RESTPutAPIGuildBanJSONBody, reason?: string) {
|
||||
await this.client.proxy.guilds(guildId).bans(memberId).put({ reason, body });
|
||||
await this.client.cache.members?.removeIfNI('GuildBans', memberId, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a ban from the guild.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param userId The ID of the user to fetch.
|
||||
* @param force Whether to force fetching the ban from the API even if it exists in the cache.
|
||||
* @returns A Promise that resolves to the fetched ban.
|
||||
*/
|
||||
async fetch(guildId: string, userId: string, force = false) {
|
||||
let ban;
|
||||
if (!force) {
|
||||
ban = await this.client.cache.bans?.get(userId, guildId);
|
||||
if (ban) return ban;
|
||||
}
|
||||
|
||||
ban = await this.client.proxy.guilds(guildId).bans(userId).get();
|
||||
await this.client.cache.members?.set(ban.user!.id, guildId, ban);
|
||||
return new GuildBan(this.client, ban, guildId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists bans in the guild based on the provided query.
|
||||
* @param guildId The ID of the guild.
|
||||
* @param query The query parameters for listing bans.
|
||||
* @param force Whether to force listing bans from the API even if they exist in the cache.
|
||||
* @returns A Promise that resolves to an array of listed bans.
|
||||
*/
|
||||
async list(guildId: string, query?: RESTGetAPIGuildBansQuery, force = false) {
|
||||
let bans;
|
||||
if (!force) {
|
||||
bans = (await this.client.cache.bans?.values(guildId)) ?? [];
|
||||
if (bans.length) return bans;
|
||||
}
|
||||
bans = await this.client.proxy.guilds(guildId).bans.get({
|
||||
query,
|
||||
});
|
||||
await this.client.cache.bans?.set(
|
||||
bans.map<[string, APIBan]>(x => [x.user!.id, x]),
|
||||
guildId,
|
||||
);
|
||||
return bans.map(m => new GuildBan(this.client, m, guildId));
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ import {
|
||||
} from './client/base';
|
||||
import type { CustomEventsKeys, ClientNameEvents, EventContext } from './events';
|
||||
import { isCloudfareWorker } from './common';
|
||||
export { Logger, PermissionStrings, Watcher } from './common';
|
||||
export { Logger, PermissionStrings, Watcher, Formatter } from './common';
|
||||
//
|
||||
export { Collection, LimitedCollection } from './collection';
|
||||
//
|
||||
|
@ -10,6 +10,7 @@ import { Sticker } from './Sticker';
|
||||
import { BaseChannel, WebhookGuildMethods } from './channels';
|
||||
import { BaseGuild } from './extra/BaseGuild';
|
||||
import type { DiscordBase } from './extra/DiscordBase';
|
||||
import { GuildBan } from './GuildBan';
|
||||
|
||||
export interface Guild extends ObjectToLower<Omit<APIGuild, 'stickers' | 'emojis' | 'roles'>>, DiscordBase {}
|
||||
export class Guild<State extends StructStates = 'api'> extends (BaseGuild as unknown as ToClass<
|
||||
@ -75,6 +76,7 @@ export class Guild<State extends StructStates = 'api'> extends (BaseGuild as unk
|
||||
roles = GuildRole.methods({ client: this.client, guildId: this.id });
|
||||
channels = BaseChannel.allMethods({ client: this.client, guildId: this.id });
|
||||
emojis = GuildEmoji.methods({ client: this.client, guildId: this.id });
|
||||
bans = GuildBan.methods({ client: this.client, guildId: this.id });
|
||||
}
|
||||
|
||||
/** Maximun custom guild emojis per level */
|
||||
|
49
src/structures/GuildBan.ts
Normal file
49
src/structures/GuildBan.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import type { APIBan, RESTGetAPIGuildBansQuery } from 'discord-api-types/v10';
|
||||
import type { UsingClient } from '../commands';
|
||||
import type { MethodContext, ObjectToLower } from '../common';
|
||||
import { DiscordBase } from './extra/DiscordBase';
|
||||
import type { BanShorter } from '../common/shorters/bans';
|
||||
|
||||
export interface GuildBan extends DiscordBase, ObjectToLower<Omit<APIBan, 'id'>> {}
|
||||
|
||||
export class GuildBan extends DiscordBase {
|
||||
constructor(
|
||||
client: UsingClient,
|
||||
data: APIBan,
|
||||
readonly guildId: string,
|
||||
) {
|
||||
super(client, { ...data, id: data.user.id });
|
||||
}
|
||||
|
||||
create(body?: Parameters<BanShorter['create']>[2], reason?: string) {
|
||||
return this.client.bans.create(this.guildId, this.id, body, reason);
|
||||
}
|
||||
|
||||
remove(reason?: string) {
|
||||
return this.client.bans.remove(this.guildId, this.id, reason);
|
||||
}
|
||||
|
||||
guild(force = false) {
|
||||
return this.client.guilds.fetch(this.guildId, force);
|
||||
}
|
||||
|
||||
fetch(force = false) {
|
||||
return this.client.bans.fetch(this.guildId, this.id, force);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `<@${this.id}>`;
|
||||
}
|
||||
|
||||
static methods({ client, guildId }: MethodContext<{ guildId: string }>) {
|
||||
return {
|
||||
fetch: (userId: string, force = false) => client.bans.fetch(guildId, userId, force),
|
||||
list: (query?: RESTGetAPIGuildBansQuery, force = false) => client.bans.list(guildId, query, force),
|
||||
create: (memberId: string, body?: Parameters<BanShorter['create']>[2], reason?: string) =>
|
||||
client.bans.create(guildId, memberId, body, reason),
|
||||
remove: (memberId: string, reason?: string) => client.bans.remove(guildId, memberId, reason),
|
||||
bulkCreate: (body: Parameters<BanShorter['bulkCreate']>[1], reason?: string) =>
|
||||
client.bans.bulkCreate(guildId, body, reason),
|
||||
};
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ import type { GuildMember } from './GuildMember';
|
||||
import type { GuildRole } from './GuildRole';
|
||||
import { DiscordBase } from './extra/DiscordBase';
|
||||
import { channelLink } from './extra/functions';
|
||||
import type { RawFile } from '..';
|
||||
import { Collection, type RawFile } from '..';
|
||||
|
||||
export class BaseChannel<T extends ChannelType> extends DiscordBase<APIChannelBase<ChannelType>> {
|
||||
declare type: T;
|
||||
@ -216,9 +216,15 @@ export class MessagesMethods extends DiscordBase {
|
||||
return this.client.channels.typing(this.id);
|
||||
}
|
||||
|
||||
messages = MessagesMethods.messages({ client: this.client, channelId: this.id });
|
||||
messages = MessagesMethods.messages({
|
||||
client: this.client,
|
||||
channelId: this.id,
|
||||
});
|
||||
pins = MessagesMethods.pins({ client: this.client, channelId: this.id });
|
||||
reactions = MessagesMethods.reactions({ client: this.client, channelId: this.id });
|
||||
reactions = MessagesMethods.reactions({
|
||||
client: this.client,
|
||||
channelId: this.id,
|
||||
});
|
||||
|
||||
static messages(ctx: MethodContext<{ channelId: string }>) {
|
||||
return {
|
||||
@ -265,7 +271,10 @@ export class MessagesMethods extends DiscordBase {
|
||||
embeds: body.embeds?.map(x => (x instanceof Embed ? x.toJSON() : x)) ?? undefined,
|
||||
attachments:
|
||||
'attachments' in body
|
||||
? body.attachments?.map((x, i) => ({ id: i, ...resolveAttachment(x) })) ?? undefined
|
||||
? body.attachments?.map((x, i) => ({
|
||||
id: i,
|
||||
...resolveAttachment(x),
|
||||
})) ?? undefined
|
||||
: (files?.map((x, id) => ({
|
||||
id,
|
||||
filename: x.name,
|
||||
@ -378,10 +387,26 @@ export class VoiceChannelMethods extends DiscordBase {
|
||||
const filter = states.filter(state => state.channelId === this.id);
|
||||
return filter;
|
||||
}
|
||||
|
||||
public async members(force?: boolean) {
|
||||
const collection = new Collection<string, GuildMember>();
|
||||
|
||||
const states = await this.states();
|
||||
|
||||
for (const state of states) {
|
||||
const member = await state.member(force);
|
||||
collection.set(member.id, member);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
||||
export class WebhookGuildMethods extends DiscordBase {
|
||||
webhooks = WebhookGuildMethods.guild({ client: this.client, guildId: this.id });
|
||||
webhooks = WebhookGuildMethods.guild({
|
||||
client: this.client,
|
||||
guildId: this.id,
|
||||
});
|
||||
|
||||
static guild(ctx: MethodContext<{ guildId: string }>) {
|
||||
return {
|
||||
@ -391,7 +416,10 @@ export class WebhookGuildMethods extends DiscordBase {
|
||||
}
|
||||
|
||||
export class WebhookChannelMethods extends DiscordBase {
|
||||
webhooks = WebhookChannelMethods.channel({ client: this.client, channelId: this.id });
|
||||
webhooks = WebhookChannelMethods.channel({
|
||||
client: this.client,
|
||||
channelId: this.id,
|
||||
});
|
||||
|
||||
static channel(ctx: MethodContext<{ channelId: string }>) {
|
||||
return {
|
||||
@ -555,6 +583,7 @@ export type AllGuildChannels =
|
||||
|
||||
export type AllTextableChannels = TextGuildChannel | VoiceChannel | DMChannel | NewsChannel | ThreadChannel;
|
||||
export type AllGuildTextableChannels = TextGuildChannel | VoiceChannel | NewsChannel | ThreadChannel;
|
||||
export type AllGuildVoiceChannels = VoiceChannel | StageChannel;
|
||||
|
||||
export type AllChannels =
|
||||
| BaseChannel<ChannelType>
|
||||
|
Loading…
x
Reference in New Issue
Block a user