diff --git a/README.md b/README.md index f09ce34..dbc8d73 100644 --- a/README.md +++ b/README.md @@ -50,10 +50,11 @@ You **MUST** grant your bot all Privileged Gateway Intents. - [x] AFK toggle (A) - [x] Send DM (s) - [x] Answer DM (a) + - [x] Peek (p) - [x] Message Receiving - [x] Markdown styling - - [ ] Common markdown (bold, italic, etc) - - [ ] Figure out how spoilers would work + - [x] Common markdown (bold, italic, etc) + - [x] Figure out how spoilers would work - [x] Emotes????? - [x] Timestamp parsing - [x] Mentions parsing diff --git a/src/commands/history.js b/src/commands/history.js index 7067b07..80ba873 100644 --- a/src/commands/history.js +++ b/src/commands/history.js @@ -1,24 +1,27 @@ const {addCommand} = require("../lib/command"); const {startPrompt} = require("../lib/prompt"); const {processMessage} = require("../lib/messages"); +const {listChannels} = require("./listChannels"); -async function getHistory(limit = 20) { - if (!comcord.state.currentChannel) { +async function getHistory(limit = 20, channel = null) { + if (!channel && !comcord.state.currentChannel) { console.log(""); return; } const messages = await comcord.client.getMessages( - comcord.state.currentChannel, - {limit} + channel ?? comcord.state.currentChannel ); messages.reverse(); console.log("--Beginning-Review".padEnd(72, "-")); + const lines = []; for (const msg of messages) { - processMessage(msg, {noColor: true, history: true}); + const processedLines = processMessage(msg, {noColor: true, history: true}); + if (processedLines) lines.push(...processedLines); } + console.log(lines.slice(-limit).join("\n")); console.log("--Review-Complete".padEnd(73, "-")); } @@ -44,3 +47,35 @@ addCommand("R", "extended history", function () { await getExtendedHistory(input); }); }); +addCommand("p", "peek at channel", function () { + if (!comcord.state.currentGuild) { + console.log(""); + return; + } + + listChannels(); + startPrompt(":peek> ", async function (input) { + console.log(""); + if (input == "") { + 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 { + await getHistory(20, target); + } + }); +}); diff --git a/src/commands/quit.js b/src/commands/quit.js index 931d5a2..3adb706 100644 --- a/src/commands/quit.js +++ b/src/commands/quit.js @@ -1,6 +1,7 @@ const {addCommand} = require("../lib/command"); addCommand("q", "quit comcord", function () { + comcord.state.quitting = true; comcord.client.disconnect({reconnect: false}); process.exit(0); }); diff --git a/src/index.js b/src/index.js index ccbb7f1..86aa96f 100644 --- a/src/index.js +++ b/src/index.js @@ -31,6 +31,7 @@ process.title = "comcord"; global.comcord = { config, state: { + connected: true, rpcConnected: false, startTime: Date.now(), currentGuild: null, @@ -118,6 +119,17 @@ client.once("ready", function () { } }); client.on("error", function () {}); +client.on("ready", function () { + if (comcord.state.connected === false) { + console.log("% Reconnected"); + } +}); +client.on("disconnect", function () { + if (!comcord.state.quitting) { + comcord.state.connected = false; + console.log("% Disconnected, retrying..."); + } +}); rpc.on("connected", function () { comcord.state.rpcConnected = true; @@ -147,6 +159,21 @@ rpc.once("ready", function () { rpc.on("error", function () {}); client.on("messageCreate", async function (msg) { + if ( + (msg.mentions.find((user) => user.id == client.user.id) || + msg.mentionEveryone) && + msg.channel.id != comcord.state.currentChannel && + msg.channel.type !== Constants.ChannelTypes.DM && + msg.channel.type !== Constants.ChannelTypes.GROUP_DM + ) { + const data = {ping: true, channel: msg.channel, author: msg.author}; + if (comcord.state.inPrompt) { + comcord.state.messageQueue.push(data); + } else { + processMessage(data); + } + } + if (!msg.author) return; if (msg.author.id === client.user.id) return; @@ -240,6 +267,7 @@ client.on("messageReactionAdd", async function (msg, emoji, reactor) { referencedMessage: reply, author: reactor?.user ?? client.users.get(reactor.id), timestamp: Date.now(), + mentions: [], content: `*reacted with ${emoji.id ? `:${emoji.name}:` : emoji.name}*`, }; diff --git a/src/lib/messages.js b/src/lib/messages.js index d4917f8..c63394c 100644 --- a/src/lib/messages.js +++ b/src/lib/messages.js @@ -3,7 +3,7 @@ const chalk = require("chalk"); const REGEX_CODEBLOCK = /```(?:([a-z0-9_+\-.]+?)\n)?\n*([^\n][^]*?)\n*```/i; const REGEX_CODEBLOCK_GLOBAL = - /```(?:([a-z0-9_+\-.]+?)\n)?\n*([^\n][^]*?)\n*```/gi; + /```(?:[a-z0-9_+\-.]+?\n)?\n*([^\n][^]*?)\n*```/gi; const REGEX_MENTION = /<@!?(\d+)>/g; const REGEX_ROLE_MENTION = /<@&?(\d+)>/g; @@ -11,6 +11,16 @@ const REGEX_CHANNEL = /<#(\d+)>/g; const REGEX_EMOTE = /<(?:\u200b|&)?a?:(\w+):(\d+)>/g; const REGEX_COMMAND = /<\/([^\s]+?):(\d+)>/g; +const REGEX_BLOCKQUOTE = /^ *>>?>? +/; +const REGEX_GREENTEXT = /^(>.+?)(?:\n|$)/; +const REGEX_SPOILER = /\|\|(.+?)\|\|/; +const REGEX_BOLD = /\*\*(.+?)\*\*/g; +const REGEX_UNDERLINE = /__(.+?)__/g; +const REGEX_ITALIC_1 = /\*(.+?)\*/g; +const REGEX_ITALIC_2 = /_(.+?)_/g; +const REGEX_STRIKE = /~~(.+?)~~/g; +const REGEX_3Y3 = /[\u{e0020}-\u{e007e}]{1,}/gu; + function readableTime(time) { const seconds = time / 1000; const minutes = seconds / 60; @@ -154,6 +164,37 @@ function replaceTimestamps(_, time, format = "f") { return TIME_FORMATS[format](time * 1000); } +function replaceStyledMarkdown(content) { + content = content.replace(REGEX_BLOCKQUOTE, chalk.blackBright("\u258e")); + content = content.replace(REGEX_GREENTEXT, (orig) => chalk.green(orig)); + + if (comcord.config.enable3y3) { + content = content.replace(REGEX_3Y3, (text) => + chalk.italic.magenta( + [...text] + .map((char) => String.fromCodePoint(char.codePointAt(0) - 0xe0000)) + .join("") + ) + ); + } + + content = content.replace(REGEX_SPOILER, (_, text) => + chalk.bgBlack.black(text) + ); + content = content.replace(REGEX_STRIKE, (_, text) => + chalk.strikethrough(text) + ); + content = content.replace(REGEX_BOLD, (_, text) => chalk.bold(text)); + content = content.replace(REGEX_UNDERLINE, (_, text) => + chalk.underline(text) + ); + content = content + .replace(REGEX_ITALIC_1, (_, text) => chalk.italic(text)) + .replace(REGEX_ITALIC_2, (_, text) => chalk.italic(text)); + + return content; +} + function formatMessage({ channel, name, @@ -163,6 +204,7 @@ function formatMessage({ stickers, reply, timestamp, + mention = false, noColor = false, dump = false, history = false, @@ -175,6 +217,16 @@ function formatMessage({ minutes = dateObj.getUTCMinutes().toString().padStart(2, "0"), seconds = dateObj.getUTCSeconds().toString().padStart(2, "0"); + let console = global.console; + const lines = []; + if (history) { + console = { + log: function (...args) { + lines.push(...args.join(" ").split("\n")); + }, + }; + } + if (name.length + 2 > comcord.state.nameLength) comcord.state.nameLength = name.length + 2; @@ -212,13 +264,11 @@ function formatMessage({ console.log( chalk.bold.white(" \u250d ") + nameColor(`[${reply.author.username}] `) + - chalk.reset( - `${ - length > 79 - ? replyContent.substring(0, 79 - headerLength) + "\u2026" - : replyContent - }` - ) + `${ + length > 79 + ? replyContent.substring(0, 79 - headerLength) + "\u2026" + : replyContent + }` ); } } @@ -261,18 +311,46 @@ function formatMessage({ if (dm) { if (noColor) { + if (comcord.config.enable3y3) { + content = content.replace( + REGEX_3Y3, + (text) => + `<3y3:${[...text] + .map((char) => + String.fromCodePoint(char.codePointAt(0) - 0xe0000) + ) + .join("")}>` + ); + } + console.log(`*${name}* ${content}\x07`); } else { - console.log( - chalk.bold.red(`*${name}*`) + chalk.reset(" " + content + "\x07") - ); + content = replaceStyledMarkdown(content); + + console.log(`${chalk.bold.red(`*${name}*`)} ${content}\x07`); } } else if ( - (content.length > 1 && - content.startsWith("*") && - content.endsWith("*")) || - (content.startsWith("_") && content.endsWith("_")) + content.length > 1 && + ((content.startsWith("*") && + content.endsWith("*") && + !content.startsWith("**") && + !content.endsWith("**")) || + (content.startsWith("_") && + content.endsWith("_") && + !content.startsWith("__") && + !content.endsWith("__"))) ) { + if (comcord.config.enable3y3) { + content = content.replace( + REGEX_3Y3, + (text) => + `<3y3:${[...text] + .map((char) => + String.fromCodePoint(char.codePointAt(0) - 0xe0000) + ) + .join("")}>` + ); + } const str = `<${name} ${content.substring(1, content.length - 1)}>`; if (noColor) { console.log(str); @@ -295,19 +373,36 @@ function formatMessage({ } } else { if (noColor) { + if (comcord.config.enable3y3) { + content = content.replace( + REGEX_3Y3, + (text) => + `<3y3:${[...text] + .map((char) => + String.fromCodePoint(char.codePointAt(0) - 0xe0000) + ) + .join("")}>` + ); + } + console.log( `[${name}]${" ".repeat( Math.abs(comcord.state.nameLength - (name.length + 2)) )} ${content}` ); } else { - const nameColor = bot ? chalk.bold.yellow : chalk.bold.cyan; + const nameColor = mention + ? chalk.bold.red + : bot + ? chalk.bold.yellow + : chalk.bold.cyan; + + content = replaceStyledMarkdown(content); - // TODO: markdown console.log( - nameColor(`[${name}]`) + - " ".repeat(Math.abs(comcord.state.nameLength - (name.length + 2))) + - chalk.reset(" " + content) + `${nameColor(`[${name}]`)}${" ".repeat( + Math.abs(comcord.state.nameLength - (name.length + 2)) + )} ${content}${mention ? "\x07" : ""}` ); } } @@ -338,6 +433,11 @@ function formatMessage({ } } } + + if (history) { + return lines; + } + return null; } function processMessage(msg, options = {}) { @@ -356,46 +456,67 @@ function processMessage(msg, options = {}) { if (msg.time) { console.log(msg.content); + return null; + } else if (msg.ping) { + console.log( + chalk.bold.red( + `**mentioned by ${msg.author?.username ?? ""} in #${ + msg.channel?.name ?? "" + } in ${msg.channel.guild?.name ?? ""}**\x07` + ) + ); + return null; } else if (msg.content && msg.content.indexOf("\n") > -1) { if (msg.content.match(REGEX_CODEBLOCK)) { - formatMessage({ + return formatMessage({ channel: msg.channel, name: msg.author.username, bot: msg.author.bot, content: msg.content.replace( REGEX_CODEBLOCK_GLOBAL, - (_, lang, content) => content + (_, content) => content ), attachments: msg.attachments, stickers: msg.stickerItems, reply: msg.referencedMessage, timestamp: msg.timestamp, + mention: + msg.mentionsEveryone || + msg.mentions.find((user) => user.id == comcord.client.user.id), dump: true, ...options, }); } else { const lines = msg.content.split("\n"); + const outLines = []; for (const index in lines) { const line = lines[index]; - formatMessage({ - channel: msg.channel, - 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 : [], - stickers: index == lines.length - 1 ? msg.stickerItems : [], - reply: index == 0 ? msg.referencedMessage : null, - timestamp: msg.timestamp, - ...options, - }); + outLines.push( + formatMessage({ + channel: msg.channel, + 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 : [], + stickers: index == lines.length - 1 ? msg.stickerItems : [], + reply: index == 0 ? msg.referencedMessage : null, + timestamp: msg.timestamp, + mention: + index == 0 && + (msg.mentionsEveryone || + msg.mentions.find((user) => user.id == comcord.client.user.id)), + ...options, + }) + ); } + return outLines; } } else { - formatMessage({ + return formatMessage({ channel: msg.channel, name: msg.author.username, bot: msg.author.bot, @@ -404,6 +525,9 @@ function processMessage(msg, options = {}) { stickers: msg.stickerItems, reply: msg.referencedMessage, timestamp: msg.timestamp, + mention: + msg.mentionsEveryone || + msg.mentions.find((user) => user.id == comcord.client.user.id), ...options, }); }