diff --git a/inbox.py b/inbox.py index bebaef28..e87272c2 100644 --- a/inbox.py +++ b/inbox.py @@ -33,6 +33,7 @@ from capabilities import getOcapFilename from capabilities import CapablePost from capabilities import capabilitiesReceiveUpdate from like import updateLikesCollection +from like import undoLikesCollectionEntry def getPersonPubKey(session,personUrl: str,personCache: {},debug: bool) -> str: if not personUrl: @@ -439,6 +440,55 @@ def receiveLike(session,handle: str,baseDir: str, \ updateLikesCollection(postFilename,messageJson['object'],messageJson['actor'],debug) return True +def receiveUndoLike(session,handle: str,baseDir: str, \ + httpPrefix: str,domain :str,port: int, \ + sendThreads: [],postLog: [],cachedWebfingers: {}, \ + personCache: {},messageJson: {},federationList: [], \ + debug : bool) -> bool: + """Receives an undo like activity within the POST section of HTTPServer + """ + if messageJson['type']!='Undo': + return False + if not messageJson.get('actor'): + return False + if not messageJson.get('object'): + return False + if not isinstance(messageJson['object'], dict): + return False + if not messageJson['object'].get('type'): + return False + if messageJson['object']['type']!='Like': + return False + if not messageJson['object'].get('object'): + if debug: + print('DEBUG: '+messageJson['type']+' like has no object') + return False + if not isinstance(messageJson['object']['object'], str): + if debug: + print('DEBUG: '+messageJson['type']+' like object is not a string') + return False + if '/users/' not in messageJson['actor']: + if debug: + print('DEBUG: "users" missing from actor in '+messageJson['type']+' like') + return False + if '/statuses/' not in messageJson['object']['object']: + if debug: + print('DEBUG: "statuses" missing from like object in '+messageJson['type']) + return False + if not os.path.isdir(baseDir+'/accounts/'+handle): + print('DEBUG: unknown recipient of undo like - '+handle) + # if this post in the outbox of the person? + postFilename=locatePost(baseDir,handle.split('@')[0],handle.split('@')[1],messageJson['object']['object']) + if not postFilename: + if debug: + print('DEBUG: post not found in inbox or outbox') + print(messageJson['object']['object']) + return True + if debug: + print('DEBUG: liked post found in inbox. Now undoing.') + undoLikesCollectionEntry(postFilename,messageJson['object'],messageJson['actor'],debug) + return True + def receiveDelete(session,handle: str,baseDir: str, \ httpPrefix: str,domain :str,port: int, \ sendThreads: [],postLog: [],cachedWebfingers: {}, \ @@ -557,6 +607,19 @@ def inboxAfterCapabilities(session,keyId: str,handle: str,messageJson: {}, \ print('DEBUG: Like accepted from '+keyId) return False + if receiveUndoLike(session,handle, \ + baseDir,httpPrefix, \ + domain,port, \ + sendThreads,postLog, \ + cachedWebfingers, \ + personCache, \ + messageJson, \ + federationList, \ + debug): + if debug: + print('DEBUG: Undo like accepted from '+keyId) + return False + if receiveAnnounce(session,handle, \ baseDir,httpPrefix, \ domain,port, \ diff --git a/like.py b/like.py index 78d80ae9..d19edde4 100644 --- a/like.py +++ b/like.py @@ -103,8 +103,9 @@ def like(session,baseDir: str,federationList: [],nickname: str,domain: str,port: 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 + actor is the person doing the liking + 'to' might be a specific person (actor) whose post was liked + object is typically the url of the message which was liked """ if not urlPermitted(objectUrl,federationList,"inbox:write"): return None @@ -176,3 +177,90 @@ def likePost(session,baseDir: str,federationList: [], \ ccList,httpPrefix,objectUrl,clientToServer, \ sendThreads,postLog,personCache,cachedWebfingers,debug) +def undolike(session,baseDir: str,federationList: [],nickname: str,domain: str,port: int, \ + ccList: [],httpPrefix: str,objectUrl: str,clientToServer: bool, \ + sendThreads: [],postLog: [],personCache: {},cachedWebfingers: {}, \ + debug: bool) -> {}: + """Removes a like + actor is the person doing the liking + 'to' might be a specific person (actor) whose post was liked + object is typically the url of the message which was liked + """ + 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) + + newUndoLikeJson = { + 'type': 'Undo', + 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, + 'object': { + 'type': 'Like', + 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, + 'object': objectUrl, + 'to': [httpPrefix+'://'+fullDomain+'/users/'+nickname+'/followers'], + 'cc': [] + }, + 'to': [httpPrefix+'://'+fullDomain+'/users/'+nickname+'/followers'], + 'cc': [] + } + if ccList: + if len(ccList)>0: + newUndoLikeJson['cc']=ccList + newUndoLikeJson['object']['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 + + undoLikesCollectionEntry(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) + else: + return None + + return newLikeJson + +def undoLikePost(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) -> {}: + """Removes a liked 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 undoLike(session,baseDir,federationList,nickname,domain,port, \ + ccList,httpPrefix,objectUrl,clientToServer, \ + sendThreads,postLog,personCache,cachedWebfingers,debug)