Store any incoming reactions PostInteraction.value

This commit is contained in:
Jamie Bliss 2023-11-09 11:08:12 -05:00
parent 83607779cd
commit 4c3cae337c
No known key found for this signature in database
3 changed files with 69 additions and 7 deletions

View file

@ -154,7 +154,7 @@ class PostInteraction(StatorModel):
)
# Used to store any interaction extra text value like the vote
# in the question/poll case
# in the question/poll case, or the reaction
value = models.CharField(max_length=50, blank=True, null=True)
# When the activity was originally created (as opposed to when we received it)
@ -392,6 +392,7 @@ class PostInteraction(StatorModel):
# Get the right type
if data["type"].lower() == "like":
type = cls.Types.like
value = data.get("content") or data.get("_misskey_reaction")
elif data["type"].lower() == "announce":
type = cls.Types.boost
elif (

View file

@ -1,4 +1,5 @@
import logging
from types import EllipsisType
from activities.models import (
Post,
@ -38,7 +39,7 @@ class PostService:
def __init__(self, post: Post):
self.post = post
def interact_as(self, identity: Identity, type: str):
def interact_as(self, identity: Identity, type: str, value: str | None = None):
"""
Performs an interaction on this Post
"""
@ -46,28 +47,39 @@ class PostService:
type=type,
identity=identity,
post=self.post,
value=value,
)[0]
if interaction.state not in PostInteractionStates.group_active():
interaction.transition_perform(PostInteractionStates.new)
self.post.calculate_stats()
def uninteract_as(self, identity, type):
def uninteract_as(self, identity, type, value: str | None | EllipsisType = ...):
"""
Undoes an interaction on this Post
"""
# Only search by value if it was actually given
additional_fields = {}
if value is not ...:
additional_fields["value"] = value
for interaction in PostInteraction.objects.filter(
type=type,
identity=identity,
post=self.post,
**additional_fields,
):
interaction.transition_perform(PostInteractionStates.undone)
self.post.calculate_stats()
def like_as(self, identity: Identity):
self.interact_as(identity, PostInteraction.Types.like)
def like_as(self, identity: Identity, reaction: str | None = None):
"""
Add a Like to the post, including reactions.
"""
self.interact_as(identity, PostInteraction.Types.like, value=reaction)
def unlike_as(self, identity: Identity):
self.uninteract_as(identity, PostInteraction.Types.like)
def unlike_as(self, identity: Identity, reaction: str | None = None):
self.uninteract_as(identity, PostInteraction.Types.like, value=reaction)
def boost_as(self, identity: Identity):
self.interact_as(identity, PostInteraction.Types.boost)

View file

@ -0,0 +1,49 @@
import pytest
from activities.models import Post, TimelineEvent
from activities.services import PostService
from users.models import Identity, InboxMessage
@pytest.mark.django_db
@pytest.mark.parametrize("local", [True, False])
@pytest.mark.parametrize("reaction", ["\U0001F607"])
def test_react_notification(
identity: Identity,
other_identity: Identity,
remote_identity: Identity,
stator,
local: bool,
reaction: str,
):
"""
Ensures that a like of a local Post notifies its author
"""
post = Post.create_local(author=identity, content="I love birds!")
if local:
PostService(post).like_as(other_identity, reaction)
else:
message = {
"id": "test",
"type": "Like",
"actor": remote_identity.actor_uri,
"object": post.object_uri,
"content": reaction,
}
InboxMessage.objects.create(message=message)
# Implement any blocks
interactor = other_identity if local else remote_identity
# Run stator thrice - to receive the post, make fanouts and then process them
stator.run_single_cycle()
stator.run_single_cycle()
stator.run_single_cycle()
# Verify we got an event
event = TimelineEvent.objects.filter(
type=TimelineEvent.Types.liked, identity=identity
).first()
assert event
assert event.subject_identity == interactor
assert event.subject_post_interaction.value == reaction