From acef7cbb0ac0ddccfca10c2cd10de2e5df6a3ab7 Mon Sep 17 00:00:00 2001 From: Philippe Dellaert Date: Fri, 25 Nov 2022 23:30:56 -0800 Subject: [PATCH 1/5] feat: stickies for forum posts which get posted on forum post create --- package-lock.json | 313 ++++++++++------- src/commands/index.ts | 2 + src/commands/moderation/sticky.ts | 460 +++++++++++++++++++++++++ src/handlers/forumPostCreate.ts | 35 ++ src/lib/schemas/stickyMessageSchema.ts | 19 + src/lib/stickyMessageEmbed.ts | 12 + 6 files changed, 713 insertions(+), 128 deletions(-) create mode 100644 src/commands/moderation/sticky.ts create mode 100644 src/handlers/forumPostCreate.ts create mode 100644 src/lib/schemas/stickyMessageSchema.ts create mode 100644 src/lib/stickyMessageEmbed.ts diff --git a/package-lock.json b/package-lock.json index 0e836029..11119a55 100644 --- a/package-lock.json +++ b/package-lock.json @@ -191,12 +191,13 @@ } }, "node_modules/@discordjs/builders": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.1.0.tgz", - "integrity": "sha512-EO8TSltiIc9Z1wE854wAFvv5AccqEtvjFmao9PPoxQhRaJ0hEb7FwWRTCA1jGg4ZWI3hcp4m+RET5ufZQz3rOg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.3.0.tgz", + "integrity": "sha512-Pvca6Nw8Hp+n3N+Wp17xjygXmMvggbh5ywUsOYE2Et4xkwwVRwgzxDJiMUuYapPtnYt4w/8aKlf5khc8ipLvhg==", "dependencies": { - "@sapphire/shapeshift": "^3.5.1", - "discord-api-types": "^0.36.3", + "@discordjs/util": "^0.1.0", + "@sapphire/shapeshift": "^3.7.0", + "discord-api-types": "^0.37.12", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.1", "tslib": "^2.4.0" @@ -206,30 +207,39 @@ } }, "node_modules/@discordjs/collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.0.1.tgz", - "integrity": "sha512-5V/wswzR3r2RVYXLxxg4TvrAnBhVCNgHTXhC+OUtLoriJ072rPMHo+Iw1SS1vrCckp8Es40XM411+WkNRPaXFw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.2.0.tgz", + "integrity": "sha512-VvrrtGb7vbfPHzbhGq9qZB5o8FOB+kfazrxdt0OtxzSkoBuw9dURMkCwWizZ00+rDpiK2HmLHBZX+y6JsG9khw==", "engines": { "node": ">=16.9.0" } }, "node_modules/@discordjs/rest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.0.1.tgz", - "integrity": "sha512-w08CTKVzzYYvKxEjXKOs9AdS7KQ1J502TrPfF8eCZ2lF6AfKuMP/32YgDakiwIyYTDjEQS/v0nKLSFcncHRMtg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.3.0.tgz", + "integrity": "sha512-U6X5J+r/MxYpPTlHFuPxXEf92aKsBaD2teBC7sWkKILIr30O8c9+XshfL7KFBCavnAqS/qE+PF9fgRilO3N44g==", "dependencies": { - "@discordjs/collection": "^1.0.1", - "@sapphire/async-queue": "^1.3.2", + "@discordjs/collection": "^1.2.0", + "@discordjs/util": "^0.1.0", + "@sapphire/async-queue": "^1.5.0", "@sapphire/snowflake": "^3.2.2", - "discord-api-types": "^0.36.3", - "file-type": "^17.1.4", + "discord-api-types": "^0.37.12", + "file-type": "^18.0.0", "tslib": "^2.4.0", - "undici": "^5.8.0" + "undici": "^5.11.0" }, "engines": { "node": ">=16.9.0" } }, + "node_modules/@discordjs/util": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz", + "integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/@elastic/ecs-helpers": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@elastic/ecs-helpers/-/ecs-helpers-1.1.0.tgz", @@ -396,18 +406,18 @@ } }, "node_modules/@sapphire/async-queue": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.3.2.tgz", - "integrity": "sha512-rUpMLATsoAMnlN3gecAcr9Ecnw1vG7zi5Xr+IX22YzRzi1k9PF9vKzoT8RuEJbiIszjcimu3rveqUnvwDopz8g==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", + "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==", "engines": { "node": ">=v14.0.0", "npm": ">=7.0.0" } }, "node_modules/@sapphire/shapeshift": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.5.1.tgz", - "integrity": "sha512-7JFsW5IglyOIUQI1eE0g6h06D/Far6HqpcowRScgCiLSqTf3hhkPWCWotVTtVycnDCMYIwPeaw6IEPBomKC8pA==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.7.0.tgz", + "integrity": "sha512-A6vI1zJoxhjWo4grsxpBRBgk96SqSdjLX5WlzKp9H+bJbkM07mvwcbtbVAmUZHbi/OG3HLfiZ1rlw4BhH6tsBQ==", "dependencies": { "fast-deep-equal": "^3.1.3", "lodash.uniqwith": "^4.5.0" @@ -1126,6 +1136,17 @@ "ieee754": "^1.1.13" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1531,26 +1552,27 @@ } }, "node_modules/discord-api-types": { - "version": "0.36.3", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.36.3.tgz", - "integrity": "sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==" + "version": "0.37.20", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.20.tgz", + "integrity": "sha512-uAO+55E11rMkYR36/paE1vKN8c2bZa1mgrIaiQIBgIZRKZTDIGOZB+8I5eMRPFJcGxrg16riUu+0aTu2JQEPew==" }, "node_modules/discord.js": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.1.1.tgz", - "integrity": "sha512-6Oa2f+Y0+s6B5HTTqcAP7Z6tUmguNTKpzbuTmE1JIeT/aUTr9dVe397D/bvcBSRpJERQzMyEWyEiVQnMRHcx4A==", - "dependencies": { - "@discordjs/builders": "^1.1.0", - "@discordjs/collection": "^1.0.1", - "@discordjs/rest": "^1.0.1", + "version": "14.6.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.6.0.tgz", + "integrity": "sha512-On1K7xpJZRe0KsziIaDih2ksYPhgxym/ZqV45i1f3yig4vUotikqs7qp5oXiTzQ/UTiNRCixUWFTh7vA1YBCqw==", + "dependencies": { + "@discordjs/builders": "^1.3.0", + "@discordjs/collection": "^1.2.0", + "@discordjs/rest": "^1.3.0", + "@discordjs/util": "^0.1.0", "@sapphire/snowflake": "^3.2.2", "@types/ws": "^8.5.3", - "discord-api-types": "^0.36.3", + "discord-api-types": "^0.37.12", "fast-deep-equal": "^3.1.3", "lodash.snakecase": "^4.1.1", "tslib": "^2.4.0", - "undici": "^5.8.0", - "ws": "^8.8.1" + "undici": "^5.11.0", + "ws": "^8.9.0" }, "engines": { "node": ">=16.9.0" @@ -2441,16 +2463,16 @@ } }, "node_modules/file-type": { - "version": "17.1.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.4.tgz", - "integrity": "sha512-3w/rJUUPBj6CYhVER3D5JCKwYJJiC36uj5dP+LnyubHI6H6FJo1TeWVCEA09YLVoZqV3/mLP26j9+Pz1GjAyjQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.0.0.tgz", + "integrity": "sha512-jjMwFpnW8PKofLE/4ohlhqwDk5k0NC6iy0UHAJFKoY1fQeGMN0GDdLgHQrvCbSpMwbqzoCZhRI5dETCZna5qVA==", "dependencies": { "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0-alpha.9", - "token-types": "^5.0.0-alpha.2" + "strtok3": "^7.0.0", + "token-types": "^5.0.1" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sindresorhus/file-type?sponsor=1" @@ -4018,11 +4040,11 @@ } }, "node_modules/peek-readable": { - "version": "5.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0-alpha.5.tgz", - "integrity": "sha512-pJohF/tDwV3ntnT5+EkUo4E700q/j/OCDuPxtM+5/kFGjyOai/sK4/We4Cy1MB2OiTQliWU5DxPvYIKQAdPqAA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==", "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=14.16" }, "funding": { "type": "github", @@ -4729,6 +4751,14 @@ "readable-stream": "^3.0.6" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -4878,15 +4908,15 @@ } }, "node_modules/strtok3": { - "version": "7.0.0-alpha.9", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0-alpha.9.tgz", - "integrity": "sha512-G8WxjBFjTZ77toVElv1i7k3jCXNkBB14FVaZ/6LIOka/WGo4La5XHLrU7neFVLdKbXESZf4BejVKZu5maOmocA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", "dependencies": { "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0-alpha.5" + "peek-readable": "^5.0.0" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=14.16" }, "funding": { "type": "github", @@ -5023,15 +5053,15 @@ } }, "node_modules/token-types": { - "version": "5.0.0-alpha.2", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.0-alpha.2.tgz", - "integrity": "sha512-EsG9UxAW4M6VATrEEjhPFTKEUi1OiJqTUMIZOGBN49fGxYjZB36k0p7to3HZSmWRoHm1QfZgrg3e02fpqAt5fQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=14.16" }, "funding": { "type": "github", @@ -5072,9 +5102,9 @@ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, "node_modules/ts-mixer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz", - "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==" + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz", + "integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A==" }, "node_modules/ts-node": { "version": "10.9.1", @@ -5144,9 +5174,9 @@ } }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -5239,9 +5269,12 @@ "dev": true }, "node_modules/undici": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.8.0.tgz", - "integrity": "sha512-1F7Vtcez5w/LwH2G2tGnFIihuWUlc58YidwLiCv+jR2Z50x0tNXpRRw7eOIJ+GvqCqIkg9SB7NWAJ/T9TLfv8Q==", + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.13.0.tgz", + "integrity": "sha512-UDZKtwb2k7KRsK4SdXWG7ErXiL7yTGgLWvk2AXO1JMjgjh404nFo6tWSCM2xMpJwMPx3J8i/vfqEh1zOqvj82Q==", + "dependencies": { + "busboy": "^1.6.0" + }, "engines": { "node": ">=12.18" } @@ -5451,9 +5484,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", - "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "engines": { "node": ">=10.0.0" }, @@ -5742,36 +5775,43 @@ } }, "@discordjs/builders": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.1.0.tgz", - "integrity": "sha512-EO8TSltiIc9Z1wE854wAFvv5AccqEtvjFmao9PPoxQhRaJ0hEb7FwWRTCA1jGg4ZWI3hcp4m+RET5ufZQz3rOg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.3.0.tgz", + "integrity": "sha512-Pvca6Nw8Hp+n3N+Wp17xjygXmMvggbh5ywUsOYE2Et4xkwwVRwgzxDJiMUuYapPtnYt4w/8aKlf5khc8ipLvhg==", "requires": { - "@sapphire/shapeshift": "^3.5.1", - "discord-api-types": "^0.36.3", + "@discordjs/util": "^0.1.0", + "@sapphire/shapeshift": "^3.7.0", + "discord-api-types": "^0.37.12", "fast-deep-equal": "^3.1.3", "ts-mixer": "^6.0.1", "tslib": "^2.4.0" } }, "@discordjs/collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.0.1.tgz", - "integrity": "sha512-5V/wswzR3r2RVYXLxxg4TvrAnBhVCNgHTXhC+OUtLoriJ072rPMHo+Iw1SS1vrCckp8Es40XM411+WkNRPaXFw==" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-1.2.0.tgz", + "integrity": "sha512-VvrrtGb7vbfPHzbhGq9qZB5o8FOB+kfazrxdt0OtxzSkoBuw9dURMkCwWizZ00+rDpiK2HmLHBZX+y6JsG9khw==" }, "@discordjs/rest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.0.1.tgz", - "integrity": "sha512-w08CTKVzzYYvKxEjXKOs9AdS7KQ1J502TrPfF8eCZ2lF6AfKuMP/32YgDakiwIyYTDjEQS/v0nKLSFcncHRMtg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-1.3.0.tgz", + "integrity": "sha512-U6X5J+r/MxYpPTlHFuPxXEf92aKsBaD2teBC7sWkKILIr30O8c9+XshfL7KFBCavnAqS/qE+PF9fgRilO3N44g==", "requires": { - "@discordjs/collection": "^1.0.1", - "@sapphire/async-queue": "^1.3.2", + "@discordjs/collection": "^1.2.0", + "@discordjs/util": "^0.1.0", + "@sapphire/async-queue": "^1.5.0", "@sapphire/snowflake": "^3.2.2", - "discord-api-types": "^0.36.3", - "file-type": "^17.1.4", + "discord-api-types": "^0.37.12", + "file-type": "^18.0.0", "tslib": "^2.4.0", - "undici": "^5.8.0" + "undici": "^5.11.0" } }, + "@discordjs/util": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-0.1.0.tgz", + "integrity": "sha512-e7d+PaTLVQav6rOc2tojh2y6FE8S7REkqLldq1XF4soCx74XB/DIjbVbVLtBemf0nLW77ntz0v+o5DytKwFNLQ==" + }, "@elastic/ecs-helpers": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@elastic/ecs-helpers/-/ecs-helpers-1.1.0.tgz", @@ -5904,14 +5944,14 @@ "integrity": "sha512-hf+3bwuBwtXsugA2ULBc95qxrOqP2pOekLz34BJhcAKawt94vfeNyUKpYc0lZQ/3sCP6LqRa7UAdHA7i5UODzQ==" }, "@sapphire/async-queue": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.3.2.tgz", - "integrity": "sha512-rUpMLATsoAMnlN3gecAcr9Ecnw1vG7zi5Xr+IX22YzRzi1k9PF9vKzoT8RuEJbiIszjcimu3rveqUnvwDopz8g==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.0.tgz", + "integrity": "sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==" }, "@sapphire/shapeshift": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.5.1.tgz", - "integrity": "sha512-7JFsW5IglyOIUQI1eE0g6h06D/Far6HqpcowRScgCiLSqTf3hhkPWCWotVTtVycnDCMYIwPeaw6IEPBomKC8pA==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@sapphire/shapeshift/-/shapeshift-3.7.0.tgz", + "integrity": "sha512-A6vI1zJoxhjWo4grsxpBRBgk96SqSdjLX5WlzKp9H+bJbkM07mvwcbtbVAmUZHbi/OG3HLfiZ1rlw4BhH6tsBQ==", "requires": { "fast-deep-equal": "^3.1.3", "lodash.uniqwith": "^4.5.0" @@ -6432,6 +6472,14 @@ "ieee754": "^1.1.13" } }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -6731,26 +6779,27 @@ } }, "discord-api-types": { - "version": "0.36.3", - "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.36.3.tgz", - "integrity": "sha512-bz/NDyG0KBo/tY14vSkrwQ/n3HKPf87a0WFW/1M9+tXYK+vp5Z5EksawfCWo2zkAc6o7CClc0eff1Pjrqznlwg==" + "version": "0.37.20", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.20.tgz", + "integrity": "sha512-uAO+55E11rMkYR36/paE1vKN8c2bZa1mgrIaiQIBgIZRKZTDIGOZB+8I5eMRPFJcGxrg16riUu+0aTu2JQEPew==" }, "discord.js": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.1.1.tgz", - "integrity": "sha512-6Oa2f+Y0+s6B5HTTqcAP7Z6tUmguNTKpzbuTmE1JIeT/aUTr9dVe397D/bvcBSRpJERQzMyEWyEiVQnMRHcx4A==", - "requires": { - "@discordjs/builders": "^1.1.0", - "@discordjs/collection": "^1.0.1", - "@discordjs/rest": "^1.0.1", + "version": "14.6.0", + "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.6.0.tgz", + "integrity": "sha512-On1K7xpJZRe0KsziIaDih2ksYPhgxym/ZqV45i1f3yig4vUotikqs7qp5oXiTzQ/UTiNRCixUWFTh7vA1YBCqw==", + "requires": { + "@discordjs/builders": "^1.3.0", + "@discordjs/collection": "^1.2.0", + "@discordjs/rest": "^1.3.0", + "@discordjs/util": "^0.1.0", "@sapphire/snowflake": "^3.2.2", "@types/ws": "^8.5.3", - "discord-api-types": "^0.36.3", + "discord-api-types": "^0.37.12", "fast-deep-equal": "^3.1.3", "lodash.snakecase": "^4.1.1", "tslib": "^2.4.0", - "undici": "^5.8.0", - "ws": "^8.8.1" + "undici": "^5.11.0", + "ws": "^8.9.0" } }, "doctrine": { @@ -7457,13 +7506,13 @@ } }, "file-type": { - "version": "17.1.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.4.tgz", - "integrity": "sha512-3w/rJUUPBj6CYhVER3D5JCKwYJJiC36uj5dP+LnyubHI6H6FJo1TeWVCEA09YLVoZqV3/mLP26j9+Pz1GjAyjQ==", + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-18.0.0.tgz", + "integrity": "sha512-jjMwFpnW8PKofLE/4ohlhqwDk5k0NC6iy0UHAJFKoY1fQeGMN0GDdLgHQrvCbSpMwbqzoCZhRI5dETCZna5qVA==", "requires": { "readable-web-to-node-stream": "^3.0.2", - "strtok3": "^7.0.0-alpha.9", - "token-types": "^5.0.0-alpha.2" + "strtok3": "^7.0.0", + "token-types": "^5.0.1" } }, "fill-range": { @@ -8629,9 +8678,9 @@ "dev": true }, "peek-readable": { - "version": "5.0.0-alpha.5", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0-alpha.5.tgz", - "integrity": "sha512-pJohF/tDwV3ntnT5+EkUo4E700q/j/OCDuPxtM+5/kFGjyOai/sK4/We4Cy1MB2OiTQliWU5DxPvYIKQAdPqAA==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.0.0.tgz", + "integrity": "sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==" }, "picomatch": { "version": "2.3.1", @@ -9167,6 +9216,11 @@ "readable-stream": "^3.0.6" } }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -9273,12 +9327,12 @@ "dev": true }, "strtok3": { - "version": "7.0.0-alpha.9", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0-alpha.9.tgz", - "integrity": "sha512-G8WxjBFjTZ77toVElv1i7k3jCXNkBB14FVaZ/6LIOka/WGo4La5XHLrU7neFVLdKbXESZf4BejVKZu5maOmocA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.0.0.tgz", + "integrity": "sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==", "requires": { "@tokenizer/token": "^0.3.0", - "peek-readable": "^5.0.0-alpha.5" + "peek-readable": "^5.0.0" } }, "supports-color": { @@ -9385,9 +9439,9 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" }, "token-types": { - "version": "5.0.0-alpha.2", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.0-alpha.2.tgz", - "integrity": "sha512-EsG9UxAW4M6VATrEEjhPFTKEUi1OiJqTUMIZOGBN49fGxYjZB36k0p7to3HZSmWRoHm1QfZgrg3e02fpqAt5fQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz", + "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==", "requires": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" @@ -9421,9 +9475,9 @@ "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" }, "ts-mixer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.1.tgz", - "integrity": "sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==" + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.2.tgz", + "integrity": "sha512-zvHx3VM83m2WYCE8XL99uaM7mFwYSkjR2OZti98fabHrwkjsCvgwChda5xctein3xGOyaQhtTeDq/1H/GNvF3A==" }, "ts-node": { "version": "10.9.1", @@ -9467,9 +9521,9 @@ } }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==" }, "tsutils": { "version": "3.21.0", @@ -9536,9 +9590,12 @@ "dev": true }, "undici": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.8.0.tgz", - "integrity": "sha512-1F7Vtcez5w/LwH2G2tGnFIihuWUlc58YidwLiCv+jR2Z50x0tNXpRRw7eOIJ+GvqCqIkg9SB7NWAJ/T9TLfv8Q==" + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.13.0.tgz", + "integrity": "sha512-UDZKtwb2k7KRsK4SdXWG7ErXiL7yTGgLWvk2AXO1JMjgjh404nFo6tWSCM2xMpJwMPx3J8i/vfqEh1zOqvj82Q==", + "requires": { + "busboy": "^1.6.0" + } }, "unicode-byte-truncate": { "version": "1.0.0", @@ -9705,9 +9762,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "ws": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.8.1.tgz", - "integrity": "sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "requires": {} }, "y18n": { diff --git a/src/commands/index.ts b/src/commands/index.ts index 2f4edf19..d845c44e 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -154,6 +154,7 @@ import { flyPadAbout } from './support/flyPadAbout'; import { navdata } from './support/navdata'; import { winss } from './support/winss'; import { simridgeLog } from './support/simbridgeLog'; +import { sticky } from './moderation/sticky'; const commands: BaseCommandDefinition[] = [ typeCommand, @@ -310,6 +311,7 @@ const commands: BaseCommandDefinition[] = [ navdata, winss, simridgeLog, + sticky, ]; const commandsObject: { [k: string]: BaseCommandDefinition } = {}; diff --git a/src/commands/moderation/sticky.ts b/src/commands/moderation/sticky.ts new file mode 100644 index 00000000..1e4c42f5 --- /dev/null +++ b/src/commands/moderation/sticky.ts @@ -0,0 +1,460 @@ +import { Colors, EmbedField, BaseChannel, TextChannel, TextBasedChannel } from 'discord.js'; +import moment from 'moment'; +import { CommandDefinition } from '../../lib/command'; +import { Roles, Channels, CommandCategory } from '../../constants'; +import { makeEmbed } from '../../lib/embed'; +import { getConn } from '../../lib/db'; +import StickyMessage from '../../lib/schemas/stickyMessageSchema'; +import { stickyMessageEmbed, STICKY_MOD_TITLE } from '../../lib/stickyMessageEmbed'; +import Logger from '../../lib/logger'; + +const permittedRoles = [ + Roles.ADMIN_TEAM, + Roles.MODERATION_TEAM, +]; +const DEFAULT_TIME_INTERVAL = 15; +const DEFAULT_MESSAGE_COUNT = 5; +const MAX_TIME_INTERVAL = 86400; +const MAX_MESSAGE_COUNT = 50; +const MIN_TIME_INTERVAL = 15; +const MIN_MESSAGE_COUNT = 5; + +const helpEmbed = (evokedCommand: String) => makeEmbed({ + title: 'Manage Sticky Messages - Help', + description: 'A command to set and manage sticky messages for channels.', + fields: [ + { + name: 'Channel ID parameter', + value: 'If you do not provide a Channel ID, the command applies to the channel in which it is executed.', + inline: false, + }, + { + name: `Setting a sticky message: \`${evokedCommand} set [channel id] \``, + value: 'Sets the provided message as a sticky message for the channel in which the command is executed. A message can contain new lines and simple Markdown elements.', + inline: false, + }, + { + name: `Setting the image URL: \`${evokedCommand} image [channel id] \``, + value: 'An image URL can be configured which gets posted as part of the sticky.', + inline: false, + }, + { + name: `Setting the minimum message count: \`${evokedCommand} count [channel id] \``, + value: 'If neither the minimum message count have been posted in the channel or the minimum time interval has passed, the sticky will not be posted. This prevents spamming a channel.', + inline: false, + }, + { + name: `Setting the minimum time interval: \`${evokedCommand} time [channel id] \``, + value: 'If neither the minimum message count have been posted in the channel or the minimum time interval has passed, the sticky will not be posted. This prevents spamming a channel.', + inline: false, + }, + { + name: `Removing a sticky message: \`${evokedCommand} unset [channel id]\``, + value: 'Removes the configured sticky message from the channel and stop posting it.', + inline: false, + }, + { + name: `Showing info about a sticky message: \`${evokedCommand} info [channel id]\``, + value: 'Shows all the information available for a configured sticky message for a channel.', + inline: false, + }, + ], +}); + +const infoEmbed = (fields: EmbedField[]) => makeEmbed({ + title: 'Sticky Message - Info', + fields, +}); + +const failedEmbed = (action: string, channel: string) => makeEmbed({ + title: `Sticky Message - ${action} failed`, + description: `Failed to ${action} the sticky message for channel <#${channel}>, change not saved to mongoDB.`, + color: Colors.Red, +}); + +const modLogEmbed = (action: string, fields: any, color: number) => makeEmbed({ + title: `${STICKY_MOD_TITLE} - ${action}`, + fields, + color, +}); + +const missingInfoEmbed = (action: string, information: string) => makeEmbed({ + title: `Sticky Message - ${action} - missing information`, + description: `${information}`, + color: Colors.Red, +}); + +const missingChannelEmbed = (channel: string) => makeEmbed({ + title: 'Sticky Message - Channel does not exist', + description: `The Channel ID ${channel} does not refer to an existing Text or Forum channel, or a Thread. Please provide a valid channel.`, + color: Colors.Red, +}); + +const noChannelEmbed = (action:string, channelName: string) => makeEmbed({ + title: `Sticky Message - ${action} - No ${channelName} channel`, + description: `The command was successful, but no message to ${channelName} was sent. Please check the channel still exists.`, + color: Colors.Yellow, +}); + +const noConnEmbed = makeEmbed({ + title: 'Sticky Message - No Connection', + description: 'Could not connect to the database.', + color: Colors.Red, +}); + +const noPermEmbed = makeEmbed({ + title: 'Sticky Message - Permission missing', + description: 'You do not have permission to use this command.', + color: Colors.Red, +}); + +const notFoundEmbed = (action: string, channel: string) => makeEmbed({ + title: `Sticky Message - ${action} - Not found`, + description: `No Sticky Message for <#${channel}> was found.`, + color: Colors.Red, +}); + +const updatedTimestampText = (updatedTimestamp: Date) => moment(updatedTimestamp).utcOffset(0).format('YYYY-MM-DD HH:mm:ss'); + +const stickyMessageEmbedField = (updatedTimestampText: string, moderator: string, message: string, imageUrl: string, timeInterval: string, messageCount: string, channel: string): EmbedField[] => [ + { + inline: true, + name: 'Channel', + value: `<#${channel}>`, + }, + { + inline: true, + name: 'Moderator', + value: moderator, + }, + { + inline: true, + name: 'Last updated timestamp', + value: updatedTimestampText, + }, + { + inline: true, + name: 'Minimum time interval', + value: timeInterval, + }, + { + inline: true, + name: 'Minimum message count', + value: messageCount, + }, + { + inline: true, + name: 'Image URL', + value: imageUrl || 'Empty', + }, + { + inline: false, + name: 'Message', + value: message, + }, +]; + +export const sticky: CommandDefinition = { + name: ['sticky'], + description: 'Manages sticky messages.', + category: CommandCategory.MODERATION, + executor: async (msg) => { + const subCommands = ['set', 'image', 'count', 'time', 'unset', 'info']; + const conn = getConn(); + if (!conn) { + return msg.channel.send({ embeds: [noConnEmbed] }); + } + + const hasPermittedRole = msg.member.roles.cache.some((role) => permittedRoles.map((r) => r.toString()).includes(role.id)); + if (!hasPermittedRole) { + return msg.channel.send({ embeds: [noPermEmbed] }); + } + + const modLogsChannel = msg.guild.channels.resolve(Channels.MOD_LOGS) as TextChannel | null; + const { author } = msg; + const [evokedCommand] = msg.content.trim().split(/\s+/); + const args = msg.content.replace(evokedCommand, '').trim(); + if (!args || args === 'help') { + return msg.channel.send({ embeds: [helpEmbed(evokedCommand)] }); + } + + let [subCommand] = args.split(/\s+/); + let subArgs = args.replace(subCommand, '').trim(); + if (!subCommands.includes(subCommand)) { + subCommand = 'set'; + subArgs = args; + } + + let { channelId } = msg; + const regexCheck = /^(?:(?[\d]{6,}))\s*.*$/s; + const regexMatches = subArgs.match(regexCheck); + if (regexMatches && regexMatches.groups.channelId) { + ({ channelId } = regexMatches.groups); + subArgs = subArgs.replace(channelId, '').trim(); + } + const stickiedChannel = msg.guild.channels.resolve(channelId) as BaseChannel | null; + if (!stickiedChannel || ['TextChannel', 'ForumChannel', 'ThreadChannel'].indexOf(stickiedChannel.constructor.name) === -1) { + return msg.channel.send({ embeds: [missingChannelEmbed(channelId)] }); + } + + const stickyMessageSearchResult = await StickyMessage.find({ channelId }); + let [stickyMessage] = stickyMessageSearchResult.length === 1 ? stickyMessageSearchResult : []; + + if (subCommand === 'set') { + const regexCheck = /^(?[\S].+[\S])\s*$/s; + const regexMatches = subArgs.match(regexCheck); + if (regexMatches === null || !regexMatches.groups.message) { + return msg.channel.send({ embeds: [missingInfoEmbed('Set', `You need to provide the expected format to create a sticky message. Check \`${evokedCommand} help\` for more details.`)] }); + } + + const { message } = regexMatches.groups; + if (!stickyMessage) { + stickyMessage = new StickyMessage({ + channelId, + timeInterval: DEFAULT_TIME_INTERVAL, + messageCount: DEFAULT_MESSAGE_COUNT, + }); + } + stickyMessage.message = message; + stickyMessage.moderator = author.toString(); + stickyMessage.updatedTimestamp = new Date(); + if (['TextChannel', 'ThreadChannel'].indexOf(stickiedChannel.constructor.name) >= 0) { + if (stickyMessage.lastPostedId) { + try { + const previousSticky = await (stickiedChannel as TextBasedChannel).messages.fetch(stickyMessage.lastPostedId); + previousSticky.delete(); + } catch { + Logger.debug('Last posted sticky can not be found or deleted, ignoring and continuing.'); + } + } + const currentSticky = await (stickiedChannel as TextBasedChannel).send({ embeds: [stickyMessageEmbed(stickyMessage.message, stickyMessage.imageUrl)] }); + stickyMessage.lastPostedId = currentSticky.id; + } + + try { + stickyMessage.save(); + } catch { + return msg.channel.send({ embeds: [failedEmbed('Set', channelId)] }); + } + + try { + await modLogsChannel.send({ + embeds: [modLogEmbed('Set', + stickyMessageEmbedField( + updatedTimestampText(stickyMessage.updatedTimestamp), + stickyMessage.moderator, + stickyMessage.message, + stickyMessage.imageUrl, + `${stickyMessage.timeInterval}`, + `${stickyMessage.messageCount}`, + stickyMessage.channelId, + ), + Colors.Green)], + }); + } catch { + msg.channel.send({ embeds: [noChannelEmbed('Set', 'Mod Log')] }); + } + + return msg.react('✅'); + } + + if (subCommand === 'image') { + if (!stickyMessage) { + return msg.channel.send({ embeds: [notFoundEmbed('Configure image URL', channelId)] }); + } + + const regexCheck = /^(?https?:\/\/[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_+.~#?&/=]*))\s*$/; + const regexMatches = subArgs.match(regexCheck); + if (regexMatches === null || !regexMatches.groups.imageUrl) { + return msg.channel.send({ embeds: [missingInfoEmbed('Configure image URL', `You need to provide a URL for the image to include in the sticky. Check \`${evokedCommand} help\` for more details.`)] }); + } + + const { imageUrl } = regexMatches.groups; + stickyMessage.imageUrl = imageUrl; + stickyMessage.moderator = author.toString(); + stickyMessage.updatedTimestamp = new Date(); + + try { + stickyMessage.save(); + } catch { + return msg.channel.send({ embeds: [failedEmbed('Configure image URL', channelId)] }); + } + + try { + await modLogsChannel.send({ + embeds: [modLogEmbed('Configure image URL', + stickyMessageEmbedField( + updatedTimestampText(stickyMessage.updatedTimestamp), + stickyMessage.moderator, + stickyMessage.message, + stickyMessage.imageUrl, + `${stickyMessage.timeInterval}`, + `${stickyMessage.messageCount}`, + stickyMessage.channelId, + ), + Colors.Green)], + }); + } catch { + msg.channel.send({ embeds: [noChannelEmbed('Configure image URL', 'Mod Log')] }); + } + + return msg.react('✅'); + } + + if (subCommand === 'count') { + if (!stickyMessage) { + return msg.channel.send({ embeds: [notFoundEmbed('Configure minimum message count', channelId)] }); + } + + const regexCheck = /^(?[\d]+)\s*$/; + const regexMatches = subArgs.match(regexCheck); + if (regexMatches === null || !regexMatches.groups.messageCount) { + return msg.channel.send({ embeds: [missingInfoEmbed('Configure minimum message count', `You need to provide a number to set the minimum message count for a sticky message. Check \`${evokedCommand} help\` for more details.`)] }); + } + + const { messageCount } = regexMatches.groups; + if (parseInt(messageCount) < MIN_MESSAGE_COUNT || parseInt(messageCount) > MAX_MESSAGE_COUNT) { + return msg.channel.send({ embeds: [missingInfoEmbed('Configure minimum message count', `You need to provide a number between ${MIN_MESSAGE_COUNT} and ${MAX_MESSAGE_COUNT} to set the minimum message count for a sticky message. Check \`${evokedCommand} help\` for more details.`)] }); + } + stickyMessage.messageCount = parseInt(messageCount); + stickyMessage.moderator = author.toString(); + stickyMessage.updatedTimestamp = new Date(); + + try { + stickyMessage.save(); + } catch { + return msg.channel.send({ embeds: [failedEmbed('Configure minimum message count', channelId)] }); + } + + try { + await modLogsChannel.send({ + embeds: [modLogEmbed('Configure minimum message count', + stickyMessageEmbedField( + updatedTimestampText(stickyMessage.updatedTimestamp), + stickyMessage.moderator, + stickyMessage.message, + stickyMessage.imageUrl, + `${stickyMessage.timeInterval}`, + `${stickyMessage.messageCount}`, + stickyMessage.channelId, + ), + Colors.Green)], + }); + } catch { + msg.channel.send({ embeds: [noChannelEmbed('Configure minimum message count', 'Mod Log')] }); + } + + return msg.react('✅'); + } + + if (subCommand === 'time') { + if (!stickyMessage) { + return msg.channel.send({ embeds: [notFoundEmbed('Configure minimum time interval', channelId)] }); + } + + const regexCheck = /^(?[\d]+)\s*$/; + const regexMatches = subArgs.match(regexCheck); + if (regexMatches === null || !regexMatches.groups.timeInterval) { + return msg.channel.send({ embeds: [missingInfoEmbed('Configure minimum time interval', `You need to provide a number to set the minimum time interval for a sticky message. Check \`${evokedCommand} help\` for more details.`)] }); + } + + const { timeInterval } = regexMatches.groups; + if (parseInt(timeInterval) < MIN_TIME_INTERVAL || parseInt(timeInterval) > MAX_TIME_INTERVAL) { + return msg.channel.send({ embeds: [missingInfoEmbed('Configure minimum time interval', `You need to provide a number between ${MIN_TIME_INTERVAL} and ${MAX_TIME_INTERVAL} to set the minimum time interval for a sticky message. Check \`${evokedCommand} help\` for more details.`)] }); + } + stickyMessage.timeInterval = parseInt(timeInterval); + stickyMessage.moderator = author.toString(); + stickyMessage.updatedTimestamp = new Date(); + + try { + stickyMessage.save(); + } catch { + return msg.channel.send({ embeds: [failedEmbed('Configure minimum message count', channelId)] }); + } + + try { + await modLogsChannel.send({ + embeds: [modLogEmbed('Configure minimum message count', + stickyMessageEmbedField( + updatedTimestampText(stickyMessage.updatedTimestamp), + stickyMessage.moderator, + stickyMessage.message, + stickyMessage.imageUrl, + `${stickyMessage.timeInterval}`, + `${stickyMessage.messageCount}`, + stickyMessage.channelId, + ), + Colors.Green)], + }); + } catch { + msg.channel.send({ embeds: [noChannelEmbed('Configure minimum message count', 'Mod Log')] }); + } + + return msg.react('✅'); + } + + if (subCommand === 'unset') { + if (!stickyMessage) { + return msg.channel.send({ embeds: [notFoundEmbed('Delete', channelId)] }); + } + + try { + if (stickyMessage.lastPostedId && ['TextChannel', 'ThreadChannel'].indexOf(stickiedChannel.constructor.name) >= 0) { + try { + const previousSticky = await (stickiedChannel as TextBasedChannel).messages.fetch(stickyMessage.lastPostedId); + previousSticky.delete(); + } catch { + Logger.debug('Last posted sticky can not be found or deleted, ignoring and continuing.'); + } + } + stickyMessage.delete(); + } catch { + return msg.channel.send({ embeds: [failedEmbed('Delete', channelId)] }); + } + + try { + await modLogsChannel.send({ + embeds: [modLogEmbed('Delete', + stickyMessageEmbedField( + updatedTimestampText(stickyMessage.updatedTimestamp), + stickyMessage.moderator, + stickyMessage.message, + stickyMessage.imageUrl, + `${stickyMessage.timeInterval}`, + `${stickyMessage.messageCount}`, + stickyMessage.channelId, + ), + Colors.Red)], + }); + } catch { + msg.channel.send({ embeds: [noChannelEmbed('Delete', 'Mod Log')] }); + } + + return msg.react('✅'); + } + + if (subCommand === 'info') { + const searchResult = await StickyMessage.find({ channelId }); + if (searchResult.length === 0) { + return msg.channel.send({ embeds: [notFoundEmbed('Info', channelId)] }); + } + + const [stickyMessage] = searchResult; + return msg.channel.send({ + embeds: [infoEmbed( + stickyMessageEmbedField( + updatedTimestampText(stickyMessage.updatedTimestamp), + stickyMessage.moderator, + stickyMessage.message, + stickyMessage.imageUrl, + `${stickyMessage.timeInterval}`, + `${stickyMessage.messageCount}`, + stickyMessage.channelId, + ), + )], + }); + } + + return msg.channel.send({ embeds: [helpEmbed(evokedCommand)] }); + }, +}; diff --git a/src/handlers/forumPostCreate.ts b/src/handlers/forumPostCreate.ts new file mode 100644 index 00000000..a79b1e6d --- /dev/null +++ b/src/handlers/forumPostCreate.ts @@ -0,0 +1,35 @@ +import { getConn } from '../lib/db'; +import Logger from '../lib/logger'; +import StickyMessage from '../lib/schemas/stickyMessageSchema'; +import { stickyMessageEmbed } from '../lib/stickyMessageEmbed'; + +module.exports = { + event: 'threadCreate', + executor: async (thread) => { + const { parentId, parent, name } = thread; + Logger.debug(`Thread Create - Handling thread create for ${name}`); + const { name: parentName, type } = parent; + Logger.debug(`Thread Create - Parent ID: ${parentId} - Parent Type: ${type} - Parent Name: ${parentName}`); + if (type !== 15) { + Logger.debug('Thread Create - Thread not created in a Forum Channel, skipping.'); + return; + } + + const conn = getConn(); + if (!conn) { + Logger.debug('Thread Create - Unable to connect to database, skipping.'); + return; + } + + const stickyMessageSearchResult = await StickyMessage.find({ channelId: parentId }); + const [stickyMessage] = stickyMessageSearchResult.length === 1 ? stickyMessageSearchResult : []; + if (!stickyMessage) { + Logger.debug('Forum Post Create - No sticky for the forum, skipping.'); + return; + } + + const { message, imageUrl } = stickyMessage; + Logger.debug('Forum Post Create - Posting new sticky.'); + await thread.send({ embeds: [stickyMessageEmbed(message, imageUrl)] }); + }, +}; diff --git a/src/lib/schemas/stickyMessageSchema.ts b/src/lib/schemas/stickyMessageSchema.ts new file mode 100644 index 00000000..b33726e9 --- /dev/null +++ b/src/lib/schemas/stickyMessageSchema.ts @@ -0,0 +1,19 @@ +import mongoose, { Schema } from 'mongoose'; + +const stickyMessageSchema = new Schema({ + channelId: { + type: String, + unique: true, + }, + message: String, + imageUrl: String, + timeInterval: Number, + messageCount: Number, + moderator: String, + updatedTimestamp: Date, + lastPostedId: String, +}); + +const stickyMessage = mongoose.model('StickyMessage', stickyMessageSchema); + +export default stickyMessage; diff --git a/src/lib/stickyMessageEmbed.ts b/src/lib/stickyMessageEmbed.ts new file mode 100644 index 00000000..b654e4d4 --- /dev/null +++ b/src/lib/stickyMessageEmbed.ts @@ -0,0 +1,12 @@ +import { Colors } from '../constants'; +import { makeEmbed } from './embed'; + +export const STICKY_MESSAGE_TITLE = 'Stickied Message'; +export const STICKY_MOD_TITLE = 'Sticky Message - Log'; + +export const stickyMessageEmbed = (description: string, imageUrl: string) => makeEmbed({ + title: STICKY_MESSAGE_TITLE, + description, + color: Colors.FBW_CYAN, + image: imageUrl ? { url: imageUrl } : null, +}); From 80237bbc93d56452bdb0379a2901d2f2358fb967 Mon Sep 17 00:00:00 2001 From: Philippe Dellaert Date: Fri, 25 Nov 2022 23:32:28 -0800 Subject: [PATCH 2/5] CHANGELOG update for stickies for forum posts --- .github/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index f762d3e3..6df73df0 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,6 +2,7 @@ Update _ December 2022 +- feat: stickies for forum posts which get posted on forum post create (14/12/2022) - refactor: outdated information + links in commands (08/12/2022) - fix: fix latlong image typo and prime meridian alignment (04/12/2022) - refactor: update wb command for change in Stable (03/12/2022) From c22e659d32c259414df99d3f524b390dcac2c469 Mon Sep 17 00:00:00 2001 From: Philippe Dellaert Date: Sat, 26 Nov 2022 20:09:53 -0800 Subject: [PATCH 3/5] Setting default action to info to avoid accidental overwriting sticky config --- src/commands/moderation/sticky.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/moderation/sticky.ts b/src/commands/moderation/sticky.ts index 1e4c42f5..8d140c34 100644 --- a/src/commands/moderation/sticky.ts +++ b/src/commands/moderation/sticky.ts @@ -181,7 +181,7 @@ export const sticky: CommandDefinition = { let [subCommand] = args.split(/\s+/); let subArgs = args.replace(subCommand, '').trim(); if (!subCommands.includes(subCommand)) { - subCommand = 'set'; + subCommand = 'info'; subArgs = args; } From 0f3a0adc7b7b7e326215296fb14b5e4cb5b1d42e Mon Sep 17 00:00:00 2001 From: Philippe Dellaert Date: Sat, 26 Nov 2022 21:42:49 -0800 Subject: [PATCH 4/5] DOC update for sticky command --- .github/command-docs.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/command-docs.md b/.github/command-docs.md index b48cb529..ad0fd752 100644 --- a/.github/command-docs.md +++ b/.github/command-docs.md @@ -173,6 +173,7 @@ | .faq | Sends the FAQ | --- | | .roleassignment | Sends the role assignment messages | --- | | .rules | Sends the rules | --- | +| .sticky | Manage sticky messages which are posted in Forum Posts when a new post is created | --- | | .temporarycommandedit | Manage temporary commands, which are simple output commands to highlight temporary situations to users. | .tempcommandedit
.tcedit
.tcmod | | .timeout | --- | --- | | .unban | --- | --- | From 605b3f00cdf8a51074b85fbd44c908afe952df33 Mon Sep 17 00:00:00 2001 From: BenW Date: Mon, 19 Dec 2022 11:05:54 +0000 Subject: [PATCH 5/5] Update CHANGELOG.md --- .github/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 6df73df0..b323e413 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,7 +2,7 @@ Update _ December 2022 -- feat: stickies for forum posts which get posted on forum post create (14/12/2022) +- feat: stickies for forum posts which get posted on forum post create (19/12/2022) - refactor: outdated information + links in commands (08/12/2022) - fix: fix latlong image typo and prime meridian alignment (04/12/2022) - refactor: update wb command for change in Stable (03/12/2022)