From 9d5879c8c697afc51c787ae91c98a1cc4ddbb690 Mon Sep 17 00:00:00 2001 From: Dragon Fire Date: Fri, 28 Feb 2020 15:03:41 -0500 Subject: [PATCH] Phone Command --- README.md | 3 +- Xiao.js | 14 ++++++++ commands/other/phone.js | 32 +++++++++++++++++ commands/util/options.js | 1 + package.json | 2 +- structures/Client.js | 1 + structures/mafia/Game.js | 3 +- structures/phone/PhoneCall.js | 66 +++++++++++++++++++++++++++++++++++ util/Util.js | 2 +- 9 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 commands/other/phone.js create mode 100644 structures/phone/PhoneCall.js diff --git a/README.md b/README.md index 74893736..c4c66440 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ Xiao is a Discord bot coded in JavaScript with ## Commands -Total: 362 +Total: 363 ### Utility: @@ -487,6 +487,7 @@ Total: 362 ### Other: * **cleverbot:** Talk to Cleverbot. +* **phone:** Starts a phone call with a random server. * **prune:** Deletes up to 99 messages from the current channel. * **rename-all:** Renames every member of the server. (Owner-Only) * **strawpoll:** Generates a Strawpoll with the options you provide. diff --git a/Xiao.js b/Xiao.js index b6f78d82..cca684e2 100644 --- a/Xiao.js +++ b/Xiao.js @@ -56,6 +56,20 @@ client.on('ready', () => { } }); +client.on('message', async msg => { + if (!msg.channel.topic || !msg.channel.topic.includes('')) return; + if (msg.author.bot || !msg.content) return; + const origin = client.phone.find(call => call.origin.id === msg.channel.id); + const recipient = client.phone.find(call => call.recipient.id === msg.channel.id); + if (!origin && !recipient) return; + try { + if (origin) await origin.send(origin.recipient, msg); + if (recipient) await recipient.send(recipient.origin, msg); + } catch (err) { + return; // eslint-disable-line no-useless-return + } +}); + client.on('guildMemberRemove', async member => { const channel = member.guild.systemChannel; if (!channel || !channel.permissionsFor(client.user).has('SEND_MESSAGES')) return null; diff --git a/commands/other/phone.js b/commands/other/phone.js new file mode 100644 index 00000000..9777cf97 --- /dev/null +++ b/commands/other/phone.js @@ -0,0 +1,32 @@ +const Command = require('../../structures/Command'); +const PhoneCall = require('../../structures/phone/PhoneCall'); + +module.exports = class PhoneCommand extends Command { + constructor(client) { + super(client, { + name: 'phone', + aliases: ['phone-call'], + group: 'other', + memberName: 'phone', + description: 'Starts a phone call with a random server.', + guildOnly: true + }); + } + + async run(msg) { + const channels = this.client.channels.cache.filter(channel => channel.type === 'text' + && channel.topic + && channel.topic.includes('') + && !msg.guild.channels.cache.has(channel.id)); + if (!channels.size) return msg.reply('No channels currently allow phone calls...'); + const channel = channels.random(); + try { + const id = `${msg.channel.id}:${channel.id}`; + this.client.phone.set(id, new PhoneCall(this.client, msg.channel, channel)); + await this.client.phone.get(id).start(); + return null; + } catch (err) { + return msg.reply('Failed to start the call. Try again later!'); + } + } +}; diff --git a/commands/util/options.js b/commands/util/options.js index 51892f96..19a57788 100644 --- a/commands/util/options.js +++ b/commands/util/options.js @@ -19,6 +19,7 @@ module.exports = class OptionsCommand extends Command { Place the option in the appropriate channel's topic to use. \`\` Disables leave messages (System Channel). + \`\` Allows this channel to recieve phone calls. \`\` Marks the channel as a portal channel for \`portal-send\`. `); } diff --git a/package.json b/package.json index 828b6163..aaca1653 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xiao", - "version": "110.9.9", + "version": "110.10.0", "description": "Your personal server companion.", "main": "Xiao.js", "scripts": { diff --git a/structures/Client.js b/structures/Client.js index 004e344f..6f6d578f 100644 --- a/structures/Client.js +++ b/structures/Client.js @@ -20,5 +20,6 @@ module.exports = class XiaoClient extends CommandoClient { this.pokemon = new PokemonStore(); this.memePoster = new MemePoster(this); this.games = new Collection(); + this.phone = new Map(); } }; diff --git a/structures/mafia/Game.js b/structures/mafia/Game.js index b5091079..ad087ba3 100644 --- a/structures/mafia/Game.js +++ b/structures/mafia/Game.js @@ -7,8 +7,9 @@ const { SUCCESS_EMOJI_ID } = process.env; module.exports = class Game { constructor(client, channel, voiceChannel) { + Object.defineProperty(this, 'client', { value: client }); + this.name = 'mafia'; - this.client = client; this.players = new Collection(); this.channel = channel; this.voiceChannelRaw = voiceChannel; diff --git a/structures/phone/PhoneCall.js b/structures/phone/PhoneCall.js new file mode 100644 index 00000000..5dd974d8 --- /dev/null +++ b/structures/phone/PhoneCall.js @@ -0,0 +1,66 @@ +const { shorten, verify } = require('../../util/Util'); + +module.exports = class PhoneCall { + constructor(client, origin, recipient) { + Object.defineProperty(this, 'client', { value: client }); + + this.id = `${origin.id}:${recipient.id}`; + this.origin = origin; + this.recipient = recipient; + this.active = false; + this.timeout = null; + } + + async start() { + await this.recipient.send(`☎️ Incoming call from **${this.origin.guild.name}**. Pick up?`); + const validation = await verify(this.recipient, null); + if (!validation) { + await this.decline(validation); + return this; + } + await this.accept(); + return this; + } + + async accept() { + this.active = true; + this.setTimeout(); + await this.origin.send(`☎️ **${this.recipient.guild.name}** picked up! Type \`hang up\` to hang up.`); + await this.recipient.send(`☎️ Accepted call from **${this.origin.guild.name}**. Type \`hang up\` to hang up.`); + return this; + } + + async decline(validation) { + const directMsg = validation === 0 ? 'didn\'t answer...' : 'declined the call...'; + this.client.phone.delete(this.id); + await this.origin.send(`☎️ **${this.recipient.guild.name}** ${directMsg}`); + return this; + } + + async hangup(quitter) { + this.active = false; + clearTimeout(this.timeout); + this.client.phone.delete(this.id); + if (quitter === 'time') { + await this.origin.send('☎️ Call ended due to inactivity.'); + await this.recipient.send('☎️ Call ended due to inactivity.'); + } else { + const channel = quitter.id === this.origin.id ? this.origin : this.recipient; + await channel.send(`☎️ **${channel.guild.name}** hung up.`); + await quitter.send('☎️ Hung up.'); + } + return this; + } + + send(channel, msg) { + if (msg.content.toLowerCase() === 'hang up') return this.hangup(channel); + this.setTimeout(); + return channel.send(`☎️ **${msg.author.tag}:** ${shorten(msg.content, 1500)}`); + } + + setTimeout() { + if (this.timeout) clearTimeout(this.timeout); + this.timeout = this.setTimeout(() => this.hangup('time'), 60000); + return this.timeout; + } +}; diff --git a/util/Util.js b/util/Util.js index d7a6b530..089effeb 100644 --- a/util/Util.js +++ b/util/Util.js @@ -77,7 +77,7 @@ module.exports = class Util { static async verify(channel, user, time = 30000) { const filter = res => { const value = res.content.toLowerCase(); - return res.author.id === user.id && (yes.includes(value) || no.includes(value)); + return (user ? res.author.id === user.id : true) && (yes.includes(value) || no.includes(value)); }; const verify = await channel.awaitMessages(filter, { max: 1,