mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-03 05:26:07 +00:00
refactor: structures & folders 2
This commit is contained in:
parent
d1d0a1ce76
commit
cc7a5848eb
51
structures/channels/BaseChannel.ts
Normal file
51
structures/channels/BaseChannel.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import type { Model } from "../Base.ts";
|
||||||
|
import type { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { DiscordChannel } from "../../vendor/external.ts";
|
||||||
|
import type TextChannel from "./TextChannel.ts";
|
||||||
|
import type VoiceChannel from "./VoiceChannel.ts";
|
||||||
|
import type DMChannel from "./DMChannel.ts";
|
||||||
|
import type NewsChannel from "./NewsChannel.ts";
|
||||||
|
import type ThreadChannel from "./ThreadChannel.ts";
|
||||||
|
import { ChannelTypes } from "../../vendor/external.ts";
|
||||||
|
import { textBasedChannels } from "./TextChannel.ts";
|
||||||
|
|
||||||
|
export abstract class BaseChannel implements Model {
|
||||||
|
constructor(session: Session, data: DiscordChannel) {
|
||||||
|
this.id = data.id;
|
||||||
|
this.session = session;
|
||||||
|
this.name = data.name;
|
||||||
|
this.type = data.type;
|
||||||
|
}
|
||||||
|
readonly id: Snowflake;
|
||||||
|
readonly session: Session;
|
||||||
|
|
||||||
|
name?: string;
|
||||||
|
type: ChannelTypes;
|
||||||
|
|
||||||
|
isText(): this is TextChannel {
|
||||||
|
return textBasedChannels.includes(this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
isVoice(): this is VoiceChannel {
|
||||||
|
return this.type === ChannelTypes.GuildVoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDM(): this is DMChannel {
|
||||||
|
return this.type === ChannelTypes.DM;
|
||||||
|
}
|
||||||
|
|
||||||
|
isNews(): this is NewsChannel {
|
||||||
|
return this.type === ChannelTypes.GuildNews;
|
||||||
|
}
|
||||||
|
|
||||||
|
isThread(): this is ThreadChannel {
|
||||||
|
return this.type === ChannelTypes.GuildPublicThread || this.type === ChannelTypes.GuildPrivateThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
toString(): string {
|
||||||
|
return `<#${this.id}>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BaseChannel;
|
39
structures/channels/ChannelFactory.ts
Normal file
39
structures/channels/ChannelFactory.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { DiscordChannel } from "../../vendor/external.ts";
|
||||||
|
import { ChannelTypes } from "../../vendor/external.ts";
|
||||||
|
import { textBasedChannels } from "./TextChannel.ts";
|
||||||
|
import TextChannel from "./TextChannel.ts";
|
||||||
|
import VoiceChannel from "./VoiceChannel.ts";
|
||||||
|
import DMChannel from "./DMChannel.ts";
|
||||||
|
import NewsChannel from "./NewsChannel.ts";
|
||||||
|
import ThreadChannel from "./ThreadChannel.ts";
|
||||||
|
|
||||||
|
export type Channel =
|
||||||
|
| TextChannel
|
||||||
|
| VoiceChannel
|
||||||
|
| DMChannel
|
||||||
|
| NewsChannel
|
||||||
|
| ThreadChannel;
|
||||||
|
|
||||||
|
export class ChannelFactory {
|
||||||
|
static from(session: Session, channel: DiscordChannel): Channel {
|
||||||
|
switch (channel.type) {
|
||||||
|
case ChannelTypes.GuildPublicThread:
|
||||||
|
case ChannelTypes.GuildPrivateThread:
|
||||||
|
return new ThreadChannel(session, channel, channel.guild_id!);
|
||||||
|
case ChannelTypes.GuildNews:
|
||||||
|
return new NewsChannel(session, channel, channel.guild_id!);
|
||||||
|
case ChannelTypes.DM:
|
||||||
|
return new DMChannel(session, channel);
|
||||||
|
case ChannelTypes.GuildVoice:
|
||||||
|
return new VoiceChannel(session, channel, channel.guild_id!);
|
||||||
|
default:
|
||||||
|
if (textBasedChannels.includes(channel.type)) {
|
||||||
|
return new TextChannel(session, channel);
|
||||||
|
}
|
||||||
|
throw new Error("Channel was not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ChannelFactory;
|
39
structures/channels/DMChannel.ts
Normal file
39
structures/channels/DMChannel.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import type { Model } from "../Base.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import type { ChannelTypes, DiscordChannel } from "../../vendor/external.ts";
|
||||||
|
import TextChannel from "./TextChannel.ts";
|
||||||
|
import BaseChannel from "./BaseChannel.ts";
|
||||||
|
import User from "../User.ts";
|
||||||
|
import * as Routes from "../../util/Routes.ts";
|
||||||
|
|
||||||
|
export class DMChannel extends BaseChannel implements Model {
|
||||||
|
constructor(session: Session, data: DiscordChannel) {
|
||||||
|
super(session, data);
|
||||||
|
this.user = new User(this.session, data.recipents!.find((r) => r.id !== this.session.botId)!);
|
||||||
|
this.type = data.type as ChannelTypes.DM | ChannelTypes.GroupDm;
|
||||||
|
if (data.last_message_id) {
|
||||||
|
this.lastMessageId = data.last_message_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override type: ChannelTypes.DM | ChannelTypes.GroupDm;
|
||||||
|
user: User;
|
||||||
|
lastMessageId?: Snowflake;
|
||||||
|
|
||||||
|
async close() {
|
||||||
|
const channel = await this.session.rest.runMethod<DiscordChannel>(
|
||||||
|
this.session.rest,
|
||||||
|
"DELETE",
|
||||||
|
Routes.CHANNEL(this.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
return new DMChannel(this.session, channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextChannel.applyTo(DMChannel);
|
||||||
|
|
||||||
|
export interface DMChannel extends Omit<TextChannel, "type">, Omit<BaseChannel, "type"> {}
|
||||||
|
|
||||||
|
export default DMChannel;
|
70
structures/channels/GuildChannel.ts
Normal file
70
structures/channels/GuildChannel.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import type { Model } from "../Base.ts";
|
||||||
|
import type { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { ChannelTypes, DiscordChannel, DiscordInviteMetadata } from "../../vendor/external.ts";
|
||||||
|
import BaseChannel from "./BaseChannel.ts";
|
||||||
|
import Invite from "../Invite.ts";
|
||||||
|
import * as Routes from "../../util/Routes.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represent the options object to create a Thread Channel
|
||||||
|
* @link https://discord.com/developers/docs/resources/channel#start-thread-without-message
|
||||||
|
*/
|
||||||
|
export interface ThreadCreateOptions {
|
||||||
|
name: string;
|
||||||
|
autoArchiveDuration: 60 | 1440 | 4320 | 10080;
|
||||||
|
type: 10 | 11 | 12;
|
||||||
|
invitable?: boolean;
|
||||||
|
reason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GuildChannel extends BaseChannel implements Model {
|
||||||
|
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
|
||||||
|
super(session, data);
|
||||||
|
this.type = data.type as number;
|
||||||
|
this.guildId = guildId;
|
||||||
|
this.position = data.position;
|
||||||
|
data.topic ? this.topic = data.topic : null;
|
||||||
|
data.parent_id ? this.parentId = data.parent_id : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
override type: Exclude<ChannelTypes, ChannelTypes.DM | ChannelTypes.GroupDm>;
|
||||||
|
guildId: Snowflake;
|
||||||
|
topic?: string;
|
||||||
|
position?: number;
|
||||||
|
parentId?: Snowflake;
|
||||||
|
|
||||||
|
async fetchInvites(): Promise<Invite[]> {
|
||||||
|
const invites = await this.session.rest.runMethod<DiscordInviteMetadata[]>(
|
||||||
|
this.session.rest,
|
||||||
|
"GET",
|
||||||
|
Routes.CHANNEL_INVITES(this.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
return invites.map((invite) => new Invite(this.session, invite));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
async createThread(options: ThreadCreateOptions): Promise<ThreadChannel> {
|
||||||
|
const thread = await this.session.rest.runMethod<DiscordChannel>(
|
||||||
|
this.session.rest,
|
||||||
|
"POST",
|
||||||
|
Routes.CHANNEL_CREATE_THREAD(this.id),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
return new ThreadChannel(this.session, thread, this.guildId);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
async delete(reason?: string) {
|
||||||
|
await this.session.rest.runMethod<DiscordChannel>(
|
||||||
|
this.session.rest,
|
||||||
|
"DELETE",
|
||||||
|
Routes.CHANNEL(this.id),
|
||||||
|
{
|
||||||
|
reason,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GuildChannel;
|
31
structures/channels/NewsChannel.ts
Normal file
31
structures/channels/NewsChannel.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import type { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { ChannelTypes, DiscordChannel } from "../../vendor/external.ts";
|
||||||
|
import GuildChannel from "./GuildChannel.ts";
|
||||||
|
import Message from "../Message.ts";
|
||||||
|
import TextChannel from "./TextChannel.ts";
|
||||||
|
|
||||||
|
export class NewsChannel extends GuildChannel {
|
||||||
|
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
|
||||||
|
super(session, data, guildId);
|
||||||
|
this.type = data.type as ChannelTypes.GuildNews;
|
||||||
|
this.defaultAutoArchiveDuration = data.default_auto_archive_duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
override type: ChannelTypes.GuildNews;
|
||||||
|
defaultAutoArchiveDuration?: number;
|
||||||
|
|
||||||
|
crosspostMessage(messageId: Snowflake): Promise<Message> {
|
||||||
|
return Message.prototype.crosspost.call({ id: messageId, channelId: this.id, session: this.session });
|
||||||
|
}
|
||||||
|
|
||||||
|
get publishMessage() {
|
||||||
|
return this.crosspostMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TextChannel.applyTo(NewsChannel);
|
||||||
|
|
||||||
|
export interface NewsChannel extends TextChannel, GuildChannel {}
|
||||||
|
|
||||||
|
export default NewsChannel;
|
221
structures/channels/TextChannel.ts
Normal file
221
structures/channels/TextChannel.ts
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
// deno-lint-ignore-file ban-types
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import type { GetMessagesOptions, GetReactions } from "../../util/Routes.ts";
|
||||||
|
import type {
|
||||||
|
DiscordChannel,
|
||||||
|
DiscordInvite,
|
||||||
|
DiscordMessage,
|
||||||
|
DiscordWebhook,
|
||||||
|
TargetTypes,
|
||||||
|
} from "../../vendor/external.ts";
|
||||||
|
import type { CreateMessage, EditMessage, ReactionResolvable } from "../Message.ts";
|
||||||
|
import { ChannelTypes } from "../../vendor/external.ts";
|
||||||
|
import { urlToBase64 } from "../../util/urlToBase64.ts";
|
||||||
|
import Message from "../Message.ts";
|
||||||
|
import Invite from "../Invite.ts";
|
||||||
|
import Webhook from "../Webhook.ts";
|
||||||
|
import * as Routes from "../../util/Routes.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the options object to create an invitation
|
||||||
|
* @link https://discord.com/developers/docs/resources/channel#create-channel-invite-json-params
|
||||||
|
*/
|
||||||
|
export interface DiscordInviteOptions {
|
||||||
|
maxAge?: number;
|
||||||
|
maxUses?: number;
|
||||||
|
unique?: boolean;
|
||||||
|
temporary: boolean;
|
||||||
|
reason?: string;
|
||||||
|
targetType?: TargetTypes;
|
||||||
|
targetUserId?: Snowflake;
|
||||||
|
targetApplicationId?: Snowflake;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateWebhook {
|
||||||
|
name: string;
|
||||||
|
avatar?: string;
|
||||||
|
reason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const textBasedChannels = [
|
||||||
|
ChannelTypes.DM,
|
||||||
|
ChannelTypes.GroupDm,
|
||||||
|
ChannelTypes.GuildPrivateThread,
|
||||||
|
ChannelTypes.GuildPublicThread,
|
||||||
|
ChannelTypes.GuildNews,
|
||||||
|
ChannelTypes.GuildText,
|
||||||
|
];
|
||||||
|
|
||||||
|
export type TextBasedChannels =
|
||||||
|
| ChannelTypes.DM
|
||||||
|
| ChannelTypes.GroupDm
|
||||||
|
| ChannelTypes.GuildPrivateThread
|
||||||
|
| ChannelTypes.GuildPublicThread
|
||||||
|
| ChannelTypes.GuildNews
|
||||||
|
| ChannelTypes.GuildText;
|
||||||
|
|
||||||
|
export class TextChannel {
|
||||||
|
constructor(session: Session, data: DiscordChannel) {
|
||||||
|
this.session = session;
|
||||||
|
this.id = data.id;
|
||||||
|
this.name = data.name;
|
||||||
|
this.type = data.type as number;
|
||||||
|
this.rateLimitPerUser = data.rate_limit_per_user ?? 0;
|
||||||
|
this.nsfw = !!data.nsfw ?? false;
|
||||||
|
|
||||||
|
if (data.last_message_id) {
|
||||||
|
this.lastMessageId = data.last_message_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.last_pin_timestamp) {
|
||||||
|
this.lastPinTimestamp = data.last_pin_timestamp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly session: Session;
|
||||||
|
readonly id: Snowflake;
|
||||||
|
name?: string;
|
||||||
|
type: TextBasedChannels;
|
||||||
|
lastMessageId?: Snowflake;
|
||||||
|
lastPinTimestamp?: string;
|
||||||
|
rateLimitPerUser: number;
|
||||||
|
nsfw: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mixin
|
||||||
|
*/
|
||||||
|
static applyTo(klass: Function) {
|
||||||
|
klass.prototype.fetchPins = TextChannel.prototype.fetchPins;
|
||||||
|
klass.prototype.createInvite = TextChannel.prototype.createInvite;
|
||||||
|
klass.prototype.fetchMessages = TextChannel.prototype.fetchMessages;
|
||||||
|
klass.prototype.sendTyping = TextChannel.prototype.sendTyping;
|
||||||
|
klass.prototype.pinMessage = TextChannel.prototype.pinMessage;
|
||||||
|
klass.prototype.unpinMessage = TextChannel.prototype.unpinMessage;
|
||||||
|
klass.prototype.addReaction = TextChannel.prototype.addReaction;
|
||||||
|
klass.prototype.removeReaction = TextChannel.prototype.removeReaction;
|
||||||
|
klass.prototype.removeReactionEmoji = TextChannel.prototype.removeReactionEmoji;
|
||||||
|
klass.prototype.nukeReactions = TextChannel.prototype.nukeReactions;
|
||||||
|
klass.prototype.fetchReactions = TextChannel.prototype.fetchReactions;
|
||||||
|
klass.prototype.sendMessage = TextChannel.prototype.sendMessage;
|
||||||
|
klass.prototype.editMessage = TextChannel.prototype.editMessage;
|
||||||
|
klass.prototype.createWebhook = TextChannel.prototype.createWebhook;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchPins(): Promise<Message[] | []> {
|
||||||
|
const messages = await this.session.rest.runMethod<DiscordMessage[]>(
|
||||||
|
this.session.rest,
|
||||||
|
"GET",
|
||||||
|
Routes.CHANNEL_PINS(this.id),
|
||||||
|
);
|
||||||
|
return messages[0] ? messages.map((x: DiscordMessage) => new Message(this.session, x)) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async createInvite(options?: DiscordInviteOptions) {
|
||||||
|
const invite = await this.session.rest.runMethod<DiscordInvite>(
|
||||||
|
this.session.rest,
|
||||||
|
"POST",
|
||||||
|
Routes.CHANNEL_INVITES(this.id),
|
||||||
|
options
|
||||||
|
? {
|
||||||
|
max_age: options.maxAge,
|
||||||
|
max_uses: options.maxUses,
|
||||||
|
temporary: options.temporary,
|
||||||
|
unique: options.unique,
|
||||||
|
target_type: options.targetType,
|
||||||
|
target_user_id: options.targetUserId,
|
||||||
|
target_application_id: options.targetApplicationId,
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Invite(this.session, invite);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchMessages(options?: GetMessagesOptions): Promise<Message[] | []> {
|
||||||
|
if (options?.limit! > 100) throw Error("Values must be between 0-100");
|
||||||
|
const messages = await this.session.rest.runMethod<DiscordMessage[]>(
|
||||||
|
this.session.rest,
|
||||||
|
"GET",
|
||||||
|
Routes.CHANNEL_MESSAGES(this.id, options),
|
||||||
|
);
|
||||||
|
return messages[0] ? messages.map((x) => new Message(this.session, x)) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendTyping() {
|
||||||
|
await this.session.rest.runMethod<undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
"POST",
|
||||||
|
Routes.CHANNEL_TYPING(this.id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async pinMessage(messageId: Snowflake) {
|
||||||
|
await Message.prototype.pin.call({ id: messageId, channelId: this.id, session: this.session });
|
||||||
|
}
|
||||||
|
|
||||||
|
async unpinMessage(messageId: Snowflake) {
|
||||||
|
await Message.prototype.unpin.call({ id: messageId, channelId: this.id, session: this.session });
|
||||||
|
}
|
||||||
|
|
||||||
|
async addReaction(messageId: Snowflake, reaction: ReactionResolvable) {
|
||||||
|
await Message.prototype.addReaction.call(
|
||||||
|
{ channelId: this.id, id: messageId, session: this.session },
|
||||||
|
reaction,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeReaction(messageId: Snowflake, reaction: ReactionResolvable, options?: { userId: Snowflake }) {
|
||||||
|
await Message.prototype.removeReaction.call(
|
||||||
|
{ channelId: this.id, id: messageId, session: this.session },
|
||||||
|
reaction,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeReactionEmoji(messageId: Snowflake, reaction: ReactionResolvable) {
|
||||||
|
await Message.prototype.removeReactionEmoji.call(
|
||||||
|
{ channelId: this.id, id: messageId, session: this.session },
|
||||||
|
reaction,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async nukeReactions(messageId: Snowflake) {
|
||||||
|
await Message.prototype.nukeReactions.call({ channelId: this.id, id: messageId });
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchReactions(messageId: Snowflake, reaction: ReactionResolvable, options?: GetReactions) {
|
||||||
|
const users = await Message.prototype.fetchReactions.call(
|
||||||
|
{ channelId: this.id, id: messageId, session: this.session },
|
||||||
|
reaction,
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage(options: CreateMessage) {
|
||||||
|
return Message.prototype.reply.call({ channelId: this.id, session: this.session }, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
editMessage(messageId: Snowflake, options: EditMessage) {
|
||||||
|
return Message.prototype.edit.call({ channelId: this.id, id: messageId, session: this.session }, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createWebhook(options: CreateWebhook) {
|
||||||
|
const webhook = await this.session.rest.runMethod<DiscordWebhook>(
|
||||||
|
this.session.rest,
|
||||||
|
"POST",
|
||||||
|
Routes.CHANNEL_WEBHOOKS(this.id),
|
||||||
|
{
|
||||||
|
name: options.name,
|
||||||
|
avatar: options.avatar ? urlToBase64(options.avatar) : undefined,
|
||||||
|
reason: options.reason,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Webhook(this.session, webhook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TextChannel;
|
35
structures/channels/ThreadChannel.ts
Normal file
35
structures/channels/ThreadChannel.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import type { Model } from "../Base.ts";
|
||||||
|
import type { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { ChannelTypes, DiscordChannel } from "../../vendor/external.ts";
|
||||||
|
import GuildChannel from "./GuildChannel.ts";
|
||||||
|
import TextChannel from "./TextChannel.ts";
|
||||||
|
|
||||||
|
export class ThreadChannel extends GuildChannel implements Model {
|
||||||
|
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
|
||||||
|
super(session, data, guildId);
|
||||||
|
this.type = data.type as number;
|
||||||
|
this.archived = !!data.thread_metadata?.archived;
|
||||||
|
this.archiveTimestamp = data.thread_metadata?.archive_timestamp;
|
||||||
|
this.autoArchiveDuration = data.thread_metadata?.auto_archive_duration;
|
||||||
|
this.locked = !!data.thread_metadata?.locked;
|
||||||
|
this.messageCount = data.message_count;
|
||||||
|
this.memberCount = data.member_count;
|
||||||
|
this.ownerId = data.owner_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
override type: ChannelTypes.GuildNewsThread | ChannelTypes.GuildPrivateThread | ChannelTypes.GuildPublicThread;
|
||||||
|
archived?: boolean;
|
||||||
|
archiveTimestamp?: string;
|
||||||
|
autoArchiveDuration?: number;
|
||||||
|
locked?: boolean;
|
||||||
|
messageCount?: number;
|
||||||
|
memberCount?: number;
|
||||||
|
ownerId?: Snowflake;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextChannel.applyTo(ThreadChannel);
|
||||||
|
|
||||||
|
export interface ThreadChannel extends Omit<GuildChannel, "type">, Omit<TextChannel, "type"> {}
|
||||||
|
|
||||||
|
export default ThreadChannel;
|
60
structures/channels/VoiceChannel.ts
Normal file
60
structures/channels/VoiceChannel.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import type { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { DiscordChannel, VideoQualityModes } from "../../vendor/external.ts";
|
||||||
|
import { GatewayOpcodes } from "../../vendor/external.ts";
|
||||||
|
import { calculateShardId } from "../../vendor/gateway/calculateShardId.ts";
|
||||||
|
import GuildChannel from "./GuildChannel.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://discord.com/developers/docs/topics/gateway#update-voice-state
|
||||||
|
*/
|
||||||
|
export interface UpdateVoiceState {
|
||||||
|
guildId: string;
|
||||||
|
channelId?: string;
|
||||||
|
selfMute: boolean;
|
||||||
|
selfDeaf: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VoiceChannel extends GuildChannel {
|
||||||
|
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
|
||||||
|
super(session, data, guildId);
|
||||||
|
this.bitRate = data.bitrate;
|
||||||
|
this.userLimit = data.user_limit ?? 0;
|
||||||
|
this.videoQuality = data.video_quality_mode;
|
||||||
|
this.nsfw = !!data.nsfw;
|
||||||
|
|
||||||
|
if (data.rtc_region) {
|
||||||
|
this.rtcRegion = data.rtc_region;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitRate?: number;
|
||||||
|
userLimit: number;
|
||||||
|
rtcRegion?: Snowflake;
|
||||||
|
|
||||||
|
videoQuality?: VideoQualityModes;
|
||||||
|
nsfw: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function was gathered from Discordeno it may not work
|
||||||
|
*/
|
||||||
|
async connect(options?: UpdateVoiceState) {
|
||||||
|
const shardId = calculateShardId(this.session.gateway, BigInt(super.guildId));
|
||||||
|
const shard = this.session.gateway.manager.shards.get(shardId);
|
||||||
|
|
||||||
|
if (!shard) {
|
||||||
|
throw new Error(`Shard (id: ${shardId} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await shard.send({
|
||||||
|
op: GatewayOpcodes.VoiceStateUpdate,
|
||||||
|
d: {
|
||||||
|
guild_id: super.guildId,
|
||||||
|
channel_id: super.id,
|
||||||
|
self_mute: Boolean(options?.selfMute),
|
||||||
|
self_deaf: options?.selfDeaf ?? true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VoiceChannel;
|
55
structures/guilds/AnonymousGuild.ts
Normal file
55
structures/guilds/AnonymousGuild.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import type { Model } from "../Base.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { DiscordGuild, GuildNsfwLevel, VerificationLevels } from "../../vendor/external.ts";
|
||||||
|
import type { ImageFormat, ImageSize } from "../../util/shared/images.ts";
|
||||||
|
import { iconBigintToHash, iconHashToBigInt } from "../../util/hash.ts";
|
||||||
|
import { formatImageUrl } from "../../util/shared/images.ts";
|
||||||
|
import BaseGuild from "./BaseGuild.ts";
|
||||||
|
import * as Routes from "../../util/Routes.ts";
|
||||||
|
|
||||||
|
export class AnonymousGuild extends BaseGuild implements Model {
|
||||||
|
constructor(session: Session, data: Partial<DiscordGuild>); // TODO: Improve this type (name and id are required)
|
||||||
|
constructor(session: Session, data: DiscordGuild) {
|
||||||
|
super(session, data);
|
||||||
|
|
||||||
|
this.splashHash = data.splash ? iconHashToBigInt(data.splash) : undefined;
|
||||||
|
this.bannerHash = data.banner ? iconHashToBigInt(data.banner) : undefined;
|
||||||
|
|
||||||
|
this.verificationLevel = data.verification_level;
|
||||||
|
this.vanityUrlCode = data.vanity_url_code ? data.vanity_url_code : undefined;
|
||||||
|
this.nsfwLevel = data.nsfw_level;
|
||||||
|
this.description = data.description ? data.description : undefined;
|
||||||
|
this.premiumSubscriptionCount = data.premium_subscription_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
splashHash?: bigint;
|
||||||
|
bannerHash?: bigint;
|
||||||
|
|
||||||
|
verificationLevel: VerificationLevels;
|
||||||
|
vanityUrlCode?: string;
|
||||||
|
nsfwLevel: GuildNsfwLevel;
|
||||||
|
description?: string;
|
||||||
|
premiumSubscriptionCount?: number;
|
||||||
|
|
||||||
|
splashUrl(options: { size?: ImageSize; format?: ImageFormat } = { size: 128 }) {
|
||||||
|
if (this.splashHash) {
|
||||||
|
return formatImageUrl(
|
||||||
|
Routes.GUILD_SPLASH(this.id, iconBigintToHash(this.splashHash)),
|
||||||
|
options.size,
|
||||||
|
options.format,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bannerUrl(options: { size?: ImageSize; format?: ImageFormat } = { size: 128 }) {
|
||||||
|
if (this.bannerHash) {
|
||||||
|
return formatImageUrl(
|
||||||
|
Routes.GUILD_BANNER(this.id, iconBigintToHash(this.bannerHash)),
|
||||||
|
options.size,
|
||||||
|
options.format,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AnonymousGuild;
|
41
structures/guilds/BaseGuild.ts
Normal file
41
structures/guilds/BaseGuild.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import type { Model } from "../Base.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { DiscordGuild, GuildFeatures } from "../../vendor/external.ts";
|
||||||
|
import { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import { iconHashToBigInt } from "../../util/hash.ts";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for {@link Guild} and {@link AnonymousGuild}
|
||||||
|
*/
|
||||||
|
export abstract class BaseGuild implements Model {
|
||||||
|
constructor(session: Session, data: DiscordGuild) {
|
||||||
|
this.session = session;
|
||||||
|
this.id = data.id;
|
||||||
|
|
||||||
|
this.name = data.name;
|
||||||
|
this.iconHash = data.icon ? iconHashToBigInt(data.icon) : undefined;
|
||||||
|
|
||||||
|
this.features = data.features;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly session: Session;
|
||||||
|
readonly id: Snowflake;
|
||||||
|
|
||||||
|
name: string;
|
||||||
|
iconHash?: bigint;
|
||||||
|
features: GuildFeatures[];
|
||||||
|
|
||||||
|
get createdTimestamp() {
|
||||||
|
return Snowflake.snowflakeToTimestamp(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
get createdAt() {
|
||||||
|
return new Date(this.createdTimestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default BaseGuild;
|
367
structures/guilds/Guild.ts
Normal file
367
structures/guilds/Guild.ts
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
import type { Model } from "../Base.ts";
|
||||||
|
import type { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type {
|
||||||
|
DiscordEmoji,
|
||||||
|
DiscordGuild,
|
||||||
|
DiscordInviteMetadata,
|
||||||
|
DiscordMemberWithUser,
|
||||||
|
DiscordRole,
|
||||||
|
} from "../../vendor/external.ts";
|
||||||
|
import type { GetInvite } from "../../util/Routes.ts";
|
||||||
|
import {
|
||||||
|
DefaultMessageNotificationLevels,
|
||||||
|
ExplicitContentFilterLevels,
|
||||||
|
VerificationLevels,
|
||||||
|
} from "../../vendor/external.ts";
|
||||||
|
import { iconBigintToHash, iconHashToBigInt } from "../../util/hash.ts";
|
||||||
|
import { urlToBase64 } from "../../util/urlToBase64.ts";
|
||||||
|
import Member from "../Member.ts";
|
||||||
|
import BaseGuild from "./BaseGuild.ts";
|
||||||
|
import Role from "../Role.ts";
|
||||||
|
import GuildEmoji from "../GuildEmoji.ts";
|
||||||
|
import Invite from "../Invite.ts";
|
||||||
|
import * as Routes from "../../util/Routes.ts";
|
||||||
|
|
||||||
|
export interface CreateRole {
|
||||||
|
name?: string;
|
||||||
|
color?: number;
|
||||||
|
iconHash?: string | bigint;
|
||||||
|
unicodeEmoji?: string;
|
||||||
|
hoist?: boolean;
|
||||||
|
mentionable?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModifyGuildRole {
|
||||||
|
name?: string;
|
||||||
|
color?: number;
|
||||||
|
hoist?: boolean;
|
||||||
|
mentionable?: boolean;
|
||||||
|
unicodeEmoji?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateGuildEmoji {
|
||||||
|
name: string;
|
||||||
|
image: string;
|
||||||
|
roles?: Snowflake[];
|
||||||
|
reason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModifyGuildEmoji {
|
||||||
|
name?: string;
|
||||||
|
roles?: Snowflake[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://discord.com/developers/docs/resources/guild#create-guild-ban
|
||||||
|
*/
|
||||||
|
export interface CreateGuildBan {
|
||||||
|
deleteMessageDays?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||||
|
reason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://discord.com/developers/docs/resources/guild#modify-guild-member
|
||||||
|
*/
|
||||||
|
export interface ModifyGuildMember {
|
||||||
|
nick?: string;
|
||||||
|
roles?: Snowflake[];
|
||||||
|
mute?: boolean;
|
||||||
|
deaf?: boolean;
|
||||||
|
channelId?: Snowflake;
|
||||||
|
communicationDisabledUntil?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @link https://discord.com/developers/docs/resources/guild#begin-guild-prune
|
||||||
|
*/
|
||||||
|
export interface BeginGuildPrune {
|
||||||
|
days?: number;
|
||||||
|
computePruneCount?: boolean;
|
||||||
|
includeRoles?: Snowflake[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModifyRolePositions {
|
||||||
|
id: Snowflake;
|
||||||
|
position?: number | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a guild
|
||||||
|
* @link https://discord.com/developers/docs/resources/guild#guild-object
|
||||||
|
*/
|
||||||
|
export class Guild extends BaseGuild implements Model {
|
||||||
|
constructor(session: Session, data: DiscordGuild) {
|
||||||
|
super(session, data);
|
||||||
|
|
||||||
|
this.splashHash = data.splash ? iconHashToBigInt(data.splash) : undefined;
|
||||||
|
this.discoverySplashHash = data.discovery_splash ? iconHashToBigInt(data.discovery_splash) : undefined;
|
||||||
|
this.ownerId = data.owner_id;
|
||||||
|
this.widgetEnabled = !!data.widget_enabled;
|
||||||
|
this.widgetChannelId = data.widget_channel_id ? data.widget_channel_id : undefined;
|
||||||
|
this.vefificationLevel = data.verification_level;
|
||||||
|
this.defaultMessageNotificationLevel = data.default_message_notifications;
|
||||||
|
this.explicitContentFilterLevel = data.explicit_content_filter;
|
||||||
|
this.members = data.members?.map((member) => new Member(session, { ...member, user: member.user! }, data.id)) ??
|
||||||
|
[];
|
||||||
|
this.roles = data.roles.map((role) => new Role(session, role, data.id));
|
||||||
|
this.emojis = data.emojis.map((guildEmoji) => new GuildEmoji(session, guildEmoji, data.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
splashHash?: bigint;
|
||||||
|
discoverySplashHash?: bigint;
|
||||||
|
ownerId: Snowflake;
|
||||||
|
widgetEnabled: boolean;
|
||||||
|
widgetChannelId?: Snowflake;
|
||||||
|
vefificationLevel: VerificationLevels;
|
||||||
|
defaultMessageNotificationLevel: DefaultMessageNotificationLevels;
|
||||||
|
explicitContentFilterLevel: ExplicitContentFilterLevels;
|
||||||
|
members: Member[];
|
||||||
|
roles: Role[];
|
||||||
|
emojis: GuildEmoji[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 'null' would reset the nickname
|
||||||
|
*/
|
||||||
|
async editBotNickname(options: { nick: string | null; reason?: string }) {
|
||||||
|
const result = await this.session.rest.runMethod<{ nick?: string } | undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
"PATCH",
|
||||||
|
Routes.USER_NICK(this.id),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
return result?.nick;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createEmoji(options: CreateGuildEmoji): Promise<GuildEmoji> {
|
||||||
|
if (options.image && !options.image.startsWith("data:image/")) {
|
||||||
|
options.image = await urlToBase64(options.image);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emoji = await this.session.rest.runMethod<DiscordEmoji>(
|
||||||
|
this.session.rest,
|
||||||
|
"POST",
|
||||||
|
Routes.GUILD_EMOJIS(this.id),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
return new GuildEmoji(this.session, emoji, this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteEmoji(id: Snowflake, { reason }: { reason?: string } = {}): Promise<void> {
|
||||||
|
await this.session.rest.runMethod<undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
"DELETE",
|
||||||
|
Routes.GUILD_EMOJI(this.id, id),
|
||||||
|
{ reason },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async editEmoji(id: Snowflake, options: ModifyGuildEmoji): Promise<GuildEmoji> {
|
||||||
|
const emoji = await this.session.rest.runMethod<DiscordEmoji>(
|
||||||
|
this.session.rest,
|
||||||
|
"PATCH",
|
||||||
|
Routes.GUILD_EMOJI(this.id, id),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
return new GuildEmoji(this.session, emoji, this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createRole(options: CreateRole): Promise<Role> {
|
||||||
|
let icon: string | undefined;
|
||||||
|
|
||||||
|
if (options.iconHash) {
|
||||||
|
if (typeof options.iconHash === "string") {
|
||||||
|
icon = options.iconHash;
|
||||||
|
} else {
|
||||||
|
icon = iconBigintToHash(options.iconHash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const role = await this.session.rest.runMethod<DiscordRole>(
|
||||||
|
this.session.rest,
|
||||||
|
"PUT",
|
||||||
|
Routes.GUILD_ROLES(this.id),
|
||||||
|
{
|
||||||
|
name: options.name,
|
||||||
|
color: options.color,
|
||||||
|
icon,
|
||||||
|
unicode_emoji: options.unicodeEmoji,
|
||||||
|
hoist: options.hoist,
|
||||||
|
mentionable: options.mentionable,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Role(this.session, role, this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteRole(roleId: Snowflake): Promise<void> {
|
||||||
|
await this.session.rest.runMethod<undefined>(this.session.rest, "DELETE", Routes.GUILD_ROLE(this.id, roleId));
|
||||||
|
}
|
||||||
|
|
||||||
|
async editRole(roleId: Snowflake, options: ModifyGuildRole): Promise<Role> {
|
||||||
|
const role = await this.session.rest.runMethod<DiscordRole>(
|
||||||
|
this.session.rest,
|
||||||
|
"PATCH",
|
||||||
|
Routes.GUILD_ROLE(this.id, roleId),
|
||||||
|
{
|
||||||
|
name: options.name,
|
||||||
|
color: options.color,
|
||||||
|
hoist: options.hoist,
|
||||||
|
mentionable: options.mentionable,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Role(this.session, role, this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async addRole(memberId: Snowflake, roleId: Snowflake, { reason }: { reason?: string } = {}) {
|
||||||
|
await this.session.rest.runMethod<undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
"PUT",
|
||||||
|
Routes.GUILD_MEMBER_ROLE(this.id, memberId, roleId),
|
||||||
|
{ reason },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeRole(memberId: Snowflake, roleId: Snowflake, { reason }: { reason?: string } = {}) {
|
||||||
|
await this.session.rest.runMethod<undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
"DELETE",
|
||||||
|
Routes.GUILD_MEMBER_ROLE(this.id, memberId, roleId),
|
||||||
|
{ reason },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the roles moved
|
||||||
|
*/
|
||||||
|
async moveRoles(options: ModifyRolePositions[]) {
|
||||||
|
const roles = await this.session.rest.runMethod<DiscordRole[]>(
|
||||||
|
this.session.rest,
|
||||||
|
"PATCH",
|
||||||
|
Routes.GUILD_ROLES(this.id),
|
||||||
|
options,
|
||||||
|
);
|
||||||
|
|
||||||
|
return roles.map((role) => new Role(this.session, role, this.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteInvite(inviteCode: string): Promise<void> {
|
||||||
|
await this.session.rest.runMethod<undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
"DELETE",
|
||||||
|
Routes.INVITE(inviteCode),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchInvite(inviteCode: string, options: GetInvite): Promise<Invite> {
|
||||||
|
const inviteMetadata = await this.session.rest.runMethod<DiscordInviteMetadata>(
|
||||||
|
this.session.rest,
|
||||||
|
"GET",
|
||||||
|
Routes.INVITE(inviteCode, options),
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Invite(this.session, inviteMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchInvites(): Promise<Invite[]> {
|
||||||
|
const invites = await this.session.rest.runMethod<DiscordInviteMetadata[]>(
|
||||||
|
this.session.rest,
|
||||||
|
"GET",
|
||||||
|
Routes.GUILD_INVITES(this.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
return invites.map((invite) => new Invite(this.session, invite));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bans the member
|
||||||
|
*/
|
||||||
|
async banMember(memberId: Snowflake, options: CreateGuildBan) {
|
||||||
|
await this.session.rest.runMethod<undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
"PUT",
|
||||||
|
Routes.GUILD_BAN(this.id, memberId),
|
||||||
|
options
|
||||||
|
? {
|
||||||
|
delete_message_days: options.deleteMessageDays,
|
||||||
|
reason: options.reason,
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kicks the member
|
||||||
|
*/
|
||||||
|
async kickMember(memberId: Snowflake, { reason }: { reason?: string }) {
|
||||||
|
await this.session.rest.runMethod<undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
"DELETE",
|
||||||
|
Routes.GUILD_MEMBER(this.id, memberId),
|
||||||
|
{ reason },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unbans the member
|
||||||
|
* */
|
||||||
|
async unbanMember(memberId: Snowflake) {
|
||||||
|
await this.session.rest.runMethod<undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
"DELETE",
|
||||||
|
Routes.GUILD_BAN(this.id, memberId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async editMember(memberId: Snowflake, options: ModifyGuildMember) {
|
||||||
|
const member = await this.session.rest.runMethod<DiscordMemberWithUser>(
|
||||||
|
this.session.rest,
|
||||||
|
"PATCH",
|
||||||
|
Routes.GUILD_MEMBER(this.id, memberId),
|
||||||
|
{
|
||||||
|
nick: options.nick,
|
||||||
|
roles: options.roles,
|
||||||
|
mute: options.mute,
|
||||||
|
deaf: options.deaf,
|
||||||
|
channel_id: options.channelId,
|
||||||
|
communication_disabled_until: options.communicationDisabledUntil
|
||||||
|
? new Date(options.communicationDisabledUntil).toISOString()
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Member(this.session, member, this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
async pruneMembers(options: BeginGuildPrune): Promise<number> {
|
||||||
|
const result = await this.session.rest.runMethod<{ pruned: number }>(
|
||||||
|
this.session.rest,
|
||||||
|
"POST",
|
||||||
|
Routes.GUILD_PRUNE(this.id),
|
||||||
|
{
|
||||||
|
days: options.days,
|
||||||
|
compute_prune_count: options.computePruneCount,
|
||||||
|
include_roles: options.includeRoles,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return result.pruned;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPruneCount(): Promise<number> {
|
||||||
|
const result = await this.session.rest.runMethod<{ pruned: number }>(
|
||||||
|
this.session.rest,
|
||||||
|
"GET",
|
||||||
|
Routes.GUILD_PRUNE(this.id),
|
||||||
|
);
|
||||||
|
|
||||||
|
return result.pruned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Guild;
|
19
structures/guilds/InviteGuild.ts
Normal file
19
structures/guilds/InviteGuild.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import type { Model } from "../Base.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type { DiscordGuild } from "../../vendor/external.ts";
|
||||||
|
import AnonymousGuild from "./AnonymousGuild.ts";
|
||||||
|
import WelcomeScreen from "../WelcomeScreen.ts";
|
||||||
|
|
||||||
|
export class InviteGuild extends AnonymousGuild implements Model {
|
||||||
|
constructor(session: Session, data: Partial<DiscordGuild>) {
|
||||||
|
super(session, data);
|
||||||
|
|
||||||
|
if (data.welcome_screen) {
|
||||||
|
this.welcomeScreen = new WelcomeScreen(session, data.welcome_screen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
welcomeScreen?: WelcomeScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default InviteGuild;
|
145
structures/interactions/Interaction.ts
Normal file
145
structures/interactions/Interaction.ts
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
import type { Model } from "../Base.ts";
|
||||||
|
import type { Snowflake } from "../../util/Snowflake.ts";
|
||||||
|
import type { Session } from "../../session/Session.ts";
|
||||||
|
import type {
|
||||||
|
DiscordInteraction,
|
||||||
|
DiscordMessage,
|
||||||
|
FileContent,
|
||||||
|
InteractionResponseTypes,
|
||||||
|
InteractionTypes,
|
||||||
|
} from "../../vendor/external.ts";
|
||||||
|
import type { MessageFlags } from "../../util/shared/flags.ts";
|
||||||
|
import type { AllowedMentions } from "../Message.ts";
|
||||||
|
import User from "../User.ts";
|
||||||
|
import Message from "../Message.ts";
|
||||||
|
import Member from "../Member.ts";
|
||||||
|
import * as Routes from "../../util/Routes.ts";
|
||||||
|
|
||||||
|
export interface InteractionResponse {
|
||||||
|
type: InteractionResponseTypes;
|
||||||
|
data?: InteractionApplicationCommandCallbackData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InteractionApplicationCommandCallbackData {
|
||||||
|
content?: string;
|
||||||
|
tts?: boolean;
|
||||||
|
allowedMentions?: AllowedMentions;
|
||||||
|
files?: FileContent[];
|
||||||
|
customId?: string;
|
||||||
|
title?: string;
|
||||||
|
// components?: Component[];
|
||||||
|
flags?: MessageFlags;
|
||||||
|
choices?: ApplicationCommandOptionChoice[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptionchoice */
|
||||||
|
export interface ApplicationCommandOptionChoice {
|
||||||
|
name: string;
|
||||||
|
value: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: abstract Interaction, CommandInteraction, ComponentInteraction, PingInteraction, etc
|
||||||
|
|
||||||
|
export class Interaction implements Model {
|
||||||
|
constructor(session: Session, data: DiscordInteraction) {
|
||||||
|
this.session = session;
|
||||||
|
this.id = data.id;
|
||||||
|
this.token = data.token;
|
||||||
|
this.type = data.type;
|
||||||
|
this.guildId = data.guild_id;
|
||||||
|
this.channelId = data.channel_id;
|
||||||
|
this.applicationId = data.application_id;
|
||||||
|
this.locale = data.locale;
|
||||||
|
this.data = data.data;
|
||||||
|
|
||||||
|
if (!data.guild_id) {
|
||||||
|
this.user = new User(session, data.user!);
|
||||||
|
} else {
|
||||||
|
this.member = new Member(session, data.member!, data.guild_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly session: Session;
|
||||||
|
readonly id: Snowflake;
|
||||||
|
readonly token: string;
|
||||||
|
|
||||||
|
type: InteractionTypes;
|
||||||
|
guildId?: Snowflake;
|
||||||
|
channelId?: Snowflake;
|
||||||
|
applicationId?: Snowflake;
|
||||||
|
locale?: string;
|
||||||
|
// deno-lint-ignore no-explicit-any
|
||||||
|
data: any;
|
||||||
|
user?: User;
|
||||||
|
member?: Member;
|
||||||
|
|
||||||
|
async respond({ type, data }: InteractionResponse) {
|
||||||
|
const toSend = {
|
||||||
|
tts: data?.tts,
|
||||||
|
title: data?.title,
|
||||||
|
flags: data?.flags,
|
||||||
|
content: data?.content,
|
||||||
|
choices: data?.choices,
|
||||||
|
custom_id: data?.customId,
|
||||||
|
allowed_mentions: data?.allowedMentions
|
||||||
|
? {
|
||||||
|
users: data.allowedMentions.users,
|
||||||
|
roles: data.allowedMentions.roles,
|
||||||
|
parse: data.allowedMentions.parse,
|
||||||
|
replied_user: data.allowedMentions.repliedUser,
|
||||||
|
}
|
||||||
|
: { parse: [] },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.session.unrepliedInteractions.delete(BigInt(this.id))) {
|
||||||
|
await this.session.rest.sendRequest<undefined>(
|
||||||
|
this.session.rest,
|
||||||
|
{
|
||||||
|
url: Routes.INTERACTION_ID_TOKEN(this.id, this.token),
|
||||||
|
method: "POST",
|
||||||
|
payload: this.session.rest.createRequestBody(this.session.rest, {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
type: type,
|
||||||
|
data: toSend,
|
||||||
|
file: data?.files,
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
// remove authorization header
|
||||||
|
Authorization: "",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await this.session.rest.sendRequest<DiscordMessage>(
|
||||||
|
this.session.rest,
|
||||||
|
{
|
||||||
|
url: Routes.WEBHOOK(this.session.applicationId ?? this.session.botId, this.token),
|
||||||
|
method: "POST",
|
||||||
|
payload: this.session.rest.createRequestBody(this.session.rest, {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
...toSend,
|
||||||
|
file: data?.files,
|
||||||
|
},
|
||||||
|
headers: {
|
||||||
|
// remove authorization header
|
||||||
|
Authorization: "",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return new Message(this.session, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
inGuild(): this is Interaction & { user: undefined; guildId: Snowflake; member: Member } {
|
||||||
|
return !!this.guildId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Interaction;
|
Loading…
x
Reference in New Issue
Block a user