From 71cabd74a2718275414770f4fb84e56aa33e67d5 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Fri, 30 Jul 2021 17:06:34 +0100 Subject: [PATCH] Unfollowing group handles --- acceptreject.py | 6 +++- daemon.py | 5 +++- epicyon.py | 12 ++++---- follow.py | 27 +++++++++++------ inbox.py | 4 ++- migrate.py | 6 ++-- tests.py | 80 +++++++++++++++++++++++++------------------------ utils.py | 17 +++++++++-- 8 files changed, 96 insertions(+), 61 deletions(-) diff --git a/acceptreject.py b/acceptreject.py index e61b6d558..2a3c73123 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -17,6 +17,7 @@ from utils import domainPermitted from utils import followPerson from utils import hasObjectDict from utils import acctDir +from utils import hasGroupPath def _createAcceptReject(baseDir: str, federationList: [], @@ -160,10 +161,13 @@ def _acceptFollow(baseDir: str, domain: str, messageJson: {}, ' but they have been unfollowed') return + # does the url path indicate that this is a group actor + groupAccount = hasGroupPath(followedActor) + if followPerson(baseDir, nickname, acceptedDomainFull, followedNickname, followedDomainFull, - federationList, debug): + federationList, debug, groupAccount): if debug: print('DEBUG: ' + nickname + '@' + acceptedDomainFull + ' followed ' + followedNickname + '@' + followedDomainFull) diff --git a/daemon.py b/daemon.py index 54646a5a1..7ba6cb06a 100644 --- a/daemon.py +++ b/daemon.py @@ -273,6 +273,7 @@ from utils import isSuspended from utils import dangerousMarkup from utils import refreshNewswire from utils import isImageFile +from utils import hasGroupPath from manualapprove import manualDenyFollowRequest from manualapprove import manualApproveFollowRequest from announce import createAnnounce @@ -2568,9 +2569,11 @@ class PubServer(BaseHTTPRequestHandler): } pathUsersSection = path.split('/users/')[1] self.postToNickname = pathUsersSection.split('/')[0] + groupAccount = hasGroupPath(followingActor) unfollowAccount(self.server.baseDir, self.postToNickname, self.server.domain, - followingNickname, followingDomainFull) + followingNickname, followingDomainFull, + self.server.debug, groupAccount) self._postToOutboxThread(unfollowJson) if callingDomain.endswith('.onion') and onionDomain: diff --git a/epicyon.py b/epicyon.py index d18f427cd..cb07e5c3f 100644 --- a/epicyon.py +++ b/epicyon.py @@ -2499,17 +2499,17 @@ if args.testdata: domainFull = domain + ':' + str(port) clearFollows(baseDir, nickname, domain) followPerson(baseDir, nickname, domain, 'maxboardroom', domainFull, - federationList, False) + federationList, False, False) followPerson(baseDir, nickname, domain, 'ultrapancake', domainFull, - federationList, False) + federationList, False, False) followPerson(baseDir, nickname, domain, 'sausagedog', domainFull, - federationList, False) + federationList, False, False) followPerson(baseDir, nickname, domain, 'drokk', domainFull, - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'drokk', domainFull, - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'maxboardroom', domainFull, - federationList, False) + federationList, False, False) setConfigParam(baseDir, 'admin', nickname) # set a lower bound to the maximum mentions diff --git a/follow.py b/follow.py index ee234a095..69059d72a 100644 --- a/follow.py +++ b/follow.py @@ -28,6 +28,7 @@ from utils import saveJson from utils import isAccountDir from utils import getUserPaths from utils import acctDir +from utils import hasGroupPath from acceptreject import createAccept from acceptreject import createReject from webfinger import webfingerHandle @@ -62,6 +63,8 @@ def createInitialLastSeen(baseDir: str, httpPrefix: str) -> None: handle = handle.replace('\n', '') nickname = handle.split('@')[0] domain = handle.split('@')[1] + if nickname.startswith('!'): + nickname = nickname[1:] actor = \ httpPrefix + '://' + domain + '/users/' + nickname lastSeenFilename = \ @@ -194,12 +197,13 @@ def getMutualsOfPerson(baseDir: str, def followerOfPerson(baseDir: str, nickname: str, domain: str, followerNickname: str, followerDomain: str, - federationList: [], debug: bool) -> bool: + federationList: [], debug: bool, + groupAccount: bool) -> bool: """Adds a follower of the given person """ return followPerson(baseDir, nickname, domain, followerNickname, followerDomain, - federationList, debug, 'followers.txt') + federationList, debug, groupAccount, 'followers.txt') def isFollowerOfPerson(baseDir: str, nickname: str, domain: str, @@ -233,13 +237,15 @@ def isFollowerOfPerson(baseDir: str, nickname: str, domain: str, def unfollowAccount(baseDir: str, nickname: str, domain: str, followNickname: str, followDomain: str, - followFile: str = 'following.txt', - debug: bool = False) -> bool: + debug: bool, groupAccount: bool, + followFile: str = 'following.txt') -> bool: """Removes a person to the follow list """ domain = removeDomainPort(domain) handle = nickname + '@' + domain handleToUnfollow = followNickname + '@' + followDomain + if groupAccount: + handleToUnfollow = '!' + handleToUnfollow if not os.path.isdir(baseDir + '/accounts'): os.mkdir(baseDir + '/accounts') if not os.path.isdir(baseDir + '/accounts/' + handle): @@ -260,8 +266,9 @@ def unfollowAccount(baseDir: str, nickname: str, domain: str, lines = f.readlines() with open(filename, 'w+') as f: for line in lines: - if line.strip("\n").strip("\r").lower() != \ - handleToUnfollowLower: + checkHandle = line.strip("\n").strip("\r").lower() + if checkHandle != handleToUnfollowLower and \ + checkHandle != '!' + handleToUnfollowLower: f.write(line) # write to an unfollowed file so that if a follow accept @@ -281,12 +288,12 @@ def unfollowAccount(baseDir: str, nickname: str, domain: str, def unfollowerOfAccount(baseDir: str, nickname: str, domain: str, followerNickname: str, followerDomain: str, - debug: bool = False) -> bool: + debug: bool, groupAccount: bool) -> bool: """Remove a follower of a person """ return unfollowAccount(baseDir, nickname, domain, followerNickname, followerDomain, - 'followers.txt', debug) + debug, groupAccount, 'followers.txt') def clearFollows(baseDir: str, nickname: str, domain: str, @@ -1418,8 +1425,10 @@ def outboxUndoFollow(baseDir: str, messageJson: {}, debug: bool) -> None: getDomainFromActor(messageJson['object']['object']) domainFollowingFull = getFullDomain(domainFollowing, portFollowing) + groupAccount = hasGroupPath(messageJson['object']['object']) if unfollowAccount(baseDir, nicknameFollower, domainFollowerFull, - nicknameFollowing, domainFollowingFull): + nicknameFollowing, domainFollowingFull, + debug, groupAccount): if debug: print('DEBUG: ' + nicknameFollower + ' unfollowed ' + nicknameFollowing + '@' + domainFollowingFull) diff --git a/inbox.py b/inbox.py index 858d845c7..4b98fa0b6 100644 --- a/inbox.py +++ b/inbox.py @@ -45,6 +45,7 @@ from utils import loadJson from utils import saveJson from utils import updateLikesCollection from utils import undoLikesCollectionEntry +from utils import hasGroupPath from categories import getHashtagCategories from categories import setHashtagCategory from httpsig import verifyPostHeaders @@ -675,10 +676,11 @@ def _receiveUndoFollow(session, baseDir: str, httpPrefix: str, getDomainFromActor(messageJson['object']['object']) domainFollowingFull = getFullDomain(domainFollowing, portFollowing) + groupAccount = hasGroupPath(messageJson['object']['actor']) if unfollowerOfAccount(baseDir, nicknameFollowing, domainFollowingFull, nicknameFollower, domainFollowerFull, - debug): + debug, groupAccount): print(nicknameFollowing + '@' + domainFollowingFull + ': ' 'Follower ' + nicknameFollower + '@' + domainFollowerFull + ' was removed') diff --git a/migrate.py b/migrate.py index b472e46f7..91f323e8b 100644 --- a/migrate.py +++ b/migrate.py @@ -12,6 +12,7 @@ from utils import isAccountDir from utils import getNicknameFromActor from utils import getDomainFromActor from utils import acctDir +from utils import hasGroupPath from webfinger import webfingerHandle from blocking import isBlocked from posts import getUserUrl @@ -102,13 +103,14 @@ def _updateMovedHandle(baseDir: str, nickname: str, domain: str, if movedToPort: if movedToPort != 80 and movedToPort != 443: movedToDomainFull = movedToDomain + ':' + str(movedToPort) + groupAccount = hasGroupPath(movedToUrl) if isBlocked(baseDir, nickname, domain, movedToNickname, movedToDomain): # someone that you follow has moved to a blocked domain # so just unfollow them unfollowAccount(baseDir, nickname, domain, movedToNickname, movedToDomainFull, - 'following.txt', debug) + debug, groupAccount, 'following.txt') return ctr followingFilename = acctDir(baseDir, nickname, domain) + '/following.txt' @@ -134,7 +136,7 @@ def _updateMovedHandle(baseDir: str, nickname: str, domain: str, unfollowAccount(baseDir, nickname, domain, handleNickname, handleDomain, - 'following.txt', debug) + debug, groupAccount, 'following.txt') ctr += 1 print('Unfollowed ' + handle + ' who has moved to ' + movedToHandle) diff --git a/tests.py b/tests.py index 61caaa0b5..25c351e1c 100644 --- a/tests.py +++ b/tests.py @@ -483,9 +483,9 @@ def createServerAlice(path: str, domain: str, port: int, assert setRole(path, nickname, domain, 'guru') if hasFollows: followPerson(path, nickname, domain, 'bob', bobAddress, - federationList, False) + federationList, False, False) followerOfPerson(path, nickname, domain, 'bob', bobAddress, - federationList, False) + federationList, False, False) if hasPosts: testFollowersOnly = False testSaveToFile = True @@ -612,9 +612,9 @@ def createServerBob(path: str, domain: str, port: int, deleteAllPosts(path, nickname, domain, 'outbox') if hasFollows: followPerson(path, nickname, domain, - 'alice', aliceAddress, federationList, False) + 'alice', aliceAddress, federationList, False, False) followerOfPerson(path, nickname, domain, - 'alice', aliceAddress, federationList, False) + 'alice', aliceAddress, federationList, False, False) if hasPosts: testFollowersOnly = False testSaveToFile = True @@ -967,10 +967,10 @@ def testPostMessageBetweenServers(): aliceDomainStr = aliceDomain + ':' + str(alicePort) followerOfPerson(bobDir, 'bob', bobDomain, 'alice', - aliceDomainStr, federationList, False) + aliceDomainStr, federationList, False, False) bobDomainStr = bobDomain + ':' + str(bobPort) followPerson(aliceDir, 'alice', aliceDomain, 'bob', - bobDomainStr, federationList, False) + bobDomainStr, federationList, False, False) sessionBob = createSession(proxyType) bobPostLog = [] @@ -1273,18 +1273,18 @@ def _testFollowersOfPerson(): clearFollows(baseDir, nickname, domain) followPerson(baseDir, nickname, domain, 'maxboardroom', domain, - federationList, False) + federationList, False, False) followPerson(baseDir, 'drokk', domain, 'ultrapancake', domain, - federationList, False) + federationList, False, False) # deliberate duplication followPerson(baseDir, 'drokk', domain, 'ultrapancake', domain, - federationList, False) + federationList, False, False) followPerson(baseDir, 'sausagedog', domain, 'ultrapancake', domain, - federationList, False) + federationList, False, False) followPerson(baseDir, nickname, domain, 'ultrapancake', domain, - federationList, False) + federationList, False, False) followPerson(baseDir, nickname, domain, 'someother', 'randodomain.net', - federationList, False) + federationList, False, False) followList = getFollowersOfPerson(baseDir, 'ultrapancake', domain) assert len(followList) == 3 @@ -1322,32 +1322,33 @@ def _testNoOfFollowersOnDomain(): httpPrefix, True, False, password) followPerson(baseDir, 'drokk', otherdomain, nickname, domain, - federationList, False) + federationList, False, False) followPerson(baseDir, 'sausagedog', otherdomain, nickname, domain, - federationList, False) + federationList, False, False) followPerson(baseDir, 'maxboardroom', otherdomain, nickname, domain, - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'cucumber', 'sandwiches.party', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'captainsensible', 'damned.zone', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'pilchard', 'zombies.attack', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'drokk', otherdomain, - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'sausagedog', otherdomain, - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'maxboardroom', otherdomain, - federationList, False) + federationList, False, False) followersOnOtherDomain = \ noOfFollowersOnDomain(baseDir, nickname + '@' + domain, otherdomain) assert followersOnOtherDomain == 3 - unfollowerOfAccount(baseDir, nickname, domain, 'sausagedog', otherdomain) + unfollowerOfAccount(baseDir, nickname, domain, 'sausagedog', otherdomain, + False, False) followersOnOtherDomain = \ noOfFollowersOnDomain(baseDir, nickname + '@' + domain, otherdomain) assert followersOnOtherDomain == 2 @@ -1376,17 +1377,17 @@ def _testGroupFollowers(): clearFollowers(baseDir, nickname, domain) followerOfPerson(baseDir, nickname, domain, 'badger', 'wild.domain', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'squirrel', 'wild.domain', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'rodent', 'wild.domain', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'utterly', 'clutterly.domain', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'zonked', 'zzz.domain', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'nap', 'zzz.domain', - federationList, False) + federationList, False, False) grouped = groupFollowersByDomain(baseDir, nickname, domain) assert len(grouped.items()) == 3 @@ -1420,15 +1421,15 @@ def _testFollows(): clearFollows(baseDir, nickname, domain) followPerson(baseDir, nickname, domain, 'badger', 'wild.com', - federationList, False) + federationList, False, False) followPerson(baseDir, nickname, domain, 'squirrel', 'secret.com', - federationList, False) + federationList, False, False) followPerson(baseDir, nickname, domain, 'rodent', 'drainpipe.com', - federationList, False) + federationList, False, False) followPerson(baseDir, nickname, domain, 'batman', 'mesh.com', - federationList, False) + federationList, False, False) followPerson(baseDir, nickname, domain, 'giraffe', 'trees.com', - federationList, False) + federationList, False, False) accountDir = acctDir(baseDir, nickname, domain) f = open(accountDir + '/following.txt', 'r') @@ -1443,7 +1444,8 @@ def _testFollows(): assert(False) assert(domainFound) - unfollowAccount(baseDir, nickname, domain, 'batman', 'mesh.com') + unfollowAccount(baseDir, nickname, domain, 'batman', 'mesh.com', + True, False) domainFound = False for followingDomain in f: @@ -1455,15 +1457,15 @@ def _testFollows(): clearFollowers(baseDir, nickname, domain) followerOfPerson(baseDir, nickname, domain, 'badger', 'wild.com', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'squirrel', 'secret.com', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'rodent', 'drainpipe.com', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'batman', 'mesh.com', - federationList, False) + federationList, False, False) followerOfPerson(baseDir, nickname, domain, 'giraffe', 'trees.com', - federationList, False) + federationList, False, False) accountDir = acctDir(baseDir, nickname, domain) f = open(accountDir + '/followers.txt', 'r') diff --git a/utils.py b/utils.py index 2f608f827..fe9726d1c 100644 --- a/utils.py +++ b/utils.py @@ -983,7 +983,7 @@ def getGroupPaths() -> []: """Returns possible group paths e.g. https://lemmy/c/groupname """ - return ('/c/') + return ['/c/'] def getDomainFromActor(actor: str) -> (str, int): @@ -1051,7 +1051,8 @@ def _setDefaultPetName(baseDir: str, nickname: str, domain: str, def followPerson(baseDir: str, nickname: str, domain: str, followNickname: str, followDomain: str, federationList: [], debug: bool, - followFile='following.txt') -> bool: + groupAccount: bool, + followFile: str = 'following.txt') -> bool: """Adds a person to the follow list """ followDomainStrLower = followDomain.lower().replace('\n', '') @@ -2679,3 +2680,15 @@ def dateSecondsToString(dateSec: int) -> str: """ thisDate = datetime.datetime.fromtimestamp(dateSec) return thisDate.strftime("%Y-%m-%dT%H:%M:%SZ") + + +def hasGroupPath(actor: str) -> bool: + """Does the given actor url contain a path which indicates + that it belongs to a group? + e.g. https://lemmy/c/groupname + """ + groupPaths = getGroupPaths() + for grpPath in groupPaths: + if grpPath in actor: + return True + return False