mirror of
https://github.com/stoatchat/javascript-client-sdk.git
synced 2026-07-20 20:16:06 -04:00
feat: implement Bot and PublicBot
This commit is contained in:
+4
-1
@@ -6,6 +6,7 @@ import type { DataLogin, RevoltConfig } from "revolt-api";
|
||||
|
||||
import { Channel, Emoji, Message, Server, ServerMember, User } from "./classes";
|
||||
import {
|
||||
BotCollection,
|
||||
ChannelCollection,
|
||||
ChannelUnreadCollection,
|
||||
EmojiCollection,
|
||||
@@ -134,13 +135,14 @@ export type ClientOptions = Partial<EventClientOptions> & {
|
||||
* Revolt.js Clients
|
||||
*/
|
||||
export class Client extends EventEmitter<Events> {
|
||||
readonly bots;
|
||||
readonly channels;
|
||||
readonly channelUnreads;
|
||||
readonly emojis;
|
||||
readonly messages;
|
||||
readonly users;
|
||||
readonly servers;
|
||||
readonly serverMembers;
|
||||
readonly users;
|
||||
|
||||
readonly api: API;
|
||||
readonly options: ClientOptions;
|
||||
@@ -199,6 +201,7 @@ export class Client extends EventEmitter<Events> {
|
||||
this.connectionFailureCount = connectionFailureCount;
|
||||
this.#setConnectionFailureCount = setConnectionFailureCount;
|
||||
|
||||
this.bots = new BotCollection(this);
|
||||
this.channels = new ChannelCollection(this);
|
||||
this.channelUnreads = new ChannelUnreadCollection(this);
|
||||
this.emojis = new EmojiCollection(this);
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
import { DataEditBot } from "revolt-api";
|
||||
import { decodeTime } from "ulid";
|
||||
|
||||
import { BotCollection } from "../collections";
|
||||
|
||||
/**
|
||||
* Bot Class
|
||||
*/
|
||||
export class Bot {
|
||||
readonly #collection: BotCollection;
|
||||
readonly id: string;
|
||||
|
||||
/**
|
||||
* Construct Bot
|
||||
* @param collection Collection
|
||||
* @param id Id
|
||||
*/
|
||||
constructor(collection: BotCollection, id: string) {
|
||||
this.#collection = collection;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to string
|
||||
* @returns String
|
||||
*/
|
||||
toString() {
|
||||
return `<@${this.id}>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Time when this user created their account
|
||||
*/
|
||||
get createdAt() {
|
||||
return new Date(decodeTime(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Corresponding user
|
||||
*/
|
||||
get user() {
|
||||
return this.#collection.client.users.get(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Owner's Id
|
||||
*/
|
||||
get ownerId() {
|
||||
return this.#collection.getUnderlyingObject(this.id).ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Owner
|
||||
*/
|
||||
get owner() {
|
||||
return this.#collection.client.users.get(this.ownerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bot Token
|
||||
*/
|
||||
get token() {
|
||||
return this.#collection.getUnderlyingObject(this.id).token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this bot can be invited by anyone
|
||||
*/
|
||||
get public() {
|
||||
return this.#collection.getUnderlyingObject(this.id).public;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this bot has analytics enabled
|
||||
*/
|
||||
get analytics() {
|
||||
return this.#collection.getUnderlyingObject(this.id).analytics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this bot shows up on Discover
|
||||
*/
|
||||
get discoverable() {
|
||||
return this.#collection.getUnderlyingObject(this.id).discoverable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interactions URL
|
||||
*/
|
||||
get interactionsUrl() {
|
||||
return this.#collection.getUnderlyingObject(this.id).interactionsUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to terms of service
|
||||
*/
|
||||
get termsOfServiceUrl() {
|
||||
return this.#collection.getUnderlyingObject(this.id).termsOfServiceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link to privacy policy
|
||||
*/
|
||||
get privacyPolicyUrl() {
|
||||
return this.#collection.getUnderlyingObject(this.id).privacyPolicyUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bot Flags
|
||||
*/
|
||||
get flags() {
|
||||
return this.#collection.getUnderlyingObject(this.id).flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a bot
|
||||
* @param data Changes
|
||||
*/
|
||||
async edit(data: DataEditBot) {
|
||||
await this.#collection.client.api.patch(`/bots/${this.id as ""}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a bot
|
||||
*/
|
||||
async delete() {
|
||||
await this.#collection.client.api.delete(`/bots/${this.id as ""}`);
|
||||
this.#collection.delete(this.id);
|
||||
}
|
||||
}
|
||||
@@ -358,21 +358,19 @@ export class Channel {
|
||||
/**
|
||||
* Delete or leave a channel
|
||||
* @param leaveSilently Whether to not send a message on leave
|
||||
* @param noRequest Whether to not send a request
|
||||
* @requires `DirectMessage`, `Group`, `TextChannel`, `VoiceChannel`
|
||||
*/
|
||||
async delete(leaveSilently?: boolean, noRequest?: boolean) {
|
||||
if (!noRequest)
|
||||
await this.#collection.client.api.delete(`/channels/${this.id as ""}`, {
|
||||
leave_silently: leaveSilently,
|
||||
});
|
||||
async delete(leaveSilently?: boolean) {
|
||||
await this.#collection.client.api.delete(`/channels/${this.id as ""}`, {
|
||||
leave_silently: leaveSilently,
|
||||
});
|
||||
|
||||
if (this.type === "DirectMessage") {
|
||||
this.#collection.updateUnderlyingObject(this.id, "active", false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.#collection.client.channels.delete(this.id);
|
||||
this.#collection.delete(this.id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { API, Channel, Client, File, Server } from "..";
|
||||
|
||||
/**
|
||||
* Public Bot Class
|
||||
*/
|
||||
export class PublicBot {
|
||||
#client: Client;
|
||||
|
||||
readonly id: string;
|
||||
readonly username: string;
|
||||
readonly avatar?: File;
|
||||
readonly description?: string;
|
||||
|
||||
/**
|
||||
* Construct Public Bot
|
||||
* @param client Client
|
||||
* @param data Data
|
||||
*/
|
||||
constructor(client: Client, data: API.PublicBot) {
|
||||
this.#client = client;
|
||||
this.id = data._id;
|
||||
this.username = data.username;
|
||||
this.avatar = data.avatar ? new File(client, data.avatar) : undefined;
|
||||
this.description = data.description!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the bot to a server
|
||||
* @param server Server
|
||||
*/
|
||||
addToServer(server: Server | string) {
|
||||
this.#client.api.post(`/bots/${this.id as ""}/invite`, {
|
||||
server: server instanceof Server ? server.id : server,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the bot to a group
|
||||
* @param group Group
|
||||
*/
|
||||
addToGroup(group: Channel | string) {
|
||||
// TODO: should use GroupChannel once that is added
|
||||
this.#client.api.post(`/bots/${this.id as ""}/invite`, {
|
||||
group: group instanceof Channel ? group.id : group,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -338,13 +338,11 @@ export class Server {
|
||||
/**
|
||||
* Delete or leave a server
|
||||
* @param leaveSilently Whether to not send a message on leave
|
||||
* @param noRequest Whether to not send a request
|
||||
*/
|
||||
async delete(leaveSilently?: boolean, avoidReq?: boolean) {
|
||||
if (!avoidReq)
|
||||
await this.#collection.client.api.delete(`/servers/${this.id as ""}`, {
|
||||
leave_silently: leaveSilently,
|
||||
});
|
||||
async delete(leaveSilently?: boolean) {
|
||||
await this.#collection.client.api.delete(`/servers/${this.id as ""}`, {
|
||||
leave_silently: leaveSilently,
|
||||
});
|
||||
|
||||
this.#collection.delete(this.id);
|
||||
}
|
||||
|
||||
+10
-8
@@ -1,9 +1,11 @@
|
||||
export { Channel } from "./Channel";
|
||||
export { Emoji } from "./Emoji";
|
||||
export { Message } from "./Message";
|
||||
export { Server } from "./Server";
|
||||
export { ServerMember } from "./ServerMember";
|
||||
export { User } from "./User";
|
||||
export { File } from "./File";
|
||||
export { ChannelUnread } from "./ChannelUnread";
|
||||
export * from "./Bot";
|
||||
export * from "./Channel";
|
||||
export * from "./ChannelUnread";
|
||||
export * from "./Emoji";
|
||||
export * from "./File";
|
||||
export * from "./Message";
|
||||
export * from "./MessageEmbed";
|
||||
export * from "./PublicBot";
|
||||
export * from "./Server";
|
||||
export * from "./ServerMember";
|
||||
export * from "./User";
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import { OwnedBotsResponse } from "revolt-api";
|
||||
|
||||
import { API, Bot, PublicBot } from "..";
|
||||
import { HydratedBot } from "../hydration/bot";
|
||||
|
||||
import { ClassCollection } from ".";
|
||||
|
||||
export class BotCollection extends ClassCollection<Bot, HydratedBot> {
|
||||
/**
|
||||
* Fetch bot by ID
|
||||
* @param id Id
|
||||
* @returns Bot
|
||||
*/
|
||||
async fetch(id: string): Promise<Bot> {
|
||||
const bot = this.get(id);
|
||||
if (bot) return bot;
|
||||
const data = await this.client.api.get(`/bots/${id as ""}`);
|
||||
this.client.users.getOrCreate(data.user._id, data.user);
|
||||
return this.getOrCreate(data.bot._id, data.bot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch owned bots
|
||||
* @returns List of bots
|
||||
*/
|
||||
async fetchOwned(): Promise<Bot[]> {
|
||||
const data = (await this.client.api.get("/bots/@me")) as OwnedBotsResponse;
|
||||
data.users.forEach((user) => this.client.users.getOrCreate(user._id, user));
|
||||
return data.bots.map((bot) => this.getOrCreate(bot._id, bot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch public bot by ID
|
||||
* @param id Id
|
||||
* @returns Public Bot
|
||||
*/
|
||||
async fetchPublic(id: string): Promise<PublicBot> {
|
||||
const data = await this.client.api.get(`/bots/${id as ""}/invite`);
|
||||
return new PublicBot(this.client, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create
|
||||
* @param id Id
|
||||
* @param data Data
|
||||
* @returns Bot
|
||||
*/
|
||||
getOrCreate(id: string, data: API.Bot) {
|
||||
if (this.has(id)) {
|
||||
return this.get(id)!;
|
||||
} else {
|
||||
const instance = new Bot(this, id);
|
||||
this.create(id, "bot", instance, this.client, data);
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a bot
|
||||
* @param name Bot name
|
||||
* @returns The newly-created bot
|
||||
*/
|
||||
async createBot(name: string) {
|
||||
const bot = await this.client.api.post(`/bots/create`, {
|
||||
name,
|
||||
});
|
||||
|
||||
return this.getOrCreate(bot._id, bot);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from "./Collection";
|
||||
|
||||
export { BotCollection } from "./BotCollection";
|
||||
export { ChannelCollection } from "./ChannelCollection";
|
||||
export { ChannelUnreadCollection } from "./ChannelUnreadCollection";
|
||||
export { EmojiCollection } from "./EmojiCollection";
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Bot as ApiBot } from "revolt-api";
|
||||
|
||||
import { Hydrate } from ".";
|
||||
|
||||
export type HydratedBot = {
|
||||
id: string;
|
||||
ownerId: string;
|
||||
token: string;
|
||||
public: boolean;
|
||||
analytics: boolean;
|
||||
discoverable: boolean;
|
||||
interactionsUrl?: string;
|
||||
termsOfServiceUrl?: string;
|
||||
privacyPolicyUrl?: string;
|
||||
flags: BotFlags;
|
||||
};
|
||||
|
||||
export const botHydration: Hydrate<ApiBot, HydratedBot> = {
|
||||
keyMapping: {
|
||||
_id: "id",
|
||||
owner: "ownerId",
|
||||
interactions_url: "interactionsUrl",
|
||||
terms_of_service_url: "termsOfServiceUrl",
|
||||
privacy_policy_url: "privacyPolicyUrl",
|
||||
},
|
||||
functions: {
|
||||
id: (bot) => bot._id,
|
||||
ownerId: (bot) => bot.owner,
|
||||
token: (bot) => bot.token,
|
||||
public: (bot) => bot.public,
|
||||
analytics: (bot) => bot.analytics!,
|
||||
discoverable: (bot) => bot.discoverable!,
|
||||
interactionsUrl: (bot) => bot.interactions_url!,
|
||||
termsOfServiceUrl: (bot) => bot.terms_of_service_url!,
|
||||
privacyPolicyUrl: (bot) => bot.privacy_policy_url!,
|
||||
flags: (bot) => bot.flags!,
|
||||
},
|
||||
initialHydration: () => ({}),
|
||||
};
|
||||
|
||||
/**
|
||||
* Flags attributed to users
|
||||
*/
|
||||
export enum BotFlags {}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { botHydration } from "./bot";
|
||||
import { channelHydration } from "./channel";
|
||||
import { channelUnreadHydration } from "./channelUnread";
|
||||
import { emojiHydration } from "./emoji";
|
||||
@@ -66,6 +67,7 @@ function hydrateInternal<Input extends object, Output>(
|
||||
}
|
||||
|
||||
const hydrators = {
|
||||
bot: botHydration,
|
||||
channel: channelHydration,
|
||||
channelUnread: channelUnreadHydration,
|
||||
emoji: emojiHydration,
|
||||
|
||||
@@ -32,13 +32,13 @@ export const userHydration: Hydrate<ApiUser, HydratedUser> = {
|
||||
functions: {
|
||||
id: (user) => user._id,
|
||||
username: (user) => user.username,
|
||||
relationship: (user) => user.relationship ?? "None",
|
||||
relationship: (user) => user.relationship!,
|
||||
|
||||
online: (user) => user.online!,
|
||||
privileged: (user) => user.privileged,
|
||||
|
||||
badges: (user) => user.badges ?? 0,
|
||||
flags: (user) => user.flags ?? 0,
|
||||
badges: (user) => user.badges!,
|
||||
flags: (user) => user.flags!,
|
||||
|
||||
avatar: (user, ctx) => new File(ctx, user.avatar!),
|
||||
status: (user) => user.status!,
|
||||
|
||||
Reference in New Issue
Block a user