mirror of
https://github.com/stoatchat/javascript-client-sdk.git
synced 2026-07-19 17:13:31 -04:00
refactor: conventional design for classes
This commit is contained in:
+12
-43
@@ -3,12 +3,7 @@ import { Accessor, Setter, createSignal } from "solid-js";
|
||||
import { API, Metadata } from "revolt-api";
|
||||
import type { DataLogin, RevoltConfig } from "revolt-api";
|
||||
|
||||
import channelClassFactory from "./classes/Channel";
|
||||
import emojiClassFactory from "./classes/Emoji";
|
||||
import messageClassFactory from "./classes/Message";
|
||||
import serverClassFactory from "./classes/Server";
|
||||
import serverMemberClassFactory from "./classes/ServerMember";
|
||||
import userClassFactory from "./classes/User";
|
||||
import { User } from "./classes";
|
||||
import {
|
||||
ChannelCollection,
|
||||
EmojiCollection,
|
||||
@@ -19,28 +14,12 @@ import {
|
||||
} from "./collections";
|
||||
import { EventClient, createEventClient } from "./events/client";
|
||||
|
||||
// eslint-disable-next-line
|
||||
type O<T extends (...args: any) => any> = InstanceType<ReturnType<T>>;
|
||||
|
||||
export type Channel = O<typeof channelClassFactory>;
|
||||
export type Emoji = O<typeof emojiClassFactory>;
|
||||
export type Message = O<typeof messageClassFactory>;
|
||||
export type Server = O<typeof serverClassFactory>;
|
||||
export type ServerMember = O<typeof serverMemberClassFactory>;
|
||||
export type User = O<typeof userClassFactory>;
|
||||
export type Session = { token: string; user_id: string } | string;
|
||||
|
||||
/**
|
||||
* Revolt.js Client
|
||||
*/
|
||||
export class Client {
|
||||
readonly Channel: ReturnType<typeof channelClassFactory>;
|
||||
readonly Emoji: ReturnType<typeof emojiClassFactory>;
|
||||
readonly Message: ReturnType<typeof messageClassFactory>;
|
||||
readonly User: ReturnType<typeof userClassFactory>;
|
||||
readonly Server: ReturnType<typeof serverClassFactory>;
|
||||
readonly ServerMember: ReturnType<typeof serverMemberClassFactory>;
|
||||
|
||||
readonly channels;
|
||||
readonly emojis;
|
||||
readonly messages;
|
||||
@@ -74,22 +53,12 @@ export class Client {
|
||||
this.ready = ready;
|
||||
this.#setReady = setReady;
|
||||
|
||||
this.channels = new ChannelCollection();
|
||||
this.emojis = new EmojiCollection();
|
||||
this.messages = new MessageCollection();
|
||||
this.users = new UserCollection();
|
||||
this.servers = new ServerCollection();
|
||||
this.serverMembers = new ServerMemberCollection();
|
||||
|
||||
this.Channel = channelClassFactory(this, this.channels as never);
|
||||
this.Emoji = emojiClassFactory(this, this.emojis as never);
|
||||
this.Message = messageClassFactory(this, this.messages as never);
|
||||
this.User = userClassFactory(this, this.users as never);
|
||||
this.Server = serverClassFactory(this, this.servers as never);
|
||||
this.ServerMember = serverMemberClassFactory(
|
||||
this,
|
||||
this.serverMembers as never
|
||||
);
|
||||
this.channels = new ChannelCollection(this);
|
||||
this.emojis = new EmojiCollection(this);
|
||||
this.messages = new MessageCollection(this);
|
||||
this.users = new UserCollection(this);
|
||||
this.servers = new ServerCollection(this);
|
||||
this.serverMembers = new ServerMemberCollection(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +70,7 @@ export class Client {
|
||||
if (event.type === "Ready") {
|
||||
console.time("load users");
|
||||
for (const user of event.users) {
|
||||
const u = new this.User(user._id, user);
|
||||
const u = this.users.getOrCreate(user._id, user);
|
||||
|
||||
if (u.relationship === "User") {
|
||||
this.user = u;
|
||||
@@ -110,22 +79,22 @@ export class Client {
|
||||
console.timeEnd("load users");
|
||||
console.time("load servers");
|
||||
for (const server of event.servers) {
|
||||
new this.Server(server._id, server);
|
||||
this.servers.getOrCreate(server._id, server);
|
||||
}
|
||||
console.timeEnd("load servers");
|
||||
console.time("load memberships");
|
||||
for (const member of event.members) {
|
||||
new this.ServerMember(member._id, member);
|
||||
this.serverMembers.getOrCreate(member._id, member);
|
||||
}
|
||||
console.timeEnd("load memberships");
|
||||
console.time("load channels");
|
||||
for (const channel of event.channels) {
|
||||
new this.Channel(channel._id, channel);
|
||||
this.channels.getOrCreate(channel._id, channel);
|
||||
}
|
||||
console.timeEnd("load channels");
|
||||
console.time("load emojis");
|
||||
for (const emoji of event.emojis) {
|
||||
new this.Emoji(emoji._id, emoji);
|
||||
this.emojis.getOrCreate(emoji._id, emoji);
|
||||
}
|
||||
console.timeEnd("load emojis");
|
||||
|
||||
|
||||
+491
-511
File diff suppressed because it is too large
Load Diff
+54
-93
@@ -1,105 +1,66 @@
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type { Emoji as ApiEmoji } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Client } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedEmoji } from "../hydration/emoji";
|
||||
import { EmojiCollection } from "../collections";
|
||||
|
||||
/**
|
||||
* Emoji Class
|
||||
*/
|
||||
export class Emoji {
|
||||
readonly collection: EmojiCollection;
|
||||
readonly id: string;
|
||||
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
/**
|
||||
* Emoji Class
|
||||
* Construct Emoji
|
||||
* @param collection Collection
|
||||
* @param id Emoji Id
|
||||
*/
|
||||
class Emoji {
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedEmoji
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedEmoji>>;
|
||||
static #get: (id: string) => HydratedEmoji;
|
||||
constructor(collection: EmojiCollection, id: string, data?: ApiEmoji) {
|
||||
this.collection = collection;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
static {
|
||||
Emoji.#collection = collection as never;
|
||||
Emoji.#set = collection.updateUnderlyingObject as never;
|
||||
Emoji.#get = collection.getUnderlyingObject as never;
|
||||
}
|
||||
/**
|
||||
* Time when this emoji was created
|
||||
*/
|
||||
get createdAt() {
|
||||
return new Date(decodeTime(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch emoji by ID
|
||||
* @param id ID
|
||||
* @returns Emoji
|
||||
*/
|
||||
static async fetch(id: string): Promise<Emoji | undefined> {
|
||||
const emoji = Emoji.#collection.get(id);
|
||||
if (emoji) return emoji;
|
||||
/**
|
||||
* Information about the parent of this emoji
|
||||
*/
|
||||
get parent() {
|
||||
return this.collection.getUnderlyingObject(this.id).parent;
|
||||
}
|
||||
|
||||
const data = await client.api.get(`/custom/emoji/${id as ""}`);
|
||||
return new Emoji(id, data);
|
||||
}
|
||||
/**
|
||||
* Creator of the emoji
|
||||
*/
|
||||
get creator() {
|
||||
return this.collection.client.users.get(
|
||||
this.collection.getUnderlyingObject(this.id).creatorId
|
||||
);
|
||||
}
|
||||
|
||||
readonly id: string;
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
get name() {
|
||||
return this.collection.getUnderlyingObject(this.id).name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct Emoji
|
||||
* @param id Emoji Id
|
||||
*/
|
||||
constructor(id: string, data?: ApiEmoji) {
|
||||
this.id = id;
|
||||
Emoji.#collection.create(id, "emoji", this, data);
|
||||
}
|
||||
/**
|
||||
* Whether the emoji is animated
|
||||
*/
|
||||
get animated() {
|
||||
return this.collection.getUnderlyingObject(this.id).animated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: string, data?: ApiEmoji) {
|
||||
return client.emojis.get(id) ?? new Emoji(id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time when this emoji was created
|
||||
*/
|
||||
get createdAt() {
|
||||
return new Date(decodeTime(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about the parent of this emoji
|
||||
*/
|
||||
get parent() {
|
||||
return Emoji.#get(this.id).parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creator of the emoji
|
||||
*/
|
||||
get creator() {
|
||||
return client.users.get(Emoji.#get(this.id).creatorId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
get name() {
|
||||
return Emoji.#get(this.id).name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the emoji is animated
|
||||
*/
|
||||
get animated() {
|
||||
return Emoji.#get(this.id).animated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the emoji is marked as mature
|
||||
*/
|
||||
get mature() {
|
||||
return Emoji.#get(this.id).nsfw;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Whether the emoji is marked as mature
|
||||
*/
|
||||
get mature() {
|
||||
return this.collection.getUnderlyingObject(this.id).nsfw;
|
||||
}
|
||||
}
|
||||
|
||||
+243
-287
@@ -1,294 +1,250 @@
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type { Message as ApiMessage } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Client } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedMessage } from "../hydration/message";
|
||||
import { MessageCollection } from "../collections";
|
||||
|
||||
/**
|
||||
* Message Class
|
||||
*/
|
||||
export class Message {
|
||||
readonly collection: MessageCollection;
|
||||
readonly id: string;
|
||||
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
/**
|
||||
* Message Class
|
||||
* Construct Message
|
||||
* @param collection Collection
|
||||
* @param id Message Id
|
||||
*/
|
||||
class Message {
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedMessage
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedMessage>>;
|
||||
static #get: (id: string) => HydratedMessage;
|
||||
constructor(collection: MessageCollection, id: string) {
|
||||
this.collection = collection;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
static {
|
||||
Message.#collection = collection as never;
|
||||
Message.#set = collection.updateUnderlyingObject as never;
|
||||
Message.#get = collection.getUnderlyingObject as never;
|
||||
/**
|
||||
* Time when this message was posted
|
||||
*/
|
||||
get createdAt() {
|
||||
return new Date(decodeTime(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute pathname to this message in the client
|
||||
*/
|
||||
get path() {
|
||||
return `${this.channel!.path}/${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to this message
|
||||
*/
|
||||
get url() {
|
||||
return this.collection.client.configuration?.app + this.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nonce value
|
||||
*/
|
||||
get nonce() {
|
||||
return this.collection.getUnderlyingObject(this.id).nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Id of channel this message was sent in
|
||||
*/
|
||||
get channelId() {
|
||||
return this.collection.getUnderlyingObject(this.id).channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel this message was sent in
|
||||
*/
|
||||
get channel() {
|
||||
return this.collection.client.channels.get(
|
||||
this.collection.getUnderlyingObject(this.id).channelId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server this message was sent in
|
||||
*/
|
||||
get server() {
|
||||
return this.channel!.server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member this message was sent by
|
||||
*/
|
||||
get member() {
|
||||
return this.collection.client.serverMembers.getByKey({
|
||||
server: this.channel!.serverId,
|
||||
user: this.authorId!,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Id of user this message was sent by
|
||||
*/
|
||||
get authorId() {
|
||||
return this.collection.getUnderlyingObject(this.id).authorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* User this message was sent by
|
||||
*/
|
||||
get author() {
|
||||
return this.collection.client.users.get(
|
||||
this.collection.getUnderlyingObject(this.id).authorId!
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Content
|
||||
*/
|
||||
get content() {
|
||||
return this.collection.getUnderlyingObject(this.id).content;
|
||||
}
|
||||
|
||||
/**
|
||||
* System message content
|
||||
*/
|
||||
get systemMessage() {
|
||||
return this.collection.getUnderlyingObject(this.id).systemMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachments
|
||||
*/
|
||||
get attachments() {
|
||||
return this.collection.getUnderlyingObject(this.id).attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time at which this message was edited
|
||||
*/
|
||||
get editedAt() {
|
||||
return this.collection.getUnderlyingObject(this.id).editedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Embeds
|
||||
*/
|
||||
get embeds() {
|
||||
return this.collection.getUnderlyingObject(this.id).embeds;
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs of users this message mentions
|
||||
*/
|
||||
get mentionIds() {
|
||||
return this.collection.getUnderlyingObject(this.id).mentionIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs of messages this message replies to
|
||||
*/
|
||||
get replyIds() {
|
||||
return this.collection.getUnderlyingObject(this.id).replyIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reactions
|
||||
*/
|
||||
get reactions() {
|
||||
return this.collection.getUnderlyingObject(this.id).reactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactions
|
||||
*/
|
||||
get interactions() {
|
||||
return this.collection.getUnderlyingObject(this.id).interactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Masquerade
|
||||
*/
|
||||
get masquerade() {
|
||||
return this.collection.getUnderlyingObject(this.id).masquerade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username for this message
|
||||
*/
|
||||
get username() {
|
||||
return (
|
||||
this.masquerade?.name ?? this.member?.nickname ?? this.author?.username
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the role colour for this message
|
||||
*/
|
||||
get roleColour() {
|
||||
return this.masquerade?.colour ?? this.member?.roleColour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the avatar URL for this message
|
||||
*/
|
||||
get avatarURL() {
|
||||
return (
|
||||
this.masqueradeAvatarURL ??
|
||||
this.member?.avatarURL ??
|
||||
this.author?.avatarURL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the animated avatar URL for this message
|
||||
*/
|
||||
get animatedAvatarURL() {
|
||||
return (
|
||||
this.masqueradeAvatarURL ??
|
||||
(this.member
|
||||
? this.member?.animatedAvatarURL
|
||||
: this.author?.animatedAvatarURL)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar URL from the masquerade
|
||||
*/
|
||||
get masqueradeAvatarURL() {
|
||||
const avatar = this.masquerade?.avatar;
|
||||
return avatar ? this.collection.client.proxyFile(avatar) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populated system message
|
||||
*/
|
||||
get populatedSystemMessage() {
|
||||
const system = this.systemMessage;
|
||||
if (!system) return { type: "none" };
|
||||
|
||||
const { type } = system;
|
||||
const get = (id: string) => this.collection.client.users.get(id);
|
||||
switch (system.type) {
|
||||
case "text":
|
||||
return system;
|
||||
case "user_added":
|
||||
return { type, user: get(system.id), by: get(system.by) };
|
||||
case "user_remove":
|
||||
return { type, user: get(system.id), by: get(system.by) };
|
||||
case "user_joined":
|
||||
return { type, user: get(system.id) };
|
||||
case "user_left":
|
||||
return { type, user: get(system.id) };
|
||||
case "user_kicked":
|
||||
return { type, user: get(system.id) };
|
||||
case "user_banned":
|
||||
return { type, user: get(system.id) };
|
||||
case "channel_renamed":
|
||||
return { type, name: system.name, by: get(system.by) };
|
||||
case "channel_description_changed":
|
||||
return { type, by: get(system.by) };
|
||||
case "channel_icon_changed":
|
||||
return { type, by: get(system.by) };
|
||||
case "channel_ownership_changed":
|
||||
return { type, from: get(system.from), to: get(system.to) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch message by ID
|
||||
* @param channelId Channel ID
|
||||
* @param messageId Message ID
|
||||
* @returns Message
|
||||
*/
|
||||
static async fetch(
|
||||
channelId: string,
|
||||
messageId: string
|
||||
): Promise<Message | undefined> {
|
||||
const message = Message.#collection.get(messageId);
|
||||
if (message) return message;
|
||||
|
||||
const data = await client.api.get(
|
||||
`/channels/${channelId as ""}/messages/${messageId as ""}`
|
||||
);
|
||||
return new Message(messageId, data);
|
||||
}
|
||||
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* Construct Message
|
||||
* @param id Message Id
|
||||
*/
|
||||
constructor(id: string, data?: ApiMessage) {
|
||||
this.id = id;
|
||||
Message.#collection.create(id, "message", this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: string, data?: ApiMessage) {
|
||||
return client.messages.get(id) ?? new Message(id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time when this message was posted
|
||||
*/
|
||||
get createdAt() {
|
||||
return new Date(decodeTime(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Absolute pathname to this message in the client
|
||||
*/
|
||||
get path() {
|
||||
return `${this.channel!.path}/${this.id}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to this message
|
||||
*/
|
||||
get url() {
|
||||
return client.configuration?.app + this.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nonce value
|
||||
*/
|
||||
get nonce() {
|
||||
return Message.#get(this.id).nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Id of channel this message was sent in
|
||||
*/
|
||||
get channelId() {
|
||||
return Message.#get(this.id).channelId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel this message was sent in
|
||||
*/
|
||||
get channel() {
|
||||
return client.channels.get(Message.#get(this.id).channelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Server this message was sent in
|
||||
*/
|
||||
get server() {
|
||||
return this.channel!.server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Member this message was sent by
|
||||
*/
|
||||
get member() {
|
||||
return client.serverMembers.getByKey({
|
||||
server: this.channel!.serverId,
|
||||
user: this.authorId!,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Id of user this message was sent by
|
||||
*/
|
||||
get authorId() {
|
||||
return Message.#get(this.id).authorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* User this message was sent by
|
||||
*/
|
||||
get author() {
|
||||
return client.users.get(Message.#get(this.id).authorId!);
|
||||
}
|
||||
|
||||
/**
|
||||
* Content
|
||||
*/
|
||||
get content() {
|
||||
return Message.#get(this.id).content;
|
||||
}
|
||||
|
||||
/**
|
||||
* System message content
|
||||
*/
|
||||
get systemMessage() {
|
||||
return Message.#get(this.id).systemMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attachments
|
||||
*/
|
||||
get attachments() {
|
||||
return Message.#get(this.id).attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time at which this message was edited
|
||||
*/
|
||||
get editedAt() {
|
||||
return Message.#get(this.id).editedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Embeds
|
||||
*/
|
||||
get embeds() {
|
||||
return Message.#get(this.id).embeds;
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs of users this message mentions
|
||||
*/
|
||||
get mentionIds() {
|
||||
return Message.#get(this.id).mentionIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs of messages this message replies to
|
||||
*/
|
||||
get replyIds() {
|
||||
return Message.#get(this.id).replyIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reactions
|
||||
*/
|
||||
get reactions() {
|
||||
return Message.#get(this.id).reactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactions
|
||||
*/
|
||||
get interactions() {
|
||||
return Message.#get(this.id).interactions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Masquerade
|
||||
*/
|
||||
get masquerade() {
|
||||
return Message.#get(this.id).masquerade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the username for this message
|
||||
*/
|
||||
get username() {
|
||||
return (
|
||||
this.masquerade?.name ?? this.member?.nickname ?? this.author?.username
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the role colour for this message
|
||||
*/
|
||||
get roleColour() {
|
||||
return this.masquerade?.colour ?? this.member?.roleColour;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the avatar URL for this message
|
||||
*/
|
||||
get avatarURL() {
|
||||
return (
|
||||
this.masqueradeAvatarURL ??
|
||||
this.member?.avatarURL ??
|
||||
this.author?.avatarURL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the animated avatar URL for this message
|
||||
*/
|
||||
get animatedAvatarURL() {
|
||||
return (
|
||||
this.masqueradeAvatarURL ??
|
||||
(this.member
|
||||
? this.member?.animatedAvatarURL
|
||||
: this.author?.animatedAvatarURL)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar URL from the masquerade
|
||||
*/
|
||||
get masqueradeAvatarURL() {
|
||||
const avatar = this.masquerade?.avatar;
|
||||
return avatar ? client.proxyFile(avatar) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populated system message
|
||||
*/
|
||||
get populatedSystemMessage() {
|
||||
const system = this.systemMessage;
|
||||
if (!system) return { type: "none" };
|
||||
|
||||
const { type } = system;
|
||||
const get = (id: string) => client.users.get(id);
|
||||
switch (system.type) {
|
||||
case "text":
|
||||
return system;
|
||||
case "user_added":
|
||||
return { type, user: get(system.id), by: get(system.by) };
|
||||
case "user_remove":
|
||||
return { type, user: get(system.id), by: get(system.by) };
|
||||
case "user_joined":
|
||||
return { type, user: get(system.id) };
|
||||
case "user_left":
|
||||
return { type, user: get(system.id) };
|
||||
case "user_kicked":
|
||||
return { type, user: get(system.id) };
|
||||
case "user_banned":
|
||||
return { type, user: get(system.id) };
|
||||
case "channel_renamed":
|
||||
return { type, name: system.name, by: get(system.by) };
|
||||
case "channel_description_changed":
|
||||
return { type, by: get(system.by) };
|
||||
case "channel_icon_changed":
|
||||
return { type, by: get(system.by) };
|
||||
case "channel_ownership_changed":
|
||||
return { type, from: get(system.from), to: get(system.to) };
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+253
-288
@@ -1,326 +1,291 @@
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type { Server as ApiServer, Category } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Channel, Client } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedServer } from "../hydration/server";
|
||||
import { ServerCollection } from "../collections";
|
||||
import { bitwiseAndEq, calculatePermission } from "../permissions/calculator";
|
||||
import { Permission } from "../permissions/definitions";
|
||||
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
import { Channel } from "./Channel";
|
||||
|
||||
/**
|
||||
* Server Class
|
||||
*/
|
||||
export class Server {
|
||||
readonly collection: ServerCollection;
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* Server Class
|
||||
* Construct Server
|
||||
* @param collection Collection
|
||||
* @param id Id
|
||||
*/
|
||||
class Server {
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedServer
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedServer>>;
|
||||
static #get: (id: string) => HydratedServer;
|
||||
constructor(collection: ServerCollection, id: string) {
|
||||
this.collection = collection;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
static {
|
||||
Server.#collection = collection as never;
|
||||
Server.#set = collection.updateUnderlyingObject as never;
|
||||
Server.#get = collection.getUnderlyingObject as never;
|
||||
}
|
||||
/**
|
||||
* Time when this server was created
|
||||
*/
|
||||
get createdAt() {
|
||||
return new Date(decodeTime(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch server by ID
|
||||
* @param id ID
|
||||
* @returns Server
|
||||
*/
|
||||
static async fetch(id: string): Promise<Server | undefined> {
|
||||
const server = Server.#collection.get(id);
|
||||
if (server) return server;
|
||||
/**
|
||||
* Owner's user ID
|
||||
*/
|
||||
get ownerId() {
|
||||
return this.collection.getUnderlyingObject(this.id).ownerId;
|
||||
}
|
||||
|
||||
const data = await client.api.get(`/servers/${id as ""}`);
|
||||
return new Server(id, data);
|
||||
}
|
||||
/**
|
||||
* Owner
|
||||
*/
|
||||
get owner() {
|
||||
return this.collection.client.users.get(
|
||||
this.collection.getUnderlyingObject(this.id).ownerId
|
||||
);
|
||||
}
|
||||
|
||||
readonly id: string;
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
get name() {
|
||||
return this.collection.getUnderlyingObject(this.id).name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
constructor(id: string, data?: ApiServer) {
|
||||
this.id = id;
|
||||
Server.#collection.create(id, "server", this, data);
|
||||
}
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
get description() {
|
||||
return this.collection.getUnderlyingObject(this.id).description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: string, data?: ApiServer) {
|
||||
return client.servers.get(id) ?? new Server(id, data);
|
||||
}
|
||||
/**
|
||||
* Icon
|
||||
*/
|
||||
get icon() {
|
||||
return this.collection.getUnderlyingObject(this.id).icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time when this server was created
|
||||
*/
|
||||
get createdAt() {
|
||||
return new Date(decodeTime(this.id));
|
||||
}
|
||||
/**
|
||||
* Banner
|
||||
*/
|
||||
get banner() {
|
||||
return this.collection.getUnderlyingObject(this.id).banner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Owner's user ID
|
||||
*/
|
||||
get ownerId() {
|
||||
return Server.#get(this.id).ownerId;
|
||||
}
|
||||
/**
|
||||
* Channel IDs
|
||||
*/
|
||||
get channelIds() {
|
||||
return this.collection.getUnderlyingObject(this.id).channelIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Owner
|
||||
*/
|
||||
get owner() {
|
||||
return client.users.get(Server.#get(this.id).ownerId);
|
||||
}
|
||||
/**
|
||||
* Channels
|
||||
*/
|
||||
get channels() {
|
||||
return [...this.collection.getUnderlyingObject(this.id).channelIds.values()]
|
||||
.map((id) => this.collection.client.channels.get(id)!)
|
||||
.filter((x) => x);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name
|
||||
*/
|
||||
get name() {
|
||||
return Server.#get(this.id).name;
|
||||
}
|
||||
/**
|
||||
* Categories
|
||||
*/
|
||||
get categories() {
|
||||
return this.collection.getUnderlyingObject(this.id).categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
get description() {
|
||||
return Server.#get(this.id).description;
|
||||
}
|
||||
/**
|
||||
* System message channels
|
||||
*/
|
||||
get systemMessages() {
|
||||
return this.collection.getUnderlyingObject(this.id).systemMessages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Icon
|
||||
*/
|
||||
get icon() {
|
||||
return Server.#get(this.id).icon;
|
||||
}
|
||||
/**
|
||||
* Roles
|
||||
*/
|
||||
get roles() {
|
||||
return this.collection.getUnderlyingObject(this.id).roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Banner
|
||||
*/
|
||||
get banner() {
|
||||
return Server.#get(this.id).banner;
|
||||
}
|
||||
/**
|
||||
* Default permissions
|
||||
*/
|
||||
get defaultPermissions() {
|
||||
return this.collection.getUnderlyingObject(this.id).defaultPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel IDs
|
||||
*/
|
||||
get channelIds() {
|
||||
return Server.#get(this.id).channelIds;
|
||||
}
|
||||
/**
|
||||
* Server flags
|
||||
*/
|
||||
get flags() {
|
||||
return this.collection.getUnderlyingObject(this.id).flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channels
|
||||
*/
|
||||
get channels() {
|
||||
return [...Server.#get(this.id).channelIds.values()]
|
||||
.map((id) => client.channels.get(id)!)
|
||||
.filter((x) => x);
|
||||
}
|
||||
/**
|
||||
* Whether analytics are enabled for this server
|
||||
*/
|
||||
get analytics() {
|
||||
return this.collection.getUnderlyingObject(this.id).analytics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Categories
|
||||
*/
|
||||
get categories() {
|
||||
return Server.#get(this.id).categories;
|
||||
}
|
||||
/**
|
||||
* Whether this server is publicly discoverable
|
||||
*/
|
||||
get discoverable() {
|
||||
return this.collection.getUnderlyingObject(this.id).discoverable;
|
||||
}
|
||||
|
||||
/**
|
||||
* System message channels
|
||||
*/
|
||||
get systemMessages() {
|
||||
return Server.#get(this.id).systemMessages;
|
||||
}
|
||||
/**
|
||||
* Whether this server is marked as mature
|
||||
*/
|
||||
get mature() {
|
||||
return this.collection.getUnderlyingObject(this.id).nsfw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Roles
|
||||
*/
|
||||
get roles() {
|
||||
return Server.#get(this.id).roles;
|
||||
}
|
||||
/**
|
||||
* Get an array of ordered categories with their respective channels.
|
||||
* Uncategorised channels are returned in `id="default"` category.
|
||||
*/
|
||||
get orderedChannels(): (Omit<Category, "channels"> & {
|
||||
channels: Channel[];
|
||||
})[] {
|
||||
const uncategorised = new Set(this.channels.map((channel) => channel.id));
|
||||
|
||||
/**
|
||||
* Default permissions
|
||||
*/
|
||||
get defaultPermissions() {
|
||||
return Server.#get(this.id).defaultPermissions;
|
||||
}
|
||||
const elements = [];
|
||||
let defaultCategory;
|
||||
|
||||
/**
|
||||
* Server flags
|
||||
*/
|
||||
get flags() {
|
||||
return Server.#get(this.id).flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether analytics are enabled for this server
|
||||
*/
|
||||
get analytics() {
|
||||
return Server.#get(this.id).analytics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this server is publicly discoverable
|
||||
*/
|
||||
get discoverable() {
|
||||
return Server.#get(this.id).discoverable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this server is marked as mature
|
||||
*/
|
||||
get mature() {
|
||||
return Server.#get(this.id).nsfw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of ordered categories with their respective channels.
|
||||
* Uncategorised channels are returned in `id="default"` category.
|
||||
*/
|
||||
get orderedChannels(): (Omit<Category, "channels"> & {
|
||||
channels: Channel[];
|
||||
})[] {
|
||||
const uncategorised = new Set(this.channels.map((channel) => channel.id));
|
||||
|
||||
const elements = [];
|
||||
let defaultCategory;
|
||||
|
||||
const categories = this.categories;
|
||||
if (categories) {
|
||||
for (const category of categories) {
|
||||
const channels = [];
|
||||
for (const key of category.channels) {
|
||||
if (uncategorised.delete(key)) {
|
||||
channels.push(client.channels.get(key)!);
|
||||
}
|
||||
const categories = this.categories;
|
||||
if (categories) {
|
||||
for (const category of categories) {
|
||||
const channels = [];
|
||||
for (const key of category.channels) {
|
||||
if (uncategorised.delete(key)) {
|
||||
channels.push(this.collection.client.channels.get(key)!);
|
||||
}
|
||||
|
||||
const cat = {
|
||||
...category,
|
||||
channels,
|
||||
};
|
||||
|
||||
if (cat.id === "default") {
|
||||
if (channels.length === 0) continue;
|
||||
|
||||
defaultCategory = cat;
|
||||
}
|
||||
|
||||
elements.push(cat);
|
||||
}
|
||||
}
|
||||
|
||||
if (uncategorised.size > 0) {
|
||||
const channels = [...uncategorised].map(
|
||||
(key) => client.channels.get(key)!
|
||||
);
|
||||
const cat = {
|
||||
...category,
|
||||
channels,
|
||||
};
|
||||
|
||||
if (defaultCategory) {
|
||||
defaultCategory.channels = [...defaultCategory.channels, ...channels];
|
||||
} else {
|
||||
elements.unshift({
|
||||
id: "default",
|
||||
title: "Default",
|
||||
channels,
|
||||
});
|
||||
if (cat.id === "default") {
|
||||
if (channels.length === 0) continue;
|
||||
|
||||
defaultCategory = cat;
|
||||
}
|
||||
|
||||
elements.push(cat);
|
||||
}
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default channel for this server
|
||||
*/
|
||||
get defaultChannel(): Channel | undefined {
|
||||
return this.orderedChannels.find((cat) => cat.channels.length)
|
||||
?.channels[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an ordered array of roles with their IDs attached.
|
||||
* The highest ranking roles will be first followed by lower
|
||||
* ranking roles. This is dictated by the "rank" property
|
||||
* which is smaller for higher priority roles.
|
||||
*/
|
||||
get orderedRoles() {
|
||||
const roles = this.roles;
|
||||
return roles
|
||||
? [...roles.entries()]
|
||||
.map(([id, role]) => ({ id, ...role }))
|
||||
.sort((a, b) => (a.rank || 0) - (b.rank || 0))
|
||||
: [];
|
||||
}
|
||||
|
||||
get unread() {
|
||||
return false;
|
||||
}
|
||||
|
||||
get mentions() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the server's icon
|
||||
*/
|
||||
get iconURL() {
|
||||
return client.generateFileURL(this.icon, { max_side: 256 });
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the server's animated icon
|
||||
*/
|
||||
get animatedIconURL() {
|
||||
return client.generateFileURL(this.icon, { max_side: 256 }, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the server's banner
|
||||
*/
|
||||
get bannerURL() {
|
||||
return client.generateFileURL(this.banner, {
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Own member object for this server
|
||||
*/
|
||||
get member() {
|
||||
return client.serverMembers.getByKey({
|
||||
server: this.id,
|
||||
user: client.user!.id,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission the currently authenticated user has against this server
|
||||
*/
|
||||
get permission() {
|
||||
return calculatePermission(client, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we have a given permission in a server
|
||||
* @param permission Permission Names
|
||||
* @returns Whether we have this permission
|
||||
*/
|
||||
havePermission(...permission: (keyof typeof Permission)[]) {
|
||||
return bitwiseAndEq(
|
||||
this.permission,
|
||||
...permission.map((x) => Permission[x])
|
||||
if (uncategorised.size > 0) {
|
||||
const channels = [...uncategorised].map(
|
||||
(key) => this.collection.client.channels.get(key)!
|
||||
);
|
||||
|
||||
if (defaultCategory) {
|
||||
defaultCategory.channels = [...defaultCategory.channels, ...channels];
|
||||
} else {
|
||||
elements.unshift({
|
||||
id: "default",
|
||||
title: "Default",
|
||||
channels,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default channel for this server
|
||||
*/
|
||||
get defaultChannel(): Channel | undefined {
|
||||
return this.orderedChannels.find((cat) => cat.channels.length)?.channels[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an ordered array of roles with their IDs attached.
|
||||
* The highest ranking roles will be first followed by lower
|
||||
* ranking roles. This is dictated by the "rank" property
|
||||
* which is smaller for higher priority roles.
|
||||
*/
|
||||
get orderedRoles() {
|
||||
const roles = this.roles;
|
||||
return roles
|
||||
? [...roles.entries()]
|
||||
.map(([id, role]) => ({ id, ...role }))
|
||||
.sort((a, b) => (a.rank || 0) - (b.rank || 0))
|
||||
: [];
|
||||
}
|
||||
|
||||
get unread() {
|
||||
return false;
|
||||
}
|
||||
|
||||
get mentions() {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the server's icon
|
||||
*/
|
||||
get iconURL() {
|
||||
return this.collection.client.generateFileURL(this.icon, { max_side: 256 });
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the server's animated icon
|
||||
*/
|
||||
get animatedIconURL() {
|
||||
return this.collection.client.generateFileURL(
|
||||
this.icon,
|
||||
{ max_side: 256 },
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the server's banner
|
||||
*/
|
||||
get bannerURL() {
|
||||
return this.collection.client.generateFileURL(this.banner, {
|
||||
max_side: 256,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Own member object for this server
|
||||
*/
|
||||
get member() {
|
||||
return this.collection.client.serverMembers.getByKey({
|
||||
server: this.id,
|
||||
user: this.collection.client.user!.id,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Permission the currently authenticated user has against this server
|
||||
*/
|
||||
get permission() {
|
||||
return calculatePermission(this.collection.client, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we have a given permission in a server
|
||||
* @param permission Permission Names
|
||||
* @returns Whether we have this permission
|
||||
*/
|
||||
havePermission(...permission: (keyof typeof Permission)[]) {
|
||||
return bitwiseAndEq(
|
||||
this.permission,
|
||||
...permission.map((x) => Permission[x])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+175
-213
@@ -1,13 +1,12 @@
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
import type { MemberCompositeKey } from "revolt-api";
|
||||
|
||||
import type { Member as ApiMember, MemberCompositeKey } from "revolt-api";
|
||||
|
||||
import { Channel, Client, FileArgs, Server } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedServerMember } from "../hydration/serverMember";
|
||||
import { ServerMemberCollection } from "../collections";
|
||||
import { bitwiseAndEq, calculatePermission } from "../permissions/calculator";
|
||||
import { Permission } from "../permissions/definitions";
|
||||
|
||||
import { Channel } from "./Channel";
|
||||
import { Server } from "./Server";
|
||||
|
||||
/**
|
||||
* Deterministic conversion of member composite key to string ID
|
||||
* @param key Key
|
||||
@@ -17,221 +16,184 @@ function key(key: MemberCompositeKey) {
|
||||
return key.server + key.user;
|
||||
}
|
||||
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
/**
|
||||
* Server Member Class
|
||||
*/
|
||||
export class ServerMember {
|
||||
readonly collection: ServerMemberCollection;
|
||||
readonly id: MemberCompositeKey;
|
||||
|
||||
/**
|
||||
* Server Member Class
|
||||
* Construct Server Member
|
||||
* @param collection Collection
|
||||
* @param id Id
|
||||
*/
|
||||
class ServerMember {
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedServerMember
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedServerMember>>;
|
||||
static #get: (id: string) => HydratedServerMember;
|
||||
constructor(collection: ServerMemberCollection, id: MemberCompositeKey) {
|
||||
this.collection = collection;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
static {
|
||||
ServerMember.#collection = collection as never;
|
||||
ServerMember.#set = collection.updateUnderlyingObject as never;
|
||||
ServerMember.#get = collection.getUnderlyingObject as never;
|
||||
/**
|
||||
* Server this member belongs to
|
||||
*/
|
||||
get server() {
|
||||
return this.collection.client.servers.get(this.id.server);
|
||||
}
|
||||
|
||||
/**
|
||||
* User corresponding to this member
|
||||
*/
|
||||
get user() {
|
||||
return this.collection.client.users.get(this.id.user);
|
||||
}
|
||||
|
||||
/**
|
||||
* When this user joined the server
|
||||
*/
|
||||
get joinedAt() {
|
||||
return this.collection.getUnderlyingObject(key(this.id)).joinedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nickname
|
||||
*/
|
||||
get nickname() {
|
||||
return this.collection.getUnderlyingObject(key(this.id)).nickname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar
|
||||
*/
|
||||
get avatar() {
|
||||
return this.collection.getUnderlyingObject(key(this.id)).avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of role IDs
|
||||
*/
|
||||
get roles() {
|
||||
return this.collection.getUnderlyingObject(key(this.id)).roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time at which timeout expires
|
||||
*/
|
||||
get timeout() {
|
||||
return this.collection.getUnderlyingObject(key(this.id)).timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ordered list of roles for this member, from lowest to highest priority.
|
||||
*/
|
||||
get orderedRoles() {
|
||||
const server = this.server!;
|
||||
return (
|
||||
this.roles
|
||||
?.map((id) => ({
|
||||
id,
|
||||
...server.roles?.get(id),
|
||||
}))
|
||||
.sort((a, b) => b.rank! - a.rank!) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Member's currently hoisted role.
|
||||
*/
|
||||
get hoistedRole() {
|
||||
const roles = this.orderedRoles.filter((x) => x.hoist);
|
||||
if (roles.length > 0) {
|
||||
return roles[roles.length - 1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Member's current role colour.
|
||||
*/
|
||||
get roleColour() {
|
||||
const roles = this.orderedRoles.filter((x) => x.colour);
|
||||
if (roles.length > 0) {
|
||||
return roles[roles.length - 1].colour;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Member's ranking.
|
||||
* Smaller values are ranked as higher priotity.
|
||||
*/
|
||||
get ranking() {
|
||||
if (this.id.user === this.server?.ownerId) {
|
||||
return -Infinity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch member by ID
|
||||
* @param id Member ID
|
||||
* @returns Member
|
||||
*/
|
||||
static async fetch(
|
||||
id: MemberCompositeKey
|
||||
): Promise<ServerMember | undefined> {
|
||||
const channel = ServerMember.#collection.get(key(id));
|
||||
if (channel) return channel;
|
||||
|
||||
const data = await client.api.get(
|
||||
`/servers/${id.server as ""}/members/${id.user as ""}`
|
||||
);
|
||||
return new ServerMember(id, data);
|
||||
const roles = this.orderedRoles;
|
||||
if (roles.length > 0) {
|
||||
return roles[roles.length - 1].rank!;
|
||||
} else {
|
||||
return Infinity;
|
||||
}
|
||||
}
|
||||
|
||||
readonly id: MemberCompositeKey;
|
||||
/**
|
||||
* Get the permissions that this member has against a certain object
|
||||
* @param target Target object to check permissions against
|
||||
* @returns Permissions that this member has
|
||||
*/
|
||||
getPermissions(target: Server | Channel) {
|
||||
return calculatePermission(this.collection.client, target, {
|
||||
member: this,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
constructor(id: MemberCompositeKey, data?: ApiMember) {
|
||||
this.id = id;
|
||||
ServerMember.#collection.create(key(id), "serverMember", this, data);
|
||||
}
|
||||
/**
|
||||
* Check whether a member has a certain permission against a certain object
|
||||
* @param target Target object to check permissions against
|
||||
* @param permission Permission names to check for
|
||||
* @returns Whether the member has this permission
|
||||
*/
|
||||
hasPermission(
|
||||
target: Server | Channel,
|
||||
...permission: (keyof typeof Permission)[]
|
||||
) {
|
||||
return bitwiseAndEq(
|
||||
this.getPermissions(target),
|
||||
...permission.map((x) => Permission[x])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: MemberCompositeKey, data?: ApiMember) {
|
||||
return client.serverMembers.get(key(id)) ?? new ServerMember(id, data);
|
||||
}
|
||||
/**
|
||||
* Checks whether the target member has a higher rank than this member.
|
||||
* @param target The member to compare against
|
||||
* @returns Whether this member is inferior to the target
|
||||
*/
|
||||
inferiorTo(target: ServerMember) {
|
||||
return target.ranking < this.ranking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Server this member belongs to
|
||||
*/
|
||||
get server() {
|
||||
return client.servers.get(this.id.server);
|
||||
}
|
||||
/**
|
||||
* URL to the member's avatar
|
||||
*/
|
||||
get avatarURL() {
|
||||
return (
|
||||
this.collection.client.generateFileURL(this.avatar, { max_side: 256 }) ??
|
||||
this.user?.avatarURL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* User corresponding to this member
|
||||
*/
|
||||
get user() {
|
||||
return client.users.get(this.id.user);
|
||||
}
|
||||
|
||||
/**
|
||||
* When this user joined the server
|
||||
*/
|
||||
get joinedAt() {
|
||||
return ServerMember.#get(key(this.id)).joinedAt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Nickname
|
||||
*/
|
||||
get nickname() {
|
||||
return ServerMember.#get(key(this.id)).nickname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar
|
||||
*/
|
||||
get avatar() {
|
||||
return ServerMember.#get(key(this.id)).avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of role IDs
|
||||
*/
|
||||
get roles() {
|
||||
return ServerMember.#get(key(this.id)).roles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time at which timeout expires
|
||||
*/
|
||||
get timeout() {
|
||||
return ServerMember.#get(key(this.id)).timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ordered list of roles for this member, from lowest to highest priority.
|
||||
*/
|
||||
get orderedRoles() {
|
||||
const server = this.server!;
|
||||
return (
|
||||
this.roles
|
||||
?.map((id) => ({
|
||||
id,
|
||||
...server.roles?.get(id),
|
||||
}))
|
||||
.sort((a, b) => b.rank! - a.rank!) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Member's currently hoisted role.
|
||||
*/
|
||||
get hoistedRole() {
|
||||
const roles = this.orderedRoles.filter((x) => x.hoist);
|
||||
if (roles.length > 0) {
|
||||
return roles[roles.length - 1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Member's current role colour.
|
||||
*/
|
||||
get roleColour() {
|
||||
const roles = this.orderedRoles.filter((x) => x.colour);
|
||||
if (roles.length > 0) {
|
||||
return roles[roles.length - 1].colour;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Member's ranking.
|
||||
* Smaller values are ranked as higher priotity.
|
||||
*/
|
||||
get ranking() {
|
||||
if (this.id.user === this.server?.ownerId) {
|
||||
return -Infinity;
|
||||
}
|
||||
|
||||
const roles = this.orderedRoles;
|
||||
if (roles.length > 0) {
|
||||
return roles[roles.length - 1].rank!;
|
||||
} else {
|
||||
return Infinity;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the permissions that this member has against a certain object
|
||||
* @param target Target object to check permissions against
|
||||
* @returns Permissions that this member has
|
||||
*/
|
||||
getPermissions(target: Server | Channel) {
|
||||
return calculatePermission(client, target, { member: this });
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a member has a certain permission against a certain object
|
||||
* @param target Target object to check permissions against
|
||||
* @param permission Permission names to check for
|
||||
* @returns Whether the member has this permission
|
||||
*/
|
||||
hasPermission(
|
||||
target: Server | Channel,
|
||||
...permission: (keyof typeof Permission)[]
|
||||
) {
|
||||
return bitwiseAndEq(
|
||||
this.getPermissions(target),
|
||||
...permission.map((x) => Permission[x])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the target member has a higher rank than this member.
|
||||
* @param target The member to compare against
|
||||
* @returns Whether this member is inferior to the target
|
||||
*/
|
||||
inferiorTo(target: ServerMember) {
|
||||
return target.ranking < this.ranking;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the member's avatar
|
||||
*/
|
||||
get avatarURL() {
|
||||
return (
|
||||
client.generateFileURL(this.avatar, { max_side: 256 }) ??
|
||||
this.user?.avatarURL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the member's animated avatar
|
||||
*/
|
||||
get animatedAvatarURL() {
|
||||
return (
|
||||
client.generateFileURL(this.avatar, { max_side: 256 }, true) ??
|
||||
this.user?.animatedAvatarURL
|
||||
);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* URL to the member's animated avatar
|
||||
*/
|
||||
get animatedAvatarURL() {
|
||||
return (
|
||||
this.collection.client.generateFileURL(
|
||||
this.avatar,
|
||||
{ max_side: 256 },
|
||||
true
|
||||
) ?? this.user?.animatedAvatarURL
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+149
-189
@@ -1,201 +1,161 @@
|
||||
import { SetStoreFunction } from "solid-js/store";
|
||||
|
||||
import type { User as ApiUser } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { Client, FileArgs } from "../Client";
|
||||
import { StoreCollection } from "../collections/Collection";
|
||||
import { HydratedUser } from "../hydration/user";
|
||||
import { UserCollection } from "../collections";
|
||||
import { U32_MAX, UserPermission } from "../permissions/definitions";
|
||||
|
||||
export default (
|
||||
client: Client,
|
||||
collection: StoreCollection<unknown, unknown>
|
||||
) =>
|
||||
/**
|
||||
* User Class
|
||||
*/
|
||||
export class User {
|
||||
readonly collection: UserCollection;
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* User Class
|
||||
* Construct User
|
||||
* @param collection Collection
|
||||
* @param id Id
|
||||
*/
|
||||
class User {
|
||||
static #collection: StoreCollection<
|
||||
InstanceType<typeof this>,
|
||||
HydratedUser
|
||||
>;
|
||||
static #set: SetStoreFunction<Record<string, HydratedUser>>;
|
||||
static #get: (id: string) => HydratedUser;
|
||||
constructor(collection: UserCollection, id: string) {
|
||||
this.collection = collection;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
static {
|
||||
User.#collection = collection as never;
|
||||
User.#set = collection.updateUnderlyingObject as never;
|
||||
User.#get = collection.getUnderlyingObject as never;
|
||||
/**
|
||||
* Time when this user created their account
|
||||
*/
|
||||
get createdAt() {
|
||||
return new Date(decodeTime(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Username
|
||||
*/
|
||||
get username() {
|
||||
return this.collection.getUnderlyingObject(this.id).username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar
|
||||
*/
|
||||
get avatar() {
|
||||
return this.collection.getUnderlyingObject(this.id).avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Badges
|
||||
*/
|
||||
get badges() {
|
||||
return this.collection.getUnderlyingObject(this.id).badges;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Status
|
||||
*/
|
||||
get status() {
|
||||
return this.collection.getUnderlyingObject(this.id).status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relationship with user
|
||||
*/
|
||||
get relationship() {
|
||||
return this.collection.getUnderlyingObject(this.id).relationship;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is online
|
||||
*/
|
||||
get online() {
|
||||
return this.collection.getUnderlyingObject(this.id).online;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is privileged
|
||||
*/
|
||||
get privileged() {
|
||||
return this.collection.getUnderlyingObject(this.id).privileged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags
|
||||
*/
|
||||
get flags() {
|
||||
return this.collection.getUnderlyingObject(this.id).flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bot information
|
||||
*/
|
||||
get bot() {
|
||||
return this.collection.getUnderlyingObject(this.id).bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the user's default avatar
|
||||
*/
|
||||
get defaultAvatarURL() {
|
||||
return `${this.collection.client.baseURL}/users/${this.id}/default_avatar`;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the user's avatar
|
||||
*/
|
||||
get avatarURL() {
|
||||
return (
|
||||
this.collection.client.generateFileURL(this.avatar, { max_side: 256 }) ??
|
||||
this.defaultAvatarURL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the user's animated avatar
|
||||
*/
|
||||
get animatedAvatarURL() {
|
||||
return (
|
||||
this.collection.client.generateFileURL(
|
||||
this.avatar,
|
||||
{ max_side: 256 },
|
||||
true
|
||||
) ?? this.defaultAvatarURL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permissions against this user
|
||||
*/
|
||||
get permission() {
|
||||
let permissions = 0;
|
||||
switch (this.relationship) {
|
||||
case "Friend":
|
||||
case "User":
|
||||
return U32_MAX;
|
||||
case "Blocked":
|
||||
case "BlockedOther":
|
||||
return UserPermission.Access;
|
||||
case "Incoming":
|
||||
case "Outgoing":
|
||||
permissions = UserPermission.Access;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch user by ID
|
||||
* @param id ID
|
||||
* @returns User
|
||||
*/
|
||||
static async fetch(id: string): Promise<User | undefined> {
|
||||
const user = this.#collection.get(id);
|
||||
if (user) return user;
|
||||
|
||||
const data = await client.api.get(`/users/${id as ""}`);
|
||||
return new User(id, data);
|
||||
}
|
||||
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* Construct
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
constructor(id: string, data?: ApiUser) {
|
||||
this.id = id;
|
||||
User.#collection.create(id, "user", this, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
static new(id: string, data?: ApiUser) {
|
||||
return client.users.get(id) ?? new User(id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Time when this user created their account
|
||||
*/
|
||||
get createdAt() {
|
||||
return new Date(decodeTime(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Username
|
||||
*/
|
||||
get username() {
|
||||
return User.#get(this.id).username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avatar
|
||||
*/
|
||||
get avatar() {
|
||||
return User.#get(this.id).avatar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Badges
|
||||
*/
|
||||
get badges() {
|
||||
return User.#get(this.id).badges;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Status
|
||||
*/
|
||||
get status() {
|
||||
return User.#get(this.id).status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Relationship with user
|
||||
*/
|
||||
get relationship() {
|
||||
return User.#get(this.id).relationship;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is online
|
||||
*/
|
||||
get online() {
|
||||
return User.#get(this.id).online;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the user is privileged
|
||||
*/
|
||||
get privileged() {
|
||||
return User.#get(this.id).privileged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flags
|
||||
*/
|
||||
get flags() {
|
||||
return User.#get(this.id).flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bot information
|
||||
*/
|
||||
get bot() {
|
||||
return User.#get(this.id).bot;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the user's default avatar
|
||||
*/
|
||||
get defaultAvatarURL() {
|
||||
return `${client.baseURL}/users/${this.id}/default_avatar`;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the user's avatar
|
||||
*/
|
||||
get avatarURL() {
|
||||
return (
|
||||
client.generateFileURL(this.avatar, { max_side: 256 }) ??
|
||||
this.defaultAvatarURL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* URL to the user's animated avatar
|
||||
*/
|
||||
get animatedAvatarURL() {
|
||||
return (
|
||||
client.generateFileURL(this.avatar, { max_side: 256 }, true) ??
|
||||
this.defaultAvatarURL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permissions against this user
|
||||
*/
|
||||
get permission() {
|
||||
let permissions = 0;
|
||||
switch (this.relationship) {
|
||||
case "Friend":
|
||||
case "User":
|
||||
return U32_MAX;
|
||||
case "Blocked":
|
||||
case "BlockedOther":
|
||||
return UserPermission.Access;
|
||||
case "Incoming":
|
||||
case "Outgoing":
|
||||
permissions = UserPermission.Access;
|
||||
if (
|
||||
this.collection.client.channels
|
||||
.toList()
|
||||
.find(
|
||||
(channel) =>
|
||||
(channel.type === "Group" || channel.type === "DirectMessage") &&
|
||||
channel.recipientIds?.includes(this.collection.client.user!.id)
|
||||
) ||
|
||||
this.collection.client.serverMembers
|
||||
.toList()
|
||||
.find((member) => member.id.user === this.collection.client.user!.id)
|
||||
) {
|
||||
if (this.collection.client.user?.bot || this.bot) {
|
||||
permissions |= UserPermission.SendMessage;
|
||||
}
|
||||
|
||||
if (
|
||||
client.channels
|
||||
.toList()
|
||||
.find(
|
||||
(channel) =>
|
||||
(channel.type === "Group" || channel.type === "DirectMessage") &&
|
||||
channel.recipientIds?.includes(client.user!.id)
|
||||
) ||
|
||||
client.serverMembers
|
||||
.toList()
|
||||
.find((member) => member.id.user === client.user!.id)
|
||||
) {
|
||||
if (client.user?.bot || this.bot) {
|
||||
permissions |= UserPermission.SendMessage;
|
||||
}
|
||||
|
||||
permissions |= UserPermission.Access | UserPermission.ViewProfile;
|
||||
}
|
||||
|
||||
return permissions;
|
||||
permissions |= UserPermission.Access | UserPermission.ViewProfile;
|
||||
}
|
||||
};
|
||||
|
||||
return permissions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
export { Channel } from "./Channel";
|
||||
export { Emoji } from "./Emoji";
|
||||
export { Message } from "./Message";
|
||||
export { Server } from "./Server";
|
||||
export { ServerMember } from "./ServerMember";
|
||||
export { User } from "./User";
|
||||
+233
-17
@@ -1,31 +1,247 @@
|
||||
import { MemberCompositeKey } from "revolt-api";
|
||||
import type * as API from "revolt-api";
|
||||
|
||||
import { Channel, Emoji, Message, Server, ServerMember, User } from "..";
|
||||
import { HydratedChannel } from "../hydration/channel";
|
||||
import { HydratedEmoji } from "../hydration/emoji";
|
||||
import { HydratedMessage } from "../hydration/message";
|
||||
import { HydratedServer } from "../hydration/server";
|
||||
import { HydratedServerMember } from "../hydration/serverMember";
|
||||
import { HydratedUser } from "../hydration/user";
|
||||
import { Client } from "..";
|
||||
import {
|
||||
Channel,
|
||||
Emoji,
|
||||
Message,
|
||||
Server,
|
||||
ServerMember,
|
||||
User,
|
||||
} from "../classes";
|
||||
import {
|
||||
HydratedChannel,
|
||||
HydratedEmoji,
|
||||
HydratedMessage,
|
||||
HydratedServer,
|
||||
HydratedServerMember,
|
||||
HydratedUser,
|
||||
} from "../hydration";
|
||||
|
||||
import { StoreCollection } from "./Collection";
|
||||
|
||||
export class ChannelCollection extends StoreCollection<
|
||||
class ClassCollection<T, V> extends StoreCollection<T, V> {
|
||||
readonly client: Client;
|
||||
|
||||
constructor(client: Client) {
|
||||
super();
|
||||
this.client = client;
|
||||
}
|
||||
}
|
||||
|
||||
export class ChannelCollection extends ClassCollection<
|
||||
Channel,
|
||||
HydratedChannel
|
||||
> {}
|
||||
export class EmojiCollection extends StoreCollection<Emoji, HydratedEmoji> {}
|
||||
export class MessageCollection extends StoreCollection<
|
||||
> {
|
||||
/**
|
||||
* Fetch channel by ID
|
||||
* @param id Id
|
||||
* @returns Channel
|
||||
*/
|
||||
async fetch(id: string): Promise<Channel | undefined> {
|
||||
const channel = this.get(id);
|
||||
if (channel) return channel;
|
||||
const data = await this.client.api.get(`/channels/${id as ""}`);
|
||||
return this.getOrCreate(data._id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
getOrCreate(id: string, data: API.Channel) {
|
||||
if (this.has(id)) {
|
||||
return this.get(id)!;
|
||||
} else {
|
||||
const instance = new Channel(this, id);
|
||||
this.create(id, "channel", instance, data);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class EmojiCollection extends ClassCollection<Emoji, HydratedEmoji> {
|
||||
/**
|
||||
* Fetch emoji by ID
|
||||
* @param id Id
|
||||
* @returns Emoji
|
||||
*/
|
||||
async fetch(id: string): Promise<Emoji | undefined> {
|
||||
const emoji = this.get(id);
|
||||
if (emoji) return emoji;
|
||||
const data = await this.client.api.get(`/custom/emoji/${id as ""}`);
|
||||
return this.getOrCreate(data._id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
getOrCreate(id: string, data: API.Emoji) {
|
||||
if (this.has(id)) {
|
||||
return this.get(id)!;
|
||||
} else {
|
||||
const instance = new Emoji(this, id);
|
||||
this.create(id, "emoji", instance, data);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MessageCollection extends ClassCollection<
|
||||
Message,
|
||||
HydratedMessage
|
||||
> {}
|
||||
export class ServerCollection extends StoreCollection<Server, HydratedServer> {}
|
||||
export class UserCollection extends StoreCollection<User, HydratedUser> {}
|
||||
export class ServerMemberCollection extends StoreCollection<
|
||||
> {
|
||||
/**
|
||||
* Fetch message by Id
|
||||
* @param channelId Channel Id
|
||||
* @param messageId Message Id
|
||||
* @returns Message
|
||||
*/
|
||||
async fetch(
|
||||
channelId: string,
|
||||
messageId: string
|
||||
): Promise<Message | undefined> {
|
||||
const message = this.get(messageId);
|
||||
if (message) return message;
|
||||
|
||||
const data = await this.client.api.get(
|
||||
`/channels/${channelId as ""}/messages/${messageId as ""}`
|
||||
);
|
||||
|
||||
return this.getOrCreate(data._id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
getOrCreate(id: string, data: API.Message) {
|
||||
if (this.has(id)) {
|
||||
return this.get(id)!;
|
||||
} else {
|
||||
const instance = new Message(this, id);
|
||||
this.create(id, "message", instance, data);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerCollection extends ClassCollection<Server, HydratedServer> {
|
||||
/**
|
||||
* Fetch server by ID
|
||||
* @param id Id
|
||||
* @returns Server
|
||||
*/
|
||||
async fetch(id: string): Promise<Server | undefined> {
|
||||
const server = this.get(id);
|
||||
if (server) return server;
|
||||
const data = await this.client.api.get(`/servers/${id as ""}`);
|
||||
return this.getOrCreate(data._id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
getOrCreate(id: string, data: API.Server) {
|
||||
if (this.has(id)) {
|
||||
return this.get(id)!;
|
||||
} else {
|
||||
const instance = new Server(this, id);
|
||||
this.create(id, "server", instance, data);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class UserCollection extends ClassCollection<User, HydratedUser> {
|
||||
/**
|
||||
* Fetch user by ID
|
||||
* @param id Id
|
||||
* @returns User
|
||||
*/
|
||||
async fetch(id: string): Promise<User | undefined> {
|
||||
const user = this.get(id);
|
||||
if (user) return user;
|
||||
const data = await this.client.api.get(`/users/${id as ""}`);
|
||||
return this.getOrCreate(data._id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
getOrCreate(id: string, data: API.User) {
|
||||
if (this.has(id)) {
|
||||
return this.get(id)!;
|
||||
} else {
|
||||
const instance = new User(this, id);
|
||||
this.create(id, "user", instance, data);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerMemberCollection extends ClassCollection<
|
||||
ServerMember,
|
||||
HydratedServerMember
|
||||
> {
|
||||
getByKey(id: MemberCompositeKey) {
|
||||
/**
|
||||
* Check if member exists by composite key
|
||||
* @param id Id
|
||||
* @returns Whether it exists
|
||||
*/
|
||||
hasByKey(id: API.MemberCompositeKey) {
|
||||
return super.has(id.server + id.user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get member by composite key
|
||||
* @param id Id
|
||||
* @returns Member
|
||||
*/
|
||||
getByKey(id: API.MemberCompositeKey) {
|
||||
return super.get(id.server + id.user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch server member by Id
|
||||
* @param serverId Server Id
|
||||
* @param userId User Id
|
||||
* @returns Message
|
||||
*/
|
||||
async fetch(
|
||||
serverId: string,
|
||||
userId: string
|
||||
): Promise<ServerMember | undefined> {
|
||||
const member = this.get(userId);
|
||||
if (member) return member;
|
||||
|
||||
const data = await this.client.api.get(
|
||||
`/servers/${serverId as ""}/members/${userId as ""}`
|
||||
);
|
||||
|
||||
return this.getOrCreate(data._id, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
*/
|
||||
getOrCreate(id: API.MemberCompositeKey, data: API.Member) {
|
||||
if (this.hasByKey(id)) {
|
||||
return this.getByKey(id)!;
|
||||
} else {
|
||||
const instance = new ServerMember(this, id);
|
||||
this.create(id.server + id.user, "serverMember", instance, data);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,13 @@ import { serverHydration } from "./server";
|
||||
import { serverMemberHydration } from "./serverMember";
|
||||
import { userHydration } from "./user";
|
||||
|
||||
export type { HydratedChannel } from "./channel";
|
||||
export type { HydratedEmoji } from "./emoji";
|
||||
export type { HydratedMessage } from "./message";
|
||||
export type { HydratedServer } from "./server";
|
||||
export type { HydratedServerMember } from "./serverMember";
|
||||
export type { HydratedUser } from "./user";
|
||||
|
||||
/**
|
||||
* Functions to map from one object to another
|
||||
*/
|
||||
|
||||
+2
-10
@@ -1,14 +1,6 @@
|
||||
export { Client } from "./Client";
|
||||
export type { Session } from "./Client";
|
||||
|
||||
export type {
|
||||
Emoji,
|
||||
Channel,
|
||||
Server,
|
||||
ServerMember,
|
||||
User,
|
||||
Session,
|
||||
Message,
|
||||
} from "./Client";
|
||||
|
||||
export * from "./classes";
|
||||
export * from "./lib/regex";
|
||||
export * as API from "revolt-api";
|
||||
|
||||
@@ -40,7 +40,7 @@ export function calculatePermission(
|
||||
return Permission.GrantAllSafe;
|
||||
}
|
||||
|
||||
if (target instanceof client.Server) {
|
||||
if (target instanceof Server) {
|
||||
// 1. Check if owner.
|
||||
if (target.owner === user?.id) {
|
||||
return Permission.GrantAllSafe;
|
||||
|
||||
Reference in New Issue
Block a user