Adds initial features

This commit is contained in:
Matheus Albino
2024-10-16 03:47:20 -03:00
commit 1e55753bbb
17 changed files with 791 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules
.env

2
.mise.toml Normal file
View File

@@ -0,0 +1,2 @@
[tools]
node = "22.9.0"

30
biome.json Normal file
View File

@@ -0,0 +1,30 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.3/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"ignoreUnknown": false,
"ignore": []
},
"formatter": {
"enabled": true,
"indentStyle": "tab"
},
"organizeImports": {
"enabled": true
},
"linter": {
"enabled": true,
"rules": {
"recommended": true
}
},
"javascript": {
"formatter": {
"quoteStyle": "double"
}
}
}

514
package-lock.json generated Normal file
View File

@@ -0,0 +1,514 @@
{
"name": "discord-repost-sniffer",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "discord-repost-sniffer",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"discord.js": "^14.16.3"
},
"devDependencies": {
"@biomejs/biome": "1.9.3",
"typescript": "^5.6.3"
}
},
"node_modules/@biomejs/biome": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.3.tgz",
"integrity": "sha512-POjAPz0APAmX33WOQFGQrwLvlu7WLV4CFJMlB12b6ZSg+2q6fYu9kZwLCOA+x83zXfcPd1RpuWOKJW0GbBwLIQ==",
"dev": true,
"hasInstallScript": true,
"license": "MIT OR Apache-2.0",
"bin": {
"biome": "bin/biome"
},
"engines": {
"node": ">=14.21.3"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/biome"
},
"optionalDependencies": {
"@biomejs/cli-darwin-arm64": "1.9.3",
"@biomejs/cli-darwin-x64": "1.9.3",
"@biomejs/cli-linux-arm64": "1.9.3",
"@biomejs/cli-linux-arm64-musl": "1.9.3",
"@biomejs/cli-linux-x64": "1.9.3",
"@biomejs/cli-linux-x64-musl": "1.9.3",
"@biomejs/cli-win32-arm64": "1.9.3",
"@biomejs/cli-win32-x64": "1.9.3"
}
},
"node_modules/@biomejs/cli-darwin-arm64": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.3.tgz",
"integrity": "sha512-QZzD2XrjJDUyIZK+aR2i5DDxCJfdwiYbUKu9GzkCUJpL78uSelAHAPy7m0GuPMVtF/Uo+OKv97W3P9nuWZangQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-darwin-x64": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.3.tgz",
"integrity": "sha512-vSCoIBJE0BN3SWDFuAY/tRavpUtNoqiceJ5PrU3xDfsLcm/U6N93JSM0M9OAiC/X7mPPfejtr6Yc9vSgWlEgVw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-linux-arm64": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.3.tgz",
"integrity": "sha512-vJkAimD2+sVviNTbaWOGqEBy31cW0ZB52KtpVIbkuma7PlfII3tsLhFa+cwbRAcRBkobBBhqZ06hXoZAN8NODQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-linux-arm64-musl": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.3.tgz",
"integrity": "sha512-VBzyhaqqqwP3bAkkBrhVq50i3Uj9+RWuj+pYmXrMDgjS5+SKYGE56BwNw4l8hR3SmYbLSbEo15GcV043CDSk+Q==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-linux-x64": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.3.tgz",
"integrity": "sha512-x220V4c+romd26Mu1ptU+EudMXVS4xmzKxPVb9mgnfYlN4Yx9vD5NZraSx/onJnd3Gh/y8iPUdU5CDZJKg9COA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-linux-x64-musl": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.3.tgz",
"integrity": "sha512-TJmnOG2+NOGM72mlczEsNki9UT+XAsMFAOo8J0me/N47EJ/vkLXxf481evfHLlxMejTY6IN8SdRSiPVLv6AHlA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-win32-arm64": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.3.tgz",
"integrity": "sha512-lg/yZis2HdQGsycUvHWSzo9kOvnGgvtrYRgoCEwPBwwAL8/6crOp3+f47tPwI/LI1dZrhSji7PNsGKGHbwyAhw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-win32-x64": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.3.tgz",
"integrity": "sha512-cQMy2zanBkVLpmmxXdK6YePzmZx0s5Z7KEnwmrW54rcXK3myCNbQa09SwGZ8i/8sLw0H9F3X7K4rxVNGU8/D4Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@discordjs/builders": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-1.9.0.tgz",
"integrity": "sha512-0zx8DePNVvQibh5ly5kCEei5wtPBIUbSoE9n+91Rlladz4tgtFbJ36PZMxxZrTEOQ7AHMZ/b0crT/0fCy6FTKg==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/formatters": "^0.5.0",
"@discordjs/util": "^1.1.1",
"@sapphire/shapeshift": "^4.0.0",
"discord-api-types": "0.37.97",
"fast-deep-equal": "^3.1.3",
"ts-mixer": "^6.0.4",
"tslib": "^2.6.3"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/builders/node_modules/discord-api-types": {
"version": "0.37.97",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz",
"integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==",
"license": "MIT"
},
"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==",
"license": "Apache-2.0",
"engines": {
"node": ">=16.11.0"
}
},
"node_modules/@discordjs/formatters": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@discordjs/formatters/-/formatters-0.5.0.tgz",
"integrity": "sha512-98b3i+Y19RFq1Xke4NkVY46x8KjJQjldHUuEbCqMvp1F5Iq9HgnGpu91jOi/Ufazhty32eRsKnnzS8n4c+L93g==",
"license": "Apache-2.0",
"dependencies": {
"discord-api-types": "0.37.97"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/formatters/node_modules/discord-api-types": {
"version": "0.37.97",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz",
"integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==",
"license": "MIT"
},
"node_modules/@discordjs/rest": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.0.tgz",
"integrity": "sha512-Xb2irDqNcq+O8F0/k/NaDp7+t091p+acb51iA4bCKfIn+WFWd6HrNvcsSbMMxIR9NjcMZS6NReTKygqiQN+ntw==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.1",
"@discordjs/util": "^1.1.1",
"@sapphire/async-queue": "^1.5.3",
"@sapphire/snowflake": "^3.5.3",
"@vladfrangu/async_event_emitter": "^2.4.6",
"discord-api-types": "0.37.97",
"magic-bytes.js": "^1.10.0",
"tslib": "^2.6.3",
"undici": "6.19.8"
},
"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/discord-api-types": {
"version": "0.37.97",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.97.tgz",
"integrity": "sha512-No1BXPcVkyVD4ZVmbNgDKaBoqgeQ+FJpzZ8wqHkfmBnTZig1FcH3iPPersiK1TUIAzgClh2IvOuVUYfcWLQAOA==",
"license": "MIT"
},
"node_modules/@discordjs/util": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz",
"integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==",
"license": "Apache-2.0",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/@discordjs/ws": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@discordjs/ws/-/ws-1.1.1.tgz",
"integrity": "sha512-PZ+vLpxGCRtmr2RMkqh8Zp+BenUaJqlS6xhgWKEZcgC/vfHLEzpHtKkB0sl3nZWpwtcKk6YWy+pU3okL2I97FA==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/collection": "^2.1.0",
"@discordjs/rest": "^2.3.0",
"@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.37.83",
"tslib": "^2.6.2",
"ws": "^8.16.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/@discordjs/ws/node_modules/discord-api-types": {
"version": "0.37.83",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.83.tgz",
"integrity": "sha512-urGGYeWtWNYMKnYlZnOnDHm8fVRffQs3U0SpE8RHeiuLKb/u92APS8HoQnPTFbnXmY1vVnXjXO4dOxcAn3J+DA==",
"license": "MIT"
},
"node_modules/@sapphire/async-queue": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.3.tgz",
"integrity": "sha512-x7zadcfJGxFka1Q3f8gCts1F0xMwCKbZweM85xECGI0hBTeIZJGGCrHgLggihBoprlQ/hBmDR5LKfIPqnmHM3w==",
"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/@types/node": {
"version": "22.7.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz",
"integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/@types/ws": {
"version": "8.5.12",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz",
"integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==",
"license": "MIT",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@vladfrangu/async_event_emitter": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz",
"integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==",
"license": "MIT",
"engines": {
"node": ">=v14.0.0",
"npm": ">=7.0.0"
}
},
"node_modules/discord-api-types": {
"version": "0.37.100",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.100.tgz",
"integrity": "sha512-a8zvUI0GYYwDtScfRd/TtaNBDTXwP5DiDVX7K5OmE+DRT57gBqKnwtOC5Ol8z0mRW8KQfETIgiB8U0YZ9NXiCA==",
"license": "MIT"
},
"node_modules/discord.js": {
"version": "14.16.3",
"resolved": "https://registry.npmjs.org/discord.js/-/discord.js-14.16.3.tgz",
"integrity": "sha512-EPCWE9OkA9DnFFNrO7Kl1WHHDYFXu3CNVFJg63bfU7hVtjZGyhShwZtSBImINQRWxWP2tgo2XI+QhdXx28r0aA==",
"license": "Apache-2.0",
"dependencies": {
"@discordjs/builders": "^1.9.0",
"@discordjs/collection": "1.5.3",
"@discordjs/formatters": "^0.5.0",
"@discordjs/rest": "^2.4.0",
"@discordjs/util": "^1.1.1",
"@discordjs/ws": "1.1.1",
"@sapphire/snowflake": "3.5.3",
"discord-api-types": "0.37.100",
"fast-deep-equal": "3.1.3",
"lodash.snakecase": "4.1.1",
"tslib": "^2.6.3",
"undici": "6.19.8"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/discordjs/discord.js?sponsor"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"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==",
"license": "MIT"
},
"node_modules/magic-bytes.js": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz",
"integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==",
"license": "MIT"
},
"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.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz",
"integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==",
"license": "0BSD"
},
"node_modules/typescript": {
"version": "5.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz",
"integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz",
"integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==",
"license": "MIT",
"engines": {
"node": ">=18.17"
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"license": "MIT"
},
"node_modules/ws": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
}
}
}

