feat: further auxiliary method implementations

This commit is contained in:
Paul Makles
2023-04-08 16:14:00 +01:00
parent 2ab8b33ba9
commit 2c2846386f
5 changed files with 285 additions and 12 deletions
+13
View File
@@ -265,6 +265,19 @@ export class Client {
return `${autumn.url}/${tag}/${_id}${query}`;
}
/**
* Proxy a file through January.
* @param url URL to proxy
* @returns Proxied media URL
*/
proxyFile(url: string): string | undefined {
if (this.configuration?.features.january.enabled) {
return `${
this.configuration.features.january.url
}/proxy?url=${encodeURIComponent(url)}`;
}
}
}
export type FileArgs = [
+132
View File
@@ -6,6 +6,8 @@ import type {
DataMessageSend,
Member,
Message,
OptionsMessageSearch,
Override,
User,
} from "revolt-api";
import { APIRoutes } from "revolt-api/dist/routes";
@@ -95,6 +97,24 @@ export default (
return Channel.#get(this.id).channelType;
}
/**
* Absolute pathname to this channel in the client
*/
get path() {
if (this.serverId) {
return `/server/${this.serverId}/channel/${this.id}`;
} else {
return `/channel/${this.id}`;
}
}
/**
* URL to this channel
*/
get url() {
return client.configuration?.app + this.path;
}
/**
* Channel name
*/
@@ -457,4 +477,116 @@ export default (
),
};
}
/**
* Search for messages
* @param params Message searching route data
* @returns The messages
*/
async search(params: Omit<OptionsMessageSearch, "include_users">) {
const messages = (await client.api.post(
`/channels/${this.id as ""}/search`,
params
)) as Message[];
return messages.map((message) =>
client.Message.new(message._id, message)
);
}
/**
* Search for messages including the users that sent them
* @param params Message searching route data
* @returns The messages
*/
async searchWithUsers(params: Omit<OptionsMessageSearch, "include_users">) {
const data = (await client.api.post(`/channels/${this.id as ""}/search`, {
...params,
include_users: true,
})) as { messages: Message[]; users: User[]; members?: Member[] };
return {
messages: data.messages.map((message) =>
client.Message.new(message._id, message)
),
users: data.users.map((user) => client.User.new(user._id, user)),
members: data.members?.map((member) =>
client.ServerMember.new(member._id, member)
),
};
}
async deleteMessages(ids: string[]) {
await client.api.delete(`/channels/${this.id as ""}/messages/bulk`, {
data: { ids },
});
}
/**
* Create an invite to the channel
* @returns Newly created invite code
*/
async createInvite() {
return await client.api.post(`/channels/${this.id as ""}/invites`);
}
#ackTimeout?: number;
#ackLimit?: number;
/**
* Mark a channel as read
* @param message Last read message or its ID
* @param skipRateLimiter Whether to skip the internal rate limiter
*/
async ack(message?: Message | string, skipRateLimiter?: boolean) {
const id =
(typeof message === "string" ? message : message?._id) ??
this.lastMessageId ??
ulid();
const performAck = () => {
this.#ackLimit = undefined;
client.api.put(`/channels/${this.id}/ack/${id as ""}`);
};
/* TODO: if (!client.options.ackRateLimiter || skipRateLimiter)
return performAck();*/
clearTimeout(this.#ackTimeout);
if (this.#ackLimit && +new Date() > this.#ackLimit) {
performAck();
}
// We need to use setTimeout here for both Node.js and browser.
this.#ackTimeout = setTimeout(performAck, 5000) as unknown as number;
if (!this.#ackLimit) {
this.#ackLimit = +new Date() + 15e3;
}
}
/**
* Set role permissions
* @param role_id Role Id, set to 'default' to affect all users
* @param permissions Permission value
*/
async setPermissions(role_id = "default", permissions: Override) {
return await client.api.put(
`/channels/${this.id as ""}/permissions/${role_id as ""}`,
{ permissions }
);
}
/**
* Start typing in this channel
*/
startTyping() {
client.events.send({ type: "BeginTyping", channel: this.id });
}
/**
* Stop typing in this channel
*/
stopTyping() {
client.events.send({ type: "EndTyping", channel: this.id });
}
};
+113
View File
@@ -74,6 +74,20 @@ export default (
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
*/
@@ -95,6 +109,23 @@ export default (
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
*/
@@ -178,4 +209,86 @@ export default (
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) };
}
}
};
+21 -1
View File
@@ -2,7 +2,7 @@ import { SetStoreFunction } from "solid-js/store";
import type { Member as ApiMember, MemberCompositeKey } from "revolt-api";
import { Channel, Client, Server } from "../Client";
import { Channel, Client, FileArgs, Server } from "../Client";
import { StoreCollection } from "../collections/Collection";
import { HydratedServerMember } from "../hydration/serverMember";
import { bitwiseAndEq, calculatePermission } from "../permissions/calculator";
@@ -214,4 +214,24 @@ export default (
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
);
}
};
+6 -11
View File
@@ -144,24 +144,19 @@ export default (
* URL to the user's avatar
*/
get avatarURL() {
return this.#generateAvatarURL({ max_side: 256 });
return (
client.generateFileURL(this.avatar, { max_side: 256 }) ??
this.defaultAvatarURL
);
}
/**
* URL to the user's animated avatar
*/
get animatedAvatarURL() {
return this.#generateAvatarURL({ max_side: 256 }, true);
}
/**
* Generate avatar URL
* @param args File args
* @returns URL
*/
#generateAvatarURL(...args: FileArgs) {
return (
client.generateFileURL(this.avatar, ...args) ?? this.defaultAvatarURL
client.generateFileURL(this.avatar, { max_side: 256 }, true) ??
this.defaultAvatarURL
);
}