Compare commits
23 Commits
toml-dump
...
rework_exp
Author | SHA1 | Date | |
---|---|---|---|
2984729841 | |||
58429a39f0 | |||
fe12631ee4 | |||
7d986a7072 | |||
46b0fe6b50 | |||
3dce62417e | |||
![]() |
d4c754c103 | ||
8a8a725002 | |||
58998e1c17 | |||
a484a41b45 | |||
![]() |
7ad318bc48 | ||
![]() |
8d5676d0b2 | ||
6d2a4d82b4 | |||
![]() |
d9d3f02fda | ||
0fca58810a | |||
181ac45bbf | |||
6a2a13bd74 | |||
0dd6930c0f | |||
33fee03059 | |||
2066c0332d | |||
![]() |
5376af3e7e | ||
![]() |
1565f17778 | ||
![]() |
ddc2ba1b43 |
23
.drone.yml
Normal file
23
.drone.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: exec
|
||||
name: build
|
||||
|
||||
platform:
|
||||
os: linux
|
||||
arch: arm64
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
commands:
|
||||
- docker build -t gcrkrause/mastodon-blocklist-deploy .
|
||||
- name: push
|
||||
environment:
|
||||
USERNAME:
|
||||
from_secret: docker-hub-user
|
||||
PASSWORD:
|
||||
from_secret: docker-hub-pw
|
||||
commands:
|
||||
- docker login -u $USERNAME -p $PASSWORD
|
||||
- docker push gcrkrause/mastodon-blocklist-deploy
|
||||
- docker image prune -a -f
|
17
DEVELOPMENT.md
Normal file
17
DEVELOPMENT.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Development Guide
|
||||
|
||||
## Docker
|
||||
|
||||
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`
|
||||
|
||||
Now you can execute any commands using
|
||||
|
||||
`docker run --rm mastodon_blocklist_deploy --help`
|
||||
|
||||
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`
|
||||
|
||||
Please be aware that changes to the package itself require a rebuild anyways.
|
12
Dockerfile
Normal file
12
Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM python:3.11-slim
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
|
||||
COPY pyproject.toml poetry.lock README.md /app/
|
||||
COPY mastodon_blocklist_deploy /app/mastodon_blocklist_deploy
|
||||
WORKDIR /app
|
||||
|
||||
ENTRYPOINT ["mastodon_blocklist_deploy"]
|
||||
|
||||
RUN pip install -e .
|
10
README.md
10
README.md
@@ -25,13 +25,15 @@ supports [Array of Tables](https://toml.io/en/v1.0.0#array-of-tables), I'd prefe
|
||||
##
|
||||
|
||||
```
|
||||
$ mastodon_blocklist_deploy -h
|
||||
usage: mastodon_blocklist_deploy [-h] [-s SERVER] [-t TOKEN] [-i INPUT_FILE] [-r REMOTE_BLOCKLIST] [-o OUTPUT] [-v] [-n] {diff,deploy,export}
|
||||
usage: mastodon_blocklist_deploy [-h] [-s SERVER] [-t TOKEN] [-i INPUT_FILE] [-r REMOTE_BLOCKLIST] [-o OUTPUT] [-v] [-n]
|
||||
[--format FORMAT] [--private]
|
||||
{diff,deploy,export}
|
||||
|
||||
Deploy blocklist updates to a mastodon server
|
||||
|
||||
positional arguments:
|
||||
{diff,deploy,export} 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 blocklist into a local file.
|
||||
{diff,deploy,export} 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 blocklist into a local file.
|
||||
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
@@ -47,6 +49,8 @@ options:
|
||||
Filename where to export the blocklist
|
||||
-v, --verbose
|
||||
-n, --no-delete Do not delete existing blocks
|
||||
--format FORMAT Export format: toml|markdown|csv
|
||||
--private When the flag is set, private comment will also be exported.
|
||||
```
|
||||
|
||||
## Obtain a server token
|
||||
|
@@ -7,6 +7,7 @@ import os
|
||||
import toml
|
||||
|
||||
from mastodon_blocklist_deploy.models import Instance
|
||||
from mastodon_blocklist_deploy.helpers import blocklist_to_markdown, blocklist_to_toml, blocklist_to_csv
|
||||
|
||||
|
||||
def load_blocklist_file(filename: str) -> [Instance]:
|
||||
@@ -39,6 +40,28 @@ def load_blocklist_from_instance(server: str, token: str) -> [Instance]:
|
||||
raise ConnectionError(f"Could not connect to the server ({response.status_code}: {response.reason})")
|
||||
|
||||
|
||||
def remove_key_from_dict(dict, key):
|
||||
del dict[key]
|
||||
return dict
|
||||
|
||||
|
||||
def exporter(blocklist, output=None, format: str = "toml", private: bool = False):
|
||||
if format == "toml":
|
||||
exported_text = blocklist_to_toml(blocklist, private)
|
||||
if format == "csv":
|
||||
exported_text = blocklist_to_csv(blocklist, private)
|
||||
if format == "markdown":
|
||||
exported_text = blocklist_to_markdown(blocklist, private)
|
||||
|
||||
# Output the text
|
||||
if output is not None:
|
||||
with open(output, "w") as f:
|
||||
f.write(exported_text)
|
||||
else:
|
||||
print(exported_text)
|
||||
|
||||
|
||||
|
||||
def cli():
|
||||
parser = argparse.ArgumentParser(description='Deploy blocklist updates to a mastodon server')
|
||||
parser.add_argument('action', choices=['diff', 'deploy', 'export'],
|
||||
@@ -53,6 +76,9 @@ def cli():
|
||||
parser.add_argument('-o', '--output', help="Filename where to export the blocklist")
|
||||
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('--format', help="Export format: toml|markdown|csv")
|
||||
parser.add_argument('--private', action='store_true', help="When the flag is set, private comment will also be "
|
||||
"exported.")
|
||||
args = parser.parse_args()
|
||||
if args.verbose:
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
@@ -79,7 +105,11 @@ def cli():
|
||||
blocklist_filename = args.input_file
|
||||
else:
|
||||
blocklist_filename = "../blocklist.toml"
|
||||
local_blocklist = load_blocklist_file(blocklist_filename)
|
||||
try:
|
||||
local_blocklist = load_blocklist_file(blocklist_filename)
|
||||
except FileNotFoundError:
|
||||
print("Local blocklist file was not found. Make sure to specify it's location via -i")
|
||||
exit()
|
||||
|
||||
if args.action == "diff":
|
||||
Instance.show_diffs(local_blocklist, remote_blocklist)
|
||||
@@ -87,12 +117,7 @@ def cli():
|
||||
diffs = Instance.list_diffs(local_blocklist, remote_blocklist)
|
||||
Instance.apply_blocks_from_diff(diffs, args.server, token, args.no_delete)
|
||||
elif args.action == "export":
|
||||
if not args.output:
|
||||
print(toml.dumps({"instances": [b.__dict__ for b in remote_blocklist]}))
|
||||
else:
|
||||
with open(args.output, "w") as f:
|
||||
toml.dump({"instances": [b.__dict__ for b in remote_blocklist]}, f)
|
||||
|
||||
exporter(remote_blocklist, args.output, args.format, args.private)
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
||||
|
31
mastodon_blocklist_deploy/helpers.py
Normal file
31
mastodon_blocklist_deploy/helpers.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from mastodon_blocklist_deploy.models import Instance
|
||||
import toml
|
||||
import io
|
||||
import csv
|
||||
|
||||
def blocklist_to_markdown(blocklist: [Instance], private: bool = False):
|
||||
if private:
|
||||
markdown_string = "| Instance | Status | Reason | Private Comment |\n | --- | --- | --- |\n"
|
||||
else:
|
||||
markdown_string = "| Instance | Status | Reason |\n | --- | --- | --- |\n"
|
||||
for instance in blocklist:
|
||||
|
||||
if private:
|
||||
markdown_string += f"| {instance.domain} | {instance.severity} | {instance.public_comment} | {instance.private_comment} |\n"
|
||||
else:
|
||||
markdown_string += f"| {instance.domain} | {instance.severity} | {instance.public_comment} |\n"
|
||||
|
||||
return markdown_string
|
||||
|
||||
def blocklist_to_toml(blocklist: [Instance], private: bool = False):
|
||||
toml_string = toml.dumps({"instances": [b.as_dict(private) for b in blocklist]})
|
||||
return toml_string
|
||||
|
||||
def blocklist_to_csv(blocklist: [Instance], private: bool = False):
|
||||
csv_string = io.StringIO()
|
||||
blocklist_as_dict = [b.as_dict(private) for b in blocklist]
|
||||
keys = blocklist_as_dict[0].keys()
|
||||
w = csv.DictWriter(csv_string, keys)
|
||||
w.writeheader()
|
||||
w.writerows(blocklist_as_dict)
|
||||
return csv_string.getvalue()
|
@@ -27,6 +27,15 @@ class Instance:
|
||||
def status_str(self):
|
||||
return f"{self.severity}\nReject reports: {self.reject_reports}\nReject media: {self.reject_media}\nObfuscate: {self.obfuscate}"
|
||||
|
||||
def as_dict(self, private=False):
|
||||
keys = ["domain", "severity", "public_comment", "obfuscate", "reject_media", "reject_reports"]
|
||||
if private:
|
||||
keys.append("private_comment")
|
||||
exportable = {}
|
||||
for key in keys:
|
||||
exportable[key] = getattr(self, key)
|
||||
return exportable
|
||||
|
||||
def parse_remote_block(self, instance_dict):
|
||||
self.domain = instance_dict["domain"]
|
||||
self.id = instance_dict["id"]
|
||||
@@ -38,7 +47,10 @@ class Instance:
|
||||
self.reject_reports = instance_dict["reject_reports"]
|
||||
|
||||
def parse_local_block(self, instance_dict):
|
||||
self.name = instance_dict["name"]
|
||||
try:
|
||||
self.name = instance_dict["name"]
|
||||
except KeyError:
|
||||
pass
|
||||
self.domain = instance_dict["domain"]
|
||||
self.severity = instance_dict["severity"]
|
||||
self.public_comment = instance_dict["public_comment"]
|
||||
@@ -75,7 +87,7 @@ class Instance:
|
||||
response = requests.put(f'https://{server}/api/v1/admin/domain_blocks/{block_id}', data=data,
|
||||
headers=headers)
|
||||
if response.status_code != 200:
|
||||
raise ConnectionError(f"Could not apply block ({response.status_code}: {response.reason})")
|
||||
raise ConnectionError(f"Could not apply block for {self.domain} ({response.status_code}: {response.reason})")
|
||||
|
||||
def delete(self, server: str, token: str):
|
||||
"""Deletes the instance from the blocklist on the remote server"""
|
||||
|
Reference in New Issue
Block a user