Add migrations option to check for moved follows

merge-requests/8/head
Bob Mottram 2021-01-09 15:08:26 +00:00
parent 7d22d2ec25
commit 579aa7f63d
3 changed files with 192 additions and 35 deletions

View File

@ -73,6 +73,7 @@ from shares import addShare
from theme import setTheme
from announce import sendAnnounceViaServer
from socnet import instancesGraph
from migrate import migrateAccounts
import argparse
@ -321,6 +322,9 @@ parser.add_argument("--i2p", type=str2bool, nargs='?',
parser.add_argument("--tor", type=str2bool, nargs='?',
const=True, default=False,
help="Route via Tor")
parser.add_argument("--migrations", type=str2bool, nargs='?',
const=True, default=False,
help="Migrate moved accounts")
parser.add_argument("--tests", type=str2bool, nargs='?',
const=True, default=False,
help="Run unit tests")
@ -1304,6 +1308,33 @@ if args.hyper:
if args.i2p:
httpPrefix = 'http'
if args.migrations:
cachedWebfingers = {}
if args.http or domain.endswith('.onion'):
httpPrefix = 'http'
port = 80
proxyType = 'tor'
elif domain.endswith('.i2p'):
httpPrefix = 'http'
port = 80
proxyType = 'i2p'
elif args.gnunet:
httpPrefix = 'gnunet'
port = 80
proxyType = 'gnunet'
else:
httpPrefix = 'https'
port = 443
session = createSession(proxyType)
ctr = migrateAccounts(baseDir, session,
httpPrefix, cachedWebfingers,
True)
if ctr == 0:
print('No followed accounts have moved')
else:
print(str(ctr) + ' followed accounts were migrated')
sys.exit()
if args.actor:
originalActor = args.actor
if '/@' in args.actor or \

View File

@ -7,48 +7,175 @@ __email__ = "bob@freedombone.net"
__status__ = "Production"
import os
from utils import getNicknameFromActor
from utils import getDomainFromActor
from webfinger import webfingerHandle
from blocking import isBlocked
from session import getJson
from posts import getUserUrl
from follow import unfollowAccount
def _migrateFollows(followFilename: str, oldHandle: str,
newHandle: str) -> None:
"""Changes a handle within following or followers list
def _moveFollowingHandlesForAccount(baseDir: str, nickname: str, domain: str,
session,
httpPrefix: str, cachedWebfingers: {},
followFile: str, debug: bool) -> int:
"""Goes through all follows for an account and updates any that have moved
"""
if not os.path.isfile(followFilename):
return
if oldHandle not in open(followFilename).read():
return
followData = None
with open(followFilename, 'r') as followFile:
followData = followFile.read()
if not followData:
return
newFollowData = followData.replace(oldHandle, newHandle)
if followData == newFollowData:
return
with open(followFilename, 'w+') as followFile:
followFile.write(newFollowData)
ctr = 0
followingFilename = \
baseDir + '/accounts/' + nickname + '@' + domain + '/' + followFile
if not os.path.isfile(followingFilename):
return ctr
with open(followingFilename, "r") as f:
followingHandles = f.readlines()
for followHandle in followingHandles:
followHandle = followHandle.strip("\n").strip("\r")
ctr += \
_updateMovedHandle(baseDir, nickname, domain,
followHandle, session,
httpPrefix, cachedWebfingers,
followFile, debug)
return ctr
def migrateAccount(baseDir: str, oldHandle: str, newHandle: str) -> None:
"""If a followed account changes then this modifies the
following and followers lists for each account accordingly
def _updateMovedHandle(baseDir: str, nickname: str, domain: str,
handle: str, session,
httpPrefix: str, cachedWebfingers: {},
followFile: str, debug: bool) -> int:
"""Check if an account has moved, and if so then alter following.txt
for each account.
Returns 1 if moved, 0 otherwise
"""
if oldHandle.startswith('@'):
oldHandle = oldHandle[1:]
if '@' not in oldHandle:
return
if newHandle.startswith('@'):
newHandle = newHandle[1:]
if '@' not in newHandle:
return
ctr = 0
if '@' not in handle:
return ctr
if len(handle) < 5:
return ctr
if handle.startswith('@'):
handle = handle[1:]
wfRequest = webfingerHandle(session, handle,
httpPrefix, cachedWebfingers,
None, __version__)
if not wfRequest:
print('updateMovedHandle unable to webfinger ' + handle)
return ctr
if not isinstance(wfRequest, dict):
print('updateMovedHandle webfinger for ' + handle +
' did not return a dict. ' + str(wfRequest))
return ctr
personUrl = None
if wfRequest.get('errors'):
print('wfRequest error: ' + str(wfRequest['errors']))
return ctr
profileStr = 'https://www.w3.org/ns/activitystreams'
asHeader = {
'Accept': 'application/activity+json; profile="' + profileStr + '"'
}
if not personUrl:
personUrl = getUserUrl(wfRequest)
if not personUrl:
return ctr
profileStr = 'https://www.w3.org/ns/activitystreams'
asHeader = {
'Accept': 'application/ld+json; profile="' + profileStr + '"'
}
personJson = \
getJson(session, personUrl, asHeader, None, __version__,
httpPrefix, None)
if not personJson:
return ctr
if not personJson.get('movedTo'):
return ctr
movedToUrl = personJson['movedTo']
if '://' not in movedToUrl:
return ctr
if '.' not in movedToUrl:
return ctr
movedToNickname = getNicknameFromActor(movedToUrl)
if not movedToNickname:
return ctr
movedToDomain, movedToPort = getDomainFromActor(movedToUrl)
if not movedToDomain:
return ctr
movedToDomainFull = movedToDomain
if movedToPort:
if movedToPort != 80 and movedToPort != 443:
movedToDomainFull = movedToDomain + ':' + str(movedToPort)
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,
followFile, debug)
return ctr
followingFilename = \
baseDir + '/accounts/' + nickname + '@' + domain + '/' + followFile
if not os.path.isfile(followingFilename):
return ctr
with open(followingFilename, "r") as f:
followingHandles = f.readlines()
movedToHandle = movedToNickname + '@' + movedToDomainFull
movedToHandleLower = movedToHandle.lower()
handleLower = handle.lower()
# does the new handle already exist in the following list?
alreadyFollowingHandle = False
for followHandle in followingHandles:
if followHandle.strip("\n").strip("\r").lower() == \
movedToHandleLower:
alreadyFollowingHandle = True
if not alreadyFollowingHandle:
# replace the old handle with the new one
with open(followingFilename, 'w+') as f:
for followHandle in followingHandles:
if followHandle.strip("\n").strip("\r").lower() != \
handleLower:
f.write(followHandle)
else:
f.write(movedToHandleLower + '\n')
ctr += 1
print('Follow moved from ' + handleLower +
' to ' + movedToHandleLower)
else:
# remove the old handle
with open(followingFilename, 'w+') as f:
for followHandle in followingHandles:
if followHandle.strip("\n").strip("\r").lower() != \
handleLower:
f.write(followHandle)
else:
ctr += 1
return ctr
def migrateAccounts(baseDir: str, session,
httpPrefix: str, cachedWebfingers: {},
debug: bool) -> int:
"""If followed accounts change then this modifies the
following lists for each account accordingly.
Returns the number of accounts migrated
"""
# update followers and following lists for each account
ctr = 0
for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for handle in dirs:
if '@' in handle:
accountDir = baseDir + '/accounts/' + handle
followFilename = accountDir + '/following.txt'
_migrateFollows(followFilename, oldHandle, newHandle)
followFilename = accountDir + '/followers.txt'
_migrateFollows(followFilename, oldHandle, newHandle)
if '@' not in handle:
continue
if handle.startswith('inbox@'):
continue
if handle.startswith('news@'):
continue
nickname = handle.split('@')[0]
domain = handle.split('@')[1]
ctr += \
_moveFollowingHandlesForAccount(baseDir, nickname, domain,
session, httpPrefix,
cachedWebfingers,
'following.txt', debug)
break
return ctr

View File

@ -2799,7 +2799,6 @@ def testFunctions():
'threadSendPost',
'sendToFollowers',
'expireCache',
'migrateAccount',
'getMutualsOfPerson',
'runPostsQueue',
'runSharesExpire',