master
Bob Mottram 2019-11-03 13:43:47 +00:00
parent a6dc579aca
commit fc2732a5ed
10 changed files with 587 additions and 273 deletions

View File

@ -58,9 +58,13 @@ def createAcceptReject(baseDir: str,federationList: [], \
def createAccept(baseDir: str,federationList: [], \ def createAccept(baseDir: str,federationList: [], \
nickname: str,domain: str,port: int, \ nickname: str,domain: str,port: int, \
toUrl: str,ccUrl: str,httpPrefix: str, \ toUrl: str,ccUrl: str,httpPrefix: str, \
objectJson: {},acceptedCaps=["inbox:write","objects:read"]) -> {}: objectJson: {}, \
acceptedCaps=["inbox:write","objects:read"]) -> {}:
# create capabilities accept # create capabilities accept
ocapNew=capabilitiesAccept(baseDir,httpPrefix,nickname,domain,port,toUrl,True,acceptedCaps) ocapNew= \
capabilitiesAccept(baseDir,httpPrefix, \
nickname,domain,port, \
toUrl,True,acceptedCaps)
return createAcceptReject(baseDir,federationList, \ return createAcceptReject(baseDir,federationList, \
nickname,domain,port, \ nickname,domain,port, \
toUrl,ccUrl,httpPrefix, \ toUrl,ccUrl,httpPrefix, \
@ -126,7 +130,7 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
print('DEBUG: nickname not found in '+thisActor) print('DEBUG: nickname not found in '+thisActor)
return return
if acceptedPort: if acceptedPort:
if not '/'+acceptedDomain+':'+str(acceptedPort)+'/users/'+nickname in thisActor: if '/'+acceptedDomain+':'+str(acceptedPort)+'/users/'+nickname not in thisActor:
if debug: if debug:
print('Port: '+str(acceptedPort)) print('Port: '+str(acceptedPort))
print('Expected: /'+acceptedDomain+':'+str(acceptedPort)+'/users/'+nickname) print('Expected: /'+acceptedDomain+':'+str(acceptedPort)+'/users/'+nickname)
@ -134,7 +138,7 @@ def acceptFollow(baseDir: str,domain : str,messageJson: {}, \
print('DEBUG: unrecognized actor '+thisActor) print('DEBUG: unrecognized actor '+thisActor)
return return
else: else:
if not '/'+acceptedDomain+'/users/'+nickname in thisActor: if '/'+acceptedDomain+'/users/'+nickname not in thisActor:
if debug: if debug:
print('Expected: /'+acceptedDomain+'/users/'+nickname) print('Expected: /'+acceptedDomain+'/users/'+nickname)
print('Actual: '+thisActor) print('Actual: '+thisActor)

View File

@ -62,7 +62,8 @@ def outboxAnnounce(baseDir: str,messageJson: {},debug: bool) -> bool:
return True return True
return False return False
def undoAnnounceCollectionEntry(postFilename: str,actor: str,debug: bool) -> None: def undoAnnounceCollectionEntry(postFilename: str,actor: str, \
debug: bool) -> None:
"""Undoes an announce for a particular actor by removing it from the "shares" """Undoes an announce for a particular actor by removing it from the "shares"
collection within a post. Note that the "shares" collection has no relation collection within a post. Note that the "shares" collection has no relation
to shared items in shares.py. It's shares of posts, not shares of physical objects. to shared items in shares.py. It's shares of posts, not shares of physical objects.
@ -102,14 +103,15 @@ def undoAnnounceCollectionEntry(postFilename: str,actor: str,debug: bool) -> Non
print('DEBUG: shares (announcements) was removed from post') print('DEBUG: shares (announcements) was removed from post')
del postJsonObject['object']['shares'] del postJsonObject['object']['shares']
else: else:
postJsonObject['object']['shares']['totalItems']=len(postJsonObject['object']['shares']['items']) postJsonObject['object']['shares']['totalItems']= \
len(postJsonObject['object']['shares']['items'])
with open(postFilename, 'w') as fp: with open(postFilename, 'w') as fp:
commentjson.dump(postJsonObject, fp, indent=4, sort_keys=True) commentjson.dump(postJsonObject, fp, indent=4, sort_keys=True)
def updateAnnounceCollection(postFilename: str,actor: str,debug: bool) -> None: def updateAnnounceCollection(postFilename: str,actor: str,debug: bool) -> None:
"""Updates the announcements collection within a post """Updates the announcements collection within a post
Confusingly this is known as "shares", but isn't the same as shared items within shares.py Confusingly this is known as "shares", but isn't the same as shared
It's shares of posts, not shares of physical objects. items within shares.py. It's shares of posts, not shares of physical objects.
""" """
with open(postFilename, 'r') as fp: with open(postFilename, 'r') as fp:
postJsonObject=commentjson.load(fp) postJsonObject=commentjson.load(fp)
@ -146,7 +148,8 @@ def updateAnnounceCollection(postFilename: str,actor: str,debug: bool) -> None:
'actor': actor 'actor': actor
} }
postJsonObject['object']['shares']['items'].append(newAnnounce) postJsonObject['object']['shares']['items'].append(newAnnounce)
postJsonObject['object']['shares']['totalItems']=len(postJsonObject['object']['shares']['items']) postJsonObject['object']['shares']['totalItems']= \
len(postJsonObject['object']['shares']['items'])
else: else:
if debug: if debug:
print('DEBUG: shares (announcements) section of post has no items list') print('DEBUG: shares (announcements) section of post has no items list')

View File

@ -46,7 +46,8 @@ def getAvailability(baseDir: str,nickname: str,domain: str) -> str:
return actorJson['availability'] return actorJson['availability']
return None return None
def outboxAvailability(baseDir: str,nickname: str,messageJson: {},debug: bool) -> bool: def outboxAvailability(baseDir: str,nickname: str,messageJson: {}, \
debug: bool) -> bool:
"""Handles receiving an availability update """Handles receiving an availability update
""" """
if not messageJson.get('type'): if not messageJson.get('type'):

View File

@ -11,7 +11,8 @@ import time
import datetime import datetime
import commentjson import commentjson
def storePersonInCache(baseDir: str,personUrl: str,personJson: {},personCache: {}) -> None: def storePersonInCache(baseDir: str,personUrl: str, \
personJson: {},personCache: {}) -> None:
"""Store an actor in the cache """Store an actor in the cache
""" """
currTime=datetime.datetime.utcnow() currTime=datetime.datetime.utcnow()
@ -35,7 +36,8 @@ def getPersonFromCache(baseDir: str,personUrl: str,personCache: {}) -> {}:
# if the actor is not in memory then try to load it from file # if the actor is not in memory then try to load it from file
loadedFromFile=False loadedFromFile=False
if not personCache.get(personUrl): if not personCache.get(personUrl):
cacheFilename=baseDir+'/cache/actors/'+personUrl.replace('/','#')+'.json' cacheFilename= \
baseDir+'/cache/actors/'+personUrl.replace('/','#')+'.json'
if os.path.isfile(cacheFilename): if os.path.isfile(cacheFilename):
personJson=None personJson=None
try: try:
@ -52,11 +54,12 @@ def getPersonFromCache(baseDir: str,personUrl: str,personCache: {}) -> {}:
if not loadedFromFile: if not loadedFromFile:
# update the timestamp for the last time the actor was retrieved # update the timestamp for the last time the actor was retrieved
currTime=datetime.datetime.utcnow() currTime=datetime.datetime.utcnow()
personCache[personUrl]['timestamp']=currTime.strftime("%Y-%m-%dT%H:%M:%SZ") personCache[personUrl]['timestamp']= \
currTime.strftime("%Y-%m-%dT%H:%M:%SZ")
return personCache[personUrl]['actor'] return personCache[personUrl]['actor']
return None return None
def expirePersonCache(personCache: {}): def expirePersonCache(personCache: {}) -> None:
"""Expires old entries from the cache in memory """Expires old entries from the cache in memory
""" """
currTime=datetime.datetime.utcnow() currTime=datetime.datetime.utcnow()

View File

@ -15,7 +15,8 @@ from auth import createPassword
from utils import getNicknameFromActor from utils import getNicknameFromActor
from utils import getDomainFromActor from utils import getDomainFromActor
def getOcapFilename(baseDir :str,nickname: str,domain: str,actor :str,subdir: str) -> str: def getOcapFilename(baseDir :str,nickname: str,domain: str, \
actor :str,subdir: str) -> str:
"""Returns the filename for a particular capability accepted or granted """Returns the filename for a particular capability accepted or granted
Also creates directories as needed Also creates directories as needed
""" """
@ -40,7 +41,8 @@ def getOcapFilename(baseDir :str,nickname: str,domain: str,actor :str,subdir: st
if not os.path.isdir(ocDir): if not os.path.isdir(ocDir):
os.mkdir(ocDir) os.mkdir(ocDir)
return baseDir+'/accounts/'+nickname+'@'+domain+'/ocap/'+subdir+'/'+actor.replace('/','#')+'.json' return baseDir+'/accounts/'+nickname+'@'+domain+'/ocap/'+ \
subdir+'/'+actor.replace('/','#')+'.json'
def CapablePost(postJson: {}, capabilityList: [], debug :bool) -> bool: def CapablePost(postJson: {}, capabilityList: [], debug :bool) -> bool:
"""Determines whether a post arriving in the inbox """Determines whether a post arriving in the inbox
@ -120,7 +122,8 @@ def capabilitiesAccept(baseDir: str,httpPrefix: str, \
fullDomain=domain+':'+str(port) fullDomain=domain+':'+str(port)
# make directories to store capabilities # make directories to store capabilities
ocapFilename=getOcapFilename(baseDir,nickname,fullDomain,acceptedActor,'accept') ocapFilename= \
getOcapFilename(baseDir,nickname,fullDomain,acceptedActor,'accept')
if not ocapFilename: if not ocapFilename:
return None return None
ocapAccept=None ocapAccept=None
@ -137,9 +140,11 @@ def capabilitiesAccept(baseDir: str,httpPrefix: str, \
return None return None
acceptedActorDomain,acceptedActorPort=getDomainFromActor(acceptedActor) acceptedActorDomain,acceptedActorPort=getDomainFromActor(acceptedActor)
if acceptedActorPort: if acceptedActorPort:
ocapId=acceptedActorNickname+'@'+acceptedActorDomain+':'+str(acceptedActorPort)+'#'+createPassword(32) ocapId=acceptedActorNickname+'@'+acceptedActorDomain+':'+ \
str(acceptedActorPort)+'#'+createPassword(32)
else: else:
ocapId=acceptedActorNickname+'@'+acceptedActorDomain+'#'+createPassword(32) ocapId=acceptedActorNickname+'@'+acceptedActorDomain+'#'+ \
createPassword(32)
ocapAccept = { ocapAccept = {
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
"id": httpPrefix+"://"+fullDomain+"/caps/"+ocapId, "id": httpPrefix+"://"+fullDomain+"/caps/"+ocapId,
@ -156,13 +161,15 @@ def capabilitiesAccept(baseDir: str,httpPrefix: str, \
commentjson.dump(ocapAccept, fp, indent=4, sort_keys=False) commentjson.dump(ocapAccept, fp, indent=4, sort_keys=False)
return ocapAccept return ocapAccept
def capabilitiesGrantedSave(baseDir :str,nickname :str,domain :str,ocap: {}) -> bool: def capabilitiesGrantedSave(baseDir :str, \
nickname :str,domain :str,ocap: {}) -> bool:
"""A capabilities accept is received, so stor it for """A capabilities accept is received, so stor it for
reference when sending to the actor reference when sending to the actor
""" """
if not ocap.get('actor'): if not ocap.get('actor'):
return False return False
ocapFilename=getOcapFilename(baseDir,nickname,domain,ocap['actor'],'granted') ocapFilename= \
getOcapFilename(baseDir,nickname,domain,ocap['actor'],'granted')
if not ocapFilename: if not ocapFilename:
return False return False
with open(ocapFilename, 'w') as fp: with open(ocapFilename, 'w') as fp:
@ -189,7 +196,8 @@ def capabilitiesUpdate(baseDir: str,httpPrefix: str, \
fullDomain=domain+':'+str(port) fullDomain=domain+':'+str(port)
# Get the filename of the capability # Get the filename of the capability
ocapFilename=getOcapFilename(baseDir,nickname,fullDomain,updateActor,'accept') ocapFilename= \
getOcapFilename(baseDir,nickname,fullDomain,updateActor,'accept')
if not ocapFilename: if not ocapFilename:
return None return None
@ -221,7 +229,8 @@ def capabilitiesUpdate(baseDir: str,httpPrefix: str, \
return None return None
updateActorDomain,updateActorPort=getDomainFromActor(updateActor) updateActorDomain,updateActorPort=getDomainFromActor(updateActor)
if updateActorPort: if updateActorPort:
ocapId=updateActorNickname+'@'+updateActorDomain+':'+str(updateActorPort)+'#'+createPassword(32) ocapId=updateActorNickname+'@'+updateActorDomain+':'+ \
str(updateActorPort)+'#'+createPassword(32)
else: else:
ocapId=updateActorNickname+'@'+updateActorDomain+'#'+createPassword(32) ocapId=updateActorNickname+'@'+updateActorDomain+'#'+createPassword(32)
ocapJson['id']=httpPrefix+"://"+fullDomain+"/caps/"+ocapId ocapJson['id']=httpPrefix+"://"+fullDomain+"/caps/"+ocapId

View File

@ -32,7 +32,7 @@ def setConfigParam(baseDir: str, variableName: str, variableValue) -> None:
with open(configFilename, 'w') as fp: with open(configFilename, 'w') as fp:
commentjson.dump(configJson, fp, indent=4, sort_keys=True) commentjson.dump(configJson, fp, indent=4, sort_keys=True)
def getConfigParam(baseDir: str, variableName: str): def getConfigParam(baseDir: str, variableName: str) -> None:
"""Gets a configuration value """Gets a configuration value
""" """
createConfig(baseDir) createConfig(baseDir)

View File

@ -30,13 +30,17 @@ def addMusicTag(content: str,tag: str) -> str:
def addWebLinks(content: str) -> str: def addWebLinks(content: str) -> str:
"""Adds markup for web links """Adds markup for web links
""" """
if not ('https://' in content or 'http://' in content or 'dat://' in content): if not ('https://' in content or \
'http://' in content or \
'dat://' in content):
return content return content
words=content.replace('\n',' --linebreak--').split(' ') words=content.replace('\n',' --linebreak--').split(' ')
replaceDict={} replaceDict={}
for w in words: for w in words:
if w.startswith('https://') or w.startswith('http://') or w.startswith('dat://'): if w.startswith('https://') or \
w.startswith('http://') or \
w.startswith('dat://'):
if w.endswith('.') or w.endswith(';'): if w.endswith('.') or w.endswith(';'):
w=w[:-1] w=w[:-1]
markup='<a href="'+w+'" rel="nofollow noopener" target="_blank">' markup='<a href="'+w+'" rel="nofollow noopener" target="_blank">'
@ -46,7 +50,9 @@ def addWebLinks(content: str) -> str:
markup+='<span class="invisible">http://</span>' markup+='<span class="invisible">http://</span>'
elif w.startswith('dat://'): elif w.startswith('dat://'):
markup+='<span class="invisible">dat://</span>' markup+='<span class="invisible">dat://</span>'
markup+='<span class="ellipsis">'+w.replace('https://','').replace('http://','').replace('dat://','')+'</span></a>' markup+='<span class="ellipsis">'+ \
w.replace('https://','').replace('http://','').replace('dat://','')+ \
'</span></a>'
replaceDict[w]=markup replaceDict[w]=markup
for url,markup in replaceDict.items(): for url,markup in replaceDict.items():
content=content.replace(url,markup) content=content.replace(url,markup)
@ -56,12 +62,15 @@ def addWebLinks(content: str) -> str:
def validHashTag(hashtag: str) -> bool: def validHashTag(hashtag: str) -> bool:
"""Returns true if the give hashtag contains valid characters """Returns true if the give hashtag contains valid characters
""" """
validChars = set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') validChars = \
set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')
if set(hashtag).issubset(validChars): if set(hashtag).issubset(validChars):
return True return True
return False return False
def addHashTags(wordStr: str,httpPrefix: str,domain: str,replaceHashTags: {},postHashtags: {}) -> bool: def addHashTags(wordStr: str, \
httpPrefix: str,domain: str, \
replaceHashTags: {},postHashtags: {}) -> bool:
"""Detects hashtags and adds them to the replacements dict """Detects hashtags and adds them to the replacements dict
Also updates the hashtags list to be added to the post Also updates the hashtags list to be added to the post
""" """
@ -81,7 +90,9 @@ def addHashTags(wordStr: str,httpPrefix: str,domain: str,replaceHashTags: {},pos
'type': 'Hashtag' 'type': 'Hashtag'
} }
replaceHashTags[wordStr]= \ replaceHashTags[wordStr]= \
"<a href=\""+hashtagUrl+"\" class=\"mention hashtag\" rel=\"tag\">#<span>"+hashtag+"</span></a>" "<a href=\""+hashtagUrl+ \
"\" class=\"mention hashtag\" rel=\"tag\">#<span>"+ \
hashtag+"</span></a>"
return True return True
def loadEmojiDict(emojiDataFilename: str,emojiDict: {}) -> None: def loadEmojiDict(emojiDataFilename: str,emojiDict: {}) -> None:
@ -104,12 +115,15 @@ def loadEmojiDict(emojiDataFilename: str,emojiDict: {}) -> None:
continue continue
if '..' in emojiUnicode: if '..' in emojiUnicode:
emojiUnicode=emojiUnicode.split('..')[0] emojiUnicode=emojiUnicode.split('..')[0]
emojiName=line.split(')',1)[1].strip().replace('\n','').replace(' ','').replace('-','') emojiName= \
line.split(')',1)[1].strip().replace('\n','').replace(' ','').replace('-','')
if '..' in emojiName: if '..' in emojiName:
emojiName=emojiName.split('..')[0] emojiName=emojiName.split('..')[0]
emojiDict[emojiName.lower()]=emojiUnicode emojiDict[emojiName.lower()]=emojiUnicode
def addEmoji(baseDir: str,wordStr: str,httpPrefix: str,domain: str,replaceEmoji: {},postTags: {},emojiDict: {}) -> bool: def addEmoji(baseDir: str,wordStr: str, \
httpPrefix: str,domain: str, \
replaceEmoji: {},postTags: {},emojiDict: {}) -> bool:
"""Detects Emoji and adds them to the replacements dict """Detects Emoji and adds them to the replacements dict
Also updates the tags list to be added to the post Also updates the tags list to be added to the post
""" """
@ -142,7 +156,9 @@ def addEmoji(baseDir: str,wordStr: str,httpPrefix: str,domain: str,replaceEmoji:
} }
return True return True
def addMention(wordStr: str,httpPrefix: str,following: str,replaceMentions: {},recipients: [],tags: {}) -> bool: def addMention(wordStr: str, \
httpPrefix: str,following: str, \
replaceMentions: {},recipients: [],tags: {}) -> bool:
"""Detects mentions and adds them to the replacements dict and recipients list """Detects mentions and adds them to the replacements dict and recipients list
""" """
if not wordStr.startswith('@'): if not wordStr.startswith('@'):
@ -158,7 +174,8 @@ def addMention(wordStr: str,httpPrefix: str,following: str,replaceMentions: {},r
for follow in following: for follow in following:
if follow.startswith(possibleNickname+'@'): if follow.startswith(possibleNickname+'@'):
replaceDomain=follow.replace('\n','').split('@')[1] replaceDomain=follow.replace('\n','').split('@')[1]
recipientActor=httpPrefix+"://"+replaceDomain+"/users/"+possibleNickname recipientActor= \
httpPrefix+"://"+replaceDomain+"/users/"+possibleNickname
if recipientActor not in recipients: if recipientActor not in recipients:
recipients.append(recipientActor) recipients.append(recipientActor)
tags[wordStr]={ tags[wordStr]={
@ -166,7 +183,11 @@ def addMention(wordStr: str,httpPrefix: str,following: str,replaceMentions: {},r
'name': wordStr, 'name': wordStr,
'type': 'Mention' 'type': 'Mention'
} }
replaceMentions[wordStr]="<span class=\"h-card\"><a href=\""+httpPrefix+"://"+replaceDomain+"/@"+possibleNickname+"\" class=\"u-url mention\">@<span>"+possibleNickname+"</span></a></span>" replaceMentions[wordStr]= \
"<span class=\"h-card\"><a href=\""+httpPrefix+ \
"://"+replaceDomain+"/@"+possibleNickname+ \
"\" class=\"u-url mention\">@<span>"+ \
possibleNickname+"</span></a></span>"
return True return True
return False return False
possibleNickname=possibleHandle.split('@')[0] possibleNickname=possibleHandle.split('@')[0]
@ -175,7 +196,8 @@ def addMention(wordStr: str,httpPrefix: str,following: str,replaceMentions: {},r
for follow in following: for follow in following:
if follow.replace('\n','')!=possibleHandle: if follow.replace('\n','')!=possibleHandle:
continue continue
recipientActor=httpPrefix+"://"+possibleDomain+"/users/"+possibleNickname recipientActor= \
httpPrefix+"://"+possibleDomain+"/users/"+possibleNickname
if recipientActor not in recipients: if recipientActor not in recipients:
recipients.append(recipientActor) recipients.append(recipientActor)
tags[wordStr]={ tags[wordStr]={
@ -183,13 +205,18 @@ def addMention(wordStr: str,httpPrefix: str,following: str,replaceMentions: {},r
'name': wordStr, 'name': wordStr,
'type': 'Mention' 'type': 'Mention'
} }
replaceMentions[wordStr]="<span class=\"h-card\"><a href=\""+httpPrefix+"://"+possibleDomain+"/@"+possibleNickname+"\" class=\"u-url mention\">@<span>"+possibleNickname+"</span></a></span>" replaceMentions[wordStr]= \
"<span class=\"h-card\"><a href=\""+httpPrefix+ \
"://"+possibleDomain+"/@"+possibleNickname+ \
"\" class=\"u-url mention\">@<span>"+possibleNickname+ \
"</span></a></span>"
return True return True
# @nick@domain # @nick@domain
if '@' in possibleHandle: if '@' in possibleHandle:
if not (possibleDomain=='localhost' or '.' in possibleDomain): if not (possibleDomain=='localhost' or '.' in possibleDomain):
return False return False
recipientActor=httpPrefix+"://"+possibleDomain+"/users/"+possibleNickname recipientActor= \
httpPrefix+"://"+possibleDomain+"/users/"+possibleNickname
if recipientActor not in recipients: if recipientActor not in recipients:
recipients.append(recipientActor) recipients.append(recipientActor)
tags[wordStr]={ tags[wordStr]={
@ -197,7 +224,11 @@ def addMention(wordStr: str,httpPrefix: str,following: str,replaceMentions: {},r
'name': wordStr, 'name': wordStr,
'type': 'Mention' 'type': 'Mention'
} }
replaceMentions[wordStr]="<span class=\"h-card\"><a href=\""+httpPrefix+"://"+possibleDomain+"/@"+possibleNickname+"\" class=\"u-url mention\">@<span>"+possibleNickname+"</span></a></span>" replaceMentions[wordStr]= \
"<span class=\"h-card\"><a href=\""+httpPrefix+ \
"://"+possibleDomain+"/@"+possibleNickname+ \
"\" class=\"u-url mention\">@<span>"+possibleNickname+ \
"</span></a></span>"
return True return True
return False return False
@ -244,22 +275,27 @@ def addHtmlTags(baseDir: str,httpPrefix: str, \
# extract mentions and tags from words # extract mentions and tags from words
for wordStr in words: for wordStr in words:
if addMention(wordStr,httpPrefix,following,replaceMentions,recipients,hashtags): if addMention(wordStr,httpPrefix,following, \
replaceMentions,recipients,hashtags):
continue continue
if addHashTags(wordStr,httpPrefix,originalDomain,replaceHashTags,hashtags): if addHashTags(wordStr,httpPrefix,originalDomain, \
replaceHashTags,hashtags):
continue continue
if len(wordStr)>2 and wordStr.startswith(':') and wordStr.endswith(':') and not emojiDict: if len(wordStr)>2 and wordStr.startswith(':') and \
wordStr.endswith(':') and not emojiDict:
print('Loading emoji lookup') print('Loading emoji lookup')
# emoji.json is generated so that it can be customized and the changes # emoji.json is generated so that it can be customized and the changes
# will be retained even if default_emoji.json is subsequently updated # will be retained even if default_emoji.json is subsequently updated
if not os.path.isfile(baseDir+'/emoji/emoji.json'): if not os.path.isfile(baseDir+'/emoji/emoji.json'):
copyfile(baseDir+'/emoji/default_emoji.json',baseDir+'/emoji/emoji.json') copyfile(baseDir+'/emoji/default_emoji.json', \
baseDir+'/emoji/emoji.json')
with open(baseDir+'/emoji/emoji.json', 'r') as fp: with open(baseDir+'/emoji/emoji.json', 'r') as fp:
emojiDict=commentjson.load(fp) emojiDict=commentjson.load(fp)
addEmoji(baseDir,wordStr,httpPrefix,originalDomain,replaceEmoji,hashtags,emojiDict) addEmoji(baseDir,wordStr,httpPrefix,originalDomain, \
replaceEmoji,hashtags,emojiDict)
# replace words with their html versions # replace words with their html versions
for wordStr,replaceStr in replaceMentions.items(): for wordStr,replaceStr in replaceMentions.items():
@ -273,7 +309,8 @@ def addHtmlTags(baseDir: str,httpPrefix: str, \
content=content.replace(' --linebreak-- ','</p><p>') content=content.replace(' --linebreak-- ','</p><p>')
return '<p>'+content+'</p>' return '<p>'+content+'</p>'
def getMentionsFromHtml(htmlText: str,matchStr="<span class=\"h-card\"><a href=\"") -> []: def getMentionsFromHtml(htmlText: str, \
matchStr="<span class=\"h-card\"><a href=\"") -> []:
"""Extracts mentioned actors from the given html content string """Extracts mentioned actors from the given html content string
""" """
mentions=[] mentions=[]

603
daemon.py

File diff suppressed because it is too large Load Diff

View File

@ -132,7 +132,8 @@ def inboxMessageHasParams(messageJson: {}) -> bool:
return False return False
return True return True
def inboxPermittedMessage(domain: str,messageJson: {},federationList: []) -> bool: def inboxPermittedMessage(domain: str,messageJson: {}, \
federationList: []) -> bool:
""" check that we are receiving from a permitted domain """ check that we are receiving from a permitted domain
""" """
testParam='actor' testParam='actor'
@ -160,7 +161,7 @@ def inboxPermittedMessage(domain: str,messageJson: {},federationList: []) -> boo
return True return True
def validPublishedDate(published) -> bool: def validPublishedDate(published: str) -> bool:
currTime=datetime.datetime.utcnow() currTime=datetime.datetime.utcnow()
pubDate=datetime.datetime.strptime(published,"%Y-%m-%dT%H:%M:%SZ") pubDate=datetime.datetime.strptime(published,"%Y-%m-%dT%H:%M:%SZ")
daysSincePublished = (currTime - pubTime).days daysSincePublished = (currTime - pubTime).days
@ -387,7 +388,9 @@ def inboxPostRecipientsAdd(baseDir :str,httpPrefix :str,toList :[], \
followerRecipients=True followerRecipients=True
return followerRecipients,recipientsDict return followerRecipients,recipientsDict
def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : str,port :int, debug :bool) -> ([],[]): def inboxPostRecipients(baseDir :str,postJsonObject :{}, \
httpPrefix :str,domain : str,port :int, \
debug :bool) -> ([],[]):
"""Returns dictionaries containing the recipients of the given post """Returns dictionaries containing the recipients of the given post
The shared dictionary contains followers The shared dictionary contains followers
""" """
@ -1052,8 +1055,8 @@ def inboxAfterCapabilities(session,keyId: str,handle: str,messageJson: {}, \
postLog: [],cachedWebfingers: {},personCache: {}, \ postLog: [],cachedWebfingers: {},personCache: {}, \
queue: [],domain: str,port: int,useTor: bool, \ queue: [],domain: str,port: int,useTor: bool, \
federationList: [],ocapAlways: bool,debug: bool, \ federationList: [],ocapAlways: bool,debug: bool, \
acceptedCaps: [], acceptedCaps: [], \
queueFilename :str,destinationFilename :str, queueFilename :str,destinationFilename :str, \
maxReplies: int,allowDeletion: bool) -> bool: maxReplies: int,allowDeletion: bool) -> bool:
""" Anything which needs to be done after capabilities checks have passed """ Anything which needs to be done after capabilities checks have passed
""" """

View File

@ -372,7 +372,8 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \
break break
return personPosts return personPosts
def deleteAllPosts(baseDir: str,nickname: str, domain: str,boxname: str) -> None: def deleteAllPosts(baseDir: str, \
nickname: str, domain: str,boxname: str) -> None:
"""Deletes all posts for a person from inbox or outbox """Deletes all posts for a person from inbox or outbox
""" """
if boxname!='inbox' and boxname!='outbox': if boxname!='inbox' and boxname!='outbox':
@ -656,8 +657,9 @@ def outboxMessageCreateWrap(httpPrefix: str, \
httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber
return newPost return newPost
def postIsAddressedToFollowers(baseDir: str, def postIsAddressedToFollowers(baseDir: str, \
nickname: str, domain: str, port: int,httpPrefix: str, nickname: str,domain: str, \
port: int,httpPrefix: str, \
postJsonObject: {}) -> bool: postJsonObject: {}) -> bool:
"""Returns true if the given post is addressed to followers of the nickname """Returns true if the given post is addressed to followers of the nickname
""" """
@ -816,13 +818,15 @@ def getMentionedPeople(baseDir: str,httpPrefix: str, \
mentions.append(actor) mentions.append(actor)
return mentions return mentions
def createDirectMessagePost(baseDir: str, def createDirectMessagePost(baseDir: str, \
nickname: str, domain: str, port: int,httpPrefix: str, \ nickname: str,domain: str,port: int, \
httpPrefix: str, \
content: str,followersOnly: bool,saveToFile: bool, content: str,followersOnly: bool,saveToFile: bool,
clientToServer: bool,\ clientToServer: bool,\
attachImageFilename: str,mediaType: str, \ attachImageFilename: str,mediaType: str, \
imageDescription: str,useBlurhash: bool, \ imageDescription: str,useBlurhash: bool, \
inReplyTo=None, inReplyToAtomUri=None, subject=None,debug=False) -> {}: inReplyTo=None,inReplyToAtomUri=None, \
subject=None,debug=False) -> {}:
"""Direct Message post """Direct Message post
""" """
mentionedPeople=getMentionedPeople(baseDir,httpPrefix,content,domain,debug) mentionedPeople=getMentionedPeople(baseDir,httpPrefix,content,domain,debug)
@ -840,9 +844,9 @@ def createDirectMessagePost(baseDir: str,
imageDescription,useBlurhash, \ imageDescription,useBlurhash, \
False,inReplyTo, inReplyToAtomUri, subject) False,inReplyTo, inReplyToAtomUri, subject)
def createReportPost(baseDir: str, def createReportPost(baseDir: str, \
nickname: str,domain: str,port: int,httpPrefix: str, \ nickname: str,domain: str,port: int,httpPrefix: str, \
content: str, followersOnly: bool, saveToFile: bool, content: str, followersOnly: bool, saveToFile: bool, \
clientToServer: bool,\ clientToServer: bool,\
attachImageFilename: str,mediaType: str, \ attachImageFilename: str,mediaType: str, \
imageDescription: str,useBlurhash: bool, \ imageDescription: str,useBlurhash: bool, \
@ -916,7 +920,8 @@ def createReportPost(baseDir: str,
return postJsonObject return postJsonObject
def threadSendPost(session,postJsonStr: str,federationList: [], \ def threadSendPost(session,postJsonStr: str,federationList: [], \
inboxUrl: str, baseDir: str,signatureHeaderJson: {},postLog: [], inboxUrl: str, baseDir: str, \
signatureHeaderJson: {},postLog: [], \
debug :bool) -> None: debug :bool) -> None:
"""Sends a post with exponential backoff """Sends a post with exponential backoff
""" """
@ -962,8 +967,10 @@ def sendPost(projectVersion: str, \
attachImageFilename: str,mediaType: str, \ attachImageFilename: str,mediaType: str, \
imageDescription: str,useBlurhash: bool, \ imageDescription: str,useBlurhash: bool, \
federationList: [], \ federationList: [], \
sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \ sendThreads: [],postLog: [], \
debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int: cachedWebfingers: {},personCache: {}, \
debug=False,inReplyTo=None, \
inReplyToAtomUri=None,subject=None) -> int:
"""Post to another inbox """Post to another inbox
""" """
withDigest=True withDigest=True
@ -1061,7 +1068,8 @@ def sendPostViaServer(projectVersion: str, \
attachImageFilename: str,mediaType: str, \ attachImageFilename: str,mediaType: str, \
imageDescription: str,useBlurhash: bool, \ imageDescription: str,useBlurhash: bool, \
cachedWebfingers: {},personCache: {}, \ cachedWebfingers: {},personCache: {}, \
debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int: debug=False,inReplyTo=None, \
inReplyToAtomUri=None,subject=None) -> int:
"""Send a post via a proxy (c2s) """Send a post via a proxy (c2s)
""" """
if not session: if not session:
@ -1566,23 +1574,31 @@ def sendToFollowers(session,baseDir: str, \
print('DEBUG: End of sendToFollowers') print('DEBUG: End of sendToFollowers')
def createInbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ def createInbox(baseDir: str,nickname: str,domain: str,port: int, \
itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: httpPrefix: str, \
itemsPerPage: int,headerOnly: bool, \
ocapAlways: bool,pageNumber=None) -> {}:
return createBoxBase(baseDir,'inbox',nickname,domain,port,httpPrefix, \ return createBoxBase(baseDir,'inbox',nickname,domain,port,httpPrefix, \
itemsPerPage,headerOnly,True,ocapAlways,pageNumber) itemsPerPage,headerOnly,True,ocapAlways,pageNumber)
def createDMTimeline(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ def createDMTimeline(baseDir: str,nickname: str,domain: str,port: int, \
itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: httpPrefix: str, \
itemsPerPage: int,headerOnly: bool, \
ocapAlways: bool,pageNumber=None) -> {}:
return createBoxBase(baseDir,'dm',nickname,domain,port,httpPrefix, \ return createBoxBase(baseDir,'dm',nickname,domain,port,httpPrefix, \
itemsPerPage,headerOnly,True,ocapAlways,pageNumber) itemsPerPage,headerOnly,True,ocapAlways,pageNumber)
def createOutbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ def createOutbox(baseDir: str,nickname: str,domain: str,port: int, \
itemsPerPage: int,headerOnly: bool,authorized: bool,pageNumber=None) -> {}: httpPrefix: str, \
itemsPerPage: int,headerOnly: bool, \
authorized: bool,pageNumber=None) -> {}:
return createBoxBase(baseDir,'outbox',nickname,domain,port,httpPrefix, \ return createBoxBase(baseDir,'outbox',nickname,domain,port,httpPrefix, \
itemsPerPage,headerOnly,authorized,False,pageNumber) itemsPerPage,headerOnly,authorized,False,pageNumber)
def createModeration(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ def createModeration(baseDir: str,nickname: str,domain: str,port: int, \
itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: httpPrefix: str, \
itemsPerPage: int,headerOnly: bool, \
ocapAlways: bool,pageNumber=None) -> {}:
boxDir = createPersonDir(nickname,domain,baseDir,'inbox') boxDir = createPersonDir(nickname,domain,baseDir,'inbox')
boxname='moderation' boxname='moderation'
@ -1857,7 +1873,8 @@ def createBoxBase(baseDir: str,boxname: str, \
return boxHeader return boxHeader
return boxItems return boxItems
def expireCache(baseDir: str,personCache: {},httpPrefix: str,archiveDir: str,maxPostsInBox=256): def expireCache(baseDir: str,personCache: {}, \
httpPrefix: str,archiveDir: str,maxPostsInBox=256):
"""Thread used to expire actors from the cache and archive old posts """Thread used to expire actors from the cache and archive old posts
""" """
while True: while True:
@ -1866,7 +1883,8 @@ def expireCache(baseDir: str,personCache: {},httpPrefix: str,archiveDir: str,max
expirePersonCache(basedir,personCache) expirePersonCache(basedir,personCache)
archivePosts(baseDir,httpPrefix,archiveDir,maxPostsInBox) archivePosts(baseDir,httpPrefix,archiveDir,maxPostsInBox)
def archivePosts(baseDir: str,httpPrefix: str,archiveDir: str,maxPostsInBox=256) -> None: def archivePosts(baseDir: str,httpPrefix: str, \
archiveDir: str,maxPostsInBox=256) -> None:
"""Archives posts for all accounts """Archives posts for all accounts
""" """
if archiveDir: if archiveDir:
@ -1899,8 +1917,10 @@ def archivePosts(baseDir: str,httpPrefix: str,archiveDir: str,maxPostsInBox=256)
'outbox',archiveSubdir, \ 'outbox',archiveSubdir, \
maxPostsInBox) maxPostsInBox)
def archivePostsForPerson(httpPrefix: str,nickname: str,domain: str,baseDir: str, \ def archivePostsForPerson(httpPrefix: str,nickname: str,domain: str, \
boxname: str,archiveDir: str,maxPostsInBox=256) -> None: baseDir: str, \
boxname: str,archiveDir: str, \
maxPostsInBox=256) -> None:
"""Retain a maximum number of posts within the given box """Retain a maximum number of posts within the given box
Move any others to an archive directory Move any others to an archive directory
""" """
@ -2005,8 +2025,11 @@ def sendCapabilitiesUpdate(session,baseDir: str,httpPrefix: str, \
sendThreads,postLog,cachedWebfingers, \ sendThreads,postLog,cachedWebfingers, \
personCache,debug,projectVersion) personCache,debug,projectVersion)
def populateRepliesJson(baseDir: str,nickname: str,domain: str,postRepliesFilename: str,authorized: bool,repliesJson: {}) -> None: def populateRepliesJson(baseDir: str,nickname: str,domain: str, \
# populate the items list with replies postRepliesFilename: str,authorized: bool, \
repliesJson: {}) -> None:
"""populate the items list with replies
"""
repliesBoxes=['outbox','inbox'] repliesBoxes=['outbox','inbox']
with open(postRepliesFilename,'r') as repliesFile: with open(postRepliesFilename,'r') as repliesFile:
for messageId in repliesFile: for messageId in repliesFile: