mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon into main
commit
bb3ea15e34
37
daemon.py
37
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)
|
||||
|
|
@ -4853,8 +4856,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
|
||||
|
|
@ -5988,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)
|
||||
|
|
@ -6951,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,
|
||||
|
|
@ -7156,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,
|
||||
|
|
@ -7243,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,
|
||||
|
|
@ -7533,6 +7539,8 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
cssCache = self.server.cssCache
|
||||
allowLocalNetworkAccess = \
|
||||
self.server.allowLocalNetworkAccess
|
||||
themeName = \
|
||||
self.server.themeName
|
||||
msg = \
|
||||
htmlIndividualPost(cssCache,
|
||||
recentPostsCache,
|
||||
|
|
@ -7553,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,
|
||||
|
|
@ -7657,6 +7666,8 @@ class PubServer(BaseHTTPRequestHandler):
|
|||
self.server.peertubeInstances
|
||||
allowLocalNetworkAccess = \
|
||||
self.server.allowLocalNetworkAccess
|
||||
themeName = \
|
||||
self.server.themeName
|
||||
msg = \
|
||||
htmlIndividualPost(self.server.cssCache,
|
||||
recentPostsCache,
|
||||
|
|
@ -7677,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,
|
||||
|
|
@ -14668,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 = \
|
||||
|
|
|
|||
39
inbox.py
39
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
|
||||
|
|
@ -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':
|
||||
|
|
@ -1408,9 +1411,12 @@ 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)
|
||||
translate, lookupActor,
|
||||
themeName)
|
||||
ttsFile = open(postFilename + '.tts', "w+")
|
||||
if ttsFile:
|
||||
ttsFile.write('\n')
|
||||
|
|
@ -2164,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
|
||||
|
|
@ -2243,7 +2250,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
|||
federationList,
|
||||
debug, translate,
|
||||
YTReplacementDomain,
|
||||
allowLocalNetworkAccess):
|
||||
allowLocalNetworkAccess,
|
||||
themeName):
|
||||
if debug:
|
||||
print('DEBUG: Announce accepted from ' + actor)
|
||||
|
||||
|
|
@ -2491,9 +2499,11 @@ 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)
|
||||
translate, None, themeName)
|
||||
if not unitTest:
|
||||
if debug:
|
||||
print('Saving inbox post as html to cache')
|
||||
|
|
@ -2513,7 +2523,8 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
|||
boxname,
|
||||
showPublishedDateOnly,
|
||||
peertubeInstances,
|
||||
allowLocalNetworkAccess)
|
||||
allowLocalNetworkAccess,
|
||||
themeName)
|
||||
if debug:
|
||||
timeDiff = \
|
||||
str(int((time.time() - htmlCacheStartTime) *
|
||||
|
|
@ -2614,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
|
||||
"""
|
||||
|
|
@ -3107,7 +3119,8 @@ def runInboxQueue(recentPostsCache: {}, maxRecentPosts: int,
|
|||
showPublishedDateOnly,
|
||||
allowLocalNetworkAccess,
|
||||
peertubeInstances,
|
||||
lastBounceMessage)
|
||||
lastBounceMessage,
|
||||
themeName)
|
||||
if debug:
|
||||
pprint(queueJson['post'])
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
62
posts.py
62
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?
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
68
speaker.py
68
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,11 @@ 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,
|
||||
followRequestsExist: bool,
|
||||
likedBy: str, postCal: bool,
|
||||
postShare: bool, themeName: str) -> {}:
|
||||
"""Returns a json endpoint for the TTS speaker
|
||||
"""
|
||||
speakerJson = {
|
||||
|
|
@ -272,7 +278,16 @@ def _speakerEndpointJson(displayName: str, summary: str,
|
|||
"say": content,
|
||||
"imageDescription": imageDescription,
|
||||
"detectedLinks": links,
|
||||
"id": postId
|
||||
"id": postId,
|
||||
"notify": {
|
||||
"theme": themeName,
|
||||
"dm": postDM,
|
||||
"reply": postReply,
|
||||
"followRequests": followRequestsExist,
|
||||
"likedBy": likedBy,
|
||||
"calendar": postCal,
|
||||
"share": postShare
|
||||
}
|
||||
}
|
||||
if gender:
|
||||
speakerJson['gender'] = gender
|
||||
|
|
@ -360,9 +375,11 @@ 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) -> {}:
|
||||
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
|
||||
|
|
@ -429,21 +446,54 @@ 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)
|
||||
|
||||
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()
|
||||
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)
|
||||
detectedLinks, gender, postId,
|
||||
postDM, postReply,
|
||||
followRequestsExist,
|
||||
likedBy,
|
||||
postCal, postShare, themeName)
|
||||
|
||||
|
||||
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:
|
||||
translate: {}, announcingActor: str,
|
||||
themeName: 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)
|
||||
translate, announcingActor,
|
||||
themeName)
|
||||
speakerFilename = \
|
||||
baseDir + '/accounts/' + nickname + '@' + domain + '/speaker.json'
|
||||
saveJson(speakerJson, speakerFilename)
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
62
utils.py
62
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
|
||||
|
|
|
|||
|
|
@ -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 += '<center>'
|
||||
deletePostStr += \
|
||||
|
|
|
|||
|
|
@ -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 = ' </td>\n'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -1139,6 +1139,7 @@ def individualPostAsHtml(allowDownloads: bool,
|
|||
showPublishedDateOnly: bool,
|
||||
peertubeInstances: [],
|
||||
allowLocalNetworkAccess: bool,
|
||||
themeName: str,
|
||||
showRepeats=True,
|
||||
showIcons=False,
|
||||
manuallyApprovesFollowers=False,
|
||||
|
|
@ -1304,9 +1305,11 @@ 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'])
|
||||
translate, postJsonObject['actor'],
|
||||
themeName)
|
||||
ttsFile = open(announceFilename + '.tts', "w+")
|
||||
if ttsFile:
|
||||
ttsFile.write('\n')
|
||||
|
|
@ -1683,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 = ''
|
||||
|
|
@ -1724,7 +1728,7 @@ def htmlIndividualPost(cssCache: {},
|
|||
YTReplacementDomain,
|
||||
showPublishedDateOnly,
|
||||
peertubeInstances,
|
||||
allowLocalNetworkAccess,
|
||||
allowLocalNetworkAccess, themeName,
|
||||
False, authorized, False, False, False)
|
||||
messageId = removeIdEnding(postJsonObject['id'])
|
||||
|
||||
|
|
@ -1752,6 +1756,7 @@ def htmlIndividualPost(cssCache: {},
|
|||
showPublishedDateOnly,
|
||||
peertubeInstances,
|
||||
allowLocalNetworkAccess,
|
||||
themeName,
|
||||
False, authorized,
|
||||
False, False, False) + postStr
|
||||
|
||||
|
|
@ -1782,6 +1787,7 @@ def htmlIndividualPost(cssCache: {},
|
|||
showPublishedDateOnly,
|
||||
peertubeInstances,
|
||||
allowLocalNetworkAccess,
|
||||
themeName,
|
||||
False, authorized,
|
||||
False, False, False)
|
||||
cssFilename = baseDir + '/epicyon-profile.css'
|
||||
|
|
@ -1803,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 = ''
|
||||
|
|
@ -1822,6 +1829,7 @@ def htmlPostReplies(cssCache: {},
|
|||
showPublishedDateOnly,
|
||||
peertubeInstances,
|
||||
allowLocalNetworkAccess,
|
||||
themeName,
|
||||
False, False, False, False, False)
|
||||
|
||||
cssFilename = baseDir + '/epicyon-profile.css'
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -723,6 +723,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
boxName != 'dm',
|
||||
showIndividualPostIcons,
|
||||
manuallyApproveFollowers,
|
||||
theme,
|
||||
False, True)
|
||||
_logTimelineTiming(enableTimingLog,
|
||||
timelineStartTime, boxName, '12')
|
||||
|
|
|
|||
Loading…
Reference in New Issue