Mention and notify users when creating a status

This commit is contained in:
Mouse Reeve 2020-11-01 10:13:51 -08:00
parent 4684a83e6f
commit 0cf1838276
6 changed files with 83 additions and 12 deletions

View File

@ -3,8 +3,9 @@ import inspect
import sys import sys
from .base_activity import ActivityEncoder, Image, PublicKey, Signature from .base_activity import ActivityEncoder, Image, PublicKey, Signature
from .base_activity import Link, Mention
from .note import Note, GeneratedNote, Article, Comment, Review, Quotation from .note import Note, GeneratedNote, Article, Comment, Review, Quotation
from .note import Tombstone, Link from .note import Tombstone
from .interaction import Boost, Like from .interaction import Boost, Like
from .ordered_collection import OrderedCollection, OrderedCollectionPage from .ordered_collection import OrderedCollection, OrderedCollectionPage
from .person import Person from .person import Person

View File

@ -21,6 +21,19 @@ class Image:
type: str = 'Image' type: str = 'Image'
@dataclass
class Link():
''' for tagging a book in a status '''
href: str
name: str
type: str = 'Link'
@dataclass
class Mention(Link):
''' a subtype of Link for mentioning an actor '''
type: str = 'Mention'
@dataclass @dataclass
class PublicKey: class PublicKey:
''' public key block ''' ''' public key block '''

View File

@ -2,7 +2,7 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Dict, List from typing import Dict, List
from .base_activity import ActivityObject, Image from .base_activity import ActivityObject, Image, Link
@dataclass(init=False) @dataclass(init=False)
class Tombstone(ActivityObject): class Tombstone(ActivityObject):
@ -20,6 +20,7 @@ class Note(ActivityObject):
inReplyTo: str inReplyTo: str
published: str published: str
attributedTo: str attributedTo: str
tag: List[Link]
to: List[str] to: List[str]
cc: List[str] cc: List[str]
content: str content: str
@ -36,17 +37,9 @@ class Article(Note):
type: str = 'Article' type: str = 'Article'
@dataclass
class Link():
''' for tagging a book in a status '''
href: str
name: str
type: str = 'Link'
@dataclass(init=False) @dataclass(init=False)
class GeneratedNote(Note): class GeneratedNote(Note):
''' just a re-typed note ''' ''' just a re-typed note '''
tag: List[Link]
type: str = 'GeneratedNote' type: str = 'GeneratedNote'

View File

@ -0,0 +1,26 @@
# Generated by Django 3.0.7 on 2020-11-01 17:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('bookwyrm', '0062_auto_20201031_1936'),
]
operations = [
migrations.RemoveConstraint(
model_name='notification',
name='notification_type_valid',
),
migrations.AlterField(
model_name='notification',
name='notification_type',
field=models.CharField(choices=[('FAVORITE', 'Favorite'), ('REPLY', 'Reply'), ('MENTION', 'Mention'), ('TAG', 'Tag'), ('FOLLOW', 'Follow'), ('FOLLOW_REQUEST', 'Follow Request'), ('BOOST', 'Boost'), ('IMPORT', 'Import')], max_length=255),
),
migrations.AddConstraint(
model_name='notification',
constraint=models.CheckConstraint(check=models.Q(notification_type__in=['FAVORITE', 'REPLY', 'MENTION', 'TAG', 'FOLLOW', 'FOLLOW_REQUEST', 'BOOST', 'IMPORT']), name='notification_type_valid'),
),
]

View File

@ -59,6 +59,7 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
@property @property
def ap_tag(self): def ap_tag(self):
''' references to books and/or users '''
tags = [] tags = []
for book in self.mention_books.all(): for book in self.mention_books.all():
tags.append(activitypub.Link( tags.append(activitypub.Link(
@ -66,6 +67,11 @@ class Status(OrderedCollectionPageMixin, BookWyrmModel):
name=book.title, name=book.title,
type='Book' type='Book'
)) ))
for user in self.mention_users.all():
tags.append(activitypub.Mention(
href=user.remote_id,
name=user.username,
))
return tags return tags
shared_mappings = [ shared_mappings = [
@ -270,7 +276,7 @@ class ReadThrough(BookWyrmModel):
NotificationType = models.TextChoices( NotificationType = models.TextChoices(
'NotificationType', 'NotificationType',
'FAVORITE REPLY TAG FOLLOW FOLLOW_REQUEST BOOST IMPORT') 'FAVORITE REPLY MENTION TAG FOLLOW FOLLOW_REQUEST BOOST IMPORT')
class Notification(BookWyrmModel): class Notification(BookWyrmModel):
''' you've been tagged, liked, followed, etc ''' ''' you've been tagged, liked, followed, etc '''

View File

@ -1,5 +1,6 @@
''' handles all the activity coming out of the server ''' ''' handles all the activity coming out of the server '''
from datetime import datetime from datetime import datetime
import re
from django.db import IntegrityError, transaction from django.db import IntegrityError, transaction
from django.http import HttpResponseNotFound, JsonResponse from django.http import HttpResponseNotFound, JsonResponse
@ -13,6 +14,7 @@ from bookwyrm.status import create_tag, create_notification
from bookwyrm.status import create_generated_note from bookwyrm.status import create_generated_note
from bookwyrm.status import delete_status from bookwyrm.status import delete_status
from bookwyrm.remote_user import get_or_create_remote_user from bookwyrm.remote_user import get_or_create_remote_user
from bookwyrm.settings import DOMAIN
@csrf_exempt @csrf_exempt
@ -211,7 +213,37 @@ def handle_status(user, form):
''' generic handler for statuses ''' ''' generic handler for statuses '''
status = form.save() status = form.save()
# notify reply parent or (TODO) tagged users # inspect the text for user tags
text = status.content
matches = re.finditer(
r'\W@[a-zA-Z_\-\.0-9]+(@[a-z-A-Z0-9_\-]+.[a-z]+)?',
text
)
for match in matches:
username = match.group().strip().split('@')[1:]
if len(username) == 1:
# this looks like a local user (@user), fill in the domain
username.append(DOMAIN)
username = '@'.join(username)
try:
mention_user = models.User.objects.get(username=username)
except models.User.DoesNotExist:
# we can ignore users we don't know about
continue
# add them to status mentions fk
status.mention_users.add(mention_user)
# create notification if the mentioned user is local
if mention_user.local:
create_notification(
mention_user,
'MENTION',
related_user=user,
related_status=status
)
status.save()
# notify reply parent or tagged users
if status.reply_parent and status.reply_parent.user.local: if status.reply_parent and status.reply_parent.user.local:
create_notification( create_notification(
status.reply_parent.user, status.reply_parent.user,