WIP: [#1]: Athena Rewrite #1
@@ -1 +1,2 @@
|
|||||||
|
registry=https://npm.hep.gg
|
||||||
@clynamic:registry=https://npm.clynamic.net
|
@clynamic:registry=https://npm.clynamic.net
|
||||||
|
|||||||
Generated
+33
-264
@@ -1,15 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "discordbot-ng",
|
"name": "hexerade",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "discordbot-ng",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clynamic/dmark": "0.0.11",
|
"@clynamic/dmark": "0.0.11",
|
||||||
"@redis/client": "5.1.0",
|
"@redis/client": "5.1.0",
|
||||||
|
"athena-prime": "4.2.0",
|
||||||
"body-parser": "2.2.2",
|
"body-parser": "2.2.2",
|
||||||
"discord.js": "14.26.4",
|
|
||||||
"dotenv": "16.5.0",
|
"dotenv": "16.5.0",
|
||||||
"express": "5.1.0",
|
"express": "5.1.0",
|
||||||
"express-session": "1.19.0",
|
"express-session": "1.19.0",
|
||||||
@@ -37,145 +36,6 @@
|
|||||||
"integrity": "sha512-efrxWJ6ONQ6kcCLwPfoIonrMznvYYm9fZ1v+DgDY5BzQ8EDrgbCJr3ri9dVS7VUt9Rh8yZTXtaMDI1jf3Gb0Aw==",
|
"integrity": "sha512-efrxWJ6ONQ6kcCLwPfoIonrMznvYYm9fZ1v+DgDY5BzQ8EDrgbCJr3ri9dVS7VUt9Rh8yZTXtaMDI1jf3Gb0Aw==",
|
||||||
"license": "UNLICENSED"
|
"license": "UNLICENSED"
|
||||||
},
|
},
|
||||||
"node_modules/@discordjs/builders": {
|
|
||||||
"version": "1.14.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.14.1.tgz",
|
|
||||||
"integrity": "sha512-gSKkhXLqs96TCzk66VZuHHl8z2bQMJFGwrXC0f33ngK+FLNau4hU1PYny3DNJfNdSH+gVMzE85/d5FQ2BpcNwQ==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@discordjs/formatters": "^0.6.2",
|
|
||||||
"@discordjs/util": "^1.2.0",
|
|
||||||
"@sapphire/shapeshift": "^4.0.0",
|
|
||||||
"discord-api-types": "^0.38.40",
|
|
||||||
"fast-deep-equal": "^3.1.3",
|
|
||||||
"ts-mixer": "^6.0.4",
|
|
||||||
"tslib": "^2.6.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.11.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@discordjs/collection": {
|
|
||||||
"version": "1.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.5.3.tgz",
|
|
||||||
"integrity": "sha512-SVb428OMd3WO1paV3rm6tSjM4wC+Kecaa1EUGX7vc6/fddvw/6lg90z4QtCqm21zvVe92vMMDt9+DkIvjXImQQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.11.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@discordjs/formatters": {
|
|
||||||
"version": "0.6.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.6.2.tgz",
|
|
||||||
"integrity": "sha512-y4UPwWhH6vChKRkGdMB4odasUbHOUwy7KL+OVwF86PvT6QVOwElx+TiI1/6kcmcEe+g5YRXJFiXSXUdabqZOvQ==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"discord-api-types": "^0.38.33"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.11.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@discordjs/rest": {
|
|
||||||
"version": "2.6.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.6.1.tgz",
|
|
||||||
"integrity": "sha512-wwQdgjeaoYFiaG+atbqx6aJDpqW7JHAo0HrQkBTbYzM3/PJ3GweQIpgElNcGZ26DCUOXMyawYd0YF7vtr+fZXg==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@discordjs/collection": "^2.1.1",
|
|
||||||
"@discordjs/util": "^1.2.0",
|
|
||||||
"@sapphire/async-queue": "^1.5.3",
|
|
||||||
"@sapphire/snowflake": "^3.5.5",
|
|
||||||
"@vladfrangu/async_event_emitter": "^2.4.6",
|
|
||||||
"discord-api-types": "^0.38.40",
|
|
||||||
"magic-bytes.js": "^1.13.0",
|
|
||||||
"tslib": "^2.6.3",
|
|
||||||
"undici": "6.24.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@discordjs/rest/node_modules/@sapphire/snowflake": {
|
|
||||||
"version": "3.5.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.5.tgz",
|
|
||||||
"integrity": "sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=v14.0.0",
|
|
||||||
"npm": ">=7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@discordjs/util": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.2.0.tgz",
|
|
||||||
"integrity": "sha512-3LKP7F2+atl9vJFhaBjn4nOaSWahZ/yWjOvA4e5pnXkt2qyXRCHLxoBQy81GFtLGCq7K9lPm9R517M1U+/90Qg==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"discord-api-types": "^0.38.33"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@discordjs/ws": {
|
|
||||||
"version": "1.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.2.3.tgz",
|
|
||||||
"integrity": "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@discordjs/collection": "^2.1.0",
|
|
||||||
"@discordjs/rest": "^2.5.1",
|
|
||||||
"@discordjs/util": "^1.1.0",
|
|
||||||
"@sapphire/async-queue": "^1.5.2",
|
|
||||||
"@types/ws": "^8.5.10",
|
|
||||||
"@vladfrangu/async_event_emitter": "^2.2.4",
|
|
||||||
"discord-api-types": "^0.38.1",
|
|
||||||
"tslib": "^2.6.2",
|
|
||||||
"ws": "^8.17.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.11.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@discordjs/ws/node_modules/@discordjs/collection": {
|
|
||||||
"version": "2.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz",
|
|
||||||
"integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.28.1",
|
"version": "0.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
|
||||||
@@ -899,39 +759,6 @@
|
|||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sapphire/async-queue": {
|
|
||||||
"version": "1.5.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz",
|
|
||||||
"integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=v14.0.0",
|
|
||||||
"npm": ">=7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sapphire/shapeshift": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-d9dUmWVA7MMiKobL3VpLF8P2aeanRTu6ypG2OIaEv/ZHH/SUQ2iHOVyi5wAPjQ+HmnMuL0whK9ez8I/raWbtIg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-deep-equal": "^3.1.3",
|
|
||||||
"lodash": "^4.17.21"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=v16"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sapphire/snowflake": {
|
|
||||||
"version": "3.5.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.3.tgz",
|
|
||||||
"integrity": "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=v14.0.0",
|
|
||||||
"npm": ">=7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@stylistic/eslint-plugin": {
|
"node_modules/@stylistic/eslint-plugin": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-4.2.0.tgz",
|
||||||
@@ -1031,6 +858,7 @@
|
|||||||
"version": "22.15.21",
|
"version": "22.15.21",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz",
|
||||||
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
|
"integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
}
|
}
|
||||||
@@ -1068,15 +896,6 @@
|
|||||||
"@types/send": "*"
|
"@types/send": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/ws": {
|
|
||||||
"version": "8.18.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
|
||||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||||
"version": "8.32.1",
|
"version": "8.32.1",
|
||||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
|
||||||
@@ -1284,16 +1103,6 @@
|
|||||||
"url": "https://opencollective.com/typescript-eslint"
|
"url": "https://opencollective.com/typescript-eslint"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@vladfrangu/async_event_emitter": {
|
|
||||||
"version": "2.4.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.7.tgz",
|
|
||||||
"integrity": "sha512-Xfe6rpCTxSxfbswi/W/Pz7zp1WWSNn4A0eW4mLkQUewCrXXtMj31lCg+iQyTkh/CkusZSq9eDflu7tjEDXUY6g==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=v14.0.0",
|
|
||||||
"npm": ">=7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/abbrev": {
|
"node_modules/abbrev": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz",
|
||||||
@@ -1394,6 +1203,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
|
||||||
},
|
},
|
||||||
|
"node_modules/athena-prime": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://npm.hep.gg/athena-prime/-/athena-prime-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-CbIPwdupVGfGRR/ZRcc9BAC/nBnRQS9sRalzWtONYQ4eNhCDJVykPLQM2N7abndvcpLctWd1cD3awpA9NDy30Q==",
|
||||||
|
"license": "NOLICENSE",
|
||||||
|
"dependencies": {
|
||||||
|
"discord-api-types": "^0.38.48",
|
||||||
|
"eventemitter3": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22.15.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"ws": "^8.21.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
@@ -1838,41 +1663,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/discord-api-types": {
|
"node_modules/discord-api-types": {
|
||||||
"version": "0.38.42",
|
"version": "0.38.53",
|
||||||
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.42.tgz",
|
"resolved": "https://npm.hep.gg/discord-api-types/-/discord-api-types-0.38.53.tgz",
|
||||||
"integrity": "sha512-qs1kya7S84r5RR8m9kgttywGrmmoHaRifU1askAoi+wkoSefLpZP6aGXusjNw5b0jD3zOg3LTwUa3Tf2iHIceQ==",
|
"integrity": "sha512-HL1zz/UuZ+bbJjA/X8Kbxx9gk8v9rJAbTeWRNYKmIdjwJ7EovjlHgoJTxcLpATfNJ+AonOtMdy3Y5MVIJAAd/A==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"scripts/actions/documentation"
|
"scripts/actions/documentation"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"node_modules/discord.js": {
|
|
||||||
"version": "14.26.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.26.4.tgz",
|
|
||||||
"integrity": "sha512-4oBp8tc6Kf8IDBwAHhbsMaAqx1b5fob9SNasZT7V6yyyUydoO5i5fGuX7TmvRtR+q/WgKRnRViRoAWnG7fNyvA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@discordjs/builders": "^1.14.1",
|
|
||||||
"@discordjs/collection": "1.5.3",
|
|
||||||
"@discordjs/formatters": "^0.6.2",
|
|
||||||
"@discordjs/rest": "^2.6.1",
|
|
||||||
"@discordjs/util": "^1.2.0",
|
|
||||||
"@discordjs/ws": "^1.2.3",
|
|
||||||
"@sapphire/snowflake": "3.5.3",
|
|
||||||
"discord-api-types": "^0.38.40",
|
|
||||||
"fast-deep-equal": "3.1.3",
|
|
||||||
"lodash.snakecase": "4.1.1",
|
|
||||||
"magic-bytes.js": "^1.13.0",
|
|
||||||
"tslib": "^2.6.3",
|
|
||||||
"undici": "6.24.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/discordjs/discord.js?sponsor"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/dotenv": {
|
"node_modules/dotenv": {
|
||||||
"version": "16.5.0",
|
"version": "16.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
|
||||||
@@ -2203,6 +2001,12 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/eventemitter3": {
|
||||||
|
"version": "5.0.4",
|
||||||
|
"resolved": "https://npm.hep.gg/eventemitter3/-/eventemitter3-5.0.4.tgz",
|
||||||
|
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/expand-template": {
|
"node_modules/expand-template": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||||
@@ -2303,7 +2107,8 @@
|
|||||||
"node_modules/fast-deep-equal": {
|
"node_modules/fast-deep-equal": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/fast-glob": {
|
"node_modules/fast-glob": {
|
||||||
"version": "3.3.3",
|
"version": "3.3.3",
|
||||||
@@ -2923,29 +2728,12 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
|
||||||
"version": "4.18.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
|
||||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/lodash.merge": {
|
"node_modules/lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/lodash.snakecase": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw=="
|
|
||||||
},
|
|
||||||
"node_modules/magic-bytes.js": {
|
|
||||||
"version": "1.13.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.13.0.tgz",
|
|
||||||
"integrity": "sha512-afO2mnxW7GDTXMm5/AoN1WuOcdoKhtgXjIvHmobqTD1grNplhGdv3PFOyjCVmrnOZBIT/gD/koDKpYG+0mvHcg==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/markdown-it": {
|
"node_modules/markdown-it": {
|
||||||
"version": "14.2.0",
|
"version": "14.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.2.0.tgz",
|
||||||
@@ -4408,18 +4196,6 @@
|
|||||||
"typescript": ">=4.8.4"
|
"typescript": ">=4.8.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-mixer": {
|
|
||||||
"version": "6.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.4.tgz",
|
|
||||||
"integrity": "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/tslib": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
|
||||||
"license": "0BSD"
|
|
||||||
},
|
|
||||||
"node_modules/tsx": {
|
"node_modules/tsx": {
|
||||||
"version": "4.22.4",
|
"version": "4.22.4",
|
||||||
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz",
|
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz",
|
||||||
@@ -4527,19 +4303,11 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici": {
|
|
||||||
"version": "6.24.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz",
|
|
||||||
"integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@@ -4704,6 +4472,7 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
|
||||||
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
|
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
|
|||||||
+3
-1
@@ -11,11 +11,12 @@
|
|||||||
"typescript": "5.8.3",
|
"typescript": "5.8.3",
|
||||||
"typescript-eslint": "8.32.1"
|
"typescript-eslint": "8.32.1"
|
||||||
},
|
},
|
||||||
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clynamic/dmark": "0.0.11",
|
"@clynamic/dmark": "0.0.11",
|
||||||
"@redis/client": "5.1.0",
|
"@redis/client": "5.1.0",
|
||||||
|
"athena-prime": "4.2.0",
|
||||||
"body-parser": "2.2.2",
|
"body-parser": "2.2.2",
|
||||||
"discord.js": "14.26.4",
|
|
||||||
"dotenv": "16.5.0",
|
"dotenv": "16.5.0",
|
||||||
"express": "5.1.0",
|
"express": "5.1.0",
|
||||||
"express-session": "1.19.0",
|
"express-session": "1.19.0",
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
"sqlite": "5.1.1",
|
"sqlite": "5.1.1",
|
||||||
"sqlite3": "6.0.1"
|
"sqlite3": "6.0.1"
|
||||||
},
|
},
|
||||||
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "tsx watch --enable-source-maps src/index.ts",
|
"dev": "tsx watch --enable-source-maps src/index.ts",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js",
|
||||||
|
|||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient, Constants, LRUCollection, Member, User } from "athena-prime";
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import config from "./config";
|
||||||
|
import events from "./events";
|
||||||
|
import commands from "./commands";
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
const client = new CommandClient({
|
||||||
|
token: `Bot ${config.DISCORD_TOKEN}`,
|
||||||
|
|
||||||
|
options: {
|
||||||
|
intents: [
|
||||||
|
Constants.GatewayIntentBits.Guilds,
|
||||||
|
Constants.GatewayIntentBits.GuildMembers,
|
||||||
|
Constants.GatewayIntentBits.GuildMessages,
|
||||||
|
Constants.GatewayIntentBits.GuildModeration,
|
||||||
|
Constants.GatewayIntentBits.GuildVoiceStates,
|
||||||
|
Constants.GatewayIntentBits.MessageContent,
|
||||||
|
],
|
||||||
|
|
||||||
|
largeBotOptimizations: true,
|
||||||
|
|
||||||
|
disableEvents: {
|
||||||
|
PRESENCE_UPDATE: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
// TODO: Move caching to Redis.
|
||||||
|
cache: {
|
||||||
|
users: () => new LRUCollection(User, 1024),
|
||||||
|
members: (guild) => new LRUCollection(Member, 1024),
|
||||||
|
|
||||||
|
guildEmojis: false,
|
||||||
|
guildStickers: false,
|
||||||
|
transientMembers: false,
|
||||||
|
userSweepIntervalMs: 3_600_000 // 1hr.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
commands.forEach(command => client.registerCommand(command));
|
||||||
|
events.forEach(event => client.registerEvent(event));
|
||||||
|
|
||||||
|
export default client;
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// External Imports
|
||||||
|
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from '../../shared/Database';
|
||||||
|
import { CreateDefaultEmbed, SetSeverity } from '../../utils';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
@SlashCommand(
|
||||||
|
new CommandBuilder('database', "Essential database functions. THESE ARE POTENTIALLY DESTRUCTIVE ACTIONS, BE SURE!")
|
||||||
|
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
|
||||||
|
.setContexts(Constants.InteractionContextType.Guild)
|
||||||
|
.setCommandType(Constants.ApplicationCommandType.ChatInput)
|
||||||
|
.setMemberPermission(Constants.PermissionFlagsBits.Administrator)
|
||||||
|
|
||||||
|
.addSubcommand('purge-user', 'Purge user messages from the database.', builder =>
|
||||||
|
builder.addUserOption('user', 'The user to purge.', true)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
class DatabaseCommand extends Command<CommandClient> {
|
||||||
|
cooldown = 60;
|
||||||
|
userPermissions = [Constants.PermissionFlagsBits.Administrator];
|
||||||
|
|
||||||
|
async handleCommand(context: CommandClient<any, any>, interaction: CommandInteraction, data: any) {
|
||||||
|
if (!interaction.inGuild() || !interaction.subcommand) return;
|
||||||
|
|
||||||
|
await interaction.defer();
|
||||||
|
|
||||||
|
switch (interaction.subcommand.at(0)) {
|
||||||
|
case 'purge-user':
|
||||||
|
const user = interaction.getRequiredMember('user');
|
||||||
|
await Database.purgeMessagesFrom(user.id);
|
||||||
|
|
||||||
|
await interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
...SetSeverity('success'),
|
||||||
|
|
||||||
|
description: `Executed user-purge on ${user.mention}.`
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new DatabaseCommand('database');
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// External Imports
|
||||||
|
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from '../../shared/Database';
|
||||||
|
import { CreateDefaultEmbed, SetSeverity } from '../../utils';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
@SlashCommand(
|
||||||
|
new CommandBuilder('rename-general', 'Rename the general channel.')
|
||||||
|
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
|
||||||
|
.setContexts(Constants.InteractionContextType.Guild)
|
||||||
|
.setCommandType(Constants.ApplicationCommandType.ChatInput)
|
||||||
|
.setMemberPermission(Constants.PermissionFlagsBits.ManageChannels)
|
||||||
|
.addStringOption({ name: 'name', description: 'The new general chat name.', required: true, max_length: 64 })
|
||||||
|
)
|
||||||
|
class RenameGeneralCommand extends Command<CommandClient> {
|
||||||
|
cooldown = 360;
|
||||||
|
userPermissions = [Constants.PermissionFlagsBits.ManageChannels];
|
||||||
|
|
||||||
|
async handleCommand(context: CommandClient<any, any>, interaction: CommandInteraction, data: any) {
|
||||||
|
if (!interaction.inGuild()) return;
|
||||||
|
await interaction.defer();
|
||||||
|
|
||||||
|
const settings = await Database.GetOrCreateSettings(interaction.guild.id);
|
||||||
|
if (!settings.general_chat_id) {
|
||||||
|
await interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
...SetSeverity('warning'),
|
||||||
|
|
||||||
|
description: 'No general channel has been configured. Please check your configuration and try again.'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const channel = context.getChannel(settings.general_chat_id);
|
||||||
|
if (!channel || !channel.isTextBased()) {
|
||||||
|
await interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
...SetSeverity('error'),
|
||||||
|
|
||||||
|
description: 'There was an issue trying to locate the configured general channel role. Please check your configuration and try again.'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = interaction.getRequiredString('name');
|
||||||
|
await context.editChannel(settings.general_chat_id, { name: name });
|
||||||
|
|
||||||
|
await interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
...SetSeverity('success'),
|
||||||
|
|
||||||
|
description: `Successfully updated ${channel.mention}`
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new RenameGeneralCommand('rename-general');
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, Guild, GuildMember, InteractionContextType, PermissionFlagsBits, SlashCommandBuilder, time, TimestampStyles, User } from 'discord.js';
|
|
||||||
import { Database } from '../shared/Database';
|
|
||||||
import { AltData, comprehensiveAltLookupFromDiscord, deferInteraction, getIdFromInput } from '../utils';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'ban',
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName('ban')
|
|
||||||
.setDescription('Bans a user.')
|
|
||||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
|
||||||
.setContexts(InteractionContextType.Guild)
|
|
||||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
|
||||||
.addStringOption(option =>
|
|
||||||
option
|
|
||||||
.setName('user')
|
|
||||||
.setDescription('The discord user mention, or ID, to ban.')
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
.addStringOption(option =>
|
|
||||||
option
|
|
||||||
.setName('reason')
|
|
||||||
.setDescription('The reason for the ban')
|
|
||||||
.setRequired(false)
|
|
||||||
.setMaxLength(400)
|
|
||||||
)
|
|
||||||
.addNumberOption(option =>
|
|
||||||
option
|
|
||||||
.setName('hours')
|
|
||||||
.setDescription('The duration of the ban, added with other options (0 for permanent).')
|
|
||||||
.setRequired(false)
|
|
||||||
)
|
|
||||||
.addNumberOption(option =>
|
|
||||||
option
|
|
||||||
.setName('minutes')
|
|
||||||
.setDescription('The duration of the ban, added with other options (0 for permanent).')
|
|
||||||
.setRequired(false)
|
|
||||||
)
|
|
||||||
.addNumberOption(option =>
|
|
||||||
option
|
|
||||||
.setName('seconds')
|
|
||||||
.setDescription('The duration of the ban, added with other options (0 for permanent).')
|
|
||||||
.setRequired(false)
|
|
||||||
)
|
|
||||||
.addNumberOption(option =>
|
|
||||||
option
|
|
||||||
.setName('delete-message-days')
|
|
||||||
.setDescription('How far back to delete messages (in days, default: 0 days).')
|
|
||||||
.setRequired(false)
|
|
||||||
.setMinValue(0)
|
|
||||||
.setMaxValue(7)
|
|
||||||
)
|
|
||||||
.addBooleanOption(option =>
|
|
||||||
option
|
|
||||||
.setName('full-ban')
|
|
||||||
.setDescription('Whether or not to prevent the user from joining on known alts (and ban all existing alts).')
|
|
||||||
.setRequired(false)
|
|
||||||
),
|
|
||||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
|
||||||
await deferInteraction(interaction);
|
|
||||||
|
|
||||||
if (!interaction.guild) return interaction.editReply('This command must be used in a server');
|
|
||||||
|
|
||||||
if (!interaction.guild.members.me) return interaction.editReply('An error has occurred. Please try again later.');
|
|
||||||
|
|
||||||
const input = interaction.options.getString('user', true);
|
|
||||||
|
|
||||||
const idToUse = getIdFromInput(input);
|
|
||||||
const reason = interaction.options.getString('reason') ?? '';
|
|
||||||
|
|
||||||
const hours = (interaction.options.getNumber('hours') ?? 0) * 3.6e+6;
|
|
||||||
const minutes = (interaction.options.getNumber('minutes') ?? 0) * 60000;
|
|
||||||
const seconds = (interaction.options.getNumber('seconds') ?? 0) * 1000;
|
|
||||||
|
|
||||||
const duration = hours + minutes + seconds;
|
|
||||||
|
|
||||||
const deleteMessageDays = (interaction.options.getNumber('delete-message-days') ?? 0) * 86400;
|
|
||||||
|
|
||||||
const fullBan = interaction.options.getBoolean('full-ban') ?? false;
|
|
||||||
|
|
||||||
let banMember: GuildMember | null = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
banMember = await interaction.guild.members.fetch(idToUse);
|
|
||||||
} catch (e) {
|
|
||||||
// Member not in server.
|
|
||||||
}
|
|
||||||
|
|
||||||
const member = await interaction.guild.members.fetch(interaction.user.id);
|
|
||||||
|
|
||||||
if (banMember && member.roles.highest.comparePositionTo(banMember.roles.highest) <= 0) {
|
|
||||||
return await interaction.editReply('You do not have permission to ban this user.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (banMember && !banMember.bannable) {
|
|
||||||
return await interaction.editReply('I do not have permission to ban this user.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const expiresAt = new Date(Date.now() + duration);
|
|
||||||
|
|
||||||
await Database.putBan(idToUse, duration > 0 ? expiresAt : null, fullBan);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await interaction.guild.bans.create(idToUse, {
|
|
||||||
reason: (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${interaction.user.username} (${interaction.user.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim(),
|
|
||||||
deleteMessageSeconds: deleteMessageDays
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
return await interaction.editReply("Error banning user (couldn't ban).");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fullBan) {
|
|
||||||
const alts = await comprehensiveAltLookupFromDiscord(idToUse, interaction.guild);
|
|
||||||
|
|
||||||
await removeAllAlts([alts], interaction.guild, interaction.user, fullBan, reason, deleteMessageDays, duration, expiresAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
await interaction.editReply(`<@${idToUse}> (${idToUse}) has been ${fullBan ? 'full banned' : 'banned'}.`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
async function removeAllAlts(altData: AltData[], guild: Guild, moderator: User, fullBan: boolean, reason: string, deleteMessageDays: number, duration: number, expiresAt: Date) {
|
|
||||||
for (const data of altData) {
|
|
||||||
if (data.type == 'discord') {
|
|
||||||
try {
|
|
||||||
if (!data.banned) {
|
|
||||||
await guild.members.kick(data.thisId as string, (reason + ` ${fullBan ? 'Full banned' : 'Banned'} by ${moderator.username} (${moderator.id})${duration > 0 ? `. Expires at: ${time(expiresAt, TimestampStyles.ShortDateTime)}` : ''}`).trim());
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await removeAllAlts(data.alts, guild, moderator, fullBan, reason, deleteMessageDays, duration, expiresAt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+54
-39
@@ -1,48 +1,63 @@
|
|||||||
import { ApplicationIntegrationType, AutocompleteInteraction, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
// External Imports
|
||||||
|
import { AutocompleteInteraction, Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
import { Database } from '../shared/Database';
|
import { Database } from '../shared/Database';
|
||||||
import { KnowledgebaseItem } from '../types';
|
import { CreateDefaultEmbed, SetSeverity } from '../utils';
|
||||||
|
|
||||||
export default {
|
// ---------------
|
||||||
name: 'cite',
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName('cite')
|
|
||||||
.setDescription('Cite content from the knowledgebase.')
|
|
||||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
|
||||||
.setContexts(InteractionContextType.Guild)
|
|
||||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageMessages)
|
|
||||||
.addIntegerOption(option =>
|
|
||||||
option
|
|
||||||
.setName('name')
|
|
||||||
.setDescription('The name of the entry.')
|
|
||||||
.setRequired(true)
|
|
||||||
.setAutocomplete(true)
|
|
||||||
),
|
|
||||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
|
||||||
if (!interaction.guild) return interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Must be ran in guild.' });
|
|
||||||
|
|
||||||
await interaction.deferReply();
|
@SlashCommand(
|
||||||
|
new CommandBuilder('cite', 'Cite a knowledgebase article.')
|
||||||
|
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
|
||||||
|
.setContexts(Constants.InteractionContextType.Guild)
|
||||||
|
.setCommandType(Constants.ApplicationCommandType.ChatInput)
|
||||||
|
.addIntegerOption({ name: 'article', description: 'The article to cite.', required: true, autocomplete: true })
|
||||||
|
)
|
||||||
|
class CiteCommand extends Command<CommandClient> {
|
||||||
|
cooldown = 5; // 5s.
|
||||||
|
|
||||||
const id = interaction.options.getInteger('name', true);
|
async handleCommand(context: CommandClient<any, any>, interaction: CommandInteraction, data: any) {
|
||||||
|
if (!interaction.inGuild()) return;
|
||||||
|
await interaction.defer();
|
||||||
|
|
||||||
const item = await Database.getFromKnowledgebase(id);
|
const article = interaction.getRequiredInteger('article');
|
||||||
|
const item = await Database.getFromKnowledgebase(article);
|
||||||
|
|
||||||
if (!item) return interaction.editReply('Knowledgebase item not found.');
|
if (!item) {
|
||||||
|
await interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
...SetSeverity('error'),
|
||||||
|
|
||||||
return interaction.editReply(item.content);
|
description: 'There was an issue locating the requested knowledgebase article.'
|
||||||
},
|
}]
|
||||||
autoComplete: async function (client: Client, interaction: AutocompleteInteraction) {
|
});
|
||||||
if (!interaction.guild) return interaction.respond([]);
|
|
||||||
|
|
||||||
const items: KnowledgebaseItem[] = await Database.getAllKnowledgebaseItems(interaction.guild.id);
|
return;
|
||||||
|
|
||||||
const value = interaction.options.getFocused();
|
|
||||||
|
|
||||||
const toRespond = items.filter(i => !value ? true : i.content.includes(value));
|
|
||||||
if (toRespond.length > 25) toRespond.length = 25;
|
|
||||||
|
|
||||||
interaction.respond(toRespond.map(p => ({
|
|
||||||
name: p.name,
|
|
||||||
value: p.id
|
|
||||||
})));
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
await interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
|
||||||
|
description: item.content
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleAutocomplete(context: CommandClient<any, any>, interaction: AutocompleteInteraction, data: any) {
|
||||||
|
if (!interaction.guild) return await interaction.acknowledge([]);
|
||||||
|
const items = await Database.getAllKnowledgebaseItems(interaction.guild.id);
|
||||||
|
|
||||||
|
const value = interaction.focused();
|
||||||
|
if(!value) return await interaction.acknowledge([]);
|
||||||
|
|
||||||
|
const response = items.filter(item => item.content.includes(value.value as string));
|
||||||
|
if (response.length > 25) response.length = 25;
|
||||||
|
|
||||||
|
return await interaction.acknowledge(response.map(res => ({ name: res.name, value: res.id })));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new CiteCommand('cite');
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, PermissionFlagsBits, SlashCommandBuilder } from 'discord.js';
|
|
||||||
import { Database } from '../shared/Database';
|
|
||||||
import { getIdFromInput } from '../utils';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'database',
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName('database')
|
|
||||||
.setDescription('Database access.')
|
|
||||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
|
||||||
.setContexts(InteractionContextType.Guild)
|
|
||||||
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
|
|
||||||
.addSubcommandGroup(subcommandGroup =>
|
|
||||||
subcommandGroup
|
|
||||||
.setName('messages')
|
|
||||||
.setDescription('Access to messages in the database.')
|
|
||||||
.addSubcommand(subcommand =>
|
|
||||||
subcommand
|
|
||||||
.setName('purge')
|
|
||||||
.setDescription("Purge a user's messages from the database.")
|
|
||||||
.addStringOption(option =>
|
|
||||||
option
|
|
||||||
.setName('user')
|
|
||||||
.setDescription('The discord user id, or mention, of the user.')
|
|
||||||
.setRequired(true)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
|
||||||
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
|
|
||||||
|
|
||||||
const group = interaction.options.getSubcommandGroup(true);
|
|
||||||
|
|
||||||
const subcommand = interaction.options.getSubcommand(true);
|
|
||||||
|
|
||||||
if (group == 'messages') {
|
|
||||||
if (subcommand == 'purge') {
|
|
||||||
const discordUserInput = interaction.options.getString('discord-user', true);
|
|
||||||
|
|
||||||
const idToUse = getIdFromInput(discordUserInput);
|
|
||||||
|
|
||||||
await Database.purgeMessagesFrom(idToUse);
|
|
||||||
|
|
||||||
return interaction.editReply('Messages purged.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import database from "./admin/database";
|
||||||
|
import renameChannel from "./admin/rename-channel";
|
||||||
|
|
||||||
|
import ban from "./moderation/ban";
|
||||||
|
import hardban from "./moderation/hardban";
|
||||||
|
|
||||||
|
import about from "./utilities/about";
|
||||||
|
import nameSync from "./utilities/name-sync";
|
||||||
|
|
||||||
|
export default [
|
||||||
|
// Admin
|
||||||
|
database,
|
||||||
|
renameChannel,
|
||||||
|
|
||||||
|
// Moderation
|
||||||
|
ban,
|
||||||
|
hardban,
|
||||||
|
|
||||||
|
// Utilities
|
||||||
|
about,
|
||||||
|
nameSync
|
||||||
|
];
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
// External Imports
|
||||||
|
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from '../../shared/Database';
|
||||||
|
import { CreateDefaultEmbed, SetSeverity } from '../../utils';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
@SlashCommand(
|
||||||
|
new CommandBuilder('ban', 'Ban a user, optionally cleanup 1 day worth of messages.')
|
||||||
|
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
|
||||||
|
.setContexts(Constants.InteractionContextType.Guild)
|
||||||
|
.setCommandType(Constants.ApplicationCommandType.ChatInput)
|
||||||
|
.setMemberPermission(Constants.PermissionFlagsBits.BanMembers)
|
||||||
|
.addUserOption('user', 'The user to ban.', true)
|
||||||
|
.addStringOption({ name: 'duration', description: '(optional): The duration of the ban. Formatted as 7d12h30m30s.', required: false })
|
||||||
|
.addStringOption({ name: 'reason', description: '(optional): The reason for banning the user.', required: false })
|
||||||
|
.addBooleanOption('clean', "(optional): Clean the user's messages upwards of a day.", false)
|
||||||
|
.addBooleanOption('purge', "(optional): Purge the user's messages from the database.", false)
|
||||||
|
)
|
||||||
|
class BanCommand extends Command<CommandClient> {
|
||||||
|
cooldown = 5;
|
||||||
|
userPermissions = [Constants.PermissionFlagsBits.BanMembers];
|
||||||
|
|
||||||
|
async handleCommand(context: CommandClient<any, any>, interaction: CommandInteraction, data: any) {
|
||||||
|
if (!interaction.inGuild()) return;
|
||||||
|
await interaction.defer();
|
||||||
|
|
||||||
|
const user = interaction.getRequiredMember('user');
|
||||||
|
const duration = interaction.getString('duration');
|
||||||
|
const reason = interaction.getString('reason') || 'No reason specified.';
|
||||||
|
const clean = interaction.getBoolean('clean') || false;
|
||||||
|
const purge = interaction.getBoolean('purge') || false;
|
||||||
|
|
||||||
|
// Nix -> Tarrgon: Could you pretty please hook up the database for this 🥺
|
||||||
|
|
||||||
|
await context.banGuildMember(interaction.guild.id, user.id, clean ? 1 : undefined, reason);
|
||||||
|
if (purge) await Database.purgeMessagesFrom(user.id);
|
||||||
|
|
||||||
|
interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
...SetSeverity('success'),
|
||||||
|
|
||||||
|
description: 'Executed ban action.',
|
||||||
|
|
||||||
|
fields: [
|
||||||
|
{ name: 'User', value: `${user.username} (${user.mention})`, inline: false },
|
||||||
|
{ name: 'Duration', value: `${duration || 'Permanent'}`, inline: false },
|
||||||
|
{ name: 'Reason', value: `${user.username} (${user.mention})`, inline: false },
|
||||||
|
{ name: 'Clean', value: `${clean ? 'Yes' : 'No'}`, inline: true },
|
||||||
|
{ name: 'Purge', value: `${purge ? 'Yes' : 'No'}`, inline: true },
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new BanCommand('ban');
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// External Imports
|
||||||
|
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
@SlashCommand(
|
||||||
|
new CommandBuilder('hard-ban', 'Permanently bans a user, and all known alternate accounts.')
|
||||||
|
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
|
||||||
|
.setContexts(Constants.InteractionContextType.Guild)
|
||||||
|
.setCommandType(Constants.ApplicationCommandType.ChatInput)
|
||||||
|
.setMemberPermission(Constants.PermissionFlagsBits.BanMembers)
|
||||||
|
.addUserOption('user', 'The user to ban.', true)
|
||||||
|
.addStringOption({ name: 'reason', description: '(optional): The reason for banning the user.', required: false })
|
||||||
|
.addBooleanOption('clean', "(optional): Clean the user's messages upwards of a day.", false)
|
||||||
|
.addBooleanOption('purge', "(optional): Purge the user's messages from the database.", false)
|
||||||
|
)
|
||||||
|
class HardBanCommand extends Command<CommandClient> {
|
||||||
|
cooldown = 5;
|
||||||
|
userPermissions = [Constants.PermissionFlagsBits.BanMembers];
|
||||||
|
|
||||||
|
async handleCommand(context: CommandClient<any, any>, interaction: CommandInteraction, fetchedData?: any) {
|
||||||
|
if (!interaction.inGuild()) return;
|
||||||
|
await interaction.defer();
|
||||||
|
|
||||||
|
const user = interaction.getRequiredMember('user');
|
||||||
|
const reason = interaction.getString('reason') || 'No reason specified.';
|
||||||
|
const clean = interaction.getBoolean('clean') || false;
|
||||||
|
const purge = interaction.getBoolean('purge') || false;
|
||||||
|
|
||||||
|
// Nix -> Tarrgon: Please implement the hard-ban here :3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new HardBanCommand('hardban');
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, MessageFlags, SlashCommandBuilder } from 'discord.js';
|
|
||||||
import { config } from '../config';
|
|
||||||
import { syncName } from '../utils';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'name-sync',
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName('name-sync')
|
|
||||||
.setDescription('Sync your discord nickname to your e621 name.')
|
|
||||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall, ApplicationIntegrationType.UserInstall)
|
|
||||||
.setContexts(InteractionContextType.Guild, InteractionContextType.BotDM)
|
|
||||||
.addIntegerOption(option =>
|
|
||||||
option
|
|
||||||
.setName('id')
|
|
||||||
.setDescription('The id of the e621 user to sync your nickname to.')
|
|
||||||
.setRequired(false)
|
|
||||||
),
|
|
||||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
|
||||||
await interaction.deferReply({ flags: [MessageFlags.Ephemeral] });
|
|
||||||
const id = interaction.options.getInteger('id');
|
|
||||||
|
|
||||||
const guild = await interaction.client.guilds.fetch(config.DISCORD_GUILD_ID!);
|
|
||||||
|
|
||||||
if (!guild) {
|
|
||||||
return interaction.editReply('An error has occurred. Please try again later.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const member = await guild.members.fetch(interaction.user.id);
|
|
||||||
|
|
||||||
if (!member || !guild.members.me) {
|
|
||||||
return interaction.editReply('An error has occurred. Please try again later.');
|
|
||||||
}
|
|
||||||
|
|
||||||
await syncName(interaction, member, id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -39,7 +39,7 @@ export default {
|
|||||||
.addComponents(button);
|
.addComponents(button);
|
||||||
|
|
||||||
await interaction.channel.send({ components: [row], content });
|
await interaction.channel.send({ components: [row], content });
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'private_help_channel_id', interaction.channelId);
|
await Database.updateSettings(interaction.guildId, 'private_help_channel_id', interaction.channelId);
|
||||||
interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Sent.' });
|
interaction.reply({ flags: [MessageFlags.Ephemeral], content: 'Sent.' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
import { ApplicationIntegrationType, ChatInputCommandInteraction, Client, InteractionContextType, PermissionFlagsBits, RateLimitError, SlashCommandBuilder } from 'discord.js';
|
|
||||||
import { msToHuman } from '../utils';
|
|
||||||
import { Database } from '../shared/Database';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'rename',
|
|
||||||
data: new SlashCommandBuilder()
|
|
||||||
.setName('rename')
|
|
||||||
.setDescription('Rename the general channel.')
|
|
||||||
.setIntegrationTypes(ApplicationIntegrationType.GuildInstall)
|
|
||||||
.setContexts(InteractionContextType.Guild)
|
|
||||||
.setDefaultMemberPermissions(PermissionFlagsBits.BanMembers)
|
|
||||||
.addStringOption(option =>
|
|
||||||
option
|
|
||||||
.setName('new-name')
|
|
||||||
.setDescription('The new name of the general channel.')
|
|
||||||
.setRequired(true)
|
|
||||||
),
|
|
||||||
handler: async function (client: Client, interaction: ChatInputCommandInteraction) {
|
|
||||||
const guildSettings = await Database.getGuildSettings(interaction.guildId!);
|
|
||||||
|
|
||||||
if (!guildSettings || !guildSettings.general_chat_id) {
|
|
||||||
return interaction.reply('No general chat id found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = interaction.options.getString('new-name', true);
|
|
||||||
|
|
||||||
if (name.length > 100) {
|
|
||||||
return interaction.reply('Name must be less than 100 characters in length.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const channel = await interaction.guild!.channels.fetch(guildSettings.general_chat_id)!;
|
|
||||||
|
|
||||||
if (!channel) {
|
|
||||||
return interaction.reply('No general chat id found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await channel.setName(name);
|
|
||||||
|
|
||||||
interaction.reply(`Renamed general to ${channel.name}`);
|
|
||||||
} catch (e: any) {
|
|
||||||
if (e instanceof RateLimitError) {
|
|
||||||
return interaction.reply(`Name change limited. Try again in ${msToHuman(e.retryAfter)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error(e);
|
|
||||||
return interaction.reply('An error has occurred.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
+14
-14
@@ -139,75 +139,75 @@ export default {
|
|||||||
|
|
||||||
const generalChannel = interaction.options.getChannel('general-channel');
|
const generalChannel = interaction.options.getChannel('general-channel');
|
||||||
if (generalChannel) {
|
if (generalChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'general_chat_id', generalChannel.id);
|
await Database.updateSettings(interaction.guildId, 'general_chat_id', generalChannel.id);
|
||||||
response.push(`**general_chat_id** has been set to: ${generalChannel}.`);
|
response.push(`**general_chat_id** has been set to: ${generalChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ticketsChannel = interaction.options.getChannel('tickets-channel');
|
const ticketsChannel = interaction.options.getChannel('tickets-channel');
|
||||||
if (ticketsChannel) {
|
if (ticketsChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'tickets_channel_id', ticketsChannel.id);
|
await Database.updateSettings(interaction.guildId, 'tickets_channel_id', ticketsChannel.id);
|
||||||
response.push(`**tickets_channel_id** has been set to: ${ticketsChannel}.`);
|
response.push(`**tickets_channel_id** has been set to: ${ticketsChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const appealsChannel = interaction.options.getChannel('appeals-channel');
|
const appealsChannel = interaction.options.getChannel('appeals-channel');
|
||||||
|
|
||||||
if (appealsChannel) {
|
if (appealsChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'appeals_channel_id', appealsChannel.id);
|
await Database.updateSettings(interaction.guildId, 'appeals_channel_id', appealsChannel.id);
|
||||||
|
|
||||||
response.push(`**appeals_channel_id** has been set to ${appealsChannel}.`);
|
response.push(`**appeals_channel_id** has been set to ${appealsChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const eventLogsChannel = interaction.options.getChannel('event-logs-channel');
|
const eventLogsChannel = interaction.options.getChannel('event-logs-channel');
|
||||||
if (eventLogsChannel) {
|
if (eventLogsChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'event_logs_channel_id', eventLogsChannel.id);
|
await Database.updateSettings(interaction.guildId, 'event_logs_channel_id', eventLogsChannel.id);
|
||||||
response.push(`**event_logs_channel_id** has been set to: ${eventLogsChannel}.`);
|
response.push(`**event_logs_channel_id** has been set to: ${eventLogsChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const discordLogsChannel = interaction.options.getChannel('discord-logs-channel');
|
const discordLogsChannel = interaction.options.getChannel('discord-logs-channel');
|
||||||
if (discordLogsChannel) {
|
if (discordLogsChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'discord_logs_channel_id', discordLogsChannel.id);
|
await Database.updateSettings(interaction.guildId, 'discord_logs_channel_id', discordLogsChannel.id);
|
||||||
response.push(`**discord_logs_channel_id** has been set to: ${discordLogsChannel}.`);
|
response.push(`**discord_logs_channel_id** has been set to: ${discordLogsChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auditLogsChannel = interaction.options.getChannel('audit-logs-channel');
|
const auditLogsChannel = interaction.options.getChannel('audit-logs-channel');
|
||||||
if (auditLogsChannel) {
|
if (auditLogsChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'audit_logs_channel_id', auditLogsChannel.id);
|
await Database.updateSettings(interaction.guildId, 'audit_logs_channel_id', auditLogsChannel.id);
|
||||||
response.push(`**audit_logs_channel_id** has been set to: ${auditLogsChannel}.`);
|
response.push(`**audit_logs_channel_id** has been set to: ${auditLogsChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const voiceLogsChannel = interaction.options.getChannel('voice-logs-channel');
|
const voiceLogsChannel = interaction.options.getChannel('voice-logs-channel');
|
||||||
if (voiceLogsChannel) {
|
if (voiceLogsChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'voice_logs_channel_id', voiceLogsChannel.id);
|
await Database.updateSettings(interaction.guildId, 'voice_logs_channel_id', voiceLogsChannel.id);
|
||||||
response.push(`**voice_logs_channel_id** has been set to: ${voiceLogsChannel}.`);
|
response.push(`**voice_logs_channel_id** has been set to: ${voiceLogsChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const newMemberLogsChannel = interaction.options.getChannel('new-member-channel');
|
const newMemberLogsChannel = interaction.options.getChannel('new-member-channel');
|
||||||
if (newMemberLogsChannel) {
|
if (newMemberLogsChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'new_member_channel_id', newMemberLogsChannel.id);
|
await Database.updateSettings(interaction.guildId, 'new_member_channel_id', newMemberLogsChannel.id);
|
||||||
response.push(`**new_member_channel_id** has been set to: ${newMemberLogsChannel}.`);
|
response.push(`**new_member_channel_id** has been set to: ${newMemberLogsChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const moderatorChannel = interaction.options.getChannel('moderator-channel');
|
const moderatorChannel = interaction.options.getChannel('moderator-channel');
|
||||||
if (moderatorChannel) {
|
if (moderatorChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'moderator_channel_id', moderatorChannel.id);
|
await Database.updateSettings(interaction.guildId, 'moderator_channel_id', moderatorChannel.id);
|
||||||
response.push(`**moderator_channel_id** has been set to": ${moderatorChannel}.`);
|
response.push(`**moderator_channel_id** has been set to": ${moderatorChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const adminRole = interaction.options.getRole('admin-role');
|
const adminRole = interaction.options.getRole('admin-role');
|
||||||
if (adminRole) {
|
if (adminRole) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'admin_role_id', adminRole.id);
|
await Database.updateSettings(interaction.guildId, 'admin_role_id', adminRole.id);
|
||||||
response.push(`**admin_role_id** has been set to: ${adminRole}.`);
|
response.push(`**admin_role_id** has been set to: ${adminRole}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const privateHelperRole = interaction.options.getRole('private-helper-role');
|
const privateHelperRole = interaction.options.getRole('private-helper-role');
|
||||||
if (privateHelperRole) {
|
if (privateHelperRole) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'private_help_role_id', privateHelperRole.id);
|
await Database.updateSettings(interaction.guildId, 'private_help_role_id', privateHelperRole.id);
|
||||||
response.push(`**private_help_role_id** has been set to: ${privateHelperRole}.`);
|
response.push(`**private_help_role_id** has been set to: ${privateHelperRole}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const devWatchRole = interaction.options.getRole('devwatch-role');
|
const devWatchRole = interaction.options.getRole('devwatch-role');
|
||||||
if (devWatchRole) {
|
if (devWatchRole) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'devwatch_role_id', devWatchRole.id);
|
await Database.updateSettings(interaction.guildId, 'devwatch_role_id', devWatchRole.id);
|
||||||
response.push(`**devwatch_role_id** has been set to ${devWatchRole}.`);
|
response.push(`**devwatch_role_id** has been set to ${devWatchRole}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,13 +264,13 @@ export default {
|
|||||||
|
|
||||||
const githubReleaseChannel = interaction.options.getChannel('github-release-channel');
|
const githubReleaseChannel = interaction.options.getChannel('github-release-channel');
|
||||||
if (githubReleaseChannel) {
|
if (githubReleaseChannel) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'github_release_channel', githubReleaseChannel.id);
|
await Database.updateSettings(interaction.guildId, 'github_release_channel', githubReleaseChannel.id);
|
||||||
response.push(`**github_release_channel** has been set to ${githubReleaseChannel}.`);
|
response.push(`**github_release_channel** has been set to ${githubReleaseChannel}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const siteBreakerRole = interaction.options.getRole('site-breaker-role');
|
const siteBreakerRole = interaction.options.getRole('site-breaker-role');
|
||||||
if (siteBreakerRole) {
|
if (siteBreakerRole) {
|
||||||
await Database.updateGuildSettings(interaction.guildId, 'site_breaker_role_id', siteBreakerRole.id);
|
await Database.updateSettings(interaction.guildId, 'site_breaker_role_id', siteBreakerRole.id);
|
||||||
response.push(`**site_breaker_role_id** has been set to ${siteBreakerRole}.`);
|
response.push(`**site_breaker_role_id** has been set to ${siteBreakerRole}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// External Imports
|
||||||
|
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, SlashCommand } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { CreateDefaultEmbed } from '../../utils';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
@SlashCommand(
|
||||||
|
new CommandBuilder('about', 'View information about Hexerade!')
|
||||||
|
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
|
||||||
|
.setContexts(Constants.InteractionContextType.Guild)
|
||||||
|
.setCommandType(Constants.ApplicationCommandType.ChatInput)
|
||||||
|
)
|
||||||
|
class AboutCommand extends Command<CommandClient> {
|
||||||
|
cooldown = 5; // 5s.
|
||||||
|
|
||||||
|
async handleCommand(context: CommandClient<any, any>, interaction: CommandInteraction, data: any) {
|
||||||
|
if (!interaction.inGuild()) return;
|
||||||
|
|
||||||
|
await interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
|
||||||
|
description: "I'm the very cool bot for the e621 Discord.",
|
||||||
|
|
||||||
|
fields: [
|
||||||
|
{ name: 'Members', value: `${interaction.guild.memberCount}`, inline: true },
|
||||||
|
{ name: 'Boosts (Tier)', value: `${interaction.guild.premiumSubscriptionCount} (${interaction.guild.premiumTier})`, inline: true },
|
||||||
|
|
||||||
|
{ name: 'Developer', value: 'tarrgon.', inline: false },
|
||||||
|
{ name: 'Contributor', value: 'nxbat', inline: false },
|
||||||
|
|
||||||
|
{ name: 'Terms of Use', value: 'https://e621.net/terms_of_use', inline: false },
|
||||||
|
{ name: 'Privacy Policy', value: 'https://e621.net/static/privacy/discordbot', inline: false },
|
||||||
|
],
|
||||||
|
|
||||||
|
thumbnail: {
|
||||||
|
url: context.user.avatarURL
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new AboutCommand('about');
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
// External Imports
|
||||||
|
import { Command, CommandBuilder, CommandClient, CommandInteraction, Constants, Member, SlashCommand } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from '../../shared/Database';
|
||||||
|
import { E621User } from '../../types';
|
||||||
|
import { CreateDefaultEmbed, getE621User, SetSeverity } from '../../utils';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
async function fetchUser(member: Member, id: number | null): Promise<E621User | null> {
|
||||||
|
const syncedIds = await Database.getE621Ids(member.id);
|
||||||
|
|
||||||
|
if (!id || !syncedIds.includes(id))
|
||||||
|
return await getE621User(syncedIds[0]);
|
||||||
|
else
|
||||||
|
return await getE621User(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SlashCommand(
|
||||||
|
new CommandBuilder('name-sync', 'Sync your Discord username with your e621 username.')
|
||||||
|
.setIntegrationTypes(Constants.ApplicationIntegrationType.GuildInstall)
|
||||||
|
.setContexts(Constants.InteractionContextType.Guild)
|
||||||
|
.setCommandType(Constants.ApplicationCommandType.ChatInput)
|
||||||
|
.addNumberOption({ name: 'id', description: 'The ID of the user to sync your name to.', required: false, max_value: 16 })
|
||||||
|
)
|
||||||
|
class NameSyncCommand extends Command<CommandClient> {
|
||||||
|
cooldown = 360; // 5m.
|
||||||
|
|
||||||
|
async handleCommand(context: CommandClient<any, any>, interaction: CommandInteraction, data: any) {
|
||||||
|
if (!interaction.inGuild()) return;
|
||||||
|
await interaction.defer();
|
||||||
|
|
||||||
|
const id = interaction.getNumber('id');
|
||||||
|
const user = await fetchUser(interaction.member, id);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
await interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
...SetSeverity('error'),
|
||||||
|
|
||||||
|
description: 'I was unable to get a user entry for your account or provided id. Please try again, or contact an administrator.'
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.member.edit({ nick: user.name }, 'Member requested a name-sync.');
|
||||||
|
|
||||||
|
await interaction.createMessage({
|
||||||
|
embeds: [{
|
||||||
|
...CreateDefaultEmbed(context),
|
||||||
|
...SetSeverity('success'),
|
||||||
|
|
||||||
|
description: `Successfully updated your display name. Hello \`${user.name}\`!`
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new NameSyncCommand('name-sync');
|
||||||
+4
-9
@@ -1,10 +1,11 @@
|
|||||||
import dotenv from 'dotenv';
|
// External Imports
|
||||||
|
import 'dotenv/config';
|
||||||
|
|
||||||
dotenv.config();
|
// ---------------
|
||||||
|
|
||||||
const { DISCORD_TOKEN, DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, DISCORD_GUILD_ID, RELEASE_SECRET, DATABASE_SECRET, LINK_SECRET, E621_BASE_URL, E926_BASE_URL, PRIVACY_POLICY_URL, GIT_REPO_BASE_URL, REDIS_URL, PORT, DEBUG } = process.env;
|
const { DISCORD_TOKEN, DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, DISCORD_GUILD_ID, RELEASE_SECRET, DATABASE_SECRET, LINK_SECRET, E621_BASE_URL, E926_BASE_URL, PRIVACY_POLICY_URL, GIT_REPO_BASE_URL, REDIS_URL, PORT, DEBUG } = process.env;
|
||||||
|
|
||||||
export const config = {
|
export default {
|
||||||
DISCORD_TOKEN,
|
DISCORD_TOKEN,
|
||||||
DISCORD_CLIENT_ID,
|
DISCORD_CLIENT_ID,
|
||||||
DISCORD_CLIENT_SECRET,
|
DISCORD_CLIENT_SECRET,
|
||||||
@@ -21,9 +22,3 @@ export const config = {
|
|||||||
DEV_MODE: process.env.npm_lifecycle_event == 'dev',
|
DEV_MODE: process.env.npm_lifecycle_event == 'dev',
|
||||||
DEBUG: DEBUG == 'true'
|
DEBUG: DEBUG == 'true'
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [key, val] of Object.entries(config)) {
|
|
||||||
if (val === undefined) {
|
|
||||||
throw new Error(`${key} is undefined in config`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient, Event, Guild, User } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from '../shared/Database';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
class GuildBanRemoveEvent extends Event<CommandClient> {
|
||||||
|
event: string = 'guildBanRemove' as const;
|
||||||
|
|
||||||
|
async handle(context: CommandClient<any, any>, guild: Guild, user: User) {
|
||||||
|
await Database.deleteBan(user.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new GuildBanRemoveEvent('guildBanRemove');
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient, Event, Guild } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from '../shared/Database';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
class GuildCreateEvent extends Event<CommandClient> {
|
||||||
|
event: string = 'guildCreate' as const;
|
||||||
|
|
||||||
|
async handle(context: CommandClient<any, any>, guild: Guild) {
|
||||||
|
await Database.GetOrCreateSettings(guild.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new GuildCreateEvent('guildCreate');
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient, Event, Guild, Member } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from '../shared/Database';
|
||||||
|
import { getE621Alts } from '../utils';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
class GuildMemberAddEvent extends Event<CommandClient> {
|
||||||
|
event: string = 'guildMemberAdd' as const;
|
||||||
|
|
||||||
|
async handle(context: CommandClient<any, any>, guild: Guild, member: Member) {
|
||||||
|
const settings = await Database.GetOrCreateSettings(guild.id);
|
||||||
|
|
||||||
|
// TODO: Seperate this into its own function and create a handleWelcomeMessage() function.
|
||||||
|
const content = `${member.toString()}'s (${member.id}) e621 and discord account(s):\n${await getE621Alts(member.id, member.guild)}`;
|
||||||
|
|
||||||
|
if (!settings.moderator_channel_id || !content.includes('[BANNED]')) return;
|
||||||
|
context.createMessage(settings.moderator_channel_id, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new GuildMemberAddEvent('guildMemberAdd');
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import { APIEmbedField, APIRole, AuditLogEvent, EmbedBuilder, Guild, GuildAuditLogsEntry, RoleFlags, SnowflakeUtil } from 'discord.js';
|
|
||||||
import { Database } from '../shared/Database';
|
|
||||||
import { formatChanges, formatExtras, formatSnowflake, getTargetType } from '../utils';
|
|
||||||
|
|
||||||
const IGNORED_ACTIONS = [
|
|
||||||
AuditLogEvent.MemberMove,
|
|
||||||
// Handled by automod.
|
|
||||||
AuditLogEvent.AutoModerationFlagToChannel
|
|
||||||
];
|
|
||||||
|
|
||||||
export async function handleAuditLogCreate(entry: GuildAuditLogsEntry, guild: Guild) {
|
|
||||||
if (!await shouldLog(entry, guild)) return;
|
|
||||||
const settings = await Database.getGuildSettings(guild.id);
|
|
||||||
|
|
||||||
if (!settings || !settings.audit_logs_channel_id) return;
|
|
||||||
|
|
||||||
const channel = await guild.channels.fetch(settings.audit_logs_channel_id);
|
|
||||||
|
|
||||||
if (!channel || !channel.isSendable()) return;
|
|
||||||
|
|
||||||
const fields: APIEmbedField[] = [
|
|
||||||
{
|
|
||||||
name: 'Actor',
|
|
||||||
value: `<@${entry.executorId}>`,
|
|
||||||
inline: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
if (entry.targetId) {
|
|
||||||
const targetType = getTargetType(entry.action);
|
|
||||||
fields.push({
|
|
||||||
name: 'Target',
|
|
||||||
value: formatSnowflake(entry.targetId, targetType),
|
|
||||||
inline: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.reason) {
|
|
||||||
fields.push({
|
|
||||||
name: 'Reason',
|
|
||||||
value: entry.reason,
|
|
||||||
inline: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.changes && entry.changes.length > 0) {
|
|
||||||
fields.push({
|
|
||||||
name: 'Changes',
|
|
||||||
value: formatChanges(entry),
|
|
||||||
inline: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry.extra) {
|
|
||||||
fields.push({
|
|
||||||
name: 'Options',
|
|
||||||
value: formatExtras(entry, guild),
|
|
||||||
inline: false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const embed = new EmbedBuilder()
|
|
||||||
.setTitle(Object.keys(AuditLogEvent)[Object.values(AuditLogEvent).indexOf(entry.action)])
|
|
||||||
.setTimestamp(Number(SnowflakeUtil.decode(entry.id).timestamp))
|
|
||||||
.addFields(...fields);
|
|
||||||
|
|
||||||
channel.send({ embeds: [embed] });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function shouldLog(entry: GuildAuditLogsEntry, guild: Guild): Promise<boolean> {
|
|
||||||
if (!entry.executorId) return true;
|
|
||||||
|
|
||||||
if (IGNORED_ACTIONS.some(a => entry.action == a)) return false;
|
|
||||||
|
|
||||||
if (entry.action == AuditLogEvent.MemberRoleUpdate) {
|
|
||||||
return await shouldLogRoleChanges(entry as GuildAuditLogsEntry<AuditLogEvent.MemberRoleUpdate>, guild);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function shouldLogRoleChanges(entry: GuildAuditLogsEntry<AuditLogEvent.MemberRoleUpdate>, guild: Guild): Promise<boolean> {
|
|
||||||
for (const change of entry.changes) {
|
|
||||||
// Get role changes from the log.
|
|
||||||
const roles = (await Promise.all((change.new! as Pick<APIRole, 'id' | 'name'>[]).map(c => guild.roles.fetch(c.id))));
|
|
||||||
|
|
||||||
// Check if role is part of onboarding.
|
|
||||||
for (const role of roles) {
|
|
||||||
if (role && !role.flags.has(RoleFlags.InPrompt)) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
import { GuildBan } from 'discord.js';
|
|
||||||
import { Database } from '../shared/Database';
|
|
||||||
|
|
||||||
export async function handleBanRemove(ban: GuildBan) {
|
|
||||||
await Database.removeBan(ban.user.id);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { Guild } from 'discord.js';
|
|
||||||
import { Database } from '../shared/Database';
|
|
||||||
|
|
||||||
export async function handleGuildCreate(guild: Guild) {
|
|
||||||
try {
|
|
||||||
if (!await Database.getGuildSettings(guild.id)) await Database.putGuild(guild.id);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import { GuildMember, GuildTextBasedChannel } from 'discord.js';
|
|
||||||
import { Database } from '../shared/Database';
|
|
||||||
import { getE621Alts } from '../utils';
|
|
||||||
|
|
||||||
export async function handleMemberJoin(member: GuildMember) {
|
|
||||||
const guildSettings = await Database.getGuildSettings(member.guild.id);
|
|
||||||
|
|
||||||
if (guildSettings?.new_member_channel_id) {
|
|
||||||
const channel = await member.guild.channels.fetch(guildSettings.new_member_channel_id) as GuildTextBasedChannel;
|
|
||||||
|
|
||||||
if (channel) {
|
|
||||||
const content = `${member.toString()}'s (${member.id}) e621 and discord account(s):\n${await getE621Alts(member.id, member.guild)}`;
|
|
||||||
|
|
||||||
channel.send(content).catch(console.error);
|
|
||||||
|
|
||||||
if (guildSettings.moderator_channel_id && content.includes('[BANNED]')) {
|
|
||||||
const modChannel = await member.guild.channels.fetch(guildSettings.moderator_channel_id) as GuildTextBasedChannel;
|
|
||||||
|
|
||||||
if (modChannel) modChannel.send(`Member joined with banned alts:\n${content}`).catch(console.error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
import { AnyThreadChannel } from 'discord.js';
|
|
||||||
|
|
||||||
export async function handleThreadCreate(thread: AnyThreadChannel, newlyCreated: boolean) {
|
|
||||||
try {
|
|
||||||
await thread.join();
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Failed to join thread:');
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
import { Guild, GuildMember, GuildTextBasedChannel, time, VoiceBasedChannel, VoiceState } from 'discord.js';
|
|
||||||
import { Database } from '../shared/Database';
|
|
||||||
|
|
||||||
export async function handleVoiceStateUpdate(oldState: VoiceState, newState: VoiceState) {
|
|
||||||
// The logChannel declaration being inside is purposeful, as this event is fired a lot for users talking.
|
|
||||||
if (newState.channelId != null && oldState.channelId != null && newState.channelId != oldState.channelId) {
|
|
||||||
const logChannel = await getVoiceLogsChannel(newState.guild);
|
|
||||||
if (!logChannel) return;
|
|
||||||
|
|
||||||
await sendMovedMessage(logChannel, newState.member!, oldState.channel!, newState.channel!);
|
|
||||||
} else if (oldState.channelId == null && newState.channelId != null) {
|
|
||||||
const logChannel = await getVoiceLogsChannel(newState.guild);
|
|
||||||
if (!logChannel) return;
|
|
||||||
|
|
||||||
await sendJoinMessage(logChannel, newState.member!, newState.channel!);
|
|
||||||
} else if (newState.channelId == null && oldState.channelId != null) {
|
|
||||||
const logChannel = await getVoiceLogsChannel(newState.guild);
|
|
||||||
if (!logChannel) return;
|
|
||||||
|
|
||||||
await sendLeftMessage(logChannel, newState.member!, oldState.channel!);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendJoinMessage(channel: GuildTextBasedChannel, member: GuildMember, voiceChannel: VoiceBasedChannel) {
|
|
||||||
await channel.send(`${member} joined ${voiceChannel} at ${time()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendLeftMessage(channel: GuildTextBasedChannel, member: GuildMember, voiceChannel: VoiceBasedChannel) {
|
|
||||||
await channel.send(`${member} left ${voiceChannel} at ${time()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendMovedMessage(channel: GuildTextBasedChannel, member: GuildMember, oldVoiceChannel: VoiceBasedChannel, newVoiceChannel: VoiceBasedChannel) {
|
|
||||||
await channel.send(`${member} moved from ${oldVoiceChannel} to ${newVoiceChannel} at ${time()}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getVoiceLogsChannel(guild: Guild): Promise<GuildTextBasedChannel | undefined> {
|
|
||||||
const settings = await Database.getGuildSettings(guild.id);
|
|
||||||
|
|
||||||
if (!settings || !settings.voice_logs_channel_id) return;
|
|
||||||
|
|
||||||
const channel = await guild.channels.fetch(settings.voice_logs_channel_id);
|
|
||||||
|
|
||||||
if (!channel || !channel.isSendable()) return;
|
|
||||||
|
|
||||||
return channel as GuildTextBasedChannel;
|
|
||||||
}
|
|
||||||
+13
-7
@@ -1,7 +1,13 @@
|
|||||||
export * from './handle-audit-log-create';
|
// export * from './handle-audit-log-create';
|
||||||
export * from './handle-ban-remove';
|
import guildBanRemove from './guildBanRemove';
|
||||||
export * from './handle-guild-create';
|
import guildCreate from './guildCreate';
|
||||||
export * from './handle-member-join';
|
import guildMemberAdd from './guildMemberAdd';
|
||||||
export * from './handle-message';
|
import ready from './ready';
|
||||||
export * from './handle-thread-create';
|
// export * from './handle-message';
|
||||||
export * from './handle-voice-state-update';
|
import threadCreate from './threadCreate';
|
||||||
|
|
||||||
|
import voiceChannelJoin from './voiceChannelJoin';
|
||||||
|
import voiceChannelLeft from './voiceChannelLeft';
|
||||||
|
import voiceChannelSwitch from './voiceChannelSwitch';
|
||||||
|
|
||||||
|
export default [guildBanRemove, guildCreate, guildMemberAdd, ready, threadCreate, voiceChannelJoin, voiceChannelLeft, voiceChannelSwitch];
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient, Event, Guild } from 'athena-prime';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
class ReadyEvent extends Event<CommandClient> {
|
||||||
|
event: string = 'ready' as const;
|
||||||
|
|
||||||
|
async handle(context: CommandClient<any, any>, guild: Guild) {
|
||||||
|
await context.deployCommands();
|
||||||
|
|
||||||
|
// :3 - This is non-negotiable.
|
||||||
|
context.setCustomActivity('Hugging my favourite bat: nix :3');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new ReadyEvent('ready');
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient, Event, ThreadChannel } from 'athena-prime';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
class ThreadCreateEvent extends Event<CommandClient> {
|
||||||
|
event: string = 'threadCreate' as const;
|
||||||
|
|
||||||
|
async handle(context: CommandClient<any, any>, channel: ThreadChannel) {
|
||||||
|
await context.joinThread(channel.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new ThreadCreateEvent('threadCreate');
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient, Event, Member, GuildVoiceChannel } from "athena-prime";
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from "../shared/Database";
|
||||||
|
import { SetSeverity } from "../utils";
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
class VoiceChannelJoinEvent extends Event<CommandClient> {
|
||||||
|
event: string = 'voiceChannelJoin' as const;
|
||||||
|
|
||||||
|
async handle(context: CommandClient<any, any>, member: Member, channel: GuildVoiceChannel) {
|
||||||
|
const settings = await Database.GetOrCreateSettings(channel.guild.id);
|
||||||
|
if (!settings.voice_logs_channel_id) return;
|
||||||
|
|
||||||
|
await context.createMessage(settings.voice_logs_channel_id, {
|
||||||
|
embed: {
|
||||||
|
...SetSeverity('success'),
|
||||||
|
|
||||||
|
author: {
|
||||||
|
name: member.username,
|
||||||
|
icon_url: member.avatarURL
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: [
|
||||||
|
{ name: 'Member', value: `${member.username} (${member.mention})`, inline: true },
|
||||||
|
{ name: 'Channel', value: `${channel.mention}`, inline: true },
|
||||||
|
{ name: 'Time', value: `<t:${new Date().toISOString()}>`, inline: true },
|
||||||
|
],
|
||||||
|
|
||||||
|
title: 'Joined Voice Channel'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new VoiceChannelJoinEvent('voiceChannelJoin');
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient, Event, Member, GuildVoiceChannel } from "athena-prime";
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from "../shared/Database";
|
||||||
|
import { SetSeverity } from "../utils";
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
class VoiceChannelLeftEvent extends Event<CommandClient> {
|
||||||
|
event: string = 'voiceChannelLeft' as const;
|
||||||
|
|
||||||
|
async handle(context: CommandClient<any, any>, member: Member, channel: GuildVoiceChannel) {
|
||||||
|
const settings = await Database.GetOrCreateSettings(channel.guild.id);
|
||||||
|
if (!settings.voice_logs_channel_id) return;
|
||||||
|
|
||||||
|
await context.createMessage(settings.voice_logs_channel_id, {
|
||||||
|
embed: {
|
||||||
|
author: {
|
||||||
|
name: member.username,
|
||||||
|
icon_url: member.avatarURL
|
||||||
|
},
|
||||||
|
|
||||||
|
...SetSeverity('error'),
|
||||||
|
|
||||||
|
fields: [
|
||||||
|
{ name: 'Member', value: `${member.username} (${member.mention})`, inline: true },
|
||||||
|
{ name: 'Channel', value: `${channel.mention}`, inline: true },
|
||||||
|
{ name: 'Time', value: `<t:${new Date().toISOString()}>`, inline: true },
|
||||||
|
],
|
||||||
|
|
||||||
|
title: 'Left Voice Channel'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new VoiceChannelLeftEvent('voiceChannelLeft');
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient, Event, Member, GuildVoiceChannel } from "athena-prime";
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import { Database } from "../shared/Database";
|
||||||
|
import { SetSeverity } from "../utils";
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
|
class VoiceChannelSwitchEvent extends Event<CommandClient> {
|
||||||
|
event: string = 'voiceChannelSwitch' as const;
|
||||||
|
|
||||||
|
async handle(context: CommandClient<any, any>, member: Member, channel: GuildVoiceChannel, old: GuildVoiceChannel) {
|
||||||
|
const settings = await Database.GetOrCreateSettings(channel.guild.id);
|
||||||
|
if (!settings.voice_logs_channel_id) return;
|
||||||
|
|
||||||
|
await context.createMessage(settings.voice_logs_channel_id, {
|
||||||
|
embed: {
|
||||||
|
...SetSeverity('warning'),
|
||||||
|
|
||||||
|
author: {
|
||||||
|
name: member.username,
|
||||||
|
icon_url: member.avatarURL
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: [
|
||||||
|
{ name: 'Member', value: `${member.username} (${member.mention})`, inline: true },
|
||||||
|
{ name: 'Channel', value: `${old.mention} -> ${channel.mention}`, inline: true },
|
||||||
|
{ name: 'Time', value: `<t:${new Date().toISOString()}>`, inline: true },
|
||||||
|
],
|
||||||
|
|
||||||
|
title: 'Moved Voice Channel'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default new VoiceChannelSwitchEvent('voiceChannelSwitch');
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
import { Client } from 'discord.js';
|
// External Imports
|
||||||
|
import { CommandClient } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
import { Task } from '../types';
|
import { Task } from '../types';
|
||||||
import ScheduledTasks from './tasks';
|
import ScheduledTasks from './tasks';
|
||||||
|
|
||||||
class Scheduler {
|
class Scheduler {
|
||||||
private tasks = new Map<Task, NodeJS.Timeout>();
|
private tasks = new Map<Task, NodeJS.Timeout>();
|
||||||
private context: Client;
|
private context: CommandClient;
|
||||||
|
|
||||||
constructor(context: Client) {
|
constructor(context: CommandClient) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,6 +21,4 @@ class Scheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export { Scheduler, ScheduledTasks }
|
||||||
ScheduledTasks, Scheduler
|
|
||||||
};
|
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
import { Client } from 'discord.js';
|
// External Imports
|
||||||
|
import { CommandClient } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
import { Task } from '../../types';
|
import { Task } from '../../types';
|
||||||
import { checkExpiredBans } from '../../utils';
|
import { checkExpiredBans } from '../../utils';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
class CheckExpiredBansTask implements Task {
|
class CheckExpiredBansTask implements Task {
|
||||||
interval: number = 300000;
|
interval: number = 300000;
|
||||||
firstRun: boolean = true;
|
firstRun: boolean = true;
|
||||||
|
|
||||||
async handle(context: Client): Promise<void> {
|
async handle(context: CommandClient) {
|
||||||
await checkExpiredBans(context);
|
await checkExpiredBans(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import { Client } from 'discord.js';
|
// External Imports
|
||||||
|
import { CommandClient } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
import { Task } from '../../types';
|
import { Task } from '../../types';
|
||||||
import { closeOldTickets } from '../../utils';
|
import { closeOldTickets } from '../../utils';
|
||||||
|
|
||||||
|
// ---------------
|
||||||
|
|
||||||
class CloseStaleTicketsTask implements Task {
|
class CloseStaleTicketsTask implements Task {
|
||||||
interval: number = 3.6e6;
|
interval: number = 3.6e6;
|
||||||
firstRun: boolean = true;
|
firstRun: boolean = true;
|
||||||
|
|
||||||
async handle(context: Client): Promise<void> {
|
async handle(context: CommandClient) {
|
||||||
await closeOldTickets(context);
|
await closeOldTickets(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
// External Imports
|
||||||
|
import { CommandClient } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
import { Task } from '../../types';
|
import { Task } from '../../types';
|
||||||
import { Database } from '../../shared/Database';
|
import { Database } from '../../shared/Database';
|
||||||
|
|
||||||
@@ -5,7 +9,7 @@ class PruneOldMessagesTask implements Task {
|
|||||||
interval: number = 3.6e6;
|
interval: number = 3.6e6;
|
||||||
firstRun: boolean = true;
|
firstRun: boolean = true;
|
||||||
|
|
||||||
async handle(): Promise<void> {
|
async handle(context: CommandClient) {
|
||||||
await Database.pruneOldMessages();
|
await Database.pruneOldMessages();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-12
@@ -2,7 +2,7 @@ import path from 'path';
|
|||||||
import { open, Database as SqliteDatabase } from 'sqlite';
|
import { open, Database as SqliteDatabase } from 'sqlite';
|
||||||
import sqlite3 from 'sqlite3';
|
import sqlite3 from 'sqlite3';
|
||||||
import { Message } from '../events';
|
import { Message } from '../events';
|
||||||
import { AppealMessage, Ban, GithubUserMapping, GuildArraySetting, GuildSetting, GuildSettings, KnowledgebaseItem, LoggedMessage, Note, PrivateHelpTicket, RoleButton, TicketMessage, TicketPhrase } from '../types';
|
import { AppealMessage, Ban, GithubUserMapping, GuildArraySetting, GuildSettings, KnowledgebaseItem, LoggedMessage, Note, PrivateHelpTicket, RoleButton, TicketMessage, TicketPhrase } from '../types';
|
||||||
import { deserializeMessage, serializeMessage, wait } from '../utils';
|
import { deserializeMessage, serializeMessage, wait } from '../utils';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
import { Encrypter } from './Encrypter';
|
import { Encrypter } from './Encrypter';
|
||||||
@@ -13,7 +13,7 @@ export const enum PrivateHelpTicketStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class Database {
|
export class Database {
|
||||||
private static db: SqliteDatabase;
|
static db: SqliteDatabase;
|
||||||
|
|
||||||
static async open(file: string): Promise<void> {
|
static async open(file: string): Promise<void> {
|
||||||
if (Database.db) return;
|
if (Database.db) return;
|
||||||
@@ -28,7 +28,6 @@ export class Database {
|
|||||||
console.log('SQLite database opened');
|
console.log('SQLite database opened');
|
||||||
|
|
||||||
await Database.ensure();
|
await Database.ensure();
|
||||||
|
|
||||||
await Database.migrate();
|
await Database.migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,15 +74,19 @@ export class Database {
|
|||||||
|
|
||||||
//#region Guild Settings
|
//#region Guild Settings
|
||||||
|
|
||||||
static async getGuildSettings(guildId: string): Promise<GuildSettings> {
|
static async GetOrCreateSettings(guildId: string): Promise<GuildSettings> {
|
||||||
return await Database.db.get<GuildSettings>('SELECT * FROM settings WHERE guild_id = ?', guildId) as GuildSettings;
|
await Database.db.run(
|
||||||
}
|
'INSERT OR IGNORE INTO settings (guild_id) VALUES (?)',
|
||||||
|
guildId
|
||||||
|
);
|
||||||
|
|
||||||
static async putGuild(guildId: string) {
|
return await Database.db.get<GuildSettings>(
|
||||||
await Database.db.run('INSERT INTO settings(guild_id) VALUES (?)', guildId);
|
'SELECT * FROM settings WHERE guild_id = ?',
|
||||||
}
|
guildId
|
||||||
|
) as GuildSettings;
|
||||||
|
}
|
||||||
|
|
||||||
static async updateGuildSettings(guildId: string, key: GuildSetting, value: string) {
|
static async updateSettings(guildId: string, key: Exclude<keyof GuildSettings, 'guild_id'>, value: string) {
|
||||||
await Database.db.run(`UPDATE settings SET ${key} = ? WHERE guild_id = ?`, value, guildId);
|
await Database.db.run(`UPDATE settings SET ${key} = ? WHERE guild_id = ?`, value, guildId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,10 +335,10 @@ export class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static async pruneExpiredBans(date: Date) {
|
static async pruneExpiredBans(date: Date) {
|
||||||
await Database.db.all<Ban[]>('DELETE from bans WHERE expires = 1 AND expires_at <= ?', date);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async removeBan(userId: string) {
|
static async deleteBan(userId: string) {
|
||||||
await Database.db.run('DELETE from bans WHERE user_id_hash = ?', Encrypter.hash(userId));
|
await Database.db.run('DELETE from bans WHERE user_id_hash = ?', Encrypter.hash(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
-1
@@ -33,7 +33,6 @@ export type GuildSettings = {
|
|||||||
site_breaker_role_id?: string
|
site_breaker_role_id?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GuildSetting = Exclude<keyof GuildSettings, 'guild_id'>
|
|
||||||
export type GuildArraySetting = 'staff_categories' | 'safe_channels' | 'link_skip_channels';
|
export type GuildArraySetting = 'staff_categories' | 'safe_channels' | 'link_skip_channels';
|
||||||
|
|
||||||
export type TicketMessage = {
|
export type TicketMessage = {
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
import { Client } from 'discord.js';
|
import { CommandClient } from 'athena-prime';
|
||||||
|
|
||||||
export interface Task {
|
export interface Task {
|
||||||
interval: number;
|
interval: number;
|
||||||
firstRun: boolean;
|
firstRun: boolean;
|
||||||
|
|
||||||
handle(context: Client): Promise<void>;
|
handle(context: CommandClient): Promise<void>;
|
||||||
};
|
};
|
||||||
|
|||||||
+10
-18
@@ -1,7 +1,10 @@
|
|||||||
import { Guild } from 'discord.js';
|
// External Imports
|
||||||
import { Database } from '../shared/Database';
|
import { Guild } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
import { userIsBanned } from './e621-utils';
|
import { userIsBanned } from './e621-utils';
|
||||||
import { config } from '../config';
|
import config from '../config';
|
||||||
|
import { Database } from '../shared/Database';
|
||||||
|
|
||||||
export type AltData = {
|
export type AltData = {
|
||||||
type: 'e621' | 'discord'
|
type: 'e621' | 'discord'
|
||||||
@@ -13,7 +16,6 @@ export type AltData = {
|
|||||||
|
|
||||||
export async function getE621Alts(discordId: string, guild: Guild, depth = 1, ignore: number[] = []): Promise<string> {
|
export async function getE621Alts(discordId: string, guild: Guild, depth = 1, ignore: number[] = []): Promise<string> {
|
||||||
const e621UserIds = await Database.getE621Ids(discordId);
|
const e621UserIds = await Database.getE621Ids(discordId);
|
||||||
|
|
||||||
const toIgnore = ignore.concat(e621UserIds);
|
const toIgnore = ignore.concat(e621UserIds);
|
||||||
|
|
||||||
let content = '';
|
let content = '';
|
||||||
@@ -22,7 +24,6 @@ export async function getE621Alts(discordId: string, guild: Guild, depth = 1, ig
|
|||||||
if (ignore.includes(e621Id)) continue;
|
if (ignore.includes(e621Id)) continue;
|
||||||
|
|
||||||
const alts = await getDiscordAlts(e621Id, guild, depth + 1, toIgnore);
|
const alts = await getDiscordAlts(e621Id, guild, depth + 1, toIgnore);
|
||||||
|
|
||||||
const banned = await userIsBanned(e621Id);
|
const banned = await userIsBanned(e621Id);
|
||||||
|
|
||||||
content += `${' '.repeat((depth - 1) * 2)}- ${config.E621_BASE_URL}/users/${e621Id}${banned ? ' [BANNED]' : ''}\n${alts}`;
|
content += `${' '.repeat((depth - 1) * 2)}- ${config.E621_BASE_URL}/users/${e621Id}${banned ? ' [BANNED]' : ''}\n${alts}`;
|
||||||
@@ -38,13 +39,10 @@ export async function getDiscordAlts(e621Id: number, guild: Guild, depth = 1, ig
|
|||||||
|
|
||||||
for (const discordId of discordIds) {
|
for (const discordId of discordIds) {
|
||||||
const alts = await getE621Alts(discordId, guild, depth + 1, ignore);
|
const alts = await getE621Alts(discordId, guild, depth + 1, ignore);
|
||||||
|
|
||||||
let banned = false;
|
let banned = false;
|
||||||
|
|
||||||
// It's either this or fetch all the bans and sift through them for every discord alt.
|
// It's either this or fetch all the bans and sift through them for every discord alt.
|
||||||
try {
|
banned = !!(await guild.getBan(discordId));
|
||||||
banned = !!(await guild.bans.fetch(discordId));
|
|
||||||
} catch (e) { }
|
|
||||||
|
|
||||||
content += `${' '.repeat((depth - 1) * 2)}- <@${discordId}> (${discordId})${banned ? ' [BANNED]' : ''}\n${alts}`;
|
content += `${' '.repeat((depth - 1) * 2)}- <@${discordId}> (${discordId})${banned ? ' [BANNED]' : ''}\n${alts}`;
|
||||||
}
|
}
|
||||||
@@ -69,14 +67,13 @@ async function getE621AltData(discordId: string, guild: Guild | null, depth = 1,
|
|||||||
|
|
||||||
// It's either this or fetch all the bans and sift through them for every discord alt.
|
// It's either this or fetch all the bans and sift through them for every discord alt.
|
||||||
try {
|
try {
|
||||||
banned = guild ? !!(await guild.bans.fetch(discordId)) : false;
|
banned = guild ? !!(await guild.getBan(discordId)) : false;
|
||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
|
|
||||||
const data: AltData = { type: 'discord', thisId: discordId, banned, alts: [] };
|
const data: AltData = { type: 'discord', thisId: discordId, banned, alts: [] };
|
||||||
|
|
||||||
for (const e621Id of e621UserIds) {
|
for (const e621Id of e621UserIds) {
|
||||||
if (ignore.includes(e621Id)) continue;
|
if (ignore.includes(e621Id)) continue;
|
||||||
|
|
||||||
data.alts.push(await getDiscordAltData(e621Id, guild, depth + 1, toIgnore));
|
data.alts.push(await getDiscordAltData(e621Id, guild, depth + 1, toIgnore));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,12 +82,9 @@ async function getE621AltData(discordId: string, guild: Guild | null, depth = 1,
|
|||||||
|
|
||||||
async function getDiscordAltData(e621Id: number, guild: Guild | null, depth = 1, ignore: number[] = []): Promise<AltData> {
|
async function getDiscordAltData(e621Id: number, guild: Guild | null, depth = 1, ignore: number[] = []): Promise<AltData> {
|
||||||
const discordIds = await Database.getDiscordIds(e621Id);
|
const discordIds = await Database.getDiscordIds(e621Id);
|
||||||
|
|
||||||
const data: AltData = { type: 'e621', thisId: e621Id, banned: await userIsBanned(e621Id), alts: [] };
|
const data: AltData = { type: 'e621', thisId: e621Id, banned: await userIsBanned(e621Id), alts: [] };
|
||||||
|
|
||||||
for (const discordId of discordIds) {
|
for (const discordId of discordIds) data.alts.push(await getE621AltData(discordId, guild, depth + 1, ignore));
|
||||||
data.alts.push(await getE621AltData(discordId, guild, depth + 1, ignore));
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -98,9 +92,7 @@ async function getDiscordAltData(e621Id: number, guild: Guild | null, depth = 1,
|
|||||||
export function e621IdsFromAltData(altData: AltData, data: number[] = []) {
|
export function e621IdsFromAltData(altData: AltData, data: number[] = []) {
|
||||||
if (altData.type == 'e621' && !data.includes(altData.thisId as number)) data.push(altData.thisId as number);
|
if (altData.type == 'e621' && !data.includes(altData.thisId as number)) data.push(altData.thisId as number);
|
||||||
|
|
||||||
for (const alt of altData.alts) {
|
for (const alt of altData.alts) e621IdsFromAltData(alt, data);
|
||||||
e621IdsFromAltData(alt, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
+11
-11
@@ -1,22 +1,22 @@
|
|||||||
import { Client } from 'discord.js';
|
// External Imports
|
||||||
|
import { CommandClient } from 'athena-prime';
|
||||||
|
|
||||||
|
// Internal Imports
|
||||||
|
import config from '../config';
|
||||||
import { Database } from '../shared/Database';
|
import { Database } from '../shared/Database';
|
||||||
import { config } from '../config';
|
import { Ban } from '../types';
|
||||||
|
|
||||||
export async function checkExpiredBans(client: Client) {
|
// ---------------
|
||||||
const guild = await client.guilds.fetch(config.DISCORD_GUILD_ID!);
|
|
||||||
|
|
||||||
|
export async function checkExpiredBans(client: CommandClient) {
|
||||||
|
const guild = client.guilds.get(config.DISCORD_GUILD_ID!);
|
||||||
if (!guild) return;
|
if (!guild) return;
|
||||||
|
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
|
|
||||||
for (const ban of await Database.getExpiredBans(date)) {
|
for (const ban of await Database.getExpiredBans(date)) {
|
||||||
try {
|
await guild.unbanMember(ban.user_id, 'Ban Expired.');
|
||||||
await guild.bans.remove(ban.user_id);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Error unbanning user: ${ban.user_id}`);
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await Database.pruneExpiredBans(date);
|
await Database.db.all<Ban[]>('DELETE from bans WHERE expires = 1 AND expires_at <= ?', date);
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
import { APIEmbed, Client } from 'discord.js';
|
import { CommandClient, Constants } from 'athena-prime';
|
||||||
|
|
||||||
type EmbedSeverity =
|
type Severity = 'info' | 'warning' | 'error' | 'success';
|
||||||
| 'default'
|
|
||||||
| 'info'
|
|
||||||
| 'warning'
|
|
||||||
| 'error'
|
|
||||||
| 'success'
|
|
||||||
|
|
||||||
export function CreateDefaultEmbed(context: Client): APIEmbed {
|
export function CreateDefaultEmbed(context: CommandClient): Constants.APIEmbed {
|
||||||
if (!context.user) return {};
|
if (!context.user) return {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
color: 0x014995,
|
||||||
|
|
||||||
footer: {
|
footer: {
|
||||||
icon_url: context.user.avatarURL()!,
|
icon_url: context.user.avatarURL,
|
||||||
text: context.user.username
|
text: context.user.username
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -20,9 +17,8 @@ export function CreateDefaultEmbed(context: Client): APIEmbed {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SetSeverity(severity: EmbedSeverity): APIEmbed {
|
export function SetSeverity(severity: Severity): Constants.APIEmbed {
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case 'default': return { color: 0x014995 };
|
|
||||||
case 'info': return { color: 0x5865F2 };
|
case 'info': return { color: 0x5865F2 };
|
||||||
case 'warning': return { color: 0xFEE75C };
|
case 'warning': return { color: 0xFEE75C };
|
||||||
case 'error': return { color: 0xED4245 };
|
case 'error': return { color: 0xED4245 };
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import { ChatInputCommandInteraction, GuildMember, ModalSubmitInteraction, UserContextMenuCommandInteraction } from 'discord.js';
|
|
||||||
import { Database } from '../shared/Database';
|
|
||||||
import { E621User } from '../types';
|
|
||||||
import { getE621User } from './e621-utils';
|
|
||||||
|
|
||||||
export async function syncName(interaction: ChatInputCommandInteraction | UserContextMenuCommandInteraction | ModalSubmitInteraction, member: GuildMember, id: number | null) {
|
|
||||||
if (member.roles.highest.comparePositionTo(member.guild.members.me!.roles.highest) > 0) {
|
|
||||||
const res = interaction.user.id == member.id ? 'your' : 'their';
|
|
||||||
return interaction.editReply(`I am unable to set ${res} nickname as ${res} role is higher than mine.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const availableIds = await Database.getE621Ids(interaction.user.id);
|
|
||||||
|
|
||||||
let e621User: E621User | null;
|
|
||||||
|
|
||||||
if (!id || !availableIds.includes(id)) {
|
|
||||||
e621User = await getE621User(availableIds[0]);
|
|
||||||
} else {
|
|
||||||
e621User = await getE621User(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!e621User) {
|
|
||||||
return interaction.editReply("Couldn't figure out what your name was. Please contact an administrator.");
|
|
||||||
}
|
|
||||||
|
|
||||||
await member.setNickname(e621User.name);
|
|
||||||
interaction.editReply(`Nickname set to: ${e621User.name}`);
|
|
||||||
}
|
|
||||||
@@ -1,26 +1,25 @@
|
|||||||
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, ChatInputCommandInteraction, Client, Guild, GuildMember, ModalBuilder, PrivateThreadChannel, TextChannel, TextInputStyle, ThreadAutoArchiveDuration, ThreadChannel, UserContextMenuCommandInteraction } from 'discord.js';
|
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, ChannelType, ChatInputCommandInteraction, Client, Guild, GuildMember, ModalBuilder, PrivateThreadChannel, TextChannel, TextInputStyle, ThreadAutoArchiveDuration, UserContextMenuCommandInteraction } from 'discord.js';
|
||||||
import { Database } from '../shared/Database';
|
import { Database } from '../shared/Database';
|
||||||
import { createTextInput, createYesNoMenu } from './modal-utils';
|
import { createTextInput, createYesNoMenu } from './modal-utils';
|
||||||
|
|
||||||
export async function closeOldTickets(client: Client) {
|
import { CommandClient, ThreadChannel } from 'athena-prime';
|
||||||
|
|
||||||
|
// TODO: Handle Modmail Errors.
|
||||||
|
export async function closeOldTickets(client: CommandClient) {
|
||||||
for (const ticket of await Database.getAllOpenPrivateHelpTickets()) {
|
for (const ticket of await Database.getAllOpenPrivateHelpTickets()) {
|
||||||
try {
|
const thread = client.getChannel(ticket.thread_id) as ThreadChannel;
|
||||||
const thread = await client.channels.fetch(ticket.thread_id) as ThreadChannel;
|
const latestMessage = (await (thread.getMessages({ limit: 1 }))).at(0);
|
||||||
const latestMessage = (await thread.messages.fetch({ limit: 1 })).at(0);
|
|
||||||
if (latestMessage && latestMessage.createdTimestamp <= Date.now() - 432e6) {
|
if (latestMessage && latestMessage.timestamp <= Date.now() - 432e6) {
|
||||||
await Database.closePrivateHelpTicket(thread.id);
|
await Database.closePrivateHelpTicket(thread.id);
|
||||||
|
|
||||||
await thread.send('This ticket has been closed due to inactivity.');
|
await thread.createMessage('This ticket has been closed due to inactivity.');
|
||||||
|
|
||||||
thread.edit({
|
thread.edit({
|
||||||
archived: true,
|
archived: true,
|
||||||
locked: true
|
locked: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.error('Error closing ticket due to inactivity:');
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user