21
package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "discord-repost-sniffer",
"type": "module",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node --experimental-strip-types --env-file=.env src/bot.ts",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"discord.js": "^14.16.3"
},
"devDependencies": {
"@biomejs/biome": "1.9.3",
"typescript": "^5.6.3"
}
}

View File

@@ -0,0 +1,15 @@
import { TextChannel, type Message } from "discord.js";
export default function attemptSendAuthorMessage(message: Message): void {
if (!(message.channel instanceof TextChannel)) return;
if (!message.guild) return;
message.author
.send(
`Hey! [__O link que voce mandou__](<${message.content}>) no canal "_${message.channel.name}_" do servidor "_${message.guild.name}_" ja foi mandado no passado!
A mensagem que voce mandou foi apagada para evitar que os chatos comentem "repost".
De nada!`,
)
.then()
.catch((_) => null);
}

41
src/bot.ts Normal file
View File

@@ -0,0 +1,41 @@
import { Client, Events, GatewayIntentBits } from "discord.js";
import type { Message } from "discord.js";
import { readyHandler } from "./ready.ts";
import feedMessagesArray from "./feedMessagesArray.ts";
import updateMessagesArray from "./updateMessagesArray.ts";
import checkForReposts from "./checkForReposts.ts";
import attemptSendAuthorMessage from "./attemptSendUserMessage.ts";
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
let messagesArr: Message[];
client.once(Events.ClientReady, async (client) => {
readyHandler(client);
messagesArr = await feedMessagesArray(client);
});
client.on(Events.MessageCreate, async (message) => {
console.log(message);
const originalMessages = checkForReposts(messagesArr, message);
console.log(originalMessages);
if (originalMessages.length >= 1) {
message.delete();
attemptSendAuthorMessage(message);
} else {
messagesArr = updateMessagesArray(messagesArr, message);
}
});
// // TODO, updateMessagesArray on message deletion
// client.on(Events.MessageDelete, async (message) => {
// });
await client.login(process.env.DISCORD_TOKEN);

