diff --git a/acceptreject.py b/acceptreject.py index cf487d799..d7abadd4d 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -1,10 +1,10 @@ -__filename__ = "acceptreject.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="acceptreject.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import json @@ -39,7 +39,7 @@ def createAcceptReject(baseDir: str,federationList: [], \ if ':' not in domain: domain=domain+':'+str(port) - newAccept = { + newAccept={ "@context": "https://www.w3.org/ns/activitystreams", 'type': acceptType, 'actor': httpPrefix+'://'+domain+'/users/'+nickname, diff --git a/announce.py b/announce.py index 89222bbdd..6b28206c6 100644 --- a/announce.py +++ b/announce.py @@ -1,10 +1,10 @@ -__filename__ = "announce.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="announce.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import time @@ -162,7 +162,7 @@ def updateAnnounceCollection(recentPostsCache: {}, \ if debug: print('DEBUG: Adding initial shares (announcements) to '+ \ postUrl) - announcementsJson = { + announcementsJson={ "@context": "https://www.w3.org/ns/activitystreams", 'id': postUrl, 'type': 'Collection', @@ -236,10 +236,10 @@ def createAnnounce(session,baseDir: str,federationList: [], \ if ':' not in domain: fullDomain=domain+':'+str(port) - statusNumber,published = getStatusNumber() + statusNumber,published=getStatusNumber() newAnnounceId= \ httpPrefix+'://'+fullDomain+'/users/'+nickname+'/statuses/'+statusNumber - newAnnounce = { + newAnnounce={ "@context": "https://www.w3.org/ns/activitystreams", 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, 'atomUri': httpPrefix+'://'+fullDomain+'/users/'+nickname+'/statuses/'+statusNumber, @@ -254,7 +254,7 @@ def createAnnounce(session,baseDir: str,federationList: [], \ if len(ccUrl)>0: newAnnounce['cc']=[ccUrl] if saveToFile: - outboxDir = createOutboxDir(nickname,domain,baseDir) + outboxDir=createOutboxDir(nickname,domain,baseDir) filename=outboxDir+'/'+newAnnounceId.replace('/','#')+'.json' saveJson(newAnnounce,filename) @@ -291,8 +291,8 @@ def announcePublic(session,baseDir: str,federationList: [], \ if ':' not in domain: fromDomain=domain+':'+str(port) - toUrl = 'https://www.w3.org/ns/activitystreams#Public' - ccUrl = httpPrefix + '://'+fromDomain+'/users/'+nickname+'/followers' + toUrl='https://www.w3.org/ns/activitystreams#Public' + ccUrl=httpPrefix+'://'+fromDomain+'/users/'+nickname+'/followers' return createAnnounce(session,baseDir,federationList, \ nickname,domain,port, \ toUrl,ccUrl,httpPrefix, \ @@ -317,7 +317,7 @@ def repeatPost(session,baseDir: str,federationList: [], \ if ':' not in announcedDomain: announcedDomain=announcedDomain+':'+str(announcePort) - objectUrl = announceHttpsPrefix + '://'+announcedDomain+'/users/'+ \ + objectUrl=announceHttpsPrefix+'://'+announcedDomain+'/users/'+ \ announceNickname+'/statuses/'+str(announceStatusNumber) return announcePublic(session,baseDir,federationList, \ @@ -352,7 +352,7 @@ def undoAnnounce(session,baseDir: str,federationList: [], \ if ':' not in domain: fullDomain=domain+':'+str(port) - newUndoAnnounce = { + newUndoAnnounce={ "@context": "https://www.w3.org/ns/activitystreams", 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, 'type': 'Undo', @@ -403,8 +403,8 @@ def undoAnnouncePublic(session,baseDir: str,federationList: [], \ if ':' not in domain: fromDomain=domain+':'+str(port) - toUrl = 'https://www.w3.org/ns/activitystreams#Public' - ccUrl = httpPrefix + '://'+fromDomain+'/users/'+nickname+'/followers' + toUrl='https://www.w3.org/ns/activitystreams#Public' + ccUrl=httpPrefix+'://'+fromDomain+'/users/'+nickname+'/followers' return undoAnnounce(session,baseDir,federationList, \ nickname,domain,port, \ toUrl,ccUrl,httpPrefix, \ @@ -429,7 +429,7 @@ def undoRepeatPost(session,baseDir: str,federationList: [], \ if ':' not in announcedDomain: announcedDomain=announcedDomain+':'+str(announcePort) - objectUrl = announceHttpsPrefix + '://'+announcedDomain+'/users/'+ \ + objectUrl=announceHttpsPrefix+'://'+announcedDomain+'/users/'+ \ announceNickname+'/statuses/'+str(announceStatusNumber) return undoAnnouncePublic(session,baseDir,federationList, \ @@ -459,14 +459,14 @@ def sendAnnounceViaServer(baseDir: str,session, \ if ':' not in fromDomain: fromDomainFull=fromDomain+':'+str(fromPort) - toUrl = 'https://www.w3.org/ns/activitystreams#Public' - ccUrl = httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers' + toUrl='https://www.w3.org/ns/activitystreams#Public' + ccUrl=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/followers' - statusNumber,published = getStatusNumber() + statusNumber,published=getStatusNumber() newAnnounceId= \ httpPrefix+'://'+fromDomainFull+'/users/'+ \ fromNickname+'/statuses/'+statusNumber - newAnnounceJson = { + newAnnounceJson={ "@context": "https://www.w3.org/ns/activitystreams", 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname, 'atomUri': newAnnounceId, @@ -481,7 +481,7 @@ def sendAnnounceViaServer(baseDir: str,session, \ handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname # lookup the inbox for the To handle - wfRequest = \ + wfRequest= \ webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ fromDomain,projectVersion) if not wfRequest: @@ -492,7 +492,7 @@ def sendAnnounceViaServer(baseDir: str,session, \ postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,fromNickname,fromDomain,postToBox) @@ -507,10 +507,12 @@ def sendAnnounceViaServer(baseDir: str,session, \ authHeader=createBasicAuthHeader(fromNickname,password) - headers = {'host': fromDomain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,newAnnounceJson,[],inboxUrl,headers,"inbox:write") if debug: diff --git a/auth.py b/auth.py index 61e1e6beb..1172343ab 100644 --- a/auth.py +++ b/auth.py @@ -1,10 +1,10 @@ -__filename__ = "auth.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="auth.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import base64 import hashlib @@ -16,24 +16,24 @@ import random def hashPassword(password: str) -> str: """Hash a password for storing """ - salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') - pwdhash = hashlib.pbkdf2_hmac('sha512', \ + salt=hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii') + pwdhash=hashlib.pbkdf2_hmac('sha512', \ password.encode('utf-8'), \ salt, 100000) - pwdhash = binascii.hexlify(pwdhash) - return (salt + pwdhash).decode('ascii') + pwdhash=binascii.hexlify(pwdhash) + return (salt+pwdhash).decode('ascii') def verifyPassword(storedPassword: str,providedPassword: str) -> bool: """Verify a stored password against one provided by user """ - salt = storedPassword[:64] - storedPassword = storedPassword[64:] - pwdhash = hashlib.pbkdf2_hmac('sha512', \ + salt=storedPassword[:64] + storedPassword=storedPassword[64:] + pwdhash=hashlib.pbkdf2_hmac('sha512', \ providedPassword.encode('utf-8'), \ salt.encode('ascii'), \ 100000) - pwdhash = binascii.hexlify(pwdhash).decode('ascii') - return pwdhash == storedPassword + pwdhash=binascii.hexlify(pwdhash).decode('ascii') + return pwdhash==storedPassword def createBasicAuthHeader(nickname: str,password: str) -> str: """This is only used by tests @@ -60,13 +60,13 @@ def authorizeBasic(baseDir: str,path: str,authHeader: str,debug: bool) -> bool: print('DEBUG: This is not a users endpoint') return False nicknameFromPath=pathUsersSection.split('/')[0] - base64Str = authHeader.split(' ')[1].replace('\n','') - plain = base64.b64decode(base64Str).decode('utf-8') + base64Str=authHeader.split(' ')[1].replace('\n','') + plain=base64.b64decode(base64Str).decode('utf-8') if ':' not in plain: if debug: print('DEBUG: Basic Auth header does not contain a ":" separator for username:password') return False - nickname = plain.split(':')[0] + nickname=plain.split(':')[0] if nickname!=nicknameFromPath: if debug: print('DEBUG: Nickname given in the path ('+nicknameFromPath+ \ @@ -78,12 +78,12 @@ def authorizeBasic(baseDir: str,path: str,authHeader: str,debug: bool) -> bool: if debug: print('DEBUG: passwords file missing') return False - providedPassword = plain.split(':')[1] - passfile = open(passwordFile, "r") + providedPassword=plain.split(':')[1] + passfile=open(passwordFile, "r") for line in passfile: if line.startswith(nickname+':'): storedPassword=line.split(':')[1].replace('\n','') - success = verifyPassword(storedPassword,providedPassword) + success=verifyPassword(storedPassword,providedPassword) if not success: if debug: print('DEBUG: Password check failed for '+nickname) diff --git a/availability.py b/availability.py index 6fb243607..e45720d49 100644 --- a/availability.py +++ b/availability.py @@ -1,10 +1,10 @@ -__filename__ = "availability.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="availability.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import json import time @@ -89,10 +89,10 @@ def sendAvailabilityViaServer(baseDir: str,session, \ if ':' not in domain: domainFull=domain+':'+str(port) - toUrl = httpPrefix+'://'+domainFull+'/users/'+nickname - ccUrl = httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers' + toUrl=httpPrefix+'://'+domainFull+'/users/'+nickname + ccUrl=httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers' - newAvailabilityJson = { + newAvailabilityJson={ 'type': 'Availability', 'actor': httpPrefix+'://'+domainFull+'/users/'+nickname, 'object': '"'+status+'"', @@ -103,8 +103,9 @@ def sendAvailabilityViaServer(baseDir: str,session, \ handle=httpPrefix+'://'+domainFull+'/@'+nickname # lookup the inbox for the To handle - wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ - domain,projectVersion) + wfRequest= \ + webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ + domain,projectVersion) if not wfRequest: if debug: print('DEBUG: announce webfinger failed for '+handle) @@ -113,7 +114,7 @@ def sendAvailabilityViaServer(baseDir: str,session, \ postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,nickname,domain,postToBox) @@ -128,10 +129,12 @@ def sendAvailabilityViaServer(baseDir: str,session, \ authHeader=createBasicAuthHeader(Nickname,password) - headers = {'host': domain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': domain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,newAvailabilityJson,[],inboxUrl,headers,"inbox:write") if debug: diff --git a/blocking.py b/blocking.py index eda5e872b..56cd37a27 100644 --- a/blocking.py +++ b/blocking.py @@ -1,10 +1,10 @@ -__filename__ = "blocking.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="blocking.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os from utils import isEvil @@ -174,10 +174,10 @@ def sendBlockViaServer(baseDir: str,session, \ toUrl= 'https://www.w3.org/ns/activitystreams#Public' ccUrl= \ - httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers' + httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/followers' blockActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname - newBlockJson = { + newBlockJson={ "@context": "https://www.w3.org/ns/activitystreams", 'type': 'Block', 'actor': blockActor, @@ -189,8 +189,9 @@ def sendBlockViaServer(baseDir: str,session, \ handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname # lookup the inbox for the To handle - wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ - fromDomain,projectVersion) + wfRequest= \ + webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ + fromDomain,projectVersion) if not wfRequest: if debug: print('DEBUG: announce webfinger failed for '+handle) @@ -199,7 +200,7 @@ def sendBlockViaServer(baseDir: str,session, \ postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,fromNickname, \ fromDomain,postToBox) @@ -215,10 +216,12 @@ def sendBlockViaServer(baseDir: str,session, \ authHeader=createBasicAuthHeader(fromNickname,password) - headers = {'host': fromDomain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,newBlockJson,[],inboxUrl,headers,"inbox:write") if debug: @@ -246,10 +249,10 @@ def sendUndoBlockViaServer(baseDir: str,session, \ toUrl= 'https://www.w3.org/ns/activitystreams#Public' ccUrl= \ - httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers' + httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/followers' blockActor=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname - newBlockJson = { + newBlockJson={ "@context": "https://www.w3.org/ns/activitystreams", 'type': 'Undo', 'actor': blockActor, @@ -276,7 +279,7 @@ def sendUndoBlockViaServer(baseDir: str,session, \ postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,fromNickname, \ fromDomain,postToBox) @@ -292,10 +295,12 @@ def sendUndoBlockViaServer(baseDir: str,session, \ authHeader=createBasicAuthHeader(fromNickname,password) - headers = {'host': fromDomain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,newBlockJson,[],inboxUrl,headers,"inbox:write") if debug: diff --git a/blog.py b/blog.py index 74274f06e..7a7f6e408 100644 --- a/blog.py +++ b/blog.py @@ -1,10 +1,10 @@ -__filename__ = "blog.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="blog.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import json import time @@ -63,12 +63,12 @@ def noOfBlogReplies(baseDir: str,httpPrefix: str,translate: {}, \ replies=0 with open(postFilename, "r") as f: - lines = f.readlines() + lines=f.readlines() for replyPostId in lines: replyPostId= \ replyPostId.replace('\n','').replace('.json','').replace('.replies','') replies+= \ - 1 + \ + 1+ \ noOfBlogReplies(baseDir,httpPrefix,translate, \ nickname,domain,domainFull, \ replyPostId,depth+1) @@ -111,7 +111,7 @@ def getBlogReplies(baseDir: str,httpPrefix: str,translate: {}, \ return '' with open(postFilename, "r") as f: - lines = f.readlines() + lines=f.readlines() repliesStr='' for replyPostId in lines: replyPostId= \ @@ -556,13 +556,13 @@ def htmlEditBlog(mediaInstance: bool,translate: {}, \ if os.path.isfile(baseDir+'/accounts/newpost.txt'): with open(baseDir+'/accounts/newpost.txt', 'r') as file: - editBlogText = '
'+file.read()+'
' + editBlogText=''+file.read()+'
' cssFilename=baseDir+'/epicyon-profile.css' if os.path.isfile(baseDir+'/epicyon.css'): cssFilename=baseDir+'/epicyon.css' with open(cssFilename, 'r') as cssFile: - editBlogCSS = cssFile.read() + editBlogCSS=cssFile.read() if httpPrefix!='https': editBlogCSS=editBlogCSS.replace('https://',httpPrefix+'://') diff --git a/blurhash.py b/blurhash.py index 3ed47e739..6e13d2bdd 100644 --- a/blurhash.py +++ b/blurhash.py @@ -32,16 +32,16 @@ Very close port of the original Swift implementation by Dag Ă…gren. import math # Alphabet for base 83 -alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~" -alphabet_values = dict(zip(alphabet, range(len(alphabet)))) +alphabet="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz#$%*+,-.:;=?@[]^_{|}~" +alphabet_values=dict(zip(alphabet,range(len(alphabet)))) def base83_decode(base83_str): """ Decodes a base83 string, as used in blurhash, to an integer. """ - value = 0 + value=0 for base83_char in base83_str: - value = value * 83 + alphabet_values[base83_char] + value=value*83+alphabet_values[base83_char] return value def base83_encode(value, length): @@ -54,9 +54,9 @@ def base83_encode(value, length): if int(value) // (83 ** (length)) != 0: raise ValueError("Specified length is too short to encode given value.") - result = "" - for i in range(1, length + 1): - digit = int(value) // (83 ** (length - i)) % 83 + result="" + for i in range(1,length+1): + digit=int(value) // (83 ** (length - i)) % 83 result += alphabet[int(digit)] return result @@ -64,10 +64,10 @@ def srgb_to_linear(value): """ srgb 0-255 integer to linear 0.0-1.0 floating point conversion. """ - value = float(value) / 255.0 + value=float(value) / 255.0 if value <= 0.04045: return value / 12.92 - return math.pow((value + 0.055) / 1.055, 2.4) + return math.pow((value+0.055) / 1.055, 2.4) def sign_pow(value, exp): """ @@ -79,10 +79,10 @@ def linear_to_srgb(value): """ linear 0.0-1.0 floating point to srgb 0-255 integer conversion. """ - value = max(0.0, min(1.0, value)) + value=max(0.0, min(1.0, value)) if value <= 0.0031308: - return int(value * 12.92 * 255 + 0.5) - return int((1.055 * math.pow(value, 1 / 2.4) - 0.055) * 255 + 0.5) + return int(value*12.92*255+0.5) + return int((1.055*math.pow(value, 1 / 2.4)-0.055)*255+0.5) def blurhash_components(blurhash): """ @@ -92,13 +92,13 @@ def blurhash_components(blurhash): raise ValueError("BlurHash must be at least 6 characters long.") # Decode metadata - size_info = base83_decode(blurhash[0]) - size_y = int(size_info / 9) + 1 - size_x = (size_info % 9) + 1 + size_info=base83_decode(blurhash[0]) + size_y=int(size_info / 9)+1 + size_x=(size_info % 9)+1 return size_x, size_y -def blurhash_decode(blurhash, width, height, punch = 1.0, linear = False): +def blurhash_decode(blurhash,width,height,punch=1.0,linear=False): """ Decodes the given blurhash to an image of the specified size. @@ -117,20 +117,20 @@ def blurhash_decode(blurhash, width, height, punch = 1.0, linear = False): raise ValueError("BlurHash must be at least 6 characters long.") # Decode metadata - size_info = base83_decode(blurhash[0]) - size_y = int(size_info / 9) + 1 - size_x = (size_info % 9) + 1 + size_info=base83_decode(blurhash[0]) + size_y=int(size_info / 9)+1 + size_x=(size_info % 9)+1 - quant_max_value = base83_decode(blurhash[1]) - real_max_value = (float(quant_max_value + 1) / 166.0) * punch + quant_max_value=base83_decode(blurhash[1]) + real_max_value=(float(quant_max_value+1)/166.0)*punch # Make sure we at least have the right number of characters - if len(blurhash) != 4 + 2 * size_x * size_y: + if len(blurhash) != 4+2*size_x*size_y: raise ValueError("Invalid BlurHash length.") # Decode DC component - dc_value = base83_decode(blurhash[2:6]) - colours = [( + dc_value=base83_decode(blurhash[2:6]) + colours=[( srgb_to_linear(dc_value >> 16), srgb_to_linear((dc_value >> 8) & 255), srgb_to_linear(dc_value & 255) @@ -138,7 +138,7 @@ def blurhash_decode(blurhash, width, height, punch = 1.0, linear = False): # Decode AC components for component in range(1, size_x * size_y): - ac_value = base83_decode(blurhash[4+component*2:4+(component+1)*2]) + ac_value=base83_decode(blurhash[4+component*2:4+(component+1)*2]) colours.append(( sign_pow((float(int(ac_value / (19 * 19))) - 9.0) / 9.0, 2.0) * real_max_value, sign_pow((float(int(ac_value / 19) % 19) - 9.0) / 9.0, 2.0) * real_max_value, @@ -147,20 +147,21 @@ def blurhash_decode(blurhash, width, height, punch = 1.0, linear = False): # Return image RGB values, as a list of lists of lists, # consumable by something like numpy or PIL. - pixels = [] + pixels=[] for y in range(height): - pixel_row = [] + pixel_row=[] for x in range(width): - pixel = [0.0, 0.0, 0.0] + pixel=[0.0, 0.0, 0.0] for j in range(size_y): for i in range(size_x): - basis = math.cos(math.pi * float(x) * float(i) / float(width)) * \ - math.cos(math.pi * float(y) * float(j) / float(height)) - colour = colours[i + j * size_x] - pixel[0] += colour[0] * basis - pixel[1] += colour[1] * basis - pixel[2] += colour[2] * basis + basis= \ + math.cos(math.pi*float(x)*float(i)/float(width))* \ + math.cos(math.pi*float(y)*float(j)/float(height)) + colour=colours[i+j*size_x] + pixel[0] += colour[0]*basis + pixel[1] += colour[1]*basis + pixel[2] += colour[2]*basis if linear == False: pixel_row.append([ linear_to_srgb(pixel[0]), @@ -172,7 +173,7 @@ def blurhash_decode(blurhash, width, height, punch = 1.0, linear = False): pixels.append(pixel_row) return pixels -def blurhash_encode(image, components_x = 4, components_y = 4, linear = False): +def blurhash_encode(image,components_x=4,components_y=4,linear=False): """ Calculates the blurhash for an image using the given x and y component counts. @@ -186,14 +187,14 @@ def blurhash_encode(image, components_x = 4, components_y = 4, linear = False): """ if components_x < 1 or components_x > 9 or components_y < 1 or components_y > 9: raise ValueError("x and y component counts must be between 1 and 9 inclusive.") - height = float(len(image)) - width = float(len(image[0])) + height=float(len(image)) + width=float(len(image[0])) # Convert to linear if neeeded - image_linear = [] - if linear == False: + image_linear=[] + if linear==False: for y in range(int(height)): - image_linear_line = [] + image_linear_line=[] for x in range(int(width)): image_linear_line.append([ srgb_to_linear(image[y][x][0]), @@ -202,19 +203,20 @@ def blurhash_encode(image, components_x = 4, components_y = 4, linear = False): ]) image_linear.append(image_linear_line) else: - image_linear = image + image_linear=image # Calculate components - components = [] - max_ac_component = 0.0 + components=[] + max_ac_component=0.0 for j in range(components_y): for i in range(components_x): - norm_factor = 1.0 if (i == 0 and j == 0) else 2.0 - component = [0.0, 0.0, 0.0] + norm_factor=1.0 if (i==0 and j==0) else 2.0 + component=[0.0,0.0,0.0] for y in range(int(height)): for x in range(int(width)): - basis = norm_factor * math.cos(math.pi * float(i) * float(x) / width) * \ - math.cos(math.pi * float(j) * float(y) / height) + basis= \ + norm_factor * math.cos(math.pi * float(i) * float(x) / width) * \ + math.cos(math.pi * float(j) * float(y) / height) component[0] += basis * image_linear[y][x][0] component[1] += basis * image_linear[y][x][1] component[2] += basis * image_linear[y][x][2] @@ -224,27 +226,32 @@ def blurhash_encode(image, components_x = 4, components_y = 4, linear = False): component[2] /= (width * height) components.append(component) - if not (i == 0 and j == 0): - max_ac_component = max(max_ac_component, abs(component[0]), abs(component[1]), abs(component[2])) + if not (i==0 and j==0): + max_ac_component= \ + max(max_ac_component,abs(component[0]), \ + 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 * 166 - 0.5)))) - ac_component_norm_factor = float(quant_max_ac_component + 1) / 166.0 + quant_max_ac_component= \ + int(max(0, min(82, math.floor(max_ac_component * 166 - 0.5)))) + ac_component_norm_factor= \ + float(quant_max_ac_component+1) / 166.0 - ac_values = [] + ac_values=[] for r, g, b in components[1:]: ac_values.append( - int(max(0.0, min(18.0, math.floor(sign_pow(r / ac_component_norm_factor, 0.5) * 9.0 + 9.5)))) * 19 * 19 + \ - int(max(0.0, min(18.0, math.floor(sign_pow(g / ac_component_norm_factor, 0.5) * 9.0 + 9.5)))) * 19 + \ - int(max(0.0, min(18.0, math.floor(sign_pow(b / ac_component_norm_factor, 0.5) * 9.0 + 9.5)))) + int(max(0.0,min(18.0,math.floor(sign_pow(r / ac_component_norm_factor, 0.5) * 9.0 + 9.5)))) * 19 * 19 + \ + int(max(0.0,min(18.0, math.floor(sign_pow(g / ac_component_norm_factor, 0.5) * 9.0 + 9.5)))) * 19 + \ + int(max(0.0,min(18.0, math.floor(sign_pow(b / ac_component_norm_factor, 0.5) * 9.0 + 9.5)))) ) # Build final blurhash - 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) diff --git a/bookmarks.py b/bookmarks.py index eab665b32..ab00318d9 100644 --- a/bookmarks.py +++ b/bookmarks.py @@ -1,10 +1,10 @@ -__filename__ = "bookmarks.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="bookmarks.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import json @@ -159,7 +159,7 @@ def updateBookmarksCollection(recentPostsCache: {}, \ if not postJsonObject['object'].get('bookmarks'): if debug: print('DEBUG: Adding initial bookmarks to '+objectUrl) - bookmarksJson = { + bookmarksJson={ "@context": "https://www.w3.org/ns/activitystreams", 'id': objectUrl, 'type': 'Collection', @@ -198,7 +198,7 @@ def updateBookmarksCollection(recentPostsCache: {}, \ if bookmarkIndex not in open(bookmarksIndexFilename).read(): try: with open(bookmarksIndexFilename, 'r+') as bookmarksIndexFile: - content = bookmarksIndexFile.read() + content=bookmarksIndexFile.read() bookmarksIndexFile.seek(0, 0) bookmarksIndexFile.write(bookmarkIndex+'\n'+content) if debug: @@ -239,7 +239,7 @@ def bookmark(recentPostsCache: {}, \ if '/statuses/' in objectUrl: bookmarkTo=[objectUrl.split('/statuses/')[0]] - newBookmarkJson = { + newBookmarkJson={ "@context": "https://www.w3.org/ns/activitystreams", 'type': 'Bookmark', 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, @@ -348,7 +348,7 @@ def undoBookmark(recentPostsCache: {}, \ if '/statuses/' in objectUrl: bookmarkTo=[objectUrl.split('/statuses/')[0]] - newUndoBookmarkJson = { + newUndoBookmarkJson={ "@context": "https://www.w3.org/ns/activitystreams", 'type': 'Undo', 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, @@ -414,8 +414,8 @@ def undoBookmarkPost(session,baseDir: str,federationList: [], \ if ':' not in bookmarkedomain: bookmarkedomain=bookmarkedomain+':'+str(bookmarkPort) - objectUrl = \ - httpPrefix + '://'+bookmarkedomain+'/users/'+bookmarkNickname+ \ + objectUrl= \ + httpPrefix+'://'+bookmarkedomain+'/users/'+bookmarkNickname+ \ '/statuses/'+str(bookmarkStatusNumber) ccUrl=httpPrefix+'://'+bookmarkedomain+'/users/'+bookmarkNickname @@ -454,7 +454,7 @@ def sendBookmarkViaServer(baseDir: str,session, \ if '/statuses/' in bookmarkUrl: toUrl=[bookmarkUrl.split('/statuses/')[0]] - newBookmarkJson = { + newBookmarkJson={ "@context": "https://www.w3.org/ns/activitystreams", 'type': 'Bookmark', 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname, @@ -474,7 +474,7 @@ def sendBookmarkViaServer(baseDir: str,session, \ postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,fromNickname, \ fromDomain,postToBox) @@ -490,10 +490,12 @@ def sendBookmarkViaServer(baseDir: str,session, \ authHeader=createBasicAuthHeader(fromNickname,password) - headers = {'host': fromDomain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,newBookmarkJson,[],inboxUrl,headers,"inbox:write") #if not postResult: # if debug: @@ -529,7 +531,7 @@ def sendUndoBookmarkViaServer(baseDir: str,session, \ if '/statuses/' in bookmarkUrl: toUrl=[bookmarkUrl.split('/statuses/')[0]] - newUndoBookmarkJson = { + newUndoBookmarkJson={ "@context": "https://www.w3.org/ns/activitystreams", 'type': 'Undo', 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname, @@ -543,8 +545,8 @@ def sendUndoBookmarkViaServer(baseDir: str,session, \ handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname # lookup the inbox for the To handle - wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ - fromDomain,projectVersion) + wfRequest=webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ + fromDomain,projectVersion) if not wfRequest: if debug: print('DEBUG: announce webfinger failed for '+handle) @@ -553,7 +555,7 @@ def sendUndoBookmarkViaServer(baseDir: str,session, \ postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,fromNickname, \ fromDomain,postToBox) @@ -569,10 +571,12 @@ def sendUndoBookmarkViaServer(baseDir: str,session, \ authHeader=createBasicAuthHeader(fromNickname,password) - headers = {'host': fromDomain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,newUndoBookmarkJson,[],inboxUrl,headers,"inbox:write") #if not postResult: # if debug: diff --git a/cache.py b/cache.py index 15b3268df..d41554c70 100644 --- a/cache.py +++ b/cache.py @@ -1,10 +1,10 @@ -__filename__ = "cache.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="cache.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import time diff --git a/capabilities.py b/capabilities.py index 983758262..315d241a5 100644 --- a/capabilities.py +++ b/capabilities.py @@ -1,10 +1,10 @@ -__filename__ = "capabilities.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="capabilities.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import datetime @@ -94,7 +94,7 @@ def capabilitiesRequest(baseDir: str,httpPrefix: str,domain: str, \ # which could be instance wide or for a particular person # This could also be added to a follow activity ocapId=createPassword(32) - ocapRequest = { + ocapRequest={ "@context": "https://www.w3.org/ns/activitystreams", "id": httpPrefix+"://"+requestedDomain+"/caps/request/"+ocapId, "type": "Request", @@ -140,7 +140,7 @@ def capabilitiesAccept(baseDir: str,httpPrefix: str, \ ocapId=acceptedActorNickname+'@'+acceptedActorDomain+':'+str(acceptedActorPort)+'#'+createPassword(32) else: ocapId=acceptedActorNickname+'@'+acceptedActorDomain+'#'+createPassword(32) - ocapAccept = { + ocapAccept={ "@context": "https://www.w3.org/ns/activitystreams", "id": httpPrefix+"://"+fullDomain+"/caps/"+ocapId, "type": "Capability", @@ -196,7 +196,7 @@ def capabilitiesUpdate(baseDir: str,httpPrefix: str, \ return None # create an update activity - ocapUpdate = { + ocapUpdate={ "@context": "https://www.w3.org/ns/activitystreams", 'type': 'Update', 'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, diff --git a/config.py b/config.py index 3eda05446..fecf674e2 100644 --- a/config.py +++ b/config.py @@ -1,10 +1,10 @@ -__filename__ = "config.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="config.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import time @@ -18,7 +18,7 @@ def createConfig(baseDir: str) -> None: configFilename=baseDir+'/config.json' if os.path.isfile(configFilename): return - configJson = { + configJson={ } saveJson(configJson,configFilename) diff --git a/content.py b/content.py index d770d8ff9..cc0a89afa 100644 --- a/content.py +++ b/content.py @@ -1,10 +1,10 @@ -__filename__ = "content.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="content.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import time @@ -158,7 +158,8 @@ def addWebLinks(content: str) -> str: def validHashTag(hashtag: str) -> bool: """Returns true if the give hashtag contains valid characters """ - validChars = set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') + validChars= \ + set('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') if set(hashtag).issubset(validChars): return True return False @@ -417,7 +418,7 @@ def addHtmlTags(baseDir: str,httpPrefix: str, \ if '@' in words: if os.path.isfile(followingFilename): with open(followingFilename, "r") as f: - following = f.readlines() + following=f.readlines() # extract mentions and tags from words longWordsList=[] @@ -569,7 +570,7 @@ def saveMediaInFormPOST(mediaBytes,debug: bool, \ if os.path.isfile(possibleOtherFormat): os.remove(possibleOtherFormat) - fd = open(filename, 'wb') + fd=open(filename, 'wb') fd.write(mediaBytes[startPos:]) fd.close() @@ -579,7 +580,7 @@ def extractTextFieldsInPOST(postBytes,boundary,debug: bool) -> {}: """Returns a dictionary containing the text fields of a http form POST The boundary argument comes from the http header """ - msg = email.parser.BytesParser().parsebytes(postBytes) + msg=email.parser.BytesParser().parsebytes(postBytes) if debug: print('DEBUG: POST arriving '+msg.get_payload(decode=True).decode('utf-8')) messageFields=msg.get_payload(decode=True).decode('utf-8').split(boundary) diff --git a/daemon.py b/daemon.py index c845e39ca..160f6e9d0 100644 --- a/daemon.py +++ b/daemon.py @@ -1,10 +1,10 @@ -__filename__ = "daemon.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="daemon.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" from http.server import BaseHTTPRequestHandler,ThreadingHTTPServer #import socketserver @@ -218,17 +218,17 @@ def readFollowList(filename: str) -> None: followlist=[] if not os.path.isfile(filename): return followlist - followUsers = open(filename, "r") + followUsers=open(filename, "r") for u in followUsers: if u not in followlist: - nickname,domain = parseHandle(u) + nickname,domain=parseHandle(u) if nickname: followlist.append(nickname+'@'+domain) followUsers.close() return followlist class PubServer(BaseHTTPRequestHandler): - protocol_version = 'HTTP/1.1' + protocol_version='HTTP/1.1' def _sendReplyToQuestion(self,nickname: str,messageId: str,answer: str) -> None: """Sends a reply to a question @@ -451,7 +451,7 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isfile(mediaFilename+'.etag'): try: with open(mediaFilename+'.etag', 'r') as etagFile: - etag = etagFile.read() + etag=etagFile.read() except: pass if not etag: @@ -771,7 +771,7 @@ class PubServer(BaseHTTPRequestHandler): beginSaveTime=time.time() # save the json for later queue processing - queueFilename = \ + queueFilename= \ savePostToInboxQueue(self.server.baseDir, self.server.httpPrefix, nickname, @@ -962,7 +962,7 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkGETtimings(GETstartTime,GETtimings,4) # check authorization - authorized = self._isAuthorized() + authorized=self._isAuthorized() if self.server.debug: if authorized: print('GET Authorization granted') @@ -1270,7 +1270,7 @@ class PubServer(BaseHTTPRequestHandler): while tries<5: try: with open('epicyon-profile.css', 'r') as cssfile: - css = cssfile.read() + css=cssfile.read() break except Exception as e: print(e) @@ -1299,7 +1299,7 @@ class PubServer(BaseHTTPRequestHandler): while tries<5: try: with open(mediaFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() break except Exception as e: print(e) @@ -1324,7 +1324,7 @@ class PubServer(BaseHTTPRequestHandler): while tries<5: try: with open(mediaFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() break except Exception as e: print(e) @@ -1349,7 +1349,7 @@ class PubServer(BaseHTTPRequestHandler): while tries<5: try: with open(mediaFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() break except Exception as e: print(e) @@ -1383,7 +1383,7 @@ class PubServer(BaseHTTPRequestHandler): else: mediaImageType='gif' with open(emojiFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() self._set_headers('image/'+mediaImageType,len(mediaBinary),cookie) self._write(mediaBinary) return @@ -1439,7 +1439,7 @@ class PubServer(BaseHTTPRequestHandler): currEtag='' try: with open(mediaFilename, 'r') as etagFile: - currEtag = etagFile.read() + currEtag=etagFile.read() except: pass if oldEtag==currEtag: @@ -1447,7 +1447,7 @@ class PubServer(BaseHTTPRequestHandler): self._304() return with open(mediaFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() self._set_headers_etag(mediaFilename,mediaFileType,mediaBinary,cookie) self._write(mediaBinary) return @@ -1477,7 +1477,7 @@ class PubServer(BaseHTTPRequestHandler): else: mediaFileType='gif' with open(mediaFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() self._set_headers('image/'+mediaFileType,len(mediaBinary),cookie) self._write(mediaBinary) return @@ -1501,7 +1501,7 @@ class PubServer(BaseHTTPRequestHandler): else: if os.path.isfile(mediaFilename): with open(mediaFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() self._set_headers('image/png',len(mediaBinary),cookie) self._write(mediaBinary) self.server.iconsCache[mediaStr]=mediaBinary @@ -1518,7 +1518,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.baseDir+'/cache/'+self.path if os.path.isfile(mediaFilename): with open(mediaFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() if mediaFilename.endswith('.png'): self._set_headers('image/png',len(mediaBinary),cookie) elif mediaFilename.endswith('.jpg'): @@ -1568,7 +1568,7 @@ class PubServer(BaseHTTPRequestHandler): else: mediaImageType='webp' with open(avatarFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() self._set_headers('image/'+mediaImageType, \ len(mediaBinary),cookie) self._write(mediaBinary) @@ -1861,7 +1861,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix+'://'+self.server.domainFull+ \ '/users/'+self.postToNickname unRepeatToStr='https://www.w3.org/ns/activitystreams#Public' - newUndoAnnounce = { + newUndoAnnounce={ "@context": "https://www.w3.org/ns/activitystreams", 'actor': undoAnnounceActor, 'type': 'Undo', @@ -2534,7 +2534,7 @@ class PubServer(BaseHTTPRequestHandler): nickname+'#statuses#'+statusNumber+'.replies' if not os.path.isfile(postRepliesFilename): # There are no replies, so show empty collection - repliesJson = { + repliesJson={ '@context': 'https://www.w3.org/ns/activitystreams', 'first': self.server.httpPrefix+'://'+self.server.domainFull+'/users/'+nickname+'/statuses/'+statusNumber+'/replies?page=true', 'id': self.server.httpPrefix+'://'+self.server.domainFull+'/users/'+nickname+'/statuses/'+statusNumber+'/replies', @@ -2574,7 +2574,7 @@ class PubServer(BaseHTTPRequestHandler): return else: # replies exist. Itterate through the text file containing message ids - repliesJson = { + repliesJson={ '@context': 'https://www.w3.org/ns/activitystreams', 'id': self.server.httpPrefix+'://'+self.server.domainFull+'/users/'+nickname+'/statuses/'+statusNumber+'?page=true', 'orderedItems': [ @@ -2637,7 +2637,7 @@ class PubServer(BaseHTTPRequestHandler): if actorJson: if actorJson.get('roles'): if self._requestHTTP(): - getPerson = \ + getPerson= \ personLookup(self.server.domain, \ self.path.replace('/roles',''), \ self.server.baseDir) @@ -2683,7 +2683,7 @@ class PubServer(BaseHTTPRequestHandler): if actorJson: if actorJson.get('skills'): if self._requestHTTP(): - getPerson = \ + getPerson= \ personLookup(self.server.domain, \ self.path.replace('/skills',''), \ self.server.baseDir) @@ -3525,7 +3525,7 @@ class PubServer(BaseHTTPRequestHandler): if pageNumberStr.isdigit(): pageNumber=int(pageNumberStr) searchPath=self.path.split('?page=')[0] - getPerson = \ + getPerson= \ personLookup(self.server.domain, \ searchPath.replace('/following',''), \ self.server.baseDir) @@ -3629,7 +3629,7 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkGETtimings(GETstartTime,GETtimings,52) # look up a person - getPerson = \ + getPerson= \ personLookup(self.server.domain,self.path, \ self.server.baseDir) if getPerson: @@ -3687,7 +3687,7 @@ class PubServer(BaseHTTPRequestHandler): filename=self.server.baseDir+self.path if os.path.isfile(filename): with open(filename, 'r', encoding='utf-8') as File: - content = File.read() + content=File.read() contentJson=json.loads(content) msg=json.dumps(contentJson,ensure_ascii=False).encode('utf-8') self._set_headers('application/json',len(msg),None) @@ -3723,12 +3723,12 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isfile(mediaFilename+'.etag'): try: with open(mediaFilename+'.etag', 'r') as etagFile: - etag = etagFile.read() + etag=etagFile.read() except: pass else: with open(mediaFilename, 'rb') as avFile: - mediaBinary = avFile.read() + mediaBinary=avFile.read() etag=sha1(mediaBinary).hexdigest() try: with open(mediaFilename+'.etag', 'w') as etagFile: @@ -3760,10 +3760,10 @@ class PubServer(BaseHTTPRequestHandler): postType: str,path: str,headers: {}, length: int,postBytes,boundary: str) -> int: # Note: this needs to happen synchronously - # 0 = this is not a new post - # 1 = new post success - # -1 = new post failed - # 2 = new post canceled + # 0=this is not a new post + # 1=new post success + # -1=new post failed + # 2=new post canceled if self.server.debug: print('DEBUG: receiving POST') @@ -3776,7 +3776,7 @@ class PubServer(BaseHTTPRequestHandler): nickname=nicknameStr.split('/')[0] else: return -1 - length = int(headers['Content-Length']) + length=int(headers['Content-Length']) if length>self.server.maxPostLength: print('POST size too large') return -1 @@ -4229,7 +4229,7 @@ class PubServer(BaseHTTPRequestHandler): headersWithoutCookie[dictEntryName]=headerLine print('New post headers: '+str(headersWithoutCookie)) - length = int(headers['Content-Length']) + length=int(headers['Content-Length']) if length>self.server.maxPostLength: print('POST size too large') return None @@ -4304,7 +4304,7 @@ class PubServer(BaseHTTPRequestHandler): cookie=self.headers['Cookie'] # check authorization - authorized = self._isAuthorized() + authorized=self._isAuthorized() if self.server.debug: if authorized: print('POST Authorization granted') @@ -4320,7 +4320,7 @@ class PubServer(BaseHTTPRequestHandler): if self.path.startswith('/login'): # get the contents of POST containing login credentials - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) if length>512: print('Login failed - credentials too long') self.send_response(401) @@ -4368,7 +4368,7 @@ class PubServer(BaseHTTPRequestHandler): if os.path.isfile(saltFilename): try: with open(saltFilename, 'r') as fp: - salt = fp.read() + salt=fp.read() except Exception as e: print('WARN: Unable to read salt for '+ \ loginNickname+' '+str(e)) @@ -4430,7 +4430,7 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(actorStr,cookie) self.server.POSTbusy=False return - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) if length>self.server.maxPostLength: print('Maximum profile data length exceeded '+str(length)) self._redirect_headers(actorStr,cookie) @@ -4855,7 +4855,7 @@ class PubServer(BaseHTTPRequestHandler): actorStr= \ self.server.httpPrefix+'://'+self.server.domainFull+ \ self.path.replace('/moderationaction','') - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) moderationParams=self.rfile.read(length).decode('utf-8') print('moderationParams: '+moderationParams) if '&' in moderationParams: @@ -4995,7 +4995,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.POSTbusy=False return # get the parameters - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) questionParams=self.rfile.read(length).decode('utf-8') questionParams= \ questionParams.replace('+',' ').replace('%40','@').replace('%3A',':').replace('%23','#').replace('%2F','/').replace('%3F','').strip() @@ -5034,7 +5034,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix+'://'+ \ self.server.domainFull+ \ self.path.replace('/searchhandle','') - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) searchParams=self.rfile.read(length).decode('utf-8') if 'submitBack=' in searchParams: # go back on search screen @@ -5165,7 +5165,7 @@ class PubServer(BaseHTTPRequestHandler): originPathStr= \ self.server.httpPrefix+'://'+self.server.domainFull+ \ self.path.split('/rmshare')[0] - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) removeShareConfirmParams=self.rfile.read(length).decode('utf-8') if '&submitYes=' in removeShareConfirmParams: removeShareConfirmParams= \ @@ -5193,7 +5193,7 @@ class PubServer(BaseHTTPRequestHandler): originPathStr= \ self.server.httpPrefix+'://'+self.server.domainFull+ \ self.path.split('/rmpost')[0] - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) removePostConfirmParams=self.rfile.read(length).decode('utf-8') if '&submitYes=' in removePostConfirmParams: removePostConfirmParams= \ @@ -5255,7 +5255,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix+'://'+self.server.domainFull+ \ self.path.split('/followconfirm')[0] followerNickname=getNicknameFromActor(originPathStr) - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) followConfirmParams=self.rfile.read(length).decode('utf-8') if '&submitView=' in followConfirmParams: followingActor= \ @@ -5308,7 +5308,7 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix+'://'+self.server.domainFull+ \ self.path.split('/unfollowconfirm')[0] followerNickname=getNicknameFromActor(originPathStr) - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) followConfirmParams=self.rfile.read(length).decode('utf-8') if '&submitYes=' in followConfirmParams: followingActor= \ @@ -5329,9 +5329,9 @@ class PubServer(BaseHTTPRequestHandler): self.server.httpPrefix+'://'+ \ self.server.domainFull+ \ '/users/'+followerNickname - statusNumber,published = getStatusNumber() + statusNumber,published=getStatusNumber() followId=followActor+'/statuses/'+str(statusNumber) - unfollowJson = { + unfollowJson={ '@context': 'https://www.w3.org/ns/activitystreams', 'id': followId+'/undo', 'type': 'Undo', @@ -5363,7 +5363,7 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(originPathStr,cookie) self.server.POSTbusy=False return - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) blockConfirmParams=self.rfile.read(length).decode('utf-8') if '&submitYes=' in blockConfirmParams: blockingActor= \ @@ -5410,7 +5410,7 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(originPathStr,cookie) self.server.POSTbusy=False return - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) blockConfirmParams=self.rfile.read(length).decode('utf-8') if '&submitYes=' in blockConfirmParams: blockingActor= \ @@ -5462,7 +5462,7 @@ class PubServer(BaseHTTPRequestHandler): self._redirect_headers(originPathStr,cookie) self.server.POSTbusy=False return - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) optionsConfirmParams= \ self.rfile.read(length).decode('utf-8').replace('%3A',':').replace('%2F','/') # page number to return to @@ -5671,7 +5671,7 @@ class PubServer(BaseHTTPRequestHandler): self._benchmarkPOSTtimings(POSTstartTime,POSTtimings,17) # read the message and convert it into a python dictionary - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) if self.server.debug: print('DEBUG: content-length: '+str(length)) if not self.headers['Content-type'].startswith('image/') and \ @@ -5740,7 +5740,7 @@ class PubServer(BaseHTTPRequestHandler): print("POST is not json: "+self.headers['Content-type']) if self.server.debug: print(str(self.headers)) - length = int(self.headers['Content-length']) + length=int(self.headers['Content-length']) if lengthAdmin of '+domain+'
', - 'statuses_count': 1, - 'url': httpPrefix+'://'+domainFull+'/@'+adminActor['preferredUsername'], - 'username': adminActor['preferredUsername'] + 'contact_account': { + 'acct': adminActor['preferredUsername'], + 'avatar': adminActor['icon']['url'], + 'avatar_static': adminActor['icon']['url'], + 'bot': isBot, + 'created_at': '2019-07-01T10:30:00Z', + 'display_name': adminActor['name'], + 'emojis': [], + 'fields': [], + 'followers_count': 1, + 'following_count': 1, + 'header': adminActor['image']['url'], + 'header_static': adminActor['image']['url'], + 'id': '1', + 'last_status_at': '2019-07-01T10:30:00Z', + 'locked': adminActor['manuallyApprovesFollowers'], + 'note': 'Admin of '+domain+'
', + 'statuses_count': 1, + 'url': httpPrefix+'://'+domainFull+'/@'+adminActor['preferredUsername'], + 'username': adminActor['preferredUsername'] }, 'description': instanceDescription, 'email': 'admin@'+domain, diff --git a/migrate.py b/migrate.py index 63bf666bb..9d561c30d 100644 --- a/migrate.py +++ b/migrate.py @@ -1,10 +1,10 @@ -__filename__ = "migrate.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="migrate.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os diff --git a/outbox.py b/outbox.py index 6878b35b0..91e24bcf8 100644 --- a/outbox.py +++ b/outbox.py @@ -1,10 +1,10 @@ -__filename__ = "outbox.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="outbox.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import json diff --git a/person.py b/person.py index 977bd1ecd..2876cb3c9 100644 --- a/person.py +++ b/person.py @@ -1,10 +1,10 @@ -__filename__ = "person.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="person.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import json import time @@ -42,9 +42,9 @@ from config import setConfigParam from config import getConfigParam def generateRSAKey() -> (str,str): - key = RSA.generate(2048) - privateKeyPem = key.exportKey("PEM").decode("utf-8") - publicKeyPem = key.publickey().exportKey("PEM").decode("utf-8") + key=RSA.generate(2048) + privateKeyPem=key.exportKey("PEM").decode("utf-8") + publicKeyPem=key.publickey().exportKey("PEM").decode("utf-8") return privateKeyPem,publicKeyPem def setProfileImage(baseDir: str,httpPrefix :str,nickname: str,domain: str, \ @@ -100,10 +100,13 @@ def setProfileImage(baseDir: str,httpPrefix :str,nickname: str,domain: str, \ personJson=loadJson(personFilename) if personJson: personJson[iconFilenameBase]['mediaType']=mediaType - personJson[iconFilenameBase]['url']=httpPrefix+'://'+fullDomain+'/users/'+nickname+'/'+iconFilename + personJson[iconFilenameBase]['url']= \ + httpPrefix+'://'+fullDomain+'/users/'+nickname+'/'+iconFilename saveJson(personJson,personFilename) - cmd = '/usr/bin/convert '+imageFilename+' -size '+resolution+' -quality 50 '+profileFilename + cmd= \ + '/usr/bin/convert '+imageFilename+' -size '+ \ + resolution+' -quality 50 '+profileFilename subprocess.call(cmd, shell=True) removeMetaData(profileFilename,profileFilename) return True @@ -180,61 +183,66 @@ def createPersonBase(baseDir: str,nickname: str,domain: str,port: int, \ approveFollowers=True personType='Application' - newPerson = {'@context': ['https://www.w3.org/ns/activitystreams', - 'https://w3id.org/security/v1', - {'Emoji': 'toot:Emoji', - 'Hashtag': 'as:Hashtag', - 'IdentityProof': 'toot:IdentityProof', - 'PropertyValue': 'schema:PropertyValue', - 'alsoKnownAs': {'@id': 'as:alsoKnownAs', '@type': '@id'}, - 'focalPoint': {'@container': '@list', '@id': 'toot:focalPoint'}, - 'manuallyApprovesFollowers': 'as:manuallyApprovesFollowers', - 'movedTo': {'@id': 'as:movedTo', '@type': '@id'}, - 'schema': 'http://schema.org#', - 'value': 'schema:value'}], - 'attachment': [], - 'alsoKnownAs': [], - 'discoverable': False, - 'endpoints': { - 'id': personId+'/endpoints', - 'sharedInbox': httpPrefix+'://'+domain+'/inbox', - }, - 'capabilityAcquisitionEndpoint': httpPrefix+'://'+domain+'/caps/new', - 'followers': personId+'/followers', - 'following': personId+'/following', - 'shares': personId+'/shares', - 'orgSchema': None, - 'skills': {}, - 'roles': {}, - 'availability': None, - 'icon': {'mediaType': 'image/png', - 'type': 'Image', - 'url': personId+'/avatar'+str(randint(10000000000000,99999999999999))+'.png'}, - 'id': personId, - 'image': {'mediaType': 'image/png', - 'type': 'Image', - 'url': personId+'/image'+str(randint(10000000000000,99999999999999))+'.png'}, - 'inbox': inboxStr, - 'manuallyApprovesFollowers': approveFollowers, - 'name': personName, - 'outbox': personId+'/outbox', - 'preferredUsername': personName, - 'summary': '', - 'publicKey': { - 'id': personId+'#main-key', - 'owner': personId, - 'publicKeyPem': publicKeyPem - }, - 'tag': [], - 'type': personType, - 'url': personUrl, - 'nomadicLocations': [{ - 'id': personId, - 'type': 'nomadicLocation', - 'locationAddress':'acct:'+nickname+'@'+domain, - 'locationPrimary':True, - 'locationDeleted':False - }] + newPerson={ + '@context': ['https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1', + {'Emoji': 'toot:Emoji', + 'Hashtag': 'as:Hashtag', + 'IdentityProof': 'toot:IdentityProof', + 'PropertyValue': 'schema:PropertyValue', + 'alsoKnownAs': {'@id': 'as:alsoKnownAs', '@type': '@id'}, + 'focalPoint': {'@container': '@list', '@id': 'toot:focalPoint'}, + 'manuallyApprovesFollowers': 'as:manuallyApprovesFollowers', + 'movedTo': {'@id': 'as:movedTo', '@type': '@id'}, + 'schema': 'http://schema.org#', + 'value': 'schema:value'}], + 'attachment': [], + 'alsoKnownAs': [], + 'discoverable': False, + 'endpoints': { + 'id': personId+'/endpoints', + 'sharedInbox': httpPrefix+'://'+domain+'/inbox', + }, + 'capabilityAcquisitionEndpoint': httpPrefix+'://'+domain+'/caps/new', + 'followers': personId+'/followers', + 'following': personId+'/following', + 'shares': personId+'/shares', + 'orgSchema': None, + 'skills': {}, + 'roles': {}, + 'availability': None, + 'icon': { + 'mediaType': 'image/png', + 'type': 'Image', + 'url': personId+'/avatar'+str(randint(10000000000000,99999999999999))+'.png' + }, + 'id': personId, + 'image': { + 'mediaType': 'image/png', + 'type': 'Image', + 'url': personId+'/image'+str(randint(10000000000000,99999999999999))+'.png' + }, + 'inbox': inboxStr, + 'manuallyApprovesFollowers': approveFollowers, + 'name': personName, + 'outbox': personId+'/outbox', + 'preferredUsername': personName, + 'summary': '', + 'publicKey': { + 'id': personId+'#main-key', + 'owner': personId, + 'publicKeyPem': publicKeyPem + }, + 'tag': [], + 'type': personType, + 'url': personUrl, + 'nomadicLocations': [{ + 'id': personId, + 'type': 'nomadicLocation', + 'locationAddress':'acct:'+nickname+'@'+domain, + 'locationPrimary':True, + 'locationDeleted':False + }] } if nickname=='inbox': @@ -344,7 +352,7 @@ def createPerson(baseDir: str,nickname: str,domain: str,port: int, \ if registrationsRemaining<=0: return None,None,None,None - privateKeyPem,publicKeyPem,newPerson,webfingerEndpoint = \ + privateKeyPem,publicKeyPem,newPerson,webfingerEndpoint= \ createPersonBase(baseDir,nickname,domain,port,httpPrefix,saveToFile,password) if noOfAccounts(baseDir)==1: #print(nickname+' becomes the instance admin and a moderator') @@ -621,7 +629,7 @@ def isSuspended(baseDir: str,nickname: str) -> bool: suspendedFilename=baseDir+'/accounts/suspended.txt' if os.path.isfile(suspendedFilename): with open(suspendedFilename, "r") as f: - lines = f.readlines() + lines=f.readlines() suspendedFile=open(suspendedFilename,"w+") for suspended in lines: if suspended.strip('\n')==nickname: @@ -634,7 +642,7 @@ def unsuspendAccount(baseDir: str,nickname: str) -> None: suspendedFilename=baseDir+'/accounts/suspended.txt' if os.path.isfile(suspendedFilename): with open(suspendedFilename, "r") as f: - lines = f.readlines() + lines=f.readlines() suspendedFile=open(suspendedFilename,"w+") for suspended in lines: if suspended.strip('\n')!=nickname: @@ -653,7 +661,7 @@ def suspendAccount(baseDir: str,nickname: str,domain: str) -> None: moderatorsFile=baseDir+'/accounts/moderators.txt' if os.path.isfile(moderatorsFile): with open(moderatorsFile, "r") as f: - lines = f.readlines() + lines=f.readlines() for moderator in lines: if moderator.strip('\n')==nickname: return @@ -668,7 +676,7 @@ def suspendAccount(baseDir: str,nickname: str,domain: str) -> None: suspendedFilename=baseDir+'/accounts/suspended.txt' if os.path.isfile(suspendedFilename): with open(suspendedFilename, "r") as f: - lines = f.readlines() + lines=f.readlines() for suspended in lines: if suspended.strip('\n')==nickname: return @@ -703,7 +711,7 @@ def canRemovePost(baseDir: str,nickname: str,domain: str,port: int,postId: str) moderatorsFile=baseDir+'/accounts/moderators.txt' if os.path.isfile(moderatorsFile): with open(moderatorsFile, "r") as f: - lines = f.readlines() + lines=f.readlines() for moderator in lines: if domainFull+'/users/'+moderator.strip('\n')+'/' in postId: return False @@ -720,10 +728,10 @@ def removeTagsForNickname(baseDir: str,nickname: str,domain: str,port: int) -> N if ':' not in domain: domainFull=domain+':'+str(port) matchStr=domainFull+'/users/'+nickname+'/' - directory = os.fsencode(baseDir+'/tags/') + directory=os.fsencode(baseDir+'/tags/') for f in os.scandir(directory): f=f.name - filename = os.fsdecode(f) + filename=os.fsdecode(f) if not filename.endswith(".txt"): continue tagFilename=os.path.join(directory,filename) @@ -732,7 +740,7 @@ def removeTagsForNickname(baseDir: str,nickname: str,domain: str,port: int) -> N if matchStr not in open(tagFilename).read(): continue with open(tagFilename, "r") as f: - lines = f.readlines() + lines=f.readlines() tagFile=open(tagFilename,"w+") if tagFile: for tagline in lines: @@ -752,7 +760,7 @@ def removeAccount(baseDir: str,nickname: str,domain: str,port: int) -> bool: moderatorsFile=baseDir+'/accounts/moderators.txt' if os.path.isfile(moderatorsFile): with open(moderatorsFile, "r") as f: - lines = f.readlines() + lines=f.readlines() for moderator in lines: if moderator.strip('\n')==nickname: return False diff --git a/pgp.py b/pgp.py index 62679319e..88e6587d8 100644 --- a/pgp.py +++ b/pgp.py @@ -1,10 +1,10 @@ -__filename__ = "pgp.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="pgp.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import json diff --git a/posts.py b/posts.py index e0b74d6db..f761d441a 100644 --- a/posts.py +++ b/posts.py @@ -1,10 +1,10 @@ -__filename__ = "posts.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="posts.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import requests import json @@ -68,7 +68,7 @@ def isModerator(baseDir: str,nickname: str) -> bool: return False with open(moderatorsFile, "r") as f: - lines = f.readlines() + lines=f.readlines() if len(lines)==0: if getConfigParam(baseDir,'admin')==nickname: return True @@ -116,7 +116,7 @@ def getPersonKey(nickname: str,domain: str,baseDir: str,keyType='public', \ return keyPem def cleanHtml(rawHtml: str) -> str: - #text = BeautifulSoup(rawHtml, 'html.parser').get_text() + #text=BeautifulSoup(rawHtml, 'html.parser').get_text() text=rawHtml return html.unescape(text) @@ -134,8 +134,8 @@ def getUserUrl(wfRequest: {}) -> str: def parseUserFeed(session,feedUrl: str,asHeader: {}, \ projectVersion: str,httpPrefix: str,domain: str) -> None: - feedJson = getJson(session,feedUrl,asHeader,None, \ - projectVersion,httpPrefix,domain) + feedJson=getJson(session,feedUrl,asHeader,None, \ + projectVersion,httpPrefix,domain) if not feedJson: return @@ -143,11 +143,11 @@ def parseUserFeed(session,feedUrl: str,asHeader: {}, \ for item in feedJson['orderedItems']: yield item - nextUrl = None + nextUrl=None if 'first' in feedJson: - nextUrl = feedJson['first'] + nextUrl=feedJson['first'] elif 'next' in feedJson: - nextUrl = feedJson['next'] + nextUrl=feedJson['next'] if nextUrl: if isinstance(nextUrl, str): @@ -165,29 +165,37 @@ def getPersonBox(baseDir: str,session,wfRequest: {},personCache: {}, \ projectVersion: str,httpPrefix: str, \ nickname: str,domain: str, \ boxName='inbox') -> (str,str,str,str,str,str,str,str): - asHeader = {'Accept': 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"'} + asHeader={ + 'Accept': 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"' + } if not wfRequest.get('errors'): - personUrl = getUserUrl(wfRequest) + personUrl=getUserUrl(wfRequest) else: if nickname=='dev': # try single user instance print('getPersonBox: Trying single user instance with ld+json') - personUrl = httpPrefix+'://'+domain - asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} + personUrl=httpPrefix+'://'+domain + asHeader={ + 'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' + } else: - personUrl = httpPrefix+'://'+domain+'/users/'+nickname + personUrl=httpPrefix+'://'+domain+'/users/'+nickname if not personUrl: return None,None,None,None,None,None,None,None - personJson = getPersonFromCache(baseDir,personUrl,personCache) + personJson=getPersonFromCache(baseDir,personUrl,personCache) if not personJson: if '/channel/' in personUrl: - asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} - personJson = getJson(session,personUrl,asHeader,None, \ - projectVersion,httpPrefix,domain) + asHeader={ + 'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' + } + personJson=getJson(session,personUrl,asHeader,None, \ + projectVersion,httpPrefix,domain) if not personJson: - asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} - personJson = getJson(session,personUrl,asHeader,None, \ - projectVersion,httpPrefix,domain) + asHeader={ + 'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' + } + personJson=getJson(session,personUrl,asHeader,None, \ + projectVersion,httpPrefix,domain) if not personJson: print('Unable to get actor') return None,None,None,None,None,None,None,None @@ -246,12 +254,16 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \ personPosts={} if not outboxUrl: return personPosts - asHeader = {'Accept': 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"'} + asHeader={ + 'Accept': 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"' + } if '/outbox/' in outboxUrl: - asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} + asHeader={ + 'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' + } if raw: - result = [] - i = 0 + result=[] + i=0 userFeed=parseUserFeed(session,outboxUrl,asHeader, \ projectVersion,httpPrefix,domain) for item in userFeed: @@ -262,7 +274,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \ pprint(result) return None - i = 0 + i=0 userFeed=parseUserFeed(session,outboxUrl,asHeader, \ projectVersion,httpPrefix,domain) for item in userFeed: @@ -291,7 +303,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \ print('No published attribute') continue #pprint(item) - published = item['object']['published'] + published=item['object']['published'] if not personPosts.get(item['id']): # check that this is a public post # #Public should appear in the "to" list @@ -304,7 +316,7 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \ if not isPublic: continue - content = item['object']['content'].replace(''',"'") + content=item['object']['content'].replace(''',"'") mentions=[] emoji={} @@ -337,12 +349,12 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \ print('max emojis reached') continue - summary = '' + summary='' if item['object'].get('summary'): if item['object']['summary']: - summary = item['object']['summary'] + summary=item['object']['summary'] - inReplyTo = '' + inReplyTo='' if item['object'].get('inReplyTo'): if item['object']['inReplyTo']: # No replies to non-permitted domains @@ -352,17 +364,17 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \ if debug: print('url not permitted '+item['object']['inReplyTo']) continue - inReplyTo = item['object']['inReplyTo'] + inReplyTo=item['object']['inReplyTo'] - conversation = '' + conversation='' if item['object'].get('conversation'): if item['object']['conversation']: # no conversations originated in non-permitted domains if urlPermitted(item['object']['conversation'], \ federationList,"objects:read"): - conversation = item['object']['conversation'] + conversation=item['object']['conversation'] - attachment = [] + attachment=[] if item['object'].get('attachment'): if item['object']['attachment']: for attach in item['object']['attachment']: @@ -376,15 +388,15 @@ def getPosts(session,outboxUrl: str,maxPosts: int, \ if debug: print('url not permitted '+attach['url']) - sensitive = False + sensitive=False if item['object'].get('sensitive'): - sensitive = item['object']['sensitive'] + sensitive=item['object']['sensitive'] if simple: print(cleanHtml(content)+'\n') else: pprint(item) - personPosts[item['id']] = { + personPosts[item['id']]={ "sensitive": sensitive, "inreplyto": inReplyTo, "summary": summary, @@ -406,10 +418,10 @@ def deleteAllPosts(baseDir: str,nickname: str, domain: str,boxname: str) -> None """ if boxname!='inbox' and boxname!='outbox' and boxname!='tlblogs': return - boxDir = createPersonDir(nickname,domain,baseDir,boxname) + boxDir=createPersonDir(nickname,domain,baseDir,boxname) for deleteFilename in os.scandir(boxDir): deleteFilename=deleteFilename.name - filePath = os.path.join(boxDir, deleteFilename) + filePath=os.path.join(boxDir,deleteFilename) try: if os.path.isfile(filePath): os.unlink(filePath) @@ -431,7 +443,7 @@ def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \ domain=domain.split(':')[0] if not postId: - statusNumber,published = getStatusNumber() + statusNumber,published=getStatusNumber() postId= \ httpPrefix+'://'+originalDomain+'/users/'+nickname+ \ '/statuses/'+statusNumber @@ -441,7 +453,7 @@ def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \ postJsonObject['object']['id']=postId postJsonObject['object']['atomUri']=postId - boxDir = createPersonDir(nickname,domain,baseDir,boxname) + boxDir=createPersonDir(nickname,domain,baseDir,boxname) filename=boxDir+'/'+postId.replace('/','#')+'.json' saveJson(postJsonObject,filename) return filename @@ -472,7 +484,7 @@ def updateHashtagsIndex(baseDir: str,tag: {},newPostId: str) -> None: if tagline not in open(tagsFilename).read(): try: with open(tagsFilename, 'r+') as tagsFile: - content = tagsFile.read() + content=tagsFile.read() tagsFile.seek(0, 0) tagsFile.write(tagline+content) except Exception as e: @@ -491,7 +503,7 @@ def addSchedulePost(baseDir: str,nickname: str,domain: str, \ if indexStr not in open(scheduleIndexFilename).read(): try: with open(scheduleIndexFilename, 'r+') as scheduleFile: - content = scheduleFile.read() + content=scheduleFile.read() scheduleFile.seek(0, 0) scheduleFile.write(indexStr+'\n'+content) print('DEBUG: scheduled post added to index') @@ -551,7 +563,7 @@ def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \ if tag['name'] not in content: del hashtagsDict[tagName] - statusNumber,published = getStatusNumber() + statusNumber,published=getStatusNumber() postTo='https://www.w3.org/ns/activitystreams#Public' postCC=httpPrefix+'://'+domain+'/users/'+nickname+'/followers' if followersOnly: @@ -659,7 +671,7 @@ def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \ if oc: if oc.get('id'): capabilityIdList=[oc['id']] - newPost = { + newPost={ "@context": postContext, 'id': newPostId+'/activity', 'capability': capabilityIdList, @@ -704,7 +716,7 @@ def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \ newPost['object'],attachImageFilename, \ mediaType,imageDescription,useBlurhash) else: - newPost = { + newPost={ "@context": postContext, 'id': newPostId, 'type': 'Note', @@ -788,16 +800,16 @@ def outboxMessageCreateWrap(httpPrefix: str, \ if port!=80 and port!=443: if ':' not in domain: domain=domain+':'+str(port) - statusNumber,published = getStatusNumber() + statusNumber,published=getStatusNumber() if messageJson.get('published'): - published = messageJson['published'] + published=messageJson['published'] newPostId=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber cc=[] if messageJson.get('cc'): cc=messageJson['cc'] # TODO capabilityUrl=[] - newPost = { + newPost={ "@context": "https://www.w3.org/ns/activitystreams", 'id': newPostId+'/activity', 'capability': capabilityUrl, @@ -1207,7 +1219,7 @@ def threadSendPost(session,postJsonStr: str,federationList: [],\ postResult=None unauthorized=False try: - postResult,unauthorized = \ + postResult,unauthorized= \ postJsonString(session,postJsonStr,federationList, \ inboxUrl,signatureHeaderJson, \ "inbox:write",debug) @@ -1272,8 +1284,8 @@ def sendPost(projectVersion: str, \ handle=httpPrefix+'://'+toDomain+'/@'+toNickname # lookup the inbox for the To handle - wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ - domain,projectVersion) + wfRequest=webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ + domain,projectVersion) if not wfRequest: return 1 @@ -1285,7 +1297,7 @@ def sendPost(projectVersion: str, \ postToBox='tlblogs' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,toPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,toPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix, \ nickname,domain,postToBox) @@ -1305,7 +1317,7 @@ def sendPost(projectVersion: str, \ return 5 # sharedInbox and capabilities are optional - postJsonObject = \ + postJsonObject= \ createPostBase(baseDir,nickname,domain,port, \ toPersonId,cc,httpPrefix,content, \ followersOnly,saveToFile,clientToServer, \ @@ -1328,7 +1340,7 @@ def sendPost(projectVersion: str, \ postJsonStr=json.dumps(postJsonObject) # construct the http header, including the message body digest - signatureHeaderJson = \ + signatureHeaderJson= \ createSignedHeader(privateKeyPem,nickname,domain,port, \ toDomain,toPort, \ postPath,httpPrefix,withDigest,postJsonStr) @@ -1339,7 +1351,7 @@ def sendPost(projectVersion: str, \ sendThreads[0].kill() sendThreads.pop(0) print('WARN: thread killed') - thr = \ + thr= \ threadWithTrace(target=threadSendPost, \ args=(session, \ postJsonStr, \ @@ -1378,7 +1390,7 @@ def sendPostViaServer(projectVersion: str, \ handle=httpPrefix+'://'+fromDomain+'/@'+fromNickname # lookup the inbox for the To handle - wfRequest = \ + wfRequest= \ webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ fromDomain,projectVersion) if not wfRequest: @@ -1391,7 +1403,7 @@ def sendPostViaServer(projectVersion: str, \ postToBox='tlblogs' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,fromNickname, \ fromDomain,postToBox) @@ -1431,7 +1443,7 @@ def sendPostViaServer(projectVersion: str, \ toDomainFull=toDomain+':'+str(toPort) toPersonId=httpPrefix+'://'+toDomainFull+'/users/'+toNickname - postJsonObject = \ + postJsonObject= \ createPostBase(baseDir, \ fromNickname,fromDomain,fromPort, \ toPersonId,cc,httpPrefix,content, \ @@ -1444,9 +1456,11 @@ def sendPostViaServer(projectVersion: str, \ authHeader=createBasicAuthHeader(fromNickname,password) if attachImageFilename: - headers = {'host': fromDomain, \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Authorization': authHeader + } + postResult= \ postImage(session,attachImageFilename,[], \ inboxUrl,headers,"inbox:write") #if not postResult: @@ -1454,10 +1468,12 @@ def sendPostViaServer(projectVersion: str, \ # print('DEBUG: Failed to upload image') # return 9 - headers = {'host': fromDomain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJsonString(session,json.dumps(postJsonObject),[], \ inboxUrl,headers,"inbox:write",debug) #if not postResult: @@ -1578,7 +1594,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \ postToBox='outbox' # get the actor inbox/outbox/capabilities for the To handle - inboxUrl,pubKeyId,pubKey,toPersonId,sharedInboxUrl,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,toPersonId,sharedInboxUrl,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,nickname,domain,postToBox) @@ -1633,7 +1649,7 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \ postJsonStr=json.dumps(postJsonObject) # construct the http header, including the message body digest - signatureHeaderJson = \ + signatureHeaderJson= \ createSignedHeader(privateKeyPem,nickname,domain,port, \ toDomain,toPort, \ postPath,httpPrefix,withDigest,postJsonStr) @@ -1648,14 +1664,15 @@ def sendSignedJson(postJsonObject: {},session,baseDir: str, \ if debug: print('DEBUG: starting thread to send post') pprint(postJsonObject) - thr = threadWithTrace(target=threadSendPost, \ - args=(session, \ - postJsonStr, \ - federationList, \ - inboxUrl,baseDir, \ - signatureHeaderJson.copy(), \ - postLog, - debug),daemon=True) + thr= \ + threadWithTrace(target=threadSendPost, \ + args=(session, \ + postJsonStr, \ + federationList, \ + inboxUrl,baseDir, \ + signatureHeaderJson.copy(), \ + postLog, + debug),daemon=True) sendThreads.append(thr) #thr.start() return 0 @@ -2030,7 +2047,7 @@ def createModeration(baseDir: str,nickname: str,domain: str,port: int, \ httpPrefix: str, \ itemsPerPage: int,headerOnly: bool, \ ocapAlways: bool,pageNumber=None) -> {}: - boxDir = createPersonDir(nickname,domain,baseDir,'inbox') + boxDir=createPersonDir(nickname,domain,baseDir,'inbox') boxname='moderation' if port: @@ -2042,24 +2059,28 @@ def createModeration(baseDir: str,nickname: str,domain: str,port: int, \ pageNumber=1 pageStr='?page='+str(pageNumber) - boxHeader = {'@context': 'https://www.w3.org/ns/activitystreams', - 'first': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true', - 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname, - 'last': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true', - 'totalItems': 0, - 'type': 'OrderedCollection'} - boxItems = {'@context': 'https://www.w3.org/ns/activitystreams', - 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+pageStr, - 'orderedItems': [ - ], - 'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname, - 'type': 'OrderedCollectionPage'} + boxHeader={ + '@context': 'https://www.w3.org/ns/activitystreams', + 'first': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true', + 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname, + 'last': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true', + 'totalItems': 0, + 'type': 'OrderedCollection' + } + boxItems={ + '@context': 'https://www.w3.org/ns/activitystreams', + 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+pageStr, + 'orderedItems': [ + ], + 'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname, + 'type': 'OrderedCollectionPage' + } if isModerator(baseDir,nickname): moderationIndexFile=baseDir+'/accounts/moderation.txt' if os.path.isfile(moderationIndexFile): with open(moderationIndexFile, "r") as f: - lines = f.readlines() + lines=f.readlines() boxHeader['totalItems']=len(lines) if headerOnly: return boxHeader @@ -2237,7 +2258,7 @@ def createSharedInboxIndex(baseDir: str,sharedBoxDir: str, \ # is the actor followed by this account? if not followingHandles: with open(followingFilename, 'r') as followingFile: - followingHandles = followingFile.read() + followingHandles=followingFile.read() if actorNickname+'@'+actorDomain not in followingHandles: continue @@ -2304,7 +2325,7 @@ def addPostToTimeline(filePath: str,boxname: str, \ """ Reads a post from file and decides whether it is valid """ with open(filePath, 'r') as postFile: - postStr = postFile.read() + postStr=postFile.read() return addPostStringToTimeline(postStr,boxname,postsInBox,boxActor) return False @@ -2327,17 +2348,17 @@ def createBoxIndexed(recentPostsCache: {}, \ if boxname!='dm' and boxname!='tlreplies' and \ boxname!='tlmedia' and boxname!='tlblogs' and \ boxname!='tlbookmarks': - boxDir = createPersonDir(nickname,domain,baseDir,boxname) + boxDir=createPersonDir(nickname,domain,baseDir,boxname) else: # extract DMs or replies or media from the inbox - boxDir = createPersonDir(nickname,domain,baseDir,'inbox') + boxDir=createPersonDir(nickname,domain,baseDir,'inbox') announceCacheDir=baseDir+'/cache/announce/'+nickname sharedBoxDir=None if boxname=='inbox' or boxname=='tlreplies' or \ boxname=='tlmedia' or boxname=='tlblogs': - sharedBoxDir = createPersonDir('inbox',domain,baseDir,boxname) + sharedBoxDir=createPersonDir('inbox',domain,baseDir,boxname) # bookmarks timeline is like the inbox but has its own separate index indexBoxName=boxname @@ -2365,18 +2386,22 @@ def createBoxIndexed(recentPostsCache: {}, \ pageStr='?page='+str(pageNumber) except: pass - boxHeader = {'@context': 'https://www.w3.org/ns/activitystreams', - 'first': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true', - 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname, - 'last': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true', - 'totalItems': 0, - 'type': 'OrderedCollection'} - boxItems = {'@context': 'https://www.w3.org/ns/activitystreams', - 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+pageStr, - 'orderedItems': [ - ], - 'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname, - 'type': 'OrderedCollectionPage'} + boxHeader={ + '@context': 'https://www.w3.org/ns/activitystreams', + 'first': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true', + 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname, + 'last': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page=true', + 'totalItems': 0, + 'type': 'OrderedCollection' + } + boxItems={ + '@context': 'https://www.w3.org/ns/activitystreams', + 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+pageStr, + 'orderedItems': [ + ], + 'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname, + 'type': 'OrderedCollectionPage' + } postsInBox=[] @@ -2528,7 +2553,7 @@ def archivePostsForPerson(httpPrefix: str,nickname: str,domain: str,baseDir: str if archiveDir: if not os.path.isdir(archiveDir): os.mkdir(archiveDir) - boxDir = createPersonDir(nickname,domain,baseDir,boxname) + boxDir=createPersonDir(nickname,domain,baseDir,boxname) postsInBox=os.scandir(boxDir) noOfPosts=0 for f in postsInBox: @@ -2624,7 +2649,7 @@ def getPublicPostsOfPerson(baseDir: str,nickname: str,domain: str, \ debug: bool,projectVersion: str) -> None: """ This is really just for test purposes """ - session = createSession(useTor) + session=createSession(useTor) personCache={} cachedWebfingers={} federationList=[] @@ -2635,7 +2660,7 @@ def getPublicPostsOfPerson(baseDir: str,nickname: str,domain: str, \ if ':' not in domain: domainFull=domain+':'+str(port) handle=httpPrefix+"://"+domainFull+"/@"+nickname - wfRequest = \ + wfRequest= \ webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ domain,projectVersion) if not wfRequest: @@ -2644,12 +2669,12 @@ def getPublicPostsOfPerson(baseDir: str,nickname: str,domain: str, \ personUrl,pubKeyId,pubKey,personId,shaedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,nickname,domain,'outbox') - wfResult = json.dumps(wfRequest, indent=2, sort_keys=False) + wfResult=json.dumps(wfRequest,indent=2,sort_keys=False) maxMentions=10 maxEmoji=10 maxAttachments=5 - userPosts = \ + userPosts= \ getPosts(session,personUrl,30,maxMentions,maxEmoji, \ maxAttachments,federationList, \ personCache,raw,simple,debug, \ diff --git a/question.py b/question.py index 3b2eb0948..57eb2d3e3 100644 --- a/question.py +++ b/question.py @@ -1,10 +1,10 @@ -__filename__ = "question.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="question.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os from utils import locatePost @@ -76,7 +76,7 @@ def questionUpdateVotes(baseDir: str,nickname: str,domain: str,replyJson: {}) -> else: # change an entry in the voters file with open(votersFilename, "r") as votersFile: - lines = votersFile.readlines() + lines=votersFile.readlines() newlines=[] saveVotersFile=False for voteLine in lines: @@ -101,7 +101,7 @@ def questionUpdateVotes(baseDir: str,nickname: str,domain: str,replyJson: {}) -> continue totalItems=0 with open(votersFilename, "r") as votersFile: - lines = votersFile.readlines() + lines=votersFile.readlines() for voteLine in lines: if voteLine.endswith(votersFileSeparator+possibleAnswer['name']+'\n'): totalItems+=1 diff --git a/roles.py b/roles.py index 36228aa4d..01c0c7848 100644 --- a/roles.py +++ b/roles.py @@ -1,10 +1,10 @@ -__filename__ = "roles.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="roles.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import json import os @@ -23,10 +23,10 @@ def clearModeratorStatus(baseDir: str) -> None: This could be slow if there are many users, but only happens rarely when moderators are appointed or removed """ - directory = os.fsencode(baseDir+'/accounts/') + directory=os.fsencode(baseDir+'/accounts/') for f in os.scandir(directory): f=f.name - filename = os.fsdecode(f) + filename=os.fsdecode(f) if filename.endswith(".json") and '@' in filename: filename=os.path.join(baseDir+'/accounts/', filename) if '"moderator"' in open(filename).read(): @@ -46,7 +46,7 @@ def addModerator(baseDir: str,nickname: str,domain: str) -> None: if os.path.isfile(moderatorsFile): # is this nickname already in the file? with open(moderatorsFile, "r") as f: - lines = f.readlines() + lines=f.readlines() for moderator in lines: moderator=moderator.strip('\n') if line==nickname: @@ -70,7 +70,7 @@ def removeModerator(baseDir: str,nickname: str): if not os.path.isfile(moderatorsFile): return with open(moderatorsFile, "r") as f: - lines = f.readlines() + lines=f.readlines() with open(moderatorsFile, "w") as f: for moderator in lines: moderator=moderator.strip('\n') @@ -229,14 +229,17 @@ def sendRoleViaServer(baseDir: str,session, \ if ':' not in delegatorDomain: delegatorDomainFull=delegatorDomain+':'+str(fromPort) - toUrl = httpPrefix+'://'+delegatorDomainFull+'/users/'+nickname - ccUrl = httpPrefix+'://'+delegatorDomainFull+'/users/'+delegatorNickname+'/followers' + toUrl= \ + httpPrefix+'://'+delegatorDomainFull+'/users/'+nickname + ccUrl= \ + httpPrefix+'://'+delegatorDomainFull+'/users/'+ \ + delegatorNickname+'/followers' if role: roleStr=project.lower()+';'+role.lower() else: roleStr=project.lower()+';' - newRoleJson = { + newRoleJson={ 'type': 'Delegate', 'actor': httpPrefix+'://'+delegatorDomainFull+'/users/'+delegatorNickname, 'object': { @@ -253,8 +256,8 @@ def sendRoleViaServer(baseDir: str,session, \ handle=httpPrefix+'://'+delegatorDomainFull+'/@'+delegatorNickname # lookup the inbox for the To handle - wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ - delegatorDomain,projectVersion) + wfRequest=webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ + delegatorDomain,projectVersion) if not wfRequest: if debug: print('DEBUG: announce webfinger failed for '+handle) @@ -263,7 +266,7 @@ def sendRoleViaServer(baseDir: str,session, \ postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix, \ delegatorNickname,delegatorDomain,postToBox) @@ -279,10 +282,12 @@ def sendRoleViaServer(baseDir: str,session, \ authHeader=createBasicAuthHeader(delegatorNickname,password) - headers = {'host': delegatorDomain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': delegatorDomain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,newRoleJson,[],inboxUrl,headers,"inbox:write") #if not postResult: # if debug: diff --git a/schedule.py b/schedule.py index 0345358b7..895e97523 100644 --- a/schedule.py +++ b/schedule.py @@ -1,10 +1,10 @@ -__filename__ = "schedule.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="schedule.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import time @@ -70,7 +70,7 @@ def updatePostSchedule(baseDir: str,handle: str,httpd,maxScheduledPosts: int) -> # set the published time # If this is not recent then http checks on the receiving side # will reject it - statusNumber,published = getStatusNumber() + statusNumber,published=getStatusNumber() if postJsonObject.get('published'): postJsonObject['published']=published if postJsonObject.get('object'): diff --git a/session.py b/session.py index eed603926..f6398737e 100644 --- a/session.py +++ b/session.py @@ -1,10 +1,10 @@ -__filename__ = "session.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="session.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import sys @@ -15,11 +15,11 @@ import json baseDirectory=None def createSession(onionRoute: bool): - session = requests.session() + session=requests.session() if onionRoute: - session.proxies = {} - session.proxies['http'] = 'socks5h://localhost:9050' - session.proxies['https'] = 'socks5h://localhost:9050' + session.proxies={} + session.proxies['http']='socks5h://localhost:9050' + session.proxies['https']='socks5h://localhost:9050' return session def getJson(session,url: str,headers: {},params: {}, \ @@ -94,11 +94,12 @@ def postJsonString(session,postJsonStr: str, \ print('postJson: '+inboxUrl+' not permitted by capabilities') return None,None - postResult = session.post(url = inboxUrl, data = postJsonStr, headers=headers) + postResult= \ + session.post(url=inboxUrl,data=postJsonStr,headers=headers) if postResult.status_code<200 or postResult.status_code>202: #if postResult.status_code==400: # headers['content-type']='application/ld+json' - # postResult = session.post(url = inboxUrl, data = postJsonStr, headers=headers) + # postResult=session.post(url=inboxUrl,data=postJsonStr,headers=headers) # if not (postResult.status_code<200 or postResult.status_code>202): # return True if postResult.status_code>=400 and postResult.status_code<=405 and \ @@ -140,8 +141,8 @@ def postImage(session,attachImageFilename: str,federationList: [], \ headers['Content-type']=contentType with open(attachImageFilename, 'rb') as avFile: - mediaBinary = avFile.read() - postResult = session.post(url=inboxUrl, data=mediaBinary, headers=headers) + mediaBinary=avFile.read() + postResult=session.post(url=inboxUrl,data=mediaBinary,headers=headers) if postResult: return postResult.text return None diff --git a/shares.py b/shares.py index 350a2d54f..d85ad006f 100644 --- a/shares.py +++ b/shares.py @@ -1,10 +1,10 @@ -__filename__ = "shares.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="shares.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import json import os @@ -142,7 +142,7 @@ def addShare(baseDir: str, \ os.remove(imageFilename) imageUrl=httpPrefix+'://'+domainFull+'/sharefiles/'+nickname+'/'+itemID+'.gif' - sharesJson[itemID] = { + sharesJson[itemID]={ "displayName": displayName, "summary": summary, "imageUrl": imageUrl, @@ -261,25 +261,27 @@ def getSharesFeedForPerson(baseDir: str, \ sharesJson=loadJson(sharesFilename) if sharesJson: noOfShares=len(sharesJson.items()) - shares = { + shares={ '@context': 'https://www.w3.org/ns/activitystreams', 'first': httpPrefix+'://'+domain+'/users/'+nickname+'/shares?page=1', 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/shares', 'totalItems': str(noOfShares), - 'type': 'OrderedCollection'} + 'type': 'OrderedCollection' + } return shares if not pageNumber: pageNumber=1 nextPageNumber=int(pageNumber+1) - shares = { + shares={ '@context': 'https://www.w3.org/ns/activitystreams', 'id': httpPrefix+'://'+domain+'/users/'+nickname+'/shares?page='+str(pageNumber), 'orderedItems': [], 'partOf': httpPrefix+'://'+domain+'/users/'+nickname+'/shares', 'totalItems': 0, - 'type': 'OrderedCollectionPage'} + 'type': 'OrderedCollectionPage' + } if not os.path.isfile(sharesFilename): print("test5") @@ -332,10 +334,10 @@ def sendShareViaServer(baseDir,session, \ if ':' not in fromDomain: fromDomainFull=fromDomain+':'+str(fromPort) - toUrl = 'https://www.w3.org/ns/activitystreams#Public' - ccUrl = httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers' + toUrl='https://www.w3.org/ns/activitystreams#Public' + ccUrl=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/followers' - newShareJson = { + newShareJson={ "@context": "https://www.w3.org/ns/activitystreams", 'type': 'Add', 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname, @@ -358,8 +360,8 @@ def sendShareViaServer(baseDir,session, \ handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname # lookup the inbox for the To handle - wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ - fromDomain,projectVersion) + wfRequest=webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ + fromDomain,projectVersion) if not wfRequest: if debug: print('DEBUG: announce webfinger failed for '+handle) @@ -368,7 +370,7 @@ def sendShareViaServer(baseDir,session, \ postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix, \ fromNickname,fromDomain,postToBox) @@ -385,15 +387,19 @@ def sendShareViaServer(baseDir,session, \ authHeader=createBasicAuthHeader(fromNickname,password) if imageFilename: - headers = {'host': fromDomain, \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Authorization': authHeader + } + postResult= \ postImage(session,imageFilename,[],inboxUrl.replace('/'+postToBox,'/shares'),headers,"inbox:write") - headers = {'host': fromDomain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,newShareJson,[],inboxUrl,headers,"inbox:write") #if not postResult: # if debug: @@ -424,10 +430,10 @@ def sendUndoShareViaServer(baseDir: str,session, \ if ':' not in fromDomain: fromDomainFull=fromDomain+':'+str(fromPort) - toUrl = 'https://www.w3.org/ns/activitystreams#Public' - ccUrl = httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers' + toUrl='https://www.w3.org/ns/activitystreams#Public' + ccUrl=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/followers' - undoShareJson = { + undoShareJson={ "@context": "https://www.w3.org/ns/activitystreams", 'type': 'Remove', 'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname, @@ -445,8 +451,8 @@ def sendUndoShareViaServer(baseDir: str,session, \ handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname # lookup the inbox for the To handle - wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ - fromDomain,projectVersion) + wfRequest=webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ + fromDomain,projectVersion) if not wfRequest: if debug: print('DEBUG: announce webfinger failed for '+handle) @@ -455,7 +461,7 @@ def sendUndoShareViaServer(baseDir: str,session, \ postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix, \ fromNickname,fromDomain,postToBox) @@ -471,10 +477,12 @@ def sendUndoShareViaServer(baseDir: str,session, \ authHeader=createBasicAuthHeader(fromNickname,password) - headers = {'host': fromDomain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': fromDomain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,undoShareJson,[],inboxUrl,headers,"inbox:write") #if not postResult: # if debug: diff --git a/skills.py b/skills.py index 6a707b5c7..43b5e992c 100644 --- a/skills.py +++ b/skills.py @@ -1,10 +1,10 @@ -__filename__ = "skills.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="skills.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import json import os @@ -109,14 +109,14 @@ def sendSkillViaServer(baseDir: str,session,nickname: str,password: str, if ':' not in domain: domainFull=domain+':'+str(port) - toUrl = httpPrefix+'://'+domainFull+'/users/'+nickname - ccUrl = httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers' + toUrl=httpPrefix+'://'+domainFull+'/users/'+nickname + ccUrl=httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers' if skillLevelPercent: skillStr=skill+';'+str(skillLevelPercent) else: skillStr=skill+';0' - newSkillJson = { + newSkillJson={ 'type': 'Skill', 'actor': httpPrefix+'://'+domainFull+'/users/'+nickname, 'object': '"'+skillStr+'"', @@ -127,8 +127,8 @@ def sendSkillViaServer(baseDir: str,session,nickname: str,password: str, handle=httpPrefix+'://'+domainFull+'/@'+nickname # lookup the inbox for the To handle - wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ - domain,projectVersion) + wfRequest=webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ + domain,projectVersion) if not wfRequest: if debug: print('DEBUG: announce webfinger failed for '+handle) @@ -137,7 +137,7 @@ def sendSkillViaServer(baseDir: str,session,nickname: str,password: str, postToBox='outbox' # get the actor inbox for the To handle - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,nickname,domain,postToBox) @@ -152,10 +152,12 @@ def sendSkillViaServer(baseDir: str,session,nickname: str,password: str, authHeader=createBasicAuthHeader(Nickname,password) - headers = {'host': domain, \ - 'Content-type': 'application/json', \ - 'Authorization': authHeader} - postResult = \ + headers={ + 'host': domain, \ + 'Content-type': 'application/json', \ + 'Authorization': authHeader + } + postResult= \ postJson(session,newSkillJson,[],inboxUrl,headers,"inbox:write") #if not postResult: # if debug: diff --git a/ssb.py b/ssb.py index 9fbe874b9..f52339033 100644 --- a/ssb.py +++ b/ssb.py @@ -1,10 +1,10 @@ -__filename__ = "ssb.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="ssb.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import json diff --git a/tests.py b/tests.py index 7b2d6ebcd..2bd3fb4ac 100644 --- a/tests.py +++ b/tests.py @@ -1,10 +1,10 @@ -__filename__ = "tests.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="tests.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import base64 import time @@ -77,9 +77,9 @@ from content import addHtmlTags from content import removeLongWords from theme import setCSSparam -testServerAliceRunning = False -testServerBobRunning = False -testServerEveRunning = False +testServerAliceRunning=False +testServerBobRunning=False +testServerEveRunning=False thrAlice=None thrBob=None thrEve=None @@ -103,7 +103,11 @@ def testHttpsigBase(withDigest): privateKeyPem,publicKeyPem,person,wfEndpoint= \ createPerson(path,nickname,domain,port,httpPrefix,False,password) assert privateKeyPem - messageBodyJson = {"a key": "a value", "another key": "A string","yet another key": "Another string"} + messageBodyJson={ + "a key": "a value", + "another key": "A string", + "yet another key": "Another string" + } messageBodyJsonStr=json.dumps(messageBodyJson) headersDomain=domain @@ -115,23 +119,33 @@ def testHttpsigBase(withDigest): dateStr=strftime("%a, %d %b %Y %H:%M:%S %Z", gmtime()) boxpath='/inbox' if not withDigest: - headers = {'host': headersDomain,'date': dateStr,'content-type': 'application/json'} - signatureHeader = \ + headers={ + 'host': headersDomain, + 'date': dateStr, + 'content-type': 'application/json' + } + signatureHeader= \ signPostHeaders(dateStr,privateKeyPem, nickname, \ domain, port, \ domain, port, \ boxpath, httpPrefix, None) else: - bodyDigest = messageContentDigest(messageBodyJsonStr) + bodyDigest=messageContentDigest(messageBodyJsonStr) contentLength=len(messageBodyJsonStr) - headers = {'host': headersDomain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': contentType,'content-length': str(contentLength)} - signatureHeader = \ + headers={ + 'host': headersDomain, + 'date': dateStr, + 'digest': f'SHA-256={bodyDigest}', + 'content-type': contentType, + 'content-length': str(contentLength) + } + signatureHeader= \ signPostHeaders(dateStr,privateKeyPem, nickname, \ domain, port, \ domain, port, \ boxpath, httpPrefix, messageBodyJsonStr) - headers['signature'] = signatureHeader + headers['signature']=signatureHeader assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \ boxpath,False,None, \ messageBodyJsonStr,False) @@ -149,14 +163,24 @@ def testHttpsigBase(withDigest): messageBodyJsonStr,False) == False if not withDigest: # fake domain - headers = {'host': 'bogon.domain','date': dateStr,'content-type': 'application/json'} + headers={ + 'host': 'bogon.domain', + 'date': dateStr, + 'content-type': 'application/json' + } else: # correct domain but fake message - messageBodyJsonStr = '{"a key": "a value", "another key": "Fake GNUs", "yet another key": "More Fake GNUs"}' + messageBodyJsonStr='{"a key": "a value", "another key": "Fake GNUs", "yet another key": "More Fake GNUs"}' contentLength=len(messageBodyJsonStr) - bodyDigest = messageContentDigest(messageBodyJsonStr) - headers = {'host': domain,'date': dateStr,'digest': f'SHA-256={bodyDigest}','content-type': contentType,'content-length': str(contentLength)} - headers['signature'] = signatureHeader + bodyDigest=messageContentDigest(messageBodyJsonStr) + headers={ + 'host': domain, + 'date': dateStr, + 'digest': f'SHA-256={bodyDigest}', + 'content-type': contentType, + 'content-length': str(contentLength) + } + headers['signature']=signatureHeader assert verifyPostHeaders(httpPrefix,publicKeyPem,headers, \ boxpath,True,None, \ messageBodyJsonStr,False) == False @@ -184,7 +208,8 @@ def testThreadsFunction(param: str): def testThreads(): print('testThreads') - thr = threadWithTrace(target=testThreadsFunction,args=('test',),daemon=True) + thr= \ + threadWithTrace(target=testThreadsFunction,args=('test',),daemon=True) thr.start() assert thr.isAlive()==True time.sleep(1) @@ -235,7 +260,7 @@ def createServerAlice(path: str,domain: str,port: int,bobAddress: str,federation "In the gardens of memory, in the palace of dreams, that is where you and I shall meet", \ False, True, clientToServer,None,None,useBlurhash) global testServerAliceRunning - testServerAliceRunning = True + testServerAliceRunning=True maxMentions=10 maxEmoji=10 onionDomain=None @@ -292,7 +317,7 @@ def createServerBob(path: str,domain: str,port: int,aliceAddress: str,federation "Quantum physics is a bit of a passion of mine", \ False, True, clientToServer,None,None,useBlurhash) global testServerBobRunning - testServerBobRunning = True + testServerBobRunning=True maxMentions=10 maxEmoji=10 onionDomain=None @@ -329,7 +354,7 @@ def createServerEve(path: str,domain: str,port: int,federationList: [], \ deleteAllPosts(path,nickname,domain,'inbox') deleteAllPosts(path,nickname,domain,'outbox') global testServerEveRunning - testServerEveRunning = True + testServerEveRunning=True maxMentions=10 maxEmoji=10 onionDomain=None @@ -346,8 +371,8 @@ def testPostMessageBetweenServers(): global testServerAliceRunning global testServerBobRunning - testServerAliceRunning = False - testServerBobRunning = False + testServerAliceRunning=False + testServerBobRunning=False httpPrefix='http' useTor=False @@ -380,7 +405,7 @@ def testPostMessageBetweenServers(): time.sleep(1) thrAlice.kill() - thrAlice = \ + thrAlice= \ threadWithTrace(target=createServerAlice, \ args=(aliceDir,aliceDomain,alicePort,bobAddress, \ federationList,False,False, \ @@ -393,7 +418,7 @@ def testPostMessageBetweenServers(): time.sleep(1) thrBob.kill() - thrBob = \ + thrBob= \ threadWithTrace(target=createServerBob, \ args=(bobDir,bobDomain,bobPort,aliceAddress, \ federationList,False,False, \ @@ -413,11 +438,11 @@ def testPostMessageBetweenServers(): print('\n\n*******************************************************') print('Alice sends to Bob') os.chdir(aliceDir) - sessionAlice = createSession(useTor) + sessionAlice=createSession(useTor) inReplyTo=None inReplyToAtomUri=None subject=None - alicePostLog = [] + alicePostLog=[] followersOnly=False saveToFile=True clientToServer=False @@ -433,7 +458,7 @@ def testPostMessageBetweenServers(): outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox' assert len([name for name in os.listdir(outboxPath) if os.path.isfile(os.path.join(outboxPath, name))])==0 - sendResult = \ + sendResult= \ sendPost(__version__, \ sessionAlice,aliceDir,'alice',aliceDomain,alicePort, \ 'bob',bobDomain,bobPort,ccUrl,httpPrefix, \ @@ -486,8 +511,8 @@ def testPostMessageBetweenServers(): followPerson(aliceDir,'alice',aliceDomain,'bob', \ bobDomain+':'+str(bobPort),federationList,False) - sessionBob = createSession(useTor) - bobPostLog = [] + sessionBob=createSession(useTor) + bobPostLog=[] bobPersonCache={} bobCachedWebfingers={} statusNumber=None @@ -563,8 +588,8 @@ def testFollowBetweenServers(): global testServerAliceRunning global testServerBobRunning - testServerAliceRunning = False - testServerBobRunning = False + testServerAliceRunning=False + testServerBobRunning=False httpPrefix='http' useTor=False @@ -597,7 +622,7 @@ def testFollowBetweenServers(): time.sleep(1) thrAlice.kill() - thrAlice = \ + thrAlice= \ threadWithTrace(target=createServerAlice, \ args=(aliceDir,aliceDomain,alicePort,bobAddress, \ federationList,False,False, \ @@ -610,7 +635,7 @@ def testFollowBetweenServers(): time.sleep(1) thrBob.kill() - thrBob = \ + thrBob= \ threadWithTrace(target=createServerBob, \ args=(bobDir,bobDomain,bobPort,aliceAddress, \ federationList,False,False, \ @@ -638,11 +663,11 @@ def testFollowBetweenServers(): print('*********************************************************') print('Alice sends a follow request to Bob') os.chdir(aliceDir) - sessionAlice = createSession(useTor) + sessionAlice=createSession(useTor) inReplyTo=None inReplyToAtomUri=None subject=None - alicePostLog = [] + alicePostLog=[] followersOnly=False saveToFile=True clientToServer=False @@ -650,7 +675,7 @@ def testFollowBetweenServers(): alicePersonCache={} aliceCachedWebfingers={} alicePostLog=[] - sendResult = \ + sendResult= \ sendFollowRequest(sessionAlice,aliceDir, \ 'alice',aliceDomain,alicePort,httpPrefix, \ 'bob',bobDomain,bobPort,httpPrefix, \ @@ -671,13 +696,13 @@ def testFollowBetweenServers(): print('\n\n*********************************************************') print('Alice sends a message to Bob') - alicePostLog = [] + alicePostLog=[] alicePersonCache={} aliceCachedWebfingers={} alicePostLog=[] useBlurhash=False isArticle=False - sendResult = \ + sendResult= \ sendPost(__version__, \ sessionAlice,aliceDir,'alice',aliceDomain,alicePort, \ 'bob',bobDomain,bobPort,ccUrl, \ @@ -860,7 +885,7 @@ def testFollows(): followPerson(baseDir,nickname,domain,'batman','mesh.com',federationList,False) followPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList,False) - f = open(baseDir+'/accounts/'+nickname+'@'+domain+'/following.txt', "r") + f=open(baseDir+'/accounts/'+nickname+'@'+domain+'/following.txt', "r") domainFound=False for followingDomain in f: testDomain=followingDomain.split('@')[1].replace('\n','') @@ -887,7 +912,7 @@ def testFollows(): followerOfPerson(baseDir,nickname,domain,'batman','mesh.com',federationList,False) followerOfPerson(baseDir,nickname,domain,'giraffe','trees.com',federationList,False) - f = open(baseDir+'/accounts/'+nickname+'@'+domain+'/followers.txt', "r") + f=open(baseDir+'/accounts/'+nickname+'@'+domain+'/followers.txt', "r") for followerDomain in f: testDomain=followerDomain.split('@')[1].replace('\n','') if testDomain not in federationList: @@ -949,7 +974,7 @@ def testDelegateRoles(): httpPrefix='http' project='artechoke' role='delegator' - newRoleJson = { + newRoleJson={ 'type': 'Delegate', 'actor': httpPrefix+'://'+domain+'/users/'+nickname, 'object': { @@ -970,7 +995,7 @@ def testDelegateRoles(): assert '"delegator"' in open(baseDir+'/accounts/'+nickname+'@'+domain+'.json').read() assert '"delegator"' in open(baseDir+'/accounts/'+nicknameDelegated+'@'+domain+'.json').read() - newRoleJson = { + newRoleJson={ 'type': 'Delegate', 'actor': httpPrefix+'://'+domain+'/users/'+nicknameDelegated, 'object': { @@ -1030,8 +1055,8 @@ def testClientToServer(): global testServerAliceRunning global testServerBobRunning - testServerAliceRunning = False - testServerBobRunning = False + testServerAliceRunning=False + testServerBobRunning=False httpPrefix='http' useTor=False @@ -1064,7 +1089,7 @@ def testClientToServer(): time.sleep(1) thrAlice.kill() - thrAlice = \ + thrAlice= \ threadWithTrace(target=createServerAlice, \ args=(aliceDir,aliceDomain,alicePort,bobAddress, \ federationList,False,False, \ @@ -1077,7 +1102,7 @@ def testClientToServer(): time.sleep(1) thrBob.kill() - thrBob = \ + thrBob= \ threadWithTrace(target=createServerBob, \ args=(bobDir,bobDomain,bobPort,aliceAddress, \ federationList,False,False, \ @@ -1103,7 +1128,7 @@ def testClientToServer(): print('\n\n*******************************************************') print('Alice sends to Bob via c2s') - sessionAlice = createSession(useTor) + sessionAlice=createSession(useTor) followersOnly=False attachedImageFilename=baseDir+'/img/logo.png' mediaType=getAttachmentMediaType(attachedImageFilename) @@ -1214,7 +1239,7 @@ def testClientToServer(): print('\n\nBob likes the post') - sessionBob = createSession(useTor) + sessionBob=createSession(useTor) password='bobpass' outboxPath=bobDir+'/accounts/bob@'+bobDomain+'/outbox' inboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/inbox' @@ -1262,7 +1287,7 @@ def testClientToServer(): inboxPath=bobDir+'/accounts/bob@'+bobDomain+'/inbox' outboxPath=aliceDir+'/accounts/alice@'+aliceDomain+'/outbox' - postsBefore = len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))]) + postsBefore=len([name for name in os.listdir(inboxPath) if os.path.isfile(os.path.join(inboxPath, name))]) print('\n\nAlice deletes her post: '+outboxPostId+' '+str(postsBefore)) password='alicepass' sendDeleteViaServer(aliceDir,sessionAlice,'alice',password, @@ -1418,7 +1443,7 @@ def testGetStatusNumber(): print('testGetStatusNumber') prevStatusNumber=None for i in range(1,20): - statusNumber,published = getStatusNumber() + statusNumber,published=getStatusNumber() if prevStatusNumber: assert len(statusNumber) == 18 assert int(statusNumber) > prevStatusNumber diff --git a/theme.py b/theme.py index ee4cc8f0c..29f466c2b 100644 --- a/theme.py +++ b/theme.py @@ -1,10 +1,10 @@ -__filename__ = "theme.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="theme.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os from utils import loadJson diff --git a/threads.py b/threads.py index 534cb0b70..34c5347cb 100644 --- a/threads.py +++ b/threads.py @@ -1,10 +1,10 @@ -__filename__ = "threads.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="threads.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import threading import os @@ -20,9 +20,9 @@ class threadWithTrace(threading.Thread): tries=0 while tries<3: try: - self._args, self._keywords = args, keywords - threading.Thread.__init__(self, *self._args, **self._keywords) - self.killed = False + self._args,self._keywords=args,keywords + threading.Thread.__init__(self,*self._args,**self._keywords) + self.killed=False break except Exception as e: print('ERROR: threads.py/__init__ failed - '+str(e)) @@ -33,8 +33,8 @@ class threadWithTrace(threading.Thread): tries=0 while tries<3: try: - self.__run_backup = self.run - self.run = self.__run + self.__run_backup=self.run + self.run=self.__run threading.Thread.start(self) break except Exception as e: @@ -47,7 +47,7 @@ class threadWithTrace(threading.Thread): def __run(self): sys.settrace(self.globaltrace) self.__run_backup() - self.run = self.__run_backup + self.run=self.__run_backup def globaltrace(self, frame, event, arg): if event == 'call': @@ -62,7 +62,7 @@ class threadWithTrace(threading.Thread): return self.localtrace def kill(self): - self.killed = True + self.killed=True def clone(self,fn): return threadWithTrace(target=fn, \ diff --git a/tox.py b/tox.py index 248fd1505..e481279c7 100644 --- a/tox.py +++ b/tox.py @@ -1,10 +1,10 @@ -__filename__ = "tox.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="tox.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import json diff --git a/utils.py b/utils.py index 2cdbea459..0bf329e1e 100644 --- a/utils.py +++ b/utils.py @@ -1,10 +1,10 @@ -__filename__ = "utils.py" -__author__ = "Bob Mottram" -__license__ = "AGPL3+" -__version__ = "1.1.0" -__maintainer__ = "Bob Mottram" -__email__ = "bob@freedombone.net" -__status__ = "Production" +__filename__="utils.py" +__author__="Bob Mottram" +__license__="AGPL3+" +__version__="1.1.0" +__maintainer__="Bob Mottram" +__email__="bob@freedombone.net" +__status__="Production" import os import time @@ -198,17 +198,21 @@ def getDomainFromActor(actor: str) -> (str,int): """ port=None if '/profile/' in actor: - domain = actor.split('/profile/')[0].replace('https://','').replace('http://','').replace('i2p://','').replace('dat://','') + domain= \ + actor.split('/profile/')[0].replace('https://','').replace('http://','').replace('i2p://','').replace('dat://','') else: if '/channel/' in actor: - domain = actor.split('/channel/')[0].replace('https://','').replace('http://','').replace('i2p://','').replace('dat://','') + domain= \ + actor.split('/channel/')[0].replace('https://','').replace('http://','').replace('i2p://','').replace('dat://','') else: if '/users/' not in actor: - domain = actor.replace('https://','').replace('http://','').replace('i2p://','').replace('dat://','') + domain= \ + actor.replace('https://','').replace('http://','').replace('i2p://','').replace('dat://','') if '/' in actor: domain=domain.split('/')[0] else: - domain = actor.split('/users/')[0].replace('https://','').replace('http://','').replace('i2p://','').replace('dat://','') + domain= \ + actor.split('/users/')[0].replace('https://','').replace('http://','').replace('i2p://','').replace('dat://','') if ':' in domain: portStr=domain.split(':')[1] if not portStr.isdigit(): @@ -252,7 +256,7 @@ def followPerson(baseDir: str,nickname: str, domain: str, \ # remove them from the unfollowed file newLines='' with open(unfollowedFilename, "r") as f: - lines = f.readlines() + lines=f.readlines() for line in lines: if handleToFollow not in line: newLines+=line @@ -271,7 +275,7 @@ def followPerson(baseDir: str,nickname: str, domain: str, \ # prepend to follow file try: with open(filename, 'r+') as followFile: - content = followFile.read() + content=followFile.read() followFile.seek(0, 0) followFile.write(handleToFollow+'\n'+content) if debug: @@ -347,7 +351,7 @@ def removeModerationPostFromIndex(baseDir: str,postUrl: str,debug: bool) -> None postId=postUrl.replace('/activity','') if postId in open(moderationIndexFile).read(): with open(moderationIndexFile, "r") as f: - lines = f.readlines() + lines=f.readlines() with open(moderationIndexFile, "w+") as f: for line in lines: if line.strip("\n") != postId: @@ -485,7 +489,7 @@ def noOfActiveAccountsMonthly(baseDir: str,months: int) -> bool: lastUsedFilename=baseDir+'/accounts/'+account+'/.lastUsed' if os.path.isfile(lastUsedFilename): with open(lastUsedFilename, 'r') as lastUsedFile: - lastUsed = lastUsedFile.read() + lastUsed=lastUsedFile.read() if lastUsed.isdigit(): timeDiff=(currTime-int(lastUsed)) if timeDiff'+file.read()+'
' + loginText=''+file.read()+'
' cssFilename=baseDir+'/epicyon-login.css' if os.path.isfile(baseDir+'/login.css'): cssFilename=baseDir+'/login.css' with open(cssFilename, 'r') as cssFile: - loginCSS = cssFile.read() + loginCSS=cssFile.read() # show the register button registerButtonStr='' @@ -1065,7 +1085,7 @@ def htmlLogin(translate: {},baseDir: str,autocomplete=True) -> str: def htmlTermsOfService(baseDir: str,httpPrefix: str,domainFull: str) -> str: """Show the terms of service screen """ - adminNickname = getConfigParam(baseDir,'admin') + adminNickname=getConfigParam(baseDir,'admin') if not os.path.isfile(baseDir+'/accounts/tos.txt'): copyfile(baseDir+'/default_tos.txt',baseDir+'/accounts/tos.txt') if os.path.isfile(baseDir+'/img/login-background.png'): @@ -1076,14 +1096,14 @@ def htmlTermsOfService(baseDir: str,httpPrefix: str,domainFull: str) -> str: TOSText='Terms of Service go here.' if os.path.isfile(baseDir+'/accounts/tos.txt'): with open(baseDir+'/accounts/tos.txt', 'r') as file: - TOSText = file.read() + TOSText=file.read() TOSForm='' cssFilename=baseDir+'/epicyon-profile.css' if os.path.isfile(baseDir+'/epicyon.css'): cssFilename=baseDir+'/epicyon.css' with open(cssFilename, 'r') as cssFile: - termsCSS = cssFile.read() + termsCSS=cssFile.read() if httpPrefix!='https': termsCSS=termsCSS.replace('https://',httpPrefix+'://') @@ -1100,7 +1120,7 @@ def htmlTermsOfService(baseDir: str,httpPrefix: str,domainFull: str) -> str: def htmlAbout(baseDir: str,httpPrefix: str,domainFull: str) -> str: """Show the about screen """ - adminNickname = getConfigParam(baseDir,'admin') + adminNickname=getConfigParam(baseDir,'admin') if not os.path.isfile(baseDir+'/accounts/about.txt'): copyfile(baseDir+'/default_about.txt',baseDir+'/accounts/about.txt') if os.path.isfile(baseDir+'/img/login-background.png'): @@ -1111,14 +1131,14 @@ def htmlAbout(baseDir: str,httpPrefix: str,domainFull: str) -> str: aboutText='Information about this instance goes here.' if os.path.isfile(baseDir+'/accounts/about.txt'): with open(baseDir+'/accounts/about.txt', 'r') as file: - aboutText = file.read() + aboutText=file.read() aboutForm='' cssFilename=baseDir+'/epicyon-profile.css' if os.path.isfile(baseDir+'/epicyon.css'): cssFilename=baseDir+'/epicyon.css' with open(cssFilename, 'r') as cssFile: - termsCSS = cssFile.read() + termsCSS=cssFile.read() if httpPrefix!='http': termsCSS=termsCSS.replace('https://',httpPrefix+'://') @@ -1231,13 +1251,13 @@ def htmlNewPost(mediaInstance: bool,translate: {}, \ if os.path.isfile(baseDir+'/accounts/newpost.txt'): with open(baseDir+'/accounts/newpost.txt', 'r') as file: - newPostText = ''+file.read()+'
' + newPostText=''+file.read()+'
' cssFilename=baseDir+'/epicyon-profile.css' if os.path.isfile(baseDir+'/epicyon.css'): cssFilename=baseDir+'/epicyon.css' with open(cssFilename, 'r') as cssFile: - newPostCSS = cssFile.read() + newPostCSS=cssFile.read() if httpPrefix!='https': newPostCSS=newPostCSS.replace('https://',httpPrefix+'://') @@ -2017,7 +2037,7 @@ def htmlProfile(defaultTimeline: str, \ if os.path.isfile(baseDir+'/epicyon.css'): cssFilename=baseDir+'/epicyon.css' with open(cssFilename, 'r') as cssFile: - profileStyle = \ + profileStyle= \ cssFile.read().replace('image.png', \ profileJson['image']['url']) @@ -2085,7 +2105,7 @@ def individualFollowAsHtml(translate: {}, \ if not avatarUrl: avatarUrl=followUrl+'/avatar.png' if domain not in followUrl: - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl2,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl2,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,nickname,domain,'outbox') if avatarUrl2: @@ -2131,8 +2151,8 @@ def cursorToEndOfMessageScript() -> str: This avoids the cursor being in the wrong position when replying """ script='function focusOnMessage() {\n' - script+=" var replyTextArea = document.getElementById('message');\n" - script+=' val = replyTextArea.value;\n' + script+=" var replyTextArea=document.getElementById('message');\n" + script+=' val=replyTextArea.value;\n' script+=' if ((val.length>0) && (val.charAt(val.length-1) != " ")) {\n' script+=' val += " ";\n' script+=' }\n' @@ -2140,8 +2160,8 @@ def cursorToEndOfMessageScript() -> str: script+=' replyTextArea.value="";\n' script+=' replyTextArea.value=val;\n' script+='}\n' - script+="var replyTextArea = document.getElementById('message')\n" - script+='replyTextArea.onFocus = function() {\n' + script+="var replyTextArea=document.getElementById('message')\n" + script+='replyTextArea.onFocus=function() {\n' script+=' focusOnMessage();' script+='}\n' return script @@ -2150,11 +2170,11 @@ def contentWarningScript() -> str: """Returns a script used for content warnings """ script='function showContentWarning(postID) {\n' - script+=' var x = document.getElementById(postID);\n' + script+=' var x=document.getElementById(postID);\n' script+=' if (x.style.display !== "block") {\n' - script+=' x.style.display = "block";\n' + script+=' x.style.display="block";\n' script+=' } else {\n' - script+=' x.style.display = "none";\n' + script+=' x.style.display="none";\n' script+=' }\n' script+='}\n' return script @@ -2164,8 +2184,8 @@ def contentWarningScriptOpen() -> str: The warning is open by default. This is used on blog replies. """ script='function showContentWarning(postID) {\n' - script+=' var x = document.getElementById(postID);\n' - script+=' x.style.display = "block";\n' + script+=' var x=document.getElementById(postID);\n' + script+=' x.style.display="block";\n' script+='}\n' return script @@ -2531,7 +2551,7 @@ def loadIndividualPostAsHtmlFromCache(baseDir: str,nickname: str,domain: str, \ while tries<3: try: with open(cachedPostFilename, 'r') as file: - postHtml = file.read() + postHtml=file.read() break except Exception as e: print(e) @@ -2847,7 +2867,7 @@ def individualPostAsHtml(recentPostsCache: {},maxRecentPosts: int, \ avatarUrl=postActor+'/avatar.png' if fullDomain not in postActor: - inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl2,displayName = \ + inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl2,displayName= \ getPersonBox(baseDir,session,wfRequest,personCache, \ projectVersion,httpPrefix,nickname,domain,'outbox') if avatarUrl2: @@ -3267,15 +3287,15 @@ def individualPostAsHtml(recentPostsCache: {},maxRecentPosts: int, \ publishedStr=postJsonObject['object']['published'] if '.' not in publishedStr: if '+' not in publishedStr: - datetimeObject = \ + datetimeObject= \ datetime.strptime(publishedStr,"%Y-%m-%dT%H:%M:%SZ") else: - datetimeObject = \ + datetimeObject= \ datetime.strptime(publishedStr.split('+')[0]+'Z', \ "%Y-%m-%dT%H:%M:%SZ") else: publishedStr=publishedStr.replace('T',' ').split('.')[0] - datetimeObject = parse(publishedStr) + datetimeObject=parse(publishedStr) publishedStr=datetimeObject.strftime("%a %b %d, %H:%M") publishedLink=messageId @@ -3459,7 +3479,7 @@ def htmlTimeline(defaultTimeline: str, \ bannerFile='banner.webp' with open(cssFilename, 'r') as cssFile: - profileStyle = \ + profileStyle= \ cssFile.read().replace('banner.png', \ '/users/'+nickname+'/'+bannerFile) if httpPrefix!='https': @@ -4069,7 +4089,7 @@ def htmlRemoveSharedItem(translate: {},baseDir: str,actor: str,shareName: str) - if os.path.isfile(baseDir+'/follow.css'): cssFilename=baseDir+'/follow.css' with open(cssFilename, 'r') as cssFile: - profileStyle = cssFile.read() + profileStyle=cssFile.read() sharesStr=htmlHeader(cssFilename,profileStyle) sharesStr+='