Compare commits
12 Commits
rework_exp
...
rename
Author | SHA1 | Date | |
---|---|---|---|
282031245c | |||
![]() |
d47a63e331 | ||
![]() |
3cdf24a5f3 | ||
735bb5fe6d | |||
2180f28c78 | |||
27168a2a6e | |||
569cff0957 | |||
e1d9fe04f9 | |||
a646714f76 | |||
173aac081d | |||
d41bd5322d | |||
4668d9023e |
10
.drone.yml
10
.drone.yml
@@ -10,7 +10,7 @@ platform:
|
|||||||
steps:
|
steps:
|
||||||
- name: build
|
- name: build
|
||||||
commands:
|
commands:
|
||||||
- docker build -t gcrkrause/mastodon-blocklist-deploy .
|
- docker build -t gcrkrause/fediverse-blocklist-deploy .
|
||||||
- name: push
|
- name: push
|
||||||
environment:
|
environment:
|
||||||
USERNAME:
|
USERNAME:
|
||||||
@@ -19,5 +19,11 @@ steps:
|
|||||||
from_secret: docker-hub-pw
|
from_secret: docker-hub-pw
|
||||||
commands:
|
commands:
|
||||||
- docker login -u $USERNAME -p $PASSWORD
|
- docker login -u $USERNAME -p $PASSWORD
|
||||||
- docker push gcrkrause/mastodon-blocklist-deploy
|
- docker push gcrkrause/fediverse-blocklist-deploy
|
||||||
- docker image prune -a -f
|
- docker image prune -a -f
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
||||||
|
branch:
|
||||||
|
- main
|
||||||
|
@@ -4,14 +4,14 @@
|
|||||||
|
|
||||||
In order to have a common development environment, its nice to use docker. Its quite easy. To build a new image, simply run
|
In order to have a common development environment, its nice to use docker. Its quite easy. To build a new image, simply run
|
||||||
|
|
||||||
`docker build . -t mastodon_blocklist_deploy`
|
`docker build . -t fediverse_blocklist_deploy`
|
||||||
|
|
||||||
Now you can execute any commands using
|
Now you can execute any commands using
|
||||||
|
|
||||||
`docker run --rm mastodon_blocklist_deploy --help`
|
`docker run --rm fediverse_blocklist_deploy --help`
|
||||||
|
|
||||||
If you want to avoid building new containers for each change, simply mount your code into the container using
|
If you want to avoid building new containers for each change, simply mount your code into the container using
|
||||||
|
|
||||||
`docker run --rm -v $(pwd):/app mastodon_blocklist_deploy`
|
`docker run --rm -v $(pwd):/app fediverse_blocklist_deploy`
|
||||||
|
|
||||||
Please be aware that changes to the package itself require a rebuild anyways.
|
Please be aware that changes to the package itself require a rebuild anyways.
|
||||||
|
@@ -4,9 +4,9 @@ ENV PYTHONDONTWRITEBYTECODE=1
|
|||||||
ENV PYTHONUNBUFFERED=1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
COPY pyproject.toml poetry.lock README.md /app/
|
COPY pyproject.toml poetry.lock README.md /app/
|
||||||
COPY mastodon_blocklist_deploy /app/mastodon_blocklist_deploy
|
COPY fediverse_blocklist_deploy /app/fediverse_blocklist_deploy
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
ENTRYPOINT ["mastodon_blocklist_deploy"]
|
ENTRYPOINT ["fediverse_blocklist_deploy"]
|
||||||
|
|
||||||
RUN pip install -e .
|
RUN pip install -e .
|
||||||
|
27
README.md
27
README.md
@@ -1,35 +1,40 @@
|
|||||||
# mastodon-blocklist-deploy
|
# fediverse-blocklist-deploy
|
||||||
|
|
||||||
A small tool to deploy blocklist updates to a mastodon server using its API.
|
A small tool to deploy blocklist updates to a fediverse server using its API.
|
||||||
|
|
||||||
## Concept
|
## Concept
|
||||||
|
|
||||||
The idea is to maintain a blocklist in a simple structured file in this repository. All changes need to be deployed to
|
The idea is to maintain a blocklist in a simple structured file in this repository. All changes need to be deployed to
|
||||||
the mastodon server, this is supposed to be automated with Drone CI.
|
the fediverse server, this is supposed to be automated with Drone CI.
|
||||||
|
|
||||||
In order to compare the list entries, we can read the whole blocklist
|
In order to compare the list entries, we can read the whole blocklist
|
||||||
using [the get endpoint](https://docs.joinmastodon.org/methods/admin/domain_blocks/#get). At the same time we read the
|
using [the get endpoint](https://docs.joinmastodon.org/methods/admin/domain_blocks/#get). At the same time we read the
|
||||||
whole file in the repository, make a comparision
|
whole file in the repository, make a comparison
|
||||||
and [remove](https://docs.joinmastodon.org/methods/admin/domain_blocks/#delete) unblocked domains from the blocklist
|
and [remove](https://docs.joinmastodon.org/methods/admin/domain_blocks/#delete) unblocked domains from the blocklist
|
||||||
and [add](https://docs.joinmastodon.org/methods/admin/domain_blocks/#create) newly added.
|
and [add](https://docs.joinmastodon.org/methods/admin/domain_blocks/#create) newly added.
|
||||||
|
|
||||||
Since we have several attributes for a domain blog, a simple `.txt` file might not be sufficient. We probably want to
|
Since we have several attributes for a domain block, a simple `.txt` file might not be sufficient. We probably want to
|
||||||
set the severity, reject_media, reject_reports and comments. This means we need a human-readable, easily python-readable
|
set the severity, reject_media, reject_reports and comments. This means we need a human-readable, easily python-readable
|
||||||
and structured file format. Since Python 3.11 got native support for [toml](https://toml.io/) and it
|
and structured file format. Since Python 3.11 got native support for [toml](https://toml.io/) and it
|
||||||
supports [Array of Tables](https://toml.io/en/v1.0.0#array-of-tables), I'd prefer to use this.
|
supports [Array of Tables](https://toml.io/en/v1.0.0#array-of-tables), I'd prefer to use this.
|
||||||
|
|
||||||
|
|
||||||
|
# Supported server types
|
||||||
|
|
||||||
|
- [x] Mastodon
|
||||||
|
- [X] GoToSocial
|
||||||
|
|
||||||
# Basic usage
|
# Basic usage
|
||||||
|
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
||||||
```
|
```
|
||||||
usage: mastodon_blocklist_deploy [-h] [-s SERVER] [-t TOKEN] [-i INPUT_FILE] [-r REMOTE_BLOCKLIST] [-o OUTPUT] [-v] [-n]
|
usage: fediverse_blocklist_deploy [-h] [-s SERVER] [-t TOKEN] [-i INPUT_FILE] [-r REMOTE_BLOCKLIST] [-o OUTPUT] [-v] [-n]
|
||||||
[--format FORMAT] [--private]
|
[--format FORMAT] [--private]
|
||||||
{diff,deploy,export}
|
{diff,deploy,export}
|
||||||
|
|
||||||
Deploy blocklist updates to a mastodon server
|
Deploy blocklist updates to a fediverse server
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
{diff,deploy,export} Either use 'diff' to check the difference between local blockĺist and the blocklist on the server, 'deploy'
|
{diff,deploy,export} Either use 'diff' to check the difference between local blockĺist and the blocklist on the server, 'deploy'
|
||||||
@@ -49,7 +54,7 @@ options:
|
|||||||
Filename where to export the blocklist
|
Filename where to export the blocklist
|
||||||
-v, --verbose
|
-v, --verbose
|
||||||
-n, --no-delete Do not delete existing blocks
|
-n, --no-delete Do not delete existing blocks
|
||||||
--format FORMAT Export format: toml|markdown|csv
|
--format FORMAT Export format: toml|markdown|csv|json
|
||||||
--private When the flag is set, private comment will also be exported.
|
--private When the flag is set, private comment will also be exported.
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -64,7 +69,7 @@ options:
|
|||||||
1. **Export the current blocklist from the server**
|
1. **Export the current blocklist from the server**
|
||||||
|
|
||||||
```
|
```
|
||||||
mastodon_blocklist_deploy export -s yourserver -t yourtoken -o blocklist.toml
|
fediverse_blocklist_deploy export -s yourserver -t yourtoken -o blocklist.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Manually add something to the blocklist**
|
2. **Manually add something to the blocklist**
|
||||||
@@ -83,12 +88,12 @@ private_comment = "We discussed this after X and Y and now that Z happend we dec
|
|||||||
3. **Check the difference between the local and remote blocklist**
|
3. **Check the difference between the local and remote blocklist**
|
||||||
|
|
||||||
```
|
```
|
||||||
mastodon_blocklist_deploy diff -s yourserver -t yourtoken -i blocklist.toml
|
fediverse_blocklist_deploy diff -s yourserver -t yourtoken -i blocklist.toml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
4. **Apply the local blocklist to the server**
|
4. **Apply the local blocklist to the server**
|
||||||
|
|
||||||
```
|
```
|
||||||
mastodon_blocklist_deploy apply -s yourserver -t yourtoken -i blocklist.toml
|
fediverse_blocklist_deploy apply -s yourserver -t yourtoken -i blocklist.toml
|
||||||
```
|
```
|
@@ -6,8 +6,9 @@ import requests
|
|||||||
import os
|
import os
|
||||||
import toml
|
import toml
|
||||||
|
|
||||||
from mastodon_blocklist_deploy.models import Instance
|
from fediverse_blocklist_deploy.models import Instance
|
||||||
from mastodon_blocklist_deploy.helpers import blocklist_to_markdown, blocklist_to_toml, blocklist_to_csv
|
from fediverse_blocklist_deploy.helpers import blocklist_to_markdown, blocklist_to_toml, blocklist_to_csv, \
|
||||||
|
blocklist_to_json
|
||||||
|
|
||||||
|
|
||||||
def load_blocklist_file(filename: str) -> [Instance]:
|
def load_blocklist_file(filename: str) -> [Instance]:
|
||||||
@@ -52,6 +53,8 @@ def exporter(blocklist, output=None, format: str = "toml", private: bool = False
|
|||||||
exported_text = blocklist_to_csv(blocklist, private)
|
exported_text = blocklist_to_csv(blocklist, private)
|
||||||
if format == "markdown":
|
if format == "markdown":
|
||||||
exported_text = blocklist_to_markdown(blocklist, private)
|
exported_text = blocklist_to_markdown(blocklist, private)
|
||||||
|
if format == "json":
|
||||||
|
exported_text = blocklist_to_json(blocklist, private)
|
||||||
|
|
||||||
# Output the text
|
# Output the text
|
||||||
if output is not None:
|
if output is not None:
|
||||||
@@ -61,13 +64,37 @@ def exporter(blocklist, output=None, format: str = "toml", private: bool = False
|
|||||||
print(exported_text)
|
print(exported_text)
|
||||||
|
|
||||||
|
|
||||||
|
def merge(input_file, merge_target, format: str = "toml", private: bool = False, overwrite=False):
|
||||||
|
input_blocklist = load_blocklist_file(input_file)
|
||||||
|
merge_target_blocklist = load_blocklist_file(merge_target)
|
||||||
|
for input_instance in input_blocklist:
|
||||||
|
# If the block is already there with the same parameters we do nothing
|
||||||
|
if input_instance in merge_target_blocklist:
|
||||||
|
continue
|
||||||
|
# Check if there is a domain in the merge target where the input domain is similar
|
||||||
|
try:
|
||||||
|
merge_target_instance = [merge_target_instance for merge_target_instance in merge_target if input_instance.domain == merge_target_instance.domain][0]
|
||||||
|
if not overwrite:
|
||||||
|
key_input = ""
|
||||||
|
while key_input not in ("i", "O"):
|
||||||
|
print(f"Different settings for {input_instance.domain} detected.")
|
||||||
|
print(f"In the input blocklist the setting is\n{input_instance} whereas it's {merge_target_instance} in the merge target")
|
||||||
|
key_input = input("Keep input (i) or original (o) [i/O]")
|
||||||
|
elif key_input == "i":
|
||||||
|
merge_target_blocklist.append(merge_target_instance)
|
||||||
|
else:
|
||||||
|
merge_target_blocklist.append(input_instance)
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def cli():
|
def cli():
|
||||||
parser = argparse.ArgumentParser(description='Deploy blocklist updates to a mastodon server')
|
parser = argparse.ArgumentParser(description='Deploy blocklist updates to a fediverse server')
|
||||||
parser.add_argument('action', choices=['diff', 'deploy', 'export'],
|
parser.add_argument('action', choices=['diff', 'deploy', 'export', 'merge'],
|
||||||
help="Either use 'diff' to check the difference between local blockĺist and the blocklist on "
|
help="Either use 'diff' to check the difference between local blockĺist and the blocklist on "
|
||||||
"the server, 'deploy' to apply the current local blocklist or 'export' to export the remote "
|
"the server, 'deploy' to apply the current local blocklist or 'export' to export the remote "
|
||||||
"blocklist into a local file.")
|
"blocklist into a local file. merge can be used to merge a blocklist (given by -i) into "
|
||||||
|
"another (-o)")
|
||||||
parser.add_argument('-s', '--server', help="The address of the server where you want to deploy (e.g. "
|
parser.add_argument('-s', '--server', help="The address of the server where you want to deploy (e.g. "
|
||||||
"mastodon.social)")
|
"mastodon.social)")
|
||||||
parser.add_argument('-t', '--token', help="Authorization token")
|
parser.add_argument('-t', '--token', help="Authorization token")
|
||||||
@@ -76,7 +103,7 @@ def cli():
|
|||||||
parser.add_argument('-o', '--output', help="Filename where to export the blocklist")
|
parser.add_argument('-o', '--output', help="Filename where to export the blocklist")
|
||||||
parser.add_argument('-v', '--verbose', action='store_true')
|
parser.add_argument('-v', '--verbose', action='store_true')
|
||||||
parser.add_argument('-n', '--no-delete', action='store_true', help="Do not delete existing blocks")
|
parser.add_argument('-n', '--no-delete', action='store_true', help="Do not delete existing blocks")
|
||||||
parser.add_argument('--format', help="Export format: toml|markdown|csv")
|
parser.add_argument('--format', default="toml", type=str, help="Export format: toml|markdown|csv|json")
|
||||||
parser.add_argument('--private', action='store_true', help="When the flag is set, private comment will also be "
|
parser.add_argument('--private', action='store_true', help="When the flag is set, private comment will also be "
|
||||||
"exported.")
|
"exported.")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -90,8 +117,6 @@ def cli():
|
|||||||
else:
|
else:
|
||||||
token = os.getenv('MBD_TOKEN')
|
token = os.getenv('MBD_TOKEN')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""if there is a remote blocklist provided load this instead of fetching it from a server (for debugging reasons)"""
|
"""if there is a remote blocklist provided load this instead of fetching it from a server (for debugging reasons)"""
|
||||||
if args.remote_blocklist:
|
if args.remote_blocklist:
|
||||||
with open(args.remote_blocklist) as f:
|
with open(args.remote_blocklist) as f:
|
||||||
@@ -100,7 +125,7 @@ def cli():
|
|||||||
remote_blocklist = load_blocklist_from_instance(server=args.server, token=token)
|
remote_blocklist = load_blocklist_from_instance(server=args.server, token=token)
|
||||||
|
|
||||||
"""Load local blocklist only when needed"""
|
"""Load local blocklist only when needed"""
|
||||||
if args.action in ["diff", "deploy"]:
|
if args.action in ["diff", "deploy", "merge"]:
|
||||||
if args.input_file:
|
if args.input_file:
|
||||||
blocklist_filename = args.input_file
|
blocklist_filename = args.input_file
|
||||||
else:
|
else:
|
||||||
@@ -118,6 +143,9 @@ def cli():
|
|||||||
Instance.apply_blocks_from_diff(diffs, args.server, token, args.no_delete)
|
Instance.apply_blocks_from_diff(diffs, args.server, token, args.no_delete)
|
||||||
elif args.action == "export":
|
elif args.action == "export":
|
||||||
exporter(remote_blocklist, args.output, args.format, args.private)
|
exporter(remote_blocklist, args.output, args.format, args.private)
|
||||||
|
elif args.action == "merge":
|
||||||
|
merge(args.input_file, args.output, args.format, args.private)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
cli()
|
cli()
|
@@ -1,7 +1,8 @@
|
|||||||
from mastodon_blocklist_deploy.models import Instance
|
from fediverse_blocklist_deploy.models import Instance
|
||||||
import toml
|
import toml
|
||||||
import io
|
import io
|
||||||
import csv
|
import csv
|
||||||
|
import json
|
||||||
|
|
||||||
def blocklist_to_markdown(blocklist: [Instance], private: bool = False):
|
def blocklist_to_markdown(blocklist: [Instance], private: bool = False):
|
||||||
if private:
|
if private:
|
||||||
@@ -29,3 +30,7 @@ def blocklist_to_csv(blocklist: [Instance], private: bool = False):
|
|||||||
w.writeheader()
|
w.writeheader()
|
||||||
w.writerows(blocklist_as_dict)
|
w.writerows(blocklist_as_dict)
|
||||||
return csv_string.getvalue()
|
return csv_string.getvalue()
|
||||||
|
|
||||||
|
def blocklist_to_json(blocklist: [Instance], private: bool = False):
|
||||||
|
json_string = json.dumps([b.as_dict(private) for b in blocklist])
|
||||||
|
return json_string
|
@@ -11,12 +11,7 @@ class Instance:
|
|||||||
self.reject_reports = False
|
self.reject_reports = False
|
||||||
self.id = None
|
self.id = None
|
||||||
|
|
||||||
"""Remote blocks and local blocks are parsed differently"""
|
self.parse_block(instance_dict)
|
||||||
try:
|
|
||||||
instance_dict["id"]
|
|
||||||
self.parse_remote_block(instance_dict)
|
|
||||||
except KeyError:
|
|
||||||
self.parse_local_block(instance_dict)
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.domain}: {self.severity}"
|
return f"{self.domain}: {self.severity}"
|
||||||
@@ -36,37 +31,20 @@ class Instance:
|
|||||||
exportable[key] = getattr(self, key)
|
exportable[key] = getattr(self, key)
|
||||||
return exportable
|
return exportable
|
||||||
|
|
||||||
def parse_remote_block(self, instance_dict):
|
def parse_block(self, instance_dict):
|
||||||
self.domain = instance_dict["domain"]
|
# this specifies possible properties and default values if not found on the remote source. If a default is None
|
||||||
self.id = instance_dict["id"]
|
# the value is required and the parse will fail
|
||||||
self.severity = instance_dict["severity"]
|
properties_and_defaults = [("domain", None), ("severity", "suspend"), ("public_comment", ""),
|
||||||
self.public_comment = instance_dict["public_comment"]
|
("private_comment", ""), ("obfuscate", False), ("reject_media", True),
|
||||||
self.private_comment = instance_dict["private_comment"]
|
("reject_reports", True)]
|
||||||
self.obfuscate = instance_dict["obfuscate"]
|
for key, default in properties_and_defaults:
|
||||||
self.reject_media = instance_dict["reject_media"]
|
|
||||||
self.reject_reports = instance_dict["reject_reports"]
|
|
||||||
|
|
||||||
def parse_local_block(self, instance_dict):
|
|
||||||
try:
|
try:
|
||||||
self.name = instance_dict["name"]
|
setattr(self, key, instance_dict[key])
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
if default is not None:
|
||||||
self.domain = instance_dict["domain"]
|
setattr(self, key, default)
|
||||||
self.severity = instance_dict["severity"]
|
else:
|
||||||
self.public_comment = instance_dict["public_comment"]
|
raise KeyError(f"The key {key} was not in the instance_dict response.")
|
||||||
self.private_comment = instance_dict["private_comment"]
|
|
||||||
try:
|
|
||||||
self.obfuscate = instance_dict["obfuscate"]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
self.reject_media = instance_dict["reject_media"]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
self.reject_reports = instance_dict["reject_reports"]
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def apply(self, server, token, block_id=None):
|
def apply(self, server, token, block_id=None):
|
||||||
"""Applies instance block on the remote server"""
|
"""Applies instance block on the remote server"""
|
@@ -1,12 +1,12 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "mastodon-blocklist-deploy"
|
name = "fediverse-blocklist-deploy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
description = "A small tool to deploy blocklist updates to a mastodon server using its API."
|
description = "A small tool to deploy blocklist updates to a fediverse server using its API."
|
||||||
authors = ["Georg Krause <mail@georg-krause.net>", "Julian-Samuel Gebühr <julian-samuel@gebuehr.net>"]
|
authors = ["Georg Krause <mail@georg-krause.net>", "Julian-Samuel Gebühr <julian-samuel@gebuehr.net>"]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
packages = [{include = "mastodon_blocklist_deploy"}]
|
packages = [{include = "fediverse_blocklist_deploy"}]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
keywords = ["mastodon", "blocklist", "fediverse"]
|
keywords = ["fediverse", "blocklist", "fediverse"]
|
||||||
|
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
@@ -16,7 +16,7 @@ rich = "^13.0.1"
|
|||||||
toml = "^0.10.2"
|
toml = "^0.10.2"
|
||||||
|
|
||||||
[tool.poetry.scripts]
|
[tool.poetry.scripts]
|
||||||
mastodon_blocklist_deploy = 'mastodon_blocklist_deploy.cli:cli'
|
fediverse_blocklist_deploy = 'fediverse_blocklist_deploy.cli:cli'
|
||||||
|
|
||||||
|
|
||||||
[build-system]
|
[build-system]
|
||||||
|
Reference in New Issue
Block a user