mirror of https://gitlab.com/bashrc2/epicyon
Enforce convention of underscore before local function names
parent
0cf0841402
commit
5cd9aa8d66
|
@ -15,10 +15,10 @@ from utils import domainPermitted
|
|||
from utils import followPerson
|
||||
|
||||
|
||||
def createAcceptReject(baseDir: str, federationList: [],
|
||||
nickname: str, domain: str, port: int,
|
||||
toUrl: str, ccUrl: str, httpPrefix: str,
|
||||
objectJson: {}, acceptType: str) -> {}:
|
||||
def _createAcceptReject(baseDir: str, federationList: [],
|
||||
nickname: str, domain: str, port: int,
|
||||
toUrl: str, ccUrl: str, httpPrefix: str,
|
||||
objectJson: {}, acceptType: str) -> {}:
|
||||
"""Accepts or rejects something (eg. a follow request or offer)
|
||||
Typically toUrl will be https://www.w3.org/ns/activitystreams#Public
|
||||
and ccUrl might be a specific person favorited or repeated and
|
||||
|
@ -51,24 +51,24 @@ def createAccept(baseDir: str, federationList: [],
|
|||
nickname: str, domain: str, port: int,
|
||||
toUrl: str, ccUrl: str, httpPrefix: str,
|
||||
objectJson: {}) -> {}:
|
||||
return createAcceptReject(baseDir, federationList,
|
||||
nickname, domain, port,
|
||||
toUrl, ccUrl, httpPrefix,
|
||||
objectJson, 'Accept')
|
||||
return _createAcceptReject(baseDir, federationList,
|
||||
nickname, domain, port,
|
||||
toUrl, ccUrl, httpPrefix,
|
||||
objectJson, 'Accept')
|
||||
|
||||
|
||||
def createReject(baseDir: str, federationList: [],
|
||||
nickname: str, domain: str, port: int,
|
||||
toUrl: str, ccUrl: str, httpPrefix: str,
|
||||
objectJson: {}) -> {}:
|
||||
return createAcceptReject(baseDir, federationList,
|
||||
nickname, domain, port,
|
||||
toUrl, ccUrl,
|
||||
httpPrefix, objectJson, 'Reject')
|
||||
return _createAcceptReject(baseDir, federationList,
|
||||
nickname, domain, port,
|
||||
toUrl, ccUrl,
|
||||
httpPrefix, objectJson, 'Reject')
|
||||
|
||||
|
||||
def acceptFollow(baseDir: str, domain: str, messageJson: {},
|
||||
federationList: [], debug: bool) -> None:
|
||||
def _acceptFollow(baseDir: str, domain: str, messageJson: {},
|
||||
federationList: [], debug: bool) -> None:
|
||||
"""Receiving a follow Accept activity
|
||||
"""
|
||||
if not messageJson.get('object'):
|
||||
|
@ -204,7 +204,7 @@ def receiveAcceptReject(session, baseDir: str,
|
|||
' does not contain a nickname. ' +
|
||||
'Assuming single user instance.')
|
||||
# receive follow accept
|
||||
acceptFollow(baseDir, domain, messageJson, federationList, debug)
|
||||
_acceptFollow(baseDir, domain, messageJson, federationList, debug)
|
||||
if debug:
|
||||
print('DEBUG: Uh, ' + messageJson['type'] + ', I guess')
|
||||
return True
|
||||
|
|
12
auth.py
12
auth.py
|
@ -14,7 +14,7 @@ import secrets
|
|||
from utils import isSystemAccount
|
||||
|
||||
|
||||
def hashPassword(password: str) -> str:
|
||||
def _hashPassword(password: str) -> str:
|
||||
"""Hash a password for storing
|
||||
"""
|
||||
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
|
||||
|
@ -25,7 +25,7 @@ def hashPassword(password: str) -> str:
|
|||
return (salt + pwdhash).decode('ascii')
|
||||
|
||||
|
||||
def getPasswordHash(salt: str, providedPassword: str) -> str:
|
||||
def _getPasswordHash(salt: str, providedPassword: str) -> str:
|
||||
"""Returns the hash of a password
|
||||
"""
|
||||
pwdhash = hashlib.pbkdf2_hmac('sha512',
|
||||
|
@ -57,7 +57,7 @@ def constantTimeStringCheck(string1: str, string2: str) -> bool:
|
|||
return matched
|
||||
|
||||
|
||||
def verifyPassword(storedPassword: str, providedPassword: str) -> bool:
|
||||
def _verifyPassword(storedPassword: str, providedPassword: str) -> bool:
|
||||
"""Verify a stored password against one provided by user
|
||||
"""
|
||||
if not storedPassword:
|
||||
|
@ -66,7 +66,7 @@ def verifyPassword(storedPassword: str, providedPassword: str) -> bool:
|
|||
return False
|
||||
salt = storedPassword[:64]
|
||||
storedPassword = storedPassword[64:]
|
||||
pwHash = getPasswordHash(salt, providedPassword)
|
||||
pwHash = _getPasswordHash(salt, providedPassword)
|
||||
return constantTimeStringCheck(pwHash, storedPassword)
|
||||
|
||||
|
||||
|
@ -137,7 +137,7 @@ def authorizeBasic(baseDir: str, path: str, authHeader: str,
|
|||
if line.startswith(nickname+':'):
|
||||
storedPassword = \
|
||||
line.split(':')[1].replace('\n', '').replace('\r', '')
|
||||
success = verifyPassword(storedPassword, providedPassword)
|
||||
success = _verifyPassword(storedPassword, providedPassword)
|
||||
if not success:
|
||||
if debug:
|
||||
print('DEBUG: Password check failed for ' + nickname)
|
||||
|
@ -159,7 +159,7 @@ def storeBasicCredentials(baseDir: str, nickname: str, password: str) -> bool:
|
|||
os.mkdir(baseDir + '/accounts')
|
||||
|
||||
passwordFile = baseDir + '/accounts/passwords'
|
||||
storeStr = nickname + ':' + hashPassword(password)
|
||||
storeStr = nickname + ':' + _hashPassword(password)
|
||||
if os.path.isfile(passwordFile):
|
||||
if nickname + ':' in open(passwordFile).read():
|
||||
with open(passwordFile, "r") as fin:
|
||||
|
|
129
blog.py
129
blog.py
|
@ -26,9 +26,9 @@ from newswire import rss2Header
|
|||
from newswire import rss2Footer
|
||||
|
||||
|
||||
def noOfBlogReplies(baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postId: str, depth=0) -> int:
|
||||
def _noOfBlogReplies(baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postId: str, depth=0) -> int:
|
||||
"""Returns the number of replies on the post
|
||||
This is recursive, so can handle replies to replies
|
||||
"""
|
||||
|
@ -66,9 +66,10 @@ def noOfBlogReplies(baseDir: str, httpPrefix: str, translate: {},
|
|||
replyPostId = replyPostId.replace('.json', '')
|
||||
if locatePost(baseDir, nickname, domain, replyPostId):
|
||||
replyPostId = replyPostId.replace('.replies', '')
|
||||
replies += 1 + noOfBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
replyPostId, depth+1)
|
||||
replies += \
|
||||
1 + _noOfBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
replyPostId, depth+1)
|
||||
else:
|
||||
# remove post which no longer exists
|
||||
removals.append(replyPostId)
|
||||
|
@ -86,9 +87,9 @@ def noOfBlogReplies(baseDir: str, httpPrefix: str, translate: {},
|
|||
return replies
|
||||
|
||||
|
||||
def getBlogReplies(baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postId: str, depth=0) -> str:
|
||||
def _getBlogReplies(baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postId: str, depth=0) -> str:
|
||||
"""Returns a string containing html blog posts
|
||||
"""
|
||||
if depth > 4:
|
||||
|
@ -136,9 +137,9 @@ def getBlogReplies(baseDir: str, httpPrefix: str, translate: {},
|
|||
continue
|
||||
with open(postFilename, "r") as postFile:
|
||||
repliesStr += postFile.read() + '\n'
|
||||
rply = getBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
replyPostId, depth+1)
|
||||
rply = _getBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
replyPostId, depth+1)
|
||||
if rply not in repliesStr:
|
||||
repliesStr += rply
|
||||
|
||||
|
@ -152,12 +153,12 @@ def getBlogReplies(baseDir: str, httpPrefix: str, translate: {},
|
|||
return ''
|
||||
|
||||
|
||||
def htmlBlogPostContent(authorized: bool,
|
||||
baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postJsonObject: {},
|
||||
handle: str, restrictToDomain: bool,
|
||||
blogSeparator='<hr>') -> str:
|
||||
def _htmlBlogPostContent(authorized: bool,
|
||||
baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postJsonObject: {},
|
||||
handle: str, restrictToDomain: bool,
|
||||
blogSeparator='<hr>') -> str:
|
||||
"""Returns the content for a single blog post
|
||||
"""
|
||||
linkedAuthor = False
|
||||
|
@ -269,9 +270,9 @@ def htmlBlogPostContent(authorized: bool,
|
|||
'/users/' + nickname + '">' + translate['About the author'] + \
|
||||
'</a></p>\n'
|
||||
|
||||
replies = noOfBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
postJsonObject['object']['id'])
|
||||
replies = _noOfBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
postJsonObject['object']['id'])
|
||||
|
||||
# separator between blogs should be centered
|
||||
if '<center>' not in blogSeparator:
|
||||
|
@ -288,23 +289,23 @@ def htmlBlogPostContent(authorized: bool,
|
|||
else:
|
||||
blogStr += blogSeparator + '<h1>' + translate['Replies'] + '</h1>\n'
|
||||
if not titleStr:
|
||||
blogStr += getBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
postJsonObject['object']['id'])
|
||||
blogStr += _getBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
postJsonObject['object']['id'])
|
||||
else:
|
||||
blogRepliesStr = getBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
postJsonObject['object']['id'])
|
||||
blogRepliesStr = _getBlogReplies(baseDir, httpPrefix, translate,
|
||||
nickname, domain, domainFull,
|
||||
postJsonObject['object']['id'])
|
||||
blogStr += blogRepliesStr.replace('>' + titleStr + '<', '')
|
||||
|
||||
return blogStr
|
||||
|
||||
|
||||
def htmlBlogPostRSS2(authorized: bool,
|
||||
baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postJsonObject: {},
|
||||
handle: str, restrictToDomain: bool) -> str:
|
||||
def _htmlBlogPostRSS2(authorized: bool,
|
||||
baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postJsonObject: {},
|
||||
handle: str, restrictToDomain: bool) -> str:
|
||||
"""Returns the RSS version 2 feed for a single blog post
|
||||
"""
|
||||
rssStr = ''
|
||||
|
@ -331,11 +332,11 @@ def htmlBlogPostRSS2(authorized: bool,
|
|||
return rssStr
|
||||
|
||||
|
||||
def htmlBlogPostRSS3(authorized: bool,
|
||||
baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postJsonObject: {},
|
||||
handle: str, restrictToDomain: bool) -> str:
|
||||
def _htmlBlogPostRSS3(authorized: bool,
|
||||
baseDir: str, httpPrefix: str, translate: {},
|
||||
nickname: str, domain: str, domainFull: str,
|
||||
postJsonObject: {},
|
||||
handle: str, restrictToDomain: bool) -> str:
|
||||
"""Returns the RSS version 3 feed for a single blog post
|
||||
"""
|
||||
rssStr = ''
|
||||
|
@ -359,7 +360,7 @@ def htmlBlogPostRSS3(authorized: bool,
|
|||
return rssStr
|
||||
|
||||
|
||||
def htmlBlogRemoveCwButton(blogStr: str, translate: {}) -> str:
|
||||
def _htmlBlogRemoveCwButton(blogStr: str, translate: {}) -> str:
|
||||
"""Removes the CW button from blog posts, where the
|
||||
summary field is instead used as the blog title
|
||||
"""
|
||||
|
@ -383,13 +384,13 @@ def htmlBlogPost(authorized: bool,
|
|||
if os.path.isfile(baseDir + '/blog.css'):
|
||||
cssFilename = baseDir + '/blog.css'
|
||||
blogStr = htmlHeaderWithExternalStyle(cssFilename)
|
||||
htmlBlogRemoveCwButton(blogStr, translate)
|
||||
_htmlBlogRemoveCwButton(blogStr, translate)
|
||||
|
||||
blogStr += htmlBlogPostContent(authorized, baseDir,
|
||||
httpPrefix, translate,
|
||||
nickname, domain,
|
||||
domainFull, postJsonObject,
|
||||
None, False)
|
||||
blogStr += _htmlBlogPostContent(authorized, baseDir,
|
||||
httpPrefix, translate,
|
||||
nickname, domain,
|
||||
domainFull, postJsonObject,
|
||||
None, False)
|
||||
|
||||
# show rss links
|
||||
blogStr += '<p class="rssfeed">'
|
||||
|
@ -428,7 +429,7 @@ def htmlBlogPage(authorized: bool, session,
|
|||
if os.path.isfile(baseDir + '/epicyon.css'):
|
||||
cssFilename = baseDir + '/epicyon.css'
|
||||
blogStr = htmlHeaderWithExternalStyle(cssFilename)
|
||||
htmlBlogRemoveCwButton(blogStr, translate)
|
||||
_htmlBlogRemoveCwButton(blogStr, translate)
|
||||
|
||||
blogsIndex = baseDir + '/accounts/' + \
|
||||
nickname + '@' + domain + '/tlblogs.index'
|
||||
|
@ -472,11 +473,11 @@ def htmlBlogPage(authorized: bool, session,
|
|||
if item['type'] != 'Create':
|
||||
continue
|
||||
|
||||
blogStr += htmlBlogPostContent(authorized, baseDir,
|
||||
httpPrefix, translate,
|
||||
nickname, domain,
|
||||
domainFull, item,
|
||||
None, True)
|
||||
blogStr += _htmlBlogPostContent(authorized, baseDir,
|
||||
httpPrefix, translate,
|
||||
nickname, domain,
|
||||
domainFull, item,
|
||||
None, True)
|
||||
|
||||
if len(timelineJson['orderedItems']) >= noOfItems:
|
||||
blogStr += navigateStr
|
||||
|
@ -544,11 +545,11 @@ def htmlBlogPageRSS2(authorized: bool, session,
|
|||
continue
|
||||
|
||||
blogRSS2 += \
|
||||
htmlBlogPostRSS2(authorized, baseDir,
|
||||
httpPrefix, translate,
|
||||
nickname, domain,
|
||||
domainFull, item,
|
||||
None, True)
|
||||
_htmlBlogPostRSS2(authorized, baseDir,
|
||||
httpPrefix, translate,
|
||||
nickname, domain,
|
||||
domainFull, item,
|
||||
None, True)
|
||||
|
||||
if includeHeader:
|
||||
return blogRSS2 + rss2Footer()
|
||||
|
@ -590,16 +591,16 @@ def htmlBlogPageRSS3(authorized: bool, session,
|
|||
continue
|
||||
|
||||
blogRSS3 += \
|
||||
htmlBlogPostRSS3(authorized, baseDir,
|
||||
httpPrefix, translate,
|
||||
nickname, domain,
|
||||
domainFull, item,
|
||||
None, True)
|
||||
_htmlBlogPostRSS3(authorized, baseDir,
|
||||
httpPrefix, translate,
|
||||
nickname, domain,
|
||||
domainFull, item,
|
||||
None, True)
|
||||
|
||||
return blogRSS3
|
||||
|
||||
|
||||
def noOfBlogAccounts(baseDir: str) -> int:
|
||||
def _noOfBlogAccounts(baseDir: str) -> int:
|
||||
"""Returns the number of blog accounts
|
||||
"""
|
||||
ctr = 0
|
||||
|
@ -617,7 +618,7 @@ def noOfBlogAccounts(baseDir: str) -> int:
|
|||
return ctr
|
||||
|
||||
|
||||
def singleBlogAccountNickname(baseDir: str) -> str:
|
||||
def _singleBlogAccountNickname(baseDir: str) -> str:
|
||||
"""Returns the nickname of a single blog account
|
||||
"""
|
||||
for subdir, dirs, files in os.walk(baseDir + '/accounts'):
|
||||
|
@ -647,8 +648,8 @@ def htmlBlogView(authorized: bool,
|
|||
cssFilename = baseDir + '/epicyon.css'
|
||||
blogStr = htmlHeaderWithExternalStyle(cssFilename)
|
||||
|
||||
if noOfBlogAccounts(baseDir) <= 1:
|
||||
nickname = singleBlogAccountNickname(baseDir)
|
||||
if _noOfBlogAccounts(baseDir) <= 1:
|
||||
nickname = _singleBlogAccountNickname(baseDir)
|
||||
if nickname:
|
||||
return htmlBlogPage(authorized, session,
|
||||
baseDir, httpPrefix, translate,
|
||||
|
|
34
blurhash.py
34
blurhash.py
|
@ -39,7 +39,7 @@ alphabet = \
|
|||
alphabet_values = dict(zip(alphabet, range(len(alphabet))))
|
||||
|
||||
|
||||
def base83_encode(value, length):
|
||||
def _base83_encode(value, length):
|
||||
"""
|
||||
Decodes an integer to a base83 string, as used in blurhash.
|
||||
|
||||
|
@ -57,7 +57,7 @@ def base83_encode(value, length):
|
|||
return result
|
||||
|
||||
|
||||
def srgb_to_linear(value):
|
||||
def _srgb_to_linear(value):
|
||||
"""
|
||||
srgb 0-255 integer to linear 0.0-1.0 floating point conversion.
|
||||
"""
|
||||
|
@ -67,14 +67,14 @@ def srgb_to_linear(value):
|
|||
return math.pow((value + 0.055) / 1.055, 2.4)
|
||||
|
||||
|
||||
def sign_pow(value, exp):
|
||||
def _sign_pow(value, exp):
|
||||
"""
|
||||
Sign-preserving exponentiation.
|
||||
"""
|
||||
return math.copysign(math.pow(abs(value), exp), value)
|
||||
|
||||
|
||||
def linear_to_srgb(value):
|
||||
def _linear_to_srgb(value):
|
||||
"""
|
||||
linear 0.0-1.0 floating point to srgb 0-255 integer conversion.
|
||||
"""
|
||||
|
@ -113,9 +113,9 @@ def blurhash_encode(image, components_x=4, components_y=4, linear=False):
|
|||
image_linear_line = []
|
||||
for x in range(int(width)):
|
||||
image_linear_line.append([
|
||||
srgb_to_linear(image[y][x][0]),
|
||||
srgb_to_linear(image[y][x][1]),
|
||||
srgb_to_linear(image[y][x][2])
|
||||
_srgb_to_linear(image[y][x][0]),
|
||||
_srgb_to_linear(image[y][x][1]),
|
||||
_srgb_to_linear(image[y][x][2])
|
||||
])
|
||||
image_linear.append(image_linear_line)
|
||||
else:
|
||||
|
@ -149,9 +149,9 @@ def blurhash_encode(image, components_x=4, components_y=4, linear=False):
|
|||
abs(component[1]), abs(component[2]))
|
||||
|
||||
# Encode components
|
||||
dc_value = (linear_to_srgb(components[0][0]) << 16) + \
|
||||
(linear_to_srgb(components[0][1]) << 8) + \
|
||||
linear_to_srgb(components[0][2])
|
||||
dc_value = (_linear_to_srgb(components[0][0]) << 16) + \
|
||||
(_linear_to_srgb(components[0][1]) << 8) + \
|
||||
_linear_to_srgb(components[0][2])
|
||||
|
||||
quant_max_ac_component = int(max(0, min(82,
|
||||
math.floor(max_ac_component *
|
||||
|
@ -163,9 +163,9 @@ def blurhash_encode(image, components_x=4, components_y=4, linear=False):
|
|||
r2 = r / ac_component_norm_factor
|
||||
g2 = g / ac_component_norm_factor
|
||||
b2 = b / ac_component_norm_factor
|
||||
r3 = math.floor(sign_pow(r2, 0.5) * 9.0 + 9.5)
|
||||
g3 = math.floor(sign_pow(g2, 0.5) * 9.0 + 9.5)
|
||||
b3 = math.floor(sign_pow(b2, 0.5) * 9.0 + 9.5)
|
||||
r3 = math.floor(_sign_pow(r2, 0.5) * 9.0 + 9.5)
|
||||
g3 = math.floor(_sign_pow(g2, 0.5) * 9.0 + 9.5)
|
||||
b3 = math.floor(_sign_pow(b2, 0.5) * 9.0 + 9.5)
|
||||
ac_values.append(
|
||||
int(max(0.0, min(18.0, r3))) * 19 * 19 +
|
||||
int(max(0.0, min(18.0, g3))) * 19 +
|
||||
|
@ -174,10 +174,10 @@ def blurhash_encode(image, components_x=4, components_y=4, linear=False):
|
|||
|
||||
# Build final blurhash
|
||||
blurhash = ""
|
||||
blurhash += base83_encode((components_x - 1) + (components_y - 1) * 9, 1)
|
||||
blurhash += base83_encode(quant_max_ac_component, 1)
|
||||
blurhash += base83_encode(dc_value, 4)
|
||||
blurhash += _base83_encode((components_x - 1) + (components_y - 1) * 9, 1)
|
||||
blurhash += _base83_encode(quant_max_ac_component, 1)
|
||||
blurhash += _base83_encode(dc_value, 4)
|
||||
for ac_value in ac_values:
|
||||
blurhash += base83_encode(ac_value, 2)
|
||||
blurhash += _base83_encode(ac_value, 2)
|
||||
|
||||
return blurhash
|
||||
|
|
|
@ -107,7 +107,7 @@ def undoBookmarksCollectionEntry(recentPostsCache: {},
|
|||
def bookmarkedByPerson(postJsonObject: {}, nickname: str, domain: str) -> bool:
|
||||
"""Returns True if the given post is bookmarked by the given person
|
||||
"""
|
||||
if noOfBookmarks(postJsonObject) == 0:
|
||||
if _noOfBookmarks(postJsonObject) == 0:
|
||||
return False
|
||||
actorMatch = domain + '/users/' + nickname
|
||||
for item in postJsonObject['object']['bookmarks']['items']:
|
||||
|
@ -116,7 +116,7 @@ def bookmarkedByPerson(postJsonObject: {}, nickname: str, domain: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def noOfBookmarks(postJsonObject: {}) -> int:
|
||||
def _noOfBookmarks(postJsonObject: {}) -> int:
|
||||
"""Returns the number of bookmarks ona given post
|
||||
"""
|
||||
if not postJsonObject.get('object'):
|
||||
|
|
62
content.py
62
content.py
|
@ -33,7 +33,7 @@ def removeHtmlTag(htmlStr: str, tag: str) -> str:
|
|||
return htmlStr
|
||||
|
||||
|
||||
def removeQuotesWithinQuotes(content: str) -> str:
|
||||
def _removeQuotesWithinQuotes(content: str) -> str:
|
||||
"""Removes any blockquote inside blockquote
|
||||
"""
|
||||
if '<blockquote>' not in content:
|
||||
|
@ -96,7 +96,7 @@ def htmlReplaceEmailQuote(content: str) -> str:
|
|||
else:
|
||||
lineStr = lineStr.replace('>', '<br>')
|
||||
newContent += '<p>' + lineStr + '</blockquote></p>'
|
||||
return removeQuotesWithinQuotes(newContent)
|
||||
return _removeQuotesWithinQuotes(newContent)
|
||||
|
||||
|
||||
def htmlReplaceQuoteMarks(content: str) -> str:
|
||||
|
@ -314,7 +314,7 @@ def replaceEmojiFromTags(content: str, tag: [], messageType: str) -> str:
|
|||
return content
|
||||
|
||||
|
||||
def addMusicTag(content: str, tag: str) -> str:
|
||||
def _addMusicTag(content: str, tag: str) -> str:
|
||||
"""If a music link is found then ensure that the post is
|
||||
tagged appropriately
|
||||
"""
|
||||
|
@ -416,8 +416,8 @@ def validHashTag(hashtag: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def addHashTags(wordStr: str, httpPrefix: str, domain: str,
|
||||
replaceHashTags: {}, postHashtags: {}) -> bool:
|
||||
def _addHashTags(wordStr: str, httpPrefix: str, domain: str,
|
||||
replaceHashTags: {}, postHashtags: {}) -> bool:
|
||||
"""Detects hashtags and adds them to the replacements dict
|
||||
Also updates the hashtags list to be added to the post
|
||||
"""
|
||||
|
@ -438,10 +438,10 @@ def addHashTags(wordStr: str, httpPrefix: str, domain: str,
|
|||
return True
|
||||
|
||||
|
||||
def addEmoji(baseDir: str, wordStr: str,
|
||||
httpPrefix: str, domain: str,
|
||||
replaceEmoji: {}, postTags: {},
|
||||
emojiDict: {}) -> bool:
|
||||
def _addEmoji(baseDir: str, wordStr: str,
|
||||
httpPrefix: str, domain: str,
|
||||
replaceEmoji: {}, postTags: {},
|
||||
emojiDict: {}) -> bool:
|
||||
"""Detects Emoji and adds them to the replacements dict
|
||||
Also updates the tags list to be added to the post
|
||||
"""
|
||||
|
@ -489,8 +489,8 @@ def tagExists(tagType: str, tagName: str, tags: {}) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def addMention(wordStr: str, httpPrefix: str, following: str,
|
||||
replaceMentions: {}, recipients: [], tags: {}) -> bool:
|
||||
def _addMention(wordStr: str, httpPrefix: str, following: str,
|
||||
replaceMentions: {}, recipients: [], tags: {}) -> bool:
|
||||
"""Detects mentions and adds them to the replacements dict and
|
||||
recipients list
|
||||
"""
|
||||
|
@ -672,7 +672,7 @@ def removeLongWords(content: str, maxWordLength: int,
|
|||
return content
|
||||
|
||||
|
||||
def loadAutoTags(baseDir: str, nickname: str, domain: str) -> []:
|
||||
def _loadAutoTags(baseDir: str, nickname: str, domain: str) -> []:
|
||||
"""Loads automatic tags file and returns a list containing
|
||||
the lines of the file
|
||||
"""
|
||||
|
@ -685,9 +685,9 @@ def loadAutoTags(baseDir: str, nickname: str, domain: str) -> []:
|
|||
return []
|
||||
|
||||
|
||||
def autoTag(baseDir: str, nickname: str, domain: str,
|
||||
wordStr: str, autoTagList: [],
|
||||
appendTags: []):
|
||||
def _autoTag(baseDir: str, nickname: str, domain: str,
|
||||
wordStr: str, autoTagList: [],
|
||||
appendTags: []):
|
||||
"""Generates a list of tags to be automatically appended to the content
|
||||
"""
|
||||
for tagRule in autoTagList:
|
||||
|
@ -719,7 +719,7 @@ def addHtmlTags(baseDir: str, httpPrefix: str,
|
|||
maxWordLength = 40
|
||||
content = content.replace('\r', '')
|
||||
content = content.replace('\n', ' --linebreak-- ')
|
||||
content = addMusicTag(content, 'nowplaying')
|
||||
content = _addMusicTag(content, 'nowplaying')
|
||||
contentSimplified = \
|
||||
content.replace(',', ' ').replace(';', ' ').replace('- ', ' ')
|
||||
contentSimplified = contentSimplified.replace('. ', ' ').strip()
|
||||
|
@ -760,7 +760,7 @@ def addHtmlTags(baseDir: str, httpPrefix: str,
|
|||
# extract mentions and tags from words
|
||||
longWordsList = []
|
||||
prevWordStr = ''
|
||||
autoTagsList = loadAutoTags(baseDir, nickname, domain)
|
||||
autoTagsList = _loadAutoTags(baseDir, nickname, domain)
|
||||
appendTags = []
|
||||
for wordStr in words:
|
||||
wordLen = len(wordStr)
|
||||
|
@ -769,13 +769,13 @@ def addHtmlTags(baseDir: str, httpPrefix: str,
|
|||
longWordsList.append(wordStr)
|
||||
firstChar = wordStr[0]
|
||||
if firstChar == '@':
|
||||
if addMention(wordStr, httpPrefix, following,
|
||||
replaceMentions, recipients, hashtags):
|
||||
if _addMention(wordStr, httpPrefix, following,
|
||||
replaceMentions, recipients, hashtags):
|
||||
prevWordStr = ''
|
||||
continue
|
||||
elif firstChar == '#':
|
||||
if addHashTags(wordStr, httpPrefix, originalDomain,
|
||||
replaceHashTags, hashtags):
|
||||
if _addHashTags(wordStr, httpPrefix, originalDomain,
|
||||
replaceHashTags, hashtags):
|
||||
prevWordStr = ''
|
||||
continue
|
||||
elif ':' in wordStr:
|
||||
|
@ -791,18 +791,18 @@ def addHtmlTags(baseDir: str, httpPrefix: str,
|
|||
emojiDict = loadJson(baseDir + '/emoji/emoji.json')
|
||||
|
||||
# print('TAG: looking up emoji for :'+wordStr2+':')
|
||||
addEmoji(baseDir, ':' + wordStr2 + ':', httpPrefix,
|
||||
originalDomain, replaceEmoji, hashtags,
|
||||
emojiDict)
|
||||
_addEmoji(baseDir, ':' + wordStr2 + ':', httpPrefix,
|
||||
originalDomain, replaceEmoji, hashtags,
|
||||
emojiDict)
|
||||
else:
|
||||
if autoTag(baseDir, nickname, domain, wordStr,
|
||||
autoTagsList, appendTags):
|
||||
if _autoTag(baseDir, nickname, domain, wordStr,
|
||||
autoTagsList, appendTags):
|
||||
prevWordStr = ''
|
||||
continue
|
||||
if prevWordStr:
|
||||
if autoTag(baseDir, nickname, domain,
|
||||
prevWordStr + ' ' + wordStr,
|
||||
autoTagsList, appendTags):
|
||||
if _autoTag(baseDir, nickname, domain,
|
||||
prevWordStr + ' ' + wordStr,
|
||||
autoTagsList, appendTags):
|
||||
prevWordStr = ''
|
||||
continue
|
||||
prevWordStr = wordStr
|
||||
|
@ -810,8 +810,8 @@ def addHtmlTags(baseDir: str, httpPrefix: str,
|
|||
# add any auto generated tags
|
||||
for appended in appendTags:
|
||||
content = content + ' ' + appended
|
||||
addHashTags(appended, httpPrefix, originalDomain,
|
||||
replaceHashTags, hashtags)
|
||||
_addHashTags(appended, httpPrefix, originalDomain,
|
||||
replaceHashTags, hashtags)
|
||||
|
||||
# replace words with their html versions
|
||||
for wordStr, replaceStr in replaceMentions.items():
|
||||
|
|
|
@ -7,7 +7,7 @@ __email__ = "bob@freedombone.net"
|
|||
__status__ = "Production"
|
||||
|
||||
|
||||
def getDonationTypes() -> str:
|
||||
def _getDonationTypes() -> str:
|
||||
return ('patreon', 'paypal', 'gofundme', 'liberapay',
|
||||
'kickstarter', 'indiegogo', 'crowdsupply',
|
||||
'subscribestar')
|
||||
|
@ -18,7 +18,7 @@ def getDonationUrl(actorJson: {}) -> str:
|
|||
"""
|
||||
if not actorJson.get('attachment'):
|
||||
return ''
|
||||
donationType = getDonationTypes()
|
||||
donationType = _getDonationTypes()
|
||||
for propertyValue in actorJson['attachment']:
|
||||
if not propertyValue.get('name'):
|
||||
continue
|
||||
|
@ -54,7 +54,7 @@ def setDonationUrl(actorJson: {}, donateUrl: str) -> None:
|
|||
if not actorJson.get('attachment'):
|
||||
actorJson['attachment'] = []
|
||||
|
||||
donationType = getDonationTypes()
|
||||
donationType = _getDonationTypes()
|
||||
donateName = None
|
||||
for paymentService in donationType:
|
||||
if paymentService in donateUrl:
|
||||
|
|
10
filters.py
10
filters.py
|
@ -79,7 +79,7 @@ def removeGlobalFilter(baseDir: str, words: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def isTwitterPost(content: str) -> bool:
|
||||
def _isTwitterPost(content: str) -> bool:
|
||||
"""Returns true if the given post content is a retweet or twitter crosspost
|
||||
"""
|
||||
if '/twitter.' in content or '@twitter.' in content:
|
||||
|
@ -89,7 +89,7 @@ def isTwitterPost(content: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def isFilteredBase(filename: str, content: str) -> bool:
|
||||
def _isFilteredBase(filename: str, content: str) -> bool:
|
||||
"""Uses the given file containing filtered words to check
|
||||
the given content
|
||||
"""
|
||||
|
@ -122,7 +122,7 @@ def isFiltered(baseDir: str, nickname: str, domain: str, content: str) -> bool:
|
|||
words must be present although not necessarily adjacent
|
||||
"""
|
||||
globalFiltersFilename = baseDir + '/accounts/filters.txt'
|
||||
if isFilteredBase(globalFiltersFilename, content):
|
||||
if _isFilteredBase(globalFiltersFilename, content):
|
||||
return True
|
||||
|
||||
if not nickname or not domain:
|
||||
|
@ -132,9 +132,9 @@ def isFiltered(baseDir: str, nickname: str, domain: str, content: str) -> bool:
|
|||
removeTwitter = baseDir + '/accounts/' + \
|
||||
nickname + '@' + domain + '/.removeTwitter'
|
||||
if os.path.isfile(removeTwitter):
|
||||
if isTwitterPost(content):
|
||||
if _isTwitterPost(content):
|
||||
return True
|
||||
|
||||
accountFiltersFilename = baseDir + '/accounts/' + \
|
||||
nickname + '@' + domain + '/filters.txt'
|
||||
return isFilteredBase(accountFiltersFilename, content)
|
||||
return _isFilteredBase(accountFiltersFilename, content)
|
||||
|
|
140
follow.py
140
follow.py
|
@ -65,10 +65,10 @@ def createInitialLastSeen(baseDir: str, httpPrefix: str) -> None:
|
|||
break
|
||||
|
||||
|
||||
def preApprovedFollower(baseDir: str,
|
||||
nickname: str, domain: str,
|
||||
approveHandle: str,
|
||||
allowNewsFollowers: bool) -> bool:
|
||||
def _preApprovedFollower(baseDir: str,
|
||||
nickname: str, domain: str,
|
||||
approveHandle: str,
|
||||
allowNewsFollowers: bool) -> bool:
|
||||
"""Is the given handle an already manually approved follower?
|
||||
"""
|
||||
# optionally allow the news account to be followed
|
||||
|
@ -84,10 +84,10 @@ def preApprovedFollower(baseDir: str,
|
|||
return False
|
||||
|
||||
|
||||
def removeFromFollowBase(baseDir: str,
|
||||
nickname: str, domain: str,
|
||||
acceptOrDenyHandle: str, followFile: str,
|
||||
debug: bool) -> None:
|
||||
def _removeFromFollowBase(baseDir: str,
|
||||
nickname: str, domain: str,
|
||||
acceptOrDenyHandle: str, followFile: str,
|
||||
debug: bool) -> None:
|
||||
"""Removes a handle from follow requests or rejects file
|
||||
"""
|
||||
handle = nickname + '@' + domain
|
||||
|
@ -114,17 +114,17 @@ def removeFromFollowRequests(baseDir: str,
|
|||
denyHandle: str, debug: bool) -> None:
|
||||
"""Removes a handle from follow requests
|
||||
"""
|
||||
removeFromFollowBase(baseDir, nickname, domain,
|
||||
denyHandle, 'followrequests', debug)
|
||||
_removeFromFollowBase(baseDir, nickname, domain,
|
||||
denyHandle, 'followrequests', debug)
|
||||
|
||||
|
||||
def removeFromFollowRejects(baseDir: str,
|
||||
nickname: str, domain: str,
|
||||
acceptHandle: str, debug: bool) -> None:
|
||||
def _removeFromFollowRejects(baseDir: str,
|
||||
nickname: str, domain: str,
|
||||
acceptHandle: str, debug: bool) -> None:
|
||||
"""Removes a handle from follow rejects
|
||||
"""
|
||||
removeFromFollowBase(baseDir, nickname, domain,
|
||||
acceptHandle, 'followrejects', debug)
|
||||
_removeFromFollowBase(baseDir, nickname, domain,
|
||||
acceptHandle, 'followrejects', debug)
|
||||
|
||||
|
||||
def isFollowingActor(baseDir: str,
|
||||
|
@ -179,8 +179,8 @@ def followerOfPerson(baseDir: str, nickname: str, domain: str,
|
|||
federationList, debug, 'followers.txt')
|
||||
|
||||
|
||||
def isFollowerOfPerson(baseDir: str, nickname: str, domain: str,
|
||||
followerNickname: str, followerDomain: str) -> bool:
|
||||
def _isFollowerOfPerson(baseDir: str, nickname: str, domain: str,
|
||||
followerNickname: str, followerDomain: str) -> bool:
|
||||
"""is the given nickname a follower of followerNickname?
|
||||
"""
|
||||
if ':' in domain:
|
||||
|
@ -291,9 +291,9 @@ def clearFollowers(baseDir: str, nickname: str, domain: str) -> None:
|
|||
clearFollows(baseDir, nickname, domain, 'followers.txt')
|
||||
|
||||
|
||||
def getNoOfFollows(baseDir: str, nickname: str, domain: str,
|
||||
authenticated: bool,
|
||||
followFile='following.txt') -> int:
|
||||
def _getNoOfFollows(baseDir: str, nickname: str, domain: str,
|
||||
authenticated: bool,
|
||||
followFile='following.txt') -> int:
|
||||
"""Returns the number of follows or followers
|
||||
"""
|
||||
# only show number of followers to authenticated
|
||||
|
@ -324,12 +324,12 @@ def getNoOfFollows(baseDir: str, nickname: str, domain: str,
|
|||
return ctr
|
||||
|
||||
|
||||
def getNoOfFollowers(baseDir: str,
|
||||
nickname: str, domain: str, authenticated: bool) -> int:
|
||||
def _getNoOfFollowers(baseDir: str,
|
||||
nickname: str, domain: str, authenticated: bool) -> int:
|
||||
"""Returns the number of followers of the given person
|
||||
"""
|
||||
return getNoOfFollows(baseDir, nickname, domain,
|
||||
authenticated, 'followers.txt')
|
||||
return _getNoOfFollows(baseDir, nickname, domain,
|
||||
authenticated, 'followers.txt')
|
||||
|
||||
|
||||
def getFollowingFeed(baseDir: str, domain: str, port: int, path: str,
|
||||
|
@ -382,7 +382,7 @@ def getFollowingFeed(baseDir: str, domain: str, port: int, path: str,
|
|||
httpPrefix + '://' + domain + '/users/' + \
|
||||
nickname + '/' + followFile
|
||||
totalStr = \
|
||||
getNoOfFollows(baseDir, nickname, domain, authenticated)
|
||||
_getNoOfFollows(baseDir, nickname, domain, authenticated)
|
||||
following = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'first': firstStr,
|
||||
|
@ -463,15 +463,15 @@ def getFollowingFeed(baseDir: str, domain: str, port: int, path: str,
|
|||
return following
|
||||
|
||||
|
||||
def followApprovalRequired(baseDir: str, nicknameToFollow: str,
|
||||
domainToFollow: str, debug: bool,
|
||||
followRequestHandle: str,
|
||||
allowNewsFollowers: bool) -> bool:
|
||||
def _followApprovalRequired(baseDir: str, nicknameToFollow: str,
|
||||
domainToFollow: str, debug: bool,
|
||||
followRequestHandle: str,
|
||||
allowNewsFollowers: bool) -> bool:
|
||||
""" Returns the policy for follower approvals
|
||||
"""
|
||||
# has this handle already been manually approved?
|
||||
if preApprovedFollower(baseDir, nicknameToFollow, domainToFollow,
|
||||
followRequestHandle, allowNewsFollowers):
|
||||
if _preApprovedFollower(baseDir, nicknameToFollow, domainToFollow,
|
||||
followRequestHandle, allowNewsFollowers):
|
||||
return False
|
||||
|
||||
manuallyApproveFollows = False
|
||||
|
@ -494,10 +494,10 @@ def followApprovalRequired(baseDir: str, nicknameToFollow: str,
|
|||
return manuallyApproveFollows
|
||||
|
||||
|
||||
def noOfFollowRequests(baseDir: str,
|
||||
nicknameToFollow: str, domainToFollow: str,
|
||||
nickname: str, domain: str, fromPort: int,
|
||||
followType: str) -> int:
|
||||
def _noOfFollowRequests(baseDir: str,
|
||||
nicknameToFollow: str, domainToFollow: str,
|
||||
nickname: str, domain: str, fromPort: int,
|
||||
followType: str) -> int:
|
||||
"""Returns the current number of follow requests
|
||||
"""
|
||||
accountsDir = baseDir + '/accounts/' + \
|
||||
|
@ -521,11 +521,11 @@ def noOfFollowRequests(baseDir: str,
|
|||
return ctr
|
||||
|
||||
|
||||
def storeFollowRequest(baseDir: str,
|
||||
nicknameToFollow: str, domainToFollow: str, port: int,
|
||||
nickname: str, domain: str, fromPort: int,
|
||||
followJson: {},
|
||||
debug: bool, personUrl: str) -> bool:
|
||||
def _storeFollowRequest(baseDir: str,
|
||||
nicknameToFollow: str, domainToFollow: str, port: int,
|
||||
nickname: str, domain: str, fromPort: int,
|
||||
followJson: {},
|
||||
debug: bool, personUrl: str) -> bool:
|
||||
"""Stores the follow request for later use
|
||||
"""
|
||||
accountsDir = baseDir + '/accounts/' + \
|
||||
|
@ -668,9 +668,9 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str,
|
|||
nicknameToFollow)
|
||||
return True
|
||||
if maxFollowers > 0:
|
||||
if getNoOfFollowers(baseDir,
|
||||
nicknameToFollow, domainToFollow,
|
||||
True) > maxFollowers:
|
||||
if _getNoOfFollowers(baseDir,
|
||||
nicknameToFollow, domainToFollow,
|
||||
True) > maxFollowers:
|
||||
print('WARN: ' + nicknameToFollow +
|
||||
' has reached their maximum number of followers')
|
||||
return True
|
||||
|
@ -682,9 +682,9 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str,
|
|||
baseDir + '/accounts/' + handleToFollow)
|
||||
return True
|
||||
|
||||
if isFollowerOfPerson(baseDir,
|
||||
nicknameToFollow, domainToFollowFull,
|
||||
nickname, domainFull):
|
||||
if _isFollowerOfPerson(baseDir,
|
||||
nicknameToFollow, domainToFollowFull,
|
||||
nickname, domainFull):
|
||||
if debug:
|
||||
print('DEBUG: ' + nickname + '@' + domain +
|
||||
' is already a follower of ' +
|
||||
|
@ -693,37 +693,37 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str,
|
|||
|
||||
# what is the followers policy?
|
||||
approveHandle = nickname + '@' + domainFull
|
||||
if followApprovalRequired(baseDir, nicknameToFollow,
|
||||
domainToFollow, debug, approveHandle,
|
||||
allowNewsFollowers):
|
||||
if _followApprovalRequired(baseDir, nicknameToFollow,
|
||||
domainToFollow, debug, approveHandle,
|
||||
allowNewsFollowers):
|
||||
print('Follow approval is required')
|
||||
if domain.endswith('.onion'):
|
||||
if noOfFollowRequests(baseDir,
|
||||
nicknameToFollow, domainToFollow,
|
||||
nickname, domain, fromPort,
|
||||
'onion') > 5:
|
||||
if _noOfFollowRequests(baseDir,
|
||||
nicknameToFollow, domainToFollow,
|
||||
nickname, domain, fromPort,
|
||||
'onion') > 5:
|
||||
print('Too many follow requests from onion addresses')
|
||||
return False
|
||||
elif domain.endswith('.i2p'):
|
||||
if noOfFollowRequests(baseDir,
|
||||
nicknameToFollow, domainToFollow,
|
||||
nickname, domain, fromPort,
|
||||
'i2p') > 5:
|
||||
if _noOfFollowRequests(baseDir,
|
||||
nicknameToFollow, domainToFollow,
|
||||
nickname, domain, fromPort,
|
||||
'i2p') > 5:
|
||||
print('Too many follow requests from i2p addresses')
|
||||
return False
|
||||
else:
|
||||
if noOfFollowRequests(baseDir,
|
||||
nicknameToFollow, domainToFollow,
|
||||
nickname, domain, fromPort,
|
||||
'') > 10:
|
||||
if _noOfFollowRequests(baseDir,
|
||||
nicknameToFollow, domainToFollow,
|
||||
nickname, domain, fromPort,
|
||||
'') > 10:
|
||||
print('Too many follow requests')
|
||||
return False
|
||||
|
||||
print('Storing follow request for approval')
|
||||
return storeFollowRequest(baseDir,
|
||||
nicknameToFollow, domainToFollow, port,
|
||||
nickname, domain, fromPort,
|
||||
messageJson, debug, messageJson['actor'])
|
||||
return _storeFollowRequest(baseDir,
|
||||
nicknameToFollow, domainToFollow, port,
|
||||
nickname, domain, fromPort,
|
||||
messageJson, debug, messageJson['actor'])
|
||||
else:
|
||||
print('Follow request does not require approval')
|
||||
# update the followers
|
||||
|
@ -920,15 +920,15 @@ def sendFollowRequest(session, baseDir: str,
|
|||
'object': followedId
|
||||
}
|
||||
|
||||
if followApprovalRequired(baseDir, nickname, domain, debug,
|
||||
followHandle, allowNewsFollowers):
|
||||
if _followApprovalRequired(baseDir, nickname, domain, debug,
|
||||
followHandle, allowNewsFollowers):
|
||||
# Remove any follow requests rejected for the account being followed.
|
||||
# It's assumed that if you are following someone then you are
|
||||
# ok with them following back. If this isn't the case then a rejected
|
||||
# follow request will block them again.
|
||||
removeFromFollowRejects(baseDir,
|
||||
nickname, domain,
|
||||
followHandle, debug)
|
||||
_removeFromFollowRejects(baseDir,
|
||||
nickname, domain,
|
||||
followHandle, debug)
|
||||
|
||||
sendSignedJson(newFollowJson, session, baseDir, nickname, domain, port,
|
||||
followNickname, followDomain, followPort,
|
||||
|
|
|
@ -34,10 +34,10 @@ def receivingCalendarEvents(baseDir: str, nickname: str, domain: str,
|
|||
return handle + '\n' in open(calendarFilename).read()
|
||||
|
||||
|
||||
def receiveCalendarEvents(baseDir: str, nickname: str, domain: str,
|
||||
followingNickname: str,
|
||||
followingDomain: str,
|
||||
add: bool) -> None:
|
||||
def _receiveCalendarEvents(baseDir: str, nickname: str, domain: str,
|
||||
followingNickname: str,
|
||||
followingDomain: str,
|
||||
add: bool) -> None:
|
||||
"""Adds or removes a handle from the following.txt list into a list
|
||||
indicating whether to receive calendar events from that account
|
||||
"""
|
||||
|
@ -100,12 +100,12 @@ def receiveCalendarEvents(baseDir: str, nickname: str, domain: str,
|
|||
def addPersonToCalendar(baseDir: str, nickname: str, domain: str,
|
||||
followingNickname: str,
|
||||
followingDomain: str) -> None:
|
||||
receiveCalendarEvents(baseDir, nickname, domain,
|
||||
followingNickname, followingDomain, True)
|
||||
_receiveCalendarEvents(baseDir, nickname, domain,
|
||||
followingNickname, followingDomain, True)
|
||||
|
||||
|
||||
def removePersonFromCalendar(baseDir: str, nickname: str, domain: str,
|
||||
followingNickname: str,
|
||||
followingDomain: str) -> None:
|
||||
receiveCalendarEvents(baseDir, nickname, domain,
|
||||
followingNickname, followingDomain, False)
|
||||
_receiveCalendarEvents(baseDir, nickname, domain,
|
||||
followingNickname, followingDomain, False)
|
||||
|
|
26
git.py
26
git.py
|
@ -10,7 +10,7 @@ import os
|
|||
import html
|
||||
|
||||
|
||||
def gitFormatContent(content: str) -> str:
|
||||
def _gitFormatContent(content: str) -> str:
|
||||
""" replace html formatting, so that it's more
|
||||
like the original patch file
|
||||
"""
|
||||
|
@ -22,8 +22,8 @@ def gitFormatContent(content: str) -> str:
|
|||
return patchStr
|
||||
|
||||
|
||||
def getGitProjectName(baseDir: str, nickname: str, domain: str,
|
||||
subject: str) -> str:
|
||||
def _getGitProjectName(baseDir: str, nickname: str, domain: str,
|
||||
subject: str) -> str:
|
||||
"""Returns the project name for a git patch
|
||||
The project name should be contained within the subject line
|
||||
and should match against a list of projects which the account
|
||||
|
@ -71,13 +71,13 @@ def isGitPatch(baseDir: str, nickname: str, domain: str,
|
|||
return False
|
||||
if checkProjectName:
|
||||
projectName = \
|
||||
getGitProjectName(baseDir, nickname, domain, subject)
|
||||
_getGitProjectName(baseDir, nickname, domain, subject)
|
||||
if not projectName:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def getGitHash(patchStr: str) -> str:
|
||||
def _getGitHash(patchStr: str) -> str:
|
||||
"""Returns the commit hash from a given patch
|
||||
"""
|
||||
patchLines = patchStr.split('\n')
|
||||
|
@ -91,7 +91,7 @@ def getGitHash(patchStr: str) -> str:
|
|||
return None
|
||||
|
||||
|
||||
def getPatchDescription(patchStr: str) -> str:
|
||||
def _getPatchDescription(patchStr: str) -> str:
|
||||
"""Returns the description from a given patch
|
||||
"""
|
||||
patchLines = patchStr.split('\n')
|
||||
|
@ -134,8 +134,8 @@ def convertPostToPatch(baseDir: str, nickname: str, domain: str,
|
|||
postJsonObject['object']['content'],
|
||||
False):
|
||||
return False
|
||||
patchStr = gitFormatContent(postJsonObject['object']['content'])
|
||||
commitHash = getGitHash(patchStr)
|
||||
patchStr = _gitFormatContent(postJsonObject['object']['content'])
|
||||
commitHash = _getGitHash(patchStr)
|
||||
if not commitHash:
|
||||
return False
|
||||
postJsonObject['object']['type'] = 'Patch'
|
||||
|
@ -146,7 +146,7 @@ def convertPostToPatch(baseDir: str, nickname: str, domain: str,
|
|||
postJsonObject['object']['hash'] = commitHash
|
||||
postJsonObject['object']['description'] = {
|
||||
"mediaType": "text/plain",
|
||||
"content": getPatchDescription(patchStr)
|
||||
"content": _getPatchDescription(patchStr)
|
||||
}
|
||||
# remove content map
|
||||
if postJsonObject['object'].get('contentMap'):
|
||||
|
@ -155,7 +155,7 @@ def convertPostToPatch(baseDir: str, nickname: str, domain: str,
|
|||
return True
|
||||
|
||||
|
||||
def gitAddFromHandle(patchStr: str, handle: str) -> str:
|
||||
def _gitAddFromHandle(patchStr: str, handle: str) -> str:
|
||||
"""Adds the activitypub handle of the sender to the patch
|
||||
"""
|
||||
fromStr = 'AP-signed-off-by: '
|
||||
|
@ -181,7 +181,7 @@ def receiveGitPatch(baseDir: str, nickname: str, domain: str,
|
|||
messageType, subject, content):
|
||||
return False
|
||||
|
||||
patchStr = gitFormatContent(content)
|
||||
patchStr = _gitFormatContent(content)
|
||||
|
||||
patchLines = patchStr.split('\n')
|
||||
patchFilename = None
|
||||
|
@ -197,7 +197,7 @@ def receiveGitPatch(baseDir: str, nickname: str, domain: str,
|
|||
patchSubject = patchSubject.replace('[PATCH]', '').strip()
|
||||
patchSubject = patchSubject.replace(' ', '_')
|
||||
projectName = \
|
||||
getGitProjectName(baseDir, nickname, domain, subject)
|
||||
_getGitProjectName(baseDir, nickname, domain, subject)
|
||||
if not os.path.isdir(patchesDir):
|
||||
os.mkdir(patchesDir)
|
||||
projectDir = patchesDir + '/' + projectName
|
||||
|
@ -209,7 +209,7 @@ def receiveGitPatch(baseDir: str, nickname: str, domain: str,
|
|||
if not patchFilename:
|
||||
return False
|
||||
patchStr = \
|
||||
gitAddFromHandle(patchStr, '@' + fromNickname + '@' + fromDomain)
|
||||
_gitAddFromHandle(patchStr, '@' + fromNickname + '@' + fromDomain)
|
||||
with open(patchFilename, 'w+') as patchFile:
|
||||
patchFile.write(patchStr)
|
||||
patchNotifyFilename = \
|
||||
|
|
32
happening.py
32
happening.py
|
@ -17,7 +17,7 @@ from utils import daysInMonth
|
|||
from utils import mergeDicts
|
||||
|
||||
|
||||
def validUuid(testUuid: str, version=4):
|
||||
def _validUuid(testUuid: str, version=4):
|
||||
"""Check if uuid_to_test is a valid UUID
|
||||
"""
|
||||
try:
|
||||
|
@ -28,7 +28,7 @@ def validUuid(testUuid: str, version=4):
|
|||
return str(uuid_obj) == testUuid
|
||||
|
||||
|
||||
def removeEventFromTimeline(eventId: str, tlEventsFilename: str) -> None:
|
||||
def _removeEventFromTimeline(eventId: str, tlEventsFilename: str) -> None:
|
||||
"""Removes the given event Id from the timeline
|
||||
"""
|
||||
if eventId + '\n' not in open(tlEventsFilename).read():
|
||||
|
@ -71,7 +71,7 @@ def saveEventPost(baseDir: str, handle: str, postId: str,
|
|||
|
||||
if eventJson.get('name') and eventJson.get('actor') and \
|
||||
eventJson.get('uuid') and eventJson.get('content'):
|
||||
if not validUuid(eventJson['uuid']):
|
||||
if not _validUuid(eventJson['uuid']):
|
||||
return False
|
||||
print('Mobilizon type event')
|
||||
# if this is a full description of an event then save it
|
||||
|
@ -92,7 +92,7 @@ def saveEventPost(baseDir: str, handle: str, postId: str,
|
|||
tlEventsFilename = baseDir + '/accounts/' + handle + '/events.txt'
|
||||
|
||||
if os.path.isfile(tlEventsFilename):
|
||||
removeEventFromTimeline(eventId, tlEventsFilename)
|
||||
_removeEventFromTimeline(eventId, tlEventsFilename)
|
||||
try:
|
||||
with open(tlEventsFilename, 'r+') as tlEventsFile:
|
||||
content = tlEventsFile.read()
|
||||
|
@ -146,7 +146,7 @@ def saveEventPost(baseDir: str, handle: str, postId: str,
|
|||
return True
|
||||
|
||||
|
||||
def isHappeningEvent(tag: {}) -> bool:
|
||||
def _isHappeningEvent(tag: {}) -> bool:
|
||||
"""Is this tag an Event or Place ActivityStreams type?
|
||||
"""
|
||||
if not tag.get('type'):
|
||||
|
@ -156,7 +156,7 @@ def isHappeningEvent(tag: {}) -> bool:
|
|||
return True
|
||||
|
||||
|
||||
def isHappeningPost(postJsonObject: {}) -> bool:
|
||||
def _isHappeningPost(postJsonObject: {}) -> bool:
|
||||
"""Is this a post with tags?
|
||||
"""
|
||||
if not postJsonObject:
|
||||
|
@ -208,13 +208,13 @@ def getTodaysEvents(baseDir: str, nickname: str, domain: str,
|
|||
continue
|
||||
|
||||
postJsonObject = loadJson(postFilename)
|
||||
if not isHappeningPost(postJsonObject):
|
||||
if not _isHappeningPost(postJsonObject):
|
||||
continue
|
||||
|
||||
postEvent = []
|
||||
dayOfMonth = None
|
||||
for tag in postJsonObject['object']['tag']:
|
||||
if not isHappeningEvent(tag):
|
||||
if not _isHappeningEvent(tag):
|
||||
continue
|
||||
# this tag is an event or a place
|
||||
if tag['type'] == 'Event':
|
||||
|
@ -275,11 +275,11 @@ def todaysEventsCheck(baseDir: str, nickname: str, domain: str) -> bool:
|
|||
continue
|
||||
|
||||
postJsonObject = loadJson(postFilename)
|
||||
if not isHappeningPost(postJsonObject):
|
||||
if not _isHappeningPost(postJsonObject):
|
||||
continue
|
||||
|
||||
for tag in postJsonObject['object']['tag']:
|
||||
if not isHappeningEvent(tag):
|
||||
if not _isHappeningEvent(tag):
|
||||
continue
|
||||
# this tag is an event or a place
|
||||
if tag['type'] != 'Event':
|
||||
|
@ -322,11 +322,11 @@ def thisWeeksEventsCheck(baseDir: str, nickname: str, domain: str) -> bool:
|
|||
continue
|
||||
|
||||
postJsonObject = loadJson(postFilename)
|
||||
if not isHappeningPost(postJsonObject):
|
||||
if not _isHappeningPost(postJsonObject):
|
||||
continue
|
||||
|
||||
for tag in postJsonObject['object']['tag']:
|
||||
if not isHappeningEvent(tag):
|
||||
if not _isHappeningEvent(tag):
|
||||
continue
|
||||
# this tag is an event or a place
|
||||
if tag['type'] != 'Event':
|
||||
|
@ -377,14 +377,14 @@ def getThisWeeksEvents(baseDir: str, nickname: str, domain: str) -> {}:
|
|||
continue
|
||||
|
||||
postJsonObject = loadJson(postFilename)
|
||||
if not isHappeningPost(postJsonObject):
|
||||
if not _isHappeningPost(postJsonObject):
|
||||
continue
|
||||
|
||||
postEvent = []
|
||||
dayOfMonth = None
|
||||
weekDayIndex = None
|
||||
for tag in postJsonObject['object']['tag']:
|
||||
if not isHappeningEvent(tag):
|
||||
if not _isHappeningEvent(tag):
|
||||
continue
|
||||
# this tag is an event or a place
|
||||
if tag['type'] == 'Event':
|
||||
|
@ -462,13 +462,13 @@ def getCalendarEvents(baseDir: str, nickname: str, domain: str,
|
|||
continue
|
||||
|
||||
postJsonObject = loadJson(postFilename)
|
||||
if not isHappeningPost(postJsonObject):
|
||||
if not _isHappeningPost(postJsonObject):
|
||||
continue
|
||||
|
||||
postEvent = []
|
||||
dayOfMonth = None
|
||||
for tag in postJsonObject['object']['tag']:
|
||||
if not isHappeningEvent(tag):
|
||||
if not _isHappeningEvent(tag):
|
||||
continue
|
||||
# this tag is an event or a place
|
||||
if tag['type'] == 'Event':
|
||||
|
|
|
@ -135,7 +135,7 @@ def createSignedHeader(privateKeyPem: str, nickname: str,
|
|||
return headers
|
||||
|
||||
|
||||
def verifyRecentSignature(signedDateStr: str) -> bool:
|
||||
def _verifyRecentSignature(signedDateStr: str) -> bool:
|
||||
"""Checks whether the given time taken from the header is within
|
||||
12 hours of the current time
|
||||
"""
|
||||
|
@ -219,7 +219,7 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
|||
else:
|
||||
if headers.get(signedHeader):
|
||||
if signedHeader == 'date':
|
||||
if not verifyRecentSignature(headers[signedHeader]):
|
||||
if not _verifyRecentSignature(headers[signedHeader]):
|
||||
if debug:
|
||||
print('DEBUG: ' +
|
||||
'verifyPostHeaders date is not recent ' +
|
||||
|
@ -230,7 +230,7 @@ def verifyPostHeaders(httpPrefix: str, publicKeyPem: str, headers: dict,
|
|||
else:
|
||||
signedHeaderCap = signedHeader.capitalize()
|
||||
if signedHeaderCap == 'Date':
|
||||
if not verifyRecentSignature(headers[signedHeaderCap]):
|
||||
if not _verifyRecentSignature(headers[signedHeaderCap]):
|
||||
if debug:
|
||||
print('DEBUG: ' +
|
||||
'verifyPostHeaders date is not recent ' +
|
||||
|
|
36
jsonldsig.py
36
jsonldsig.py
|
@ -28,21 +28,21 @@ import base64
|
|||
import json
|
||||
|
||||
|
||||
def b64safeEncode(payload: {}) -> str:
|
||||
def _b64safeEncode(payload: {}) -> str:
|
||||
"""
|
||||
b64 url safe encoding with the padding removed.
|
||||
"""
|
||||
return base64.urlsafe_b64encode(payload).rstrip(b'=')
|
||||
|
||||
|
||||
def b64safeDecode(payload: {}) -> str:
|
||||
def _b64safeDecode(payload: {}) -> str:
|
||||
"""
|
||||
b64 url safe decoding with the padding added.
|
||||
"""
|
||||
return base64.urlsafe_b64decode(payload + b'=' * (4 - len(payload) % 4))
|
||||
|
||||
|
||||
def normalizeJson(payload: {}) -> str:
|
||||
def _normalizeJson(payload: {}) -> str:
|
||||
"""
|
||||
Normalize with URDNA2015
|
||||
"""
|
||||
|
@ -50,7 +50,7 @@ def normalizeJson(payload: {}) -> str:
|
|||
sort_keys=True).encode('utf-8')
|
||||
|
||||
|
||||
def signRs256(payload: {}, privateKeyPem: str) -> str:
|
||||
def _signRs256(payload: {}, privateKeyPem: str) -> str:
|
||||
"""
|
||||
Produce a RS256 signature of the payload
|
||||
"""
|
||||
|
@ -60,7 +60,7 @@ def signRs256(payload: {}, privateKeyPem: str) -> str:
|
|||
return signature
|
||||
|
||||
|
||||
def verifyRs256(payload: {}, signature: str, publicKeyPem: str) -> bool:
|
||||
def _verifyRs256(payload: {}, signature: str, publicKeyPem: str) -> bool:
|
||||
"""
|
||||
Verifies a RS256 signature
|
||||
"""
|
||||
|
@ -69,7 +69,7 @@ def verifyRs256(payload: {}, signature: str, publicKeyPem: str) -> bool:
|
|||
return verifier.verify(SHA256.new(payload), signature)
|
||||
|
||||
|
||||
def signJws(payload: {}, privateKeyPem: str) -> str:
|
||||
def _signJws(payload: {}, privateKeyPem: str) -> str:
|
||||
"""
|
||||
Prepare payload to sign
|
||||
"""
|
||||
|
@ -78,28 +78,28 @@ def signJws(payload: {}, privateKeyPem: str) -> str:
|
|||
'b64': False,
|
||||
'crit': ['b64']
|
||||
}
|
||||
normalizedJson = normalizeJson(header)
|
||||
encodedHeader = b64safeEncode(normalizedJson)
|
||||
normalizedJson = _normalizeJson(header)
|
||||
encodedHeader = _b64safeEncode(normalizedJson)
|
||||
preparedPayload = b'.'.join([encodedHeader, payload])
|
||||
|
||||
signature = signRs256(preparedPayload, privateKeyPem)
|
||||
encodedSignature = b64safeEncode(signature)
|
||||
signature = _signRs256(preparedPayload, privateKeyPem)
|
||||
encodedSignature = _b64safeEncode(signature)
|
||||
jwsSignature = b'..'.join([encodedHeader, encodedSignature])
|
||||
|
||||
return jwsSignature
|
||||
|
||||
|
||||
def verifyJws(payload: {}, jwsSignature: str, publicKeyPem: str) -> bool:
|
||||
def _verifyJws(payload: {}, jwsSignature: str, publicKeyPem: str) -> bool:
|
||||
"""
|
||||
Verifies a signature using the given public key
|
||||
"""
|
||||
encodedHeader, encodedSignature = jwsSignature.split(b'..')
|
||||
signature = b64safeDecode(encodedSignature)
|
||||
signature = _b64safeDecode(encodedSignature)
|
||||
payload = b'.'.join([encodedHeader, payload])
|
||||
return verifyRs256(payload, signature, publicKeyPem)
|
||||
return _verifyRs256(payload, signature, publicKeyPem)
|
||||
|
||||
|
||||
def jsonldNormalize(jldDocument: str):
|
||||
def _jsonldNormalize(jldDocument: str):
|
||||
"""
|
||||
Normalize and hash the json-ld document
|
||||
"""
|
||||
|
@ -117,8 +117,8 @@ def jsonldSign(jldDocument: {}, privateKeyPem: str) -> {}:
|
|||
Produces a signed JSON-LD document with a Json Web Signature
|
||||
"""
|
||||
jldDocument = deepcopy(jldDocument)
|
||||
normalizedJldHash = jsonldNormalize(jldDocument)
|
||||
jwsSignature = signJws(normalizedJldHash, privateKeyPem)
|
||||
normalizedJldHash = _jsonldNormalize(jldDocument)
|
||||
jwsSignature = _signJws(normalizedJldHash, privateKeyPem)
|
||||
|
||||
# construct the signature document and add it to jsonld
|
||||
signature = {
|
||||
|
@ -138,9 +138,9 @@ def jsonldVerify(signedJldDocument: {}, publicKeyPem: str) -> bool:
|
|||
signedJldDocument = deepcopy(signedJldDocument)
|
||||
signature = signedJldDocument.pop('signature')
|
||||
jwsSignature = signature['signatureValue'].encode('utf-8')
|
||||
normalizedJldHash = jsonldNormalize(signedJldDocument)
|
||||
normalizedJldHash = _jsonldNormalize(signedJldDocument)
|
||||
|
||||
return verifyJws(normalizedJldHash, jwsSignature, publicKeyPem)
|
||||
return _verifyJws(normalizedJldHash, jwsSignature, publicKeyPem)
|
||||
|
||||
|
||||
def testSignJsonld(jldDocument: {}, privateKeyPem: str) -> {}:
|
||||
|
|
28
like.py
28
like.py
|
@ -50,15 +50,15 @@ def noOfLikes(postJsonObject: {}) -> int:
|
|||
return len(postJsonObject['object']['likes']['items'])
|
||||
|
||||
|
||||
def like(recentPostsCache: {},
|
||||
session, baseDir: str, federationList: [],
|
||||
nickname: str, domain: str, port: int,
|
||||
ccList: [], httpPrefix: str,
|
||||
objectUrl: str, actorLiked: str,
|
||||
clientToServer: bool,
|
||||
sendThreads: [], postLog: [],
|
||||
personCache: {}, cachedWebfingers: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
def _like(recentPostsCache: {},
|
||||
session, baseDir: str, federationList: [],
|
||||
nickname: str, domain: str, port: int,
|
||||
ccList: [], httpPrefix: str,
|
||||
objectUrl: str, actorLiked: str,
|
||||
clientToServer: bool,
|
||||
sendThreads: [], postLog: [],
|
||||
personCache: {}, cachedWebfingers: {},
|
||||
debug: bool, projectVersion: str) -> {}:
|
||||
"""Creates a like
|
||||
actor is the person doing the liking
|
||||
'to' might be a specific person (actor) whose post was liked
|
||||
|
@ -134,11 +134,11 @@ def likePost(recentPostsCache: {},
|
|||
actorLiked = httpPrefix + '://' + likeDomain + '/users/' + likeNickname
|
||||
objectUrl = actorLiked + '/statuses/' + str(likeStatusNumber)
|
||||
|
||||
return like(recentPostsCache,
|
||||
session, baseDir, federationList, nickname, domain, port,
|
||||
ccList, httpPrefix, objectUrl, actorLiked, clientToServer,
|
||||
sendThreads, postLog, personCache, cachedWebfingers,
|
||||
debug, projectVersion)
|
||||
return _like(recentPostsCache,
|
||||
session, baseDir, federationList, nickname, domain, port,
|
||||
ccList, httpPrefix, objectUrl, actorLiked, clientToServer,
|
||||
sendThreads, postLog, personCache, cachedWebfingers,
|
||||
debug, projectVersion)
|
||||
|
||||
|
||||
def sendLikeViaServer(baseDir: str, session,
|
||||
|
|
|
@ -62,7 +62,7 @@ def manualDenyFollowRequest(session, baseDir: str,
|
|||
print('Follow request from ' + denyHandle + ' was denied.')
|
||||
|
||||
|
||||
def approveFollowerHandle(accountDir: str, approveHandle: str) -> None:
|
||||
def _approveFollowerHandle(accountDir: str, approveHandle: str) -> None:
|
||||
""" Record manually approved handles so that if they unfollow and then
|
||||
re-follow later then they don't need to be manually approved again
|
||||
"""
|
||||
|
@ -203,7 +203,7 @@ def manualApproveFollowRequest(session, baseDir: str,
|
|||
# in followers.txt
|
||||
if approveHandleFull in open(followersFilename).read():
|
||||
# mark this handle as approved for following
|
||||
approveFollowerHandle(accountDir, approveHandle)
|
||||
_approveFollowerHandle(accountDir, approveHandle)
|
||||
# update the follow requests with the handles not yet approved
|
||||
os.rename(approveFollowsFilename + '.new', approveFollowsFilename)
|
||||
# remove the .follow file
|
||||
|
|
12
media.py
12
media.py
|
@ -56,12 +56,12 @@ def removeMetaData(imageFilename: str, outputFilename: str) -> None:
|
|||
os.system('/usr/bin/mogrify -strip ' + outputFilename) # nosec
|
||||
|
||||
|
||||
def getImageHash(imageFilename: str) -> str:
|
||||
def _getImageHash(imageFilename: str) -> str:
|
||||
value = numpy.array(Image.open(imageFilename).convert("RGB"))
|
||||
return blurhash_encode(value)
|
||||
|
||||
|
||||
def isMedia(imageFilename: str) -> bool:
|
||||
def _isMedia(imageFilename: str) -> bool:
|
||||
permittedMedia = getMediaExtensions()
|
||||
for m in permittedMedia:
|
||||
if imageFilename.endswith('.' + m):
|
||||
|
@ -103,7 +103,7 @@ def getAttachmentMediaType(filename: str) -> str:
|
|||
return mediaType
|
||||
|
||||
|
||||
def updateEtag(mediaFilename: str) -> None:
|
||||
def _updateEtag(mediaFilename: str) -> None:
|
||||
""" calculate the etag, which is a sha1 of the data
|
||||
"""
|
||||
# only create etags for media
|
||||
|
@ -143,7 +143,7 @@ def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int,
|
|||
Blurhash is optional, since low power systems may take a long
|
||||
time to calculate it
|
||||
"""
|
||||
if not isMedia(imageFilename):
|
||||
if not _isMedia(imageFilename):
|
||||
return postJson
|
||||
|
||||
fileExtension = None
|
||||
|
@ -182,7 +182,7 @@ def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int,
|
|||
if mediaType.startswith('image/'):
|
||||
attachmentJson['focialPoint'] = [0.0, 0.0]
|
||||
if useBlurhash:
|
||||
attachmentJson['blurhash'] = getImageHash(imageFilename)
|
||||
attachmentJson['blurhash'] = _getImageHash(imageFilename)
|
||||
postJson['attachment'] = [attachmentJson]
|
||||
|
||||
if baseDir:
|
||||
|
@ -190,7 +190,7 @@ def attachMedia(baseDir: str, httpPrefix: str, domain: str, port: int,
|
|||
removeMetaData(imageFilename, mediaFilename)
|
||||
else:
|
||||
copyfile(imageFilename, mediaFilename)
|
||||
updateEtag(mediaFilename)
|
||||
_updateEtag(mediaFilename)
|
||||
|
||||
return postJson
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ __status__ = "Production"
|
|||
import os
|
||||
|
||||
|
||||
def migrateFollows(followFilename: str, oldHandle: str,
|
||||
newHandle: str) -> None:
|
||||
def _migrateFollows(followFilename: str, oldHandle: str,
|
||||
newHandle: str) -> None:
|
||||
"""Changes a handle within following or followers list
|
||||
"""
|
||||
if not os.path.isfile(followFilename):
|
||||
|
@ -48,7 +48,7 @@ def migrateAccount(baseDir: str, oldHandle: str, newHandle: str) -> None:
|
|||
if '@' in handle:
|
||||
accountDir = baseDir + '/accounts/' + handle
|
||||
followFilename = accountDir + '/following.txt'
|
||||
migrateFollows(followFilename, oldHandle, newHandle)
|
||||
_migrateFollows(followFilename, oldHandle, newHandle)
|
||||
followFilename = accountDir + '/followers.txt'
|
||||
migrateFollows(followFilename, oldHandle, newHandle)
|
||||
_migrateFollows(followFilename, oldHandle, newHandle)
|
||||
break
|
||||
|
|
108
newsdaemon.py
108
newsdaemon.py
|
@ -35,7 +35,7 @@ from inbox import storeHashTags
|
|||
from session import createSession
|
||||
|
||||
|
||||
def updateFeedsOutboxIndex(baseDir: str, domain: str, postId: str) -> None:
|
||||
def _updateFeedsOutboxIndex(baseDir: str, domain: str, postId: str) -> None:
|
||||
"""Updates the index used for imported RSS feeds
|
||||
"""
|
||||
basePath = baseDir + '/accounts/news@' + domain
|
||||
|
@ -59,7 +59,7 @@ def updateFeedsOutboxIndex(baseDir: str, domain: str, postId: str) -> None:
|
|||
feedsFile.close()
|
||||
|
||||
|
||||
def saveArrivedTime(baseDir: str, postFilename: str, arrived: str) -> None:
|
||||
def _saveArrivedTime(baseDir: str, postFilename: str, arrived: str) -> None:
|
||||
"""Saves the time when an rss post arrived to a file
|
||||
"""
|
||||
arrivedFile = open(postFilename + '.arrived', 'w+')
|
||||
|
@ -68,7 +68,7 @@ def saveArrivedTime(baseDir: str, postFilename: str, arrived: str) -> None:
|
|||
arrivedFile.close()
|
||||
|
||||
|
||||
def removeControlCharacters(content: str) -> str:
|
||||
def _removeControlCharacters(content: str) -> str:
|
||||
"""Remove escaped html
|
||||
"""
|
||||
if '&' in content:
|
||||
|
@ -227,14 +227,14 @@ def hashtagRuleTree(operators: [],
|
|||
return tree
|
||||
|
||||
|
||||
def newswireHashtagProcessing(session, baseDir: str, postJsonObject: {},
|
||||
hashtags: [], httpPrefix: str,
|
||||
domain: str, port: int,
|
||||
personCache: {},
|
||||
cachedWebfingers: {},
|
||||
federationList: [],
|
||||
sendThreads: [], postLog: [],
|
||||
moderated: bool, url: str) -> bool:
|
||||
def _newswireHashtagProcessing(session, baseDir: str, postJsonObject: {},
|
||||
hashtags: [], httpPrefix: str,
|
||||
domain: str, port: int,
|
||||
personCache: {},
|
||||
cachedWebfingers: {},
|
||||
federationList: [],
|
||||
sendThreads: [], postLog: [],
|
||||
moderated: bool, url: str) -> bool:
|
||||
"""Applies hashtag rules to a news post.
|
||||
Returns true if the post should be saved to the news timeline
|
||||
of this instance
|
||||
|
@ -356,9 +356,9 @@ def newswireHashtagProcessing(session, baseDir: str, postJsonObject: {},
|
|||
return True
|
||||
|
||||
|
||||
def createNewsMirror(baseDir: str, domain: str,
|
||||
postIdNumber: str, url: str,
|
||||
maxMirroredArticles: int) -> bool:
|
||||
def _createNewsMirror(baseDir: str, domain: str,
|
||||
postIdNumber: str, url: str,
|
||||
maxMirroredArticles: int) -> bool:
|
||||
"""Creates a local mirror of a news article
|
||||
"""
|
||||
if '|' in url or '>' in url:
|
||||
|
@ -446,17 +446,17 @@ def createNewsMirror(baseDir: str, domain: str,
|
|||
return True
|
||||
|
||||
|
||||
def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
||||
domain: str, port: int,
|
||||
newswire: {},
|
||||
translate: {},
|
||||
recentPostsCache: {}, maxRecentPosts: int,
|
||||
session, cachedWebfingers: {},
|
||||
personCache: {},
|
||||
federationList: [],
|
||||
sendThreads: [], postLog: [],
|
||||
maxMirroredArticles: int,
|
||||
allowLocalNetworkAccess: bool) -> None:
|
||||
def _convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
||||
domain: str, port: int,
|
||||
newswire: {},
|
||||
translate: {},
|
||||
recentPostsCache: {}, maxRecentPosts: int,
|
||||
session, cachedWebfingers: {},
|
||||
personCache: {},
|
||||
federationList: [],
|
||||
sendThreads: [], postLog: [],
|
||||
maxMirroredArticles: int,
|
||||
allowLocalNetworkAccess: bool) -> None:
|
||||
"""Converts rss items in a newswire into posts
|
||||
"""
|
||||
if not newswire:
|
||||
|
@ -497,7 +497,7 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
newswire[originalDateStr][3] = filename
|
||||
continue
|
||||
|
||||
rssTitle = removeControlCharacters(item[0])
|
||||
rssTitle = _removeControlCharacters(item[0])
|
||||
url = item[1]
|
||||
if dangerousMarkup(url, allowLocalNetworkAccess) or \
|
||||
dangerousMarkup(rssTitle, allowLocalNetworkAccess):
|
||||
|
@ -505,7 +505,7 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
rssDescription = ''
|
||||
|
||||
# get the rss description if it exists
|
||||
rssDescription = removeControlCharacters(item[4])
|
||||
rssDescription = _removeControlCharacters(item[4])
|
||||
if rssDescription.startswith('<![CDATA['):
|
||||
rssDescription = rssDescription.replace('<![CDATA[', '')
|
||||
rssDescription = rssDescription.replace(']]>', '')
|
||||
|
@ -555,8 +555,8 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
continue
|
||||
|
||||
if mirrored:
|
||||
if not createNewsMirror(baseDir, domain, statusNumber,
|
||||
url, maxMirroredArticles):
|
||||
if not _createNewsMirror(baseDir, domain, statusNumber,
|
||||
url, maxMirroredArticles):
|
||||
continue
|
||||
|
||||
idStr = \
|
||||
|
@ -590,12 +590,12 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
|
||||
moderated = item[5]
|
||||
|
||||
savePost = newswireHashtagProcessing(session, baseDir, blog, hashtags,
|
||||
httpPrefix, domain, port,
|
||||
personCache, cachedWebfingers,
|
||||
federationList,
|
||||
sendThreads, postLog,
|
||||
moderated, url)
|
||||
savePost = _newswireHashtagProcessing(session, baseDir, blog, hashtags,
|
||||
httpPrefix, domain, port,
|
||||
personCache, cachedWebfingers,
|
||||
federationList,
|
||||
sendThreads, postLog,
|
||||
moderated, url)
|
||||
|
||||
# save the post and update the index
|
||||
if savePost:
|
||||
|
@ -628,7 +628,7 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
blog['object']['content'] = content
|
||||
|
||||
# update the newswire tags if new ones have been found by
|
||||
# newswireHashtagProcessing
|
||||
# _newswireHashtagProcessing
|
||||
for tag in hashtags:
|
||||
if tag not in newswire[originalDateStr][6]:
|
||||
newswire[originalDateStr][6].append(tag)
|
||||
|
@ -637,14 +637,14 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
|
||||
clearFromPostCaches(baseDir, recentPostsCache, postId)
|
||||
if saveJson(blog, filename):
|
||||
updateFeedsOutboxIndex(baseDir, domain, postId + '.json')
|
||||
_updateFeedsOutboxIndex(baseDir, domain, postId + '.json')
|
||||
|
||||
# Save a file containing the time when the post arrived
|
||||
# this can then later be used to construct the news timeline
|
||||
# excluding items during the voting period
|
||||
if moderated:
|
||||
saveArrivedTime(baseDir, filename,
|
||||
blog['object']['arrived'])
|
||||
_saveArrivedTime(baseDir, filename,
|
||||
blog['object']['arrived'])
|
||||
else:
|
||||
if os.path.isfile(filename + '.arrived'):
|
||||
os.remove(filename + '.arrived')
|
||||
|
@ -658,7 +658,7 @@ def convertRSStoActivityPub(baseDir: str, httpPrefix: str,
|
|||
newswire[originalDateStr][3] = filename
|
||||
|
||||
|
||||
def mergeWithPreviousNewswire(oldNewswire: {}, newNewswire: {}) -> None:
|
||||
def _mergeWithPreviousNewswire(oldNewswire: {}, newNewswire: {}) -> None:
|
||||
"""Preserve any votes or generated activitypub post filename
|
||||
as rss feeds are updated
|
||||
"""
|
||||
|
@ -707,26 +707,26 @@ def runNewswireDaemon(baseDir: str, httpd,
|
|||
if os.path.isfile(newswireStateFilename):
|
||||
httpd.newswire = loadJson(newswireStateFilename)
|
||||
|
||||
mergeWithPreviousNewswire(httpd.newswire, newNewswire)
|
||||
_mergeWithPreviousNewswire(httpd.newswire, newNewswire)
|
||||
|
||||
httpd.newswire = newNewswire
|
||||
if newNewswire:
|
||||
saveJson(httpd.newswire, newswireStateFilename)
|
||||
print('Newswire updated')
|
||||
|
||||
convertRSStoActivityPub(baseDir,
|
||||
httpPrefix, domain, port,
|
||||
newNewswire, translate,
|
||||
httpd.recentPostsCache,
|
||||
httpd.maxRecentPosts,
|
||||
httpd.session,
|
||||
httpd.cachedWebfingers,
|
||||
httpd.personCache,
|
||||
httpd.federationList,
|
||||
httpd.sendThreads,
|
||||
httpd.postLog,
|
||||
httpd.maxMirroredArticles,
|
||||
httpd.allowLocalNetworkAccess)
|
||||
_convertRSStoActivityPub(baseDir,
|
||||
httpPrefix, domain, port,
|
||||
newNewswire, translate,
|
||||
httpd.recentPostsCache,
|
||||
httpd.maxRecentPosts,
|
||||
httpd.session,
|
||||
httpd.cachedWebfingers,
|
||||
httpd.personCache,
|
||||
httpd.federationList,
|
||||
httpd.sendThreads,
|
||||
httpd.postLog,
|
||||
httpd.maxMirroredArticles,
|
||||
httpd.allowLocalNetworkAccess)
|
||||
print('Newswire feed converted to ActivityPub')
|
||||
|
||||
if httpd.maxNewsPosts > 0:
|
||||
|
|
248
newswire.py
248
newswire.py
|
@ -29,7 +29,7 @@ from blocking import isBlockedHashtag
|
|||
from filters import isFiltered
|
||||
|
||||
|
||||
def removeCDATA(text: str) -> str:
|
||||
def _removeCDATA(text: str) -> str:
|
||||
"""Removes any CDATA from the given text
|
||||
"""
|
||||
if 'CDATA[' in text:
|
||||
|
@ -95,13 +95,13 @@ def getNewswireTags(text: str, maxTags: int) -> []:
|
|||
return tags
|
||||
|
||||
|
||||
def addNewswireDictEntry(baseDir: str, domain: str,
|
||||
newswire: {}, dateStr: str,
|
||||
title: str, link: str,
|
||||
votesStatus: str, postFilename: str,
|
||||
description: str, moderated: bool,
|
||||
mirrored: bool,
|
||||
tags=[], maxTags=32) -> None:
|
||||
def _addNewswireDictEntry(baseDir: str, domain: str,
|
||||
newswire: {}, dateStr: str,
|
||||
title: str, link: str,
|
||||
votesStatus: str, postFilename: str,
|
||||
description: str, moderated: bool,
|
||||
mirrored: bool,
|
||||
tags=[], maxTags=32) -> None:
|
||||
"""Update the newswire dictionary
|
||||
"""
|
||||
# remove any markup
|
||||
|
@ -143,7 +143,7 @@ def addNewswireDictEntry(baseDir: str, domain: str,
|
|||
]
|
||||
|
||||
|
||||
def validFeedDate(pubDate: str) -> bool:
|
||||
def _validFeedDate(pubDate: str) -> bool:
|
||||
# convert from YY-MM-DD HH:MM:SS+00:00 to
|
||||
# YY-MM-DDTHH:MM:SSZ
|
||||
postDate = pubDate.replace(' ', 'T').replace('+00:00', 'Z')
|
||||
|
@ -219,12 +219,12 @@ def loadHashtagCategories(baseDir: str, language: str) -> None:
|
|||
|
||||
with open(hashtagCategoriesFilename, 'r') as fp:
|
||||
xmlStr = fp.read()
|
||||
xml2StrToHashtagCategories(baseDir, xmlStr, 1024, True)
|
||||
_xml2StrToHashtagCategories(baseDir, xmlStr, 1024, True)
|
||||
|
||||
|
||||
def xml2StrToHashtagCategories(baseDir: str, xmlStr: str,
|
||||
maxCategoriesFeedItemSizeKb: int,
|
||||
force=False) -> None:
|
||||
def _xml2StrToHashtagCategories(baseDir: str, xmlStr: str,
|
||||
maxCategoriesFeedItemSizeKb: int,
|
||||
force=False) -> None:
|
||||
"""Updates hashtag categories based upon an rss feed
|
||||
"""
|
||||
rssItems = xmlStr.split('<item>')
|
||||
|
@ -261,11 +261,11 @@ def xml2StrToHashtagCategories(baseDir: str, xmlStr: str,
|
|||
setHashtagCategory(baseDir, hashtag, categoryStr, force)
|
||||
|
||||
|
||||
def xml2StrToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int,
|
||||
maxCategoriesFeedItemSizeKb: int) -> {}:
|
||||
def _xml2StrToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int,
|
||||
maxCategoriesFeedItemSizeKb: int) -> {}:
|
||||
"""Converts an xml RSS 2.0 string to a dictionary
|
||||
"""
|
||||
if '<item>' not in xmlStr:
|
||||
|
@ -274,8 +274,8 @@ def xml2StrToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
|
||||
# is this an rss feed containing hashtag categories?
|
||||
if '<title>#categories</title>' in xmlStr:
|
||||
xml2StrToHashtagCategories(baseDir, xmlStr,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
_xml2StrToHashtagCategories(baseDir, xmlStr,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
return {}
|
||||
|
||||
rssItems = xmlStr.split('<item>')
|
||||
|
@ -300,17 +300,17 @@ def xml2StrToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
if '</pubDate>' not in rssItem:
|
||||
continue
|
||||
title = rssItem.split('<title>')[1]
|
||||
title = removeCDATA(title.split('</title>')[0])
|
||||
title = _removeCDATA(title.split('</title>')[0])
|
||||
description = ''
|
||||
if '<description>' in rssItem and '</description>' in rssItem:
|
||||
description = rssItem.split('<description>')[1]
|
||||
description = removeCDATA(description.split('</description>')[0])
|
||||
description = _removeCDATA(description.split('</description>')[0])
|
||||
else:
|
||||
if '<media:description>' in rssItem and \
|
||||
'</media:description>' in rssItem:
|
||||
description = rssItem.split('<media:description>')[1]
|
||||
description = description.split('</media:description>')[0]
|
||||
description = removeCDATA(description)
|
||||
description = _removeCDATA(description)
|
||||
link = rssItem.split('<link>')[1]
|
||||
link = link.split('</link>')[0]
|
||||
if '://' not in link:
|
||||
|
@ -325,14 +325,14 @@ def xml2StrToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
|
||||
pubDateStr = parseFeedDate(pubDate)
|
||||
if pubDateStr:
|
||||
if validFeedDate(pubDateStr):
|
||||
if _validFeedDate(pubDateStr):
|
||||
postFilename = ''
|
||||
votesStatus = []
|
||||
addNewswireDictEntry(baseDir, domain,
|
||||
result, pubDateStr,
|
||||
title, link,
|
||||
votesStatus, postFilename,
|
||||
description, moderated, mirrored)
|
||||
_addNewswireDictEntry(baseDir, domain,
|
||||
result, pubDateStr,
|
||||
title, link,
|
||||
votesStatus, postFilename,
|
||||
description, moderated, mirrored)
|
||||
postCtr += 1
|
||||
if postCtr >= maxPostsPerSource:
|
||||
break
|
||||
|
@ -341,11 +341,11 @@ def xml2StrToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
return result
|
||||
|
||||
|
||||
def xml1StrToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int,
|
||||
maxCategoriesFeedItemSizeKb: int) -> {}:
|
||||
def _xml1StrToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int,
|
||||
maxCategoriesFeedItemSizeKb: int) -> {}:
|
||||
"""Converts an xml RSS 1.0 string to a dictionary
|
||||
https://validator.w3.org/feed/docs/rss1.html
|
||||
"""
|
||||
|
@ -356,8 +356,8 @@ def xml1StrToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
|
||||
# is this an rss feed containing hashtag categories?
|
||||
if '<title>#categories</title>' in xmlStr:
|
||||
xml2StrToHashtagCategories(baseDir, xmlStr,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
_xml2StrToHashtagCategories(baseDir, xmlStr,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
return {}
|
||||
|
||||
rssItems = xmlStr.split(itemStr)
|
||||
|
@ -384,17 +384,17 @@ def xml1StrToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
if '</dc:date>' not in rssItem:
|
||||
continue
|
||||
title = rssItem.split('<title>')[1]
|
||||
title = removeCDATA(title.split('</title>')[0])
|
||||
title = _removeCDATA(title.split('</title>')[0])
|
||||
description = ''
|
||||
if '<description>' in rssItem and '</description>' in rssItem:
|
||||
description = rssItem.split('<description>')[1]
|
||||
description = removeCDATA(description.split('</description>')[0])
|
||||
description = _removeCDATA(description.split('</description>')[0])
|
||||
else:
|
||||
if '<media:description>' in rssItem and \
|
||||
'</media:description>' in rssItem:
|
||||
description = rssItem.split('<media:description>')[1]
|
||||
description = description.split('</media:description>')[0]
|
||||
description = removeCDATA(description)
|
||||
description = _removeCDATA(description)
|
||||
link = rssItem.split('<link>')[1]
|
||||
link = link.split('</link>')[0]
|
||||
if '://' not in link:
|
||||
|
@ -409,14 +409,14 @@ def xml1StrToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
|
||||
pubDateStr = parseFeedDate(pubDate)
|
||||
if pubDateStr:
|
||||
if validFeedDate(pubDateStr):
|
||||
if _validFeedDate(pubDateStr):
|
||||
postFilename = ''
|
||||
votesStatus = []
|
||||
addNewswireDictEntry(baseDir, domain,
|
||||
result, pubDateStr,
|
||||
title, link,
|
||||
votesStatus, postFilename,
|
||||
description, moderated, mirrored)
|
||||
_addNewswireDictEntry(baseDir, domain,
|
||||
result, pubDateStr,
|
||||
title, link,
|
||||
votesStatus, postFilename,
|
||||
description, moderated, mirrored)
|
||||
postCtr += 1
|
||||
if postCtr >= maxPostsPerSource:
|
||||
break
|
||||
|
@ -425,10 +425,10 @@ def xml1StrToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
return result
|
||||
|
||||
|
||||
def atomFeedToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int) -> {}:
|
||||
def _atomFeedToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int) -> {}:
|
||||
"""Converts an atom feed string to a dictionary
|
||||
"""
|
||||
if '<entry>' not in xmlStr:
|
||||
|
@ -456,17 +456,17 @@ def atomFeedToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
if '</updated>' not in atomItem:
|
||||
continue
|
||||
title = atomItem.split('<title>')[1]
|
||||
title = removeCDATA(title.split('</title>')[0])
|
||||
title = _removeCDATA(title.split('</title>')[0])
|
||||
description = ''
|
||||
if '<summary>' in atomItem and '</summary>' in atomItem:
|
||||
description = atomItem.split('<summary>')[1]
|
||||
description = removeCDATA(description.split('</summary>')[0])
|
||||
description = _removeCDATA(description.split('</summary>')[0])
|
||||
else:
|
||||
if '<media:description>' in atomItem and \
|
||||
'</media:description>' in atomItem:
|
||||
description = atomItem.split('<media:description>')[1]
|
||||
description = description.split('</media:description>')[0]
|
||||
description = removeCDATA(description)
|
||||
description = _removeCDATA(description)
|
||||
link = atomItem.split('<link>')[1]
|
||||
link = link.split('</link>')[0]
|
||||
if '://' not in link:
|
||||
|
@ -481,14 +481,14 @@ def atomFeedToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
|
||||
pubDateStr = parseFeedDate(pubDate)
|
||||
if pubDateStr:
|
||||
if validFeedDate(pubDateStr):
|
||||
if _validFeedDate(pubDateStr):
|
||||
postFilename = ''
|
||||
votesStatus = []
|
||||
addNewswireDictEntry(baseDir, domain,
|
||||
result, pubDateStr,
|
||||
title, link,
|
||||
votesStatus, postFilename,
|
||||
description, moderated, mirrored)
|
||||
_addNewswireDictEntry(baseDir, domain,
|
||||
result, pubDateStr,
|
||||
title, link,
|
||||
votesStatus, postFilename,
|
||||
description, moderated, mirrored)
|
||||
postCtr += 1
|
||||
if postCtr >= maxPostsPerSource:
|
||||
break
|
||||
|
@ -497,10 +497,10 @@ def atomFeedToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
return result
|
||||
|
||||
|
||||
def atomFeedYTToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int) -> {}:
|
||||
def _atomFeedYTToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int) -> {}:
|
||||
"""Converts an atom-style YouTube feed string to a dictionary
|
||||
"""
|
||||
if '<entry>' not in xmlStr:
|
||||
|
@ -532,17 +532,17 @@ def atomFeedYTToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
if '</yt:videoId>' not in atomItem:
|
||||
continue
|
||||
title = atomItem.split('<title>')[1]
|
||||
title = removeCDATA(title.split('</title>')[0])
|
||||
title = _removeCDATA(title.split('</title>')[0])
|
||||
description = ''
|
||||
if '<media:description>' in atomItem and \
|
||||
'</media:description>' in atomItem:
|
||||
description = atomItem.split('<media:description>')[1]
|
||||
description = description.split('</media:description>')[0]
|
||||
description = removeCDATA(description)
|
||||
description = _removeCDATA(description)
|
||||
elif '<summary>' in atomItem and '</summary>' in atomItem:
|
||||
description = atomItem.split('<summary>')[1]
|
||||
description = description.split('</summary>')[0]
|
||||
description = removeCDATA(description)
|
||||
description = _removeCDATA(description)
|
||||
link = atomItem.split('<yt:videoId>')[1]
|
||||
link = link.split('</yt:videoId>')[0]
|
||||
link = 'https://www.youtube.com/watch?v=' + link.strip()
|
||||
|
@ -551,14 +551,14 @@ def atomFeedYTToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
|
||||
pubDateStr = parseFeedDate(pubDate)
|
||||
if pubDateStr:
|
||||
if validFeedDate(pubDateStr):
|
||||
if _validFeedDate(pubDateStr):
|
||||
postFilename = ''
|
||||
votesStatus = []
|
||||
addNewswireDictEntry(baseDir, domain,
|
||||
result, pubDateStr,
|
||||
title, link,
|
||||
votesStatus, postFilename,
|
||||
description, moderated, mirrored)
|
||||
_addNewswireDictEntry(baseDir, domain,
|
||||
result, pubDateStr,
|
||||
title, link,
|
||||
votesStatus, postFilename,
|
||||
description, moderated, mirrored)
|
||||
postCtr += 1
|
||||
if postCtr >= maxPostsPerSource:
|
||||
break
|
||||
|
@ -567,36 +567,36 @@ def atomFeedYTToDict(baseDir: str, domain: str, xmlStr: str,
|
|||
return result
|
||||
|
||||
|
||||
def xmlStrToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int,
|
||||
maxCategoriesFeedItemSizeKb: int) -> {}:
|
||||
def _xmlStrToDict(baseDir: str, domain: str, xmlStr: str,
|
||||
moderated: bool, mirrored: bool,
|
||||
maxPostsPerSource: int,
|
||||
maxFeedItemSizeKb: int,
|
||||
maxCategoriesFeedItemSizeKb: int) -> {}:
|
||||
"""Converts an xml string to a dictionary
|
||||
"""
|
||||
if '<yt:videoId>' in xmlStr and '<yt:channelId>' in xmlStr:
|
||||
print('YouTube feed: reading')
|
||||
return atomFeedYTToDict(baseDir, domain,
|
||||
xmlStr, moderated, mirrored,
|
||||
maxPostsPerSource, maxFeedItemSizeKb)
|
||||
return _atomFeedYTToDict(baseDir, domain,
|
||||
xmlStr, moderated, mirrored,
|
||||
maxPostsPerSource, maxFeedItemSizeKb)
|
||||
elif 'rss version="2.0"' in xmlStr:
|
||||
return xml2StrToDict(baseDir, domain,
|
||||
xmlStr, moderated, mirrored,
|
||||
maxPostsPerSource, maxFeedItemSizeKb,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
elif '<?xml version="1.0"' in xmlStr:
|
||||
return xml1StrToDict(baseDir, domain,
|
||||
xmlStr, moderated, mirrored,
|
||||
maxPostsPerSource, maxFeedItemSizeKb,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
elif 'xmlns="http://www.w3.org/2005/Atom"' in xmlStr:
|
||||
return atomFeedToDict(baseDir, domain,
|
||||
return _xml2StrToDict(baseDir, domain,
|
||||
xmlStr, moderated, mirrored,
|
||||
maxPostsPerSource, maxFeedItemSizeKb)
|
||||
maxPostsPerSource, maxFeedItemSizeKb,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
elif '<?xml version="1.0"' in xmlStr:
|
||||
return _xml1StrToDict(baseDir, domain,
|
||||
xmlStr, moderated, mirrored,
|
||||
maxPostsPerSource, maxFeedItemSizeKb,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
elif 'xmlns="http://www.w3.org/2005/Atom"' in xmlStr:
|
||||
return _atomFeedToDict(baseDir, domain,
|
||||
xmlStr, moderated, mirrored,
|
||||
maxPostsPerSource, maxFeedItemSizeKb)
|
||||
return {}
|
||||
|
||||
|
||||
def YTchannelToAtomFeed(url: str) -> str:
|
||||
def _YTchannelToAtomFeed(url: str) -> str:
|
||||
"""Converts a YouTube channel url into an atom feed url
|
||||
"""
|
||||
if 'youtube.com/channel/' not in url:
|
||||
|
@ -633,17 +633,17 @@ def getRSS(baseDir: str, domain: str, session, url: str,
|
|||
'Mozilla/5.0 (X11; Linux x86_64; rv:81.0) Gecko/20100101 Firefox/81.0'
|
||||
if not session:
|
||||
print('WARN: no session specified for getRSS')
|
||||
url = YTchannelToAtomFeed(url)
|
||||
url = _YTchannelToAtomFeed(url)
|
||||
try:
|
||||
result = session.get(url, headers=sessionHeaders, params=sessionParams)
|
||||
if result:
|
||||
if int(len(result.text) / 1024) < maxFeedSizeKb and \
|
||||
not containsInvalidChars(result.text):
|
||||
return xmlStrToDict(baseDir, domain, result.text,
|
||||
moderated, mirrored,
|
||||
maxPostsPerSource,
|
||||
maxFeedItemSizeKb,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
return _xmlStrToDict(baseDir, domain, result.text,
|
||||
moderated, mirrored,
|
||||
maxPostsPerSource,
|
||||
maxFeedItemSizeKb,
|
||||
maxCategoriesFeedItemSizeKb)
|
||||
else:
|
||||
print('WARN: feed is too large, ' +
|
||||
'or contains invalid characters: ' + url)
|
||||
|
@ -692,7 +692,7 @@ def getRSSfromDict(baseDir: str, newswire: {},
|
|||
continue
|
||||
rssStr += '<item>\n'
|
||||
rssStr += ' <title>' + fields[0] + '</title>\n'
|
||||
description = removeCDATA(firstParagraphFromString(fields[4]))
|
||||
description = _removeCDATA(firstParagraphFromString(fields[4]))
|
||||
rssStr += ' <description>' + description + '</description>\n'
|
||||
url = fields[1]
|
||||
if '://' not in url:
|
||||
|
@ -707,7 +707,7 @@ def getRSSfromDict(baseDir: str, newswire: {},
|
|||
return rssStr
|
||||
|
||||
|
||||
def isNewswireBlogPost(postJsonObject: {}) -> bool:
|
||||
def _isNewswireBlogPost(postJsonObject: {}) -> bool:
|
||||
"""Is the given object a blog post?
|
||||
There isn't any difference between a blog post and a newswire blog post
|
||||
but we may here need to check for different properties than
|
||||
|
@ -727,7 +727,7 @@ def isNewswireBlogPost(postJsonObject: {}) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def getHashtagsFromPost(postJsonObject: {}) -> []:
|
||||
def _getHashtagsFromPost(postJsonObject: {}) -> []:
|
||||
"""Returns a list of any hashtags within a post
|
||||
"""
|
||||
if not postJsonObject.get('object'):
|
||||
|
@ -753,11 +753,11 @@ def getHashtagsFromPost(postJsonObject: {}) -> []:
|
|||
return tags
|
||||
|
||||
|
||||
def addAccountBlogsToNewswire(baseDir: str, nickname: str, domain: str,
|
||||
newswire: {},
|
||||
maxBlogsPerAccount: int,
|
||||
indexFilename: str,
|
||||
maxTags: int) -> None:
|
||||
def _addAccountBlogsToNewswire(baseDir: str, nickname: str, domain: str,
|
||||
newswire: {},
|
||||
maxBlogsPerAccount: int,
|
||||
indexFilename: str,
|
||||
maxTags: int) -> None:
|
||||
"""Adds blogs for the given account to the newswire
|
||||
"""
|
||||
if not os.path.isfile(indexFilename):
|
||||
|
@ -803,7 +803,7 @@ def addAccountBlogsToNewswire(baseDir: str, nickname: str, domain: str,
|
|||
postJsonObject = None
|
||||
if fullPostFilename:
|
||||
postJsonObject = loadJson(fullPostFilename)
|
||||
if isNewswireBlogPost(postJsonObject):
|
||||
if _isNewswireBlogPost(postJsonObject):
|
||||
published = postJsonObject['object']['published']
|
||||
published = published.replace('T', ' ')
|
||||
published = published.replace('Z', '+00:00')
|
||||
|
@ -812,24 +812,24 @@ def addAccountBlogsToNewswire(baseDir: str, nickname: str, domain: str,
|
|||
votes = loadJson(fullPostFilename + '.votes')
|
||||
content = postJsonObject['object']['content']
|
||||
description = firstParagraphFromString(content)
|
||||
description = removeCDATA(description)
|
||||
addNewswireDictEntry(baseDir, domain,
|
||||
newswire, published,
|
||||
postJsonObject['object']['summary'],
|
||||
postJsonObject['object']['url'],
|
||||
votes, fullPostFilename,
|
||||
description, moderated, False,
|
||||
getHashtagsFromPost(postJsonObject),
|
||||
maxTags)
|
||||
description = _removeCDATA(description)
|
||||
_addNewswireDictEntry(baseDir, domain,
|
||||
newswire, published,
|
||||
postJsonObject['object']['summary'],
|
||||
postJsonObject['object']['url'],
|
||||
votes, fullPostFilename,
|
||||
description, moderated, False,
|
||||
_getHashtagsFromPost(postJsonObject),
|
||||
maxTags)
|
||||
|
||||
ctr += 1
|
||||
if ctr >= maxBlogsPerAccount:
|
||||
break
|
||||
|
||||
|
||||
def addBlogsToNewswire(baseDir: str, domain: str, newswire: {},
|
||||
maxBlogsPerAccount: int,
|
||||
maxTags: int) -> None:
|
||||
def _addBlogsToNewswire(baseDir: str, domain: str, newswire: {},
|
||||
maxBlogsPerAccount: int,
|
||||
maxTags: int) -> None:
|
||||
"""Adds blogs from each user account into the newswire
|
||||
"""
|
||||
moderationDict = {}
|
||||
|
@ -857,9 +857,9 @@ def addBlogsToNewswire(baseDir: str, domain: str, newswire: {},
|
|||
blogsIndex = accountDir + '/tlblogs.index'
|
||||
if os.path.isfile(blogsIndex):
|
||||
domain = handle.split('@')[1]
|
||||
addAccountBlogsToNewswire(baseDir, nickname, domain,
|
||||
newswire, maxBlogsPerAccount,
|
||||
blogsIndex, maxTags)
|
||||
_addAccountBlogsToNewswire(baseDir, nickname, domain,
|
||||
newswire, maxBlogsPerAccount,
|
||||
blogsIndex, maxTags)
|
||||
break
|
||||
|
||||
# sort the moderation dict into chronological order, latest first
|
||||
|
@ -926,8 +926,8 @@ def getDictFromNewswire(session, baseDir: str, domain: str,
|
|||
result[dateStr] = item
|
||||
|
||||
# add blogs from each user account
|
||||
addBlogsToNewswire(baseDir, domain, result,
|
||||
maxPostsPerSource, maxTags)
|
||||
_addBlogsToNewswire(baseDir, domain, result,
|
||||
maxPostsPerSource, maxTags)
|
||||
|
||||
# sort into chronological order, latest first
|
||||
sortedResult = OrderedDict(sorted(result.items(), reverse=True))
|
||||
|
|
34
person.py
34
person.py
|
@ -134,7 +134,7 @@ def setOrganizationScheme(baseDir: str, nickname: str, domain: str,
|
|||
return True
|
||||
|
||||
|
||||
def accountExists(baseDir: str, nickname: str, domain: str) -> bool:
|
||||
def _accountExists(baseDir: str, nickname: str, domain: str) -> bool:
|
||||
"""Returns true if the given account exists
|
||||
"""
|
||||
if ':' in domain:
|
||||
|
@ -201,10 +201,10 @@ def getDefaultPersonContext() -> str:
|
|||
}
|
||||
|
||||
|
||||
def createPersonBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||
httpPrefix: str, saveToFile: bool,
|
||||
manualFollowerApproval: bool,
|
||||
password=None) -> (str, str, {}, {}):
|
||||
def _createPersonBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||
httpPrefix: str, saveToFile: bool,
|
||||
manualFollowerApproval: bool,
|
||||
password=None) -> (str, str, {}, {}):
|
||||
"""Returns the private key, public key, actor and webfinger endpoint
|
||||
"""
|
||||
privateKeyPem, publicKeyPem = generateRSAKey()
|
||||
|
@ -377,7 +377,7 @@ def registerAccount(baseDir: str, httpPrefix: str, domain: str, port: int,
|
|||
manualFollowerApproval: bool) -> bool:
|
||||
"""Registers a new account from the web interface
|
||||
"""
|
||||
if accountExists(baseDir, nickname, domain):
|
||||
if _accountExists(baseDir, nickname, domain):
|
||||
return False
|
||||
if not validNickname(domain, nickname):
|
||||
print('REGISTER: Nickname ' + nickname + ' is invalid')
|
||||
|
@ -449,12 +449,12 @@ def createPerson(baseDir: str, nickname: str, domain: str, port: int,
|
|||
return None, None, None, None
|
||||
|
||||
(privateKeyPem, publicKeyPem,
|
||||
newPerson, webfingerEndpoint) = createPersonBase(baseDir, nickname,
|
||||
domain, port,
|
||||
httpPrefix,
|
||||
saveToFile,
|
||||
manualFollowerApproval,
|
||||
password)
|
||||
newPerson, webfingerEndpoint) = _createPersonBase(baseDir, nickname,
|
||||
domain, port,
|
||||
httpPrefix,
|
||||
saveToFile,
|
||||
manualFollowerApproval,
|
||||
password)
|
||||
if not getConfigParam(baseDir, 'admin'):
|
||||
if nickname != 'news':
|
||||
# print(nickname+' becomes the instance admin and a moderator')
|
||||
|
@ -525,8 +525,8 @@ def createSharedInbox(baseDir: str, nickname: str, domain: str, port: int,
|
|||
httpPrefix: str) -> (str, str, {}, {}):
|
||||
"""Generates the shared inbox
|
||||
"""
|
||||
return createPersonBase(baseDir, nickname, domain, port, httpPrefix,
|
||||
True, True, None)
|
||||
return _createPersonBase(baseDir, nickname, domain, port, httpPrefix,
|
||||
True, True, None)
|
||||
|
||||
|
||||
def createNewsInbox(baseDir: str, domain: str, port: int,
|
||||
|
@ -845,8 +845,8 @@ def canRemovePost(baseDir: str, nickname: str,
|
|||
return True
|
||||
|
||||
|
||||
def removeTagsForNickname(baseDir: str, nickname: str,
|
||||
domain: str, port: int) -> None:
|
||||
def _removeTagsForNickname(baseDir: str, nickname: str,
|
||||
domain: str, port: int) -> None:
|
||||
"""Removes tags for a nickname
|
||||
"""
|
||||
if not os.path.isdir(baseDir + '/tags'):
|
||||
|
@ -900,7 +900,7 @@ def removeAccount(baseDir: str, nickname: str,
|
|||
unsuspendAccount(baseDir, nickname)
|
||||
handle = nickname + '@' + domain
|
||||
removePassword(baseDir, nickname)
|
||||
removeTagsForNickname(baseDir, nickname, domain, port)
|
||||
_removeTagsForNickname(baseDir, nickname, domain, port)
|
||||
if os.path.isdir(baseDir + '/deactivated/' + handle):
|
||||
shutil.rmtree(baseDir + '/deactivated/' + handle)
|
||||
if os.path.isdir(baseDir + '/accounts/' + handle):
|
||||
|
|
|
@ -77,8 +77,8 @@ def getPetName(baseDir: str, nickname: str, domain: str,
|
|||
return ''
|
||||
|
||||
|
||||
def getPetNameHandle(baseDir: str, nickname: str, domain: str,
|
||||
petname: str) -> str:
|
||||
def _getPetNameHandle(baseDir: str, nickname: str, domain: str,
|
||||
petname: str) -> str:
|
||||
"""Given a petname returns the handle
|
||||
"""
|
||||
if petname.startswith('@'):
|
||||
|
@ -113,7 +113,7 @@ def resolvePetnames(baseDir: str, nickname: str, domain: str,
|
|||
if not wrd.startswith('@'):
|
||||
break
|
||||
# does a petname handle exist for this?
|
||||
handle = getPetNameHandle(baseDir, nickname, domain, wrd)
|
||||
handle = _getPetNameHandle(baseDir, nickname, domain, wrd)
|
||||
if not handle:
|
||||
continue
|
||||
# replace the petname with the handle
|
||||
|
|
565
posts.py
565
posts.py
|
@ -116,8 +116,8 @@ def noOfFollowersOnDomain(baseDir: str, handle: str,
|
|||
return ctr
|
||||
|
||||
|
||||
def getPersonKey(nickname: str, domain: str, baseDir: str, keyType='public',
|
||||
debug=False):
|
||||
def _getPersonKey(nickname: str, domain: str, baseDir: str, keyType='public',
|
||||
debug=False):
|
||||
"""Returns the public or private key of a person
|
||||
"""
|
||||
handle = nickname + '@' + domain
|
||||
|
@ -136,7 +136,7 @@ def getPersonKey(nickname: str, domain: str, baseDir: str, keyType='public',
|
|||
return keyPem
|
||||
|
||||
|
||||
def cleanHtml(rawHtml: str) -> str:
|
||||
def _cleanHtml(rawHtml: str) -> str:
|
||||
# text=BeautifulSoup(rawHtml, 'html.parser').get_text()
|
||||
text = rawHtml
|
||||
return html.unescape(text)
|
||||
|
@ -288,14 +288,14 @@ def getPersonBox(baseDir: str, session, wfRequest: {},
|
|||
avatarUrl, displayName
|
||||
|
||||
|
||||
def getPosts(session, outboxUrl: str, maxPosts: int,
|
||||
maxMentions: int,
|
||||
maxEmoji: int, maxAttachments: int,
|
||||
federationList: [],
|
||||
personCache: {}, raw: bool,
|
||||
simple: bool, debug: bool,
|
||||
projectVersion: str, httpPrefix: str,
|
||||
domain: str) -> {}:
|
||||
def _getPosts(session, outboxUrl: str, maxPosts: int,
|
||||
maxMentions: int,
|
||||
maxEmoji: int, maxAttachments: int,
|
||||
federationList: [],
|
||||
personCache: {}, raw: bool,
|
||||
simple: bool, debug: bool,
|
||||
projectVersion: str, httpPrefix: str,
|
||||
domain: str) -> {}:
|
||||
"""Gets public posts from an outbox
|
||||
"""
|
||||
personPosts = {}
|
||||
|
@ -445,7 +445,7 @@ def getPosts(session, outboxUrl: str, maxPosts: int,
|
|||
sensitive = item['object']['sensitive']
|
||||
|
||||
if simple:
|
||||
print(cleanHtml(content) + '\n')
|
||||
print(_cleanHtml(content) + '\n')
|
||||
else:
|
||||
pprint(item)
|
||||
personPosts[item['id']] = {
|
||||
|
@ -453,7 +453,7 @@ def getPosts(session, outboxUrl: str, maxPosts: int,
|
|||
"inreplyto": inReplyTo,
|
||||
"summary": summary,
|
||||
"html": content,
|
||||
"plaintext": cleanHtml(content),
|
||||
"plaintext": _cleanHtml(content),
|
||||
"attachment": attachment,
|
||||
"mentions": mentions,
|
||||
"emoji": emoji,
|
||||
|
@ -519,15 +519,15 @@ def getPostDomains(session, outboxUrl: str, maxPosts: int,
|
|||
return postDomains
|
||||
|
||||
|
||||
def getPostsForBlockedDomains(baseDir: str,
|
||||
session, outboxUrl: str, maxPosts: int,
|
||||
maxMentions: int,
|
||||
maxEmoji: int, maxAttachments: int,
|
||||
federationList: [],
|
||||
personCache: {},
|
||||
debug: bool,
|
||||
projectVersion: str, httpPrefix: str,
|
||||
domain: str) -> {}:
|
||||
def _getPostsForBlockedDomains(baseDir: str,
|
||||
session, outboxUrl: str, maxPosts: int,
|
||||
maxMentions: int,
|
||||
maxEmoji: int, maxAttachments: int,
|
||||
federationList: [],
|
||||
personCache: {},
|
||||
debug: bool,
|
||||
projectVersion: str, httpPrefix: str,
|
||||
domain: str) -> {}:
|
||||
"""Returns a dictionary of posts for blocked domains
|
||||
"""
|
||||
if not outboxUrl:
|
||||
|
@ -643,7 +643,7 @@ def savePostToBox(baseDir: str, httpPrefix: str, postId: str,
|
|||
return filename
|
||||
|
||||
|
||||
def updateHashtagsIndex(baseDir: str, tag: {}, newPostId: str) -> None:
|
||||
def _updateHashtagsIndex(baseDir: str, tag: {}, newPostId: str) -> None:
|
||||
"""Writes the post url for hashtags to a file
|
||||
This allows posts for a hashtag to be quickly looked up
|
||||
"""
|
||||
|
@ -677,8 +677,8 @@ def updateHashtagsIndex(baseDir: str, tag: {}, newPostId: str) -> None:
|
|||
tagsFilename + ' ' + str(e))
|
||||
|
||||
|
||||
def addSchedulePost(baseDir: str, nickname: str, domain: str,
|
||||
eventDateStr: str, postId: str) -> None:
|
||||
def _addSchedulePost(baseDir: str, nickname: str, domain: str,
|
||||
eventDateStr: str, postId: str) -> None:
|
||||
"""Adds a scheduled post to the index
|
||||
"""
|
||||
handle = nickname + '@' + domain
|
||||
|
@ -703,18 +703,18 @@ def addSchedulePost(baseDir: str, nickname: str, domain: str,
|
|||
scheduleFile.close()
|
||||
|
||||
|
||||
def appendEventFields(newPost: {},
|
||||
eventUUID: str, eventStatus: str,
|
||||
anonymousParticipationEnabled: bool,
|
||||
repliesModerationOption: str,
|
||||
category: str,
|
||||
joinMode: str,
|
||||
eventDateStr: str,
|
||||
endDateStr: str,
|
||||
location: str,
|
||||
maximumAttendeeCapacity: int,
|
||||
ticketUrl: str,
|
||||
subject: str) -> None:
|
||||
def _appendEventFields(newPost: {},
|
||||
eventUUID: str, eventStatus: str,
|
||||
anonymousParticipationEnabled: bool,
|
||||
repliesModerationOption: str,
|
||||
category: str,
|
||||
joinMode: str,
|
||||
eventDateStr: str,
|
||||
endDateStr: str,
|
||||
location: str,
|
||||
maximumAttendeeCapacity: int,
|
||||
ticketUrl: str,
|
||||
subject: str) -> None:
|
||||
"""Appends Mobilizon-type event fields to a post
|
||||
"""
|
||||
if not eventUUID:
|
||||
|
@ -758,7 +758,7 @@ def validContentWarning(cw: str) -> str:
|
|||
return cw
|
||||
|
||||
|
||||
def loadAutoCW(baseDir: str, nickname: str, domain: str) -> []:
|
||||
def _loadAutoCW(baseDir: str, nickname: str, domain: str) -> []:
|
||||
"""Loads automatic CWs file and returns a list containing
|
||||
the lines of the file
|
||||
"""
|
||||
|
@ -771,13 +771,13 @@ def loadAutoCW(baseDir: str, nickname: str, domain: str) -> []:
|
|||
return []
|
||||
|
||||
|
||||
def addAutoCW(baseDir: str, nickname: str, domain: str,
|
||||
subject: str, content: str) -> str:
|
||||
def _addAutoCW(baseDir: str, nickname: str, domain: str,
|
||||
subject: str, content: str) -> str:
|
||||
"""Appends any automatic CW to the subject line
|
||||
and returns the new subject line
|
||||
"""
|
||||
newSubject = subject
|
||||
autoCWList = loadAutoCW(baseDir, nickname, domain)
|
||||
autoCWList = _loadAutoCW(baseDir, nickname, domain)
|
||||
for cwRule in autoCWList:
|
||||
if '->' not in cwRule:
|
||||
continue
|
||||
|
@ -793,26 +793,26 @@ def addAutoCW(baseDir: str, nickname: str, domain: str,
|
|||
return newSubject
|
||||
|
||||
|
||||
def createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||
toUrl: str, ccUrl: str, httpPrefix: str, content: str,
|
||||
followersOnly: bool, saveToFile: bool, clientToServer: bool,
|
||||
commentsEnabled: bool,
|
||||
attachImageFilename: str,
|
||||
mediaType: str, imageDescription: str,
|
||||
useBlurhash: bool, isModerationReport: bool,
|
||||
isArticle: bool,
|
||||
inReplyTo=None,
|
||||
inReplyToAtomUri=None, subject=None, schedulePost=False,
|
||||
eventDate=None, eventTime=None, location=None,
|
||||
eventUUID=None, category=None, joinMode=None,
|
||||
endDate=None, endTime=None,
|
||||
maximumAttendeeCapacity=None,
|
||||
repliesModerationOption=None,
|
||||
anonymousParticipationEnabled=None,
|
||||
eventStatus=None, ticketUrl=None) -> {}:
|
||||
def _createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
||||
toUrl: str, ccUrl: str, httpPrefix: str, content: str,
|
||||
followersOnly: bool, saveToFile: bool,
|
||||
clientToServer: bool, commentsEnabled: bool,
|
||||
attachImageFilename: str,
|
||||
mediaType: str, imageDescription: str,
|
||||
useBlurhash: bool, isModerationReport: bool,
|
||||
isArticle: bool,
|
||||
inReplyTo=None,
|
||||
inReplyToAtomUri=None, subject=None, schedulePost=False,
|
||||
eventDate=None, eventTime=None, location=None,
|
||||
eventUUID=None, category=None, joinMode=None,
|
||||
endDate=None, endTime=None,
|
||||
maximumAttendeeCapacity=None,
|
||||
repliesModerationOption=None,
|
||||
anonymousParticipationEnabled=None,
|
||||
eventStatus=None, ticketUrl=None) -> {}:
|
||||
"""Creates a message
|
||||
"""
|
||||
subject = addAutoCW(baseDir, nickname, domain, subject, content)
|
||||
subject = _addAutoCW(baseDir, nickname, domain, subject, content)
|
||||
|
||||
if nickname != 'news':
|
||||
mentionedRecipients = \
|
||||
|
@ -885,7 +885,7 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
|||
if not tagExists(tag['type'], tag['name'], tags):
|
||||
tags.append(tag)
|
||||
if isPublic:
|
||||
updateHashtagsIndex(baseDir, tag, newPostId)
|
||||
_updateHashtagsIndex(baseDir, tag, newPostId)
|
||||
print('Content tags: ' + str(tags))
|
||||
|
||||
if inReplyTo and not sensitive:
|
||||
|
@ -1031,13 +1031,13 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
|||
attachMedia(baseDir, httpPrefix, domain, port,
|
||||
newPost['object'], attachImageFilename,
|
||||
mediaType, imageDescription, useBlurhash)
|
||||
appendEventFields(newPost['object'], eventUUID, eventStatus,
|
||||
anonymousParticipationEnabled,
|
||||
repliesModerationOption,
|
||||
category, joinMode,
|
||||
eventDateStr, endDateStr,
|
||||
location, maximumAttendeeCapacity,
|
||||
ticketUrl, subject)
|
||||
_appendEventFields(newPost['object'], eventUUID, eventStatus,
|
||||
anonymousParticipationEnabled,
|
||||
repliesModerationOption,
|
||||
category, joinMode,
|
||||
eventDateStr, endDateStr,
|
||||
location, maximumAttendeeCapacity,
|
||||
ticketUrl, subject)
|
||||
else:
|
||||
idStr = \
|
||||
httpPrefix + '://' + domain + '/users/' + nickname + \
|
||||
|
@ -1079,13 +1079,13 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
|||
attachMedia(baseDir, httpPrefix, domain, port,
|
||||
newPost, attachImageFilename,
|
||||
mediaType, imageDescription, useBlurhash)
|
||||
appendEventFields(newPost, eventUUID, eventStatus,
|
||||
anonymousParticipationEnabled,
|
||||
repliesModerationOption,
|
||||
category, joinMode,
|
||||
eventDateStr, endDateStr,
|
||||
location, maximumAttendeeCapacity,
|
||||
ticketUrl, subject)
|
||||
_appendEventFields(newPost, eventUUID, eventStatus,
|
||||
anonymousParticipationEnabled,
|
||||
repliesModerationOption,
|
||||
category, joinMode,
|
||||
eventDateStr, endDateStr,
|
||||
location, maximumAttendeeCapacity,
|
||||
ticketUrl, subject)
|
||||
if ccUrl:
|
||||
if len(ccUrl) > 0:
|
||||
newPost['cc'] = [ccUrl]
|
||||
|
@ -1123,7 +1123,8 @@ def createPostBase(baseDir: str, nickname: str, domain: str, port: int,
|
|||
if schedulePost:
|
||||
if eventDate and eventTime:
|
||||
# add an item to the scheduled post index file
|
||||
addSchedulePost(baseDir, nickname, domain, eventDateStr, newPostId)
|
||||
_addSchedulePost(baseDir, nickname, domain,
|
||||
eventDateStr, newPostId)
|
||||
savePostToBox(baseDir, httpPrefix, newPostId,
|
||||
nickname, domain, newPost, 'scheduled')
|
||||
else:
|
||||
|
@ -1179,10 +1180,10 @@ def outboxMessageCreateWrap(httpPrefix: str,
|
|||
return newPost
|
||||
|
||||
|
||||
def postIsAddressedToFollowers(baseDir: str,
|
||||
nickname: str, domain: str, port: int,
|
||||
httpPrefix: str,
|
||||
postJsonObject: {}) -> bool:
|
||||
def _postIsAddressedToFollowers(baseDir: str,
|
||||
nickname: str, domain: str, port: int,
|
||||
httpPrefix: str,
|
||||
postJsonObject: {}) -> bool:
|
||||
"""Returns true if the given post is addressed to followers of the nickname
|
||||
"""
|
||||
domainFull = getFullDomain(domain, port)
|
||||
|
@ -1227,18 +1228,18 @@ def createPublicPost(baseDir: str,
|
|||
"""Public post
|
||||
"""
|
||||
domainFull = getFullDomain(domain, port)
|
||||
return createPostBase(baseDir, nickname, domain, port,
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
httpPrefix + '://' + domainFull + '/users/' +
|
||||
nickname + '/followers',
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, inReplyTo, inReplyToAtomUri, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
None, None, None, None, None,
|
||||
None, None, None, None, None)
|
||||
return _createPostBase(baseDir, nickname, domain, port,
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
httpPrefix + '://' + domainFull + '/users/' +
|
||||
nickname + '/followers',
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, inReplyTo, inReplyToAtomUri, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
None, None, None, None, None,
|
||||
None, None, None, None, None)
|
||||
|
||||
|
||||
def createBlogPost(baseDir: str,
|
||||
|
@ -1328,18 +1329,18 @@ def createQuestionPost(baseDir: str,
|
|||
"""
|
||||
domainFull = getFullDomain(domain, port)
|
||||
messageJson = \
|
||||
createPostBase(baseDir, nickname, domain, port,
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
httpPrefix + '://' + domainFull + '/users/' +
|
||||
nickname + '/followers',
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, None, None, subject,
|
||||
False, None, None, None, None, None,
|
||||
None, None, None,
|
||||
None, None, None, None, None)
|
||||
_createPostBase(baseDir, nickname, domain, port,
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
httpPrefix + '://' + domainFull + '/users/' +
|
||||
nickname + '/followers',
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, None, None, subject,
|
||||
False, None, None, None, None, None,
|
||||
None, None, None,
|
||||
None, None, None, None, None)
|
||||
messageJson['object']['type'] = 'Question'
|
||||
messageJson['object']['oneOf'] = []
|
||||
messageJson['object']['votersCount'] = 0
|
||||
|
@ -1373,18 +1374,18 @@ def createUnlistedPost(baseDir: str,
|
|||
"""Unlisted post. This has the #Public and followers links inverted.
|
||||
"""
|
||||
domainFull = getFullDomain(domain, port)
|
||||
return createPostBase(baseDir, nickname, domain, port,
|
||||
httpPrefix + '://' + domainFull + '/users/' +
|
||||
nickname + '/followers',
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, inReplyTo, inReplyToAtomUri, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
None, None, None, None, None,
|
||||
None, None, None, None, None)
|
||||
return _createPostBase(baseDir, nickname, domain, port,
|
||||
httpPrefix + '://' + domainFull + '/users/' +
|
||||
nickname + '/followers',
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, inReplyTo, inReplyToAtomUri, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
None, None, None, None, None,
|
||||
None, None, None, None, None)
|
||||
|
||||
|
||||
def createFollowersOnlyPost(baseDir: str,
|
||||
|
@ -1402,18 +1403,18 @@ def createFollowersOnlyPost(baseDir: str,
|
|||
"""Followers only post
|
||||
"""
|
||||
domainFull = getFullDomain(domain, port)
|
||||
return createPostBase(baseDir, nickname, domain, port,
|
||||
httpPrefix + '://' + domainFull + '/users/' +
|
||||
nickname + '/followers',
|
||||
None,
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, inReplyTo, inReplyToAtomUri, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
None, None, None, None, None,
|
||||
None, None, None, None, None)
|
||||
return _createPostBase(baseDir, nickname, domain, port,
|
||||
httpPrefix + '://' + domainFull + '/users/' +
|
||||
nickname + '/followers',
|
||||
None,
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, inReplyTo, inReplyToAtomUri, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
None, None, None, None, None,
|
||||
None, None, None, None, None)
|
||||
|
||||
|
||||
def createEventPost(baseDir: str,
|
||||
|
@ -1451,19 +1452,19 @@ def createEventPost(baseDir: str,
|
|||
if followersOnly:
|
||||
toStr1 = toStr2
|
||||
toStr2 = None
|
||||
return createPostBase(baseDir, nickname, domain, port,
|
||||
toStr1, toStr2,
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, None, None, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
eventUUID, category, joinMode,
|
||||
endDate, endTime, maximumAttendeeCapacity,
|
||||
repliesModerationOption,
|
||||
anonymousParticipationEnabled,
|
||||
eventStatus, ticketUrl)
|
||||
return _createPostBase(baseDir, nickname, domain, port,
|
||||
toStr1, toStr2,
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, None, None, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
eventUUID, category, joinMode,
|
||||
endDate, endTime, maximumAttendeeCapacity,
|
||||
repliesModerationOption,
|
||||
anonymousParticipationEnabled,
|
||||
eventStatus, ticketUrl)
|
||||
|
||||
|
||||
def getMentionedPeople(baseDir: str, httpPrefix: str,
|
||||
|
@ -1526,16 +1527,16 @@ def createDirectMessagePost(baseDir: str,
|
|||
postTo = None
|
||||
postCc = None
|
||||
messageJson = \
|
||||
createPostBase(baseDir, nickname, domain, port,
|
||||
postTo, postCc,
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, inReplyTo, inReplyToAtomUri, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
None, None, None, None, None,
|
||||
None, None, None, None, None)
|
||||
_createPostBase(baseDir, nickname, domain, port,
|
||||
postTo, postCc,
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, False, inReplyTo, inReplyToAtomUri, subject,
|
||||
schedulePost, eventDate, eventTime, location,
|
||||
None, None, None, None, None,
|
||||
None, None, None, None, None)
|
||||
# mentioned recipients go into To rather than Cc
|
||||
messageJson['to'] = messageJson['object']['cc']
|
||||
messageJson['object']['to'] = messageJson['to']
|
||||
|
@ -1616,16 +1617,16 @@ def createReportPost(baseDir: str,
|
|||
handle = toNickname + '@' + domain
|
||||
|
||||
postJsonObject = \
|
||||
createPostBase(baseDir, nickname, domain, port,
|
||||
toUrl, postCc,
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
True, False, None, None, subject,
|
||||
False, None, None, None, None, None,
|
||||
None, None, None,
|
||||
None, None, None, None, None)
|
||||
_createPostBase(baseDir, nickname, domain, port,
|
||||
toUrl, postCc,
|
||||
httpPrefix, content, followersOnly, saveToFile,
|
||||
clientToServer, commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
True, False, None, None, subject,
|
||||
False, None, None, None, None, None,
|
||||
None, None, None,
|
||||
None, None, None, None, None)
|
||||
if not postJsonObject:
|
||||
continue
|
||||
|
||||
|
@ -1766,20 +1767,20 @@ def sendPost(projectVersion: str,
|
|||
# sharedInbox is optional
|
||||
|
||||
postJsonObject = \
|
||||
createPostBase(baseDir, nickname, domain, port,
|
||||
toPersonId, cc, httpPrefix, content,
|
||||
followersOnly, saveToFile, clientToServer,
|
||||
commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, isArticle, inReplyTo,
|
||||
inReplyToAtomUri, subject,
|
||||
False, None, None, None, None, None,
|
||||
None, None, None,
|
||||
None, None, None, None, None)
|
||||
_createPostBase(baseDir, nickname, domain, port,
|
||||
toPersonId, cc, httpPrefix, content,
|
||||
followersOnly, saveToFile, clientToServer,
|
||||
commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, isArticle, inReplyTo,
|
||||
inReplyToAtomUri, subject,
|
||||
False, None, None, None, None, None,
|
||||
None, None, None,
|
||||
None, None, None, None, None)
|
||||
|
||||
# get the senders private key
|
||||
privateKeyPem = getPersonKey(nickname, domain, baseDir, 'private')
|
||||
privateKeyPem = _getPersonKey(nickname, domain, baseDir, 'private')
|
||||
if len(privateKeyPem) == 0:
|
||||
return 6
|
||||
|
||||
|
@ -1902,18 +1903,18 @@ def sendPostViaServer(projectVersion: str,
|
|||
'/users/' + toNickname
|
||||
|
||||
postJsonObject = \
|
||||
createPostBase(baseDir,
|
||||
fromNickname, fromDomain, fromPort,
|
||||
toPersonId, cc, httpPrefix, content,
|
||||
followersOnly, saveToFile, clientToServer,
|
||||
commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, isArticle, inReplyTo,
|
||||
inReplyToAtomUri, subject,
|
||||
False, None, None, None, None, None,
|
||||
None, None, None,
|
||||
None, None, None, None, None)
|
||||
_createPostBase(baseDir,
|
||||
fromNickname, fromDomain, fromPort,
|
||||
toPersonId, cc, httpPrefix, content,
|
||||
followersOnly, saveToFile, clientToServer,
|
||||
commentsEnabled,
|
||||
attachImageFilename, mediaType,
|
||||
imageDescription, useBlurhash,
|
||||
False, isArticle, inReplyTo,
|
||||
inReplyToAtomUri, subject,
|
||||
False, None, None, None, None, None,
|
||||
None, None, None,
|
||||
None, None, None, None, None)
|
||||
|
||||
authHeader = createBasicAuthHeader(fromNickname, password)
|
||||
|
||||
|
@ -1969,7 +1970,7 @@ def groupFollowersByDomain(baseDir: str, nickname: str, domain: str) -> {}:
|
|||
return grouped
|
||||
|
||||
|
||||
def addFollowersToPublicPost(postJsonObject: {}) -> None:
|
||||
def _addFollowersToPublicPost(postJsonObject: {}) -> None:
|
||||
"""Adds followers entry to cc if it doesn't exist
|
||||
"""
|
||||
if not postJsonObject.get('actor'):
|
||||
|
@ -2099,7 +2100,7 @@ def sendSignedJson(postJsonObject: {}, session, baseDir: str,
|
|||
# sharedInbox is optional
|
||||
|
||||
# get the senders private key
|
||||
privateKeyPem = getPersonKey(nickname, domain, baseDir, 'private', debug)
|
||||
privateKeyPem = _getPersonKey(nickname, domain, baseDir, 'private', debug)
|
||||
if len(privateKeyPem) == 0:
|
||||
if debug:
|
||||
print('DEBUG: Private key not found for ' +
|
||||
|
@ -2112,7 +2113,7 @@ def sendSignedJson(postJsonObject: {}, session, baseDir: str,
|
|||
return 7
|
||||
postPath = inboxUrl.split(toDomain, 1)[1]
|
||||
|
||||
addFollowersToPublicPost(postJsonObject)
|
||||
_addFollowersToPublicPost(postJsonObject)
|
||||
|
||||
if not postJsonObject.get('signature'):
|
||||
try:
|
||||
|
@ -2332,7 +2333,7 @@ def sendToNamedAddresses(session, baseDir: str,
|
|||
personCache, debug, projectVersion)
|
||||
|
||||
|
||||
def hasSharedInbox(session, httpPrefix: str, domain: str) -> bool:
|
||||
def _hasSharedInbox(session, httpPrefix: str, domain: str) -> bool:
|
||||
"""Returns true if the given domain has a shared inbox
|
||||
This tries the new and the old way of webfingering the shared inbox
|
||||
"""
|
||||
|
@ -2351,7 +2352,7 @@ def hasSharedInbox(session, httpPrefix: str, domain: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def sendingProfileUpdate(postJsonObject: {}) -> bool:
|
||||
def _sendingProfileUpdate(postJsonObject: {}) -> bool:
|
||||
"""Returns true if the given json is a profile update
|
||||
"""
|
||||
if postJsonObject['type'] != 'Update':
|
||||
|
@ -2386,9 +2387,9 @@ def sendToFollowers(session, baseDir: str,
|
|||
if not session:
|
||||
print('WARN: No session for sendToFollowers')
|
||||
return
|
||||
if not postIsAddressedToFollowers(baseDir, nickname, domain,
|
||||
port, httpPrefix,
|
||||
postJsonObject):
|
||||
if not _postIsAddressedToFollowers(baseDir, nickname, domain,
|
||||
port, httpPrefix,
|
||||
postJsonObject):
|
||||
if debug:
|
||||
print('Post is not addressed to followers')
|
||||
return
|
||||
|
@ -2428,7 +2429,7 @@ def sendToFollowers(session, baseDir: str,
|
|||
print('Sending post to followers domain is active: ' +
|
||||
followerDomainUrl)
|
||||
|
||||
withSharedInbox = hasSharedInbox(session, httpPrefix, followerDomain)
|
||||
withSharedInbox = _hasSharedInbox(session, httpPrefix, followerDomain)
|
||||
if debug:
|
||||
if withSharedInbox:
|
||||
print(followerDomain + ' has shared inbox')
|
||||
|
@ -2467,7 +2468,7 @@ def sendToFollowers(session, baseDir: str,
|
|||
toNickname = 'inbox'
|
||||
|
||||
if toNickname != 'inbox' and postJsonObject.get('type'):
|
||||
if sendingProfileUpdate(postJsonObject):
|
||||
if _sendingProfileUpdate(postJsonObject):
|
||||
print('Sending post to followers ' +
|
||||
'shared inbox of ' + toDomain)
|
||||
toNickname = 'inbox'
|
||||
|
@ -2554,77 +2555,77 @@ def createInbox(recentPostsCache: {},
|
|||
session, baseDir: str, nickname: str, domain: str, port: int,
|
||||
httpPrefix: str, itemsPerPage: int, headerOnly: bool,
|
||||
pageNumber=None) -> {}:
|
||||
return createBoxIndexed(recentPostsCache,
|
||||
session, baseDir, 'inbox',
|
||||
nickname, domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
return _createBoxIndexed(recentPostsCache,
|
||||
session, baseDir, 'inbox',
|
||||
nickname, domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
|
||||
|
||||
def createBookmarksTimeline(session, baseDir: str, nickname: str, domain: str,
|
||||
port: int, httpPrefix: str, itemsPerPage: int,
|
||||
headerOnly: bool, pageNumber=None) -> {}:
|
||||
return createBoxIndexed({}, session, baseDir, 'tlbookmarks',
|
||||
nickname, domain,
|
||||
port, httpPrefix, itemsPerPage, headerOnly,
|
||||
True, 0, False, 0, pageNumber)
|
||||
return _createBoxIndexed({}, session, baseDir, 'tlbookmarks',
|
||||
nickname, domain,
|
||||
port, httpPrefix, itemsPerPage, headerOnly,
|
||||
True, 0, False, 0, pageNumber)
|
||||
|
||||
|
||||
def createEventsTimeline(recentPostsCache: {},
|
||||
session, baseDir: str, nickname: str, domain: str,
|
||||
port: int, httpPrefix: str, itemsPerPage: int,
|
||||
headerOnly: bool, pageNumber=None) -> {}:
|
||||
return createBoxIndexed(recentPostsCache, session, baseDir, 'tlevents',
|
||||
nickname, domain,
|
||||
port, httpPrefix, itemsPerPage, headerOnly,
|
||||
True, 0, False, 0, pageNumber)
|
||||
return _createBoxIndexed(recentPostsCache, session, baseDir, 'tlevents',
|
||||
nickname, domain,
|
||||
port, httpPrefix, itemsPerPage, headerOnly,
|
||||
True, 0, False, 0, pageNumber)
|
||||
|
||||
|
||||
def createDMTimeline(recentPostsCache: {},
|
||||
session, baseDir: str, nickname: str, domain: str,
|
||||
port: int, httpPrefix: str, itemsPerPage: int,
|
||||
headerOnly: bool, pageNumber=None) -> {}:
|
||||
return createBoxIndexed(recentPostsCache,
|
||||
session, baseDir, 'dm', nickname,
|
||||
domain, port, httpPrefix, itemsPerPage,
|
||||
headerOnly, True, 0, False, 0, pageNumber)
|
||||
return _createBoxIndexed(recentPostsCache,
|
||||
session, baseDir, 'dm', nickname,
|
||||
domain, port, httpPrefix, itemsPerPage,
|
||||
headerOnly, True, 0, False, 0, pageNumber)
|
||||
|
||||
|
||||
def createRepliesTimeline(recentPostsCache: {},
|
||||
session, baseDir: str, nickname: str, domain: str,
|
||||
port: int, httpPrefix: str, itemsPerPage: int,
|
||||
headerOnly: bool, pageNumber=None) -> {}:
|
||||
return createBoxIndexed(recentPostsCache, session, baseDir, 'tlreplies',
|
||||
nickname, domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
return _createBoxIndexed(recentPostsCache, session, baseDir, 'tlreplies',
|
||||
nickname, domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
|
||||
|
||||
def createBlogsTimeline(session, baseDir: str, nickname: str, domain: str,
|
||||
port: int, httpPrefix: str, itemsPerPage: int,
|
||||
headerOnly: bool, pageNumber=None) -> {}:
|
||||
return createBoxIndexed({}, session, baseDir, 'tlblogs', nickname,
|
||||
domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
return _createBoxIndexed({}, session, baseDir, 'tlblogs', nickname,
|
||||
domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
|
||||
|
||||
def createFeaturesTimeline(session, baseDir: str, nickname: str, domain: str,
|
||||
port: int, httpPrefix: str, itemsPerPage: int,
|
||||
headerOnly: bool, pageNumber=None) -> {}:
|
||||
return createBoxIndexed({}, session, baseDir, 'tlfeatures', nickname,
|
||||
domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
return _createBoxIndexed({}, session, baseDir, 'tlfeatures', nickname,
|
||||
domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
|
||||
|
||||
def createMediaTimeline(session, baseDir: str, nickname: str, domain: str,
|
||||
port: int, httpPrefix: str, itemsPerPage: int,
|
||||
headerOnly: bool, pageNumber=None) -> {}:
|
||||
return createBoxIndexed({}, session, baseDir, 'tlmedia', nickname,
|
||||
domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
return _createBoxIndexed({}, session, baseDir, 'tlmedia', nickname,
|
||||
domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
0, False, 0, pageNumber)
|
||||
|
||||
|
||||
def createNewsTimeline(session, baseDir: str, nickname: str, domain: str,
|
||||
|
@ -2632,21 +2633,21 @@ def createNewsTimeline(session, baseDir: str, nickname: str, domain: str,
|
|||
headerOnly: bool, newswireVotesThreshold: int,
|
||||
positiveVoting: bool, votingTimeMins: int,
|
||||
pageNumber=None) -> {}:
|
||||
return createBoxIndexed({}, session, baseDir, 'outbox', 'news',
|
||||
domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
newswireVotesThreshold, positiveVoting,
|
||||
votingTimeMins, pageNumber)
|
||||
return _createBoxIndexed({}, session, baseDir, 'outbox', 'news',
|
||||
domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, True,
|
||||
newswireVotesThreshold, positiveVoting,
|
||||
votingTimeMins, pageNumber)
|
||||
|
||||
|
||||
def createOutbox(session, baseDir: str, nickname: str, domain: str,
|
||||
port: int, httpPrefix: str,
|
||||
itemsPerPage: int, headerOnly: bool, authorized: bool,
|
||||
pageNumber=None) -> {}:
|
||||
return createBoxIndexed({}, session, baseDir, 'outbox',
|
||||
nickname, domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, authorized,
|
||||
0, False, 0, pageNumber)
|
||||
return _createBoxIndexed({}, session, baseDir, 'outbox',
|
||||
nickname, domain, port, httpPrefix,
|
||||
itemsPerPage, headerOnly, authorized,
|
||||
0, False, 0, pageNumber)
|
||||
|
||||
|
||||
def createModeration(baseDir: str, nickname: str, domain: str, port: int,
|
||||
|
@ -2816,8 +2817,8 @@ def isReply(postJsonObject: {}, actor: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def addPostStringToTimeline(postStr: str, boxname: str,
|
||||
postsInBox: [], boxActor: str) -> bool:
|
||||
def _addPostStringToTimeline(postStr: str, boxname: str,
|
||||
postsInBox: [], boxActor: str) -> bool:
|
||||
""" is this a valid timeline post?
|
||||
"""
|
||||
# must be a recognized ActivityPub type
|
||||
|
@ -2853,8 +2854,8 @@ def addPostStringToTimeline(postStr: str, boxname: str,
|
|||
return False
|
||||
|
||||
|
||||
def addPostToTimeline(filePath: str, boxname: str,
|
||||
postsInBox: [], boxActor: str) -> bool:
|
||||
def _addPostToTimeline(filePath: str, boxname: str,
|
||||
postsInBox: [], boxActor: str) -> bool:
|
||||
""" Reads a post from file and decides whether it is valid
|
||||
"""
|
||||
with open(filePath, 'r') as postFile:
|
||||
|
@ -2866,16 +2867,16 @@ def addPostToTimeline(filePath: str, boxname: str,
|
|||
# append a replies identifier, which will later be removed
|
||||
postStr += '<hasReplies>'
|
||||
|
||||
return addPostStringToTimeline(postStr, boxname, postsInBox, boxActor)
|
||||
return _addPostStringToTimeline(postStr, boxname, postsInBox, boxActor)
|
||||
return False
|
||||
|
||||
|
||||
def createBoxIndexed(recentPostsCache: {},
|
||||
session, baseDir: str, boxname: str,
|
||||
nickname: str, domain: str, port: int, httpPrefix: str,
|
||||
itemsPerPage: int, headerOnly: bool, authorized: bool,
|
||||
newswireVotesThreshold: int, positiveVoting: bool,
|
||||
votingTimeMins: int, pageNumber=None) -> {}:
|
||||
def _createBoxIndexed(recentPostsCache: {},
|
||||
session, baseDir: str, boxname: str,
|
||||
nickname: str, domain: str, port: int, httpPrefix: str,
|
||||
itemsPerPage: int, headerOnly: bool, authorized: bool,
|
||||
newswireVotesThreshold: int, positiveVoting: bool,
|
||||
votingTimeMins: int, pageNumber=None) -> {}:
|
||||
"""Constructs the box feed for a person with the given nickname
|
||||
"""
|
||||
if not authorized or not pageNumber:
|
||||
|
@ -3006,9 +3007,9 @@ def createBoxIndexed(recentPostsCache: {},
|
|||
if postUrl in recentPostsCache['index']:
|
||||
if recentPostsCache['json'].get(postUrl):
|
||||
url = recentPostsCache['json'][postUrl]
|
||||
addPostStringToTimeline(url,
|
||||
boxname, postsInBox,
|
||||
boxActor)
|
||||
_addPostStringToTimeline(url,
|
||||
boxname, postsInBox,
|
||||
boxActor)
|
||||
postsCtr += 1
|
||||
continue
|
||||
|
||||
|
@ -3017,8 +3018,8 @@ def createBoxIndexed(recentPostsCache: {},
|
|||
locatePost(baseDir, nickname,
|
||||
domain, postUrl, False)
|
||||
if fullPostFilename:
|
||||
addPostToTimeline(fullPostFilename, boxname,
|
||||
postsInBox, boxActor)
|
||||
_addPostToTimeline(fullPostFilename, boxname,
|
||||
postsInBox, boxActor)
|
||||
else:
|
||||
# if this is the features timeline
|
||||
if timelineNickname != nickname:
|
||||
|
@ -3026,8 +3027,8 @@ def createBoxIndexed(recentPostsCache: {},
|
|||
locatePost(baseDir, timelineNickname,
|
||||
domain, postUrl, False)
|
||||
if fullPostFilename:
|
||||
addPostToTimeline(fullPostFilename, boxname,
|
||||
postsInBox, boxActor)
|
||||
_addPostToTimeline(fullPostFilename, boxname,
|
||||
postsInBox, boxActor)
|
||||
else:
|
||||
print('WARN: unable to locate post ' + postUrl)
|
||||
else:
|
||||
|
@ -3314,10 +3315,10 @@ def getPublicPostsOfPerson(baseDir: str, nickname: str, domain: str,
|
|||
maxMentions = 10
|
||||
maxEmoji = 10
|
||||
maxAttachments = 5
|
||||
getPosts(session, personUrl, 30, maxMentions, maxEmoji,
|
||||
maxAttachments, federationList,
|
||||
personCache, raw, simple, debug,
|
||||
projectVersion, httpPrefix, domain)
|
||||
_getPosts(session, personUrl, 30, maxMentions, maxEmoji,
|
||||
maxAttachments, federationList,
|
||||
personCache, raw, simple, debug,
|
||||
projectVersion, httpPrefix, domain)
|
||||
|
||||
|
||||
def getPublicPostDomains(session, baseDir: str, nickname: str, domain: str,
|
||||
|
@ -3413,14 +3414,14 @@ def getPublicPostInfo(session, baseDir: str, nickname: str, domain: str,
|
|||
domainsInfo[d] = []
|
||||
|
||||
blockedPosts = \
|
||||
getPostsForBlockedDomains(baseDir, session, personUrl, maxPosts,
|
||||
maxMentions,
|
||||
maxEmoji, maxAttachments,
|
||||
federationList,
|
||||
personCache,
|
||||
debug,
|
||||
projectVersion, httpPrefix,
|
||||
domain)
|
||||
_getPostsForBlockedDomains(baseDir, session, personUrl, maxPosts,
|
||||
maxMentions,
|
||||
maxEmoji, maxAttachments,
|
||||
federationList,
|
||||
personCache,
|
||||
debug,
|
||||
projectVersion, httpPrefix,
|
||||
domain)
|
||||
for blockedDomain, postUrlList in blockedPosts.items():
|
||||
domainsInfo[blockedDomain] += postUrlList
|
||||
|
||||
|
@ -3467,8 +3468,8 @@ def getPublicPostDomainsBlocked(session, baseDir: str,
|
|||
return blockedDomains
|
||||
|
||||
|
||||
def getNonMutualsOfPerson(baseDir: str,
|
||||
nickname: str, domain: str) -> []:
|
||||
def _getNonMutualsOfPerson(baseDir: str,
|
||||
nickname: str, domain: str) -> []:
|
||||
"""Returns the followers who are not mutuals of a person
|
||||
i.e. accounts which follow you but you don't follow them
|
||||
"""
|
||||
|
@ -3490,7 +3491,7 @@ def checkDomains(session, baseDir: str,
|
|||
maxBlockedDomains: int, singleCheck: bool):
|
||||
"""Checks follower accounts for references to globally blocked domains
|
||||
"""
|
||||
nonMutuals = getNonMutualsOfPerson(baseDir, nickname, domain)
|
||||
nonMutuals = _getNonMutualsOfPerson(baseDir, nickname, domain)
|
||||
if not nonMutuals:
|
||||
print('No non-mutual followers were found')
|
||||
return
|
||||
|
@ -3614,7 +3615,7 @@ def populateRepliesJson(baseDir: str, nickname: str, domain: str,
|
|||
repliesJson['orderedItems'].append(pjo)
|
||||
|
||||
|
||||
def rejectAnnounce(announceFilename: str):
|
||||
def _rejectAnnounce(announceFilename: str):
|
||||
"""Marks an announce as rejected
|
||||
"""
|
||||
if not os.path.isfile(announceFilename + '.reject'):
|
||||
|
@ -3699,40 +3700,40 @@ def downloadAnnounce(session, baseDir: str, httpPrefix: str,
|
|||
if not isinstance(announcedJson, dict):
|
||||
print('WARN: announce json is not a dict - ' +
|
||||
postJsonObject['object'])
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
return None
|
||||
if not announcedJson.get('id'):
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
return None
|
||||
if '/statuses/' not in announcedJson['id']:
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
return None
|
||||
if '/users/' not in announcedJson['id'] and \
|
||||
'/accounts/' not in announcedJson['id'] and \
|
||||
'/channel/' not in announcedJson['id'] and \
|
||||
'/profile/' not in announcedJson['id']:
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
return None
|
||||
if not announcedJson.get('type'):
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
# pprint(announcedJson)
|
||||
return None
|
||||
if announcedJson['type'] != 'Note' and \
|
||||
announcedJson['type'] != 'Article':
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
# pprint(announcedJson)
|
||||
return None
|
||||
if not announcedJson.get('content'):
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
return None
|
||||
if not announcedJson.get('published'):
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
return None
|
||||
if not validPostDate(announcedJson['published']):
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
return None
|
||||
if isFiltered(baseDir, nickname, domain, announcedJson['content']):
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
return None
|
||||
# remove any long words
|
||||
announcedJson['content'] = \
|
||||
|
@ -3748,7 +3749,7 @@ def downloadAnnounce(session, baseDir: str, httpPrefix: str,
|
|||
actorNickname, actorDomain, actorPort,
|
||||
announcedJson)
|
||||
if announcedJson['type'] != 'Create':
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
# pprint(announcedJson)
|
||||
return None
|
||||
|
||||
|
@ -3765,7 +3766,7 @@ def downloadAnnounce(session, baseDir: str, httpPrefix: str,
|
|||
attributedDomain = getFullDomain(attributedDomain, attributedPort)
|
||||
if isBlocked(baseDir, nickname, domain,
|
||||
attributedNickname, attributedDomain):
|
||||
rejectAnnounce(announceFilename)
|
||||
_rejectAnnounce(announceFilename)
|
||||
return None
|
||||
postJsonObject = announcedJson
|
||||
replaceYouTube(postJsonObject, YTReplacementDomain)
|
||||
|
|
22
roles.py
22
roles.py
|
@ -63,7 +63,7 @@ def clearEditorStatus(baseDir: str) -> None:
|
|||
saveJson(actorJson, filename)
|
||||
|
||||
|
||||
def addModerator(baseDir: str, nickname: str, domain: str) -> None:
|
||||
def _addModerator(baseDir: str, nickname: str, domain: str) -> None:
|
||||
"""Adds a moderator nickname to the file
|
||||
"""
|
||||
if ':' in domain:
|
||||
|
@ -92,7 +92,7 @@ def addModerator(baseDir: str, nickname: str, domain: str) -> None:
|
|||
f.write(nickname + '\n')
|
||||
|
||||
|
||||
def removeModerator(baseDir: str, nickname: str):
|
||||
def _removeModerator(baseDir: str, nickname: str):
|
||||
"""Removes a moderator nickname from the file
|
||||
"""
|
||||
moderatorsFile = baseDir + '/accounts/moderators.txt'
|
||||
|
@ -125,7 +125,7 @@ def setRole(baseDir: str, nickname: str, domain: str,
|
|||
if role:
|
||||
# add the role
|
||||
if project == 'instance' and 'role' == 'moderator':
|
||||
addModerator(baseDir, nickname, domain)
|
||||
_addModerator(baseDir, nickname, domain)
|
||||
if actorJson['roles'].get(project):
|
||||
if role not in actorJson['roles'][project]:
|
||||
actorJson['roles'][project].append(role)
|
||||
|
@ -134,7 +134,7 @@ def setRole(baseDir: str, nickname: str, domain: str,
|
|||
else:
|
||||
# remove the role
|
||||
if project == 'instance':
|
||||
removeModerator(baseDir, nickname)
|
||||
_removeModerator(baseDir, nickname)
|
||||
if actorJson['roles'].get(project):
|
||||
actorJson['roles'][project].remove(role)
|
||||
# if the project contains no roles then remove it
|
||||
|
@ -144,8 +144,8 @@ def setRole(baseDir: str, nickname: str, domain: str,
|
|||
return True
|
||||
|
||||
|
||||
def getRoles(baseDir: str, nickname: str, domain: str,
|
||||
project: str) -> []:
|
||||
def _getRoles(baseDir: str, nickname: str, domain: str,
|
||||
project: str) -> []:
|
||||
"""Returns the roles for a given person on a given project
|
||||
"""
|
||||
actorFilename = baseDir + '/accounts/' + \
|
||||
|
@ -198,8 +198,8 @@ def outboxDelegate(baseDir: str, authenticatedNickname: str,
|
|||
# instance delegators can delagate to other projects
|
||||
# than their own
|
||||
canDelegate = False
|
||||
delegatorRoles = getRoles(baseDir, delegatorNickname,
|
||||
domain, 'instance')
|
||||
delegatorRoles = _getRoles(baseDir, delegatorNickname,
|
||||
domain, 'instance')
|
||||
if delegatorRoles:
|
||||
if 'delegator' in delegatorRoles:
|
||||
canDelegate = True
|
||||
|
@ -207,8 +207,8 @@ def outboxDelegate(baseDir: str, authenticatedNickname: str,
|
|||
if not canDelegate:
|
||||
canDelegate = True
|
||||
# non-instance delegators can only delegate within their project
|
||||
delegatorRoles = getRoles(baseDir, delegatorNickname,
|
||||
domain, project)
|
||||
delegatorRoles = _getRoles(baseDir, delegatorNickname,
|
||||
domain, project)
|
||||
if delegatorRoles:
|
||||
if 'delegator' not in delegatorRoles:
|
||||
return False
|
||||
|
@ -230,7 +230,7 @@ def outboxDelegate(baseDir: str, authenticatedNickname: str,
|
|||
return True
|
||||
|
||||
# what roles is this person already assigned to?
|
||||
existingRoles = getRoles(baseDir, nickname, domain, project)
|
||||
existingRoles = _getRoles(baseDir, nickname, domain, project)
|
||||
if existingRoles:
|
||||
if role in existingRoles:
|
||||
if debug:
|
||||
|
|
|
@ -14,8 +14,8 @@ from utils import loadJson
|
|||
from outbox import postMessageToOutbox
|
||||
|
||||
|
||||
def updatePostSchedule(baseDir: str, handle: str, httpd,
|
||||
maxScheduledPosts: int) -> None:
|
||||
def _updatePostSchedule(baseDir: str, handle: str, httpd,
|
||||
maxScheduledPosts: int) -> None:
|
||||
"""Checks if posts are due to be delivered and if so moves them to the outbox
|
||||
"""
|
||||
scheduleIndexFilename = baseDir + '/accounts/' + handle + '/schedule.index'
|
||||
|
@ -145,7 +145,7 @@ def runPostSchedule(baseDir: str, httpd, maxScheduledPosts: int):
|
|||
baseDir + '/accounts/' + account + '/schedule.index'
|
||||
if not os.path.isfile(scheduleIndexFilename):
|
||||
continue
|
||||
updatePostSchedule(baseDir, account, httpd, maxScheduledPosts)
|
||||
_updatePostSchedule(baseDir, account, httpd, maxScheduledPosts)
|
||||
break
|
||||
|
||||
|
||||
|
|
|
@ -176,11 +176,11 @@ def expireShares(baseDir: str) -> None:
|
|||
continue
|
||||
nickname = account.split('@')[0]
|
||||
domain = account.split('@')[1]
|
||||
expireSharesForAccount(baseDir, nickname, domain)
|
||||
_expireSharesForAccount(baseDir, nickname, domain)
|
||||
break
|
||||
|
||||
|
||||
def expireSharesForAccount(baseDir: str, nickname: str, domain: str) -> None:
|
||||
def _expireSharesForAccount(baseDir: str, nickname: str, domain: str) -> None:
|
||||
"""Removes expired items from shares for a particular account
|
||||
"""
|
||||
handleDomain = domain
|
||||
|
|
23
tests.py
23
tests.py
|
@ -101,7 +101,7 @@ thrBob = None
|
|||
thrEve = None
|
||||
|
||||
|
||||
def testHttpsigBase(withDigest):
|
||||
def _testHttpsigBase(withDigest):
|
||||
print('testHttpsig(' + str(withDigest) + ')')
|
||||
|
||||
baseDir = os.getcwd()
|
||||
|
@ -206,8 +206,8 @@ def testHttpsigBase(withDigest):
|
|||
|
||||
|
||||
def testHttpsig():
|
||||
testHttpsigBase(True)
|
||||
testHttpsigBase(False)
|
||||
_testHttpsigBase(True)
|
||||
_testHttpsigBase(False)
|
||||
|
||||
|
||||
def testCache():
|
||||
|
@ -2617,6 +2617,11 @@ def testFunctions():
|
|||
excludeImports = [
|
||||
'link'
|
||||
]
|
||||
excludeLocal = [
|
||||
'pyjsonld',
|
||||
'daemon',
|
||||
'tests'
|
||||
]
|
||||
# check that functions are called somewhere
|
||||
for name, properties in functionProperties.items():
|
||||
if name in exclusions:
|
||||
|
@ -2626,6 +2631,16 @@ def testFunctions():
|
|||
' in module ' + properties['module'] +
|
||||
' is not called anywhere')
|
||||
assert properties['calledInModule']
|
||||
|
||||
if len(properties['calledInModule']) == 1:
|
||||
modName = properties['calledInModule'][0]
|
||||
if modName not in excludeLocal and \
|
||||
modName == properties['module']:
|
||||
if not name.startswith('_'):
|
||||
print('Local function ' + name +
|
||||
' in ' + modName + ' does not begin with _')
|
||||
assert False
|
||||
|
||||
if name not in excludeImports:
|
||||
for modName in properties['calledInModule']:
|
||||
if modName == properties['module']:
|
||||
|
@ -2635,8 +2650,6 @@ def testFunctions():
|
|||
print(importStr + ' not found in ' + modName + '.py')
|
||||
assert False
|
||||
print('Function: ' + name + ' ✓')
|
||||
# print(str(function))
|
||||
# print(str(functionProperties))
|
||||
|
||||
|
||||
def runAllTests():
|
||||
|
|
108
theme.py
108
theme.py
|
@ -14,7 +14,7 @@ from shutil import copyfile
|
|||
from content import dangerousCSS
|
||||
|
||||
|
||||
def getThemeFiles() -> []:
|
||||
def _getThemeFiles() -> []:
|
||||
return ('epicyon.css', 'login.css', 'follow.css',
|
||||
'suspended.css', 'calendar.css', 'blog.css',
|
||||
'options.css', 'search.css', 'links.css')
|
||||
|
@ -38,7 +38,7 @@ def getThemesList(baseDir: str) -> []:
|
|||
return themes
|
||||
|
||||
|
||||
def setThemeInConfig(baseDir: str, name: str) -> bool:
|
||||
def _setThemeInConfig(baseDir: str, name: str) -> bool:
|
||||
configFilename = baseDir + '/config.json'
|
||||
if not os.path.isfile(configFilename):
|
||||
return False
|
||||
|
@ -49,7 +49,7 @@ def setThemeInConfig(baseDir: str, name: str) -> bool:
|
|||
return saveJson(configJson, configFilename)
|
||||
|
||||
|
||||
def setNewswirePublishAsIcon(baseDir: str, useIcon: bool) -> bool:
|
||||
def _setNewswirePublishAsIcon(baseDir: str, useIcon: bool) -> bool:
|
||||
"""Shows the newswire publish action as an icon or a button
|
||||
"""
|
||||
configFilename = baseDir + '/config.json'
|
||||
|
@ -62,7 +62,7 @@ def setNewswirePublishAsIcon(baseDir: str, useIcon: bool) -> bool:
|
|||
return saveJson(configJson, configFilename)
|
||||
|
||||
|
||||
def setIconsAsButtons(baseDir: str, useButtons: bool) -> bool:
|
||||
def _setIconsAsButtons(baseDir: str, useButtons: bool) -> bool:
|
||||
"""Whether to show icons in the header (inbox, outbox, etc)
|
||||
as buttons
|
||||
"""
|
||||
|
@ -76,7 +76,7 @@ def setIconsAsButtons(baseDir: str, useButtons: bool) -> bool:
|
|||
return saveJson(configJson, configFilename)
|
||||
|
||||
|
||||
def setRssIconAtTop(baseDir: str, atTop: bool) -> bool:
|
||||
def _setRssIconAtTop(baseDir: str, atTop: bool) -> bool:
|
||||
"""Whether to show RSS icon at the top of the timeline
|
||||
"""
|
||||
configFilename = baseDir + '/config.json'
|
||||
|
@ -89,7 +89,7 @@ def setRssIconAtTop(baseDir: str, atTop: bool) -> bool:
|
|||
return saveJson(configJson, configFilename)
|
||||
|
||||
|
||||
def setPublishButtonAtTop(baseDir: str, atTop: bool) -> bool:
|
||||
def _setPublishButtonAtTop(baseDir: str, atTop: bool) -> bool:
|
||||
"""Whether to show the publish button above the title image
|
||||
in the newswire column
|
||||
"""
|
||||
|
@ -103,7 +103,7 @@ def setPublishButtonAtTop(baseDir: str, atTop: bool) -> bool:
|
|||
return saveJson(configJson, configFilename)
|
||||
|
||||
|
||||
def setFullWidthTimelineButtonHeader(baseDir: str, fullWidth: bool) -> bool:
|
||||
def _setFullWidthTimelineButtonHeader(baseDir: str, fullWidth: bool) -> bool:
|
||||
"""Shows the timeline button header containing inbox, outbox,
|
||||
calendar, etc as full width
|
||||
"""
|
||||
|
@ -127,8 +127,8 @@ def getTheme(baseDir: str) -> str:
|
|||
return 'default'
|
||||
|
||||
|
||||
def removeTheme(baseDir: str):
|
||||
themeFiles = getThemeFiles()
|
||||
def _removeTheme(baseDir: str):
|
||||
themeFiles = _getThemeFiles()
|
||||
for filename in themeFiles:
|
||||
if os.path.isfile(baseDir + '/' + filename):
|
||||
os.remove(baseDir + '/' + filename)
|
||||
|
@ -183,14 +183,14 @@ def setCSSparam(css: str, param: str, value: str) -> str:
|
|||
return newcss.strip()
|
||||
|
||||
|
||||
def setThemeFromDict(baseDir: str, name: str,
|
||||
themeParams: {}, bgParams: {},
|
||||
allowLocalNetworkAccess: bool) -> None:
|
||||
def _setThemeFromDict(baseDir: str, name: str,
|
||||
themeParams: {}, bgParams: {},
|
||||
allowLocalNetworkAccess: bool) -> None:
|
||||
"""Uses a dictionary to set a theme
|
||||
"""
|
||||
if name:
|
||||
setThemeInConfig(baseDir, name)
|
||||
themeFiles = getThemeFiles()
|
||||
_setThemeInConfig(baseDir, name)
|
||||
themeFiles = _getThemeFiles()
|
||||
for filename in themeFiles:
|
||||
# check for custom css within the theme directory
|
||||
templateFilename = baseDir + '/theme/' + name + '/epicyon-' + filename
|
||||
|
@ -215,33 +215,33 @@ def setThemeFromDict(baseDir: str, name: str,
|
|||
for paramName, paramValue in themeParams.items():
|
||||
if paramName == 'newswire-publish-icon':
|
||||
if paramValue.lower() == 'true':
|
||||
setNewswirePublishAsIcon(baseDir, True)
|
||||
_setNewswirePublishAsIcon(baseDir, True)
|
||||
else:
|
||||
setNewswirePublishAsIcon(baseDir, False)
|
||||
_setNewswirePublishAsIcon(baseDir, False)
|
||||
continue
|
||||
elif paramName == 'full-width-timeline-buttons':
|
||||
if paramValue.lower() == 'true':
|
||||
setFullWidthTimelineButtonHeader(baseDir, True)
|
||||
_setFullWidthTimelineButtonHeader(baseDir, True)
|
||||
else:
|
||||
setFullWidthTimelineButtonHeader(baseDir, False)
|
||||
_setFullWidthTimelineButtonHeader(baseDir, False)
|
||||
continue
|
||||
elif paramName == 'icons-as-buttons':
|
||||
if paramValue.lower() == 'true':
|
||||
setIconsAsButtons(baseDir, True)
|
||||
_setIconsAsButtons(baseDir, True)
|
||||
else:
|
||||
setIconsAsButtons(baseDir, False)
|
||||
_setIconsAsButtons(baseDir, False)
|
||||
continue
|
||||
elif paramName == 'rss-icon-at-top':
|
||||
if paramValue.lower() == 'true':
|
||||
setRssIconAtTop(baseDir, True)
|
||||
_setRssIconAtTop(baseDir, True)
|
||||
else:
|
||||
setRssIconAtTop(baseDir, False)
|
||||
_setRssIconAtTop(baseDir, False)
|
||||
continue
|
||||
elif paramName == 'publish-button-at-top':
|
||||
if paramValue.lower() == 'true':
|
||||
setPublishButtonAtTop(baseDir, True)
|
||||
_setPublishButtonAtTop(baseDir, True)
|
||||
else:
|
||||
setPublishButtonAtTop(baseDir, False)
|
||||
_setPublishButtonAtTop(baseDir, False)
|
||||
continue
|
||||
css = setCSSparam(css, paramName, paramValue)
|
||||
filename = baseDir + '/' + filename
|
||||
|
@ -249,17 +249,17 @@ def setThemeFromDict(baseDir: str, name: str,
|
|||
cssfile.write(css)
|
||||
|
||||
if bgParams.get('login'):
|
||||
setBackgroundFormat(baseDir, name, 'login', bgParams['login'])
|
||||
_setBackgroundFormat(baseDir, name, 'login', bgParams['login'])
|
||||
if bgParams.get('follow'):
|
||||
setBackgroundFormat(baseDir, name, 'follow', bgParams['follow'])
|
||||
_setBackgroundFormat(baseDir, name, 'follow', bgParams['follow'])
|
||||
if bgParams.get('options'):
|
||||
setBackgroundFormat(baseDir, name, 'options', bgParams['options'])
|
||||
_setBackgroundFormat(baseDir, name, 'options', bgParams['options'])
|
||||
if bgParams.get('search'):
|
||||
setBackgroundFormat(baseDir, name, 'search', bgParams['search'])
|
||||
_setBackgroundFormat(baseDir, name, 'search', bgParams['search'])
|
||||
|
||||
|
||||
def setBackgroundFormat(baseDir: str, name: str,
|
||||
backgroundType: str, extension: str) -> None:
|
||||
def _setBackgroundFormat(baseDir: str, name: str,
|
||||
backgroundType: str, extension: str) -> None:
|
||||
"""Sets the background file extension
|
||||
"""
|
||||
if extension == 'jpg':
|
||||
|
@ -277,7 +277,7 @@ def setBackgroundFormat(baseDir: str, name: str,
|
|||
def enableGrayscale(baseDir: str) -> None:
|
||||
"""Enables grayscale for the current theme
|
||||
"""
|
||||
themeFiles = getThemeFiles()
|
||||
themeFiles = _getThemeFiles()
|
||||
for filename in themeFiles:
|
||||
templateFilename = baseDir + '/' + filename
|
||||
if not os.path.isfile(templateFilename):
|
||||
|
@ -300,7 +300,7 @@ def enableGrayscale(baseDir: str) -> None:
|
|||
def disableGrayscale(baseDir: str) -> None:
|
||||
"""Disables grayscale for the current theme
|
||||
"""
|
||||
themeFiles = getThemeFiles()
|
||||
themeFiles = _getThemeFiles()
|
||||
for filename in themeFiles:
|
||||
templateFilename = baseDir + '/' + filename
|
||||
if not os.path.isfile(templateFilename):
|
||||
|
@ -318,7 +318,7 @@ def disableGrayscale(baseDir: str) -> None:
|
|||
os.remove(grayscaleFilename)
|
||||
|
||||
|
||||
def setCustomFont(baseDir: str):
|
||||
def _setCustomFont(baseDir: str):
|
||||
"""Uses a dictionary to set a theme
|
||||
"""
|
||||
customFontExt = None
|
||||
|
@ -337,7 +337,7 @@ def setCustomFont(baseDir: str):
|
|||
if not customFontExt:
|
||||
return
|
||||
|
||||
themeFiles = getThemeFiles()
|
||||
themeFiles = _getThemeFiles()
|
||||
for filename in themeFiles:
|
||||
templateFilename = baseDir + '/' + filename
|
||||
if not os.path.isfile(templateFilename):
|
||||
|
@ -356,9 +356,9 @@ def setCustomFont(baseDir: str):
|
|||
cssfile.write(css)
|
||||
|
||||
|
||||
def readVariablesFile(baseDir: str, themeName: str,
|
||||
variablesFile: str,
|
||||
allowLocalNetworkAccess: bool) -> None:
|
||||
def _readVariablesFile(baseDir: str, themeName: str,
|
||||
variablesFile: str,
|
||||
allowLocalNetworkAccess: bool) -> None:
|
||||
"""Reads variables from a file in the theme directory
|
||||
"""
|
||||
themeParams = loadJson(variablesFile, 0)
|
||||
|
@ -370,14 +370,14 @@ def readVariablesFile(baseDir: str, themeName: str,
|
|||
"options": "jpg",
|
||||
"search": "jpg"
|
||||
}
|
||||
setThemeFromDict(baseDir, themeName, themeParams, bgParams,
|
||||
allowLocalNetworkAccess)
|
||||
_setThemeFromDict(baseDir, themeName, themeParams, bgParams,
|
||||
allowLocalNetworkAccess)
|
||||
|
||||
|
||||
def setThemeDefault(baseDir: str, allowLocalNetworkAccess: bool):
|
||||
def _setThemeDefault(baseDir: str, allowLocalNetworkAccess: bool):
|
||||
name = 'default'
|
||||
removeTheme(baseDir)
|
||||
setThemeInConfig(baseDir, name)
|
||||
_removeTheme(baseDir)
|
||||
_setThemeInConfig(baseDir, name)
|
||||
bgParams = {
|
||||
"login": "jpg",
|
||||
"follow": "jpg",
|
||||
|
@ -394,11 +394,11 @@ def setThemeDefault(baseDir: str, allowLocalNetworkAccess: bool):
|
|||
"banner-height-mobile": "10vh",
|
||||
"search-banner-height-mobile": "15vh"
|
||||
}
|
||||
setThemeFromDict(baseDir, name, themeParams, bgParams,
|
||||
allowLocalNetworkAccess)
|
||||
_setThemeFromDict(baseDir, name, themeParams, bgParams,
|
||||
allowLocalNetworkAccess)
|
||||
|
||||
|
||||
def setThemeFonts(baseDir: str, themeName: str) -> None:
|
||||
def _setThemeFonts(baseDir: str, themeName: str) -> None:
|
||||
"""Adds custom theme fonts
|
||||
"""
|
||||
themeNameLower = themeName.lower()
|
||||
|
@ -422,7 +422,7 @@ def setThemeFonts(baseDir: str, themeName: str) -> None:
|
|||
break
|
||||
|
||||
|
||||
def setThemeImages(baseDir: str, name: str) -> None:
|
||||
def _setThemeImages(baseDir: str, name: str) -> None:
|
||||
"""Changes the profile background image
|
||||
and banner to the defaults
|
||||
"""
|
||||
|
@ -557,7 +557,7 @@ def setTheme(baseDir: str, name: str, domain: str,
|
|||
result = False
|
||||
|
||||
prevThemeName = getTheme(baseDir)
|
||||
removeTheme(baseDir)
|
||||
_removeTheme(baseDir)
|
||||
|
||||
themes = getThemesList(baseDir)
|
||||
for themeName in themes:
|
||||
|
@ -573,21 +573,21 @@ def setTheme(baseDir: str, name: str, domain: str,
|
|||
if prevThemeName.lower() != themeNameLower:
|
||||
# change the banner and profile image
|
||||
# to the default for the theme
|
||||
setThemeImages(baseDir, name)
|
||||
setThemeFonts(baseDir, name)
|
||||
_setThemeImages(baseDir, name)
|
||||
_setThemeFonts(baseDir, name)
|
||||
result = True
|
||||
|
||||
if not result:
|
||||
# default
|
||||
setThemeDefault(baseDir)
|
||||
_setThemeDefault(baseDir)
|
||||
result = True
|
||||
|
||||
variablesFile = baseDir + '/theme/' + name + '/theme.json'
|
||||
if os.path.isfile(variablesFile):
|
||||
readVariablesFile(baseDir, name, variablesFile,
|
||||
allowLocalNetworkAccess)
|
||||
_readVariablesFile(baseDir, name, variablesFile,
|
||||
allowLocalNetworkAccess)
|
||||
|
||||
setCustomFont(baseDir)
|
||||
_setCustomFont(baseDir)
|
||||
|
||||
# set the news avatar
|
||||
newsAvatarThemeFilename = \
|
||||
|
@ -604,5 +604,5 @@ def setTheme(baseDir: str, name: str, domain: str,
|
|||
else:
|
||||
disableGrayscale(baseDir)
|
||||
|
||||
setThemeInConfig(baseDir, name)
|
||||
_setThemeInConfig(baseDir, name)
|
||||
return result
|
||||
|
|
27
utils.py
27
utils.py
|
@ -197,7 +197,7 @@ def isSystemAccount(nickname: str) -> bool:
|
|||
return False
|
||||
|
||||
|
||||
def createConfig(baseDir: str) -> None:
|
||||
def _createConfig(baseDir: str) -> None:
|
||||
"""Creates a configuration file
|
||||
"""
|
||||
configFilename = baseDir + '/config.json'
|
||||
|
@ -211,7 +211,7 @@ def createConfig(baseDir: str) -> None:
|
|||
def setConfigParam(baseDir: str, variableName: str, variableValue) -> None:
|
||||
"""Sets a configuration value
|
||||
"""
|
||||
createConfig(baseDir)
|
||||
_createConfig(baseDir)
|
||||
configFilename = baseDir + '/config.json'
|
||||
configJson = {}
|
||||
if os.path.isfile(configFilename):
|
||||
|
@ -223,7 +223,7 @@ def setConfigParam(baseDir: str, variableName: str, variableValue) -> None:
|
|||
def getConfigParam(baseDir: str, variableName: str):
|
||||
"""Gets a configuration value
|
||||
"""
|
||||
createConfig(baseDir)
|
||||
_createConfig(baseDir)
|
||||
configFilename = baseDir + '/config.json'
|
||||
configJson = loadJson(configFilename)
|
||||
if configJson:
|
||||
|
@ -610,8 +610,8 @@ def getDomainFromActor(actor: str) -> (str, int):
|
|||
return domain, port
|
||||
|
||||
|
||||
def setDefaultPetName(baseDir: str, nickname: str, domain: str,
|
||||
followNickname: str, followDomain: str) -> None:
|
||||
def _setDefaultPetName(baseDir: str, nickname: str, domain: str,
|
||||
followNickname: str, followDomain: str) -> None:
|
||||
"""Sets a default petname
|
||||
This helps especially when using onion or i2p address
|
||||
"""
|
||||
|
@ -723,8 +723,8 @@ def followPerson(baseDir: str, nickname: str, domain: str,
|
|||
addPersonToCalendar(baseDir, nickname, domain,
|
||||
followNickname, followDomain)
|
||||
# add a default petname
|
||||
setDefaultPetName(baseDir, nickname, domain,
|
||||
followNickname, followDomain)
|
||||
_setDefaultPetName(baseDir, nickname, domain,
|
||||
followNickname, followDomain)
|
||||
return True
|
||||
|
||||
|
||||
|
@ -864,7 +864,8 @@ def locatePost(baseDir: str, nickname: str, domain: str,
|
|||
return None
|
||||
|
||||
|
||||
def removeAttachment(baseDir: str, httpPrefix: str, domain: str, postJson: {}):
|
||||
def _removeAttachment(baseDir: str, httpPrefix: str, domain: str,
|
||||
postJson: {}):
|
||||
if not postJson.get('attachment'):
|
||||
return
|
||||
if not postJson['attachment'][0].get('url'):
|
||||
|
@ -907,8 +908,8 @@ def removeModerationPostFromIndex(baseDir: str, postUrl: str,
|
|||
' from moderation index')
|
||||
|
||||
|
||||
def isReplyToBlogPost(baseDir: str, nickname: str, domain: str,
|
||||
postJsonObject: str):
|
||||
def _isReplyToBlogPost(baseDir: str, nickname: str, domain: str,
|
||||
postJsonObject: str):
|
||||
"""Is the given post a reply to a blog post?
|
||||
"""
|
||||
if not postJsonObject.get('object'):
|
||||
|
@ -947,8 +948,8 @@ def deletePost(baseDir: str, httpPrefix: str,
|
|||
return
|
||||
|
||||
# don't remove replies to blog posts
|
||||
if isReplyToBlogPost(baseDir, nickname, domain,
|
||||
postJsonObject):
|
||||
if _isReplyToBlogPost(baseDir, nickname, domain,
|
||||
postJsonObject):
|
||||
return
|
||||
|
||||
# remove from recent posts cache in memory
|
||||
|
@ -966,7 +967,7 @@ def deletePost(baseDir: str, httpPrefix: str,
|
|||
del recentPostsCache['html'][postId]
|
||||
|
||||
# remove any attachment
|
||||
removeAttachment(baseDir, httpPrefix, domain, postJsonObject)
|
||||
_removeAttachment(baseDir, httpPrefix, domain, postJsonObject)
|
||||
|
||||
extensions = ('votes', 'arrived', 'muted')
|
||||
for ext in extensions:
|
||||
|
|
|
@ -89,11 +89,11 @@ def htmlCalendarDeleteConfirm(cssCache: {}, translate: {}, baseDir: str,
|
|||
return deletePostStr
|
||||
|
||||
|
||||
def htmlCalendarDay(cssCache: {}, translate: {},
|
||||
baseDir: str, path: str,
|
||||
year: int, monthNumber: int, dayNumber: int,
|
||||
nickname: str, domain: str, dayEvents: [],
|
||||
monthName: str, actor: str) -> str:
|
||||
def _htmlCalendarDay(cssCache: {}, translate: {},
|
||||
baseDir: str, path: str,
|
||||
year: int, monthNumber: int, dayNumber: int,
|
||||
nickname: str, domain: str, dayEvents: [],
|
||||
monthName: str, actor: str) -> str:
|
||||
"""Show a day within the calendar
|
||||
"""
|
||||
accountDir = baseDir + '/accounts/' + nickname + '@' + domain
|
||||
|
@ -251,10 +251,10 @@ def htmlCalendar(cssCache: {}, translate: {},
|
|||
if events:
|
||||
if events.get(str(dayNumber)):
|
||||
dayEvents = events[str(dayNumber)]
|
||||
return htmlCalendarDay(cssCache, translate, baseDir, path,
|
||||
year, monthNumber, dayNumber,
|
||||
nickname, domain, dayEvents,
|
||||
monthName, actor)
|
||||
return _htmlCalendarDay(cssCache, translate, baseDir, path,
|
||||
year, monthNumber, dayNumber,
|
||||
nickname, domain, dayEvents,
|
||||
monthName, actor)
|
||||
|
||||
events = \
|
||||
getCalendarEvents(baseDir, nickname, domain, year, monthNumber)
|
||||
|
|
|
@ -19,18 +19,18 @@ from webapp_utils import htmlFooter
|
|||
from webapp_utils import getBannerFile
|
||||
|
||||
|
||||
def linksExist(baseDir: str) -> bool:
|
||||
def _linksExist(baseDir: str) -> bool:
|
||||
"""Returns true if links have been created
|
||||
"""
|
||||
linksFilename = baseDir + '/accounts/links.txt'
|
||||
return os.path.isfile(linksFilename)
|
||||
|
||||
|
||||
def getLeftColumnShares(baseDir: str,
|
||||
httpPrefix: str, domainFull: str,
|
||||
nickname: str,
|
||||
maxSharesInLeftColumn: int,
|
||||
translate: {}) -> []:
|
||||
def _getLeftColumnShares(baseDir: str,
|
||||
httpPrefix: str, domainFull: str,
|
||||
nickname: str,
|
||||
maxSharesInLeftColumn: int,
|
||||
translate: {}) -> []:
|
||||
"""get any shares and turn them into the left column links format
|
||||
"""
|
||||
pageNumber = 1
|
||||
|
@ -164,9 +164,9 @@ def getLeftColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|||
# show a number of shares
|
||||
maxSharesInLeftColumn = 3
|
||||
sharesList = \
|
||||
getLeftColumnShares(baseDir,
|
||||
httpPrefix, domainFull, nickname,
|
||||
maxSharesInLeftColumn, translate)
|
||||
_getLeftColumnShares(baseDir,
|
||||
httpPrefix, domainFull, nickname,
|
||||
maxSharesInLeftColumn, translate)
|
||||
if linksList and sharesList:
|
||||
linksList = sharesList + linksList
|
||||
|
||||
|
@ -271,7 +271,7 @@ def htmlLinksMobile(cssCache: {}, baseDir: str,
|
|||
headerButtonsFrontScreen(translate, nickname,
|
||||
'links', authorized,
|
||||
iconsAsButtons) + '</center>'
|
||||
if linksExist(baseDir):
|
||||
if _linksExist(baseDir):
|
||||
htmlStr += \
|
||||
getLeftColumnContent(baseDir, nickname, domainFull,
|
||||
httpPrefix, translate,
|
||||
|
|
|
@ -24,7 +24,7 @@ from webapp_utils import htmlPostSeparator
|
|||
from webapp_utils import headerButtonsFrontScreen
|
||||
|
||||
|
||||
def votesIndicator(totalVotes: int, positiveVoting: bool) -> str:
|
||||
def _votesIndicator(totalVotes: int, positiveVoting: bool) -> str:
|
||||
"""Returns an indicator of the number of votes on a newswire item
|
||||
"""
|
||||
if totalVotes <= 0:
|
||||
|
@ -177,8 +177,8 @@ def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|||
|
||||
# show the newswire lines
|
||||
newswireContentStr = \
|
||||
htmlNewswire(baseDir, newswire, nickname, moderator, translate,
|
||||
positiveVoting)
|
||||
_htmlNewswire(baseDir, newswire, nickname, moderator, translate,
|
||||
positiveVoting)
|
||||
htmlStr += newswireContentStr
|
||||
|
||||
# show the rss icon at the bottom, typically on the right hand side
|
||||
|
@ -187,8 +187,8 @@ def getRightColumnContent(baseDir: str, nickname: str, domainFull: str,
|
|||
return htmlStr
|
||||
|
||||
|
||||
def htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
|
||||
translate: {}, positiveVoting: bool) -> str:
|
||||
def _htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
|
||||
translate: {}, positiveVoting: bool) -> str:
|
||||
"""Converts a newswire dict into html
|
||||
"""
|
||||
separatorStr = htmlPostSeparator(baseDir, 'right')
|
||||
|
@ -220,7 +220,7 @@ def htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
|
|||
if moderator:
|
||||
totalVotes = votesOnNewswireItem(item[2])
|
||||
totalVotesStr = \
|
||||
votesIndicator(totalVotes, positiveVoting)
|
||||
_votesIndicator(totalVotes, positiveVoting)
|
||||
|
||||
title = removeLongWords(item[0], 16, []).replace('\n', '<br>')
|
||||
htmlStr += '<p class="newswireItemVotedOn">' + \
|
||||
|
@ -247,7 +247,7 @@ def htmlNewswire(baseDir: str, newswire: {}, nickname: str, moderator: bool,
|
|||
# show a number of ticks or crosses for how many
|
||||
# votes for or against
|
||||
totalVotesStr = \
|
||||
votesIndicator(totalVotes, positiveVoting)
|
||||
_votesIndicator(totalVotes, positiveVoting)
|
||||
|
||||
title = removeLongWords(item[0], 16, []).replace('\n', '<br>')
|
||||
if moderator and moderatedItem:
|
||||
|
|
|
@ -17,8 +17,8 @@ from webapp_utils import htmlHeaderWithExternalStyle
|
|||
from webapp_utils import htmlFooter
|
||||
|
||||
|
||||
def htmlFollowingDataList(baseDir: str, nickname: str,
|
||||
domain: str, domainFull: str) -> str:
|
||||
def _htmlFollowingDataList(baseDir: str, nickname: str,
|
||||
domain: str, domainFull: str) -> str:
|
||||
"""Returns a datalist of handles being followed
|
||||
"""
|
||||
listStr = '<datalist id="followingHandles">\n'
|
||||
|
@ -57,20 +57,20 @@ def htmlFollowingDataList(baseDir: str, nickname: str,
|
|||
return listStr
|
||||
|
||||
|
||||
def htmlNewPostDropDown(scopeIcon: str, scopeDescription: str,
|
||||
replyStr: str,
|
||||
translate: {},
|
||||
showPublicOnDropdown: bool,
|
||||
defaultTimeline: str,
|
||||
pathBase: str,
|
||||
dropdownNewPostSuffix: str,
|
||||
dropdownNewBlogSuffix: str,
|
||||
dropdownUnlistedSuffix: str,
|
||||
dropdownFollowersSuffix: str,
|
||||
dropdownDMSuffix: str,
|
||||
dropdownReminderSuffix: str,
|
||||
dropdownEventSuffix: str,
|
||||
dropdownReportSuffix: str) -> str:
|
||||
def _htmlNewPostDropDown(scopeIcon: str, scopeDescription: str,
|
||||
replyStr: str,
|
||||
translate: {},
|
||||
showPublicOnDropdown: bool,
|
||||
defaultTimeline: str,
|
||||
pathBase: str,
|
||||
dropdownNewPostSuffix: str,
|
||||
dropdownNewBlogSuffix: str,
|
||||
dropdownUnlistedSuffix: str,
|
||||
dropdownFollowersSuffix: str,
|
||||
dropdownDMSuffix: str,
|
||||
dropdownReminderSuffix: str,
|
||||
dropdownEventSuffix: str,
|
||||
dropdownReportSuffix: str) -> str:
|
||||
"""Returns the html for a drop down list of new post types
|
||||
"""
|
||||
dropDownContent = '<div class="newPostDropdown">\n'
|
||||
|
@ -617,20 +617,20 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
|
|||
dropDownContent = ''
|
||||
if not reportUrl and not shareDescription:
|
||||
dropDownContent = \
|
||||
htmlNewPostDropDown(scopeIcon, scopeDescription,
|
||||
replyStr,
|
||||
translate,
|
||||
showPublicOnDropdown,
|
||||
defaultTimeline,
|
||||
pathBase,
|
||||
dropdownNewPostSuffix,
|
||||
dropdownNewBlogSuffix,
|
||||
dropdownUnlistedSuffix,
|
||||
dropdownFollowersSuffix,
|
||||
dropdownDMSuffix,
|
||||
dropdownReminderSuffix,
|
||||
dropdownEventSuffix,
|
||||
dropdownReportSuffix)
|
||||
_htmlNewPostDropDown(scopeIcon, scopeDescription,
|
||||
replyStr,
|
||||
translate,
|
||||
showPublicOnDropdown,
|
||||
defaultTimeline,
|
||||
pathBase,
|
||||
dropdownNewPostSuffix,
|
||||
dropdownNewBlogSuffix,
|
||||
dropdownUnlistedSuffix,
|
||||
dropdownFollowersSuffix,
|
||||
dropdownDMSuffix,
|
||||
dropdownReminderSuffix,
|
||||
dropdownEventSuffix,
|
||||
dropdownReportSuffix)
|
||||
else:
|
||||
if not shareDescription:
|
||||
# reporting a post to moderator
|
||||
|
@ -717,7 +717,7 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
|
|||
' <input type="text" name="mentions" ' + \
|
||||
'list="followingHandles" value="' + mentionsStr + '" selected>\n'
|
||||
newPostForm += \
|
||||
htmlFollowingDataList(baseDir, nickname, domain, domainFull)
|
||||
_htmlFollowingDataList(baseDir, nickname, domain, domainFull)
|
||||
newPostForm += ''
|
||||
selectedStr = ''
|
||||
|
||||
|
|
|
@ -20,14 +20,14 @@ from webapp_column_right import getRightColumnContent
|
|||
from webapp_post import individualPostAsHtml
|
||||
|
||||
|
||||
def htmlFrontScreenPosts(recentPostsCache: {}, maxRecentPosts: int,
|
||||
translate: {},
|
||||
baseDir: str, httpPrefix: str,
|
||||
nickname: str, domain: str, port: int,
|
||||
session, wfRequest: {}, personCache: {},
|
||||
projectVersion: str,
|
||||
YTReplacementDomain: str,
|
||||
showPublishedDateOnly: bool) -> str:
|
||||
def _htmlFrontScreenPosts(recentPostsCache: {}, maxRecentPosts: int,
|
||||
translate: {},
|
||||
baseDir: str, httpPrefix: str,
|
||||
nickname: str, domain: str, port: int,
|
||||
session, wfRequest: {}, personCache: {},
|
||||
projectVersion: str,
|
||||
YTReplacementDomain: str,
|
||||
showPublishedDateOnly: bool) -> str:
|
||||
"""Shows posts on the front screen of a news instance
|
||||
These should only be public blog posts from the features timeline
|
||||
which is the blog timeline of the news actor
|
||||
|
@ -139,14 +139,14 @@ def htmlFrontScreen(rssIconAtTop: bool,
|
|||
bannerFile, bannerFilename = \
|
||||
getBannerFile(baseDir, nickname, domain, theme)
|
||||
profileStr += \
|
||||
htmlFrontScreenPosts(recentPostsCache, maxRecentPosts,
|
||||
translate,
|
||||
baseDir, httpPrefix,
|
||||
nickname, domain, port,
|
||||
session, wfRequest, personCache,
|
||||
projectVersion,
|
||||
YTReplacementDomain,
|
||||
showPublishedDateOnly) + licenseStr
|
||||
_htmlFrontScreenPosts(recentPostsCache, maxRecentPosts,
|
||||
translate,
|
||||
baseDir, httpPrefix,
|
||||
nickname, domain, port,
|
||||
session, wfRequest, personCache,
|
||||
projectVersion,
|
||||
YTReplacementDomain,
|
||||
showPublishedDateOnly) + licenseStr
|
||||
|
||||
# Footer which is only used for system accounts
|
||||
profileFooterStr = ' </td>\n'
|
||||
|
|
|
@ -51,7 +51,7 @@ def getHashtagCategoriesFeed(baseDir: str,
|
|||
return rssStr
|
||||
|
||||
|
||||
def getHashtagDomainMax(domainHistogram: {}) -> str:
|
||||
def _getHashtagDomainMax(domainHistogram: {}) -> str:
|
||||
"""Returns the domain with the maximum number of hashtags
|
||||
"""
|
||||
maxCount = 1
|
||||
|
@ -63,7 +63,7 @@ def getHashtagDomainMax(domainHistogram: {}) -> str:
|
|||
return maxDomain
|
||||
|
||||
|
||||
def getHashtagDomainHistogram(domainHistogram: {}, translate: {}) -> str:
|
||||
def _getHashtagDomainHistogram(domainHistogram: {}, translate: {}) -> str:
|
||||
"""Returns the html for a histogram of domains
|
||||
from which hashtags are coming
|
||||
"""
|
||||
|
@ -88,7 +88,7 @@ def getHashtagDomainHistogram(domainHistogram: {}, translate: {}) -> str:
|
|||
rightColStr = ''
|
||||
|
||||
for i in range(len(domainHistogram)):
|
||||
domain = getHashtagDomainMax(domainHistogram)
|
||||
domain = _getHashtagDomainMax(domainHistogram)
|
||||
if not domain:
|
||||
break
|
||||
percent = int(domainHistogram[domain] * 100 / totalCount)
|
||||
|
@ -224,7 +224,7 @@ def htmlHashTagSwarm(baseDir: str, actor: str, translate: {}) -> str:
|
|||
getContentWarningButton('alltags', translate, tagSwarmStr)
|
||||
|
||||
tagSwarmHtml = categorySwarmStr + tagSwarmStr.strip() + '\n'
|
||||
# tagSwarmHtml += getHashtagDomainHistogram(domainHistogram, translate)
|
||||
# tagSwarmHtml += _getHashtagDomainHistogram(domainHistogram, translate)
|
||||
return tagSwarmHtml
|
||||
|
||||
|
||||
|
|
|
@ -7,8 +7,8 @@ __email__ = "bob@freedombone.net"
|
|||
__status__ = "Production"
|
||||
|
||||
|
||||
def addEmbeddedVideoFromSites(translate: {}, content: str,
|
||||
width=400, height=300) -> str:
|
||||
def _addEmbeddedVideoFromSites(translate: {}, content: str,
|
||||
width=400, height=300) -> str:
|
||||
"""Adds embedded videos
|
||||
"""
|
||||
if '>vimeo.com/' in content:
|
||||
|
@ -122,7 +122,7 @@ def addEmbeddedVideoFromSites(translate: {}, content: str,
|
|||
return content
|
||||
|
||||
|
||||
def addEmbeddedAudio(translate: {}, content: str) -> str:
|
||||
def _addEmbeddedAudio(translate: {}, content: str) -> str:
|
||||
"""Adds embedded audio for mp3/ogg
|
||||
"""
|
||||
if not ('.mp3' in content or '.ogg' in content):
|
||||
|
@ -167,8 +167,8 @@ def addEmbeddedAudio(translate: {}, content: str) -> str:
|
|||
return content
|
||||
|
||||
|
||||
def addEmbeddedVideo(translate: {}, content: str,
|
||||
width=400, height=300) -> str:
|
||||
def _addEmbeddedVideo(translate: {}, content: str,
|
||||
width=400, height=300) -> str:
|
||||
"""Adds embedded video for mp4/webm/ogv
|
||||
"""
|
||||
if not ('.mp4' in content or '.webm' in content or '.ogv' in content):
|
||||
|
@ -219,6 +219,6 @@ def addEmbeddedVideo(translate: {}, content: str,
|
|||
def addEmbeddedElements(translate: {}, content: str) -> str:
|
||||
"""Adds embedded elements for various media types
|
||||
"""
|
||||
content = addEmbeddedVideoFromSites(translate, content)
|
||||
content = addEmbeddedAudio(translate, content)
|
||||
return addEmbeddedVideo(translate, content)
|
||||
content = _addEmbeddedVideoFromSites(translate, content)
|
||||
content = _addEmbeddedAudio(translate, content)
|
||||
return _addEmbeddedVideo(translate, content)
|
||||
|
|
703
webapp_post.py
703
webapp_post.py
File diff suppressed because it is too large
Load Diff
|
@ -214,14 +214,14 @@ def htmlProfileAfterSearch(cssCache: {},
|
|||
imageUrl = profileJson['image']['url']
|
||||
|
||||
profileStr = \
|
||||
getProfileHeaderAfterSearch(baseDir,
|
||||
nickname, defaultTimeline,
|
||||
searchNickname,
|
||||
searchDomainFull,
|
||||
translate,
|
||||
displayName,
|
||||
profileDescriptionShort,
|
||||
avatarUrl, imageUrl)
|
||||
_getProfileHeaderAfterSearch(baseDir,
|
||||
nickname, defaultTimeline,
|
||||
searchNickname,
|
||||
searchDomainFull,
|
||||
translate,
|
||||
displayName,
|
||||
profileDescriptionShort,
|
||||
avatarUrl, imageUrl)
|
||||
|
||||
domainFull = getFullDomain(domain, port)
|
||||
|
||||
|
@ -287,14 +287,14 @@ def htmlProfileAfterSearch(cssCache: {},
|
|||
return htmlHeaderWithExternalStyle(cssFilename) + profileStr + htmlFooter()
|
||||
|
||||
|
||||
def getProfileHeader(baseDir: str, nickname: str, domain: str,
|
||||
domainFull: str, translate: {},
|
||||
defaultTimeline: str,
|
||||
displayName: str,
|
||||
avatarDescription: str,
|
||||
profileDescriptionShort: str,
|
||||
loginButton: str, avatarUrl: str,
|
||||
theme: str) -> str:
|
||||
def _getProfileHeader(baseDir: str, nickname: str, domain: str,
|
||||
domainFull: str, translate: {},
|
||||
defaultTimeline: str,
|
||||
displayName: str,
|
||||
avatarDescription: str,
|
||||
profileDescriptionShort: str,
|
||||
loginButton: str, avatarUrl: str,
|
||||
theme: str) -> str:
|
||||
"""The header of the profile screen, containing background
|
||||
image and avatar
|
||||
"""
|
||||
|
@ -327,14 +327,14 @@ def getProfileHeader(baseDir: str, nickname: str, domain: str,
|
|||
return htmlStr
|
||||
|
||||
|
||||
def getProfileHeaderAfterSearch(baseDir: str,
|
||||
nickname: str, defaultTimeline: str,
|
||||
searchNickname: str,
|
||||
searchDomainFull: str,
|
||||
translate: {},
|
||||
displayName: str,
|
||||
profileDescriptionShort: str,
|
||||
avatarUrl: str, imageUrl: str) -> str:
|
||||
def _getProfileHeaderAfterSearch(baseDir: str,
|
||||
nickname: str, defaultTimeline: str,
|
||||
searchNickname: str,
|
||||
searchDomainFull: str,
|
||||
translate: {},
|
||||
displayName: str,
|
||||
profileDescriptionShort: str,
|
||||
avatarUrl: str, imageUrl: str) -> str:
|
||||
"""The header of a searched for handle, containing background
|
||||
image and avatar
|
||||
"""
|
||||
|
@ -568,12 +568,12 @@ def htmlProfile(rssIconAtTop: bool,
|
|||
|
||||
avatarUrl = profileJson['icon']['url']
|
||||
profileHeaderStr = \
|
||||
getProfileHeader(baseDir, nickname, domain,
|
||||
domainFull, translate,
|
||||
defaultTimeline, displayName,
|
||||
avatarDescription,
|
||||
profileDescriptionShort,
|
||||
loginButton, avatarUrl, theme)
|
||||
_getProfileHeader(baseDir, nickname, domain,
|
||||
domainFull, translate,
|
||||
defaultTimeline, displayName,
|
||||
avatarDescription,
|
||||
profileDescriptionShort,
|
||||
loginButton, avatarUrl, theme)
|
||||
|
||||
profileStr = profileHeaderStr + donateSection
|
||||
profileStr += '<div class="container" id="buttonheader">\n'
|
||||
|
@ -621,44 +621,44 @@ def htmlProfile(rssIconAtTop: bool,
|
|||
|
||||
if selected == 'posts':
|
||||
profileStr += \
|
||||
htmlProfilePosts(recentPostsCache, maxRecentPosts,
|
||||
translate,
|
||||
baseDir, httpPrefix, authorized,
|
||||
nickname, domain, port,
|
||||
session, wfRequest, personCache,
|
||||
projectVersion,
|
||||
YTReplacementDomain,
|
||||
showPublishedDateOnly) + licenseStr
|
||||
_htmlProfilePosts(recentPostsCache, maxRecentPosts,
|
||||
translate,
|
||||
baseDir, httpPrefix, authorized,
|
||||
nickname, domain, port,
|
||||
session, wfRequest, personCache,
|
||||
projectVersion,
|
||||
YTReplacementDomain,
|
||||
showPublishedDateOnly) + licenseStr
|
||||
elif selected == 'following':
|
||||
profileStr += \
|
||||
htmlProfileFollowing(translate, baseDir, httpPrefix,
|
||||
authorized, nickname,
|
||||
domain, port, session,
|
||||
wfRequest, personCache, extraJson,
|
||||
projectVersion, ["unfollow"], selected,
|
||||
usersPath, pageNumber, maxItemsPerPage,
|
||||
dormantMonths)
|
||||
_htmlProfileFollowing(translate, baseDir, httpPrefix,
|
||||
authorized, nickname,
|
||||
domain, port, session,
|
||||
wfRequest, personCache, extraJson,
|
||||
projectVersion, ["unfollow"], selected,
|
||||
usersPath, pageNumber, maxItemsPerPage,
|
||||
dormantMonths)
|
||||
elif selected == 'followers':
|
||||
profileStr += \
|
||||
htmlProfileFollowing(translate, baseDir, httpPrefix,
|
||||
authorized, nickname,
|
||||
domain, port, session,
|
||||
wfRequest, personCache, extraJson,
|
||||
projectVersion, ["block"],
|
||||
selected, usersPath, pageNumber,
|
||||
maxItemsPerPage, dormantMonths)
|
||||
_htmlProfileFollowing(translate, baseDir, httpPrefix,
|
||||
authorized, nickname,
|
||||
domain, port, session,
|
||||
wfRequest, personCache, extraJson,
|
||||
projectVersion, ["block"],
|
||||
selected, usersPath, pageNumber,
|
||||
maxItemsPerPage, dormantMonths)
|
||||
elif selected == 'roles':
|
||||
profileStr += \
|
||||
htmlProfileRoles(translate, nickname, domainFull,
|
||||
extraJson)
|
||||
_htmlProfileRoles(translate, nickname, domainFull,
|
||||
extraJson)
|
||||
elif selected == 'skills':
|
||||
profileStr += \
|
||||
htmlProfileSkills(translate, nickname, domainFull, extraJson)
|
||||
_htmlProfileSkills(translate, nickname, domainFull, extraJson)
|
||||
elif selected == 'shares':
|
||||
profileStr += \
|
||||
htmlProfileShares(actor, translate,
|
||||
nickname, domainFull,
|
||||
extraJson) + licenseStr
|
||||
_htmlProfileShares(actor, translate,
|
||||
nickname, domainFull,
|
||||
extraJson) + licenseStr
|
||||
|
||||
profileStr = \
|
||||
htmlHeaderWithExternalStyle(cssFilename) + \
|
||||
|
@ -666,15 +666,15 @@ def htmlProfile(rssIconAtTop: bool,
|
|||
return profileStr
|
||||
|
||||
|
||||
def htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int,
|
||||
translate: {},
|
||||
baseDir: str, httpPrefix: str,
|
||||
authorized: bool,
|
||||
nickname: str, domain: str, port: int,
|
||||
session, wfRequest: {}, personCache: {},
|
||||
projectVersion: str,
|
||||
YTReplacementDomain: str,
|
||||
showPublishedDateOnly: bool) -> str:
|
||||
def _htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int,
|
||||
translate: {},
|
||||
baseDir: str, httpPrefix: str,
|
||||
authorized: bool,
|
||||
nickname: str, domain: str, port: int,
|
||||
session, wfRequest: {}, personCache: {},
|
||||
projectVersion: str,
|
||||
YTReplacementDomain: str,
|
||||
showPublishedDateOnly: bool) -> str:
|
||||
"""Shows posts on the profile screen
|
||||
These should only be public posts
|
||||
"""
|
||||
|
@ -720,16 +720,16 @@ def htmlProfilePosts(recentPostsCache: {}, maxRecentPosts: int,
|
|||
return profileStr
|
||||
|
||||
|
||||
def htmlProfileFollowing(translate: {}, baseDir: str, httpPrefix: str,
|
||||
authorized: bool,
|
||||
nickname: str, domain: str, port: int,
|
||||
session, wfRequest: {}, personCache: {},
|
||||
followingJson: {}, projectVersion: str,
|
||||
buttons: [],
|
||||
feedName: str, actor: str,
|
||||
pageNumber: int,
|
||||
maxItemsPerPage: int,
|
||||
dormantMonths: int) -> str:
|
||||
def _htmlProfileFollowing(translate: {}, baseDir: str, httpPrefix: str,
|
||||
authorized: bool,
|
||||
nickname: str, domain: str, port: int,
|
||||
session, wfRequest: {}, personCache: {},
|
||||
followingJson: {}, projectVersion: str,
|
||||
buttons: [],
|
||||
feedName: str, actor: str,
|
||||
pageNumber: int,
|
||||
maxItemsPerPage: int,
|
||||
dormantMonths: int) -> str:
|
||||
"""Shows following on the profile screen
|
||||
"""
|
||||
profileStr = ''
|
||||
|
@ -756,12 +756,12 @@ def htmlProfileFollowing(translate: {}, baseDir: str, httpPrefix: str,
|
|||
dormantMonths)
|
||||
|
||||
profileStr += \
|
||||
individualFollowAsHtml(translate, baseDir, session,
|
||||
wfRequest, personCache,
|
||||
domain, followingActor,
|
||||
authorized, nickname,
|
||||
httpPrefix, projectVersion, dormant,
|
||||
buttons)
|
||||
_individualFollowAsHtml(translate, baseDir, session,
|
||||
wfRequest, personCache,
|
||||
domain, followingActor,
|
||||
authorized, nickname,
|
||||
httpPrefix, projectVersion, dormant,
|
||||
buttons)
|
||||
|
||||
if authorized and maxItemsPerPage and pageNumber:
|
||||
if len(followingJson['orderedItems']) >= maxItemsPerPage:
|
||||
|
@ -778,8 +778,8 @@ def htmlProfileFollowing(translate: {}, baseDir: str, httpPrefix: str,
|
|||
return profileStr
|
||||
|
||||
|
||||
def htmlProfileRoles(translate: {}, nickname: str, domain: str,
|
||||
rolesJson: {}) -> str:
|
||||
def _htmlProfileRoles(translate: {}, nickname: str, domain: str,
|
||||
rolesJson: {}) -> str:
|
||||
"""Shows roles on the profile screen
|
||||
"""
|
||||
profileStr = ''
|
||||
|
@ -801,8 +801,8 @@ def htmlProfileRoles(translate: {}, nickname: str, domain: str,
|
|||
return profileStr
|
||||
|
||||
|
||||
def htmlProfileSkills(translate: {}, nickname: str, domain: str,
|
||||
skillsJson: {}) -> str:
|
||||
def _htmlProfileSkills(translate: {}, nickname: str, domain: str,
|
||||
skillsJson: {}) -> str:
|
||||
"""Shows skills on the profile screen
|
||||
"""
|
||||
profileStr = ''
|
||||
|
@ -817,8 +817,8 @@ def htmlProfileSkills(translate: {}, nickname: str, domain: str,
|
|||
return profileStr
|
||||
|
||||
|
||||
def htmlProfileShares(actor: str, translate: {},
|
||||
nickname: str, domain: str, sharesJson: {}) -> str:
|
||||
def _htmlProfileShares(actor: str, translate: {},
|
||||
nickname: str, domain: str, sharesJson: {}) -> str:
|
||||
"""Shows shares on the profile screen
|
||||
"""
|
||||
profileStr = ''
|
||||
|
@ -1450,16 +1450,16 @@ def htmlEditProfile(cssCache: {}, translate: {}, baseDir: str, path: str,
|
|||
return editProfileForm
|
||||
|
||||
|
||||
def individualFollowAsHtml(translate: {},
|
||||
baseDir: str, session, wfRequest: {},
|
||||
personCache: {}, domain: str,
|
||||
followUrl: str,
|
||||
authorized: bool,
|
||||
actorNickname: str,
|
||||
httpPrefix: str,
|
||||
projectVersion: str,
|
||||
dormant: bool,
|
||||
buttons=[]) -> str:
|
||||
def _individualFollowAsHtml(translate: {},
|
||||
baseDir: str, session, wfRequest: {},
|
||||
personCache: {}, domain: str,
|
||||
followUrl: str,
|
||||
authorized: bool,
|
||||
actorNickname: str,
|
||||
httpPrefix: str,
|
||||
projectVersion: str,
|
||||
dormant: bool,
|
||||
buttons=[]) -> str:
|
||||
"""An individual follow entry on the profile screen
|
||||
"""
|
||||
nickname = getNicknameFromActor(followUrl)
|
||||
|
|
|
@ -27,8 +27,8 @@ from webapp_headerbuttons import headerButtonsTimeline
|
|||
from posts import isModerator
|
||||
|
||||
|
||||
def logTimelineTiming(enableTimingLog: bool, timelineStartTime,
|
||||
boxName: str, debugId: str) -> None:
|
||||
def _logTimelineTiming(enableTimingLog: bool, timelineStartTime,
|
||||
boxName: str, debugId: str) -> None:
|
||||
"""Create a log of timings for performance tuning
|
||||
"""
|
||||
if not enableTimingLog:
|
||||
|
@ -127,7 +127,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
bannerFile, bannerFilename = \
|
||||
getBannerFile(baseDir, nickname, domain, theme)
|
||||
|
||||
logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '1')
|
||||
_logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '1')
|
||||
|
||||
# is the user a moderator?
|
||||
if not moderator:
|
||||
|
@ -137,7 +137,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
if not editor:
|
||||
editor = isEditor(baseDir, nickname)
|
||||
|
||||
logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '2')
|
||||
_logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '2')
|
||||
|
||||
# the appearance of buttons - highlighted or not
|
||||
inboxButton = 'button'
|
||||
|
@ -221,7 +221,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
'" src="/icons/person.png"/></a>\n'
|
||||
break
|
||||
|
||||
logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '3')
|
||||
_logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '3')
|
||||
|
||||
# moderation / reports button
|
||||
moderationButtonStr = ''
|
||||
|
@ -256,7 +256,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
|
||||
tlStr = htmlHeaderWithExternalStyle(cssFilename)
|
||||
|
||||
logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '4')
|
||||
_logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '4')
|
||||
|
||||
# if this is a news instance and we are viewing the news timeline
|
||||
newsHeader = False
|
||||
|
@ -487,17 +487,17 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
|
||||
tlStr += '</div>\n</form>\n'
|
||||
|
||||
logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '6')
|
||||
_logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '6')
|
||||
|
||||
if boxName == 'tlshares':
|
||||
maxSharesPerAccount = itemsPerPage
|
||||
return (tlStr +
|
||||
htmlSharesTimeline(translate, pageNumber, itemsPerPage,
|
||||
baseDir, actor, nickname, domain, port,
|
||||
maxSharesPerAccount, httpPrefix) +
|
||||
_htmlSharesTimeline(translate, pageNumber, itemsPerPage,
|
||||
baseDir, actor, nickname, domain, port,
|
||||
maxSharesPerAccount, httpPrefix) +
|
||||
htmlFooter())
|
||||
|
||||
logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '7')
|
||||
_logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '7')
|
||||
|
||||
# page up arrow
|
||||
if pageNumber > 1:
|
||||
|
@ -543,14 +543,14 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
preparePostFromHtmlCache(currTlStr,
|
||||
boxName,
|
||||
pageNumber)
|
||||
logTimelineTiming(enableTimingLog,
|
||||
timelineStartTime,
|
||||
boxName, '10')
|
||||
_logTimelineTiming(enableTimingLog,
|
||||
timelineStartTime,
|
||||
boxName, '10')
|
||||
|
||||
if not currTlStr:
|
||||
logTimelineTiming(enableTimingLog,
|
||||
timelineStartTime,
|
||||
boxName, '11')
|
||||
_logTimelineTiming(enableTimingLog,
|
||||
timelineStartTime,
|
||||
boxName, '11')
|
||||
|
||||
# read the post from disk
|
||||
currTlStr = \
|
||||
|
@ -570,8 +570,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
showIndividualPostIcons,
|
||||
manuallyApproveFollowers,
|
||||
False, True)
|
||||
logTimelineTiming(enableTimingLog,
|
||||
timelineStartTime, boxName, '12')
|
||||
_logTimelineTiming(enableTimingLog,
|
||||
timelineStartTime, boxName, '12')
|
||||
|
||||
if currTlStr:
|
||||
itemCtr += 1
|
||||
|
@ -612,7 +612,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str,
|
|||
rightColumnStr + ' </td>\n'
|
||||
tlStr += ' </tr>\n'
|
||||
|
||||
logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '9')
|
||||
_logTimelineTiming(enableTimingLog, timelineStartTime, boxName, '9')
|
||||
|
||||
tlStr += ' </tbody>\n'
|
||||
tlStr += '</table>\n'
|
||||
|
@ -656,10 +656,10 @@ def htmlIndividualShare(actor: str, item: {}, translate: {},
|
|||
return profileStr
|
||||
|
||||
|
||||
def htmlSharesTimeline(translate: {}, pageNumber: int, itemsPerPage: int,
|
||||
baseDir: str, actor: str,
|
||||
nickname: str, domain: str, port: int,
|
||||
maxSharesPerAccount: int, httpPrefix: str) -> str:
|
||||
def _htmlSharesTimeline(translate: {}, pageNumber: int, itemsPerPage: int,
|
||||
baseDir: str, actor: str,
|
||||
nickname: str, domain: str, port: int,
|
||||
maxSharesPerAccount: int, httpPrefix: str) -> str:
|
||||
"""Show shared items timeline as html
|
||||
"""
|
||||
sharesJson, lastPage = \
|
||||
|
|
|
@ -167,7 +167,7 @@ def getContentWarningButton(postID: str, translate: {},
|
|||
'</div></details>\n'
|
||||
|
||||
|
||||
def getActorPropertyUrl(actorJson: {}, propertyName: str) -> str:
|
||||
def _getActorPropertyUrl(actorJson: {}, propertyName: str) -> str:
|
||||
"""Returns a url property from an actor
|
||||
"""
|
||||
if not actorJson.get('attachment'):
|
||||
|
@ -206,10 +206,10 @@ def getActorPropertyUrl(actorJson: {}, propertyName: str) -> str:
|
|||
def getBlogAddress(actorJson: {}) -> str:
|
||||
"""Returns blog address for the given actor
|
||||
"""
|
||||
return getActorPropertyUrl(actorJson, 'Blog')
|
||||
return _getActorPropertyUrl(actorJson, 'Blog')
|
||||
|
||||
|
||||
def setActorPropertyUrl(actorJson: {}, propertyName: str, url: str) -> None:
|
||||
def _setActorPropertyUrl(actorJson: {}, propertyName: str, url: str) -> None:
|
||||
"""Sets a url for the given actor property
|
||||
"""
|
||||
if not actorJson.get('attachment'):
|
||||
|
@ -269,7 +269,7 @@ def setActorPropertyUrl(actorJson: {}, propertyName: str, url: str) -> None:
|
|||
def setBlogAddress(actorJson: {}, blogAddress: str) -> None:
|
||||
"""Sets an blog address for the given actor
|
||||
"""
|
||||
setActorPropertyUrl(actorJson, 'Blog', removeHtml(blogAddress))
|
||||
_setActorPropertyUrl(actorJson, 'Blog', removeHtml(blogAddress))
|
||||
|
||||
|
||||
def updateAvatarImageCache(session, baseDir: str, httpPrefix: str,
|
||||
|
@ -475,8 +475,8 @@ def postContainsPublic(postJsonObject: {}) -> bool:
|
|||
return containsPublic
|
||||
|
||||
|
||||
def getImageFile(baseDir: str, name: str, directory: str,
|
||||
nickname: str, domain: str, theme: str) -> (str, str):
|
||||
def _getImageFile(baseDir: str, name: str, directory: str,
|
||||
nickname: str, domain: str, theme: str) -> (str, str):
|
||||
"""
|
||||
returns the filenames for an image with the given name
|
||||
"""
|
||||
|
@ -495,30 +495,30 @@ def getImageFile(baseDir: str, name: str, directory: str,
|
|||
|
||||
def getBannerFile(baseDir: str,
|
||||
nickname: str, domain: str, theme: str) -> (str, str):
|
||||
return getImageFile(baseDir, 'banner',
|
||||
baseDir + '/accounts/' + nickname + '@' + domain,
|
||||
nickname, domain, theme)
|
||||
return _getImageFile(baseDir, 'banner',
|
||||
baseDir + '/accounts/' + nickname + '@' + domain,
|
||||
nickname, domain, theme)
|
||||
|
||||
|
||||
def getSearchBannerFile(baseDir: str,
|
||||
nickname: str, domain: str, theme: str) -> (str, str):
|
||||
return getImageFile(baseDir, 'search_banner',
|
||||
baseDir + '/accounts/' + nickname + '@' + domain,
|
||||
nickname, domain, theme)
|
||||
return _getImageFile(baseDir, 'search_banner',
|
||||
baseDir + '/accounts/' + nickname + '@' + domain,
|
||||
nickname, domain, theme)
|
||||
|
||||
|
||||
def getLeftImageFile(baseDir: str,
|
||||
nickname: str, domain: str, theme: str) -> (str, str):
|
||||
return getImageFile(baseDir, 'left_col_image',
|
||||
baseDir + '/accounts/' + nickname + '@' + domain,
|
||||
nickname, domain, theme)
|
||||
return _getImageFile(baseDir, 'left_col_image',
|
||||
baseDir + '/accounts/' + nickname + '@' + domain,
|
||||
nickname, domain, theme)
|
||||
|
||||
|
||||
def getRightImageFile(baseDir: str,
|
||||
nickname: str, domain: str, theme: str) -> (str, str):
|
||||
return getImageFile(baseDir, 'right_col_image',
|
||||
baseDir + '/accounts/' + nickname + '@' + domain,
|
||||
nickname, domain, theme)
|
||||
return _getImageFile(baseDir, 'right_col_image',
|
||||
baseDir + '/accounts/' + nickname + '@' + domain,
|
||||
nickname, domain, theme)
|
||||
|
||||
|
||||
def htmlHeaderWithExternalStyle(cssFilename: str, lang='en') -> str:
|
||||
|
|
12
webfinger.py
12
webfinger.py
|
@ -25,7 +25,7 @@ from utils import saveJson
|
|||
from utils import getProtocolPrefixes
|
||||
|
||||
|
||||
def parseHandle(handle: str) -> (str, str):
|
||||
def _parseHandle(handle: str) -> (str, str):
|
||||
if '.' not in handle:
|
||||
return None, None
|
||||
prefixes = getProtocolPrefixes()
|
||||
|
@ -54,7 +54,7 @@ def webfingerHandle(session, handle: str, httpPrefix: str,
|
|||
print('WARN: No session specified for webfingerHandle')
|
||||
return None
|
||||
|
||||
nickname, domain = parseHandle(handle)
|
||||
nickname, domain = _parseHandle(handle)
|
||||
if not nickname:
|
||||
return None
|
||||
wfDomain = domain
|
||||
|
@ -97,7 +97,7 @@ def webfingerHandle(session, handle: str, httpPrefix: str,
|
|||
return result
|
||||
|
||||
|
||||
def generateMagicKey(publicKeyPem) -> str:
|
||||
def _generateMagicKey(publicKeyPem) -> str:
|
||||
"""See magic_key method in
|
||||
https://github.com/tootsuite/mastodon/blob/
|
||||
707ddf7808f90e3ab042d7642d368c2ce8e95e6f/app/models/account.rb
|
||||
|
@ -170,7 +170,7 @@ def createWebfingerEndpoint(nickname: str, domain: str, port: int,
|
|||
"type": "application/activity+json"
|
||||
},
|
||||
{
|
||||
"href": generateMagicKey(publicKeyPem),
|
||||
"href": _generateMagicKey(publicKeyPem),
|
||||
"rel": "magic-public-key"
|
||||
}
|
||||
],
|
||||
|
@ -271,7 +271,7 @@ def webfingerLookup(path: str, baseDir: str,
|
|||
return wfJson
|
||||
|
||||
|
||||
def webfingerUpdateFromProfile(wfJson: {}, actorJson: {}) -> bool:
|
||||
def _webfingerUpdateFromProfile(wfJson: {}, actorJson: {}) -> bool:
|
||||
"""Updates webfinger Email/blog/xmpp links from profile
|
||||
Returns true if one or more tags has been changed
|
||||
"""
|
||||
|
@ -350,6 +350,6 @@ def webfingerUpdate(baseDir: str, nickname: str, domain: str,
|
|||
if not actorJson:
|
||||
return
|
||||
|
||||
if webfingerUpdateFromProfile(wfJson, actorJson):
|
||||
if _webfingerUpdateFromProfile(wfJson, actorJson):
|
||||
if saveJson(wfJson, filename):
|
||||
cachedWebfingers[handle] = wfJson
|
||||
|
|
Loading…
Reference in New Issue