From 66056face50e61c800da3b31a6d7e7466d9bd434 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Mar 2021 13:52:02 +0000 Subject: [PATCH 1/5] Add dm and reply notifications to speaker endpoint --- inbox.py | 12 ++++++---- posts.py | 62 -------------------------------------------------- speaker.py | 27 +++++++++++++++++----- utils.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ webapp_post.py | 5 ++-- 5 files changed, 94 insertions(+), 74 deletions(-) diff --git a/inbox.py b/inbox.py index 131647ef9..8acba772d 100644 --- a/inbox.py +++ b/inbox.py @@ -58,12 +58,12 @@ from filters import isFiltered from utils import updateAnnounceCollection from utils import undoAnnounceCollectionEntry from utils import dangerousMarkup +from utils import isDM +from utils import isReply from httpsig import messageContentDigest from posts import createDirectMessagePost from posts import validContentWarning from posts import downloadAnnounce -from posts import isDM -from posts import isReply from posts import isMuted from posts import isImageMedia from posts import sendSignedJson @@ -1408,7 +1408,9 @@ def _receiveAnnounce(recentPostsCache: {}, if isRecentPost(postJsonObject): if not os.path.isfile(postFilename + '.tts'): - updateSpeaker(baseDir, nickname, domain, + domainFull = getFullDomain(domain, port) + updateSpeaker(baseDir, httpPrefix, + nickname, domain, domainFull, postJsonObject, personCache, translate, lookupActor) ttsFile = open(postFilename + '.tts', "w+") @@ -2491,7 +2493,9 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, else: if boxname == 'inbox': if isRecentPost(postJsonObject): - updateSpeaker(baseDir, nickname, domain, + domainFull = getFullDomain(domain, port) + updateSpeaker(baseDir, httpPrefix, + nickname, domain, domainFull, postJsonObject, personCache, translate, None) if not unitTest: diff --git a/posts.py b/posts.py index 33959a7cc..fcfe3bce4 100644 --- a/posts.py +++ b/posts.py @@ -2921,34 +2921,6 @@ def createModeration(baseDir: str, nickname: str, domain: str, port: int, 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, nickname: str, domain: str, postJsonObject: {}, translate: {}, @@ -2992,40 +2964,6 @@ def isImageMedia(session, baseDir: str, httpPrefix: str, 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, postsInBox: [], boxActor: str) -> bool: """ is this a valid timeline post? diff --git a/speaker.py b/speaker.py index 2b6ab90cc..b9314e36e 100644 --- a/speaker.py +++ b/speaker.py @@ -12,6 +12,8 @@ import random import urllib.parse from auth import createBasicAuthHeader from session import getJson +from utils import isDM +from utils import isReply from utils import camelCaseSplit from utils import getDomainFromActor from utils import getNicknameFromActor @@ -263,7 +265,8 @@ def getSpeakerFromServer(baseDir: str, session, def _speakerEndpointJson(displayName: str, summary: 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 """ speakerJson = { @@ -272,7 +275,11 @@ def _speakerEndpointJson(displayName: str, summary: str, "say": content, "imageDescription": imageDescription, "detectedLinks": links, - "id": postId + "id": postId, + "notify": { + "dm": postDM, + "reply": postReply + } } if gender: speakerJson['gender'] = gender @@ -360,7 +367,8 @@ def getSSMLbox(baseDir: str, path: str, instanceTitle, gender) -def _postToSpeakerJson(baseDir: str, nickname: str, domain: str, +def _postToSpeakerJson(baseDir: str, httpPrefix: str, + nickname: str, domain: str, domainFull: str, postJsonObject: {}, personCache: {}, translate: {}, announcingActor: str) -> {}: """Converts an ActivityPub post into some Json containing @@ -429,19 +437,26 @@ def _postToSpeakerJson(baseDir: str, nickname: str, domain: str, postId = None if postJsonObject['object'].get('id'): postId = postJsonObject['object']['id'] + + actor = httpPrefix + '://' + domainFull + '/users/' + nickname + postDM = isDM(postJsonObject) + postReply = isReply(postJsonObject, actor) return _speakerEndpointJson(speakerName, summary, 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: {}, translate: {}, announcingActor: str) -> None: """ Generates a json file which can be used for TTS announcement of incoming inbox posts """ speakerJson = \ - _postToSpeakerJson(baseDir, nickname, domain, + _postToSpeakerJson(baseDir, httpPrefix, + nickname, domain, domainFull, postJsonObject, personCache, translate, announcingActor) speakerFilename = \ diff --git a/utils.py b/utils.py index 57d41662f..1a977d692 100644 --- a/utils.py +++ b/utils.py @@ -2068,3 +2068,65 @@ def rejectPostId(baseDir: str, nickname: str, domain: str, if rejectFile: rejectFile.write('\n') 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 diff --git a/webapp_post.py b/webapp_post.py index 532f535d2..aca73ad33 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -19,9 +19,9 @@ from like import noOfLikes from follow import isFollowingActor from posts import postIsMuted from posts import getPersonBox -from posts import isDM from posts import downloadAnnounce from posts import populateRepliesJson +from utils import isDM from utils import rejectPostId from utils import isRecentPost from utils import getConfigParam @@ -1304,7 +1304,8 @@ def individualPostAsHtml(allowDownloads: bool, postJsonObject['id']) if announceFilename and postJsonObject.get('actor'): if not os.path.isfile(announceFilename + '.tts'): - updateSpeaker(baseDir, nickname, domain, + updateSpeaker(baseDir, httpPrefix, + nickname, domain, domainFull, postJsonObject, personCache, translate, postJsonObject['actor']) ttsFile = open(announceFilename + '.tts', "w+") From 28a6174e963d4638748607c7bbf20026d5cd49a9 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Mar 2021 15:01:35 +0000 Subject: [PATCH 2/5] Notification flags on speaker endpoint --- daemon.py | 3 +-- scripts/epicyon-notification | 4 ++-- speaker.py | 28 ++++++++++++++++++++++++---- 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/daemon.py b/daemon.py index 5ea50f7ff..60642e4d1 100644 --- a/daemon.py +++ b/daemon.py @@ -4853,8 +4853,7 @@ class PubServer(BaseHTTPRequestHandler): # only receive DMs from accounts you follow followDMsFilename = \ baseDir + '/accounts/' + \ - nickname + '@' + domain + \ - '/.followDMs' + nickname + '@' + domain + '/.followDMs' if onFinalWelcomeScreen: # initial default setting created via # the welcome screen diff --git a/scripts/epicyon-notification b/scripts/epicyon-notification index cdeb716e6..5b4f4ec89 100755 --- a/scripts/epicyon-notification +++ b/scripts/epicyon-notification @@ -209,9 +209,9 @@ function notifications { if [[ "$epicyonLikeFileContent" == *':'* ]]; then epicyonLikeMessage="Epicyon: $epicyonLikeFileContent" fi - "${PROJECT_NAME}-notification" -u "$USERNAME" -s "Epicyon" -m "$epicyonLikeMessage" --sensitive yes + sendNotification "$USERNAME" "Epicyon" "$epicyonLikeMessage" echo "##sent##" > "$epicyonLikeFile" - chown ${PROJECT_NAME}:${PROJECT_NAME} "$epicyonLkeFile" + chown ${PROJECT_NAME}:${PROJECT_NAME} "$epicyonLikeFile" fi fi diff --git a/speaker.py b/speaker.py index b9314e36e..a1c4b5df9 100644 --- a/speaker.py +++ b/speaker.py @@ -266,7 +266,9 @@ def getSpeakerFromServer(baseDir: str, session, def _speakerEndpointJson(displayName: str, summary: str, content: str, imageDescription: str, links: [], gender: str, postId: str, - postDM: bool, postReply: bool) -> {}: + postDM: bool, postReply: bool, + followRequestsExist: bool, + likedBy: str) -> {}: """Returns a json endpoint for the TTS speaker """ speakerJson = { @@ -278,7 +280,9 @@ def _speakerEndpointJson(displayName: str, summary: str, "id": postId, "notify": { "dm": postDM, - "reply": postReply + "reply": postReply, + "followRequests": followRequestsExist, + "likedBy": likedBy } } if gender: @@ -441,11 +445,27 @@ def _postToSpeakerJson(baseDir: str, httpPrefix: str, actor = httpPrefix + '://' + domainFull + '/users/' + nickname postDM = isDM(postJsonObject) postReply = isReply(postJsonObject, actor) + + followRequestsExist = False + accountsDir = baseDir + '/accounts/' + nickname + '@' + domainFull + approveFollowsFilename = accountsDir + '/followrequests.txt' + if os.path.isfile(approveFollowsFilename): + with open(approveFollowsFilename, 'r') as fp: + follows = fp.readlines() + if len(follows) > 0: + followRequestsExist = True + likedBy = '' + likeFilename = accountsDir + '/.newLike' + if os.path.isfile(likeFilename): + with open(likeFilename, 'r') as fp: + likedBy = fp.read() + return _speakerEndpointJson(speakerName, summary, content, imageDescription, detectedLinks, gender, postId, - postDM, postReply) - + postDM, postReply, + followRequestsExist, + likedBy) def updateSpeaker(baseDir: str, httpPrefix: str, nickname: str, domain: str, domainFull: str, From 5d4bbb453d69380b3c722b5f4bde931ff7619f8a Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Mar 2021 15:11:10 +0000 Subject: [PATCH 3/5] Ignore if like notification has already sent --- speaker.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/speaker.py b/speaker.py index a1c4b5df9..1e955f4a8 100644 --- a/speaker.py +++ b/speaker.py @@ -459,6 +459,8 @@ def _postToSpeakerJson(baseDir: str, httpPrefix: str, if os.path.isfile(likeFilename): with open(likeFilename, 'r') as fp: likedBy = fp.read() + if '##sent##' in likedBy: + likedBy = '' return _speakerEndpointJson(speakerName, summary, content, imageDescription, From 88d4c8338d1ea87131b8a99ae32365c0d8415985 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Mar 2021 16:58:57 +0000 Subject: [PATCH 4/5] Include extra notification flags within speaker endpoint --- speaker.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/speaker.py b/speaker.py index 1e955f4a8..029339e13 100644 --- a/speaker.py +++ b/speaker.py @@ -268,7 +268,8 @@ def _speakerEndpointJson(displayName: str, summary: str, links: [], gender: str, postId: str, postDM: bool, postReply: bool, followRequestsExist: bool, - likedBy: str) -> {}: + likedBy: str, postCal: bool, + postShare: bool) -> {}: """Returns a json endpoint for the TTS speaker """ speakerJson = { @@ -282,7 +283,9 @@ def _speakerEndpointJson(displayName: str, summary: str, "dm": postDM, "reply": postReply, "followRequests": followRequestsExist, - "likedBy": likedBy + "likedBy": likedBy, + "calendar": postCal, + "share": postShare } } if gender: @@ -461,13 +464,19 @@ def _postToSpeakerJson(baseDir: str, httpPrefix: str, likedBy = fp.read() if '##sent##' in likedBy: likedBy = '' + calendarFilename = accountsDir + '/.newCalendar' + postCal = os.path.isfile(calendarFilename) + shareFilename = accountsDir + '/.newShare' + postShare = os.path.isfile(shareFilename) return _speakerEndpointJson(speakerName, summary, content, imageDescription, detectedLinks, gender, postId, postDM, postReply, followRequestsExist, - likedBy) + likedBy, + postCal, postShare) + def updateSpeaker(baseDir: str, httpPrefix: str, nickname: str, domain: str, domainFull: str, From e4921907211686b915bed74b2ed82b846d23ca7e Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Mar 2021 19:52:10 +0000 Subject: [PATCH 5/5] Notification sounds --- daemon.py | 34 +++++-- inbox.py | 27 ++++-- notifications_client.py | 156 +++++++++++++++++++++--------- speaker.py | 14 ++- theme/default/sounds/calendar.ogg | Bin 0 -> 14514 bytes theme/default/sounds/dm.ogg | Bin 0 -> 14514 bytes theme/default/sounds/follow.ogg | Bin 0 -> 14514 bytes theme/default/sounds/like.ogg | Bin 0 -> 14514 bytes theme/default/sounds/reply.ogg | Bin 0 -> 14514 bytes theme/default/sounds/share.ogg | Bin 0 -> 14514 bytes webapp_confirm.py | 4 +- webapp_frontscreen.py | 7 +- webapp_post.py | 15 ++- webapp_profile.py | 11 ++- webapp_search.py | 8 +- webapp_timeline.py | 1 + 16 files changed, 197 insertions(+), 80 deletions(-) create mode 100644 theme/default/sounds/calendar.ogg create mode 100644 theme/default/sounds/dm.ogg create mode 100644 theme/default/sounds/follow.ogg create mode 100644 theme/default/sounds/like.ogg create mode 100644 theme/default/sounds/reply.ogg create mode 100644 theme/default/sounds/share.ogg diff --git a/daemon.py b/daemon.py index 60642e4d1..d007ec902 100644 --- a/daemon.py +++ b/daemon.py @@ -2719,7 +2719,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain, self.server.showPublishedDateOnly, self.server.peertubeInstances, - self.server.allowLocalNetworkAccess) + self.server.allowLocalNetworkAccess, + self.server.themeName) if hashtagStr: msg = hashtagStr.encode('utf-8') msglen = len(msg) @@ -2772,7 +2773,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain, self.server.showPublishedDateOnly, self.server.peertubeInstances, - self.server.allowLocalNetworkAccess) + self.server.allowLocalNetworkAccess, + self.server.themeName) if historyStr: msg = historyStr.encode('utf-8') msglen = len(msg) @@ -2862,7 +2864,8 @@ class PubServer(BaseHTTPRequestHandler): showPublishedDateOnly, self.server.defaultTimeline, self.server.peertubeInstances, - allowLocalNetworkAccess) + allowLocalNetworkAccess, + self.server.themeName) if profileStr: msg = profileStr.encode('utf-8') msglen = len(msg) @@ -5987,7 +5990,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain, self.server.showPublishedDateOnly, self.server.peertubeInstances, - self.server.allowLocalNetworkAccess) + self.server.allowLocalNetworkAccess, + self.server.themeName) if hashtagStr: msg = hashtagStr.encode('utf-8') msglen = len(msg) @@ -6950,7 +6954,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.YTReplacementDomain, self.server.showPublishedDateOnly, self.server.peertubeInstances, - self.server.allowLocalNetworkAccess) + self.server.allowLocalNetworkAccess, + self.server.themeName) if deleteStr: deleteStrLen = len(deleteStr) self._set_headers('text/html', deleteStrLen, @@ -7155,7 +7160,8 @@ class PubServer(BaseHTTPRequestHandler): ytDomain, self.server.showPublishedDateOnly, peertubeInstances, - self.server.allowLocalNetworkAccess) + self.server.allowLocalNetworkAccess, + self.server.themeName) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -7242,7 +7248,8 @@ class PubServer(BaseHTTPRequestHandler): ytDomain, self.server.showPublishedDateOnly, peertubeInstances, - self.server.allowLocalNetworkAccess) + self.server.allowLocalNetworkAccess, + self.server.themeName) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -7532,6 +7539,8 @@ class PubServer(BaseHTTPRequestHandler): cssCache = self.server.cssCache allowLocalNetworkAccess = \ self.server.allowLocalNetworkAccess + themeName = \ + self.server.themeName msg = \ htmlIndividualPost(cssCache, recentPostsCache, @@ -7552,7 +7561,8 @@ class PubServer(BaseHTTPRequestHandler): ytDomain, showPublishedDateOnly, peertubeInstances, - allowLocalNetworkAccess) + allowLocalNetworkAccess, + themeName) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -7656,6 +7666,8 @@ class PubServer(BaseHTTPRequestHandler): self.server.peertubeInstances allowLocalNetworkAccess = \ self.server.allowLocalNetworkAccess + themeName = \ + self.server.themeName msg = \ htmlIndividualPost(self.server.cssCache, recentPostsCache, @@ -7676,7 +7688,8 @@ class PubServer(BaseHTTPRequestHandler): ytDomain, showPublishedDateOnly, peertubeInstances, - allowLocalNetworkAccess) + allowLocalNetworkAccess, + themeName) msg = msg.encode('utf-8') msglen = len(msg) self._set_headers('text/html', msglen, @@ -14667,7 +14680,8 @@ def runDaemon(brochMode: bool, httpd.maxFollowers, httpd.allowLocalNetworkAccess, httpd.peertubeInstances, - verifyAllSignatures), daemon=True) + verifyAllSignatures, + httpd.themeName), daemon=True) print('Creating scheduled post thread') httpd.thrPostSchedule = \ diff --git a/inbox.py b/inbox.py index 8acba772d..6203db0e4 100644 --- a/inbox.py +++ b/inbox.py @@ -158,7 +158,8 @@ def _inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int, allowDeletion: bool, boxname: str, showPublishedDateOnly: bool, peertubeInstances: [], - allowLocalNetworkAccess: bool) -> None: + allowLocalNetworkAccess: bool, + themeName: str) -> None: """Converts the json post into html and stores it in a cache This enables the post to be quickly displayed later """ @@ -176,6 +177,7 @@ def _inboxStorePostToHtmlCache(recentPostsCache: {}, maxRecentPosts: int, httpPrefix, __version__, boxname, None, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, + themeName, not isDM(postJsonObject), True, True, False, True) @@ -1290,7 +1292,8 @@ def _receiveAnnounce(recentPostsCache: {}, personCache: {}, messageJson: {}, federationList: [], debug: bool, translate: {}, YTReplacementDomain: str, - allowLocalNetworkAccess: bool) -> bool: + allowLocalNetworkAccess: bool, + themeName: str) -> bool: """Receives an announce activity within the POST section of HTTPServer """ if messageJson['type'] != 'Announce': @@ -1412,7 +1415,8 @@ def _receiveAnnounce(recentPostsCache: {}, updateSpeaker(baseDir, httpPrefix, nickname, domain, domainFull, postJsonObject, personCache, - translate, lookupActor) + translate, lookupActor, + themeName) ttsFile = open(postFilename + '.tts', "w+") if ttsFile: ttsFile.write('\n') @@ -2166,7 +2170,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly: bool, allowLocalNetworkAccess: bool, peertubeInstances: [], - lastBounceMessage: []) -> bool: + lastBounceMessage: [], + themeName: str) -> bool: """ Anything which needs to be done after initial checks have passed """ actor = keyId @@ -2245,7 +2250,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, federationList, debug, translate, YTReplacementDomain, - allowLocalNetworkAccess): + allowLocalNetworkAccess, + themeName): if debug: print('DEBUG: Announce accepted from ' + actor) @@ -2497,7 +2503,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, updateSpeaker(baseDir, httpPrefix, nickname, domain, domainFull, postJsonObject, personCache, - translate, None) + translate, None, themeName) if not unitTest: if debug: print('Saving inbox post as html to cache') @@ -2517,7 +2523,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int, boxname, showPublishedDateOnly, peertubeInstances, - allowLocalNetworkAccess) + allowLocalNetworkAccess, + themeName) if debug: timeDiff = \ str(int((time.time() - htmlCacheStartTime) * @@ -2618,7 +2625,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly: bool, maxFollowers: int, allowLocalNetworkAccess: bool, peertubeInstances: [], - verifyAllSignatures: bool) -> None: + verifyAllSignatures: bool, + themeName: str) -> None: """Processes received items and moves them to the appropriate directories """ @@ -3111,7 +3119,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly, allowLocalNetworkAccess, peertubeInstances, - lastBounceMessage) + lastBounceMessage, + themeName) if debug: pprint(queueJson['post']) diff --git a/notifications_client.py b/notifications_client.py index 67b37b72e..eb09f5389 100644 --- a/notifications_client.py +++ b/notifications_client.py @@ -66,6 +66,17 @@ def _speakerPicospeaker(pitch: int, rate: int, systemLanguage: str, os.system(speakerCmd) +def _playNotificationSound(soundFilename: str, player='ffplay') -> None: + """Plays a sound + """ + if not os.path.isfile(soundFilename): + return + + if player == 'ffplay': + os.system('ffplay ' + soundFilename + + ' -autoexit -hide_banner -nodisp') + + def runNotificationsClient(baseDir: str, proxyType: str, httpPrefix: str, nickname: str, domain: str, port: int, password: str, screenreader: str, @@ -73,64 +84,121 @@ def runNotificationsClient(baseDir: str, proxyType: str, httpPrefix: str, """Runs the notifications and screen reader client, which announces new inbox items """ - if screenreader == 'espeak': - print('Setting up espeak') - from espeak import espeak - elif screenreader != 'picospeaker': - print(screenreader + ' is not a supported TTS system') - return + if screenreader: + if screenreader == 'espeak': + print('Setting up espeak') + from espeak import espeak + elif screenreader != 'picospeaker': + print(screenreader + ' is not a supported TTS system') + return - print('Running ' + screenreader + ' for ' + nickname + '@' + domain) + print('Running ' + screenreader + ' for ' + nickname + '@' + domain) prevSay = '' + prevDM = False + prevReply = False + prevCalendar = False + prevFollow = False + prevLike = '' + prevShare = False + dmSoundFilename = 'dm.ogg' + replySoundFilename = 'reply.ogg' + calendarSoundFilename = 'calendar.ogg' + followSoundFilename = 'follow.ogg' + likeSoundFilename = 'like.ogg' + shareSoundFilename = 'share.ogg' + player = 'ffplay' while (1): session = createSession(proxyType) speakerJson = \ getSpeakerFromServer(baseDir, session, nickname, password, domain, port, httpPrefix, True, __version__) if speakerJson: - if speakerJson['say'] != prevSay: - if speakerJson.get('name'): - nameStr = speakerJson['name'] - gender = 'They/Them' - if speakerJson.get('gender'): - gender = speakerJson['gender'] + if speakerJson.get('notify'): + soundsDir = 'theme/default/sounds' + if speakerJson['notify'].get('theme'): + soundsDir = \ + 'theme/' + speakerJson['notify']['theme'] + '/sounds' + if not os.path.isdir(soundsDir): + soundsDir = 'theme/default/sounds' + if dmSoundFilename: + if speakerJson['notify']['dm'] != prevDM: + _playNotificationSound(soundsDir + '/' + + dmSoundFilename, player) + elif replySoundFilename: + if speakerJson['notify']['reply'] != prevReply: + _playNotificationSound(soundsDir + '/' + + replySoundFilename, player) + elif calendarSoundFilename: + if speakerJson['notify']['calendar'] != prevCalendar: + _playNotificationSound(soundsDir + '/' + + calendarSoundFilename, player) + elif followSoundFilename: + if speakerJson['notify']['followRequests'] != prevFollow: + _playNotificationSound(soundsDir + '/' + + followSoundFilename, player) + elif likeSoundFilename: + if speakerJson['notify']['like'] != prevLike: + _playNotificationSound(soundsDir + '/' + + likeSoundFilename, player) + elif shareSoundFilename: + if speakerJson['notify']['share'] != prevShare: + _playNotificationSound(soundsDir + '/' + + shareSoundFilename, player) - # get the speech parameters - pitch = getSpeakerPitch(nameStr, screenreader, gender) - rate = getSpeakerRate(nameStr, screenreader) - srange = getSpeakerRange(nameStr) + prevDM = speakerJson['notify']['dm'] + prevReply = speakerJson['notify']['reply'] + prevCalendar = speakerJson['notify']['calendar'] + prevFollow = speakerJson['notify']['followRequests'] + prevLike = speakerJson['notify']['like'] + prevShare = speakerJson['notify']['share'] - # say the speaker's name - if screenreader == 'espeak': - _speakerEspeak(espeak, pitch, rate, srange, nameStr) - elif screenreader == 'picospeaker': - _speakerPicospeaker(pitch, rate, - systemLanguage, nameStr) - time.sleep(2) + if speakerJson.get('say'): + if speakerJson['say'] != prevSay: + if speakerJson.get('name'): + nameStr = speakerJson['name'] + gender = 'They/Them' + if speakerJson.get('gender'): + gender = speakerJson['gender'] - # append image description if needed - if not speakerJson.get('imageDescription'): - sayStr = speakerJson['say'] - # echo spoken text to the screen - print(html.unescape(nameStr) + ': ' + - html.unescape(speakerJson['say']) + '\n') - else: - sayStr = speakerJson['say'] + '. ' + \ - speakerJson['imageDescription'] - # echo spoken text to the screen - print(html.unescape(nameStr) + ': ' + - html.unescape(speakerJson['say']) + '\n' + - html.unescape(speakerJson['imageDescription'])) + # get the speech parameters + pitch = getSpeakerPitch(nameStr, screenreader, gender) + rate = getSpeakerRate(nameStr, screenreader) + srange = getSpeakerRange(nameStr) - # speak the post content - if screenreader == 'espeak': - _speakerEspeak(espeak, pitch, rate, srange, sayStr) - elif screenreader == 'picospeaker': - _speakerPicospeaker(pitch, rate, - systemLanguage, sayStr) + # say the speaker's name + if screenreader == 'espeak': + _speakerEspeak(espeak, pitch, rate, srange, + nameStr) + elif screenreader == 'picospeaker': + _speakerPicospeaker(pitch, rate, + systemLanguage, nameStr) + time.sleep(2) - prevSay = speakerJson['say'] + # append image description if needed + if not speakerJson.get('imageDescription'): + sayStr = speakerJson['say'] + # echo spoken text to the screen + print(html.unescape(nameStr) + ': ' + + html.unescape(speakerJson['say']) + '\n') + else: + sayStr = speakerJson['say'] + '. ' + \ + speakerJson['imageDescription'] + # echo spoken text to the screen + imageDescription = \ + html.unescape(speakerJson['imageDescription']) + print(html.unescape(nameStr) + ': ' + + html.unescape(speakerJson['say']) + '\n' + + imageDescription) + + # speak the post content + if screenreader == 'espeak': + _speakerEspeak(espeak, pitch, rate, srange, sayStr) + elif screenreader == 'picospeaker': + _speakerPicospeaker(pitch, rate, + systemLanguage, sayStr) + + prevSay = speakerJson['say'] # wait for a while, or until a key is pressed keyPress = _waitForKeypress(30, debug) diff --git a/speaker.py b/speaker.py index 029339e13..fbd3f8952 100644 --- a/speaker.py +++ b/speaker.py @@ -269,7 +269,7 @@ def _speakerEndpointJson(displayName: str, summary: str, postDM: bool, postReply: bool, followRequestsExist: bool, likedBy: str, postCal: bool, - postShare: bool) -> {}: + postShare: bool, themeName: str) -> {}: """Returns a json endpoint for the TTS speaker """ speakerJson = { @@ -280,6 +280,7 @@ def _speakerEndpointJson(displayName: str, summary: str, "detectedLinks": links, "id": postId, "notify": { + "theme": themeName, "dm": postDM, "reply": postReply, "followRequests": followRequestsExist, @@ -377,7 +378,8 @@ def getSSMLbox(baseDir: str, path: str, def _postToSpeakerJson(baseDir: str, httpPrefix: str, nickname: str, domain: str, domainFull: str, postJsonObject: {}, personCache: {}, - translate: {}, announcingActor: str) -> {}: + translate: {}, announcingActor: str, + themeName: str) -> {}: """Converts an ActivityPub post into some Json containing speech synthesis parameters. NOTE: There currently appears to be no standardized json @@ -475,13 +477,14 @@ def _postToSpeakerJson(baseDir: str, httpPrefix: str, postDM, postReply, followRequestsExist, likedBy, - postCal, postShare) + postCal, postShare, themeName) def updateSpeaker(baseDir: str, httpPrefix: str, nickname: str, domain: str, domainFull: str, postJsonObject: {}, personCache: {}, - translate: {}, announcingActor: str) -> None: + translate: {}, announcingActor: str, + themeName: str) -> None: """ Generates a json file which can be used for TTS announcement of incoming inbox posts """ @@ -489,7 +492,8 @@ def updateSpeaker(baseDir: str, httpPrefix: str, _postToSpeakerJson(baseDir, httpPrefix, nickname, domain, domainFull, postJsonObject, personCache, - translate, announcingActor) + translate, announcingActor, + themeName) speakerFilename = \ baseDir + '/accounts/' + nickname + '@' + domain + '/speaker.json' saveJson(speakerJson, speakerFilename) diff --git a/theme/default/sounds/calendar.ogg b/theme/default/sounds/calendar.ogg new file mode 100644 index 0000000000000000000000000000000000000000..05ba472e25eba6f2985a13d306e24f25f9375d73 GIT binary patch literal 14514 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 literal 0 HcmV?d00001 diff --git a/theme/default/sounds/dm.ogg b/theme/default/sounds/dm.ogg new file mode 100644 index 0000000000000000000000000000000000000000..05ba472e25eba6f2985a13d306e24f25f9375d73 GIT binary patch literal 14514 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 literal 0 HcmV?d00001 diff --git a/theme/default/sounds/follow.ogg b/theme/default/sounds/follow.ogg new file mode 100644 index 0000000000000000000000000000000000000000..05ba472e25eba6f2985a13d306e24f25f9375d73 GIT binary patch literal 14514 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 literal 0 HcmV?d00001 diff --git a/theme/default/sounds/like.ogg b/theme/default/sounds/like.ogg new file mode 100644 index 0000000000000000000000000000000000000000..05ba472e25eba6f2985a13d306e24f25f9375d73 GIT binary patch literal 14514 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 literal 0 HcmV?d00001 diff --git a/theme/default/sounds/reply.ogg b/theme/default/sounds/reply.ogg new file mode 100644 index 0000000000000000000000000000000000000000..05ba472e25eba6f2985a13d306e24f25f9375d73 GIT binary patch literal 14514 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 literal 0 HcmV?d00001 diff --git a/theme/default/sounds/share.ogg b/theme/default/sounds/share.ogg new file mode 100644 index 0000000000000000000000000000000000000000..05ba472e25eba6f2985a13d306e24f25f9375d73 GIT binary patch literal 14514 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 literal 0 HcmV?d00001 diff --git a/webapp_confirm.py b/webapp_confirm.py index 26b8289b7..346d94893 100644 --- a/webapp_confirm.py +++ b/webapp_confirm.py @@ -31,7 +31,8 @@ def htmlConfirmDelete(cssCache: {}, YTReplacementDomain: str, showPublishedDateOnly: bool, peertubeInstances: [], - allowLocalNetworkAccess: bool) -> str: + allowLocalNetworkAccess: bool, + themeName: str) -> str: """Shows a screen asking to confirm the deletion of a post """ if '/statuses/' not in messageId: @@ -72,6 +73,7 @@ def htmlConfirmDelete(cssCache: {}, YTReplacementDomain, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, + themeName, False, False, False, False, False) deletePostStr += '
' deletePostStr += \ diff --git a/webapp_frontscreen.py b/webapp_frontscreen.py index 4e5ad14fc..0d3d18486 100644 --- a/webapp_frontscreen.py +++ b/webapp_frontscreen.py @@ -30,7 +30,8 @@ def _htmlFrontScreenPosts(recentPostsCache: {}, maxRecentPosts: int, YTReplacementDomain: str, showPublishedDateOnly: bool, peertubeInstances: [], - allowLocalNetworkAccess: bool) -> str: + allowLocalNetworkAccess: bool, + themeName: str) -> str: """Shows posts on the front screen of a news instance These should only be public blog posts from the features timeline which is the blog timeline of the news actor @@ -71,6 +72,7 @@ def _htmlFrontScreenPosts(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, + themeName, False, False, False, True, False) if postStr: profileStr += postStr + separatorStr @@ -159,7 +161,8 @@ def htmlFrontScreen(rssIconAtTop: bool, YTReplacementDomain, showPublishedDateOnly, peertubeInstances, - allowLocalNetworkAccess) + licenseStr + allowLocalNetworkAccess, + theme) + licenseStr # Footer which is only used for system accounts profileFooterStr = ' \n' diff --git a/webapp_post.py b/webapp_post.py index aca73ad33..36aec0653 100644 --- a/webapp_post.py +++ b/webapp_post.py @@ -1139,6 +1139,7 @@ def individualPostAsHtml(allowDownloads: bool, showPublishedDateOnly: bool, peertubeInstances: [], allowLocalNetworkAccess: bool, + themeName: str, showRepeats=True, showIcons=False, manuallyApprovesFollowers=False, @@ -1307,7 +1308,8 @@ def individualPostAsHtml(allowDownloads: bool, updateSpeaker(baseDir, httpPrefix, nickname, domain, domainFull, postJsonObject, personCache, - translate, postJsonObject['actor']) + translate, postJsonObject['actor'], + themeName) ttsFile = open(announceFilename + '.tts', "w+") if ttsFile: ttsFile.write('\n') @@ -1684,7 +1686,8 @@ def htmlIndividualPost(cssCache: {}, YTReplacementDomain: str, showPublishedDateOnly: bool, peertubeInstances: [], - allowLocalNetworkAccess: bool) -> str: + allowLocalNetworkAccess: bool, + themeName: str) -> str: """Show an individual post as html """ postStr = '' @@ -1725,7 +1728,7 @@ def htmlIndividualPost(cssCache: {}, YTReplacementDomain, showPublishedDateOnly, peertubeInstances, - allowLocalNetworkAccess, + allowLocalNetworkAccess, themeName, False, authorized, False, False, False) messageId = removeIdEnding(postJsonObject['id']) @@ -1753,6 +1756,7 @@ def htmlIndividualPost(cssCache: {}, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, + themeName, False, authorized, False, False, False) + postStr @@ -1783,6 +1787,7 @@ def htmlIndividualPost(cssCache: {}, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, + themeName, False, authorized, False, False, False) cssFilename = baseDir + '/epicyon-profile.css' @@ -1804,7 +1809,8 @@ def htmlPostReplies(cssCache: {}, YTReplacementDomain: str, showPublishedDateOnly: bool, peertubeInstances: [], - allowLocalNetworkAccess: bool) -> str: + allowLocalNetworkAccess: bool, + themeName: str) -> str: """Show the replies to an individual post as html """ repliesStr = '' @@ -1823,6 +1829,7 @@ def htmlPostReplies(cssCache: {}, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, + themeName, False, False, False, False, False) cssFilename = baseDir + '/epicyon-profile.css' diff --git a/webapp_profile.py b/webapp_profile.py index 0b5bb8db1..1a987244b 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -66,7 +66,8 @@ def htmlProfileAfterSearch(cssCache: {}, showPublishedDateOnly: bool, defaultTimeline: str, peertubeInstances: [], - allowLocalNetworkAccess: bool) -> str: + allowLocalNetworkAccess: bool, + themeName: str) -> str: """Show a profile page after a search for a fediverse address """ if hasUsersPath(profileHandle) or '/@' in profileHandle: @@ -300,6 +301,7 @@ def htmlProfileAfterSearch(cssCache: {}, YTReplacementDomain, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, + themeName, False, False, False, False, False) i += 1 if i >= 20: @@ -804,7 +806,8 @@ def htmlProfile(rssIconAtTop: bool, YTReplacementDomain, showPublishedDateOnly, peertubeInstances, - allowLocalNetworkAccess) + licenseStr + allowLocalNetworkAccess, + theme) + licenseStr elif selected == 'following': profileStr += \ _htmlProfileFollowing(translate, baseDir, httpPrefix, @@ -856,7 +859,8 @@ def _htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int, YTReplacementDomain: str, showPublishedDateOnly: bool, peertubeInstances: [], - allowLocalNetworkAccess: bool) -> str: + allowLocalNetworkAccess: bool, + themeName: str) -> str: """Shows posts on the profile screen These should only be public posts """ @@ -896,6 +900,7 @@ def _htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, + themeName, False, False, False, True, False) if postStr: profileStr += postStr + separatorStr diff --git a/webapp_search.py b/webapp_search.py index 0912beb90..6d982c45d 100644 --- a/webapp_search.py +++ b/webapp_search.py @@ -527,7 +527,8 @@ def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str, YTReplacementDomain: str, showPublishedDateOnly: bool, peertubeInstances: [], - allowLocalNetworkAccess: bool) -> str: + allowLocalNetworkAccess: bool, + themeName: str) -> str: """Show a page containing search results for your post history """ if historysearch.startswith('!'): @@ -604,6 +605,7 @@ def htmlHistorySearch(cssCache: {}, translate: {}, baseDir: str, showPublishedDateOnly, peertubeInstances, allowLocalNetworkAccess, + themeName, showIndividualPostIcons, showIndividualPostIcons, False, False, False) @@ -626,7 +628,8 @@ def htmlHashtagSearch(cssCache: {}, YTReplacementDomain: str, showPublishedDateOnly: bool, peertubeInstances: [], - allowLocalNetworkAccess: bool) -> str: + allowLocalNetworkAccess: bool, + themeName: str) -> str: """Show a page containing search results for a hashtag """ if hashtag.startswith('#'): @@ -778,6 +781,7 @@ def htmlHashtagSearch(cssCache: {}, allowLocalNetworkAccess, showRepeats, showIcons, manuallyApprovesFollowers, + themeName, showPublicOnly, storeToCache) if postStr: diff --git a/webapp_timeline.py b/webapp_timeline.py index 71e88d932..db256378f 100644 --- a/webapp_timeline.py +++ b/webapp_timeline.py @@ -723,6 +723,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, boxName != 'dm', showIndividualPostIcons, manuallyApproveFollowers, + theme, False, True) _logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '12')