post comment as ap

This commit is contained in:
Her Email 2023-11-19 15:19:49 -05:00 committed by Henri Dickson
parent 0f9493d6c1
commit a0225fd6ca
7 changed files with 89 additions and 117 deletions

View file

@ -111,11 +111,11 @@ class PodcastEpisode(Item):
def cover_image_url(self):
return self.cover_url or self.program.cover_image_url
def get_absolute_url_with_position(self, position=None):
def get_url_with_position(self, position=None):
return (
self.absolute_url
self.url
if position is None or position == ""
else f"{self.absolute_url}?position={position}"
else f"{self.url}?position={position}"
)
class Meta:

View file

@ -17,7 +17,7 @@ class Comment(Content):
@property
def ap_object(self):
return {
d = {
"id": self.absolute_url,
"type": "Comment",
"content": self.text,
@ -27,6 +27,10 @@ class Comment(Content):
"relatedWith": self.item.absolute_url,
"href": self.absolute_url,
}
if self.metadata.get("position"):
d["relatedWithItemPosition"] = self.metadata["position"]
d["relatedWithItemPositionType"] = "time"
return d
@classmethod
def update_by_ap_object(cls, owner, item, obj, post_id, visibility):
@ -42,6 +46,8 @@ class Comment(Content):
"created_time": datetime.fromisoformat(obj["published"]),
"edited_time": datetime.fromisoformat(obj["updated"]),
}
if obj.get("relatedWithItemPosition"):
d["metadata"] = {"position": obj["relatedWithItemPosition"]}
p, _ = cls.objects.update_or_create(owner=owner, item=item, defaults=d)
p.link_post_id(post_id)
return p
@ -65,7 +71,7 @@ class Comment(Content):
@property
def item_url(self):
if self.metadata.get("position"):
return self.item.get_absolute_url_with_position(self.metadata["position"])
return self.item.get_url_with_position(self.metadata["position"])
else:
return self.item.url

View file

@ -80,6 +80,7 @@ class ShelfMember(ListMember):
def update_by_ap_object(
cls, owner: APIdentity, item: Identity, obj: dict, post_id: int, visibility: int
):
# TODO check timestamp? (update may come in with inconsistent sequence)
if not obj:
cls.objects.filter(owner=owner, item=item).delete()
return

View file

@ -15,16 +15,7 @@ from .collection import (
user_liked_collection_list,
)
from .common import piece_delete
from .mark import (
comment,
like,
mark,
mark_log,
share_comment,
unlike,
user_mark_list,
wish,
)
from .mark import comment, like, mark, mark_log, unlike, user_mark_list, wish
from .post import piece_replies, post_like, post_replies, post_reply, post_unlike
from .profile import profile, user_calendar_data
from .review import ReviewFeed, review_edit, review_retrieve, user_review_list

View file

