diff --git a/app/activitypub.py b/app/activitypub.py
index 1ac0d15..7611e33 100644
--- a/app/activitypub.py
+++ b/app/activitypub.py
@@ -61,9 +61,9 @@ ME = {
},
"url": config.ID + "/", # the path is important for Mastodon compat
"manuallyApprovesFollowers": config.CONFIG.manually_approves_followers,
- "attachment": [], # TODO media support
+ "attachment": [], # TODO media support
"icon": {
- "mediaType": "image/png", # TODO media support
+ "mediaType": "image/png", # TODO media support
"type": "Image",
"url": config.CONFIG.icon_url,
},
@@ -72,16 +72,17 @@ ME = {
"owner": config.ID,
"publicKeyPem": get_pubkey_as_pem(config.KEY_PATH),
},
- "tag": [] # TODO tag support
+ "tag": [], # TODO tag support
}
+
class BaseActor:
def __init__(self, ap_actor: RawObject, **_) -> None:
if (ap_type := ap_actor.get("type")) not in ACTOR_TYPES:
raise ValueError(f"Unexpected actor type: {ap_type}")
self._ap_actor = ap_actor
- self._ap_type : str = ap_type # type: ignore
+ self._ap_type: str = ap_type # type: ignore
@property
def ap_actor(self) -> RawObject:
@@ -101,8 +102,7 @@ class BaseActor:
@property
def share_inbox_url(self) -> str:
- return self.ap_actor.get("endpoints", {}).get("sharedInbox") \
- or self.inbox_url
+ return self.ap_actor.get("endpoints", {}).get("sharedInbox") or self.inbox_url
class VisibilityEnum(str, enum.Enum):
@@ -125,7 +125,6 @@ def handle_visibility(ap_object: dict) -> VisibilityEnum:
def wrap_ap_object(ap_object: dict) -> dict:
if ap_object["type"] in ["Note"]:
-
if "@context" in ap_object:
del ap_object["@context"]
diff --git a/app/actor.py b/app/actor.py
index bb94750..835dce2 100644
--- a/app/actor.py
+++ b/app/actor.py
@@ -7,7 +7,7 @@ from urllib.parse import urlparse
from sqlalchemy import select
from loguru import logger
-from app.hyap import fetch # type: ignore
+from app.hyap import fetch # type: ignore
from app import models
from app.database import AsyncSession
@@ -17,16 +17,14 @@ if typing.TYPE_CHECKING:
async def fetch_actor(
- db_session: AsyncSession,
- actor_id: str,
+ db_session: AsyncSession,
+ actor_id: str,
) -> "ActorModel":
"""Fetch actor on db, if not exist will be grabed and stored
in db."""
exist_actor = (
await db_session.scalars(
- select(models.Actor).where(
- models.Actor.ap_id == actor_id
- )
+ select(models.Actor).where(models.Actor.ap_id == actor_id)
)
).one_or_none()
@@ -38,10 +36,7 @@ async def fetch_actor(
return exist_actor
-async def save_actor(
- ap_object: dict,
- db_session: AsyncSession
-) -> "ActorModel":
+async def save_actor(ap_object: dict, db_session: AsyncSession) -> "ActorModel":
"""Save actor to db."""
logger.info("save actor " + ap_object["id"])
actor = models.Actor(
@@ -58,26 +53,22 @@ async def save_actor(
def _handle(
- ap_object: dict,
+ ap_object: dict,
) -> str:
ap_id = urlparse(ap_object["id"])
if not ap_id.hostname:
raise ValueError(f"Invalid actor ID {ap_id}")
- handle = '@' + ap_object["preferredUsername"] + '@' + ap_id.hostname
+ handle = "@" + ap_object["preferredUsername"] + "@" + ap_id.hostname
return handle
-async def get_public_key(
- db_session: AsyncSession,
- key_id: str
-) -> str:
+async def get_public_key(db_session: AsyncSession, key_id: str) -> str:
"""Give key id and reutrn public key."""
existing_actor = (
await db_session.scalars(
- select(models.Actor).where(
- models.Actor.ap_id == key_id.split("#")[0])
+ select(models.Actor).where(models.Actor.ap_id == key_id.split("#")[0])
)
).one_or_none()
public_key = existing_actor.ap_actor["publicKey"]["publicKeyPem"]
diff --git a/app/boxes.py b/app/boxes.py
index 6c76956..7e67133 100644
--- a/app/boxes.py
+++ b/app/boxes.py
@@ -17,7 +17,7 @@ from app.actor import fetch_actor
from app.httpsig import k
import app.activitypub as ap
-from app.hyap import post # type: ignore
+from app.hyap import post # type: ignore
from urllib.parse import urlparse
from sqlalchemy import select
@@ -29,7 +29,6 @@ from uuid import uuid4
from datetime import datetime
-
def allocate_outbox_id() -> str:
return str(uuid.uuid4())
@@ -61,7 +60,7 @@ async def get_outbox_object(
async def get_inbox_object(
db_session: AsyncSession,
- ap_id: str,
+ ap_id: str,
) -> InboxObject | None:
return (
await db_session.execute(
@@ -77,8 +76,8 @@ async def get_inbox_object(
async def save_incoming(
- db_session: AsyncSession,
- payload: dict,
+ db_session: AsyncSession,
+ payload: dict,
) -> models.IncomingActivity | None:
ap_id: str
if "@context" not in payload:
@@ -107,8 +106,8 @@ async def save_incoming(
async def process_incoming(
- db_session: AsyncSession,
- ap_object: dict[str, Any],
+ db_session: AsyncSession,
+ ap_object: dict[str, Any],
) -> bool:
actor = await fetch_actor(db_session, ap_object["actor"])
@@ -122,8 +121,7 @@ async def process_incoming(
ap_object["object"]["id"],
)
relates_to_inbox_object = await get_inbox_object(
- db_session,
- ap_object["object"]["id"]
+ db_session, ap_object["object"]["id"]
)
else:
if ap_object["object"].startswith(BASE_URL):
@@ -132,15 +130,13 @@ async def process_incoming(
ap_object["object"],
)
relates_to_inbox_object = await get_inbox_object(
- db_session,
- ap_object["object"]
+ db_session, ap_object["object"]
)
-
def build_object(
- object,
- relates_to_inbox_object = None,
- relates_to_outbox_object = None,
+ object,
+ relates_to_inbox_object=None,
+ relates_to_outbox_object=None,
) -> InboxObject:
inbox_object = models.InboxObject(
actor_id=actor.id,
@@ -172,8 +168,8 @@ async def process_incoming(
return True
return False
elif "Undo" == ap_object["type"]:
- #inbox_object = build_object(ap_object)
- #db_session.add(inbox_object)
+ # inbox_object = build_object(ap_object)
+ # db_session.add(inbox_object)
if await _handle_undo(db_session, ap_object):
return True
elif ap_object["type"] in ["Accept", "Rejact"]:
@@ -181,16 +177,21 @@ async def process_incoming(
if isinstance(follow_id, dict):
follow_id = follow_id["id"]
- relate_following_object = (await db_session.execute(
- select(models.OutboxObject)
- .where(models.OutboxObject.ap_id == follow_id)
- .options(
- joinedload(models.OutboxObject.relates_to_inbox_object).options(
- joinedload(models.InboxObject.actor)
- )
+ relate_following_object = (
+ (
+ await db_session.execute(
+ select(models.OutboxObject)
+ .where(models.OutboxObject.ap_id == follow_id)
+ .options(
+ joinedload(models.OutboxObject.relates_to_inbox_object).options(
+ joinedload(models.InboxObject.actor)
+ )
+ )
+ )
)
+ .unique()
+ .scalar_one_or_none()
)
- ).unique().scalar_one_or_none()
if "Accept" == ap_object["type"]:
try:
@@ -253,13 +254,13 @@ async def process_incoming(
async def _handle_follow(
- db_session: AsyncSession,
- actor: Actor,
- inbox_object: InboxObject,
+ db_session: AsyncSession,
+ actor: Actor,
+ inbox_object: InboxObject,
) -> bool:
- if ME["id"] != inbox_object.ap_object["object"]: #type: ignore
+ if ME["id"] != inbox_object.ap_object["object"]: # type: ignore
# await db_session.delete(ap_object)
- logger.warning("no match follow object!" + inbox_object.ap_object["id"]) #type: ignore
+ logger.warning("no match follow object!" + inbox_object.ap_object["id"]) # type: ignore
return False
if MANUALLY_APPROVES_FOLLOWERS:
@@ -270,14 +271,14 @@ async def _handle_follow(
async def _send_accept(
- db_session: AsyncSession,
- actor: Actor,
- inbox_object: InboxObject,
+ db_session: AsyncSession,
+ actor: Actor,
+ inbox_object: InboxObject,
) -> None:
follower = models.Follower(
actor_id=inbox_object.actor_id,
inbox_object_id=inbox_object.id,
- ap_actor_id=inbox_object.ap_object["actor"], #type: ignore
+ ap_actor_id=inbox_object.ap_object["actor"], # type: ignore
)
try:
@@ -290,52 +291,49 @@ async def _send_accept(
try:
reply_id = allocate_outbox_id()
- url = actor.inbox_url # type: ignore
+ url = actor.inbox_url # type: ignore
out = {
"@context": ap.AS_CTX,
"id": build_object_id(reply_id),
"type": "Accept",
"actor": ME["id"],
- "object": inbox_object.ap_object["id"], #type: ignore
+ "object": inbox_object.ap_object["id"], # type: ignore
}
- #TODO outcoming
+ # TODO outcoming
await save_to_outbox(
db_session,
reply_id,
out,
- relates_to_actor_id=actor.id, # type: ignore
+ relates_to_actor_id=actor.id, # type: ignore
)
- await post(url, out) # type: ignore
+ await post(url, out) # type: ignore
except Exception as e:
logger.error(e)
-async def _handle_undo(
- db_session: AsyncSession,
- inbox_object: dict
-) -> bool:
+async def _handle_undo(db_session: AsyncSession, inbox_object: dict) -> bool:
if inbox_object["object"]["object"] != ME["id"]:
- logger.warning("Wrong undo object! "
- + inbox_object["object"]["actor"])
+ logger.warning("Wrong undo object! " + inbox_object["object"]["actor"])
return False
if "Follow" == inbox_object["object"]["type"]:
- relate_object = (await db_session.execute(
- select(models.InboxObject)
- .where(models.InboxObject.ap_id == inbox_object["object"]["id"])
- .options(
- joinedload(models.InboxObject.actor),
- joinedload(models.InboxObject.relates_to_inbox_object),
- joinedload(models.InboxObject.relates_to_outbox_object),
+ relate_object = (
+ await db_session.execute(
+ select(models.InboxObject)
+ .where(models.InboxObject.ap_id == inbox_object["object"]["id"])
+ .options(
+ joinedload(models.InboxObject.actor),
+ joinedload(models.InboxObject.relates_to_inbox_object),
+ joinedload(models.InboxObject.relates_to_outbox_object),
)
)
).scalar_one_or_none() # type: ignore
if relate_object:
- relate_object.undo_id=inbox_object["object"]["id"]
- relate_object.is_deleted=True
+ relate_object.undo_id = inbox_object["object"]["id"]
+ relate_object.is_deleted = True
await db_session.execute(
delete(models.Follower).where(
@@ -348,23 +346,20 @@ async def _handle_undo(
return False
-async def send_follow(
- db_session : AsyncSession,
- acct : str
-):
+async def send_follow(db_session: AsyncSession, acct: str):
await _send_follow(db_session, acct)
await db_session.commit()
await db_session.flush()
async def _handle_announce(
- db_session: AsyncSession,
- inbox_object: InboxObject,
- relates_to_outbox_object: models.OutboxObject | None,
- relates_to_inbox_object: models.InboxObject | None,
-) -> bool :
+ db_session: AsyncSession,
+ inbox_object: InboxObject,
+ relates_to_outbox_object: models.OutboxObject | None,
+ relates_to_inbox_object: models.InboxObject | None,
+) -> bool:
if relates_to_outbox_object:
- relates_to_outbox_object.announces_count = ( # type: ignore
+ relates_to_outbox_object.announces_count = ( # type: ignore
models.OutboxObject.announces_count + 1
)
logger.info(f"announces +1 {relates_to_outbox_object.ap_id}")
@@ -378,9 +373,9 @@ async def _handle_like(
inbox_object: InboxObject,
relates_to_outbox_object: models.OutboxObject | None,
relates_to_inbox_object: models.InboxObject | None,
-) -> bool :
+) -> bool:
if relates_to_outbox_object:
- relates_to_outbox_object.likes_count = ( # type: ignore
+ relates_to_outbox_object.likes_count = ( # type: ignore
models.OutboxObject.likes_count + 1
)
logger.info(f"likes +1 {relates_to_outbox_object.ap_id}")
@@ -390,8 +385,8 @@ async def _handle_like(
async def _send_follow(
- db_session : AsyncSession,
- actor_url : str,
+ db_session: AsyncSession,
+ actor_url: str,
):
actor = await fetch_actor(db_session, actor_url)
@@ -408,8 +403,8 @@ async def _send_follow(
db_session,
follow_id,
out,
- relates_to_actor_id=actor.id, # type: ignore
- activity_object_ap_id=actor.ap_id
+ relates_to_actor_id=actor.id, # type: ignore
+ activity_object_ap_id=actor.ap_id,
)
await post(
@@ -419,11 +414,11 @@ async def _send_follow(
async def _send_create(
- db_session: AsyncSession,
- ap_type: str,
- content: str,
- visibility: ap.VisibilityEnum,
- published: str | None = None,
+ db_session: AsyncSession,
+ ap_type: str,
+ content: str,
+ visibility: ap.VisibilityEnum,
+ published: str | None = None,
) -> bool:
object_id = allocate_outbox_id()
if not published:
@@ -485,18 +480,14 @@ async def _send_create(
except Exception as e:
logger.error(e)
-
return True
async def _compute_recipients(
- db_session: AsyncSession,
- ap_object: dict,
+ db_session: AsyncSession,
+ ap_object: dict,
) -> set[str]:
-
- async def process_collection(
- db_session,
- url) -> list[Actor]:
+ async def process_collection(db_session, url) -> list[Actor]:
if url == BASE_URL + "/followers":
followers = (
(
@@ -535,14 +526,13 @@ async def _compute_recipients(
async def save_to_inbox(
- db_session : AsyncSession,
- inbox_id : str,
- ap_object : dict,
- relates_to_inbox_object_id: int | None = None,
- relates_to_outbox_object_id: int | None = None,
- relates_to_actor_id: int | None = None,
+ db_session: AsyncSession,
+ inbox_id: str,
+ ap_object: dict,
+ relates_to_inbox_object_id: int | None = None,
+ relates_to_outbox_object_id: int | None = None,
+ relates_to_actor_id: int | None = None,
) -> InboxObject:
-
ap_type = ap_object["type"]
ap_id = ap_object["id"]
visibility = handle_visibility(ap_object)
@@ -552,7 +542,7 @@ async def save_to_inbox(
ap_object=ap_object,
ap_id=ap_id,
ap_type=ap_type,
- visibility =visibility,
+ visibility=visibility,
relates_to_inbox_object_id=relates_to_inbox_object_id,
relates_to_outbox_object_id=relates_to_outbox_object_id,
relates_to_actor_id=relates_to_actor_id,
@@ -566,13 +556,13 @@ async def save_to_inbox(
async def save_to_outbox(
- db_session : AsyncSession,
- outbox_id : str,
- ap_object : dict,
- activity_object_ap_id: str | None = None,
- relates_to_inbox_object_id: int | None = None,
- relates_to_outbox_object_id: int | None = None,
- relates_to_actor_id: int | None = None,
+ db_session: AsyncSession,
+ outbox_id: str,
+ ap_object: dict,
+ activity_object_ap_id: str | None = None,
+ relates_to_inbox_object_id: int | None = None,
+ relates_to_outbox_object_id: int | None = None,
+ relates_to_actor_id: int | None = None,
) -> OutboxObject:
ap_type = ap_object["type"]
ap_id = ap_object["id"]
@@ -583,7 +573,7 @@ async def save_to_outbox(
ap_object=ap_object,
ap_id=ap_id,
ap_type=ap_type,
- visibility =visibility,
+ visibility=visibility,
activity_object_ap_id=activity_object_ap_id,
relates_to_inbox_object_id=relates_to_inbox_object_id,
relates_to_outbox_object_id=relates_to_outbox_object_id,
diff --git a/app/config.py b/app/config.py
index 1e660b8..d8b2f7b 100644
--- a/app/config.py
+++ b/app/config.py
@@ -35,28 +35,30 @@ class Config(pydantic.BaseModel):
ROOT_DIR = Path().parent.resolve()
_CONFIG_FILE = os.getenv("FOXHOLE_CONFIG_FILE", "config.toml")
+
def load_config() -> Config:
try:
return Config.parse_obj(
tomli.loads((ROOT_DIR / "data" / _CONFIG_FILE).read_text())
)
except FileNotFoundError:
- raise ValueError(
- f"{_CONFIG_FILE} is missing"
- )
+ raise ValueError(f"{_CONFIG_FILE} is missing")
+
def get_version_commit() -> str:
import subprocess
+
try:
return (
- '+' +
- subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"])
+ "+"
+ + subprocess.check_output(["git", "rev-parse", "--short=8", "HEAD"])
.split()[0]
.decode()
)
except Exception:
return "+dev"
+
CONFIG = load_config()
DOMAIN = CONFIG.domain
@@ -67,9 +69,9 @@ _SCHEME = "https" if CONFIG.https else "http"
ID = f"{_SCHEME}://{DOMAIN}"
BASE_URL = ID
USERNAME = CONFIG.username
-KEY_PATH = (ROOT_DIR / os.getenv("FOXHOLE_KEY_PATH", "data/key.pem"))
+KEY_PATH = ROOT_DIR / os.getenv("FOXHOLE_KEY_PATH", "data/key.pem")
-VERSION = "Foxhole-"+ MAIN_VERSION + get_version_commit()
+VERSION = "Foxhole-" + MAIN_VERSION + get_version_commit()
USER_AGENT = "Fediverse Application/" + VERSION
AP_CONTENT_TYPE = "application/activity+json"
diff --git a/app/database.py b/app/database.py
index 2d1338a..16d5147 100644
--- a/app/database.py
+++ b/app/database.py
@@ -25,13 +25,16 @@ async_engine = create_async_engine(
)
async_session = sessionmaker(async_engine, class_=AsyncSession, expire_on_commit=False)
+
class Base(DeclarativeBase):
pass
+
metadata_obj = MetaData()
+
async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
- async with async_session() as session: # type: ignore
+ async with async_session() as session: # type: ignore
try:
yield session
finally:
diff --git a/app/httpsig.py b/app/httpsig.py
index 0e67281..1e7a844 100644
--- a/app/httpsig.py
+++ b/app/httpsig.py
@@ -12,36 +12,30 @@ from Crypto.Signature import PKCS1_v1_5
from Crypto.PublicKey import RSA
-
class HttpSignature:
"""
calculation and verification of HTTP signatures
"""
@classmethod
- def calculation_digest(
- cls,
- body : bytes,
- algorithm : str ="sha-256"
- )-> str :
+ def calculation_digest(cls, body: bytes, algorithm: str = "sha-256") -> str:
"""
Calculates the digest header value for a given HTTP body
"""
if "sha-256" == algorithm:
h = SHA256.new()
h.update(body)
- return "SHA-256=" + \
- base64.b64encode(h.digest()).decode("utf-8")
+ return "SHA-256=" + base64.b64encode(h.digest()).decode("utf-8")
else:
raise ValueError(f"No support algorithm {algorithm}")
@classmethod
def verify_signature(
- cls,
- signature_string : str,
- signature : bytes,
- pubkey,
- ) -> bool :
+ cls,
+ signature_string: str,
+ signature: bytes,
+ pubkey,
+ ) -> bool:
pubkey = RSA.importKey(pubkey)
signer = PKCS1_v1_5.new(pubkey)
digest = SHA256.new()
@@ -65,13 +59,13 @@ class HttpSignature:
@classmethod
def build_signature_string(
- cls,
- method : str,
- path : str,
- signed_headers : list,
- body_digest : str | None,
- headers,
- ) -> str :
+ cls,
+ method: str,
+ path: str,
+ signed_headers: list,
+ body_digest: str | None,
+ headers,
+ ) -> str:
signed_string = []
for signed_header in signed_headers:
if signed_header == "(request-target)":
@@ -87,9 +81,7 @@ class HTTPXSigAuth(httpx.Auth):
def __init__(self, key) -> None:
self.key = key
- def auth_flow(
- self, r: httpx.Request
- ):
+ def auth_flow(self, r: httpx.Request):
bodydigest = None
if r.content:
bh = hashlib.new("sha256")
@@ -122,6 +114,7 @@ class HTTPXSigAuth(httpx.Auth):
r.headers["signature"] = sig_value
yield r
+
k = KEY_PATH.read_text()
k = RSA.importKey(k)
auth = HTTPXSigAuth(k)
diff --git a/app/ldsig.py b/app/ldsig.py
index e0c10f4..83241ef 100644
--- a/app/ldsig.py
+++ b/app/ldsig.py
@@ -16,8 +16,8 @@ from app.database import AsyncSession
from app.actor import get_public_key
+requests_loader = pyld.documentloader.requests.requests_document_loader() # type: ignore
-requests_loader = pyld.documentloader.requests.requests_document_loader() # type: ignore
def _loader(url, options):
if options is None:
@@ -47,7 +47,7 @@ def _options_hash(doc: ap.RawObject) -> str:
doc, {"algorithm": "URDNA2015", "format": "application/nquads"}
)
doc_hash = hashlib.new("sha256")
- doc_hash.update(normalized.encode("utf-8")) # type: ignore
+ doc_hash.update(normalized.encode("utf-8")) # type: ignore
return doc_hash.hexdigest()
@@ -59,7 +59,7 @@ def _doc_hash(doc: ap.RawObject) -> str:
doc, {"algorithm": "URDNA2015", "format": "application/nquads"}
)
doc_hash = hashlib.new("sha256")
- doc_hash.update(normalized.encode("utf-8")) # type: ignore
+ doc_hash.update(normalized.encode("utf-8")) # type: ignore
return doc_hash.hexdigest()
@@ -80,7 +80,9 @@ async def verify_signature(
signer = PKCS1_v1_5.new(pubkey)
digest = SHA256.new()
digest.update(to_be_signed.encode("utf-8"))
- return signer.verify(digest, base64.b64decode(signature)) # pylint: disable=not-callable
+ return signer.verify(
+ digest, base64.b64decode(signature)
+ ) # pylint: disable=not-callable
def generate_signature(doc: ap.RawObject, key) -> None:
diff --git a/app/main.py b/app/main.py
index b9acd2b..21b038e 100644
--- a/app/main.py
+++ b/app/main.py
@@ -35,14 +35,12 @@ from app.hysql import get_index_status
def _check_0rtt_early_data(request: Request) -> None:
"""Disable TLS1.3 0-RTT requests for non-GET."""
- if request.headers.get("Early-Data", None) == "1" \
- and request.method != "GET":
+ if request.headers.get("Early-Data", None) == "1" and request.method != "GET":
raise fastapi.HTTPException(status_code=425, detail="Too early")
app = FastAPI(
- docs_url=None, redoc_url=None,
- dependencies=[Depends(_check_0rtt_early_data)]
+ docs_url=None, redoc_url=None, dependencies=[Depends(_check_0rtt_early_data)]
)
templates = Jinja2Templates(directory="templates")
@@ -55,6 +53,7 @@ logger.add("output.log", level="DEBUG")
# pylint: disable=too-few-public-methods
class ActivityPubResponse(JSONResponse):
"""Simple wrap JSONresponse return ActivityPub response."""
+
media_type = "application/activity+json"
@@ -114,8 +113,8 @@ async def inbox(
@app.post("/outbox")
async def outbox(
- request: Request,
- db_session: AsyncSession = Depends(get_db_session),
+ request: Request,
+ db_session: AsyncSession = Depends(get_db_session),
) -> Response:
"""ActivityPub outbox endpoint, now only process client post request."""
payload = await request.json()
@@ -137,12 +136,7 @@ async def outbox(
logger.info("True token")
note_content = to_html(payload["content"]).replace("\n", "")
- await _send_create(
- db_session,
- "Note",
- note_content,
- payload["visibility"]
- )
+ await _send_create(db_session, "Note", note_content, payload["visibility"])
return Response(status_code=200)
diff --git a/app/models.py b/app/models.py
index da646ff..f105a3f 100644
--- a/app/models.py
+++ b/app/models.py
@@ -42,8 +42,12 @@ class Actor(Base, BaseActor):
handle = mapped_column(String, nullable=True, index=True)
- is_blocked = mapped_column(Boolean, nullable=False, default=False, server_default="0")
- is_deleted = mapped_column(Boolean, nullable=False, default=False, server_default="0")
+ is_blocked = mapped_column(
+ Boolean, nullable=False, default=False, server_default="0"
+ )
+ is_deleted = mapped_column(
+ Boolean, nullable=False, default=False, server_default="0"
+ )
class InboxObject(Base, BaseObject):
@@ -179,7 +183,9 @@ class Follower(Base):
created_at = mapped_column(DateTime(timezone=True), nullable=False, default=now)
updated_at = mapped_column(DateTime(timezone=True), nullable=False, default=now)
- actor_id = mapped_column(Integer, ForeignKey("actor.id"), nullable=False, unique=True)
+ actor_id = mapped_column(
+ Integer, ForeignKey("actor.id"), nullable=False, unique=True
+ )
actor: Mapped[Actor] = relationship(Actor, uselist=False)
inbox_object_id = mapped_column(Integer, ForeignKey("inbox.id"), nullable=False)
@@ -195,7 +201,9 @@ class Following(Base):
created_at = mapped_column(DateTime(timezone=True), nullable=False, default=now)
updated_at = mapped_column(DateTime(timezone=True), nullable=False, default=now)
- actor_id = mapped_column(Integer, ForeignKey("actor.id"), nullable=False, unique=True)
+ actor_id = mapped_column(
+ Integer, ForeignKey("actor.id"), nullable=False, unique=True
+ )
actor: Mapped[Actor] = relationship(Actor, uselist=False)
outbox_object_id = mapped_column(Integer, ForeignKey("outbox.id"), nullable=False)
diff --git a/app/orgpython/document.py b/app/orgpython/document.py
index f961544..8eae177 100644
--- a/app/orgpython/document.py
+++ b/app/orgpython/document.py
@@ -36,7 +36,8 @@ LIST_STATUS_REGEXP = re.compile(r"\[( |X|-)\]\s")
LIST_LEVEL_REGEXP = re.compile(r"(\s*)(.+)$")
HEADLINE_REGEXP = re.compile(
- r"^(\*+)(?:\s+(.+?))?(?:\s+\[#(.+)\])?(\s+.*?)(?:\s+:(.+):)?$")
+ r"^(\*+)(?:\s+(.+?))?(?:\s+\[#(.+)\])?(\s+.*?)(?:\s+:(.+):)?$"
+)
KEYWORD_REGEXP = re.compile(r"^(\s*)#\+([^:]+):(\s+(.*)|$)")
COMMENT_REGEXP = re.compile(r"^(\s*)#(.*)")
ATTRIBUTE_REGEXP = re.compile(r"(?:^|\s+)(:[-\w]+)\s+(.*)$")
@@ -172,7 +173,7 @@ class Parser(object):
num = index + 1
while num < end:
if node.matchend(num, lines):
- node.preparse(lines[index + 1:num])
+ node.preparse(lines[index + 1 : num])
return node, num
num += 1
return None, index
@@ -188,7 +189,7 @@ class Parser(object):
if node.matchend(num, lines):
break
num += 1
- node.preparse(lines[index + 1:num])
+ node.preparse(lines[index + 1 : num])
return node, num
def parse_headline(self, index, lines):
@@ -260,7 +261,7 @@ class Parser(object):
def __str__(self):
str_children = [str(child) for child in self.children]
- return self.__class__.__name__ + '(' + ','.join(str_children) + ')'
+ return self.__class__.__name__ + "(" + ",".join(str_children) + ")"
def __repr__(self):
return self.__str__()
@@ -268,13 +269,14 @@ class Parser(object):
class Headline(Parser):
def __init__(
- self,
- title,
- stars=1,
- keyword=None,
- priority=None,
- tags=[],
- todo_keywords=TODO_KEYWORDS):
+ self,
+ title,
+ stars=1,
+ keyword=None,
+ priority=None,
+ tags=[],
+ todo_keywords=TODO_KEYWORDS,
+ ):
super(Headline, self).__init__()
self.title = title
self.stars = stars
@@ -311,7 +313,7 @@ class Headline(Parser):
)
def id(self):
- hid = 'org-{0}'.format(sha1(self.title.encode()).hexdigest()[:10])
+ hid = "org-{0}".format(sha1(self.title.encode()).hexdigest()[:10])
if self.properties:
return self.properties.get("CUSTOM_ID", hid)
return hid
@@ -319,18 +321,18 @@ class Headline(Parser):
def toc(self):
b = ""
if self.keyword:
- b = b + "{0}".format(self.keyword)
+ b = b + '{0}'.format(self.keyword)
if self.priority:
- b = b + "{0}".format(self.priority)
+ b = b + '{0}'.format(self.priority)
b = b + self.inlinetext(self.title).to_html()
for tag in self.tags:
- b = b + "{0}".format(tag)
+ b = b + '{0}'.format(tag)
return b.strip()
def to_html(self):
- b = "
\n{0}\n
" + self.element = '\n{0}\n
' def add_child(self, node): self.children.append(node) @@ -453,7 +455,7 @@ class Src(Block): super(Src, self).__init__("src", params) self.language = language self.highlight_code = highlight - self.element = "\n{1}\n" + self.element = '
\n{1}\n' self.needparse = False self.escape = False self.parsed_nodes = () @@ -482,7 +484,7 @@ class Example(Src): class BlockResult(Parser): def __init__(self): super(BlockResult, self).__init__() - self.element = "
\n{0}\n" + self.element = '
\n{0}\n' @classmethod def match(cls, line): @@ -512,7 +514,7 @@ class ListItem(Parser): content = line status_match = LIST_STATUS_REGEXP.match(line) if status_match: - status, content = status_match[1], content[len("[ ] "):] + status, content = status_match[1], content[len("[ ] ") :] node = cls(status) node.add_child(node.inlinetext(content)) @@ -524,8 +526,7 @@ class ListItem(Parser): if self.checkbox == "HTML": if self.status == "X": - node = self.inlinetext( - '') + node = self.inlinetext('') else: node = self.inlinetext('') node.needparse = False @@ -641,7 +642,7 @@ class TableRow(Parser): self.is_sep = False self.header = header self.element = "