├── .editorconfig
├── .eslintrc
├── .github
└── workflows
│ └── release.yml
├── .gitignore
├── .husky
└── pre-commit
├── .idea
├── .gitignore
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── jsLinters
│ └── eslint.xml
├── mastodont.iml
├── modules.xml
├── prettier.xml
└── vcs.xml
├── .prettierrc
├── LICENSE.txt
├── README.md
├── dist
├── LICENSE.txt
├── README.md
├── args.js
├── blocks.js
├── config.js
├── index.js
└── package.json
├── esbuild.js
├── examples
├── blocklist.txt
├── mastodont.crontab
├── push-workflow.yml
└── schedule-workflow.yml
├── package-lock.json
├── package.json
├── src
├── args.ts
├── blocks.ts
├── config.ts
├── header.ts
├── index.ts
├── types
│ ├── index.d.ts
│ └── validations.d.ts
└── validations.ts
└── tsconfig.json
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | # Unix-style newlines with a newline ending every file
5 | [*]
6 | end_of_line = lf
7 | insert_final_newline = true
8 | charset = utf-8
9 | indent_style = space
10 | indent_size = 2
11 |
12 | [*.cs]
13 | indent_size = 4
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["standard", "prettier", "eslint:recommended", "plugin:@typescript-eslint/recommended"],
4 | "parser": "@typescript-eslint/parser",
5 | "plugins": ["@typescript-eslint"]
6 | }
7 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 | on:
3 | push:
4 | branches:
5 | - main
6 | jobs:
7 | release:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | contents: write
11 | pull-requests: read
12 | steps:
13 | - name: Checkout repo
14 | uses: actions/checkout@v3
15 | with:
16 | fetch-depth: 0
17 |
18 | - name: Setup Node
19 | uses: actions/setup-node@v2
20 | with:
21 | node-version: 16
22 |
23 | - name: Check package version
24 | id: check
25 | uses: 'EndBug/version-check@v1'
26 | with:
27 | token: ${{ secrets.GITHUB_TOKEN }}
28 | diff-search: true
29 |
30 | - name: Create release
31 | uses: 'zendesk/action-create-release@v1'
32 | if: ${{ steps.check.outputs.changed == 'true' }}
33 | env:
34 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35 | with:
36 | tag_name: v${{ steps.check.outputs.version}}
37 | release_name: v${{ steps.check.outputs.version}}
38 | draft: false
39 | prerelease: false
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/macos,linux,windows,webstorm,yarn,diff,snyk,node,tower
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,windows,webstorm,yarn,diff,snyk,node,tower
3 |
4 | ### Diff ###
5 | *.patch
6 | *.diff
7 |
8 | ### Linux ###
9 | *~
10 |
11 | # temporary files which can be created if a process still has a handle open of a deleted file
12 | .fuse_hidden*
13 |
14 | # KDE directory preferences
15 | .directory
16 |
17 | # Linux trash folder which might appear on any partition or disk
18 | .Trash-*
19 |
20 | # .nfs files are created when an open file is removed but is still being accessed
21 | .nfs*
22 |
23 | ### macOS ###
24 | # General
25 | .DS_Store
26 | .AppleDouble
27 | .LSOverride
28 |
29 | # Icon must end with two \r
30 | Icon
31 |
32 |
33 | # Thumbnails
34 | ._*
35 |
36 | # Files that might appear in the root of a volume
37 | .DocumentRevisions-V100
38 | .fseventsd
39 | .Spotlight-V100
40 | .TemporaryItems
41 | .Trashes
42 | .VolumeIcon.icns
43 | .com.apple.timemachine.donotpresent
44 |
45 | # Directories potentially created on remote AFP share
46 | .AppleDB
47 | .AppleDesktop
48 | Network Trash Folder
49 | Temporary Items
50 | .apdisk
51 |
52 | ### macOS Patch ###
53 | # iCloud generated files
54 | *.icloud
55 |
56 | ### Node ###
57 | # Logs
58 | logs
59 | *.log
60 | npm-debug.log*
61 | yarn-debug.log*
62 | yarn-error.log*
63 | lerna-debug.log*
64 | .pnpm-debug.log*
65 |
66 | # Diagnostic reports (https://nodejs.org/api/report.html)
67 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
68 |
69 | # Runtime data
70 | pids
71 | *.pid
72 | *.seed
73 | *.pid.lock
74 |
75 | # Directory for instrumented libs generated by jscoverage/JSCover
76 | lib-cov
77 |
78 | # Coverage directory used by tools like istanbul
79 | coverage
80 | *.lcov
81 |
82 | # nyc test coverage
83 | .nyc_output
84 |
85 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
86 | .grunt
87 |
88 | # Bower dependency directory (https://bower.io/)
89 | bower_components
90 |
91 | # node-waf configuration
92 | .lock-wscript
93 |
94 | # Compiled binary addons (https://nodejs.org/api/addons.html)
95 | build/Release
96 |
97 | # Dependency directories
98 | node_modules/
99 | jspm_packages/
100 |
101 | # Snowpack dependency directory (https://snowpack.dev/)
102 | web_modules/
103 |
104 | # TypeScript cache
105 | *.tsbuildinfo
106 |
107 | # Optional npm cache directory
108 | .npm
109 |
110 | # Optional eslint cache
111 | .eslintcache
112 |
113 | # Optional stylelint cache
114 | .stylelintcache
115 |
116 | # Microbundle cache
117 | .rpt2_cache/
118 | .rts2_cache_cjs/
119 | .rts2_cache_es/
120 | .rts2_cache_umd/
121 |
122 | # Optional REPL history
123 | .node_repl_history
124 |
125 | # Output of 'npm pack'
126 | *.tgz
127 |
128 | # Yarn Integrity file
129 | .yarn-integrity
130 |
131 | # dotenv environment variable files
132 | .env
133 | .env.development.local
134 | .env.test.local
135 | .env.production.local
136 | .env.local
137 |
138 | # parcel-bundler cache (https://parceljs.org/)
139 | .cache
140 | .parcel-cache
141 |
142 | # Next.js build output
143 | .next
144 | out
145 |
146 | # Nuxt.js build / generate output
147 | .nuxt
148 |
149 | # Gatsby files
150 | .cache/
151 | # Comment in the public line in if your project uses Gatsby and not Next.js
152 | # https://nextjs.org/blog/next-9-1#public-directory-support
153 | # public
154 |
155 | # vuepress build output
156 | .vuepress/dist
157 |
158 | # vuepress v2.x temp and cache directory
159 | .temp
160 |
161 | # Docusaurus cache and generated files
162 | .docusaurus
163 |
164 | # Serverless directories
165 | .serverless/
166 |
167 | # FuseBox cache
168 | .fusebox/
169 |
170 | # DynamoDB Local files
171 | .dynamodb/
172 |
173 | # TernJS port file
174 | .tern-port
175 |
176 | # Stores VSCode versions used for testing VSCode extensions
177 | .vscode-test
178 |
179 | # yarn v2
180 | .yarn/cache
181 | .yarn/unplugged
182 | .yarn/build-state.yml
183 | .yarn/install-state.gz
184 | .pnp.*
185 |
186 | ### Node Patch ###
187 | # Serverless Webpack directories
188 | .webpack/
189 |
190 | # Optional stylelint cache
191 |
192 | # SvelteKit build / generate output
193 | .svelte-kit
194 |
195 | ### Snyk ###
196 | # DeepCode
197 | .dccache
198 |
199 | ### Tower ###
200 | # Tower.app - http://www.git-tower.com/
201 | Icon.png
202 |
203 | ### WebStorm ###
204 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
205 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
206 |
207 | # User-specific stuff
208 | .idea/**/workspace.xml
209 | .idea/**/tasks.xml
210 | .idea/**/usage.statistics.xml
211 | .idea/**/dictionaries
212 | .idea/**/shelf
213 |
214 | # AWS User-specific
215 | .idea/**/aws.xml
216 |
217 | # Generated files
218 | .idea/**/contentModel.xml
219 |
220 | # Sensitive or high-churn files
221 | .idea/**/dataSources/
222 | .idea/**/dataSources.ids
223 | .idea/**/dataSources.local.xml
224 | .idea/**/sqlDataSources.xml
225 | .idea/**/dynamic.xml
226 | .idea/**/uiDesigner.xml
227 | .idea/**/dbnavigator.xml
228 |
229 | # Gradle
230 | .idea/**/gradle.xml
231 | .idea/**/libraries
232 |
233 | # Gradle and Maven with auto-import
234 | # When using Gradle or Maven with auto-import, you should exclude module files,
235 | # since they will be recreated, and may cause churn. Uncomment if using
236 | # auto-import.
237 | # .idea/artifacts
238 | # .idea/compiler.xml
239 | # .idea/jarRepositories.xml
240 | # .idea/modules.xml
241 | # .idea/*.iml
242 | # .idea/modules
243 | # *.iml
244 | # *.ipr
245 |
246 | # CMake
247 | cmake-build-*/
248 |
249 | # Mongo Explorer plugin
250 | .idea/**/mongoSettings.xml
251 |
252 | # File-based project format
253 | *.iws
254 |
255 | # IntelliJ
256 | out/
257 |
258 | # mpeltonen/sbt-idea plugin
259 | .idea_modules/
260 |
261 | # JIRA plugin
262 | atlassian-ide-plugin.xml
263 |
264 | # Cursive Clojure plugin
265 | .idea/replstate.xml
266 |
267 | # SonarLint plugin
268 | .idea/sonarlint/
269 |
270 | # Crashlytics plugin (for Android Studio and IntelliJ)
271 | com_crashlytics_export_strings.xml
272 | crashlytics.properties
273 | crashlytics-build.properties
274 | fabric.properties
275 |
276 | # Editor-based Rest Client
277 | .idea/httpRequests
278 |
279 | # Android studio 3.1+ serialized cache file
280 | .idea/caches/build_file_checksums.ser
281 |
282 | ### WebStorm Patch ###
283 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
284 |
285 | # *.iml
286 | # modules.xml
287 | # .idea/misc.xml
288 | # *.ipr
289 |
290 | # Sonarlint plugin
291 | # https://plugins.jetbrains.com/plugin/7973-sonarlint
292 | .idea/**/sonarlint/
293 |
294 | # SonarQube Plugin
295 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
296 | .idea/**/sonarIssues.xml
297 |
298 | # Markdown Navigator plugin
299 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
300 | .idea/**/markdown-navigator.xml
301 | .idea/**/markdown-navigator-enh.xml
302 | .idea/**/markdown-navigator/
303 |
304 | # Cache file creation bug
305 | # See https://youtrack.jetbrains.com/issue/JBR-2257
306 | .idea/$CACHE_FILE$
307 |
308 | # CodeStream plugin
309 | # https://plugins.jetbrains.com/plugin/12206-codestream
310 | .idea/codestream.xml
311 |
312 | # Azure Toolkit for IntelliJ plugin
313 | # https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
314 | .idea/**/azureSettings.xml
315 |
316 | ### Windows ###
317 | # Windows thumbnail cache files
318 | Thumbs.db
319 | Thumbs.db:encryptable
320 | ehthumbs.db
321 | ehthumbs_vista.db
322 |
323 | # Dump file
324 | *.stackdump
325 |
326 | # Folder config file
327 | [Dd]esktop.ini
328 |
329 | # Recycle Bin used on file shares
330 | $RECYCLE.BIN/
331 |
332 | # Windows Installer files
333 | *.cab
334 | *.msi
335 | *.msix
336 | *.msm
337 | *.msp
338 |
339 | # Windows shortcuts
340 | *.lnk
341 |
342 | ### yarn ###
343 | # https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored
344 |
345 | .yarn/*
346 | !.yarn/releases
347 | !.yarn/patches
348 | !.yarn/plugins
349 | !.yarn/sdks
350 | !.yarn/versions
351 |
352 | # if you are NOT using Zero-installs, then:
353 | # comment the following lines
354 | !.yarn/cache
355 |
356 | # and uncomment the following lines
357 | # .pnp.*
358 |
359 | # End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,webstorm,yarn,diff,snyk,node,tower
360 |
361 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npm run build
5 | git add .
6 | npx lint-staged
7 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 |
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/jsLinters/eslint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/mastodont.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/prettier.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "avoid",
3 | "bracketSpacing": true,
4 | "jsxBracketSameLine": true,
5 | "printWidth": 120,
6 | "proseWrap": "preserve",
7 | "semi": false,
8 | "singleQuote": true,
9 | "tabWidth": 2,
10 | "trailingComma": "none"
11 | }
12 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 The Self Agency, LLC
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 | # Mastodont
2 |
3 | Mastodont is a CLI tool to import blocklists into Mastodon written in Node.js.
4 |
5 | It uses the `/admin/domain_blocks` [endpoint](https://docs.joinmastodon.org/methods/admin/domain_blocks/#create) newly
6 | available in Mastodon v4 and therefore requires instances run on v4+.
7 |
8 | 
9 |
10 | ## Installation
11 |
12 | Go to `https://${YOUR_INSTANCE_URL}/settings/applications/new` and create a new application called `Mastodont` with the
13 | permissions:
14 |
15 | - `admin:read:domain_allows`
16 | - `admin:write:domain_allows`
17 |
18 | Save the application, click on it, and copy the value of `Your access token` to the clipboard.
19 |
20 | Open a terminal and run:
21 |
22 | ```bash
23 | > npm install -g mastodont
24 | ```
25 |
26 | ## Usage
27 |
28 | ```bash
29 | > mastodont
30 | ```
31 |
32 | Mastodont operates interactively and requires only a Mastodon instance URL, an access token with the necessary
33 | permissions, and a text file containing a list of domains to block, one per line. The file can be hosted locally or remotely.
34 | A sample blocklist is provided in the `examples` folder at the root of this repository. You can also try
35 | [mastodon-defederate](https://github.com/Anthchirp/mastodon-defederate), which will download blocklists from servers you trust.
36 |
37 | Mastodont will prompt you for your instance URL, access token, and the location of the blocklist file. It will
38 | optionally save the former two options, along with your preferences for domain blocks, to a `.mastodont.yml` file in your home
39 | folder so that you don't need to enter them repeatedly.
40 |
41 | If you want to skip the prompts, you can pass the values as arguments using the following flags:
42 |
43 | ### Mastodont config
44 |
45 | - `--help`: Show help
46 | - `-c $PATH, --config $PATH`: Optional custom config file path
47 | - `--non-interactive`: Disable interactive mode
48 | - `--save`: Save config to default location
49 | - `--reset`: Delete config (cannot be used with other options)
50 |
51 | ### Instance config
52 |
53 | - `-e $URL, --endpoint $URL`: Mastodon server URL
54 | - `-t $TOKEN, --access-token $TOKEN`: Mastodon Access Token
55 | - `-b $LOCATION, --blocklist $LOCATION`: Blocklist filepath or URL
56 |
57 | ### Block config
58 |
59 | - `-s $LEVEL, --severity $LEVEL`: Block severity level (`limit`, `suspend`, `noop`)
60 | - `--obfuscate`: Obfuscate domains in public listing
61 | - `--reject-media`: Reject media from domains (works with `limit`, `noop`)
62 | - `--reject-reports`: Reject reports from domains (works with `limit`,`noop`)
63 | - `--private-comment $COMMENT`: Private comment
64 | - `--public-comment $COMMENT`: Public comment
65 |
66 | ### Automation
67 |
68 | Mastodont can be used to automatically update your blocklist on a regular basis. To do so, you can use a cron job or a
69 | CI workflow. Examples of a cron job and CI workflows are provided in the `examples` folder at the root of this
70 | repository.
71 |
72 | ### Debugging
73 |
74 | Something not working as expected? You can see more detailed debugging output if you add `DEBUG=*` before the command.
75 | For example:
76 |
77 | ```bash
78 | > DEBUG=* mastodont
79 | ```
80 |
81 | ## License
82 |
83 | MIT
84 |
85 | ## Author
86 |
87 | [@selfagency](https://kibitz.cloud/@selfagency)
88 |
--------------------------------------------------------------------------------
/dist/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 The Self Agency, LLC
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 |
--------------------------------------------------------------------------------
/dist/README.md:
--------------------------------------------------------------------------------
1 | # Mastodont
2 |
3 | Mastodont is a CLI tool to import blocklists into Mastodon written in Node.js.
4 |
5 | It uses the `/admin/domain_blocks` [endpoint](https://docs.joinmastodon.org/methods/admin/domain_blocks/#create) newly
6 | available in Mastodon v4 and therefore requires instances run on v4+.
7 |
8 | 
9 |
10 | ## Installation
11 |
12 | Go to `https://${YOUR_INSTANCE_URL}/settings/applications/new` and create a new application called `Mastodont` with the
13 | permissions:
14 |
15 | - `admin:read:domain_allows`
16 | - `admin:write:domain_allows`
17 |
18 | Save the application, click on it, and copy the value of `Your access token` to the clipboard.
19 |
20 | Open a terminal and run:
21 |
22 | ```bash
23 | > npm install -g mastodont
24 | ```
25 |
26 | ## Usage
27 |
28 | ```bash
29 | > mastodont
30 | ```
31 |
32 | Mastodont operates interactively and requires only a Mastodon instance URL, an access token with the necessary
33 | permissions, and a text file containing a list of domains to block, one per line. A sample blocklist is provided in the
34 | `examples` folder at the root of this repository. The file can be hosted locally or remotely.
35 |
36 | Mastodont will prompt you for your instance URL, access token, and the location of the blocklist file. It will
37 | optionally save
38 | the former two options, along with your preferences for domain blocks, to a `.mastodont.yml` file in your home folder so
39 | that you don't need to enter them repeatedly.
40 |
41 | If you want to skip the prompts, you can pass the values as arguments using the following flags:
42 |
43 | ### Mastodont config
44 |
45 | - `--help`: Show help
46 | - `-c $PATH, --config $PATH`: Optional custom config file path
47 | - `--non-interactive`: Disable interactive mode
48 | - `--save`: Save config to default location
49 | - `--reset`: Delete config (cannot be used with other options)
50 |
51 | ### Instance config
52 |
53 | - `-e $URL, --endpoint $URL`: Mastodon server URL
54 | - `-t $TOKEN, --access-token $TOKEN`: Mastodon Access Token
55 | - `-b $LOCATION, --blocklist $LOCATION`: Blocklist filepath or URL
56 |
57 | ### Block config
58 |
59 | - `-s $LEVEL, --severity $LEVEL`: Block severity level (`limit`, `suspend`, `noop`)
60 | - `--obfuscate`: Obfuscate domains in public listing
61 | - `--reject-media`: Reject media from domains (works with `limit`, `noop`)
62 | - `--reject-reports`: Reject reports from domains (works with `limit`,`noop`)
63 | - `--private-comment $COMMENT`: Private comment
64 | - `--public-comment $COMMENT`: Public comment
65 |
66 | ### Automation
67 |
68 | Mastodont can be used to automatically update your blocklist on a regular basis. To do so, you can use a cron job or a
69 | CI workflow. Examples of a cron job and CI workflows are provided in the `examples` folder at the root of this
70 | repository.
71 |
72 | ### Debugging
73 |
74 | Something not working as expected? You can see more detailed debugging output if you add `DEBUG=*` before the command.
75 | For example:
76 |
77 | ```bash
78 | > DEBUG=* mastodont
79 | ```
80 |
81 | ## License
82 |
83 | MIT
84 |
85 | ## Author
86 |
87 | [@selfagency](https://kibitz.cloud/@selfagency)
88 |
--------------------------------------------------------------------------------
/dist/args.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import {createRequire} from 'module';
3 | const require = createRequire(import.meta.url);
4 |
5 | var Pt=Object.create;var Ge=Object.defineProperty;var Ht=Object.getOwnPropertyDescriptor;var qt=Object.getOwnPropertyNames;var Wt=Object.getPrototypeOf,Yt=Object.prototype.hasOwnProperty;var q=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,u)=>(typeof require<"u"?require:t)[u]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+e+'" is not supported')});var Vt=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Jt=(e,t,u,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let D of qt(t))!Yt.call(e,D)&&D!==u&&Ge(e,D,{get:()=>t[D],enumerable:!(r=Ht(t,D))||r.enumerable});return e};var zt=(e,t,u)=>(u=e!=null?Pt(Wt(e)):{},Jt(t||!e||!e.__esModule?Ge(u,"default",{value:e,enumerable:!0}):u,e));var Ut=Vt((eD,jt)=>{"use strict";function Ve(e){return e&&typeof e=="object"&&"default"in e?e.default:e}var ke=Ve(q("util")),pu=q("path"),Cu=q("fs"),gu=Ve(q("os")),Ft=Ve(q("tty"));function Je(e,t){return e(t={exports:{}},t.exports),t.exports}var xe,pt=(xe=Object.freeze({__proto__:null,default:[{name:"AppVeyor",constant:"APPVEYOR",env:"APPVEYOR",pr:"APPVEYOR_PULL_REQUEST_NUMBER"},{name:"Bamboo",constant:"BAMBOO",env:"bamboo_planKey"},{name:"Bitbucket Pipelines",constant:"BITBUCKET",env:"BITBUCKET_COMMIT"},{name:"Bitrise",constant:"BITRISE",env:"BITRISE_IO",pr:"BITRISE_PULL_REQUEST"},{name:"Buddy",constant:"BUDDY",env:"BUDDY_WORKSPACE_ID",pr:"BUDDY_EXECUTION_PULL_REQUEST_ID"},{name:"Buildkite",constant:"BUILDKITE",env:"BUILDKITE",pr:{env:"BUILDKITE_PULL_REQUEST",ne:"false"}},{name:"CircleCI",constant:"CIRCLE",env:"CIRCLECI",pr:"CIRCLE_PULL_REQUEST"},{name:"Cirrus CI",constant:"CIRRUS",env:"CIRRUS_CI",pr:"CIRRUS_PR"},{name:"AWS CodeBuild",constant:"CODEBUILD",env:"CODEBUILD_BUILD_ARN"},{name:"Codeship",constant:"CODESHIP",env:{CI_NAME:"codeship"}},{name:"Drone",constant:"DRONE",env:"DRONE",pr:{DRONE_BUILD_EVENT:"pull_request"}},{name:"dsari",constant:"DSARI",env:"DSARI"},{name:"GitLab CI",constant:"GITLAB",env:"GITLAB_CI"},{name:"GoCD",constant:"GOCD",env:"GO_PIPELINE_LABEL"},{name:"Hudson",constant:"HUDSON",env:"HUDSON_URL"},{name:"Jenkins",constant:"JENKINS",env:["JENKINS_URL","BUILD_ID"],pr:{any:["ghprbPullId","CHANGE_ID"]}},{name:"Magnum CI",constant:"MAGNUM",env:"MAGNUM"},{name:"Sail CI",constant:"SAIL",env:"SAILCI",pr:"SAIL_PULL_REQUEST_NUMBER"},{name:"Semaphore",constant:"SEMAPHORE",env:"SEMAPHORE",pr:"PULL_REQUEST_NUMBER"},{name:"Shippable",constant:"SHIPPABLE",env:"SHIPPABLE",pr:{IS_PULL_REQUEST:"true"}},{name:"Solano CI",constant:"SOLANO",env:"TDDIUM",pr:"TDDIUM_PR_ID"},{name:"Strider CD",constant:"STRIDER",env:"STRIDER"},{name:"TaskCluster",constant:"TASKCLUSTER",env:["TASK_ID","RUN_ID"]},{name:"Solano CI",constant:"TDDIUM",env:"TDDIUM",pr:"TDDIUM_PR_ID",deprecated:!0},{name:"TeamCity",constant:"TEAMCITY",env:"TEAMCITY_VERSION"},{name:"Team Foundation Server",constant:"TFS",env:"TF_BUILD"},{name:"Travis CI",constant:"TRAVIS",env:"TRAVIS",pr:{env:"TRAVIS_PULL_REQUEST",ne:"false"}}]}))&&xe.default||xe,de=Je(function(e,t){var u=process.env;function r(D){return typeof D=="string"?!!u[D]:Object.keys(D).every(function(n){return u[n]===D[n]})}Object.defineProperty(t,"_vendors",{value:pt.map(function(D){return D.constant})}),t.name=null,t.isPR=null,pt.forEach(function(D){var n=(Array.isArray(D.env)?D.env:[D.env]).every(function(o){return r(o)});if(t[D.constant]=n,n)switch(t.name=D.name,typeof D.pr){case"string":t.isPR=!!u[D.pr];break;case"object":"env"in D.pr?t.isPR=D.pr.env in u&&u[D.pr.env]!==D.pr.ne:"any"in D.pr?t.isPR=D.pr.any.some(function(o){return!!u[o]}):t.isPR=r(D.pr);break;default:t.isPR=null}}),t.isCI=!!(u.CI||u.CONTINUOUS_INTEGRATION||u.BUILD_NUMBER||u.RUN_ID||t.name)}),Bt=(de.name,de.isPR,de.isCI,!1),vt=!1,At=!1,ue="development",du=typeof window<"u",Ee="",_t=!1;function $e(e){return!(!e||e==="false")}typeof process<"u"&&(process.platform&&(Ee=String(process.platform)),process.stdout&&(At=$e(process.stdout.isTTY)),Bt=Boolean(de.isCI),process.env&&(process.env.NODE_ENV&&(ue=process.env.NODE_ENV),vt=$e(process.env.DEBUG),_t=$e(process.env.MINIMAL)));var Y={browser:du,test:ue==="test",dev:ue==="development"||ue==="dev",production:ue==="production",debug:vt,ci:Bt,tty:At,minimal:void 0,minimalCLI:void 0,windows:/^win/i.test(Ee),darwin:/^darwin/i.test(Ee),linux:/^linux/i.test(Ee)};Y.minimal=_t||Y.ci||Y.test||!Y.tty,Y.minimalCLI=Y.minimal;var Te=Object.freeze(Y),m={};m[m.Fatal=0]="Fatal",m[m.Error=0]="Error",m[m.Warn=1]="Warn",m[m.Log=2]="Log",m[m.Info=3]="Info",m[m.Success=3]="Success",m[m.Debug=4]="Debug",m[m.Trace=5]="Trace",m[m.Silent=-1/0]="Silent",m[m.Verbose=1/0]="Verbose";var Eu={silent:{level:-1},fatal:{level:m.Fatal},error:{level:m.Error},warn:{level:m.Warn},log:{level:m.Log},info:{level:m.Info},success:{level:m.Success},debug:{level:m.Debug},trace:{level:m.Trace},verbose:{level:m.Trace},ready:{level:m.Info},start:{level:m.Info}};function mu(e){return t=e,Object.prototype.toString.call(t)==="[object Object]"&&!(!e.message&&!e.args)&&!e.stack;var t}var Re=!1,Ct=[],A=class{constructor(t={}){this._reporters=t.reporters||[],this._types=t.types||Eu,this.level=t.level!==void 0?t.level:3,this._defaults=t.defaults||{},this._async=t.async!==void 0?t.async:void 0,this._stdout=t.stdout,this._stderr=t.stderr,this._mockFn=t.mockFn,this._throttle=t.throttle||1e3,this._throttleMin=t.throttleMin||5;for(let u in this._types){let r={type:u,...this._types[u],...this._defaults};this[u]=this._wrapLogFn(r),this[u].raw=this._wrapLogFn(r,!0)}this._mockFn&&this.mockTypes(),this._lastLogSerialized=void 0,this._lastLog=void 0,this._lastLogTime=void 0,this._lastLogCount=0,this._throttleTimeout=void 0}get stdout(){return this._stdout||console._stdout}get stderr(){return this._stderr||console._stderr}create(t){return new A(Object.assign({reporters:this._reporters,level:this.level,types:this._types,defaults:this._defaults,stdout:this._stdout,stderr:this._stderr,mockFn:this._mockFn},t))}withDefaults(t){return this.create({defaults:Object.assign({},this._defaults,t)})}withTag(t){return this.withDefaults({tag:this._defaults.tag?this._defaults.tag+":"+t:t})}addReporter(t){return this._reporters.push(t),this}removeReporter(t){if(t){let u=this._reporters.indexOf(t);if(u>=0)return this._reporters.splice(u,1)}else this._reporters.splice(0);return this}setReporters(t){return this._reporters=Array.isArray(t)?t:[t],this}wrapAll(){this.wrapConsole(),this.wrapStd()}restoreAll(){this.restoreConsole(),this.restoreStd()}wrapConsole(){for(let t in this._types)console["__"+t]||(console["__"+t]=console[t]),console[t]=this[t].raw}restoreConsole(){for(let t in this._types)console["__"+t]&&(console[t]=console["__"+t],delete console["__"+t])}wrapStd(){this._wrapStream(this.stdout,"log"),this._wrapStream(this.stderr,"log")}_wrapStream(t,u){t&&(t.__write||(t.__write=t.write),t.write=r=>{this[u].raw(String(r).trim())})}restoreStd(){this._restoreStream(this.stdout),this._restoreStream(this.stderr)}_restoreStream(t){t&&t.__write&&(t.write=t.__write,delete t.__write)}pauseLogs(){Re=!0}resumeLogs(){Re=!1;let t=Ct.splice(0);for(let u of t)u[0]._logFn(u[1],u[2])}mockTypes(t){if(this._mockFn=t||this._mockFn,typeof this._mockFn=="function")for(let u in this._types)this[u]=this._mockFn(u,this._types[u])||this[u],this[u].raw=this[u]}_wrapLogFn(t,u){return(...r)=>{if(!Re)return this._logFn(t,r,u);Ct.push([this,t,r,u])}}_logFn(t,u,r){if(t.level>this.level)return!!this._async&&Promise.resolve(!1);let D=Object.assign({date:new Date,args:[]},t);!r&&u.length===1&&mu(u[0])?Object.assign(D,u[0]):D.args=Array.from(u),D.message&&(D.args.unshift(D.message),delete D.message),D.additional&&(Array.isArray(D.additional)||(D.additional=D.additional.split(`
6 | `)),D.args.push(`
7 | `+D.additional.join(`
8 | `)),delete D.additional),D.type=typeof D.type=="string"?D.type.toLowerCase():"",D.tag=typeof D.tag=="string"?D.tag.toLowerCase():"";let n=(s=!1)=>{let i=this._lastLogCount-this._throttleMin;if(this._lastLog&&i>0){let c=[...this._lastLog.args];i>1&&c.push(`(repeated ${i} times)`),this._log({...this._lastLog,args:c}),this._lastLogCount=1}if(s){if(this._lastLog=D,this._async)return this._logAsync(D);this._log(D)}};clearTimeout(this._throttleTimeout);let o=this._lastLogTime?D.date-this._lastLogTime:0;if(this._lastLogTime=D.date,othis._throttleMin))return void(this._throttleTimeout=setTimeout(n,this._throttle))}catch{}n(!0)}_log(t){for(let u of this._reporters)u.log(t,{async:!1,stdout:this.stdout,stderr:this.stderr})}_logAsync(t){return Promise.all(this._reporters.map(u=>u.log(t,{async:!0,stdout:this.stdout,stderr:this.stderr})))}};function wt(e){let t=process.cwd()+pu.sep;return e.split(`
9 | `).splice(1).map(u=>u.trim().replace("file://","").replace(t,""))}A.prototype.add=A.prototype.addReporter,A.prototype.remove=A.prototype.removeReporter,A.prototype.clear=A.prototype.removeReporter,A.prototype.withScope=A.prototype.withTag,A.prototype.mock=A.prototype.mockTypes,A.prototype.pause=A.prototype.pauseLogs,A.prototype.resume=A.prototype.resumeLogs;var bu=Je(function(e,t){e.exports=function(){var u="millisecond",r="second",D="minute",n="hour",o="day",s="week",i="month",c="quarter",l="year",E=/^(\d{4})-?(\d{1,2})-?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d{1,3})?$/,y=/\[([^\]]+)]|Y{2,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g,M=function(C,f,a){var p=String(C);return!p||p.length>=f?C:""+Array(f+1-p.length).join(a)+C},k={s:M,z:function(C){var f=-C.utcOffset(),a=Math.abs(f),p=Math.floor(a/60),F=a%60;return(f<=0?"+":"-")+M(p,2,"0")+":"+M(F,2,"0")},m:function(C,f){var a=12*(f.year()-C.year())+(f.month()-C.month()),p=C.clone().add(a,i),F=f-p<0,d=C.clone().add(a+(F?-1:1),i);return Number(-(a+(f-p)/(F?p-d:d-p))||0)},a:function(C){return C<0?Math.ceil(C)||0:Math.floor(C)},p:function(C){return{M:i,y:l,w:s,d:o,D:"date",h:n,m:D,s:r,ms:u,Q:c}[C]||String(C||"").toLowerCase().replace(/s$/,"")},u:function(C){return C===void 0}},U={name:"en",weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_")},L="en",x={};x[L]=U;var ve=function(C){return C instanceof se},oe=function(C,f,a){var p;if(!C)return L;if(typeof C=="string")x[C]&&(p=C),f&&(x[C]=f,p=C);else{var F=C.name;x[F]=C,p=F}return!a&&p&&(L=p),p||!a&&L},w=function(C,f){if(ve(C))return C.clone();var a=typeof f=="object"?f:{};return a.date=C,a.args=arguments,new se(a)},b=k;b.l=oe,b.i=ve,b.w=function(C,f){return w(C,{locale:f.$L,utc:f.$u,$offset:f.$offset})};var se=function(){function C(a){this.$L=this.$L||oe(a.locale,null,!0),this.parse(a)}var f=C.prototype;return f.parse=function(a){this.$d=function(p){var F=p.date,d=p.utc;if(F===null)return new Date(NaN);if(b.u(F))return new Date;if(F instanceof Date)return new Date(F);if(typeof F=="string"&&!/Z$/i.test(F)){var g=F.match(E);if(g)return d?new Date(Date.UTC(g[1],g[2]-1,g[3]||1,g[4]||0,g[5]||0,g[6]||0,g[7]||0)):new Date(g[1],g[2]-1,g[3]||1,g[4]||0,g[5]||0,g[6]||0,g[7]||0)}return new Date(F)}(a),this.init()},f.init=function(){var a=this.$d;this.$y=a.getFullYear(),this.$M=a.getMonth(),this.$D=a.getDate(),this.$W=a.getDay(),this.$H=a.getHours(),this.$m=a.getMinutes(),this.$s=a.getSeconds(),this.$ms=a.getMilliseconds()},f.$utils=function(){return b},f.isValid=function(){return this.$d.toString()!=="Invalid Date"},f.isSame=function(a,p){var F=w(a);return this.startOf(p)<=F&&F<=this.endOf(p)},f.isAfter=function(a,p){return w(a)e?`[${e}]`:"",De=class{constructor(t){this.options=Object.assign({},yu,t)}formatStack(t){return" "+wt(t).join(`
10 | `)}formatArgs(t){let u=t.map(r=>r&&typeof r.stack=="string"?r.message+`
11 | `+this.formatStack(r.stack):r);return typeof ke.formatWithOptions=="function"?ke.formatWithOptions(this.options.formatOptions,...u):ke.format(...u)}formatDate(t){return this.options.formatOptions.date?function(u,r){return bu(r).format(u)}(this.options.dateFormat,t):""}filterAndJoin(t){return t.filter(u=>u).join(" ")}formatLogObj(t){let u=this.formatArgs(t.args);return this.filterAndJoin([gt(t.type),gt(t.tag),u])}log(t,{async:u,stdout:r,stderr:D}={}){return function(n,o,s="default"){let i=o.__write||o.write;switch(s){case"async":return new Promise(c=>{i.call(o,n)===!0?c():o.once("drain",()=>{c()})});case"sync":return Cu.writeSync(o.fd,n);default:return i.call(o,n)}}(this.formatLogObj(t,{width:r.columns||0})+`
12 | `,t.level<2?D:r,u?"async":"default")}},Bu=e=>typeof e=="string"?e.replace((({onlyFirst:t=!1}={})=>{let u=["[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)","(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"].join("|");return new RegExp(u,t?void 0:"g")})(),""):e,Ot=e=>!Number.isNaN(e)&&e>=4352&&(e<=4447||e===9001||e===9002||11904<=e&&e<=12871&&e!==12351||12880<=e&&e<=19903||19968<=e&&e<=42182||43360<=e&&e<=43388||44032<=e&&e<=55203||63744<=e&&e<=64255||65040<=e&&e<=65049||65072<=e&&e<=65131||65281<=e&&e<=65376||65504<=e&&e<=65510||110592<=e&&e<=110593||127488<=e&&e<=127569||131072<=e&&e<=262141),Mt=Ot,vu=Ot;Mt.default=vu;var It=e=>{if(typeof(e=e.replace(/\uD83C\uDFF4\uDB40\uDC67\uDB40\uDC62(?:\uDB40\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDB40\uDC73\uDB40\uDC63\uDB40\uDC74|\uDB40\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F|\uD83D\uDC68(?:\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68\uD83C\uDFFB|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFE])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D)?\uD83D\uDC68|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D[\uDC68\uDC69])\u200D(?:\uD83D[\uDC66\uDC67])|[\u2695\u2696\u2708]\uFE0F|\uD83D[\uDC66\uDC67]|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|(?:\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708])\uFE0F|\uD83C\uDFFB\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C[\uDFFB-\uDFFF])|(?:\uD83E\uDDD1\uD83C\uDFFB\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)\uD83C\uDFFB|\uD83E\uDDD1(?:\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1)|(?:\uD83E\uDDD1\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFF\u200D\uD83E\uDD1D\u200D(?:\uD83D[\uDC68\uDC69]))(?:\uD83C[\uDFFB-\uDFFE])|(?:\uD83E\uDDD1\uD83C\uDFFC\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB\uDFFC])|\uD83D\uDC69(?:\uD83C\uDFFE\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB-\uDFFD\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFC\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFD-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFB\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFC-\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFD\u200D(?:\uD83E\uDD1D\u200D\uD83D\uDC68(?:\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\u200D(?:\u2764\uFE0F\u200D(?:\uD83D\uDC8B\u200D(?:\uD83D[\uDC68\uDC69])|\uD83D[\uDC68\uDC69])|\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD])|\uD83C\uDFFF\u200D(?:\uD83C[\uDF3E\uDF73\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E[\uDDAF-\uDDB3\uDDBC\uDDBD]))|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67]))|(?:\uD83E\uDDD1\uD83C\uDFFD\u200D\uD83E\uDD1D\u200D\uD83E\uDDD1|\uD83D\uDC69\uD83C\uDFFE\u200D\uD83E\uDD1D\u200D\uD83D\uDC69)(?:\uD83C[\uDFFB-\uDFFD])|\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC69\u200D(?:\uD83D[\uDC66\uDC67])|(?:\uD83D\uDC41\uFE0F\u200D\uD83D\uDDE8|\uD83D\uDC69(?:\uD83C\uDFFF\u200D[\u2695\u2696\u2708]|\uD83C\uDFFE\u200D[\u2695\u2696\u2708]|\uD83C\uDFFC\u200D[\u2695\u2696\u2708]|\uD83C\uDFFB\u200D[\u2695\u2696\u2708]|\uD83C\uDFFD\u200D[\u2695\u2696\u2708]|\u200D[\u2695\u2696\u2708])|(?:(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)\uFE0F|\uD83D\uDC6F|\uD83E[\uDD3C\uDDDE\uDDDF])\u200D[\u2640\u2642]|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:(?:\uD83C[\uDFFB-\uDFFF])\u200D[\u2640\u2642]|\u200D[\u2640\u2642])|\uD83C\uDFF4\u200D\u2620)\uFE0F|\uD83D\uDC69\u200D\uD83D\uDC67\u200D(?:\uD83D[\uDC66\uDC67])|\uD83C\uDFF3\uFE0F\u200D\uD83C\uDF08|\uD83D\uDC15\u200D\uD83E\uDDBA|\uD83D\uDC69\u200D\uD83D\uDC66|\uD83D\uDC69\u200D\uD83D\uDC67|\uD83C\uDDFD\uD83C\uDDF0|\uD83C\uDDF4\uD83C\uDDF2|\uD83C\uDDF6\uD83C\uDDE6|[#\*0-9]\uFE0F\u20E3|\uD83C\uDDE7(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF])|\uD83C\uDDF9(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF])|\uD83C\uDDEA(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA])|\uD83E\uDDD1(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF7(?:\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC])|\uD83D\uDC69(?:\uD83C[\uDFFB-\uDFFF])|\uD83C\uDDF2(?:\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF])|\uD83C\uDDE6(?:\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF])|\uD83C\uDDF0(?:\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF])|\uD83C\uDDED(?:\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA])|\uD83C\uDDE9(?:\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF])|\uD83C\uDDFE(?:\uD83C[\uDDEA\uDDF9])|\uD83C\uDDEC(?:\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE])|\uD83C\uDDF8(?:\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF])|\uD83C\uDDEB(?:\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7])|\uD83C\uDDF5(?:\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE])|\uD83C\uDDFB(?:\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA])|\uD83C\uDDF3(?:\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF])|\uD83C\uDDE8(?:\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF5\uDDF7\uDDFA-\uDDFF])|\uD83C\uDDF1(?:\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE])|\uD83C\uDDFF(?:\uD83C[\uDDE6\uDDF2\uDDFC])|\uD83C\uDDFC(?:\uD83C[\uDDEB\uDDF8])|\uD83C\uDDFA(?:\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF])|\uD83C\uDDEE(?:\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9])|\uD83C\uDDEF(?:\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5])|(?:\uD83C[\uDFC3\uDFC4\uDFCA]|\uD83D[\uDC6E\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4-\uDEB6]|\uD83E[\uDD26\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD-\uDDCF\uDDD6-\uDDDD])(?:\uD83C[\uDFFB-\uDFFF])|(?:\u26F9|\uD83C[\uDFCB\uDFCC]|\uD83D\uDD75)(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u261D\u270A-\u270D]|\uD83C[\uDF85\uDFC2\uDFC7]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC70\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDCAA\uDD74\uDD7A\uDD90\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1C\uDD1E\uDD1F\uDD30-\uDD36\uDDB5\uDDB6\uDDBB\uDDD2-\uDDD5])(?:\uD83C[\uDFFB-\uDFFF])|(?:[\u231A\u231B\u23E9-\u23EC\u23F0\u23F3\u25FD\u25FE\u2614\u2615\u2648-\u2653\u267F\u2693\u26A1\u26AA\u26AB\u26BD\u26BE\u26C4\u26C5\u26CE\u26D4\u26EA\u26F2\u26F3\u26F5\u26FA\u26FD\u2705\u270A\u270B\u2728\u274C\u274E\u2753-\u2755\u2757\u2795-\u2797\u27B0\u27BF\u2B1B\u2B1C\u2B50\u2B55]|\uD83C[\uDC04\uDCCF\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF7C\uDF7E-\uDF93\uDFA0-\uDFCA\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF4\uDFF8-\uDFFF]|\uD83D[\uDC00-\uDC3E\uDC40\uDC42-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDD7A\uDD95\uDD96\uDDA4\uDDFB-\uDE4F\uDE80-\uDEC5\uDECC\uDED0-\uDED2\uDED5\uDEEB\uDEEC\uDEF4-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])|(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5\uDEE0-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFA\uDFE0-\uDFEB]|\uD83E[\uDD0D-\uDD3A\uDD3C-\uDD45\uDD47-\uDD71\uDD73-\uDD76\uDD7A-\uDDA2\uDDA5-\uDDAA\uDDAE-\uDDCA\uDDCD-\uDDFF\uDE70-\uDE73\uDE78-\uDE7A\uDE80-\uDE82\uDE90-\uDE95])\uFE0F|(?:[\u261D\u26F9\u270A-\u270D]|\uD83C[\uDF85\uDFC2-\uDFC4\uDFC7\uDFCA-\uDFCC]|\uD83D[\uDC42\uDC43\uDC46-\uDC50\uDC66-\uDC78\uDC7C\uDC81-\uDC83\uDC85-\uDC87\uDC8F\uDC91\uDCAA\uDD74\uDD75\uDD7A\uDD90\uDD95\uDD96\uDE45-\uDE47\uDE4B-\uDE4F\uDEA3\uDEB4-\uDEB6\uDEC0\uDECC]|\uD83E[\uDD0F\uDD18-\uDD1F\uDD26\uDD30-\uDD39\uDD3C-\uDD3E\uDDB5\uDDB6\uDDB8\uDDB9\uDDBB\uDDCD-\uDDCF\uDDD1-\uDDDD])/g," "))!="string"||e.length===0)return 0;e=Bu(e);let t=0;for(let u=0;u=127&&r<=159||r>=768&&r<=879||(r>65535&&u++,t+=Mt(r)?2:1)}return t},Pe=It,Au=It;Pe.default=Au;var _u=/[|\\{}()[\]^$+*?.]/g,wu=function(e){if(typeof e!="string")throw new TypeError("Expected a string");return e.replace(_u,"\\$&")},{platform:St}=process,R={tick:"\u2714",cross:"\u2716",star:"\u2605",square:"\u2587",squareSmall:"\u25FB",squareSmallFilled:"\u25FC",play:"\u25B6",circle:"\u25EF",circleFilled:"\u25C9",circleDotted:"\u25CC",circleDouble:"\u25CE",circleCircle:"\u24DE",circleCross:"\u24E7",circlePipe:"\u24BE",circleQuestionMark:"?\u20DD",bullet:"\u25CF",dot:"\u2024",line:"\u2500",ellipsis:"\u2026",pointer:"\u276F",pointerSmall:"\u203A",info:"\u2139",warning:"\u26A0",hamburger:"\u2630",smiley:"\u32E1",mustache:"\u0DF4",heart:"\u2665",nodejs:"\u2B22",arrowUp:"\u2191",arrowDown:"\u2193",arrowLeft:"\u2190",arrowRight:"\u2192",radioOn:"\u25C9",radioOff:"\u25EF",checkboxOn:"\u2612",checkboxOff:"\u2610",checkboxCircleOn:"\u24E7",checkboxCircleOff:"\u24BE",questionMarkPrefix:"?\u20DD",oneHalf:"\xBD",oneThird:"\u2153",oneQuarter:"\xBC",oneFifth:"\u2155",oneSixth:"\u2159",oneSeventh:"\u2150",oneEighth:"\u215B",oneNinth:"\u2151",oneTenth:"\u2152",twoThirds:"\u2154",twoFifths:"\u2156",threeQuarters:"\xBE",threeFifths:"\u2157",threeEighths:"\u215C",fourFifths:"\u2158",fiveSixths:"\u215A",fiveEighths:"\u215D",sevenEighths:"\u215E"},kt={tick:"\u221A",cross:"\xD7",star:"*",square:"\u2588",squareSmall:"[ ]",squareSmallFilled:"[\u2588]",play:"\u25BA",circle:"( )",circleFilled:"(*)",circleDotted:"( )",circleDouble:"( )",circleCircle:"(\u25CB)",circleCross:"(\xD7)",circlePipe:"(\u2502)",circleQuestionMark:"(?)",bullet:"*",dot:".",line:"\u2500",ellipsis:"...",pointer:">",pointerSmall:"\xBB",info:"i",warning:"\u203C",hamburger:"\u2261",smiley:"\u263A",mustache:"\u250C\u2500\u2510",heart:R.heart,nodejs:"\u2666",arrowUp:R.arrowUp,arrowDown:R.arrowDown,arrowLeft:R.arrowLeft,arrowRight:R.arrowRight,radioOn:"(*)",radioOff:"( )",checkboxOn:"[\xD7]",checkboxOff:"[ ]",checkboxCircleOn:"(\xD7)",checkboxCircleOff:"( )",questionMarkPrefix:"\uFF1F",oneHalf:"1/2",oneThird:"1/3",oneQuarter:"1/4",oneFifth:"1/5",oneSixth:"1/6",oneSeventh:"1/7",oneEighth:"1/8",oneNinth:"1/9",oneTenth:"1/10",twoThirds:"2/3",twoFifths:"2/5",threeQuarters:"3/4",threeFifths:"3/5",threeEighths:"3/8",fourFifths:"4/5",fiveSixths:"5/6",fiveEighths:"5/8",sevenEighths:"7/8"};St==="linux"&&(R.questionMarkPrefix="?");var Ce=St==="win32"?kt:R,Q=Object.assign(e=>{if(Ce===R)return e;for(let[t,u]of Object.entries(R))u!==Ce[t]&&(e=e.replace(new RegExp(wu(u),"g"),Ce[t]));return e},Ce),Ou=R,Mu=kt;Q.main=Ou,Q.windows=Mu;var re={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},xt={};for(let e of Object.keys(re))xt[re[e]]=e;var h={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}},J=h;for(let e of Object.keys(h)){if(!("channels"in h[e]))throw new Error("missing channels property: "+e);if(!("labels"in h[e]))throw new Error("missing channel labels property: "+e);if(h[e].labels.length!==h[e].channels)throw new Error("channel and label counts mismatch: "+e);let{channels:t,labels:u}=h[e];delete h[e].channels,delete h[e].labels,Object.defineProperty(h[e],"channels",{value:t}),Object.defineProperty(h[e],"labels",{value:u})}function Iu(e){let t=function(){let r={},D=Object.keys(J);for(let n=D.length,o=0;o1&&(D-=1)),[360*D,100*n,100*c]},h.rgb.hwb=function(e){let t=e[0],u=e[1],r=e[2],D=h.rgb.hsl(e)[0],n=1/255*Math.min(t,Math.min(u,r));return r=1-1/255*Math.max(t,Math.max(u,r)),[D,100*n,100*r]},h.rgb.cmyk=function(e){let t=e[0]/255,u=e[1]/255,r=e[2]/255,D=Math.min(1-t,1-u,1-r);return[100*((1-t-D)/(1-D)||0),100*((1-u-D)/(1-D)||0),100*((1-r-D)/(1-D)||0),100*D]},h.rgb.keyword=function(e){let t=xt[e];if(t)return t;let u,r=1/0;for(let o of Object.keys(re)){let s=(n=re[o],((D=e)[0]-n[0])**2+(D[1]-n[1])**2+(D[2]-n[2])**2);s.04045?((t+.055)/1.055)**2.4:t/12.92,u=u>.04045?((u+.055)/1.055)**2.4:u/12.92,r=r>.04045?((r+.055)/1.055)**2.4:r/12.92,[100*(.4124*t+.3576*u+.1805*r),100*(.2126*t+.7152*u+.0722*r),100*(.0193*t+.1192*u+.9505*r)]},h.rgb.lab=function(e){let t=h.rgb.xyz(e),u=t[0],r=t[1],D=t[2];return u/=95.047,r/=100,D/=108.883,u=u>.008856?u**(1/3):7.787*u+16/116,r=r>.008856?r**(1/3):7.787*r+16/116,D=D>.008856?D**(1/3):7.787*D+16/116,[116*r-16,500*(u-r),200*(r-D)]},h.hsl.rgb=function(e){let t=e[0]/360,u=e[1]/100,r=e[2]/100,D,n,o;if(u===0)return o=255*r,[o,o,o];D=r<.5?r*(1+u):r+u-r*u;let s=2*r-D,i=[0,0,0];for(let c=0;c<3;c++)n=t+1/3*-(c-1),n<0&&n++,n>1&&n--,o=6*n<1?s+6*(D-s)*n:2*n<1?D:3*n<2?s+(D-s)*(2/3-n)*6:s,i[c]=255*o;return i},h.hsl.hsv=function(e){let t=e[0],u=e[1]/100,r=e[2]/100,D=u,n=Math.max(r,.01);return r*=2,u*=r<=1?r:2-r,D*=n<=1?n:2-n,[t,100*(r===0?2*D/(n+D):2*u/(r+u)),100*((r+u)/2)]},h.hsv.rgb=function(e){let t=e[0]/60,u=e[1]/100,r=e[2]/100,D=Math.floor(t)%6,n=t-Math.floor(t),o=255*r*(1-u),s=255*r*(1-u*n),i=255*r*(1-u*(1-n));switch(r*=255,D){case 0:return[r,i,o];case 1:return[s,r,o];case 2:return[o,r,i];case 3:return[o,s,r];case 4:return[i,o,r];case 5:return[r,o,s]}},h.hsv.hsl=function(e){let t=e[0],u=e[1]/100,r=e[2]/100,D=Math.max(r,.01),n,o;o=(2-u)*r;let s=(2-u)*D;return n=u*D,n/=s<=1?s:2-s,n=n||0,o/=2,[t,100*n,100*o]},h.hwb.rgb=function(e){let t=e[0]/360,u=e[1]/100,r=e[2]/100,D=u+r,n;D>1&&(u/=D,r/=D);let o=Math.floor(6*t),s=1-r;n=6*t-o,(1&o)!=0&&(n=1-n);let i=u+n*(s-u),c,l,E;switch(o){default:case 6:case 0:c=s,l=i,E=u;break;case 1:c=i,l=s,E=u;break;case 2:c=u,l=s,E=i;break;case 3:c=u,l=i,E=s;break;case 4:c=i,l=u,E=s;break;case 5:c=s,l=u,E=i}return[255*c,255*l,255*E]},h.cmyk.rgb=function(e){let t=e[0]/100,u=e[1]/100,r=e[2]/100,D=e[3]/100;return[255*(1-Math.min(1,t*(1-D)+D)),255*(1-Math.min(1,u*(1-D)+D)),255*(1-Math.min(1,r*(1-D)+D))]},h.xyz.rgb=function(e){let t=e[0]/100,u=e[1]/100,r=e[2]/100,D,n,o;return D=3.2406*t+-1.5372*u+-.4986*r,n=-.9689*t+1.8758*u+.0415*r,o=.0557*t+-.204*u+1.057*r,D=D>.0031308?1.055*D**(1/2.4)-.055:12.92*D,n=n>.0031308?1.055*n**(1/2.4)-.055:12.92*n,o=o>.0031308?1.055*o**(1/2.4)-.055:12.92*o,D=Math.min(Math.max(0,D),1),n=Math.min(Math.max(0,n),1),o=Math.min(Math.max(0,o),1),[255*D,255*n,255*o]},h.xyz.lab=function(e){let t=e[0],u=e[1],r=e[2];return t/=95.047,u/=100,r/=108.883,t=t>.008856?t**(1/3):7.787*t+16/116,u=u>.008856?u**(1/3):7.787*u+16/116,r=r>.008856?r**(1/3):7.787*r+16/116,[116*u-16,500*(t-u),200*(u-r)]},h.lab.xyz=function(e){let t,u,r;u=(e[0]+16)/116,t=e[1]/500+u,r=u-e[2]/200;let D=u**3,n=t**3,o=r**3;return u=D>.008856?D:(u-16/116)/7.787,t=n>.008856?n:(t-16/116)/7.787,r=o>.008856?o:(r-16/116)/7.787,t*=95.047,u*=100,r*=108.883,[t,u,r]},h.lab.lch=function(e){let t=e[0],u=e[1],r=e[2],D;return D=360*Math.atan2(r,u)/2/Math.PI,D<0&&(D+=360),[t,Math.sqrt(u*u+r*r),D]},h.lch.lab=function(e){let t=e[0],u=e[1],r=e[2]/360*2*Math.PI;return[t,u*Math.cos(r),u*Math.sin(r)]},h.rgb.ansi16=function(e,t=null){let[u,r,D]=e,n=t===null?h.rgb.hsv(e)[2]:t;if(n=Math.round(n/50),n===0)return 30;let o=30+(Math.round(D/255)<<2|Math.round(r/255)<<1|Math.round(u/255));return n===2&&(o+=60),o},h.hsv.ansi16=function(e){return h.rgb.ansi16(h.hsv.rgb(e),e[2])},h.rgb.ansi256=function(e){let t=e[0],u=e[1],r=e[2];return t===u&&u===r?t<8?16:t>248?231:Math.round((t-8)/247*24)+232:16+36*Math.round(t/255*5)+6*Math.round(u/255*5)+Math.round(r/255*5)},h.ansi16.rgb=function(e){let t=e%10;if(t===0||t===7)return e>50&&(t+=3.5),t=t/10.5*255,[t,t,t];let u=.5*(1+~~(e>50));return[(1&t)*u*255,(t>>1&1)*u*255,(t>>2&1)*u*255]},h.ansi256.rgb=function(e){if(e>=232){let u=10*(e-232)+8;return[u,u,u]}let t;return e-=16,[Math.floor(e/36)/5*255,Math.floor((t=e%36)/6)/5*255,t%6/5*255]},h.rgb.hex=function(e){let t=(((255&Math.round(e[0]))<<16)+((255&Math.round(e[1]))<<8)+(255&Math.round(e[2]))).toString(16).toUpperCase();return"000000".substring(t.length)+t},h.hex.rgb=function(e){let t=e.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!t)return[0,0,0];let u=t[0];t[0].length===3&&(u=u.split("").map(D=>D+D).join(""));let r=parseInt(u,16);return[r>>16&255,r>>8&255,255&r]},h.rgb.hcg=function(e){let t=e[0]/255,u=e[1]/255,r=e[2]/255,D=Math.max(Math.max(t,u),r),n=Math.min(Math.min(t,u),r),o=D-n,s,i;return s=o<1?n/(1-o):0,i=o<=0?0:D===t?(u-r)/o%6:D===u?2+(r-t)/o:4+(t-u)/o,i/=6,i%=1,[360*i,100*o,100*s]},h.hsl.hcg=function(e){let t=e[1]/100,u=e[2]/100,r=u<.5?2*t*u:2*t*(1-u),D=0;return r<1&&(D=(u-.5*r)/(1-r)),[e[0],100*r,100*D]},h.hsv.hcg=function(e){let t=e[1]/100,u=e[2]/100,r=t*u,D=0;return r<1&&(D=(u-r)/(1-r)),[e[0],100*r,100*D]},h.hcg.rgb=function(e){let t=e[0]/360,u=e[1]/100,r=e[2]/100;if(u===0)return[255*r,255*r,255*r];let D=[0,0,0],n=t%1*6,o=n%1,s=1-o,i=0;switch(Math.floor(n)){case 0:D[0]=1,D[1]=o,D[2]=0;break;case 1:D[0]=s,D[1]=1,D[2]=0;break;case 2:D[0]=0,D[1]=1,D[2]=o;break;case 3:D[0]=0,D[1]=s,D[2]=1;break;case 4:D[0]=o,D[1]=0,D[2]=1;break;default:D[0]=1,D[1]=0,D[2]=s}return i=(1-u)*r,[255*(u*D[0]+i),255*(u*D[1]+i),255*(u*D[2]+i)]},h.hcg.hsv=function(e){let t=e[1]/100,u=t+e[2]/100*(1-t),r=0;return u>0&&(r=t/u),[e[0],100*r,100*u]},h.hcg.hsl=function(e){let t=e[1]/100,u=e[2]/100*(1-t)+.5*t,r=0;return u>0&&u<.5?r=t/(2*u):u>=.5&&u<1&&(r=t/(2*(1-u))),[e[0],100*r,100*u]},h.hcg.hwb=function(e){let t=e[1]/100,u=t+e[2]/100*(1-t);return[e[0],100*(u-t),100*(1-u)]},h.hwb.hcg=function(e){let t=e[1]/100,u=1-e[2]/100,r=u-t,D=0;return r<1&&(D=(u-r)/(1-r)),[e[0],100*r,100*D]},h.apple.rgb=function(e){return[e[0]/65535*255,e[1]/65535*255,e[2]/65535*255]},h.rgb.apple=function(e){return[e[0]/255*65535,e[1]/255*65535,e[2]/255*65535]},h.gray.rgb=function(e){return[e[0]/100*255,e[0]/100*255,e[0]/100*255]},h.gray.hsl=function(e){return[0,0,e[0]]},h.gray.hsv=h.gray.hsl,h.gray.hwb=function(e){return[0,100,e[0]]},h.gray.cmyk=function(e){return[0,0,0,e[0]]},h.gray.lab=function(e){return[e[0],0,0]},h.gray.hex=function(e){let t=255&Math.round(e[0]/100*255),u=((t<<16)+(t<<8)+t).toString(16).toUpperCase();return"000000".substring(u.length)+u},h.rgb.gray=function(e){return[(e[0]+e[1]+e[2])/3/255*100]};var G={};Object.keys(J).forEach(e=>{G[e]={},Object.defineProperty(G[e],"channels",{value:J[e].channels}),Object.defineProperty(G[e],"labels",{value:J[e].labels});let t=function(u){let r=Iu(u),D={},n=Object.keys(r);for(let o=n.length,s=0;s{let r=t[u];G[e][u]=function(D){let n=function(...o){let s=o[0];if(s==null)return s;s.length>1&&(o=s);let i=D(o);if(typeof i=="object")for(let c=i.length,l=0;l1&&(o=s),D(o))};return"conversion"in D&&(n.conversion=D.conversion),n}(r)})});var xu=G,ne=Je(function(e){let t=(c,l)=>(...E)=>`\x1B[${c(...E)+l}m`,u=(c,l)=>(...E)=>{let y=c(...E);return`\x1B[${38+l};5;${y}m`},r=(c,l)=>(...E)=>{let y=c(...E);return`\x1B[${38+l};2;${y[0]};${y[1]};${y[2]}m`},D=c=>c,n=(c,l,E)=>[c,l,E],o=(c,l,E)=>{Object.defineProperty(c,l,{get:()=>{let y=E();return Object.defineProperty(c,l,{value:y,enumerable:!0,configurable:!0}),y},enumerable:!0,configurable:!0})},s,i=(c,l,E,y)=>{s===void 0&&(s=xu);let M=y?10:0,k={};for(let[U,L]of Object.entries(s)){let x=U==="ansi16"?"ansi":U;U===l?k[x]=c(E,M):typeof L=="object"&&(k[x]=c(L[l],M))}return k};Object.defineProperty(e,"exports",{enumerable:!0,get:function(){let c=new Map,l={modifier:{reset:[0,0],bold:[1,22],dim:[2,22],italic:[3,23],underline:[4,24],inverse:[7,27],hidden:[8,28],strikethrough:[9,29]},color:{black:[30,39],red:[31,39],green:[32,39],yellow:[33,39],blue:[34,39],magenta:[35,39],cyan:[36,39],white:[37,39],blackBright:[90,39],redBright:[91,39],greenBright:[92,39],yellowBright:[93,39],blueBright:[94,39],magentaBright:[95,39],cyanBright:[96,39],whiteBright:[97,39]},bgColor:{bgBlack:[40,49],bgRed:[41,49],bgGreen:[42,49],bgYellow:[43,49],bgBlue:[44,49],bgMagenta:[45,49],bgCyan:[46,49],bgWhite:[47,49],bgBlackBright:[100,49],bgRedBright:[101,49],bgGreenBright:[102,49],bgYellowBright:[103,49],bgBlueBright:[104,49],bgMagentaBright:[105,49],bgCyanBright:[106,49],bgWhiteBright:[107,49]}};l.color.gray=l.color.blackBright,l.bgColor.bgGray=l.bgColor.bgBlackBright,l.color.grey=l.color.blackBright,l.bgColor.bgGrey=l.bgColor.bgBlackBright;for(let[E,y]of Object.entries(l)){for(let[M,k]of Object.entries(y))l[M]={open:`\x1B[${k[0]}m`,close:`\x1B[${k[1]}m`},y[M]=l[M],c.set(k[0],k[1]);Object.defineProperty(l,E,{value:y,enumerable:!1})}return Object.defineProperty(l,"codes",{value:c,enumerable:!1}),l.color.close="\x1B[39m",l.bgColor.close="\x1B[49m",o(l.color,"ansi",()=>i(t,"ansi16",D,!1)),o(l.color,"ansi256",()=>i(u,"ansi256",D,!1)),o(l.color,"ansi16m",()=>i(r,"rgb",n,!1)),o(l.bgColor,"ansi",()=>i(t,"ansi16",D,!0)),o(l.bgColor,"ansi256",()=>i(u,"ansi256",D,!0)),o(l.bgColor,"ansi16m",()=>i(r,"rgb",n,!0)),l}})}),$=(e,t=process.argv)=>{let u=e.startsWith("-")?"":e.length===1?"-":"--",r=t.indexOf(u+e),D=t.indexOf("--");return r!==-1&&(D===-1||r=2,has16m:e>=3}}function je(e,t){if(K===0)return 0;if($("color=16m")||$("color=full")||$("color=truecolor"))return 3;if($("color=256"))return 2;if(e&&!t&&K===void 0)return 0;let u=K||0;if(_.TERM==="dumb")return u;if(process.platform==="win32"){let r=gu.release().split(".");return Number(r[0])>=10&&Number(r[2])>=10586?Number(r[2])>=14931?3:2:1}if("CI"in _)return["TRAVIS","CIRCLECI","APPVEYOR","GITLAB_CI"].some(r=>r in _)||_.CI_NAME==="codeship"?1:u;if("TEAMCITY_VERSION"in _)return/^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(_.TEAMCITY_VERSION)?1:0;if("GITHUB_ACTIONS"in _)return 1;if(_.COLORTERM==="truecolor")return 3;if("TERM_PROGRAM"in _){let r=parseInt((_.TERM_PROGRAM_VERSION||"").split(".")[0],10);switch(_.TERM_PROGRAM){case"iTerm.app":return r>=3?3:2;case"Apple_Terminal":return 2}}return/-256(color)?$/i.test(_.TERM)?2:/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(_.TERM)||"COLORTERM"in _?1:u}$("no-color")||$("no-colors")||$("color=false")||$("color=never")?K=0:($("color")||$("colors")||$("color=true")||$("color=always"))&&(K=1),"FORCE_COLOR"in _&&(K=_.FORCE_COLOR==="true"?1:_.FORCE_COLOR==="false"?0:_.FORCE_COLOR.length===0?1:Math.min(parseInt(_.FORCE_COLOR,10),3));var $u={supportsColor:function(e){return Le(je(e,e&&e.isTTY))},stdout:Le(je(!0,Ft.isatty(1))),stderr:Le(je(!0,Ft.isatty(2)))},Tu={stringReplaceAll:(e,t,u)=>{let r=e.indexOf(t);if(r===-1)return e;let D=t.length,n=0,o="";do o+=e.substr(n,r-n)+t+u,n=r+D,r=e.indexOf(t,n);while(r!==-1);return o+=e.substr(n),o},stringEncaseCRLFWithFirstIndex:(e,t,u,r)=>{let D=0,n="";do{let o=e[r-1]==="\r";n+=e.substr(D,(o?r-1:r)-D)+t+(o?`\r
13 | `:`
14 | `)+u,D=r+1,r=e.indexOf(`
15 | `,D)}while(r!==-1);return n+=e.substr(D),n}},Ru=/(?:\\(u(?:[a-f\d]{4}|\{[a-f\d]{1,6}\})|x[a-f\d]{2}|.))|(?:\{(~)?(\w+(?:\([^)]*\))?(?:\.\w+(?:\([^)]*\))?)*)(?:[ \t]|(?=\r?\n)))|(\})|((?:.|[\r\n\f])+?)/gi,dt=/(?:^|\.)(\w+)(?:\(([^)]*)\))?/g,Lu=/^(['"])((?:\\.|(?!\1)[^\\])*)\1$/,ju=/\\(u(?:[a-f\d]{4}|{[a-f\d]{1,6}})|x[a-f\d]{2}|.)|([^\\])/gi,Uu=new Map([["n",`
16 | `],["r","\r"],["t"," "],["b","\b"],["f","\f"],["v","\v"],["0","\0"],["\\","\\"],["e","\x1B"],["a","\x07"]]);function $t(e){let t=e[0]==="u",u=e[1]==="{";return t&&!u&&e.length===5||e[0]==="x"&&e.length===3?String.fromCharCode(parseInt(e.slice(1),16)):t&&u?String.fromCodePoint(parseInt(e.slice(2,-1),16)):Uu.get(e)||e}function Nu(e,t){let u=[],r=t.trim().split(/\s*,\s*/g),D;for(let n of r){let o=Number(n);if(Number.isNaN(o)){if(!(D=n.match(Lu)))throw new Error(`Invalid Chalk template style argument: ${n} (in style '${e}')`);u.push(D[2].replace(ju,(s,i,c)=>i?$t(i):c))}else u.push(o)}return u}function Pu(e){dt.lastIndex=0;let t=[],u;for(;(u=dt.exec(e))!==null;){let r=u[1];if(u[2]){let D=Nu(r,u[2]);t.push([r].concat(D))}else t.push([r])}return t}function Et(e,t){let u={};for(let D of t)for(let n of D.styles)u[n[0]]=D.inverse?null:n.slice(1);let r=e;for(let[D,n]of Object.entries(u))if(Array.isArray(n)){if(!(D in r))throw new Error("Unknown Chalk style: "+D);r=n.length>0?r[D](...n):r[D]}return r}var Hu=(e,t)=>{let u=[],r=[],D=[];if(t.replace(Ru,(n,o,s,i,c,l)=>{if(o)D.push($t(o));else if(i){let E=D.join("");D=[],r.push(u.length===0?E:Et(e,u)(E)),u.push({inverse:s,styles:Pu(i)})}else if(c){if(u.length===0)throw new Error("Found extraneous } in Chalk template literal");r.push(Et(e,u)(D.join(""))),D=[],u.pop()}else D.push(l)}),r.push(D.join("")),u.length>0){let n=`Chalk template literal is missing ${u.length} closing bracket${u.length===1?"":"s"} (\`}\`)`;throw new Error(n)}return r.join("")},{stdout:He,stderr:Ue}=$u,{stringReplaceAll:qu,stringEncaseCRLFWithFirstIndex:Wu}=Tu,Tt=["ansi","ansi","ansi256","ansi16m"],Z=Object.create(null),qe=class{constructor(t){return Rt(t)}},Rt=e=>{let t={};return((u,r={})=>{if(r.level&&!(Number.isInteger(r.level)&&r.level>=0&&r.level<=3))throw new Error("The `level` option should be an integer from 0 to 3");let D=He?He.level:0;u.level=r.level===void 0?D:r.level})(t,e),t.template=(...u)=>Ju(t.template,...u),Object.setPrototypeOf(t,ye.prototype),Object.setPrototypeOf(t.template,t),t.template.constructor=()=>{throw new Error("`chalk.constructor()` is deprecated. Use `new chalk.Instance()` instead.")},t.template.Instance=qe,t.template};function ye(e){return Rt(e)}for(let[e,t]of Object.entries(ne))Z[e]={get(){let u=Be(this,ze(t.open,t.close,this._styler),this._isEmpty);return Object.defineProperty(this,e,{value:u}),u}};Z.visible={get(){let e=Be(this,this._styler,!0);return Object.defineProperty(this,"visible",{value:e}),e}};var Lt=["rgb","hex","keyword","hsl","hsv","hwb","ansi","ansi256"];for(let e of Lt)Z[e]={get(){let{level:t}=this;return function(...u){let r=ze(ne.color[Tt[t]][e](...u),ne.color.close,this._styler);return Be(this,r,this._isEmpty)}}};for(let e of Lt)Z["bg"+e[0].toUpperCase()+e.slice(1)]={get(){let{level:t}=this;return function(...u){let r=ze(ne.bgColor[Tt[t]][e](...u),ne.bgColor.close,this._styler);return Be(this,r,this._isEmpty)}}};var Yu=Object.defineProperties(()=>{},{...Z,level:{enumerable:!0,get(){return this._generator.level},set(e){this._generator.level=e}}}),ze=(e,t,u)=>{let r,D;return u===void 0?(r=e,D=t):(r=u.openAll+e,D=t+u.closeAll),{open:e,close:t,openAll:r,closeAll:D,parent:u}},Be=(e,t,u)=>{let r=(...D)=>Vu(r,D.length===1?""+D[0]:D.join(" "));return Object.setPrototypeOf(r,Yu),r._generator=e,r._styler=t,r._isEmpty=u,r},Vu=(e,t)=>{if(e.level<=0||!t)return e._isEmpty?"":t;let u=e._styler;if(u===void 0)return t;let{openAll:r,closeAll:D}=u;if(t.indexOf("\x1B")!==-1)for(;u!==void 0;)t=qu(t,u.close,u.open),u=u.parent;let n=t.indexOf(`
17 | `);return n!==-1&&(t=Wu(t,D,r,n)),r+t+D},Ne,Ju=(e,...t)=>{let[u]=t;if(!Array.isArray(u))return t.join(" ");let r=t.slice(1),D=[u.raw[0]];for(let n=1;n" "+D.replace(/^at +/,n=>u(n)).replace(/\((.+)\)/,(n,o)=>`(${r(o)})`)).join(`
19 | `)}formatType(t,u){let r=zu[t.type]||Gu[t.level]||this.options.secondaryColor;if(u)return function(n){let o=bt[n];return o||(o=n[0]==="#"?V.bgHex(n):V["bg"+n[0].toUpperCase()+n.slice(1)]||V.bgKeyword(n),bt[n]=o,o)}(r).black(` ${t.type.toUpperCase()} `);let D=typeof yt[t.type]=="string"?yt[t.type]:t.icon||t.type;return D?ge(r)(D):""}formatLogObj(t,{width:u}){let[r,...D]=this.formatArgs(t.args).split(`
20 | `),n=t.badge!==void 0?Boolean(t.badge):t.level<2,o=ge(this.options.secondaryColor),s=this.formatDate(t.date),i=s&&o(s),c=this.formatType(t,n),l=t.tag?o(t.tag):"",E=r.replace(/`([^`]+)`/g,(L,x)=>V.cyan(x)),y,M=this.filterAndJoin([c,E]),k=this.filterAndJoin([l,i]),U=u-Pe(M)-Pe(k)-2;return y=U>0&&u>=80?M+" ".repeat(U)+k:M,y+=D.length?`
21 | `+D.join(`
22 | `):"",n?`
23 | `+y+`
24 | `:y}},We=class{constructor({stream:t}={}){this.stream=t||process.stdout}log(t){this.stream.write(JSON.stringify(t)+`
25 | `)}},Ku=typeof __non_webpack_require__<"u"?__non_webpack_require__:q,Ye=class{constructor(t){if(t&&t.log)this.logger=t;else{let u=Ku("winston");this.logger=u.createLogger(Object.assign({level:"info",format:u.format.simple(),transports:[new u.transports.Console]},t))}}log(t){let u=[].concat(t.args),r=u.shift();this.logger.log({level:Zu[t.level]||"info",label:t.tag,message:r,args:u,timestamp:t.date.getTime()/1e3})}},Zu={0:"error",1:"warn",2:"info",3:"verbose",4:"debug",5:"silly"};global.consola||(global.consola=function(){let e=Te.debug?4:3;process.env.CONSOLA_LEVEL&&(e=parseInt(process.env.CONSOLA_LEVEL)||e);let t=new A({level:e,reporters:[Te.ci||Te.test?new De:new be]});return t.Consola=A,t.BasicReporter=De,t.FancyReporter=be,t.JSONReporter=We,t.WinstonReporter=Ye,t.LogLevel=m,t}());var Xu=global.consola;jt.exports=Xu});var Qe={default:95,hint:90,multi:90,param:96,required:33};function ae(e,t){return!t&&(t={}),(...u)=>{let r=JSON.stringify(u);return r!==void 0?r in t?t[r]:t[r]=e.apply(null,u):e.apply(null,u)}}var le=ae((e,t)=>e.repeat(t));var Ke=e=>/^[a-f0-9]+$/i.test(e);var Ze=e=>/^[-+]?\d+$/.test(e),Xe=e=>/^[-+]?\d*\.?\d+(e[-+]?\d+)?$/i.test(e);var ce=(e,t=u=>u!==void 0?": "+u:"")=>class extends Error{constructor(u){super(e(u)+t(u))}};var Gt=ce(()=>"illegal argument(s)"),T=e=>{throw new Gt(e)};var _e=e=>Xe(e)?parseFloat(e):T(`not a numeric value: ${e}`),et=e=>e.map(_e),we=e=>Ke(e)?parseInt(e,16):T(`not a hex value: ${e}`),tt=e=>e.map(we),Oe=e=>Ze(e)?parseInt(e):T(`not an integer: ${e}`),ut=e=>e.map(Oe);var he=(e,t)=>u=>({coerce:e,hint:t,group:"main",...u}),fe=(e,t)=>u=>({hint:Qt(t,u.delim),multi:!0,coerce:e,group:"main",...u}),Qt=(e,t)=>e+(t?`[${t}..]`:""),W=e=>({flag:!0,default:!1,group:"flags",...e}),H=he(e=>e,"STR"),br=fe(e=>e,"STR"),yr=he(_e,"NUM"),Br=he(we,"HEX"),vr=he(Oe,"INT"),Ar=fe(et,"NUM"),_r=fe(tt,"HEX"),wr=fe(ut,"INT");var rt=Array.isArray;var Kt=e=>e.toUpperCase(),Dt=e=>e.toLowerCase(),nt=e=>e[0].toUpperCase()+e.substring(1),ot=(e,t="-")=>Dt(e.replace(/([a-z0-9\u00e0-\u00fd])([A-Z\u00c0-\u00dd])/g,(u,r,D)=>r+t+D));var st=(e,t="-")=>Dt(e).replace(new RegExp(`\\${t}+(\\w)`,"g"),(u,r)=>Kt(r));var Zt=/\x1b\[[0-9;]+m/g,Xt=e=>e.replace(Zt,"");var Fe=e=>Xt(e).length;var it=ae((e,t=" ")=>{let u=le(String(t),e);return(r,D)=>r==null?u:(r=r.toString(),D=D!==void 0?D:r.length,Du=>e||typeof u!="string"&&typeof u!="number"?JSON.stringify(u,null,t):String(u);function*Me(e,t=/\r?\n/g,u=!1){let r=0,D=e.length,n=~~u,o=typeof t=="string"?new RegExp(t,"g"):t;for(;r0),this}toString(){return this.w.join(" ")}},eu={length:e=>e.length,split:(e,t)=>t},ct={length:Fe,split:(e,t)=>{let u=/\x1b\[[0-9;]+m/g,r=t,D;for(;(D=u.exec(e))&&!(D.index>=t);){let n=D[0].length;r+=n,t+=n}return r}},lt=(e,t,u,r)=>{let D=e[e.length-1];D&&r-D.n>u?D.add(t,u):e.push(new pe(t,u))},tu=(e,{width:t,min:u,hard:r,splitter:D},n=0,o=[])=>{let s=D.length(e),i=t-n;for(ii;){let c=D.split(e,i),l=e.substring(0,c);lt(o,l,i,t),e=e.substring(c),i=t,s=D.length(e)}return lt(o,e,s,t),o},uu=(e,t,u=[])=>{if(!e.length)return u.push(new pe),u;let r={width:80,min:4,hard:!1,splitter:eu,...t};for(let D of Me(e,t.delimWord||/\s/g)){let n=u[u.length-1];tu(D,r,n&&n.n>0?n.n+1:0,u)}return u},ht=(e,t)=>{let u=[];for(let r of Me(e,t.delimLine))u=u.concat(uu(r,t));return u};var Se=(e,t={})=>{t={lineWidth:80,paramWidth:32,showDefaults:!0,prefix:"",suffix:"",groups:["flags","main"],...t};let u=t.color!==!1?{...Qe,...t.color}:{},r=le(" ",t.paramWidth),D=s=>s.map(i=>ru(i,e[i],t,u,r)),n=Object.keys(e).sort(),o=t.groups?t.groups.map(s=>[s,n.filter(i=>e[i].group===s)]).filter(s=>!!s[1].length):[["options",n]];return[...Ie(t.prefix,t.lineWidth),...o.map(([s,i])=>[...t.showGroupNames?[`${nt(s)}:
26 | `]:[],...D(i),""].join(`
27 | `)),...Ie(t.suffix,t.lineWidth)].join(`
28 | `)},ru=(e,t,u,r,D)=>{let n=Du(t,r),o=nu(t,r,n),s=te(`--${ot(e)}`,r.param),i=`${o}${s}${n}`,c=t.optional===!1&&t.default===void 0,l=[];c&&l.push("required"),t.multi&&l.push("multiple");let E=ou(l,r,c)+(t.desc||"")+su(t,u,r);return it(u.paramWidth)(i,Fe(i))+Ie(E,u.lineWidth-u.paramWidth).map((y,M)=>M>0?D+y:y).join(`
29 | `)},Du=(e,t)=>e.hint?te(" "+e.hint,t.hint):"",nu=(e,t,u)=>e.alias?`${te("-"+e.alias,t.param)}${u}, `:"",ou=(e,t,u)=>e.length?te(`[${e.join(", ")}] `,u?t.required:t.multi):"",su=(e,t,u)=>t.showDefaults&&e.default!=null&&e.default!==!1?te(` (default: ${at(!0)(e.defaultHint!=null?e.defaultHint:e.default)})`,u.default):"",te=(e,t)=>t!=null?`\x1B[${t}m${e}\x1B[0m`:e,Ie=(e,t)=>e?ht(e,{width:t,splitter:ct,hard:!0}):[];var iu=ce(()=>"parse error"),ft=(e,t,u)=>{u={start:2,showUsage:!0,help:["--help","-h"],...u};try{return au(e,t,u)}catch(r){throw u.showUsage&&console.log(r.message+`
30 |
31 | `+Se(e,u.usageOpts)),new iu(r.message)}},au=(e,t,u)=>{let r=lu(e),D={},n,o,s=u.start;for(;s=t.length}},lu=e=>Object.entries(e).reduce((t,[u,r])=>(r.alias&&(t[r.alias]=u),t),{}),cu=(e,t,u,r)=>{if(r[0]==="-"){let D;if(r[1]==="-"){if(r==="--")return{state:1};D=st(r.substring(2))}else D=t[r.substring(1)],!D&&T(`unknown option: ${r}`);let n=e[D];return!n&&T(D),n.flag&&(u[D]=!0,D=void 0,n.fn&&!n.fn("true"))?{state:1,spec:n}:{state:0,id:D,spec:n}}return{state:2}},hu=(e,t,u,r)=>(/^-[a-z]/i.test(r)&&T(`missing value for: --${u}`),e.multi?rt(t[u])?t[u].push(r):t[u]=[r]:t[u]=r,e.fn&&!e.fn(r)),fu=(e,t)=>{let u;for(let r in e)u=e[r],t[r]===void 0?u.default!==void 0?t[r]=u.default:u.optional===!1&&T(`missing arg: --${r}`):u.coerce&&Fu(u,t,r);return t},Fu=(e,t,u)=>{try{e.multi&&e.delim&&(t[u]=t[u].reduce((r,D)=>(r.push(...D.split(e.delim)),r),[])),t[u]=e.coerce(t[u])}catch(r){throw new Error(`arg --${u}: ${r.message}`)}};var Nt=zt(Ut(),1),er={accessToken:H({alias:"t",hint:"TOKEN",desc:"Mastodon Access Token"}),blocklist:H({alias:"b",hint:"LOCATION",desc:"Blocklist filepath or URL"}),config:H({alias:"c",hint:"PATH",desc:"Custom config filepath"}),endpoint:H({alias:"e",hint:"URL",desc:"Mastodon server URL"}),reset:W({desc:"Reset config (cannot be used with other options)"}),save:W({desc:"Save config to default location"}),rejectMedia:W({desc:"Reject media from domains (works with `limit`, `noop`)"}),rejectReports:W({desc:"Reject reports from domains (works with `limit`, `noop`)"}),severity:H({alias:"s",hint:"LEVEL",desc:"Block severity level (`silence`, `suspend`, `noop`)"}),obfuscate:W({desc:"Obfuscate domains in public comment"}),privateComment:H({hint:"COMMENT",desc:"Private comment"}),publicComment:H({hint:"COMMENT",desc:"Public comment"}),nonInteractive:W({desc:"Disable interactive mode"})},rD=async()=>{let e=ft(er,process.argv);return Nt.default.debug(`Arguments: ${JSON.stringify(e==null?void 0:e.result,null,2)}`),e==null?void 0:e.result};export{rD as args};
32 |
--------------------------------------------------------------------------------
/dist/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mastodont",
3 | "version": "0.1.12",
4 | "description": "CLI tool to import server blocklists into Mastodon v4+",
5 | "main": "index.js",
6 | "bin": "index.js",
7 | "type": "module",
8 | "scripts": {
9 | "build": "node esbuild.js; chmod +x dist/index.js",
10 | "dev": "tsc && DEBUG=* node --es-module-specifier-resolution node dist",
11 | "format": "prettier -w ./src",
12 | "lint": "eslint . --ext .ts --fix",
13 | "tsc:check": "tsc --noEmit",
14 | "test": "echo \"Error: no test specified\" && exit 1",
15 | "postinstall": "if $(git rev-parse --is-inside-work-tree); then husky install; fi"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/selfagency/mastodont.git"
20 | },
21 | "keywords": [
22 | "mastodon",
23 | "fediverse",
24 | "moderation",
25 | "blocklist",
26 | "filter"
27 | ],
28 | "author": "Daniel Sieradski ",
29 | "license": "MIT",
30 | "bugs": {
31 | "url": "https://github.com/selfagency/mastodont/issues"
32 | },
33 | "lint-staged": {
34 | "src/**/*.ts": [
35 | "prettier --write",
36 | "eslint --fix"
37 | ]
38 | },
39 | "homepage": "https://github.com/selfagency/mastodont#readme",
40 | "devDependencies": {
41 | "@thi.ng/args": "^2.2.8",
42 | "@types/node": "^18.11.9",
43 | "@types/prompts": "^2.4.1",
44 | "@typescript-eslint/eslint-plugin": "^5.44.0",
45 | "@typescript-eslint/parser": "^5.44.0",
46 | "consola": "^2.15.3",
47 | "esbuild": "^0.15.15",
48 | "esbuild-plugin-clean": "^1.0.1",
49 | "esbuild-plugin-copy-file": "^0.0.2",
50 | "esbuild-plugin-fileloc": "^0.0.6",
51 | "eslint": "^8.28.0",
52 | "eslint-config-prettier": "^8.5.0",
53 | "eslint-config-standard": "^17.0.0",
54 | "husky": "^8.0.2",
55 | "is-url-superb": "^6.1.0",
56 | "lint-staged": "^13.0.3",
57 | "node-fetch": "^3.3.0",
58 | "open": "^8.4.0",
59 | "ora": "^6.1.2",
60 | "prettier": "^2.7.1",
61 | "prompts": "^2.4.2",
62 | "ts-node": "^10.9.1",
63 | "tsconfig-paths": "^4.1.0",
64 | "typescript": "^4.9.3",
65 | "yaml": "^2.1.3"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/esbuild.js:
--------------------------------------------------------------------------------
1 | import esbuild from 'esbuild'
2 | import { clean } from 'esbuild-plugin-clean'
3 | import copyFilePlugin from 'esbuild-plugin-copy-file'
4 | import fileloc from 'esbuild-plugin-fileloc'
5 |
6 | const banner = `#!/usr/bin/env node
7 | import {createRequire} from 'module';
8 | const require = createRequire(import.meta.url);
9 | `
10 |
11 | esbuild
12 | .build({
13 | entryPoints: ['./src/index.ts', './src/args.ts', './src/blocks.ts', './src/config.ts'],
14 | platform: 'node',
15 | target: 'node16',
16 | bundle: true,
17 | plugins: [
18 | clean({
19 | patterns: ['./dist/*']
20 | }),
21 | fileloc.filelocPlugin(),
22 | copyFilePlugin({
23 | after: {
24 | './dist/package.json': './package.json',
25 | './dist/README.md': './README.md',
26 | './dist/LICENSE.txt': './LICENSE.txt'
27 | }
28 | })
29 | ],
30 | loader: {
31 | '.ts': 'ts',
32 | '.js': 'js',
33 | '.cjs': 'js',
34 | '.yml': 'text'
35 | },
36 | outdir: 'dist',
37 | external: ['package.json'],
38 | minify: true,
39 | sourcemap: false,
40 | format: 'esm',
41 | banner: {
42 | js: banner
43 | }
44 | })
45 | .catch(error => {
46 | console.error(error.message)
47 | process.exit(1)
48 | })
49 |
--------------------------------------------------------------------------------
/examples/blocklist.txt:
--------------------------------------------------------------------------------
1 | 23.illuminati.org
2 | anime.website
3 | ap.uwu.st
4 | bae.st
5 | bajax.us
6 | baraag.net
7 | beta.birdsite.live
8 | birb.elfenban.de
9 | bird.evilcyberhacker.net
10 | bird.froth.zone
11 | bird.nzbr.de
12 | birdbots.leptonics.com
13 | birdsite.link
14 | birdsite.monster
15 | birdsite.slashdev.space
16 | birdsite.thorlaksson.com
17 | birdsite.wilde.cloud
18 | birdsitelive.treffler.cloud
19 | bridge.birb.space
20 | brighteon.social
21 | bv.umbrellix.org
22 | cawfee.club
23 | chudbuds.lol
24 | club.darknight-coffee.eu
25 | clubcyberia.co
26 | collapsitarian.io
27 | comfyboy.club
28 | cum.salon
29 | cyberstorm.one
30 | daishouri.moe
31 | detroitriotcity.com
32 | develop.gab.com
33 | egirls.gay
34 | elekk.xyz
35 | en0.io
36 | f.haeder.net
37 | freeatlantis.com
38 | freecumextremist.com
39 | freefedifollowers.ga
40 | freespeech.firedragonstudios.comx
41 | freespeechextremist.com
42 | frennet.link
43 | gab.ai
44 | gab.com
45 | gameliberty.club
46 | gegenstimme.tv
47 | gitmo.life
48 | gleasonator.com
49 | glindr.org
50 | glowers.club
51 | honkwerx.tech
52 | iddqd.social
53 | itmslaves.com
54 | jaeger.website
55 | kenfm.quadplay.tv
56 | kiwifarms.cc
57 | libre.tube
58 | ligma.pro
59 | lizards.live
60 | lolicon.rocks
61 | lovingexpressions.net
62 | mastodon.popps.org
63 | meta-tube.de
64 | metacode.biz
65 | mi.tkw.fm
66 | midnightride.rs
67 | mugicha.club
68 | my.dirtyhobby.xyz
69 | netzsphaere.xyz
70 | nicecrew.digital
71 | nnia.space
72 | noagendasocial.com
73 | not-develop.gab.com
74 | novoa.nagoya
75 | pawoo.net
76 | paypig.org
77 | pedo.school
78 | pieville.net
79 | pl.info.natehiggers.online
80 | pl.natehiggers.online
81 | pl.tkammer.de
82 | pleroma.8777.ch
83 | pleroma.kitsunemimi.club
84 | pleroma.narrativerry.xyz
85 | pleroma.nobodyhasthe.biz
86 | pleroma.rareome.ga
87 | pleroma.stream
88 | poa.st
89 | poster.place
90 | posting.lolicon.rocks
91 | researchanddestroy.technology
92 | rojogato.com
93 | ryona.agency
94 | s.sneak.berlin
95 | seal.cafe
96 | shitpost.cloud
97 | shitposter.club
98 | shota.house
99 | shortstackran.ch
100 | sinblr.com
101 | skippers-bin.com
102 | sleepy.cafe
103 | social.ancreport.com
104 | social.beachcom.org
105 | social.diskseven.com
106 | social.nobodyhasthe.biz
107 | social.researchanddestroy.technology
108 | social.urspringer.de
109 | solagg.com
110 | spinster.xyz
111 | strelizia.net
112 | tastingtraffic.net
113 | toot.canberrasocial.net
114 | truthsocial.co.in
115 | tube.kenfm.de
116 | tube.querdenken-711.de
117 | twitterbridge.jannis.rocks
118 | twtr.plus
119 | varishangout.net
120 | weedis.life
121 | wiki-tube.de
122 | yggdrasil.social
123 |
--------------------------------------------------------------------------------
/examples/mastodont.crontab:
--------------------------------------------------------------------------------
1 | # Be sure `mastodont` is available in your `$PATH` and these variables are defined in `$HOME/.profile` or directly in the crontab file
2 | 0 0 * * * . $HOME/.profile; mastodont -e $MASTODONT_INSTANCE_URL -t $MASTODONT_ACCESS_TOKEN -b $MASTODONT_BLOCKLIST -s suspend --private-comment "Added from shared blocklist, $(date +%F %T)" --non-interactive
3 |
--------------------------------------------------------------------------------
/examples/push-workflow.yml:
--------------------------------------------------------------------------------
1 | name: Update server blocklist
2 | description: This workflow runs whenever the blocklist file is updated in the repository
3 | on:
4 | push:
5 | branches:
6 | - main
7 | paths:
8 | - 'blocklist.txt'
9 | jobs:
10 | release:
11 | runs-on: ubuntu-latest
12 | permissions:
13 | contents: write
14 | pull-requests: read
15 | steps:
16 | - name: Checkout repo
17 | uses: actions/checkout@v2
18 | with:
19 | fetch-depth: 0
20 |
21 | - name: Setup Node
22 | uses: actions/setup-node@v1
23 | with:
24 | node-version: 16
25 |
26 | - name: Install Mastodont
27 | run: npm i -g mastodont
28 |
29 | - name: Update blocklist
30 | env:
31 | MASTODONT_ACCESS_TOKEN: ${{ secrets.MASTODONT_ACCESS_TOKEN }}
32 | MASTODONT_INSTANCE_URL: https://my.instance.social
33 | MASTODONT_BLOCKLIST: https://raw.githubusercontent.com/my-community/blocklist/main/blocklist.txt
34 | run: |
35 | mastodont \
36 | -e $MASTODONT_INSTANCE_URL \
37 | -t $MASTODONT_ACCESS_TOKEN \
38 | -b $MASTODONT_BLOCKLIST \
39 | -s suspend \
40 | --private-comment "Added from shared blocklist, $(date +%F %T)" \
41 | --non-interactive
42 |
--------------------------------------------------------------------------------
/examples/schedule-workflow.yml:
--------------------------------------------------------------------------------
1 | name: Update server blocklist
2 | description: This workflow runs every night at midnight
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 | jobs:
7 | release:
8 | runs-on: ubuntu-latest
9 | permissions:
10 | contents: write
11 | pull-requests: read
12 | steps:
13 | - name: Checkout repo
14 | uses: actions/checkout@v2
15 | with:
16 | fetch-depth: 0
17 |
18 | - name: Setup Node
19 | uses: actions/setup-node@v1
20 | with:
21 | node-version: 16
22 |
23 | - name: Install Mastodont
24 | run: npm i -g mastodont
25 |
26 | - name: Update blocklist
27 | env:
28 | MASTODONT_ACCESS_TOKEN: ${{ secrets.MASTODONT_ACCESS_TOKEN }}
29 | MASTODONT_INSTANCE_URL: https://my.instance.social
30 | MASTODONT_BLOCKLIST: https://raw.githubusercontent.com/my-community/blocklist/main/blocklist.txt
31 | run: |
32 | mastodont \
33 | -e $MASTODONT_INSTANCE_URL \
34 | -t $MASTODONT_ACCESS_TOKEN \
35 | -b $MASTODONT_BLOCKLIST \
36 | -s suspend \
37 | --private-comment "Added from shared blocklist, $(date +%F %T)" \
38 | --non-interactive
39 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mastodont",
3 | "version": "0.1.12",
4 | "description": "CLI tool to import server blocklists into Mastodon v4+",
5 | "main": "index.js",
6 | "bin": "index.js",
7 | "type": "module",
8 | "scripts": {
9 | "build": "node esbuild.js; chmod +x dist/index.js",
10 | "dev": "tsc && DEBUG=* node --es-module-specifier-resolution node dist",
11 | "format": "prettier -w ./src",
12 | "lint": "eslint . --ext .ts --fix",
13 | "tsc:check": "tsc --noEmit",
14 | "test": "echo \"Error: no test specified\" && exit 1",
15 | "postinstall": "if $(git rev-parse --is-inside-work-tree); then husky install; fi"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "git+https://github.com/selfagency/mastodont.git"
20 | },
21 | "keywords": [
22 | "mastodon",
23 | "fediverse",
24 | "moderation",
25 | "blocklist",
26 | "filter"
27 | ],
28 | "author": "Daniel Sieradski ",
29 | "license": "MIT",
30 | "bugs": {
31 | "url": "https://github.com/selfagency/mastodont/issues"
32 | },
33 | "lint-staged": {
34 | "src/**/*.ts": [
35 | "prettier --write",
36 | "eslint --fix"
37 | ]
38 | },
39 | "homepage": "https://github.com/selfagency/mastodont#readme",
40 | "devDependencies": {
41 | "@thi.ng/args": "^2.2.8",
42 | "@types/node": "^18.11.9",
43 | "@types/prompts": "^2.4.1",
44 | "@typescript-eslint/eslint-plugin": "^5.44.0",
45 | "@typescript-eslint/parser": "^5.44.0",
46 | "consola": "^2.15.3",
47 | "esbuild": "^0.15.15",
48 | "esbuild-plugin-clean": "^1.0.1",
49 | "esbuild-plugin-copy-file": "^0.0.2",
50 | "esbuild-plugin-fileloc": "^0.0.6",
51 | "eslint": "^8.28.0",
52 | "eslint-config-prettier": "^8.5.0",
53 | "eslint-config-standard": "^17.0.0",
54 | "husky": "^8.0.2",
55 | "is-url-superb": "^6.1.0",
56 | "lint-staged": "^13.0.3",
57 | "node-fetch": "^3.3.0",
58 | "open": "^8.4.0",
59 | "ora": "^6.1.2",
60 | "prettier": "^2.7.1",
61 | "prompts": "^2.4.2",
62 | "ts-node": "^10.9.1",
63 | "tsconfig-paths": "^4.1.0",
64 | "typescript": "^4.9.3",
65 | "yaml": "^2.1.3"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/args.ts:
--------------------------------------------------------------------------------
1 | import { type Args, flag, parse, string } from '@thi.ng/args'
2 | import consola from 'consola'
3 | import { type MastodontArgs } from './types'
4 |
5 | const specs: Args = {
6 | accessToken: string({
7 | alias: 't',
8 | hint: 'TOKEN',
9 | desc: 'Mastodon Access Token'
10 | }),
11 | blocklist: string({
12 | alias: 'b',
13 | hint: 'LOCATION',
14 | desc: 'Blocklist filepath or URL'
15 | }),
16 | config: string({
17 | alias: 'c',
18 | hint: 'PATH',
19 | desc: 'Custom config filepath'
20 | }),
21 | endpoint: string({
22 | alias: 'e',
23 | hint: 'URL',
24 | desc: 'Mastodon server URL'
25 | }),
26 | reset: flag({
27 | desc: 'Reset config (cannot be used with other options)'
28 | }),
29 | save: flag({
30 | desc: 'Save config to default location'
31 | }),
32 | rejectMedia: flag({
33 | desc: 'Reject media from domains (works with `limit`, `noop`)'
34 | }),
35 | rejectReports: flag({
36 | desc: 'Reject reports from domains (works with `limit`, `noop`)'
37 | }),
38 | severity: string({
39 | alias: 's',
40 | hint: 'LEVEL',
41 | desc: 'Block severity level (`silence`, `suspend`, `noop`)'
42 | }),
43 | obfuscate: flag({
44 | desc: 'Obfuscate domains in public comment'
45 | }),
46 | privateComment: string({
47 | hint: 'COMMENT',
48 | desc: 'Private comment'
49 | }),
50 | publicComment: string({
51 | hint: 'COMMENT',
52 | desc: 'Public comment'
53 | }),
54 | nonInteractive: flag({
55 | desc: 'Disable interactive mode'
56 | })
57 | }
58 |
59 | export const args = async (): Promise => {
60 | const args = parse(specs, process.argv)
61 | consola.debug(`Arguments: ${JSON.stringify(args?.result, null, 2)}`)
62 | return args?.result
63 | }
64 |
--------------------------------------------------------------------------------
/src/blocks.ts:
--------------------------------------------------------------------------------
1 | import isUrl from 'is-url-superb'
2 | import fetch from 'node-fetch'
3 | import consola from 'consola'
4 | import ora from 'ora'
5 | import { type Block, type MastodontConfig } from './types'
6 | import { readFile } from 'fs/promises'
7 |
8 | const endpoint = (config: MastodontConfig) => `${config.endpoint}/api/v1/admin/domain_blocks`
9 |
10 | export const getBlocks = async (config: MastodontConfig, quiet: boolean): Promise => {
11 | let spinner
12 | if (!quiet) spinner = ora('Querying instance blocks.')
13 | if (!quiet) spinner.start()
14 | const res = await fetch(endpoint(config), {
15 | headers: {
16 | Authorization: `Bearer ${config.accessToken}`
17 | }
18 | })
19 |
20 | if (!quiet) spinner.stopAndPersist()
21 | const blocks = await res.json()
22 | if (!quiet) consola.debug(`Domain blocks: ${JSON.stringify(blocks, null, 2)}`)
23 |
24 | if (res.status !== 200) {
25 | if (!quiet) spinner.fail()
26 | consola.debug(`Blocks query response: ${JSON.stringify(res, null, 2)}`)
27 | throw new Error('Failed to retrieve current domain blocks.')
28 | } else {
29 | if (!quiet) spinner.succeed()
30 | return blocks
31 | }
32 | }
33 |
34 | export const setBlocks = async (config: MastodontConfig) => {
35 | const currentBlocks = await getBlocks(config, false)
36 | const spinner = ora('Updating instance blocks.').start()
37 |
38 | let blocklist: string[] = []
39 | try {
40 | if (!config?.blocklist) {
41 | throw new Error('No blocklist specified.')
42 | }
43 |
44 | if (isUrl(config.blocklist)) {
45 | blocklist = (await (await fetch(config.blocklist)).text()).split(/\r?\n/)
46 | } else {
47 | blocklist = (await readFile(config?.blocklist, 'utf8')).split('\n')
48 | }
49 | } catch (e) {
50 | spinner.fail()
51 | consola.error(`Failed to load blocklist: ${(e).message}`)
52 | process.exit(1)
53 | }
54 |
55 | const blocksToAdd = blocklist.filter(domain => !currentBlocks.map(block => block.domain).includes(domain))
56 |
57 | if (blocksToAdd.length === 0) {
58 | spinner.succeed('No new domains to block.')
59 | process.exit(0)
60 | }
61 |
62 | const baseUrl =
63 | `${endpoint(config)}?` +
64 | `&severity=${config.severity}` +
65 | (config.severity !== 'suspend'
66 | ? `&reject_media=${config.rejectMedia}` + `&reject_reports=${config.rejectReports}`
67 | : '') +
68 | `&obfuscate=${config.obfuscate}` +
69 | `&private_comments=${config.privateComment}` +
70 | `&public_comment=${config.publicComment}`
71 |
72 | const blocksToAddPromises = blocksToAdd.map(domain =>
73 | fetch(`${baseUrl}&domain=${domain}`, {
74 | method: 'POST',
75 | headers: {
76 | Authorization: `Bearer ${config.accessToken}`
77 | }
78 | })
79 | )
80 |
81 | try {
82 | await Promise.all(blocksToAddPromises)
83 | spinner.succeed()
84 | } catch (e) {
85 | spinner.fail()
86 | consola.error(`Error adding blocks: ${(e).message}`)
87 | process.exit(1)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/config.ts:
--------------------------------------------------------------------------------
1 | import { homedir } from 'os'
2 | import path from 'path'
3 | import { readFile, unlink, writeFile } from 'fs/promises'
4 | import { parse as yaml, stringify as yamlStringify } from 'yaml'
5 | import consola from 'consola'
6 | import prompts, { type PromptObject } from 'prompts'
7 | import ora from 'ora'
8 | import { MastodontArgs, MastodontConfig } from './types'
9 |
10 | const defaultConfigPath = path.join(homedir(), '.mastodont.yml')
11 | const redact = (str: string) => str.replace(/./g, '*')
12 |
13 | const interactivePrompts = async (config: MastodontConfig, flags: MastodontArgs): Promise => {
14 | try {
15 | const questions: Record = {
16 | endpoint: {
17 | type: 'text',
18 | name: 'value',
19 | initial: config.endpoint || '',
20 | message: 'Mastodon server URL:'
21 | },
22 | accessToken: {
23 | type: 'password',
24 | name: 'value',
25 | initial: config.accessToken || '',
26 | message: 'Mastodon access token:'
27 | },
28 | rejectMedia: {
29 | type: 'confirm',
30 | name: 'value',
31 | initial: config.rejectMedia || false,
32 | message: 'Reject media from imported domains?'
33 | },
34 | rejectReports: {
35 | type: 'confirm',
36 | name: 'value',
37 | initial: config.rejectReports || false,
38 | message: 'Reject reports from imported domains?'
39 | },
40 | obfuscate: {
41 | type: 'confirm',
42 | name: 'value',
43 | initial: config.obfuscate || false,
44 | message: 'Obfuscate domains in public blocklist?'
45 | },
46 | severity: {
47 | type: 'select',
48 | name: 'value',
49 | choices: [
50 | { title: 'silence', value: 'silence' },
51 | { title: 'suspend', value: 'suspend' },
52 | {
53 | title: 'noop',
54 | value: 'noop'
55 | }
56 | ],
57 | message: 'Block severity:'
58 | }
59 | }
60 |
61 | let changed = false
62 | for (const key of Object.keys(questions)) {
63 | consola.debug(`config.${key}: ${config[key]}, flag.${key}: ${flags[key]}`)
64 | if (config[key] === undefined && !flags[key]) {
65 | config[key] = (await prompts(questions[key]))?.value
66 | changed = true
67 | } else {
68 | config[key] = flags[key] || config[key]
69 | }
70 | }
71 |
72 | if (!flags.blocklist) {
73 | config.blocklist = (
74 | await prompts({
75 | type: 'text',
76 | name: 'value',
77 | message: 'Blocklist filepath or URL:'
78 | })
79 | )?.value
80 | }
81 |
82 | if (!flags.publicComment) {
83 | config.publicComment = (
84 | await prompts({
85 | type: 'text',
86 | name: 'value',
87 | initial: '',
88 | message: 'Public comment:'
89 | })
90 | )?.value
91 | }
92 |
93 | if (!flags.privateComment) {
94 | config.privateComment = (
95 | await prompts({
96 | type: 'text',
97 | name: 'value',
98 | initial: `Imported by Mastodont on ${new Date().toISOString()}`,
99 | message: 'Private comment:'
100 | })
101 | )?.value
102 | }
103 |
104 | if (changed && !flags.save) {
105 | config.save = (
106 | await prompts({
107 | type: 'confirm',
108 | name: 'value',
109 | initial: false,
110 | message: `Save config to ${defaultConfigPath}?`
111 | })
112 | ).value
113 | }
114 |
115 | return config
116 | } catch (e) {
117 | consola.error(e)
118 | process.exit(1)
119 | }
120 | }
121 |
122 | export const getConfig = async (flags: MastodontArgs): Promise => {
123 | const spinner = ora('Loading config.').start()
124 | let config: MastodontConfig = {}
125 |
126 | // check if yml file exists else use default
127 | if (flags.config) {
128 | try {
129 | config = yaml(await readFile(flags.config, 'utf-8'))
130 | spinner.succeed()
131 | } catch (e) {
132 | spinner.fail()
133 | consola.error(`Config file not found at \`${flags.config}\`.`)
134 | process.exit(1)
135 | }
136 | } else {
137 | try {
138 | config = yaml(await readFile(defaultConfigPath, 'utf-8'))
139 | spinner.succeed()
140 | } catch (e) {
141 | spinner.stopAndPersist({ text: `Default config file not found.` })
142 | }
143 | }
144 |
145 | if (!flags.nonInteractive) {
146 | spinner.stop()
147 | config = await interactivePrompts(config, flags)
148 | }
149 |
150 | if (config && (!config.endpoint?.length || !config.accessToken?.length)) {
151 | throw new Error('Mastodon server URL and access token are required.')
152 | }
153 |
154 | const debugConfig = { ...config }
155 | debugConfig.accessToken = redact(config.accessToken)
156 | consola.debug(`Config: ${JSON.stringify(debugConfig, null, 2)}`)
157 |
158 | return config
159 | }
160 |
161 | export const setConfig = async (config: MastodontConfig): Promise => {
162 | const spinner = ora(`Writing config to \`${defaultConfigPath}\`.`).start()
163 | try {
164 | ['save', 'nonInteractive', 'config', 'blocklist', 'reset', 'publicComment', 'privateComment'].forEach(
165 | key => delete config[key]
166 | )
167 | spinner.stopAndPersist()
168 | const yamlConfig = yamlStringify(config)
169 | consola.debug(`Writing config:\n${yamlConfig}`)
170 | await writeFile(defaultConfigPath, yamlConfig)
171 | spinner.succeed()
172 | } catch (e) {
173 | spinner.fail()
174 | throw new Error(`Unable to write config file: ${(e).message}`)
175 | }
176 | }
177 |
178 | export const resetConfig = async (): Promise => {
179 | const spinner = ora(`Resetting config.`).start()
180 | try {
181 | await unlink(defaultConfigPath)
182 | spinner.succeed()
183 | consola.debug(`Config file successfully deleted.`)
184 | process.exit(0)
185 | } catch (e) {
186 | spinner.fail()
187 | throw new Error(`Unable to reset config file: ${(e).message}`)
188 | }
189 | }
190 |
--------------------------------------------------------------------------------
/src/header.ts:
--------------------------------------------------------------------------------
1 | export const header = `
2 |
3 | 88b d88 88
4 | 888b d888 ,d 88 ,d
5 | 88\`8b d8'88 88 88 88
6 | 88 \`8b d8' 88 ,adPPYYba, ,adPPYba, MM88MMM ,adPPYba, ,adPPYb,88 ,adPPYba, 8b,dPPYba, MM88MMM
7 | 88 \`8b d8' 88 "" \`Y8 I8[ "" 88 a8" "8a a8" \`Y88 a8" "8a 88P' \`"8a 88
8 | 88 \`8b d8' 88 ,adPPPPP88 \`"Y8ba, 88 8b d8 8b 88 8b d8 88 88 88
9 | 88 \`888' 88 88, ,88 aa ]8I 88, "8a, ,a8" "8a, ,d88 "8a, ,a8" 88 88 88,
10 | 88 \`8' 88 \`"8bbdP"Y8 \`"YbbdP"' "Y888 \`"YbbdP"' \`"8bbdP"Y8 \`"YbbdP"' 88 88 "Y888
11 |
12 | Blocklist importer for Mastodon by @selfagency@kibitz.cloud (https://kibitz.cloud)
13 | Requires an API access token with \`admin:read:domain_blocks\`/\`admin:write:domain_blocks\` and a blocklist file.
14 | See README.md for more info.
15 |
16 | `
17 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import consola from 'consola'
2 | import { getConfig, resetConfig, setConfig } from './config'
3 | import { args } from './args'
4 | import { validateCredentials, validateEndpoint } from './validations'
5 | import { setBlocks } from './blocks'
6 | import { header } from './header'
7 | import open from 'open'
8 |
9 | const main = async (): Promise => {
10 | consola.log(header)
11 |
12 | // check if flags are set
13 | const flags = await args()
14 |
15 | if (flags) {
16 | // reset config
17 | if (flags?.reset) {
18 | await resetConfig()
19 | }
20 |
21 | // get config
22 | const config = await getConfig(flags)
23 |
24 | // validate config
25 | const instance = await validateEndpoint(config)
26 |
27 | // validate credentials
28 | await validateCredentials(config)
29 |
30 | // save config to yml
31 | if (instance && config?.save) {
32 | await setConfig(config)
33 | }
34 |
35 | // process blocklist
36 | await setBlocks(config)
37 |
38 | // open browser
39 | if (!flags.nonInteractive) {
40 | consola.info('Opening browser to instance blocklist...')
41 | await open(`${config.endpoint}/admin/instances?limited=1`)
42 | }
43 | }
44 | }
45 |
46 | main().catch(e => consola.error(e.message))
47 |
--------------------------------------------------------------------------------
/src/types/index.d.ts:
--------------------------------------------------------------------------------
1 | export interface MastodontArgs {
2 | accessToken?: string
3 | nonInteractive?: boolean
4 | privateComment?: string
5 | publicComment?: string
6 | rejectMedia?: boolean
7 | rejectReports?: boolean
8 | blocklist?: string
9 | config?: string
10 | endpoint?: string
11 | obfuscate?: boolean
12 | reset?: boolean
13 | save?: boolean
14 | severity?: string
15 | }
16 |
17 | export type MastodontConfig = MastodontArgs
18 |
19 | export interface Block {
20 | id: string
21 | domain: string
22 | created_at: string
23 | severity: string
24 | reject_media: boolean
25 | reject_reports: boolean
26 | private_comment?: null
27 | public_comment?: null
28 | obfuscate: boolean
29 | }
30 |
--------------------------------------------------------------------------------
/src/types/validations.d.ts:
--------------------------------------------------------------------------------
1 | import { MastodontConfig } from './index'
2 |
3 | export interface Users {
4 | active_month: number
5 | }
6 |
7 | export interface Usage {
8 | users: Users
9 | }
10 |
11 | export interface Versions {
12 | '@1x': string
13 | '@2x': string
14 | }
15 |
16 | export interface Thumbnail {
17 | url: string
18 | blurhash: string
19 | versions: Versions
20 | }
21 |
22 | export interface Urls {
23 | streaming: string
24 | }
25 |
26 | export interface Accounts {
27 | max_featured_tags: number
28 | }
29 |
30 | export interface Statuses {
31 | max_characters: number
32 | max_media_attachments: number
33 | characters_reserved_per_url: number
34 | }
35 |
36 | export interface MediaAttachments {
37 | supported_mime_types?: string[] | null
38 | image_size_limit: number
39 | image_matrix_limit: number
40 | video_size_limit: number
41 | video_frame_rate_limit: number
42 | video_matrix_limit: number
43 | }
44 |
45 | export interface Polls {
46 | max_options: number
47 | max_characters_per_option: number
48 | min_expiration: number
49 | max_expiration: number
50 | }
51 |
52 | export interface Translation {
53 | enabled: boolean
54 | }
55 |
56 | export interface Registrations {
57 | enabled: boolean
58 | approval_required: boolean
59 | message?: null
60 | }
61 |
62 | export interface FieldsEntity {
63 | name: string
64 | value: string
65 | verified_at?: null
66 | }
67 |
68 | export interface RulesEntity {
69 | id: string
70 | text: string
71 | }
72 |
73 | export interface Account {
74 | id: string
75 | username: string
76 | acct: string
77 | display_name: string
78 | locked: boolean
79 | bot: boolean
80 | discoverable: boolean
81 | group: boolean
82 | created_at: string
83 | note: string
84 | url: string
85 | avatar: string
86 | avatar_static: string
87 | header: string
88 | header_static: string
89 | followers_count: number
90 | following_count: number
91 | statuses_count: number
92 | last_status_at: string
93 | noindex: boolean
94 | emojis?: null[] | null
95 | fields?: FieldsEntity[] | null
96 | }
97 |
98 | export interface Contact {
99 | email: string
100 | account: Account
101 | }
102 |
103 | export interface Configuration {
104 | urls: Urls
105 | accounts: Accounts
106 | statuses: Statuses
107 | media_attachments: MediaAttachments
108 | polls: Polls
109 | translation: Translation
110 | }
111 |
112 | export interface MastodonInstance {
113 | domain: string
114 | title: string
115 | version: string
116 | source_url: string
117 | description: string
118 | usage: Usage
119 | thumbnail: Thumbnail
120 | languages?: string[] | null
121 | configuration: Configuration
122 | registrations: Registrations
123 | contact: Contact
124 | rules?: RulesEntity[] | null
125 | }
126 |
127 | declare namespace validations {
128 | export function validateEndpoint(config: MastodontConfig): Promise
129 | }
130 |
--------------------------------------------------------------------------------
/src/validations.ts:
--------------------------------------------------------------------------------
1 | import isUrl from 'is-url-superb'
2 | import fetch from 'node-fetch'
3 | import consola from 'consola'
4 | import { getBlocks } from './blocks'
5 | import { MastodonInstance } from './types/validations'
6 | import ora from 'ora'
7 | import { MastodontConfig } from './types'
8 |
9 | export const validateEndpoint = async (config: MastodontConfig) => {
10 | const spinner = ora('Validating endpoint.').start()
11 | if (config.endpoint && !isUrl(config.endpoint)) {
12 | spinner.fail()
13 | throw new Error('Mastodon server URL is invalid.')
14 | } else {
15 | const res = await fetch(`${config.endpoint}/api/v2/instance`)
16 | if (res.status !== 200) {
17 | spinner.fail()
18 | throw new Error('Mastodon server URL is invalid.')
19 | } else {
20 | // validate api endpoint exists and uses v4+
21 | const instance = await res.json()
22 | if (!instance?.version.startsWith('4.')) {
23 | spinner.fail()
24 | throw new Error('Mastodon version 4 or higher required.')
25 | } else {
26 | spinner.succeed()
27 | consola.debug('Successfully validated Mastodon version.')
28 | }
29 | return instance
30 | }
31 | }
32 | }
33 |
34 | export const validateCredentials = async (config: MastodontConfig) => {
35 | const spinner = ora('Validating credentials.').start()
36 | try {
37 | await getBlocks(config, true)
38 | spinner.succeed()
39 | } catch (e) {
40 | spinner.fail()
41 | throw new Error('Failed to authenticate to API. Access token is likely invalid.')
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "allowJs": true,
4 | "allowSyntheticDefaultImports": true,
5 | "baseUrl": ".",
6 | "emitDecoratorMetadata": true,
7 | "esModuleInterop": true,
8 | "experimentalDecorators": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "importHelpers": true,
11 | "isolatedModules": true,
12 | "lib": ["es2020"],
13 | "module": "es2020",
14 | "moduleResolution": "node",
15 | "noImplicitAny": false,
16 | "noImplicitReturns": true,
17 | "noImplicitThis": true,
18 | "outDir": "dist",
19 | "resolveJsonModule": true,
20 | "skipLibCheck": true,
21 | "sourceMap": true,
22 | "strict": true,
23 | "strictNullChecks": true,
24 | "target": "es2020",
25 | "typeRoots": ["./node_modules/@types", "./src/types"],
26 | "types": ["node"]
27 | },
28 | "exclude": ["node_modules", "dist"],
29 | "include": ["src/**/*.ts"],
30 | "ts-node": {
31 | "esm": true,
32 | "experimentalSpecifierResolution": true
33 | }
34 | }
35 |
--------------------------------------------------------------------------------