forked from indymedia/epicyon
Mark dormant followed accounts on profile
parent
9ba729c6fd
commit
d6e60ff3d3
13
daemon.py
13
daemon.py
|
@ -6491,6 +6491,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
YTReplacementDomain,
|
YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
self.server.newswire,
|
self.server.newswire,
|
||||||
|
self.server.dormantMonths,
|
||||||
actorJson['roles'],
|
actorJson['roles'],
|
||||||
None, None)
|
None, None)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
|
@ -6570,6 +6571,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
YTReplacementDomain,
|
YTReplacementDomain,
|
||||||
showPublishedDateOnly,
|
showPublishedDateOnly,
|
||||||
self.server.newswire,
|
self.server.newswire,
|
||||||
|
self.server.dormantMonths,
|
||||||
actorJson['skills'],
|
actorJson['skills'],
|
||||||
None, None)
|
None, None)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
|
@ -8258,6 +8260,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.YTReplacementDomain,
|
self.server.YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
self.server.newswire,
|
self.server.newswire,
|
||||||
|
self.server.dormantMonths,
|
||||||
shares,
|
shares,
|
||||||
pageNumber, sharesPerPage)
|
pageNumber, sharesPerPage)
|
||||||
msg = msg.encode('utf-8')
|
msg = msg.encode('utf-8')
|
||||||
|
@ -8349,6 +8352,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.YTReplacementDomain,
|
self.server.YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
self.server.newswire,
|
self.server.newswire,
|
||||||
|
self.server.dormantMonths,
|
||||||
following,
|
following,
|
||||||
pageNumber,
|
pageNumber,
|
||||||
followsPerPage).encode('utf-8')
|
followsPerPage).encode('utf-8')
|
||||||
|
@ -8440,6 +8444,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.YTReplacementDomain,
|
self.server.YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
self.server.newswire,
|
self.server.newswire,
|
||||||
|
self.server.dormantMonths,
|
||||||
followers,
|
followers,
|
||||||
pageNumber,
|
pageNumber,
|
||||||
followsPerPage).encode('utf-8')
|
followsPerPage).encode('utf-8')
|
||||||
|
@ -8506,6 +8511,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self.server.YTReplacementDomain,
|
self.server.YTReplacementDomain,
|
||||||
self.server.showPublishedDateOnly,
|
self.server.showPublishedDateOnly,
|
||||||
self.server.newswire,
|
self.server.newswire,
|
||||||
|
self.server.dormantMonths,
|
||||||
None, None).encode('utf-8')
|
None, None).encode('utf-8')
|
||||||
self._set_headers('text/html', len(msg),
|
self._set_headers('text/html', len(msg),
|
||||||
cookie, callingDomain)
|
cookie, callingDomain)
|
||||||
|
@ -12926,7 +12932,8 @@ def loadTokens(baseDir: str, tokensDict: {}, tokensLookup: {}) -> None:
|
||||||
tokensLookup[token] = nickname
|
tokensLookup[token] = nickname
|
||||||
|
|
||||||
|
|
||||||
def runDaemon(maxNewswirePosts: int,
|
def runDaemon(dormantMonths: int,
|
||||||
|
maxNewswirePosts: int,
|
||||||
allowLocalNetworkAccess: bool,
|
allowLocalNetworkAccess: bool,
|
||||||
maxFeedItemSizeKb: int,
|
maxFeedItemSizeKb: int,
|
||||||
publishButtonAtTop: bool,
|
publishButtonAtTop: bool,
|
||||||
|
@ -13120,6 +13127,10 @@ def runDaemon(maxNewswirePosts: int,
|
||||||
# maximum size of a hashtag category, in K
|
# maximum size of a hashtag category, in K
|
||||||
httpd.maxCategoriesFeedItemSizeKb = 1024
|
httpd.maxCategoriesFeedItemSizeKb = 1024
|
||||||
|
|
||||||
|
# how many months does a followed account need to be unseen
|
||||||
|
# for it to be considered dormant?
|
||||||
|
httpd.dormantMonths = dormantMonths
|
||||||
|
|
||||||
if registration == 'open':
|
if registration == 'open':
|
||||||
httpd.registration = True
|
httpd.registration = True
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -116,6 +116,11 @@ parser.add_argument('--postsPerSource',
|
||||||
dest='maxNewswirePostsPerSource', type=int,
|
dest='maxNewswirePostsPerSource', type=int,
|
||||||
default=4,
|
default=4,
|
||||||
help='Maximum newswire posts per feed or account')
|
help='Maximum newswire posts per feed or account')
|
||||||
|
parser.add_argument('--dormantMonths',
|
||||||
|
dest='dormantMonths', type=int,
|
||||||
|
default=3,
|
||||||
|
help='How many months does a followed account need to ' +
|
||||||
|
'be unseen for before being considered dormant')
|
||||||
parser.add_argument('--maxNewswirePosts',
|
parser.add_argument('--maxNewswirePosts',
|
||||||
dest='maxNewswirePosts', type=int,
|
dest='maxNewswirePosts', type=int,
|
||||||
default=20,
|
default=20,
|
||||||
|
@ -2080,7 +2085,8 @@ if setTheme(baseDir, themeName, domain, args.allowLocalNetworkAccess):
|
||||||
print('Theme set to ' + themeName)
|
print('Theme set to ' + themeName)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
runDaemon(args.maxNewswirePosts,
|
runDaemon(args.dormantMonths,
|
||||||
|
args.maxNewswirePosts,
|
||||||
args.allowLocalNetworkAccess,
|
args.allowLocalNetworkAccess,
|
||||||
args.maxFeedItemSizeKb,
|
args.maxFeedItemSizeKb,
|
||||||
args.publishButtonAtTop,
|
args.publishButtonAtTop,
|
||||||
|
|
12
tests.py
12
tests.py
|
@ -296,8 +296,10 @@ def createServerAlice(path: str, domain: str, port: int,
|
||||||
i2pDomain = None
|
i2pDomain = None
|
||||||
allowLocalNetworkAccess = True
|
allowLocalNetworkAccess = True
|
||||||
maxNewswirePosts = 20
|
maxNewswirePosts = 20
|
||||||
|
dormantMonths = 3
|
||||||
print('Server running: Alice')
|
print('Server running: Alice')
|
||||||
runDaemon(maxNewswirePosts, allowLocalNetworkAccess,
|
runDaemon(dormantMonths, maxNewswirePosts,
|
||||||
|
allowLocalNetworkAccess,
|
||||||
2048, False, True, False, False, True, 10, False,
|
2048, False, True, False, False, True, 10, False,
|
||||||
0, 100, 1024, 5, False,
|
0, 100, 1024, 5, False,
|
||||||
0, False, 1, False, False, False,
|
0, False, 1, False, False, False,
|
||||||
|
@ -364,8 +366,10 @@ def createServerBob(path: str, domain: str, port: int,
|
||||||
i2pDomain = None
|
i2pDomain = None
|
||||||
allowLocalNetworkAccess = True
|
allowLocalNetworkAccess = True
|
||||||
maxNewswirePosts = 20
|
maxNewswirePosts = 20
|
||||||
|
dormantMonths = 3
|
||||||
print('Server running: Bob')
|
print('Server running: Bob')
|
||||||
runDaemon(maxNewswirePosts, allowLocalNetworkAccess,
|
runDaemon(dormantMonths, maxNewswirePosts,
|
||||||
|
allowLocalNetworkAccess,
|
||||||
2048, False, True, False, False, True, 10, False,
|
2048, False, True, False, False, True, 10, False,
|
||||||
0, 100, 1024, 5, False, 0,
|
0, 100, 1024, 5, False, 0,
|
||||||
False, 1, False, False, False,
|
False, 1, False, False, False,
|
||||||
|
@ -406,8 +410,10 @@ def createServerEve(path: str, domain: str, port: int, federationList: [],
|
||||||
i2pDomain = None
|
i2pDomain = None
|
||||||
allowLocalNetworkAccess = True
|
allowLocalNetworkAccess = True
|
||||||
maxNewswirePosts = 20
|
maxNewswirePosts = 20
|
||||||
|
dormantMonths = 3
|
||||||
print('Server running: Eve')
|
print('Server running: Eve')
|
||||||
runDaemon(maxNewswirePosts, allowLocalNetworkAccess,
|
runDaemon(dormantMonths, maxNewswirePosts,
|
||||||
|
allowLocalNetworkAccess,
|
||||||
2048, False, True, False, False, True, 10, False,
|
2048, False, True, False, False, True, 10, False,
|
||||||
0, 100, 1024, 5, False, 0,
|
0, 100, 1024, 5, False, 0,
|
||||||
False, 1, False, False, False,
|
False, 1, False, False, False,
|
||||||
|
|
27
utils.py
27
utils.py
|
@ -19,6 +19,33 @@ from calendar import monthrange
|
||||||
from followingCalendar import addPersonToCalendar
|
from followingCalendar import addPersonToCalendar
|
||||||
|
|
||||||
|
|
||||||
|
def isDormant(baseDir: str, nickname: str, domain: str, actor: str,
|
||||||
|
dormantMonths=3) -> bool:
|
||||||
|
"""Is the given followed actor dormant, from the standpoint
|
||||||
|
of the given account
|
||||||
|
"""
|
||||||
|
lastSeenFilename = \
|
||||||
|
baseDir + '/accounts/' + nickname + '@' + domain + \
|
||||||
|
'/lastseen/' + actor.replace('/', '#') + '.txt'
|
||||||
|
|
||||||
|
if not os.path.isfile(lastSeenFilename):
|
||||||
|
return False
|
||||||
|
|
||||||
|
with open(lastSeenFilename, 'r') as lastSeenFile:
|
||||||
|
daysSinceEpochStr = lastSeenFile.read()
|
||||||
|
if not daysSinceEpochStr:
|
||||||
|
return False
|
||||||
|
if not daysSinceEpochStr.isdigit():
|
||||||
|
return False
|
||||||
|
currTime = datetime.datetime.utcnow()
|
||||||
|
currDaysSinceEpoch = (currTime - datetime.datetime(1970, 1, 1)).days
|
||||||
|
timeDiffMonths = \
|
||||||
|
int((currDaysSinceEpoch - int(daysSinceEpochStr)) / 30)
|
||||||
|
if timeDiffMonths >= dormantMonths:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def getHashtagCategory(baseDir: str, hashtag: str) -> str:
|
def getHashtagCategory(baseDir: str, hashtag: str) -> str:
|
||||||
"""Returns the category for the hashtag
|
"""Returns the category for the hashtag
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,6 +8,7 @@ __status__ = "Production"
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
from utils import isDormant
|
||||||
from utils import getNicknameFromActor
|
from utils import getNicknameFromActor
|
||||||
from utils import getDomainFromActor
|
from utils import getDomainFromActor
|
||||||
from utils import isSystemAccount
|
from utils import isSystemAccount
|
||||||
|
@ -364,8 +365,9 @@ def htmlProfile(rssIconAtTop: bool,
|
||||||
session, wfRequest: {}, personCache: {},
|
session, wfRequest: {}, personCache: {},
|
||||||
YTReplacementDomain: str,
|
YTReplacementDomain: str,
|
||||||
showPublishedDateOnly: bool,
|
showPublishedDateOnly: bool,
|
||||||
newswire: {}, extraJson=None,
|
newswire: {}, dormantMonths: int,
|
||||||
pageNumber=None, maxItemsPerPage=None) -> str:
|
extraJson=None, pageNumber=None,
|
||||||
|
maxItemsPerPage=None) -> str:
|
||||||
"""Show the profile page as html
|
"""Show the profile page as html
|
||||||
"""
|
"""
|
||||||
nickname = profileJson['preferredUsername']
|
nickname = profileJson['preferredUsername']
|
||||||
|
@ -628,7 +630,8 @@ def htmlProfile(rssIconAtTop: bool,
|
||||||
domain, port, session,
|
domain, port, session,
|
||||||
wfRequest, personCache, extraJson,
|
wfRequest, personCache, extraJson,
|
||||||
projectVersion, ["unfollow"], selected,
|
projectVersion, ["unfollow"], selected,
|
||||||
usersPath, pageNumber, maxItemsPerPage)
|
usersPath, pageNumber, maxItemsPerPage,
|
||||||
|
dormantMonths)
|
||||||
elif selected == 'followers':
|
elif selected == 'followers':
|
||||||
profileStr += \
|
profileStr += \
|
||||||
htmlProfileFollowing(translate, baseDir, httpPrefix,
|
htmlProfileFollowing(translate, baseDir, httpPrefix,
|
||||||
|
@ -637,7 +640,7 @@ def htmlProfile(rssIconAtTop: bool,
|
||||||
wfRequest, personCache, extraJson,
|
wfRequest, personCache, extraJson,
|
||||||
projectVersion, ["block"],
|
projectVersion, ["block"],
|
||||||
selected, usersPath, pageNumber,
|
selected, usersPath, pageNumber,
|
||||||
maxItemsPerPage)
|
maxItemsPerPage, dormantMonths)
|
||||||
elif selected == 'roles':
|
elif selected == 'roles':
|
||||||
profileStr += \
|
profileStr += \
|
||||||
htmlProfileRoles(translate, nickname, domainFull,
|
htmlProfileRoles(translate, nickname, domainFull,
|
||||||
|
@ -719,7 +722,8 @@ def htmlProfileFollowing(translate: {}, baseDir: str, httpPrefix: str,
|
||||||
buttons: [],
|
buttons: [],
|
||||||
feedName: str, actor: str,
|
feedName: str, actor: str,
|
||||||
pageNumber: int,
|
pageNumber: int,
|
||||||
maxItemsPerPage: int) -> str:
|
maxItemsPerPage: int,
|
||||||
|
dormantMonths: int) -> str:
|
||||||
"""Shows following on the profile screen
|
"""Shows following on the profile screen
|
||||||
"""
|
"""
|
||||||
profileStr = ''
|
profileStr = ''
|
||||||
|
@ -737,12 +741,18 @@ def htmlProfileFollowing(translate: {}, baseDir: str, httpPrefix: str,
|
||||||
translate['Page up'] + '"></a>\n' + \
|
translate['Page up'] + '"></a>\n' + \
|
||||||
' </center>\n'
|
' </center>\n'
|
||||||
|
|
||||||
for item in followingJson['orderedItems']:
|
for followingActor in followingJson['orderedItems']:
|
||||||
|
dormant = False
|
||||||
|
if feedName == 'following':
|
||||||
|
dormant = \
|
||||||
|
isDormant(baseDir, nickname, domain, followingActor,
|
||||||
|
dormantMonths)
|
||||||
profileStr += \
|
profileStr += \
|
||||||
individualFollowAsHtml(translate, baseDir, session,
|
individualFollowAsHtml(translate, baseDir, session,
|
||||||
wfRequest, personCache,
|
wfRequest, personCache,
|
||||||
domain, item, authorized, nickname,
|
domain, followingActor,
|
||||||
httpPrefix, projectVersion,
|
authorized, nickname,
|
||||||
|
httpPrefix, projectVersion, dormant,
|
||||||
buttons)
|
buttons)
|
||||||
if authorized and maxItemsPerPage and pageNumber:
|
if authorized and maxItemsPerPage and pageNumber:
|
||||||
if len(followingJson['orderedItems']) >= maxItemsPerPage:
|
if len(followingJson['orderedItems']) >= maxItemsPerPage:
|
||||||
|
@ -1436,12 +1446,15 @@ def individualFollowAsHtml(translate: {},
|
||||||
actorNickname: str,
|
actorNickname: str,
|
||||||
httpPrefix: str,
|
httpPrefix: str,
|
||||||
projectVersion: str,
|
projectVersion: str,
|
||||||
|
dormant: bool,
|
||||||
buttons=[]) -> str:
|
buttons=[]) -> str:
|
||||||
"""An individual follow entry on the profile screen
|
"""An individual follow entry on the profile screen
|
||||||
"""
|
"""
|
||||||
nickname = getNicknameFromActor(followUrl)
|
nickname = getNicknameFromActor(followUrl)
|
||||||
domain, port = getDomainFromActor(followUrl)
|
domain, port = getDomainFromActor(followUrl)
|
||||||
titleStr = '@' + nickname + '@' + domain
|
titleStr = '@' + nickname + '@' + domain
|
||||||
|
if dormant:
|
||||||
|
titleStr += '💤'
|
||||||
avatarUrl = getPersonAvatarUrl(baseDir, followUrl, personCache, True)
|
avatarUrl = getPersonAvatarUrl(baseDir, followUrl, personCache, True)
|
||||||
if not avatarUrl:
|
if not avatarUrl:
|
||||||
avatarUrl = followUrl + '/avatar.png'
|
avatarUrl = followUrl + '/avatar.png'
|
||||||
|
|
Loading…
Reference in New Issue