Add logger to project (#183)
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

# Description

Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.

- Add the winston package to handle logging
- Add logging to the project's logic

#146

## Type of change

Please delete options that are not relevant.

- [x] New feature (non-breaking change which adds functionality)

# How Has This Been Tested?

Please describe the tests that you ran to verify the changes. Provide instructions so we can reproduce. Please also list any relevant details to your test configuration.

# Checklist

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that provde my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules

Reviewed-on: https://gitea.vylpes.xyz/External/card-drop/pulls/183
Reviewed-by: VylpesTester <tester@vylpes.com>
Co-authored-by: Ethan Lane <ethan@vylpes.com>
Co-committed-by: Ethan Lane <ethan@vylpes.com>
This commit is contained in:
Ethan Lane 2024-03-15 17:33:12 +00:00 committed by Vylpes
parent 42dfe2d047
commit 5dd50a3f3b
27 changed files with 465 additions and 26 deletions

View file

@ -12,6 +12,7 @@ BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816 BOT_OWNERID=147392775707426816
BOT_CLIENTID=682942374040961060 BOT_CLIENTID=682942374040961060
BOT_ENV=4 BOT_ENV=4
BOT_LOGLEVEL=info
BOT_ADMINS=147392775707426816,887272961504071690 BOT_ADMINS=147392775707426816,887272961504071690
ABOUT_FUNDING= ABOUT_FUNDING=

View file

@ -12,6 +12,7 @@ BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816 BOT_OWNERID=147392775707426816
BOT_CLIENTID=1093810443589529631 BOT_CLIENTID=1093810443589529631
BOT_ENV=1 BOT_ENV=1
BOT_LOGLEVEL=info
BOT_ADMINS=147392775707426816,887272961504071690 BOT_ADMINS=147392775707426816,887272961504071690
ABOUT_FUNDING= ABOUT_FUNDING=

View file

@ -12,6 +12,7 @@ BOT_AUTHOR=Vylpes
BOT_OWNERID=147392775707426816 BOT_OWNERID=147392775707426816
BOT_CLIENTID=1147976642942214235 BOT_CLIENTID=1147976642942214235
BOT_ENV=2 BOT_ENV=2
BOT_LOGLEVEL=info
BOT_ADMINS=147392775707426816,887272961504071690 BOT_ADMINS=147392775707426816,887272961504071690
ABOUT_FUNDING= ABOUT_FUNDING=

222
package-lock.json generated
View file

@ -25,7 +25,8 @@
"minimatch": "9.0.3", "minimatch": "9.0.3",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"ts-jest": "^29.0.0", "ts-jest": "^29.0.0",
"typeorm": "0.3.20" "typeorm": "0.3.20",
"winston": "^3.11.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.0.0", "@types/node": "^20.0.0",
@ -711,6 +712,24 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/@colors/colors": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
"integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==",
"engines": {
"node": ">=0.1.90"
}
},
"node_modules/@dabh/diagnostics": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
"integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
"dependencies": {
"colorspace": "1.1.x",
"enabled": "2.0.x",
"kuler": "^2.0.0"
}
},
"node_modules/@discordjs/builders": { "node_modules/@discordjs/builders": {
"version": "1.7.0", "version": "1.7.0",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -1940,6 +1959,11 @@
"version": "2.0.3", "version": "2.0.3",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/triple-beam": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz",
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw=="
},
"node_modules/@types/uuid": { "node_modules/@types/uuid": {
"version": "9.0.8", "version": "9.0.8",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
@ -2576,6 +2600,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/async": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
"integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
},
"node_modules/babel-jest": { "node_modules/babel-jest": {
"version": "29.7.0", "version": "29.7.0",
"license": "MIT", "license": "MIT",
@ -3582,6 +3611,15 @@
"version": "1.0.2", "version": "1.0.2",
"license": "MIT" "license": "MIT"
}, },
"node_modules/color": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
"integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==",
"dependencies": {
"color-convert": "^1.9.3",
"color-string": "^1.6.0"
}
},
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"license": "MIT", "license": "MIT",
@ -3596,6 +3634,15 @@
"version": "1.1.4", "version": "1.1.4",
"license": "MIT" "license": "MIT"
}, },
"node_modules/color-string": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
"dependencies": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
}
},
"node_modules/color-support": { "node_modules/color-support": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
@ -3606,6 +3653,28 @@
"color-support": "bin.js" "color-support": "bin.js"
} }
}, },
"node_modules/color/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/color/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/colorspace": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
"integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==",
"dependencies": {
"color": "^3.1.3",
"text-hex": "1.0.x"
}
},
"node_modules/commander": { "node_modules/commander": {
"version": "6.2.1", "version": "6.2.1",
"dev": true, "dev": true,
@ -4211,6 +4280,11 @@
"version": "8.0.0", "version": "8.0.0",
"license": "MIT" "license": "MIT"
}, },
"node_modules/enabled": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
"integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ=="
},
"node_modules/encodeurl": { "node_modules/encodeurl": {
"version": "1.0.2", "version": "1.0.2",
"license": "MIT", "license": "MIT",
@ -4767,6 +4841,11 @@
"bser": "2.1.1" "bser": "2.1.1"
} }
}, },
"node_modules/fecha": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
"integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw=="
},
"node_modules/figures": { "node_modules/figures": {
"version": "2.0.0", "version": "2.0.0",
"dev": true, "dev": true,
@ -4859,6 +4938,11 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true "dev": true
}, },
"node_modules/fn.name": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz",
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
},
"node_modules/foreground-child": { "node_modules/foreground-child": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@ -7055,6 +7139,11 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/kuler": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz",
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="
},
"node_modules/latest-version": { "node_modules/latest-version": {
"version": "7.0.0", "version": "7.0.0",
"dev": true, "dev": true,
@ -7555,6 +7644,27 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/logform": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz",
"integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==",
"dependencies": {
"@colors/colors": "1.6.0",
"@types/triple-beam": "^1.3.2",
"fecha": "^4.2.0",
"ms": "^2.1.1",
"safe-stable-stringify": "^2.3.1",
"triple-beam": "^1.3.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/logform/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/lowercase-keys": { "node_modules/lowercase-keys": {
"version": "2.0.0", "version": "2.0.0",
"dev": true, "dev": true,
@ -8670,6 +8780,14 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"node_modules/one-time": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz",
"integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==",
"dependencies": {
"fn.name": "1.x.x"
}
},
"node_modules/onetime": { "node_modules/onetime": {
"version": "5.1.2", "version": "5.1.2",
"license": "MIT", "license": "MIT",
@ -9887,6 +10005,14 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/safe-stable-stringify": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz",
"integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==",
"engines": {
"node": ">=10"
}
},
"node_modules/safer-buffer": { "node_modules/safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
"license": "MIT" "license": "MIT"
@ -10034,6 +10160,19 @@
"version": "3.0.7", "version": "3.0.7",
"license": "ISC" "license": "ISC"
}, },
"node_modules/simple-swizzle": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
"dependencies": {
"is-arrayish": "^0.3.1"
}
},
"node_modules/simple-swizzle/node_modules/is-arrayish": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
},
"node_modules/sisteransi": { "node_modules/sisteransi": {
"version": "1.0.5", "version": "1.0.5",
"license": "MIT" "license": "MIT"
@ -10223,6 +10362,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==",
"engines": {
"node": "*"
}
},
"node_modules/stack-utils": { "node_modules/stack-utils": {
"version": "2.0.6", "version": "2.0.6",
"license": "MIT", "license": "MIT",
@ -10571,6 +10718,11 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/text-hex": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz",
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg=="
},
"node_modules/text-table": { "node_modules/text-table": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
@ -10656,6 +10808,14 @@
"optional": true, "optional": true,
"peer": true "peer": true
}, },
"node_modules/triple-beam": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz",
"integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==",
"engines": {
"node": ">= 14.0.0"
}
},
"node_modules/ts-api-utils": { "node_modules/ts-api-utils": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
@ -11244,6 +11404,66 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/winston": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz",
"integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==",
"dependencies": {
"@colors/colors": "^1.6.0",
"@dabh/diagnostics": "^2.0.2",
"async": "^3.2.3",
"is-stream": "^2.0.0",
"logform": "^2.4.0",
"one-time": "^1.0.0",
"readable-stream": "^3.4.0",
"safe-stable-stringify": "^2.3.1",
"stack-trace": "0.0.x",
"triple-beam": "^1.3.0",
"winston-transport": "^4.5.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/winston-transport": {
"version": "4.7.0",
"resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz",
"integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==",
"dependencies": {
"logform": "^2.3.2",
"readable-stream": "^3.6.0",
"triple-beam": "^1.3.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/winston-transport/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/winston/node_modules/readable-stream": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/wrap-ansi": { "node_modules/wrap-ansi": {
"version": "8.1.0", "version": "8.1.0",
"license": "MIT", "license": "MIT",

View file

@ -41,7 +41,8 @@
"minimatch": "9.0.3", "minimatch": "9.0.3",
"mysql": "^2.18.1", "mysql": "^2.18.1",
"ts-jest": "^29.0.0", "ts-jest": "^29.0.0",
"typeorm": "0.3.20" "typeorm": "0.3.20",
"winston": "^3.11.0"
}, },
"overrides": { "overrides": {
"undici": "^5.28.3" "undici": "^5.28.3"

View file

@ -4,6 +4,7 @@ import Config from "../database/entities/app/Config";
import { glob } from "glob"; import { glob } from "glob";
import { SeriesMetadata } from "../contracts/SeriesMetadata"; import { SeriesMetadata } from "../contracts/SeriesMetadata";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import AppLogger from "../client/appLogger";
export interface CardMetadataResult { export interface CardMetadataResult {
IsSuccess: boolean; IsSuccess: boolean;
@ -21,16 +22,22 @@ export interface FindMetadataResult {
export default class CardMetadataFunction { export default class CardMetadataFunction {
public static async Execute(overrideSafeMode: boolean = false): Promise<CardMetadataResult> { public static async Execute(overrideSafeMode: boolean = false): Promise<CardMetadataResult> {
if (!overrideSafeMode && await Config.GetValue("safemode") == "true") return { AppLogger.LogInfo("Functions/CardMetadataFunction", "Executing");
IsSuccess: false,
ErrorMessage: "Safe mode is on and not overridden", if (!overrideSafeMode && await Config.GetValue("safemode") == "true") {
}; AppLogger.LogWarn("Functions/CardMetadataFunction", "Safe Mode is active, refusing to resync");
return {
IsSuccess: false,
ErrorMessage: "Safe mode is on and not overridden",
};
}
const cardResult = await this.FindMetadataJSONs(); const cardResult = await this.FindMetadataJSONs();
if (cardResult.IsSuccess) { if (cardResult.IsSuccess) {
CoreClient.Cards = cardResult.Result!; CoreClient.Cards = cardResult.Result!;
console.log(`Loaded ${CoreClient.Cards.flatMap(x => x.cards).length} cards to database`); AppLogger.LogInfo("Functions/CardMetadataFunction", `Loaded ${CoreClient.Cards.flatMap(x => x.cards).length} cards to database`);
return { return {
IsSuccess: true, IsSuccess: true,
@ -38,6 +45,7 @@ export default class CardMetadataFunction {
} }
await Config.SetValue("safemode", "true"); await Config.SetValue("safemode", "true");
AppLogger.LogError("Functions/CardMetadataFunction", `Safe Mode activated due to error: ${cardResult.Error!.Message}`);
return { return {
IsSuccess: false, IsSuccess: false,
@ -52,13 +60,13 @@ export default class CardMetadataFunction {
for (const jsonPath of seriesJSONs) { for (const jsonPath of seriesJSONs) {
try { try {
console.log(`Reading file ${jsonPath}`); AppLogger.LogVerbose("Functions/CardMetadataFunction", `Reading file ${jsonPath}`);
const jsonFile = readFileSync(jsonPath); const jsonFile = readFileSync(jsonPath);
const parsedJson: SeriesMetadata[] = JSON.parse(jsonFile.toString()); const parsedJson: SeriesMetadata[] = JSON.parse(jsonFile.toString());
res.push(...parsedJson); res.push(...parsedJson);
} catch (e) { } catch (e) {
console.error(e); AppLogger.LogError("Functions/CardMetadataFunction", `Error reading file ${jsonPath}: ${e}`);
return { return {
IsSuccess: false, IsSuccess: false,

View file

@ -3,6 +3,7 @@ import { ButtonEvent } from "../type/buttonEvent";
import Inventory from "../database/entities/app/Inventory"; import Inventory from "../database/entities/app/Inventory";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import { default as eClaim } from "../database/entities/app/Claim"; import { default as eClaim } from "../database/entities/app/Claim";
import AppLogger from "../client/appLogger";
export default class Claim extends ButtonEvent { export default class Claim extends ButtonEvent {
public override async execute(interaction: ButtonInteraction) { public override async execute(interaction: ButtonInteraction) {
@ -13,6 +14,8 @@ export default class Claim extends ButtonEvent {
const droppedBy = interaction.customId.split(" ")[3]; const droppedBy = interaction.customId.split(" ")[3];
const userId = interaction.user.id; const userId = interaction.user.id;
AppLogger.LogSilly("Button/Claim", `Parameters: cardNumber=${cardNumber}, claimId=${claimId}, droppedBy=${droppedBy}, userId=${userId}`);
await interaction.deferReply(); await interaction.deferReply();
const claimed = await eClaim.FetchOneByClaimId(claimId); const claimed = await eClaim.FetchOneByClaimId(claimId);

View file

@ -1,6 +1,7 @@
import { ButtonInteraction } from "discord.js"; import { ButtonInteraction } from "discord.js";
import { ButtonEvent } from "../type/buttonEvent"; import { ButtonEvent } from "../type/buttonEvent";
import InventoryHelper from "../helpers/InventoryHelper"; import InventoryHelper from "../helpers/InventoryHelper";
import AppLogger from "../client/appLogger";
export default class Inventory extends ButtonEvent { export default class Inventory extends ButtonEvent {
public override async execute(interaction: ButtonInteraction) { public override async execute(interaction: ButtonInteraction) {
@ -9,6 +10,8 @@ export default class Inventory extends ButtonEvent {
const userid = interaction.customId.split(" ")[1]; const userid = interaction.customId.split(" ")[1];
const page = interaction.customId.split(" ")[2]; const page = interaction.customId.split(" ")[2];
AppLogger.LogSilly("Button/Inventory", `Parameters: userid=${userid}, page=${page}`);
const member = interaction.guild.members.cache.find(x => x.id == userid) || await interaction.guild.members.fetch(userid); const member = interaction.guild.members.cache.find(x => x.id == userid) || await interaction.guild.members.fetch(userid);
if (!member) { if (!member) {
@ -17,6 +20,8 @@ export default class Inventory extends ButtonEvent {
} }
try { try {
AppLogger.LogVerbose("Button/Inventory", `Generating inventory page ${page} for ${member.user.username} with id ${member.user.id}`);
const embed = await InventoryHelper.GenerateInventoryPage(member.user.username, member.user.id, Number(page)); const embed = await InventoryHelper.GenerateInventoryPage(member.user.username, member.user.id, Number(page));
await interaction.update({ await interaction.update({
@ -24,7 +29,8 @@ export default class Inventory extends ButtonEvent {
components: [ embed.row ], components: [ embed.row ],
}); });
} catch (e) { } catch (e) {
console.error(e); AppLogger.LogError("Button/Inventory", `Error generating inventory page for ${member.user.username} with id ${member.user.id}: ${e}`);
await interaction.reply("No page for user found."); await interaction.reply("No page for user found.");
} }
} }

View file

@ -7,6 +7,7 @@ import Inventory from "../database/entities/app/Inventory";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
import path from "path"; import path from "path";
import AppLogger from "../client/appLogger";
export default class Reroll extends ButtonEvent { export default class Reroll extends ButtonEvent {
public override async execute(interaction: ButtonInteraction) { public override async execute(interaction: ButtonInteraction) {
@ -16,6 +17,8 @@ export default class Reroll extends ButtonEvent {
} }
if (await Config.GetValue("safemode") == "true") { if (await Config.GetValue("safemode") == "true") {
AppLogger.LogWarn("Button/Reroll", "Safe Mode is active, refusing to send next drop.");
await interaction.reply("Safe Mode has been activated, please resync to continue."); await interaction.reply("Safe Mode has been activated, please resync to continue.");
return; return;
} }
@ -30,6 +33,8 @@ export default class Reroll extends ButtonEvent {
await interaction.deferReply(); await interaction.deferReply();
try { try {
AppLogger.LogVerbose("Button/Reroll", `Sending next drop: ${randomCard.card.id} (${randomCard.card.name})`);
const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", randomCard.card.path)); const image = readFileSync(path.join(process.env.DATA_DIR!, "cards", randomCard.card.path));
const imageFileName = randomCard.card.path.split("/").pop()!; const imageFileName = randomCard.card.path.split("/").pop()!;
@ -51,9 +56,8 @@ export default class Reroll extends ButtonEvent {
}); });
CoreClient.ClaimId = claimId; CoreClient.ClaimId = claimId;
} catch (e) { } catch (e) {
console.error(e); AppLogger.LogError("Button/Reroll", `Error sending next drop for card ${randomCard.card.id}: ${e}`);
await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`); await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`);
} }

View file

@ -3,11 +3,14 @@ import { ButtonEvent } from "../type/buttonEvent";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import Inventory from "../database/entities/app/Inventory"; import Inventory from "../database/entities/app/Inventory";
import EmbedColours from "../constants/EmbedColours"; import EmbedColours from "../constants/EmbedColours";
import AppLogger from "../client/appLogger";
export default class Trade extends ButtonEvent { export default class Trade extends ButtonEvent {
public override async execute(interaction: ButtonInteraction) { public override async execute(interaction: ButtonInteraction) {
const action = interaction.customId.split(" ")[1]; const action = interaction.customId.split(" ")[1];
AppLogger.LogSilly("Button/Trade", `Parameters: action=${action}`);
switch (action) { switch (action) {
case "accept": case "accept":
await this.AcceptTrade(interaction); await this.AcceptTrade(interaction);
@ -26,6 +29,8 @@ export default class Trade extends ButtonEvent {
const expiry = interaction.customId.split(" ")[6]; const expiry = interaction.customId.split(" ")[6];
const timeoutId = interaction.customId.split(" ")[7]; const timeoutId = interaction.customId.split(" ")[7];
AppLogger.LogSilly("Button/Trade/AcceptTrade", `Parameters: giveUserId=${giveUserId}, receiveUserId=${receiveUserId}, giveCardNumber=${giveCardNumber}, receiveCardNumber=${receiveCardNumber}, expiry=${expiry}, timeoutId=${timeoutId}`);
const expiryDate = new Date(expiry); const expiryDate = new Date(expiry);
if (expiryDate < new Date()) { if (expiryDate < new Date()) {
@ -140,6 +145,8 @@ export default class Trade extends ButtonEvent {
// No need to get expiry date // No need to get expiry date
const timeoutId = interaction.customId.split(" ")[7]; const timeoutId = interaction.customId.split(" ")[7];
AppLogger.LogSilly("Button/Trade/DeclineTrade", `Parameters: giveUserId=${giveUserId}, receiveUserId=${receiveUserId}, giveCardNumber=${giveCardNumber}, receiveCardNumber=${receiveCardNumber}, timeoutId=${timeoutId}`);
if (interaction.user.id != receiveUserId && interaction.user.id !==giveUserId) { if (interaction.user.id != receiveUserId && interaction.user.id !==giveUserId) {
await interaction.reply("You are not the user who the trade is intended for"); await interaction.reply("You are not the user who the trade is intended for");
return; return;

65
src/client/appLogger.ts Normal file
View file

@ -0,0 +1,65 @@
import { Logger, createLogger, format, transports } from "winston";
export default class AppLogger {
public static Logger: Logger;
public static InitialiseLogger(logLevel: string, outputToConsole: boolean) {
const customFormat = format.printf(({ level, message, timestamp, label }) => {
return `${timestamp} [${label}] ${level}: ${message}`;
});
const logger = createLogger({
level: logLevel,
format: format.combine(
format.timestamp({
format: "YYYY-MM-DD HH:mm:ss"
}),
format.errors({ stack: true }),
format.splat(),
customFormat,
),
defaultMeta: { service: "bot" },
transports: [
new transports.File({ filename: "error.log", level: "error" }),
new transports.File({ filename: "combined.log" }),
],
});
if (outputToConsole) {
logger.add(new transports.Console({
format: format.combine(
format.colorize(),
format.timestamp(),
customFormat,
)}));
}
AppLogger.Logger = logger;
AppLogger.LogInfo("AppLogger", `Log Level: ${logLevel}`);
}
public static LogError(label: string, message: string) {
AppLogger.Logger.error({ label, message });
}
public static LogWarn(label: string, message: string) {
AppLogger.Logger.warn({ label, message });
}
public static LogInfo(label: string, message: string) {
AppLogger.Logger.info({ label, message });
}
public static LogVerbose(label: string, message: string) {
AppLogger.Logger.verbose({ label, message });
}
public static LogDebug(label: string, message: string) {
AppLogger.Logger.debug({ label, message });
}
public static LogSilly(label: string, message: string) {
AppLogger.Logger.silly({ label, message });
}
}

View file

@ -13,6 +13,7 @@ import { Environment } from "../constants/Environment";
import Webhooks from "../webhooks"; import Webhooks from "../webhooks";
import CardMetadataFunction from "../Functions/CardMetadataFunction"; import CardMetadataFunction from "../Functions/CardMetadataFunction";
import { SeriesMetadata } from "../contracts/SeriesMetadata"; import { SeriesMetadata } from "../contracts/SeriesMetadata";
import AppLogger from "./appLogger";
export class CoreClient extends Client { export class CoreClient extends Client {
private static _commandItems: ICommandItem[]; private static _commandItems: ICommandItem[];
@ -44,6 +45,14 @@ export class CoreClient extends Client {
super({ intents: intents }); super({ intents: intents });
dotenv.config(); dotenv.config();
CoreClient.Environment = Number(process.env.BOT_ENV);
const loglevel = process.env.BOT_LOGLEVEL ?? "info";
AppLogger.InitialiseLogger(loglevel, CoreClient.Environment == Environment.Local);
AppLogger.LogInfo("Client", "Initialising Client");
CoreClient._commandItems = []; CoreClient._commandItems = [];
CoreClient._buttonEvents = []; CoreClient._buttonEvents = [];
@ -51,21 +60,24 @@ export class CoreClient extends Client {
this._util = new Util(); this._util = new Util();
this._webhooks = new Webhooks(); this._webhooks = new Webhooks();
CoreClient.Environment = Number(process.env.BOT_ENV); AppLogger.LogInfo("Client", `Environment: ${CoreClient.Environment}`);
console.log(`Bot Environment: ${CoreClient.Environment}`);
CoreClient.AllowDrops = true; CoreClient.AllowDrops = true;
} }
public async start() { public async start() {
if (!process.env.BOT_TOKEN) { if (!process.env.BOT_TOKEN) {
console.error("BOT_TOKEN is not defined in .env"); AppLogger.LogError("Client", "BOT_TOKEN is not defined in .env");
return; return;
} }
await AppDataSource.initialize() await AppDataSource.initialize()
.then(() => console.log("App Data Source Initialised")) .then(() => AppLogger.LogInfo("Client", "App Data Source Initialised"))
.catch(err => console.error("Error initialising App Data Source", err)); .catch(err => {
AppLogger.LogError("Client", "App Data Source Initialisation Failed");
AppLogger.LogError("Client", err);
throw err;
});
super.on("interactionCreate", this._events.onInteractionCreate); super.on("interactionCreate", this._events.onInteractionCreate);
super.on("ready", this._events.onReady); super.on("ready", this._events.onReady);
@ -90,6 +102,8 @@ export class CoreClient extends Client {
if ((environment & CoreClient.Environment) == CoreClient.Environment) { if ((environment & CoreClient.Environment) == CoreClient.Environment) {
CoreClient._commandItems.push(item); CoreClient._commandItems.push(item);
AppLogger.LogVerbose("Client", `Registered Command: ${name}`);
} }
} }
@ -112,6 +126,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Channel Create Event");
} }
public static RegisterChannelDeleteEvent(fn: (channel: DMChannel | NonThreadGuildBasedChannel) => void) { public static RegisterChannelDeleteEvent(fn: (channel: DMChannel | NonThreadGuildBasedChannel) => void) {
@ -133,6 +149,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Channel Delete Event");
} }
public static RegisterChannelUpdateEvent(fn: (channel: DMChannel | NonThreadGuildBasedChannel) => void) { public static RegisterChannelUpdateEvent(fn: (channel: DMChannel | NonThreadGuildBasedChannel) => void) {
@ -154,6 +172,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Channel Update Event");
} }
public static RegisterGuildBanAddEvent(fn: (ban: GuildBan) => void) { public static RegisterGuildBanAddEvent(fn: (ban: GuildBan) => void) {
@ -175,6 +195,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Ban Add Event");
} }
public static RegisterGuildBanRemoveEvent(fn: (channel: GuildBan) => void) { public static RegisterGuildBanRemoveEvent(fn: (channel: GuildBan) => void) {
@ -196,6 +218,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Ban Remove Event");
} }
public static RegisterGuildCreateEvent(fn: (guild: Guild) => void) { public static RegisterGuildCreateEvent(fn: (guild: Guild) => void) {
@ -217,6 +241,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Create Event");
} }
public static RegisterGuildMemberAddEvent(fn: (member: GuildMember) => void) { public static RegisterGuildMemberAddEvent(fn: (member: GuildMember) => void) {
@ -238,6 +264,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Member Add Event");
} }
public static RegisterGuildMemberRemoveEvent(fn: (member: GuildMember | PartialGuildMember) => void) { public static RegisterGuildMemberRemoveEvent(fn: (member: GuildMember | PartialGuildMember) => void) {
@ -259,6 +287,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Member Remove Event");
} }
public static GuildMemebrUpdate(fn: (oldMember: GuildMember | PartialGuildMember, newMember: GuildMember) => void) { public static GuildMemebrUpdate(fn: (oldMember: GuildMember | PartialGuildMember, newMember: GuildMember) => void) {
@ -280,6 +310,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Guild Member Update Event");
} }
public static RegisterMessageCreateEvent(fn: (message: Message<boolean>) => void) { public static RegisterMessageCreateEvent(fn: (message: Message<boolean>) => void) {
@ -301,6 +333,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Message Create Event");
} }
public static RegisterMessageDeleteEvent(fn: (message: Message<boolean> | PartialMessage) => void) { public static RegisterMessageDeleteEvent(fn: (message: Message<boolean> | PartialMessage) => void) {
@ -322,6 +356,8 @@ export class CoreClient extends Client {
MessageUpdate: [], MessageUpdate: [],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Message Delete Event");
} }
public static RegisterMessageUpdateEvent(fn: (oldMessage: Message<boolean> | PartialMessage, newMessage: Message<boolean> | PartialMessage) => void) { public static RegisterMessageUpdateEvent(fn: (oldMessage: Message<boolean> | PartialMessage, newMessage: Message<boolean> | PartialMessage) => void) {
@ -343,6 +379,8 @@ export class CoreClient extends Client {
MessageUpdate: [ fn ], MessageUpdate: [ fn ],
}; };
} }
AppLogger.LogVerbose("Client", "Registered Message Update Event");
} }
public static RegisterButtonEvent(buttonId: string, event: ButtonEvent, environment: Environment = Environment.All) { public static RegisterButtonEvent(buttonId: string, event: ButtonEvent, environment: Environment = Environment.All) {
@ -354,6 +392,8 @@ export class CoreClient extends Client {
if ((environment & CoreClient.Environment) == CoreClient.Environment) { if ((environment & CoreClient.Environment) == CoreClient.Environment) {
CoreClient._buttonEvents.push(item); CoreClient._buttonEvents.push(item);
AppLogger.LogVerbose("Client", `Registered Button Event: ${buttonId}`);
} }
} }
} }

View file

@ -1,22 +1,25 @@
import { Interaction } from "discord.js"; import { Interaction } from "discord.js";
import ChatInputCommand from "./interactionCreate/ChatInputCommand"; import ChatInputCommand from "./interactionCreate/ChatInputCommand";
import Button from "./interactionCreate/Button"; import Button from "./interactionCreate/Button";
import AppLogger from "./appLogger";
export class Events { export class Events {
public async onInteractionCreate(interaction: Interaction) { public async onInteractionCreate(interaction: Interaction) {
if (!interaction.guildId) return; if (!interaction.guildId) return;
if (interaction.isChatInputCommand()) { if (interaction.isChatInputCommand()) {
AppLogger.LogVerbose("Client", `ChatInputCommand: ${interaction.commandName}`);
ChatInputCommand.onChatInput(interaction); ChatInputCommand.onChatInput(interaction);
} }
if (interaction.isButton()) { if (interaction.isButton()) {
AppLogger.LogVerbose("Client", `Button: ${interaction.customId}`);
Button.onButtonClicked(interaction); Button.onButtonClicked(interaction);
} }
} }
// Emit when bot is logged in and ready to use // Emit when bot is logged in and ready to use
public onReady() { public onReady() {
console.log("Ready"); AppLogger.LogInfo("Client", "Ready");
} }
} }

View file

@ -1,5 +1,6 @@
import { ButtonInteraction } from "discord.js"; import { ButtonInteraction } from "discord.js";
import { CoreClient } from "../client"; import { CoreClient } from "../client";
import AppLogger from "../appLogger";
export default class Button { export default class Button {
public static async onButtonClicked(interaction: ButtonInteraction) { public static async onButtonClicked(interaction: ButtonInteraction) {
@ -8,14 +9,19 @@ export default class Button {
const item = CoreClient.buttonEvents.find(x => x.ButtonId == interaction.customId.split(" ")[0]); const item = CoreClient.buttonEvents.find(x => x.ButtonId == interaction.customId.split(" ")[0]);
if (!item) { if (!item) {
AppLogger.LogVerbose("Button", `Event not found: ${interaction.customId}`);
await interaction.reply("Event not found"); await interaction.reply("Event not found");
return; return;
} }
try { try {
AppLogger.LogDebug("Button", `Executing ${interaction.customId}`);
item.Event.execute(interaction); item.Event.execute(interaction);
} catch (e) { } catch (e) {
console.error(e); AppLogger.LogError("Button", `Error occurred while executing event: ${interaction.customId}`);
AppLogger.LogError("Button", e as string);
await interaction.reply("An error occurred while executing the event"); await interaction.reply("An error occurred while executing the event");
} }

View file

@ -1,6 +1,7 @@
import { Interaction } from "discord.js"; import { Interaction } from "discord.js";
import { CoreClient } from "../client"; import { CoreClient } from "../client";
import ICommandItem from "../../contracts/ICommandItem"; import ICommandItem from "../../contracts/ICommandItem";
import AppLogger from "../appLogger";
export default class ChatInputCommand { export default class ChatInputCommand {
public static async onChatInput(interaction: Interaction) { public static async onChatInput(interaction: Interaction) {
@ -13,6 +14,8 @@ export default class ChatInputCommand {
if (!itemForServer) { if (!itemForServer) {
if (!item) { if (!item) {
AppLogger.LogVerbose("ChatInputCommand", `Command not found: ${interaction.commandName}`);
await interaction.reply("Command not found"); await interaction.reply("Command not found");
return; return;
} }
@ -23,9 +26,12 @@ export default class ChatInputCommand {
} }
try { try {
AppLogger.LogDebug("Command", `Executing ${interaction.commandName}`);
itemToUse.Command.execute(interaction); itemToUse.Command.execute(interaction);
} catch (e) { } catch (e) {
console.error(e); AppLogger.LogError("ChatInputCommand", `Error occurred while executing command: ${interaction.commandName}`);
AppLogger.LogError("ChatInputCommand", e as string);
await interaction.reply("An error occurred while executing the command"); await interaction.reply("An error occurred while executing the command");
} }

View file

@ -1,6 +1,7 @@
import { Client, REST, Routes, SlashCommandBuilder } from "discord.js"; import { Client, REST, Routes, SlashCommandBuilder } from "discord.js";
import EventExecutors from "../contracts/EventExecutors"; import EventExecutors from "../contracts/EventExecutors";
import { CoreClient } from "./client"; import { CoreClient } from "./client";
import AppLogger from "./appLogger";
export class Util { export class Util {
public loadSlashCommands(client: Client) { public loadSlashCommands(client: Client) {
@ -29,6 +30,8 @@ export class Util {
const rest = new REST({ version: "10" }).setToken(process.env.BOT_TOKEN!); const rest = new REST({ version: "10" }).setToken(process.env.BOT_TOKEN!);
AppLogger.LogVerbose("Util", `REST PUT: ${globalCommandData.flatMap(x => x.name).join(", ")}`);
rest.put( rest.put(
Routes.applicationCommands(process.env.BOT_CLIENTID!), Routes.applicationCommands(process.env.BOT_CLIENTID!),
{ {
@ -49,6 +52,8 @@ export class Util {
if (!client.guilds.cache.has(guild)) continue; if (!client.guilds.cache.has(guild)) continue;
AppLogger.LogVerbose("Util", `REST PUT: ${guild} - ${guildCommandData.flatMap(x => x.name).join(", ")}`);
rest.put( rest.put(
Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild), Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild),
{ {

View file

@ -7,6 +7,7 @@ import Inventory from "../database/entities/app/Inventory";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
import path from "path"; import path from "path";
import AppLogger from "../client/appLogger";
export default class Drop extends Command { export default class Drop extends Command {
constructor() { constructor() {
@ -24,6 +25,8 @@ export default class Drop extends Command {
} }
if (await Config.GetValue("safemode") == "true") { if (await Config.GetValue("safemode") == "true") {
AppLogger.LogWarn("Commands/Drop", "Safe Mode is active, refusing to send next drop.");
await interaction.reply("Safe Mode has been activated, please resync to continue."); await interaction.reply("Safe Mode has been activated, please resync to continue.");
return; return;
} }
@ -31,6 +34,8 @@ export default class Drop extends Command {
const randomCard = CardDropHelperMetadata.GetRandomCard(); const randomCard = CardDropHelperMetadata.GetRandomCard();
if (!randomCard) { if (!randomCard) {
AppLogger.LogWarn("Commands/Drop", "Unable to fetch card, please try again. (randomCard is null)");
await interaction.reply("Unable to fetch card, please try again."); await interaction.reply("Unable to fetch card, please try again.");
return; return;
} }
@ -61,7 +66,7 @@ export default class Drop extends Command {
CoreClient.ClaimId = claimId; CoreClient.ClaimId = claimId;
} catch (e) { } catch (e) {
console.error(e); AppLogger.LogError("Commands/Drop", `Error sending next drop for card ${randomCard.card.id}: ${e}`);
await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`); await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. (${randomCard.card.id})`);
} }

View file

@ -4,6 +4,7 @@ import { ExecException, exec } from "child_process";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardMetadataFunction from "../Functions/CardMetadataFunction"; import CardMetadataFunction from "../Functions/CardMetadataFunction";
import AppLogger from "../client/appLogger";
export default class Gdrivesync extends Command { export default class Gdrivesync extends Command {
constructor() { constructor() {
@ -32,19 +33,28 @@ export default class Gdrivesync extends Command {
CoreClient.AllowDrops = false; CoreClient.AllowDrops = false;
AppLogger.LogInfo("Commands/GDriveSync", "Syncing google drive to the bot");
exec(`rclone sync card-drop-gdrive: ${process.env.DATA_DIR}/cards`, async (error: ExecException | null) => { exec(`rclone sync card-drop-gdrive: ${process.env.DATA_DIR}/cards`, async (error: ExecException | null) => {
if (error) { if (error) {
AppLogger.LogError("Commands/GDriveSync", `Error while running sync command: ${error.code}, ${error.message}`);
AppLogger.LogWarn("Commands/GDriveSync", "Safe mode activated");
await interaction.editReply(`Error while running sync command. Safe Mode has been activated. Code: ${error.code}`); await interaction.editReply(`Error while running sync command. Safe Mode has been activated. Code: ${error.code}`);
await Config.SetValue("safemode", "true"); await Config.SetValue("safemode", "true");
} else { } else {
const result = await CardMetadataFunction.Execute(true); const result = await CardMetadataFunction.Execute(true);
if (result.IsSuccess) { if (result.IsSuccess) {
AppLogger.LogInfo("Commands/GDriveSync", "Synced successfully");
await interaction.editReply("Synced successfully."); await interaction.editReply("Synced successfully.");
CoreClient.AllowDrops = true; CoreClient.AllowDrops = true;
await Config.SetValue("safemode", "false"); await Config.SetValue("safemode", "false");
} else { } else {
AppLogger.LogError("Commands/GDriveSync", `Error while running sync command: ${result.ErrorMessage}`);
await interaction.editReply(`Sync failed \`\`\`${result.ErrorMessage}\`\`\``); await interaction.editReply(`Sync failed \`\`\`${result.ErrorMessage}\`\`\``);
} }
} }

View file

@ -4,6 +4,7 @@ import { CoreClient } from "../client/client";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
import Inventory from "../database/entities/app/Inventory"; import Inventory from "../database/entities/app/Inventory";
import AppLogger from "../client/appLogger";
export default class Give extends Command { export default class Give extends Command {
constructor() { constructor() {
@ -46,6 +47,8 @@ export default class Give extends Command {
const cardNumber = interaction.options.get("cardnumber", true); const cardNumber = interaction.options.get("cardnumber", true);
const user = interaction.options.getUser("user", true); const user = interaction.options.getUser("user", true);
AppLogger.LogSilly("Commands/Give", `Parameters: cardNumber=${cardNumber.value}, user=${user.id}`);
const card = CardDropHelperMetadata.GetCardByCardNumber(cardNumber.value!.toString()); const card = CardDropHelperMetadata.GetCardByCardNumber(cardNumber.value!.toString());
if (!card) { if (!card) {

View file

@ -1,6 +1,7 @@
import { CommandInteraction, SlashCommandBuilder } from "discord.js"; import { CommandInteraction, SlashCommandBuilder } from "discord.js";
import { Command } from "../type/command"; import { Command } from "../type/command";
import InventoryHelper from "../helpers/InventoryHelper"; import InventoryHelper from "../helpers/InventoryHelper";
import AppLogger from "../client/appLogger";
export default class Inventory extends Command { export default class Inventory extends Command {
constructor() { constructor() {
@ -23,6 +24,8 @@ export default class Inventory extends Command {
const page = interaction.options.get("page"); const page = interaction.options.get("page");
const user = interaction.options.getUser("user") || interaction.user; const user = interaction.options.getUser("user") || interaction.user;
AppLogger.LogSilly("Commands/Inventory", `Parameters: page=${page?.value}, user=${user.id}`);
try { try {
let pageNumber = 0; let pageNumber = 0;
@ -36,7 +39,9 @@ export default class Inventory extends Command {
embeds: [ embed.embed ], embeds: [ embed.embed ],
components: [ embed.row ], components: [ embed.row ],
}); });
} catch { } catch (e) {
AppLogger.LogError("Commands/Inventory", e as string);
await interaction.reply("No page for user found."); await interaction.reply("No page for user found.");
} }
} }

View file

@ -2,6 +2,7 @@ import { CacheType, CommandInteraction, PermissionsBitField, SlashCommandBuilder
import { Command } from "../type/command"; import { Command } from "../type/command";
import Config from "../database/entities/app/Config"; import Config from "../database/entities/app/Config";
import CardMetadataFunction from "../Functions/CardMetadataFunction"; import CardMetadataFunction from "../Functions/CardMetadataFunction";
import AppLogger from "../client/appLogger";
export default class Resync extends Command { export default class Resync extends Command {
constructor() { constructor() {
@ -23,10 +24,14 @@ export default class Resync extends Command {
return; return;
} }
AppLogger.LogInfo("Commands/Resync", "Resyncing database");
const result = await CardMetadataFunction.Execute(true); const result = await CardMetadataFunction.Execute(true);
if (result) { if (result) {
if (await Config.GetValue("safemode") == "true") { if (await Config.GetValue("safemode") == "true") {
AppLogger.LogInfo("Commands/Resync", "Resync successful, safe mode disabled");
await Config.SetValue("safemode", "false"); await Config.SetValue("safemode", "false");
await interaction.reply("Resynced database and disabled safe mode."); await interaction.reply("Resynced database and disabled safe mode.");
@ -34,6 +39,8 @@ export default class Resync extends Command {
} }
await interaction.reply("Resynced database."); await interaction.reply("Resynced database.");
} else { } else {
AppLogger.LogWarn("Commands/Resync", "Resync failed, safe mode activated");
await interaction.reply("Resync failed, safe mode has been activated until successful resync."); await interaction.reply("Resync failed, safe mode has been activated until successful resync.");
} }
} }

View file

@ -3,6 +3,7 @@ import { Command } from "../type/command";
import Inventory from "../database/entities/app/Inventory"; import Inventory from "../database/entities/app/Inventory";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import EmbedColours from "../constants/EmbedColours"; import EmbedColours from "../constants/EmbedColours";
import AppLogger from "../client/appLogger";
export default class Trade extends Command { export default class Trade extends Command {
constructor() { constructor() {
@ -33,6 +34,8 @@ export default class Trade extends Command {
const give = interaction.options.get("give")!; const give = interaction.options.get("give")!;
const receive = interaction.options.get("receive")!; const receive = interaction.options.get("receive")!;
AppLogger.LogSilly("Commands/Trade", `Parameters: user=${user.id}, give=${give.value}, receive=${receive.value}`);
const giveItemEntity = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, give.value!.toString()); const giveItemEntity = await Inventory.FetchOneByCardNumberAndUserId(interaction.user.id, give.value!.toString());
const receiveItemEntity = await Inventory.FetchOneByCardNumberAndUserId(user.id, receive.value!.toString()); const receiveItemEntity = await Inventory.FetchOneByCardNumberAndUserId(user.id, receive.value!.toString());
@ -102,6 +105,8 @@ export default class Trade extends Command {
} }
private async autoDecline(interaction: CommandInteraction, giveUsername: string, receiveUsername: string, giveCardNumber: string, receiveCardNumber: string, giveCardName: string, receiveCardName: string) { private async autoDecline(interaction: CommandInteraction, giveUsername: string, receiveUsername: string, giveCardNumber: string, receiveCardNumber: string, giveCardName: string, receiveCardName: string) {
AppLogger.LogSilly("Commands/Trade/AutoDecline", `Auto declining trade between ${giveUsername} and ${receiveUsername}`);
const tradeEmbed = new EmbedBuilder() const tradeEmbed = new EmbedBuilder()
.setTitle("Trade Expired") .setTitle("Trade Expired")
.setDescription(`Trade initiated between ${receiveUsername} and ${giveUsername}`) .setDescription(`Trade initiated between ${receiveUsername} and ${giveUsername}`)

View file

@ -5,6 +5,7 @@ import { readFileSync } from "fs";
import path from "path"; import path from "path";
import Inventory from "../database/entities/app/Inventory"; import Inventory from "../database/entities/app/Inventory";
import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata"; import CardDropHelperMetadata from "../helpers/CardDropHelperMetadata";
import AppLogger from "../client/appLogger";
export default class View extends Command { export default class View extends Command {
constructor() { constructor() {
@ -23,6 +24,8 @@ export default class View extends Command {
public override async execute(interaction: CommandInteraction) { public override async execute(interaction: CommandInteraction) {
const cardNumber = interaction.options.get("cardnumber"); const cardNumber = interaction.options.get("cardnumber");
AppLogger.LogSilly("Commands/View", `Parameters: cardNumber=${cardNumber?.value}`);
if (!cardNumber || !cardNumber.value) { if (!cardNumber || !cardNumber.value) {
await interaction.reply("Card number is required."); await interaction.reply("Card number is required.");
return; return;
@ -46,6 +49,8 @@ export default class View extends Command {
try { try {
image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.path)); image = readFileSync(path.join(process.env.DATA_DIR!, "cards", card.path));
} catch { } catch {
AppLogger.LogError("Commands/View", `Unable to fetch image for card ${card.id}.`);
await interaction.reply(`Unable to fetch image for card ${card.id}.`); await interaction.reply(`Unable to fetch image for card ${card.id}.`);
return; return;
} }
@ -65,7 +70,7 @@ export default class View extends Command {
files: [ attachment ], files: [ attachment ],
}); });
} catch (e) { } catch (e) {
console.error(e); AppLogger.LogError("Commands/View", `Error sending view for card ${card.id}: ${e}`);
if (e instanceof DiscordAPIError) { if (e instanceof DiscordAPIError) {
await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. Code: ${e.code}.`); await interaction.editReply(`Unable to send next drop. Please try again, and report this if it keeps happening. Code: ${e.code}.`);

View file

@ -3,6 +3,7 @@ import { CardRarity, CardRarityToColour, CardRarityToString } from "../constants
import CardRarityChances from "../constants/CardRarityChances"; import CardRarityChances from "../constants/CardRarityChances";
import { CardMetadata, DropResult } from "../contracts/SeriesMetadata"; import { CardMetadata, DropResult } from "../contracts/SeriesMetadata";
import { CoreClient } from "../client/client"; import { CoreClient } from "../client/client";
import AppLogger from "../client/appLogger";
export default class CardDropHelperMetadata { export default class CardDropHelperMetadata {
public static GetRandomCard(): DropResult | undefined { public static GetRandomCard(): DropResult | undefined {
@ -23,10 +24,14 @@ export default class CardDropHelperMetadata {
const randomCard = this.GetRandomCardByRarity(cardRarity); const randomCard = this.GetRandomCardByRarity(cardRarity);
AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCard", `Random card: ${randomCard?.card.id} ${randomCard?.card.name}`);
return randomCard; return randomCard;
} }
public static GetRandomCardByRarity(rarity: CardRarity): DropResult | undefined { public static GetRandomCardByRarity(rarity: CardRarity): DropResult | undefined {
AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCardByRarity", `Parameters: rarity=${rarity}`);
const allCards = CoreClient.Cards const allCards = CoreClient.Cards
.flatMap(x => x.cards) .flatMap(x => x.cards)
.filter(x => x.type == rarity); .filter(x => x.type == rarity);
@ -38,9 +43,13 @@ export default class CardDropHelperMetadata {
.find(x => x.cards.includes(card)); .find(x => x.cards.includes(card));
if (!series) { if (!series) {
AppLogger.LogWarn("CardDropHelperMetadata/GetRandomCardByRarity", `Series not found for card ${card.id}`);
return undefined; return undefined;
} }
AppLogger.LogSilly("CardDropHelperMetadata/GetRandomCardByRarity", `Random card: ${card.id} ${card.name}`);
return { return {
series: series, series: series,
card: card, card: card,
@ -48,14 +57,20 @@ export default class CardDropHelperMetadata {
} }
public static GetCardByCardNumber(cardNumber: string): CardMetadata | undefined { public static GetCardByCardNumber(cardNumber: string): CardMetadata | undefined {
AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Parameters: cardNumber=${cardNumber}`);
const card = CoreClient.Cards const card = CoreClient.Cards
.flatMap(x => x.cards) .flatMap(x => x.cards)
.find(x => x.id == cardNumber); .find(x => x.id == cardNumber);
AppLogger.LogSilly("CardDropHelperMetadata/GetCardByCardNumber", `Card: ${card?.id} ${card?.name}`);
return card; return card;
} }
public static GenerateDropEmbed(drop: DropResult, quantityClaimed: number, imageFileName: string): EmbedBuilder { public static GenerateDropEmbed(drop: DropResult, quantityClaimed: number, imageFileName: string): EmbedBuilder {
AppLogger.LogSilly("CardDropHelperMetadata/GenerateDropEmbed", `Parameters: drop=${drop.card.id}, quantityClaimed=${quantityClaimed}, imageFileName=${imageFileName}`);
let description = ""; let description = "";
description += `Series: ${drop.series.name}\n`; description += `Series: ${drop.series.name}\n`;
description += `Claimed: ${quantityClaimed}\n`; description += `Claimed: ${quantityClaimed}\n`;
@ -69,6 +84,8 @@ export default class CardDropHelperMetadata {
} }
public static GenerateDropButtons(drop: DropResult, claimId: string, userId: string): ActionRowBuilder<ButtonBuilder> { public static GenerateDropButtons(drop: DropResult, claimId: string, userId: string): ActionRowBuilder<ButtonBuilder> {
AppLogger.LogSilly("CardDropHelperMetadata/GenerateDropButtons", `Parameters: drop=${drop.card.id}, claimId=${claimId}, userId=${userId}`);
return new ActionRowBuilder<ButtonBuilder>() return new ActionRowBuilder<ButtonBuilder>()
.addComponents( .addComponents(
new ButtonBuilder() new ButtonBuilder()

View file

@ -4,6 +4,7 @@ import { CoreClient } from "../client/client";
import EmbedColours from "../constants/EmbedColours"; import EmbedColours from "../constants/EmbedColours";
import { CardRarity, CardRarityToString } from "../constants/CardRarity"; import { CardRarity, CardRarityToString } from "../constants/CardRarity";
import cloneDeep from "clone-deep"; import cloneDeep from "clone-deep";
import AppLogger from "../client/appLogger";
interface InventoryPage { interface InventoryPage {
id: number, id: number,
@ -21,6 +22,8 @@ interface InventoryPageCards {
export default class InventoryHelper { export default class InventoryHelper {
public static async GenerateInventoryPage(username: string, userid: string, page: number): Promise<{ embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder> }> { public static async GenerateInventoryPage(username: string, userid: string, page: number): Promise<{ embed: EmbedBuilder, row: ActionRowBuilder<ButtonBuilder> }> {
AppLogger.LogSilly("Helpers/InventoryHelper", `Parameters: username=${username}, userid=${userid}, page=${page}`);
const cardsPerPage = 15; const cardsPerPage = 15;
const inventory = await Inventory.FetchAllByUserId(userid); const inventory = await Inventory.FetchAllByUserId(userid);
@ -73,7 +76,7 @@ export default class InventoryHelper {
const currentPage = pages[page]; const currentPage = pages[page];
if (!currentPage) { if (!currentPage) {
console.error("Unable to find page"); AppLogger.LogError("Helpers/InventoryHelper", "Unable to find page");
return Promise.reject("Unable to find page"); return Promise.reject("Unable to find page");
} }

View file

@ -1,8 +1,9 @@
import { Request, Response } from "express"; import { Request, Response } from "express";
import CardMetadataFunction from "../Functions/CardMetadataFunction"; import CardMetadataFunction from "../Functions/CardMetadataFunction";
import AppLogger from "../client/appLogger";
export default async function ReloadDB(req: Request, res: Response) { export default async function ReloadDB(req: Request, res: Response) {
console.log("Reloading Card DB..."); AppLogger.LogInfo("Hooks/ReloadDB", "Reloading Card DB...");
await CardMetadataFunction.Execute(); await CardMetadataFunction.Execute();

View file

@ -1,6 +1,7 @@
import bodyParser from "body-parser"; import bodyParser from "body-parser";
import express, { Application } from "express"; import express, { Application } from "express";
import ReloadDB from "./hooks/ReloadDB"; import ReloadDB from "./hooks/ReloadDB";
import AppLogger from "./client/appLogger";
export default class Webhooks { export default class Webhooks {
private app: Application; private app: Application;
@ -24,7 +25,7 @@ export default class Webhooks {
private setupListen() { private setupListen() {
this.app.listen(this.port, () => { this.app.listen(this.port, () => {
console.log(`API listening on port ${this.port}`); AppLogger.LogInfo("Webhooks", `API listening on port ${this.port}`);
}); });
} }
} }