133 lines
3.4 KiB
Python
133 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import httpx
|
|
import traceback
|
|
|
|
from aputils import Message, ObjectType
|
|
from basgi import Request
|
|
from collections.abc import Awaitable, Callable
|
|
|
|
from . import TRANS
|
|
from .database.objects import Follow, User
|
|
|
|
|
|
ProcessHandler = Callable[[Request, Message, User], Awaitable[None]]
|
|
PROCESSORS: dict[ObjectType, ProcessHandler] = {}
|
|
|
|
|
|
def register(*objtypes: ObjectType) -> Callable[[ProcessHandler], ProcessHandler]:
|
|
def wrapper(func: ProcessHandler) -> ProcessHandler:
|
|
for objtype in objtypes:
|
|
PROCESSORS[objtype] = func
|
|
|
|
return func
|
|
|
|
return wrapper
|
|
|
|
|
|
@register(ObjectType.FOLLOW)
|
|
async def handle_follow(request: Request, message: Message, user: User) -> None:
|
|
if message.object_id != user.actor:
|
|
logging.warning(
|
|
"Follow object does not match actor: msg=%s, actor=%s",
|
|
message.object_id,
|
|
user.actor
|
|
)
|
|
|
|
with request.app.state.database.session(False) as s:
|
|
with s.transaction():
|
|
if (follow := s.get_follow(request.state.user, user)) is not None:
|
|
follow.followid = message.id
|
|
follow = s.update_row(follow)
|
|
|
|
else:
|
|
follow = s.insert_row(Follow.new_from_users(
|
|
s, request.state.user, user, message.id, not user.locked
|
|
))
|
|
|
|
if not follow.accepted:
|
|
return
|
|
|
|
msg = Message.new(ObjectType.ACCEPT if follow.accepted else ObjectType.REJECT, {
|
|
'id': f'https://{request.app.state.config.web_host}/activity/follow/{follow.id}',
|
|
'to': [request.state.user.actor],
|
|
'actor': user.actor,
|
|
'object': {
|
|
'id': follow.followid,
|
|
'type': 'Follow',
|
|
'object': user.actor,
|
|
'actor': request.state.user.actor
|
|
}
|
|
})
|
|
|
|
await request.app.client.post(request.state.user.inbox, msg, signer = user.signer)
|
|
|
|
|
|
@register(ObjectType.UNDO)
|
|
async def handle_undo(request: Request, message: Message, user: User) -> None:
|
|
if isinstance(message.object, str):
|
|
obj = await request.app.client.fetch_json(
|
|
message.object,
|
|
Message,
|
|
signer = user.signer
|
|
)
|
|
|
|
else:
|
|
obj = Message.parse(message.object)
|
|
|
|
if obj.type not in ObjectType.Actor:
|
|
print(repr(obj.type))
|
|
return
|
|
|
|
if obj.object_id != user.actor:
|
|
logging.warning(
|
|
"Follow object does not match actor: msg=%s, actor=%s",
|
|
message.object_id,
|
|
user.actor
|
|
)
|
|
|
|
with request.app.state.database.session(False) as s:
|
|
if (follow := s.get_follow_by_id(obj.id)) is None:
|
|
logging.warning("Could not find follow with id: %s", obj.id)
|
|
return
|
|
|
|
if follow.sourceid != request.state.user.id or follow.targetid != user.id:
|
|
logging.warning(TRANS.fetch(
|
|
"error", "mismatched-ids",
|
|
fsid = follow.sourceid,
|
|
ftid = follow.targetid,
|
|
sid = request.state.user.id,
|
|
tid = user.id
|
|
))
|
|
|
|
return
|
|
|
|
with s.transaction():
|
|
s.delete_row(follow)
|
|
|
|
|
|
async def process_message(request: Request, message: Message, user: User) -> None:
|
|
print(f"process_message: {user.handle}, message:", message.to_json(4), flush = True)
|
|
|
|
if request.state.user.actor != message.actor_id:
|
|
logging.warning(
|
|
"Message actor and signature keyid do not match: msg=%s, actor=%s",
|
|
message.actor_id,
|
|
request.state.user.actor
|
|
)
|
|
|
|
try:
|
|
handler = PROCESSORS[message.type]
|
|
await handler(request, message, user)
|
|
|
|
except httpx.HTTPStatusError as error:
|
|
logging.error("Error when sending request: %i", error.response.status_code)
|
|
logging.error(error.response.read())
|
|
|
|
except KeyError:
|
|
logging.debug("Unhandled message type: %s", message.type)
|
|
|
|
except Exception:
|
|
traceback.print_exc()
|