mirror of https://gitlab.com/bashrc2/epicyon
Move shared inbox account into daemon
parent
cb79ddb760
commit
c301f45b33
|
@ -18,6 +18,7 @@ from webfinger import webfingerLookup
|
|||
from webfinger import webfingerHandle
|
||||
from person import personLookup
|
||||
from person import personBoxJson
|
||||
from person import createSharedInbox
|
||||
from posts import outboxMessageCreateWrap
|
||||
from posts import savePostToBox
|
||||
from inbox import inboxPermittedMessage
|
||||
|
@ -540,6 +541,10 @@ def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \
|
|||
httpd.acceptedCaps.append('inbox:noannounce')
|
||||
if cw:
|
||||
httpd.acceptedCaps.append('inbox:cw')
|
||||
|
||||
print('Creating shared inbox: inbox@'+domain)
|
||||
createSharedInbox(baseDir,'inbox',domain,port,httpPrefix)
|
||||
|
||||
print('Running ActivityPub daemon on ' + domain + ' port ' + str(port))
|
||||
httpd.thrInboxQueue= \
|
||||
threadWithTrace(target=runInboxQueue, \
|
||||
|
|
10
epicyon.py
10
epicyon.py
|
@ -400,13 +400,9 @@ if not os.path.isdir(baseDir+'/accounts/'+nickname+'@'+domain):
|
|||
setConfigParam(baseDir,'adminPassword',adminPassword)
|
||||
createPerson(baseDir,nickname,domain,port,httpPrefix,True,adminPassword)
|
||||
|
||||
if not os.path.isdir(baseDir+'/accounts/inbox@'+domain):
|
||||
print('Creating shared inbox: inbox@'+domain)
|
||||
createSharedInbox(baseDir,'inbox',domain,port,httpPrefix)
|
||||
|
||||
if not os.path.isdir(baseDir+'/accounts/capabilities@'+domain):
|
||||
print('Creating capabilities account which can sign requests')
|
||||
createCapabilitiesInbox(baseDir,'capabilities',domain,port,httpPrefix)
|
||||
#if not os.path.isdir(baseDir+'/accounts/capabilities@'+domain):
|
||||
# print('Creating capabilities account which can sign requests')
|
||||
# createCapabilitiesInbox(baseDir,'capabilities',domain,port,httpPrefix)
|
||||
|
||||
if args.testdata:
|
||||
nickname='testuser567'
|
||||
|
|
38
follow.py
38
follow.py
|
@ -27,6 +27,8 @@ def getFollowersOfPerson(baseDir: str, \
|
|||
Used by the shared inbox to know who to send incoming mail to
|
||||
"""
|
||||
followers=[]
|
||||
if ':' in domain:
|
||||
domain=domain.split(':')[0]
|
||||
handle=nickname.lower()+'@'+domain.lower()
|
||||
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
||||
return followers
|
||||
|
@ -345,38 +347,64 @@ def sendFollowRequest(session,baseDir: str, \
|
|||
|
||||
return newFollowJson
|
||||
|
||||
def getFollowersOfActor(baseDir :str,actor :str,recipientsDict: {}) -> {}:
|
||||
def getFollowersOfActor(baseDir :str,actor :str,debug: bool) -> {}:
|
||||
"""In a shared inbox if we receive a post we know who it's from
|
||||
and if it's addressed to followers then we need to get a list of those.
|
||||
This returns a list of account handles which follow the given actor
|
||||
and also the corresponding capability id if it exists
|
||||
"""
|
||||
if debug:
|
||||
print('DEBUG: getting followers of '+actor)
|
||||
recipientsDict={}
|
||||
if ':' not in actor:
|
||||
return recipientsDict
|
||||
httpPrefix=actor.split(':')[0]
|
||||
nickname=getNicknameFromActor(actor)
|
||||
if not nickname:
|
||||
if debug:
|
||||
print('DEBUG: no nickname found in '+actor)
|
||||
return recipientsDict
|
||||
domain,port=getDomainFromActor(actor)
|
||||
if not domain:
|
||||
if debug:
|
||||
print('DEBUG: no domain found in '+actor)
|
||||
return recipientsDict
|
||||
actorHandle=nickname+'@'+domain
|
||||
if debug:
|
||||
print('DEBUG: searching for handle '+actorHandle)
|
||||
# for each of the accounts
|
||||
for subdir, dirs, files in os.walk(baseDir+'/accounts'):
|
||||
for account in dirs:
|
||||
if '@' in account and not account.startswith('inbox@'):
|
||||
followingFilename = os.path.join(subdir, account)+'/following.txt'
|
||||
if debug:
|
||||
print('DEBUG: examining follows of '+account)
|
||||
print(followingFilename)
|
||||
if os.path.isfile(followingFilename):
|
||||
# does this account follow the given actor?
|
||||
if debug:
|
||||
print('DEBUG: checking if '+actorHandle+' in '+followingFilename)
|
||||
if actorHandle in open(followingFilename).read():
|
||||
if debug:
|
||||
print('DEBUG: '+account+' follows '+actorHandle)
|
||||
ocapFilename=baseDir+'/accounts/'+account+'/ocap/accept/'+httpPrefix+':##'+domain+':'+str(port)+'#users#'+nickname+'.json'
|
||||
if debug:
|
||||
print('DEBUG: checking capabilities of'+account)
|
||||
if os.path.isfile(ocapFilename):
|
||||
with open(ocapFilename, 'r') as fp:
|
||||
ocapJson=commentjson.load(fp)
|
||||
if ocapJson.get('id'):
|
||||
recipientsDict[account]=ocapJson['id']
|
||||
else:
|
||||
recipientsDict[account]=None
|
||||
if ocapJson.get('id'):
|
||||
if debug:
|
||||
print('DEBUG: capabilities id found for '+account)
|
||||
|
||||
recipientsDict[account]=ocapJson['id']
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: capabilities has no id attribute')
|
||||
recipientsDict[account]=None
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: No capabilities file found for '+account+' granted by '+actorHandle)
|
||||
print(ocapFilename)
|
||||
recipientsDict[account]=None
|
||||
return recipientsDict
|
||||
|
|
80
inbox.py
80
inbox.py
|
@ -19,6 +19,7 @@ from utils import getStatusNumber
|
|||
from utils import getDomainFromActor
|
||||
from utils import getNicknameFromActor
|
||||
from utils import domainPermitted
|
||||
from utils import locatePost
|
||||
from httpsig import verifyPostHeaders
|
||||
from session import createSession
|
||||
from session import getJson
|
||||
|
@ -87,7 +88,7 @@ def inboxPermittedMessage(domain: str,messageJson: {},federationList: []) -> boo
|
|||
if not urlPermitted(actor,federationList,"inbox:write"):
|
||||
return False
|
||||
|
||||
if messageJson['type']!='Follow':
|
||||
if messageJson['type']!='Follow' and messageJson['type']!='Like':
|
||||
if messageJson.get('object'):
|
||||
if messageJson['object'].get('inReplyTo'):
|
||||
inReplyTo=messageJson['object']['inReplyTo']
|
||||
|
@ -210,7 +211,7 @@ def inboxCheckCapabilities(baseDir :str,nickname :str,domain :str, \
|
|||
def inboxPostRecipientsAdd(baseDir :str,httpPrefix :str,toList :[], \
|
||||
recipientsDict :{}, \
|
||||
domainMatch: str,domain :str, \
|
||||
actor :str) -> bool:
|
||||
actor :str,debug: bool) -> bool:
|
||||
"""Given a list of post recipients (toList) from 'to' or 'cc' parameters
|
||||
populate a recipientsDict with the handle and capabilities id for each
|
||||
"""
|
||||
|
@ -235,12 +236,23 @@ def inboxPostRecipientsAdd(baseDir :str,httpPrefix :str,toList :[], \
|
|||
else:
|
||||
recipientsDict[handle]=None
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: '+ocapFilename+' not found')
|
||||
recipientsDict[handle]=None
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: '+baseDir+'/accounts/'+handle+' does not exist')
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: '+recipient+' is not local to '+domainMatch)
|
||||
print(str(toList))
|
||||
if recipient.endswith('followers'):
|
||||
if debug:
|
||||
print('DEBUG: followers detected as post recipients')
|
||||
followerRecipients=True
|
||||
return followerRecipients,recipientsDict
|
||||
|
||||
def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : str,port :int) -> ([],[]):
|
||||
def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain : str,port :int, debug :bool) -> ([],[]):
|
||||
"""Returns dictionaries containing the recipients of the given post
|
||||
The shared dictionary contains followers
|
||||
"""
|
||||
|
@ -248,6 +260,9 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain :
|
|||
recipientsDictFollowers={}
|
||||
|
||||
if not postJsonObject.get('actor'):
|
||||
if debug:
|
||||
pprint(postJsonObject)
|
||||
print('WARNING: inbox post has no actor')
|
||||
return recipientsDict,recipientsDictFollowers
|
||||
|
||||
if ':' in domain:
|
||||
|
@ -264,29 +279,48 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain :
|
|||
if postJsonObject.get('object'):
|
||||
if isinstance(postJsonObject['object'], dict):
|
||||
if postJsonObject['object'].get('to'):
|
||||
if debug:
|
||||
print('DEBUG: resolving "to"')
|
||||
includesFollowers,recipientsDict= \
|
||||
inboxPostRecipientsAdd(baseDir,httpPrefix, \
|
||||
postJsonObject['object']['to'], \
|
||||
recipientsDict, \
|
||||
domainMatch,domainBase,actor)
|
||||
domainMatch,domainBase, \
|
||||
actor,debug)
|
||||
if includesFollowers:
|
||||
followerRecipients=True
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: inbox post has no "to"')
|
||||
|
||||
if postJsonObject['object'].get('cc'):
|
||||
includesFollowers,recipientsDict= \
|
||||
inboxPostRecipientsAdd(baseDir,httpPrefix, \
|
||||
postJsonObject['object']['cc'], \
|
||||
recipientsDict, \
|
||||
domainMatch,domainBase,actor)
|
||||
domainMatch,domainBase, \
|
||||
actor,debug)
|
||||
if includesFollowers:
|
||||
followerRecipients=True
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: inbox post has no cc')
|
||||
else:
|
||||
if debug:
|
||||
if isinstance(postJsonObject['object'], str):
|
||||
if '/statuses/' in postJsonObject['object']:
|
||||
print('DEBUG: inbox item is a link to a post')
|
||||
else:
|
||||
if '/users/' in postJsonObject['object']:
|
||||
print('DEBUG: inbox item is a link to an actor')
|
||||
|
||||
if postJsonObject.get('to'):
|
||||
includesFollowers,recipientsDict= \
|
||||
inboxPostRecipientsAdd(baseDir,httpPrefix, \
|
||||
postJsonObject['to'], \
|
||||
recipientsDict, \
|
||||
domainMatch,domainBase,actor)
|
||||
domainMatch,domainBase, \
|
||||
actor,debug)
|
||||
if includesFollowers:
|
||||
followerRecipients=True
|
||||
|
||||
|
@ -295,16 +329,19 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain :
|
|||
inboxPostRecipientsAdd(baseDir,httpPrefix, \
|
||||
postJsonObject['cc'], \
|
||||
recipientsDict, \
|
||||
domainMatch,domainBase,actor)
|
||||
domainMatch,domainBase, \
|
||||
actor,debug)
|
||||
if includesFollowers:
|
||||
followerRecipients=True
|
||||
|
||||
if not followerRecipients:
|
||||
if debug:
|
||||
print('DEBUG: no followers were resolved')
|
||||
return recipientsDict,recipientsDictFollowers
|
||||
|
||||
# now resolve the followers
|
||||
recipientsDictFollowers= \
|
||||
getFollowersOfActor(baseDir,actor,recipientsDict)
|
||||
getFollowersOfActor(baseDir,actor,debug)
|
||||
|
||||
return recipientsDict,recipientsDictFollowers
|
||||
|
||||
|
@ -388,18 +425,7 @@ def receiveLike(session,handle: str,baseDir: str, \
|
|||
if not os.path.isdir(baseDir+'/accounts/'+handle):
|
||||
print('DEBUG: unknown recipient of like - '+handle)
|
||||
# if this post in the outbox of the person?
|
||||
boxName='outbox'
|
||||
postFilename=baseDir+'/accounts/'+handle+'/'+boxName+'/'+messageJson['object'].replace('/','#')+'.json'
|
||||
if not os.path.isfile(postFilename):
|
||||
# if this post in the inbox of the person?
|
||||
boxName='inbox'
|
||||
postFilename=baseDir+'/accounts/'+handle+'/'+boxName+'/'+messageJson['object'].replace('/','#')+'.json'
|
||||
if not os.path.isfile(postFilename):
|
||||
# if this post in the shared inbox?
|
||||
handle='inbox@'+domain
|
||||
postFilename=baseDir+'/accounts/'+handle+'/'+boxName+'/'+messageJson['object'].replace('/','#')+'.json'
|
||||
if not os.path.isfile(postFilename):
|
||||
postFilename=None
|
||||
postFilename=locatePost(baseDir,handle.split('@')[0],handle.split('@')[1],messageJson['object'])
|
||||
if not postFilename:
|
||||
if debug:
|
||||
print('DEBUG: post not found in inbox or outbox')
|
||||
|
@ -569,7 +595,15 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
|
|||
|
||||
# get recipients list
|
||||
recipientsDict,recipientsDictFollowers= \
|
||||
inboxPostRecipients(baseDir,queueJson['post'],httpPrefix,domain,port)
|
||||
inboxPostRecipients(baseDir,queueJson['post'],httpPrefix,domain,port,debug)
|
||||
if len(recipientsDict.items())==0 and \
|
||||
len(recipientsDictFollowers.items())==0:
|
||||
if debug:
|
||||
pprint(queueJson['post'])
|
||||
print('DEBUG: no recipients were resolved for post arriving in inbox')
|
||||
os.remove(queueFilename)
|
||||
queue.pop(0)
|
||||
continue
|
||||
|
||||
# if there are only a small number of followers then process them as if they
|
||||
# were specifically addresses to particular accounts
|
||||
|
@ -579,7 +613,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
|
|||
if debug:
|
||||
print('DEBUG: moving '+str(noOfFollowItems)+' inbox posts addressed to followers')
|
||||
for handle,postItem in recipientsDictFollowers.items():
|
||||
recipientsDict['handle']=postItem
|
||||
recipientsDict[handle]=postItem
|
||||
recipientsDictFollowers={}
|
||||
recipientsList=[recipientsDict,recipientsDictFollowers]
|
||||
|
||||
|
@ -587,7 +621,7 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [],cache
|
|||
print('*************************************')
|
||||
print('Resolved recipients list:')
|
||||
pprint(recipientsDict)
|
||||
print('Resolved sollowers list:')
|
||||
print('Resolved followers list:')
|
||||
pprint(recipientsDictFollowers)
|
||||
print('*************************************')
|
||||
|
||||
|
|
174
like.py
174
like.py
|
@ -8,80 +8,28 @@ __status__ = "Production"
|
|||
|
||||
import json
|
||||
import commentjson
|
||||
from pprint import pprint
|
||||
from utils import urlPermitted
|
||||
from utils import getNicknameFromActor
|
||||
from utils import getDomainFromActor
|
||||
from utils import locatePost
|
||||
from posts import sendSignedJson
|
||||
|
||||
def like(session,baseDir: str,federationList: [],nickname: str,domain: str,port: int, \
|
||||
ccUrl: str,httpPrefix: str,objectUrl: str,clientToServer: bool, \
|
||||
sendThreads: [],postLog: [],personCache: {},cachedWebfingers: {}) -> {}:
|
||||
"""Creates a like
|
||||
ccUrl might be a specific person whose post was liked
|
||||
objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase
|
||||
"""
|
||||
if not urlPermitted(objectUrl,federationList,"inbox:write"):
|
||||
return None
|
||||
|
||||
if port!=80 and port!=443:
|
||||
domain=domain+':'+str(port)
|
||||
|
||||
newLikeJson = {
|
||||
'type': 'Like',
|
||||
'actor': httpPrefix+'://'+domain+'/users/'+nickname,
|
||||
'object': objectUrl,
|
||||
'to': [httpPrefix+'://'+domain+'/users/'+nickname+'/followers'],
|
||||
'cc': []
|
||||
}
|
||||
if ccUrl:
|
||||
if len(ccUrl)>0:
|
||||
newLikeJson['cc']=ccUrl
|
||||
|
||||
# Extract the domain and nickname from a statuses link
|
||||
likedPostNickname=None
|
||||
likedPostDomain=None
|
||||
likedPostPort=None
|
||||
if '/users/' in objectUrl:
|
||||
likedPostNickname=getNicknameFromActor(objectUrl)
|
||||
likedPostDomain,likedPostPort=getDomainFromActor(objectUrl)
|
||||
|
||||
if likedPostNickname:
|
||||
sendSignedJson(newlikeJson,session,baseDir, \
|
||||
nickname,domain,port, \
|
||||
likedPostNickname,likedPostDomain,likedPostPort, \
|
||||
'https://www.w3.org/ns/activitystreams#Public', \
|
||||
httpPrefix,True,clientToServer,federationList, \
|
||||
sendThreads,postLog,cachedWebfingers,personCache,debug)
|
||||
|
||||
return newLikeJson
|
||||
|
||||
def likePost(session,baseDir: str,federationList: [], \
|
||||
nickname: str, domain: str, port: int, httpPrefix: str, \
|
||||
likeNickname: str, likeDomain: str, likePort: int, \
|
||||
likeHttps: bool, likeStatusNumber: int, \
|
||||
clientToServer: bool,sendThreads: [],postLog: [], \
|
||||
personCache: {},cachedWebfingers: {}) -> {}:
|
||||
"""Likes a given status post
|
||||
"""
|
||||
likeDomain=likeDomain
|
||||
if likePort!=80 and likePort!=443:
|
||||
likeDomain=likeDomain+':'+str(likePort)
|
||||
|
||||
objectUrl = \
|
||||
httpPrefix + '://'+likeDomain+'/users/'+likeNickname+ \
|
||||
'/statuses/'+str(likeStatusNumber)
|
||||
|
||||
return like(session,baseDir,federationList,nickname,domain,port, \
|
||||
ccUrl,httpPrefix,objectUrl,clientToServer, \
|
||||
sendThreads,postLog,personCache,cachedWebfingers)
|
||||
|
||||
def updateLikesCollection(postFilename: str,objectUrl: str, actor: str) -> None:
|
||||
def updateLikesCollection(postFilename: str,objectUrl: str, actor: str,debug: bool) -> None:
|
||||
"""Updates the likes collection within a post
|
||||
"""
|
||||
with open(postFilename, 'r') as fp:
|
||||
postJson=commentjson.load(fp)
|
||||
if not postJson.get('object'):
|
||||
if debug:
|
||||
pprint(postJson)
|
||||
print('DEBUG: post '+objectUrl+' has no object')
|
||||
return
|
||||
if not objectUrl.endswith('/likes'):
|
||||
objectUrl=objectUrl+'/likes'
|
||||
if not postJson.get('likes'):
|
||||
if not postJson['object'].get('likes'):
|
||||
if debug:
|
||||
print('DEBUG: Adding initial likes to '+objectUrl)
|
||||
likesJson = {
|
||||
'id': objectUrl,
|
||||
'type': 'Collection',
|
||||
|
@ -92,17 +40,103 @@ def updateLikesCollection(postFilename: str,objectUrl: str, actor: str) -> None:
|
|||
|
||||
}]
|
||||
}
|
||||
postJson['likes']=likesJson
|
||||
postJson['object']['likes']=likesJson
|
||||
else:
|
||||
if postJson['likes'].get('items'):
|
||||
if postJson['object']['likes'].get('items'):
|
||||
for likeItem in postJson['likes']['items']:
|
||||
if likeItem['actor']==actor:
|
||||
return
|
||||
if likeItem.get('actor'):
|
||||
if likeItem['actor']==actor:
|
||||
return
|
||||
newLike={
|
||||
'type': 'Like',
|
||||
'actor': actor
|
||||
}
|
||||
postJson['likes']['items'].append(newLike)
|
||||
postJson['likes']['totalItems']=len(postJson['likes']['items'])
|
||||
postJson['object']['likes']['items'].append(newLike)
|
||||
postJson['object']['likes']['totalItems']=len(postJson['likes']['items'])
|
||||
else:
|
||||
if debug:
|
||||
print('DEBUG: likes section of post has no items list')
|
||||
|
||||
if debug:
|
||||
print('DEBUG: saving post with likes added')
|
||||
with open(postFilename, 'w') as fp:
|
||||
commentjson.dump(postJson, fp, indent=4, sort_keys=True)
|
||||
|
||||
def like(session,baseDir: str,federationList: [],nickname: str,domain: str,port: int, \
|
||||
ccList: [],httpPrefix: str,objectUrl: str,clientToServer: bool, \
|
||||
sendThreads: [],postLog: [],personCache: {},cachedWebfingers: {}, \
|
||||
debug: bool) -> {}:
|
||||
"""Creates a like
|
||||
ccUrl might be a specific person whose post was liked
|
||||
objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase
|
||||
"""
|
||||
if not urlPermitted(objectUrl,federationList,"inbox:write"):
|
||||
return None
|
||||
|
||||
fullDomain=domain
|
||||
if port!=80 and port!=443:
|
||||
if ':' not in domain:
|
||||
fullDomain=domain+':'+str(port)
|
||||
|
||||
newLikeJson = {
|
||||
'type': 'Like',
|
||||
'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname,
|
||||
'object': objectUrl,
|
||||
'to': [httpPrefix+'://'+fullDomain+'/users/'+nickname+'/followers'],
|
||||
'cc': []
|
||||
}
|
||||
if ccList:
|
||||
if len(ccList)>0:
|
||||
newLikeJson['cc']=ccList
|
||||
|
||||
# Extract the domain and nickname from a statuses link
|
||||
likedPostNickname=None
|
||||
likedPostDomain=None
|
||||
likedPostPort=None
|
||||
if '/users/' in objectUrl:
|
||||
likedPostNickname=getNicknameFromActor(objectUrl)
|
||||
likedPostDomain,likedPostPort=getDomainFromActor(objectUrl)
|
||||
|
||||
if likedPostNickname:
|
||||
postFilename=locatePost(baseDir,nickname,domain,objectUrl)
|
||||
if not postFilename:
|
||||
return None
|
||||
|
||||
updateLikesCollection(postFilename,objectUrl,newLikeJson['actor'],debug)
|
||||
|
||||
sendSignedJson(newLikeJson,session,baseDir, \
|
||||
nickname,domain,port, \
|
||||
likedPostNickname,likedPostDomain,likedPostPort, \
|
||||
'https://www.w3.org/ns/activitystreams#Public', \
|
||||
httpPrefix,True,clientToServer,federationList, \
|
||||
sendThreads,postLog,cachedWebfingers,personCache,debug)
|
||||
|
||||
return newLikeJson
|
||||
|
||||
def likePost(session,baseDir: str,federationList: [], \
|
||||
nickname: str,domain: str,port: int,httpPrefix: str, \
|
||||
likeNickname: str,likeDomain: str,likePort: int, \
|
||||
ccList: [], \
|
||||
likeStatusNumber: int,clientToServer: bool, \
|
||||
sendThreads: [],postLog: [], \
|
||||
personCache: {},cachedWebfingers: {}, \
|
||||
debug: bool) -> {}:
|
||||
"""Likes a given status post
|
||||
"""
|
||||
likeDomain=likeDomain
|
||||
if likePort!=80 and likePort!=443:
|
||||
likeDomain=likeDomain+':'+str(likePort)
|
||||
|
||||
objectUrl = \
|
||||
httpPrefix + '://'+likeDomain+'/users/'+likeNickname+ \
|
||||
'/statuses/'+str(likeStatusNumber)
|
||||
|
||||
if likePort!=80 and likePort!=443:
|
||||
ccUrl=httpPrefix+'://'+likeDomain+':'+str(likePort)+'/users/'+likeNickname
|
||||
else:
|
||||
ccUrl=httpPrefix+'://'+likeDomain+'/users/'+likeNickname
|
||||
|
||||
return like(session,baseDir,federationList,nickname,domain,port, \
|
||||
ccList,httpPrefix,objectUrl,clientToServer, \
|
||||
sendThreads,postLog,personCache,cachedWebfingers,debug)
|
||||
|
||||
|
|
|
@ -88,6 +88,14 @@ def createPersonBase(baseDir: str,nickname: str,domain: str,port: int, \
|
|||
os.mkdir(baseDir+peopleSubdir)
|
||||
if not os.path.isdir(baseDir+peopleSubdir+'/'+handle):
|
||||
os.mkdir(baseDir+peopleSubdir+'/'+handle)
|
||||
if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/inbox'):
|
||||
os.mkdir(baseDir+peopleSubdir+'/'+handle+'/inbox')
|
||||
if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/outbox'):
|
||||
os.mkdir(baseDir+peopleSubdir+'/'+handle+'/outbox')
|
||||
if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/ocap'):
|
||||
os.mkdir(baseDir+peopleSubdir+'/'+handle+'/ocap')
|
||||
if not os.path.isdir(baseDir+peopleSubdir+'/'+handle+'/queue'):
|
||||
os.mkdir(baseDir+peopleSubdir+'/'+handle+'/queue')
|
||||
filename=baseDir+peopleSubdir+'/'+handle+'.json'
|
||||
with open(filename, 'w') as fp:
|
||||
commentjson.dump(newPerson, fp, indent=4, sort_keys=False)
|
||||
|
|
69
tests.py
69
tests.py
|
@ -36,13 +36,13 @@ from follow import unfollowPerson
|
|||
from follow import unfollowerOfPerson
|
||||
from follow import getFollowersOfPerson
|
||||
from follow import sendFollowRequest
|
||||
from follow import getFollowersOfActor
|
||||
from person import createPerson
|
||||
from person import setPreferredNickname
|
||||
from person import setBio
|
||||
from auth import createBasicAuthHeader
|
||||
from auth import authorizeBasic
|
||||
from auth import storeBasicCredentials
|
||||
from like import likePost
|
||||
|
||||
testServerAliceRunning = False
|
||||
testServerBobRunning = False
|
||||
|
@ -210,7 +210,6 @@ def testPostMessageBetweenServers():
|
|||
|
||||
httpPrefix='http'
|
||||
useTor=False
|
||||
federationList=['127.0.0.50','127.0.0.100']
|
||||
|
||||
baseDir=os.getcwd()
|
||||
if os.path.isdir(baseDir+'/.tests'):
|
||||
|
@ -223,12 +222,13 @@ def testPostMessageBetweenServers():
|
|||
aliceDir=baseDir+'/.tests/alice'
|
||||
aliceDomain='127.0.0.50'
|
||||
alicePort=61935
|
||||
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,True,True,ocapAlways),daemon=True)
|
||||
|
||||
bobDir=baseDir+'/.tests/bob'
|
||||
bobDomain='127.0.0.100'
|
||||
bobPort=61936
|
||||
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,True,True,ocapAlways),daemon=True)
|
||||
federationList=[bobDomain,aliceDomain]
|
||||
|
||||
thrAlice = threadWithTrace(target=createServerAlice,args=(aliceDir,aliceDomain,alicePort,federationList,False,False,ocapAlways),daemon=True)
|
||||
thrBob = threadWithTrace(target=createServerBob,args=(bobDir,bobDomain,bobPort,federationList,False,False,ocapAlways),daemon=True)
|
||||
|
||||
thrAlice.start()
|
||||
thrBob.start()
|
||||
|
@ -241,6 +241,7 @@ def testPostMessageBetweenServers():
|
|||
|
||||
time.sleep(1)
|
||||
|
||||
print('\n\n*******************************************************')
|
||||
print('Alice sends to Bob')
|
||||
os.chdir(aliceDir)
|
||||
sessionAlice = createSession(aliceDomain,alicePort,useTor)
|
||||
|
@ -255,6 +256,11 @@ def testPostMessageBetweenServers():
|
|||
ccUrl=None
|
||||
alicePersonCache={}
|
||||
aliceCachedWebfingers={}
|
||||
|
||||
# nothing in Alice's outbox
|
||||
outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
|
||||
assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0
|
||||
|
||||
sendResult = sendPost(sessionAlice,aliceDir,'alice', aliceDomain, alicePort, 'bob', bobDomain, bobPort, ccUrl, httpPrefix, 'Why is a mouse when it spins?', followersOnly, saveToFile, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers,alicePersonCache,inReplyTo, inReplyToAtomUri, subject)
|
||||
print('sendResult: '+str(sendResult))
|
||||
|
||||
|
@ -263,9 +269,53 @@ def testPostMessageBetweenServers():
|
|||
for i in range(30):
|
||||
if os.path.isdir(inboxPath):
|
||||
if len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])>0:
|
||||
break
|
||||
if len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==1:
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
# inbox item created
|
||||
assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1
|
||||
# queue item removed
|
||||
assert len([name for name in os.listdir(queuePath) if os.path.isfile(os.path.join(queuePath, name))])==0
|
||||
|
||||
#print('\n\n*******************************************************')
|
||||
#print("Bob likes Alice's post")
|
||||
|
||||
#followerOfPerson(bobDir,'bob',bobDomain,'alice',aliceDomain+':'+str(alicePort),federationList,True)
|
||||
#followPerson(aliceDir,'alice',aliceDomain,'bob',bobDomain+':'+str(bobPort),federationList,True)
|
||||
#followList=getFollowersOfPerson(bobDir,'bob',bobDomain,'followers.txt')
|
||||
#assert len(followList)==1
|
||||
|
||||
#sessionBob = createSession(bobDomain,bobPort,useTor)
|
||||
#bobSendThreads = []
|
||||
#bobPostLog = []
|
||||
#bobPersonCache={}
|
||||
#bobCachedWebfingers={}
|
||||
#statusNumber=None
|
||||
#outboxPostFilename=None
|
||||
#outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox'
|
||||
#for name in os.listdir(outboxPath):
|
||||
# if '#statuses#' in name:
|
||||
# statusNumber=int(name.split('#statuses#')[1].replace('.json',''))
|
||||
# outboxPostFilename=outboxPath+'/'+name
|
||||
#assert statusNumber
|
||||
#assert outboxPostFilename
|
||||
#assert likePost(sessionBob,bobDir,federationList, \
|
||||
# 'bob',bobDomain,bobPort,httpPrefix, \
|
||||
# 'alice',aliceDomain,alicePort,[], \
|
||||
# statusNumber,False,bobSendThreads,bobPostLog, \
|
||||
# bobPersonCache,bobCachedWebfingers,True)
|
||||
|
||||
#for i in range(20):
|
||||
# if 'likes' in open(outboxPostFilename).read():
|
||||
# break
|
||||
# time.sleep(1)
|
||||
|
||||
#with open(outboxPostFilename, 'r') as fp:
|
||||
# alicePostJson=commentjson.load(fp)
|
||||
# pprint(alicePostJson)
|
||||
#assert 'likes' in open(outboxPostFilename).read()
|
||||
|
||||
# stop the servers
|
||||
thrAlice.kill()
|
||||
thrAlice.join()
|
||||
|
@ -275,11 +325,6 @@ def testPostMessageBetweenServers():
|
|||
thrBob.join()
|
||||
assert thrBob.isAlive()==False
|
||||
|
||||
# inbox item created
|
||||
assert len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))])==1
|
||||
# queue item removed
|
||||
assert len([name for name in os.listdir(queuePath) if os.path.isfile(os.path.join(queuePath, name))])==0
|
||||
|
||||
os.chdir(baseDir)
|
||||
shutil.rmtree(aliceDir)
|
||||
shutil.rmtree(bobDir)
|
||||
|
@ -439,7 +484,7 @@ def testFollowBetweenServers():
|
|||
|
||||
assert aliceMessageArrived==True
|
||||
print('Message from Alice to Bob succeeded, since it was granted capabilities')
|
||||
|
||||
|
||||
print('\n\n*********************************************************')
|
||||
print("\nBob changes Alice's capabilities so that she can't reply on his posts")
|
||||
bobCapsFilename=bobDir+'/accounts/bob@'+bobDomain+'/ocap/accept/'+httpPrefix+':##'+aliceDomain+':'+str(alicePort)+'#users#alice.json'
|
||||
|
|
27
utils.py
27
utils.py
|
@ -44,6 +44,8 @@ def createInboxQueueDir(nickname: str,domain: str,baseDir: str) -> str:
|
|||
def domainPermitted(domain: str, federationList: []):
|
||||
if len(federationList)==0:
|
||||
return True
|
||||
if ':' in domain:
|
||||
domain=domain.split(':')[0]
|
||||
if domain in federationList:
|
||||
return True
|
||||
return False
|
||||
|
@ -91,6 +93,8 @@ def followPerson(baseDir: str,nickname: str, domain: str, \
|
|||
if debug:
|
||||
print('DEBUG: follow of domain '+followDomain+' not permitted')
|
||||
return False
|
||||
if debug:
|
||||
print('DEBUG: follow of domain '+followDomain)
|
||||
handle=nickname.lower()+'@'+domain.lower()
|
||||
handleToFollow=followNickname.lower()+'@'+followDomain.lower()
|
||||
if not os.path.isdir(baseDir+'/accounts'):
|
||||
|
@ -100,10 +104,33 @@ def followPerson(baseDir: str,nickname: str, domain: str, \
|
|||
filename=baseDir+'/accounts/'+handle+'/'+followFile
|
||||
if os.path.isfile(filename):
|
||||
if handleToFollow in open(filename).read():
|
||||
if debug:
|
||||
print('DEBUG: follow already exists')
|
||||
return True
|
||||
with open(filename, "a") as followfile:
|
||||
followfile.write(handleToFollow+'\n')
|
||||
if debug:
|
||||
print('DEBUG: follow added')
|
||||
return True
|
||||
if debug:
|
||||
print('DEBUG: creating new following file')
|
||||
with open(filename, "w") as followfile:
|
||||
followfile.write(handleToFollow+'\n')
|
||||
return True
|
||||
|
||||
def locatePost(baseDir: str,nickname: str,domain: str,postUrl: str):
|
||||
"""Returns the filename for the given status post url
|
||||
"""
|
||||
boxName='outbox'
|
||||
postFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+boxName+'/'+postUrl.replace('/','#')+'.json'
|
||||
if not os.path.isfile(postFilename):
|
||||
# if this post in the inbox of the person?
|
||||
boxName='inbox'
|
||||
postFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+boxName+'/'+postUrl.replace('/','#')+'.json'
|
||||
if not os.path.isfile(postFilename):
|
||||
# if this post in the shared inbox?
|
||||
handle='inbox@'+domain
|
||||
postFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+boxName+'/'+postUrl.replace('/','#')+'.json'
|
||||
if not os.path.isfile(postFilename):
|
||||
postFilename=None
|
||||
return postFilename
|
||||
|
|
Loading…
Reference in New Issue