refactor everything out of one file, add channel rememberance

This commit is contained in:
Cynthia Foxwell 2022-08-28 16:01:31 -06:00
parent e8860722f0
commit adf3e5fe4d
17 changed files with 661 additions and 686 deletions

View file

@ -26,5 +26,6 @@ module.exports = {
"require-atomic-updates": OFF, "require-atomic-updates": OFF,
}, },
globals: { globals: {
comcord: true,
}, },
}; };

View file

@ -56,4 +56,4 @@ You **MUST** grant your bot all Privileged Gateway Intents.
- [ ] Default guild/channel - [ ] Default guild/channel
- [ ] Threads - [ ] Threads
- [ ] Not have the token just be in argv - [ ] Not have the token just be in argv
- [ ] Not have everything in one file - [x] Not have everything in one file

5
src/commands/clear.js Normal file
View file

@ -0,0 +1,5 @@
const {addCommand} = require("../lib/command");
addCommand("c", "clear", function () {
console.clear();
});

26
src/commands/emote.js Normal file
View file

@ -0,0 +1,26 @@
const {addCommand} = require("../lib/command");
const {startPrompt} = require("../lib/prompt");
addCommand("e", "emote", function () {
if (!comcord.state.currentChannel) {
console.log("<not in a channel>");
return;
}
startPrompt(":emote> ", async function (input) {
if (input == "") {
console.log("<no message sent>");
} 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("<failed to send message: " + err.message + ">");
}
}
});
});

29
src/commands/help.js Normal file
View file

@ -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");
});

72
src/commands/history.js Normal file
View file

@ -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("<not in a channel>");
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("<not a number>");
return;
}
try {
await getHistory(input);
} catch (err) {
console.log("<failed to get history: " + err.message + ">");
}
}
addCommand("r", "channel history", getHistory);
addCommand("R", "extended history", function () {
startPrompt(":lines> ", async function (input) {
process.stdout.write("\n");
await getExtendedHistory(input);
});
});

View file

@ -0,0 +1,62 @@
const Eris = require("eris");
const {addCommand} = require("../lib/command");
function listChannels() {
if (!comcord.state.currentGuild) {
console.log("<not in a guild>");
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,
};

View file

@ -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,
};

86
src/commands/listUsers.js Normal file
View file

@ -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("<not in a guild>");
return;
}
if (!comcord.state.currentChannel) {
console.log("<not in a channel>");
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,
};

6
src/commands/quit.js Normal file
View file

@ -0,0 +1,6 @@
const {addCommand} = require("../lib/command");
addCommand("q", "quit comcord", function () {
comcord.client.disconnect(false);
process.exit(0);
});

35
src/commands/send.js Normal file
View file

@ -0,0 +1,35 @@
const chalk = require("chalk");
const {startPrompt} = require("../lib/prompt");
function sendMode() {
if (!comcord.state.currentChannel) {
console.log("<not in a channel>");
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("<no message sent>");
} else {
try {
process.stdout.write("\n");
await comcord.client.createMessage(
comcord.state.currentChannel,
input
);
} catch (err) {
console.log("<failed to send message: " + err.message + ">");
}
}
}
);
}
module.exports = {sendMode};

View file

@ -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("<channel not found>");
} 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("<not in a guild>");
return;
}
startPrompt(":channel> ", switchChannel);
});

View file

@ -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("<guild not found>");
} 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);
});

View file