22
src/checkForReposts.ts Normal file
View File

@@ -0,0 +1,22 @@
import type { Message } from "discord.js";
import { clearUrl } from "./utils/url.ts";
import { transformMessageArrayInMap } from "./utils/message.ts";
export default function checkForReposts(
messagesArray: Message[],
newMessage: Message,
): Message[] {
const urlMessagesMap = transformMessageArrayInMap(messagesArray);
const newMessageClearedUrl = clearUrl(newMessage.content);
if (!newMessageClearedUrl) return [];
const urlMessagesMatchArray = urlMessagesMap.get(newMessageClearedUrl);
if (!urlMessagesMatchArray) return [];
if (urlMessagesMatchArray.length > 1) return urlMessagesMatchArray;
return [];
}

View File

@@ -0,0 +1,33 @@
import type { Message } from "discord.js";
import type MessageInfoWithReposts from "./interfaces/MessageInfoWithReposts.ts";
import { transformMessageArrayInMap } from "./utils/message.ts";
export default function checkMessagesArrayForReposts(
messagesArray: Message[],
): Map<string, MessageInfoWithReposts> {
const urlMessagesMap = transformMessageArrayInMap(messagesArray);
const messageInfoWithRepostsMap = new Map<string, MessageInfoWithReposts>();
for (const [url, messagesMentioningUrlArray] of urlMessagesMap) {
if (messagesMentioningUrlArray.length === 1) continue;
const originalMessage = messagesMentioningUrlArray.reduce((prev, curr) =>
prev.createdTimestamp < curr.createdTimestamp ? prev : curr,
);
const messageInfoWithRepostsMapPosition =
messageInfoWithRepostsMap.get(url);
if (!messageInfoWithRepostsMapPosition) {
messageInfoWithRepostsMap.set(url, {
originalMessage,
repetitions: messagesMentioningUrlArray.filter(
(elm) => elm !== originalMessage,
),
});
}
}
return messageInfoWithRepostsMap;
}

