minor changes

This commit is contained in:
Yuzu 2022-06-23 00:44:30 -05:00
parent 5083ce45a5
commit 8a5798e6a2
13 changed files with 260 additions and 101 deletions

View File

@ -1 +1 @@
export * from "./MessageRelated.ts";
export * from "./MessageRelated.ts";

View File

@ -2,17 +2,17 @@ import type { DiscordMessage, DiscordReady } from "../vendor/external.ts";
import type { Session } from "../session/Session.ts";
import { Message } from "../structures/Message.ts";
type Handler<T> = (...args: [ Session, number, T ]) => void;
type Handler<T> = (...args: [Session, number, T]) => void;
// TODO: move this lol
export const READY: Handler<DiscordReady> = (session, shardId, payload) => {
session.emit("ready", shardId, payload);
session.emit("ready", shardId, payload);
};
export const MESSAGE_CREATE: Handler<DiscordMessage> = (session, _shardId, message) => {
session.emit("messageCreate", new Message(session, message));
session.emit("messageCreate", new Message(session, message));
};
export const raw: Handler<unknown> = (session, shardId, data) => {
session.emit("raw", data, shardId);
}
session.emit("raw", data, shardId);
};

View File

@ -1,8 +1,4 @@
import type {
DiscordGetGatewayBot,
GatewayBot,
GatewayIntents,
} from "../vendor/external.ts";
import type { DiscordGetGatewayBot, GatewayBot, GatewayIntents } from "../vendor/external.ts";
import { EventEmitter, Routes, Snowflake } from "../util/mod.ts";

View File

@ -3,10 +3,10 @@ import type { Session } from "../session/mod.ts";
/**
* Represents a Discord data model
* */
export interface Base {
*/
export interface Model {
/** id of the model */
id: Snowflake;
id: Snowflake;
/** reference to the client that instantiated the model */
session: Session;
}
}

View File

@ -1,94 +1,136 @@
import type { Base } from "./Base.ts";
import type { Model } from "./Base.ts";
import type { Snowflake } from "../util/Snowflake.ts";
import type { Session } from "../session/mod.ts";
import type { DiscordMessage, AllowedMentionsTypes } from "../vendor/external.ts";
import { Routes } from "../util/mod.ts";
import type { AllowedMentionsTypes, DiscordMessage } from "../vendor/external.ts";
import { User } from "./User.ts";
import { MessageFlags, Routes } from "../util/mod.ts";
/**
/**
* @link https://discord.com/developers/docs/resources/channel#allowed-mentions-object
* */
*/
export interface AllowedMentions {
parse?: AllowedMentionsTypes[];
repliedUser?: boolean;
roles?: Snowflake[];
users?: Snowflake[];
parse?: AllowedMentionsTypes[];
repliedUser?: boolean;
roles?: Snowflake[];
users?: Snowflake[];
}
/**
/**
* @link https://discord.com/developers/docs/resources/channel#edit-message-json-params
* */
*/
export interface EditMessage {
content?: string;
allowedMentions?: AllowedMentions;
content?: string;
allowedMentions?: AllowedMentions;
flags?: MessageFlags;
}
/**
/**
* @link https://discord.com/developers/docs/resources/channel#create-message-json-params
* */
*/
export interface CreateMessage {
content?: string;
allowedMentions?: AllowedMentions;
content?: string;
allowedMentions?: AllowedMentions;
}
/**
* Represents a message
* @link https://discord.com/developers/docs/resources/channel#message-object
* */
export class Message implements Base {
constructor(session: Session, data: DiscordMessage) {
this.session = session;
*/
export class Message implements Model {
constructor(session: Session, data: DiscordMessage) {
this.session = session;
this.id = data.id;
this.id = data.id;
this.channelId = data.channel_id;
this.guildId = data.guild_id;
this.channelId = data.channel_id;
}
this.author = new User(session, data.author);
this.flags = data.flags;
this.pinned = !!data.pinned;
this.tts = !!data.tts;
this.content = data.content!;
}
/** the session that instantiated the message */
session: Session;
readonly session: Session;
readonly id: Snowflake;
/** the id of the message */
id: Snowflake;
channelId: Snowflake;
guildId?: Snowflake;
author: User;
flags?: MessageFlags;
pinned: boolean;
tts: boolean;
content: string;
/** the id of the channel where the message was sent */
channelId: Snowflake;
get url() {
return `https://discord.com/channels/${this.guildId ?? "@me"}/${this.channelId}/${this.id}`;
}
/** Edits the current message */
async edit({ content, allowedMentions }: EditMessage): Promise<Message> {
const message = await this.session.rest.runMethod(
this.session.rest,
"POST",
Routes.CHANNEL_MESSAGE(this.id, this.channelId),
{
content,
allowed_mentions: {
parse: allowedMentions?.parse,
roles: allowedMentions?.roles,
users: allowedMentions?.users,
replied_user: allowedMentions?.repliedUser,
},
}
);
/** Edits the current message */
async edit({ content, allowedMentions, flags }: EditMessage): Promise<Message> {
const message = await this.session.rest.runMethod(
this.session.rest,
"POST",
Routes.CHANNEL_MESSAGE(this.id, this.channelId),
{
content,
allowed_mentions: {
parse: allowedMentions?.parse,
roles: allowedMentions?.roles,
users: allowedMentions?.users,
replied_user: allowedMentions?.repliedUser,
},
flags,
},
);
return message;
}
return message;
}
/** Responds directly in the channel the message was sent */
async respond({ content, allowedMentions }: CreateMessage): Promise<Message> {
const message = await this.session.rest.runMethod(
this.session.rest,
"POST",
Routes.CHANNEL_MESSAGES(this.channelId),
{
content,
allowed_mentions: {
parse: allowedMentions?.parse,
roles: allowedMentions?.roles,
users: allowedMentions?.users,
replied_user: allowedMentions?.repliedUser,
},
}
);
async suppressEmbeds(suppress: true): Promise<Message>;
async suppressEmbeds(suppress: false): Promise<Message | undefined>;
async suppressEmbeds(suppress = true) {
if (this.flags === MessageFlags.SUPPRESS_EMBEDS && suppress === false) {
return;
}
return message;
}
}
const message = await this.edit({ flags: MessageFlags.SUPPRESS_EMBEDS });
return message;
}
async delete({ reason }: { reason: string }): Promise<Message> {
await this.session.rest.runMethod<undefined>(
this.session.rest,
"DELETE",
Routes.CHANNEL_MESSAGE(this.channelId, this.id),
{ reason },
);
return this;
}
/** Responds directly in the channel the message was sent */
async respond({ content, allowedMentions }: CreateMessage): Promise<Message> {
const message = await this.session.rest.runMethod(
this.session.rest,
"POST",
Routes.CHANNEL_MESSAGES(this.channelId),
{
content,
allowed_mentions: {
parse: allowedMentions?.parse,
roles: allowedMentions?.roles,
users: allowedMentions?.users,
replied_user: allowedMentions?.repliedUser,
},
},
);
return message;
}
inGuild(): this is { guildId: string } & Message {
return Boolean(this.guildId);
}
}

