From d60313ee26441985d6a9530b0f15538fbc251540 Mon Sep 17 00:00:00 2001 From: Szedann <65607498+Szedann@users.noreply.github.com> Date: Sun, 30 Jul 2023 14:58:03 +0200 Subject: [PATCH] Create 0.5.1c issue detector (#5) --- Dockerfile | 2 +- package.json | 5 +- pnpm-lock.yaml | 166 ++++++++++-------- src/handlers/log.handler.ts | 37 +++- src/logAnalyzers/_logAnalyzers.ts | 6 - src/logIssueAnalyzers/_logIssueAnalyzers.ts | 12 ++ src/logIssueAnalyzers/createVersion.ts | 12 ++ src/logIssueAnalyzers/minecraftVersion.ts | 15 ++ .../optifine.ts | 4 +- src/logs/Analyzer.ts | 107 +++++++++++ 10 files changed, 272 insertions(+), 94 deletions(-) delete mode 100644 src/logAnalyzers/_logAnalyzers.ts create mode 100644 src/logIssueAnalyzers/_logIssueAnalyzers.ts create mode 100644 src/logIssueAnalyzers/createVersion.ts create mode 100644 src/logIssueAnalyzers/minecraftVersion.ts rename src/{logAnalyzers => logIssueAnalyzers}/optifine.ts (73%) create mode 100644 src/logs/Analyzer.ts diff --git a/Dockerfile b/Dockerfile index 6e88e06..c25aab4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ RUN corepack prepare pnpm@latest --activate WORKDIR /app -COPY package.json pnpm-lock.yaml . +COPY package.json pnpm-lock.yaml ./ RUN pnpm install --frozen-lockfile COPY . . diff --git a/package.json b/package.json index 3e58dc7..d360a67 100644 --- a/package.json +++ b/package.json @@ -1,4 +1,7 @@ { + "name": "snrbot", + "version": "1.0.0", + "license": "GPL-3.0", "scripts": { "dev": "cross-env NODE_ENV=development tsx watch src/index.ts", "start": "tsx src/index.ts", @@ -10,6 +13,7 @@ "colorette": "^2.0.20", "discord.js": "14.11.0", "express": "^4.18.2", + "gray-matter": "^4.0.3", "tsx": "3.12.7" }, "devDependencies": { @@ -20,7 +24,6 @@ "cross-env": "^7.0.3", "dotenv": "16.3.1", "eslint": "8.43.0", - "gray-matter": "4.0.3", "prettier": "2.8.8", "typescript": "5.1.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 84f309c..d1ed1fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,53 +5,53 @@ settings: excludeLinksFromLockfile: false dependencies: - '@discordjs/rest': - specifier: 1.7.1 - version: 1.7.1 - colorette: - specifier: ^2.0.20 - version: 2.0.20 - discord.js: - specifier: 14.11.0 - version: 14.11.0 - express: - specifier: ^4.18.2 - version: 4.18.2 - tsx: - specifier: 3.12.7 - version: 3.12.7 + '@discordjs/rest': + specifier: 1.7.1 + version: 1.7.1 + colorette: + specifier: ^2.0.20 + version: 2.0.20 + discord.js: + specifier: 14.11.0 + version: 14.11.0 + express: + specifier: ^4.18.2 + version: 4.18.2 + gray-matter: + specifier: ^4.0.3 + version: 4.0.3 + tsx: + specifier: 3.12.7 + version: 3.12.7 devDependencies: - '@types/express': - specifier: ^4.17.17 - version: 4.17.17 - '@types/node': - specifier: 20.3.1 - version: 20.3.1 - '@typescript-eslint/eslint-plugin': - specifier: 5.60.1 - version: 5.60.1(@typescript-eslint/parser@5.60.1)(eslint@8.43.0)(typescript@5.1.3) - '@typescript-eslint/parser': - specifier: 5.60.1 - version: 5.60.1(eslint@8.43.0)(typescript@5.1.3) - cross-env: - specifier: ^7.0.3 - version: 7.0.3 - dotenv: - specifier: 16.3.1 - version: 16.3.1 - eslint: - specifier: 8.43.0 - version: 8.43.0 - gray-matter: - specifier: 4.0.3 - version: 4.0.3 - prettier: - specifier: 2.8.8 - version: 2.8.8 - typescript: - specifier: 5.1.3 - version: 5.1.3 + '@types/express': + specifier: ^4.17.17 + version: 4.17.17 + '@types/node': + specifier: 20.3.1 + version: 20.3.1 + '@typescript-eslint/eslint-plugin': + specifier: 5.60.1 + version: 5.60.1(@typescript-eslint/parser@5.60.1)(eslint@8.43.0)(typescript@5.1.3) + '@typescript-eslint/parser': + specifier: 5.60.1 + version: 5.60.1(eslint@8.43.0)(typescript@5.1.3) + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + dotenv: + specifier: 16.3.1 + version: 16.3.1 + eslint: + specifier: 8.43.0 + version: 8.43.0 + prettier: + specifier: 2.8.8 + version: 2.8.8 + typescript: + specifier: 5.1.3 + version: 5.1.3 packages: /@aashutoshrathi/word-wrap@1.2.6: @@ -936,7 +936,7 @@ packages: } dependencies: sprintf-js: 1.0.3 - dev: true + dev: false /argparse@2.0.1: resolution: @@ -1531,7 +1531,7 @@ packages: engines: { node: '>=0.10.0' } dependencies: is-extendable: 0.1.1 - dev: true + dev: false /fast-deep-equal@3.1.3: resolution: @@ -1803,13 +1803,20 @@ packages: strip-bom-string: 1.0.0 dev: true - /has-flag@4.0.0: - resolution: - { - integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, - } - engines: { node: '>=8' } - dev: true + /gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + dependencies: + js-yaml: 3.14.1 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + dev: false + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true /has-proto@1.0.1: resolution: @@ -1925,7 +1932,7 @@ packages: integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==, } engines: { node: '>=0.10.0' } - dev: true + dev: false /is-extglob@2.1.1: resolution: @@ -1977,17 +1984,22 @@ packages: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: true + dev: false - /js-yaml@4.1.0: - resolution: - { - integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, - } - hasBin: true - dependencies: - argparse: 2.0.1 - dev: true + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: false + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true /json-schema-traverse@0.4.1: resolution: @@ -2011,6 +2023,11 @@ packages: engines: { node: '>=0.10.0' } dev: true + /kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + dev: false + /levn@0.4.1: resolution: { @@ -2479,18 +2496,15 @@ packages: dependencies: extend-shallow: 2.0.1 kind-of: 6.0.3 - dev: true + dev: false - /semver@7.5.4: - resolution: - { - integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==, - } - engines: { node: '>=10' } - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true /send@0.18.0: resolution: diff --git a/src/handlers/log.handler.ts b/src/handlers/log.handler.ts index fa3e3f8..80703a1 100644 --- a/src/handlers/log.handler.ts +++ b/src/handlers/log.handler.ts @@ -2,9 +2,10 @@ import { EmbedBuilder, Events } from 'discord.js'; // log providers import logProviders from '../logProviders/_logProviders'; -import logAnalyzers from '../logAnalyzers/_logAnalyzers'; +import logAnalyzers from '../logIssueAnalyzers/_logIssueAnalyzers'; import { Handler } from '..'; +import { analyzers } from '../logs/Analyzer'; export type LogAnalyzer = ( url: string @@ -14,6 +15,10 @@ export interface LogProvider { parse: (url: string) => Promise; } +export type Analyzer = ( + log: string +) => Promise<{ name: string; value: string } | null>; + const hostnameMap = new Map Promise>(); for (const provider of logProviders) { @@ -77,22 +82,38 @@ export const logHandler: Handler = (client) => { if (!regexPasses.find((reg) => log.match(reg))) return; + const logInfo: { name: string; value: string }[] = []; + + for (const analyzer of analyzers) { + const info = await analyzer(log); + if (info) logInfo.push(info); + } + + const logInfoEmbed = new EmbedBuilder() + .setTitle('Log File') + .setDescription('__Environment info__') + .setColor('Green') + .setFields(...logInfo); + const issues = await findIssues(log); - const embed = new EmbedBuilder() + if (!issues.length) { + message.reply({ embeds: [logInfoEmbed] }); + return; + } + + const issuesEmbed = new EmbedBuilder() .setTitle('Log analysis') .setDescription( - `${issues.length || 'No'} issue${ + `${issues.length} issue${ issues.length == 1 ? '' : 's' } found automatically` ) .setFields(...issues) - .setColor(issues.length ? 'Red' : 'Green'); + .setColor('Red'); - if (log != null) { - message.reply({ embeds: [embed] }); - return; - } + message.reply({ embeds: [logInfoEmbed, issuesEmbed] }); + return; } catch (error) { console.error('Unhandled exception on MessageCreate', error); } diff --git a/src/logAnalyzers/_logAnalyzers.ts b/src/logAnalyzers/_logAnalyzers.ts deleted file mode 100644 index 1591349..0000000 --- a/src/logAnalyzers/_logAnalyzers.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { LogAnalyzer } from '../handlers/log.handler'; -import { optifineAnalyzer } from './optifine'; - -export const logAnalyzers: LogAnalyzer[] = [optifineAnalyzer]; - -export default logAnalyzers; diff --git a/src/logIssueAnalyzers/_logIssueAnalyzers.ts b/src/logIssueAnalyzers/_logIssueAnalyzers.ts new file mode 100644 index 0000000..de14ad3 --- /dev/null +++ b/src/logIssueAnalyzers/_logIssueAnalyzers.ts @@ -0,0 +1,12 @@ +import { Analyzer } from '../handlers/log.handler'; +import { createVersionAnalyzer } from './createVersion'; +import { minecraftVersionAnalyzer } from './minecraftVersion'; +import { optifineAnalyzer } from './optifine'; + +export const logAnalyzers: Analyzer[] = [ + optifineAnalyzer, + minecraftVersionAnalyzer, + createVersionAnalyzer, +]; + +export default logAnalyzers; diff --git a/src/logIssueAnalyzers/createVersion.ts b/src/logIssueAnalyzers/createVersion.ts new file mode 100644 index 0000000..1ec817a --- /dev/null +++ b/src/logIssueAnalyzers/createVersion.ts @@ -0,0 +1,12 @@ +import { Analyzer } from '../handlers/log.handler'; + +export const createVersionAnalyzer: Analyzer = async (text) => { + const matchesCreate = text.match(/create-(.)+-0\.5\.1\.c/); + if (matchesCreate) { + return { + name: 'Incompatible with Create 0.5.1', + value: "Create: Steam 'n' Rails is currently incompatible with `Create 0.5.1c`. Downgrade to `Create 0.5.1b`.", + }; + } + return null; +}; diff --git a/src/logIssueAnalyzers/minecraftVersion.ts b/src/logIssueAnalyzers/minecraftVersion.ts new file mode 100644 index 0000000..71c727b --- /dev/null +++ b/src/logIssueAnalyzers/minecraftVersion.ts @@ -0,0 +1,15 @@ +import { Analyzer } from '../handlers/log.handler'; +import { getMinecraftVersion } from '../logs/Analyzer'; + +export const minecraftVersionAnalyzer: Analyzer = async (log) => { + const minecraftVersion = getMinecraftVersion(log); + console.log(minecraftVersion); + if (!minecraftVersion) return null; + const incompatibleVersions = new Set(['1.19.3', '1.19.4']); + if (incompatibleVersions.has(minecraftVersion)) + return { + name: 'Incompatible with that version of Minecraft', + value: "Steam 'n' Rails is currently only compatible with MC 1.18.2 and 1.19.2.", + }; + return null; +}; diff --git a/src/logAnalyzers/optifine.ts b/src/logIssueAnalyzers/optifine.ts similarity index 73% rename from src/logAnalyzers/optifine.ts rename to src/logIssueAnalyzers/optifine.ts index 5382f53..0f711b3 100644 --- a/src/logAnalyzers/optifine.ts +++ b/src/logIssueAnalyzers/optifine.ts @@ -1,6 +1,6 @@ -import { LogAnalyzer } from '../handlers/log.handler'; +import { Analyzer } from '../handlers/log.handler'; -export const optifineAnalyzer: LogAnalyzer = async (text) => { +export const optifineAnalyzer: Analyzer = async (text) => { const matchesOptifine = text.match(/f_174747_/); if (matchesOptifine) { return { diff --git a/src/logs/Analyzer.ts b/src/logs/Analyzer.ts new file mode 100644 index 0000000..01a5fdc --- /dev/null +++ b/src/logs/Analyzer.ts @@ -0,0 +1,107 @@ +import { Analyzer } from '../handlers/log.handler'; + +export const getMinecraftVersion = (log: string) => { + const minecraftVerionRegexes = [ + /\n\|[\s\d]+\| Minecraft\s+\| minecraft\s+\| (\S+).+\n/, + /: Loading Minecraft (\S+)/, + /--fml.mcVersion, ([^\s,]+)/, + /--version, ([^,]+),/, + / --version (\\S+) /, + /Minecraft Version: (.+)/, + ]; + + for (const regex of minecraftVerionRegexes) { + const regRes = log.match(regex); + if (!regRes) continue; + return regRes[1].toString(); + } +}; + +const minecraftVersionAnalyser: Analyzer = async (log) => { + const version = getMinecraftVersion(log); + if (!version) return null; + return { + name: 'Minecraft Version', + value: '`' + version + '`', + }; +}; + +export const getJavaVersion = (log: string) => { + const javaVersionRegexes = [/Java Version: (.+)/, /Java is version (.+?),/]; + for (const regex of javaVersionRegexes) { + const regRes = log.match(regex); + if (!regRes) continue; + const version = regRes[1].toString(); + return version; + } +}; + +export const javaVersionAnalyzer: Analyzer = async (log) => { + const version = getJavaVersion(log); + if (!version) return null; + return { + name: 'Java Version', + value: '`' + version + '`', + }; +}; + +export const javaArgumentsAnalyzer: Analyzer = async (text) => { + const javaArgumentsRegex = /Java Arguments:\s+(\[[^\]]+\])/; + const javaArgumentsMatch = text.match(javaArgumentsRegex); + + if (javaArgumentsMatch) { + const javaargs = javaArgumentsMatch[1].toString(); + return { + name: 'Java Arguments', + value: `\`\`\`${javaargs}\`\`\``, + }; + } + return null; +}; + +export type Loader = 'Quilt' | 'Fabric' | 'Forge'; + +export function getLoader( + log: string +): { loader: Loader; version: string } | undefined { + const loaderRegexes = new Map(); + loaderRegexes.set('Quilt', [ + /\n\|[\s\d]+\| Quilt Loader\s+\| quilt_loader\s+\| (\S+).+\n/, + /: Loading .+ with Quilt Loader (\S+)/, + ]); + loaderRegexes.set('Fabric', [/: Loading .+ with Fabric Loader (\S+)/]); + loaderRegexes.set('Forge', [ + /--fml.forgeVersion, ([^\s,]+)/, + /MinecraftForge v([^\\s,]+) Initialized/, + ]); + + for (const loader of loaderRegexes.keys()) { + const regexes = loaderRegexes.get(loader); + if (!regexes) continue; + for (const regex of regexes) { + const regRes = log.match(regex); + if (!regRes) continue; + const version = regRes[1].toString(); + return { + loader: loader, + version: version, + }; + } + } +} + +const loaderAnalyser: Analyzer = async (log) => { + const loaderData = getLoader(log); + if (!loaderData) return null; + const { loader, version } = loaderData; + return { + name: 'Loader', + value: `\`${loader} (${version})\``, + }; +}; + +export const analyzers: Analyzer[] = [ + minecraftVersionAnalyser, + javaVersionAnalyzer, + loaderAnalyser, +];