34
src/feedMessagesArray.ts Normal file
View File

@@ -0,0 +1,34 @@
import type { Client, Message } from "discord.js";
export default async function feedMessagesArray(
client: Client,
): Promise<Message[]> {
const messagesDataArr: Message[] = [];
const guilds = await client.guilds.fetch();
for (const [, guildToFetch] of guilds) {
const guild = await guildToFetch.fetch();
const channels = await guild.channels.fetch();
for (const [, channel] of channels) {
if (!channel || channel.type !== 0) continue;
const messages = await channel.messages.fetch();
for (const [, message] of messages) {
if (message.type !== 0) continue;
const messageIsUrl = message.content.startsWith("https://");
// const messageAttachments = message.attachments
// if (!messageIsUrl && messageAttachments.size == 0) continue;
if (!messageIsUrl) continue;
messagesDataArr.push(message);
}
}
}
return messagesDataArr;
}

View File

@@ -0,0 +1,6 @@
import type { Message } from "discord.js";
export default interface MessageInfoWithReposts {
originalMessage: Message;
repetitions: Message[];
}

5
src/ready.ts Normal file
View File

@@ -0,0 +1,5 @@
import type { Client } from "discord.js";
export function readyHandler(c: Client<true>): void {
console.log(`Ready! Logged in as ${c.user.tag}`);
}

View File

@@ -0,0 +1,8 @@
import type { Message } from "discord.js";
export default function updateMessagesArray(
messagesArray: Message[],
newMessage: Message,
): Message[] {
return [...messagesArray, newMessage];
}

7
src/utils/logging.ts Normal file
View File

@@ -0,0 +1,7 @@
import { inspect } from "node:util";
export default function logObject(variable: object): void {
console.log(
inspect(variable, { showHidden: false, depth: null, colors: true }),
);
}

25
src/utils/message.ts Normal file
View File

@@ -0,0 +1,25 @@
import type { Message } from "discord.js";
import { clearUrl } from "./url.ts";
export function transformMessageArrayInMap(
messagesArray: Message[],
): Map<string, Message[]> {
const urlMessagesMap = new Map<string, Message[]>();
for (const [, message] of Object.entries(messagesArray)) {
const messageContent = message.content;
const clearedUrl = clearUrl(messageContent);
if (!clearedUrl) continue;
if (!urlMessagesMap.get(clearedUrl)) {
urlMessagesMap.set(clearedUrl, []);
}
// biome-ignore lint: will never be undefined
urlMessagesMap.get(clearedUrl)!.push(message);
}
return urlMessagesMap;
}

7
src/utils/url.ts Normal file
View File

@@ -0,0 +1,7 @@
export function clearUrl(url: string): string {
const clearedUrl = url.split("?")[0];
if (!clearedUrl) return "";
return clearedUrl;
}

19
tsconfig.json Normal file
View File

@@ -0,0 +1,19 @@
{
"compilerOptions": {
"esModuleInterop": true,
"skipLibCheck": true,
"target": "es2022",
"allowJs": true,
"resolveJsonModule": true,
"moduleDetection": "force",
"isolatedModules": true,
"verbatimModuleSyntax": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"module": "preserve",
"noEmit": true,
"lib": ["es2022"],
"allowImportingTsExtensions": true
}
}