64
structures/User.ts Normal file
View File

@ -0,0 +1,64 @@
import type { Model } from "./Base.ts";
import type { Snowflake } from "../util/Snowflake.ts";
import type { Session } from "../session/mod.ts";
import type { DiscordUser } from "../vendor/external.ts";
import { iconBigintToHash, iconHashToBigInt } from "../util/hash.ts";
import { Routes } from "../util/mod.ts";
/**
* @link https://discord.com/developers/docs/reference#image-formatting
*/
export type ImageFormat = "jpg" | "jpeg" | "png" | "webp" | "gif" | "json";
/**
* @link https://discord.com/developers/docs/reference#image-formatting
*/
export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096;
/**
* Represents a user
* @link https://discord.com/developers/docs/resources/user#user-object
*/
export class User implements Model {
constructor(session: Session, data: DiscordUser) {
this.session = session;
this.id = data.id;
this.username = data.username;
this.discriminator = data.discriminator;
this.avatarHash = data.avatar ? iconHashToBigInt(data.avatar) : undefined;
this.accentColor = data.accent_color;
this.bot = !!data.bot;
this.system = !!data.system;
this.banner = data.banner;
}
readonly session: Session;
readonly id: Snowflake;
username: string;
discriminator: string;
avatarHash?: bigint;
accentColor?: number;
bot: boolean;
system: boolean;
banner?: string;
/** gets the user's username#discriminator */
get tag() {
return `${this.username}#${this.discriminator}}`;
}
/** gets the user's avatar */
avatarUrl(options: { format?: ImageFormat; size?: ImageSize } = { size: 128 }) {
let url: string;
if (!this.avatarHash) {
url = Routes.USER_DEFAULT_AVATAR(Number(this.discriminator) % 5);
} else {
url = Routes.USER_AVATAR(this.id, iconBigintToHash(this.avatarHash));
}
return `${url}.${options.format ?? (url.includes("/a_") ? "gif" : "jpg")}?size=${options.size}`;
}
}

View File

