forked from forks/microblog.pub
Add DMs section
This commit is contained in:
parent
ccd77adcb6
commit
6b8a4d94b2
3 changed files with 190 additions and 1 deletions
171
app/admin.py
171
app/admin.py
|
@ -1,3 +1,5 @@
|
|||
from datetime import datetime
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter
|
||||
from fastapi import Cookie
|
||||
|
@ -8,7 +10,9 @@ from fastapi import UploadFile
|
|||
from fastapi.exceptions import HTTPException
|
||||
from fastapi.responses import RedirectResponse
|
||||
from loguru import logger
|
||||
from sqlalchemy import and_
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy import or_
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
|
@ -383,6 +387,173 @@ async def admin_inbox(
|
|||
)
|
||||
|
||||
|
||||
@router.get("/direct_messages")
|
||||
async def admin_direct_messages(
|
||||
request: Request,
|
||||
db_session: AsyncSession = Depends(get_db_session),
|
||||
cursor: str | None = None,
|
||||
) -> templates.TemplateResponse:
|
||||
inbox_convos = (
|
||||
(
|
||||
await db_session.execute(
|
||||
select(
|
||||
models.InboxObject.ap_context,
|
||||
models.InboxObject.actor_id,
|
||||
func.count(1).label("count"),
|
||||
func.max(models.InboxObject.ap_published_at).label(
|
||||
"most_recent_date"
|
||||
),
|
||||
)
|
||||
.where(
|
||||
models.InboxObject.visibility == ap.VisibilityEnum.DIRECT,
|
||||
models.InboxObject.ap_context.is_not(None),
|
||||
)
|
||||
.group_by(models.InboxObject.ap_context, models.InboxObject.actor_id)
|
||||
)
|
||||
)
|
||||
.unique()
|
||||
.all()
|
||||
)
|
||||
outbox_convos = (
|
||||
(
|
||||
await db_session.execute(
|
||||
select(
|
||||
models.OutboxObject.ap_context,
|
||||
func.count(1).label("count"),
|
||||
func.max(models.OutboxObject.ap_published_at).label(
|
||||
"most_recent_date"
|
||||
),
|
||||
)
|
||||
.where(
|
||||
models.OutboxObject.visibility == ap.VisibilityEnum.DIRECT,
|
||||
models.OutboxObject.ap_context.is_not(None),
|
||||
)
|
||||
.group_by(models.OutboxObject.ap_context)
|
||||
)
|
||||
)
|
||||
.unique()
|
||||
.all()
|
||||
)
|
||||
|
||||
convos = {}
|
||||
for inbox_convo in inbox_convos:
|
||||
if inbox_convo.ap_context not in convos:
|
||||
convos[inbox_convo.ap_context] = {
|
||||
"actor_ids": {inbox_convo.actor_id},
|
||||
"count": inbox_convo.count,
|
||||
"most_recent_from_inbox": inbox_convo.most_recent_date,
|
||||
"most_recent_from_outbox": datetime.min,
|
||||
}
|
||||
else:
|
||||
convos[inbox_convo.ap_context]["actor_ids"].add(inbox_convo.actor_id)
|
||||
convos[inbox_convo.ap_context]["count"] += inbox_convo.count
|
||||
convos[inbox_convo.ap_context]["most_recent_from_inbox"] = max(
|
||||
inbox_convo.most_recent_date,
|
||||
convos[inbox_convo.ap_context]["most_recent_from_inbox"],
|
||||
)
|
||||
|
||||
for outbox_convo in outbox_convos:
|
||||
if outbox_convo.ap_context not in convos:
|
||||
convos[outbox_convo.ap_context] = {
|
||||
"actor_ids": set(),
|
||||
"count": outbox_convo.count,
|
||||
"most_recent_from_inbox": datetime.min,
|
||||
"most_recent_from_outbox": outbox_convo.most_recent_date,
|
||||
}
|
||||
else:
|
||||
convos[outbox_convo.ap_context]["count"] += outbox_convo.count
|
||||
convos[outbox_convo.ap_context]["most_recent_from_outbox"] = max(
|
||||
outbox_convo.most_recent_date,
|
||||
convos[outbox_convo.ap_context]["most_recent_from_outbox"],
|
||||
)
|
||||
|
||||
convos_with_last_from_inbox = []
|
||||
convos_with_last_from_outbox = []
|
||||
for context, convo in convos.items():
|
||||
if convo["most_recent_from_inbox"] > convo["most_recent_from_outbox"]:
|
||||
convos_with_last_from_inbox.append(
|
||||
and_(
|
||||
models.InboxObject.ap_context == context,
|
||||
models.InboxObject.ap_published_at
|
||||
== convo["most_recent_from_inbox"],
|
||||
)
|
||||
)
|
||||
else:
|
||||
convos_with_last_from_outbox.append(
|
||||
and_(
|
||||
models.OutboxObject.ap_context == context,
|
||||
models.OutboxObject.ap_published_at
|
||||
== convo["most_recent_from_outbox"],
|
||||
)
|
||||
)
|
||||
last_from_inbox = (
|
||||
(
|
||||
await db_session.scalars(
|
||||
select(models.InboxObject)
|
||||
.where(or_(*convos_with_last_from_inbox))
|
||||
.options(
|
||||
joinedload(models.InboxObject.actor),
|
||||
)
|
||||
)
|
||||
)
|
||||
.unique()
|
||||
.all()
|
||||
)
|
||||
last_from_outbox = (
|
||||
(
|
||||
await db_session.scalars(
|
||||
select(models.OutboxObject)
|
||||
.where(or_(*convos_with_last_from_outbox))
|
||||
.options(
|
||||
joinedload(models.OutboxObject.outbox_object_attachments).options(
|
||||
joinedload(models.OutboxObjectAttachment.upload)
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
.unique()
|
||||
.all()
|
||||
)
|
||||
threads = []
|
||||
for anybox_object in sorted(
|
||||
last_from_inbox + last_from_outbox,
|
||||
key=lambda x: x.ap_published_at,
|
||||
reverse=True,
|
||||
):
|
||||
convo = convos[anybox_object.ap_context]
|
||||
actors = list(
|
||||
(
|
||||
await db_session.execute(
|
||||
select(models.Actor).where(models.Actor.id.in_(convo["actor_ids"]))
|
||||
)
|
||||
).scalars()
|
||||
)
|
||||
# If this message from outbox starts a thread with no replies, look
|
||||
# at the mentions
|
||||
if not actors and anybox_object.is_from_outbox:
|
||||
actors = ( # type: ignore
|
||||
await db_session.execute(
|
||||
select(models.Actor).where(
|
||||
models.Actor.ap_id.in_(
|
||||
mention["href"]
|
||||
for mention in anybox_object.tags
|
||||
if mention["type"] == "Mention"
|
||||
)
|
||||
)
|
||||
)
|
||||
).scalars()
|
||||
threads.append((anybox_object, convo, actors))
|
||||
|
||||
return await templates.render_template(
|
||||
db_session,
|
||||
request,
|
||||
"admin_direct_messages.html",
|
||||
{
|
||||
"threads": threads,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@router.get("/outbox")
|
||||
async def admin_outbox(
|
||||
request: Request,
|
||||
|
|
18
app/templates/admin_direct_messages.html
Normal file
18
app/templates/admin_direct_messages.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
{%- import "utils.html" as utils with context -%}
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block head %}
|
||||
<title>{{ local_actor.display_name }} - Direct messages</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% for anybox_object, convo, actors in threads %}
|
||||
<div class="actor-action">
|
||||
With {% for actor in actors %}
|
||||
<a href="">{{ actor.handle }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{{ utils.display_object(anybox_object) }}
|
||||
{% endfor %}
|
||||
{% endblock %}
|
|
@ -26,11 +26,11 @@
|
|||
<div style="margin-bottom:30px;padding: 0 20px;">
|
||||
<nav class="flexbox">
|
||||
<ul>
|
||||
<li>Admin</li>
|
||||
<li>{{ admin_link("index", "Public") }}</li>
|
||||
<li>{{ admin_link("admin_new", "New") }}</li>
|
||||
<li>{{ admin_link("admin_stream", "Stream") }}</li>
|
||||
<li>{{ admin_link("admin_inbox", "Inbox") }} / {{ admin_link("admin_outbox", "Outbox") }}</li>
|
||||
<li>{{ admin_link("admin_direct_messages", "DMs") }}</li>
|
||||
<li>{{ admin_link("get_notifications", "Notifications") }} {% if notifications_count %}({{ notifications_count }}){% endif %}</li>
|
||||
<li>{{ admin_link("get_lookup", "Lookup") }}</li>
|
||||
<li>{{ admin_link("admin_bookmarks", "Bookmarks") }}</li>
|
||||
|
|
Loading…
Reference in a new issue