From adf3e5fe4d6283d2f26b87a500446caccf3274c2 Mon Sep 17 00:00:00 2001 From: Cynthia Foxwell Date: Sun, 28 Aug 2022 16:01:31 -0600 Subject: [PATCH] refactor everything out of one file, add channel rememberance --- .eslintrc.js | 1 + README.md | 2 +- src/commands/clear.js | 5 + src/commands/emote.js | 26 ++ src/commands/help.js | 29 ++ src/commands/history.js | 72 ++++ src/commands/listChannels.js | 62 +++ src/commands/listGuilds.js | 34 ++ src/commands/listUsers.js | 86 ++++ src/commands/quit.js | 6 + src/commands/send.js | 35 ++ src/commands/switchChannel.js | 41 ++ src/commands/switchGuild.js | 51 +++ src/index.js | 738 +++------------------------------- src/lib/command.js | 17 + src/lib/messages.js | 117 ++++++ src/lib/prompt.js | 25 ++ 17 files changed, 661 insertions(+), 686 deletions(-) create mode 100644 src/commands/clear.js create mode 100644 src/commands/emote.js create mode 100644 src/commands/help.js create mode 100644 src/commands/history.js create mode 100644 src/commands/listChannels.js create mode 100644 src/commands/listGuilds.js create mode 100644 src/commands/listUsers.js create mode 100644 src/commands/quit.js create mode 100644 src/commands/send.js create mode 100644 src/commands/switchChannel.js create mode 100644 src/commands/switchGuild.js create mode 100644 src/lib/command.js create mode 100644 src/lib/messages.js create mode 100644 src/lib/prompt.js diff --git a/.eslintrc.js b/.eslintrc.js index 4b9468b..e78c339 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -26,5 +26,6 @@ module.exports = { "require-atomic-updates": OFF, }, globals: { + comcord: true, }, }; diff --git a/README.md b/README.md index 2531bae..aaaf716 100644 --- a/README.md +++ b/README.md @@ -56,4 +56,4 @@ You **MUST** grant your bot all Privileged Gateway Intents. - [ ] Default guild/channel - [ ] Threads - [ ] Not have the token just be in argv -- [ ] Not have everything in one file +- [x] Not have everything in one file diff --git a/src/commands/clear.js b/src/commands/clear.js new file mode 100644 index 0000000..db973c5 --- /dev/null +++ b/src/commands/clear.js @@ -0,0 +1,5 @@ +const {addCommand} = require("../lib/command"); + +addCommand("c", "clear", function () { + console.clear(); +}); diff --git a/src/commands/emote.js b/src/commands/emote.js new file mode 100644 index 0000000..aef4b7d --- /dev/null +++ b/src/commands/emote.js @@ -0,0 +1,26 @@ +const {addCommand} = require("../lib/command"); +const {startPrompt} = require("../lib/prompt"); + +addCommand("e", "emote", function () { + if (!comcord.state.currentChannel) { + console.log(""); + return; + } + + startPrompt(":emote> ", async function (input) { + if (input == "") { + console.log(""); + } else { + try { + process.stdout.write("\n"); + await comcord.client.createMessage( + comcord.state.currentChannel, + `*${input}*` + ); + console.log(`<${comcord.client.user.username} ${input}>`); + } catch (err) { + console.log(""); + } + } + }); +}); diff --git a/src/commands/help.js b/src/commands/help.js new file mode 100644 index 0000000..27047cf --- /dev/null +++ b/src/commands/help.js @@ -0,0 +1,29 @@ +const chalk = require("chalk"); + +const {addCommand} = require("../lib/command"); + +addCommand("h", "command help", function () { + console.log("\nCOMcord (c)left 2022\n"); + + const keys = Object.keys(comcord.commands); + keys.sort((a, b) => a.localeCompare(b)); + + let index = 0; + for (const key of keys) { + const desc = comcord.commands[key].name; + const length = ` ${key} - ${desc}`.length; + + process.stdout.write( + " " + + chalk.bold.yellow(key) + + chalk.reset(" - " + desc) + + " ".repeat(Math.abs(25 - length)) + ); + + index++; + if (index % 3 == 0) process.stdout.write("\n"); + } + if (index % 3 != 0) process.stdout.write("\n"); + + console.log("\nTo begin TALK MODE, press [SPACE]\n"); +}); diff --git a/src/commands/history.js b/src/commands/history.js new file mode 100644 index 0000000..b22b52b --- /dev/null +++ b/src/commands/history.js @@ -0,0 +1,72 @@ +const {addCommand} = require("../lib/command"); +const {startPrompt} = require("../lib/prompt"); +const {processMessage} = require("../lib/messages"); + +async function getHistory(limit = 20) { + if (!comcord.state.currentChannel) { + console.log(""); + return; + } + + const messages = await comcord.client.getMessages( + comcord.state.currentChannel, + {limit} + ); + messages.reverse(); + + console.log("--Beginning-Review".padEnd(72, "-")); + + for (const msg of messages) { + if (msg.content.indexOf("\n") > -1) { + const lines = msg.content.split("\n"); + for (const index in lines) { + const line = lines[index]; + processMessage({ + name: msg.author.username, + bot: msg.author.bot, + content: + line + + (msg.editedTimestamp != null && index == lines.length - 1 + ? " (edited)" + : ""), + attachments: index == lines.length - 1 ? msg.attachments : null, + reply: index == 0 ? msg.referencedMessage : null, + noColor: true, + }); + } + } else { + processMessage({ + name: msg.author.username, + bot: msg.author.bot, + content: msg.content + (msg.editedTimestamp != null ? " (edited)" : ""), + attachments: msg.attachments, + reply: msg.referencedMessage, + noColor: true, + }); + } + } + + console.log("--Review-Complete".padEnd(73, "-")); +} + +async function getExtendedHistory(input) { + input = parseInt(input); + if (isNaN(input)) { + console.log(""); + return; + } + + try { + await getHistory(input); + } catch (err) { + console.log(""); + } +} + +addCommand("r", "channel history", getHistory); +addCommand("R", "extended history", function () { + startPrompt(":lines> ", async function (input) { + process.stdout.write("\n"); + await getExtendedHistory(input); + }); +}); diff --git a/src/commands/listChannels.js b/src/commands/listChannels.js new file mode 100644 index 0000000..7d12c3e --- /dev/null +++ b/src/commands/listChannels.js @@ -0,0 +1,62 @@ +const Eris = require("eris"); + +const {addCommand} = require("../lib/command"); + +function listChannels() { + if (!comcord.state.currentGuild) { + console.log(""); + return; + } + + let longest = 0; + let longestTopic = 0; + const guild = comcord.client.guilds.get(comcord.state.currentGuild); + const channels = [...guild.channels.values()].filter((c) => c.type == 0); + channels.sort((a, b) => a.position - b.position); + + for (const channel of channels) { + const perms = channel.permissionsOf(comcord.client.user.id); + const private = !perms.has(Eris.Constants.Permissions.readMessages); + + if (channel.name.length + (private ? 1 : 0) > longest) + longest = Math.min(25, channel.name.length + (private ? 1 : 0)); + if (channel.topic != null && channel.topic.length > longestTopic) + longestTopic = channel.topic.length; + } + + console.log(""); + console.log( + " " + + "channel-name".padStart(longest, " ") + + " " + + "topic".padStart(Math.min(80 - (longest + 5), longestTopic), " ") + ); + console.log("-".repeat(80)); + for (const channel of channels) { + const topic = + channel.topic != null ? channel.topic.replace(/\n/g, " ") : ""; + const perms = channel.permissionsOf(comcord.client.user.id); + const private = !perms.has(Eris.Constants.Permissions.readMessages); + + const name = (private ? "*" : "") + channel.name; + + console.log( + " " + + (name.length > 24 ? name.substring(0, 24) + "\u2026" : name).padStart( + longest, + " " + ) + + " " + + (topic.length > 80 - longest + 9 + ? topic.substring(0, 79 - (longest + 5)) + "\u2026" + : topic.padStart(Math.min(80 - (longest + 5), longestTopic), " ")) + ); + } + console.log(""); +} + +addCommand("l", "list channels", listChannels); + +module.exports = { + listChannels, +}; diff --git a/src/commands/listGuilds.js b/src/commands/listGuilds.js new file mode 100644 index 0000000..fc9fd3a --- /dev/null +++ b/src/commands/listGuilds.js @@ -0,0 +1,34 @@ +const {addCommand} = require("../lib/command"); + +function listGuilds() { + let longest = 0; + const guilds = []; + + for (const guild of comcord.client.guilds.values()) { + if (guild.name.length > longest) longest = guild.name.length; + + const online = [...guild.members.values()].filter((m) => m.status).length; + guilds.push({name: guild.name, members: guild.memberCount, online}); + } + + console.log(""); + console.log(" " + "guild-name".padStart(longest, " ") + " online total"); + console.log("-".repeat(80)); + for (const guild of guilds) { + console.log( + " " + + guild.name.padStart(longest, " ") + + " " + + guild.online.toString().padStart(6, " ") + + " " + + guild.members.toString().padStart(5, " ") + ); + } + console.log(""); +} + +addCommand("L", "list guilds", listGuilds); + +module.exports = { + listGuilds, +}; diff --git a/src/commands/listUsers.js b/src/commands/listUsers.js new file mode 100644 index 0000000..acaa2f5 --- /dev/null +++ b/src/commands/listUsers.js @@ -0,0 +1,86 @@ +const chalk = require("chalk"); + +const {addCommand} = require("../lib/command"); + +function getStatus(status) { + let color; + switch (status) { + case "online": + color = chalk.bold.green; + break; + case "idle": + color = chalk.bold.yellow; + break; + case "dnd": + color = chalk.bold.red; + break; + default: + color = chalk.bold; + break; + } + + return color(" \u2022 "); +} + +function listUsers() { + if (!comcord.state.currentGuild) { + console.log(""); + return; + } + if (!comcord.state.currentChannel) { + console.log(""); + return; + } + + const guild = comcord.client.guilds.get(comcord.state.currentGuild); + const channel = guild.channels.get(comcord.state.currentChannel); + + console.log( + `\n[you are in '${guild.name}' in '${channel.name}' among ${guild.memberCount}]\n` + ); + + const online = [...guild.members.values()].filter((m) => m.status); + online.sort((a, b) => a.name - b.name); + + let longest = 0; + for (const member of online) { + const name = member.user.username + "#" + member.user.discriminator; + if (name.length + 3 > longest) longest = name.length + 3; + } + + const columns = Math.ceil(process.stdout.columns / longest); + + let index = 0; + for (const member of online) { + const name = member.user.username + "#" + member.user.discriminator; + const status = getStatus(member.status); + const nameAndStatus = chalk.reset(name) + status; + + index++; + process.stdout.write( + nameAndStatus + + " ".repeat( + index % columns == 0 ? 0 : Math.abs(longest - (name.length + 3)) + ) + ); + + if (index % columns == 0) process.stdout.write("\n"); + } + if (index % columns != 0) process.stdout.write("\n"); + console.log(""); + + if (channel.topic != null) { + console.log("--Topic".padEnd(80, "-")); + console.log(channel.topic); + console.log("-".repeat(80)); + console.log(""); + } +} + +if (!comcord.commands.w) { + addCommand("w", "who is in guild", listUsers); +} + +module.exports = { + listUsers, +}; diff --git a/src/commands/quit.js b/src/commands/quit.js new file mode 100644 index 0000000..aafaac5 --- /dev/null +++ b/src/commands/quit.js @@ -0,0 +1,6 @@ +const {addCommand} = require("../lib/command"); + +addCommand("q", "quit comcord", function () { + comcord.client.disconnect(false); + process.exit(0); +}); diff --git a/src/commands/send.js b/src/commands/send.js new file mode 100644 index 0000000..b067c17 --- /dev/null +++ b/src/commands/send.js @@ -0,0 +1,35 @@ +const chalk = require("chalk"); + +const {startPrompt} = require("../lib/prompt"); + +function sendMode() { + if (!comcord.state.currentChannel) { + console.log(""); + return; + } + + startPrompt( + chalk.bold.cyan(`[${comcord.client.user.username}]`) + + " ".repeat( + comcord.state.nameLength - (comcord.client.user.username.length + 2) + ) + + chalk.reset(" "), + async function (input) { + if (input == "") { + console.log(""); + } else { + try { + process.stdout.write("\n"); + await comcord.client.createMessage( + comcord.state.currentChannel, + input + ); + } catch (err) { + console.log(""); + } + } + } + ); +} + +module.exports = {sendMode}; diff --git a/src/commands/switchChannel.js b/src/commands/switchChannel.js new file mode 100644 index 0000000..41bf69b --- /dev/null +++ b/src/commands/switchChannel.js @@ -0,0 +1,41 @@ +const {addCommand} = require("../lib/command"); +const {startPrompt} = require("../lib/prompt"); + +const {listUsers} = require("./listUsers"); + +function switchChannel(input) { + if (input == "") { + listUsers(); + comcord.state.channelSwitch = false; + return; + } + let target; + + const guild = comcord.client.guilds.get(comcord.state.currentGuild); + const channels = [...guild.channels.values()].filter((c) => c.type == 0); + channels.sort((a, b) => a.position - b.position); + + for (const channel of channels) { + if (channel.name.toLowerCase().indexOf(input.toLowerCase()) > -1) { + target = channel.id; + break; + } + } + + if (target == null) { + console.log(""); + } else { + comcord.state.currentChannel = target; + comcord.state.lastChannel.set(comcord.state.currentGuild, target); + + listUsers(); + } +} + +addCommand("g", "goto channel", function () { + if (!comcord.state.currentGuild) { + console.log(""); + return; + } + startPrompt(":channel> ", switchChannel); +}); diff --git a/src/commands/switchGuild.js b/src/commands/switchGuild.js new file mode 100644 index 0000000..844a561 --- /dev/null +++ b/src/commands/switchGuild.js @@ -0,0 +1,51 @@ +const {addCommand} = require("../lib/command"); +const {startPrompt} = require("../lib/prompt"); + +const {listChannels} = require("./listChannels"); +const {listUsers} = require("./listUsers"); + +function findTopChannel(guildId) { + const guild = comcord.client.guilds.get(guildId); + const channels = [...guild.channels.values()].filter((c) => c.type == 0); + channels.sort((a, b) => a.position - b.position); + + return channels[0]; +} + +function switchGuild(input) { + if (input == "") { + listChannels(); + listUsers(); + return; + } + + let target; + + for (const guild of comcord.client.guilds.values()) { + if (guild.name.toLowerCase().indexOf(input.toLowerCase()) > -1) { + target = guild.id; + break; + } + } + + if (target == null) { + console.log(""); + } else { + comcord.state.currentGuild = target; + // TODO: store last visited channel and switch to it if we've been to this guild before + if (!comcord.state.lastChannel.has(target)) { + const topChannel = findTopChannel(target); + comcord.state.currentChannel = topChannel.id; + comcord.state.lastChannel.set(target, topChannel.id); + } else { + comcord.state.currentChannel = comcord.state.lastChannel.get(target); + } + + listChannels(); + listUsers(); + } +} + +addCommand("G", "goto guild", function () { + startPrompt(":guild> ", switchGuild); +}); diff --git a/src/index.js b/src/index.js index ec62eb5..781799c 100644 --- a/src/index.js +++ b/src/index.js @@ -2,46 +2,41 @@ const Eris = require("eris"); const chalk = require("chalk"); const token = process.argv[2]; -const stdin = process.stdin; -const stdout = process.stdout; -stdin.setRawMode(true); -stdin.resume(); -stdin.setEncoding("utf8"); - -let currentGuild, - currentChannel, - inSendMode = false, - inEmoteMode = false, - guildSwitch = false, - channelSwitch = false, - extendedHistory = false, - nameLength = 2; - -const messageQueue = []; - -const commands = { - q: "quit comcord", - e: "emote", - g: "goto a channel", - G: "goto a guild", - l: "list channels", - L: "list guilds", - w: "who is in guild", - f: "finger", - r: "channel history", - R: "extended history", - h: "command help", - c: "clear", - "<": "surf backwards", - ">": "surf forwards", +global.comcord = { + state: { + currentGuild: null, + currentChannel: null, + nameLength: 2, + inPrompt: false, + messageQueue: [], + lastChannel: new Map(), + }, + commands: {}, }; - const client = new Eris("Bot " + token, { defaultImageFormat: "png", defaultImageSize: 1024, intents: Eris.Constants.Intents.all, }); +comcord.client = client; + +const {finalizePrompt} = require("./lib/prompt"); +const {processMessage, processQueue} = require("./lib/messages"); + +require("./commands/quit"); +require("./commands/clear"); +require("./commands/help"); +const {sendMode} = require("./commands/send"); +require("./commands/emote"); +const {listGuilds} = require("./commands/listGuilds"); +require("./commands/switchGuild"); // loads listChannels and listUsers +require("./commands/switchChannel"); //loads listUsers +require("./commands/history"); // includes extended history + +process.stdin.setRawMode(true); +process.stdin.resume(); +process.stdin.setEncoding("utf8"); client.once("ready", function () { console.log( @@ -50,125 +45,17 @@ client.once("ready", function () { `${client.user.username}#${client.user.discriminator} (${client.user.id})` ) ); - nameLength = client.user.username.length + 2; + comcord.state.nameLength = client.user.username.length + 2; listGuilds(); }); -function processMessage({ - name, - content, - bot, - attachments, - reply, - isHistory = false, -}) { - if (name.length + 2 > nameLength) nameLength = name.length + 2; - - if (reply) { - const nameColor = reply.author.bot ? chalk.bold.yellow : chalk.bold.cyan; - - const headerLength = 5 + reply.author.username.length; - const length = headerLength + reply.content.length; - - if (isHistory) { - console.log( - ` \u250d [${reply.author.username}] ${ - length > 79 - ? reply.content.substring(0, length - headerLength) + "\u2026" - : reply.content - }` - ); - } else { - console.log( - chalk.bold.white(" \u250d ") + - nameColor(`[${reply.author.username}] `) + - chalk.reset( - `${ - length > 79 - ? reply.content.substring(0, length - headerLength) + "\u2026" - : reply.content - }` - ) - ); - } - } - - if ( - (content.startsWith("*") && content.endsWith("*")) || - (content.startsWith("_") && content.endsWith("_")) - ) { - if (isHistory) { - console.log(`<${name} ${content.substring(1, content.length - 1)}>`); - } else { - console.log( - chalk.bold.green( - `<${name} ${content.substring(1, content.length - 1)}>` - ) - ); - } - } else { - if (isHistory) { - console.log( - `[${name}]${" ".repeat(nameLength - (name.length + 2))} ${content}` - ); - } else { - const nameColor = bot ? chalk.bold.yellow : chalk.bold.cyan; - - // TODO: markdown - console.log( - nameColor(`[${name}]`) + - " ".repeat(nameLength - (name.length + 2)) + - chalk.reset(" " + content) - ); - } - } - - if (attachments) { - for (const attachment of attachments) { - if (isHistory) { - console.log(``); - } else { - console.log(chalk.bold.yellow(``)); - } - } - } -} - -function processQueue() { - for (const msg of messageQueue) { - if (msg.content.indexOf("\n") > -1) { - const lines = msg.content.split("\n"); - for (const index in lines) { - const line = lines[index]; - processMessage({ - name: msg.author.username, - bot: msg.author.bot, - content: line, - attachments: index == lines.length - 1 ? msg.attachments : [], - reply: index == 0 ? msg.referencedMessage : null, - }); - } - } else { - processMessage({ - name: msg.author.username, - bot: msg.author.bot, - content: msg.content, - attachments: msg.attachments, - reply: msg.referencedMessage, - }); - } - } - - messageQueue.splice(0, messageQueue.length); -} - client.on("messageCreate", function (msg) { if (msg.author.id === client.user.id) return; - if (msg.channel.id == currentChannel) { - if (inSendMode || inEmoteMode) { - messageQueue.push(msg); + if (msg.channel.id == comcord.state.currentChannel) { + if (comcord.state.inPrompt) { + comcord.state.messageQueue.push(msg); } else { if (msg.content.indexOf("\n") > -1) { const lines = msg.content.split("\n"); @@ -197,11 +84,11 @@ client.on("messageCreate", function (msg) { client.on("messageUpdate", function (msg, old) { if (msg.author.id === client.user.id) return; - if (msg.channel.id == currentChannel) { + if (msg.channel.id == comcord.state.currentChannel) { if (msg.content == old.content) return; - if (inSendMode || inEmoteMode) { - messageQueue.push(msg); + if (comcord.state.inPrompt) { + comcord.state.messageQueue.push(msg); } else { if (msg.content.indexOf("\n") > -1) { const lines = msg.content.split("\n"); @@ -228,551 +115,32 @@ client.on("messageUpdate", function (msg, old) { } }); -let toSend = ""; -async function setupSendMode() { - inSendMode = true; - toSend = ""; - stdout.write( - chalk.bold.cyan(`[${client.user.username}]`) + - " ".repeat(nameLength - (client.user.username.length + 2)) + - chalk.reset(" ") - ); - try { - await client.guilds - .get(currentGuild) - .channels.get(currentChannel) - .sendTyping(); - } catch (err) { - // - } -} -async function sendMessage() { - toSend = toSend.trim(); - if (toSend === "") { - stdout.write("\n"); - } else { - try { - stdout.write("\n"); - await client.createMessage(currentChannel, toSend); - } catch (err) { - console.log(""); - } - } - inSendMode = false; - processQueue(); -} - -function showHelp() { - console.log("\nCOMcord (c)left 2022\n"); - - const keys = Object.keys(commands); - keys.sort((a, b) => a.localeCompare(b)); - - let index = 0; - for (const key of keys) { - const desc = commands[key]; - const length = ` ${key} - ${desc}`.length; - - stdout.write( - " " + - chalk.bold.yellow(key) + - chalk.reset(" - " + desc) + - " ".repeat(Math.abs(25 - length)) - ); - - index++; - if (index % 3 == 0) stdout.write("\n"); - } - if (index % 3 != 0) stdout.write("\n"); - - console.log("\nTo begin TALK MODE, press [SPACE]\n"); -} - -function listGuilds() { - let longest = 0; - const guilds = []; - - for (const guild of client.guilds.values()) { - if (guild.name.length > longest) longest = guild.name.length; - - const online = [...guild.members.values()].filter((m) => m.status).length; - guilds.push({name: guild.name, members: guild.memberCount, online}); - } - - console.log(""); - console.log(" " + "guild-name".padStart(longest, " ") + " online total"); - console.log("-".repeat(80)); - for (const guild of guilds) { - console.log( - " " + - guild.name.padStart(longest, " ") + - " " + - guild.online.toString().padStart(6, " ") + - " " + - guild.members.toString().padStart(5, " ") - ); - } - console.log(""); -} - -function listChannels() { - let longest = 0; - let longestTopic = 0; - const guild = client.guilds.get(currentGuild); - const channels = [...guild.channels.values()].filter((c) => c.type == 0); - channels.sort((a, b) => a.position - b.position); - - for (const channel of channels) { - const perms = channel.permissionsOf(client.user.id); - const private = !perms.has(Eris.Constants.Permissions.readMessages); - - if (channel.name.length + (private ? 1 : 0) > longest) - longest = channel.name.length + (private ? 1 : 0); - if (channel.topic != null && channel.topic.length > longestTopic) - longestTopic = channel.topic.length; - } - - console.log(""); - console.log( - " " + - "channel-name".padStart(longest, " ") + - " " + - "topic".padStart(Math.min(80 - (longest + 5), longestTopic), " ") - ); - console.log("-".repeat(80)); - for (const channel of channels) { - const topic = - channel.topic != null ? channel.topic.replace(/\n/g, " ") : ""; - const perms = channel.permissionsOf(client.user.id); - const private = !perms.has(Eris.Constants.Permissions.readMessages); - - console.log( - " " + - ((private ? "*" : "") + channel.name).padStart(longest, " ") + - " " + - (topic.length > 80 - longest + 9 - ? topic.substring(0, 79 - (longest + 5)) + "\u2026" - : topic.padStart(Math.min(80 - (longest + 5), longestTopic), " ")) - ); - } - console.log(""); -} - -let targetGuild = ""; -function gotoGuild() { - targetGuild = ""; - guildSwitch = true; - - stdout.write(":guild> "); -} - -function findTopChannel(guildId) { - const guild = client.guilds.get(guildId); - const channels = [...guild.channels.values()].filter((c) => c.type == 0); - channels.sort((a, b) => a.position - b.position); - - return channels[0]; -} - -function getStatus(status) { - let color; - switch (status) { - case "online": - color = chalk.bold.green; - break; - case "idle": - color = chalk.bold.yellow; - break; - case "dnd": - color = chalk.bold.red; - break; - default: - color = chalk.bold; - break; - } - - return color(" \u2022 "); -} - -function listUsers() { - const guild = client.guilds.get(currentGuild); - const channel = guild.channels.get(currentChannel); - - console.log( - `\n[you are in '${guild.name}' in '${channel.name}' among ${guild.memberCount}]\n` - ); - - const online = [...guild.members.values()].filter((m) => m.status); - online.sort((a, b) => a.name - b.name); - - let longest = 0; - for (const member of online) { - const name = member.user.username + "#" + member.user.discriminator; - if (name.length + 3 > longest) longest = name.length + 3; - } - - const columns = Math.ceil(stdout.columns / longest); - - let index = 0; - for (const member of online) { - const name = member.user.username + "#" + member.user.discriminator; - const status = getStatus(member.status); - const nameAndStatus = chalk.reset(name) + status; - - index++; - stdout.write( - nameAndStatus + - " ".repeat( - index % columns == 0 ? 0 : Math.abs(longest - (name.length + 3)) - ) - ); - - if (index % columns == 0) stdout.write("\n"); - } - if (index % columns != 0) stdout.write("\n"); - console.log(""); - - if (channel.topic != null) { - console.log("--Topic".padEnd(80, "-")); - console.log(channel.topic); - console.log("-".repeat(80)); - console.log(""); - } -} - -function switchGuild() { - targetGuild = targetGuild.trim(); - if (targetGuild == "") { - listChannels(); - listUsers(); - guildSwitch = false; - return; - } - - let target; - - for (const guild of client.guilds.values()) { - if (guild.name.toLowerCase().indexOf(targetGuild.toLowerCase()) > -1) { - target = guild.id; - break; - } - } - - if (target == null) { - console.log(""); - } else { - currentGuild = target; - // TODO: store last visited channel and switch to it if we've been to this guild before - const topChannel = findTopChannel(target); - currentChannel = topChannel.id; - - listChannels(); - listUsers(); - } - - guildSwitch = false; -} - -let targetChannel = ""; -function gotoChannel() { - targetChannel = ""; - channelSwitch = true; - - stdout.write(":channel> "); -} - -function switchChannel() { - targetChannel = targetChannel.trim(); - if (targetChannel == "") { - listUsers(); - channelSwitch = false; - return; - } - let target; - - const guild = client.guilds.get(currentGuild); - const channels = [...guild.channels.values()].filter((c) => c.type == 0); - channels.sort((a, b) => a.position - b.position); - - for (const channel of channels) { - if (channel.name.toLowerCase().indexOf(targetChannel.toLowerCase()) > -1) { - target = channel.id; - break; - } - } - - if (target == null) { - console.log(""); - } else { - currentChannel = target; - - listUsers(); - } - - channelSwitch = false; -} - -function startEmote() { - toSend = ""; - inEmoteMode = true; - - stdout.write(":emote> "); -} - -async function sendEmote() { - toSend = toSend.trim(); - if (toSend === "") { - console.log(""); - } else { - try { - await client.createMessage(currentChannel, "*" + toSend + "*"); - console.log(`<${client.user.username} ${toSend}>`); - } catch (err) { - console.log(""); - } - } - inEmoteMode = false; - processQueue(); -} - -async function getHistory(limit = 20) { - const messages = await client.getMessages(currentChannel, {limit}); - messages.reverse(); - - console.log("--Beginning-Review".padEnd(72, "-")); - - for (const msg of messages) { - if (msg.content.indexOf("\n") > -1) { - const lines = msg.content.split("\n"); - for (const index in lines) { - const line = lines[index]; - processMessage({ - name: msg.author.username, - bot: msg.author.bot, - content: - line + - (msg.editedTimestamp != null && index == lines.length - 1 - ? " (edited)" - : ""), - attachments: index == lines.length - 1 ? msg.attachments : null, - reply: index == 0 ? msg.referencedMessage : null, - isHistory: true, - }); - } - } else { - processMessage({ - name: msg.author.username, - bot: msg.author.bot, - content: msg.content + (msg.editedTimestamp != null ? " (edited)" : ""), - attachments: msg.attachments, - reply: msg.referencedMessage, - isHistory: true, - }); - } - } - - console.log("--Review-Complete".padEnd(73, "-")); -} - -let numLines = ""; -function startExtendedHistory() { - numLines = ""; - extendedHistory = true; - - stdout.write(":lines> "); -} - -async function getExtendedHistory() { - numLines = numLines.trim(); - numLines = parseInt(numLines); - if (isNaN(numLines)) { - console.log(""); - extendedHistory = false; - return; - } - - try { - await getHistory(numLines); - } catch (err) { - console.log(""); - } - - extendedHistory = false; -} - -stdin.on("data", function (key) { - if (guildSwitch) { +process.stdin.on("data", async function (key) { + if (comcord.state.inPrompt) { if (key === "\r") { - console.log(""); - switchGuild(); + await finalizePrompt(); + processQueue(); } else { if (key === "\b") { - if (targetGuild.length > 0) { - stdout.moveCursor(-1); - stdout.write(" "); - stdout.moveCursor(-1); - targetGuild = targetGuild.substring(0, targetGuild.length - 1); + if (comcord.state.promptInput.length > 0) { + process.stdout.moveCursor(-1); + process.stdout.write(" "); + process.stdout.moveCursor(-1); + comcord.state.promptInput = comcord.state.promptInput.substring( + 0, + comcord.state.promptInput.length - 1 + ); } } else { - stdout.write(key); - targetGuild += key; - } - } - } else if (channelSwitch) { - if (key === "\r") { - console.log(""); - switchChannel(); - } else { - if (key === "\b") { - if (targetChannel.length > 0) { - stdout.moveCursor(-1); - stdout.write(" "); - stdout.moveCursor(-1); - targetChannel = targetChannel.substring(0, targetChannel.length - 1); - } - } else { - stdout.write(key); - targetChannel += key; - } - } - } else if (inSendMode) { - if (key === "\r") { - sendMessage(); - } else { - if (key === "\b") { - if (toSend.length > 0) { - stdout.moveCursor(-1); - stdout.write(" "); - stdout.moveCursor(-1); - toSend = toSend.substring(0, toSend.length - 1); - } - } else { - stdout.write(key); - toSend += key; - } - } - } else if (inEmoteMode) { - if (key === "\r") { - console.log(""); - sendEmote(); - } else { - if (key === "\b") { - if (toSend.length > 0) { - stdout.moveCursor(-1); - stdout.write(" "); - stdout.moveCursor(-1); - toSend = toSend.substring(0, toSend.length - 1); - } - } else { - stdout.write(key); - toSend += key; - } - } - } else if (extendedHistory) { - if (key === "\r") { - console.log(""); - getExtendedHistory(); - } else { - if (key === "\b") { - if (numLines.length > 0) { - stdout.moveCursor(-1); - stdout.write(" "); - stdout.moveCursor(-1); - numLines = numLines.substring(0, numLines.length - 1); - } - } else { - stdout.write(key); - numLines += key; + process.stdout.write(key); + comcord.state.promptInput += key; } } } else { - switch (key) { - case "\u0003": - case "q": { - client.disconnect(false); - process.exit(0); - break; - } - case "h": { - showHelp(); - break; - } - case "g": { - if (currentGuild == null) { - console.log(""); - break; - } - gotoChannel(); - break; - } - case "G": { - gotoGuild(); - break; - } - case "l": { - if (currentGuild == null) { - console.log(""); - break; - } - listChannels(); - break; - } - case "L": { - listGuilds(); - break; - } - case "w": { - if (currentGuild == null) { - console.log(""); - break; - } - listUsers(); - break; - } - case "e": { - if (currentChannel == null) { - console.log(""); - break; - } - startEmote(); - break; - } - case "r": { - if (currentChannel == null) { - console.log(""); - break; - } - getHistory(); - break; - } - case "R": { - if (currentChannel == null) { - console.log(""); - break; - } - startExtendedHistory(); - break; - } - case "c": { - console.clear(); - break; - } - case "<": { - break; - } - case ">": { - break; - } - case " ": - case "\r": - default: { - if (currentChannel == null) { - console.log(""); - break; - } - setupSendMode(); - break; - } + if (comcord.commands[key]) { + comcord.commands[key].callback(); + } else { + sendMode(); } } }); diff --git a/src/lib/command.js b/src/lib/command.js new file mode 100644 index 0000000..842ea3f --- /dev/null +++ b/src/lib/command.js @@ -0,0 +1,17 @@ +function addCommand(key, name, callback) { + if (comcord.commands[key]) { + console.error( + `Registering duplicate key for "${key}": "${name}" wants to overwrite "${comcord.commands[key].name}"!` + ); + return; + } + + comcord.commands[key] = { + name, + callback, + }; +} + +module.exports = { + addCommand, +}; diff --git a/src/lib/messages.js b/src/lib/messages.js new file mode 100644 index 0000000..27ca9b5 --- /dev/null +++ b/src/lib/messages.js @@ -0,0 +1,117 @@ +const chalk = require("chalk"); + +function processMessage({ + name, + content, + bot, + attachments, + reply, + noColor = false, +}) { + if (name.length + 2 > comcord.state.nameLength) + comcord.statenameLength = name.length + 2; + + if (reply) { + const nameColor = reply.author.bot ? chalk.bold.yellow : chalk.bold.cyan; + + const headerLength = 5 + reply.author.username.length; + const length = headerLength + reply.content.length; + + if (noColor) { + console.log( + ` \u250d [${reply.author.username}] ${ + length > 79 + ? reply.content.substring(0, length - headerLength) + "\u2026" + : reply.content + }` + ); + } else { + console.log( + chalk.bold.white(" \u250d ") + + nameColor(`[${reply.author.username}] `) + + chalk.reset( + `${ + length > 79 + ? reply.content.substring(0, length - headerLength) + "\u2026" + : reply.content + }` + ) + ); + } + } + + if ( + (content.startsWith("*") && content.endsWith("*")) || + (content.startsWith("_") && content.endsWith("_")) + ) { + if (noColor) { + console.log(`<${name} ${content.substring(1, content.length - 1)}>`); + } else { + console.log( + chalk.bold.green( + `<${name} ${content.substring(1, content.length - 1)}>` + ) + ); + } + } else { + if (noColor) { + console.log( + `[${name}]${" ".repeat( + Math.abs(comcord.state.nameLength - (name.length + 2)) + )} ${content}` + ); + } else { + const nameColor = bot ? chalk.bold.yellow : chalk.bold.cyan; + + // TODO: markdown + console.log( + nameColor(`[${name}]`) + + " ".repeat(Math.abs(comcord.state.nameLength - (name.length + 2))) + + chalk.reset(" " + content) + ); + } + } + + if (attachments) { + for (const attachment of attachments) { + if (noColor) { + console.log(``); + } else { + console.log(chalk.bold.yellow(``)); + } + } + } +} + +function processQueue() { + for (const msg of comcord.state.messageQueue) { + if (msg.content.indexOf("\n") > -1) { + const lines = msg.content.split("\n"); + for (const index in lines) { + const line = lines[index]; + processMessage({ + name: msg.author.username, + bot: msg.author.bot, + content: line, + attachments: index == lines.length - 1 ? msg.attachments : [], + reply: index == 0 ? msg.referencedMessage : null, + }); + } + } else { + processMessage({ + name: msg.author.username, + bot: msg.author.bot, + content: msg.content, + attachments: msg.attachments, + reply: msg.referencedMessage, + }); + } + } + + comcord.state.messageQueue.splice(0, comcord.state.messageQueue.length); +} + +module.exports = { + processMessage, + processQueue, +}; diff --git a/src/lib/prompt.js b/src/lib/prompt.js new file mode 100644 index 0000000..0c882ed --- /dev/null +++ b/src/lib/prompt.js @@ -0,0 +1,25 @@ +function startPrompt(display, callback) { + comcord.state.inPrompt = true; + comcord.state.promptText = display; + comcord.state.promptInput = ""; + + comcord.state.promptCallback = callback; + + process.stdout.write(display); +} + +async function finalizePrompt() { + comcord.state.inPrompt = false; + comcord.state.promptText = null; + + const input = comcord.state.promptInput.trim(); + await comcord.state.promptCallback(input); + + comcord.state.promptInput = null; + comcord.state.promptCallback = null; +} + +module.exports = { + startPrompt, + finalizePrompt, +};