@ -1,19 +1,20 @@
import * as Discord from "./deps.ts";
import { GatewayIntents, Session } from "./deps.ts";
if (!Deno.args[0]) {
throw new Error("Please provide a token");
}
const session = new Discord.Session({
token: Deno.args[0],
intents: Discord.GatewayIntents.MessageContent | Discord.GatewayIntents.Guilds |
Discord.GatewayIntents.GuildMessages,
const intents = GatewayIntents.MessageContent | GatewayIntents.Guilds | GatewayIntents.GuildMessages;
const session = new Session({ token: Deno.args[0], intents });
session.on("ready", (_shardId, payload) => {
console.log("Logged in as:", payload.user.username);
});
session.on("ready", (payload) => console.log(payload));
session.on("messageCreate", (payload) => console.log(payload));
// session.on("raw", (data, shardId) => console.log(shardId, data));
session.on("messageCreate", (message) => {
if (message.content === "!ping") {
message.respond({ content: "pong!" });
}
});
console.log("hello");
session.start();
await session.start();

13
util/Cdn.ts Normal file
View File

@ -0,0 +1,13 @@
import type { Snowflake } from "./Snowflake.ts";
import { baseEndpoints as Endpoints } from "../vendor/external.ts";
export function USER_AVATAR(userId: Snowflake, icon: string) {
return `${Endpoints.CDN_URL}/avatars/${userId}/${icon}`;
}
export function USER_DEFAULT_AVATAR(
/** user discriminator */
altIcon: number,
) {
return `${Endpoints.CDN_URL}/embed/avatars/${altIcon}.png`;
}

View File

@ -1,26 +1,29 @@
import type { Snowflake } from "./Snowflake.ts";
// cdn endpoints
export * from "./Cdn.ts";
export function GATEWAY_BOT() {
return "/gateway/bot";
}
export interface GetMessagesOptions {
limit?: number;
limit?: number;
}
export interface GetMessagesOptions {
around?: Snowflake;
limit?: number;
around?: Snowflake;
limit?: number;
}
export interface GetMessagesOptions {
before?: Snowflake;
limit?: number;
before?: Snowflake;
limit?: number;
}
export interface GetMessagesOptions {
after?: Snowflake;
limit?: number;
after?: Snowflake;
limit?: number;
}
/** used to send messages */
@ -40,4 +43,4 @@ export function CHANNEL_MESSAGES(channelId: Snowflake, options?: GetMessagesOpti
/** used to edit messages */
export function CHANNEL_MESSAGE(channelId: Snowflake, messageId: Snowflake) {
return `/channels/${channelId}/messages/${messageId}`;
}
}

14
util/hash.ts Normal file
View File

@ -0,0 +1,14 @@
/**
* Memory optimizations
* All credits to the Discordeno authors
*/
export function iconHashToBigInt(hash: string) {
return BigInt("0x" + hash.startsWith("a_") ? `a${hash.substring(2)}` : `b${hash}`);
}
export function iconBigintToHash(icon: bigint) {
const hash = icon.toString(16);
return hash.startsWith("a") ? `a_${hash.substring(1)}` : hash.substring(1);
}

View File

@ -1,3 +1,5 @@
export * from "./EventEmmiter.ts";
export * from "./Snowflake.ts";
export * from "./hash.ts";
export * from "./shared/flags.ts";
export * as Routes from "./Routes.ts";

23
util/shared/flags.ts Normal file
View File

@ -0,0 +1,23 @@
/**
* @link https://discord.com/developers/docs/resources/channel#message-object-message-flags
*/
export enum MessageFlags {
/** this message has been published to subscribed channels (via Channel Following) */
CROSSPOSTED = 1 << 0,
/** this message originated from a message in another channel (via Channel Following) */
IS_CROSSPOST = 1 << 1,
/** do not include any embeds when serializing this message */
SUPPRESS_EMBEDS = 1 << 2,
/** the source message for this crosspost has been deleted (via Channel Following) */
SOURCE_MESSAGE_DELETED = 1 << 3,
/** this message came from the urgent message system */
URGENT = 1 << 4,
/** this message has an associated thread, with the same id as the message */
HAS_THREAD = 1 << 5,
/** this message is only visible to the user who invoked the Interaction */
EPHEMERAL = 1 << 6,
/** this message is an Interaction Response and the bot is "thinking" */
LOADING = 1 << 7,
/** this message failed to mention some roles and add their members to the thread */
FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8,
}

1
vendor/external.ts vendored
View File

@ -1,3 +1,4 @@
export * from "./gateway/mod.ts";
export * from "./rest/mod.ts";
export * from "./types/mod.ts";
export * from "./util/constants.ts";