diff --git a/README.md b/README.md index f984cf4f9..50b72cd38 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Also: https://raw.githubusercontent.com/w3c/activitypub/gh-pages/activitypub-tut https://blog.dereferenced.org/what-is-ocap-and-why-should-i-care +https://alexcastano.com/what-is-activity-pub + This project is currently *pre alpha* and not recommended for any real world uses. ## Goals diff --git a/acceptreject.py b/acceptreject.py index 840a8e59b..31fabd389 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -15,11 +15,15 @@ from utils import getDomainFromActor from utils import getNicknameFromActor from utils import domainPermitted -def createAcceptReject(baseDir: str,federationList: [],capsList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,httpPrefix: str,objectUrl: str,acceptType: str) -> {}: - """Accepts or rejects something (eg. a follow request) +def createAcceptReject(baseDir: str,federationList: [],capsList: [], \ + nickname: str,domain: str,port: int, \ + toUrl: str,ccUrl: str,httpPrefix: str, \ + objectUrl: str,acceptType: str) -> {}: + """Accepts or rejects something (eg. a follow request or offer) Typically toUrl will be https://www.w3.org/ns/activitystreams#Public - and ccUrl might be a specific person favorited or repeated and the followers url - objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase + and ccUrl might be a specific person favorited or repeated and + the followers url objectUrl is typically the url of the message, + corresponding to url or atomUri in createPostBase """ if not urlPermitted(objectUrl,federationList,capsList,"inbox:write"): return None @@ -39,13 +43,28 @@ def createAcceptReject(baseDir: str,federationList: [],capsList: [],nickname: st newAccept['cc']=ccUrl return newAccept -def createAccept(baseDir: str,federationList: [],capsList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,httpPrefix: str,objectUrl: str) -> {}: - return createAcceptReject(baseDir,federationList,capsList,nickname,domain,port,toUrl,ccUrl,httpPrefix,objectUrl,'Accept') +def createAccept(baseDir: str,federationList: [],capsList: [], \ + nickname: str,domain: str,port: int, \ + toUrl: str,ccUrl: str,httpPrefix: str, \ + objectUrl: str) -> {}: + return createAcceptReject(baseDir,federationList,capsList, \ + nickname,domain,port, \ + toUrl,ccUrl,httpPrefix, \ + objectUrl,'Accept') -def createReject(baseDir: str,federationList: [],capsList: [],nickname: str,domain: str,port: int,toUrl: str,ccUrl: str,httpPrefix: str,objectUrl: str) -> {}: - return createAcceptReject(baseDir,federationList,capsList,nickname,domain,port,toUrl,ccUrl,httpPrefix,objectUrl,'Reject') +def createReject(baseDir: str,federationList: [],capsList: [], \ + nickname: str,domain: str,port: int, \ + toUrl: str,ccUrl: str,httpPrefix: str, \ + objectUrl: str) -> {}: + return createAcceptReject(baseDir,federationList,capsList, \ + nickname,domain,port, \ + toUrl,ccUrl, \ + httpPrefix,objectUrl,'Reject') -def receiveAcceptReject(session,baseDir: str,httpPrefix: str,port: int,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},messageJson: {},federationList: [],capsList: [],debug : bool) -> bool: +def receiveAcceptReject(session,baseDir: str,httpPrefix: str,port: int, \ + sendThreads: [],postLog: [],cachedWebfingers: {}, \ + personCache: {},messageJson: {},federationList: [], \ + capsList: [],debug : bool) -> bool: """Receives an Accept or Reject within the POST section of HTTPServer """ if messageJson['type']!='Accept' and messageJson['type']!='Reject': diff --git a/announce.py b/announce.py index 0b587721f..7a8baaff1 100644 --- a/announce.py +++ b/announce.py @@ -18,8 +18,9 @@ def createAnnounce(baseDir: str,federationList: [], capsList: [], \ objectUrl: str, saveToFile: bool) -> {}: """Creates an announce message Typically toUrl will be https://www.w3.org/ns/activitystreams#Public - and ccUrl might be a specific person favorited or repeated and the followers url - objectUrl is typically the url of the message, corresponding to url or atomUri in createPostBase + and ccUrl might be a specific person favorited or repeated and the + followers url objectUrl is typically the url of the message, + corresponding to url or atomUri in createPostBase """ if not urlPermitted(objectUrl,federationList,capsList,"inbox:write"): return None @@ -28,7 +29,8 @@ def createAnnounce(baseDir: str,federationList: [], capsList: [], \ domain=domain+':'+str(port) statusNumber,published = getStatusNumber() - newAnnounceId=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber + newAnnounceId= \ + httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber newAnnounce = { 'actor': httpPrefix+'://'+domain+'/users/'+nickname, 'atomUri': httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber, @@ -79,5 +81,6 @@ def repeatPost(baseDir: str,federationList: [], \ objectUrl = announceHttpsPrefix + '://'+announcedDomain+'/users/'+ \ announceNickname+'/statuses/'+str(announceStatusNumber) - return announcePublic(baseDir,nickname, domain, port, httpPrefix, objectUrl, saveToFile) + return announcePublic(baseDir,nickname, domain, port, \ + httpPrefix, objectUrl, saveToFile) diff --git a/auth.py b/auth.py index f1974df3f..d65d9d79f 100644 --- a/auth.py +++ b/auth.py @@ -65,7 +65,9 @@ def authorizeBasic(baseDir: str,path: str,authHeader: str,debug: bool) -> bool: nickname = plain.split(':')[0] if nickname!=nicknameFromPath: if debug: - print('DEBUG: Nickname given in the path ('+nicknameFromPath+') does not match the one in the Authorization header ('+nickname+')') + print('DEBUG: Nickname given in the path ('+nicknameFromPath+ \ + ') does not match the one in the Authorization header ('+ \ + nickname+')') return False passwordFile=baseDir+'/accounts/passwords' if not os.path.isfile(passwordFile): diff --git a/cache.py b/cache.py index 239e55a38..6c26add9f 100644 --- a/cache.py +++ b/cache.py @@ -12,7 +12,10 @@ def storePersonInCache(personUrl: str,personJson: {},personCache: {}) -> None: """Store an actor in the cache """ currTime=datetime.datetime.utcnow() - personCache[personUrl]={ "actor": personJson, "timestamp": currTime.strftime("%Y-%m-%dT%H:%M:%SZ") } + personCache[personUrl]={ + "actor": personJson, + "timestamp": currTime.strftime("%Y-%m-%dT%H:%M:%SZ") + } def storeWebfingerInCache(handle: str,wf,cachedWebfingers: {}) -> None: """Store a webfinger endpoint in the cache @@ -25,7 +28,9 @@ def getPersonFromCache(personUrl: str,personCache: {}) -> {}: if personCache.get(personUrl): # how old is the cached data? currTime=datetime.datetime.utcnow() - cacheTime=datetime.datetime.strptime(personCache[personUrl]['timestamp'],"%Y-%m-%dT%H:%M:%SZ") + cacheTime= \ + datetime.datetime.strptime(personCache[personUrl]['timestamp'], \ + "%Y-%m-%dT%H:%M:%SZ") daysSinceCached=(currTime - cacheTime).days # return cached value if it has not expired if daysSinceCached <= 2: diff --git a/daemon.py b/daemon.py index 38a37358b..90d74b3a2 100644 --- a/daemon.py +++ b/daemon.py @@ -132,16 +132,22 @@ class PubServer(BaseHTTPRequestHandler): return False # https://www.w3.org/TR/activitypub/#create-activity-outbox messageJson['object']['attributedTo']=messageJson['actor'] - permittedOutboxTypes=['Create','Announce','Like','Follow','Undo','Update','Add','Remove','Block','Delete'] + permittedOutboxTypes=[ + 'Create','Announce','Like','Follow','Undo', \ + 'Update','Add','Remove','Block','Delete' + ] if messageJson['type'] not in permittedOutboxTypes: if self.server.debug: - print('DEBUG: POST to outbox - '+messageJson['type']+' is not a permitted activity type') + print('DEBUG: POST to outbox - '+messageJson['type']+ \ + ' is not a permitted activity type') return False if messageJson.get('id'): postId=messageJson['id'] else: postId=None - savePostToBox(self.server.baseDir,postId,self.postToNickname,self.server.domain,messageJson,'outbox') + savePostToBox(self.server.baseDir,postId, \ + self.postToNickname, \ + self.server.domain,messageJson,'outbox') return True def _updateInboxQueue(self,nickname: str,messageJson: {}) -> bool: @@ -158,7 +164,6 @@ class PubServer(BaseHTTPRequestHandler): '/'+self.path.split('/')[-1], self.server.debug) if queueFilename: - print('**************************************') if queueFilename not in self.server.inboxQueue: self.server.inboxQueue.append(queueFilename) self.send_response(201) @@ -169,7 +174,9 @@ class PubServer(BaseHTTPRequestHandler): def do_GET(self): if self.server.debug: - print('DEBUG: GET from '+self.server.baseDir+' path: '+self.path+' busy: '+str(self.server.GETbusy)) + print('DEBUG: GET from '+self.server.baseDir+ \ + ' path: '+self.path+' busy: '+ \ + str(self.server.GETbusy)) if self.server.GETbusy: currTimeGET=int(time.time()) if currTimeGET-self.server.lastGET<10: @@ -211,7 +218,8 @@ class PubServer(BaseHTTPRequestHandler): return else: if self.server.debug: - print('DEBUG: '+nickname+' was not authorized to access '+self.path) + print('DEBUG: '+nickname+ \ + ' was not authorized to access '+self.path) if self.server.debug: print('DEBUG: GET access to inbox is unauthorized') self.send_response(405) @@ -239,7 +247,8 @@ class PubServer(BaseHTTPRequestHandler): return followers=getFollowingFeed(self.server.baseDir,self.server.domain, \ self.server.port,self.path, \ - self.server.httpPrefix,followsPerPage,'followers') + self.server.httpPrefix, \ + followsPerPage,'followers') if followers: self._set_headers('application/json') self.wfile.write(json.dumps(followers).encode('utf-8')) @@ -279,7 +288,9 @@ class PubServer(BaseHTTPRequestHandler): def do_POST(self): if self.server.debug: - print('DEBUG: POST to from '+self.server.baseDir+' path: '+self.path+' busy: '+str(self.server.POSTbusy)) + print('DEBUG: POST to from '+self.server.baseDir+ \ + ' path: '+self.path+' busy: '+ \ + str(self.server.POSTbusy)) if self.server.POSTbusy: currTimePOST=int(time.time()) if currTimePOST-self.server.lastPOST<10: @@ -314,7 +325,9 @@ class PubServer(BaseHTTPRequestHandler): if self.path.endswith('/outbox'): if '/users/' in self.path: if self.headers.get('Authorization'): - if authorize(self.server.baseDir,self.path,self.headers['Authorization'],self.server.debug): + if authorize(self.server.baseDir,self.path, \ + self.headers['Authorization'], \ + self.server.debug): self.outboxAuthenticated=True pathUsersSection=path.split('/users/')[1] self.postToNickname=pathUsersSection.split('/')[0] @@ -354,7 +367,8 @@ class PubServer(BaseHTTPRequestHandler): # https://www.w3.org/TR/activitypub/#object-without-create if self.outboxAuthenticated: if self._postToOutbox(messageJson): - self.send_header('Location',messageJson['object']['atomUri']) + self.send_header('Location', \ + messageJson['object']['atomUri']) self.send_response(201) self.end_headers() self.server.POSTbusy=False @@ -379,7 +393,10 @@ class PubServer(BaseHTTPRequestHandler): self.server.POSTbusy=False return - if not inboxPermittedMessage(self.server.domain,messageJson,self.server.federationList,self.server.capsList): + if not inboxPermittedMessage(self.server.domain, \ + messageJson, \ + self.server.federationList, \ + self.server.capsList): if self.server.debug: # https://www.youtube.com/watch?v=K3PrSj9XEu4 print('DEBUG: Ah Ah Ah') @@ -425,7 +442,8 @@ class PubServer(BaseHTTPRequestHandler): self.end_headers() self.server.POSTbusy=False -def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https',fedList=[],capsList=[],useTor=False,debug=False) -> None: +def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https', \ + fedList=[],capsList=[],useTor=False,debug=False) -> None: if len(domain)==0: domain='localhost' if '.' not in domain: @@ -456,6 +474,12 @@ def runDaemon(baseDir: str,domain: str,port=80,httpPrefix='https',fedList=[],cap httpd.sendThreads=[] httpd.postLog=[] print('Running ActivityPub daemon on ' + domain + ' port ' + str(port)) - httpd.thrInboxQueue=threadWithTrace(target=runInboxQueue,args=(baseDir,httpPrefix,httpd.sendThreads,httpd.postLog,httpd.cachedWebfingers,httpd.personCache,httpd.inboxQueue,domain,port,useTor,httpd.federationList,httpd.capsList,debug),daemon=True) + httpd.thrInboxQueue= \ + threadWithTrace(target=runInboxQueue, \ + args=(baseDir,httpPrefix,httpd.sendThreads, \ + httpd.postLog,httpd.cachedWebfingers, \ + httpd.personCache,httpd.inboxQueue, \ + domain,port,useTor,httpd.federationList, \ + httpd.capsList,debug),daemon=True) httpd.thrInboxQueue.start() httpd.serve_forever() diff --git a/follow.py b/follow.py index 05602c594..4ec27282e 100644 --- a/follow.py +++ b/follow.py @@ -18,7 +18,9 @@ from posts import sendSignedJson from capabilities import isCapable from acceptreject import createAccept -def getFollowersOfPerson(baseDir: str,nickname: str,domain: str,followFile='following.txt') -> []: +def getFollowersOfPerson(baseDir: str, \ + nickname: str,domain: str, \ + followFile='following.txt') -> []: """Returns a list containing the followers of the given person Used by the shared inbox to know who to send incoming mail to """ @@ -46,7 +48,8 @@ def followPerson(baseDir: str,nickname: str, domain: str, \ federationList: [], followFile='following.txt') -> bool: """Adds a person to the follow list """ - if not domainPermitted(followDomain.lower().replace('\n',''), federationList): + if not domainPermitted(followDomain.lower().replace('\n',''), \ + federationList): return False handle=nickname.lower()+'@'+domain.lower() handleToFollow=followNickname.lower()+'@'+followDomain.lower() @@ -100,9 +103,11 @@ def unfollowerOfPerson(baseDir: str,nickname: str,domain: str, \ followerNickname: str,followerDomain: str) -> None: """Remove a follower of a person """ - unfollowPerson(baseDir,nickname,domain,followerNickname,followerDomain,'followers.txt') + unfollowPerson(baseDir,nickname,domain, \ + followerNickname,followerDomain,'followers.txt') -def clearFollows(baseDir: str,nickname: str,domain: str,followFile='following.txt') -> None: +def clearFollows(baseDir: str,nickname: str,domain: str, \ + followFile='following.txt') -> None: """Removes all follows """ handle=nickname.lower()+'@'+domain.lower() @@ -119,7 +124,8 @@ def clearFollowers(baseDir: str,nickname: str,domain: str) -> None: """ clearFollows(baseDir,nickname, domain,'followers.txt') -def getNoOfFollows(baseDir: str,nickname: str,domain: str,followFile='following.txt') -> int: +def getNoOfFollows(baseDir: str,nickname: str,domain: str, \ + followFile='following.txt') -> int: """Returns the number of follows or followers """ handle=nickname.lower()+'@'+domain.lower() @@ -142,8 +148,9 @@ def getNoOfFollowers(baseDir: str,nickname: str,domain: str) -> int: """ return getNoOfFollows(baseDir,nickname,domain,'followers.txt') -def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,httpPrefix: str, \ - followsPerPage=12,followFile='following') -> {}: +def getFollowingFeed(baseDir: str,domain: str,port: int,path: str, \ + httpPrefix: str, followsPerPage=12, \ + followFile='following') -> {}: """Returns the following and followers feeds from GET requests """ if '/'+followFile not in path: @@ -233,7 +240,11 @@ def getFollowingFeed(baseDir: str,domain: str,port: int,path: str,httpPrefix: st following['next']=httpPrefix+'://'+domain+'/users/'+nickname+'/'+followFile+'?page='+str(lastPage) return following -def receiveFollowRequest(session,baseDir: str,httpPrefix: str,port: int,sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {},messageJson: {},federationList: [],capsList: [],debug : bool) -> bool: +def receiveFollowRequest(session,baseDir: str,httpPrefix: str, \ + port: int,sendThreads: [],postLog: [], \ + cachedWebfingers: {},personCache: {}, \ + messageJson: {},federationList: [], \ + capsList: [],debug : bool) -> bool: """Receives a follow request within the POST section of HTTPServer """ if not messageJson['type'].startswith('Follow'): @@ -278,33 +289,46 @@ def receiveFollowRequest(session,baseDir: str,httpPrefix: str,port: int,sendThre if domainToFollow==domain: if not os.path.isdir(baseDir+'/accounts/'+handleToFollow): if debug: - print('DEBUG: followed account not found - '+baseDir+'/accounts/'+handleToFollow) + print('DEBUG: followed account not found - '+ \ + baseDir+'/accounts/'+handleToFollow) return False - if not followerOfPerson(baseDir,nickname,domain,nicknameToFollow,domainToFollow,federationList): + if not followerOfPerson(baseDir,nickname,domain, \ + nicknameToFollow,domainToFollow,federationList): if debug: - print('DEBUG: '+nickname+'@'+domain+' is already a follower of '+nicknameToFollow+'@'+domainToFollow) + print('DEBUG: '+nickname+'@'+domain+ \ + ' is already a follower of '+ \ + nicknameToFollow+'@'+domainToFollow) return False # send accept back if debug: - print('DEBUG: sending Accept for follow request which arrived at '+nicknameToFollow+'@'+domainToFollow+' back to '+nickname+'@'+domain) + print('DEBUG: sending Accept for follow request which arrived at '+ \ + nicknameToFollow+'@'+domainToFollow+' back to '+nickname+'@'+domain) personUrl=messageJson['actor'] - acceptJson=createAccept(baseDir,federationList,capsList,nickname,domain,port, \ + acceptJson=createAccept(baseDir,federationList,capsList, \ + nickname,domain,port, \ personUrl,'',httpPrefix,messageJson['object']) if debug: pprint(acceptJson) - print('DEBUG: sending follow Accept from '+nicknameToFollow+'@'+domainToFollow+' port '+str(port)+' to '+nickname+'@'+domain+' port '+ str(fromPort)) + print('DEBUG: sending follow Accept from '+ \ + nicknameToFollow+'@'+domainToFollow+ \ + ' port '+str(port)+' to '+ \ + nickname+'@'+domain+' port '+ str(fromPort)) clientToServer=False - return sendSignedJson(acceptJson,session,baseDir,nicknameToFollow,domainToFollow,port, \ + return sendSignedJson(acceptJson,session,baseDir, \ + nicknameToFollow,domainToFollow,port, \ nickname,domain,fromPort, '', \ httpPrefix,True,clientToServer, \ federationList, capsList, \ - sendThreads,postLog,cachedWebfingers,personCache,debug) + sendThreads,postLog,cachedWebfingers, \ + personCache,debug) -def sendFollowRequest(session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ - followNickname: str,followDomain: str,followPort: bool,followHttpPrefix: str, \ +def sendFollowRequest(session,baseDir: str, \ + nickname: str,domain: str,port: int,httpPrefix: str, \ + followNickname: str,followDomain: str, \ + followPort: bool,followHttpPrefix: str, \ clientToServer: bool,federationList: [],capsList: [], \ - sendThreads: [],postLog: [],cachedWebfingers: {},personCache: {}, - debug : bool) -> {}: + sendThreads: [],postLog: [],cachedWebfingers: {}, \ + personCache: {},debug : bool) -> {}: """Gets the json object for sending a follow request """ if not domainPermitted(followDomain,federationList): diff --git a/posts.py b/posts.py index 3f16c2dbe..d970f52d1 100644 --- a/posts.py +++ b/posts.py @@ -36,7 +36,8 @@ try: except ImportError: from bs4 import BeautifulSoup -def noOfFollowersOnDomain(baseDir: str,handle: str, domain: str, followFile='followers.txt') -> int: +def noOfFollowersOnDomain(baseDir: str,handle: str, \ + domain: str, followFile='followers.txt') -> int: """Returns the number of followers of the given handle from the given domain """ filename=baseDir+'/accounts/'+handle+'/'+followFile @@ -47,12 +48,14 @@ def noOfFollowersOnDomain(baseDir: str,handle: str, domain: str, followFile='fol with open(filename, "r") as followersFilename: for followerHandle in followersFilename: if '@' in followerHandle: - followerDomain=followerHandle.split('@')[1].replace('\n','') + followerDomain= \ + followerHandle.split('@')[1].replace('\n','') if domain==followerDomain: ctr+=1 return ctr -def getPersonKey(nickname: str,domain: str,baseDir: str,keyType='public',debug=False): +def getPersonKey(nickname: str,domain: str,baseDir: str,keyType='public', \ + debug=False): """Returns the public or private key of a person """ handle=nickname+'@'+domain @@ -101,7 +104,8 @@ def parseUserFeed(session,feedUrl: str,asHeader: {}) -> None: for item in parseUserFeed(session,nextUrl,asHeader): yield item -def getPersonBox(session,wfRequest: {},personCache: {},boxName='inbox') -> (str,str,str,str,str): +def getPersonBox(session,wfRequest: {},personCache: {}, \ + boxName='inbox') -> (str,str,str,str,str): asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} personUrl = getUserUrl(wfRequest) if not personUrl: @@ -140,18 +144,8 @@ def getPersonBox(session,wfRequest: {},personCache: {},boxName='inbox') -> (str, if personJson['endpoints'].get('sharedInbox'): sharedInbox=personJson['endpoints']['sharedInbox'] capabilityAcquisition=None - if personJson.get('capabilityAcquisition'): - capabilityAcquisition=personJson['capabilityAcquisition'] - else: - if personJson.get('capabilityAcquisitionEndpoint'): - capabilityAcquisition=personJson['capabilityAcquisitionEndpoint'] - else: - if personJson.get('endpoints'): - if personJson['endpoints'].get('capabilityAcquisition'): - capabilityAcquisition=personJson['endpoints']['capabilityAcquisition'] - else: - if personJson['endpoints'].get('capabilityAcquisitionEndpoint'): - capabilityAcquisition=personJson['endpoints']['capabilityAcquisitionEndpoint'] + if personJson.get('capabilityAcquisitionEndpoint'): + capabilityAcquisition=personJson['capabilityAcquisitionEndpoint'] storePersonInCache(personUrl,personJson,personCache) @@ -198,7 +192,9 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \ if tagItem.get('name') and tagItem.get('icon'): if tagItem['icon'].get('url'): # No emoji from non-permitted domains - if urlPermitted(tagItem['icon']['url'],federationList,capsList,"objects:read"): + if urlPermitted(tagItem['icon']['url'], \ + federationList,capsList, \ + "objects:read"): emojiName=tagItem['name'] emojiIcon=tagItem['icon']['url'] emoji[emojiName]=emojiIcon @@ -220,7 +216,9 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \ if item['object'].get('inReplyTo'): if item['object']['inReplyTo']: # No replies to non-permitted domains - if not urlPermitted(item['object']['inReplyTo'],federationList,capsList,"objects:read"): + if not urlPermitted(item['object']['inReplyTo'], \ + federationList,capsList, \ + "objects:read"): continue inReplyTo = item['object']['inReplyTo'] @@ -228,7 +226,8 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \ if item['object'].get('conversation'): if item['object']['conversation']: # no conversations originated in non-permitted domains - if urlPermitted(item['object']['conversation'],federationList,"objects:read"): + if urlPermitted(item['object']['conversation'], \ + federationList,"objects:read"): conversation = item['object']['conversation'] attachment = [] @@ -237,7 +236,9 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \ for attach in item['object']['attachment']: if attach.get('name') and attach.get('url'): # no attachments from non-permitted domains - if urlPermitted(attach['url'],federationList,capsList,"objects:read"): + if urlPermitted(attach['url'], \ + federationList,capsList, \ + "objects:read"): attachment.append([attach['name'],attach['url']]) sensitive = False @@ -264,7 +265,8 @@ def getPosts(session,outboxUrl: str,maxPosts: int,maxMentions: int, \ break return personPosts -def createBoxArchive(nickname: str,domain: str,baseDir: str,boxname: str) -> str: +def createBoxArchive(nickname: str,domain: str,baseDir: str, \ + boxname: str) -> str: """Creates an archive directory for inbox/outbox posts """ handle=nickname.lower()+'@'+domain.lower() @@ -290,7 +292,9 @@ def deleteAllPosts(baseDir: str,nickname: str, domain: str,boxname: str) -> None except Exception as e: print(e) -def savePostToBox(baseDir: str,httpPrefix: str,postId: str,nickname: str, domain: str,postJson: {},boxname: str) -> None: +def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \ + nickname: str, domain: str,postJson: {}, \ + boxname: str) -> None: """Saves the give json to the give box """ if boxname!='inbox' and boxname!='outbox': @@ -300,7 +304,8 @@ def savePostToBox(baseDir: str,httpPrefix: str,postId: str,nickname: str, domain if not postId: statusNumber,published = getStatusNumber() - postId=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber + postId=httpPrefix+'://'+domain+'/users/'+nickname+ \ + '/statuses/'+statusNumber postJson['id']=postId+'/activity' if postJson.get('object'): postJson['object']['id']=postId @@ -410,10 +415,12 @@ def createPostBase(baseDir: str,nickname: str, domain: str, port: int, \ newPost['cc']=ccUrl newPost['object']['cc']=ccUrl if saveToFile: - savePostToBox(baseDir,httpPrefix,newPostId,nickname,domain,newPost,'outbox') + savePostToBox(baseDir,httpPrefix,newPostId, \ + nickname,domain,newPost,'outbox') return newPost -def outboxMessageCreateWrap(httpPrefix: str,nickname: str,domain: str,messageJson: {}) -> {}: +def outboxMessageCreateWrap(httpPrefix: str,nickname: str,domain: str, \ + messageJson: {}) -> {}: """Wraps a received message in a Create https://www.w3.org/TR/activitypub/#object-without-create """ @@ -438,8 +445,10 @@ def outboxMessageCreateWrap(httpPrefix: str,nickname: str,domain: str,messageJso 'object': messageJson } newPost['object']['id']=newPost['id'] - newPost['object']['url']=httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber - newPost['object']['atomUri']=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber + newPost['object']['url']= \ + httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber + newPost['object']['atomUri']= \ + httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber return newPost def createPublicPost(baseDir: str, @@ -452,8 +461,8 @@ def createPublicPost(baseDir: str, return createPostBase(baseDir,nickname, domain, port, \ 'https://www.w3.org/ns/activitystreams#Public', \ httpPrefix+'://'+domain+'/users/'+nickname+'/followers', \ - httpPrefix, content, followersOnly, saveToFile, clientToServer, \ - capsList, + httpPrefix, content, followersOnly, saveToFile, \ + clientToServer, capsList, \ inReplyTo, inReplyToAtomUri, subject) def threadSendPost(session,postJsonObject: {},federationList: [],capsList: [],\ @@ -483,7 +492,8 @@ def threadSendPost(session,postJsonObject: {},federationList: [],capsList: [],\ # our work here is done break if debug: - print('DEBUG: json post to '+inboxUrl+' failed. Waiting for '+str(backoffTime)+' seconds.') + print('DEBUG: json post to '+inboxUrl+' failed. Waiting for '+ \ + str(backoffTime)+' seconds.') time.sleep(backoffTime) backoffTime *= 2 @@ -573,12 +583,13 @@ def sendPost(session,baseDir: str,nickname: str, domain: str, port: int, \ thr.start() return 0 -def sendSignedJson(postJsonObject: {},session,baseDir: str,nickname: str, domain: str, port: int, \ +def sendSignedJson(postJsonObject: {},session,baseDir: str, \ + nickname: str, domain: str, port: int, \ toNickname: str, toDomain: str, toPort: int, cc: str, \ httpPrefix: str, saveToFile: bool, clientToServer: bool, \ federationList: [], capsList: [], \ - sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \ - debug: bool) -> int: + sendThreads: [], postLog: [], cachedWebfingers: {}, \ + personCache: {}, debug: bool) -> int: """Sends a signed json object to an inbox/outbox """ withDigest=True @@ -646,14 +657,15 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str,nickname: str, domain while len(sendThreads)>10: sendThreads[0].kill() sendThreads.pop(0) - thr = threadWithTrace(target=threadSendPost,args=(session, \ - postJsonObject.copy(), \ - federationList, \ - capsList, \ - inboxUrl,baseDir, \ - signatureHeaderJson.copy(), \ - postLog, - debug),daemon=True) + thr = threadWithTrace(target=threadSendPost, \ + args=(session, \ + postJsonObject.copy(), \ + federationList, \ + capsList, \ + inboxUrl,baseDir, \ + signatureHeaderJson.copy(), \ + postLog, + debug),daemon=True) sendThreads.append(thr) thr.start() return 0 @@ -667,7 +679,8 @@ def createOutbox(baseDir: str,nickname: str,domain: str,port: int,httpPrefix: st return createBoxBase(baseDir,'outbox',nickname,domain,port,httpPrefix, \ itemsPerPage,headerOnly,pageNumber) -def createBoxBase(baseDir: str,boxname: str,nickname: str,domain: str,port: int,httpPrefix: str, \ +def createBoxBase(baseDir: str,boxname: str, \ + nickname: str,domain: str,port: int,httpPrefix: str, \ itemsPerPage: int,headerOnly: bool,pageNumber=None) -> {}: """Constructs the box feed """ @@ -727,7 +740,8 @@ def createBoxBase(baseDir: str,boxname: str,nickname: str,domain: str,port: int, # update the prev entry for the last message id postId = prevPostFilename.split('#statuses#')[1].replace('#activity','') boxHeader['prev']= \ - httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?min_id='+postId+'&page=true' + httpPrefix+'://'+domain+'/users/'+nickname+'/'+ \ + boxname+'?min_id='+postId+'&page=true' # get the full path of the post file filePath = os.path.join(boxDir, postFilename) try: @@ -788,7 +802,8 @@ def archivePosts(nickname: str,domain: str,baseDir: str, \ if noOfPosts <= maxPostsInBox: break -def getPublicPostsOfPerson(nickname: str,domain: str,raw: bool,simple: bool) -> None: +def getPublicPostsOfPerson(nickname: str,domain: str, \ + raw: bool,simple: bool) -> None: """ This is really just for test purposes """ useTor=True @@ -800,7 +815,8 @@ def getPublicPostsOfPerson(nickname: str,domain: str,raw: bool,simple: bool) -> httpPrefix='https' handle=httpPrefix+"://"+domain+"/@"+nickname - wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers) + wfRequest = \ + webfingerHandle(session,handle,httpPrefix,cachedWebfingers) if not wfRequest: sys.exit() @@ -811,5 +827,7 @@ def getPublicPostsOfPerson(nickname: str,domain: str,raw: bool,simple: bool) -> maxMentions=10 maxEmoji=10 maxAttachments=5 - userPosts = getPosts(session,personUrl,30,maxMentions,maxEmoji,maxAttachments,federationList,personCache,raw,simple) + userPosts = getPosts(session,personUrl,30,maxMentions,maxEmoji, \ + maxAttachments,federationList,personCache, \ + raw,simple) #print(str(userPosts))