mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon
commit
b1e6354e0c
24
content.py
24
content.py
|
|
@ -202,29 +202,29 @@ def dangerousCSS(filename: str, allowLocalNetworkAccess: bool) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def switchWords(baseDir: str, nickname: str, domain: str, content: str) -> str:
|
def switchWords(baseDir: str, nickname: str, domain: str, content: str,
|
||||||
|
rules: [] = []) -> str:
|
||||||
"""Performs word replacements. eg. Trump -> The Orange Menace
|
"""Performs word replacements. eg. Trump -> The Orange Menace
|
||||||
"""
|
"""
|
||||||
if isPGPEncrypted(content) or containsPGPPublicKey(content):
|
if isPGPEncrypted(content) or containsPGPPublicKey(content):
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
if not rules:
|
||||||
switchWordsFilename = baseDir + '/accounts/' + \
|
switchWordsFilename = baseDir + '/accounts/' + \
|
||||||
nickname + '@' + domain + '/replacewords.txt'
|
nickname + '@' + domain + '/replacewords.txt'
|
||||||
if not os.path.isfile(switchWordsFilename):
|
if not os.path.isfile(switchWordsFilename):
|
||||||
return content
|
return content
|
||||||
with open(switchWordsFilename, 'r') as fp:
|
with open(switchWordsFilename, 'r') as fp:
|
||||||
for line in fp:
|
rules = fp.readlines()
|
||||||
|
|
||||||
|
for line in rules:
|
||||||
replaceStr = line.replace('\n', '').replace('\r', '')
|
replaceStr = line.replace('\n', '').replace('\r', '')
|
||||||
|
splitters = ('->', ':', ',', ';', '-')
|
||||||
wordTransform = None
|
wordTransform = None
|
||||||
if '->' in replaceStr:
|
for splitStr in splitters:
|
||||||
wordTransform = replaceStr.split('->')
|
if splitStr in replaceStr:
|
||||||
elif ':' in replaceStr:
|
wordTransform = replaceStr.split(splitStr)
|
||||||
wordTransform = replaceStr.split(':')
|
break
|
||||||
elif ',' in replaceStr:
|
|
||||||
wordTransform = replaceStr.split(',')
|
|
||||||
elif ';' in replaceStr:
|
|
||||||
wordTransform = replaceStr.split(';')
|
|
||||||
elif '-' in replaceStr:
|
|
||||||
wordTransform = replaceStr.split('-')
|
|
||||||
if not wordTransform:
|
if not wordTransform:
|
||||||
continue
|
continue
|
||||||
if len(wordTransform) == 2:
|
if len(wordTransform) == 2:
|
||||||
|
|
|
||||||
317
daemon.py
317
daemon.py
|
|
@ -286,6 +286,8 @@ from bookmarks import undoBookmark
|
||||||
from petnames import setPetName
|
from petnames import setPetName
|
||||||
from followingCalendar import addPersonToCalendar
|
from followingCalendar import addPersonToCalendar
|
||||||
from followingCalendar import removePersonFromCalendar
|
from followingCalendar import removePersonFromCalendar
|
||||||
|
from notifyOnPost import addNotifyOnPost
|
||||||
|
from notifyOnPost import removeNotifyOnPost
|
||||||
from devices import E2EEdevicesCollection
|
from devices import E2EEdevicesCollection
|
||||||
from devices import E2EEvalidDevice
|
from devices import E2EEvalidDevice
|
||||||
from devices import E2EEaddDevice
|
from devices import E2EEaddDevice
|
||||||
|
|
@ -2087,6 +2089,34 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.POSTbusy = False
|
self.server.POSTbusy = False
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# person options screen, on notify checkbox
|
||||||
|
# See htmlPersonOptions
|
||||||
|
if '&submitNotifyOnPost=' in optionsConfirmParams:
|
||||||
|
notify = None
|
||||||
|
if 'notifyOnPost=' in optionsConfirmParams:
|
||||||
|
notify = optionsConfirmParams.split('notifyOnPost=')[1]
|
||||||
|
if '&' in notify:
|
||||||
|
notify = notify.split('&')[0]
|
||||||
|
if notify == 'on':
|
||||||
|
addNotifyOnPost(baseDir,
|
||||||
|
chooserNickname,
|
||||||
|
domain,
|
||||||
|
optionsNickname,
|
||||||
|
optionsDomainFull)
|
||||||
|
else:
|
||||||
|
removeNotifyOnPost(baseDir,
|
||||||
|
chooserNickname,
|
||||||
|
domain,
|
||||||
|
optionsNickname,
|
||||||
|
optionsDomainFull)
|
||||||
|
usersPathStr = \
|
||||||
|
usersPath + '/' + self.server.defaultTimeline + \
|
||||||
|
'?page=' + str(pageNumber)
|
||||||
|
self._redirect_headers(usersPathStr, cookie,
|
||||||
|
callingDomain)
|
||||||
|
self.server.POSTbusy = False
|
||||||
|
return
|
||||||
|
|
||||||
# person options screen, permission to post to newswire
|
# person options screen, permission to post to newswire
|
||||||
# See htmlPersonOptions
|
# See htmlPersonOptions
|
||||||
if '&submitPostToNews=' in optionsConfirmParams:
|
if '&submitPostToNews=' in optionsConfirmParams:
|
||||||
|
|
@ -7670,31 +7700,54 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
if '/' not in namedStatus:
|
if '/' not in namedStatus:
|
||||||
# show actor
|
# show actor
|
||||||
nickname = namedStatus
|
nickname = namedStatus
|
||||||
else:
|
return False
|
||||||
|
|
||||||
postSections = namedStatus.split('/')
|
postSections = namedStatus.split('/')
|
||||||
if len(postSections) == 2:
|
if len(postSections) != 2:
|
||||||
|
return False
|
||||||
nickname = postSections[0]
|
nickname = postSections[0]
|
||||||
statusNumber = postSections[1]
|
statusNumber = postSections[1]
|
||||||
if len(statusNumber) > 10 and statusNumber.isdigit():
|
if len(statusNumber) <= 10 or not statusNumber.isdigit():
|
||||||
|
return False
|
||||||
|
|
||||||
postFilename = \
|
postFilename = \
|
||||||
baseDir + '/accounts/' + \
|
baseDir + '/accounts/' + nickname + '@' + domain + '/outbox/' + \
|
||||||
nickname + '@' + \
|
httpPrefix + ':##' + domainFull + '#users#' + nickname + \
|
||||||
domain + '/outbox/' + \
|
'#statuses#' + statusNumber + '.json'
|
||||||
httpPrefix + ':##' + \
|
|
||||||
domainFull + '#users#' + \
|
return self._showPostFromFile(postFilename, likedBy,
|
||||||
nickname + '#statuses#' + \
|
authorized, callingDomain, path,
|
||||||
statusNumber + '.json'
|
baseDir, httpPrefix, nickname,
|
||||||
if os.path.isfile(postFilename):
|
domain, domainFull, port,
|
||||||
|
onionDomain, i2pDomain,
|
||||||
|
GETstartTime, GETtimings,
|
||||||
|
proxyType, cookie, debug)
|
||||||
|
|
||||||
|
def _showPostFromFile(self, postFilename: str, likedBy: str,
|
||||||
|
authorized: bool,
|
||||||
|
callingDomain: str, path: str,
|
||||||
|
baseDir: str, httpPrefix: str, nickname: str,
|
||||||
|
domain: str, domainFull: str, port: int,
|
||||||
|
onionDomain: str, i2pDomain: str,
|
||||||
|
GETstartTime, GETtimings: {},
|
||||||
|
proxyType: str, cookie: str,
|
||||||
|
debug: str) -> bool:
|
||||||
|
"""Shows an individual post from its filename
|
||||||
|
"""
|
||||||
|
if not os.path.isfile(postFilename):
|
||||||
|
self._404()
|
||||||
|
self.server.GETbusy = False
|
||||||
|
return True
|
||||||
|
|
||||||
postJsonObject = loadJson(postFilename)
|
postJsonObject = loadJson(postFilename)
|
||||||
loadedPost = False
|
if not postJsonObject:
|
||||||
if postJsonObject:
|
self.send_response(429)
|
||||||
loadedPost = True
|
self.end_headers()
|
||||||
else:
|
self.server.GETbusy = False
|
||||||
postJsonObject = {}
|
return True
|
||||||
if loadedPost:
|
|
||||||
# Only authorized viewers get to see likes
|
# Only authorized viewers get to see likes on posts
|
||||||
# on posts. Otherwize marketers could gain
|
# Otherwize marketers could gain more social graph info
|
||||||
# more social graph info
|
|
||||||
if not authorized:
|
if not authorized:
|
||||||
pjo = postJsonObject
|
pjo = postJsonObject
|
||||||
if not isPublicPost(pjo):
|
if not isPublicPost(pjo):
|
||||||
|
|
@ -7703,56 +7756,36 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
return True
|
return True
|
||||||
removePostInteractions(pjo, True)
|
removePostInteractions(pjo, True)
|
||||||
if self._requestHTTP():
|
if self._requestHTTP():
|
||||||
recentPostsCache = \
|
|
||||||
self.server.recentPostsCache
|
|
||||||
maxRecentPosts = \
|
|
||||||
self.server.maxRecentPosts
|
|
||||||
translate = \
|
|
||||||
self.server.translate
|
|
||||||
cachedWebfingers = \
|
|
||||||
self.server.cachedWebfingers
|
|
||||||
personCache = \
|
|
||||||
self.server.personCache
|
|
||||||
projectVersion = \
|
|
||||||
self.server.projectVersion
|
|
||||||
ytDomain = \
|
|
||||||
self.server.YTReplacementDomain
|
|
||||||
showPublishedDateOnly = \
|
|
||||||
self.server.showPublishedDateOnly
|
|
||||||
peertubeInstances = \
|
|
||||||
self.server.peertubeInstances
|
|
||||||
cssCache = self.server.cssCache
|
|
||||||
allowLocalNetworkAccess = \
|
|
||||||
self.server.allowLocalNetworkAccess
|
|
||||||
themeName = \
|
|
||||||
self.server.themeName
|
|
||||||
msg = \
|
msg = \
|
||||||
htmlIndividualPost(cssCache,
|
htmlIndividualPost(self.server.cssCache,
|
||||||
recentPostsCache,
|
self.server.recentPostsCache,
|
||||||
maxRecentPosts,
|
self.server.maxRecentPosts,
|
||||||
translate,
|
self.server.translate,
|
||||||
self.server.baseDir,
|
baseDir,
|
||||||
self.server.session,
|
self.server.session,
|
||||||
cachedWebfingers,
|
self.server.cachedWebfingers,
|
||||||
personCache,
|
self.server.personCache,
|
||||||
nickname,
|
nickname, domain, port,
|
||||||
domain,
|
|
||||||
port,
|
|
||||||
authorized,
|
authorized,
|
||||||
postJsonObject,
|
postJsonObject,
|
||||||
httpPrefix,
|
httpPrefix,
|
||||||
projectVersion,
|
self.server.projectVersion,
|
||||||
likedBy,
|
likedBy,
|
||||||
ytDomain,
|
self.server.YTReplacementDomain,
|
||||||
showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
peertubeInstances,
|
self.server.peertubeInstances,
|
||||||
allowLocalNetworkAccess,
|
self.server.allowLocalNetworkAccess,
|
||||||
themeName)
|
self.server.themeName)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
msglen = len(msg)
|
msglen = len(msg)
|
||||||
self._set_headers('text/html', msglen,
|
self._set_headers('text/html', msglen,
|
||||||
cookie, callingDomain)
|
cookie, callingDomain)
|
||||||
self._write(msg)
|
self._write(msg)
|
||||||
|
self._benchmarkGETtimings(GETstartTime,
|
||||||
|
GETtimings,
|
||||||
|
'show skills ' +
|
||||||
|
'done',
|
||||||
|
'show status')
|
||||||
else:
|
else:
|
||||||
if self._fetchAuthenticated():
|
if self._fetchAuthenticated():
|
||||||
msg = json.dumps(postJsonObject,
|
msg = json.dumps(postJsonObject,
|
||||||
|
|
@ -7766,15 +7799,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
else:
|
else:
|
||||||
self._404()
|
self._404()
|
||||||
self.server.GETbusy = False
|
self.server.GETbusy = False
|
||||||
self._benchmarkGETtimings(GETstartTime, GETtimings,
|
|
||||||
'new post done',
|
|
||||||
'individual post shown')
|
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
self._404()
|
|
||||||
self.server.GETbusy = False
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _showIndividualPost(self, authorized: bool,
|
def _showIndividualPost(self, authorized: bool,
|
||||||
callingDomain: str, path: str,
|
callingDomain: str, path: str,
|
||||||
|
|
@ -7802,108 +7827,51 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
statusNumber = postSections[2]
|
statusNumber = postSections[2]
|
||||||
if len(statusNumber) <= 10 or (not statusNumber.isdigit()):
|
if len(statusNumber) <= 10 or (not statusNumber.isdigit()):
|
||||||
return False
|
return False
|
||||||
postFilename = \
|
|
||||||
baseDir + '/accounts/' + \
|
|
||||||
nickname + '@' + \
|
|
||||||
domain + '/outbox/' + \
|
|
||||||
httpPrefix + ':##' + \
|
|
||||||
domainFull + '#users#' + \
|
|
||||||
nickname + '#statuses#' + \
|
|
||||||
statusNumber + '.json'
|
|
||||||
if os.path.isfile(postFilename):
|
|
||||||
postJsonObject = loadJson(postFilename)
|
|
||||||
if not postJsonObject:
|
|
||||||
self.send_response(429)
|
|
||||||
self.end_headers()
|
|
||||||
self.server.GETbusy = False
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
# Only authorized viewers get to see likes
|
|
||||||
# on posts
|
|
||||||
# Otherwize marketers could gain more social
|
|
||||||
# graph info
|
|
||||||
if not authorized:
|
|
||||||
pjo = postJsonObject
|
|
||||||
if not isPublicPost(pjo):
|
|
||||||
self._404()
|
|
||||||
self.server.GETbusy = False
|
|
||||||
return True
|
|
||||||
removePostInteractions(pjo, True)
|
|
||||||
|
|
||||||
if self._requestHTTP():
|
postFilename = \
|
||||||
recentPostsCache = \
|
baseDir + '/accounts/' + nickname + '@' + domain + '/outbox/' + \
|
||||||
self.server.recentPostsCache
|
httpPrefix + ':##' + domainFull + '#users#' + nickname + \
|
||||||
maxRecentPosts = \
|
'#statuses#' + statusNumber + '.json'
|
||||||
self.server.maxRecentPosts
|
|
||||||
translate = \
|
return self._showPostFromFile(postFilename, likedBy,
|
||||||
self.server.translate
|
authorized, callingDomain, path,
|
||||||
cachedWebfingers = \
|
baseDir, httpPrefix, nickname,
|
||||||
self.server.cachedWebfingers
|
domain, domainFull, port,
|
||||||
personCache = \
|
onionDomain, i2pDomain,
|
||||||
self.server.personCache
|
GETstartTime, GETtimings,
|
||||||
projectVersion = \
|
proxyType, cookie, debug)
|
||||||
self.server.projectVersion
|
|
||||||
ytDomain = \
|
def _showNotifyPost(self, authorized: bool,
|
||||||
self.server.YTReplacementDomain
|
callingDomain: str, path: str,
|
||||||
showPublishedDateOnly = \
|
baseDir: str, httpPrefix: str,
|
||||||
self.server.showPublishedDateOnly
|
domain: str, domainFull: str, port: int,
|
||||||
peertubeInstances = \
|
onionDomain: str, i2pDomain: str,
|
||||||
self.server.peertubeInstances
|
GETstartTime, GETtimings: {},
|
||||||
allowLocalNetworkAccess = \
|
proxyType: str, cookie: str,
|
||||||
self.server.allowLocalNetworkAccess
|
debug: str) -> bool:
|
||||||
themeName = \
|
"""Shows an individual post from an account which you are following
|
||||||
self.server.themeName
|
and where you have the notify checkbox set on person options
|
||||||
msg = \
|
"""
|
||||||
htmlIndividualPost(self.server.cssCache,
|
likedBy = None
|
||||||
recentPostsCache,
|
postId = path.split('?notifypost=')[1].strip()
|
||||||
maxRecentPosts,
|
postId = postId.replace('-', '/')
|
||||||
translate,
|
path = path.split('?notifypost=')[0]
|
||||||
baseDir,
|
nickname = path.split('/users/')[1]
|
||||||
self.server.session,
|
if '/' in nickname:
|
||||||
cachedWebfingers,
|
|
||||||
personCache,
|
|
||||||
nickname,
|
|
||||||
domain,
|
|
||||||
port,
|
|
||||||
authorized,
|
|
||||||
postJsonObject,
|
|
||||||
httpPrefix,
|
|
||||||
projectVersion,
|
|
||||||
likedBy,
|
|
||||||
ytDomain,
|
|
||||||
showPublishedDateOnly,
|
|
||||||
peertubeInstances,
|
|
||||||
allowLocalNetworkAccess,
|
|
||||||
themeName)
|
|
||||||
msg = msg.encode('utf-8')
|
|
||||||
msglen = len(msg)
|
|
||||||
self._set_headers('text/html', msglen,
|
|
||||||
cookie, callingDomain)
|
|
||||||
self._write(msg)
|
|
||||||
self._benchmarkGETtimings(GETstartTime,
|
|
||||||
GETtimings,
|
|
||||||
'show skills ' +
|
|
||||||
'done',
|
|
||||||
'show status')
|
|
||||||
else:
|
|
||||||
if self._fetchAuthenticated():
|
|
||||||
msg = json.dumps(postJsonObject,
|
|
||||||
ensure_ascii=False)
|
|
||||||
msg = msg.encode('utf-8')
|
|
||||||
msglen = len(msg)
|
|
||||||
self._set_headers('application/json',
|
|
||||||
msglen,
|
|
||||||
None, callingDomain)
|
|
||||||
self._write(msg)
|
|
||||||
else:
|
|
||||||
self._404()
|
|
||||||
self.server.GETbusy = False
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
self._404()
|
|
||||||
self.server.GETbusy = False
|
|
||||||
return True
|
|
||||||
return False
|
return False
|
||||||
|
replies = False
|
||||||
|
|
||||||
|
postFilename = locatePost(baseDir, nickname, domain, postId, replies)
|
||||||
|
if not postFilename:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return self._showPostFromFile(postFilename, likedBy,
|
||||||
|
authorized, callingDomain, path,
|
||||||
|
baseDir, httpPrefix, nickname,
|
||||||
|
domain, domainFull, port,
|
||||||
|
onionDomain, i2pDomain,
|
||||||
|
GETstartTime, GETtimings,
|
||||||
|
proxyType, cookie, debug)
|
||||||
|
|
||||||
def _showInbox(self, authorized: bool,
|
def _showInbox(self, authorized: bool,
|
||||||
callingDomain: str, path: str,
|
callingDomain: str, path: str,
|
||||||
|
|
@ -12483,6 +12451,21 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
'post roles done',
|
'post roles done',
|
||||||
'show skills done')
|
'show skills done')
|
||||||
|
|
||||||
|
if '?notifypost=' in self.path and usersInPath and authorized:
|
||||||
|
if self._showNotifyPost(authorized,
|
||||||
|
callingDomain, self.path,
|
||||||
|
self.server.baseDir,
|
||||||
|
self.server.httpPrefix,
|
||||||
|
self.server.domain,
|
||||||
|
self.server.domainFull,
|
||||||
|
self.server.port,
|
||||||
|
self.server.onionDomain,
|
||||||
|
self.server.i2pDomain,
|
||||||
|
GETstartTime, GETtimings,
|
||||||
|
self.server.proxyType,
|
||||||
|
cookie, self.server.debug):
|
||||||
|
return
|
||||||
|
|
||||||
# get an individual post from the path
|
# get an individual post from the path
|
||||||
# /users/nickname/statuses/number
|
# /users/nickname/statuses/number
|
||||||
if '/statuses/' in self.path and usersInPath:
|
if '/statuses/' in self.path and usersInPath:
|
||||||
|
|
|
||||||
45
inbox.py
45
inbox.py
|
|
@ -86,6 +86,7 @@ from categories import guessHashtagCategory
|
||||||
from context import hasValidContext
|
from context import hasValidContext
|
||||||
from speaker import updateSpeaker
|
from speaker import updateSpeaker
|
||||||
from announce import isSelfAnnounce
|
from announce import isSelfAnnounce
|
||||||
|
from notifyOnPost import notifyWhenPersonPosts
|
||||||
|
|
||||||
|
|
||||||
def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None:
|
def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None:
|
||||||
|
|
@ -1850,6 +1851,25 @@ def _likeNotify(baseDir: str, domain: str, onionDomain: str,
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _notifyPostArrival(baseDir: str, handle: str, url: str) -> None:
|
||||||
|
"""Creates a notification that a new post has arrived.
|
||||||
|
This is for followed accounts with the notify checkbox enabled
|
||||||
|
on the person options screen
|
||||||
|
"""
|
||||||
|
accountDir = baseDir + '/accounts/' + handle
|
||||||
|
if not os.path.isdir(accountDir):
|
||||||
|
return
|
||||||
|
notifyFile = accountDir + '/.newNotifiedPost'
|
||||||
|
if os.path.isfile(notifyFile):
|
||||||
|
# check that the same notification is not repeatedly sent
|
||||||
|
with open(notifyFile, 'r') as fp:
|
||||||
|
existingNotificationMessage = fp.read()
|
||||||
|
if url in existingNotificationMessage:
|
||||||
|
return
|
||||||
|
with open(notifyFile, 'w+') as fp:
|
||||||
|
fp.write(url)
|
||||||
|
|
||||||
|
|
||||||
def _replyNotify(baseDir: str, handle: str, url: str) -> None:
|
def _replyNotify(baseDir: str, handle: str, url: str) -> None:
|
||||||
"""Creates a notification that a new reply has arrived
|
"""Creates a notification that a new reply has arrived
|
||||||
"""
|
"""
|
||||||
|
|
@ -2275,6 +2295,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
||||||
|
|
||||||
_updateLastSeen(baseDir, handle, actor)
|
_updateLastSeen(baseDir, handle, actor)
|
||||||
|
|
||||||
|
postIsDM = False
|
||||||
isGroup = _groupHandle(baseDir, handle)
|
isGroup = _groupHandle(baseDir, handle)
|
||||||
|
|
||||||
if _receiveLike(recentPostsCache,
|
if _receiveLike(recentPostsCache,
|
||||||
|
|
@ -2392,6 +2413,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
||||||
postJsonObject = messageJson
|
postJsonObject = messageJson
|
||||||
|
|
||||||
nickname = handle.split('@')[0]
|
nickname = handle.split('@')[0]
|
||||||
|
jsonObj = None
|
||||||
if _validPostContent(baseDir, nickname, domain,
|
if _validPostContent(baseDir, nickname, domain,
|
||||||
postJsonObject, maxMentions, maxEmoji,
|
postJsonObject, maxMentions, maxEmoji,
|
||||||
allowLocalNetworkAccess, debug):
|
allowLocalNetworkAccess, debug):
|
||||||
|
|
@ -2473,8 +2495,7 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
||||||
|
|
||||||
# get the actor being replied to
|
# get the actor being replied to
|
||||||
domainFull = getFullDomain(domain, port)
|
domainFull = getFullDomain(domain, port)
|
||||||
actor = httpPrefix + '://' + domainFull + \
|
actor = httpPrefix + '://' + domainFull + '/users/' + nickname
|
||||||
'/users/' + handle.split('@')[0]
|
|
||||||
|
|
||||||
# create a reply notification file if needed
|
# create a reply notification file if needed
|
||||||
if not postIsDM and isReply(postJsonObject, actor):
|
if not postIsDM and isReply(postJsonObject, actor):
|
||||||
|
|
@ -2512,6 +2533,26 @@ def _inboxAfterInitial(recentPostsCache: {}, maxRecentPosts: int,
|
||||||
|
|
||||||
# save the post to file
|
# save the post to file
|
||||||
if saveJson(postJsonObject, destinationFilename):
|
if saveJson(postJsonObject, destinationFilename):
|
||||||
|
# should we notify that a post from this person has arrived?
|
||||||
|
# This is for cases where the notify checkbox is enabled
|
||||||
|
# on the person options screen
|
||||||
|
if not postIsDM and jsonObj:
|
||||||
|
if jsonObj.get('attributedTo') and jsonObj.get('id'):
|
||||||
|
attributedTo = jsonObj['attributedTo']
|
||||||
|
if isinstance(attributedTo, str):
|
||||||
|
fromNickname = getNicknameFromActor(attributedTo)
|
||||||
|
fromDomain, fromPort = getDomainFromActor(attributedTo)
|
||||||
|
fromDomainFull = getFullDomain(fromDomain, fromPort)
|
||||||
|
if notifyWhenPersonPosts(baseDir, nickname, domain,
|
||||||
|
fromNickname, fromDomainFull):
|
||||||
|
postId = removeIdEnding(jsonObj['id'])
|
||||||
|
postLink = \
|
||||||
|
httpPrefix + '://' + \
|
||||||
|
getFullDomain(domain, port) + \
|
||||||
|
'/users/' + nickname + \
|
||||||
|
'?notifypost=' + postId.replace('/', '-')
|
||||||
|
_notifyPostArrival(baseDir, handle, postLink)
|
||||||
|
|
||||||
# If this is a reply to a muted post then also mute it.
|
# If this is a reply to a muted post then also mute it.
|
||||||
# This enables you to ignore a threat that's getting boring
|
# This enables you to ignore a threat that's getting boring
|
||||||
if isReplyToMutedPost:
|
if isReplyToMutedPost:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
__filename__ = "notifyOnPost.py"
|
||||||
|
__author__ = "Bob Mottram"
|
||||||
|
__license__ = "AGPL3+"
|
||||||
|
__version__ = "1.2.0"
|
||||||
|
__maintainer__ = "Bob Mottram"
|
||||||
|
__email__ = "bob@freedombone.net"
|
||||||
|
__status__ = "Production"
|
||||||
|
__module_group__ = "Calendar"
|
||||||
|
|
||||||
|
import os
|
||||||
|
from utils import removeDomainPort
|
||||||
|
|
||||||
|
|
||||||
|
def _notifyOnPostArrival(baseDir: str, nickname: str, domain: str,
|
||||||
|
followingNickname: str,
|
||||||
|
followingDomain: str,
|
||||||
|
add: bool) -> None:
|
||||||
|
"""Adds or removes a handle from the following.txt list into a list
|
||||||
|
indicating whether to notify when a new post arrives from that account
|
||||||
|
"""
|
||||||
|
# check that a following file exists
|
||||||
|
domain = removeDomainPort(domain)
|
||||||
|
followingFilename = baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/following.txt'
|
||||||
|
if not os.path.isfile(followingFilename):
|
||||||
|
print("WARN: following.txt doesn't exist for " +
|
||||||
|
nickname + '@' + domain)
|
||||||
|
return
|
||||||
|
handle = followingNickname + '@' + followingDomain
|
||||||
|
|
||||||
|
# check that you are following this handle
|
||||||
|
if handle + '\n' not in open(followingFilename).read():
|
||||||
|
print('WARN: ' + handle + ' is not in ' + followingFilename)
|
||||||
|
return
|
||||||
|
|
||||||
|
notifyOnPostFilename = baseDir + '/accounts/' + \
|
||||||
|
nickname + '@' + domain + '/notifyOnPost.txt'
|
||||||
|
|
||||||
|
# get the contents of the notifyOnPost file, which is
|
||||||
|
# a set of handles
|
||||||
|
followingHandles = ''
|
||||||
|
if os.path.isfile(notifyOnPostFilename):
|
||||||
|
print('notify file exists')
|
||||||
|
with open(notifyOnPostFilename, 'r') as calendarFile:
|
||||||
|
followingHandles = calendarFile.read()
|
||||||
|
else:
|
||||||
|
# create a new notifyOnPost file from the following file
|
||||||
|
print('Creating notifyOnPost file ' + notifyOnPostFilename)
|
||||||
|
followingHandles = ''
|
||||||
|
with open(followingFilename, 'r') as followingFile:
|
||||||
|
followingHandles = followingFile.read()
|
||||||
|
if add:
|
||||||
|
with open(notifyOnPostFilename, 'w+') as fp:
|
||||||
|
fp.write(followingHandles + handle + '\n')
|
||||||
|
|
||||||
|
# already in the notifyOnPost file?
|
||||||
|
if handle + '\n' in followingHandles:
|
||||||
|
print(handle + ' exists in notifyOnPost.txt')
|
||||||
|
if add:
|
||||||
|
# already added
|
||||||
|
return
|
||||||
|
# remove from calendar file
|
||||||
|
followingHandles = followingHandles.replace(handle + '\n', '')
|
||||||
|
with open(notifyOnPostFilename, 'w+') as fp:
|
||||||
|
fp.write(followingHandles)
|
||||||
|
else:
|
||||||
|
print(handle + ' not in notifyOnPost.txt')
|
||||||
|
# not already in the notifyOnPost file
|
||||||
|
if add:
|
||||||
|
# append to the list of handles
|
||||||
|
followingHandles += handle + '\n'
|
||||||
|
with open(notifyOnPostFilename, 'w+') as fp:
|
||||||
|
fp.write(followingHandles)
|
||||||
|
|
||||||
|
|
||||||
|
def addNotifyOnPost(baseDir: str, nickname: str, domain: str,
|
||||||
|
followingNickname: str,
|
||||||
|
followingDomain: str) -> None:
|
||||||
|
_notifyOnPostArrival(baseDir, nickname, domain,
|
||||||
|
followingNickname, followingDomain, True)
|
||||||
|
|
||||||
|
|
||||||
|
def removeNotifyOnPost(baseDir: str, nickname: str, domain: str,
|
||||||
|
followingNickname: str,
|
||||||
|
followingDomain: str) -> None:
|
||||||
|
_notifyOnPostArrival(baseDir, nickname, domain,
|
||||||
|
followingNickname, followingDomain, False)
|
||||||
|
|
||||||
|
|
||||||
|
def notifyWhenPersonPosts(baseDir: str, nickname: str, domain: str,
|
||||||
|
followingNickname: str,
|
||||||
|
followingDomain: str) -> bool:
|
||||||
|
"""Returns true if receiving notifications when the given publishes a post
|
||||||
|
"""
|
||||||
|
if followingNickname == nickname and followingDomain == domain:
|
||||||
|
return False
|
||||||
|
notifyOnPostFilename = \
|
||||||
|
baseDir + '/accounts/' + nickname + '@' + domain + '/notifyOnPost.txt'
|
||||||
|
handle = followingNickname + '@' + followingDomain
|
||||||
|
if not os.path.isfile(notifyOnPostFilename):
|
||||||
|
# create a new notifyOnPost file
|
||||||
|
with open(notifyOnPostFilename, 'w+') as fp:
|
||||||
|
fp.write('')
|
||||||
|
return handle + '\n' in open(notifyOnPostFilename).read()
|
||||||
|
|
@ -215,6 +215,21 @@ function notifications {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# send notifications for posts arriving from a particular person
|
||||||
|
epicyonNotifyFile="$epicyonDir/.newNotifiedPost"
|
||||||
|
if [ -f "$epicyonNotifyFile" ]; then
|
||||||
|
if ! grep -q "##sent##" "$epicyonNotifyFile"; then
|
||||||
|
epicyonNotifyMessage=$(notification_translate_text 'New post')
|
||||||
|
epicyonNotifyFileContent=$(echo "$epicyonNotifyMessage")" "$(cat "$epicyonNotifyFile")
|
||||||
|
if [[ "$epicyonNotifyFileContent" == *':'* ]]; then
|
||||||
|
epicyonNotifyMessage="Epicyon: $epicyonNotifyFileContent"
|
||||||
|
fi
|
||||||
|
sendNotification "$USERNAME" "Epicyon" "$epicyonNotifyMessage"
|
||||||
|
echo "##sent##" > "$epicyonNotifyFile"
|
||||||
|
chown ${PROJECT_NAME}:${PROJECT_NAME} "$epicyonNotifyFile"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# send notifications for replies to XMPP/email users
|
# send notifications for replies to XMPP/email users
|
||||||
epicyonReplyFile="$epicyonDir/.newReply"
|
epicyonReplyFile="$epicyonDir/.newReply"
|
||||||
if [ -f "$epicyonReplyFile" ]; then
|
if [ -f "$epicyonReplyFile" ]; then
|
||||||
|
|
|
||||||
81
tests.py
81
tests.py
|
|
@ -94,6 +94,7 @@ from inbox import jsonPostAllowsComments
|
||||||
from inbox import validInbox
|
from inbox import validInbox
|
||||||
from inbox import validInboxFilenames
|
from inbox import validInboxFilenames
|
||||||
from categories import guessHashtagCategory
|
from categories import guessHashtagCategory
|
||||||
|
from content import switchWords
|
||||||
from content import extractTextFieldsInPOST
|
from content import extractTextFieldsInPOST
|
||||||
from content import validHashTag
|
from content import validHashTag
|
||||||
from content import htmlReplaceEmailQuote
|
from content import htmlReplaceEmailQuote
|
||||||
|
|
@ -3384,62 +3385,6 @@ def _testFunctions():
|
||||||
_diagramGroups(['Core', 'Accessibility'], ['utils'],
|
_diagramGroups(['Core', 'Accessibility'], ['utils'],
|
||||||
modules, modGroups, maxModuleCalls)
|
modules, modGroups, maxModuleCalls)
|
||||||
|
|
||||||
callGraphStr = 'digraph Epicyon {\n\n'
|
|
||||||
callGraphStr += ' size="8,6"; ratio=fill;\n'
|
|
||||||
callGraphStr += ' graph [fontsize=10 fontname="Verdana" compound=true];\n'
|
|
||||||
callGraphStr += ' node [shape=record fontsize=10 fontname="Verdana"];\n\n'
|
|
||||||
|
|
||||||
for modName, modProperties in modules.items():
|
|
||||||
callGraphStr += ' subgraph cluster_' + modName + ' {\n'
|
|
||||||
callGraphStr += ' label = "' + modName + '";\n'
|
|
||||||
callGraphStr += ' node [style=filled];\n'
|
|
||||||
moduleFunctionsStr = ''
|
|
||||||
for name in modProperties['functions']:
|
|
||||||
if name.startswith('test'):
|
|
||||||
continue
|
|
||||||
if name not in excludeFuncs:
|
|
||||||
if not functionProperties[name]['calls']:
|
|
||||||
moduleFunctionsStr += \
|
|
||||||
' "' + name + '" [fillcolor=yellow style=filled];\n'
|
|
||||||
continue
|
|
||||||
noOfCalls = len(functionProperties[name]['calls'])
|
|
||||||
if noOfCalls < int(maxFunctionCalls / 4):
|
|
||||||
moduleFunctionsStr += ' "' + name + \
|
|
||||||
'" [fillcolor=orange style=filled];\n'
|
|
||||||
else:
|
|
||||||
moduleFunctionsStr += ' "' + name + \
|
|
||||||
'" [fillcolor=red style=filled];\n'
|
|
||||||
|
|
||||||
if moduleFunctionsStr:
|
|
||||||
callGraphStr += moduleFunctionsStr + '\n'
|
|
||||||
callGraphStr += ' color=blue;\n'
|
|
||||||
callGraphStr += ' }\n\n'
|
|
||||||
|
|
||||||
for name, properties in functionProperties.items():
|
|
||||||
if not properties['calls']:
|
|
||||||
continue
|
|
||||||
noOfCalls = len(properties['calls'])
|
|
||||||
if noOfCalls <= int(maxFunctionCalls / 8):
|
|
||||||
modColor = 'blue'
|
|
||||||
elif noOfCalls < int(maxFunctionCalls / 4):
|
|
||||||
modColor = 'green'
|
|
||||||
else:
|
|
||||||
modColor = 'red'
|
|
||||||
for calledFunc in properties['calls']:
|
|
||||||
if calledFunc.startswith('test'):
|
|
||||||
continue
|
|
||||||
if calledFunc not in excludeFuncs:
|
|
||||||
callGraphStr += ' "' + name + '" -> "' + calledFunc + \
|
|
||||||
'" [color=' + modColor + '];\n'
|
|
||||||
|
|
||||||
callGraphStr += '\n}\n'
|
|
||||||
with open('epicyon.dot', 'w+') as fp:
|
|
||||||
fp.write(callGraphStr)
|
|
||||||
print('Call graph saved to epicyon.dot')
|
|
||||||
print('Plot using: ' +
|
|
||||||
'sfdp -x -Goverlap=prism -Goverlap_scaling=8 ' +
|
|
||||||
'-Gsep=+120 -Tx11 epicyon.dot')
|
|
||||||
|
|
||||||
|
|
||||||
def _testLinksWithinPost() -> None:
|
def _testLinksWithinPost() -> None:
|
||||||
baseDir = os.getcwd()
|
baseDir = os.getcwd()
|
||||||
|
|
@ -4173,9 +4118,33 @@ def _testUserAgentDomain() -> None:
|
||||||
assert userAgentDomain(userAgent, False) is None
|
assert userAgentDomain(userAgent, False) is None
|
||||||
|
|
||||||
|
|
||||||
|
def _testSwitchWords() -> None:
|
||||||
|
print('testSwitchWords')
|
||||||
|
rules = [
|
||||||
|
"rock -> hamster",
|
||||||
|
"orange -> lemon"
|
||||||
|
]
|
||||||
|
baseDir = os.getcwd()
|
||||||
|
nickname = 'testuser'
|
||||||
|
domain = 'testdomain.com'
|
||||||
|
|
||||||
|
content = 'This is a test'
|
||||||
|
result = switchWords(baseDir, nickname, domain, content, rules)
|
||||||
|
assert result == content
|
||||||
|
|
||||||
|
content = 'This is orange test'
|
||||||
|
result = switchWords(baseDir, nickname, domain, content, rules)
|
||||||
|
assert result == 'This is lemon test'
|
||||||
|
|
||||||
|
content = 'This is a test rock'
|
||||||
|
result = switchWords(baseDir, nickname, domain, content, rules)
|
||||||
|
assert result == 'This is a test hamster'
|
||||||
|
|
||||||
|
|
||||||
def runAllTests():
|
def runAllTests():
|
||||||
print('Running tests...')
|
print('Running tests...')
|
||||||
updateDefaultThemesList(os.getcwd())
|
updateDefaultThemesList(os.getcwd())
|
||||||
|
_testSwitchWords()
|
||||||
_testFunctions()
|
_testFunctions()
|
||||||
_testUserAgentDomain()
|
_testUserAgentDomain()
|
||||||
_testRoles()
|
_testRoles()
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "استيراد الموضوع",
|
"Import Theme": "استيراد الموضوع",
|
||||||
"Export Theme": "موضوع التصدير",
|
"Export Theme": "موضوع التصدير",
|
||||||
"Custom post submit button text": "عرف نشر إرسال نص زر",
|
"Custom post submit button text": "عرف نشر إرسال نص زر",
|
||||||
"Blocked User Agents": "عوامل المستخدم المحظورة"
|
"Blocked User Agents": "عوامل المستخدم المحظورة",
|
||||||
|
"Notify me when this account posts": "أعلمني عندما ينشر الحساب هذا"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Importació temàtica",
|
"Import Theme": "Importació temàtica",
|
||||||
"Export Theme": "Tema d'exportació",
|
"Export Theme": "Tema d'exportació",
|
||||||
"Custom post submit button text": "Text de botó d'enviament de publicacions personalitzades",
|
"Custom post submit button text": "Text de botó d'enviament de publicacions personalitzades",
|
||||||
"Blocked User Agents": "Agents d'usuari bloquejats"
|
"Blocked User Agents": "Agents d'usuari bloquejats",
|
||||||
|
"Notify me when this account posts": "Aviseu-me quan publiqui aquest compte"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Thema Mewnforio",
|
"Import Theme": "Thema Mewnforio",
|
||||||
"Export Theme": "Thema Allforio",
|
"Export Theme": "Thema Allforio",
|
||||||
"Custom post submit button text": "Testun Post Post Post",
|
"Custom post submit button text": "Testun Post Post Post",
|
||||||
"Blocked User Agents": "Asiantau defnyddwyr wedi'u blocio"
|
"Blocked User Agents": "Asiantau defnyddwyr wedi'u blocio",
|
||||||
|
"Notify me when this account posts": "Rhoi gwybod i mi pan fydd y cyfrifon cyfrif hwn"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Theme importieren",
|
"Import Theme": "Theme importieren",
|
||||||
"Export Theme": "Theme exportieren",
|
"Export Theme": "Theme exportieren",
|
||||||
"Custom post submit button text": "Benutzerdefinierte Post-Senden Schaltfläche Text",
|
"Custom post submit button text": "Benutzerdefinierte Post-Senden Schaltfläche Text",
|
||||||
"Blocked User Agents": "Blockierte Benutzeragenten"
|
"Blocked User Agents": "Blockierte Benutzeragenten",
|
||||||
|
"Notify me when this account posts": "Benachrichtigen Sie mich, wenn dieses Konto postet"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Import Theme",
|
"Import Theme": "Import Theme",
|
||||||
"Export Theme": "Export Theme",
|
"Export Theme": "Export Theme",
|
||||||
"Custom post submit button text": "Custom post submit button text",
|
"Custom post submit button text": "Custom post submit button text",
|
||||||
"Blocked User Agents": "Blocked User Agents"
|
"Blocked User Agents": "Blocked User Agents",
|
||||||
|
"Notify me when this account posts": "Notify me when this account posts"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Tema de importación",
|
"Import Theme": "Tema de importación",
|
||||||
"Export Theme": "Tema de exportación",
|
"Export Theme": "Tema de exportación",
|
||||||
"Custom post submit button text": "POST POST PERSONALIZADO Botón Texto",
|
"Custom post submit button text": "POST POST PERSONALIZADO Botón Texto",
|
||||||
"Blocked User Agents": "Agentes de usuario bloqueados"
|
"Blocked User Agents": "Agentes de usuario bloqueados",
|
||||||
|
"Notify me when this account posts": "Notifíqueme cuando se publique esta cuenta"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Import thème",
|
"Import Theme": "Import thème",
|
||||||
"Export Theme": "Thème d'exportation",
|
"Export Theme": "Thème d'exportation",
|
||||||
"Custom post submit button text": "Texte de bouton d'envoi postal personnalisé",
|
"Custom post submit button text": "Texte de bouton d'envoi postal personnalisé",
|
||||||
"Blocked User Agents": "Agents d'utilisateur bloqués"
|
"Blocked User Agents": "Agents d'utilisateur bloqués",
|
||||||
|
"Notify me when this account posts": "Avertissez-moi quand ce compte publie"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Téama Iompórtáil",
|
"Import Theme": "Téama Iompórtáil",
|
||||||
"Export Theme": "Téama Easpórtála",
|
"Export Theme": "Téama Easpórtála",
|
||||||
"Custom post submit button text": "Post saincheaptha Cuir isteach an cnaipe Téacs",
|
"Custom post submit button text": "Post saincheaptha Cuir isteach an cnaipe Téacs",
|
||||||
"Blocked User Agents": "Gníomhairí úsáideora blocáilte"
|
"Blocked User Agents": "Gníomhairí úsáideora blocáilte",
|
||||||
|
"Notify me when this account posts": "Cuir in iúl dom nuair a phostófar an cuntas seo"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "आयात विषय",
|
"Import Theme": "आयात विषय",
|
||||||
"Export Theme": "निर्यात विषय",
|
"Export Theme": "निर्यात विषय",
|
||||||
"Custom post submit button text": "कस्टम पोस्ट सबमिट बटन टेक्स्ट",
|
"Custom post submit button text": "कस्टम पोस्ट सबमिट बटन टेक्स्ट",
|
||||||
"Blocked User Agents": "अवरुद्ध उपयोगकर्ता एजेंट"
|
"Blocked User Agents": "अवरुद्ध उपयोगकर्ता एजेंट",
|
||||||
|
"Notify me when this account posts": "यह खाता पोस्ट होने पर मुझे सूचित करें"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Tema dell'importazione",
|
"Import Theme": "Tema dell'importazione",
|
||||||
"Export Theme": "Esportare tema",
|
"Export Theme": "Esportare tema",
|
||||||
"Custom post submit button text": "Pulsante di invio del post personalizzato",
|
"Custom post submit button text": "Pulsante di invio del post personalizzato",
|
||||||
"Blocked User Agents": "Agenti utente bloccati"
|
"Blocked User Agents": "Agenti utente bloccati",
|
||||||
|
"Notify me when this account posts": "Avvisami quando questo account messaggi"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "輸入テーマ",
|
"Import Theme": "輸入テーマ",
|
||||||
"Export Theme": "テーマをエクスポートします",
|
"Export Theme": "テーマをエクスポートします",
|
||||||
"Custom post submit button text": "カスタムポスト送信ボタンテキスト",
|
"Custom post submit button text": "カスタムポスト送信ボタンテキスト",
|
||||||
"Blocked User Agents": "ブロックされたユーザーエージェント"
|
"Blocked User Agents": "ブロックされたユーザーエージェント",
|
||||||
|
"Notify me when this account posts": "この口座投稿を通知する"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Mijara Import",
|
"Import Theme": "Mijara Import",
|
||||||
"Export Theme": "Mijara Export",
|
"Export Theme": "Mijara Export",
|
||||||
"Custom post submit button text": "Nivîsa bişkojka paşîn a paşîn",
|
"Custom post submit button text": "Nivîsa bişkojka paşîn a paşîn",
|
||||||
"Blocked User Agents": "Karmendên bikarhêner asteng kirin"
|
"Blocked User Agents": "Karmendên bikarhêner asteng kirin",
|
||||||
|
"Notify me when this account posts": "Dema ku ev postên hesabê min agahdar bikin"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -445,5 +445,6 @@
|
||||||
"Import Theme": "Import Theme",
|
"Import Theme": "Import Theme",
|
||||||
"Export Theme": "Export Theme",
|
"Export Theme": "Export Theme",
|
||||||
"Custom post submit button text": "Custom post submit button text",
|
"Custom post submit button text": "Custom post submit button text",
|
||||||
"Blocked User Agents": "Blocked User Agents"
|
"Blocked User Agents": "Blocked User Agents",
|
||||||
|
"Notify me when this account posts": "Notify me when this account posts"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Importar tema",
|
"Import Theme": "Importar tema",
|
||||||
"Export Theme": "Exportar tema",
|
"Export Theme": "Exportar tema",
|
||||||
"Custom post submit button text": "Texto de botão de envio de post personalizado",
|
"Custom post submit button text": "Texto de botão de envio de post personalizado",
|
||||||
"Blocked User Agents": "Agentes de usuário bloqueados"
|
"Blocked User Agents": "Agentes de usuário bloqueados",
|
||||||
|
"Notify me when this account posts": "Notifique-me quando esta conta posts"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Импортировать тему",
|
"Import Theme": "Импортировать тему",
|
||||||
"Export Theme": "Экспортная тема",
|
"Export Theme": "Экспортная тема",
|
||||||
"Custom post submit button text": "Пользовательский пост Отправить кнопку текста",
|
"Custom post submit button text": "Пользовательский пост Отправить кнопку текста",
|
||||||
"Blocked User Agents": "Заблокированные пользовательские агенты"
|
"Blocked User Agents": "Заблокированные пользовательские агенты",
|
||||||
|
"Notify me when this account posts": "Сообщите мне, когда эта учетная запись"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "Ingiza mandhari",
|
"Import Theme": "Ingiza mandhari",
|
||||||
"Export Theme": "Tuma mandhari",
|
"Export Theme": "Tuma mandhari",
|
||||||
"Custom post submit button text": "Ujumbe wa Desturi Wasilisha Nakala ya kifungo",
|
"Custom post submit button text": "Ujumbe wa Desturi Wasilisha Nakala ya kifungo",
|
||||||
"Blocked User Agents": "Wakala wa watumiaji waliozuiwa"
|
"Blocked User Agents": "Wakala wa watumiaji waliozuiwa",
|
||||||
|
"Notify me when this account posts": "Nijulishe wakati akaunti hii ya akaunti."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -449,5 +449,6 @@
|
||||||
"Import Theme": "进口主题",
|
"Import Theme": "进口主题",
|
||||||
"Export Theme": "出口主题",
|
"Export Theme": "出口主题",
|
||||||
"Custom post submit button text": "自定义发布提交按钮文本",
|
"Custom post submit button text": "自定义发布提交按钮文本",
|
||||||
"Blocked User Agents": "阻止用户代理商"
|
"Blocked User Agents": "阻止用户代理商",
|
||||||
|
"Notify me when this account posts": "此帐户帖子时通知我"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ from blocking import isBlocked
|
||||||
from follow import isFollowerOfPerson
|
from follow import isFollowerOfPerson
|
||||||
from follow import isFollowingActor
|
from follow import isFollowingActor
|
||||||
from followingCalendar import receivingCalendarEvents
|
from followingCalendar import receivingCalendarEvents
|
||||||
|
from notifyOnPost import notifyWhenPersonPosts
|
||||||
from webapp_utils import htmlHeaderWithExternalStyle
|
from webapp_utils import htmlHeaderWithExternalStyle
|
||||||
from webapp_utils import htmlFooter
|
from webapp_utils import htmlFooter
|
||||||
from webapp_utils import getBrokenLinkSubstitute
|
from webapp_utils import getBrokenLinkSubstitute
|
||||||
|
|
@ -246,8 +247,21 @@ def htmlPersonOptions(defaultTimeline: str,
|
||||||
'name="submitPetname">' + \
|
'name="submitPetname">' + \
|
||||||
translate['Submit'] + '</button><br>\n'
|
translate['Submit'] + '</button><br>\n'
|
||||||
|
|
||||||
# checkbox for receiving calendar events
|
# Notify when a post arrives from this person
|
||||||
if isFollowingActor(baseDir, nickname, domain, optionsActor):
|
if isFollowingActor(baseDir, nickname, domain, optionsActor):
|
||||||
|
checkboxStr = \
|
||||||
|
' <input type="checkbox" class="profilecheckbox" ' + \
|
||||||
|
'name="notifyOnPost" checked> 🔔' + \
|
||||||
|
translate['Notify me when this account posts'] + \
|
||||||
|
'\n <button type="submit" class="buttonsmall" ' + \
|
||||||
|
'name="submitNotifyOnPost">' + \
|
||||||
|
translate['Submit'] + '</button><br>\n'
|
||||||
|
if not notifyWhenPersonPosts(baseDir, nickname, domain,
|
||||||
|
optionsNickname,
|
||||||
|
optionsDomainFull):
|
||||||
|
checkboxStr = checkboxStr.replace(' checked>', '>')
|
||||||
|
optionsStr += checkboxStr
|
||||||
|
|
||||||
checkboxStr = \
|
checkboxStr = \
|
||||||
' <input type="checkbox" ' + \
|
' <input type="checkbox" ' + \
|
||||||
'class="profilecheckbox" name="onCalendar" checked> ' + \
|
'class="profilecheckbox" name="onCalendar" checked> ' + \
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue