From 343d6e3992e00c0f33c0433d06da68599c14072f Mon Sep 17 00:00:00 2001 From: Paul Date: Thu, 12 Aug 2021 12:00:07 +0100 Subject: [PATCH] Add support for bot login. Add flags + bot information for users. --- .env.example | 3 +- README.md | 6 ++- package.json | 4 +- src/Client.ts | 38 +++++++++---- src/api/routes.ts | 68 +++++++++++++++++++++++- src/index.ts | 2 +- src/maps/Users.ts | 9 +++- src/tester.ts | 97 +++++++++++++++------------------- src/websocket/client.ts | 12 +++-- src/websocket/notifications.ts | 1 + yarn.lock | 8 +-- 11 files changed, 169 insertions(+), 79 deletions(-) diff --git a/.env.example b/.env.example index 8f6743f1..fc323268 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ API_URL=https://api.revolt.chat EMAIL=1@example.com -PASSWORD=password \ No newline at end of file +PASSWORD=password +BOT_TOKEN=abc \ No newline at end of file diff --git a/README.md b/README.md index 02610a0e..74f18c4b 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,11 @@ client.on('message', async message => { } }); -// Either create a new session: +// To login as a bot: +client.loginBot('..'); + +// To login as a user, +// either create a new session: client.login({ email: '..', password: '..' }); // Or use an existing session: diff --git a/package.json b/package.json index b128a84b..9d0c11a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "revolt.js", - "version": "5.0.0-alpha.21", + "version": "5.0.1-alpha.0", "main": "dist/index.js", "repository": "https://gitlab.insrt.uk/revolt/revolt.js", "author": "Paul Makles ", @@ -30,7 +30,7 @@ "in-publish": "^2.0.1", "jsdoc-babel": "^0.5.0", "node-fetch": "^2.6.1", - "revolt-api": "0.5.1-alpha.22", + "revolt-api": "^0.5.2-alpha.0", "rimraf": "^3.0.2", "typedoc": "^0.21.4", "typescript": "^4.2.3" diff --git a/src/Client.ts b/src/Client.ts index 31b4dedb..3b49a712 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -63,7 +63,7 @@ export type FileArgs = [ options?: SizeOptions, allowAnimation?: boolean, fallba export class Client extends EventEmitter { heartbeat: number; - session?: Session; + session?: Session | string; user?: User; websocket: WebSocketClient; @@ -217,10 +217,16 @@ export class Client extends EventEmitter { await this.connect(); } - private $generateHeaders(session: Session | undefined = this.session) { - return { - 'x-user-id': session?.user_id, - 'x-session-token': session?.session_token + private $generateHeaders(session: Session | string | undefined = this.session) { + if (typeof session === 'string') { + return { + 'x-bot-token': session + } + } else { + return { + 'x-user-id': session?.user_id, + 'x-session-token': session?.session_token + } } } @@ -230,24 +236,35 @@ export class Client extends EventEmitter { * @returns An onboarding function if onboarding is required, undefined otherwise */ async login(details: Route<'POST', '/auth/login'>["data"]) { - this.fetchConfiguration(); + await this.fetchConfiguration(); this.session = await this.req('POST', '/auth/login', details); return await this.$connect(); } /** - * Use an existing session to log into REVOLT. + * Use an existing session to log into Revolt. * @param session Session data object * @returns An onboarding function if onboarding is required, undefined otherwise */ async useExistingSession(session: Session) { - this.fetchConfiguration(); + await this.fetchConfiguration(); await this.request('GET', '/auth/check', { headers: this.$generateHeaders(session) }); this.session = session; return await this.$connect(); } + /** + * Log in as a bot. + * @param token Bot token + */ + async loginBot(token: string) { + await this.fetchConfiguration(); + this.session = token; + this.Axios.defaults.headers = this.$generateHeaders(); + return await this.websocket.connect(); + } + // Check onboarding status and connect to notifications service. private async $connect() { this.Axios.defaults.headers = this.$generateHeaders(); @@ -380,9 +397,8 @@ 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], @@ -394,7 +410,7 @@ export class Client extends EventEmitter { return sub; } - )*/ + ) .replace( RE_SPOILER, '' diff --git a/src/api/routes.ts b/src/api/routes.ts index 20b94f8d..2b31b8aa 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -1,3 +1,4 @@ +import type { Bot, PublicBot } from 'revolt-api/types/Bots'; 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' @@ -144,7 +145,8 @@ type Routes = route: `/onboard/hello`, data: undefined, response: { - onboarding: boolean + onboarding: boolean, + id?: string } } | { @@ -600,6 +602,8 @@ type Routes = data: { name?: string, colour?: string, + hoist?: boolean, + rank?: number, remove?: RemoveRoleField }, response: undefined @@ -623,6 +627,68 @@ type Routes = }, response: undefined } + /** + * Bots + */ + | { + // Create Bot + method: 'POST', + route: `/bots/create`, + data: { + name: string + }, + response: Bot + } + | { + // Fetch Owned Bots + method: 'GET', + route: `/bots/@me`, + data: undefined, + response: Bot[] + } + | { + // Fetch Bot + method: 'GET', + route: `/bots/${Id}`, + data: undefined, + response: Bot + } + | { + // Edit Bot + method: 'PATCH', + route: `/bots/${Id}`, + data: { + name?: string, + public?: boolean, + interactionsURL?: string, + remove?: 'InteractionsURL' + }, + response: undefined + } + | { + // Delete Bot + method: 'DELETE', + route: `/bots/${Id}`, + data: undefined, + response: undefined + } + | { + // Fetch Public Bot + method: 'GET', + route: `/bots/${Id}/invite`, + data: undefined, + response: PublicBot + } + | { + // Invite Public Bot + method: 'POST', + route: `/bots/${Id}/invite`, + data: ( + { server: string } | + { group: string } + ), + response: undefined + } /** * Invites */ diff --git a/src/index.ts b/src/index.ts index bac09602..473ca146 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ export * from './Client'; export { UserPermission, ChannelPermission, ServerPermission } from './api/permissions'; -export const LIBRARY_VERSION = '5.0.0-alpha.21'; +export const LIBRARY_VERSION = '5.0.1-alpha.0'; export const defaultConfig = { apiURL: 'https://api.revolt.chat', diff --git a/src/maps/Users.ts b/src/maps/Users.ts index 51ee0cf3..14444887 100644 --- a/src/maps/Users.ts +++ b/src/maps/Users.ts @@ -1,4 +1,4 @@ -import type { Status, User as UserI } from 'revolt-api/types/Users'; +import type { BotInformation, Status, User as UserI } from 'revolt-api/types/Users'; import type { RemoveUserField, Route } from '../api/routes'; import type { Attachment } from 'revolt-api/types/Autumn'; @@ -9,6 +9,7 @@ import { U32_MAX, UserPermission } from '../api/permissions'; import { toNullable, Nullable } from '../util/null'; import Collection from './Collection'; import { Client, FileArgs, SYSTEM_USER_ID } from '..'; +import _ from 'lodash'; enum RelationshipStatus { None = "None", @@ -31,6 +32,8 @@ export class User { status: Nullable; relationship: Nullable; online: Nullable; + flags: Nullable; + bot: Nullable; constructor(client: Client, data: UserI) { this.client = client; @@ -43,6 +46,8 @@ export class User { this.status = toNullable(data.status); this.relationship = toNullable(data.relationship); this.online = toNullable(data.online); + this.flags = toNullable(data.flags); + this.bot = toNullable(data.bot); makeAutoObservable(this, { _id: false, @@ -81,6 +86,8 @@ export class User { apply("status"); apply("relationship"); apply("online"); + apply("flags"); + apply("bot"); } /** diff --git a/src/tester.ts b/src/tester.ts index 320d02d4..94406527 100644 --- a/src/tester.ts +++ b/src/tester.ts @@ -5,63 +5,52 @@ config(); // 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 -}); - -client.on('ready', async () => { - console.info(`Logged in as ${client.user!.username}!`); - - [...client.users.values()].forEach(x => console.info(`perm against ${x.username} is ${x.permission}`)); - [...client.servers.values()].forEach(x => console.info(`perm against ${x.name} is ${x.permission}`)); - [...client.channels.values()].forEach(x => console.info(`perm against ${x.name} is ${x.permission}`)); - - setTimeout(() => { - client.users.edit({ - remove: 'Avatar' - }); - }, 10_000); -}); - -client.on('message', async message => { - if (message.content === 'sus') { - message.channel!.sendMessage('sus!'); - } -}); - -import { autorun } from 'mobx'; - -autorun(() => { - console.log(` -BATCH UPDATE ----- -srv ids: ${[...client.servers.values()].map(x => x._id).length} -chn ids: ${[...client.channels.values()].map(x => x._id).length} -usr ids: ${[...client.users.values()].map(x => x._id).length} -msg ids: ${[...client.messages.values()].map(x => x._id).length} -mbr ids: ${[...client.members.values()].map(x => x._id).length} -----`); -}); - -client.once('ready', () => { - autorun(() => { - console.log(`Changed username to ${client.user!.username}!`); +function user() { + let client = new Client({ + apiURL: process.env.API_URL }); - autorun(() => { - console.log(`Avatar URL: ${client.user!.generateAvatarURL()}!`); + client.on('ready', async () => { + console.info(`Logged in as ${client.user!.username}!`); }); - let server_id = [...client.servers.keys()][0]; - autorun(() => { - console.log(` - -Server ------- -Name: ${client.servers.get(server_id)!.name} -`); + client.on('message', async message => { + if (message.content === 'sus') { + message.channel!.sendMessage('sus!'); + } else if (message.content === 'bot') { + let bot = await client.req('POST', '/bots/create', { name: 'basedbot3' }); + message.channel!.sendMessage(JSON.stringify(bot)); + } else if (message.content === 'my bots') { + message.channel!.sendMessage(JSON.stringify( + await client.req('GET', '/bots/@me') + )); + } else if (message.content === 'join bot') { + await client.req('POST', `/bots/01FCV7DCMRD9MT3JBYT5VEKVRD/invite` as '/bots/id/invite', + { group: message.channel_id }); + // { server: '01FATEGMHEE2M1QGPA65NS6V8K' }); + } }); -}); -client.login({ email: process.env.EMAIL as string, password: process.env.PASSWORD as string }) -// client.useExistingSession({ user_id: process.env.USER_ID as string, session_token: process.env.SESSION_TOKEN as string }); + client.login({ email: process.env.EMAIL as string, password: process.env.PASSWORD as string }) +} + +function bot() { + let client = new Client({ + apiURL: process.env.API_URL + }); + + client.on('ready', async () => { + console.info(`Logged in as ${client.user!.username}! [${client.user!._id}]`); + }); + + client.on('message', async message => { + if (message.content === 'sus') { + message.channel!.sendMessage('sus!'); + } + }); + + client.loginBot(process.env.BOT_TOKEN as string) +} + +// user(); +bot(); diff --git a/src/websocket/client.ts b/src/websocket/client.ts index 95f63a8d..fcc6cc91 100644 --- a/src/websocket/client.ts +++ b/src/websocket/client.ts @@ -3,9 +3,9 @@ import { backOff } from 'exponential-backoff'; import { Client, SYSTEM_USER_ID } from '..'; import { ServerboundNotification, ClientboundNotification } from './notifications'; -import { Session } from 'revolt-api/types/Auth'; import { runInAction } from 'mobx'; + import { Role } from 'revolt-api/types/Servers'; export class WebSocketClient { @@ -78,7 +78,11 @@ export class WebSocketClient { this.ws = ws; ws.onopen = () => { - this.send({ type: 'Authenticate', ...this.client.session as Session }); + if (typeof this.client.session === 'string') { + this.send({ type: 'Authenticate', token: this.client.session! }); + } else { + this.send({ type: 'Authenticate', ...this.client.session! }); + } }; let timeouts: Record = {}; @@ -123,7 +127,9 @@ export class WebSocketClient { } }); - this.client.user = this.client.users.get(this.client.session!.user_id)!; + this.client.user = this.client.users.get( + packet.users.find(x => x.relationship === 'User')!._id + )!; this.client.emit('ready'); this.ready = true; diff --git a/src/websocket/notifications.ts b/src/websocket/notifications.ts index 6a711dad..791434e8 100644 --- a/src/websocket/notifications.ts +++ b/src/websocket/notifications.ts @@ -14,6 +14,7 @@ export type ServerboundNotification = ( { type: 'Ping', time: number } | { type: 'Pong', time: number } | ({ type: 'Authenticate' } & Session) | + ({ type: 'Authenticate', token: string }) | ({ type: 'BeginTyping', channel: string }) | ({ type: 'EndTyping', channel: string }) ); diff --git a/yarn.lock b/yarn.lock index a68d0f3b..a8c85682 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2014,10 +2014,10 @@ 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.22: - version "0.5.1-alpha.22" - resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.1-alpha.22.tgz#ba60bad959e90a1ba75864d359b7d91b438c79ce" - integrity sha512-U97JcetsdzjZDfn/RBxLWuFtEevwdaAvT7hu6EYGxXr/8uYzNs7/MuiPDsom3KRaft443AL6Upswgsc7idntBQ== +revolt-api@^0.5.2-alpha.0: + version "0.5.2-alpha.0" + resolved "https://registry.yarnpkg.com/revolt-api/-/revolt-api-0.5.2-alpha.0.tgz#a41f44ee38622636c9b5b5843f9e2798a79f00d3" + integrity sha512-VI/o4nQTPXrDCVdFpZFfZfj7Q4nunj62gftdmYJtuSmXx+6eN2Nve7QQZjNt6UIH6Dc/IDgiFDcBdafBF9YXug== rimraf@^3.0.2: version "3.0.2"