mirror of https://gitlab.com/bashrc2/epicyon
Merge branch 'main' of gitlab.com:bashrc2/epicyon
commit
2921f8f6c2
104
posts.py
104
posts.py
|
@ -32,6 +32,7 @@ from webfinger import webfingerHandle
|
|||
from httpsig import createSignedHeader
|
||||
from siteactive import siteIsActive
|
||||
from languages import understoodPostLanguage
|
||||
from utils import getUserPaths
|
||||
from utils import invalidCiphertext
|
||||
from utils import hasObjectStringType
|
||||
from utils import removeIdEnding
|
||||
|
@ -1259,6 +1260,29 @@ def _createPostPlaceAndTime(eventDate: str, endDate: str,
|
|||
return eventDateStr
|
||||
|
||||
|
||||
def _consolidateActorsList(actorsList: []) -> None:
|
||||
""" consolidate duplicated actors
|
||||
https://domain/@nick gets merged with https://domain/users/nick
|
||||
"""
|
||||
possibleDuplicateActors = []
|
||||
for ccActor in actorsList:
|
||||
if '/@' in ccActor:
|
||||
if ccActor not in possibleDuplicateActors:
|
||||
possibleDuplicateActors.append(ccActor)
|
||||
if possibleDuplicateActors:
|
||||
uPaths = getUserPaths()
|
||||
removeActors = []
|
||||
for ccActor in possibleDuplicateActors:
|
||||
for usrPath in uPaths:
|
||||
ccActorFull = ccActor.replace('/@', usrPath)
|
||||
if ccActorFull in actorsList:
|
||||
if ccActor not in removeActors:
|
||||
removeActors.append(ccActor)
|
||||
break
|
||||
for ccActor in removeActors:
|
||||
actorsList.remove(ccActor)
|
||||
|
||||
|
||||
def _createPostMentions(ccUrl: str, newPost: {},
|
||||
toRecipients: [], tags: []) -> None:
|
||||
"""Updates mentions for a new post
|
||||
|
@ -1267,9 +1291,10 @@ def _createPostMentions(ccUrl: str, newPost: {},
|
|||
return
|
||||
if len(ccUrl) == 0:
|
||||
return
|
||||
newPost['cc'] = [ccUrl]
|
||||
|
||||
if newPost.get('object'):
|
||||
newPost['object']['cc'] = [ccUrl]
|
||||
if ccUrl not in newPost['object']['cc']:
|
||||
newPost['object']['cc'] = [ccUrl] + newPost['object']['cc']
|
||||
|
||||
# if this is a public post then include any mentions in cc
|
||||
toCC = newPost['object']['cc']
|
||||
|
@ -1283,6 +1308,13 @@ def _createPostMentions(ccUrl: str, newPost: {},
|
|||
if tag['href'] not in toCC:
|
||||
newPost['object']['cc'].append(tag['href'])
|
||||
|
||||
_consolidateActorsList(newPost['object']['cc'])
|
||||
newPost['cc'] = newPost['object']['cc']
|
||||
else:
|
||||
if ccUrl not in newPost['cc']:
|
||||
newPost['cc'] = [ccUrl] + newPost['cc']
|
||||
_consolidateActorsList(['cc'])
|
||||
|
||||
|
||||
def _createPostModReport(baseDir: str,
|
||||
isModerationReport: bool, newPost: {},
|
||||
|
@ -1302,6 +1334,30 @@ def _createPostModReport(baseDir: str,
|
|||
modFile.write(newPostId + '\n')
|
||||
|
||||
|
||||
def getActorFromInReplyTo(inReplyTo: str) -> str:
|
||||
"""Tries to get the replied to actor from the inReplyTo post id
|
||||
Note: this will not always be successful for some instance types
|
||||
"""
|
||||
replyNickname = getNicknameFromActor(inReplyTo)
|
||||
if not replyNickname:
|
||||
return None
|
||||
replyActor = None
|
||||
if '/' + replyNickname + '/' in inReplyTo:
|
||||
replyActor = \
|
||||
inReplyTo.split('/' + replyNickname + '/')[0] + \
|
||||
'/' + replyNickname
|
||||
elif '#' + replyNickname + '#' in inReplyTo:
|
||||
replyActor = \
|
||||
inReplyTo.split('#' + replyNickname + '#')[0] + \
|
||||
'#' + replyNickname
|
||||
replyActor = replyActor.replace('#', '/')
|
||||
if not replyActor:
|
||||
return None
|
||||
if '://' not in replyActor:
|
||||
return None
|
||||
return replyActor
|
||||
|
||||
|
||||
def _createPostBase(baseDir: str,
|
||||
nickname: str, domain: str, port: int,
|
||||
toUrl: str, ccUrl: str, httpPrefix: str, content: str,
|
||||
|
@ -1394,14 +1450,15 @@ def _createPostBase(baseDir: str,
|
|||
if mention not in toCC:
|
||||
toCC.append(mention)
|
||||
|
||||
isPublic = False
|
||||
for recipient in toRecipients:
|
||||
if recipient.endswith('#Public'):
|
||||
isPublic = True
|
||||
break
|
||||
|
||||
# create a list of hashtags
|
||||
# Only posts which are #Public are searchable by hashtag
|
||||
if hashtagsDict:
|
||||
isPublic = False
|
||||
for recipient in toRecipients:
|
||||
if recipient.endswith('#Public'):
|
||||
isPublic = True
|
||||
break
|
||||
for tagName, tag in hashtagsDict.items():
|
||||
if not tagExists(tag['type'], tag['name'], tags):
|
||||
tags.append(tag)
|
||||
|
@ -1421,18 +1478,27 @@ def _createPostBase(baseDir: str,
|
|||
|
||||
postContext = getIndividualPostContext()
|
||||
|
||||
# make sure that CC doesn't also contain a To address
|
||||
# eg. To: [ "https://mydomain/users/foo/followers" ]
|
||||
# CC: [ "X", "Y", "https://mydomain/users/foo", "Z" ]
|
||||
removeFromCC = []
|
||||
for ccRecipient in toCC:
|
||||
for sendToActor in toRecipients:
|
||||
if ccRecipient in sendToActor and \
|
||||
ccRecipient not in removeFromCC:
|
||||
removeFromCC.append(ccRecipient)
|
||||
break
|
||||
for ccRemoval in removeFromCC:
|
||||
toCC.remove(ccRemoval)
|
||||
if not isPublic:
|
||||
# make sure that CC doesn't also contain a To address
|
||||
# eg. To: [ "https://mydomain/users/foo/followers" ]
|
||||
# CC: [ "X", "Y", "https://mydomain/users/foo", "Z" ]
|
||||
removeFromCC = []
|
||||
for ccRecipient in toCC:
|
||||
for sendToActor in toRecipients:
|
||||
if ccRecipient in sendToActor and \
|
||||
ccRecipient not in removeFromCC:
|
||||
removeFromCC.append(ccRecipient)
|
||||
break
|
||||
for ccRemoval in removeFromCC:
|
||||
toCC.remove(ccRemoval)
|
||||
else:
|
||||
if inReplyTo:
|
||||
# If this is a public post then get the actor being
|
||||
# replied to end ensure that it is within the CC list
|
||||
replyActor = getActorFromInReplyTo(inReplyTo)
|
||||
if replyActor:
|
||||
if replyActor not in toCC:
|
||||
toCC.append(replyActor)
|
||||
|
||||
# the type of post to be made
|
||||
postObjectType = 'Note'
|
||||
|
|
31
tests.py
31
tests.py
|
@ -36,6 +36,7 @@ from threads import threadWithTrace
|
|||
from daemon import runDaemon
|
||||
from session import createSession
|
||||
from session import getJson
|
||||
from posts import getActorFromInReplyTo
|
||||
from posts import regenerateIndexForBox
|
||||
from posts import removePostInteractions
|
||||
from posts import getMentionedPeople
|
||||
|
@ -4239,6 +4240,7 @@ def _testReplyToPublicPost(baseDir: str) -> None:
|
|||
mediaType = None
|
||||
imageDescription = 'Some description'
|
||||
city = 'London, England'
|
||||
testInReplyTo = postId
|
||||
testInReplyToAtomUri = None
|
||||
testSubject = None
|
||||
testSchedulePost = False
|
||||
|
@ -4254,7 +4256,7 @@ def _testReplyToPublicPost(baseDir: str) -> None:
|
|||
content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, city, postId,
|
||||
imageDescription, city, testInReplyTo,
|
||||
testInReplyToAtomUri,
|
||||
testSubject, testSchedulePost,
|
||||
testEventDate, testEventTime, testLocation,
|
||||
|
@ -4275,8 +4277,20 @@ def _testReplyToPublicPost(baseDir: str) -> None:
|
|||
assert len(reply['object']['cc']) >= 1
|
||||
assert reply['object']['cc'][0].endswith(nickname + '/followers')
|
||||
assert len(reply['object']['tag']) == 1
|
||||
if len(reply['object']['cc']) != 2:
|
||||
print('reply["object"]["cc"]: ' + str(reply['object']['cc']))
|
||||
assert len(reply['object']['cc']) == 2
|
||||
assert reply['object']['cc'][1] == httpPrefix + '://rat.site/@ninjarodent'
|
||||
assert reply['object']['cc'][1] == \
|
||||
httpPrefix + '://rat.site/users/ninjarodent'
|
||||
|
||||
assert len(reply['to']) == 1
|
||||
assert reply['to'][0].endswith('#Public')
|
||||
assert len(reply['cc']) >= 1
|
||||
assert reply['cc'][0].endswith(nickname + '/followers')
|
||||
if len(reply['cc']) != 2:
|
||||
print('reply["cc"]: ' + str(reply['cc']))
|
||||
assert len(reply['cc']) == 2
|
||||
assert reply['cc'][1] == httpPrefix + '://rat.site/users/ninjarodent'
|
||||
|
||||
|
||||
def _getFunctionCallArgs(name: str, lines: [], startLineCtr: int) -> []:
|
||||
|
@ -6013,6 +6027,18 @@ def _testHttpsigBaseNew(withDigest: bool, baseDir: str,
|
|||
shutil.rmtree(path, ignore_errors=False, onerror=None)
|
||||
|
||||
|
||||
def _testGetActorFromInReplyTo() -> None:
|
||||
print('testGetActorFromInReplyTo')
|
||||
inReplyTo = \
|
||||
'https://fosstodon.org/users/bashrc/statuses/107400700612621140'
|
||||
replyActor = getActorFromInReplyTo(inReplyTo)
|
||||
assert replyActor == 'https://fosstodon.org/users/bashrc'
|
||||
|
||||
inReplyTo = 'https://fosstodon.org/activity/107400700612621140'
|
||||
replyActor = getActorFromInReplyTo(inReplyTo)
|
||||
assert replyActor is None
|
||||
|
||||
|
||||
def runAllTests():
|
||||
baseDir = os.getcwd()
|
||||
print('Running tests...')
|
||||
|
@ -6020,6 +6046,7 @@ def runAllTests():
|
|||
_translateOntology(baseDir)
|
||||
_testGetPriceFromString()
|
||||
_testFunctions()
|
||||
_testGetActorFromInReplyTo()
|
||||
_testValidEmojiContent()
|
||||
_testAddCWfromLists(baseDir)
|
||||
_testWordsSimilarity()
|
||||
|
|
10
utils.py
10
utils.py
|
@ -600,6 +600,14 @@ def removeIdEnding(idStr: str) -> str:
|
|||
return idStr
|
||||
|
||||
|
||||
def removeHashFromPostId(postId: str) -> str:
|
||||
"""Removes any has from a post id
|
||||
"""
|
||||
if '#' not in postId:
|
||||
return postId
|
||||
return postId.split('#')[0]
|
||||
|
||||
|
||||
def getProtocolPrefixes() -> []:
|
||||
"""Returns a list of valid prefixes
|
||||
"""
|
||||
|
@ -3138,6 +3146,8 @@ def hasActor(postJsonObject: {}, debug: bool) -> bool:
|
|||
"""Does the given post have an actor?
|
||||
"""
|
||||
if postJsonObject.get('actor'):
|
||||
if '#' in postJsonObject['actor']:
|
||||
return False
|
||||
return True
|
||||
if debug:
|
||||
if postJsonObject.get('type'):
|
||||
|
|
|
@ -23,6 +23,7 @@ from posts import postIsMuted
|
|||
from posts import getPersonBox
|
||||
from posts import downloadAnnounce
|
||||
from posts import populateRepliesJson
|
||||
from utils import removeHashFromPostId
|
||||
from utils import removeHtml
|
||||
from utils import getActorLanguagesList
|
||||
from utils import getBaseContentFromPost
|
||||
|
@ -390,11 +391,8 @@ def _getReplyIconHtml(baseDir: str, nickname: str, domain: str,
|
|||
return replyStr
|
||||
|
||||
# reply is permitted - create reply icon
|
||||
if '#' not in postJsonObject['object']['id']:
|
||||
replyToLink = removeIdEnding(postJsonObject['object']['id'])
|
||||
else:
|
||||
replyToLink = \
|
||||
removeIdEnding(postJsonObject['object']['id'].split('#')[0])
|
||||
replyToLink = removeHashFromPostId(postJsonObject['object']['id'])
|
||||
replyToLink = removeIdEnding(replyToLink)
|
||||
|
||||
# see Mike MacGirvin's replyTo suggestion
|
||||
if postJsonObject['object'].get('replyTo'):
|
||||
|
@ -575,11 +573,8 @@ def _getAnnounceIconHtml(isAnnounced: bool,
|
|||
unannounceLinkStr = '?unannounce=' + \
|
||||
removeIdEnding(announceJsonObject['id'])
|
||||
|
||||
if '#' not in postJsonObject['object']['id']:
|
||||
announcePostId = removeIdEnding(postJsonObject['object']['id'])
|
||||
else:
|
||||
announcePostId = \
|
||||
removeIdEnding(postJsonObject['object']['id'].split('#')[0])
|
||||
announcePostId = removeHashFromPostId(postJsonObject['object']['id'])
|
||||
announcePostId = removeIdEnding(announcePostId)
|
||||
announceLinkStr = '?' + \
|
||||
announceLink + '=' + announcePostId + pageNumberParam
|
||||
announceStr = \
|
||||
|
@ -647,10 +642,8 @@ def _getLikeIconHtml(nickname: str, domainFull: str,
|
|||
likeStr += '<label class="likesCount">'
|
||||
likeStr += likeCountStr.replace('(', '').replace(')', '').strip()
|
||||
likeStr += '</label>\n'
|
||||
if '#' not in postJsonObject['id']:
|
||||
likePostId = removeIdEnding(postJsonObject['id'])
|
||||
else:
|
||||
likePostId = removeIdEnding(postJsonObject['id'].split('#')[0])
|
||||
likePostId = removeHashFromPostId(postJsonObject['id'])
|
||||
likePostId = removeIdEnding(likePostId)
|
||||
likeStr += \
|
||||
' <a class="imageAnchor" href="/users/' + nickname + '?' + \
|
||||
likeLink + '=' + likePostId + \
|
||||
|
@ -696,11 +689,8 @@ def _getBookmarkIconHtml(nickname: str, domainFull: str,
|
|||
if translate.get(bookmarkTitle):
|
||||
bookmarkTitle = translate[bookmarkTitle]
|
||||
_logPostTiming(enableTimingLog, postStartTime, '12.6')
|
||||
if '#' not in postJsonObject['object']['id']:
|
||||
bookmarkPostId = removeIdEnding(postJsonObject['object']['id'])
|
||||
else:
|
||||
bookmarkPostId = \
|
||||
removeIdEnding(postJsonObject['object']['id'].split('#')[0])
|
||||
bookmarkPostId = removeHashFromPostId(postJsonObject['object']['id'])
|
||||
bookmarkPostId = removeIdEnding(bookmarkPostId)
|
||||
bookmarkStr = \
|
||||
' <a class="imageAnchor" href="/users/' + nickname + '?' + \
|
||||
bookmarkLink + '=' + bookmarkPostId + \
|
||||
|
@ -737,11 +727,8 @@ def _getReactionIconHtml(nickname: str, domainFull: str,
|
|||
if translate.get(reactionTitle):
|
||||
reactionTitle = translate[reactionTitle]
|
||||
_logPostTiming(enableTimingLog, postStartTime, '12.65')
|
||||
if '#' not in postJsonObject['object']['id']:
|
||||
reactionPostId = removeIdEnding(postJsonObject['object']['id'])
|
||||
else:
|
||||
reactionPostId = \
|
||||
removeIdEnding(postJsonObject['object']['id'].split('#')[0])
|
||||
reactionPostId = removeHashFromPostId(postJsonObject['object']['id'])
|
||||
reactionPostId = removeIdEnding(reactionPostId)
|
||||
reactionStr = \
|
||||
' <a class="imageAnchor" href="/users/' + nickname + \
|
||||
'?selreact=' + reactionPostId + pageNumberParam + \
|
||||
|
@ -1383,10 +1370,8 @@ def individualPostAsHtml(signingPrivateKeyPem: str,
|
|||
avatarPosition = ''
|
||||
messageId = ''
|
||||
if postJsonObject.get('id'):
|
||||
if '#' not in postJsonObject['id']:
|
||||
messageId = removeIdEnding(postJsonObject['id'])
|
||||
else:
|
||||
messageId = removeIdEnding(postJsonObject['id'].split('#')[0])
|
||||
messageId = removeHashFromPostId(postJsonObject['id'])
|
||||
messageId = removeIdEnding(messageId)
|
||||
|
||||
_logPostTiming(enableTimingLog, postStartTime, '2')
|
||||
|
||||
|
@ -1645,8 +1630,11 @@ def individualPostAsHtml(signingPrivateKeyPem: str,
|
|||
if postJsonObject['object']['conversation']:
|
||||
conversationId = postJsonObject['object']['conversation']
|
||||
|
||||
publicReply = False
|
||||
if isPublicPost(postJsonObject):
|
||||
publicReply = True
|
||||
replyStr = _getReplyIconHtml(baseDir, nickname, domain,
|
||||
isPublicRepeat,
|
||||
publicReply,
|
||||
showIcons, commentsEnabled,
|
||||
postJsonObject, pageNumberParam,
|
||||
translate, systemLanguage,
|
||||
|
|
Loading…
Reference in New Issue