mirror of https://gitlab.com/bashrc2/epicyon
Reduce some line lengths
parent
e3be2f4328
commit
3a6fe719a9
|
@ -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
|
||||
|
|
|
@ -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':
|
||||
|
|
11
announce.py
11
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)
|
||||
|
||||
|
|
4
auth.py
4
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):
|
||||
|
|
9
cache.py
9
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:
|
||||
|
|
50
daemon.py
50
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()
|
||||
|
|
64
follow.py
64
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):
|
||||
|
|
110
posts.py
110
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))
|
||||
|
|
Loading…
Reference in New Issue