Add dm and reply notifications to speaker endpoint

merge-requests/21/head
Bob Mottram 2021-03-09 13:52:02 +00:00
parent 6a388a7103
commit 66056face5
5 changed files with 94 additions and 74 deletions

View File

@ -58,12 +58,12 @@ from filters import isFiltered
from utils import updateAnnounceCollection from utils import updateAnnounceCollection
from utils import undoAnnounceCollectionEntry from utils import undoAnnounceCollectionEntry
from utils import dangerousMarkup from utils import dangerousMarkup
from utils import isDM
from utils import isReply
from httpsig import messageContentDigest from httpsig import messageContentDigest
from posts import createDirectMessagePost from posts import createDirectMessagePost
from posts import validContentWarning from posts import validContentWarning
from posts import downloadAnnounce from posts import downloadAnnounce
from posts import isDM
from posts import isReply
from posts import isMuted from posts import isMuted
from posts import isImageMedia from posts import isImageMedia
from posts import sendSignedJson from posts import sendSignedJson
@ -1408,7 +1408,9 @@ def _receiveAnnounce(recentPostsCache: {},
if isRecentPost(postJsonObject): if isRecentPost(postJsonObject):
if not os.path.isfile(postFilename + '.tts'): if not os.path.isfile(postFilename + '.tts'):
updateSpeaker(baseDir, nickname, domain, domainFull = getFullDomain(domain, port)
updateSpeaker(baseDir, httpPrefix,
nickname, domain, domainFull,
postJsonObject, personCache, postJsonObject, personCache,
translate, lookupActor) translate, lookupActor)
ttsFile = open(postFilename + '.tts', "w+") ttsFile = open(postFilename + '.tts', "w+")
@ -2491,7 +2493,9 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
else: else:
if boxname == 'inbox': if boxname == 'inbox':
if isRecentPost(postJsonObject): if isRecentPost(postJsonObject):
updateSpeaker(baseDir, nickname, domain, domainFull = getFullDomain(domain, port)
updateSpeaker(baseDir, httpPrefix,
nickname, domain, domainFull,
postJsonObject, personCache, postJsonObject, personCache,
translate, None) translate, None)
if not unitTest: if not unitTest:

View File

@ -2921,34 +2921,6 @@ def createModeration(baseDir: str, nickname: str, domain: str, port: int,
return boxItems return boxItems
def isDM(postJsonObject: {}) -> bool:
"""Returns true if the given post is a DM
"""
if postJsonObject['type'] != 'Create':
return False
if not postJsonObject.get('object'):
return False
if not isinstance(postJsonObject['object'], dict):
return False
if postJsonObject['object']['type'] != 'Note' and \
postJsonObject['object']['type'] != 'Patch' and \
postJsonObject['object']['type'] != 'EncryptedMessage' and \
postJsonObject['object']['type'] != 'Article':
return False
if postJsonObject['object'].get('moderationStatus'):
return False
fields = ('to', 'cc')
for f in fields:
if not postJsonObject['object'].get(f):
continue
for toAddress in postJsonObject['object'][f]:
if toAddress.endswith('#Public'):
return False
if toAddress.endswith('followers'):
return False
return True
def isImageMedia(session, baseDir: str, httpPrefix: str, def isImageMedia(session, baseDir: str, httpPrefix: str,
nickname: str, domain: str, nickname: str, domain: str,
postJsonObject: {}, translate: {}, postJsonObject: {}, translate: {},
@ -2992,40 +2964,6 @@ def isImageMedia(session, baseDir: str, httpPrefix: str,
return False return False
def isReply(postJsonObject: {}, actor: str) -> bool:
"""Returns true if the given post is a reply to the given actor
"""
if postJsonObject['type'] != 'Create':
return False
if not postJsonObject.get('object'):
return False
if not isinstance(postJsonObject['object'], dict):
return False
if postJsonObject['object'].get('moderationStatus'):
return False
if postJsonObject['object']['type'] != 'Note' and \
postJsonObject['object']['type'] != 'EncryptedMessage' and \
postJsonObject['object']['type'] != 'Article':
return False
if postJsonObject['object'].get('inReplyTo'):
if isinstance(postJsonObject['object']['inReplyTo'], str):
if postJsonObject['object']['inReplyTo'].startswith(actor):
return True
if not postJsonObject['object'].get('tag'):
return False
if not isinstance(postJsonObject['object']['tag'], list):
return False
for tag in postJsonObject['object']['tag']:
if not tag.get('type'):
continue
if tag['type'] == 'Mention':
if not tag.get('href'):
continue
if actor in tag['href']:
return True
return False
def _addPostStringToTimeline(postStr: str, boxname: str, def _addPostStringToTimeline(postStr: str, boxname: str,
postsInBox: [], boxActor: str) -> bool: postsInBox: [], boxActor: str) -> bool:
""" is this a valid timeline post? """ is this a valid timeline post?

View File

@ -12,6 +12,8 @@ import random
import urllib.parse import urllib.parse
from auth import createBasicAuthHeader from auth import createBasicAuthHeader
from session import getJson from session import getJson
from utils import isDM
from utils import isReply
from utils import camelCaseSplit from utils import camelCaseSplit
from utils import getDomainFromActor from utils import getDomainFromActor
from utils import getNicknameFromActor from utils import getNicknameFromActor
@ -263,7 +265,8 @@ def getSpeakerFromServer(baseDir: str, session,
def _speakerEndpointJson(displayName: str, summary: str, def _speakerEndpointJson(displayName: str, summary: str,
content: str, imageDescription: str, content: str, imageDescription: str,
links: [], gender: str, postId: str) -> {}: links: [], gender: str, postId: str,
postDM: bool, postReply: bool) -> {}:
"""Returns a json endpoint for the TTS speaker """Returns a json endpoint for the TTS speaker
""" """
speakerJson = { speakerJson = {
@ -272,7 +275,11 @@ def _speakerEndpointJson(displayName: str, summary: str,
"say": content, "say": content,
"imageDescription": imageDescription, "imageDescription": imageDescription,
"detectedLinks": links, "detectedLinks": links,
"id": postId "id": postId,
"notify": {
"dm": postDM,
"reply": postReply
}
} }
if gender: if gender:
speakerJson['gender'] = gender speakerJson['gender'] = gender
@ -360,7 +367,8 @@ def getSSMLbox(baseDir: str, path: str,
instanceTitle, gender) instanceTitle, gender)
def _postToSpeakerJson(baseDir: str, nickname: str, domain: str, def _postToSpeakerJson(baseDir: str, httpPrefix: str,
nickname: str, domain: str, domainFull: str,
postJsonObject: {}, personCache: {}, postJsonObject: {}, personCache: {},
translate: {}, announcingActor: str) -> {}: translate: {}, announcingActor: str) -> {}:
"""Converts an ActivityPub post into some Json containing """Converts an ActivityPub post into some Json containing
@ -429,19 +437,26 @@ def _postToSpeakerJson(baseDir: str, nickname: str, domain: str,
postId = None postId = None
if postJsonObject['object'].get('id'): if postJsonObject['object'].get('id'):
postId = postJsonObject['object']['id'] postId = postJsonObject['object']['id']
actor = httpPrefix + '://' + domainFull + '/users/' + nickname
postDM = isDM(postJsonObject)
postReply = isReply(postJsonObject, actor)
return _speakerEndpointJson(speakerName, summary, return _speakerEndpointJson(speakerName, summary,
content, imageDescription, content, imageDescription,
detectedLinks, gender, postId) detectedLinks, gender, postId,
postDM, postReply)
def updateSpeaker(baseDir: str, nickname: str, domain: str, def updateSpeaker(baseDir: str, httpPrefix: str,
nickname: str, domain: str, domainFull: str,
postJsonObject: {}, personCache: {}, postJsonObject: {}, personCache: {},
translate: {}, announcingActor: str) -> None: translate: {}, announcingActor: str) -> None:
""" Generates a json file which can be used for TTS announcement """ Generates a json file which can be used for TTS announcement
of incoming inbox posts of incoming inbox posts
""" """
speakerJson = \ speakerJson = \
_postToSpeakerJson(baseDir, nickname, domain, _postToSpeakerJson(baseDir, httpPrefix,
nickname, domain, domainFull,
postJsonObject, personCache, postJsonObject, personCache,
translate, announcingActor) translate, announcingActor)
speakerFilename = \ speakerFilename = \

View File

@ -2068,3 +2068,65 @@ def rejectPostId(baseDir: str, nickname: str, domain: str,
if rejectFile: if rejectFile:
rejectFile.write('\n') rejectFile.write('\n')
rejectFile.close() rejectFile.close()
def isDM(postJsonObject: {}) -> bool:
"""Returns true if the given post is a DM
"""
if postJsonObject['type'] != 'Create':
return False
if not postJsonObject.get('object'):
return False
if not isinstance(postJsonObject['object'], dict):
return False
if postJsonObject['object']['type'] != 'Note' and \
postJsonObject['object']['type'] != 'Patch' and \
postJsonObject['object']['type'] != 'EncryptedMessage' and \
postJsonObject['object']['type'] != 'Article':
return False
if postJsonObject['object'].get('moderationStatus'):
return False
fields = ('to', 'cc')
for f in fields:
if not postJsonObject['object'].get(f):
continue
for toAddress in postJsonObject['object'][f]:
if toAddress.endswith('#Public'):
return False
if toAddress.endswith('followers'):
return False
return True
def isReply(postJsonObject: {}, actor: str) -> bool:
"""Returns true if the given post is a reply to the given actor
"""
if postJsonObject['type'] != 'Create':
return False
if not postJsonObject.get('object'):
return False
if not isinstance(postJsonObject['object'], dict):
return False
if postJsonObject['object'].get('moderationStatus'):
return False
if postJsonObject['object']['type'] != 'Note' and \
postJsonObject['object']['type'] != 'EncryptedMessage' and \
postJsonObject['object']['type'] != 'Article':
return False
if postJsonObject['object'].get('inReplyTo'):
if isinstance(postJsonObject['object']['inReplyTo'], str):
if postJsonObject['object']['inReplyTo'].startswith(actor):
return True
if not postJsonObject['object'].get('tag'):
return False
if not isinstance(postJsonObject['object']['tag'], list):
return False
for tag in postJsonObject['object']['tag']:
if not tag.get('type'):
continue
if tag['type'] == 'Mention':
if not tag.get('href'):
continue
if actor in tag['href']:
return True
return False

View File

@ -19,9 +19,9 @@ from like import noOfLikes
from follow import isFollowingActor from follow import isFollowingActor
from posts import postIsMuted from posts import postIsMuted
from posts import getPersonBox from posts import getPersonBox
from posts import isDM
from posts import downloadAnnounce from posts import downloadAnnounce
from posts import populateRepliesJson from posts import populateRepliesJson
from utils import isDM
from utils import rejectPostId from utils import rejectPostId
from utils import isRecentPost from utils import isRecentPost
from utils import getConfigParam from utils import getConfigParam
@ -1304,7 +1304,8 @@ def individualPostAsHtml(allowDownloads: bool,
postJsonObject['id']) postJsonObject['id'])
if announceFilename and postJsonObject.get('actor'): if announceFilename and postJsonObject.get('actor'):
if not os.path.isfile(announceFilename + '.tts'): if not os.path.isfile(announceFilename + '.tts'):
updateSpeaker(baseDir, nickname, domain, updateSpeaker(baseDir, httpPrefix,
nickname, domain, domainFull,
postJsonObject, personCache, postJsonObject, personCache,
translate, postJsonObject['actor']) translate, postJsonObject['actor'])
ttsFile = open(announceFilename + '.tts', "w+") ttsFile = open(announceFilename + '.tts', "w+")