UNPKG

@averagehelper/corde

Version:

A simple library for Discord bot tests. (Republished fork to demonstrate a bugfix)

331 lines (330 loc) 12.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.CordeBot = void 0; const rxjs_1 = require("rxjs"); const events_1 = require("./events"); const cordeClientError_1 = require("../errors/cordeClientError"); const errors_1 = require("../errors"); const DEFAULT_TEST_TIMEOUT = 5000; /** * Encapsulation of Discord Client with all specific * functions for corde test. */ class CordeBot extends events_1.Events { /** * Starts new instance of Discord client with its events. * * @param prefix Corde bot prefix. * @param guildId Guild id where corde bot is located in. * @param channelId Channel id where corde bot is located in. * @param waitTimeOut Timeout for message wait. * @param testBotId id of testing bot. */ constructor(prefix, guildId, channelId, waitTimeOut = DEFAULT_TEST_TIMEOUT, testBotId, client) { super(client); this._channelId = channelId; this._prefix = prefix; this._guildId = guildId; this._waitTimeOut = waitTimeOut; this._testBotId = testBotId; this._onStart = new rxjs_1.BehaviorSubject(false); this._reactionsObserved = new rxjs_1.BehaviorSubject(null); this.loadClientEvents(); } /** * Observes if corde bot is **ready**. * This is invoked after onReady in Discord.Client. * Used to initialize some data for CordeBot. * **Do not use onReady declared in Events if the intention is to ensure that * cordebot is ready for usage** */ get onStart() { return this._onStart.asObservable(); } get guild() { return this.textChannel.guild; } /** * Authenticate Corde bot to the installed bot in Discord server. * * @param token Corde bot token * * @returns Promise resolve for success connection, or a promise * rejection with a formatted message if there was found a error in * connection attempt. */ login(token) { return __awaiter(this, void 0, void 0, function* () { try { return yield this._client.login(token); } catch (error) { return Promise.reject(this.buildLoginErrorMessage(token, error)); } }); } /** * Destroy client connection. */ logout() { this._client.destroy(); } /** * Sends a pure message without prefix it. * @param message Data to be send to channel */ sendMessage(message) { return __awaiter(this, void 0, void 0, function* () { return yield this.textChannel.send(message); }); } /** * Send a message to a channel defined in configs. * * @see Runtime * * @param message Message without prefix that will be sent to defined server's channel * @description The message is concatenated with the stored **prefix** and is sent to the channel. * * @return Promise rejection if a testing bot does not send any message in the timeout value setted, * or a resolve for the promise with the message returned by the testing bot. */ sendTextMessage(message) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { try { this.validateMessageAndChannel(message); const formattedMessage = this._prefix + message; const returnedMessage = yield this.textChannel.send(formattedMessage); resolve(returnedMessage); } catch (error) { reject(error); } })); }); } /** * Observes for a message send by the testing bot after corde bot * send it's message. */ awaitMessagesFromTestingBot() { return __awaiter(this, void 0, void 0, function* () { const msg = yield this.textChannel.awaitMessages((responseName) => this.responseAuthorIsTestingBot(responseName.author.id), this.createWatchResponseConfigs()); if (msg) { return msg.first(); } throw new cordeClientError_1.CordeClientError("No message was send"); }); } waitForAddedReactions(message, reactions) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { try { const filter = this.createWaitForAdedReactionsFilter(reactions); const maxReactionsToWait = this.defineMaxReactionsToWaitForAddedReactions(reactions); const collectedReactions = yield message.awaitReactions(filter, this.createWatchResponseConfigs(maxReactionsToWait)); resolve(collectedReactions); } catch (error) { reject(new errors_1.TimeoutError()); } })); }); } createWaitForAdedReactionsFilter(reactions) { if (reactions && Array.isArray(reactions)) { return (reaction, user) => { return reactions.includes(reaction.emoji.name) && this.responseAuthorIsTestingBot(user.id); }; } return (_reaction, user) => { return this.responseAuthorIsTestingBot(user.id); }; } defineMaxReactionsToWaitForAddedReactions(reactions) { if (reactions && Array.isArray(reactions)) { return reactions.length; } if (typeof reactions === "number" && reactions > 0) { return reactions; } return 1; } waitForRemovedReactions(message, take) { let amount = 0; const reactions = []; return new Promise((resolve, reject) => { setTimeout(() => { reject(new errors_1.TimeoutError()); }, this._waitTimeOut ? this._waitTimeOut : DEFAULT_TEST_TIMEOUT); this._reactionsObserved.subscribe((reaction) => { if (reaction) { if (reaction.message.id === message.id) { amount++; reactions.push(reaction); } if (amount >= take) { resolve(reactions); } } }); }); } /** * Checks if corde bot is connected */ isLoggedIn() { return !!this._client && !!this._client.readyAt && this._onStart.value; } findMessage(data) { return __awaiter(this, void 0, void 0, function* () { const messageData = data; if (messageData && messageData.text) { return this._findMessage((m) => m.content === messageData.text); } if (messageData && messageData.id) { return this._findMessage((m) => m.id === messageData.id); } if (data) { return this._findMessage(data); } return null; }); } findPinnedMessage(messageData) { return __awaiter(this, void 0, void 0, function* () { const msgs = yield this.textChannel.messages.fetchPinned(); if (messageData && messageData.text) { return msgs.find((m) => m.content === messageData.text); } if (messageData && messageData.id) { return msgs.find((m) => m.id === messageData.id); } return null; }); } fetchRole(id) { return __awaiter(this, void 0, void 0, function* () { return yield this.guild.roles.fetch(id, false, true); }); } hasRole(roleData) { return __awaiter(this, void 0, void 0, function* () { return !!(yield this.findRole(roleData)); }); } findRole(roleData) { return __awaiter(this, void 0, void 0, function* () { const data = yield this.guild.roles.fetch(); if (roleData.id) { return data.cache.find((r) => r.id === roleData.id); } else if (roleData.name) { return data.cache.find((r) => r.name === roleData.name); } return null; }); } getRoles() { return this.guild.roles.cache; } /** * Search for messages based in a filter query. */ _findMessage(fn) { return __awaiter(this, void 0, void 0, function* () { const collection = yield this.textChannel.messages.fetch(); if (collection) { return collection.find(fn); } return null; }); } /** * Get a channel based in the id stored in configs. * * @see Runtime */ loadChannel() { const guild = this.findGuild(this._guildId); const channel = this.findChannel(guild, this._channelId); this.textChannel = this.convertToTextChannel(channel); } responseAuthorIsTestingBot(idAuthor) { return idAuthor === this._testBotId; } createWatchResponseConfigs(max = 1) { return { max, time: this._waitTimeOut ? this._waitTimeOut : DEFAULT_TEST_TIMEOUT, errors: ["time"], }; } loadClientEvents() { this.onReady(() => { this.loadChannel(); this._onStart.next(true); }); this.onMessageReactionRemoveEmoji((reaction) => { this._reactionsObserved.next(reaction); }); } buildLoginErrorMessage(token, error) { return `Error trying to login with token ${token}. \n` + error; } validateMessageAndChannel(message) { if (!message) { throw new cordeClientError_1.CordeClientError("No tests were declared"); } if (!this.textChannel) { throw new cordeClientError_1.CordeClientError("Channel not found"); } } findGuild(guildId) { if (!this._client.guilds) { throw new cordeClientError_1.CordeClientError(`corde bot isn't added in a guild. Please add it to the guild: ${guildId}`); } else if (!this._client.guilds.cache.has(guildId)) { throw new cordeClientError_1.CordeClientError(`Guild ${guildId} doesn't belong to corde bot. change the guild id ` + ` in corde.config or add the bot to a valid guild`); } else { const guild = this._client.guilds.cache.find((_guild) => _guild.id === guildId); if (guild) { return guild; } const availableGuildsIds = this._client.guilds.cache.map((guildAvailable) => guildAvailable.id); throw new cordeClientError_1.CordeClientError(`Could not find the guild ${guildId}.` + ` this client has connections with the following guilds: ${availableGuildsIds}`); } } findChannel(guild, channelId) { if (!guild.channels) { throw new cordeClientError_1.CordeClientError(`Guild '${guild.name}' do not have any channel.`); } else if (!guild.channels.cache.has(channelId)) { throw new cordeClientError_1.CordeClientError(`channel ${channelId} doesn't appear to be a channel of guild ${guild.name}`); } else { const channel = guild.channels.cache.find((ch) => ch.id === channelId); if (channel === undefined) { throw new cordeClientError_1.CordeClientError("There is no informed channel to start tests"); } return channel; } } convertToTextChannel(channel) { return channel; } } exports.CordeBot = CordeBot;