mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-01 20:46:08 +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[],
|
||||
);
|
||||
}
|
||||
}
|
1112
src/client/base.ts
1112
src/client/base.ts
File diff suppressed because it is too large
Load Diff
@ -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));
|
||||
}
|
||||
}
|
224
src/index.ts
224
src/index.ts
@ -1,112 +1,112 @@
|
||||
import { GatewayIntentBits } from 'discord-api-types/gateway/v10';
|
||||
import {
|
||||
BaseClient,
|
||||
type BaseClientOptions,
|
||||
type InternalRuntimeConfig,
|
||||
type InternalRuntimeConfigHTTP,
|
||||
type RuntimeConfig,
|
||||
type RuntimeConfigHTTP,
|
||||
} from './client/base';
|
||||
import type { CustomEventsKeys, ClientNameEvents, EventContext } from './events';
|
||||
import { isCloudfareWorker } from './common';
|
||||
export { Logger, PermissionStrings, Watcher } from './common';
|
||||
//
|
||||
export { Collection, LimitedCollection } from './collection';
|
||||
//
|
||||
export * from './api';
|
||||
export * from './builders';
|
||||
export * from './cache';
|
||||
export * from './commands';
|
||||
export * from './components';
|
||||
export * from './events';
|
||||
export * from './langs';
|
||||
//
|
||||
export { ShardManager, WorkerManager } from './websocket/discord';
|
||||
//
|
||||
export * from './structures';
|
||||
//
|
||||
export * from './client';
|
||||
//
|
||||
|
||||
export function throwError(msg: string): never {
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event with the specified data and run function.
|
||||
*
|
||||
* @param data - The event data.
|
||||
* @returns The created event.
|
||||
*
|
||||
* @example
|
||||
* const myEvent = createEvent({
|
||||
* data: { name: 'ready', once: true },
|
||||
* run: (user, client, shard) => {
|
||||
* client.logger.info(`Start ${user.username} on shard #${shard}`);
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
export function createEvent<E extends ClientNameEvents | CustomEventsKeys>(data: {
|
||||
data: { name: E; once?: boolean };
|
||||
run: (...args: EventContext<{ data: { name: E } }>) => any;
|
||||
}) {
|
||||
data.data.once ??= false;
|
||||
return data;
|
||||
}
|
||||
|
||||
export const config = {
|
||||
/**
|
||||
* Configurations for the bot.
|
||||
*
|
||||
* @param data - The runtime configuration data for gateway connections.
|
||||
* @returns The internal runtime configuration.
|
||||
*/
|
||||
bot(data: RuntimeConfig) {
|
||||
return {
|
||||
...data,
|
||||
intents:
|
||||
'intents' in data
|
||||
? typeof data.intents === 'number'
|
||||
? data.intents
|
||||
: data.intents?.reduce<number>(
|
||||
(pr, acc) => pr | (typeof acc === 'number' ? acc : GatewayIntentBits[acc]),
|
||||
0,
|
||||
) ?? 0
|
||||
: 0,
|
||||
} as InternalRuntimeConfig;
|
||||
},
|
||||
/**
|
||||
* Configurations for the HTTP server.
|
||||
*
|
||||
* @param data - The runtime configuration data for http server.
|
||||
* @returns The internal runtime configuration for HTTP.
|
||||
*/
|
||||
http(data: RuntimeConfigHTTP) {
|
||||
const obj = {
|
||||
port: 8080,
|
||||
...data,
|
||||
} as InternalRuntimeConfigHTTP;
|
||||
if (isCloudfareWorker()) BaseClient._seyfertConfig = obj;
|
||||
return obj;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends the context of a command interaction.
|
||||
*
|
||||
* @param cb - The callback function to extend the context.
|
||||
* @returns The extended context.
|
||||
*
|
||||
* @example
|
||||
* const customContext = extendContext((interaction) => {
|
||||
* return {
|
||||
* owner: '123456789012345678',
|
||||
* // Add your custom properties here
|
||||
* };
|
||||
* });
|
||||
*/
|
||||
export function extendContext<T extends {}>(
|
||||
cb: (interaction: Parameters<NonNullable<BaseClientOptions['context']>>[0]) => T,
|
||||
) {
|
||||
return cb;
|
||||
}
|
||||
import { GatewayIntentBits } from 'discord-api-types/gateway/v10';
|
||||
import {
|
||||
BaseClient,
|
||||
type BaseClientOptions,
|
||||
type InternalRuntimeConfig,
|
||||
type InternalRuntimeConfigHTTP,
|
||||
type RuntimeConfig,
|
||||
type RuntimeConfigHTTP,
|
||||
} from './client/base';
|
||||
import type { CustomEventsKeys, ClientNameEvents, EventContext } from './events';
|
||||
import { isCloudfareWorker } from './common';
|
||||
export { Logger, PermissionStrings, Watcher, Formatter } from './common';
|
||||
//
|
||||
export { Collection, LimitedCollection } from './collection';
|
||||
//
|
||||
export * from './api';
|
||||
export * from './builders';
|
||||
export * from './cache';
|
||||
export * from './commands';
|
||||
export * from './components';
|
||||
export * from './events';
|
||||
export * from './langs';
|
||||
//
|
||||
export { ShardManager, WorkerManager } from './websocket/discord';
|
||||
//
|
||||
export * from './structures';
|
||||
//
|
||||
export * from './client';
|
||||
//
|
||||
|
||||
export function throwError(msg: string): never {
|
||||
throw new Error(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event with the specified data and run function.
|
||||
*
|
||||
* @param data - The event data.
|
||||
* @returns The created event.
|
||||
*
|
||||
* @example
|
||||
* const myEvent = createEvent({
|
||||
* data: { name: 'ready', once: true },
|
||||
* run: (user, client, shard) => {
|
||||
* client.logger.info(`Start ${user.username} on shard #${shard}`);
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
export function createEvent<E extends ClientNameEvents | CustomEventsKeys>(data: {
|
||||
data: { name: E; once?: boolean };
|
||||
run: (...args: EventContext<{ data: { name: E } }>) => any;
|
||||
}) {
|
||||
data.data.once ??= false;
|
||||
return data;
|
||||
}
|
||||
|
||||
export const config = {
|
||||
/**
|
||||
* Configurations for the bot.
|
||||
*
|
||||
* @param data - The runtime configuration data for gateway connections.
|
||||
* @returns The internal runtime configuration.
|
||||
*/
|
||||
bot(data: RuntimeConfig) {
|
||||
return {
|
||||
...data,
|
||||
intents:
|
||||
'intents' in data
|
||||
? typeof data.intents === 'number'
|
||||
? data.intents
|
||||
: data.intents?.reduce<number>(
|
||||
(pr, acc) => pr | (typeof acc === 'number' ? acc : GatewayIntentBits[acc]),
|
||||
0,
|
||||
) ?? 0
|
||||
: 0,
|
||||
} as InternalRuntimeConfig;
|
||||
},
|
||||
/**
|
||||
* Configurations for the HTTP server.
|
||||
*
|
||||
* @param data - The runtime configuration data for http server.
|
||||
* @returns The internal runtime configuration for HTTP.
|
||||
*/
|
||||
http(data: RuntimeConfigHTTP) {
|
||||
const obj = {
|
||||
port: 8080,
|
||||
...data,
|
||||
} as InternalRuntimeConfigHTTP;
|
||||
if (isCloudfareWorker()) BaseClient._seyfertConfig = obj;
|
||||
return obj;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends the context of a command interaction.
|
||||
*
|
||||
* @param cb - The callback function to extend the context.
|
||||
* @returns The extended context.
|
||||
*
|
||||
* @example
|
||||
* const customContext = extendContext((interaction) => {
|
||||
* return {
|
||||
* owner: '123456789012345678',
|
||||
* // Add your custom properties here
|
||||
* };
|
||||
* });
|
||||
*/
|
||||
export function extendContext<T extends {}>(
|
||||
cb: (interaction: Parameters<NonNullable<BaseClientOptions['context']>>[0]) => T,
|
||||
) {
|
||||
return cb;
|
||||
}
|
@ -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