diff --git a/assets/special_messages.json b/assets/special_messages.json new file mode 100644 index 0000000..7591830 --- /dev/null +++ b/assets/special_messages.json @@ -0,0 +1,54 @@ +[ + "joined the game", + "left the game", + + "has completed the challenge", + "has made the advancement", + "has reached the goal", + + "fell off", + "fell while climbing", + "fell from a high place", + "was doomed to fall", + "fell too far and was finished by", + "was struck by lightning", + "went up in flames", + "walked into fire", + "burned to death", + "was burnt to a crisp", + "tried to swim in lava", + "discovered the floor was lava", + "walked into danger zone due to", + "suffocated in a wall", + "was squished too much", + "was squashed by", + "drowned", + "starved to death", + "was pricked to death", + "walked into a cactus", + "died", + "blew up", + "was blown up by", + "withered away", + "was shot by a's skull", + "was slain by", + "was shot by", + "was fireballed by", + "was pummeled by", + "was killed", + "was impaled", + "hit the ground too hard", + "fell out of the world", + "didn't want to live in the same world as", + "was roasted in dragon breath", + "experienced kinetic energy", + "went off with a bang", + "was poked to death by a sweet berry bush", + "was stung to death", + "froze to death", + "was frozen to death by", + "was skewered by", + "was obliterated by a sonically-charged shriek", + "left the confines of this world", + "Actually, message was too long to deliver fully. Sorry! Here's stripped version: %s" +] diff --git a/package.json b/package.json index 97aea74..01994b2 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "format": "prettier --write \"**/*.ts\"", "lint": "eslint .", "lint-fix": "yarn lint --fix", - "start": "node .", + "start": "node --disable-warning=ExperimentalWarning .", "test": "jest --config=tests/jest.config.js" }, "keywords": [ diff --git a/src/bridge.ts b/src/bridge.ts index 28d65c0..4ce1fbc 100644 --- a/src/bridge.ts +++ b/src/bridge.ts @@ -1,6 +1,6 @@ +import { DiscordMessage, MinecraftMessage } from "./lib/messages.js"; import { Message, TextChannel } from "discord.js"; import { IServer } from "./interfaces.js"; -import { MinecraftMessage } from "./lib/messages.js"; import { PacketTooBigError } from "./lib/rcon/packet.js"; import { Rcon } from "./lib/rcon/rcon.js"; import { Tail } from "tail"; @@ -32,7 +32,7 @@ export class Bridge { const tail = new Tail(this.serverLogPath); tail.on("line", (line: string) => { - this.channel.send(line); + this.sendToDiscord(line); }); tail.on("error", (error: any) => { @@ -40,6 +40,13 @@ export class Bridge { }); } + async sendToDiscord(line: string): Promise { + const message = await DiscordMessage.filter(line, this.channel); + if (!message) return; // Message does not match what we want to send to Discord + + this.channel.send({ content: message, allowedMentions: { parse: ["users", "roles"] } }); + } + async sendToMinecraft(message: Message): Promise { // TODO: Handle errors in send const minecraftMsg = MinecraftMessage.format(message); diff --git a/src/endbot.ts b/src/endbot.ts index 7589221..3c8f75e 100644 --- a/src/endbot.ts +++ b/src/endbot.ts @@ -10,6 +10,7 @@ export class Endbot extends Client { super({ intents: [ GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMembers, GatewayIntentBits.GuildMessages, GatewayIntentBits.MessageContent, ], diff --git a/src/lib/messages.ts b/src/lib/messages.ts index 19fdc47..7bfbb7b 100644 --- a/src/lib/messages.ts +++ b/src/lib/messages.ts @@ -1,4 +1,87 @@ -import { Message } from "discord.js"; +import { Message, TextChannel, escapeMarkdown } from "discord.js"; +import specialMessages from "../../assets/special_messages.json" with { type: "json" }; + +export class DiscordMessage { + static async filter(line: string, channel: TextChannel): Promise { + const regex = /\[Server thread\/INFO\]: (.*)/; + const text = line.match(regex); + if (!text) return; // Filter out messages not part of the INFO log + + let message = text[1]; + // Remove messages of entities dying + if ( + (message.startsWith("Villager") && message.endsWith("'")) || + message.startsWith("Named entity class_") + ) + return; + + // Remove messages that aren't from players or special + if (message.startsWith("<")) { + message = await this.formatContent(message, channel); + } else if (!this.isSpecial(message)) { + return; + } + + return escapeMarkdown(message); + } + + private static async formatContent(message: string, channel: TextChannel): Promise { + if (message.includes("@")) message = await this.formatUsersAndRoles(message, channel); + if (message.includes("#")) message = this.formatChannels(message, channel); + return message; + } + + private static async formatUsersAndRoles( + message: string, + channel: TextChannel, + ): Promise { + // Members need to be fetched as the cache doesn't always contain all members. Not sure why, + // but the cache can be used for roles and channels just fine (as well as members in the + // MinecraftMessage class) + const members = await channel.guild!.members.fetch(); + members.forEach((member) => { + const memberId = "<@" + member.id + ">"; + const memberName = "@" + member.displayName; + if (message.includes(memberName)) { + message = message.replaceAll(memberName, memberId); + } + }); + + const roles = channel.guild!.roles.cache; + roles.forEach((role) => { + const roleId = "<@&" + role.id + ">"; + const roleName = "@" + role.name; + if (message.includes(roleName)) { + message = message.replaceAll(roleName, roleId); + } + }); + + return message; + } + + private static formatChannels(message: string, channel: TextChannel): string { + const channels = channel.guild!.channels.cache; + channels.forEach((discordChannel) => { + const channelId = "<#" + discordChannel.id + ">"; + const channelName = "#" + discordChannel.name; + if (message.includes(channelName)) { + message = message.replaceAll(channelName, channelId); + } + }); + + return message; + } + + private static isSpecial(message: string): boolean { + for (const phrase of specialMessages) { + if (message.includes(phrase)) { + return true; + } + } + + return false; + } +} export class MinecraftMessage { static format(message: Message): string { diff --git a/tsconfig.json b/tsconfig.json index acd7f36..5c6f7a8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,6 @@ "composite": true, "resolveJsonModule": true }, - "include": ["**/*"], + "include": ["**/*", "assets/*.json"], "exclude": ["node_modules", "dist"] }