epicyon/migrate.py

200 lines
7.3 KiB
Python

__filename__ = "migrate.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
__version__ = "1.2.0"
__maintainer__ = "Bob Mottram"
__email__ = "bob@freedombone.net"
__status__ = "Production"
__module_group__ = "Core"
import os
from utils import isAccountDir
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 _moveFollowingHandlesForAccount(baseDir: str, nickname: str, domain: str,
session,
httpPrefix: str, cachedWebfingers: {},
debug: bool) -> int:
"""Goes through all follows for an account and updates any that have moved
"""
ctr = 0
followingFilename = \
baseDir + '/accounts/' + nickname + '@' + domain + '/following.txt'
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,
debug)
return ctr
def _updateMovedHandle(baseDir: str, nickname: str, domain: str,
handle: str, session,
httpPrefix: str, cachedWebfingers: {},
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
"""
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__, debug)
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, 0, debug)
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,
debug, __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,
'following.txt', debug)
return ctr
followingFilename = \
baseDir + '/accounts/' + nickname + '@' + domain + '/following.txt'
if os.path.isfile(followingFilename):
with open(followingFilename, "r") as f:
followingHandles = f.readlines()
movedToHandle = movedToNickname + '@' + movedToDomainFull
handleLower = handle.lower()
refollowFilename = \
baseDir + '/accounts/' + \
nickname + '@' + domain + '/refollow.txt'
# unfollow 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:
handleNickname = handle.split('@')[0]
handleDomain = handle.split('@')[1]
unfollowAccount(baseDir, nickname, domain,
handleNickname,
handleDomain,
'following.txt', debug)
ctr += 1
print('Unfollowed ' + handle + ' who has moved to ' +
movedToHandle)
# save the new handles to the refollow list
if os.path.isfile(refollowFilename):
with open(refollowFilename, 'a+') as f:
f.write(movedToHandle + '\n')
else:
with open(refollowFilename, 'w+') as f:
f.write(movedToHandle + '\n')
followersFilename = \
baseDir + '/accounts/' + nickname + '@' + domain + '/followers.txt'
if os.path.isfile(followersFilename):
with open(followersFilename, "r") as f:
followerHandles = f.readlines()
handleLower = handle.lower()
# remove followers who have moved
with open(followersFilename, 'w+') as f:
for followerHandle in followerHandles:
if followerHandle.strip("\n").strip("\r").lower() != \
handleLower:
f.write(followerHandle)
else:
ctr += 1
print('Removed follower who has moved ' + handle)
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 not isAccountDir(handle):
continue
nickname = handle.split('@')[0]
domain = handle.split('@')[1]
ctr += \
_moveFollowingHandlesForAccount(baseDir, nickname, domain,
session, httpPrefix,
cachedWebfingers, debug)
break
return ctr