@ -2,46 +2,41 @@ const Eris = require("eris");
const chalk = require("chalk"); const chalk = require("chalk");
const token = process.argv[2]; const token = process.argv[2];
const stdin = process.stdin;
const stdout = process.stdout;
stdin.setRawMode(true); global.comcord = {
stdin.resume(); state: {
stdin.setEncoding("utf8"); currentGuild: null,
currentChannel: null,
let currentGuild, nameLength: 2,
currentChannel, inPrompt: false,
inSendMode = false, messageQueue: [],
inEmoteMode = false, lastChannel: new Map(),
guildSwitch = false, },
channelSwitch = false, commands: {},
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",
}; };
const client = new Eris("Bot " + token, { const client = new Eris("Bot " + token, {
defaultImageFormat: "png", defaultImageFormat: "png",
defaultImageSize: 1024, defaultImageSize: 1024,
intents: Eris.Constants.Intents.all, 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 () { client.once("ready", function () {
console.log( console.log(
@ -50,125 +45,17 @@ client.once("ready", function () {
`${client.user.username}#${client.user.discriminator} (${client.user.id})` `${client.user.username}#${client.user.discriminator} (${client.user.id})`
) )
); );
nameLength = client.user.username.length + 2; comcord.state.nameLength = client.user.username.length + 2;
listGuilds(); 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(`<attachment: ${attachment.url} >`);
} else {
console.log(chalk.bold.yellow(`<attachment: ${attachment.url} >`));
}
}
}
}
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) { client.on("messageCreate", function (msg) {
if (msg.author.id === client.user.id) return; if (msg.author.id === client.user.id) return;
if (msg.channel.id == currentChannel) { if (msg.channel.id == comcord.state.currentChannel) {
if (inSendMode || inEmoteMode) { if (comcord.state.inPrompt) {
messageQueue.push(msg); comcord.state.messageQueue.push(msg);
} else { } else {
if (msg.content.indexOf("\n") > -1) { if (msg.content.indexOf("\n") > -1) {
const lines = msg.content.split("\n"); const lines = msg.content.split("\n");
@ -197,11 +84,11 @@ client.on("messageCreate", function (msg) {
client.on("messageUpdate", function (msg, old) { client.on("messageUpdate", function (msg, old) {
if (msg.author.id === client.user.id) return; 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 (msg.content == old.content) return;
if (inSendMode || inEmoteMode) { if (comcord.state.inPrompt) {
messageQueue.push(msg); comcord.state.messageQueue.push(msg);
} else { } else {
if (msg.content.indexOf("\n") > -1) { if (msg.content.indexOf("\n") > -1) {
const lines = msg.content.split("\n"); const lines = msg.content.split("\n");
@ -228,551 +115,32 @@ client.on("messageUpdate", function (msg, old) {
} }
}); });
let toSend = ""; process.stdin.on("data", async function (key) {
async function setupSendMode() { if (comcord.state.inPrompt) {
inSendMode = true; if (key === "\r") {
toSend = ""; await finalizePrompt();
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("<no message sent>\n");
} else {
try {
stdout.write("\n");
await client.createMessage(currentChannel, toSend);
} catch (err) {
console.log("<failed to send message: " + err.message + ">");
}
}
inSendMode = false;
processQueue(); 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("<guild not found>");
} 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("<channel not found>");
} else {
currentChannel = target;
listUsers();
}
channelSwitch = false;
}
function startEmote() {
toSend = "";
inEmoteMode = true;
stdout.write(":emote> ");
}
async function sendEmote() {
toSend = toSend.trim();
if (toSend === "") {
console.log("<no message sent>");
} else {
try {
await client.createMessage(currentChannel, "*" + toSend + "*");
console.log(`<${client.user.username} ${toSend}>`);
} catch (err) {
console.log("<failed to send message: " + err.message + ">");
}
}
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("<not a number>");
extendedHistory = false;
return;
}
try {
await getHistory(numLines);
} catch (err) {
console.log("<failed to get history: " + err.message + ">");
}
extendedHistory = false;
}
stdin.on("data", function (key) {
if (guildSwitch) {
if (key === "\r") {
console.log("");
switchGuild();
} else { } else {
if (key === "\b") { if (key === "\b") {
if (targetGuild.length > 0) { if (comcord.state.promptInput.length > 0) {
stdout.moveCursor(-1); process.stdout.moveCursor(-1);
stdout.write(" "); process.stdout.write(" ");
stdout.moveCursor(-1); process.stdout.moveCursor(-1);
targetGuild = targetGuild.substring(0, targetGuild.length - 1); comcord.state.promptInput = comcord.state.promptInput.substring(
0,
comcord.state.promptInput.length - 1
);
} }
} else { } else {
stdout.write(key); process.stdout.write(key);
targetGuild += key; comcord.state.promptInput += 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;
} }
} }
} else { } else {
switch (key) { if (comcord.commands[key]) {
case "\u0003": comcord.commands[key].callback();
case "q": { } else {
client.disconnect(false); sendMode();
process.exit(0);
break;
}
case "h": {
showHelp();
break;
}
case "g": {
if (currentGuild == null) {
console.log("<not in a guild>");
break;
}
gotoChannel();
break;
}
case "G": {
gotoGuild();
break;
}
case "l": {
if (currentGuild == null) {
console.log("<not in a guild>");
break;
}
listChannels();
break;
}
case "L": {
listGuilds();
break;
}
case "w": {
if (currentGuild == null) {
console.log("<not in a guild>");
break;
}
listUsers();
break;
}
case "e": {
if (currentChannel == null) {
console.log("<not in a channel>");
break;
}
startEmote();
break;
}
case "r": {
if (currentChannel == null) {
console.log("<not in a channel>");
break;
}
getHistory();
break;
}
case "R": {
if (currentChannel == null) {
console.log("<not in a channel>");
break;
}
startExtendedHistory();
break;
}
case "c": {
console.clear();
break;
}
case "<": {
break;
}
case ">": {
break;
}
case " ":
case "\r":
default: {
if (currentChannel == null) {
console.log("<not in a channel>");
break;
}
setupSendMode();
break;
}
} }
} }
}); });

17
src/lib/command.js Normal file
View file

@ -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,
};

117
src/lib/messages.js Normal file
View file

@ -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(`<attachment: ${attachment.url} >`);
} else {
console.log(chalk.bold.yellow(`<attachment: ${attachment.url} >`));
}
}
}
}
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,
};

25
src/lib/prompt.js Normal file
View file

@ -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,
};