mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-02 21:16:09 +00:00
Merge branch 'main' of https://github.com/yuzudev/biscuit into add-n128
This commit is contained in:
commit
fd15e1a5f4
@ -1,39 +1,78 @@
|
||||
import type { DiscordMessage, DiscordReady } from "../vendor/external.ts";
|
||||
import type {
|
||||
DiscordGuildMemberAdd,
|
||||
DiscordGuildMemberRemove,
|
||||
DiscordGuildMemberUpdate,
|
||||
DiscordInteraction,
|
||||
DiscordMessage,
|
||||
DiscordMessageDelete,
|
||||
DiscordReady,
|
||||
} from "../vendor/external.ts";
|
||||
import type { Snowflake } from "../util/Snowflake.ts";
|
||||
import type { Session } from "../session/Session.ts";
|
||||
import Member from "../structures/Member.ts";
|
||||
import Message from "../structures/Message.ts";
|
||||
import User from "../structures/User.ts";
|
||||
import Interaction from "../structures/Interaction.ts";
|
||||
|
||||
export type RawHandler<T extends unknown[]> = (...args: [Session, number, ...T]) => void;
|
||||
export type RawHandler<T> = (...args: [Session, number, T]) => void;
|
||||
export type Handler<T extends unknown[]> = (...args: T) => unknown;
|
||||
|
||||
export type Ready = [DiscordReady];
|
||||
export const READY: RawHandler<Ready> = (session, shardId, payload) => {
|
||||
session.emit("ready", payload, shardId);
|
||||
export const READY: RawHandler<DiscordReady> = (session, shardId, payload) => {
|
||||
session.applicationId = payload.application.id;
|
||||
session.botId = payload.user.id;
|
||||
session.emit("ready", { ...payload, user: new User(session, payload.user) }, shardId);
|
||||
};
|
||||
|
||||
export type MessageCreate = [DiscordMessage];
|
||||
export const MESSAGE_CREATE: RawHandler<MessageCreate> = (session, _shardId, message) => {
|
||||
export const MESSAGE_CREATE: RawHandler<DiscordMessage> = (session, _shardId, message) => {
|
||||
session.emit("messageCreate", new Message(session, message));
|
||||
};
|
||||
|
||||
export type MessageUpdate = [DiscordMessage];
|
||||
export const MESSAGE_UPDATE: RawHandler<MessageUpdate> = (session, _shardId, new_message) => {
|
||||
export const MESSAGE_UPDATE: RawHandler<DiscordMessage> = (session, _shardId, new_message) => {
|
||||
session.emit("messageUpdate", new Message(session, new_message));
|
||||
};
|
||||
|
||||
export type MessageDelete = [Snowflake];
|
||||
export const MESSAGE_DELETE: RawHandler<MessageDelete> = (session, _shardId, deleted_message_id) => {
|
||||
session.emit("messageDelete", deleted_message_id);
|
||||
export const MESSAGE_DELETE: RawHandler<DiscordMessageDelete> = (session, _shardId, { id, channel_id, guild_id }) => {
|
||||
session.emit("messageDelete", { id, channelId: channel_id, guildId: guild_id });
|
||||
};
|
||||
|
||||
export const raw: RawHandler<[unknown]> = (session, shardId, data) => {
|
||||
export const GUILD_MEMBER_ADD: RawHandler<DiscordGuildMemberAdd> = (session, _shardId, member) => {
|
||||
session.emit("guildMemberAdd", new Member(session, member, member.guild_id));
|
||||
};
|
||||
|
||||
export const GUILD_MEMBER_UPDATE: RawHandler<DiscordGuildMemberUpdate> = (session, _shardId, member) => {
|
||||
session.emit("guildMemberUpdate", new Member(session, member, member.guild_id));
|
||||
};
|
||||
|
||||
export const GUILD_MEMBER_REMOVE: RawHandler<DiscordGuildMemberRemove> = (session, _shardId, member) => {
|
||||
session.emit("guildMemberRemove", new User(session, member.user), member.guild_id);
|
||||
};
|
||||
|
||||
export const INTERACTION_CREATE: RawHandler<DiscordInteraction> = (session, _shardId, interaction) => {
|
||||
session.unrepliedInteractions.add(BigInt(interaction.id));
|
||||
|
||||
// could be improved
|
||||
setTimeout(() => session.unrepliedInteractions.delete(BigInt(interaction.id)), 15 * 60 * 1000);
|
||||
|
||||
session.emit("interactionCreate", new Interaction(session, interaction));
|
||||
};
|
||||
|
||||
export const raw: RawHandler<unknown> = (session, shardId, data) => {
|
||||
session.emit("raw", data, shardId);
|
||||
};
|
||||
|
||||
export interface Events {
|
||||
"ready": Handler<[DiscordReady, number]>;
|
||||
"messageCreate": Handler<[Message]>;
|
||||
"messageUpdate": Handler<[Message]>;
|
||||
"messageDelete": Handler<[Snowflake]>;
|
||||
"raw": Handler<[unknown]>;
|
||||
export interface Ready extends Omit<DiscordReady, "user"> {
|
||||
user: User;
|
||||
}
|
||||
|
||||
// deno-fmt-ignore-file
|
||||
export interface Events {
|
||||
"ready": Handler<[Ready, number]>;
|
||||
"messageCreate": Handler<[Message]>;
|
||||
"messageUpdate": Handler<[Message]>;
|
||||
"messageDelete": Handler<[{ id: Snowflake, channelId: Snowflake, guildId?: Snowflake }]>;
|
||||
"guildMemberAdd": Handler<[Member]>;
|
||||
"guildMemberUpdate": Handler<[Member]>;
|
||||
"guildMemberRemove": Handler<[User, Snowflake]>;
|
||||
"interactionCreate": Handler<[Interaction]>;
|
||||
"raw": Handler<[unknown, number]>;
|
||||
}
|
||||
|
5
mod.ts
5
mod.ts
@ -2,12 +2,15 @@ export * from "./structures/AnonymousGuild.ts";
|
||||
export * from "./structures/Attachment.ts";
|
||||
export * from "./structures/Base.ts";
|
||||
export * from "./structures/BaseGuild.ts";
|
||||
export * from "./structures/Channel.ts";
|
||||
export * from "./structures/BaseChannel.ts";
|
||||
export * from "./structures/Component.ts";
|
||||
export * from "./structures/DMChannel.ts";
|
||||
export * from "./structures/Embed.ts";
|
||||
export * from "./structures/Emoji.ts";
|
||||
export * from "./structures/Guild.ts";
|
||||
export * from "./structures/GuildChannel.ts";
|
||||
export * from "./structures/GuildEmoji.ts";
|
||||
export * from "./structures/Interaction.ts";
|
||||
export * from "./structures/Invite.ts";
|
||||
export * from "./structures/InviteGuild.ts";
|
||||
export * from "./structures/Member.ts";
|
||||
|
@ -4,7 +4,7 @@ import type { Events } from "../handlers/Actions.ts";
|
||||
|
||||
import { Snowflake } from "../util/Snowflake.ts";
|
||||
import { EventEmitter } from "../util/EventEmmiter.ts";
|
||||
import { createGatewayManager, createRestManager } from "../vendor/external.ts";
|
||||
import { createGatewayManager, createRestManager, getBotIdFromToken } from "../vendor/external.ts";
|
||||
|
||||
import * as Routes from "../util/Routes.ts";
|
||||
import * as Actions from "../handlers/Actions.ts";
|
||||
@ -39,6 +39,27 @@ export class Session extends EventEmitter {
|
||||
rest: ReturnType<typeof createRestManager>;
|
||||
gateway: ReturnType<typeof createGatewayManager>;
|
||||
|
||||
unrepliedInteractions: Set<bigint> = new Set();
|
||||
|
||||
#botId: Snowflake;
|
||||
#applicationId?: Snowflake;
|
||||
|
||||
set applicationId(id: Snowflake) {
|
||||
this.#applicationId = id;
|
||||
}
|
||||
|
||||
get applicationId() {
|
||||
return this.#applicationId!;
|
||||
}
|
||||
|
||||
set botId(id: Snowflake) {
|
||||
this.#botId = id;
|
||||
}
|
||||
|
||||
get botId() {
|
||||
return this.#botId;
|
||||
}
|
||||
|
||||
constructor(options: SessionOptions) {
|
||||
super();
|
||||
this.options = options;
|
||||
@ -71,7 +92,8 @@ export class Session extends EventEmitter {
|
||||
},
|
||||
handleDiscordPayload: this.options.rawHandler ?? defHandler,
|
||||
});
|
||||
// TODO: set botId in Session.botId or something
|
||||
|
||||
this.#botId = getBotIdFromToken(options.token).toString();
|
||||
}
|
||||
|
||||
override on<K extends keyof Events>(event: K, func: Events[K]): this;
|
||||
@ -89,6 +111,11 @@ export class Session extends EventEmitter {
|
||||
return super.once(event, func);
|
||||
}
|
||||
|
||||
override emit<K extends keyof Events>(event: K, ...params: Parameters<Events[K]>): boolean;
|
||||
override emit<K extends string>(event: K, ...params: unknown[]): boolean {
|
||||
return super.emit(event, ...params);
|
||||
}
|
||||
|
||||
async start() {
|
||||
const getGatewayBot = () => this.rest.runMethod<DiscordGetGatewayBot>(this.rest, "GET", Routes.GATEWAY_BOT());
|
||||
|
||||
|
49
structures/BaseChannel.ts
Normal file
49
structures/BaseChannel.ts
Normal file
@ -0,0 +1,49 @@
|
||||
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 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 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 this instanceof TextChannel;
|
||||
}
|
||||
|
||||
isVoice(): this is VoiceChannel {
|
||||
return this instanceof VoiceChannel;
|
||||
}
|
||||
|
||||
isDM(): this is DMChannel {
|
||||
return this instanceof DMChannel;
|
||||
}
|
||||
|
||||
isNews(): this is NewsChannel {
|
||||
return this instanceof NewsChannel;
|
||||
}
|
||||
|
||||
isThread(): this is ThreadChannel {
|
||||
return this instanceof ThreadChannel;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `<#${this.id}>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseChannel;
|
@ -1,24 +0,0 @@
|
||||
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";
|
||||
|
||||
export abstract class Channel 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;
|
||||
|
||||
toString(): string {
|
||||
return `<#${this.id}>`;
|
||||
}
|
||||
}
|
||||
|
||||
export default Channel;
|
41
structures/Component.ts
Normal file
41
structures/Component.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import type { Session } from "../session/Session.ts";
|
||||
import type { DiscordComponent, MessageComponentTypes } from "../vendor/external.ts";
|
||||
import Emoji from "./Emoji.ts";
|
||||
|
||||
export class Component {
|
||||
constructor(session: Session, data: DiscordComponent) {
|
||||
this.session = session;
|
||||
this.customId = data.custom_id;
|
||||
this.type = data.type;
|
||||
this.components = data.components?.map((component) => new Component(session, component));
|
||||
this.disabled = !!data.disabled;
|
||||
|
||||
if (data.emoji) {
|
||||
this.emoji = new Emoji(session, data.emoji);
|
||||
}
|
||||
|
||||
this.maxValues = data.max_values;
|
||||
this.minValues = data.min_values;
|
||||
this.label = data.label;
|
||||
this.value = data.value;
|
||||
this.options = data.options ?? [];
|
||||
this.placeholder = data.placeholder;
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
|
||||
customId?: string;
|
||||
type: MessageComponentTypes;
|
||||
components?: Component[];
|
||||
disabled: boolean;
|
||||
emoji?: Emoji;
|
||||
maxValues?: number;
|
||||
minValues?: number;
|
||||
label?: string;
|
||||
value?: string;
|
||||
// deno-lint-ignore no-explicit-any
|
||||
options: any[];
|
||||
placeholder?: string;
|
||||
}
|
||||
|
||||
export default Component;
|
@ -1,16 +1,23 @@
|
||||
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 Channel from "./Channel.ts";
|
||||
import BaseChannel from "./BaseChannel.ts";
|
||||
import User from "./User.ts";
|
||||
import * as Routes from "../util/Routes.ts";
|
||||
|
||||
export class DMChannel extends Channel {
|
||||
export class DMChannel extends BaseChannel implements Model {
|
||||
constructor(session: Session, data: DiscordChannel) {
|
||||
super(session, data);
|
||||
data.last_message_id ? this.lastMessageId = data.last_message_id : undefined;
|
||||
// Waiting for implementation of botId in session
|
||||
//this.user = new User(this.session, data.recipents!.find((r) => r.id !== this.session.botId));
|
||||
|
||||
this.user = new User(this.session, data.recipents!.find((r) => r.id !== this.session.botId)!);
|
||||
|
||||
if (data.last_message_id) {
|
||||
this.lastMessageId = data.last_message_id;
|
||||
}
|
||||
}
|
||||
|
||||
user: User;
|
||||
lastMessageId?: Snowflake;
|
||||
|
||||
async close() {
|
||||
|
101
structures/Embed.ts
Normal file
101
structures/Embed.ts
Normal file
@ -0,0 +1,101 @@
|
||||
import type { DiscordEmbed, EmbedTypes } from "../vendor/external.ts";
|
||||
|
||||
export interface Embed {
|
||||
title?: string;
|
||||
timestamp?: string;
|
||||
type?: EmbedTypes;
|
||||
url?: string;
|
||||
color?: number;
|
||||
description?: string;
|
||||
author?: {
|
||||
name: string;
|
||||
iconURL?: string;
|
||||
proxyIconURL?: string;
|
||||
url?: string;
|
||||
};
|
||||
footer?: {
|
||||
text: string;
|
||||
iconURL?: string;
|
||||
proxyIconURL?: string;
|
||||
};
|
||||
fields?: Array<{
|
||||
name: string;
|
||||
value: string;
|
||||
inline?: boolean;
|
||||
}>;
|
||||
thumbnail?: {
|
||||
url: string;
|
||||
proxyURL?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
video?: {
|
||||
url?: string;
|
||||
proxyURL?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
image?: {
|
||||
url: string;
|
||||
proxyURL?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
provider?: {
|
||||
url?: string;
|
||||
name?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function embed(data: Embed): DiscordEmbed {
|
||||
return {
|
||||
title: data.title,
|
||||
timestamp: data.timestamp,
|
||||
type: data.type,
|
||||
url: data.url,
|
||||
color: data.color,
|
||||
description: data.description,
|
||||
author: {
|
||||
name: data.author?.name!,
|
||||
url: data.author?.url,
|
||||
icon_url: data.author?.iconURL,
|
||||
proxy_icon_url: data.author?.proxyIconURL,
|
||||
},
|
||||
footer: data.footer || {
|
||||
text: data.footer!.text,
|
||||
icon_url: data.footer!.iconURL,
|
||||
proxy_icon_url: data.footer!.proxyIconURL,
|
||||
},
|
||||
fields: data.fields?.map((f) => {
|
||||
return {
|
||||
name: f.name,
|
||||
value: f.value,
|
||||
inline: f.inline,
|
||||
};
|
||||
}),
|
||||
thumbnail: data.thumbnail || {
|
||||
url: data.thumbnail!.url,
|
||||
proxy_url: data.thumbnail!.proxyURL,
|
||||
width: data.thumbnail!.width,
|
||||
height: data.thumbnail!.height,
|
||||
},
|
||||
video: {
|
||||
url: data.video?.url,
|
||||
proxy_url: data.video?.proxyURL,
|
||||
width: data.video?.width,
|
||||
height: data.video?.height,
|
||||
},
|
||||
image: data.image || {
|
||||
url: data.image!.url,
|
||||
proxy_url: data.image!.proxyURL,
|
||||
width: data.image!.width,
|
||||
height: data.image!.height,
|
||||
},
|
||||
provider: {
|
||||
url: data.provider?.url,
|
||||
name: data.provider?.name,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default Embed;
|
@ -1,7 +1,13 @@
|
||||
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, DiscordRole } from "../vendor/external.ts";
|
||||
import type {
|
||||
DiscordEmoji,
|
||||
DiscordGuild,
|
||||
DiscordInviteMetadata,
|
||||
DiscordMemberWithUser,
|
||||
DiscordRole,
|
||||
} from "../vendor/external.ts";
|
||||
import type { GetInvite } from "../util/Routes.ts";
|
||||
import {
|
||||
DefaultMessageNotificationLevels,
|
||||
@ -46,6 +52,40 @@ export interface ModifyGuildEmoji {
|
||||
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
|
||||
@ -62,7 +102,8 @@ export class Guild extends BaseGuild implements Model {
|
||||
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! })) ?? [];
|
||||
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));
|
||||
}
|
||||
@ -79,6 +120,20 @@ export class Guild extends BaseGuild implements Model {
|
||||
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);
|
||||
@ -162,6 +217,40 @@ export class Guild extends BaseGuild implements Model {
|
||||
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,
|
||||
@ -190,6 +279,91 @@ export class Guild extends BaseGuild implements Model {
|
||||
|
||||
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;
|
||||
|
@ -2,11 +2,11 @@ import type { Model } from "./Base.ts";
|
||||
import type { Snowflake } from "../util/Snowflake.ts";
|
||||
import type { Session } from "../session/Session.ts";
|
||||
import type { DiscordChannel, DiscordInviteMetadata } from "../vendor/external.ts";
|
||||
import Channel from "./Channel.ts";
|
||||
import BaseChannel from "./BaseChannel.ts";
|
||||
import Invite from "./Invite.ts";
|
||||
import * as Routes from "../util/Routes.ts";
|
||||
|
||||
export abstract class GuildChannel extends Channel implements Model {
|
||||
export abstract class GuildChannel extends BaseChannel implements Model {
|
||||
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
|
||||
super(session, data);
|
||||
this.guildId = guildId;
|
||||
|
143
structures/Interaction.ts
Normal file
143
structures/Interaction.ts
Normal file
@ -0,0 +1,143 @@
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
@ -1,31 +1,24 @@
|
||||
import type { Model } from "./Base.ts";
|
||||
import type { Snowflake } from "../util/Snowflake.ts";
|
||||
import type { Session } from "../session/Session.ts";
|
||||
import type { DiscordMember, MakeRequired } from "../vendor/external.ts";
|
||||
import type { DiscordMemberWithUser } from "../vendor/external.ts";
|
||||
import type { ImageFormat, ImageSize } from "../util/shared/images.ts";
|
||||
import type { CreateGuildBan, ModifyGuildMember } from "./Guild.ts";
|
||||
import { iconBigintToHash, iconHashToBigInt } from "../util/hash.ts";
|
||||
import User from "./User.ts";
|
||||
import Guild from "./Guild.ts";
|
||||
import * as Routes from "../util/Routes.ts";
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/guild#create-guild-ban
|
||||
*/
|
||||
export interface CreateGuildBan {
|
||||
/** Number of days to delete messages for (0-7) */
|
||||
deleteMessageDays?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
/** Reason for the ban */
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a guild member
|
||||
* TODO: add a `guild` property somehow
|
||||
* @link https://discord.com/developers/docs/resources/guild#guild-member-object
|
||||
*/
|
||||
export class Member implements Model {
|
||||
constructor(session: Session, data: MakeRequired<DiscordMember, "user">) {
|
||||
constructor(session: Session, data: DiscordMemberWithUser, guildId: Snowflake) {
|
||||
this.session = session;
|
||||
this.user = new User(session, data.user);
|
||||
this.guildId = guildId;
|
||||
this.avatarHash = data.avatar ? iconHashToBigInt(data.avatar) : undefined;
|
||||
this.nickname = data.nick ? data.nick : undefined;
|
||||
this.joinedTimestamp = Number.parseInt(data.joined_at);
|
||||
@ -39,8 +32,8 @@ export class Member implements Model {
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
|
||||
user: User;
|
||||
guildId: Snowflake;
|
||||
avatarHash?: bigint;
|
||||
nickname?: string;
|
||||
joinedTimestamp: number;
|
||||
@ -63,39 +56,40 @@ export class Member implements Model {
|
||||
return new Date(this.joinedTimestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bans the member
|
||||
*/
|
||||
async ban(guildId: Snowflake, options: CreateGuildBan): Promise<Member> {
|
||||
await this.session.rest.runMethod<undefined>(
|
||||
this.session.rest,
|
||||
"PUT",
|
||||
Routes.GUILD_BAN(guildId, this.id),
|
||||
options
|
||||
? {
|
||||
delete_message_days: options.deleteMessageDays,
|
||||
reason: options.reason,
|
||||
}
|
||||
: {},
|
||||
);
|
||||
async ban(options: CreateGuildBan): Promise<Member> {
|
||||
await Guild.prototype.banMember.call({ id: this.guildId, session: this.session }, this.user.id, options);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks the member
|
||||
*/
|
||||
async kick(guildId: Snowflake, { reason }: { reason?: string }): Promise<Member> {
|
||||
await this.session.rest.runMethod<undefined>(
|
||||
this.session.rest,
|
||||
"DELETE",
|
||||
Routes.GUILD_MEMBER(guildId, this.id),
|
||||
{ reason },
|
||||
);
|
||||
async kick(options: { reason?: string }): Promise<Member> {
|
||||
await Guild.prototype.kickMember.call({ id: this.guildId, session: this.session }, this.user.id, options);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
async unban() {
|
||||
await Guild.prototype.unbanMember.call({ id: this.guildId, session: this.session }, this.user.id);
|
||||
}
|
||||
|
||||
async edit(options: ModifyGuildMember): Promise<Member> {
|
||||
const member = await Guild.prototype.editMember.call(
|
||||
{ id: this.guildId, session: this.session },
|
||||
this.user.id,
|
||||
options,
|
||||
);
|
||||
|
||||
return member;
|
||||
}
|
||||
|
||||
async addRole(roleId: Snowflake, options: { reason?: string } = {}) {
|
||||
await Guild.prototype.addRole.call({ id: this.guildId, session: this.session }, this.user.id, roleId, options);
|
||||
}
|
||||
|
||||
async removeRole(roleId: Snowflake, options: { reason?: string } = {}) {
|
||||
await Guild.prototype.removeRole.call({ id: this.guildId, session: this.session }, this.user.id, roleId, options);
|
||||
}
|
||||
|
||||
/** gets the user's avatar */
|
||||
avatarUrl(options: { format?: ImageFormat; size?: ImageSize } = { size: 128 }) {
|
||||
let url: string;
|
||||
@ -103,7 +97,7 @@ export class Member implements Model {
|
||||
if (!this.avatarHash) {
|
||||
url = Routes.USER_DEFAULT_AVATAR(Number(this.user.discriminator) % 5);
|
||||
} else {
|
||||
url = Routes.USER_AVATAR(this.id, iconBigintToHash(this.avatarHash));
|
||||
url = Routes.USER_AVATAR(this.user.id, iconBigintToHash(this.avatarHash));
|
||||
}
|
||||
|
||||
return `${url}.${options.format ?? (url.includes("/a_") ? "gif" : "jpg")}?size=${options.size}`;
|
||||
|
@ -1,7 +1,14 @@
|
||||
import type { Model } from "./Base.ts";
|
||||
import type { Snowflake } from "../util/Snowflake.ts";
|
||||
import type { Session } from "../session/Session.ts";
|
||||
import type { AllowedMentionsTypes, DiscordMessage, FileContent } from "../vendor/external.ts";
|
||||
import type {
|
||||
AllowedMentionsTypes,
|
||||
DiscordEmbed,
|
||||
DiscordMessage,
|
||||
DiscordUser,
|
||||
FileContent,
|
||||
} from "../vendor/external.ts";
|
||||
import type { GetReactions } from "../util/Routes.ts";
|
||||
import { MessageFlags } from "../util/shared/flags.ts";
|
||||
import User from "./User.ts";
|
||||
import Member from "./Member.ts";
|
||||
@ -33,6 +40,7 @@ export interface CreateMessage {
|
||||
allowedMentions?: AllowedMentions;
|
||||
files?: FileContent[];
|
||||
messageReference?: CreateMessageReference;
|
||||
embeds?: DiscordEmbed[];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,6 +50,11 @@ export interface EditMessage extends Partial<CreateMessage> {
|
||||
flags?: MessageFlags;
|
||||
}
|
||||
|
||||
export type ReactionResolvable = string | {
|
||||
name: string;
|
||||
id: Snowflake;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a message
|
||||
* @link https://discord.com/developers/docs/resources/channel#message-object
|
||||
@ -63,12 +76,10 @@ export class Message implements Model {
|
||||
this.attachments = data.attachments.map((attachment) => new Attachment(session, attachment));
|
||||
|
||||
// user is always null on MessageCreate and its replaced with author
|
||||
this.member = data.member
|
||||
? new Member(session, {
|
||||
...data.member,
|
||||
user: data.author,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
if (data.guild_id && data.member) {
|
||||
this.member = new Member(session, { ...data.member, user: data.author }, data.guild_id);
|
||||
}
|
||||
}
|
||||
|
||||
readonly session: Session;
|
||||
@ -89,21 +100,38 @@ export class Message implements Model {
|
||||
return `https://discord.com/channels/${this.guildId ?? "@me"}/${this.channelId}/${this.id}`;
|
||||
}
|
||||
|
||||
async pin() {
|
||||
await this.session.rest.runMethod<undefined>(
|
||||
this.session.rest,
|
||||
"PUT",
|
||||
Routes.CHANNEL_PIN(this.channelId, this.id),
|
||||
);
|
||||
}
|
||||
|
||||
async unpin() {
|
||||
await this.session.rest.runMethod<undefined>(
|
||||
this.session.rest,
|
||||
"DELETE",
|
||||
Routes.CHANNEL_PIN(this.channelId, this.id),
|
||||
);
|
||||
}
|
||||
|
||||
/** Edits the current message */
|
||||
async edit({ content, allowedMentions, flags }: EditMessage): Promise<Message> {
|
||||
async edit(options: EditMessage): Promise<Message> {
|
||||
const message = await this.session.rest.runMethod(
|
||||
this.session.rest,
|
||||
"POST",
|
||||
Routes.CHANNEL_MESSAGE(this.id, this.channelId),
|
||||
{
|
||||
content,
|
||||
content: options.content,
|
||||
allowed_mentions: {
|
||||
parse: allowedMentions?.parse,
|
||||
roles: allowedMentions?.roles,
|
||||
users: allowedMentions?.users,
|
||||
replied_user: allowedMentions?.repliedUser,
|
||||
parse: options.allowedMentions?.parse,
|
||||
roles: options.allowedMentions?.roles,
|
||||
users: options.allowedMentions?.users,
|
||||
replied_user: options.allowedMentions?.repliedUser,
|
||||
},
|
||||
flags,
|
||||
flags: options.flags,
|
||||
embeds: options.embeds,
|
||||
},
|
||||
);
|
||||
|
||||
@ -156,12 +184,98 @@ export class Message implements Model {
|
||||
fail_if_not_exists: options.messageReference.failIfNotExists ?? true,
|
||||
}
|
||||
: undefined,
|
||||
embeds: options.embeds,
|
||||
},
|
||||
);
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* alias for Message.addReaction
|
||||
*/
|
||||
get react() {
|
||||
return this.addReaction;
|
||||
}
|
||||
|
||||
async addReaction(reaction: ReactionResolvable) {
|
||||
const r = typeof reaction === "string" ? reaction : `${reaction.name}:${reaction.id}`;
|
||||
|
||||
await this.session.rest.runMethod<undefined>(
|
||||
this.session.rest,
|
||||
"PUT",
|
||||
Routes.CHANNEL_MESSAGE_REACTION_ME(this.channelId, this.id, r),
|
||||
{},
|
||||
);
|
||||
}
|
||||
|
||||
async removeReaction(reaction: ReactionResolvable, options?: { userId: Snowflake }) {
|
||||
const r = typeof reaction === "string" ? reaction : `${reaction.name}:${reaction.id}`;
|
||||
|
||||
await this.session.rest.runMethod<undefined>(
|
||||
this.session.rest,
|
||||
"DELETE",
|
||||
options?.userId
|
||||
? Routes.CHANNEL_MESSAGE_REACTION_USER(
|
||||
this.channelId,
|
||||
this.id,
|
||||
r,
|
||||
options.userId,
|
||||
)
|
||||
: Routes.CHANNEL_MESSAGE_REACTION_ME(this.channelId, this.id, r),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users who reacted with this emoji
|
||||
*/
|
||||
async fetchReactions(reaction: ReactionResolvable, options?: GetReactions): Promise<User[]> {
|
||||
const r = typeof reaction === "string" ? reaction : `${reaction.name}:${reaction.id}`;
|
||||
|
||||
const users = await this.session.rest.runMethod<DiscordUser[]>(
|
||||
this.session.rest,
|
||||
"GET",
|
||||
Routes.CHANNEL_MESSAGE_REACTION(this.channelId, this.id, encodeURIComponent(r), options),
|
||||
);
|
||||
|
||||
return users.map((user) => new User(this.session, user));
|
||||
}
|
||||
|
||||
async removeReactionEmoji(reaction: ReactionResolvable) {
|
||||
const r = typeof reaction === "string" ? reaction : `${reaction.name}:${reaction.id}`;
|
||||
|
||||
await this.session.rest.runMethod<undefined>(
|
||||
this.session.rest,
|
||||
"DELETE",
|
||||
Routes.CHANNEL_MESSAGE_REACTION(this.channelId, this.id, r),
|
||||
);
|
||||
}
|
||||
|
||||
async nukeReactions() {
|
||||
await this.session.rest.runMethod<undefined>(
|
||||
this.session.rest,
|
||||
"DELETE",
|
||||
Routes.CHANNEL_MESSAGE_REACTIONS(this.channelId, this.id),
|
||||
);
|
||||
}
|
||||
|
||||
async crosspost() {
|
||||
const message = await this.session.rest.runMethod<DiscordMessage>(
|
||||
this.session.rest,
|
||||
"POST",
|
||||
Routes.CHANNEL_MESSAGE_CROSSPOST(this.channelId, this.id),
|
||||
);
|
||||
|
||||
return new Message(this.session, message);
|
||||
}
|
||||
|
||||
/*
|
||||
* alias of Message.crosspost
|
||||
* */
|
||||
get publish() {
|
||||
return this.crosspost;
|
||||
}
|
||||
|
||||
inGuild(): this is { guildId: Snowflake } & Message {
|
||||
return !!this.guildId;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import type { Snowflake } from "../util/Snowflake.ts";
|
||||
import type { Session } from "../session/Session.ts";
|
||||
import type { DiscordChannel } from "../vendor/external.ts";
|
||||
import TextChannel from "./TextChannel.ts";
|
||||
import Message from "./Message.ts";
|
||||
|
||||
export class NewsChannel extends TextChannel {
|
||||
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
|
||||
@ -9,6 +10,14 @@ export class NewsChannel extends TextChannel {
|
||||
this.defaultAutoArchiveDuration = data.default_auto_archive_duration;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
export default NewsChannel;
|
||||
|
@ -48,7 +48,6 @@ export class Role implements Model {
|
||||
}
|
||||
|
||||
async delete(): Promise<void> {
|
||||
// cool jS trick
|
||||
await Guild.prototype.deleteRole.call({ id: this.guildId, session: this.session }, this.id);
|
||||
}
|
||||
|
||||
@ -57,6 +56,14 @@ export class Role implements Model {
|
||||
return role;
|
||||
}
|
||||
|
||||
async add(memberId: Snowflake, options: { reason?: string } = {}) {
|
||||
await Guild.prototype.addRole.call({ id: this.guildId, session: this.session }, memberId, this.id, options);
|
||||
}
|
||||
|
||||
async remove(memberId: Snowflake, options: { reason?: string } = {}) {
|
||||
await Guild.prototype.removeRole.call({ id: this.guildId, session: this.session }, memberId, this.id, options);
|
||||
}
|
||||
|
||||
toString() {
|
||||
switch (this.id) {
|
||||
case this.guildId:
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { Session } from "../session/Session.ts";
|
||||
import type { Snowflake } from "../util/Snowflake.ts";
|
||||
import type { GetMessagesOptions } from "../util/Routes.ts";
|
||||
import type { GetMessagesOptions, GetReactions } from "../util/Routes.ts";
|
||||
import type { DiscordChannel, DiscordInvite, DiscordMessage, TargetTypes } from "../vendor/external.ts";
|
||||
import type { CreateMessage, EditMessage, ReactionResolvable } from "./Message.ts";
|
||||
import GuildChannel from "./GuildChannel.ts";
|
||||
import Guild from "./Guild.ts";
|
||||
import ThreadChannel from "./ThreadChannel.ts";
|
||||
import Message from "./Message.ts";
|
||||
import Invite from "./Invite.ts";
|
||||
@ -37,7 +37,7 @@ export interface ThreadCreateOptions {
|
||||
}
|
||||
|
||||
export class TextChannel extends GuildChannel {
|
||||
constructor(session: Session, data: DiscordChannel, guildId: Guild["id"]) {
|
||||
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
|
||||
super(session, data, guildId);
|
||||
data.last_message_id ? this.lastMessageId = data.last_message_id : undefined;
|
||||
data.last_pin_timestamp ? this.lastPinTimestamp = data.last_pin_timestamp : undefined;
|
||||
@ -107,6 +107,58 @@ export class TextChannel extends GuildChannel {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
export default TextChannel;
|
||||
|
@ -1,16 +1,31 @@
|
||||
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, guildId: Snowflake, data: DiscordChannel) {
|
||||
constructor(session: Session, data: DiscordChannel, guildId: Snowflake) {
|
||||
super(session, data, guildId);
|
||||
this.bitRate = data.bitrate;
|
||||
this.userLimit = data.user_limit ?? 0;
|
||||
data.rtc_region ? this.rtcRegion = data.rtc_region : undefined;
|
||||
this.videoQuality = data.video_quality_mode;
|
||||
this.nsfw = !!data.nsfw;
|
||||
|
||||
if (data.rtc_region) {
|
||||
this.rtcRegion = data.rtc_region;
|
||||
}
|
||||
}
|
||||
bitRate?: number;
|
||||
userLimit: number;
|
||||
@ -18,6 +33,28 @@ export class VoiceChannel extends GuildChannel {
|
||||
|
||||
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;
|
||||
|
@ -46,10 +46,6 @@ export function MESSAGE_CREATE_THREAD(channelId: Snowflake, messageId: Snowflake
|
||||
return `/channels/${channelId}/messages/${messageId}/threads`;
|
||||
}
|
||||
|
||||
export function CHANNEL_PINS(channelId: Snowflake) {
|
||||
return `/channels/${channelId}/pins`;
|
||||
}
|
||||
|
||||
/** used to send messages */
|
||||
export function CHANNEL_MESSAGES(channelId: Snowflake, options?: GetMessagesOptions) {
|
||||
let url = `/channels/${channelId}/messages?`;
|
||||
@ -139,3 +135,92 @@ export function INVITE(inviteCode: string, options?: GetInvite) {
|
||||
export function GUILD_INVITES(guildId: Snowflake) {
|
||||
return `/guilds/${guildId}/invites`;
|
||||
}
|
||||
|
||||
export function INTERACTION_ID_TOKEN(interactionId: Snowflake, token: string) {
|
||||
return `/interactions/${interactionId}/${token}/callback`;
|
||||
}
|
||||
|
||||
export function WEBHOOK(webhookId: Snowflake, token: string, options?: { wait?: boolean; threadId?: Snowflake }) {
|
||||
let url = `/webhooks/${webhookId}/${token}?`;
|
||||
|
||||
if (options?.wait !== undefined) url += `wait=${options.wait}`;
|
||||
if (options?.threadId) url += `threadId=${options.threadId}`;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function USER_NICK(guildId: Snowflake) {
|
||||
return `/guilds/${guildId}/members/@me`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/guild#get-guild-prune-count
|
||||
*/
|
||||
export interface GetGuildPruneCountQuery {
|
||||
days?: number;
|
||||
includeRoles?: Snowflake | Snowflake[];
|
||||
}
|
||||
|
||||
export function GUILD_PRUNE(guildId: Snowflake, options?: GetGuildPruneCountQuery) {
|
||||
let url = `/guilds/${guildId}/prune?`;
|
||||
|
||||
if (options?.days) url += `days=${options.days}`;
|
||||
if (options?.includeRoles) url += `&include_roles=${options.includeRoles}`;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function CHANNEL_PIN(channelId: Snowflake, messageId: Snowflake) {
|
||||
return `/channels/${channelId}/pins/${messageId}`;
|
||||
}
|
||||
|
||||
export function CHANNEL_PINS(channelId: Snowflake) {
|
||||
return `/channels/${channelId}/pins`;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_REACTION_ME(channelId: Snowflake, messageId: Snowflake, emoji: string) {
|
||||
return `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}/@me`;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_REACTION_USER(
|
||||
channelId: Snowflake,
|
||||
messageId: Snowflake,
|
||||
emoji: string,
|
||||
userId: Snowflake,
|
||||
) {
|
||||
return `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}/${userId}`;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_REACTIONS(channelId: Snowflake, messageId: Snowflake) {
|
||||
return `/channels/${channelId}/messages/${messageId}/reactions`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @link https://discord.com/developers/docs/resources/channel#get-reactions-query-string-params
|
||||
*/
|
||||
export interface GetReactions {
|
||||
after?: string;
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_REACTION(
|
||||
channelId: Snowflake,
|
||||
messageId: Snowflake,
|
||||
emoji: string,
|
||||
options?: GetReactions,
|
||||
) {
|
||||
let url = `/channels/${channelId}/messages/${messageId}/reactions/${encodeURIComponent(emoji)}?`;
|
||||
|
||||
if (options?.after) url += `after=${options.after}`;
|
||||
if (options?.limit) url += `&limit=${options.limit}`;
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
export function CHANNEL_MESSAGE_CROSSPOST(channelId: Snowflake, messageId: Snowflake) {
|
||||
return `/channels/${channelId}/messages/${messageId}/crosspost`;
|
||||
}
|
||||
|
||||
export function GUILD_MEMBER_ROLE(guildId: Snowflake, memberId: Snowflake, roleId: Snowflake) {
|
||||
return `/guilds/${guildId}/members/${memberId}/roles/${roleId}`;
|
||||
}
|
||||
|
1
vendor/external.ts
vendored
1
vendor/external.ts
vendored
@ -2,3 +2,4 @@ export * from "./gateway/mod.ts";
|
||||
export * from "./rest/mod.ts";
|
||||
export * from "./types/mod.ts";
|
||||
export * from "./util/constants.ts";
|
||||
export * from "./util/token.ts";
|
||||
|
Loading…
x
Reference in New Issue
Block a user