mirror of
https://github.com/zkldi/Tachi.git
synced 2026-09-30 19:07:58 +03:00
refactor: migrate bot/ to a sensible logging solution
This commit is contained in:
Vendored
+10
-10
@@ -9,36 +9,36 @@
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.biome": "explicit",
|
||||
}
|
||||
"source.fixAll.biome": "explicit"
|
||||
}
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.biome": "explicit",
|
||||
}
|
||||
"source.fixAll.biome": "explicit"
|
||||
}
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.biome": "explicit",
|
||||
}
|
||||
"source.fixAll.biome": "explicit"
|
||||
}
|
||||
},
|
||||
"[json]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.biome": "explicit",
|
||||
}
|
||||
"source.fixAll.biome": "explicit"
|
||||
}
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "biomejs.biome",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.biome": "explicit",
|
||||
}
|
||||
"source.fixAll.biome": "explicit"
|
||||
}
|
||||
},
|
||||
"files.associations": {
|
||||
"Justfile-*": "makefile"
|
||||
|
||||
@@ -19,6 +19,7 @@ fix: fmt
|
||||
|
||||
just gen
|
||||
bun run --filter '*' lint-fix
|
||||
.scripts/ts_autoinherit.js
|
||||
|
||||
# Automatically fix any issues we can in the codebase and then
|
||||
# make a commit for them
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
"mei-logger": "catalog:",
|
||||
"monk": "catalog:",
|
||||
"node-fetch": "catalog:",
|
||||
"pino": "^10.3.1",
|
||||
"prudence": "catalog:",
|
||||
"seq-logging": "catalog:",
|
||||
"tachi-common": "workspace:*",
|
||||
@@ -400,6 +401,7 @@
|
||||
"node-html-parser": "catalog:",
|
||||
"nodemailer": "catalog:",
|
||||
"pg": "catalog:",
|
||||
"pino": "^10.3.1",
|
||||
"prudence": "catalog:",
|
||||
"rate-limit-redis": "catalog:",
|
||||
"redis": "catalog:",
|
||||
@@ -1200,6 +1202,8 @@
|
||||
|
||||
"@paralleldrive/cuid2": ["@paralleldrive/cuid2@2.3.1", "", { "dependencies": { "@noble/hashes": "^1.1.5" } }, "sha512-XO7cAxhnTZl0Yggq6jOgjiOHhbgcO4NqFqwSmQpjK3b6TEE6Uj/jfSk6wzYyemh3+I0sHirKSetjQwn5cZktFw=="],
|
||||
|
||||
"@pinojs/redact": ["@pinojs/redact@0.4.0", "", {}, "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg=="],
|
||||
|
||||
"@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="],
|
||||
|
||||
"@popperjs/core": ["@popperjs/core@2.11.8", "", {}, "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="],
|
||||
@@ -1636,6 +1640,8 @@
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"atomic-sleep": ["atomic-sleep@1.0.0", "", {}, "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ=="],
|
||||
|
||||
"auto-bind": ["auto-bind@4.0.0", "", {}, "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ=="],
|
||||
|
||||
"available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="],
|
||||
@@ -2782,6 +2788,8 @@
|
||||
|
||||
"oblivious-set": ["oblivious-set@1.0.0", "", {}, "sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw=="],
|
||||
|
||||
"on-exit-leak-free": ["on-exit-leak-free@2.1.2", "", {}, "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA=="],
|
||||
|
||||
"on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="],
|
||||
|
||||
"on-headers": ["on-headers@1.0.2", "", {}, "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="],
|
||||
@@ -2874,6 +2882,12 @@
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"pino": ["pino@10.3.1", "", { "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^3.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^4.0.0" }, "bin": { "pino": "bin.js" } }, "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg=="],
|
||||
|
||||
"pino-abstract-transport": ["pino-abstract-transport@3.0.0", "", { "dependencies": { "split2": "^4.0.0" } }, "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg=="],
|
||||
|
||||
"pino-std-serializers": ["pino-std-serializers@7.1.0", "", {}, "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw=="],
|
||||
|
||||
"pirates": ["pirates@3.0.2", "", { "dependencies": { "node-modules-regexp": "^1.0.0" } }, "sha512-c5CgUJq6H2k6MJz72Ak1F5sN9n9wlSlJyEnwvpm9/y3WB4E3pHBDT2c6PEiS1vyJvq2bUxUAIu0EGf8Cx4Ic7Q=="],
|
||||
|
||||
"pkg-dir": ["pkg-dir@4.2.0", "", { "dependencies": { "find-up": "^4.0.0" } }, "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ=="],
|
||||
@@ -2908,6 +2922,8 @@
|
||||
|
||||
"process-on-spawn": ["process-on-spawn@1.1.0", "", { "dependencies": { "fromentries": "^1.2.0" } }, "sha512-JOnOPQ/8TZgjs1JIH/m9ni7FfimjNa/PRx7y/Wb5qdItsnhO0jE4AT7fC0HjC28DUQWDr50dwSYZLdRMlqDq3Q=="],
|
||||
|
||||
"process-warning": ["process-warning@5.0.0", "", {}, "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA=="],
|
||||
|
||||
"prom-client": ["prom-client@15.1.3", "", { "dependencies": { "@opentelemetry/api": "^1.4.0", "tdigest": "^0.1.1" } }, "sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g=="],
|
||||
|
||||
"promise": ["promise@7.3.1", "", { "dependencies": { "asap": "~2.0.3" } }, "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg=="],
|
||||
@@ -2940,6 +2956,8 @@
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"quick-format-unescaped": ["quick-format-unescaped@4.0.4", "", {}, "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg=="],
|
||||
|
||||
"ramda": ["ramda@0.32.0", "", {}, "sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ=="],
|
||||
|
||||
"random-bytes": ["random-bytes@1.0.0", "", {}, "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="],
|
||||
@@ -2994,6 +3012,8 @@
|
||||
|
||||
"readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||
|
||||
"real-require": ["real-require@0.2.0", "", {}, "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg=="],
|
||||
|
||||
"rechoir": ["rechoir@0.8.0", "", { "dependencies": { "resolve": "^1.20.0" } }, "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ=="],
|
||||
|
||||
"redeyed": ["redeyed@2.1.1", "", { "dependencies": { "esprima": "~4.0.0" } }, "sha512-FNpGGo1DycYAdnrKFxCMmKYgo/mILAqtRYbkdQD8Ep/Hk2PQ5+aEAEx+IU713RTDmuBaH0c8P5ZozurNu5ObRQ=="],
|
||||
@@ -3122,6 +3142,8 @@
|
||||
|
||||
"snappy": ["snappy@7.3.3", "", { "optionalDependencies": { "@napi-rs/snappy-android-arm-eabi": "7.3.3", "@napi-rs/snappy-android-arm64": "7.3.3", "@napi-rs/snappy-darwin-arm64": "7.3.3", "@napi-rs/snappy-darwin-x64": "7.3.3", "@napi-rs/snappy-freebsd-x64": "7.3.3", "@napi-rs/snappy-linux-arm-gnueabihf": "7.3.3", "@napi-rs/snappy-linux-arm64-gnu": "7.3.3", "@napi-rs/snappy-linux-arm64-musl": "7.3.3", "@napi-rs/snappy-linux-ppc64-gnu": "7.3.3", "@napi-rs/snappy-linux-riscv64-gnu": "7.3.3", "@napi-rs/snappy-linux-s390x-gnu": "7.3.3", "@napi-rs/snappy-linux-x64-gnu": "7.3.3", "@napi-rs/snappy-linux-x64-musl": "7.3.3", "@napi-rs/snappy-openharmony-arm64": "7.3.3", "@napi-rs/snappy-wasm32-wasi": "7.3.3", "@napi-rs/snappy-win32-arm64-msvc": "7.3.3", "@napi-rs/snappy-win32-ia32-msvc": "7.3.3", "@napi-rs/snappy-win32-x64-msvc": "7.3.3" } }, "sha512-UDJVCunvgblRpfTOjo/uT7pQzfrTsSICJ4yVS4aq7SsGBaUSpJwaVP15nF//jqinSLpN7boe/BqbUmtWMTQ5MQ=="],
|
||||
|
||||
"sonic-boom": ["sonic-boom@4.2.1", "", { "dependencies": { "atomic-sleep": "^1.0.0" } }, "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q=="],
|
||||
|
||||
"source-map": ["source-map@0.5.7", "", {}, "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
@@ -3258,6 +3280,8 @@
|
||||
|
||||
"text-hex": ["text-hex@1.0.0", "", {}, "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="],
|
||||
|
||||
"thread-stream": ["thread-stream@4.0.0", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA=="],
|
||||
|
||||
"tildify": ["tildify@2.0.0", "", {}, "sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw=="],
|
||||
|
||||
"tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
|
||||
|
||||
+1
-1
@@ -25,6 +25,6 @@ funcsave _tide_item_tachi > /dev/null
|
||||
|
||||
set -U tide_tachi_color cc527a
|
||||
set -U tide_tachi_bg_color 131313
|
||||
set -U tide_right_prompt_items status cmd_duration context jobs direnv bun node python rustc java php pulumi ruby go gcloud kubectl distrobox toolbox terraform aws nix_shell crystal elixir zig time tachi
|
||||
set -U tide_right_prompt_items status cmd_duration context jobs direnv bun node python rustc java php pulumi ruby go gcloud kubectl distrobox toolbox terraform aws nix_shell crystal elixir zig tachi time
|
||||
|
||||
# rr
|
||||
+14
-5
@@ -28,10 +28,10 @@
|
||||
},
|
||||
"catalog": {
|
||||
"@typescript/native-preview": "7.0.0-dev.20260309.1",
|
||||
"@types/node": "18.11.18",
|
||||
"@types/node": "*",
|
||||
"@types/pg": "^8.11.11",
|
||||
"ts-node": "^8.5.2",
|
||||
"typescript": "^3.7.2",
|
||||
"typescript": "^4.3.4",
|
||||
"kysely": "^0.27.5",
|
||||
"pg": "^8.13.3",
|
||||
"@types/history": "4.7.11",
|
||||
@@ -203,8 +203,8 @@
|
||||
"which": "^2.0.2",
|
||||
"@types/eslint__js": "8.42.3",
|
||||
"@eslint/js": "^9.20.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.23.0",
|
||||
"@typescript-eslint/parser": "^8.23.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.27.0",
|
||||
"@typescript-eslint/parser": "^4.27.0",
|
||||
"@typescript-eslint/utils": "^8.24.1",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.7.0",
|
||||
@@ -218,7 +218,16 @@
|
||||
"typescript-eslint": "^8.24.1",
|
||||
"vitest": "~3.2.0",
|
||||
"@vitest/coverage-v8": "~3.2.0",
|
||||
"@vitest/coverage-istanbul": "~3.2.0"
|
||||
"@vitest/coverage-istanbul": "~3.2.0",
|
||||
"pino": "^10.3.1",
|
||||
"@types/jest": "^26.0.23",
|
||||
"eslint": "^7.29.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-plugin-jest": "^24.3.6",
|
||||
"jest": "^27.0.4",
|
||||
"standard": "^16.0.3",
|
||||
"ts-jest": "^27.0.3",
|
||||
"tsd": "^0.17.0"
|
||||
},
|
||||
"overrides": {
|
||||
"@types/mongodb": "3.6.20",
|
||||
|
||||
@@ -8,8 +8,5 @@ export default [
|
||||
},
|
||||
{
|
||||
files: ["**/*.{ts,tsx}"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-unsafe-assignment": "warn",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
@@ -38,9 +38,10 @@
|
||||
"json5": "catalog:",
|
||||
"lodash": "catalog:",
|
||||
"luxon": "catalog:",
|
||||
"mei-logger": "catalog:",
|
||||
"mei-log": "catalog:",
|
||||
"monk": "catalog:",
|
||||
"node-fetch": "catalog:",
|
||||
"pino": "catalog:",
|
||||
"prudence": "catalog:",
|
||||
"seq-logging": "catalog:",
|
||||
"tachi-common": "workspace:*",
|
||||
|
||||
@@ -14,9 +14,9 @@ config();
|
||||
// Reads the bots config file from $pwd/conf.json5.
|
||||
// Validates it using prudence.
|
||||
|
||||
// the real logger tries to bind to discord, and is dependent on the options
|
||||
// the real log tries to bind to discord, and is dependent on the options
|
||||
// below.
|
||||
const logger = console;
|
||||
const log = console;
|
||||
|
||||
export interface BotConfig {
|
||||
TACHI_SERVER_LOCATION: string;
|
||||
@@ -43,10 +43,10 @@ function ParseBotConfig(fileLoc = "conf.json5"): BotConfig {
|
||||
try {
|
||||
const contents = fs.readFileSync(fileLoc, "utf-8");
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
|
||||
data = JSON5.parse(contents);
|
||||
} catch (err) {
|
||||
logger.error("Failed to find/parse a valid conf.json5 file. Cannot boot.", { err });
|
||||
log.error({ err }, "Failed to find/parse a valid conf.json5 file. Cannot boot.");
|
||||
|
||||
throw err;
|
||||
}
|
||||
@@ -93,7 +93,7 @@ function ParseBotConfig(fileLoc = "conf.json5"): BotConfig {
|
||||
});
|
||||
|
||||
if (err) {
|
||||
logger.error(FormatPrError(err, "Invalid conf.json5 file. Cannot safely boot."));
|
||||
log.error(FormatPrError(err, "Invalid conf.json5 file. Cannot safely boot."));
|
||||
|
||||
throw err;
|
||||
}
|
||||
@@ -105,8 +105,6 @@ export interface ProcessEnvironment {
|
||||
nodeEnv: "dev" | "production" | "staging" | "test";
|
||||
mongoUrl: string;
|
||||
port: integer;
|
||||
seqUrl?: string;
|
||||
seqApiKey?: string;
|
||||
}
|
||||
|
||||
function ParseEnvVars() {
|
||||
@@ -123,15 +121,13 @@ function ParseEnvVars() {
|
||||
PORT: (self) =>
|
||||
p.isPositiveInteger(Number(self)) === true ||
|
||||
"Should be a string representing a whole integer port.",
|
||||
SEQ_URL: "*string",
|
||||
SEQ_API_KEY: "*string",
|
||||
},
|
||||
{},
|
||||
{ allowExcessKeys: true },
|
||||
);
|
||||
|
||||
if (err) {
|
||||
logger.error(FormatPrError(err, "Invalid environment. Cannot safely boot."));
|
||||
log.error(FormatPrError(err, "Invalid environment. Cannot safely boot."));
|
||||
|
||||
throw err;
|
||||
}
|
||||
@@ -140,8 +136,6 @@ function ParseEnvVars() {
|
||||
nodeEnv: process.env.NODE_ENV,
|
||||
mongoUrl: process.env.MONGO_URL,
|
||||
port: Number(process.env.PORT),
|
||||
seqUrl: process.env.SEQ_URL,
|
||||
seqApiKey: process.env.SEQ_API_KEY,
|
||||
} as ProcessEnvironment;
|
||||
}
|
||||
|
||||
@@ -154,13 +148,13 @@ async function GetServerConfig() {
|
||||
// this fetch is complete, and it saves us having to do a singleton pattern or worse.
|
||||
// This *should* be solved with top-level-await, but good luck actually getting
|
||||
// typescript to output the right stuff here.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
|
||||
const res = await fetch(`${BotConfig.TACHI_SERVER_LOCATION}/api/v1/config`).then((res) =>
|
||||
res.json(),
|
||||
);
|
||||
|
||||
if (!res.success) {
|
||||
logger.error(
|
||||
log.error(
|
||||
`Failed to fetch server info from ${BotConfig.TACHI_SERVER_LOCATION}. Can't run.`,
|
||||
);
|
||||
process.exit(1);
|
||||
@@ -177,7 +171,7 @@ export const ProcessEnv = ParseEnvVars();
|
||||
// This warns people if their parent server supports games that they aren't acknowledging.
|
||||
for (const game of ServerConfig.GAMES) {
|
||||
if (!Object.prototype.hasOwnProperty.call(BotConfig.DISCORD.GAME_CHANNELS, game)) {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`${ServerConfig.NAME} declares support for ${game}, but no channel is mapped to it in your conf.json5.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
export enum LoggerLayers {
|
||||
apiRequests = "apiRequests",
|
||||
botConfigSetup = "botConfigSetup",
|
||||
buildChartEmbed = "buildChartEmbed",
|
||||
buildProfileEmbed = "buildProfileEmbed",
|
||||
chartSearch = "chartSearch",
|
||||
client = "client",
|
||||
database = "database",
|
||||
databaseQuery = "databaseQuery",
|
||||
profile = "profile",
|
||||
selectInteractionHandler = "selectInteractionHandler",
|
||||
server = "server",
|
||||
serverAuth = "serverAuth",
|
||||
slashCommands = "slashCommands",
|
||||
tachiAuth = "tachiAuth",
|
||||
tachiFetch = "tachiFetch",
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
import { log } from "#utils/log";
|
||||
import monk from "monk";
|
||||
|
||||
import type { DiscordUserMapDocument } from "./documents";
|
||||
|
||||
import { ProcessEnv } from "../config";
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { CreateLayeredLogger } from "../utils/logger";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.database);
|
||||
|
||||
logger.info(`Connecting to ${ProcessEnv.mongoUrl}...`);
|
||||
log.info(`Connecting to ${ProcessEnv.mongoUrl}...`);
|
||||
|
||||
const monkDB = monk(ProcessEnv.mongoUrl);
|
||||
|
||||
monkDB
|
||||
.then(() => {
|
||||
logger.info(`Database connection successful.`);
|
||||
log.info(`Database connection successful.`);
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.crit(err);
|
||||
log.fatal(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@@ -26,16 +23,16 @@ const db = {
|
||||
};
|
||||
|
||||
export async function SetIndexes(hardReset = false) {
|
||||
logger.info(`Recieved request to set indexes.`);
|
||||
log.info(`Recieved request to set indexes.`);
|
||||
|
||||
if (hardReset) {
|
||||
logger.warn(`Hard resetting indexes!`);
|
||||
log.warn(`Hard resetting indexes!`);
|
||||
await db.discordUserMap.dropIndexes();
|
||||
}
|
||||
|
||||
await db.discordUserMap.createIndex({ discordID: 1 }, { unique: true });
|
||||
|
||||
logger.info(`Indexes have been set.`);
|
||||
log.info(`Indexes have been set.`);
|
||||
}
|
||||
|
||||
export default db;
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import type { integer } from "tachi-common";
|
||||
|
||||
import { log } from "#utils/log.js";
|
||||
|
||||
import type { DiscordUserMapDocument } from "./documents";
|
||||
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { CreateLayeredLogger } from "../utils/logger";
|
||||
import db from "./mongo";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.databaseQuery);
|
||||
|
||||
export function GetUserAndTokenForDiscordID(
|
||||
discordID: string,
|
||||
): Promise<DiscordUserMapDocument | null> {
|
||||
logger.verbose(`Fetching linked user & token with DiscordID: ${discordID}.`);
|
||||
log.debug(`Fetching linked user & token with DiscordID: ${discordID}.`);
|
||||
|
||||
return db.discordUserMap.findOne({ discordID });
|
||||
}
|
||||
|
||||
export async function GetUserIDForDiscordID(discordID: string): Promise<integer | null> {
|
||||
logger.verbose(`Fetching linked user & token with DiscordID: ${discordID}.`);
|
||||
log.debug(`Fetching linked user & token with DiscordID: ${discordID}.`);
|
||||
|
||||
const user = await db.discordUserMap.findOne({ discordID }, { projection: { userID: 1 } });
|
||||
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import { log } from "#utils/log";
|
||||
import { type CommandInteraction, MessageEmbed } from "discord.js";
|
||||
|
||||
import type { DiscordUserMapDocument } from "../database/documents";
|
||||
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { SLASH_COMMANDS } from "../slashCommands/commands";
|
||||
import { CreateLayeredLogger } from "../utils/logger";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.slashCommands);
|
||||
|
||||
/**
|
||||
* Handles incoming command requests by resolving the interaction to the command
|
||||
@@ -29,7 +26,7 @@ export async function handleIsCommand(
|
||||
|
||||
await interaction.deferReply();
|
||||
|
||||
logger.verbose(`Running ${command.info.name} interaction.`);
|
||||
log.debug(`Running ${command.info.name} interaction.`);
|
||||
try {
|
||||
const response = await command.exec(interaction, requestingUser);
|
||||
|
||||
@@ -43,13 +40,13 @@ export async function handleIsCommand(
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`An error occured while executing a command.`, { command, err });
|
||||
log.error({ command, err }, `An error occured while executing a command.`);
|
||||
|
||||
void interaction.editReply(
|
||||
`An error has occured while executing this command (${err}). This has been reported.`,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error("Failed to handle isCommand interaction", { error: e });
|
||||
log.error({ error: e }, "Failed to handle isCommand interaction");
|
||||
}
|
||||
}
|
||||
|
||||
+11
-16
@@ -1,17 +1,16 @@
|
||||
import { log } from "#utils/log.js";
|
||||
import { GetLimboChannel } from "#utils/misc";
|
||||
import { Client, type CommandInteraction, Intents, type SelectMenuInteraction } from "discord.js";
|
||||
|
||||
import { BotConfig, ProcessEnv, ServerConfig } from "./config";
|
||||
import { LoggerLayers } from "./data/data";
|
||||
import { GetUserAndTokenForDiscordID } from "./database/queries";
|
||||
import { handleIsCommand } from "./interactionHandlers/handleIsCommand";
|
||||
import { app } from "./server/server";
|
||||
import { RegisterSlashCommands } from "./slashCommands/register";
|
||||
import { CreateLayeredLogger } from "./utils/logger";
|
||||
import { VERSION_PRETTY } from "./version";
|
||||
|
||||
// hack: DiscordJS's endpoints sometimes return bigints that end up in our logger.
|
||||
// when our logger tries to format that content, JSON.stringify fails.
|
||||
// hack: DiscordJS's endpoints sometimes return bigints that end up in our log.
|
||||
// when our log tries to format that content, JSON.stringify fails.
|
||||
//
|
||||
// I personally cannot believe that the spec now made JSON.stringify fallible in such
|
||||
// a common case. It's kind of absurdly ridiculous. But hey ho; monkey patch our way
|
||||
@@ -21,8 +20,6 @@ BigInt.prototype.toJSON = function toJSON() {
|
||||
return this.toString();
|
||||
};
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.client);
|
||||
|
||||
export const client = new Client({
|
||||
intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.DIRECT_MESSAGES, Intents.FLAGS.GUILD_MESSAGES],
|
||||
});
|
||||
@@ -53,11 +50,11 @@ client.on("interactionCreate", async (interaction) => {
|
||||
if (interaction.isCommand()) {
|
||||
await handleIsCommand(interaction, requestingUser);
|
||||
}
|
||||
} catch (e) {
|
||||
} catch (err) {
|
||||
await interaction.channel?.send(
|
||||
"We failed to handle this request. Are your DMs shut to non-friends?",
|
||||
);
|
||||
logger.error("Failed to run interaction.", { interaction, e });
|
||||
log.error({ interaction, err }, "Failed to run interaction.");
|
||||
}
|
||||
});
|
||||
|
||||
@@ -79,25 +76,25 @@ We've sent you a DM with instructions on how to link your account.`,
|
||||
|
||||
void (async () => {
|
||||
try {
|
||||
logger.info(`Booting Tachi Bot ${VERSION_PRETTY}.`);
|
||||
log.info(`Booting Tachi Bot ${VERSION_PRETTY}.`);
|
||||
|
||||
// Login to discord.
|
||||
await client.login(BotConfig.DISCORD.TOKEN);
|
||||
|
||||
logger.info(`Logged in successfully to ${client.guilds.cache.size} guilds.`);
|
||||
log.info(`Logged in successfully to ${client.guilds.cache.size} guilds.`);
|
||||
|
||||
// Mount our express server.
|
||||
app.listen(ProcessEnv.port);
|
||||
|
||||
logger.info(
|
||||
log.info(
|
||||
`Invite URL: https://discord.com/api/oauth2/authorize?client_id=${
|
||||
client.application!.id
|
||||
}&permissions=8&scope=applications.commands%20bot`,
|
||||
);
|
||||
|
||||
await RegisterSlashCommands(client);
|
||||
} catch (err) {
|
||||
logger.crit("Failed to properly boot.", err);
|
||||
} catch (error) {
|
||||
log.fatal({ error }, "Failed to properly boot.");
|
||||
process.exit(1);
|
||||
}
|
||||
})();
|
||||
@@ -105,7 +102,5 @@ void (async () => {
|
||||
// taken from https://nodejs.org/api/process.html#process_event_unhandledrejection
|
||||
// to avoid future deprecation.
|
||||
process.on("unhandledRejection", (reason, promise) => {
|
||||
// @ts-expect-error reason is an error, and the logger can handle errors
|
||||
// it just refuses.
|
||||
logger.error(reason, { promise });
|
||||
log.error({ promise }, reason as string);
|
||||
});
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import type { RequestHandler } from "express";
|
||||
|
||||
import { log } from "#utils/log";
|
||||
|
||||
import { BotConfig } from "../config";
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { CreateLayeredLogger } from "../utils/logger";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.serverAuth);
|
||||
|
||||
/**
|
||||
* Middleware that checks that a webhook request has Authorization set to
|
||||
@@ -17,7 +16,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => {
|
||||
const auth = req.header("Authorization");
|
||||
|
||||
if (!auth) {
|
||||
logger.info(`Received unauthed request from ${req.ip}.`);
|
||||
log.info(`Received unauthed request from ${req.ip}.`);
|
||||
return res.status(401).json({
|
||||
success: false,
|
||||
description: "No authorization provided.",
|
||||
@@ -27,7 +26,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => {
|
||||
const [type, value] = auth.split(" ", 2);
|
||||
|
||||
if (type !== "Bearer") {
|
||||
logger.info(`Received invalid auth type request from ${req.ip}, got auth type ${type}.`);
|
||||
log.info(`Received invalid auth type request from ${req.ip}, got auth type ${type}.`);
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
description: "Invalid authorization type. Expected Bearer.",
|
||||
@@ -35,7 +34,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => {
|
||||
}
|
||||
|
||||
if (value !== BotConfig.OAUTH.CLIENT_SECRET) {
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`Recieved invalid auth value from ${req.ip}. Has the client secret been changed?`,
|
||||
);
|
||||
return res.status(403).json({
|
||||
@@ -44,7 +43,7 @@ export const ValidateWebhookRequest: RequestHandler = (req, res, next) => {
|
||||
});
|
||||
}
|
||||
|
||||
logger.debug("Webhook authorisation successful.");
|
||||
log.debug("Webhook authorisation successful.");
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import type { APITokenDocument, UserDocument, WebhookEvents } from "tachi-common";
|
||||
|
||||
import { client } from "#main";
|
||||
import { log } from "#utils/log.js";
|
||||
import { HandleQuestAchievedV1 } from "#webhookHandlers/questAchieved";
|
||||
import express, { type Express } from "express";
|
||||
import path from "path";
|
||||
|
||||
import { BotConfig, ProcessEnv } from "../config";
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import db from "../database/mongo";
|
||||
import { RequestTypes, TachiServerV1Get, TachiServerV1Request } from "../utils/fetchTachi";
|
||||
import { CreateLayeredLogger } from "../utils/logger";
|
||||
import { VERSION_PRETTY } from "../version";
|
||||
import { HandleClassUpdateV1 } from "../webhookHandlers/classUpdate";
|
||||
import { HandleGoalAchievedV1 } from "../webhookHandlers/goalsAchieved";
|
||||
@@ -17,7 +15,6 @@ import { ValidateWebhookRequest } from "./middleware";
|
||||
|
||||
export const app: Express = express();
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.server);
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
@@ -33,7 +30,7 @@ app.set("query parser", "simple");
|
||||
*
|
||||
* @name GET /
|
||||
*/
|
||||
app.get("/", (req, res) =>
|
||||
app.get("/", (_req, res) =>
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
description: "Bot is online!",
|
||||
@@ -76,7 +73,7 @@ app.get("/oauth/callback", async (req, res) => {
|
||||
);
|
||||
|
||||
if (!tokenRes.success) {
|
||||
logger.error(
|
||||
log.error(
|
||||
`Failed to convert code ${req.query.code} to a token. ${tokenRes.description} Cannot auth.`,
|
||||
);
|
||||
return res.status(401).json({
|
||||
@@ -91,7 +88,8 @@ app.get("/oauth/callback", async (req, res) => {
|
||||
const whoamiRes = await TachiServerV1Get<UserDocument>("/users/me", apiToken);
|
||||
|
||||
if (!whoamiRes.success) {
|
||||
logger.severe("Failed to request user with token we just got?", { discordID });
|
||||
log.error({ discordID }
|
||||
, "Failed to request user with token we just got?");
|
||||
return res
|
||||
.status(500)
|
||||
.send(
|
||||
@@ -101,12 +99,12 @@ app.get("/oauth/callback", async (req, res) => {
|
||||
|
||||
const user = whoamiRes.body;
|
||||
|
||||
logger.info(`Saving user-discord-link for ${user.username} (id: ${user.id}).`);
|
||||
log.info(`Saving user-discord-link for ${user.username} (id: ${user.id}).`);
|
||||
|
||||
const existingLink = await db.discordUserMap.findOne({ userID: user.id });
|
||||
|
||||
if (existingLink) {
|
||||
logger.info(`Updating user-discord-link for ${user.username} (id: ${user.id})`);
|
||||
log.info(`Updating user-discord-link for ${user.username} (id: ${user.id})`);
|
||||
|
||||
await db.discordUserMap.update(
|
||||
{
|
||||
@@ -163,7 +161,7 @@ app.post("/webhook", ValidateWebhookRequest, async (req, res) => {
|
||||
// to define new webhooks, and the bot might not
|
||||
// However, tachi-(server/common) may recieve an update
|
||||
// According to the types, this should never happen.
|
||||
logger.warn(
|
||||
log.warn(
|
||||
`Received unknown webhook event ${
|
||||
(webhookEvent as WebhookEvents).type
|
||||
}. Have we got support for this?`,
|
||||
@@ -183,7 +181,7 @@ app.post("/webhook", ValidateWebhookRequest, async (req, res) => {
|
||||
*
|
||||
* @name ALL *
|
||||
*/
|
||||
app.all("*", (req, res) =>
|
||||
app.all("*", (_req, res) =>
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
description: "Nothing found here.",
|
||||
@@ -210,16 +208,14 @@ const MainExpressErrorHandler: express.ErrorRequestHandler = (err, req, res, _ne
|
||||
const expErr: ExpressJSONErr = err as ExpressJSONErr;
|
||||
|
||||
if (expErr.status === 400 && "body" in expErr) {
|
||||
logger.info(`Error in parsing JSON in request body from ${req.url}`, {
|
||||
url: req.originalUrl,
|
||||
});
|
||||
log.info({ url: req.originalUrl, err: err }, `Error in parsing JSON in request body from ${req.url}`);
|
||||
return res.status(400).send({ success: false, description: err.message });
|
||||
}
|
||||
|
||||
// else, this isn't a JSON parsing error
|
||||
}
|
||||
|
||||
logger.error(err, req.route);
|
||||
log.error({ err, route: req.route }, "Fatal error propagated to server root?");
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
@@ -229,4 +225,4 @@ const MainExpressErrorHandler: express.ErrorRequestHandler = (err, req, res, _ne
|
||||
|
||||
app.use(MainExpressErrorHandler);
|
||||
|
||||
logger.info(`Starting express server on port ${ProcessEnv.port}.`);
|
||||
log.info(`Starting express server on port ${ProcessEnv.port}.`);
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import type { Client } from "discord.js";
|
||||
|
||||
import { log } from "#utils/log";
|
||||
import { REST } from "@discordjs/rest";
|
||||
import { Routes } from "discord-api-types/v9";
|
||||
|
||||
import { BotConfig, ProcessEnv } from "../config";
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { CreateLayeredLogger } from "../utils/logger";
|
||||
import { SLASH_COMMANDS } from "./commands";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.slashCommands);
|
||||
|
||||
const rest = new REST({
|
||||
version: "9",
|
||||
}).setToken(BotConfig.DISCORD.TOKEN);
|
||||
@@ -23,18 +20,18 @@ export async function RegisterSlashCommands(client: Client): Promise<void> {
|
||||
const commandsArray = [...SLASH_COMMANDS.values()];
|
||||
|
||||
// always unregister guild slash commands, just in case.
|
||||
logger.info(`Unregistering guild slash commands.`);
|
||||
log.info(`Unregistering guild slash commands.`);
|
||||
|
||||
await UnregisterAllCommands(client);
|
||||
|
||||
if (ProcessEnv.nodeEnv === "production") {
|
||||
logger.info(`Updating global commands.`);
|
||||
log.info(`Updating global commands.`);
|
||||
|
||||
await rest.put(Routes.applicationCommands(client.application!.id), {
|
||||
body: commandsArray.map((command) => command.info),
|
||||
});
|
||||
} else {
|
||||
logger.info("Registering guild slash commands.");
|
||||
log.info("Registering guild slash commands.");
|
||||
|
||||
await rest.put(
|
||||
Routes.applicationGuildCommands(
|
||||
@@ -47,9 +44,9 @@ export async function RegisterSlashCommands(client: Client): Promise<void> {
|
||||
);
|
||||
}
|
||||
|
||||
logger.info("Successfully registered guild slash commands.");
|
||||
log.info("Successfully registered guild slash commands.");
|
||||
} catch (err) {
|
||||
logger.error("Failed to register guild slash commands.", err);
|
||||
log.error({ err }, "Failed to register guild slash commands.");
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -59,7 +56,7 @@ export async function RegisterSlashCommands(client: Client): Promise<void> {
|
||||
*/
|
||||
async function UnregisterAllCommands(client: Client): Promise<void> {
|
||||
try {
|
||||
logger.info("Tidying old guild slash commands.");
|
||||
log.info("Tidying old guild slash commands.");
|
||||
const guilds = client.guilds.cache;
|
||||
|
||||
// discord.js doesn't use arrays because those aren't cool anymore
|
||||
@@ -77,9 +74,9 @@ async function UnregisterAllCommands(client: Client): Promise<void> {
|
||||
// parallelise waiting for these to be deleted.
|
||||
await Promise.all(promises);
|
||||
|
||||
logger.info(`Successfully tidied ${promises.length} old guild slash commands.`);
|
||||
log.info(`Successfully tidied ${promises.length} old guild slash commands.`);
|
||||
} catch (err) {
|
||||
logger.error("Failed to tidy old guild slash commands.", err);
|
||||
log.error({ err }, "Failed to tidy old guild slash commands.");
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -13,16 +13,13 @@ import type {
|
||||
} from "tachi-common";
|
||||
|
||||
import { BotConfig } from "#config";
|
||||
import { log } from "#utils/log";
|
||||
|
||||
import type { ImportDeferred, ImportPollStatus, UGPTStats } from "./returnTypes";
|
||||
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { RequestTypes, TachiServerV1Get, TachiServerV1Request } from "./fetchTachi";
|
||||
import { CreateLayeredLogger } from "./logger";
|
||||
import { Sleep } from "./misc";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.apiRequests);
|
||||
|
||||
export async function GetUserInfo(userID: string | integer) {
|
||||
const res = await TachiServerV1Get<UserDocument>(`/users/${userID}`, null);
|
||||
|
||||
@@ -116,7 +113,7 @@ export async function PerformScoreImport(
|
||||
|
||||
if (!initRes.success) {
|
||||
if (initRes.statusCode >= 500) {
|
||||
logger.error(`Failed to perform score import on ${url}.`, { body });
|
||||
log.error({ body }, `Failed to perform score import on ${url}.`);
|
||||
throw new Error(`Failed to perform import on ${url}.`);
|
||||
} else {
|
||||
return initRes.description;
|
||||
@@ -172,7 +169,7 @@ Please go to ${BotConfig.TACHI_SERVER_LOCATION}/u/me/integrations/services to un
|
||||
}
|
||||
}
|
||||
|
||||
logger.error(`Unexpected status code ${initRes.statusCode} returned from ${url}.`, { body });
|
||||
log.error({ body }, `Unexpected status code ${initRes.statusCode} returned from ${url}.`);
|
||||
|
||||
throw new Error(`Unexpected status code ${initRes.statusCode} returned from ${url}.`);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import type { integer, SuccessfulAPIResponse, UnsuccessfulAPIResponse } from "tachi-common";
|
||||
|
||||
import { log } from "#utils/log";
|
||||
import fetch from "node-fetch";
|
||||
import { URLSearchParams } from "url";
|
||||
|
||||
import { BotConfig } from "../config";
|
||||
import { LoggerLayers } from "../data/data";
|
||||
import { VERSION_STR } from "../version";
|
||||
import { CreateLayeredLogger } from "./logger";
|
||||
|
||||
const logger = CreateLayeredLogger(LoggerLayers.tachiFetch);
|
||||
|
||||
export type APIResponse<T> = (({ body: null } & UnsuccessfulAPIResponse) | SuccessfulAPIResponse<T>) & {
|
||||
statusCode: integer;
|
||||
@@ -41,9 +38,9 @@ export async function TachiServerV1Request<T>(
|
||||
body: unknown = {},
|
||||
): Promise<APIResponse<T>> {
|
||||
const realUrl = PrependTachiUrl(url, "1");
|
||||
const loggerUrl = `${method} ${realUrl}`;
|
||||
const logUrl = `${method} ${realUrl}`;
|
||||
|
||||
logger.debug(`Making a request to ${loggerUrl}.`);
|
||||
log.debug(`Making a request to ${logUrl}.`);
|
||||
|
||||
try {
|
||||
const res = await fetch(realUrl, {
|
||||
@@ -60,11 +57,11 @@ export async function TachiServerV1Request<T>(
|
||||
|
||||
const contents = { ...json, statusCode: res.status };
|
||||
|
||||
LogRequestResult(loggerUrl, contents);
|
||||
LogRequestResult(logUrl, contents);
|
||||
|
||||
return contents;
|
||||
} catch (err) {
|
||||
logger.error(`Failed while requesting ${method} ${realUrl}.`, { err });
|
||||
log.error({ err }, `Failed while requesting ${method} ${realUrl}.`);
|
||||
|
||||
// Throw the error upwards for it to be caught be a higher handler.
|
||||
throw err;
|
||||
@@ -94,7 +91,7 @@ export async function TachiServerV1Get<T = unknown>(
|
||||
|
||||
const realUrl = `${PrependTachiUrl(url, "1")}?${urlParams.toString()}`;
|
||||
|
||||
logger.verbose(`GET ${realUrl}`);
|
||||
log.debug(`GET ${realUrl}`);
|
||||
|
||||
const res = await fetch(realUrl, {
|
||||
method: RequestTypes.GET,
|
||||
@@ -111,7 +108,7 @@ export async function TachiServerV1Get<T = unknown>(
|
||||
|
||||
return contents;
|
||||
} catch (err) {
|
||||
logger.error(`Failed while requesting GET ${url}.\n\n${err}\n`);
|
||||
log.error(`Failed while requesting GET ${url}.\n\n${err}\n`);
|
||||
|
||||
throw err;
|
||||
}
|
||||
@@ -133,12 +130,12 @@ export function PrependTachiUrl(url: string, version: "1" = "1"): string {
|
||||
* Logs the result of a request.
|
||||
* Logs at WARN level if was unsuccessful, DEBUG otherwise.
|
||||
*/
|
||||
function LogRequestResult(loggerUrl: string, res: APIResponse<unknown>): void {
|
||||
function LogRequestResult(logUrl: string, res: APIResponse<unknown>): void {
|
||||
if (!res.success) {
|
||||
logger.warn(
|
||||
`Request ${loggerUrl} was unsuccessful: ${res.description} (${res.statusCode})`,
|
||||
log.warn(
|
||||
`Request ${logUrl} was unsuccessful: ${res.description} (${res.statusCode})`,
|
||||
);
|
||||
} else {
|
||||
logger.debug(`Request ${loggerUrl} was successful: ${res.description} (${res.statusCode})`);
|
||||
log.debug(`Request ${logUrl} was successful: ${res.description} (${res.statusCode})`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import pino from "pino";
|
||||
|
||||
// Use JSON log lines when LOG_JSON=1 (dev/prod); otherwise pretty format for localdev.
|
||||
// Deliberately process.env here instead of importing ENV to avoid a circular dep.
|
||||
const useJson = process.env.LOG_JSON === "1" || process.env.LOG_JSON === "true";
|
||||
|
||||
export const log = pino({
|
||||
level: process.env.LOG_LEVEL ?? "info",
|
||||
...(useJson
|
||||
? {}
|
||||
: {
|
||||
transport: {
|
||||
options: { colorize: true },
|
||||
target: "pino-pretty",
|
||||
},
|
||||
}),
|
||||
});
|
||||
@@ -1,54 +0,0 @@
|
||||
import type { SeqLogLevel } from "seq-logging";
|
||||
|
||||
import { ProcessEnv } from "#config";
|
||||
import { Transport as SeqTransport } from "@valuabletouch/winston-seq";
|
||||
import { CreateLogger, type MeiLogger } from "mei-logger";
|
||||
import { transports } from "winston";
|
||||
|
||||
import type { LoggerLayers } from "../data/data";
|
||||
|
||||
const tports: Array<any> = [new transports.Console({})];
|
||||
|
||||
if (ProcessEnv.seqUrl) {
|
||||
// Turns winston log levels into seq format.
|
||||
const levelMap: Record<string, SeqLogLevel> = {
|
||||
crit: "Fatal",
|
||||
severe: "Error",
|
||||
error: "Error",
|
||||
warn: "Warning",
|
||||
info: "Information",
|
||||
|
||||
// Note that Seq interprets these in reverse,
|
||||
// however, it's easier to read this code if I just
|
||||
// use the same levels, instead of the right ones.
|
||||
verbose: "Verbose",
|
||||
debug: "Debug",
|
||||
};
|
||||
|
||||
tports.push(
|
||||
new SeqTransport({
|
||||
apiKey: ProcessEnv.seqApiKey,
|
||||
serverUrl: ProcessEnv.seqUrl,
|
||||
onError: (err) => {
|
||||
console.error(`Failed to send seq message: ${err.message}.`);
|
||||
},
|
||||
levelMapper(level = "") {
|
||||
return levelMap[level] ?? "Information";
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const logger = CreateLogger(`tachi-bot`, undefined, tports);
|
||||
|
||||
export default logger;
|
||||
|
||||
export function CreateLayeredLogger(layerName: LoggerLayers) {
|
||||
const lg = logger.child({
|
||||
context: [layerName],
|
||||
});
|
||||
|
||||
lg.defaultMeta = { ...(lg.defaultMeta ?? {}), context: [layerName] };
|
||||
|
||||
return lg as MeiLogger;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import { client } from "../main";
|
||||
import { GetUGPTStats, GetUserInfo } from "../utils/apiRequests";
|
||||
import { CreateEmbed } from "../utils/embeds";
|
||||
import { PrependTachiUrl } from "../utils/fetchTachi";
|
||||
import logger from "../utils/logger";
|
||||
import { log } from "#utils/log"
|
||||
import { FormatClass, GetGameChannel } from "../utils/misc";
|
||||
|
||||
export async function HandleClassUpdateV1(
|
||||
@@ -29,12 +29,12 @@ export async function HandleClassUpdateV1(
|
||||
} catch (e) {
|
||||
const err = e as Error;
|
||||
|
||||
logger.error(`ClassUpdate handler failed: ${err.message}`);
|
||||
log.error(`ClassUpdate handler failed: ${err.message}`);
|
||||
return 500;
|
||||
}
|
||||
|
||||
if (!ShouldRenderUpdate(game, playtype, event.set, event.new)) {
|
||||
logger.info(
|
||||
log.info(
|
||||
`Not rendering class update ${event.set}: ${event.old} -> ${event.new} (not relevant).`,
|
||||
);
|
||||
return 204;
|
||||
@@ -49,7 +49,7 @@ export async function HandleClassUpdateV1(
|
||||
|
||||
// Do not render if the user hasn't hit the score cap.
|
||||
if (totalScores < minimumNecessaryScores) {
|
||||
logger.info(
|
||||
log.info(
|
||||
`Not rendering class update ${event.set}: ${event.old} -> ${event.new} (not enough scores).`,
|
||||
);
|
||||
return 204;
|
||||
@@ -91,7 +91,7 @@ function ShouldRenderUpdate(
|
||||
const classSpec = config.classes[classSet];
|
||||
|
||||
if (classSpec === undefined) {
|
||||
logger.error(`Invalid class ${classSet} for ${game} ${playtype}`);
|
||||
log.error(`Invalid class ${classSet} for ${game} ${playtype}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -105,12 +105,12 @@ function ShouldRenderUpdate(
|
||||
const minimumId = ids.indexOf(classSpec.minimumRelevantValue);
|
||||
|
||||
if (currentId < 0) {
|
||||
logger.error(`Invalid classValue ${classValue} for ${game} ${playtype}`);
|
||||
log.error(`Invalid classValue ${classValue} for ${game} ${playtype}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minimumId < 0) {
|
||||
logger.error(`Invalid minimum classValue ${classValue} for ${game} ${playtype}`);
|
||||
log.error(`Invalid minimum classValue ${classValue} for ${game} ${playtype}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ function GetMinimumScores(
|
||||
const classSpec = config.classes[classSet];
|
||||
|
||||
if (classSpec === undefined) {
|
||||
logger.error(`Invalid class ${classSet} for ${game} ${playtype}`);
|
||||
log.error(`Invalid class ${classSet} for ${game} ${playtype}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { BotConfig } from "#config";
|
||||
import { PrependTachiUrl } from "#utils/fetchTachi";
|
||||
import { log } from "#utils/log"
|
||||
import {
|
||||
GetGameGroupConfig,
|
||||
type GoalDocument,
|
||||
@@ -10,7 +11,6 @@ import {
|
||||
import { client } from "../main";
|
||||
import { GetGoalWithID, GetUserInfo } from "../utils/apiRequests";
|
||||
import { CreateEmbed } from "../utils/embeds";
|
||||
import logger from "../utils/logger";
|
||||
import { GetGameChannel, Pluralise } from "../utils/misc";
|
||||
|
||||
export async function HandleGoalAchievedV1(
|
||||
@@ -25,7 +25,7 @@ export async function HandleGoalAchievedV1(
|
||||
} catch (e) {
|
||||
const err = e as Error;
|
||||
|
||||
logger.error(`ClassUpdate handler failed: ${err.message}`);
|
||||
log.error(`ClassUpdate handler failed: ${err.message}`);
|
||||
return 500;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { BotConfig } from "#config";
|
||||
import { PrependTachiUrl } from "#utils/fetchTachi";
|
||||
import { log } from "#utils/log";
|
||||
import { GetGameGroupConfig, type integer, type WebhookEventQuestAchievedV1 } from "tachi-common";
|
||||
|
||||
import { client } from "../main";
|
||||
import { GetQuestWithID, GetUserInfo } from "../utils/apiRequests";
|
||||
import { CreateEmbed } from "../utils/embeds";
|
||||
import logger from "../utils/logger";
|
||||
import { GetGameChannel } from "../utils/misc";
|
||||
|
||||
export async function HandleQuestAchievedV1(
|
||||
@@ -20,7 +20,7 @@ export async function HandleQuestAchievedV1(
|
||||
} catch (e) {
|
||||
const err = e as Error;
|
||||
|
||||
logger.error(`ClassUpdate handler failed: ${err.message}`);
|
||||
log.error({ err }, "ClassUpdate handler failed.");
|
||||
return 500;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "Preserve",
|
||||
"moduleResolution": "bundler",
|
||||
"noImplicitAny": false,
|
||||
"strictNullChecks": true,
|
||||
"allowJs": true,
|
||||
"strict": false
|
||||
},
|
||||
"exclude": ["../seeds-scripts/node_modules"]
|
||||
}
|
||||
@@ -95,6 +95,7 @@
|
||||
"node-html-parser": "catalog:",
|
||||
"nodemailer": "catalog:",
|
||||
"pg": "catalog:",
|
||||
"pino": "catalog:",
|
||||
"prudence": "catalog:",
|
||||
"rate-limit-redis": "catalog:",
|
||||
"redis": "catalog:",
|
||||
@@ -107,10 +108,10 @@
|
||||
"ts-node": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"uuidv7": "catalog:",
|
||||
"zod": "catalog:",
|
||||
"winston": "catalog:",
|
||||
"winston-loki": "catalog:",
|
||||
"winston-transport": "catalog:"
|
||||
"winston-transport": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"nyc": {
|
||||
"reporter": [
|
||||
|
||||
@@ -23,7 +23,7 @@ async function CloseEverythingElse() {
|
||||
logger.info("Closing Mongo Database.", { shutdownInfo: true });
|
||||
await monkDB.close();
|
||||
|
||||
logger.info("Closing database...", {shutdownInfo: true});
|
||||
logger.info("Closing database...", { shutdownInfo: true });
|
||||
await ClosePgConnection();
|
||||
|
||||
logger.info("Closing Redis Connection.", { shutdownInfo: true });
|
||||
|
||||
@@ -120,7 +120,6 @@ const tports: Array<winston.transport> = [
|
||||
}),
|
||||
];
|
||||
|
||||
|
||||
export const rootLogger = winston.createLogger({
|
||||
levels: {
|
||||
// entire process termination is necessary
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import JSON5 from "json5";
|
||||
import { allSupportedGameGroups, type GameGroup , type ImportTypes } from "tachi-common";
|
||||
import { allSupportedGameGroups, type GameGroup, type ImportTypes } from "tachi-common";
|
||||
import { allImportTypes } from "tachi-common/constants/import-types";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -198,7 +198,6 @@ if (!["crit", "debug", "error", "info", "severe", "verbose", "warn"].includes(lo
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
const POSTGRES_URL = process.env.POSTGRES_URL ?? "";
|
||||
|
||||
if (!POSTGRES_URL) {
|
||||
|
||||
@@ -51,10 +51,7 @@ const userSessionMiddleware = expressSession({
|
||||
ServerConfig.ENABLE_SERVER_HTTPS,
|
||||
|
||||
// Very important. Without this, we're vulnerable to CSRF!
|
||||
sameSite:
|
||||
Env.NODE_ENV === "production" || Env.NODE_ENV === "staging"
|
||||
? "strict"
|
||||
: "none",
|
||||
sameSite: Env.NODE_ENV === "production" || Env.NODE_ENV === "staging" ? "strict" : "none",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user