@ -13,12 +13,7 @@ from django.utils.translation import gettext_lazy as _
from catalog.models import *
from common.utils import AuthedHttpRequest, PageLinksGenerator, get_uuid_or_404
from mastodon.api import (
get_spoiler_text,
get_status_id_by_url,
get_visibility,
post_toot,
)
from mastodon.api import boost_toot
from takahe.utils import Takahe
from ..models import Comment, Mark, Piece, ShelfType, ShelfTypeNames, TagManager
@ -168,36 +163,6 @@ def mark(request: AuthedHttpRequest, item_uuid):
raise BadRequest()
def share_comment(user, item, text, visibility, shared_link=None, position=None):
post_error = False
status_id = get_status_id_by_url(shared_link)
link = (
item.get_absolute_url_with_position(position) if position else item.absolute_url
)
action_label = "评论" if text else "分享"
status = f"{action_label}{ItemCategory(item.category).label}{item.display_title}\n{link}\n\n{text}"
spoiler, status = get_spoiler_text(status, item)
try:
response = post_toot(
user.mastodon_site,
status,
get_visibility(visibility, user),
user.mastodon_token,
False,
status_id,
spoiler,
)
if response and response.status_code in [200, 201]:
j = response.json()
if "url" in j:
shared_link = j["url"]
except Exception as e:
if settings.DEBUG:
raise
post_error = True
return post_error, shared_link
@login_required
def mark_log(request: AuthedHttpRequest, item_uuid, log_id):
"""
@ -220,15 +185,7 @@ def comment(request: AuthedHttpRequest, item_uuid):
item = get_object_or_404(Item, uid=get_uuid_or_404(item_uuid))
if not item.class_name in ["podcastepisode", "tvepisode"]:
raise BadRequest("不支持评论此类型的条目")
# episode = None
# if item.class_name == "tvseason":
# try:
# episode = int(request.POST.get("episode", 0))
# except:
# episode = 0
# if episode <= 0:
# raise BadRequest("请输入正确的集数")
comment = Comment.objects.filter(owner=request.user, item=item).first()
comment = Comment.objects.filter(owner=request.user.identity, item=item).first()
if request.method == "GET":
return render(
request,
@ -256,49 +213,22 @@ def comment(request: AuthedHttpRequest, item_uuid):
if settings.DEBUG:
raise
position = None
share_to_mastodon = bool(request.POST.get("share_to_mastodon", default=False))
shared_link = comment.metadata.get("shared_link") if comment else None
post_error = False
if share_to_mastodon and request.user.mastodon_username:
post_error, shared_link = share_comment(
request.user, item, text, visibility, shared_link, position
)
Comment.objects.update_or_create(
owner=request.user,
item=item,
# metadata__episode=episode,
defaults={
"text": text,
"visibility": visibility,
"metadata": {
"shared_link": shared_link,
"position": position,
},
},
d = {"text": text, "visibility": visibility}
if position:
d["metadata"] = {"position": position}
comment, _ = Comment.objects.update_or_create(
owner=request.user.identity, item=item, defaults=d
)
# if comment:
# comment.visibility = visibility
# comment.text = text
# comment.metadata["position"] = position
# comment.metadata["episode"] = episode
# if shared_link:
# comment.metadata["shared_link"] = shared_link
# comment.save()
# else:
# comment = Comment.objects.create(
# owner=request.user,
# item=item,
# text=text,
# visibility=visibility,
# metadata={
# "shared_link": shared_link,
# "position": position,
# "episode": episode,
# },
# )
if post_error:
return render_relogin(request)
post = Takahe.post_comment(comment, False)
share_to_mastodon = bool(request.POST.get("share_to_mastodon", default=False))
if post and share_to_mastodon and request.user.mastodon_username:
boost_toot(
request.user.mastodon_site,
request.user.mastodon_token,
post.url,
)
# if post_error:
# return render_relogin(request)
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
raise BadRequest()

View file

@ -18,6 +18,7 @@ _supported_ap_catalog_item_types = [
"Album",
"Game",
"Podcast",
"PodcastEpisode",
"Performance",
"PerformanceProduction",
]
@ -30,14 +31,12 @@ _supported_ap_journal_types = {
}
def _parse_item_links(objects):
def _parse_items(objects):
logger.debug(f"Parsing item links from {objects}")
if not objects:
return []
objs = objects if isinstance(objects, list) else [objects]
items = [
obj["href"] for obj in objs if obj["type"] in _supported_ap_catalog_item_types
]
items = [obj for obj in objs if obj["type"] in _supported_ap_catalog_item_types]
return items
@ -55,8 +54,14 @@ def _parse_piece_objects(objects):
return pieces
def _get_or_create_item_by_ap_url(url):
logger.debug(f"Fetching item by ap from {url}")
def _get_or_create_item(item_obj):
logger.debug(f"Fetching item by ap from {item_obj}")
typ = item_obj["type"]
url = item_obj["href"]
if typ in ["TVEpisode", "PodcastEpisode"]:
# TODO support episode item
# match and fetch parent item first
return None
site = SiteManager.get_site_by_url(url)
if not site:
return None
@ -75,13 +80,13 @@ def _get_visibility(post_visibility):
return 0
def _update_or_create_post(pk, obj):
def post_fetched(pk, obj):
post = Post.objects.get(pk=pk)
owner = Takahe.get_or_create_remote_apidentity(post.author)
if not post.type_data:
logger.warning(f"Post {post} has no type_data")
return
items = _parse_item_links(post.type_data["object"]["tag"])
items = _parse_items(post.type_data["object"]["tag"])
pieces = _parse_piece_objects(post.type_data["object"]["relatedWith"])
logger.info(f"Post {post} has items {items} and pieces {pieces}")
if len(items) == 0:
@ -90,20 +95,15 @@ def _update_or_create_post(pk, obj):
elif len(items) > 1:
logger.warning(f"Post {post} has more than one remote item")
return
remote_url = items[0]
item = _get_or_create_item_by_ap_url(remote_url)
item = _get_or_create_item(items[0])
if not item:
logger.warning(f"Post {post} has no local item")
logger.warning(f"Post {post} has no local item matched or created")
return
for p in pieces:
cls = _supported_ap_journal_types[p["type"]]
cls.update_by_ap_object(owner, item, p, pk, _get_visibility(post.visibility))
def post_fetched(pk, obj):
_update_or_create_post(pk, obj)
def post_deleted(pk, obj):
Piece.objects.filter(posts__id=pk, local=False).delete()

View file

@ -397,6 +397,50 @@ class Takahe:
def delete_posts(post_pks):
Post.objects.filter(pk__in=post_pks).update(state="deleted")
@staticmethod
def post_comment(comment, share_as_new_post: bool) -> Post | None:
from catalog.common import ItemCategory
user = comment.owner.user
category = str(ItemCategory(comment.item.category).label)
tags = (
"\n" + user.preference.mastodon_append_tag.replace("[category]", category)
if user.preference.mastodon_append_tag
else ""
)
item_link = f"{settings.SITE_INFO['site_url']}/~neodb~{comment.item_url}"
action_label = "评论" if comment.text else "分享"
pre_conetent = f'{action_label}{category}<a href="{item_link}">《{comment.item.display_title}》</a><br>'
content = f"{comment.text}\n{tags}"
data = {
"object": {
"tag": [comment.item.ap_object_ref],
"relatedWith": [comment.ap_object],
}
}
if comment.visibility == 1:
v = Takahe.Visibilities.followers
elif comment.visibility == 2:
v = Takahe.Visibilities.mentioned
elif user.preference.mastodon_publish_public:
v = Takahe.Visibilities.public
else:
v = Takahe.Visibilities.unlisted
existing_post = None if share_as_new_post else comment.latest_post
post = Takahe.post( # TODO post as Article?
comment.owner.pk,
pre_conetent,
content,
v,
data,
existing_post.pk if existing_post else None,
comment.created_time,
)
if not post:
return
comment.link_post(post)
return post
@staticmethod
def post_review(review, share_as_new_post: bool) -> Post | None:
from catalog.common import ItemCategory