unfollowing functions

master
Bob Mottram 2019-07-17 11:34:00 +01:00
parent 3813809958
commit c55b5c9e15
3 changed files with 229 additions and 10 deletions

View File

@ -29,6 +29,7 @@ from inbox import inboxMessageHasParams
from inbox import runInboxQueue from inbox import runInboxQueue
from inbox import savePostToInboxQueue from inbox import savePostToInboxQueue
from follow import getFollowingFeed from follow import getFollowingFeed
from follow import outboxUndoFollow
from auth import authorize from auth import authorize
from auth import createPassword from auth import createPassword
from threads import threadWithTrace from threads import threadWithTrace
@ -200,6 +201,9 @@ class PubServer(BaseHTTPRequestHandler):
self.server.cachedWebfingers, \ self.server.cachedWebfingers, \
self.server.personCache, \ self.server.personCache, \
messageJson,self.server.debug) messageJson,self.server.debug)
if self.server.debug:
print('DEBUG: handle any unfollow requests')
outboxUndoFollow(self.server.baseDir,messageJson,self.server.debug)
if self.server.debug: if self.server.debug:
print('DEBUG: sending c2s post to named addresses') print('DEBUG: sending c2s post to named addresses')
print('c2s sender: '+self.postToNickname+'@'+self.server.domain+':'+str(self.server.port)) print('c2s sender: '+self.postToNickname+'@'+self.server.domain+':'+str(self.server.port))

144
follow.py
View File

