mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of ssh://code.freedombone.net:2222/bashrc/epicyon
commit
85fc28f01a
31
daemon.py
31
daemon.py
|
|
@ -67,6 +67,7 @@ from person import removeAccount
|
||||||
from person import canRemovePost
|
from person import canRemovePost
|
||||||
from person import personSnooze
|
from person import personSnooze
|
||||||
from person import personUnsnooze
|
from person import personUnsnooze
|
||||||
|
from posts import removePostInteractions
|
||||||
from posts import outboxMessageCreateWrap
|
from posts import outboxMessageCreateWrap
|
||||||
from posts import getPinnedPostAsJson
|
from posts import getPinnedPostAsJson
|
||||||
from posts import pinPost
|
from posts import pinPost
|
||||||
|
|
@ -460,32 +461,6 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
else:
|
else:
|
||||||
print('ERROR: unable to create vote')
|
print('ERROR: unable to create vote')
|
||||||
|
|
||||||
def _removePostInteractions(self, postJsonObject: {}) -> None:
|
|
||||||
"""Removes potentially sensitive interactions from a post
|
|
||||||
This is the type of thing which would be of interest to marketers
|
|
||||||
or of saleable value to them. eg. Knowing who likes who or what.
|
|
||||||
"""
|
|
||||||
if postJsonObject.get('likes'):
|
|
||||||
postJsonObject['likes'] = {'items': []}
|
|
||||||
|
|
||||||
removeCollections = (
|
|
||||||
'shares', 'replies', 'bookmarks', 'ignores'
|
|
||||||
)
|
|
||||||
for removeName in removeCollections:
|
|
||||||
if postJsonObject.get(removeName):
|
|
||||||
postJsonObject[removeName] = {}
|
|
||||||
|
|
||||||
if not postJsonObject.get('object'):
|
|
||||||
return
|
|
||||||
if not isinstance(postJsonObject['object'], dict):
|
|
||||||
return
|
|
||||||
if postJsonObject['object'].get('likes'):
|
|
||||||
postJsonObject['object']['likes'] = {'items': []}
|
|
||||||
|
|
||||||
for removeName in removeCollections:
|
|
||||||
if postJsonObject['object'].get(removeName):
|
|
||||||
postJsonObject['object'][removeName] = {}
|
|
||||||
|
|
||||||
def _requestHTTP(self) -> bool:
|
def _requestHTTP(self) -> bool:
|
||||||
"""Should a http response be given?
|
"""Should a http response be given?
|
||||||
"""
|
"""
|
||||||
|
|
@ -7676,7 +7651,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self._404()
|
self._404()
|
||||||
self.server.GETbusy = False
|
self.server.GETbusy = False
|
||||||
return True
|
return True
|
||||||
self._removePostInteractions(pjo)
|
removePostInteractions(pjo, True)
|
||||||
if self._requestHTTP():
|
if self._requestHTTP():
|
||||||
recentPostsCache = \
|
recentPostsCache = \
|
||||||
self.server.recentPostsCache
|
self.server.recentPostsCache
|
||||||
|
|
@ -7803,7 +7778,7 @@ class PubServer(BaseHTTPRequestHandler):
|
||||||
self._404()
|
self._404()
|
||||||
self.server.GETbusy = False
|
self.server.GETbusy = False
|
||||||
return True
|
return True
|
||||||
self._removePostInteractions(pjo)
|
removePostInteractions(pjo, True)
|
||||||
|
|
||||||
if self._requestHTTP():
|
if self._requestHTTP():
|
||||||
recentPostsCache = \
|
recentPostsCache = \
|
||||||
|
|
|
||||||
168
posts.py
168
posts.py
|
|
@ -3036,6 +3036,92 @@ def _addPostToTimeline(filePath: str, boxname: str,
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def removePostInteractions(postJsonObject: {}, force: bool) -> bool:
|
||||||
|
""" Don't show likes, replies, bookmarks, DMs or shares (announces) to
|
||||||
|
unauthorized viewers. This makes the timeline less useful to
|
||||||
|
marketers and other surveillance-oriented organizations.
|
||||||
|
Returns False if this is a private post
|
||||||
|
"""
|
||||||
|
hasObject = False
|
||||||
|
if postJsonObject.get('object'):
|
||||||
|
if isinstance(postJsonObject['object'], dict):
|
||||||
|
hasObject = True
|
||||||
|
if hasObject:
|
||||||
|
postObj = postJsonObject['object']
|
||||||
|
if not force:
|
||||||
|
# If not authorized and it's a private post
|
||||||
|
# then just don't show it within timelines
|
||||||
|
if not isPublicPost(postObj):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
postObj = postJsonObject
|
||||||
|
|
||||||
|
# clear the likes
|
||||||
|
if postObj.get('likes'):
|
||||||
|
postObj['likes'] = {
|
||||||
|
'items': []
|
||||||
|
}
|
||||||
|
# remove other collections
|
||||||
|
removeCollections = (
|
||||||
|
'replies', 'shares', 'bookmarks', 'ignores'
|
||||||
|
)
|
||||||
|
for removeName in removeCollections:
|
||||||
|
if postObj.get(removeName):
|
||||||
|
postObj[removeName] = {}
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _passedNewswireVoting(newswireVotesThreshold: int,
|
||||||
|
baseDir: str, domain: str,
|
||||||
|
postFilename: str,
|
||||||
|
positiveVoting: bool,
|
||||||
|
votingTimeMins: int) -> bool:
|
||||||
|
"""Returns true if the post has passed through newswire voting
|
||||||
|
"""
|
||||||
|
# apply votes within this timeline
|
||||||
|
if newswireVotesThreshold <= 0:
|
||||||
|
return True
|
||||||
|
# note that the presence of an arrival file also indicates
|
||||||
|
# that this post is moderated
|
||||||
|
arrivalDate = \
|
||||||
|
locateNewsArrival(baseDir, domain, postFilename)
|
||||||
|
if not arrivalDate:
|
||||||
|
return True
|
||||||
|
# how long has elapsed since this post arrived?
|
||||||
|
currDate = datetime.datetime.utcnow()
|
||||||
|
timeDiffMins = \
|
||||||
|
int((currDate - arrivalDate).total_seconds() / 60)
|
||||||
|
# has the voting time elapsed?
|
||||||
|
if timeDiffMins < votingTimeMins:
|
||||||
|
# voting is still happening, so don't add this
|
||||||
|
# post to the timeline
|
||||||
|
return False
|
||||||
|
# if there a votes file for this post?
|
||||||
|
votesFilename = \
|
||||||
|
locateNewsVotes(baseDir, domain, postFilename)
|
||||||
|
if not votesFilename:
|
||||||
|
return True
|
||||||
|
# load the votes file and count the votes
|
||||||
|
votesJson = loadJson(votesFilename, 0, 2)
|
||||||
|
if not votesJson:
|
||||||
|
return True
|
||||||
|
if not positiveVoting:
|
||||||
|
if votesOnNewswireItem(votesJson) >= \
|
||||||
|
newswireVotesThreshold:
|
||||||
|
# Too many veto votes.
|
||||||
|
# Continue without incrementing
|
||||||
|
# the posts counter
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
if votesOnNewswireItem < \
|
||||||
|
newswireVotesThreshold:
|
||||||
|
# Not enough votes.
|
||||||
|
# Continue without incrementing
|
||||||
|
# the posts counter
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _createBoxIndexed(recentPostsCache: {},
|
def _createBoxIndexed(recentPostsCache: {},
|
||||||
session, baseDir: str, boxname: str,
|
session, baseDir: str, boxname: str,
|
||||||
nickname: str, domain: str, port: int, httpPrefix: str,
|
nickname: str, domain: str, port: int, httpPrefix: str,
|
||||||
|
|
@ -3103,7 +3189,7 @@ def _createBoxIndexed(recentPostsCache: {},
|
||||||
indexFilename = \
|
indexFilename = \
|
||||||
baseDir + '/accounts/' + timelineNickname + '@' + domain + \
|
baseDir + '/accounts/' + timelineNickname + '@' + domain + \
|
||||||
'/' + indexBoxName + '.index'
|
'/' + indexBoxName + '.index'
|
||||||
postsCtr = 0
|
totalPostsCount = 0
|
||||||
postsAddedToTimeline = 0
|
postsAddedToTimeline = 0
|
||||||
if os.path.isfile(indexFilename):
|
if os.path.isfile(indexFilename):
|
||||||
with open(indexFilename, 'r') as indexFile:
|
with open(indexFilename, 'r') as indexFile:
|
||||||
|
|
@ -3114,47 +3200,17 @@ def _createBoxIndexed(recentPostsCache: {},
|
||||||
if not postFilename:
|
if not postFilename:
|
||||||
break
|
break
|
||||||
|
|
||||||
# apply votes within this timeline
|
# Has this post passed through the newswire voting stage?
|
||||||
if newswireVotesThreshold > 0:
|
if not _passedNewswireVoting(newswireVotesThreshold,
|
||||||
# note that the presence of an arrival file also indicates
|
baseDir, domain,
|
||||||
# that this post is moderated
|
postFilename,
|
||||||
arrivalDate = \
|
positiveVoting,
|
||||||
locateNewsArrival(baseDir, domain, postFilename)
|
votingTimeMins):
|
||||||
if arrivalDate:
|
|
||||||
# how long has elapsed since this post arrived?
|
|
||||||
currDate = datetime.datetime.utcnow()
|
|
||||||
timeDiffMins = \
|
|
||||||
int((currDate - arrivalDate).total_seconds() / 60)
|
|
||||||
# has the voting time elapsed?
|
|
||||||
if timeDiffMins < votingTimeMins:
|
|
||||||
# voting is still happening, so don't add this
|
|
||||||
# post to the timeline
|
|
||||||
continue
|
|
||||||
# if there a votes file for this post?
|
|
||||||
votesFilename = \
|
|
||||||
locateNewsVotes(baseDir, domain, postFilename)
|
|
||||||
if votesFilename:
|
|
||||||
# load the votes file and count the votes
|
|
||||||
votesJson = loadJson(votesFilename, 0, 2)
|
|
||||||
if votesJson:
|
|
||||||
if not positiveVoting:
|
|
||||||
if votesOnNewswireItem(votesJson) >= \
|
|
||||||
newswireVotesThreshold:
|
|
||||||
# Too many veto votes.
|
|
||||||
# Continue without incrementing
|
|
||||||
# the posts counter
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
if votesOnNewswireItem < \
|
|
||||||
newswireVotesThreshold:
|
|
||||||
# Not enough votes.
|
|
||||||
# Continue without incrementing
|
|
||||||
# the posts counter
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Skip through any posts previous to the current page
|
# Skip through any posts previous to the current page
|
||||||
if postsCtr < int((pageNumber - 1) * itemsPerPage):
|
if totalPostsCount < int((pageNumber - 1) * itemsPerPage):
|
||||||
postsCtr += 1
|
totalPostsCount += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# if this is a full path then remove the directories
|
# if this is a full path then remove the directories
|
||||||
|
|
@ -3176,7 +3232,7 @@ def _createBoxIndexed(recentPostsCache: {},
|
||||||
if _addPostStringToTimeline(url,
|
if _addPostStringToTimeline(url,
|
||||||
boxname, postsInBox,
|
boxname, postsInBox,
|
||||||
boxActor):
|
boxActor):
|
||||||
postsCtr += 1
|
totalPostsCount += 1
|
||||||
postsAddedToTimeline += 1
|
postsAddedToTimeline += 1
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
@ -3192,7 +3248,11 @@ def _createBoxIndexed(recentPostsCache: {},
|
||||||
if _addPostToTimeline(fullPostFilename, boxname,
|
if _addPostToTimeline(fullPostFilename, boxname,
|
||||||
postsInBox, boxActor):
|
postsInBox, boxActor):
|
||||||
postsAddedToTimeline += 1
|
postsAddedToTimeline += 1
|
||||||
postsCtr += 1
|
totalPostsCount += 1
|
||||||
|
else:
|
||||||
|
print('WARN: Unable to add post ' + postUrl +
|
||||||
|
' nickname ' + nickname +
|
||||||
|
' timeline ' + boxname)
|
||||||
else:
|
else:
|
||||||
if timelineNickname != nickname:
|
if timelineNickname != nickname:
|
||||||
# if this is the features timeline
|
# if this is the features timeline
|
||||||
|
|
@ -3203,7 +3263,11 @@ def _createBoxIndexed(recentPostsCache: {},
|
||||||
if _addPostToTimeline(fullPostFilename, boxname,
|
if _addPostToTimeline(fullPostFilename, boxname,
|
||||||
postsInBox, boxActor):
|
postsInBox, boxActor):
|
||||||
postsAddedToTimeline += 1
|
postsAddedToTimeline += 1
|
||||||
postsCtr += 1
|
totalPostsCount += 1
|
||||||
|
else:
|
||||||
|
print('WARN: Unable to add features post ' +
|
||||||
|
postUrl + ' nickname ' + nickname +
|
||||||
|
' timeline ' + boxname)
|
||||||
else:
|
else:
|
||||||
print('WARN: features timeline. ' +
|
print('WARN: features timeline. ' +
|
||||||
'Unable to locate post ' + postUrl)
|
'Unable to locate post ' + postUrl)
|
||||||
|
|
@ -3211,13 +3275,13 @@ def _createBoxIndexed(recentPostsCache: {},
|
||||||
print('WARN: Unable to locate post ' + postUrl +
|
print('WARN: Unable to locate post ' + postUrl +
|
||||||
' nickname ' + nickname)
|
' nickname ' + nickname)
|
||||||
|
|
||||||
if postsCtr < 3:
|
if totalPostsCount < 3:
|
||||||
print('Posts added to json timeline ' + boxname + ': ' +
|
print('Posts added to json timeline ' + boxname + ': ' +
|
||||||
str(postsAddedToTimeline))
|
str(postsAddedToTimeline))
|
||||||
|
|
||||||
# Generate first and last entries within header
|
# Generate first and last entries within header
|
||||||
if postsCtr > 0:
|
if totalPostsCount > 0:
|
||||||
lastPage = int(postsCtr / itemsPerPage)
|
lastPage = int(totalPostsCount / itemsPerPage)
|
||||||
if lastPage < 1:
|
if lastPage < 1:
|
||||||
lastPage = 1
|
lastPage = 1
|
||||||
boxHeader['last'] = \
|
boxHeader['last'] = \
|
||||||
|
|
@ -3258,21 +3322,9 @@ def _createBoxIndexed(recentPostsCache: {},
|
||||||
# created by individualPostAsHtml
|
# created by individualPostAsHtml
|
||||||
p['hasReplies'] = hasReplies
|
p['hasReplies'] = hasReplies
|
||||||
|
|
||||||
# Don't show likes, replies, bookmarks, DMs or shares (announces) to
|
|
||||||
# unauthorized viewers
|
|
||||||
if not authorized:
|
if not authorized:
|
||||||
if p.get('object'):
|
if not removePostInteractions(p, False):
|
||||||
if isinstance(p['object'], dict):
|
|
||||||
if not isPublicPost(p):
|
|
||||||
continue
|
continue
|
||||||
if p['object'].get('likes'):
|
|
||||||
p['object']['likes'] = {'items': []}
|
|
||||||
removeCollections = {
|
|
||||||
'replies', 'shares', 'bookmarks', 'ignores'
|
|
||||||
}
|
|
||||||
for removeName in removeCollections:
|
|
||||||
if p['object'].get(removeName):
|
|
||||||
p['object'][removeName] = {}
|
|
||||||
|
|
||||||
boxItems['orderedItems'].append(p)
|
boxItems['orderedItems'].append(p)
|
||||||
|
|
||||||
|
|
|
||||||
34
tests.py
34
tests.py
|
|
@ -21,6 +21,7 @@ from cache import getPersonFromCache
|
||||||
from threads import threadWithTrace
|
from threads import threadWithTrace
|
||||||
from daemon import runDaemon
|
from daemon import runDaemon
|
||||||
from session import createSession
|
from session import createSession
|
||||||
|
from posts import removePostInteractions
|
||||||
from posts import getMentionedPeople
|
from posts import getMentionedPeople
|
||||||
from posts import validContentWarning
|
from posts import validContentWarning
|
||||||
from posts import deleteAllPosts
|
from posts import deleteAllPosts
|
||||||
|
|
@ -3591,9 +3592,42 @@ def testUpdateActor():
|
||||||
shutil.rmtree(baseDir + '/.tests')
|
shutil.rmtree(baseDir + '/.tests')
|
||||||
|
|
||||||
|
|
||||||
|
def testRemovePostInteractions() -> None:
|
||||||
|
print('testRemovePostInteractions')
|
||||||
|
postJsonObject = {
|
||||||
|
"type": "Create",
|
||||||
|
"object": {
|
||||||
|
"to": ["#Public"],
|
||||||
|
"likes": {
|
||||||
|
"items": ["a", "b", "c"]
|
||||||
|
},
|
||||||
|
"replies": {
|
||||||
|
"replyStuff": ["a", "b", "c"]
|
||||||
|
},
|
||||||
|
"shares": {
|
||||||
|
"sharesStuff": ["a", "b", "c"]
|
||||||
|
},
|
||||||
|
"bookmarks": {
|
||||||
|
"bookmarksStuff": ["a", "b", "c"]
|
||||||
|
},
|
||||||
|
"ignores": {
|
||||||
|
"ignoresStuff": ["a", "b", "c"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
removePostInteractions(postJsonObject, True)
|
||||||
|
assert postJsonObject['object']['likes']['items'] == []
|
||||||
|
assert postJsonObject['object']['replies'] == {}
|
||||||
|
assert postJsonObject['object']['shares'] == {}
|
||||||
|
assert postJsonObject['object']['bookmarks'] == {}
|
||||||
|
assert postJsonObject['object']['ignores'] == {}
|
||||||
|
assert not removePostInteractions(postJsonObject, False)
|
||||||
|
|
||||||
|
|
||||||
def runAllTests():
|
def runAllTests():
|
||||||
print('Running tests...')
|
print('Running tests...')
|
||||||
testFunctions()
|
testFunctions()
|
||||||
|
testRemovePostInteractions()
|
||||||
testExtractPGPPublicKey()
|
testExtractPGPPublicKey()
|
||||||
testEmojiImages()
|
testEmojiImages()
|
||||||
testCamelCaseSplit()
|
testCamelCaseSplit()
|
||||||
|
|
|
||||||
|
|
@ -749,7 +749,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
||||||
str(itemCtr) + ' ' + str(timelineJson['orderedItems']))
|
str(itemCtr) + ' ' + str(timelineJson['orderedItems']))
|
||||||
|
|
||||||
# page down arrow
|
# page down arrow
|
||||||
if itemCtr > 2:
|
if itemCtr > 0:
|
||||||
tlStr += textModeSeparator
|
tlStr += textModeSeparator
|
||||||
tlStr += \
|
tlStr += \
|
||||||
' <center>\n' + \
|
' <center>\n' + \
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue