├── .gitignore
├── .prettierrc
├── Dockerfile.example
├── LICENSE
├── README.md
├── config.json.example
├── package-lock.json
├── package.json
├── scripts
└── register.js
└── src
├── index.js
└── paginator.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | # IDE config
107 | .idea/
108 |
109 | # other
110 | .DS_Store
111 | config.json
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "tabWidth": 4,
3 | "useTabs": true,
4 | "semi": true,
5 | "printWidth": 80,
6 | "singleQuote": false
7 | }
8 |
--------------------------------------------------------------------------------
/Dockerfile.example:
--------------------------------------------------------------------------------
1 | FROM node:16
2 |
3 | # Create app directory for bot
4 | RUN mkdir -p /usr/src/app
5 | WORKDIR /usr/src/app
6 |
7 | # Copy app and install dependencies for bot
8 | COPY . /usr/src/app
9 | RUN npm install
10 |
11 | # Register guild (Optional)
12 | RUN npm run register [guild id]
13 |
14 | RUN apt update && apt upgrade -y
15 |
16 | CMD [ "npm", "run", "bot" ]
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Dank Developers
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Sniper
4 |
5 | > An easy to run simple bot that lets you snipe messages in your Discord server.
6 |
7 | ## Setup
8 |
9 | [Node.js 16.9.0](https://nodejs.org) or newer is required.
10 |
11 | 1. Run:
12 |
13 | ```bash
14 | $ git clone https://github.com/DankMemer/sniper.git
15 | $ cd ./sniper
16 | ```
17 |
18 | 2. Create config.json:
19 |
20 | ```json
21 | {
22 | "token": "",
23 | "application_id": ""
24 | }
25 | ```
26 |
27 | 3. Run:
28 |
29 | ```bash
30 | $ npm i
31 | $ npm run register [guild id]
32 | $ npm run bot
33 | ```
34 |
35 | > Note:
36 | > Without specifying [guild id], the snipe commands will be available to all of your app's guilds. They will fan out slowly across all guilds and will be guaranteed to be updated in an hour (due to Discord's cache).
37 | >
38 | > With [guild id] they will be available only within the guild specified and will update instantly.
39 |
40 | ## Dockerfile instructions
41 | An [example Dockerfile](Dockerfile.example) has been included. Modify line 12 to change the guild id. If you want to make the bot global, simply comment out line 12.
42 |
43 | ## License
44 |
45 | [MIT](https://tldrlegal.com/license/mit-license)
46 |
--------------------------------------------------------------------------------
/config.json.example:
--------------------------------------------------------------------------------
1 | {
2 | "token": "",
3 | "application_id": ""
4 | }
5 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sniper",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "sniper",
9 | "version": "1.0.0",
10 | "license": "MIT",
11 | "dependencies": {
12 | "@discordjs/rest": "^0.1.0-canary.0",
13 | "discord-api-types": "^0.24.0",
14 | "discord.js": "^13.3.1"
15 | },
16 | "devDependencies": {
17 | "prettier": "^2.4.1"
18 | }
19 | },
20 | "node_modules/@discordjs/builders": {
21 | "version": "0.8.2",
22 | "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.8.2.tgz",
23 | "integrity": "sha512-/YRd11SrcluqXkKppq/FAVzLIPRVlIVmc6X8ZklspzMIHDtJ+A4W37D43SHvLdH//+NnK+SHW/WeOF4Ts54PeQ==",
24 | "dependencies": {
25 | "@sindresorhus/is": "^4.2.0",
26 | "discord-api-types": "^0.24.0",
27 | "ow": "^0.27.0",
28 | "ts-mixer": "^6.0.0",
29 | "tslib": "^2.3.1"
30 | },
31 | "engines": {
32 | "node": ">=16.0.0",
33 | "npm": ">=7.0.0"
34 | }
35 | },
36 | "node_modules/@discordjs/collection": {
37 | "version": "0.3.2",
38 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.3.2.tgz",
39 | "integrity": "sha512-dMjLl60b2DMqObbH1MQZKePgWhsNe49XkKBZ0W5Acl5uVV43SN414i2QfZwRI7dXAqIn8pEWD2+XXQFn9KWxqg==",
40 | "engines": {
41 | "node": ">=16.0.0",
42 | "npm": ">=7.0.0"
43 | }
44 | },
45 | "node_modules/@discordjs/form-data": {
46 | "version": "3.0.1",
47 | "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz",
48 | "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==",
49 | "dependencies": {
50 | "asynckit": "^0.4.0",
51 | "combined-stream": "^1.0.8",
52 | "mime-types": "^2.1.12"
53 | },
54 | "engines": {
55 | "node": ">= 6"
56 | }
57 | },
58 | "node_modules/@discordjs/rest": {
59 | "version": "0.1.0-canary.0",
60 | "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.1.0-canary.0.tgz",
61 | "integrity": "sha512-d+s//ISYVV+e0w/926wMEeO7vju+Pn11x1JM4tcmVMCHSDgpi6pnFCNAXF1TEdnDcy7xf9tq5cf2pQkb/7ySTQ==",
62 | "dependencies": {
63 | "@discordjs/collection": "^0.1.6",
64 | "@sapphire/async-queue": "^1.1.4",
65 | "@sapphire/snowflake": "^1.3.5",
66 | "abort-controller": "^3.0.0",
67 | "discord-api-types": "^0.18.1",
68 | "form-data": "^4.0.0",
69 | "node-fetch": "^2.6.1",
70 | "tslib": "^2.3.0"
71 | },
72 | "engines": {
73 | "node": ">=16.0.0"
74 | }
75 | },
76 | "node_modules/@discordjs/rest/node_modules/@discordjs/collection": {
77 | "version": "0.1.6",
78 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz",
79 | "integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ=="
80 | },
81 | "node_modules/@discordjs/rest/node_modules/discord-api-types": {
82 | "version": "0.18.1",
83 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.18.1.tgz",
84 | "integrity": "sha512-hNC38R9ZF4uaujaZQtQfm5CdQO58uhdkoHQAVvMfIL0LgOSZeW575W8H6upngQOuoxWd8tiRII3LLJm9zuQKYg==",
85 | "deprecated": "No longer supported. Install the latest release (0.20.2)",
86 | "engines": {
87 | "node": ">=12"
88 | }
89 | },
90 | "node_modules/@sapphire/async-queue": {
91 | "version": "1.1.9",
92 | "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.9.tgz",
93 | "integrity": "sha512-CbXaGwwlEMq+l1TRu01FJCvySJ1CEFKFclHT48nIfNeZXaAAmmwwy7scUKmYHPUa3GhoMp6Qr1B3eAJux6XgOQ==",
94 | "engines": {
95 | "node": ">=v14.0.0",
96 | "npm": ">=7.0.0"
97 | }
98 | },
99 | "node_modules/@sapphire/snowflake": {
100 | "version": "1.3.6",
101 | "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-1.3.6.tgz",
102 | "integrity": "sha512-QnzuLp+p9D7agynVub/zqlDVriDza9y3STArBhNiNBUgIX8+GL5FpQxstRfw1jDr5jkZUjcuKYAHxjIuXKdJAg==",
103 | "deprecated": "This version has been automatically deprecated by @favware/npm-deprecate. Please use a newer version.",
104 | "engines": {
105 | "node": ">=12",
106 | "npm": ">=6"
107 | }
108 | },
109 | "node_modules/@sindresorhus/is": {
110 | "version": "4.2.0",
111 | "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz",
112 | "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw==",
113 | "engines": {
114 | "node": ">=10"
115 | },
116 | "funding": {
117 | "url": "https://github.com/sindresorhus/is?sponsor=1"
118 | }
119 | },
120 | "node_modules/@types/node": {
121 | "version": "16.11.7",
122 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz",
123 | "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw=="
124 | },
125 | "node_modules/@types/node-fetch": {
126 | "version": "2.5.12",
127 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz",
128 | "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==",
129 | "dependencies": {
130 | "@types/node": "*",
131 | "form-data": "^3.0.0"
132 | }
133 | },
134 | "node_modules/@types/node-fetch/node_modules/form-data": {
135 | "version": "3.0.1",
136 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
137 | "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
138 | "dependencies": {
139 | "asynckit": "^0.4.0",
140 | "combined-stream": "^1.0.8",
141 | "mime-types": "^2.1.12"
142 | },
143 | "engines": {
144 | "node": ">= 6"
145 | }
146 | },
147 | "node_modules/@types/ws": {
148 | "version": "8.2.0",
149 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.0.tgz",
150 | "integrity": "sha512-cyeefcUCgJlEk+hk2h3N+MqKKsPViQgF5boi9TTHSK+PoR9KWBb/C5ccPcDyAqgsbAYHTwulch725DV84+pSpg==",
151 | "dependencies": {
152 | "@types/node": "*"
153 | }
154 | },
155 | "node_modules/abort-controller": {
156 | "version": "3.0.0",
157 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
158 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
159 | "dependencies": {
160 | "event-target-shim": "^5.0.0"
161 | },
162 | "engines": {
163 | "node": ">=6.5"
164 | }
165 | },
166 | "node_modules/asynckit": {
167 | "version": "0.4.0",
168 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
169 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
170 | },
171 | "node_modules/callsites": {
172 | "version": "3.1.0",
173 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
174 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
175 | "engines": {
176 | "node": ">=6"
177 | }
178 | },
179 | "node_modules/combined-stream": {
180 | "version": "1.0.8",
181 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
182 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
183 | "dependencies": {
184 | "delayed-stream": "~1.0.0"
185 | },
186 | "engines": {
187 | "node": ">= 0.8"
188 | }
189 | },
190 | "node_modules/delayed-stream": {
191 | "version": "1.0.0",
192 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
193 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
194 | "engines": {
195 | "node": ">=0.4.0"
196 | }
197 | },
198 | "node_modules/discord-api-types": {
199 | "version": "0.24.0",
200 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.24.0.tgz",
201 | "integrity": "sha512-X0uA2a92cRjowUEXpLZIHWl4jiX1NsUpDhcEOpa1/hpO1vkaokgZ8kkPtPih9hHth5UVQ3mHBu/PpB4qjyfJ4A==",
202 | "engines": {
203 | "node": ">=12"
204 | }
205 | },
206 | "node_modules/discord.js": {
207 | "version": "13.3.1",
208 | "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.3.1.tgz",
209 | "integrity": "sha512-zn4G8tL5+tMV00+0aSsVYNYcIfMSdT2g0nudKny+Ikd+XKv7m6bqI7n3Vji0GIRqXDr5ArPaw+iYFM2I1Iw3vg==",
210 | "dependencies": {
211 | "@discordjs/builders": "^0.8.1",
212 | "@discordjs/collection": "^0.3.2",
213 | "@discordjs/form-data": "^3.0.1",
214 | "@sapphire/async-queue": "^1.1.8",
215 | "@types/node-fetch": "^2.5.12",
216 | "@types/ws": "^8.2.0",
217 | "discord-api-types": "^0.24.0",
218 | "node-fetch": "^2.6.1",
219 | "ws": "^8.2.3"
220 | },
221 | "engines": {
222 | "node": ">=16.6.0",
223 | "npm": ">=7.0.0"
224 | }
225 | },
226 | "node_modules/dot-prop": {
227 | "version": "6.0.1",
228 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
229 | "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
230 | "dependencies": {
231 | "is-obj": "^2.0.0"
232 | },
233 | "engines": {
234 | "node": ">=10"
235 | },
236 | "funding": {
237 | "url": "https://github.com/sponsors/sindresorhus"
238 | }
239 | },
240 | "node_modules/event-target-shim": {
241 | "version": "5.0.1",
242 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
243 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
244 | "engines": {
245 | "node": ">=6"
246 | }
247 | },
248 | "node_modules/form-data": {
249 | "version": "4.0.0",
250 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
251 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
252 | "dependencies": {
253 | "asynckit": "^0.4.0",
254 | "combined-stream": "^1.0.8",
255 | "mime-types": "^2.1.12"
256 | },
257 | "engines": {
258 | "node": ">= 6"
259 | }
260 | },
261 | "node_modules/is-obj": {
262 | "version": "2.0.0",
263 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
264 | "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
265 | "engines": {
266 | "node": ">=8"
267 | }
268 | },
269 | "node_modules/lodash.isequal": {
270 | "version": "4.5.0",
271 | "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
272 | "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
273 | },
274 | "node_modules/mime-db": {
275 | "version": "1.49.0",
276 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
277 | "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
278 | "engines": {
279 | "node": ">= 0.6"
280 | }
281 | },
282 | "node_modules/mime-types": {
283 | "version": "2.1.32",
284 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
285 | "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
286 | "dependencies": {
287 | "mime-db": "1.49.0"
288 | },
289 | "engines": {
290 | "node": ">= 0.6"
291 | }
292 | },
293 | "node_modules/node-fetch": {
294 | "version": "2.6.5",
295 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
296 | "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
297 | "dependencies": {
298 | "whatwg-url": "^5.0.0"
299 | },
300 | "engines": {
301 | "node": "4.x || >=6.0.0"
302 | }
303 | },
304 | "node_modules/ow": {
305 | "version": "0.27.0",
306 | "resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz",
307 | "integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==",
308 | "dependencies": {
309 | "@sindresorhus/is": "^4.0.1",
310 | "callsites": "^3.1.0",
311 | "dot-prop": "^6.0.1",
312 | "lodash.isequal": "^4.5.0",
313 | "type-fest": "^1.2.1",
314 | "vali-date": "^1.0.0"
315 | },
316 | "engines": {
317 | "node": ">=12"
318 | },
319 | "funding": {
320 | "url": "https://github.com/sponsors/sindresorhus"
321 | }
322 | },
323 | "node_modules/prettier": {
324 | "version": "2.4.1",
325 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
326 | "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
327 | "dev": true,
328 | "bin": {
329 | "prettier": "bin-prettier.js"
330 | },
331 | "engines": {
332 | "node": ">=10.13.0"
333 | }
334 | },
335 | "node_modules/tr46": {
336 | "version": "0.0.3",
337 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
338 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
339 | },
340 | "node_modules/ts-mixer": {
341 | "version": "6.0.0",
342 | "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.0.tgz",
343 | "integrity": "sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ=="
344 | },
345 | "node_modules/tslib": {
346 | "version": "2.3.1",
347 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
348 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
349 | },
350 | "node_modules/type-fest": {
351 | "version": "1.4.0",
352 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
353 | "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
354 | "engines": {
355 | "node": ">=10"
356 | },
357 | "funding": {
358 | "url": "https://github.com/sponsors/sindresorhus"
359 | }
360 | },
361 | "node_modules/vali-date": {
362 | "version": "1.0.0",
363 | "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
364 | "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY=",
365 | "engines": {
366 | "node": ">=0.10.0"
367 | }
368 | },
369 | "node_modules/webidl-conversions": {
370 | "version": "3.0.1",
371 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
372 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
373 | },
374 | "node_modules/whatwg-url": {
375 | "version": "5.0.0",
376 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
377 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
378 | "dependencies": {
379 | "tr46": "~0.0.3",
380 | "webidl-conversions": "^3.0.0"
381 | }
382 | },
383 | "node_modules/ws": {
384 | "version": "8.2.3",
385 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
386 | "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
387 | "engines": {
388 | "node": ">=10.0.0"
389 | },
390 | "peerDependencies": {
391 | "bufferutil": "^4.0.1",
392 | "utf-8-validate": "^5.0.2"
393 | },
394 | "peerDependenciesMeta": {
395 | "bufferutil": {
396 | "optional": true
397 | },
398 | "utf-8-validate": {
399 | "optional": true
400 | }
401 | }
402 | }
403 | },
404 | "dependencies": {
405 | "@discordjs/builders": {
406 | "version": "0.8.2",
407 | "resolved": "https://registry.npmjs.org/@discordjs/builders/-/builders-0.8.2.tgz",
408 | "integrity": "sha512-/YRd11SrcluqXkKppq/FAVzLIPRVlIVmc6X8ZklspzMIHDtJ+A4W37D43SHvLdH//+NnK+SHW/WeOF4Ts54PeQ==",
409 | "requires": {
410 | "@sindresorhus/is": "^4.2.0",
411 | "discord-api-types": "^0.24.0",
412 | "ow": "^0.27.0",
413 | "ts-mixer": "^6.0.0",
414 | "tslib": "^2.3.1"
415 | }
416 | },
417 | "@discordjs/collection": {
418 | "version": "0.3.2",
419 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.3.2.tgz",
420 | "integrity": "sha512-dMjLl60b2DMqObbH1MQZKePgWhsNe49XkKBZ0W5Acl5uVV43SN414i2QfZwRI7dXAqIn8pEWD2+XXQFn9KWxqg=="
421 | },
422 | "@discordjs/form-data": {
423 | "version": "3.0.1",
424 | "resolved": "https://registry.npmjs.org/@discordjs/form-data/-/form-data-3.0.1.tgz",
425 | "integrity": "sha512-ZfFsbgEXW71Rw/6EtBdrP5VxBJy4dthyC0tpQKGKmYFImlmmrykO14Za+BiIVduwjte0jXEBlhSKf0MWbFp9Eg==",
426 | "requires": {
427 | "asynckit": "^0.4.0",
428 | "combined-stream": "^1.0.8",
429 | "mime-types": "^2.1.12"
430 | }
431 | },
432 | "@discordjs/rest": {
433 | "version": "0.1.0-canary.0",
434 | "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-0.1.0-canary.0.tgz",
435 | "integrity": "sha512-d+s//ISYVV+e0w/926wMEeO7vju+Pn11x1JM4tcmVMCHSDgpi6pnFCNAXF1TEdnDcy7xf9tq5cf2pQkb/7ySTQ==",
436 | "requires": {
437 | "@discordjs/collection": "^0.1.6",
438 | "@sapphire/async-queue": "^1.1.4",
439 | "@sapphire/snowflake": "^1.3.5",
440 | "abort-controller": "^3.0.0",
441 | "discord-api-types": "^0.18.1",
442 | "form-data": "^4.0.0",
443 | "node-fetch": "^2.6.1",
444 | "tslib": "^2.3.0"
445 | },
446 | "dependencies": {
447 | "@discordjs/collection": {
448 | "version": "0.1.6",
449 | "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-0.1.6.tgz",
450 | "integrity": "sha512-utRNxnd9kSS2qhyivo9lMlt5qgAUasH2gb7BEOn6p0efFh24gjGomHzWKMAPn2hEReOPQZCJaRKoURwRotKucQ=="
451 | },
452 | "discord-api-types": {
453 | "version": "0.18.1",
454 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.18.1.tgz",
455 | "integrity": "sha512-hNC38R9ZF4uaujaZQtQfm5CdQO58uhdkoHQAVvMfIL0LgOSZeW575W8H6upngQOuoxWd8tiRII3LLJm9zuQKYg=="
456 | }
457 | }
458 | },
459 | "@sapphire/async-queue": {
460 | "version": "1.1.9",
461 | "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.1.9.tgz",
462 | "integrity": "sha512-CbXaGwwlEMq+l1TRu01FJCvySJ1CEFKFclHT48nIfNeZXaAAmmwwy7scUKmYHPUa3GhoMp6Qr1B3eAJux6XgOQ=="
463 | },
464 | "@sapphire/snowflake": {
465 | "version": "1.3.6",
466 | "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-1.3.6.tgz",
467 | "integrity": "sha512-QnzuLp+p9D7agynVub/zqlDVriDza9y3STArBhNiNBUgIX8+GL5FpQxstRfw1jDr5jkZUjcuKYAHxjIuXKdJAg=="
468 | },
469 | "@sindresorhus/is": {
470 | "version": "4.2.0",
471 | "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.2.0.tgz",
472 | "integrity": "sha512-VkE3KLBmJwcCaVARtQpfuKcKv8gcBmUubrfHGF84dXuuW6jgsRYxPtzcIhPyK9WAPpRt2/xY6zkD9MnRaJzSyw=="
473 | },
474 | "@types/node": {
475 | "version": "16.11.7",
476 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.7.tgz",
477 | "integrity": "sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw=="
478 | },
479 | "@types/node-fetch": {
480 | "version": "2.5.12",
481 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz",
482 | "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==",
483 | "requires": {
484 | "@types/node": "*",
485 | "form-data": "^3.0.0"
486 | },
487 | "dependencies": {
488 | "form-data": {
489 | "version": "3.0.1",
490 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
491 | "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
492 | "requires": {
493 | "asynckit": "^0.4.0",
494 | "combined-stream": "^1.0.8",
495 | "mime-types": "^2.1.12"
496 | }
497 | }
498 | }
499 | },
500 | "@types/ws": {
501 | "version": "8.2.0",
502 | "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.2.0.tgz",
503 | "integrity": "sha512-cyeefcUCgJlEk+hk2h3N+MqKKsPViQgF5boi9TTHSK+PoR9KWBb/C5ccPcDyAqgsbAYHTwulch725DV84+pSpg==",
504 | "requires": {
505 | "@types/node": "*"
506 | }
507 | },
508 | "abort-controller": {
509 | "version": "3.0.0",
510 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
511 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
512 | "requires": {
513 | "event-target-shim": "^5.0.0"
514 | }
515 | },
516 | "asynckit": {
517 | "version": "0.4.0",
518 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
519 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
520 | },
521 | "callsites": {
522 | "version": "3.1.0",
523 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
524 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
525 | },
526 | "combined-stream": {
527 | "version": "1.0.8",
528 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
529 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
530 | "requires": {
531 | "delayed-stream": "~1.0.0"
532 | }
533 | },
534 | "delayed-stream": {
535 | "version": "1.0.0",
536 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
537 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
538 | },
539 | "discord-api-types": {
540 | "version": "0.24.0",
541 | "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.24.0.tgz",
542 | "integrity": "sha512-X0uA2a92cRjowUEXpLZIHWl4jiX1NsUpDhcEOpa1/hpO1vkaokgZ8kkPtPih9hHth5UVQ3mHBu/PpB4qjyfJ4A=="
543 | },
544 | "discord.js": {
545 | "version": "13.3.1",
546 | "resolved": "https://registry.npmjs.org/discord.js/-/discord.js-13.3.1.tgz",
547 | "integrity": "sha512-zn4G8tL5+tMV00+0aSsVYNYcIfMSdT2g0nudKny+Ikd+XKv7m6bqI7n3Vji0GIRqXDr5ArPaw+iYFM2I1Iw3vg==",
548 | "requires": {
549 | "@discordjs/builders": "^0.8.1",
550 | "@discordjs/collection": "^0.3.2",
551 | "@discordjs/form-data": "^3.0.1",
552 | "@sapphire/async-queue": "^1.1.8",
553 | "@types/node-fetch": "^2.5.12",
554 | "@types/ws": "^8.2.0",
555 | "discord-api-types": "^0.24.0",
556 | "node-fetch": "^2.6.1",
557 | "ws": "^8.2.3"
558 | }
559 | },
560 | "dot-prop": {
561 | "version": "6.0.1",
562 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
563 | "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
564 | "requires": {
565 | "is-obj": "^2.0.0"
566 | }
567 | },
568 | "event-target-shim": {
569 | "version": "5.0.1",
570 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
571 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
572 | },
573 | "form-data": {
574 | "version": "4.0.0",
575 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
576 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
577 | "requires": {
578 | "asynckit": "^0.4.0",
579 | "combined-stream": "^1.0.8",
580 | "mime-types": "^2.1.12"
581 | }
582 | },
583 | "is-obj": {
584 | "version": "2.0.0",
585 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
586 | "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
587 | },
588 | "lodash.isequal": {
589 | "version": "4.5.0",
590 | "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
591 | "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
592 | },
593 | "mime-db": {
594 | "version": "1.49.0",
595 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
596 | "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA=="
597 | },
598 | "mime-types": {
599 | "version": "2.1.32",
600 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
601 | "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
602 | "requires": {
603 | "mime-db": "1.49.0"
604 | }
605 | },
606 | "node-fetch": {
607 | "version": "2.6.5",
608 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz",
609 | "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==",
610 | "requires": {
611 | "whatwg-url": "^5.0.0"
612 | }
613 | },
614 | "ow": {
615 | "version": "0.27.0",
616 | "resolved": "https://registry.npmjs.org/ow/-/ow-0.27.0.tgz",
617 | "integrity": "sha512-SGnrGUbhn4VaUGdU0EJLMwZWSupPmF46hnTRII7aCLCrqixTAC5eKo8kI4/XXf1eaaI8YEVT+3FeGNJI9himAQ==",
618 | "requires": {
619 | "@sindresorhus/is": "^4.0.1",
620 | "callsites": "^3.1.0",
621 | "dot-prop": "^6.0.1",
622 | "lodash.isequal": "^4.5.0",
623 | "type-fest": "^1.2.1",
624 | "vali-date": "^1.0.0"
625 | }
626 | },
627 | "prettier": {
628 | "version": "2.4.1",
629 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz",
630 | "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==",
631 | "dev": true
632 | },
633 | "tr46": {
634 | "version": "0.0.3",
635 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
636 | "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
637 | },
638 | "ts-mixer": {
639 | "version": "6.0.0",
640 | "resolved": "https://registry.npmjs.org/ts-mixer/-/ts-mixer-6.0.0.tgz",
641 | "integrity": "sha512-nXIb1fvdY5CBSrDIblLn73NW0qRDk5yJ0Sk1qPBF560OdJfQp9jhl+0tzcY09OZ9U+6GpeoI9RjwoIKFIoB9MQ=="
642 | },
643 | "tslib": {
644 | "version": "2.3.1",
645 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz",
646 | "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw=="
647 | },
648 | "type-fest": {
649 | "version": "1.4.0",
650 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
651 | "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="
652 | },
653 | "vali-date": {
654 | "version": "1.0.0",
655 | "resolved": "https://registry.npmjs.org/vali-date/-/vali-date-1.0.0.tgz",
656 | "integrity": "sha1-G5BKWWCfsyjvB4E4Qgk09rhnCaY="
657 | },
658 | "webidl-conversions": {
659 | "version": "3.0.1",
660 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
661 | "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
662 | },
663 | "whatwg-url": {
664 | "version": "5.0.0",
665 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
666 | "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
667 | "requires": {
668 | "tr46": "~0.0.3",
669 | "webidl-conversions": "^3.0.0"
670 | }
671 | },
672 | "ws": {
673 | "version": "8.2.3",
674 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
675 | "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==",
676 | "requires": {}
677 | }
678 | }
679 | }
680 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "sniper",
3 | "version": "1.0.0",
4 | "description": "An easy to run simple bot that lets you snipe messages in your Discord server.",
5 | "author": "badosz0 ",
6 | "main": "src/index.js",
7 | "scripts": {
8 | "register": "node scripts/register.js",
9 | "bot": "node src/index.js",
10 | "prettier": "prettier --write \"**/*.js\""
11 | },
12 | "license": "MIT",
13 | "dependencies": {
14 | "@discordjs/rest": "^0.1.0-canary.0",
15 | "discord-api-types": "^0.24.0",
16 | "discord.js": "^13.3.1"
17 | },
18 | "devDependencies": {
19 | "prettier": "^2.4.1"
20 | },
21 | "bugs": {
22 | "url": "https://github.com/DankMemer/sniper/issues"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/DankMemer/sniper.git"
27 | },
28 | "homepage": "https://github.com/DankMemer/sniper#readme"
29 | }
30 |
--------------------------------------------------------------------------------
/scripts/register.js:
--------------------------------------------------------------------------------
1 | const { REST } = require("@discordjs/rest");
2 | const { Routes } = require("discord-api-types/v9");
3 |
4 | const { token, application_id } = require("../config.json");
5 |
6 | const guild = process.argv[2];
7 |
8 | const channelOption = {
9 | type: 7, // text channel
10 | name: "channel",
11 | description: "The channel to snipe",
12 | };
13 | const commands = [
14 | {
15 | name: "snipe",
16 | description: "Shows the last deleted message from a specified channel!",
17 | options: [
18 | channelOption,
19 | {
20 | type: 3, // string
21 | name: "options",
22 | description: "Other parts of the deleted message, if present",
23 | choices: [
24 | {
25 | name: "embeds",
26 | value: "embeds",
27 | },
28 | {
29 | name: "attachments",
30 | value: "attachments",
31 | },
32 | ],
33 | },
34 | ],
35 | },
36 | {
37 | name: "editsnipe",
38 | description: "Shows the last edited message from a specified channel!",
39 | options: [channelOption],
40 | },
41 | {
42 | name: "reactionsnipe",
43 | description:
44 | "Shows the last removed reaction from a specified channel!",
45 | options: [channelOption],
46 | },
47 | ];
48 |
49 | const rest = new REST({ version: "9" }).setToken(token);
50 |
51 | (async () => {
52 | try {
53 | console.log("[sniper] :: Started refreshing application (/) commands.");
54 |
55 | await rest.put(
56 | guild
57 | ? Routes.applicationGuildCommands(application_id, guild)
58 | : Routes.applicationCommands(application_id),
59 | {
60 | body: commands,
61 | }
62 | );
63 |
64 | console.log(
65 | "[sniper] :: Successfully reloaded application (/) commands."
66 | );
67 | } catch (error) {
68 | console.error(error);
69 | }
70 | })();
71 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const { Client, Intents, MessageEmbed } = require("discord.js");
2 | const client = new Client({
3 | intents: [
4 | Intents.FLAGS.GUILDS,
5 | Intents.FLAGS.GUILD_MESSAGES,
6 | Intents.FLAGS.GUILD_MESSAGE_REACTIONS,
7 | ],
8 | partials: ["MESSAGE", "REACTION", "USER"],
9 | });
10 | const { token } = require("../config.json");
11 | const Paginator = require("./paginator");
12 |
13 | const snipes = {};
14 | const editSnipes = {};
15 | const reactionSnipes = {};
16 |
17 | const formatEmoji = (emoji) => {
18 | return !emoji.id || emoji.available
19 | ? emoji.toString() // bot has access or unicode emoji
20 | : `[:${emoji.name}:](${emoji.url})`; // bot cannot use the emoji
21 | };
22 |
23 | process.on("unhandledRejection", console.error); // prevent exit on error
24 |
25 | client.on("ready", () => {
26 | console.log(`[sniper] :: Logged in as ${client.user.tag}.`);
27 | });
28 |
29 | client.on("messageDelete", async (message) => {
30 | if (message.partial) return; // content is null or deleted embed
31 |
32 | snipes[message.channel.id] = {
33 | author: message.author.tag,
34 | content: message.content,
35 | embeds: message.embeds,
36 | attachments: [...message.attachments.values()].map((a) => a.proxyURL),
37 | createdAt: message.createdTimestamp,
38 | };
39 | });
40 |
41 | client.on("messageUpdate", async (oldMessage, newMessage) => {
42 | if (oldMessage.partial) return; // content is null
43 |
44 | editSnipes[oldMessage.channel.id] = {
45 | author: oldMessage.author.tag,
46 | content: oldMessage.content,
47 | createdAt: newMessage.editedTimestamp,
48 | };
49 | });
50 |
51 | client.on("messageReactionRemove", async (reaction, user) => {
52 | if (reaction.partial) reaction = await reaction.fetch();
53 |
54 | reactionSnipes[reaction.message.channel.id] = {
55 | user: user.tag,
56 | emoji: reaction.emoji,
57 | messageURL: reaction.message.url,
58 | createdAt: Date.now(),
59 | };
60 | });
61 |
62 | client.on("interactionCreate", async (interaction) => {
63 | if (!interaction.isCommand()) return;
64 |
65 | const channel =
66 | interaction.options.getChannel("channel") || interaction.channel;
67 |
68 | if (interaction.commandName === "snipe") {
69 | const snipe = snipes[channel.id];
70 |
71 | if (!snipe) return interaction.reply("There's nothing to snipe!");
72 |
73 | const type = interaction.options.getString("options");
74 |
75 | if (type === "embeds") {
76 | if (!snipe.embeds.length)
77 | return interaction.reply("The message has no embeds!");
78 | const paginator = new Paginator(
79 | snipe.embeds.map((e) => ({ embeds: [e] }))
80 | );
81 | await paginator.start({ interaction });
82 | } else if (type === "attachments") {
83 | if (!snipe.attachments.length)
84 | return interaction.reply("The message has no embeds!");
85 | const paginator = new Paginator(
86 | snipe.attachments.map((a) => ({ content: a }))
87 | );
88 | await paginator.start({ interaction });
89 | } else {
90 | const embed = new MessageEmbed()
91 | .setAuthor(snipe.author)
92 | .setFooter(`#${channel.name}`)
93 | .setTimestamp(snipe.createdAt);
94 | if (snipe.content) embed.setDescription(snipe.content);
95 | if (snipe.attachments.length) embed.setImage(snipe.attachments[0]);
96 | if (snipe.attachments.length || snipe.embeds.length)
97 | embed.addField(
98 | "Extra Info",
99 | `*Message also contained \`${snipe.embeds.length}\` embeds and \`${snipe.attachments.length}\` attachments.*`
100 | );
101 |
102 | await interaction.reply({ embeds: [embed] });
103 | }
104 | } else if (interaction.commandName === "editsnipe") {
105 | const snipe = editSnipes[channel.id];
106 |
107 | await interaction.reply(
108 | snipe
109 | ? {
110 | embeds: [
111 | new MessageEmbed()
112 | .setDescription(snipe.content)
113 | .setAuthor(snipe.author)
114 | .setFooter(`#${channel.name}`)
115 | .setTimestamp(snipe.createdAt),
116 | ],
117 | }
118 | : "There's nothing to snipe!"
119 | );
120 | } else if (interaction.commandName === "reactionsnipe") {
121 | const snipe = reactionSnipes[channel.id];
122 |
123 | await interaction.reply(
124 | snipe
125 | ? {
126 | embeds: [
127 | new MessageEmbed()
128 | .setDescription(
129 | `reacted with ${formatEmoji(
130 | snipe.emoji
131 | )} on [this message](${snipe.messageURL})`
132 | )
133 | .setAuthor(snipe.user)
134 | .setFooter(`#${channel.name}`)
135 | .setTimestamp(snipe.createdAt),
136 | ],
137 | }
138 | : "There's nothing to snipe!"
139 | );
140 | }
141 | });
142 |
143 | client.login(token);
144 |
--------------------------------------------------------------------------------
/src/paginator.js:
--------------------------------------------------------------------------------
1 | const {
2 | CommandInteraction,
3 | ButtonInteraction,
4 | InteractionCollector,
5 | Message,
6 | MessageActionRow,
7 | MessageButton,
8 | MessageEditOptions,
9 | } = require("discord.js");
10 |
11 | module.exports = class Paginator {
12 | /**
13 | * @param {MessageEditOptions[]} data Array with edit options for each page.
14 | */
15 | constructor(data) {
16 | if (!data?.length)
17 | throw new TypeError("Paginator data must have at least one value.");
18 |
19 | this.data = data;
20 | this.currentPage = null; // 0-indexed
21 | this.row = new MessageActionRow().addComponents(
22 | new MessageButton()
23 | .setCustomId("first")
24 | .setLabel("<<")
25 | .setStyle("PRIMARY"),
26 | new MessageButton()
27 | .setCustomId("previous")
28 | .setLabel("<")
29 | .setStyle("PRIMARY"),
30 | new MessageButton()
31 | .setCustomId("currentPage")
32 | .setStyle("SECONDARY")
33 | .setDisabled(true),
34 | new MessageButton()
35 | .setCustomId("next")
36 | .setLabel(">")
37 | .setStyle("PRIMARY"),
38 | new MessageButton()
39 | .setCustomId("last")
40 | .setLabel(">>")
41 | .setStyle("PRIMARY")
42 | );
43 | this.stopRow = new MessageActionRow().addComponents(
44 | new MessageButton()
45 | .setCustomId("stop")
46 | .setLabel("Stop")
47 | .setStyle("DANGER")
48 | );
49 | }
50 |
51 | /**
52 | * Starts the paginator.
53 | * @param {object} options
54 | * @param {CommandInteraction} options.interaction
55 | * @param {number=} options.time
56 | * @returns {Promise}
57 | */
58 | async start({ interaction, time = 30000 }) {
59 | let message = await interaction.reply({
60 | ...this.getPage(0),
61 | fetchReply: true,
62 | });
63 |
64 | const filter = (i) => {
65 | return i.user.id === interaction.user.id;
66 | };
67 | const collector = message.createMessageComponentCollector({
68 | time,
69 | filter,
70 | componentType: "BUTTON",
71 | });
72 | collector.on("collect", (i) => this.onClicked(i, collector));
73 | collector.on("end", () => this.onEnd(interaction));
74 | }
75 |
76 | /**
77 | * Listener for when a button is clicked.
78 | * @param {ButtonInteraction} interaction
79 | * @param {InteractionCollector} collector
80 | * @returns {Promise}
81 | */
82 | async onClicked(interaction, collector) {
83 | if (interaction.customId === "first") {
84 | if (this.currentPage === 0) {
85 | interaction.deferUpdate();
86 | return;
87 | }
88 | await interaction.update(this.getPage(0));
89 | } else if (interaction.customId === "previous") {
90 | if (this.currentPage === 0) {
91 | interaction.deferUpdate();
92 | return;
93 | }
94 | await interaction.update(this.getPage(this.currentPage - 1));
95 | } else if (interaction.customId === "next") {
96 | if (this.currentPage === this.data.length - 1) {
97 | interaction.deferUpdate();
98 | return;
99 | }
100 | await interaction.update(this.getPage(this.currentPage + 1));
101 | } else if (interaction.customId === "last") {
102 | if (this.currentPage === this.data.length - 1) {
103 | interaction.deferUpdate();
104 | return;
105 | }
106 | await interaction.update(this.getPage(this.data.length - 1));
107 | } else if (interaction.customId === "stop") {
108 | collector.stop();
109 | }
110 | }
111 |
112 | /**
113 | * Listener for when the collector ends.
114 | * @param {CommandInteraction} interaction
115 | * @returns {Promise}
116 | */
117 | async onEnd(interaction) {
118 | this.row.components.forEach((component) => component.setDisabled(true));
119 | await interaction.editReply({ components: [this.row] });
120 | }
121 |
122 | /**
123 | * Gets the send options for a page.
124 | * @param {number} number
125 | */
126 | getPage(number) {
127 | this.currentPage = number;
128 | this.row.components
129 | .find((component) => component.customId === "currentPage")
130 | .setLabel(`${number + 1}/${this.data.length}`);
131 | return { ...this.data[number], components: [this.row, this.stopRow] };
132 | }
133 | };
134 |
--------------------------------------------------------------------------------