mirror of
https://github.com/stoatchat/javascript-client-sdk.git
synced 2026-07-18 16:34:27 -04:00
Remove caching completely.
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
API_URL=https://api.revolt.chat
|
||||
EMAIL=1@example.com
|
||||
PASSWORD=password
|
||||
@@ -1 +1,30 @@
|
||||
Data-oriented library for interacting with the Revolt API.
|
||||
# revolt.js
|
||||
|
||||
 
|
||||
|
||||
**revolt.js** is a direct implementation of the entire Revolt API and provides a way to authenticate and start communicating with Revolt servers.
|
||||
|
||||
revolt.js (as of version 0.5) is (for now) bring-your-own-cache ™️, it is entirely up to you to store and keep track of any data you want to keep. This is intentional to reduce the complexity of the project while the web app is still in development, you can either use an old version of revolt.js (`<= 0.4`) but potentially miss out on features or help re-implement caching.
|
||||
|
||||
### Example Usage
|
||||
|
||||
```typescript
|
||||
let client = new Client({
|
||||
apiURL: process.env.API_URL
|
||||
});
|
||||
|
||||
client.on('ready', (packet) => {
|
||||
let user = packet.users.find(x => client.user_id! === x._id)!;
|
||||
console.info(`Logged in as ${user.username}!`);
|
||||
});
|
||||
|
||||
client.on('packet', (packet) => {
|
||||
if (packet.type === 'Message') {
|
||||
if (packet.content === 'ping') {
|
||||
client.channels.sendMessage(packet.channel, 'pong!');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.login({ email: process.env.EMAIL as string, password: process.env.PASSWORD as string })
|
||||
```
|
||||
|
||||
+2
-4
@@ -1,12 +1,11 @@
|
||||
{
|
||||
"name": "revolt.js",
|
||||
"version": "4.4.0-alpha.0",
|
||||
"version": "5.0.0-alpha.0",
|
||||
"main": "dist/index.js",
|
||||
"repository": "https://gitlab.insrt.uk/revolt/revolt.js",
|
||||
"author": "Paul Makles <insrt.uk>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@insertish/mutable": "1.1.0",
|
||||
"axios": "^0.19.2",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"exponential-backoff": "^3.1.0",
|
||||
@@ -23,13 +22,12 @@
|
||||
"@types/node": "^14.14.31",
|
||||
"@types/ws": "^7.2.1",
|
||||
"dotenv": "^8.2.0",
|
||||
"fake-indexeddb": "^3.1.2",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-typedoc": "^3.0.1",
|
||||
"idb": "^6.1.2",
|
||||
"in-publish": "^2.0.1",
|
||||
"jsdoc-babel": "^0.5.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"revolt-api": "^0.5.1-alpha.10",
|
||||
"rimraf": "^3.0.2",
|
||||
"typedoc": "^0.21.4",
|
||||
"typescript": "^4.2.3"
|
||||
|
||||
+26
-93
@@ -1,52 +1,40 @@
|
||||
import Axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
|
||||
import defaultsDeep from 'lodash.defaultsdeep';
|
||||
import { EventEmitter } from 'eventemitter3';
|
||||
import { IDBPDatabase } from 'idb';
|
||||
|
||||
import { defaultConfig } from '.';
|
||||
import { WebSocketClient } from './websocket/client';
|
||||
import { Route, RoutePath, RouteMethod } from './api/routes';
|
||||
import { Core, Auth, User, Message, Autumn } from './api/objects';
|
||||
import { ClientboundNotification } from './websocket/notifications';
|
||||
import { ClientboundNotification, ReadyPacket } from './websocket/notifications';
|
||||
|
||||
import type { User } from 'revolt-api/types/Users';
|
||||
import type { Session } from 'revolt-api/types/Auth';
|
||||
|
||||
import Users from './maps/Users';
|
||||
import Servers from './maps/Servers';
|
||||
import Members from './maps/Members';
|
||||
import Channels from './maps/Channels';
|
||||
import { RevoltConfiguration } from 'revolt-api/types/Core';
|
||||
import { SizeOptions } from 'revolt-api/types/Autumn';
|
||||
|
||||
/**
|
||||
* Client options object
|
||||
*/
|
||||
export interface ClientOptions {
|
||||
apiURL: string
|
||||
autoReconnect: boolean
|
||||
heartbeat: number
|
||||
debug: boolean
|
||||
db: IDBPDatabase
|
||||
cache: boolean
|
||||
|
||||
heartbeat: number
|
||||
autoReconnect: boolean
|
||||
}
|
||||
|
||||
export declare interface Client {
|
||||
// WebSocket related events.
|
||||
|
||||
// Event that gets fired on a successful WebSocket connection.
|
||||
on(event: 'connected', listener: () => void): this;
|
||||
// Event that gets fired when the WebSocket connection is pending connection.
|
||||
on(event: 'connecting', listener: () => void): this;
|
||||
// Event that gets fired when the WebSocket connection is dropped.
|
||||
on(event: 'dropped', listener: () => void): this;
|
||||
// Event that gets fired when the WebSocket connection is ready.
|
||||
on(event: 'ready', listener: () => void): this;
|
||||
// Event that gets fired on a new clientbound packet.
|
||||
on(event: 'ready', listener: (packet: ReadyPacket) => void): this;
|
||||
on(event: 'packet', listener: (packet: ClientboundNotification) => void): this;
|
||||
|
||||
// Message events.
|
||||
|
||||
// Event that gets fired on a new message.
|
||||
on(event: 'message', listener: (message: Message) => void): this;
|
||||
// Event that gets fired on an update (for example, edit) of a message.
|
||||
on(event: 'message/update', listener: (id: string, patch: Partial<Message>) => void): this;
|
||||
// Event that gets fired on the deletion of a message.
|
||||
on(event: 'message/delete', listener: (id: string) => void): this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,38 +53,34 @@ export const RE_MENTIONS = /<@([A-z0-9]{26})>/g;
|
||||
export const RE_SPOILER = /!!.+!!/g;
|
||||
|
||||
export class Client extends EventEmitter {
|
||||
db?: IDBPDatabase;
|
||||
heartbeat: number;
|
||||
user?: Readonly<User>;
|
||||
session?: Auth.Session;
|
||||
|
||||
session?: Session;
|
||||
user_id?: Readonly<string>;
|
||||
|
||||
websocket: WebSocketClient;
|
||||
private Axios: AxiosInstance;
|
||||
private options: ClientOptions;
|
||||
configuration?: Core.RevoltNodeConfiguration;
|
||||
configuration?: RevoltConfiguration;
|
||||
|
||||
users: Users;
|
||||
servers: Servers;
|
||||
members: Members;
|
||||
channels: Channels;
|
||||
messages: string[];
|
||||
|
||||
constructor(options: Partial<ClientOptions> = {}) {
|
||||
super();
|
||||
|
||||
this.options = defaultsDeep(options, defaultConfig);
|
||||
if (this.options.cache) throw "Cache is not supported yet.";
|
||||
|
||||
this.Axios = Axios.create({ baseURL: this.apiURL });
|
||||
this.websocket = new WebSocketClient(this);
|
||||
this.heartbeat = this.options.heartbeat;
|
||||
|
||||
if (options.db) {
|
||||
this.db = options.db;
|
||||
}
|
||||
|
||||
this.users = new Users(this);
|
||||
this.servers = new Servers(this);
|
||||
this.members = new Members(this);
|
||||
this.channels = new Channels(this);
|
||||
this.messages = [];
|
||||
|
||||
if (options.debug) {
|
||||
this.Axios.interceptors.request.use(request => {
|
||||
@@ -109,43 +93,6 @@ export class Client extends EventEmitter {
|
||||
return response
|
||||
})
|
||||
}
|
||||
|
||||
// Internal loopback.
|
||||
this.on('message', async message => {
|
||||
let channel = await this.channels.fetchMutable(message.channel);
|
||||
if (channel.channel_type === 'DirectMessage') {
|
||||
channel.active = true;
|
||||
}
|
||||
|
||||
if (channel.channel_type === 'DirectMessage' ||
|
||||
channel.channel_type === 'Group') {
|
||||
if (typeof message.content === 'string') {
|
||||
channel.last_message = {
|
||||
_id: message._id,
|
||||
author: message.author,
|
||||
short: this.markdownToText(message.content).substr(0, 128)
|
||||
}
|
||||
}
|
||||
} else if (channel.channel_type === 'TextChannel') {
|
||||
channel.last_message = message._id;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore users, channels, set the system user and specify the current user.
|
||||
* @param user_id Current user ID
|
||||
*/
|
||||
async restore(user_id?: string) {
|
||||
await this.users.restore(user => { return { ...user, online: false } });
|
||||
await this.channels.restore();
|
||||
await this.servers.restore();
|
||||
await this.members.restore();
|
||||
this.users.set({
|
||||
_id: SYSTEM_USER_ID,
|
||||
username: 'REVOLT'
|
||||
});
|
||||
if (user_id) this.user = this.users.get(user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -228,7 +175,7 @@ export class Client extends EventEmitter {
|
||||
await this.connect();
|
||||
}
|
||||
|
||||
private $generateHeaders(session: Auth.Session | undefined = this.session) {
|
||||
private $generateHeaders(session: Session | undefined = this.session) {
|
||||
return {
|
||||
'x-user-id': session?.user_id,
|
||||
'x-session-token': session?.session_token
|
||||
@@ -252,7 +199,7 @@ export class Client extends EventEmitter {
|
||||
* @param session Session data object
|
||||
* @returns An onboarding function if onboarding is required, undefined otherwise
|
||||
*/
|
||||
async useExistingSession(session: Auth.Session) {
|
||||
async useExistingSession(session: Session) {
|
||||
this.fetchConfiguration();
|
||||
await this.request('GET', '/auth/check', { headers: this.$generateHeaders(session) });
|
||||
this.session = session;
|
||||
@@ -302,17 +249,7 @@ export class Client extends EventEmitter {
|
||||
* @returns Data provided by invite.
|
||||
*/
|
||||
async joinInvite(code: string) {
|
||||
let res = await this.req('POST', `/invites/${code}` as '/invites/id');
|
||||
|
||||
switch (res.type) {
|
||||
case 'Server': {
|
||||
await this.channels.fetch(res.channel._id, res.channel);
|
||||
await this.servers.fetch(res.server._id, res.server);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
return await this.req('POST', `/invites/${code}` as '/invites/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,14 +312,9 @@ export class Client extends EventEmitter {
|
||||
*/
|
||||
reset() {
|
||||
this.websocket.disconnect();
|
||||
delete this.user;
|
||||
delete this.user_id;
|
||||
delete this.session;
|
||||
|
||||
this.users.clear();
|
||||
this.channels.clear();
|
||||
this.servers.clear();
|
||||
this.members.clear();
|
||||
|
||||
this.users = new Users(this);
|
||||
this.channels = new Channels(this);
|
||||
this.servers = new Servers(this);
|
||||
@@ -404,8 +336,9 @@ export class Client extends EventEmitter {
|
||||
* @returns Modified plain text
|
||||
*/
|
||||
markdownToText(source: string) {
|
||||
console.warn('revolt.js: Client.markdownToText() is deprecated, please stop using it.');
|
||||
return source
|
||||
.replace(
|
||||
/*.replace(
|
||||
RE_MENTIONS,
|
||||
(sub: string, ...args: any[]) => {
|
||||
const id = args[0],
|
||||
@@ -417,7 +350,7 @@ export class Client extends EventEmitter {
|
||||
|
||||
return sub;
|
||||
}
|
||||
)
|
||||
)*/
|
||||
.replace(
|
||||
RE_SPOILER,
|
||||
'<spoiler>'
|
||||
@@ -443,7 +376,7 @@ export class Client extends EventEmitter {
|
||||
* @param fallback Fallback URL
|
||||
* @returns Generated URL or nothing
|
||||
*/
|
||||
generateFileURL(attachment?: { tag: string, _id: string, content_type?: string }, options?: Autumn.SizeOptions, allowAnimation?: boolean, fallback?: string) {
|
||||
generateFileURL(attachment?: { tag: string, _id: string, content_type?: string }, options?: SizeOptions, allowAnimation?: boolean, fallback?: string) {
|
||||
let autumn = this.configuration?.features.autumn;
|
||||
if (!autumn?.enabled) return fallback;
|
||||
if (!attachment) return fallback;
|
||||
|
||||
@@ -1,349 +0,0 @@
|
||||
export namespace Core {
|
||||
export interface RevoltNodeConfiguration {
|
||||
revolt: string,
|
||||
features: {
|
||||
registration: boolean,
|
||||
captcha: {
|
||||
enabled: boolean,
|
||||
key: string,
|
||||
},
|
||||
email: boolean,
|
||||
invite_only: string,
|
||||
autumn: {
|
||||
enabled: boolean,
|
||||
url: string
|
||||
},
|
||||
january: {
|
||||
enabled: boolean,
|
||||
url: string
|
||||
},
|
||||
voso: {
|
||||
enabled: boolean,
|
||||
url: string,
|
||||
ws: string
|
||||
}
|
||||
},
|
||||
ws: string,
|
||||
app: string,
|
||||
vapid: string
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Auth {
|
||||
export interface Session {
|
||||
id?: string,
|
||||
user_id: string,
|
||||
session_token: string
|
||||
}
|
||||
}
|
||||
export type AttachmentMetadata = (
|
||||
{ type: 'File' } |
|
||||
{ type: 'Text' } |
|
||||
{ type: 'Audio' } |
|
||||
{ type: 'Image', width: number, height: number } |
|
||||
{ type: 'Video', width: number, height: number }
|
||||
);
|
||||
|
||||
export type Attachment = {
|
||||
_id: string,
|
||||
tag: string,
|
||||
size: number,
|
||||
filename: string,
|
||||
metadata: AttachmentMetadata,
|
||||
content_type: string,
|
||||
};
|
||||
|
||||
export type EmbedImage = {
|
||||
url: string,
|
||||
width: number,
|
||||
height: number,
|
||||
size: 'Large' | 'Preview'
|
||||
};
|
||||
|
||||
export type EmbedVideo = {
|
||||
url: string,
|
||||
width: number,
|
||||
height: number
|
||||
};
|
||||
|
||||
export type EmbedSpecial = (
|
||||
{ type: 'None' } |
|
||||
{ type: 'YouTube', id: string } |
|
||||
{ type: 'Twitch', content_type: 'Channel' | 'Video' | 'Clip', id: string } |
|
||||
{ type: 'Spotify', content_type: string, id: string } |
|
||||
{ type: 'Soundcloud' } |
|
||||
{ type: 'Bandcamp', content_type: 'Album' | 'Track', id: string }
|
||||
);
|
||||
|
||||
export type Embed = (
|
||||
{
|
||||
type: 'None'
|
||||
} | {
|
||||
type: 'Website',
|
||||
url?: string,
|
||||
special?: EmbedSpecial,
|
||||
|
||||
title?: string,
|
||||
description?: string,
|
||||
image?: EmbedImage,
|
||||
video?: EmbedVideo,
|
||||
|
||||
site_name?: string,
|
||||
icon_url?: string,
|
||||
color?: string
|
||||
} | ({
|
||||
type: 'Image'
|
||||
} & EmbedImage)
|
||||
);
|
||||
|
||||
export namespace Users {
|
||||
export enum Relationship {
|
||||
None = "None",
|
||||
User = "User",
|
||||
Friend = "Friend",
|
||||
Outgoing = "Outgoing",
|
||||
Incoming = "Incoming",
|
||||
Blocked = "Blocked",
|
||||
BlockedOther = "BlockedOther",
|
||||
}
|
||||
|
||||
export type Relationships = { _id: string, status: Relationship }[];
|
||||
|
||||
export enum Presence {
|
||||
Online = "Online",
|
||||
Idle = "Idle",
|
||||
Busy = "Busy",
|
||||
Invisible = "Invisible"
|
||||
}
|
||||
|
||||
export type Status = {
|
||||
text?: string
|
||||
presence?: Presence
|
||||
}
|
||||
|
||||
export interface User {
|
||||
_id: string,
|
||||
username: string,
|
||||
avatar?: Attachment,
|
||||
relations?: Relationships,
|
||||
|
||||
badges?: number,
|
||||
status?: Status,
|
||||
|
||||
relationship?: Relationship,
|
||||
online?: boolean
|
||||
}
|
||||
|
||||
export interface Profile {
|
||||
content?: string,
|
||||
background?: Attachment
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Channels {
|
||||
export type LastMessage = {
|
||||
_id: string,
|
||||
author: string,
|
||||
short: string
|
||||
}
|
||||
|
||||
export type SavedMessagesChannel = {
|
||||
_id: string,
|
||||
channel_type: 'SavedMessages',
|
||||
user: string
|
||||
}
|
||||
|
||||
export type DirectMessageChannel = {
|
||||
_id: string,
|
||||
channel_type: 'DirectMessage',
|
||||
|
||||
active: boolean,
|
||||
recipients: string[],
|
||||
last_message: LastMessage
|
||||
}
|
||||
|
||||
export type GroupChannel = {
|
||||
_id: string,
|
||||
channel_type: 'Group',
|
||||
recipients: string[],
|
||||
|
||||
name: string,
|
||||
owner: string,
|
||||
description?: string,
|
||||
|
||||
last_message: LastMessage,
|
||||
icon?: Attachment,
|
||||
|
||||
permissions?: number
|
||||
}
|
||||
|
||||
export type ServerChannel = {
|
||||
_id: string,
|
||||
server: string,
|
||||
|
||||
name: string,
|
||||
description: string,
|
||||
icon?: Attachment,
|
||||
|
||||
default_permissions?: number,
|
||||
role_permissions?: { [key: string]: number }
|
||||
}
|
||||
|
||||
export type TextChannel = ServerChannel & {
|
||||
channel_type: 'TextChannel',
|
||||
last_message: string
|
||||
}
|
||||
|
||||
export type VoiceChannel = ServerChannel & {
|
||||
channel_type: 'VoiceChannel'
|
||||
}
|
||||
|
||||
export type Channel = (SavedMessagesChannel | DirectMessageChannel | GroupChannel | TextChannel | VoiceChannel) & { nonce?: string }
|
||||
|
||||
export type Message = {
|
||||
_id: string,
|
||||
nonce?: string,
|
||||
channel: string,
|
||||
author: string,
|
||||
|
||||
content: string | SystemMessage,
|
||||
attachments?: Attachment[],
|
||||
edited?: { $date: string },
|
||||
embeds?: Embed[],
|
||||
mentions?: string[],
|
||||
replies?: string[]
|
||||
}
|
||||
|
||||
export type SystemMessage =
|
||||
| { type: "text"; content: string }
|
||||
| { type: "user_added"; id: string; by: string }
|
||||
| { type: "user_remove"; id: string; by: string }
|
||||
| { type: "user_joined"; id: string }
|
||||
| { type: "user_left"; id: string }
|
||||
| { type: "user_kicked"; id: string }
|
||||
| { type: "user_banned"; id: string }
|
||||
| { type: "channel_renamed"; name: string, by: string }
|
||||
| { type: "channel_description_changed"; by: string }
|
||||
| { type: "channel_icon_changed"; by: string };
|
||||
}
|
||||
|
||||
export namespace Servers {
|
||||
export type MemberCompositeKey = {
|
||||
server: string,
|
||||
user: string
|
||||
}
|
||||
|
||||
export type Member = {
|
||||
_id: MemberCompositeKey,
|
||||
|
||||
nickname?: string,
|
||||
avatar?: Attachment,
|
||||
|
||||
roles?: string[]
|
||||
}
|
||||
|
||||
export type Ban = {
|
||||
_id: MemberCompositeKey,
|
||||
reason?: string
|
||||
}
|
||||
|
||||
export type PermissionTuple = [
|
||||
number, // server permission
|
||||
number // channel permission
|
||||
]
|
||||
|
||||
export type Role = {
|
||||
name: string,
|
||||
permissions: PermissionTuple,
|
||||
colour?: string
|
||||
}
|
||||
|
||||
export type Category = {
|
||||
id: string,
|
||||
title: string,
|
||||
channels: string[]
|
||||
}
|
||||
|
||||
export type SystemMessageChannels = {
|
||||
user_joined?: string,
|
||||
user_left?: string,
|
||||
user_kicked?: string,
|
||||
user_banned?: string,
|
||||
}
|
||||
|
||||
export type Server = {
|
||||
_id: string,
|
||||
nonce?: string,
|
||||
owner: string,
|
||||
|
||||
name: string,
|
||||
description?: string,
|
||||
|
||||
channels: string[],
|
||||
categories?: Category[],
|
||||
system_messages?: SystemMessageChannels,
|
||||
|
||||
roles?: { [key: string]: Role },
|
||||
default_permissions: PermissionTuple,
|
||||
|
||||
icon?: Attachment,
|
||||
banner?: Attachment
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Invites {
|
||||
export type ServerInvite = {
|
||||
type: 'Server',
|
||||
_id: string,
|
||||
server: string,
|
||||
creator: string,
|
||||
channel: string,
|
||||
}
|
||||
|
||||
export type Invite = ServerInvite;
|
||||
|
||||
export type RetrievedInvite = {
|
||||
type: 'Server',
|
||||
server_id: string,
|
||||
server_name: string,
|
||||
server_icon?: Attachment,
|
||||
server_banner?: Attachment,
|
||||
channel_id: string,
|
||||
channel_name: string,
|
||||
channel_description?: string,
|
||||
user_name: string,
|
||||
user_avatar?: Attachment
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Sync {
|
||||
export type UserSettings = {
|
||||
[key: string]: [ number, string ]
|
||||
}
|
||||
|
||||
export type ChannelCompositeKey = {
|
||||
channel: string,
|
||||
user: string
|
||||
}
|
||||
|
||||
export interface ChannelUnread {
|
||||
_id: ChannelCompositeKey,
|
||||
|
||||
last_id: string,
|
||||
mentions?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Autumn {
|
||||
export interface SizeOptions {
|
||||
width?: number,
|
||||
height?: number,
|
||||
size?: number,
|
||||
max_side?: number
|
||||
}
|
||||
}
|
||||
|
||||
export type User = Users.User;
|
||||
export type Channel = Channels.Channel;
|
||||
export type Message = Channels.Message;
|
||||
export type Server = Servers.Server;
|
||||
@@ -1,5 +1,5 @@
|
||||
import { RelationshipStatus } from 'revolt-api/types/Users';
|
||||
import { Client } from '../Client';
|
||||
import { Users } from './objects';
|
||||
|
||||
export const UserPermission = {
|
||||
Access: 0b00000000000000000000000000000001, // 1
|
||||
@@ -56,14 +56,14 @@ export class PermissionCalculator {
|
||||
|
||||
let permissions = 0;
|
||||
switch (user.relationship) {
|
||||
case Users.Relationship.Friend:
|
||||
case Users.Relationship.User:
|
||||
case RelationshipStatus.Friend:
|
||||
case RelationshipStatus.User:
|
||||
return U32_MAX;
|
||||
case Users.Relationship.Blocked:
|
||||
case Users.Relationship.BlockedOther:
|
||||
case RelationshipStatus.Blocked:
|
||||
case RelationshipStatus.BlockedOther:
|
||||
return UserPermission.Access;
|
||||
case Users.Relationship.Incoming:
|
||||
case Users.Relationship.Outgoing:
|
||||
case RelationshipStatus.Incoming:
|
||||
case RelationshipStatus.Outgoing:
|
||||
permissions = UserPermission.Access;
|
||||
}
|
||||
|
||||
@@ -134,6 +134,8 @@ export class PermissionCalculator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
forServer(server_id: string): number {
|
||||
|
||||
+49
-54
@@ -1,4 +1,9 @@
|
||||
import { Channels, Core, Invites, Servers, Sync, Users } from './objects';
|
||||
import type { RevoltConfiguration } from 'revolt-api/types/Core';
|
||||
import type { UserSettings, ChannelUnread } from 'revolt-api/types/Sync';
|
||||
import type { RetrievedInvite, ServerInvite } from 'revolt-api/types/Invites'
|
||||
import type { Profile, Relationship, RelationshipOnly, Status, User } from 'revolt-api/types/Users';
|
||||
import type { Ban, Category, Member, PermissionTuple, Server, SystemMessageChannels } from 'revolt-api/types/Servers';
|
||||
import type { Channel, DirectMessageChannel, GroupChannel, Message, TextChannel, VoiceChannel } from 'revolt-api/types/Channels';
|
||||
|
||||
export type RemoveUserField = 'ProfileContent' | 'ProfileBackground' | 'StatusText' | 'Avatar';
|
||||
export type RemoveChannelField = 'Icon' | 'Description';
|
||||
@@ -16,7 +21,7 @@ type Routes =
|
||||
method: 'GET',
|
||||
route: `/`,
|
||||
data: undefined,
|
||||
response: Core.RevoltNodeConfiguration
|
||||
response: RevoltConfiguration
|
||||
}
|
||||
/**
|
||||
* Auth
|
||||
@@ -161,14 +166,14 @@ type Routes =
|
||||
method: 'GET',
|
||||
route: `/users/${Id}`,
|
||||
data: undefined,
|
||||
response: Users.User
|
||||
response: User
|
||||
}
|
||||
| {
|
||||
// Edit user.
|
||||
method: 'PATCH',
|
||||
route: `/users/${Id}`,
|
||||
data: {
|
||||
status?: Users.Status,
|
||||
status?: Status,
|
||||
profile?: {
|
||||
content?: string,
|
||||
background?: string
|
||||
@@ -193,37 +198,35 @@ type Routes =
|
||||
method: 'GET',
|
||||
route: `/users/${Id}/profile`,
|
||||
data: undefined,
|
||||
response: Users.Profile
|
||||
response: Profile
|
||||
}
|
||||
| {
|
||||
// Fetch all your DM conversations.
|
||||
method: 'GET',
|
||||
route: `/users/dms`,
|
||||
data: undefined,
|
||||
response: (Channels.DirectMessageChannel | Channels.GroupChannel)[]
|
||||
response: (DirectMessageChannel | GroupChannel)[]
|
||||
}
|
||||
| {
|
||||
// Open a DM with another user.
|
||||
method: 'GET',
|
||||
route: `/users/${Id}/dm`,
|
||||
data: undefined,
|
||||
response: Channels.DirectMessageChannel
|
||||
response: DirectMessageChannel
|
||||
}
|
||||
| {
|
||||
// Fetch all of your relationships with other users.
|
||||
method: 'GET',
|
||||
route: `/users/relationships`,
|
||||
data: undefined,
|
||||
response: Users.Relationships
|
||||
response: Relationship[]
|
||||
}
|
||||
| {
|
||||
// Fetch your relationship with another other user.
|
||||
method: 'GET',
|
||||
route: `/users/${Id}/relationship`,
|
||||
data: undefined,
|
||||
response: {
|
||||
status: Users.Relationship
|
||||
}
|
||||
response: RelationshipOnly
|
||||
}
|
||||
| {
|
||||
// Fetch your mutual relationships with another user.
|
||||
@@ -239,36 +242,28 @@ type Routes =
|
||||
method: 'PUT',
|
||||
route: `/users/${Id}/friend`,
|
||||
data: undefined,
|
||||
response: {
|
||||
status: Users.Relationship
|
||||
}
|
||||
response: RelationshipOnly
|
||||
}
|
||||
| {
|
||||
// Delete a friend request / remove a friend.
|
||||
method: 'DELETE',
|
||||
route: `/users/${Id}/friend`,
|
||||
data: undefined,
|
||||
response: {
|
||||
status: Users.Relationship
|
||||
}
|
||||
response: RelationshipOnly
|
||||
}
|
||||
| {
|
||||
// Block a user.
|
||||
method: 'PUT',
|
||||
route: `/users/${Id}/block`,
|
||||
data: undefined,
|
||||
response: {
|
||||
status: Users.Relationship
|
||||
}
|
||||
response: RelationshipOnly
|
||||
}
|
||||
| {
|
||||
// Unblock a user.
|
||||
method: 'DELETE',
|
||||
route: `/users/${Id}/block`,
|
||||
data: undefined,
|
||||
response: {
|
||||
status: Users.Relationship
|
||||
}
|
||||
response: RelationshipOnly
|
||||
}
|
||||
| {
|
||||
// Fetch a user's avatar.
|
||||
@@ -292,14 +287,14 @@ type Routes =
|
||||
method: 'GET',
|
||||
route: `/channels/${Id}`,
|
||||
data: undefined,
|
||||
response: Channels.Channel
|
||||
response: Channel
|
||||
}
|
||||
| {
|
||||
// Fetch a channel's members by ID.
|
||||
method: 'GET',
|
||||
route: `/channels/${Id}/members`,
|
||||
data: undefined,
|
||||
response: Users.User[]
|
||||
response: User[]
|
||||
}
|
||||
| {
|
||||
// Edit a group channel.
|
||||
@@ -330,7 +325,7 @@ type Routes =
|
||||
nonce: string,
|
||||
users: string[]
|
||||
},
|
||||
response: Channels.GroupChannel
|
||||
response: GroupChannel
|
||||
}
|
||||
| {
|
||||
// Create an invite to a channel.
|
||||
@@ -382,14 +377,14 @@ type Routes =
|
||||
attachments?: string[],
|
||||
replies?: { id: string, mention: boolean }[]
|
||||
},
|
||||
response: Channels.Message
|
||||
response: Message
|
||||
}
|
||||
| {
|
||||
// Fetch message from channel.
|
||||
method: 'GET',
|
||||
route: `/channels/${Id}/messages/${Id}`,
|
||||
data: undefined,
|
||||
response: Channels.Message
|
||||
response: Message
|
||||
}
|
||||
| {
|
||||
// Query messages from channel (optionally include users as well).
|
||||
@@ -403,10 +398,10 @@ type Routes =
|
||||
nearby?: string,
|
||||
include_users?: boolean
|
||||
},
|
||||
response: Channels.Message[] | {
|
||||
messages: Channels.Message[],
|
||||
users: Users.User[],
|
||||
members?: Servers.Member[]
|
||||
response: Message[] | {
|
||||
messages: Message[],
|
||||
users: User[],
|
||||
members?: Member[]
|
||||
}
|
||||
}
|
||||
| {
|
||||
@@ -422,10 +417,10 @@ type Routes =
|
||||
sort?: 'Relevance' | 'Latest' | 'Oldest',
|
||||
include_users?: boolean
|
||||
},
|
||||
response: Channels.Message[] | {
|
||||
messages: Channels.Message[],
|
||||
users: Users.User[],
|
||||
members?: Servers.Member[]
|
||||
response: Message[] | {
|
||||
messages: Message[],
|
||||
users: User[],
|
||||
members?: Member[]
|
||||
}
|
||||
}
|
||||
| {
|
||||
@@ -436,7 +431,7 @@ type Routes =
|
||||
ids: string[]
|
||||
},
|
||||
response: {
|
||||
updated: Channels.Message[],
|
||||
updated: Message[],
|
||||
deleted: string[]
|
||||
}
|
||||
}
|
||||
@@ -467,14 +462,14 @@ type Routes =
|
||||
name: string,
|
||||
nonce: string
|
||||
},
|
||||
response: Servers.Server
|
||||
response: Server
|
||||
}
|
||||
| {
|
||||
// Fetch a server by ID.
|
||||
method: 'GET',
|
||||
route: `/servers/${Id}`,
|
||||
data: undefined,
|
||||
response: Servers.Server
|
||||
response: Server
|
||||
}
|
||||
| {
|
||||
// Delete or leave a server.
|
||||
@@ -492,8 +487,8 @@ type Routes =
|
||||
description?: string,
|
||||
icon?: string,
|
||||
banner?: string,
|
||||
categories?: Servers.Category[],
|
||||
system_messages?: Servers.SystemMessageChannels,
|
||||
categories?: Category[],
|
||||
system_messages?: SystemMessageChannels,
|
||||
remove?: RemoveServerField
|
||||
},
|
||||
response: undefined
|
||||
@@ -504,8 +499,8 @@ type Routes =
|
||||
route: `/servers/${Id}/members`,
|
||||
data: undefined,
|
||||
response: {
|
||||
members: Servers.Member[],
|
||||
users: Users.User[]
|
||||
members: Member[],
|
||||
users: User[]
|
||||
}
|
||||
}
|
||||
| {
|
||||
@@ -513,7 +508,7 @@ type Routes =
|
||||
method: 'GET',
|
||||
route: `/servers/${Id}/members/${Id}`,
|
||||
data: undefined,
|
||||
response: Servers.Member
|
||||
response: Member
|
||||
}
|
||||
| {
|
||||
// Edit a server member
|
||||
@@ -556,8 +551,8 @@ type Routes =
|
||||
route: `/servers/${Id}/bans`,
|
||||
data: undefined,
|
||||
response: {
|
||||
users: Pick<Users.User, '_id' | 'username' | 'avatar'>[],
|
||||
bans: Servers.Ban[]
|
||||
users: Pick<User, '_id' | 'username' | 'avatar'>[],
|
||||
bans: Ban[]
|
||||
}
|
||||
}
|
||||
| {
|
||||
@@ -570,14 +565,14 @@ type Routes =
|
||||
description?: string,
|
||||
nonce: string
|
||||
},
|
||||
response: Channels.TextChannel | Channels.VoiceChannel
|
||||
response: TextChannel | VoiceChannel
|
||||
}
|
||||
| {
|
||||
// Fetch a server's invites
|
||||
method: 'GET',
|
||||
route: `/servers/${Id}/invites`,
|
||||
data: undefined,
|
||||
response: Invites.ServerInvite[]
|
||||
response: ServerInvite[]
|
||||
}
|
||||
| {
|
||||
// Create role
|
||||
@@ -588,7 +583,7 @@ type Routes =
|
||||
},
|
||||
response: {
|
||||
id: string,
|
||||
permissions: Servers.PermissionTuple
|
||||
permissions: PermissionTuple
|
||||
}
|
||||
}
|
||||
| {
|
||||
@@ -629,7 +624,7 @@ type Routes =
|
||||
method: 'GET',
|
||||
route: `/invites/${Id}`,
|
||||
data: undefined,
|
||||
response: Invites.RetrievedInvite
|
||||
response: RetrievedInvite
|
||||
}
|
||||
| {
|
||||
// Use an invite.
|
||||
@@ -639,8 +634,8 @@ type Routes =
|
||||
response: (
|
||||
{
|
||||
type: 'Server',
|
||||
channel: Channels.TextChannel,
|
||||
server: Servers.Server
|
||||
channel: TextChannel,
|
||||
server: Server
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -661,7 +656,7 @@ type Routes =
|
||||
data: {
|
||||
keys: string[]
|
||||
},
|
||||
response: Sync.UserSettings
|
||||
response: UserSettings
|
||||
}
|
||||
| {
|
||||
// Set user settings.
|
||||
@@ -677,7 +672,7 @@ type Routes =
|
||||
method: 'GET',
|
||||
route: `/sync/unreads`,
|
||||
data: undefined,
|
||||
response: Sync.ChannelUnread[]
|
||||
response: ChannelUnread[]
|
||||
}
|
||||
/**
|
||||
* Push API
|
||||
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
export * from './Client';
|
||||
export { Channel, User, Message } from './api/objects';
|
||||
export { PermissionCalculator } from './api/permissions';
|
||||
// export { PermissionCalculator } from './api/permissions';
|
||||
|
||||
export const LIBRARY_VERSION = '4.3.3-alpha.19';
|
||||
export const LIBRARY_VERSION = '5.0.0-alpha.0';
|
||||
|
||||
export const defaultConfig = {
|
||||
apiURL: 'https://api.revolt.chat',
|
||||
autoReconnect: true,
|
||||
heartbeat: 30,
|
||||
debug: false,
|
||||
cache: false
|
||||
};
|
||||
|
||||
+25
-155
@@ -1,25 +1,15 @@
|
||||
import { Autumn, Channel, Channels as ChannelsNS, Message, Servers, User } from '../api/objects';
|
||||
import { flattenMember } from './Members';
|
||||
import type { Channel, Message, SystemMessage } from 'revolt-api/types/Channels';
|
||||
import type { Member } from 'revolt-api/types/Servers';
|
||||
import type { User } from 'revolt-api/types/Users';
|
||||
|
||||
import { ulid } from 'ulid';
|
||||
import { Client } from '..';
|
||||
import { Route } from '../api/routes';
|
||||
import Collection from './Collection';
|
||||
import { Client } from '..';
|
||||
import { ulid } from 'ulid';
|
||||
|
||||
export default class Channels extends Collection<Channel> {
|
||||
constructor(client: Client) {
|
||||
super(client, 'channels');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the recipient of a direct message channel.
|
||||
* @param id ID of the target channel
|
||||
* @returns
|
||||
* @throws An error if the channel is not of type `DirectMessage`
|
||||
*/
|
||||
getRecipient(id: string) {
|
||||
let channel = this.getThrow(id);
|
||||
if (channel.channel_type !== 'DirectMessage') throw "Not a DirectMessage.";
|
||||
return channel.recipients.find(user => user !== this.client.user?._id) as string;
|
||||
super(client);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,7 +17,7 @@ export default class Channels extends Collection<Channel> {
|
||||
* @param content Content of the system message
|
||||
* @returns The parsed system message
|
||||
*/
|
||||
tryParseSystemMessage(content: string): ChannelsNS.SystemMessage {
|
||||
tryParseSystemMessage(content: string): SystemMessage {
|
||||
try {
|
||||
return JSON.parse(content);
|
||||
} catch (e) {
|
||||
@@ -36,37 +26,12 @@ export default class Channels extends Collection<Channel> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a channel, but do not make the return value read-only
|
||||
* Fetch a channel
|
||||
* @param id Channel ID
|
||||
* @returns The channel
|
||||
*/
|
||||
async fetchMutable(id: string, data?: Channel) {
|
||||
if (this.map[id]) return this.get(id) as Channel;
|
||||
let res = data ?? await this.client.req('GET', `/channels/${id}` as '/channels/id');
|
||||
|
||||
// Fetch user information if this is the first time we are seeing this object.
|
||||
// We shouldn't need to fetch for anything apart from groups.
|
||||
if (res.channel_type === 'Group') {
|
||||
let hasUnknowns = res.recipients.find(id => typeof this.client.users.get(id) === 'undefined');
|
||||
if (hasUnknowns) {
|
||||
let members = await this.fetchMembers(id);
|
||||
for (let member of members) {
|
||||
this.client.users.set(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.set(res);
|
||||
return this.get(id) as Channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a channel and make the return value read-only
|
||||
* @param id Channel ID
|
||||
* @returns The channel in read-only state
|
||||
*/
|
||||
async fetch(id: string, data?: Channel) {
|
||||
return await this.fetchMutable(id, data) as Readonly<Channel>;
|
||||
async fetch(id: string) {
|
||||
return await this.client.req('GET', `/channels/${id}` as '/channels/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,13 +40,7 @@ export default class Channels extends Collection<Channel> {
|
||||
* @returns An array of the channel's members.
|
||||
*/
|
||||
async fetchMembers(id: string) {
|
||||
let res = await this.client.req('GET', `/channels/${id}/members` as '/channels/id/members');
|
||||
|
||||
for (let user of res) {
|
||||
this.client.users.set(user);
|
||||
}
|
||||
|
||||
return res;
|
||||
return await this.client.req('GET', `/channels/${id}/members` as '/channels/id/members');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,40 +49,15 @@ export default class Channels extends Collection<Channel> {
|
||||
* @param data Channel editing route data
|
||||
*/
|
||||
async edit(id: string, data: Route<'PATCH', '/channels/id'>["data"]) {
|
||||
let channel = this.getThrow(id);
|
||||
if (channel.channel_type === 'Group' || channel.channel_type === 'TextChannel' || channel.channel_type === 'VoiceChannel') {
|
||||
await this.client.req('PATCH', `/channels/${id}` as '/channels/id', data);
|
||||
|
||||
if (data.name) channel.name = data.name;
|
||||
if (data.description) channel.description = data.description;
|
||||
} else {
|
||||
throw "Channel is not group, text or voice channel.";
|
||||
}
|
||||
return await this.client.req('PATCH', `/channels/${id}` as '/channels/id', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a channel
|
||||
* @param id ID of the target channel
|
||||
* @param avoidRequest Whether to disable the API request, essentially making the change client-side only
|
||||
*/
|
||||
async delete(id: string, avoidRequest?: boolean) {
|
||||
let channel = this.getMutable(id);
|
||||
if (channel?.channel_type === 'SavedMessages') throw "Cannot delete Saved Messages.";
|
||||
if (!avoidRequest)
|
||||
await this.client.req('DELETE', `/channels/${id}` as '/channels/id');
|
||||
|
||||
if (!channel) return;
|
||||
if (channel.channel_type === 'TextChannel' || channel.channel_type === 'VoiceChannel') {
|
||||
let id = channel._id;
|
||||
let server = await this.client.servers.fetchMutable(channel.server);
|
||||
server.channels = server.channels.filter(x => x !== id);
|
||||
}
|
||||
|
||||
if (channel.channel_type === 'DirectMessage') {
|
||||
channel.active = false;
|
||||
} else {
|
||||
super.delete(id);
|
||||
}
|
||||
async delete(id: string) {
|
||||
return await this.client.req('DELETE', `/channels/${id}` as '/channels/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,9 +66,7 @@ export default class Channels extends Collection<Channel> {
|
||||
* @returns The newly-created group
|
||||
*/
|
||||
async createGroup(data: Route<'POST', '/channels/create'>["data"]) {
|
||||
let res = await this.client.req('POST', `/channels/create`, data);
|
||||
this.set(res);
|
||||
return this.get(res._id) as Readonly<ChannelsNS.GroupChannel>;
|
||||
return await this.client.req('POST', `/channels/create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,13 +75,7 @@ export default class Channels extends Collection<Channel> {
|
||||
* @param user_id ID of the target user
|
||||
*/
|
||||
async addMember(id: string, user_id: string) {
|
||||
let channel = this.getThrow(id);
|
||||
if (channel.channel_type !== 'Group') throw "Channel is not group channel.";
|
||||
await this.client.req('PUT', `/channels/${id}/recipients/${user_id}` as '/channels/id/recipients/id');
|
||||
channel.recipients = [
|
||||
...channel.recipients.filter(user => user !== user_id),
|
||||
user_id
|
||||
];
|
||||
return await this.client.req('PUT', `/channels/${id}/recipients/${user_id}` as '/channels/id/recipients/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,11 +84,7 @@ export default class Channels extends Collection<Channel> {
|
||||
* @param user_id ID of the target user
|
||||
*/
|
||||
async removeMember(id: string, user_id: string) {
|
||||
let channel = this.getThrow(id);
|
||||
if (channel.channel_type !== 'Group') throw "Channel is not group channel.";
|
||||
await this.client.req('DELETE', `/channels/${id}/recipients/${user_id}` as '/channels/id/recipients/id');
|
||||
channel.recipients = channel.recipients
|
||||
.filter(user => user !== user_id);
|
||||
return await this.client.req('DELETE', `/channels/${id}/recipients/${user_id}` as '/channels/id/recipients/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,14 +99,7 @@ export default class Channels extends Collection<Channel> {
|
||||
...(typeof data === 'string' ? { content: data } : data)
|
||||
};
|
||||
|
||||
this.getThrow(id);
|
||||
let message = await this.client.req('POST', `/channels/${id}/messages` as '/channels/id/messages', msg);
|
||||
if (!this.client.messages.includes(message._id)) {
|
||||
this.client.messages.push(message._id);
|
||||
this.client.emit('message', message);
|
||||
}
|
||||
|
||||
return message;
|
||||
return await this.client.req('POST', `/channels/${id}/messages` as '/channels/id/messages', msg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -211,25 +126,10 @@ export default class Channels extends Collection<Channel> {
|
||||
* Fetch multiple messages from a channel including the users that sent them
|
||||
* @param id ID of the target channel
|
||||
* @param params Message fetching route data
|
||||
* @param processUsers Whether to automatically patch user objects using fetched data.
|
||||
* @returns Object including messages and users
|
||||
*/
|
||||
async fetchMessagesWithUsers(id: string, params?: Omit<Route<'GET', '/channels/id/messages'>["data"], 'include_users'>, processUsers?: boolean) {
|
||||
let res = await this.client.request('GET', `/channels/${id}/messages` as '/channels/id/messages', { params: { ...params, include_users: true } }) as { messages: Message[], users: User[], members: Servers.Member[] };
|
||||
|
||||
if (processUsers) {
|
||||
for (let user of res.users) {
|
||||
this.client.users.set(user);
|
||||
}
|
||||
|
||||
if (res.members) {
|
||||
for (let member of res.members) {
|
||||
this.client.members.set(flattenMember(member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
async fetchMessagesWithUsers(id: string, params?: Omit<Route<'GET', '/channels/id/messages'>["data"], 'include_users'>) {
|
||||
return await this.client.request('GET', `/channels/${id}/messages` as '/channels/id/messages', { params: { ...params, include_users: true } }) as { messages: Message[], users: User[], members?: Member[] };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,22 +148,8 @@ export default class Channels extends Collection<Channel> {
|
||||
* @param params Message searching route data
|
||||
* @returns The messages
|
||||
*/
|
||||
async searchWithUsers(id: string, params: Omit<Route<'POST', '/channels/id/search'>["data"], 'include_users'>, processUsers?: boolean) {
|
||||
let res = await this.client.req('POST', `/channels/${id}/search` as '/channels/id/search', { ...params, include_users: true }) as { messages: Message[], users: User[], members: Servers.Member[] };
|
||||
|
||||
if (processUsers) {
|
||||
for (let user of res.users) {
|
||||
this.client.users.set(user);
|
||||
}
|
||||
|
||||
if (res.members) {
|
||||
for (let member of res.members) {
|
||||
this.client.members.set(flattenMember(member));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
async searchWithUsers(id: string, params: Omit<Route<'POST', '/channels/id/search'>["data"], 'include_users'>) {
|
||||
return await this.client.req('POST', `/channels/${id}/search` as '/channels/id/search', { ...params, include_users: true }) as { messages: Message[], users: User[], members?: Member[] };;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,8 +169,7 @@ export default class Channels extends Collection<Channel> {
|
||||
* @param data Message edit route data
|
||||
*/
|
||||
async editMessage(id: string, message_id: string, data: Route<'PATCH', '/channels/id/messages/id'>["data"]) {
|
||||
await this.client.req('PATCH', `/channels/${id}/messages/${message_id}` as '/channels/id/messages/id', data);
|
||||
this.client.emit('message/edit', message_id, data);
|
||||
return await this.client.req('PATCH', `/channels/${id}/messages/${message_id}` as '/channels/id/messages/id', data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -293,8 +178,7 @@ export default class Channels extends Collection<Channel> {
|
||||
* @param message_id ID of the target message
|
||||
*/
|
||||
async deleteMessage(id: string, message_id: string) {
|
||||
await this.client.req('DELETE', `/channels/${id}/messages/${message_id}` as '/channels/id/messages/id');
|
||||
this.client.emit('message/delete', message_id);
|
||||
return await this.client.req('DELETE', `/channels/${id}/messages/${message_id}` as '/channels/id/messages/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -334,18 +218,4 @@ export default class Channels extends Collection<Channel> {
|
||||
async setPermissions(id: string, role_id: string = 'default', permissions?: number) {
|
||||
return await this.client.req('PUT', `/channels/${id}/permissions/${role_id}` as '/channels/id/permissions/id', { permissions });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon URL of a channel
|
||||
* @param id ID of the target channel
|
||||
* @param options Optional query parameters to modify object
|
||||
* @param allowAnimation Whether to allow links to the original GIFs to be returned
|
||||
* @param fallback Fallback image
|
||||
*/
|
||||
getIconURL(id: string, options?: Autumn.SizeOptions, allowAnimation?: boolean, fallback?: string) {
|
||||
let channel = this.getMutable(id);
|
||||
if (channel && (channel.channel_type !== 'SavedMessages' && channel.channel_type !== 'DirectMessage')) {
|
||||
return this.client.generateFileURL(channel.icon, options, allowAnimation, fallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-16
@@ -1,23 +1,9 @@
|
||||
import { MutableMap, ObjectWithId } from "@insertish/mutable";
|
||||
import { Client } from '../Client';
|
||||
|
||||
export default class Collection<T extends ObjectWithId> extends MutableMap<T> {
|
||||
export default class Collection<T> {
|
||||
client: Client;
|
||||
|
||||
constructor(client: Client, col: string) {
|
||||
super(col, client.db);
|
||||
constructor(client: Client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an object within the collection while throwing an error if it does not exist
|
||||
* @param id ID of the object within the collection
|
||||
* @returns The item
|
||||
* @throws An error if the object does not exist
|
||||
*/
|
||||
getThrow(id: string) {
|
||||
let obj = this.getMutable(id);
|
||||
if (!obj) throw "Object does not exist.";
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-67
@@ -1,68 +1,22 @@
|
||||
import { Servers } from '../api/objects';
|
||||
import Collection from './Collection';
|
||||
import type { Member } from 'revolt-api/types/Servers';
|
||||
|
||||
import { Client } from '..';
|
||||
import Collection from './Collection';
|
||||
import { Route } from '../api/routes';
|
||||
|
||||
export type MemberFlatKey = Omit<Servers.Member, '_id'> & { _id: string };
|
||||
|
||||
export function flatKeyToObject(key: string) {
|
||||
return {
|
||||
server: key.substr(0, 26),
|
||||
user: key.substr(26)
|
||||
}
|
||||
}
|
||||
|
||||
export function objectToFlatKey(obj: { server: string, user: string }) {
|
||||
return `${obj.server}${obj.user}`;
|
||||
}
|
||||
|
||||
export function flattenMember(obj: Servers.Member): MemberFlatKey {
|
||||
return {
|
||||
...obj,
|
||||
_id: objectToFlatKey(obj._id)
|
||||
}
|
||||
}
|
||||
|
||||
export function unflattenMember(obj: MemberFlatKey): Servers.Member {
|
||||
return {
|
||||
...obj,
|
||||
_id: flatKeyToObject(obj._id)
|
||||
}
|
||||
}
|
||||
|
||||
export default class Members extends Collection<MemberFlatKey> {
|
||||
export default class Members extends Collection<Member> {
|
||||
constructor(client: Client) {
|
||||
super(client, 'members');
|
||||
}
|
||||
|
||||
findMembers(server_id: string): MemberFlatKey[] {
|
||||
return this.keys()
|
||||
.filter(key => this.map[key]._id.substr(0, 26) === server_id)
|
||||
.map(k => this.map[k])
|
||||
super(client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a member, but do not make the return value read-only
|
||||
* Fetch a member
|
||||
* @param id Server ID
|
||||
* @param user_id User ID
|
||||
* @returns The member
|
||||
*/
|
||||
async fetchMutable(id: string, user_id: string): Promise<MemberFlatKey> {
|
||||
if (this.map[id]) return this.get(id) as MemberFlatKey;
|
||||
let res = await this.client.req('GET', `/servers/${id}/members/${user_id}` as '/servers/id/members/id');
|
||||
|
||||
this.set(flattenMember(res));
|
||||
return this.get(id) as MemberFlatKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a server and make the return value read-only
|
||||
* @param id Server ID
|
||||
* @param user_id User ID
|
||||
* @returns The server in read-only state
|
||||
*/
|
||||
async fetch(id: string, user_id: string) {
|
||||
return await this.fetchMutable(id, user_id) as Readonly<MemberFlatKey>;
|
||||
async fetch(id: string, user_id: string): Promise<Member> {
|
||||
return await this.client.req('GET', `/servers/${id}/members/${user_id}` as '/servers/id/members/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,7 +37,7 @@ export default class Members extends Collection<MemberFlatKey> {
|
||||
* @returns Server member object
|
||||
*/
|
||||
async editMember(id: string, user_id: string, data: Route<'PATCH', '/servers/id/members/id'>["data"]) {
|
||||
await this.client.req('PATCH', `/servers/${id}/members/${user_id}` as '/servers/id/members/id', data);
|
||||
return await this.client.req('PATCH', `/servers/${id}/members/${user_id}` as '/servers/id/members/id', data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,7 +46,7 @@ export default class Members extends Collection<MemberFlatKey> {
|
||||
* @param user_id User ID
|
||||
*/
|
||||
async kickMember(id: string, user_id: string) {
|
||||
await this.client.req('DELETE', `/servers/${id}/members/${user_id}` as '/servers/id/members/id');
|
||||
return await this.client.req('DELETE', `/servers/${id}/members/${user_id}` as '/servers/id/members/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,16 +55,6 @@ export default class Members extends Collection<MemberFlatKey> {
|
||||
* @returns An array of the server's members and their user objects.
|
||||
*/
|
||||
async fetchMembers(id: string) {
|
||||
let res = await this.client.req('GET', `/servers/${id}/members` as '/servers/id/members');
|
||||
|
||||
for (let user of res.users) {
|
||||
this.client.users.set(user);
|
||||
}
|
||||
|
||||
for (let member of res.members) {
|
||||
this.set(flattenMember(member));
|
||||
}
|
||||
|
||||
return res;
|
||||
return await this.client.req('GET', `/servers/${id}/members` as '/servers/id/members');
|
||||
}
|
||||
}
|
||||
|
||||
+15
-86
@@ -1,11 +1,12 @@
|
||||
import { Autumn, Channels, Server, Servers as ServersNS } from '../api/objects';
|
||||
import type { Server } from 'revolt-api/types/Servers';
|
||||
|
||||
import { Client } from '..';
|
||||
import { Route } from '../api/routes';
|
||||
import Collection from './Collection';
|
||||
import { Client } from '..';
|
||||
|
||||
export default class Servers extends Collection<Server> {
|
||||
constructor(client: Client) {
|
||||
super(client, 'servers');
|
||||
super(client);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -13,27 +14,8 @@ export default class Servers extends Collection<Server> {
|
||||
* @param id Server ID
|
||||
* @returns The server
|
||||
*/
|
||||
async fetchMutable(id: string, data?: Server): Promise<Server> {
|
||||
if (this.map[id]) return this.get(id) as Server;
|
||||
let res = data ?? await this.client.req('GET', `/servers/${id}` as '/servers/id');
|
||||
|
||||
// Fetch channel information.
|
||||
for (let channel of res.channels) {
|
||||
// ! FIXME: add route for fetching all channels in future, copy code for fetching group recipients
|
||||
await this.client.channels.fetch(channel);
|
||||
}
|
||||
|
||||
this.set(res);
|
||||
return this.get(id) as Server;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a server and make the return value read-only
|
||||
* @param id Server ID
|
||||
* @returns The server in read-only state
|
||||
*/
|
||||
async fetch(id: string, data?: Server) {
|
||||
return await this.fetchMutable(id, data) as Readonly<Server>;
|
||||
async fetch(id: string): Promise<Server> {
|
||||
return await this.client.req('GET', `/servers/${id}` as '/servers/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,9 +24,7 @@ export default class Servers extends Collection<Server> {
|
||||
* @returns The newly-created server
|
||||
*/
|
||||
async createServer(data: Route<'POST', '/servers/create'>["data"]) {
|
||||
let res = await this.client.req('POST', `/servers/create`, data);
|
||||
this.set(res);
|
||||
return this.get(res._id) as Readonly<ServersNS.Server>;
|
||||
return await this.client.req('POST', `/servers/create`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -54,18 +34,7 @@ export default class Servers extends Collection<Server> {
|
||||
* @returns The newly-created channel
|
||||
*/
|
||||
async createChannel(id: string, data: Route<'POST', '/servers/id/channels'>["data"]) {
|
||||
let server = this.client.servers.getMutable(id);
|
||||
if (!server) throw "Server does not exist!";
|
||||
|
||||
let res = await this.client.req('POST', `/servers/${id}/channels` as '/servers/id/channels', data);
|
||||
this.client.channels.set(res);
|
||||
|
||||
server.channels = [
|
||||
...server.channels.filter(x => x !== res._id),
|
||||
res._id
|
||||
];
|
||||
|
||||
return this.client.channels.get(res._id) as Readonly<Channels.TextChannel | Channels.VoiceChannel>;
|
||||
return await this.client.req('POST', `/servers/${id}/channels` as '/servers/id/channels', data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,31 +43,15 @@ export default class Servers extends Collection<Server> {
|
||||
* @param data Server editing route data
|
||||
*/
|
||||
async edit(id: string, data: Route<'PATCH', '/servers/id'>["data"]) {
|
||||
let server = this.getThrow(id);
|
||||
await this.client.req('PATCH', `/servers/${id}` as '/servers/id', data);
|
||||
|
||||
if (data.name) server.name = data.name;
|
||||
if (data.description) server.description = data.description;
|
||||
return await this.client.req('PATCH', `/servers/${id}` as '/servers/id', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a guild
|
||||
* @param id ID of the target server
|
||||
*/
|
||||
async delete(id: string, avoidRequest?: boolean) {
|
||||
if (!avoidRequest)
|
||||
await this.client.req('DELETE', `/servers/${id}` as '/servers/id');
|
||||
|
||||
for (let channel of this.client.channels.toArray()) {
|
||||
if ((channel.channel_type === 'TextChannel' || channel.channel_type === 'VoiceChannel') && channel.server === id) {
|
||||
this.client.channels.delete(channel._id, true);
|
||||
}
|
||||
}
|
||||
|
||||
this.client.members.findMembers(id)
|
||||
.forEach(member => this.client.members.delete(member._id));
|
||||
|
||||
super.delete(id);
|
||||
async delete(id: string) {
|
||||
return await this.client.req('DELETE', `/servers/${id}` as '/servers/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -107,7 +60,7 @@ export default class Servers extends Collection<Server> {
|
||||
* @param user_id User ID
|
||||
*/
|
||||
async banUser(id: string, user_id: string, data: Route<'PUT', '/servers/id/bans/id'>["data"]) {
|
||||
await this.client.req('PUT', `/servers/${id}/bans/${user_id}` as '/servers/id/bans/id', data);
|
||||
return await this.client.req('PUT', `/servers/${id}/bans/${user_id}` as '/servers/id/bans/id', data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +69,7 @@ export default class Servers extends Collection<Server> {
|
||||
* @param user_id User ID
|
||||
*/
|
||||
async unbanUser(id: string, user_id: string) {
|
||||
await this.client.req('DELETE', `/servers/${id}/bans/${user_id}` as '/servers/id/bans/id');
|
||||
return await this.client.req('DELETE', `/servers/${id}/bans/${user_id}` as '/servers/id/bans/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,9 +116,7 @@ export default class Servers extends Collection<Server> {
|
||||
* @param data Role editing route data
|
||||
*/
|
||||
async editRole(id: string, role_id: string, data: Route<'PATCH', '/servers/id/roles/id'>["data"]) {
|
||||
let server = this.getThrow(id);
|
||||
if (typeof server.roles?.[role_id] === 'undefined') return;
|
||||
await this.client.req('PATCH', `/servers/${id}/roles/${role_id}` as '/servers/id/roles/id', data);
|
||||
return await this.client.req('PATCH', `/servers/${id}/roles/${role_id}` as '/servers/id/roles/id', data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -174,28 +125,6 @@ export default class Servers extends Collection<Server> {
|
||||
* @param role_id Role ID
|
||||
*/
|
||||
async deleteRole(id: string, role_id: string) {
|
||||
await this.client.req('DELETE', `/servers/${id}/roles/${role_id}` as '/servers/id/roles/id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the icon URL of a server
|
||||
* @param id ID of the target server
|
||||
* @param options Optional query parameters to modify object
|
||||
* @param allowAnimation Whether to allow links to the original GIFs to be returned
|
||||
*/
|
||||
getIconURL(id: string, options?: Autumn.SizeOptions, allowAnimation?: boolean) {
|
||||
let server = this.getMutable(id);
|
||||
return this.client.generateFileURL(server?.icon, options, allowAnimation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the banner URL of a server
|
||||
* @param id ID of the target server
|
||||
* @param options Optional query parameters to modify object
|
||||
* @param allowAnimation Whether to allow links to the original GIFs to be returned
|
||||
*/
|
||||
getBannerURL(id: string, options?: Autumn.SizeOptions, allowAnimation?: boolean) {
|
||||
let server = this.getMutable(id);
|
||||
return this.client.generateFileURL(server?.banner, options, allowAnimation);
|
||||
return await this.client.req('DELETE', `/servers/${id}/roles/${role_id}` as '/servers/id/roles/id');
|
||||
}
|
||||
}
|
||||
|
||||
+12
-72
@@ -1,11 +1,12 @@
|
||||
import Collection from './Collection';
|
||||
import { Autumn, User, Users as UsersI } from '../api/objects';
|
||||
import type { User } from 'revolt-api/types/Users';
|
||||
|
||||
import { Client } from '..';
|
||||
import Collection from './Collection';
|
||||
import { Route } from '../api/routes';
|
||||
|
||||
export default class Users extends Collection<User> {
|
||||
constructor(client: Client) {
|
||||
super(client, 'users');
|
||||
super(client);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -13,20 +14,8 @@ export default class Users extends Collection<User> {
|
||||
* @param id User ID
|
||||
* @returns The user
|
||||
*/
|
||||
async fetchMutable(id: string) {
|
||||
if (this.map[id]) return this.get(id) as User;
|
||||
let res = await this.client.req('GET', `/users/${id}` as '/users/id');
|
||||
this.set(res);
|
||||
return this.get(id) as User;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a user and make the return value read-only
|
||||
* @param id User ID
|
||||
* @returns The user in read-only state
|
||||
*/
|
||||
async fetch(id: string) {
|
||||
return await this.fetchMutable(id) as Readonly<User>;
|
||||
return await this.client.req('GET', `/users/${id}` as '/users/id');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,30 +24,7 @@ export default class Users extends Collection<User> {
|
||||
* @returns The DM channel
|
||||
*/
|
||||
async openDM(id: string) {
|
||||
this.getThrow(id);
|
||||
|
||||
let channel;
|
||||
if (id === this.client.user?._id) {
|
||||
channel = this.client
|
||||
.channels
|
||||
.toArray()
|
||||
.find(channel => channel.channel_type === 'SavedMessages');
|
||||
} else {
|
||||
channel = this.client
|
||||
.channels
|
||||
.toArray()
|
||||
.find(channel =>
|
||||
(channel.channel_type === 'DirectMessage'
|
||||
&& channel.recipients.find(user => user === id))
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof channel === 'undefined') {
|
||||
channel = await this.client.req('GET', `/users/${id}/dm` as '/users/id/dm');
|
||||
this.client.channels.set(channel);
|
||||
}
|
||||
|
||||
return this.client.channels.get(channel._id);
|
||||
return await this.client.req('GET', `/users/${id}/dm` as '/users/id/dm');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,11 +32,7 @@ export default class Users extends Collection<User> {
|
||||
* @param username Username of the target user
|
||||
*/
|
||||
async addFriend(username: string) {
|
||||
await this.client.req('PUT', `/users/${username}/friend` as '/users/id/friend');
|
||||
// ! FIXME
|
||||
// ! WE NEED TO GET THE ID HERE SOMEHOW
|
||||
// ! IDEALLY WE WANT TO CHANGE THE ONES BELOW
|
||||
// ! TO USE USERNAME AS WELL
|
||||
return await this.client.req('PUT', `/users/${username}/friend` as '/users/id/friend');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +40,7 @@ export default class Users extends Collection<User> {
|
||||
* @param id ID of the target user
|
||||
*/
|
||||
async removeFriend(id: string) {
|
||||
await this.client.req('DELETE', `/users/${id}/friend` as '/users/id/friend');
|
||||
return await this.client.req('DELETE', `/users/${id}/friend` as '/users/id/friend');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,7 +48,7 @@ export default class Users extends Collection<User> {
|
||||
* @param id ID of the target user
|
||||
*/
|
||||
async blockUser(id: string) {
|
||||
await this.client.req('PUT', `/users/${id}/block` as '/users/id/block');
|
||||
return await this.client.req('PUT', `/users/${id}/block` as '/users/id/block');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +56,7 @@ export default class Users extends Collection<User> {
|
||||
* @param id ID of the target user
|
||||
*/
|
||||
async unblockUser(id: string) {
|
||||
await this.client.req('DELETE', `/users/${id}/block` as '/users/id/block');
|
||||
return await this.client.req('DELETE', `/users/${id}/block` as '/users/id/block');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,7 +82,7 @@ export default class Users extends Collection<User> {
|
||||
* @param data User edit data object
|
||||
*/
|
||||
async editUser(data: Route<'PATCH', '/users/id'>["data"]) {
|
||||
await this.client.req('PATCH', '/users/id', data);
|
||||
return await this.client.req('PATCH', '/users/id', data);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,19 +91,7 @@ export default class Users extends Collection<User> {
|
||||
* @param password Current password
|
||||
*/
|
||||
async changeUsername(username: string, password: string) {
|
||||
await this.client.req('PATCH', '/users/id/username', { username, password });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the avatar URL of a user
|
||||
* @param id ID of the target user
|
||||
* @param options Optional query parameters to modify object
|
||||
* @param allowAnimation Whether to allow GIFs to play
|
||||
* @param disableFallback Don't return default avatar if no avatar
|
||||
*/
|
||||
getAvatarURL(id: string, options?: Autumn.SizeOptions, allowAnimation?: boolean, disableFallback?: boolean) {
|
||||
let attachment = this.getMutable(id)?.avatar;
|
||||
return this.client.generateFileURL(attachment, options, allowAnimation, disableFallback ? undefined : this.getDefaultAvatarURL(id));
|
||||
return await this.client.req('PATCH', '/users/id/username', { username, password });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,14 +101,4 @@ export default class Users extends Collection<User> {
|
||||
getDefaultAvatarURL(id: string) {
|
||||
return `${this.client.apiURL}/users/${id}/default_avatar`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the background URL of a user
|
||||
* @param profile Profile to use
|
||||
* @param options Optional query parameters to modify object
|
||||
*/
|
||||
getBackgroundURL(profile: UsersI.Profile, options?: Autumn.SizeOptions, allowAnimation?: boolean) {
|
||||
let attachment = profile?.background;
|
||||
return this.client.generateFileURL(attachment, options, allowAnimation);
|
||||
}
|
||||
}
|
||||
|
||||
+32
-120
@@ -1,125 +1,37 @@
|
||||
import { config } from 'dotenv';
|
||||
import { Client } from '.';
|
||||
config();
|
||||
|
||||
require('fake-indexeddb/auto');
|
||||
|
||||
import { openDB } from 'idb';
|
||||
import { Client } from ".";
|
||||
|
||||
openDB('Test', 1, {
|
||||
upgrade(db) {
|
||||
db.createObjectStore('channels', {
|
||||
keyPath: '_id'
|
||||
});
|
||||
db.createObjectStore('users', {
|
||||
keyPath: '_id'
|
||||
});
|
||||
db.createObjectStore('servers', {
|
||||
keyPath: '_id'
|
||||
});
|
||||
db.createObjectStore('members', {
|
||||
keyPath: '_id'
|
||||
});
|
||||
},
|
||||
}).then(db => {
|
||||
|
||||
let client = new Client({ apiURL: 'http://local.revolt.chat:8000', db, debug: false });
|
||||
|
||||
// client2
|
||||
let client2 = new Client({ apiURL: 'http://local.revolt.chat:8000', debug: false });
|
||||
client2.connect().then(() => client2.login({ email: '2@example.com', password: 'nBeNaSsukrCCYhVMKQEM', device_name: 'r.js' }))
|
||||
client2.once('ready', async () => console.log('Client 2 logged in.'))
|
||||
|
||||
// client.channels.on('mutation', console.log);
|
||||
|
||||
client.once('ready', async () => {
|
||||
console.log(`Logged in as @${client.user?.username}`);
|
||||
|
||||
let servers = client.servers.toArray();
|
||||
for (let server of servers) {
|
||||
await client.servers.delete(server._id);
|
||||
}
|
||||
|
||||
function dothing() {
|
||||
(async () => {
|
||||
let server = await client.servers.createServer({ name: 'sus amongus', nonce: ''+Math.random() });
|
||||
|
||||
let invite: string;
|
||||
let tasks = [
|
||||
async () => await client.servers.createChannel(server._id, { name: 'based!', nonce: ''+Math.random() }),
|
||||
async () => await client.channels.delete(server.channels[0]),
|
||||
// async () => await client.servers.edit(server._id, { name: ''+Math.random()*100, description: 'henlo' }),
|
||||
// async () => await client.channels.edit(server.channels[0], { name: 'edited!', description: 'haha yes' }),
|
||||
async () => invite = await client.channels.createInvite(server.channels[0]),
|
||||
async () => await client.fetchInvite(invite),
|
||||
async () => await client2.joinInvite(invite),
|
||||
async () => await client2.channels.sendMessage(server.channels[0], 'message 2'),
|
||||
async () => await client.members.fetchMembers(server._id),
|
||||
async () => await client2.members.fetchMembers(server._id),
|
||||
async () => await client.members.editMember(server._id, client.user?._id as string, { nickname: 'gamer' }),
|
||||
async () => await client.members.fetchMember(server._id, client.user?._id as string),
|
||||
|
||||
async () => console.log(client.members.toArray()),
|
||||
|
||||
async () => await client.members.kickMember(server._id, client2.user!._id),
|
||||
async () => await client2.joinInvite(invite),
|
||||
async () => await client.servers.banUser(server._id, client2.user!._id, { reason: 'bad' }),
|
||||
async () => await client2.joinInvite(invite).catch(() => console.log('Failed succesfully.')),
|
||||
|
||||
// async () => console.log(await client.servers.fetchBans(server._id)),
|
||||
|
||||
async () => await client.servers.unbanUser(server._id, client2.user!._id),
|
||||
async () => await client2.joinInvite(invite),
|
||||
async () => await client2.servers.delete(server._id),
|
||||
|
||||
() => new Promise(r => setTimeout(r, 200)),
|
||||
async () => console.log('c1', client.members.toArray()),
|
||||
async () => console.log('c2', client2.members.toArray()),
|
||||
|
||||
() => new Promise(r => setTimeout(r, 1000)),
|
||||
async () => await client.channels.sendMessage(server.channels[0], 'message 1'),
|
||||
async () => await client.deleteInvite(invite),
|
||||
async () => await client.servers.fetchInvites(server._id),
|
||||
// async () => await client.servers.delete(server._id)
|
||||
];
|
||||
|
||||
for (let task of tasks) {
|
||||
await task();
|
||||
}
|
||||
})()
|
||||
}
|
||||
|
||||
dothing();
|
||||
});
|
||||
|
||||
client.on('connecting', () => {
|
||||
console.log(`Connecting to the notifications server.`);
|
||||
});
|
||||
|
||||
client.on('connected', () => {
|
||||
console.log(`Connected to notifications server.`);
|
||||
});
|
||||
6
|
||||
client.on('dropped', () => {
|
||||
console.log(`Connection dropped.`);
|
||||
});
|
||||
|
||||
client.on('message', (msg) => {
|
||||
let user = client.users.get(msg.author);
|
||||
console.log(`[client1] @${user?.username}: ${msg.content}`);
|
||||
});
|
||||
|
||||
client2.on('message', (msg) => {
|
||||
let user = client.users.get(msg.author);
|
||||
console.log(`[client2] @${user?.username}: ${msg.content}`);
|
||||
});
|
||||
|
||||
(async () => {
|
||||
await client.connect();
|
||||
let onboarding = await client.login({ email: '1@example.com', password: 'nBeNaSsukrCCYhVMKQEM', device_name: 'r.js' });
|
||||
if (onboarding) {
|
||||
await onboarding("username", false);
|
||||
}
|
||||
})();
|
||||
// To run this example, you need to have a local Revolt server running and an existing account.
|
||||
// Copy and paste `.env.example` to `.env` and edit accordingly.
|
||||
|
||||
let client = new Client({
|
||||
apiURL: process.env.API_URL
|
||||
});
|
||||
|
||||
let username: string;
|
||||
client.on('ready', (packet) => {
|
||||
let user = packet.users.find(x => client.user_id! === x._id)!;
|
||||
username = user.username;
|
||||
console.info(`Logged in as ${username}!`);
|
||||
});
|
||||
|
||||
client.on('packet', (packet) => {
|
||||
if (packet.type === 'Message') {
|
||||
if (packet.content === 'ping') {
|
||||
client.channels.sendMessage(packet.channel, 'pong!');
|
||||
}
|
||||
|
||||
if (packet.content === 'who are you') {
|
||||
client.channels.sendMessage(packet.channel, `<@${packet.author}> I am ${username}`);
|
||||
}
|
||||
} else if (packet.type === 'UserUpdate') {
|
||||
if (packet.id === client.user_id!) {
|
||||
if (packet.data.username) {
|
||||
username = packet.data.username;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
client.login({ email: process.env.EMAIL as string, password: process.env.PASSWORD as string })
|
||||
|
||||
+4
-183
@@ -1,10 +1,9 @@
|
||||
import WebSocket from 'isomorphic-ws';
|
||||
import { backOff } from 'exponential-backoff';
|
||||
|
||||
import { Auth, Servers, User } from '../api/objects';
|
||||
import { Client, SYSTEM_USER_ID } from '..';
|
||||
import { flattenMember, MemberFlatKey, objectToFlatKey } from '../maps/Members';
|
||||
import { ServerboundNotification, ClientboundNotification } from './notifications';
|
||||
import { Session } from 'revolt-api/types/Auth';
|
||||
|
||||
export class WebSocketClient {
|
||||
client: Client;
|
||||
@@ -76,7 +75,7 @@ export class WebSocketClient {
|
||||
this.ws = ws;
|
||||
|
||||
ws.onopen = () => {
|
||||
this.send({ type: 'Authenticate', ...this.client.session as Auth.Session });
|
||||
this.send({ type: 'Authenticate', ...this.client.session as Session });
|
||||
};
|
||||
|
||||
let handle = async (msg: WebSocket.MessageEvent) => {
|
||||
@@ -98,26 +97,8 @@ export class WebSocketClient {
|
||||
break;
|
||||
}
|
||||
case 'Ready': {
|
||||
for (let user of packet.users) {
|
||||
this.client.users.setOverride(user);
|
||||
}
|
||||
|
||||
// INFO:
|
||||
// Our user object should be included in this
|
||||
// payload so we can just take it out of the map.
|
||||
this.client.user = this.client.users.get(this.client.session?.user_id as string) as User;
|
||||
|
||||
this.client.channels.clear();
|
||||
for (let channel of packet.channels) {
|
||||
this.client.channels.setOverride(channel);
|
||||
}
|
||||
|
||||
this.client.servers.clear();
|
||||
for (let server of packet.servers) {
|
||||
this.client.servers.setOverride(server);
|
||||
}
|
||||
|
||||
this.client.emit('ready');
|
||||
this.client.user_id = this.client.session!.user_id;
|
||||
this.client.emit('ready', packet);
|
||||
this.ready = true;
|
||||
resolve();
|
||||
|
||||
@@ -130,163 +111,6 @@ export class WebSocketClient {
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Message': {
|
||||
if (!this.client.messages.includes(packet._id)) {
|
||||
if (packet.author === SYSTEM_USER_ID) {
|
||||
if (typeof packet.content === 'object') {
|
||||
switch (packet.content.type) {
|
||||
case 'user_added':
|
||||
case 'user_remove':
|
||||
await this.client.users.fetch(packet.content.by);
|
||||
case 'user_left':
|
||||
await this.client.users.fetch(packet.content.id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await this.client.users.fetch(packet.author);
|
||||
}
|
||||
|
||||
if (!this.client.messages.includes(packet._id)) {
|
||||
this.client.messages.push(packet._id);
|
||||
this.client.emit('message', packet);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'MessageUpdate': this.client.emit('message/update', packet.id, packet.data); break;
|
||||
case 'MessageDelete': this.client.emit('message/delete', packet.id); break;
|
||||
|
||||
case 'ChannelCreate': {
|
||||
if (packet.channel_type === 'TextChannel' ||
|
||||
packet.channel_type === 'VoiceChannel') {
|
||||
let id = packet._id;
|
||||
let server = await this.client.servers.fetchMutable(packet.server);
|
||||
server.channels = [
|
||||
...server.channels.filter(x => x !== id),
|
||||
id
|
||||
];
|
||||
}
|
||||
|
||||
this.client.channels.set(packet);
|
||||
break;
|
||||
}
|
||||
case 'ChannelUpdate': {
|
||||
if (packet.clear) {
|
||||
switch (packet.clear) {
|
||||
case 'Icon': this.client.channels.removeField(packet.id, 'icon'); break;
|
||||
case 'Description': this.client.channels.removeField(packet.id, 'description'); break;
|
||||
}
|
||||
}
|
||||
|
||||
this.client.channels.patch(packet.id, packet.data);
|
||||
break;
|
||||
}
|
||||
case 'ChannelDelete': this.client.channels.delete(packet.id, true); break;
|
||||
case 'ChannelGroupJoin': {
|
||||
let channel = await this.client.channels.fetchMutable(packet.id);
|
||||
if (channel.channel_type !== 'Group') throw "Not a group channel.";
|
||||
let user_id = packet.user;
|
||||
await this.client.users.fetch(user_id);
|
||||
|
||||
channel.recipients = [
|
||||
...channel.recipients.filter(user => user !== user_id),
|
||||
user_id
|
||||
];
|
||||
break;
|
||||
}
|
||||
case 'ChannelGroupLeave': {
|
||||
let channel = await this.client.channels.fetchMutable(packet.id);
|
||||
if (channel.channel_type !== 'Group') throw "Not a group channel.";
|
||||
let user_id = packet.user;
|
||||
channel.recipients = channel.recipients.filter(user => user !== user_id);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ServerUpdate': {
|
||||
if (packet.clear) {
|
||||
switch (packet.clear) {
|
||||
case 'Icon': this.client.servers.removeField(packet.id, 'icon'); break;
|
||||
case 'Banner': this.client.servers.removeField(packet.id, 'banner'); break;
|
||||
case 'Description': this.client.servers.removeField(packet.id, 'description'); break;
|
||||
}
|
||||
}
|
||||
|
||||
this.client.servers.patch(packet.id, packet.data);
|
||||
break;
|
||||
}
|
||||
case 'ServerDelete': this.client.servers.delete(packet.id, true); break;
|
||||
case 'ServerMemberJoin': {
|
||||
await this.client.servers.fetchMutable(packet.id);
|
||||
await this.client.users.fetchMutable(packet.user);
|
||||
|
||||
this.client.members.set({
|
||||
_id: objectToFlatKey({ server: packet.id, user: packet.user })
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case 'ServerMemberUpdate': {
|
||||
const id = objectToFlatKey(packet.id);
|
||||
if (packet.clear) {
|
||||
switch (packet.clear) {
|
||||
case 'Avatar': this.client.members.removeField(id, 'avatar'); break;
|
||||
case 'Nickname': this.client.members.removeField(id, 'nickname'); break;
|
||||
}
|
||||
}
|
||||
|
||||
this.client.members.patch(id, packet.data as Partial<MemberFlatKey>);
|
||||
break;
|
||||
}
|
||||
case 'ServerMemberLeave': {
|
||||
if (packet.user === this.client.user?._id) {
|
||||
this.client.servers.delete(packet.id, true);
|
||||
} else {
|
||||
this.client.members.delete(objectToFlatKey({ server: packet.id, user: packet.user }));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'ServerRoleUpdate': {
|
||||
let server = await this.client.servers.fetchMutable(packet.id);
|
||||
server.roles = {
|
||||
...server.roles,
|
||||
[packet.role_id]: {
|
||||
...server.roles?.[packet.role_id],
|
||||
...packet.data
|
||||
} as Servers.Role
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
case 'ServerRoleDelete': {
|
||||
let server = await this.client.servers.fetchMutable(packet.id);
|
||||
let { [packet.role_id]: _, ...roles } = server.roles ?? {};
|
||||
server.roles = roles;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'UserUpdate': {
|
||||
if (packet.clear) {
|
||||
switch (packet.clear) {
|
||||
case 'Avatar': this.client.users.removeField(packet.id, 'avatar'); break;
|
||||
case 'StatusText': this.client.users.removeField(packet.id, 'status', 'text'); break;
|
||||
}
|
||||
}
|
||||
|
||||
this.client.users.patch(packet.id, packet.data);
|
||||
break;
|
||||
}
|
||||
case 'UserRelationship': {
|
||||
this.client.users.setOverride(packet.user);
|
||||
break;
|
||||
}
|
||||
case 'UserPresence': {
|
||||
let user = await this.client.users.fetchMutable(packet.id);
|
||||
user.online = packet.online;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,9 +137,6 @@ export class WebSocketClient {
|
||||
this.connected = false;
|
||||
this.ready = false;
|
||||
|
||||
this.client.users.toMutableArray()
|
||||
.forEach(user => user.online = false);
|
||||
|
||||
if (!disallowReconnect && this.client.autoReconnect) {
|
||||
backOff(() => this.connect(true)).catch(reject);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { Auth, Channels, Servers, Sync, Users } from '../api/objects';
|
||||
import { RemoveChannelField, RemoveServerField, RemoveUserField, RemoveMemberField } from '../api/routes';
|
||||
|
||||
import type { Session } from 'revolt-api/types/Auth';
|
||||
import type { UserSettings } from 'revolt-api/types/Sync';
|
||||
import type { Channel, Message } from 'revolt-api/types/Channels';
|
||||
import type { RelationshipStatus, User } from 'revolt-api/types/Users';
|
||||
import type { Member, MemberCompositeKey, Role, Server } from 'revolt-api/types/Servers';
|
||||
import type { RemoveChannelField, RemoveServerField, RemoveUserField, RemoveMemberField } from '../api/routes';
|
||||
|
||||
type WebSocketError = {
|
||||
error: 'InternalError' | 'InvalidSession' | 'OnboardingNotFinished' | 'AlreadyAuthenticated'
|
||||
@@ -8,24 +13,31 @@ type WebSocketError = {
|
||||
export type ServerboundNotification = (
|
||||
{ type: 'Ping', time: number } |
|
||||
{ type: 'Pong', time: number } |
|
||||
({ type: 'Authenticate' } & Auth.Session) |
|
||||
({ type: 'Authenticate' } & Session) |
|
||||
({ type: 'BeginTyping', channel: string }) |
|
||||
({ type: 'EndTyping', channel: string })
|
||||
);
|
||||
|
||||
export type ReadyPacket = {
|
||||
type: 'Ready',
|
||||
users: User[],
|
||||
servers: Server[],
|
||||
channels: Channel[]
|
||||
}
|
||||
|
||||
export type ClientboundNotification = (
|
||||
{ type: 'Ping', time: number } |
|
||||
{ type: 'Pong', time: number } |
|
||||
({ type: 'Error' } & WebSocketError) |
|
||||
{ type: 'Authenticated' } |
|
||||
{ type: 'Ready', users: Users.User[], servers: Servers.Server[], channels: Channels.Channel[] } |
|
||||
ReadyPacket |
|
||||
|
||||
({ type: 'Message' } & Channels.Message) |
|
||||
({ type: 'MessageUpdate', id: string, data: Partial<Channels.Message> }) |
|
||||
({ type: 'Message' } & Message) |
|
||||
({ type: 'MessageUpdate', id: string, data: Partial<Message> }) |
|
||||
({ type: 'MessageDelete', id: string }) |
|
||||
|
||||
({ type: 'ChannelCreate' } & Channels.Channel) |
|
||||
{ type: 'ChannelUpdate', id: string, data: Partial<Channels.Channel>, clear?: RemoveChannelField } |
|
||||
({ type: 'ChannelCreate' } & Channel) |
|
||||
{ type: 'ChannelUpdate', id: string, data: Partial<Channel>, clear?: RemoveChannelField } |
|
||||
{ type: 'ChannelDelete', id: string } |
|
||||
{ type: 'ChannelGroupJoin', id: string, user: string } |
|
||||
{ type: 'ChannelGroupLeave', id: string, user: string } |
|
||||
@@ -33,16 +45,16 @@ export type ClientboundNotification = (
|
||||
{ type: 'ChannelStopTyping', id: string, user: string } |
|
||||
{ type: 'ChannelAck', id: string, user: string, message_id: string } |
|
||||
|
||||
({ type: 'ServerUpdate', id: string, data: Partial<Servers.Server>, clear?: RemoveServerField }) |
|
||||
({ type: 'ServerUpdate', id: string, data: Partial<Server>, clear?: RemoveServerField }) |
|
||||
({ type: 'ServerDelete', id: string }) |
|
||||
({ type: 'ServerMemberUpdate', id: Servers.MemberCompositeKey, data: Partial<Servers.Member>, clear?: RemoveMemberField }) |
|
||||
({ type: 'ServerMemberUpdate', id: MemberCompositeKey, data: Partial<Member>, clear?: RemoveMemberField }) |
|
||||
({ type: 'ServerMemberJoin', id: string, user: string }) |
|
||||
({ type: 'ServerMemberLeave', id: string, user: string }) |
|
||||
({ type: 'ServerRoleUpdate', id: string, role_id: string, data: Partial<Servers.Role> }) |
|
||||
({ type: 'ServerRoleUpdate', id: string, role_id: string, data: Partial<Role> }) |
|
||||
({ type: 'ServerRoleDelete', id: string, role_id: string }) |
|
||||
|
||||
{ type: 'UserUpdate', id: string, data: Partial<Users.User>, clear?: RemoveUserField } |
|
||||
{ type: 'UserRelationship', user: Users.User, status: Users.Relationship } |
|
||||
{ type: 'UserUpdate', id: string, data: Partial<User>, clear?: RemoveUserField } |
|
||||
{ type: 'UserRelationship', user: User, status: RelationshipStatus } |
|
||||
{ type: 'UserPresence', id: string, online: boolean } |
|
||||
{ type: 'UserSettingsUpdate', id: string, update: Sync.UserSettings }
|
||||
{ type: 'UserSettingsUpdate', id: string, update: UserSettings }
|
||||
)
|
||||
|
||||
@@ -2,14 +2,6 @@
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@insertish/mutable@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@insertish/mutable/-/mutable-1.1.0.tgz#06f95f855691ccb69ee3c339887a80bcd1498116"
|
||||
integrity sha512-NH7aCGFAKRE1gFprrW/HsJoWCWQy18TZBarxLdeLVWdLFvkb2lD6Z5B70oOoUHFNpykiTC8IcRonsd9Xn13n8Q==
|
||||
dependencies:
|
||||
eventemitter3 "^4.0.7"
|
||||
lodash.isequal "^4.5.0"
|
||||
|
||||
"@types/events@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7"
|
||||
@@ -220,11 +212,6 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-arraybuffer-es6@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.7.0.tgz#dbe1e6c87b1bf1ca2875904461a7de40f21abc86"
|
||||
integrity sha512-ESyU/U1CFZDJUdr+neHRhNozeCv72Y7Vm0m1DCbjX3KBjT6eYocvAJlSk6+8+HkVwXlT1FNxhGW6q3UKAlCvvw==
|
||||
|
||||
base@^0.11.1:
|
||||
version "0.11.2"
|
||||
resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
|
||||
@@ -441,11 +428,6 @@ copy-props@^2.0.1:
|
||||
each-props "^1.3.2"
|
||||
is-plain-object "^5.0.0"
|
||||
|
||||
core-js@^2.5.3:
|
||||
version "2.6.12"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
|
||||
integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||
@@ -538,13 +520,6 @@ detect-file@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
|
||||
integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
|
||||
|
||||
domexception@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
|
||||
integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
|
||||
dependencies:
|
||||
webidl-conversions "^4.0.2"
|
||||
|
||||
dotenv@^8.2.0:
|
||||
version "8.2.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
||||
@@ -720,14 +695,6 @@ extglob@^2.0.4:
|
||||
snapdragon "^0.8.1"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
fake-indexeddb@^3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-3.1.2.tgz#8073a12ed3b254f7afc064f3cc2629f0110a5303"
|
||||
integrity sha512-W60eRBrE8r9o/EePyyUc63sr2I/MI9p3zVwLlC1WI1xdmQVqqM6+wec9KDWDz2EZyvJKhrDvy3cGC6hK8L1pfg==
|
||||
dependencies:
|
||||
realistic-structured-clone "^2.0.1"
|
||||
setimmediate "^1.0.5"
|
||||
|
||||
fancy-log@^1.3.2, fancy-log@^1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7"
|
||||
@@ -1098,11 +1065,6 @@ hosted-git-info@^2.1.4:
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
|
||||
|
||||
idb@^6.1.2:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/idb/-/idb-6.1.2.tgz#82ef5c951b8e1f47875d36ccafa4bedafc62f2f1"
|
||||
integrity sha512-1DNDVu3yDhAZkFDlJf0t7r+GLZ248F5pTAtA7V0oVG3yjmV125qZOx3g0XpAEkGZVYQiFDAsSOnGet2bhugc3w==
|
||||
|
||||
in-publish@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.1.tgz#948b1a535c8030561cea522f73f78f4be357e00c"
|
||||
@@ -1450,12 +1412,7 @@ lodash.defaultsdeep@^4.6.1:
|
||||
resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6"
|
||||
integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==
|
||||
|
||||
lodash.isequal@^4.5.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
|
||||
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
|
||||
|
||||
lodash@^4.17.10, lodash@^4.7.0:
|
||||
lodash@^4.17.10:
|
||||
version "4.17.21"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
@@ -1897,11 +1854,6 @@ pumpify@^1.3.5:
|
||||
inherits "^2.0.3"
|
||||
pump "^2.0.0"
|
||||
|
||||
punycode@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
read-pkg-up@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
|
||||
@@ -1941,16 +1893,6 @@ readdirp@^2.2.1:
|
||||
micromatch "^3.1.10"
|
||||
readable-stream "^2.0.2"
|
||||
|
||||
realistic-structured-clone@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-2.0.2.tgz#2f8ec225b1f9af20efc79ac96a09043704414959"
|
||||
integrity sha512-5IEvyfuMJ4tjQOuKKTFNvd+H9GSbE87IcendSBannE28PTrbolgaVg5DdEApRKhtze794iXqVUFKV60GLCNKEg==
|
||||
dependencies:
|
||||
core-js "^2.5.3"
|
||||
domexception "^1.0.1"
|
||||
typeson "^5.8.2"
|
||||
typeson-registry "^1.0.0-alpha.20"
|
||||
|
||||
rechoir@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
|
||||
@@ -2055,6 +1997,11 @@ ret@~0.1.10:
|
||||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
|
||||
|
||||
revolt-api@^0.5.1-alpha.10:
|
||||
version "0.5.1-alpha.10"
|
||||
resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.1-alpha.10.tgz#34ff1f75bda6d11547431c26901fe8dfc8993ce0"
|
||||
integrity sha512-PBibOZ5xntckg3S4IORdNvKMsusBT2tkTcONV8krWuxdAlocuVV48gYAR2p+ltbd4gRpCTSk1EmwwVtsZz2ECQ==
|
||||
|
||||
rimraf@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
|
||||
@@ -2113,11 +2060,6 @@ set-value@^2.0.0, set-value@^2.0.1:
|
||||
is-plain-object "^2.0.3"
|
||||
split-string "^3.0.1"
|
||||
|
||||
setimmediate@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||
@@ -2401,13 +2343,6 @@ to-through@^2.0.0:
|
||||
dependencies:
|
||||
through2 "^2.0.3"
|
||||
|
||||
tr46@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479"
|
||||
integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
tsc-watch@^4.1.0:
|
||||
version "4.2.9"
|
||||
resolved "https://registry.yarnpkg.com/tsc-watch/-/tsc-watch-4.2.9.tgz#d93fc74233ca4ef7ee6b12d08c0fe6aca3e19044"
|
||||
@@ -2458,25 +2393,6 @@ typescript@^4.2.3:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3"
|
||||
integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==
|
||||
|
||||
typeson-registry@^1.0.0-alpha.20:
|
||||
version "1.0.0-alpha.39"
|
||||
resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.39.tgz#9e0f5aabd5eebfcffd65a796487541196f4b1211"
|
||||
integrity sha512-NeGDEquhw+yfwNhguLPcZ9Oj0fzbADiX4R0WxvoY8nGhy98IbzQy1sezjoEFWOywOboj/DWehI+/aUlRVrJnnw==
|
||||
dependencies:
|
||||
base64-arraybuffer-es6 "^0.7.0"
|
||||
typeson "^6.0.0"
|
||||
whatwg-url "^8.4.0"
|
||||
|
||||
typeson@^5.8.2:
|
||||
version "5.18.2"
|
||||
resolved "https://registry.yarnpkg.com/typeson/-/typeson-5.18.2.tgz#0d217fc0e11184a66aa7ca0076d9aa7707eb7bc2"
|
||||
integrity sha512-Vetd+OGX05P4qHyHiSLdHZ5Z5GuQDrHHwSdjkqho9NSCYVSLSfRMjklD/unpHH8tXBR9Z/R05rwJSuMpMFrdsw==
|
||||
|
||||
typeson@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/typeson/-/typeson-6.0.0.tgz#a1e8465028376565f9efed96fef6879a9a844c10"
|
||||
integrity sha512-WFgL4bEdyyfH6VfzC39AcSfeGqTFycW8TvWQy/hbtN8ssbuXSrkSdW2OCt0bUmUZdmFR0wrszyr0CIhvvs4RQw==
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.13.10"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.13.10.tgz#a6bd0d28d38f592c3adb6b180ea6e07e1e540a8d"
|
||||
@@ -2632,25 +2548,6 @@ vscode-textmate@5.2.0:
|
||||
resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e"
|
||||
integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ==
|
||||
|
||||
webidl-conversions@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||
integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
|
||||
|
||||
webidl-conversions@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
|
||||
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
|
||||
|
||||
whatwg-url@^8.4.0:
|
||||
version "8.5.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.5.0.tgz#7752b8464fc0903fec89aa9846fc9efe07351fd3"
|
||||
integrity sha512-fy+R77xWv0AiqfLl4nuGUlQ3/6b5uNfQ4WAbGQVMYshCTCCPK9psC1nWh3XHuxGVCtlcDDQPQW1csmmIQo+fwg==
|
||||
dependencies:
|
||||
lodash "^4.7.0"
|
||||
tr46 "^2.0.2"
|
||||
webidl-conversions "^6.1.0"
|
||||
|
||||
which-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
|
||||
|
||||
Reference in New Issue
Block a user