merge: remote-tracking branch 'origin/main' into explicit-returns-and-imports

This commit is contained in:
Paul Makles
2025-03-23 18:52:41 +00:00
39 changed files with 1535 additions and 2381 deletions
-49
View File
@@ -1,49 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": ["solid", "spellcheck"],
"extends": [
"eslint:recommended",
"plugin:solid/typescript",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"@typescript-eslint/no-non-null-assertion": "off",
"spellcheck/spell-checker": [
"warn",
{
"lang": "en_GB",
"strings": false,
"identifiers": false,
"templates": false,
"skipWords": ["uri", "webhook", "webhooks", "unreads"],
"minLength": 3
}
],
"@typescript-eslint/no-unused-vars": [
"warn",
{
"varsIgnorePattern": "^_"
}
],
"no-unused-vars": [
"warn",
{
"varsIgnorePattern": "^_"
}
],
"require-jsdoc": [
"warn",
{
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": true,
"ArrowFunctionExpression": true,
"FunctionExpression": true
}
}
]
}
}
+23
View File
@@ -0,0 +1,23 @@
import eslint from "@eslint/js";
import prettier from "eslint-plugin-prettier/recommended";
import solid from "eslint-plugin-solid/configs/typescript";
import { defineConfig } from "eslint/config";
import tseslint from "typescript-eslint";
export default defineConfig([
eslint.configs.recommended,
tseslint.configs.recommended,
solid,
{
rules: {
"@typescript-eslint/no-unused-vars": [
"warn",
{
caughtErrors: "all",
varsIgnorePattern: "^_",
},
],
},
},
prettier,
]);
-3
View File
@@ -1,3 +0,0 @@
{
"type": "module"
}
+19 -30
View File
@@ -1,16 +1,14 @@
{
"name": "revolt.js",
"version": "7.0.5",
"main": "lib/cjs/index.js",
"module": "lib/esm/index.js",
"types": "lib/esm/index.d.ts",
"type": "module",
"module": "lib/index.js",
"types": "lib/index.d.ts",
"repository": "https://github.com/revoltchat/revolt.js",
"author": "Paul Makles <insrt.uk>",
"license": "MIT",
"scripts": {
"build": "pnpm run build:esm && pnpm run build:cjs",
"build:esm": "tsc && cp package-esm.json lib/esm/package.json",
"build:cjs": "tsc -p tsconfig.cjs.json",
"build": "tsc",
"build:watch": "tsc-watch --onSuccess \"node .\"",
"lint": "eslint --ext .ts,.tsx src/",
"lint:fix": "eslint --fix --ext .ts,.tsx src/",
@@ -26,32 +24,23 @@
"description": "Library for interacting with the Revolt API.",
"packageManager": "pnpm@9.4.0",
"dependencies": {
"@solid-primitives/map": "^0.4.11",
"@solid-primitives/set": "^0.4.11",
"eventemitter3": "^5.0.1",
"isomorphic-ws": "^5.0.0",
"long": "^5.2.3",
"@solid-primitives/map": "^0.6.0",
"@solid-primitives/set": "^0.6.0",
"@vladfrangu/async_event_emitter": "^2.4.6",
"revolt-api": "0.8.3",
"solid-js": "^1.8.15",
"ulid": "^2.3.0",
"ws": "^8.16.0"
"ulid": "^2.3.0"
},
"devDependencies": {
"@mxssfd/typedoc-theme": "^1.1.3",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/node": "^18.19.24",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.62.0",
"dotenv": "^16.4.5",
"eslint": "^8.57.0",
"eslint-config-prettier": "^8.10.0",
"eslint-plugin-solid": "^0.12.1",
"eslint-plugin-spellcheck": "^0.0.20",
"prettier": "^2.8.8",
"tsc-watch": "^6.0.4",
"typed-emitter": "^2.1.0",
"typedoc": "^0.25.1",
"typescript": "^5.4.2"
"@mxssfd/typedoc-theme": "^1.1.7",
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
"@types/node": "^22.13.10",
"eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-solid": "^0.14.5",
"eslint": "^9.22.0",
"prettier": "^3.5.3",
"typedoc": "^0.27.9",
"typescript-eslint": "^8.26.1",
"typescript": "^5.8.2"
}
}
}
+1196 -1991
View File
File diff suppressed because it is too large Load Diff
+47 -50
View File
@@ -1,9 +1,9 @@
import type { Accessor, Setter } from "solid-js";
import { batch, createSignal } from "solid-js";
import EventEmitter from "eventemitter3";
import type { DataLogin, RevoltConfig, Role } from "revolt-api";
import { AsyncEventEmitter } from "@vladfrangu/async_event_emitter";
import { API } from "revolt-api";
import type { DataLogin, RevoltConfig, Role } from "revolt-api";
import type { Channel } from "./classes/Channel.js";
import type { Emoji } from "./classes/Emoji.js";
@@ -43,55 +43,52 @@ export type Session = { _id: string; token: string; user_id: string } | string;
*/
export type Events = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error(error: any): void;
error: [error: any];
connected(): void;
connecting(): void;
disconnected(): void;
ready(): void;
logout(): void;
connected: [];
connecting: [];
disconnected: [];
ready: [];
logout: [];
messageCreate(message: Message): void;
messageUpdate(message: Message, previousMessage: HydratedMessage): void;
messageDelete(message: HydratedMessage): void;
messageDeleteBulk(messages: HydratedMessage[], channel?: Channel): void;
messageReactionAdd(message: Message, userId: string, emoji: string): void;
messageReactionRemove(message: Message, userId: string, emoji: string): void;
messageReactionRemoveEmoji(message: Message, emoji: string): void;
messageCreate: [message: Message];
messageUpdate: [message: Message, previousMessage: HydratedMessage];
messageDelete: [message: HydratedMessage];
messageDeleteBulk: [messages: HydratedMessage[], channel?: Channel];
messageReactionAdd: [message: Message, userId: string, emoji: string];
messageReactionRemove: [message: Message, userId: string, emoji: string];
messageReactionRemoveEmoji: [message: Message, emoji: string];
channelCreate(channel: Channel): void;
channelUpdate(channel: Channel, previousChannel: HydratedChannel): void;
channelDelete(channel: HydratedChannel): void;
channelGroupJoin(channel: Channel, user: User): void;
channelGroupLeave(channel: Channel, user?: User): void;
channelStartTyping(channel: Channel, user?: User): void;
channelStopTyping(channel: Channel, user?: User): void;
channelAcknowledged(channel: Channel, messageId: string): void;
channelCreate: [channel: Channel];
channelUpdate: [channel: Channel, previousChannel: HydratedChannel];
channelDelete: [channel: HydratedChannel];
channelGroupJoin: [channel: Channel, user: User];
channelGroupLeave: [channel: Channel, user?: User];
channelStartTyping: [channel: Channel, user?: User];
channelStopTyping: [channel: Channel, user?: User];
channelAcknowledged: [channel: Channel, messageId: string];
serverCreate(server: Server): void;
serverUpdate(server: Server, previousServer: HydratedServer): void;
serverDelete(server: HydratedServer): void;
serverLeave(server: HydratedServer): void;
serverRoleUpdate(server: Server, roleId: string, previousRole: Role): void;
serverRoleDelete(server: Server, roleId: string, role: Role): void;
serverCreate: [server: Server];
serverUpdate: [server: Server, previousServer: HydratedServer];
serverDelete: [server: HydratedServer];
serverLeave: [server: HydratedServer];
serverRoleUpdate: [server: Server, roleId: string, previousRole: Role];
serverRoleDelete: [server: Server, roleId: string, role: Role];
serverMemberUpdate(
serverMemberUpdate: [
member: ServerMember,
previousMember: HydratedServerMember
): void;
serverMemberJoin(member: ServerMember): void;
serverMemberLeave(member: HydratedServerMember): void;
previousMember: HydratedServerMember,
];
serverMemberJoin: [member: ServerMember];
serverMemberLeave: [member: HydratedServerMember];
userUpdate(user: User, previousUser: HydratedUser): void;
// ^ userRelationshipChanged(user: User, previousRelationship: RelationshipStatus): void;
// ^ userPresenceChanged(user: User, previousPresence: boolean): void;
userSettingsUpdate(
id: string,
update: Record<string, [number, string]>
): void;
userUpdate: [user: User, previousUser: HydratedUser];
// ^ userRelationshipChanged: [user: User, previousRelationship: RelationshipStatus];
// ^ userPresenceChanged: [user: User, previousPresence: boolean];
userSettingsUpdate: [id: string, update: Record<string, [number, string]>];
emojiCreate(emoji: Emoji): void;
emojiDelete(emoji: HydratedEmoji): void;
emojiCreate: [emoji: Emoji];
emojiDelete: [emoji: HydratedEmoji];
};
/**
@@ -154,7 +151,7 @@ export type ClientOptions = Partial<EventClientOptions> & {
/**
* Revolt.js Clients
*/
export class Client extends EventEmitter<Events> {
export class Client extends AsyncEventEmitter<Events> {
readonly account;
readonly bots;
readonly channels;
@@ -260,7 +257,7 @@ export class Client extends EventEmitter<Events> {
this.#reconnectTimeout = setTimeout(
() => this.connect(),
this.options.retryDelayFunction(this.connectionFailureCount()) *
1e3
1e3,
) as never;
this.#setConnectionFailureCount((count) => count + 1);
@@ -270,7 +267,7 @@ export class Client extends EventEmitter<Events> {
});
this.events.on("event", (event) =>
handleEvent(this, event, this.#setReady)
handleEvent(this, event, this.#setReady),
);
}
@@ -299,7 +296,7 @@ export class Client extends EventEmitter<Events> {
this.#setReady(false);
this.events.connect(
this.configuration?.ws ?? "wss://ws.revolt.chat",
typeof this.#session === "string" ? this.#session : this.#session!.token
typeof this.#session === "string" ? this.#session : this.#session!.token,
);
}
@@ -394,9 +391,9 @@ export class Client extends EventEmitter<Events> {
*/
proxyFile(url: string): string | undefined {
if (this.configuration?.features.january.enabled) {
return `${
this.configuration.features.january.url
}/proxy?url=${encodeURIComponent(url)}`;
return `${this.configuration.features.january.url}/proxy?url=${encodeURIComponent(
url,
)}`;
} else {
return url;
}
+62 -50
View File
@@ -15,7 +15,8 @@ import type {
import type { APIRoutes } from "revolt-api/dist/routes";
import { decodeTime, ulid } from "ulid";
import type { ChannelCollection } from "../collections/ChannelCollection.js";
import { ChannelCollection } from "../collections/index.js";
import { hydrate } from "../hydration/index.js";
import {
bitwiseAndEq,
calculatePermission,
@@ -107,8 +108,8 @@ export class Channel {
return this.type === "SavedMessages"
? this.user?.username
: this.type === "DirectMessage"
? this.recipient?.username
: this.name;
? this.recipient?.username
: this.name;
}
/**
@@ -144,7 +145,7 @@ export class Channel {
*/
get typing(): User[] {
return [...this.typingIds.values()].map(
(id) => this.#collection.client.users.get(id)!
(id) => this.#collection.client.users.get(id)!,
);
}
@@ -170,7 +171,7 @@ export class Channel {
get recipient(): User | undefined {
return this.type === "DirectMessage"
? this.recipients?.find(
(user) => user?.id !== this.#collection.client.user!.id
(user) => user?.id !== this.#collection.client.user!.id,
)
: undefined;
}
@@ -187,7 +188,7 @@ export class Channel {
*/
get user(): User | undefined {
return this.#collection.client.users.get(
this.#collection.getUnderlyingObject(this.id).userId!
this.#collection.getUnderlyingObject(this.id).userId!,
);
}
@@ -203,7 +204,7 @@ export class Channel {
*/
get owner(): User | undefined {
return this.#collection.client.users.get(
this.#collection.getUnderlyingObject(this.id).ownerId!
this.#collection.getUnderlyingObject(this.id).ownerId!,
);
}
@@ -219,7 +220,7 @@ export class Channel {
*/
get server(): Server | undefined {
return this.#collection.client.servers.get(
this.#collection.getUnderlyingObject(this.id).serverId!
this.#collection.getUnderlyingObject(this.id).serverId!,
);
}
@@ -338,12 +339,12 @@ export class Channel {
(role) =>
bitwiseAndEq(
this.rolePermissions?.[role]?.d ?? 0,
Permission.ViewChannel
Permission.ViewChannel,
) ||
bitwiseAndEq(
this.server?.roles.get(role)?.permissions.d ?? 0,
Permission.ViewChannel
)
Permission.ViewChannel,
),
)
);
}
@@ -363,7 +364,7 @@ export class Channel {
havePermission(...permission: (keyof typeof Permission)[]): boolean {
return bitwiseAndEq(
this.permission,
...permission.map((x) => Permission[x])
...permission.map((x) => Permission[x]),
);
}
@@ -375,7 +376,7 @@ export class Channel {
orPermission(...permission: (keyof typeof Permission)[]): boolean {
return (
permission.findIndex((x) =>
bitwiseAndEq(this.permission, Permission[x])
bitwiseAndEq(this.permission, Permission[x]),
) !== -1
);
}
@@ -387,13 +388,13 @@ export class Channel {
*/
async fetchMembers(): Promise<User[]> {
const members = await this.#collection.client.api.get(
`/channels/${this.id as ""}/members`
`/channels/${this.id as ""}/members`,
);
return batch(() =>
members.map((user) =>
this.#collection.client.users.getOrCreate(user._id, user)
)
this.#collection.client.users.getOrCreate(user._id, user),
),
);
}
@@ -404,13 +405,16 @@ export class Channel {
*/
async fetchWebhooks(): Promise<ChannelWebhook[]> {
const webhooks = await this.#collection.client.api.get(
`/channels/${this.id as ""}/webhooks`
`/channels/${this.id as ""}/webhooks`,
);
return batch(() =>
webhooks.map((webhook) =>
this.#collection.client.channelWebhooks.getOrCreate(webhook.id, webhook)
)
this.#collection.client.channelWebhooks.getOrCreate(
webhook.id,
webhook,
),
),
);
}
@@ -418,8 +422,16 @@ export class Channel {
* Edit a channel
* @param data Changes
*/
async edit(data: DataEditChannel): Promise<void> {
await this.#collection.client.api.patch(`/channels/${this.id as ""}`, data);
async edit(data: DataEditChannel) {
const channel = await this.#collection.client.api.patch(
`/channels/${this.id as ""}`,
data,
);
this.#collection.updateUnderlyingObject(
this.id,
hydrate("channel", channel, this.#collection.client, false),
);
}
/**
@@ -447,7 +459,7 @@ export class Channel {
*/
async addMember(user_id: string): Promise<void> {
return await this.#collection.client.api.put(
`/channels/${this.id as ""}/recipients/${user_id as ""}`
`/channels/${this.id as ""}/recipients/${user_id as ""}`,
);
}
@@ -458,7 +470,7 @@ export class Channel {
*/
async removeMember(user_id: string): Promise<void> {
return await this.#collection.client.api.delete(
`/channels/${this.id as ""}/recipients/${user_id as ""}`
`/channels/${this.id as ""}/recipients/${user_id as ""}`,
);
}
@@ -470,7 +482,7 @@ export class Channel {
*/
async sendMessage(
data: string | DataMessageSend,
idempotencyKey: string = ulid()
idempotencyKey: string = ulid(),
): Promise<Message> {
const msg: DataMessageSend =
typeof data === "string" ? { content: data } : data;
@@ -489,13 +501,13 @@ export class Channel {
headers: {
"Idempotency-Key": idempotencyKey,
},
}
},
);
return this.#collection.client.messages.getOrCreate(
message._id,
message,
true
true,
);
}
@@ -507,7 +519,7 @@ export class Channel {
*/
async fetchMessage(messageId: string): Promise<Message> {
const message = await this.#collection.client.api.get(
`/channels/${this.id as ""}/messages/${messageId as ""}`
`/channels/${this.id as ""}/messages/${messageId as ""}`,
);
return this.#collection.client.messages.getOrCreate(message._id, message);
@@ -526,15 +538,15 @@ export class Channel {
path: "/channels/{target}/messages";
})["params"],
"include_users"
>
>,
): Promise<Message[]> {
const messages = (await this.#collection.client.api.get(
`/channels/${this.id as ""}/messages`,
{ ...params }
{ ...params },
)) as APIMessage[];
return messages.map((message) =>
this.#collection.client.messages.getOrCreate(message._id, message)
this.#collection.client.messages.getOrCreate(message._id, message),
);
}
@@ -551,7 +563,7 @@ export class Channel {
path: "/channels/{target}/messages";
})["params"],
"include_users"
>
>,
): Promise<{
messages: Message[];
users: User[];
@@ -559,18 +571,18 @@ export class Channel {
}> {
const data = (await this.#collection.client.api.get(
`/channels/${this.id as ""}/messages`,
{ ...params, include_users: true }
{ ...params, include_users: true },
)) as { messages: APIMessage[]; users: APIUser[]; members?: APIMember[] };
return batch(() => ({
messages: data.messages.map((message) =>
this.#collection.client.messages.getOrCreate(message._id, message)
this.#collection.client.messages.getOrCreate(message._id, message),
),
users: data.users.map((user) =>
this.#collection.client.users.getOrCreate(user._id, user)
this.#collection.client.users.getOrCreate(user._id, user),
),
members: data.members?.map((member) =>
this.#collection.client.serverMembers.getOrCreate(member._id, member)
this.#collection.client.serverMembers.getOrCreate(member._id, member),
),
}));
}
@@ -582,17 +594,17 @@ export class Channel {
* @returns Messages
*/
async search(
params: Omit<DataMessageSearch, "include_users">
params: Omit<DataMessageSearch, "include_users">,
): Promise<Message[]> {
const messages = (await this.#collection.client.api.post(
`/channels/${this.id as ""}/search`,
params
params,
)) as APIMessage[];
return batch(() =>
messages.map((message) =>
this.#collection.client.messages.getOrCreate(message._id, message)
)
this.#collection.client.messages.getOrCreate(message._id, message),
),
);
}
@@ -603,7 +615,7 @@ export class Channel {
* @returns Object including messages and users
*/
async searchWithUsers(
params: Omit<DataMessageSearch, "include_users">
params: Omit<DataMessageSearch, "include_users">,
): Promise<{
messages: Message[];
users: User[];
@@ -614,18 +626,18 @@ export class Channel {
{
...params,
include_users: true,
}
},
)) as { messages: APIMessage[]; users: APIUser[]; members?: APIMember[] };
return batch(() => ({
messages: data.messages.map((message) =>
this.#collection.client.messages.getOrCreate(message._id, message)
this.#collection.client.messages.getOrCreate(message._id, message),
),
users: data.users.map((user) =>
this.#collection.client.users.getOrCreate(user._id, user)
this.#collection.client.users.getOrCreate(user._id, user),
),
members: data.members?.map((member) =>
this.#collection.client.serverMembers.getOrCreate(member._id, member)
this.#collection.client.serverMembers.getOrCreate(member._id, member),
),
}));
}
@@ -640,7 +652,7 @@ export class Channel {
`/channels/${this.id as ""}/messages/bulk`,
{
ids,
}
},
);
}
@@ -651,7 +663,7 @@ export class Channel {
*/
async createInvite(): Promise<Invite> {
return await this.#collection.client.api.post(
`/channels/${this.id as ""}/invites`
`/channels/${this.id as ""}/invites`,
);
}
@@ -671,7 +683,7 @@ export class Channel {
message?: Message | string,
skipRateLimiter?: boolean,
skipRequest?: boolean,
skipNextMarking?: boolean
skipNextMarking?: boolean,
): Promise<void> {
if (!message && this.#manuallyMarked) {
this.#manuallyMarked = false;
@@ -708,7 +720,7 @@ export class Channel {
const performAck = (): void => {
this.#ackLimit = undefined;
this.#collection.client.api.put(
`/channels/${this.id}/ack/${lastMessageId as ""}`
`/channels/${this.id}/ack/${lastMessageId as ""}`,
);
};
@@ -735,11 +747,11 @@ export class Channel {
*/
async setPermissions(
role_id = "default",
permissions: Override
permissions: Override,
): Promise<APIChannel> {
return await this.#collection.client.api.put(
`/channels/${this.id as ""}/permissions/${role_id as ""}`,
{ permissions }
{ permissions },
);
}
+18 -5
View File
@@ -1,3 +1,5 @@
import * as APITmp from "revolt-api";
import type { ChannelWebhookCollection } from "../collections/ChannelWebhookCollection.js";
import { hydrate } from "../hydration/index.js";
@@ -63,7 +65,7 @@ export class ChannelWebhook {
*/
get channel(): Channel | undefined {
return this.#collection.client.channels.get(
this.#collection.getUnderlyingObject(this.id).channelId
this.#collection.getUnderlyingObject(this.id).channelId,
);
}
@@ -78,17 +80,28 @@ export class ChannelWebhook {
* Edit this webhook
* TODO: not in production
*/
async edit(data: any /*: DataEditWebhook*/): Promise<void> {
async edit(
data: Partial<{
name: string;
avatar: string;
permissions: number;
remove: ["Icon"];
}>,
): Promise<void> {
// @ts-expect-error this should error once edit webhook is stable
// eslint-disable-next-line
type TodoUseThisDefinitionOnceItExists = APITmp.DataEditWebhook;
const webhook = await this.#collection.client.api.patch(
// @ts-expect-error not in prod
`/webhooks/${this.id as ""}/${this.token as ""}`,
data
data,
);
this.#collection.updateUnderlyingObject(
this.id,
// @ts-expect-error not in prod
hydrate("channelWebhook", webhook, this.#collection.client)
hydrate("channelWebhook", webhook, this.#collection.client),
);
}
@@ -99,7 +112,7 @@ export class ChannelWebhook {
async delete(): Promise<void> {
await this.#collection.client.api.delete(
// @ts-expect-error not in prod
`/webhooks/${this.id}/${this.token}`
`/webhooks/${this.id}/${this.token}`,
);
this.#collection.delete(this.id);
+1 -1
View File
@@ -56,7 +56,7 @@ export class Emoji {
*/
get creator(): User | undefined {
return this.#collection.client.users.get(
this.#collection.getUnderlyingObject(this.id).creatorId
this.#collection.getUnderlyingObject(this.id).creatorId,
);
}
+1 -1
View File
@@ -45,7 +45,7 @@ export class File {
*/
constructor(
client: Client,
file: Pick<APIFile, "_id" | "tag" | "metadata"> & Partial<APIFile>
file: Pick<APIFile, "_id" | "tag" | "metadata"> & Partial<APIFile>,
) {
this.#client = client;
this.id = file._id;
+2 -2
View File
@@ -93,7 +93,7 @@ export class MFATicket {
constructor(
client: Client,
ticket: TicketType,
mutate: SetStoreFunction<MultiFactorStatus>
mutate: SetStoreFunction<MultiFactorStatus>,
) {
this.#client = client;
this.#ticket = ticket;
@@ -142,7 +142,7 @@ export class MFATicket {
headers: {
"X-MFA-Ticket": this.token,
},
}
},
);
this.#mutate("recovery_active", true);
+14 -12
View File
@@ -84,7 +84,7 @@ export class Message {
*/
get channel(): Channel | undefined {
return this.#collection.client.channels.get(
this.#collection.getUnderlyingObject(this.id).channelId
this.#collection.getUnderlyingObject(this.id).channelId,
);
}
@@ -117,7 +117,7 @@ export class Message {
*/
get author(): User | undefined {
return this.#collection.client.users.get(
this.#collection.getUnderlyingObject(this.id).authorId!
this.#collection.getUnderlyingObject(this.id).authorId!,
);
}
@@ -220,7 +220,9 @@ export class Message {
return (
this.masquerade?.name ??
(webhook ? webhook.name : this.member?.nickname ?? this.author?.username)
(webhook
? webhook.name
: (this.member?.nickname ?? this.author?.username))
);
}
@@ -241,7 +243,7 @@ export class Message {
this.masqueradeAvatarURL ??
(webhook
? webhook.avatarURL
: this.member?.avatarURL ?? this.author?.avatarURL)
: (this.member?.avatarURL ?? this.author?.avatarURL))
);
}
@@ -256,8 +258,8 @@ export class Message {
(webhook
? webhook.avatarURL
: this.member
? this.member?.animatedAvatarURL
: this.author?.animatedAvatarURL)
? this.member?.animatedAvatarURL
: this.author?.animatedAvatarURL)
);
}
@@ -283,7 +285,7 @@ export class Message {
async edit(data: DataEditMessage): Promise<APIMessage> {
return await this.#collection.client.api.patch(
`/channels/${this.channelId as ""}/messages/${this.id as ""}`,
data
data,
);
}
@@ -292,7 +294,7 @@ export class Message {
*/
async delete(): Promise<void> {
return await this.#collection.client.api.delete(
`/channels/${this.channelId as ""}/messages/${this.id as ""}`
`/channels/${this.channelId as ""}/messages/${this.id as ""}`,
);
}
@@ -312,7 +314,7 @@ export class Message {
| (Omit<DataMessageSend, "nonce"> & {
nonce?: string;
}),
mention = true
mention = true,
): Promise<Message> | undefined {
const obj = typeof data === "string" ? { content: data } : data;
return this.channel?.sendMessage({
@@ -326,7 +328,7 @@ export class Message {
*/
async clearReactions(): Promise<void> {
return await this.#collection.client.api.delete(
`/channels/${this.channelId as ""}/messages/${this.id as ""}/reactions`
`/channels/${this.channelId as ""}/messages/${this.id as ""}/reactions`,
);
}
@@ -338,7 +340,7 @@ export class Message {
return await this.#collection.client.api.put(
`/channels/${this.channelId as ""}/messages/${this.id as ""}/reactions/${
emoji as ""
}`
}`,
);
}
@@ -350,7 +352,7 @@ export class Message {
return await this.#collection.client.api.delete(
`/channels/${this.channelId as ""}/messages/${this.id as ""}/reactions/${
emoji as ""
}`
}`,
);
}
}
+2 -2
View File
@@ -130,7 +130,7 @@ export class WebsiteEmbed extends MessageEmbed {
*/
constructor(
client: Client,
embed: Omit<Embed & { type: "Website" }, "type">
embed: Omit<Embed & { type: "Website" }, "type">,
) {
super(client, "Website");
@@ -177,7 +177,7 @@ export class WebsiteEmbed extends MessageEmbed {
return `https://open.spotify.com/embed/${this.specialContent.content_type}/${this.specialContent.id}`;
case "Soundcloud":
return `https://w.soundcloud.com/player/?url=${encodeURIComponent(
this.url!
this.url!,
)}&color=%23FF7F50&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true`;
case "Bandcamp": {
return `https://bandcamp.com/EmbeddedPlayer/${this.specialContent.content_type.toLowerCase()}=${
+1 -1
View File
@@ -113,7 +113,7 @@ export class ServerPublicInvite extends PublicChannelInvite {
return this.client!.servers.getOrCreate(
invite.server._id,
invite.server,
true
true,
);
});
} else {
+46 -46
View File
@@ -85,7 +85,7 @@ export class Server {
*/
get owner(): User | undefined {
return this.#collection.client.users.get(
this.#collection.getUnderlyingObject(this.id).ownerId
this.#collection.getUnderlyingObject(this.id).ownerId,
);
}
@@ -239,7 +239,7 @@ export class Server {
if (uncategorised.size > 0) {
const channels = [...uncategorised].map(
(key) => this.#collection.client.channels.get(key)!
(key) => this.#collection.client.channels.get(key)!,
);
if (defaultCategory) {
@@ -299,7 +299,7 @@ export class Server {
*/
get mentions(): string[] {
const arr = this.channels.map((channel) =>
Array.from(channel.mentions?.values() ?? [])
Array.from(channel.mentions?.values() ?? []),
);
return ([] as string[]).concat(...arr);
@@ -351,7 +351,7 @@ export class Server {
havePermission(...permission: (keyof typeof Permission)[]): boolean {
return bitwiseAndEq(
this.permission,
...permission.map((x) => Permission[x])
...permission.map((x) => Permission[x]),
);
}
@@ -363,7 +363,7 @@ export class Server {
orPermission(...permission: (keyof typeof Permission)[]): boolean {
return (
permission.findIndex((x) =>
bitwiseAndEq(this.permission, Permission[x])
bitwiseAndEq(this.permission, Permission[x]),
) !== -1
);
}
@@ -388,7 +388,7 @@ export class Server {
async createChannel(data: DataCreateServerChannel): Promise<Channel> {
const channel = await this.#collection.client.api.post(
`/servers/${this.id as ""}/channels`,
data
data,
);
return this.#collection.client.channels.getOrCreate(channel._id, channel);
@@ -405,11 +405,11 @@ export class Server {
"server",
await this.#collection.client.api.patch(
`/servers/${this.id as ""}`,
data
data,
),
this.#collection.client,
false
)
false,
),
);
}
@@ -420,14 +420,14 @@ export class Server {
$delete(leaveEvent?: boolean): void {
batch(() => {
const server = this.#collection.client.servers.getUnderlyingObject(
this.id
this.id,
);
// Avoid race conditions
if (server.id) {
this.#collection.client.emit(
leaveEvent ? "serverLeave" : "serverDelete",
server
server,
);
for (const channel of this.channelIds) {
@@ -472,18 +472,18 @@ export class Server {
*/
async banUser(
user: string | User | ServerMember,
options: DataBanCreate = {}
options: DataBanCreate = {},
): Promise<ServerBan> {
const userId =
user instanceof User
? user.id
: user instanceof ServerMember
? user.id.user
: user;
? user.id.user
: user;
const ban = await this.#collection.client.api.put(
`/servers/${this.id as ""}/bans/${userId as ""}`,
options
options,
);
return new ServerBan(this.#collection.client, ban);
@@ -499,9 +499,9 @@ export class Server {
typeof user === "string"
? user
: user instanceof User
? user.id
: user.id.user
}`
? user.id
: user.id.user
}`,
);
}
@@ -512,7 +512,7 @@ export class Server {
async unbanUser(user: string | User): Promise<void> {
const userId = user instanceof User ? user.id : user;
return await this.#collection.client.api.delete(
`/servers/${this.id as ""}/bans/${userId}`
`/servers/${this.id as ""}/bans/${userId}`,
);
}
@@ -522,11 +522,11 @@ export class Server {
*/
async fetchInvites(): Promise<ChannelInvite[]> {
const invites = await this.#collection.client.api.get(
`/servers/${this.id as ""}/invites`
`/servers/${this.id as ""}/invites`,
);
return invites.map((invite) =>
ChannelInvite.from(this.#collection.client, invite)
ChannelInvite.from(this.#collection.client, invite),
);
}
@@ -536,17 +536,17 @@ export class Server {
*/
async fetchBans(): Promise<ServerBan[]> {
const { users, bans } = await this.#collection.client.api.get(
`/servers/${this.id as ""}/bans`
`/servers/${this.id as ""}/bans`,
);
const userDict = users.reduce(
(d, c) => ({ ...d, [c._id]: c }),
{} as Record<string, BannedUser>
{} as Record<string, BannedUser>,
);
return bans.map(
(ban) =>
new ServerBan(this.#collection.client, ban, userDict[ban._id.user])
new ServerBan(this.#collection.client, ban, userDict[ban._id.user]),
);
}
@@ -557,11 +557,11 @@ export class Server {
*/
async setPermissions(
roleId = "default",
permissions: Override | number
permissions: Override | number,
): Promise<APIServer> {
return await this.#collection.client.api.put(
`/servers/${this.id as ""}/permissions/${roleId as ""}`,
{ permissions: permissions as Override }
{ permissions: permissions as Override },
);
}
@@ -574,7 +574,7 @@ export class Server {
`/servers/${this.id as ""}/roles`,
{
name,
}
},
);
}
@@ -586,7 +586,7 @@ export class Server {
async editRole(roleId: string, data: DataEditRole): Promise<Role> {
return await this.#collection.client.api.patch(
`/servers/${this.id as ""}/roles/${roleId as ""}`,
data
data,
);
}
@@ -596,7 +596,7 @@ export class Server {
*/
async deleteRole(roleId: string): Promise<void> {
return await this.#collection.client.api.delete(
`/servers/${this.id as ""}/roles/${roleId as ""}`
`/servers/${this.id as ""}/roles/${roleId as ""}`,
);
}
@@ -627,7 +627,7 @@ export class Server {
const data = await this.#collection.client.api.get(
`/servers/${this.id as ""}/members`,
{ exclude_offline: excludeOffline }
{ exclude_offline: excludeOffline },
);
batch(() => {
@@ -638,7 +638,7 @@ export class Server {
this.#collection.client.users.getOrCreate(user._id, user);
this.#collection.client.serverMembers.getOrCreate(
data.members[i]._id,
data.members[i]
data.members[i],
);
}
}
@@ -646,11 +646,11 @@ export class Server {
for (let i = 0; i < data.users.length; i++) {
this.#collection.client.users.getOrCreate(
data.users[i]._id,
data.users[i]
data.users[i],
);
this.#collection.client.serverMembers.getOrCreate(
data.members[i]._id,
data.members[i]
data.members[i],
);
}
}
@@ -671,15 +671,15 @@ export class Server {
async fetchMembers(): Promise<{ members: ServerMember[]; users: User[] }> {
const data = (await this.#collection.client.api.get(
// @ts-expect-error TODO weird typing issue
`/servers/${this.id as ""}/members`
`/servers/${this.id as ""}/members`,
)) as AllMemberResponse;
return batch(() => ({
members: data.members.map((member) =>
this.#collection.client.serverMembers.getOrCreate(member._id, member)
this.#collection.client.serverMembers.getOrCreate(member._id, member),
),
users: data.users.map((user) =>
this.#collection.client.users.getOrCreate(user._id, user)
this.#collection.client.users.getOrCreate(user._id, user),
),
}));
}
@@ -690,22 +690,22 @@ export class Server {
* @returns List of the server's members and their user objects
*/
async queryMembersExperimental(
query: string
query: string,
): Promise<{ members: ServerMember[]; users: User[] }> {
const data = (await this.#collection.client.api.get(
`/servers/${
this.id as ""
}/members_experimental_query?experimental_api=true&query=${encodeURIComponent(
query
)}` as never
query,
)}` as never,
)) as AllMemberResponse;
return batch(() => ({
members: data.members.map((member) =>
this.#collection.client.serverMembers.getOrCreate(member._id, member)
this.#collection.client.serverMembers.getOrCreate(member._id, member),
),
users: data.users.map((user) =>
this.#collection.client.users.getOrCreate(user._id, user)
this.#collection.client.users.getOrCreate(user._id, user),
),
}));
}
@@ -717,7 +717,7 @@ export class Server {
*/
async createEmoji(
autumnId: string,
options: Omit<DataCreateEmoji, "parent">
options: Omit<DataCreateEmoji, "parent">,
): Promise<Emoji> {
const emoji = await this.#collection.client.api.put(
`/custom/emoji/${autumnId as ""}`,
@@ -727,7 +727,7 @@ export class Server {
id: this.id,
},
...options,
}
},
);
return this.#collection.client.emojis.getOrCreate(emoji._id, emoji, true);
@@ -739,13 +739,13 @@ export class Server {
*/
async fetchEmojis(): Promise<Emoji[]> {
const emojis = await this.#collection.client.api.get(
`/servers/${this.id as ""}/emojis`
`/servers/${this.id as ""}/emojis`,
);
return batch(() =>
emojis.map((emoji) =>
this.#collection.client.emojis.getOrCreate(emoji._id, emoji)
)
this.#collection.client.emojis.getOrCreate(emoji._id, emoji),
),
);
}
+1 -1
View File
@@ -42,7 +42,7 @@ export class ServerBan {
*/
async pardon(): Promise<void> {
await this.client.api.delete(
`/servers/${this.id.server as ""}/bans/${this.id.user as ""}`
`/servers/${this.id.server as ""}/bans/${this.id.user as ""}`,
);
}
}
+2 -2
View File
@@ -186,7 +186,7 @@ export class ServerMember {
): boolean {
return bitwiseAndEq(
this.getPermissions(target),
...permission.map((x) => Permission[x])
...permission.map((x) => Permission[x]),
);
}
@@ -227,7 +227,7 @@ export class ServerMember {
async edit(data: DataMemberEdit): Promise<void> {
await this.#collection.client.api.patch(
`/servers/${this.id.server as ""}/members/${this.id.user as ""}`,
data
data,
);
}
+6 -6
View File
@@ -68,7 +68,7 @@ export class TextSystemMessage extends SystemMessage {
*/
constructor(
client: Client,
systemMessage: APISystemMessage & { type: "text" }
systemMessage: APISystemMessage & { type: "text" },
) {
super(client, systemMessage.type);
this.content = systemMessage.content;
@@ -96,7 +96,7 @@ export class UserSystemMessage extends SystemMessage {
| "user_left"
| "user_kicked"
| "user_banned";
}
},
) {
super(client, systemMessage.type);
this.userId = systemMessage.id;
@@ -125,7 +125,7 @@ export class UserModeratedSystemMessage extends UserSystemMessage {
client: Client,
systemMessage: APISystemMessage & {
type: "user_added" | "user_remove";
}
},
) {
super(client, systemMessage);
this.byId = systemMessage.by;
@@ -157,7 +157,7 @@ export class ChannelEditSystemMessage extends SystemMessage {
| "channel_renamed"
| "channel_description_changed"
| "channel_icon_changed";
}
},
) {
super(client, systemMessage.type);
this.byId = systemMessage.by;
@@ -186,7 +186,7 @@ export class ChannelRenamedSystemMessage extends ChannelEditSystemMessage {
client: Client,
systemMessage: APISystemMessage & {
type: "channel_renamed";
}
},
) {
super(client, systemMessage);
this.name = systemMessage.name;
@@ -209,7 +209,7 @@ export class ChannelOwnershipChangeSystemMessage extends SystemMessage {
client: Client,
systemMessage: APISystemMessage & {
type: "channel_ownership_changed";
}
},
) {
super(client, systemMessage.type);
this.fromId = systemMessage.from;
+12 -12
View File
@@ -166,7 +166,7 @@ export class User {
* Presence
*/
get presence(): Presence {
return this.online ? this.status?.presence ?? "Online" : "Invisible";
return this.online ? (this.status?.presence ?? "Online") : "Invisible";
}
/**
@@ -175,11 +175,11 @@ export class User {
* @returns Status message
*/
statusMessage(
translate: (presence: Presence) => string = (a) => a
translate: (presence: Presence) => string = (a) => a,
): string | undefined {
return this.online
? this.status?.text ??
(this.presence === "Focus" ? translate("Focus") : undefined)
? (this.status?.text ??
(this.presence === "Focus" ? translate("Focus") : undefined))
: undefined;
}
@@ -204,10 +204,10 @@ export class User {
this.#collection.client.channels.find(
(channel) =>
(channel.type === "Group" || channel.type === "DirectMessage") &&
channel.recipientIds.has(this.id)
channel.recipientIds.has(this.id),
) ||
this.#collection.client.serverMembers.find(
(member) => member.id.user === this.id
(member) => member.id.user === this.id,
)
) {
if (this.#collection.client.user?.bot || this.bot) {
@@ -229,7 +229,7 @@ export class User {
`/users/${
this.id === this.#collection.client.user?.id ? "@me" : this.id
}`,
data
data,
);
}
@@ -251,7 +251,7 @@ export class User {
*/
async openDM(): Promise<Channel> {
let dm = [...this.#collection.client.channels.values()].find(
(x) => x.type === "DirectMessage" && x.recipient == this
(x) => x.type === "DirectMessage" && x.recipient == this,
);
if (dm) {
@@ -259,12 +259,12 @@ export class User {
this.#collection.client.channels.updateUnderlyingObject(
dm.id,
"active",
true
true,
);
}
} else {
const data = await this.#collection.client.api.get(
`/users/${this.id as ""}/dm`
`/users/${this.id as ""}/dm`,
);
dm = this.#collection.client.channels.getOrCreate(data._id, data)!;
@@ -312,7 +312,7 @@ export class User {
async fetchProfile(): Promise<UserProfile> {
return new UserProfile(
this.#collection.client,
await this.#collection.client.api.get(`/users/${this.id as ""}/profile`)
await this.#collection.client.api.get(`/users/${this.id as ""}/profile`),
);
}
@@ -322,7 +322,7 @@ export class User {
*/
async fetchMutual(): Promise<{ users: string[]; servers: string[] }> {
return await this.#collection.client.api.get(
`/users/${this.id as ""}/mutual`
`/users/${this.id as ""}/mutual`,
);
}
}
+2 -2
View File
@@ -86,7 +86,7 @@ export class AccountCollection {
confirmPasswordReset(
token: string,
newPassword: string,
removeSessions: boolean
removeSessions: boolean,
): Promise<void> {
return this.client.api.patch("/auth/account/reset_password", {
token,
@@ -138,7 +138,7 @@ export class AccountCollection {
*/
setSettings(
settings: Record<string, any>,
timestamp = +new Date()
timestamp = +new Date(),
): Promise<void> {
return this.client.api.post("/sync/settings/set", {
...settings,
+1 -1
View File
@@ -33,7 +33,7 @@ export class BotCollection extends ClassCollection<Bot, HydratedBot> {
const data = (await this.client.api.get("/bots/@me")) as OwnedBotsResponse;
return batch(() => {
data.users.forEach((user) =>
this.client.users.getOrCreate(user._id, user)
this.client.users.getOrCreate(user._id, user),
);
return data.bots.map((bot) => this.getOrCreate(bot._id, bot));
});
+1 -1
View File
@@ -47,7 +47,7 @@ export class ChannelCollection extends ClassCollection<
} else {
const instance = new Channel(this, id);
this.create(id, "channel", instance, this.client, data);
isNew && this.client.emit("channelCreate", instance);
if (isNew) this.client.emit("channelCreate", instance);
return instance;
}
}
+1 -1
View File
@@ -37,7 +37,7 @@ export class ChannelWebhookCollection extends ClassCollection<
if (webhook) return webhook;
const data = await this.client.api.get(
// @ts-expect-error not in prod
`/webhooks/${id as ""}/${token as ""}`
`/webhooks/${id as ""}/${token as ""}`,
);
// @ts-expect-error not in prod
return this.getOrCreate(data.id, data);
+2 -2
View File
@@ -59,7 +59,7 @@ export abstract class Collection<T> {
* @param cb Callback for each pair
*/
abstract forEach(
cb: (value: T, key: string, map: Map<string, T>) => void
cb: (value: T, key: string, map: Map<string, T>) => void,
): void;
/**
@@ -169,7 +169,7 @@ export abstract class StoreCollection<T, V> extends Collection<T> {
type: keyof Hydrators,
instance: T,
context: unknown,
data?: unknown
data?: unknown,
): void {
this.#storage.hydrate(id, type, context, data);
this.#objects.set(id, instance);
+1 -1
View File
@@ -33,7 +33,7 @@ export class EmojiCollection extends ClassCollection<Emoji, HydratedEmoji> {
} else {
const instance = new Emoji(this, id);
this.create(id, "emoji", instance, this.client, data);
isNew && this.client.emit("emojiCreate", instance);
if (isNew) this.client.emit("emojiCreate", instance);
return instance;
}
}
+2 -2
View File
@@ -23,7 +23,7 @@ export class MessageCollection extends ClassCollection<
if (message && !this.isPartial(messageId)) return message;
const data = await this.client.api.get(
`/channels/${channelId as ""}/messages/${messageId as ""}`
`/channels/${channelId as ""}/messages/${messageId as ""}`,
);
return this.getOrCreate(data._id, data, false);
@@ -41,7 +41,7 @@ export class MessageCollection extends ClassCollection<
} else {
const instance = new Message(this, id);
this.create(id, "message", instance, this.client, data);
isNew && this.client.emit("messageCreate", instance);
if (isNew) this.client.emit("messageCreate", instance);
return instance;
}
}
+2 -2
View File
@@ -52,7 +52,7 @@ export class ServerCollection extends ClassCollection<Server, HydratedServer> {
} else {
const instance = new Server(this, id);
this.create(id, "server", instance, this.client, data);
isNew && this.client.emit("serverCreate", instance);
if (isNew) this.client.emit("serverCreate", instance);
return instance;
}
}
@@ -82,7 +82,7 @@ export class ServerCollection extends ClassCollection<Server, HydratedServer> {
async createServer(data: DataCreateServer): Promise<Server> {
const { server, channels } = await this.client.api.post(
`/servers/create`,
data
data,
);
return batch(() => {
+2 -2
View File
@@ -53,7 +53,7 @@ export class ServerMemberCollection extends ClassCollection<
`/servers/${serverId as ""}/members/${userId as ""}`,
{
roles: false,
}
},
// TODO: fix typings in revolt-api
)) as Member;
@@ -75,7 +75,7 @@ export class ServerMemberCollection extends ClassCollection<
"serverMember",
instance,
this.client,
data
data,
);
return instance;
}
+1 -1
View File
@@ -21,7 +21,7 @@ export class SessionCollection extends ClassCollection<
async fetch(): Promise<Session[]> {
const data = await this.client.api.get("/auth/session/all");
return batch(() =>
data.map((session) => this.getOrCreate(session._id, session))
data.map((session) => this.getOrCreate(session._id, session)),
);
}
+15 -20
View File
@@ -1,8 +1,7 @@
import type { Accessor, Setter } from "solid-js";
import { createSignal } from "solid-js";
import EventEmitter from "eventemitter3";
import WebSocket from "isomorphic-ws";
import { AsyncEventEmitter } from "@vladfrangu/async_event_emitter";
import type { Error } from "revolt-api";
import type { ProtocolV1 } from "./v1.js";
@@ -67,17 +66,17 @@ export interface EventClientOptions {
* Events provided by the client.
*/
type Events<T extends AvailableProtocols, P extends EventProtocol<T>> = {
error: (error: Error) => void;
event: (event: P["server"]) => void;
state: (state: ConnectionState) => void;
error: [error: Error];
event: [event: P["server"]];
state: [state: ConnectionState];
};
/**
* Simple wrapper around the Revolt websocket service.
*/
export class EventClient<T extends AvailableProtocols> extends EventEmitter<
Events<T, EventProtocol<T>>
> {
export class EventClient<
T extends AvailableProtocols,
> extends AsyncEventEmitter<Events<T, EventProtocol<T>>> {
readonly options: EventClientOptions;
#protocolVersion: T;
@@ -106,7 +105,7 @@ export class EventClient<T extends AvailableProtocols> extends EventEmitter<
constructor(
protocolVersion: T,
transportFormat: "json" = "json",
options?: Partial<EventClientOptions>
options?: Partial<EventClientOptions>,
) {
super();
@@ -153,13 +152,11 @@ export class EventClient<T extends AvailableProtocols> extends EventEmitter<
this.#connectTimeoutReference = setTimeout(
() => this.disconnect(),
this.options.pongTimeout * 1e3
this.options.pongTimeout * 1e3,
) as never;
this.#socket = new WebSocket(
`${uri}?version=${this.#protocolVersion}&format=${
this.#transportFormat
}&token=${token}`
`${uri}?version=${this.#protocolVersion}&format=${this.#transportFormat}&token=${token}`,
);
this.#socket.onopen = () => {
@@ -167,7 +164,7 @@ export class EventClient<T extends AvailableProtocols> extends EventEmitter<
this.send({ type: "Ping", data: +new Date() });
this.#pongTimeoutReference = setTimeout(
() => this.disconnect(),
this.options.pongTimeout * 1e3
this.options.pongTimeout * 1e3,
) as never;
}, this.options.heartbeatInterval * 1e3) as never;
};
@@ -215,7 +212,7 @@ export class EventClient<T extends AvailableProtocols> extends EventEmitter<
* @param event Event
*/
send(event: EventProtocol<T>["client"]): void {
this.options.debug && console.debug("[C->S]", event);
if (this.options.debug) console.debug("[C->S]", event);
if (!this.#socket) throw "Socket closed, trying to send.";
this.#socket.send(JSON.stringify(event));
}
@@ -225,7 +222,7 @@ export class EventClient<T extends AvailableProtocols> extends EventEmitter<
* @param event Event
*/
handle(event: EventProtocol<T>["server"]): void {
this.options.debug && console.debug("[S->C]", event);
if (this.options.debug) console.debug("[S->C]", event);
switch (event.type) {
case "Ping":
this.send({
@@ -236,7 +233,7 @@ export class EventClient<T extends AvailableProtocols> extends EventEmitter<
case "Pong":
clearTimeout(this.#pongTimeoutReference);
this.#setPing(+new Date() - event.data);
this.options.debug && console.debug(`[ping] ${this.ping()}ms`);
if (this.options.debug) console.debug(`[ping] ${this.ping()}ms`);
return;
case "Error":
this.#lastError = {
@@ -267,9 +264,7 @@ export class EventClient<T extends AvailableProtocols> extends EventEmitter<
}
break;
default:
throw `Unreachable code. Received ${
event.type
} in state ${this.state()}.`;
throw `Unreachable code. Received ${event.type} in state ${this.state()}.`;
}
}
+22 -22
View File
@@ -190,7 +190,7 @@ type ReadyData = {
export async function handleEvent(
client: Client,
event: ServerMessage,
setReady: Setter<boolean>
setReady: Setter<boolean>,
): Promise<void> {
if (client.options.debug) {
console.debug("[S->C]", event);
@@ -257,7 +257,7 @@ export async function handleEvent(
client.channels.updateUnderlyingObject(
event.channel,
"lastMessageId",
event._id
event._id,
);
});
}
@@ -276,7 +276,7 @@ export async function handleEvent(
"message",
{ ...event.data, channel: event.channel },
client,
false
false,
),
editedAt: new Date(),
});
@@ -296,14 +296,14 @@ export async function handleEvent(
client.messages.updateUnderlyingObject(event.id, "embeds", (embeds) => [
...(embeds ?? []),
...(event.append.embeds?.map((embed) =>
MessageEmbed.from(client, embed)
MessageEmbed.from(client, embed),
) ?? []),
]);
client.messages.updateUnderlyingObject(
event.id,
"channelId",
event.channel
event.channel,
);
client.emit("messageUpdate", message, previousMessage);
@@ -333,8 +333,8 @@ export async function handleEvent(
return undefined!;
})
.filter((x) => x),
client.channels.get(event.channel)
)
client.channels.get(event.channel),
),
);
break;
}
@@ -354,7 +354,7 @@ export async function handleEvent(
"messageReactionAdd",
message,
event.user_id,
event.emoji_id
event.emoji_id,
);
}
break;
@@ -373,7 +373,7 @@ export async function handleEvent(
"messageReactionRemove",
message,
event.user_id,
event.emoji_id
event.emoji_id,
);
}
break;
@@ -448,7 +448,7 @@ export async function handleEvent(
client.emit(
"channelGroupJoin",
channel,
await client.users.fetch(event.user)
await client.users.fetch(event.user),
);
}
break;
@@ -465,7 +465,7 @@ export async function handleEvent(
client.emit(
"channelGroupLeave",
channel,
client.users.getOrPartial(event.user)!
client.users.getOrPartial(event.user)!,
);
}
break;
@@ -482,7 +482,7 @@ export async function handleEvent(
client.emit(
"channelStartTyping",
channel,
client.users.getOrPartial(event.user)!
client.users.getOrPartial(event.user)!,
);
}
break;
@@ -499,7 +499,7 @@ export async function handleEvent(
client.emit(
"channelStopTyping",
channel,
client.users.getOrPartial(event.user)!
client.users.getOrPartial(event.user)!,
);
}
break;
@@ -614,7 +614,7 @@ export async function handleEvent(
client.serverMembers.getOrCreate(id, {
_id: id,
joined_at: new Date().toUTCString(),
})
}),
);
}
break;
@@ -624,7 +624,7 @@ export async function handleEvent(
if (member) {
const previousMember = {
...client.serverMembers.getUnderlyingObject(
event.id.server + event.id.user
event.id.server + event.id.user,
),
};
@@ -651,7 +651,7 @@ export async function handleEvent(
client.serverMembers.updateUnderlyingObject(
event.id.server + event.id.user,
changes as never
changes as never,
);
client.emit("serverMemberUpdate", member, previousMember);
@@ -666,7 +666,7 @@ export async function handleEvent(
type: "ServerDelete",
id: event.id,
},
setReady
setReady,
);
return;
@@ -679,7 +679,7 @@ export async function handleEvent(
if (client.serverMembers.getOrPartial(id)) {
const member = client.serverMembers.getUnderlyingObject(
id.server + id.user
id.server + id.user,
);
client.emit("serverMemberLeave", member);
@@ -735,7 +735,7 @@ export async function handleEvent(
relationship: event.user.relationship!,
},
},
setReady
setReady,
);
break;
}
@@ -749,7 +749,7 @@ export async function handleEvent(
online: event.online,
},
},
setReady
setReady,
);
break;
}
@@ -769,7 +769,7 @@ export async function handleEvent(
.filter((message) => message.authorId === event.user_id)
.map((message) => message.id),
},
setReady
setReady,
);
handleEvent(
@@ -786,7 +786,7 @@ export async function handleEvent(
},
clear: ["Avatar", "StatusPresence", "StatusText"],
},
setReady
setReady,
);
});
+6 -5
View File
@@ -40,19 +40,20 @@ export type Hydrate<Input, Output> = {
function hydrateInternal<Input extends object, Output>(
hydration: Hydrate<Input, Output>,
input: Input,
context: unknown
context: unknown,
): Output {
return (Object.keys(input) as (keyof Input)[]).reduce((acc, key) => {
let targetKey, value;
try {
targetKey = hydration.keyMapping[key] ?? key;
value = hydration.functions[targetKey as keyof Output](input, context);
} catch (err) {
if (key === "partial")
} catch {
if (key === "partial") {
return {
...acc,
partial: input["partial" as never],
};
}
if (key === "type") return acc;
console.debug(`Skipping key ${String(key)} during hydration!`);
return acc;
@@ -97,11 +98,11 @@ export function hydrate<T extends keyof Hydrators>(
type: T,
input: Partial<ExtractInput<Hydrators[T]>>,
context: unknown,
initial?: boolean
initial?: boolean,
): ExtractOutput<Hydrators[T]> {
return hydrateInternal(
hydrators[type] as never,
initial ? { ...hydrators[type].initialHydration(), ...input } : input,
context
context,
) as ExtractOutput<Hydrators[T]>;
}
+1 -1
View File
@@ -53,7 +53,7 @@ export const serverHydration: Hydrate<APIServer, HydratedServer> = {
systemMessages: (server) => server.system_messages ?? {},
roles: (server) =>
new ReactiveMap(
Object.keys(server.roles!).map((id) => [id, server.roles![id]])
Object.keys(server.roles!).map((id) => [id, server.roles![id]]),
),
defaultPermissions: (server) => server.default_permissions,
icon: (server, ctx) => new File(ctx as Client, server.icon!),
+2 -3
View File
@@ -5,9 +5,8 @@ export type Merge<T extends object> = {
[k in NonCommonKeys<T>]?: PickTypeOf<T, k>;
};
type PickTypeOf<T, K extends string | number | symbol> = K extends AllKeys<T>
? PickType<T, K>
: never;
type PickTypeOf<T, K extends string | number | symbol> =
K extends AllKeys<T> ? PickType<T, K> : never;
// eslint-disable-next-line
type PickType<T, K extends AllKeys<T>> = T extends { [k in K]?: any }
+16 -25
View File
@@ -1,9 +1,4 @@
import Long from "long";
import type { Client } from "../Client.js";
import type { Channel } from "../classes/Channel.js";
import { Server } from "../classes/Server.js";
import type { ServerMember } from "../classes/ServerMember.js";
import { Channel, Client, Server, ServerMember } from "../index.js";
import {
ALLOW_IN_TIMEOUT,
@@ -19,8 +14,8 @@ import {
* @param b Inputs (OR'd together)
*/
export function bitwiseAndEq(a: number, ...b: number[]): boolean {
const value = b.reduce((prev, cur) => prev.or(cur), Long.fromNumber(0));
return value.and(a).eq(value);
const value = b.reduce((prev, cur) => prev | BigInt(cur), 0n);
return (value & BigInt(a)) === value;
}
/**
@@ -36,7 +31,7 @@ export function calculatePermission(
* Pretend to be another ServerMember
*/
member?: ServerMember;
}
},
): number {
const user = options?.member ? options?.member.user : client.user;
if (user?.privileged) {
@@ -58,26 +53,26 @@ export function calculatePermission(
if (!member) return 0;
// 3. Apply allows from default_permissions.
let perm = Long.fromNumber(target.defaultPermissions);
let perm = BigInt(target.defaultPermissions);
// 4. If user has roles, iterate in order.
if (member.roles && target.roles) {
// 5. Apply allows and denies from roles.
const permissions = member.orderedRoles.map(
(role) => role.permissions ?? { a: 0, d: 0 }
(role) => role.permissions ?? { a: 0, d: 0 },
);
for (const permission of permissions) {
perm = perm.or(permission.a).and(Long.fromNumber(permission.d).not());
perm = (perm | BigInt(permission.a)) & ~BigInt(permission.d);
}
}
// 5. Revoke permissions if ServerMember is timed out.
if (member.timeout && member.timeout > new Date()) {
perm = perm.and(ALLOW_IN_TIMEOUT);
perm = perm & BigInt(ALLOW_IN_TIMEOUT);
}
return perm.toNumber();
return Number(perm);
}
} else {
// 1. Check channel type.
@@ -124,15 +119,13 @@ export function calculatePermission(
if (!member) return 0;
// 5. Calculate server base permissions.
let perm = Long.fromNumber(
calculatePermission(client, server, options)
);
let perm = BigInt(calculatePermission(client, server, options));
// 6. Apply default allows and denies for channel.
if (target.defaultPermissions) {
perm = perm
.or(target.defaultPermissions.a)
.and(Long.fromNumber(target.defaultPermissions.d).not());
perm =
(perm | BigInt(target.defaultPermissions.a)) &
~BigInt(target.defaultPermissions.d);
}
// 7. If user has roles, iterate in order.
@@ -143,19 +136,17 @@ export function calculatePermission(
for (const id of roles) {
const override = target.rolePermissions[id];
if (override) {
perm = perm
.or(override.a)
.and(Long.fromNumber(override.d).not());
perm = (perm | BigInt(override.a)) & ~BigInt(override.d);
}
}
}
// 8. Revoke permissions if ServerMember is timed out.
if (member.timeout && member.timeout > new Date()) {
perm = perm.and(ALLOW_IN_TIMEOUT);
perm = perm & BigInt(ALLOW_IN_TIMEOUT);
}
return perm.toNumber();
return Number(perm);
}
}
}
-12
View File
@@ -1,12 +0,0 @@
require("dotenv").config();
const { Client } = require(".");
const client = new Client({ debug: true });
client.on("ready", () => console.info(`Logged in as ${client.user.username}!`));
client.on("disconnected", () => console.info("Disconnected."));
client.on("messageCreate", (message) => console.info(message.content));
client.loginBot(process.env.TOKEN);
+4 -3
View File
@@ -1,6 +1,7 @@
import "dotenv/config";
#!/usr/bin/env -S node --env-file
import { env } from "node:process";
import { Client } from "./lib/esm/index.js";
import { Client } from "./lib/index.js";
const client = new Client({ debug: true });
@@ -9,4 +10,4 @@ client.on("disconnected", () => console.info("Disconnected."));
client.on("messageCreate", (message) => console.info(message.content));
client.loginBot(process.env.TOKEN);
client.loginBot(env.TOKEN);
-10
View File
@@ -1,10 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"declaration": true,
"declarationMap": true,
"outDir": "./lib/cjs"
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
"rootDir": "./src",
"declaration": true,
"declarationMap": true,
"outDir": "./lib/esm",
"outDir": "./lib",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,