@ -62,7 +62,7 @@ def followerOfPerson(baseDir: str,nickname: str, domain: str, \
def unfollowPerson(baseDir: str,nickname: str, domain: str, \ def unfollowPerson(baseDir: str,nickname: str, domain: str, \
followNickname: str, followDomain: str, \ followNickname: str, followDomain: str, \
followFile='following.txt') -> None: followFile='following.txt') -> bool:
"""Removes a person to the follow list """Removes a person to the follow list
""" """
handle=nickname.lower()+'@'+domain.lower() handle=nickname.lower()+'@'+domain.lower()
@ -72,15 +72,16 @@ def unfollowPerson(baseDir: str,nickname: str, domain: str, \
if not os.path.isdir(baseDir+'/accounts/'+handle): if not os.path.isdir(baseDir+'/accounts/'+handle):
os.mkdir(baseDir+'/accounts/'+handle) os.mkdir(baseDir+'/accounts/'+handle)
filename=baseDir+'/accounts/'+handle+'/'+followFile filename=baseDir+'/accounts/'+handle+'/'+followFile
if os.path.isfile(filename): if not os.path.isfile(filename):
if handleToUnfollow not in open(filename).read(): return False
return if handleToUnfollow not in open(filename).read():
with open(filename, "r") as f: return
lines = f.readlines() with open(filename, "r") as f:
with open(filename, "w") as f: lines = f.readlines()
for line in lines: with open(filename, "w") as f:
if line.strip("\n") != handleToUnfollow: for line in lines:
f.write(line) if line.strip("\n") != handleToUnfollow:
f.write(line)
def unfollowerOfPerson(baseDir: str,nickname: str,domain: str, \ def unfollowerOfPerson(baseDir: str,nickname: str,domain: str, \
followerNickname: str,followerDomain: str) -> None: followerNickname: str,followerDomain: str) -> None:
@ -432,6 +433,81 @@ def sendFollowRequestViaServer(session,fromNickname: str,password: str,
return newFollowJson return newFollowJson
def sendUnfollowRequestViaServer(session,fromNickname: str,password: str,
fromDomain: str,fromPort: int, \
followNickname: str,followDomain: str,followPort: int, \
httpPrefix: str, \
cachedWebfingers: {},personCache: {}, \
debug: bool) -> {}:
"""Creates a unfollow request via c2s
"""
if not session:
print('WARN: No session for sendUnfollowRequestViaServer')
return 6
fromDomainFull=fromDomain
if fromPort!=80 and fromPort!=443:
fromDomainFull=fromDomain+':'+str(fromPort)
followDomainFull=followDomain
if followPort!=80 and followPort!=443:
followDomainFull=followDomain+':'+str(followPort)
followActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname
followedId=httpPrefix+'://'+followDomainFull+'/users/'+followNickname
unfollowJson = {
'type': 'Undo',
'actor': followActor,
'object': {
'type': 'Follow',
'actor': followActor,
'object': followedId,
'to': [followedId],
'cc': ['https://www.w3.org/ns/activitystreams#Public']
}
}
handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname
# lookup the inbox for the To handle
wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers)
if not wfRequest:
if debug:
print('DEBUG: announce webfinger failed for '+handle)
return 1
postToBox='outbox'
# get the actor inbox for the To handle
inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition = \
getPersonBox(session,wfRequest,personCache,postToBox)
if not inboxUrl:
if debug:
print('DEBUG: No '+postToBox+' was found for '+handle)
return 3
if not fromPersonId:
if debug:
print('DEBUG: No actor was found for '+handle)
return 4
authHeader=createBasicAuthHeader(fromNickname,password)
headers = {'host': fromDomain, \
'Content-type': 'application/json', \
'Authorization': authHeader}
postResult = \
postJson(session,unfollowJson,[],inboxUrl,headers,"inbox:write")
#if not postResult:
# if debug:
# print('DEBUG: POST announce failed for c2s to '+inboxUrl)
# return 5
if debug:
print('DEBUG: c2s POST unfollow success')
return unfollowJson
def getFollowersOfActor(baseDir :str,actor :str,debug: bool) -> {}: def getFollowersOfActor(baseDir :str,actor :str,debug: bool) -> {}:
"""In a shared inbox if we receive a post we know who it's from """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. and if it's addressed to followers then we need to get a list of those.
@ -493,3 +569,51 @@ def getFollowersOfActor(baseDir :str,actor :str,debug: bool) -> {}:
print(ocapFilename) print(ocapFilename)
recipientsDict[account]=None recipientsDict[account]=None
return recipientsDict return recipientsDict
def outboxUndoFollow(baseDir: str,messageJson: {},debug: bool) -> None:
"""When an unfollow request is received by the outbox from c2s
This removes the followed handle from the following.txt file
of the relevant account
"""
if not messageJson.get('type'):
return
if not messageJson['type']=='Undo':
return
if not messageJson.get('object'):
return
if not isinstance(messageJson['object'], dict):
return
if not messageJson['object'].get('type'):
return
if not messageJson['object']['type']=='Follow':
return
if not messageJson['object'].get('object'):
return
if not messageJson['object'].get('actor'):
return
if not isinstance(messageJson['object']['object'], str):
return
if debug:
print('DEBUG: undo follow arrived in outbox')
nicknameFollower=getNicknameFromActor(messageJson['object']['actor'])
domainFollower,portFollower=getDomainFromActor(messageJson['object']['actor'])
domainFollowerFull=domainFollower
if portFollower:
if portFollower!=80 and portFollower!=443:
domainFollowerFull=domainFollower+':'+str(portFollower)
nicknameFollowing=getNicknameFromActor(messageJson['object']['object'])
domainFollowing,portFollowing=getDomainFromActor(messageJson['object']['object'])
domainFollowingFull=domainFollowing
if portFollowing:
if portFollowing!=80 and portFollowing!=443:
domainFollowingFull=domainFollowing+':'+str(portFollowing)
if unfollowPerson(baseDir,nicknameFollower,domainFollowerFull, \
nicknameFollowing,domainFollowingFull):
if debug:
print('DEBUG: '+nicknameFollower+' unfollowed '+nicknameFollowing+'@'+domainFollowingFull)
else:
if debug:
print('WARN: '+nicknameFollower+' could not unfollow '+nicknameFollowing+'@'+domainFollowingFull)

View File

@ -379,6 +379,82 @@ def inboxPostRecipients(baseDir :str,postJsonObject :{},httpPrefix :str,domain :
return recipientsDict,recipientsDictFollowers return recipientsDict,recipientsDictFollowers
def receiveUndoFollow(session,baseDir: str,httpPrefix: str, \
port: int,messageJson: {},debug : bool) -> bool:
if not messageJson['object'].get('actor'):
if debug:
print('DEBUG: follow request has no actor within object')
return False
if '/users/' not in messageJson['object']['actor']:
if debug:
print('DEBUG: "users" missing from actor within object')
return False
if messageJson['object']['actor'] != messageJson['actor']:
if debug:
print('DEBUG: actors do not match')
return False
nicknameFollower=getNicknameFromActor(messageJson['object']['actor'])
domainFollower,portFollower=getDomainFromActor(messageJson['object']['actor'])
domainFollowerFull=domainFollower
if portFollower:
if portFollower!=80 and portFollower!=443:
domainFollowerFull=domainFollower+':'+str(portFollower)
nicknameFollowing=getNicknameFromActor(messageJson['object']['object'])
domainFollowing,portFollowing=getDomainFromActor(messageJson['object']['object'])
domainFollowingFull=domainFollowing
if portFollowing:
if portFollowing!=80 and portFollowing!=443:
domainFollowingFull=domainFollowing+':'+str(portFollowing)
unfollowerOfPerson(baseDir,nicknameFollower,domainFollowerFull, \
nicknameFollowing,domainFollowingFull,federationList,debug)
return True
def receiveUndo(session,baseDir: str,httpPrefix: str, \
port: int,sendThreads: [],postLog: [], \
cachedWebfingers: {},personCache: {}, \
messageJson: {},federationList: [], \
debug : bool, \
acceptedCaps=["inbox:write","objects:read"]) -> bool:
"""Receives an undo request within the POST section of HTTPServer
"""
if not messageJson['type'].startswith('Undo'):
return False
if not messageJson.get('actor'):
if debug:
print('DEBUG: follow request has no actor')
return False
if '/users/' not in messageJson['actor']:
if debug:
print('DEBUG: "users" missing from actor')
return False
if not messageJson.get('object'):
if debug:
print('DEBUG: '+messageJson['type']+' has no object')
return False
if not isinstance(messageJson['object'], dict):
if debug:
print('DEBUG: '+messageJson['type']+' object is not a dict')
return False
if not messageJson['object'].get('type'):
if debug:
print('DEBUG: '+messageJson['type']+' has no object type')
return False
if not messageJson['object'].get('object'):
if debug:
print('DEBUG: '+messageJson['type']+' has no object within object')
return False
if not isinstance(messageJson['object']['object'], str):
if debug:
print('DEBUG: '+messageJson['type']+' object within object is not a string')
return False
if messageJson['object']['type']=='Follow':
return receiveUndoFollow(session,baseDir,httpPrefix, \
port,messageJson,debug)
return False
def receiveUpdate(session,baseDir: str, \ def receiveUpdate(session,baseDir: str, \
httpPrefix: str,domain :str,port: int, \ httpPrefix: str,domain :str,port: int, \
sendThreads: [],postLog: [],cachedWebfingers: {}, \ sendThreads: [],postLog: [],cachedWebfingers: {}, \
@ -953,6 +1029,21 @@ def runInboxQueue(baseDir: str,httpPrefix: str,sendThreads: [],postLog: [], \
if debug: if debug:
print('DEBUG: Signature check success') print('DEBUG: Signature check success')
if receiveUndo(session, \
baseDir,httpPrefix,port, \
sendThreads,postLog, \
cachedWebfingers,
personCache,
queueJson['post'], \
federationList, \
debug, \
acceptedCaps=["inbox:write","objects:read"]):
if debug:
print('DEBUG: Undo accepted from '+keyId)
os.remove(queueFilename)
queue.pop(0)
continue
if receiveFollowRequest(session, \ if receiveFollowRequest(session, \
baseDir,httpPrefix,port, \ baseDir,httpPrefix,port, \
sendThreads,postLog, \ sendThreads,postLog, \