| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  | __filename__ = "posts.py" | 
					
						
							|  |  |  | __author__ = "Bob Mottram" | 
					
						
							|  |  |  | __license__ = "AGPL3+" | 
					
						
							| 
									
										
										
										
											2019-12-14 10:52:19 +00:00
										 |  |  | __version__ = "1.1.0" | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  | __maintainer__ = "Bob Mottram" | 
					
						
							|  |  |  | __email__ = "bob@freedombone.net" | 
					
						
							|  |  |  | __status__ = "Production" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import requests | 
					
						
							|  |  |  | import json | 
					
						
							|  |  |  | import html | 
					
						
							| 
									
										
										
										
											2019-06-29 10:08:59 +00:00
										 |  |  | import datetime | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  | import os | 
					
						
							|  |  |  | import shutil | 
					
						
							| 
									
										
										
										
											2019-06-30 13:20:23 +00:00
										 |  |  | import threading | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  | import sys | 
					
						
							|  |  |  | import trace | 
					
						
							| 
									
										
										
										
											2019-07-01 11:48:54 +00:00
										 |  |  | import time | 
					
						
							| 
									
										
										
										
											2019-10-10 13:48:05 +00:00
										 |  |  | from time import gmtime, strftime | 
					
						
							| 
									
										
										
										
											2019-07-14 09:17:50 +00:00
										 |  |  | from collections import OrderedDict | 
					
						
							| 
									
										
										
										
											2019-06-30 16:36:58 +00:00
										 |  |  | from threads import threadWithTrace | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  | from cache import storePersonInCache | 
					
						
							|  |  |  | from cache import getPersonFromCache | 
					
						
							| 
									
										
										
										
											2019-08-20 11:51:29 +00:00
										 |  |  | from cache import expirePersonCache | 
					
						
							| 
									
										
										
										
											2019-06-29 10:08:59 +00:00
										 |  |  | from pprint import pprint | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  | from random import randint | 
					
						
							| 
									
										
										
										
											2019-07-03 10:33:55 +00:00
										 |  |  | from session import createSession | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  | from session import getJson | 
					
						
							| 
									
										
										
										
											2019-08-17 10:15:01 +00:00
										 |  |  | from session import postJsonString | 
					
						
							| 
									
										
										
										
											2019-07-16 14:23:06 +00:00
										 |  |  | from session import postImage | 
					
						
							| 
									
										
										
										
											2019-06-30 22:56:37 +00:00
										 |  |  | from webfinger import webfingerHandle | 
					
						
							| 
									
										
										
										
											2019-07-01 09:31:02 +00:00
										 |  |  | from httpsig import createSignedHeader | 
					
						
							| 
									
										
										
										
											2019-12-01 13:57:43 +00:00
										 |  |  | from utils import removePostFromCache | 
					
						
							| 
									
										
										
										
											2019-12-01 13:51:44 +00:00
										 |  |  | from utils import getCachedPostFilename | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  | from utils import getStatusNumber | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  | from utils import createPersonDir | 
					
						
							| 
									
										
										
										
											2019-07-02 10:39:55 +00:00
										 |  |  | from utils import urlPermitted | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  | from utils import getNicknameFromActor | 
					
						
							|  |  |  | from utils import getDomainFromActor | 
					
						
							| 
									
										
										
										
											2019-07-14 16:37:01 +00:00
										 |  |  | from utils import deletePost | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  | from utils import validNickname | 
					
						
							| 
									
										
										
										
											2019-10-02 14:40:39 +00:00
										 |  |  | from utils import locatePost | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  | from utils import loadJson | 
					
						
							|  |  |  | from utils import saveJson | 
					
						
							| 
									
										
										
										
											2019-07-07 16:12:30 +00:00
										 |  |  | from capabilities import getOcapFilename | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  | from capabilities import capabilitiesUpdate | 
					
						
							| 
									
										
										
										
											2019-08-30 18:45:14 +00:00
										 |  |  | from media import attachMedia | 
					
						
							| 
									
										
										
										
											2020-01-15 22:31:04 +00:00
										 |  |  | from media import replaceYouTube | 
					
						
							| 
									
										
										
										
											2019-08-09 09:09:21 +00:00
										 |  |  | from content import addHtmlTags | 
					
						
							| 
									
										
										
										
											2019-09-29 17:42:51 +00:00
										 |  |  | from content import replaceEmojiFromTags | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  | from auth import createBasicAuthHeader | 
					
						
							| 
									
										
										
										
											2019-08-11 11:25:27 +00:00
										 |  |  | from config import getConfigParam | 
					
						
							| 
									
										
										
										
											2019-09-28 16:31:03 +00:00
										 |  |  | from blocking import isBlocked | 
					
						
							| 
									
										
										
										
											2020-02-05 14:57:10 +00:00
										 |  |  | from filters import isFiltered | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  | try:  | 
					
						
							|  |  |  |     from BeautifulSoup import BeautifulSoup | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  |     from bs4 import BeautifulSoup | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 13:22:17 +00:00
										 |  |  | def isModerator(baseDir: str,nickname: str) -> bool: | 
					
						
							|  |  |  |     """Returns true if the given nickname is a moderator
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     moderatorsFile=baseDir+'/accounts/moderators.txt' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not os.path.isfile(moderatorsFile): | 
					
						
							|  |  |  |         if getConfigParam(baseDir,'admin')==nickname: | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     with open(moderatorsFile, "r") as f: | 
					
						
							|  |  |  |         lines = f.readlines() | 
					
						
							|  |  |  |         if len(lines)==0: | 
					
						
							|  |  |  |             if getConfigParam(baseDir,'admin')==nickname: | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  |         for moderator in lines: | 
					
						
							|  |  |  |             moderator=moderator.strip('\n') | 
					
						
							|  |  |  |             if moderator==nickname: | 
					
						
							|  |  |  |                 return True | 
					
						
							|  |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def noOfFollowersOnDomain(baseDir: str,handle: str, \ | 
					
						
							|  |  |  |                           domain: str, followFile='followers.txt') -> int: | 
					
						
							| 
									
										
										
										
											2019-07-05 14:39:24 +00:00
										 |  |  |     """Returns the number of followers of the given handle from the given domain
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     filename=baseDir+'/accounts/'+handle+'/'+followFile | 
					
						
							|  |  |  |     if not os.path.isfile(filename): | 
					
						
							|  |  |  |         return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     ctr=0 | 
					
						
							|  |  |  |     with open(filename, "r") as followersFilename: | 
					
						
							|  |  |  |         for followerHandle in followersFilename: | 
					
						
							|  |  |  |             if '@' in followerHandle: | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                 followerDomain= \ | 
					
						
							|  |  |  |                     followerHandle.split('@')[1].replace('\n','') | 
					
						
							| 
									
										
										
										
											2019-07-05 14:39:24 +00:00
										 |  |  |                 if domain==followerDomain: | 
					
						
							|  |  |  |                     ctr+=1 | 
					
						
							|  |  |  |     return ctr | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def getPersonKey(nickname: str,domain: str,baseDir: str,keyType='public', \ | 
					
						
							|  |  |  |                  debug=False): | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |     """Returns the public or private key of a person
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |     handle=nickname+'@'+domain | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |     keyFilename=baseDir+'/keys/'+keyType+'/'+handle.lower()+'.key' | 
					
						
							|  |  |  |     if not os.path.isfile(keyFilename): | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: private key file not found: '+keyFilename) | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |         return '' | 
					
						
							|  |  |  |     keyPem='' | 
					
						
							|  |  |  |     with open(keyFilename, "r") as pemFile: | 
					
						
							|  |  |  |         keyPem=pemFile.read() | 
					
						
							|  |  |  |     if len(keyPem)<20: | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: private key was too short: '+keyPem) | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |         return '' | 
					
						
							|  |  |  |     return keyPem | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |      | 
					
						
							|  |  |  | def cleanHtml(rawHtml: str) -> str: | 
					
						
							|  |  |  |     text = BeautifulSoup(rawHtml, 'html.parser').get_text() | 
					
						
							|  |  |  |     return html.unescape(text) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-21 15:55:30 +00:00
										 |  |  | def getUserUrl(wfRequest: {}) -> str: | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |     if wfRequest.get('links'): | 
					
						
							|  |  |  |         for link in wfRequest['links']: | 
					
						
							|  |  |  |             if link.get('type') and link.get('href'): | 
					
						
							| 
									
										
										
										
											2019-11-09 21:39:04 +00:00
										 |  |  |                 if link['type'] == 'application/activity+json': | 
					
						
							| 
									
										
										
										
											2019-10-21 16:15:12 +00:00
										 |  |  |                     if not ('/users/' in link['href'] or \ | 
					
						
							|  |  |  |                             '/profile/' in link['href'] or \ | 
					
						
							|  |  |  |                             '/channel/' in link['href']): | 
					
						
							|  |  |  |                         print('Webfinger activity+json contains single user instance actor') | 
					
						
							|  |  |  |                     return link['href'] | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |     return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  | def parseUserFeed(session,feedUrl: str,asHeader: {}, \ | 
					
						
							|  |  |  |                   projectVersion: str,httpPrefix: str,domain: str) -> None: | 
					
						
							|  |  |  |     feedJson = getJson(session,feedUrl,asHeader,None, \ | 
					
						
							|  |  |  |                        projectVersion,httpPrefix,domain) | 
					
						
							| 
									
										
										
										
											2019-07-04 17:31:41 +00:00
										 |  |  |     if not feedJson: | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-29 10:08:59 +00:00
										 |  |  |     if 'orderedItems' in feedJson: | 
					
						
							| 
									
										
										
										
											2019-06-29 10:59:16 +00:00
										 |  |  |         for item in feedJson['orderedItems']: | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |             yield item | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     nextUrl = None | 
					
						
							| 
									
										
										
										
											2019-06-29 10:08:59 +00:00
										 |  |  |     if 'first' in feedJson: | 
					
						
							| 
									
										
										
										
											2019-06-29 10:59:16 +00:00
										 |  |  |         nextUrl = feedJson['first'] | 
					
						
							| 
									
										
										
										
											2019-06-29 10:08:59 +00:00
										 |  |  |     elif 'next' in feedJson: | 
					
						
							|  |  |  |         nextUrl = feedJson['next'] | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if nextUrl: | 
					
						
							| 
									
										
										
										
											2019-09-01 13:13:52 +00:00
										 |  |  |         if isinstance(nextUrl, str): | 
					
						
							|  |  |  |             userFeed=parseUserFeed(session,nextUrl,asHeader, \ | 
					
						
							|  |  |  |                                    projectVersion,httpPrefix,domain) | 
					
						
							|  |  |  |             for item in userFeed: | 
					
						
							|  |  |  |                 yield item | 
					
						
							|  |  |  |         elif isinstance(nextUrl, dict): | 
					
						
							|  |  |  |             userFeed=nextUrl | 
					
						
							|  |  |  |             if userFeed.get('orderedItems'): | 
					
						
							|  |  |  |                 for item in userFeed['orderedItems']: | 
					
						
							|  |  |  |                     yield item         | 
					
						
							| 
									
										
										
										
											2019-06-30 11:34:19 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  | def getPersonBox(baseDir: str,session,wfRequest: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-10-17 15:55:05 +00:00
										 |  |  |                  projectVersion: str,httpPrefix: str, \ | 
					
						
							|  |  |  |                  nickname: str,domain: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-23 12:33:09 +00:00
										 |  |  |                  boxName='inbox') -> (str,str,str,str,str,str,str,str): | 
					
						
							| 
									
										
										
										
											2019-09-01 11:52:46 +00:00
										 |  |  |     asHeader = {'Accept': 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"'} | 
					
						
							| 
									
										
										
										
											2019-10-17 15:55:05 +00:00
										 |  |  |     if not wfRequest.get('errors'): | 
					
						
							|  |  |  |         personUrl = getUserUrl(wfRequest) | 
					
						
							|  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2019-10-21 16:03:44 +00:00
										 |  |  |         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"'} | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             personUrl = httpPrefix+'://'+domain+'/users/'+nickname | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     if not personUrl: | 
					
						
							| 
									
										
										
										
											2019-10-21 16:03:44 +00:00
										 |  |  |         return None,None,None,None,None,None,None,None | 
					
						
							| 
									
										
										
										
											2019-08-20 09:37:09 +00:00
										 |  |  |     personJson = getPersonFromCache(baseDir,personUrl,personCache) | 
					
						
							| 
									
										
										
										
											2019-06-30 11:34:19 +00:00
										 |  |  |     if not personJson: | 
					
						
							| 
									
										
										
										
											2019-10-17 22:26:47 +00:00
										 |  |  |         if '/channel/' in personUrl: | 
					
						
							|  |  |  |             asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |         personJson = getJson(session,personUrl,asHeader,None, \ | 
					
						
							|  |  |  |                              projectVersion,httpPrefix,domain) | 
					
						
							| 
									
										
										
										
											2019-07-04 17:31:41 +00:00
										 |  |  |         if not personJson: | 
					
						
							| 
									
										
										
										
											2019-10-21 16:20:33 +00:00
										 |  |  |             asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} | 
					
						
							|  |  |  |             personJson = getJson(session,personUrl,asHeader,None, \ | 
					
						
							|  |  |  |                                  projectVersion,httpPrefix,domain) | 
					
						
							|  |  |  |             if not personJson: | 
					
						
							| 
									
										
										
										
											2019-10-21 16:21:16 +00:00
										 |  |  |                 print('Unable to get actor') | 
					
						
							| 
									
										
										
										
											2019-10-21 16:20:33 +00:00
										 |  |  |                 return None,None,None,None,None,None,None,None | 
					
						
							| 
									
										
										
										
											2019-07-05 13:38:29 +00:00
										 |  |  |     boxJson=None | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     if not personJson.get(boxName): | 
					
						
							| 
									
										
										
										
											2019-07-05 13:38:29 +00:00
										 |  |  |         if personJson.get('endpoints'): | 
					
						
							|  |  |  |             if personJson['endpoints'].get(boxName): | 
					
						
							|  |  |  |                 boxJson=personJson['endpoints'][boxName] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         boxJson=personJson[boxName] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not boxJson: | 
					
						
							| 
									
										
										
										
											2019-07-23 12:33:09 +00:00
										 |  |  |         return None,None,None,None,None,None,None,None | 
					
						
							| 
									
										
										
										
											2019-07-05 13:38:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     personId=None | 
					
						
							|  |  |  |     if personJson.get('id'): | 
					
						
							|  |  |  |         personId=personJson['id'] | 
					
						
							| 
									
										
										
										
											2019-07-01 10:25:03 +00:00
										 |  |  |     pubKeyId=None | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     pubKey=None | 
					
						
							|  |  |  |     if personJson.get('publicKey'): | 
					
						
							| 
									
										
										
										
											2019-07-01 10:25:03 +00:00
										 |  |  |         if personJson['publicKey'].get('id'): | 
					
						
							|  |  |  |             pubKeyId=personJson['publicKey']['id'] | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |         if personJson['publicKey'].get('publicKeyPem'): | 
					
						
							|  |  |  |             pubKey=personJson['publicKey']['publicKeyPem'] | 
					
						
							| 
									
										
										
										
											2019-07-05 13:50:27 +00:00
										 |  |  |     sharedInbox=None | 
					
						
							|  |  |  |     if personJson.get('sharedInbox'): | 
					
						
							|  |  |  |         sharedInbox=personJson['sharedInbox'] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if personJson.get('endpoints'): | 
					
						
							|  |  |  |             if personJson['endpoints'].get('sharedInbox'): | 
					
						
							|  |  |  |                 sharedInbox=personJson['endpoints']['sharedInbox'] | 
					
						
							| 
									
										
										
										
											2019-07-05 20:32:21 +00:00
										 |  |  |     capabilityAcquisition=None | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |     if personJson.get('capabilityAcquisitionEndpoint'): | 
					
						
							|  |  |  |         capabilityAcquisition=personJson['capabilityAcquisitionEndpoint'] | 
					
						
							| 
									
										
										
										
											2019-07-22 14:21:49 +00:00
										 |  |  |     avatarUrl=None | 
					
						
							| 
									
										
										
										
											2019-07-22 14:09:21 +00:00
										 |  |  |     if personJson.get('icon'): | 
					
						
							|  |  |  |         if personJson['icon'].get('url'): | 
					
						
							| 
									
										
										
										
											2019-07-22 14:21:49 +00:00
										 |  |  |             avatarUrl=personJson['icon']['url'] | 
					
						
							| 
									
										
										
										
											2019-08-22 18:36:07 +00:00
										 |  |  |     displayName=None | 
					
						
							|  |  |  |     if personJson.get('name'): | 
					
						
							|  |  |  |         displayName=personJson['name'] | 
					
						
							| 
									
										
										
										
											2019-06-30 10:21:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |     storePersonInCache(baseDir,personUrl,personJson,personCache) | 
					
						
							| 
									
										
										
										
											2019-06-30 10:21:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-22 18:36:07 +00:00
										 |  |  |     return boxJson,pubKeyId,pubKey,personId,sharedInbox,capabilityAcquisition,avatarUrl,displayName | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  | def getPosts(session,outboxUrl: str,maxPosts: int, \ | 
					
						
							|  |  |  |              maxMentions: int, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 10:33:57 +00:00
										 |  |  |              maxEmoji: int,maxAttachments: int, \ | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |              federationList: [], \ | 
					
						
							|  |  |  |              personCache: {},raw: bool, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |              simple: bool,debug: bool, \ | 
					
						
							|  |  |  |              projectVersion: str,httpPrefix: str,domain: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-28 11:08:14 +00:00
										 |  |  |     """Gets public posts from an outbox
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-09-01 13:13:52 +00:00
										 |  |  |     personPosts={}     | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |     if not outboxUrl: | 
					
						
							|  |  |  |         return personPosts | 
					
						
							| 
									
										
										
										
											2019-09-01 12:09:29 +00:00
										 |  |  |     asHeader = {'Accept': 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"'} | 
					
						
							| 
									
										
										
										
											2019-10-17 22:26:47 +00:00
										 |  |  |     if '/outbox/' in outboxUrl: | 
					
						
							|  |  |  |         asHeader = {'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} | 
					
						
							| 
									
										
										
										
											2019-07-03 11:24:38 +00:00
										 |  |  |     if raw: | 
					
						
							|  |  |  |         result = [] | 
					
						
							|  |  |  |         i = 0 | 
					
						
							| 
									
										
										
										
											2019-09-01 13:13:52 +00:00
										 |  |  |         userFeed=parseUserFeed(session,outboxUrl,asHeader, \ | 
					
						
							|  |  |  |                                projectVersion,httpPrefix,domain) | 
					
						
							|  |  |  |         for item in userFeed: | 
					
						
							| 
									
										
										
										
											2019-07-03 11:24:38 +00:00
										 |  |  |             result.append(item) | 
					
						
							|  |  |  |             i += 1 | 
					
						
							|  |  |  |             if i == maxPosts: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |         pprint(result) | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |     i = 0 | 
					
						
							| 
									
										
										
										
											2019-09-01 13:13:52 +00:00
										 |  |  |     userFeed=parseUserFeed(session,outboxUrl,asHeader, \ | 
					
						
							|  |  |  |                            projectVersion,httpPrefix,domain) | 
					
						
							|  |  |  |     for item in userFeed: | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |         if not item.get('id'): | 
					
						
							|  |  |  |             if debug: | 
					
						
							|  |  |  |                 print('No id') | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |         if not item.get('type'): | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |             if debug: | 
					
						
							|  |  |  |                 print('No type') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |             continue | 
					
						
							|  |  |  |         if item['type'] != 'Create': | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |             if debug: | 
					
						
							|  |  |  |                 print('Not Create type') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |             continue | 
					
						
							|  |  |  |         if not item.get('object'): | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |             if debug: | 
					
						
							|  |  |  |                 print('No object') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |             continue | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |         if not isinstance(item['object'], dict): | 
					
						
							|  |  |  |             if debug: | 
					
						
							|  |  |  |                 print('item object is not a dict') | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         if not item['object'].get('published'): | 
					
						
							|  |  |  |             if debug: | 
					
						
							|  |  |  |                 print('No published attribute') | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         #pprint(item) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |         published = item['object']['published'] | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |         if not personPosts.get(item['id']): | 
					
						
							| 
									
										
										
										
											2019-07-28 11:08:14 +00:00
										 |  |  |             # check that this is a public post | 
					
						
							|  |  |  |             # #Public should appear in the "to" list | 
					
						
							|  |  |  |             if item['object'].get('to'): | 
					
						
							|  |  |  |                 isPublic=False | 
					
						
							|  |  |  |                 for recipient in item['object']['to']: | 
					
						
							|  |  |  |                     if recipient.endswith('#Public'): | 
					
						
							|  |  |  |                         isPublic=True | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                 if not isPublic: | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |              | 
					
						
							| 
									
										
										
										
											2019-08-09 08:46:38 +00:00
										 |  |  |             content = item['object']['content'].replace(''',"'") | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             mentions=[] | 
					
						
							|  |  |  |             emoji={} | 
					
						
							|  |  |  |             if item['object'].get('tag'): | 
					
						
							|  |  |  |                 for tagItem in item['object']['tag']: | 
					
						
							|  |  |  |                     tagType=tagItem['type'].lower() | 
					
						
							|  |  |  |                     if tagType=='emoji': | 
					
						
							|  |  |  |                         if tagItem.get('name') and tagItem.get('icon'): | 
					
						
							|  |  |  |                             if tagItem['icon'].get('url'): | 
					
						
							|  |  |  |                                 # No emoji from non-permitted domains | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                                 if urlPermitted(tagItem['icon']['url'], \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                                                 federationList, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                                                 "objects:read"): | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |                                     emojiName=tagItem['name'] | 
					
						
							|  |  |  |                                     emojiIcon=tagItem['icon']['url'] | 
					
						
							|  |  |  |                                     emoji[emojiName]=emojiIcon | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |                                 else: | 
					
						
							|  |  |  |                                     if debug: | 
					
						
							|  |  |  |                                         print('url not permitted '+tagItem['icon']['url']) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |                     if tagType=='mention': | 
					
						
							|  |  |  |                         if tagItem.get('name'): | 
					
						
							|  |  |  |                             if tagItem['name'] not in mentions: | 
					
						
							|  |  |  |                                 mentions.append(tagItem['name']) | 
					
						
							|  |  |  |             if len(mentions)>maxMentions: | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |                 if debug: | 
					
						
							|  |  |  |                     print('max mentions reached') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |                 continue | 
					
						
							|  |  |  |             if len(emoji)>maxEmoji: | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |                 if debug: | 
					
						
							|  |  |  |                     print('max emojis reached') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             summary = '' | 
					
						
							|  |  |  |             if item['object'].get('summary'): | 
					
						
							|  |  |  |                 if item['object']['summary']: | 
					
						
							|  |  |  |                     summary = item['object']['summary'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             inReplyTo = '' | 
					
						
							|  |  |  |             if item['object'].get('inReplyTo'): | 
					
						
							|  |  |  |                 if item['object']['inReplyTo']: | 
					
						
							|  |  |  |                     # No replies to non-permitted domains | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                     if not urlPermitted(item['object']['inReplyTo'], \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                                         federationList, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                                         "objects:read"): | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |                         if debug: | 
					
						
							|  |  |  |                             print('url not permitted '+item['object']['inReplyTo']) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |                         continue | 
					
						
							|  |  |  |                     inReplyTo = item['object']['inReplyTo'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             conversation = '' | 
					
						
							|  |  |  |             if item['object'].get('conversation'): | 
					
						
							|  |  |  |                 if item['object']['conversation']: | 
					
						
							|  |  |  |                     # no conversations originated in non-permitted domains | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                     if urlPermitted(item['object']['conversation'], \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                                     federationList,"objects:read"):   | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |                         conversation = item['object']['conversation'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             attachment = [] | 
					
						
							|  |  |  |             if item['object'].get('attachment'): | 
					
						
							|  |  |  |                 if item['object']['attachment']: | 
					
						
							|  |  |  |                     for attach in item['object']['attachment']: | 
					
						
							|  |  |  |                         if attach.get('name') and attach.get('url'): | 
					
						
							|  |  |  |                             # no attachments from non-permitted domains | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                             if urlPermitted(attach['url'], \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                                             federationList, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                                             "objects:read"): | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |                                 attachment.append([attach['name'],attach['url']]) | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |                             else: | 
					
						
							|  |  |  |                                 if debug: | 
					
						
							|  |  |  |                                     print('url not permitted '+attach['url']) | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             sensitive = False | 
					
						
							|  |  |  |             if item['object'].get('sensitive'): | 
					
						
							|  |  |  |                 sensitive = item['object']['sensitive'] | 
					
						
							| 
									
										
										
										
											2019-07-03 11:24:38 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if simple: | 
					
						
							|  |  |  |                 print(cleanHtml(content)+'\n') | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |                 pprint(item) | 
					
						
							|  |  |  |                 personPosts[item['id']] = { | 
					
						
							| 
									
										
										
										
											2019-07-03 11:24:38 +00:00
										 |  |  |                     "sensitive": sensitive, | 
					
						
							|  |  |  |                     "inreplyto": inReplyTo, | 
					
						
							|  |  |  |                     "summary": summary, | 
					
						
							|  |  |  |                     "html": content, | 
					
						
							|  |  |  |                     "plaintext": cleanHtml(content), | 
					
						
							|  |  |  |                     "attachment": attachment, | 
					
						
							|  |  |  |                     "mentions": mentions, | 
					
						
							|  |  |  |                     "emoji": emoji, | 
					
						
							|  |  |  |                     "conversation": conversation | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |         i += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if i == maxPosts: | 
					
						
							|  |  |  |             break | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |     return personPosts | 
					
						
							| 
									
										
										
										
											2019-06-29 10:08:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  | def deleteAllPosts(baseDir: str,nickname: str, domain: str,boxname: str) -> None: | 
					
						
							|  |  |  |     """Deletes all posts for a person from inbox or outbox
 | 
					
						
							| 
									
										
										
										
											2019-06-29 11:47:33 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  |     if boxname!='inbox' and boxname!='outbox': | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     boxDir = createPersonDir(nickname,domain,baseDir,boxname) | 
					
						
							| 
									
										
										
										
											2019-09-27 12:09:04 +00:00
										 |  |  |     for deleteFilename in os.scandir(boxDir): | 
					
						
							|  |  |  |         deleteFilename=deleteFilename.name | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  |         filePath = os.path.join(boxDir, deleteFilename) | 
					
						
							| 
									
										
										
										
											2019-06-29 11:47:33 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             if os.path.isfile(filePath): | 
					
						
							|  |  |  |                 os.unlink(filePath) | 
					
						
							|  |  |  |             elif os.path.isdir(filePath): shutil.rmtree(filePath) | 
					
						
							|  |  |  |         except Exception as e: | 
					
						
							|  |  |  |             print(e) | 
					
						
							| 
									
										
										
										
											2019-07-03 22:16:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def savePostToBox(baseDir: str,httpPrefix: str,postId: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-14 16:57:06 +00:00
										 |  |  |                   nickname: str, domain: str,postJsonObject: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |                   boxname: str) -> str: | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  |     """Saves the give json to the give box
 | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |     Returns the filename | 
					
						
							| 
									
										
										
										
											2019-07-03 22:16:03 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |     if boxname!='inbox' and boxname!='outbox' and boxname!='scheduled': | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |         return None | 
					
						
							| 
									
										
										
										
											2019-07-18 11:35:48 +00:00
										 |  |  |     originalDomain=domain     | 
					
						
							| 
									
										
										
										
											2019-07-03 22:16:03 +00:00
										 |  |  |     if ':' in domain: | 
					
						
							|  |  |  |         domain=domain.split(':')[0] | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 22:59:56 +00:00
										 |  |  |     if not postId: | 
					
						
							|  |  |  |         statusNumber,published = getStatusNumber() | 
					
						
							| 
									
										
										
										
											2020-02-21 12:39:50 +00:00
										 |  |  |         postId= \ | 
					
						
							|  |  |  |             httpPrefix+'://'+originalDomain+'/users/'+nickname+ \ | 
					
						
							|  |  |  |             '/statuses/'+statusNumber | 
					
						
							| 
									
										
										
										
											2019-07-14 16:57:06 +00:00
										 |  |  |         postJsonObject['id']=postId+'/activity' | 
					
						
							|  |  |  |     if postJsonObject.get('object'): | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |         if isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |             postJsonObject['object']['id']=postId | 
					
						
							|  |  |  |             postJsonObject['object']['atomUri']=postId | 
					
						
							| 
									
										
										
										
											2019-07-03 22:59:56 +00:00
										 |  |  |           | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  |     boxDir = createPersonDir(nickname,domain,baseDir,boxname) | 
					
						
							|  |  |  |     filename=boxDir+'/'+postId.replace('/','#')+'.json' | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |     saveJson(postJsonObject,filename) | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |     return filename | 
					
						
							| 
									
										
										
										
											2019-07-03 22:16:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 11:12:08 +00:00
										 |  |  | def updateHashtagsIndex(baseDir: str,tag: {},newPostId: str) -> None: | 
					
						
							|  |  |  |     """Writes the post url for hashtags to a file
 | 
					
						
							|  |  |  |     This allows posts for a hashtag to be quickly looked up | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-08-09 17:42:11 +00:00
										 |  |  |     if tag['type']!='Hashtag': | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-12-17 10:24:52 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 17:42:11 +00:00
										 |  |  |     # create hashtags directory     | 
					
						
							| 
									
										
										
										
											2019-08-09 11:12:08 +00:00
										 |  |  |     tagsDir=baseDir+'/tags' | 
					
						
							|  |  |  |     if not os.path.isdir(tagsDir): | 
					
						
							|  |  |  |         os.mkdir(tagsDir) | 
					
						
							|  |  |  |     tagName=tag['name'] | 
					
						
							|  |  |  |     tagsFilename=tagsDir+'/'+tagName[1:]+'.txt' | 
					
						
							| 
									
										
										
										
											2019-12-17 10:24:52 +00:00
										 |  |  |     tagline=newPostId+'\n' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not os.path.isfile(tagsFilename): | 
					
						
							|  |  |  |         # create a new tags index file | 
					
						
							|  |  |  |         tagsFile=open(tagsFilename, "w+") | 
					
						
							|  |  |  |         if tagsFile: | 
					
						
							|  |  |  |             tagsFile.write(tagline) | 
					
						
							|  |  |  |             tagsFile.close() | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         # prepend to tags index file | 
					
						
							|  |  |  |         if tagline not in open(tagsFilename).read(): | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 with open(tagsFilename, 'r+') as tagsFile: | 
					
						
							|  |  |  |                     content = tagsFile.read() | 
					
						
							|  |  |  |                     tagsFile.seek(0, 0) | 
					
						
							|  |  |  |                     tagsFile.write(tagline+content) | 
					
						
							|  |  |  |             except Exception as e: | 
					
						
							|  |  |  |                 print('WARN: Failed to write entry to tags file '+ \ | 
					
						
							|  |  |  |                       tagsFilename+' '+str(e)) | 
					
						
							| 
									
										
										
										
											2019-08-09 11:12:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-13 10:49:03 +00:00
										 |  |  | def addSchedulePost(baseDir: str,nickname: str,domain: str, \ | 
					
						
							|  |  |  |                     eventDateStr: str,postId: str) -> None: | 
					
						
							|  |  |  |     """Adds a scheduled post to the index
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     handle=nickname+'@'+domain | 
					
						
							|  |  |  |     scheduleIndexFilename=baseDir+'/accounts/'+handle+'/schedule.index' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     indexStr=eventDateStr+' '+postId.replace('/','#') | 
					
						
							|  |  |  |     if os.path.isfile(scheduleIndexFilename): | 
					
						
							|  |  |  |         if indexStr not in open(scheduleIndexFilename).read(): | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 with open(scheduleIndexFilename, 'r+') as scheduleFile: | 
					
						
							|  |  |  |                     content = scheduleFile.read() | 
					
						
							|  |  |  |                     scheduleFile.seek(0, 0) | 
					
						
							|  |  |  |                     scheduleFile.write(indexStr+'\n'+content) | 
					
						
							| 
									
										
										
										
											2020-01-13 11:45:36 +00:00
										 |  |  |                     print('DEBUG: scheduled post added to index') | 
					
						
							| 
									
										
										
										
											2020-01-13 10:49:03 +00:00
										 |  |  |             except Exception as e: | 
					
						
							|  |  |  |                 print('WARN: Failed to write entry to scheduled posts index '+ \ | 
					
						
							|  |  |  |                       scheduleIndexFilename+' '+str(e)) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         scheduleFile=open(scheduleIndexFilename,'w') | 
					
						
							|  |  |  |         if scheduleFile: | 
					
						
							|  |  |  |             scheduleFile.write(indexStr+'\n') | 
					
						
							|  |  |  |             scheduleFile.close()         | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-10 12:28:12 +00:00
										 |  |  | def createPostBase(baseDir: str,nickname: str,domain: str,port: int, \ | 
					
						
							|  |  |  |                    toUrl: str,ccUrl: str,httpPrefix: str,content: str, \ | 
					
						
							|  |  |  |                    followersOnly: bool,saveToFile: bool,clientToServer: bool, \ | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                    attachImageFilename: str, \ | 
					
						
							|  |  |  |                    mediaType: str,imageDescription: str, \ | 
					
						
							| 
									
										
										
										
											2019-08-11 20:38:10 +00:00
										 |  |  |                    useBlurhash: bool,isModerationReport: bool,inReplyTo=None, \ | 
					
						
							| 
									
										
										
										
											2019-11-10 12:28:12 +00:00
										 |  |  |                    inReplyToAtomUri=None,subject=None, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                    schedulePost=False, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:13:59 +00:00
										 |  |  |                    eventDate=None,eventTime=None,location=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-01 12:14:49 +00:00
										 |  |  |     """Creates a message
 | 
					
						
							| 
									
										
										
										
											2019-06-29 22:29:18 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-08-19 09:37:14 +00:00
										 |  |  |     mentionedRecipients= \ | 
					
						
							|  |  |  |         getMentionedPeople(baseDir,httpPrefix,content,domain,False) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-09 11:12:08 +00:00
										 |  |  |     tags=[] | 
					
						
							|  |  |  |     hashtagsDict={} | 
					
						
							| 
									
										
										
										
											2019-07-15 14:41:15 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-01 12:47:08 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-10 16:55:53 +00:00
										 |  |  |     # convert content to html | 
					
						
							| 
									
										
										
										
											2019-09-29 17:42:51 +00:00
										 |  |  |     emojisDict={} | 
					
						
							| 
									
										
										
										
											2019-11-01 10:19:21 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # add tags | 
					
						
							| 
									
										
										
										
											2019-08-09 16:18:00 +00:00
										 |  |  |     content= \ | 
					
						
							|  |  |  |         addHtmlTags(baseDir,httpPrefix, \ | 
					
						
							|  |  |  |                     nickname,domain,content, \ | 
					
						
							|  |  |  |                     mentionedRecipients, \ | 
					
						
							| 
									
										
										
										
											2019-10-29 13:04:38 +00:00
										 |  |  |                     hashtagsDict,True) | 
					
						
							| 
									
										
										
										
											2020-02-21 15:17:55 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # replace emoji with unicode | 
					
						
							|  |  |  |     tags=[] | 
					
						
							|  |  |  |     for tagName,tag in hashtagsDict.items(): | 
					
						
							|  |  |  |         tags.append(tag) | 
					
						
							|  |  |  |     # get list of tags | 
					
						
							|  |  |  |     content=replaceEmojiFromTags(content,tags,'content') | 
					
						
							|  |  |  |     # remove replaced emoji | 
					
						
							| 
									
										
										
										
											2020-02-21 15:20:13 +00:00
										 |  |  |     hashtagsDictCopy=hashtagsDict.copy() | 
					
						
							|  |  |  |     for tagName,tag in hashtagsDictCopy.items(): | 
					
						
							| 
									
										
										
										
											2020-02-21 15:17:55 +00:00
										 |  |  |         if tag.get('name'): | 
					
						
							|  |  |  |             if tag['name'].startswith(':'): | 
					
						
							|  |  |  |                 if tag['name'] not in content: | 
					
						
							|  |  |  |                     del hashtagsDict[tagName] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-29 22:29:18 +00:00
										 |  |  |     statusNumber,published = getStatusNumber() | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |     postTo='https://www.w3.org/ns/activitystreams#Public' | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |     postCC=httpPrefix+'://'+domain+'/users/'+nickname+'/followers' | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |     if followersOnly: | 
					
						
							|  |  |  |         postTo=postCC | 
					
						
							|  |  |  |         postCC='' | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |     newPostId=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber | 
					
						
							| 
									
										
										
										
											2019-08-11 20:38:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-29 10:23:40 +00:00
										 |  |  |     sensitive=False | 
					
						
							| 
									
										
										
										
											2019-06-30 21:20:02 +00:00
										 |  |  |     summary=None | 
					
						
							| 
									
										
										
										
											2019-06-29 10:23:40 +00:00
										 |  |  |     if subject: | 
					
						
							|  |  |  |         summary=subject | 
					
						
							|  |  |  |         sensitive=True | 
					
						
							| 
									
										
										
										
											2019-07-15 14:41:15 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-19 10:52:38 +00:00
										 |  |  |     toRecipients=[] | 
					
						
							| 
									
										
										
										
											2019-11-04 12:09:59 +00:00
										 |  |  |     toCC=[] | 
					
						
							| 
									
										
										
										
											2019-08-11 18:32:29 +00:00
										 |  |  |     if toUrl: | 
					
						
							|  |  |  |         if not isinstance(toUrl, str): | 
					
						
							|  |  |  |             print('ERROR: toUrl is not a string') | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2019-08-19 10:52:38 +00:00
										 |  |  |         toRecipients=[toUrl]         | 
					
						
							| 
									
										
										
										
											2019-08-11 18:32:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-05 16:56:32 +00:00
										 |  |  |     # who to send to | 
					
						
							| 
									
										
										
										
											2019-08-19 12:40:59 +00:00
										 |  |  |     if mentionedRecipients: | 
					
						
							|  |  |  |         for mention in mentionedRecipients: | 
					
						
							| 
									
										
										
										
											2019-11-04 12:09:59 +00:00
										 |  |  |             if mention not in toCC: | 
					
						
							|  |  |  |                 toCC.append(mention) | 
					
						
							| 
									
										
										
										
											2019-08-09 11:12:08 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # create a list of hashtags | 
					
						
							| 
									
										
										
										
											2019-09-05 11:37:41 +00:00
										 |  |  |     # Only posts which are #Public are searchable by hashtag | 
					
						
							| 
									
										
										
										
											2019-08-10 16:55:17 +00:00
										 |  |  |     if hashtagsDict: | 
					
						
							|  |  |  |         isPublic=False | 
					
						
							|  |  |  |         for recipient in toRecipients: | 
					
						
							|  |  |  |             if recipient.endswith('#Public'): | 
					
						
							|  |  |  |                 isPublic=True | 
					
						
							|  |  |  |                 break | 
					
						
							| 
									
										
										
										
											2019-08-09 11:12:08 +00:00
										 |  |  |         for tagName,tag in hashtagsDict.items(): | 
					
						
							|  |  |  |             tags.append(tag) | 
					
						
							| 
									
										
										
										
											2019-08-10 11:51:54 +00:00
										 |  |  |             if isPublic: | 
					
						
							|  |  |  |                 updateHashtagsIndex(baseDir,tag,newPostId) | 
					
						
							| 
									
										
										
										
											2019-09-29 19:39:35 +00:00
										 |  |  |         print('Content tags: '+str(tags)) | 
					
						
							| 
									
										
										
										
											2019-10-02 14:40:39 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if inReplyTo and not sensitive: | 
					
						
							|  |  |  |         # locate the post which this is a reply to and check if | 
					
						
							|  |  |  |         # it has a content warning. If it does then reproduce | 
					
						
							|  |  |  |         # the same warning | 
					
						
							| 
									
										
										
										
											2019-10-02 15:00:22 +00:00
										 |  |  |         replyPostFilename=locatePost(baseDir,nickname,domain,inReplyTo) | 
					
						
							| 
									
										
										
										
											2019-10-02 14:40:39 +00:00
										 |  |  |         if replyPostFilename: | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |             replyToJson=loadJson(replyPostFilename) | 
					
						
							| 
									
										
										
										
											2019-10-02 14:40:39 +00:00
										 |  |  |             if replyToJson: | 
					
						
							|  |  |  |                 if replyToJson.get('object'): | 
					
						
							|  |  |  |                     if replyToJson['object'].get('sensitive'): | 
					
						
							|  |  |  |                         if replyToJson['object']['sensitive']: | 
					
						
							|  |  |  |                             sensitive=True | 
					
						
							|  |  |  |                             if replyToJson['object'].get('summary'): | 
					
						
							|  |  |  |                                 summary=replyToJson['object']['summary'] | 
					
						
							| 
									
										
										
										
											2020-01-12 12:58:05 +00:00
										 |  |  |     eventDateStr=None | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |     if eventDate: | 
					
						
							|  |  |  |         eventName=summary | 
					
						
							|  |  |  |         if not eventName: | 
					
						
							|  |  |  |             eventName=content | 
					
						
							|  |  |  |         eventDateStr=eventDate | 
					
						
							|  |  |  |         if eventTime: | 
					
						
							| 
									
										
										
										
											2019-10-10 13:24:29 +00:00
										 |  |  |             if eventTime.endswith('Z'): | 
					
						
							|  |  |  |                 eventDateStr=eventDate+'T'+eventTime | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2019-10-10 13:48:05 +00:00
										 |  |  |                 eventDateStr=eventDate+'T'+eventTime+':00'+strftime("%z", gmtime()) | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2019-10-10 13:48:05 +00:00
										 |  |  |             eventDateStr=eventDate+'T12:00:00Z' | 
					
						
							| 
									
										
										
										
											2020-01-12 13:19:03 +00:00
										 |  |  |         if not schedulePost: | 
					
						
							|  |  |  |             tags.append({ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                 "@context": "https://www.w3.org/ns/activitystreams", | 
					
						
							|  |  |  |                 "type": "Event", | 
					
						
							|  |  |  |                 "name": eventName, | 
					
						
							|  |  |  |                 "startTime": eventDateStr, | 
					
						
							|  |  |  |                 "endTime": eventDateStr | 
					
						
							| 
									
										
										
										
											2020-01-12 13:19:03 +00:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2020-01-12 13:16:02 +00:00
										 |  |  |     if location: | 
					
						
							|  |  |  |         tags.append({ | 
					
						
							|  |  |  |             "@context": "https://www.w3.org/ns/activitystreams", | 
					
						
							|  |  |  |             "type": "Place", | 
					
						
							|  |  |  |             "name": location | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2019-10-19 15:59:49 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     postContext=[ | 
					
						
							|  |  |  |         'https://www.w3.org/ns/activitystreams', | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             'Hashtag': 'as:Hashtag', | 
					
						
							|  |  |  |             'sensitive': 'as:sensitive', | 
					
						
							|  |  |  |             'toot': 'http://joinmastodon.org/ns#', | 
					
						
							|  |  |  |             'votersCount': 'toot:votersCount' | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  |              | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |     if not clientToServer: | 
					
						
							| 
									
										
										
										
											2019-07-06 10:33:57 +00:00
										 |  |  |         actorUrl=httpPrefix+'://'+domain+'/users/'+nickname | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-07 11:53:32 +00:00
										 |  |  |         # if capabilities have been granted for this actor | 
					
						
							|  |  |  |         # then get the corresponding id | 
					
						
							|  |  |  |         capabilityId=None | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |         capabilityIdList=[] | 
					
						
							|  |  |  |         ocapFilename=getOcapFilename(baseDir,nickname,domain,toUrl,'granted') | 
					
						
							| 
									
										
										
										
											2019-08-18 20:47:12 +00:00
										 |  |  |         if ocapFilename: | 
					
						
							|  |  |  |             if os.path.isfile(ocapFilename): | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |                 oc=loadJson(ocapFilename) | 
					
						
							|  |  |  |                 if oc: | 
					
						
							|  |  |  |                     if oc.get('id'): | 
					
						
							|  |  |  |                         capabilityIdList=[oc['id']] | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |         newPost = { | 
					
						
							| 
									
										
										
										
											2019-10-19 15:59:49 +00:00
										 |  |  |             "@context": postContext, | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |             'id': newPostId+'/activity', | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |             'capability': capabilityIdList, | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |             'type': 'Create', | 
					
						
							| 
									
										
										
										
											2019-07-06 10:33:57 +00:00
										 |  |  |             'actor': actorUrl, | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |             'published': published, | 
					
						
							| 
									
										
										
										
											2019-11-04 12:09:59 +00:00
										 |  |  |             'to': toRecipients, | 
					
						
							|  |  |  |             'cc': toCC, | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |             'object': { | 
					
						
							|  |  |  |                 'id': newPostId, | 
					
						
							|  |  |  |                 'type': 'Note', | 
					
						
							|  |  |  |                 'summary': summary, | 
					
						
							|  |  |  |                 'inReplyTo': inReplyTo, | 
					
						
							|  |  |  |                 'published': published, | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |                 'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber, | 
					
						
							|  |  |  |                 'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname, | 
					
						
							| 
									
										
										
										
											2019-08-05 16:56:32 +00:00
										 |  |  |                 'to': toRecipients, | 
					
						
							| 
									
										
										
										
											2019-11-04 12:09:59 +00:00
										 |  |  |                 'cc': toCC, | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |                 'sensitive': sensitive, | 
					
						
							| 
									
										
										
										
											2019-07-03 22:59:56 +00:00
										 |  |  |                 'atomUri': newPostId, | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |                 'inReplyToAtomUri': inReplyToAtomUri, | 
					
						
							|  |  |  |                 'content': content, | 
					
						
							|  |  |  |                 'contentMap': { | 
					
						
							|  |  |  |                     'en': content | 
					
						
							|  |  |  |                 }, | 
					
						
							|  |  |  |                 'attachment': [], | 
					
						
							| 
									
										
										
										
											2019-08-09 11:12:08 +00:00
										 |  |  |                 'tag': tags, | 
					
						
							| 
									
										
										
										
											2019-07-11 13:46:12 +00:00
										 |  |  |                 'replies': { | 
					
						
							|  |  |  |                     'id': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies', | 
					
						
							|  |  |  |                     'type': 'Collection', | 
					
						
							|  |  |  |                     'first': { | 
					
						
							|  |  |  |                         'type': 'CollectionPage', | 
					
						
							|  |  |  |                         'partOf': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies', | 
					
						
							|  |  |  |                         'items': [] | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-07-12 19:08:46 +00:00
										 |  |  |         if attachImageFilename: | 
					
						
							|  |  |  |             newPost['object']= \ | 
					
						
							| 
									
										
										
										
											2019-08-30 18:32:34 +00:00
										 |  |  |                 attachMedia(baseDir,httpPrefix,domain,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-12 19:26:54 +00:00
										 |  |  |                             newPost['object'],attachImageFilename, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                             mediaType,imageDescription,useBlurhash)             | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |     else: | 
					
						
							|  |  |  |         newPost = { | 
					
						
							| 
									
										
										
										
											2019-10-19 15:59:49 +00:00
										 |  |  |             "@context": postContext, | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |             'id': newPostId, | 
					
						
							|  |  |  |             'type': 'Note', | 
					
						
							|  |  |  |             'summary': summary, | 
					
						
							|  |  |  |             'inReplyTo': inReplyTo, | 
					
						
							|  |  |  |             'published': published, | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |             'url': httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber, | 
					
						
							|  |  |  |             'attributedTo': httpPrefix+'://'+domain+'/users/'+nickname, | 
					
						
							| 
									
										
										
										
											2019-08-05 16:56:32 +00:00
										 |  |  |             'to': toRecipients, | 
					
						
							| 
									
										
										
										
											2019-11-04 12:09:59 +00:00
										 |  |  |             'cc': toCC, | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |             'sensitive': sensitive, | 
					
						
							| 
									
										
										
										
											2019-07-03 22:59:56 +00:00
										 |  |  |             'atomUri': newPostId, | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |             'inReplyToAtomUri': inReplyToAtomUri, | 
					
						
							|  |  |  |             'content': content, | 
					
						
							|  |  |  |             'contentMap': { | 
					
						
							|  |  |  |                 'en': content | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             'attachment': [], | 
					
						
							| 
									
										
										
										
											2019-08-09 11:12:08 +00:00
										 |  |  |             'tag': tags, | 
					
						
							| 
									
										
										
										
											2019-07-12 19:08:46 +00:00
										 |  |  |             'replies': { | 
					
						
							|  |  |  |                 'id': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies', | 
					
						
							|  |  |  |                 'type': 'Collection', | 
					
						
							|  |  |  |                 'first': { | 
					
						
							|  |  |  |                     'type': 'CollectionPage', | 
					
						
							|  |  |  |                     'partOf': 'https://'+domain+'/users/'+nickname+'/statuses/'+statusNumber+'/replies', | 
					
						
							|  |  |  |                     'items': [] | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-07-12 19:08:46 +00:00
										 |  |  |         if attachImageFilename: | 
					
						
							|  |  |  |             newPost= \ | 
					
						
							| 
									
										
										
										
											2019-08-30 18:32:34 +00:00
										 |  |  |                 attachMedia(baseDir,httpPrefix,domain,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-12 19:26:54 +00:00
										 |  |  |                             newPost,attachImageFilename, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                             mediaType,imageDescription,useBlurhash)             | 
					
						
							| 
									
										
										
										
											2019-07-01 12:14:49 +00:00
										 |  |  |     if ccUrl: | 
					
						
							|  |  |  |         if len(ccUrl)>0: | 
					
						
							| 
									
										
										
										
											2019-07-19 18:18:06 +00:00
										 |  |  |             newPost['cc']=[ccUrl] | 
					
						
							| 
									
										
										
										
											2019-07-19 18:12:50 +00:00
										 |  |  |             if newPost.get('object'): | 
					
						
							| 
									
										
										
										
											2019-07-19 18:18:06 +00:00
										 |  |  |                 newPost['object']['cc']=[ccUrl] | 
					
						
							| 
									
										
										
										
											2019-08-11 20:38:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # if this is a moderation report then add a status | 
					
						
							|  |  |  |     if isModerationReport: | 
					
						
							| 
									
										
										
										
											2019-08-12 13:22:17 +00:00
										 |  |  |         # add status | 
					
						
							| 
									
										
										
										
											2019-08-11 20:38:10 +00:00
										 |  |  |         if newPost.get('object'): | 
					
						
							|  |  |  |             newPost['object']['moderationStatus']='pending' | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             newPost['moderationStatus']='pending' | 
					
						
							| 
									
										
										
										
											2019-08-12 13:22:17 +00:00
										 |  |  |         # save to index file | 
					
						
							|  |  |  |         moderationIndexFile=baseDir+'/accounts/moderation.txt' | 
					
						
							|  |  |  |         modFile=open(moderationIndexFile, "a+") | 
					
						
							|  |  |  |         if modFile: | 
					
						
							|  |  |  |             modFile.write(newPostId+'\n') | 
					
						
							|  |  |  |             modFile.close() | 
					
						
							| 
									
										
										
										
											2019-08-11 20:38:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-12 20:53:00 +00:00
										 |  |  |     if schedulePost: | 
					
						
							|  |  |  |         if eventDate and eventTime:     | 
					
						
							|  |  |  |             # add an item to the scheduled post index file | 
					
						
							| 
									
										
										
										
											2020-01-12 21:04:20 +00:00
										 |  |  |             addSchedulePost(baseDir,nickname,domain,eventDateStr,newPostId) | 
					
						
							| 
									
										
										
										
											2020-01-12 20:53:00 +00:00
										 |  |  |             savePostToBox(baseDir,httpPrefix,newPostId, \ | 
					
						
							|  |  |  |                           nickname,domain,newPost,'scheduled') | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             print('Unable to create scheduled post without date and time values') | 
					
						
							|  |  |  |             return newPost | 
					
						
							|  |  |  |     elif saveToFile: | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |         savePostToBox(baseDir,httpPrefix,newPostId, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 20:53:00 +00:00
										 |  |  |                       nickname,domain,newPost,'outbox') | 
					
						
							| 
									
										
										
										
											2019-06-28 18:55:29 +00:00
										 |  |  |     return newPost | 
					
						
							| 
									
										
										
										
											2019-06-29 10:08:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  | def outboxMessageCreateWrap(httpPrefix: str, \ | 
					
						
							|  |  |  |                             nickname: str,domain: str,port: int, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                             messageJson: {}) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-03 21:37:46 +00:00
										 |  |  |     """Wraps a received message in a Create
 | 
					
						
							|  |  |  |     https://www.w3.org/TR/activitypub/#object-without-create | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-03 21:37:46 +00:00
										 |  |  |     statusNumber,published = getStatusNumber() | 
					
						
							|  |  |  |     if messageJson.get('published'): | 
					
						
							|  |  |  |         published = messageJson['published'] | 
					
						
							|  |  |  |     newPostId=httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber | 
					
						
							|  |  |  |     cc=[] | 
					
						
							|  |  |  |     if messageJson.get('cc'): | 
					
						
							|  |  |  |         cc=messageJson['cc'] | 
					
						
							| 
									
										
										
										
											2019-07-05 20:46:47 +00:00
										 |  |  |     # TODO | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     capabilityUrl=[] | 
					
						
							| 
									
										
										
										
											2019-07-03 21:37:46 +00:00
										 |  |  |     newPost = { | 
					
						
							| 
									
										
										
										
											2019-08-18 11:07:06 +00:00
										 |  |  |         "@context": "https://www.w3.org/ns/activitystreams", | 
					
						
							| 
									
										
										
										
											2019-07-03 21:37:46 +00:00
										 |  |  |         'id': newPostId+'/activity', | 
					
						
							| 
									
										
										
										
											2019-07-05 20:46:47 +00:00
										 |  |  |         'capability': capabilityUrl, | 
					
						
							| 
									
										
										
										
											2019-07-03 21:37:46 +00:00
										 |  |  |         'type': 'Create', | 
					
						
							|  |  |  |         'actor': httpPrefix+'://'+domain+'/users/'+nickname, | 
					
						
							|  |  |  |         'published': published, | 
					
						
							|  |  |  |         'to': messageJson['to'], | 
					
						
							|  |  |  |         'cc': cc, | 
					
						
							|  |  |  |         'object': messageJson | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     newPost['object']['id']=newPost['id'] | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |     newPost['object']['url']= \ | 
					
						
							|  |  |  |         httpPrefix+'://'+domain+'/@'+nickname+'/'+statusNumber | 
					
						
							|  |  |  |     newPost['object']['atomUri']= \ | 
					
						
							|  |  |  |         httpPrefix+'://'+domain+'/users/'+nickname+'/statuses/'+statusNumber | 
					
						
							| 
									
										
										
										
											2019-07-03 21:37:46 +00:00
										 |  |  |     return newPost | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  | def postIsAddressedToFollowers(baseDir: str, | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                                nickname: str,domain: str,port: int, \ | 
					
						
							|  |  |  |                                httpPrefix: str, | 
					
						
							| 
									
										
										
										
											2019-07-14 16:57:06 +00:00
										 |  |  |                                postJsonObject: {}) -> bool: | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |     """Returns true if the given post is addressed to followers of the nickname
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-14 16:57:06 +00:00
										 |  |  |     if not postJsonObject.get('object'): | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |     toList=[] | 
					
						
							|  |  |  |     ccList=[] | 
					
						
							| 
									
										
										
										
											2019-08-20 21:09:56 +00:00
										 |  |  |     if postJsonObject['type']!='Update' and \ | 
					
						
							|  |  |  |        isinstance(postJsonObject['object'], dict): | 
					
						
							| 
									
										
										
										
											2019-11-04 12:46:51 +00:00
										 |  |  |         if postJsonObject['object'].get('to'): | 
					
						
							|  |  |  |             toList=postJsonObject['object']['to'] | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |         if postJsonObject['object'].get('cc'): | 
					
						
							|  |  |  |             ccList=postJsonObject['object']['cc'] | 
					
						
							|  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2019-11-04 12:46:51 +00:00
										 |  |  |         if postJsonObject.get('to'): | 
					
						
							|  |  |  |             toList=postJsonObject['to'] | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |         if postJsonObject.get('cc'): | 
					
						
							|  |  |  |             ccList=postJsonObject['cc'] | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |          | 
					
						
							|  |  |  |     followersUrl=httpPrefix+'://'+domain+'/users/'+nickname+'/followers' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # does the followers url exist in 'to' or 'cc' lists? | 
					
						
							|  |  |  |     addressedToFollowers=False | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |     if followersUrl in toList: | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |         addressedToFollowers=True | 
					
						
							| 
									
										
										
										
											2019-11-04 12:46:51 +00:00
										 |  |  |     elif followersUrl in ccList: | 
					
						
							|  |  |  |         addressedToFollowers=True | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |     return addressedToFollowers | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-15 17:22:51 +00:00
										 |  |  | def postIsAddressedToPublic(baseDir: str,postJsonObject: {}) -> bool: | 
					
						
							|  |  |  |     """Returns true if the given post is addressed to public
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not postJsonObject.get('object'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not postJsonObject['object'].get('to'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |     publicUrl='https://www.w3.org/ns/activitystreams#Public' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # does the public url exist in 'to' or 'cc' lists? | 
					
						
							|  |  |  |     addressedToPublic=False | 
					
						
							|  |  |  |     if publicUrl in postJsonObject['object']['to']: | 
					
						
							|  |  |  |         addressedToPublic=True | 
					
						
							|  |  |  |     if not addressedToPublic: | 
					
						
							|  |  |  |         if not postJsonObject['object'].get('cc'): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         if publicUrl in postJsonObject['object']['cc']: | 
					
						
							|  |  |  |             addressedToPublic=True | 
					
						
							|  |  |  |     return addressedToPublic | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  | def createPublicPost(baseDir: str, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                      nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                      content: str,followersOnly: bool,saveToFile: bool, | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                      clientToServer: bool,\ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                      attachImageFilename: str,mediaType: str, \ | 
					
						
							|  |  |  |                      imageDescription: str,useBlurhash: bool, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                      inReplyTo=None,inReplyToAtomUri=None,subject=None, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                      schedulePost=False, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                      eventDate=None,eventTime=None,location=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |     """Public post
 | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-28 18:06:20 +00:00
										 |  |  |     domainFull=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domainFull=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |     return createPostBase(baseDir,nickname,domain,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-02 20:54:22 +00:00
										 |  |  |                           'https://www.w3.org/ns/activitystreams#Public', \ | 
					
						
							| 
									
										
										
										
											2019-07-28 18:06:20 +00:00
										 |  |  |                           httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers', \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                           httpPrefix,content,followersOnly,saveToFile, \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                           clientToServer, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                           attachImageFilename,mediaType, \ | 
					
						
							|  |  |  |                           imageDescription,useBlurhash, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                           False,inReplyTo,inReplyToAtomUri,subject, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                           schedulePost,eventDate,eventTime,location) | 
					
						
							| 
									
										
										
										
											2019-07-02 20:54:22 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-24 13:32:19 +00:00
										 |  |  | def createBlogPost(baseDir: str, \ | 
					
						
							|  |  |  |                    nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                    content: str,followersOnly: bool,saveToFile: bool, | 
					
						
							|  |  |  |                    clientToServer: bool,\ | 
					
						
							|  |  |  |                    attachImageFilename: str,mediaType: str, \ | 
					
						
							|  |  |  |                    imageDescription: str,useBlurhash: bool, \ | 
					
						
							|  |  |  |                    inReplyTo=None,inReplyToAtomUri=None,subject=None, \ | 
					
						
							|  |  |  |                    schedulePost=False, \ | 
					
						
							|  |  |  |                    eventDate=None,eventTime=None,location=None) -> {}: | 
					
						
							|  |  |  |     blog= \ | 
					
						
							|  |  |  |         createPublicPost(baseDir, \ | 
					
						
							|  |  |  |                          nickname,domain,port,httpPrefix, \ | 
					
						
							|  |  |  |                          content,followersOnly,saveToFile, | 
					
						
							|  |  |  |                          clientToServer,\ | 
					
						
							|  |  |  |                          attachImageFilename,mediaType, \ | 
					
						
							|  |  |  |                          imageDescription,useBlurhash, \ | 
					
						
							|  |  |  |                          inReplyTo,inReplyToAtomUri,subject, \ | 
					
						
							|  |  |  |                          schedulePost, \ | 
					
						
							|  |  |  |                          eventDate,eventTime,location) | 
					
						
							|  |  |  |     blog['object']['type']='Article' | 
					
						
							|  |  |  |     return blog | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-25 22:34:26 +00:00
										 |  |  | def createQuestionPost(baseDir: str, | 
					
						
							|  |  |  |                        nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                        content: str,qOptions: [], \ | 
					
						
							|  |  |  |                        followersOnly: bool,saveToFile: bool, | 
					
						
							|  |  |  |                        clientToServer: bool,\ | 
					
						
							|  |  |  |                        attachImageFilename: str,mediaType: str, \ | 
					
						
							|  |  |  |                        imageDescription: str,useBlurhash: bool, \ | 
					
						
							|  |  |  |                        subject: str,durationDays: int) -> {}: | 
					
						
							|  |  |  |     """Question post with multiple choice options
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     domainFull=domain | 
					
						
							|  |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domainFull=domain+':'+str(port) | 
					
						
							|  |  |  |     messageJson= \ | 
					
						
							|  |  |  |         createPostBase(baseDir,nickname,domain,port, \ | 
					
						
							|  |  |  |                        'https://www.w3.org/ns/activitystreams#Public', \ | 
					
						
							|  |  |  |                        httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers', \ | 
					
						
							|  |  |  |                        httpPrefix,content,followersOnly,saveToFile, \ | 
					
						
							|  |  |  |                        clientToServer, \ | 
					
						
							|  |  |  |                        attachImageFilename,mediaType, \ | 
					
						
							|  |  |  |                        imageDescription,useBlurhash, \ | 
					
						
							|  |  |  |                        False,None,None,subject, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                        False,None,None,None) | 
					
						
							| 
									
										
										
										
											2019-11-25 22:34:26 +00:00
										 |  |  |     messageJson['object']['type']='Question' | 
					
						
							| 
									
										
										
										
											2019-11-29 13:54:25 +00:00
										 |  |  |     messageJson['object']['oneOf']=[] | 
					
						
							| 
									
										
										
										
											2019-11-25 22:34:26 +00:00
										 |  |  |     messageJson['object']['votersCount']=0 | 
					
						
							|  |  |  |     currTime=datetime.datetime.utcnow() | 
					
						
							|  |  |  |     daysSinceEpoch=int((currTime - datetime.datetime(1970,1,1)).days + durationDays) | 
					
						
							|  |  |  |     endTime=datetime.datetime(1970,1,1) + datetime.timedelta(daysSinceEpoch) | 
					
						
							|  |  |  |     messageJson['object']['endTime']=endTime.strftime("%Y-%m-%dT%H:%M:%SZ") | 
					
						
							|  |  |  |     for questionOption in qOptions: | 
					
						
							| 
									
										
										
										
											2019-11-29 13:54:25 +00:00
										 |  |  |         messageJson['object']['oneOf'].append({ | 
					
						
							| 
									
										
										
										
											2019-11-25 22:34:26 +00:00
										 |  |  |             "type": "Note", | 
					
						
							|  |  |  |             "name": questionOption, | 
					
						
							|  |  |  |             "replies": { | 
					
						
							|  |  |  |                 "type": "Collection", | 
					
						
							|  |  |  |                 "totalItems": 0 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |     return messageJson | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-24 13:32:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-28 11:08:14 +00:00
										 |  |  | def createUnlistedPost(baseDir: str, | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                        nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                        content: str,followersOnly: bool,saveToFile: bool, | 
					
						
							| 
									
										
										
										
											2019-07-28 11:08:14 +00:00
										 |  |  |                        clientToServer: bool,\ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                        attachImageFilename: str,mediaType: str, \ | 
					
						
							|  |  |  |                        imageDescription: str,useBlurhash: bool, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                        inReplyTo=None,inReplyToAtomUri=None,subject=None, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                        schedulePost=False, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                        eventDate=None,eventTime=None,location=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-28 11:08:14 +00:00
										 |  |  |     """Unlisted post. This has the #Public and followers links inverted.
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-28 18:06:20 +00:00
										 |  |  |     domainFull=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domainFull=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-10-11 16:16:56 +00:00
										 |  |  |     return createPostBase(baseDir,nickname,domain,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-28 18:06:20 +00:00
										 |  |  |                           httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers', \ | 
					
						
							| 
									
										
										
										
											2019-07-28 11:08:14 +00:00
										 |  |  |                           'https://www.w3.org/ns/activitystreams#Public', \ | 
					
						
							| 
									
										
										
										
											2019-10-11 16:16:56 +00:00
										 |  |  |                           httpPrefix,content,followersOnly,saveToFile, \ | 
					
						
							| 
									
										
										
										
											2019-07-28 11:08:14 +00:00
										 |  |  |                           clientToServer, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                           attachImageFilename,mediaType, \ | 
					
						
							|  |  |  |                           imageDescription,useBlurhash, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                           False,inReplyTo, inReplyToAtomUri, subject, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                           schedulePost,eventDate,eventTime,location) | 
					
						
							| 
									
										
										
										
											2019-07-28 11:08:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  | def createFollowersOnlyPost(baseDir: str, | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                             nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                             content: str,followersOnly: bool,saveToFile: bool, | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |                             clientToServer: bool,\ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                             attachImageFilename: str,mediaType: str, \ | 
					
						
							|  |  |  |                             imageDescription: str,useBlurhash: bool, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                             inReplyTo=None,inReplyToAtomUri=None,subject=None, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                             schedulePost=False, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                             eventDate=None,eventTime=None,location=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |     """Followers only post
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-28 18:06:20 +00:00
										 |  |  |     domainFull=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443:  | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domainFull=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-10-11 16:16:56 +00:00
										 |  |  |     return createPostBase(baseDir,nickname,domain,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-28 18:06:20 +00:00
										 |  |  |                           httpPrefix+'://'+domainFull+'/users/'+nickname+'/followers', \ | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |                           None, | 
					
						
							| 
									
										
										
										
											2019-10-11 16:16:56 +00:00
										 |  |  |                           httpPrefix,content,followersOnly,saveToFile, \ | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |                           clientToServer, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                           attachImageFilename,mediaType, \ | 
					
						
							|  |  |  |                           imageDescription,useBlurhash, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                           False,inReplyTo, inReplyToAtomUri, subject, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                           schedulePost,eventDate,eventTime,location) | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-19 09:11:25 +00:00
										 |  |  | def getMentionedPeople(baseDir: str,httpPrefix: str, \ | 
					
						
							|  |  |  |                        content: str,domain: str,debug: bool) -> []: | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |     """Extracts a list of mentioned actors from the given message content
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if '@' not in content: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     mentions=[] | 
					
						
							|  |  |  |     words=content.split(' ') | 
					
						
							|  |  |  |     for wrd in words: | 
					
						
							|  |  |  |         if wrd.startswith('@'): | 
					
						
							|  |  |  |             handle=wrd[1:] | 
					
						
							| 
									
										
										
										
											2019-08-19 09:11:25 +00:00
										 |  |  |             if debug: | 
					
						
							|  |  |  |                 print('DEBUG: mentioned handle '+handle) | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |             if '@' not in handle: | 
					
						
							|  |  |  |                 handle=handle+'@'+domain | 
					
						
							|  |  |  |                 if not os.path.isdir(baseDir+'/accounts/'+handle): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 externalDomain=handle.split('@')[1] | 
					
						
							|  |  |  |                 if not ('.' in externalDomain or externalDomain=='localhost'): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |             mentionedNickname=handle.split('@')[0] | 
					
						
							| 
									
										
										
										
											2019-08-23 13:47:29 +00:00
										 |  |  |             mentionedDomain=handle.split('@')[1].strip('\n') | 
					
						
							|  |  |  |             if ':' in mentionedDomain: | 
					
						
							|  |  |  |                 mentionedDomain=mentionedDomain.split(':')[0] | 
					
						
							|  |  |  |             if not validNickname(mentionedDomain,mentionedNickname): | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |             actor= \ | 
					
						
							|  |  |  |                 httpPrefix+'://'+handle.split('@')[1]+ \ | 
					
						
							|  |  |  |                 '/users/'+mentionedNickname | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |             mentions.append(actor) | 
					
						
							| 
									
										
										
										
											2019-08-19 09:16:33 +00:00
										 |  |  |     return mentions | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def createDirectMessagePost(baseDir: str, | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                             nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                             content: str,followersOnly: bool,saveToFile: bool, | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |                             clientToServer: bool,\ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                             attachImageFilename: str,mediaType: str, \ | 
					
						
							|  |  |  |                             imageDescription: str,useBlurhash: bool, \ | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                             inReplyTo=None,inReplyToAtomUri=None, \ | 
					
						
							|  |  |  |                             subject=None,debug=False, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                             schedulePost=False, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                             eventDate=None,eventTime=None,location=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |     """Direct Message post
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2020-02-21 12:39:50 +00:00
										 |  |  |     mentionedPeople= \ | 
					
						
							|  |  |  |         getMentionedPeople(baseDir,httpPrefix,content,domain,debug) | 
					
						
							| 
									
										
										
										
											2019-08-19 09:11:25 +00:00
										 |  |  |     if debug: | 
					
						
							|  |  |  |         print('mentionedPeople: '+str(mentionedPeople)) | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  |     if not mentionedPeople: | 
					
						
							|  |  |  |         return None | 
					
						
							| 
									
										
										
										
											2019-08-19 09:37:14 +00:00
										 |  |  |     postTo=None | 
					
						
							|  |  |  |     postCc=None | 
					
						
							| 
									
										
										
										
											2019-11-10 12:28:12 +00:00
										 |  |  |     messageJson= \ | 
					
						
							|  |  |  |         createPostBase(baseDir,nickname,domain,port, \ | 
					
						
							|  |  |  |                        postTo,postCc, \ | 
					
						
							|  |  |  |                        httpPrefix,content,followersOnly,saveToFile, \ | 
					
						
							|  |  |  |                        clientToServer, \ | 
					
						
							|  |  |  |                        attachImageFilename,mediaType, \ | 
					
						
							|  |  |  |                        imageDescription,useBlurhash, \ | 
					
						
							|  |  |  |                        False,inReplyTo,inReplyToAtomUri,subject, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                        schedulePost,eventDate,eventTime,location) | 
					
						
							| 
									
										
										
										
											2019-11-10 12:28:12 +00:00
										 |  |  |     # mentioned recipients go into To rather than Cc | 
					
						
							| 
									
										
										
										
											2019-11-10 12:34:44 +00:00
										 |  |  |     messageJson['to']=messageJson['object']['cc'] | 
					
						
							|  |  |  |     messageJson['object']['to']=messageJson['to'] | 
					
						
							|  |  |  |     messageJson['cc']=[] | 
					
						
							| 
									
										
										
										
											2019-11-10 12:28:12 +00:00
										 |  |  |     messageJson['object']['cc']=[] | 
					
						
							| 
									
										
										
										
											2020-01-13 13:13:40 +00:00
										 |  |  |     if schedulePost: | 
					
						
							|  |  |  |         savePostToBox(baseDir,httpPrefix,messageJson['object']['id'], \ | 
					
						
							|  |  |  |                       nickname,domain,messageJson,'scheduled') | 
					
						
							| 
									
										
										
										
											2019-11-10 12:28:12 +00:00
										 |  |  |     return messageJson | 
					
						
							| 
									
										
										
										
											2019-07-27 22:48:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-11 11:25:27 +00:00
										 |  |  | def createReportPost(baseDir: str, | 
					
						
							|  |  |  |                      nickname: str, domain: str, port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                      content: str, followersOnly: bool, saveToFile: bool, | 
					
						
							|  |  |  |                      clientToServer: bool,\ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                      attachImageFilename: str,mediaType: str, \ | 
					
						
							|  |  |  |                      imageDescription: str,useBlurhash: bool, \ | 
					
						
							| 
									
										
										
										
											2019-08-11 11:25:27 +00:00
										 |  |  |                      debug: bool,subject=None) -> {}: | 
					
						
							|  |  |  |     """Send a report to moderators
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     domainFull=domain | 
					
						
							|  |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domainFull=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-08-11 11:25:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-11 11:33:29 +00:00
										 |  |  |     # add a title to distinguish moderation reports from other posts | 
					
						
							|  |  |  |     reportTitle='Moderation Report' | 
					
						
							|  |  |  |     if not subject: | 
					
						
							|  |  |  |         subject=reportTitle | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if not subject.startswith(reportTitle): | 
					
						
							|  |  |  |             subject=reportTitle+': '+subject | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-11 13:02:36 +00:00
										 |  |  |     # create the list of moderators from the moderators file | 
					
						
							| 
									
										
										
										
											2019-08-11 11:25:27 +00:00
										 |  |  |     moderatorsList=[] | 
					
						
							|  |  |  |     moderatorsFile=baseDir+'/accounts/moderators.txt' | 
					
						
							|  |  |  |     if os.path.isfile(moderatorsFile): | 
					
						
							|  |  |  |         with open (moderatorsFile, "r") as fileHandler: | 
					
						
							|  |  |  |             for line in fileHandler: | 
					
						
							|  |  |  |                 line=line.strip('\n') | 
					
						
							|  |  |  |                 if line.startswith('#'): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 if line.startswith('/users/'): | 
					
						
							|  |  |  |                     line=line.replace('users','') | 
					
						
							|  |  |  |                 if line.startswith('@'): | 
					
						
							|  |  |  |                     line=line[1:] | 
					
						
							|  |  |  |                 if '@' in line: | 
					
						
							|  |  |  |                     moderatorActor=httpPrefix+'://'+domainFull+'/users/'+line.split('@')[0] | 
					
						
							|  |  |  |                     if moderatorActor not in moderatorList: | 
					
						
							|  |  |  |                         moderatorsList.append(moderatorActor) | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 if line.startswith('http') or line.startswith('dat'): | 
					
						
							|  |  |  |                     # must be a local address - no remote moderators | 
					
						
							|  |  |  |                     if '://'+domainFull+'/' in line: | 
					
						
							|  |  |  |                         if line not in moderatorsList: | 
					
						
							|  |  |  |                             moderatorsList.append(line) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     if '/' not in line: | 
					
						
							|  |  |  |                         moderatorActor=httpPrefix+'://'+domainFull+'/users/'+line | 
					
						
							|  |  |  |                         if moderatorActor not in moderatorsList: | 
					
						
							|  |  |  |                             moderatorsList.append(moderatorActor) | 
					
						
							|  |  |  |     if len(moderatorsList)==0: | 
					
						
							|  |  |  |         # if there are no moderators then the admin becomes the moderator | 
					
						
							|  |  |  |         adminNickname=getConfigParam(baseDir,'admin') | 
					
						
							|  |  |  |         if adminNickname: | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |             moderatorsList.append(httpPrefix+'://'+domainFull+ \ | 
					
						
							|  |  |  |                                   '/users/'+adminNickname) | 
					
						
							| 
									
										
										
										
											2019-08-11 11:25:27 +00:00
										 |  |  |     if not moderatorsList: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: Sending report to moderators') | 
					
						
							|  |  |  |         print(str(moderatorsList)) | 
					
						
							|  |  |  |     postTo=moderatorsList | 
					
						
							|  |  |  |     postCc=None | 
					
						
							| 
									
										
										
										
											2019-08-11 18:32:29 +00:00
										 |  |  |     postJsonObject=None | 
					
						
							| 
									
										
										
										
											2019-11-16 15:24:07 +00:00
										 |  |  |     for toUrl in postTo:         | 
					
						
							| 
									
										
										
										
											2019-11-16 22:09:54 +00:00
										 |  |  |         # who is this report going to? | 
					
						
							|  |  |  |         toNickname=toUrl.split('/users/')[1] | 
					
						
							|  |  |  |         handle=toNickname+'@'+domain | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-11 18:32:29 +00:00
										 |  |  |         postJsonObject= \ | 
					
						
							| 
									
										
										
										
											2019-11-16 22:09:54 +00:00
										 |  |  |             createPostBase(baseDir,nickname,domain,port, \ | 
					
						
							| 
									
										
										
										
											2019-08-11 18:32:29 +00:00
										 |  |  |                            toUrl,postCc, \ | 
					
						
							| 
									
										
										
										
											2019-11-16 22:09:54 +00:00
										 |  |  |                            httpPrefix,content,followersOnly,saveToFile, \ | 
					
						
							| 
									
										
										
										
											2019-08-11 18:32:29 +00:00
										 |  |  |                            clientToServer, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                            attachImageFilename,mediaType, \ | 
					
						
							|  |  |  |                            imageDescription,useBlurhash, \ | 
					
						
							| 
									
										
										
										
											2019-11-16 22:09:54 +00:00
										 |  |  |                            True,None,None,subject, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                            False,None,None,None) | 
					
						
							| 
									
										
										
										
											2019-11-16 15:24:07 +00:00
										 |  |  |         if not postJsonObject: | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2019-11-16 18:14:00 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-16 18:11:30 +00:00
										 |  |  |         # update the inbox index with the report filename | 
					
						
							| 
									
										
										
										
											2019-11-16 22:09:54 +00:00
										 |  |  |         #indexFilename=baseDir+'/accounts/'+handle+'/inbox.index' | 
					
						
							|  |  |  |         #indexEntry=postJsonObject['id'].replace('/activity','').replace('/','#')+'.json' | 
					
						
							|  |  |  |         #if indexEntry not in open(indexFilename).read():         | 
					
						
							|  |  |  |         #    try: | 
					
						
							|  |  |  |         #        with open(indexFilename, 'a+') as fp: | 
					
						
							|  |  |  |         #            fp.write(indexEntry) | 
					
						
							|  |  |  |         #    except: | 
					
						
							|  |  |  |         #        pass | 
					
						
							| 
									
										
										
										
											2019-11-16 18:11:30 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-16 15:24:07 +00:00
										 |  |  |         # save a notification file so that the moderator | 
					
						
							|  |  |  |         # knows something new has appeared | 
					
						
							|  |  |  |         newReportFile=baseDir+'/accounts/'+handle+'/.newReport' | 
					
						
							|  |  |  |         if os.path.isfile(newReportFile): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             with open(newReportFile, 'w') as fp: | 
					
						
							|  |  |  |                 fp.write(toUrl+'/moderation') | 
					
						
							|  |  |  |         except: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-11 18:32:29 +00:00
										 |  |  |     return postJsonObject | 
					
						
							| 
									
										
										
										
											2019-08-11 11:25:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-17 10:15:01 +00:00
										 |  |  | def threadSendPost(session,postJsonStr: str,federationList: [],\ | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                    inboxUrl: str, baseDir: str, \ | 
					
						
							|  |  |  |                    signatureHeaderJson: {},postLog: [], \ | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |                    debug :bool) -> None: | 
					
						
							| 
									
										
										
										
											2019-10-23 18:28:04 +00:00
										 |  |  |     """Sends a with retries
 | 
					
						
							| 
									
										
										
										
											2019-06-30 13:38:01 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-06-30 13:20:23 +00:00
										 |  |  |     tries=0 | 
					
						
							| 
									
										
										
										
											2019-10-23 18:28:04 +00:00
										 |  |  |     sendIntervalSec=30 | 
					
						
							| 
									
										
										
										
											2019-06-30 13:38:01 +00:00
										 |  |  |     for attempt in range(20): | 
					
						
							| 
									
										
										
										
											2019-10-14 21:05:14 +00:00
										 |  |  |         postResult=None | 
					
						
							| 
									
										
										
										
											2019-10-23 18:44:03 +00:00
										 |  |  |         unauthorized=False | 
					
						
							| 
									
										
										
										
											2019-10-14 21:05:14 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2019-10-23 18:44:03 +00:00
										 |  |  |             postResult,unauthorized = \ | 
					
						
							| 
									
										
										
										
											2019-10-14 21:05:14 +00:00
										 |  |  |                 postJsonString(session,postJsonStr,federationList, \ | 
					
						
							|  |  |  |                                inboxUrl,signatureHeaderJson, \ | 
					
						
							|  |  |  |                                "inbox:write",debug) | 
					
						
							|  |  |  |         except Exception as e: | 
					
						
							| 
									
										
										
										
											2019-10-23 18:44:03 +00:00
										 |  |  |             print('ERROR: postJsonString failed '+str(e)) | 
					
						
							|  |  |  |         if unauthorized==True: | 
					
						
							|  |  |  |             print(postJsonStr) | 
					
						
							|  |  |  |             print('threadSendPost: Post is unauthorized') | 
					
						
							|  |  |  |             break | 
					
						
							| 
									
										
										
										
											2019-08-21 21:05:37 +00:00
										 |  |  |         if postResult: | 
					
						
							|  |  |  |             logStr='Success on try '+str(tries)+': '+postJsonStr | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             logStr='Retry '+str(tries)+': '+postJsonStr | 
					
						
							|  |  |  |         postLog.append(logStr) | 
					
						
							|  |  |  |         # keep the length of the log finite | 
					
						
							|  |  |  |         # Don't accumulate massive files on systems with limited resources | 
					
						
							|  |  |  |         while len(postLog)>16: | 
					
						
							| 
									
										
										
										
											2019-09-01 10:11:06 +00:00
										 |  |  |             postLog.pop(0) | 
					
						
							| 
									
										
										
										
											2019-10-16 11:27:43 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             # save the log file | 
					
						
							|  |  |  |             postLogFilename=baseDir+'/post.log' | 
					
						
							|  |  |  |             with open(postLogFilename, "a+") as logFile: | 
					
						
							|  |  |  |                 logFile.write(logStr+'\n') | 
					
						
							| 
									
										
										
										
											2019-08-21 21:05:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 13:38:01 +00:00
										 |  |  |         if postResult: | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |             if debug: | 
					
						
							| 
									
										
										
										
											2019-08-26 12:10:19 +00:00
										 |  |  |                 print('DEBUG: successful json post to '+inboxUrl) | 
					
						
							| 
									
										
										
										
											2019-06-30 13:38:01 +00:00
										 |  |  |             # our work here is done | 
					
						
							| 
									
										
										
										
											2019-06-30 13:20:23 +00:00
										 |  |  |             break | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-08-18 09:58:28 +00:00
										 |  |  |             print(postJsonStr) | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |             print('DEBUG: json post to '+inboxUrl+' failed. Waiting for '+ \ | 
					
						
							| 
									
										
										
										
											2019-10-23 18:28:04 +00:00
										 |  |  |                   str(sendIntervalSec)+' seconds.') | 
					
						
							|  |  |  |         time.sleep(sendIntervalSec) | 
					
						
							| 
									
										
										
										
											2019-08-21 20:23:20 +00:00
										 |  |  |         tries+=1 | 
					
						
							| 
									
										
										
										
											2019-10-16 10:08:21 +00:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  | def sendPost(projectVersion: str, \ | 
					
						
							|  |  |  |              session,baseDir: str,nickname: str, domain: str, port: int, \ | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |              toNickname: str, toDomain: str, toPort: int, cc: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |              httpPrefix: str, content: str, followersOnly: bool, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 10:33:57 +00:00
										 |  |  |              saveToFile: bool, clientToServer: bool, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |              attachImageFilename: str,mediaType: str, \ | 
					
						
							|  |  |  |              imageDescription: str,useBlurhash: bool, \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |              federationList: [],\ | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |              sendThreads: [], postLog: [], cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |              debug=False,inReplyTo=None,inReplyToAtomUri=None,subject=None) -> int: | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     """Post to another inbox
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-01 09:31:02 +00:00
										 |  |  |     withDigest=True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-23 14:08:10 +00:00
										 |  |  |     if toNickname=='inbox': | 
					
						
							|  |  |  |         # shared inbox actor on @domain@domain | 
					
						
							|  |  |  |         toNickname=toDomain | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     toDomainOriginal=toDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if toPort: | 
					
						
							|  |  |  |         if toPort!=80 and toPort!=443: | 
					
						
							|  |  |  |             if ':' not in toDomain: | 
					
						
							|  |  |  |                 toDomain=toDomain+':'+str(toPort)         | 
					
						
							| 
									
										
										
										
											2019-06-30 22:56:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |     handle=httpPrefix+'://'+toDomain+'/@'+toNickname | 
					
						
							| 
									
										
										
										
											2019-06-30 22:56:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # lookup the inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |     wfRequest = webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ | 
					
						
							|  |  |  |                                 domain,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     if not wfRequest: | 
					
						
							|  |  |  |         return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if not clientToServer: | 
					
						
							|  |  |  |         postToBox='inbox' | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         postToBox='outbox' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 22:56:37 +00:00
										 |  |  |     # get the actor inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-22 18:36:07 +00:00
										 |  |  |     inboxUrl,pubKeyId,pubKey,toPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |         getPersonBox(baseDir,session,wfRequest,personCache, \ | 
					
						
							| 
									
										
										
										
											2019-10-17 15:55:05 +00:00
										 |  |  |                      projectVersion,httpPrefix, \ | 
					
						
							|  |  |  |                      nickname,domain,postToBox) | 
					
						
							| 
									
										
										
										
											2019-07-05 14:39:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # If there are more than one followers on the target domain | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     # then send to the shared inbox indead of the individual inbox | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if nickname=='capabilities': | 
					
						
							|  |  |  |         inboxUrl=capabilityAcquisition | 
					
						
							|  |  |  |         if not capabilityAcquisition: | 
					
						
							|  |  |  |             return 2 | 
					
						
							| 
									
										
										
										
											2019-07-05 14:39:24 +00:00
										 |  |  |                       | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     if not inboxUrl: | 
					
						
							|  |  |  |         return 3 | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if not pubKey: | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |         return 4 | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if not toPersonId: | 
					
						
							|  |  |  |         return 5 | 
					
						
							|  |  |  |     # sharedInbox and capabilities are optional | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |     postJsonObject = \ | 
					
						
							|  |  |  |             createPostBase(baseDir,nickname,domain,port, \ | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |                            toPersonId,cc,httpPrefix,content, \ | 
					
						
							| 
									
										
										
										
											2019-07-03 15:10:18 +00:00
										 |  |  |                            followersOnly,saveToFile,clientToServer, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                            attachImageFilename,mediaType, \ | 
					
						
							|  |  |  |                            imageDescription,useBlurhash, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                            False,inReplyTo,inReplyToAtomUri,subject, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                            False,None,None,None) | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-30 22:56:37 +00:00
										 |  |  |     # get the senders private key | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |     privateKeyPem=getPersonKey(nickname,domain,baseDir,'private') | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     if len(privateKeyPem)==0: | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |         return 6 | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if toDomain not in inboxUrl: | 
					
						
							|  |  |  |         return 7 | 
					
						
							| 
									
										
										
										
											2019-08-23 14:08:10 +00:00
										 |  |  |     postPath=inboxUrl.split(toDomain,1)[1] | 
					
						
							| 
									
										
										
										
											2019-08-17 10:15:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # convert json to string so that there are no | 
					
						
							|  |  |  |     # subsequent conversions after creating message body digest | 
					
						
							|  |  |  |     postJsonStr=json.dumps(postJsonObject) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # construct the http header, including the message body digest | 
					
						
							| 
									
										
										
										
											2019-07-02 20:54:22 +00:00
										 |  |  |     signatureHeaderJson = \ | 
					
						
							| 
									
										
										
										
											2019-08-16 13:47:01 +00:00
										 |  |  |         createSignedHeader(privateKeyPem,nickname,domain,port, \ | 
					
						
							|  |  |  |                            toDomain,toPort, \ | 
					
						
							| 
									
										
										
										
											2019-08-17 10:15:01 +00:00
										 |  |  |                            postPath,httpPrefix,withDigest,postJsonStr) | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Keep the number of threads being used small | 
					
						
							| 
									
										
										
										
											2019-11-07 13:35:33 +00:00
										 |  |  |     while len(sendThreads)>1000: | 
					
						
							| 
									
										
										
										
											2019-10-16 14:46:29 +00:00
										 |  |  |         print('WARN: Maximum threads reached - killing send thread') | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |         sendThreads[0].kill() | 
					
						
							|  |  |  |         sendThreads.pop(0) | 
					
						
							| 
									
										
										
										
											2019-10-16 14:46:29 +00:00
										 |  |  |         print('WARN: thread killed') | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |     thr = \ | 
					
						
							|  |  |  |         threadWithTrace(target=threadSendPost, \ | 
					
						
							|  |  |  |                         args=(session, \ | 
					
						
							|  |  |  |                               postJsonStr, \ | 
					
						
							|  |  |  |                               federationList, \ | 
					
						
							|  |  |  |                               inboxUrl,baseDir, \ | 
					
						
							|  |  |  |                               signatureHeaderJson.copy(), \ | 
					
						
							|  |  |  |                               postLog, | 
					
						
							|  |  |  |                               debug),daemon=True) | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |     sendThreads.append(thr) | 
					
						
							|  |  |  |     thr.start() | 
					
						
							|  |  |  |     return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  | def sendPostViaServer(projectVersion: str, \ | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |                       baseDir: str,session,fromNickname: str,password: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |                       fromDomain: str, fromPort: int, \ | 
					
						
							|  |  |  |                       toNickname: str, toDomain: str, toPort: int, cc: str, \ | 
					
						
							|  |  |  |                       httpPrefix: str, content: str, followersOnly: bool, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                       attachImageFilename: str,mediaType: str, \ | 
					
						
							|  |  |  |                       imageDescription: str,useBlurhash: bool, \ | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |                       cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                       debug=False,inReplyTo=None, \ | 
					
						
							|  |  |  |                       inReplyToAtomUri=None,subject=None) -> int: | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     """Send a post via a proxy (c2s)
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-16 11:33:40 +00:00
										 |  |  |     if not session: | 
					
						
							|  |  |  |         print('WARN: No session for sendPostViaServer') | 
					
						
							|  |  |  |         return 6 | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     withDigest=True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if toPort: | 
					
						
							|  |  |  |         if toPort!=80 and toPort!=443: | 
					
						
							|  |  |  |             if ':' not in fromDomain: | 
					
						
							|  |  |  |                 fromDomain=fromDomain+':'+str(fromPort) | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     handle=httpPrefix+'://'+fromDomain+'/@'+fromNickname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # lookup the inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |     wfRequest = \ | 
					
						
							|  |  |  |         webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ | 
					
						
							|  |  |  |                         fromDomain,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     if not wfRequest: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: webfinger failed for '+handle) | 
					
						
							|  |  |  |         return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     postToBox='outbox' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # get the actor inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-22 18:36:07 +00:00
										 |  |  |     inboxUrl,pubKeyId,pubKey,fromPersonId,sharedInbox,capabilityAcquisition,avatarUrl,displayName = \ | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |         getPersonBox(baseDir,session,wfRequest,personCache, \ | 
					
						
							| 
									
										
										
										
											2019-10-17 15:55:05 +00:00
										 |  |  |                      projectVersion,httpPrefix,fromNickname, \ | 
					
						
							|  |  |  |                      fromDomain,postToBox) | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |                       | 
					
						
							|  |  |  |     if not inboxUrl: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: No '+postToBox+' was found for '+handle) | 
					
						
							|  |  |  |         return 3 | 
					
						
							|  |  |  |     if not fromPersonId: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: No actor was found for '+handle) | 
					
						
							|  |  |  |         return 4 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Get the json for the c2s post, not saving anything to file | 
					
						
							|  |  |  |     # Note that baseDir is set to None | 
					
						
							|  |  |  |     saveToFile=False | 
					
						
							|  |  |  |     clientToServer=True | 
					
						
							| 
									
										
										
										
											2019-07-17 14:43:51 +00:00
										 |  |  |     if toDomain.lower().endswith('public'): | 
					
						
							|  |  |  |         toPersonId='https://www.w3.org/ns/activitystreams#Public' | 
					
						
							|  |  |  |         fromDomainFull=fromDomain | 
					
						
							|  |  |  |         if fromPort: | 
					
						
							|  |  |  |             if fromPort!=80 and fromPort!=443: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |                 if ':' not in fromDomain: | 
					
						
							|  |  |  |                     fromDomainFull=fromDomain+':'+str(fromPort)                 | 
					
						
							| 
									
										
										
										
											2019-07-17 14:43:51 +00:00
										 |  |  |         cc=httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname+'/followers' | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         if toDomain.lower().endswith('followers') or \ | 
					
						
							|  |  |  |            toDomain.lower().endswith('followersonly'): | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |             toPersonId= \ | 
					
						
							|  |  |  |                 httpPrefix+'://'+ \ | 
					
						
							|  |  |  |                 fromDomainFull+'/users/'+fromNickname+'/followers' | 
					
						
							| 
									
										
										
										
											2019-07-17 14:43:51 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             toDomainFull=toDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |             if toPort: | 
					
						
							|  |  |  |                 if toPort!=80 and toPort!=443: | 
					
						
							|  |  |  |                     if ':' not in toDomain: | 
					
						
							|  |  |  |                         toDomainFull=toDomain+':'+str(toPort)         | 
					
						
							| 
									
										
										
										
											2019-07-17 14:43:51 +00:00
										 |  |  |             toPersonId=httpPrefix+'://'+toDomainFull+'/users/'+toNickname | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     postJsonObject = \ | 
					
						
							| 
									
										
										
										
											2019-08-09 16:24:44 +00:00
										 |  |  |             createPostBase(baseDir, \ | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |                            fromNickname,fromDomain,fromPort, \ | 
					
						
							|  |  |  |                            toPersonId,cc,httpPrefix,content, \ | 
					
						
							|  |  |  |                            followersOnly,saveToFile,clientToServer, \ | 
					
						
							| 
									
										
										
										
											2019-08-30 15:50:20 +00:00
										 |  |  |                            attachImageFilename,mediaType, \ | 
					
						
							|  |  |  |                            imageDescription,useBlurhash, \ | 
					
						
							| 
									
										
										
										
											2019-10-10 13:12:13 +00:00
										 |  |  |                            False,inReplyTo,inReplyToAtomUri,subject, \ | 
					
						
							| 
									
										
										
										
											2020-01-12 11:18:49 +00:00
										 |  |  |                            False,None,None,None) | 
					
						
							| 
									
										
										
										
											2019-07-16 14:23:06 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     authHeader=createBasicAuthHeader(fromNickname,password) | 
					
						
							| 
									
										
										
										
											2019-07-16 14:23:06 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if attachImageFilename: | 
					
						
							|  |  |  |         headers = {'host': fromDomain, \ | 
					
						
							|  |  |  |                    'Authorization': authHeader} | 
					
						
							|  |  |  |         postResult = \ | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |             postImage(session,attachImageFilename,[], \ | 
					
						
							|  |  |  |                       inboxUrl,headers,"inbox:write") | 
					
						
							| 
									
										
										
										
											2019-07-16 14:23:06 +00:00
										 |  |  |         #if not postResult: | 
					
						
							|  |  |  |         #    if debug: | 
					
						
							|  |  |  |         #        print('DEBUG: Failed to upload image') | 
					
						
							|  |  |  |         #    return 9 | 
					
						
							|  |  |  |       | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     headers = {'host': fromDomain, \ | 
					
						
							| 
									
										
										
										
											2019-11-09 21:39:04 +00:00
										 |  |  |                'Content-type': 'application/json', \ | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |                'Authorization': authHeader} | 
					
						
							|  |  |  |     postResult = \ | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |         postJsonString(session,json.dumps(postJsonObject),[], \ | 
					
						
							|  |  |  |                        inboxUrl,headers,"inbox:write",debug) | 
					
						
							| 
									
										
										
										
											2019-07-16 14:23:06 +00:00
										 |  |  |     #if not postResult: | 
					
						
							|  |  |  |     #    if debug: | 
					
						
							|  |  |  |     #        print('DEBUG: POST failed for c2s to '+inboxUrl) | 
					
						
							|  |  |  |     #    return 5 | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: c2s POST success') | 
					
						
							|  |  |  |     return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-08 08:51:33 +00:00
										 |  |  | def groupFollowersByDomain(baseDir :str,nickname :str,domain :str) -> {}: | 
					
						
							|  |  |  |     """Returns a dictionary with followers grouped by domain
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     handle=nickname+'@'+domain | 
					
						
							|  |  |  |     followersFilename=baseDir+'/accounts/'+handle+'/followers.txt' | 
					
						
							|  |  |  |     if not os.path.isfile(followersFilename): | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     grouped={} | 
					
						
							|  |  |  |     with open(followersFilename, "r") as f: | 
					
						
							|  |  |  |         for followerHandle in f: | 
					
						
							|  |  |  |             if '@' in followerHandle: | 
					
						
							|  |  |  |                 fHandle=followerHandle.strip().replace('\n','') | 
					
						
							|  |  |  |                 followerDomain=fHandle.split('@')[1] | 
					
						
							|  |  |  |                 if not grouped.get(followerDomain): | 
					
						
							|  |  |  |                     grouped[followerDomain]=[fHandle] | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     grouped[followerDomain].append(fHandle) | 
					
						
							|  |  |  |     return grouped | 
					
						
							| 
									
										
										
										
											2019-10-16 10:58:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def addFollowersToPublicPost(postJsonObject: {}) -> None: | 
					
						
							|  |  |  |     """Adds followers entry to cc if it doesn't exist
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not postJsonObject.get('actor'): | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if isinstance(postJsonObject['object'], str): | 
					
						
							|  |  |  |         if not postJsonObject.get('to'): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if len(postJsonObject['to'])>1: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if len(postJsonObject['to'])==0: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if not postJsonObject['to'][0].endswith('#Public'): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if postJsonObject.get('cc'): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         postJsonObject['cc']=postJsonObject['actor']+'/followers' | 
					
						
							|  |  |  |     elif isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |         if not postJsonObject['object'].get('to'): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if len(postJsonObject['object']['to'])>1: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if len(postJsonObject['object']['to'])==0: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if not postJsonObject['object']['to'][0].endswith('#Public'): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if postJsonObject['object'].get('cc'): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         postJsonObject['object']['cc']=postJsonObject['actor']+'/followers' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  | def sendSignedJson(postJsonObject: {},session,baseDir: str, \ | 
					
						
							|  |  |  |                    nickname: str, domain: str, port: int, \ | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |                    toNickname: str, toDomain: str, toPort: int, cc: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 10:33:57 +00:00
										 |  |  |                    httpPrefix: str, saveToFile: bool, clientToServer: bool, \ | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |                    federationList: [], \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                    sendThreads: [], postLog: [], cachedWebfingers: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                    personCache: {}, debug: bool,projectVersion: str) -> int: | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |     """Sends a signed json object to an inbox/outbox
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: sendSignedJson start') | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     if not session: | 
					
						
							|  |  |  |         print('WARN: No session specified for sendSignedJson') | 
					
						
							|  |  |  |         return 8 | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |     withDigest=True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |     sharedInbox=False | 
					
						
							|  |  |  |     if toNickname=='inbox': | 
					
						
							| 
									
										
										
										
											2019-08-23 13:47:29 +00:00
										 |  |  |         # shared inbox actor on @domain@domain | 
					
						
							|  |  |  |         toNickname=toDomain | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |         sharedInbox=True | 
					
						
							| 
									
										
										
										
											2019-08-16 20:04:24 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-23 14:08:10 +00:00
										 |  |  |     toDomainOriginal=toDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:04:24 +00:00
										 |  |  |     if toPort: | 
					
						
							|  |  |  |         if toPort!=80 and toPort!=443: | 
					
						
							|  |  |  |             if ':' not in toDomain: | 
					
						
							|  |  |  |                 toDomain=toDomain+':'+str(toPort)         | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 13:35:33 +00:00
										 |  |  |     handleBase=httpPrefix+'://'+toDomain+'/@' | 
					
						
							| 
									
										
										
										
											2019-10-21 14:12:22 +00:00
										 |  |  |     if toNickname: | 
					
						
							| 
									
										
										
										
											2019-11-07 13:35:33 +00:00
										 |  |  |         handle=handleBase+toNickname | 
					
						
							| 
									
										
										
										
											2019-10-21 14:12:22 +00:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2019-10-21 15:25:13 +00:00
										 |  |  |         singleUserInstanceNickname='dev' | 
					
						
							| 
									
										
										
										
											2019-11-07 13:35:33 +00:00
										 |  |  |         handle=handleBase+singleUserInstanceNickname | 
					
						
							| 
									
										
										
										
											2019-08-22 20:01:01 +00:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: handle - '+handle+' toPort '+str(toPort)) | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-23 13:47:29 +00:00
										 |  |  |     # lookup the inbox for the To handle | 
					
						
							|  |  |  |     wfRequest=webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ | 
					
						
							|  |  |  |                               domain,projectVersion) | 
					
						
							|  |  |  |     if not wfRequest: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: webfinger for '+handle+' failed') | 
					
						
							|  |  |  |         return 1 | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-17 14:41:47 +00:00
										 |  |  |     if wfRequest.get('errors'): | 
					
						
							|  |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-10-17 16:00:34 +00:00
										 |  |  |             print('DEBUG: webfinger for '+handle+' failed with errors '+str(wfRequest['errors'])) | 
					
						
							| 
									
										
										
										
											2019-10-17 14:41:47 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if not clientToServer: | 
					
						
							|  |  |  |         postToBox='inbox' | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         postToBox='outbox' | 
					
						
							| 
									
										
										
										
											2019-08-22 20:26:57 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     # get the actor inbox/outbox/capabilities for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-22 18:36:07 +00:00
										 |  |  |     inboxUrl,pubKeyId,pubKey,toPersonId,sharedInboxUrl,capabilityAcquisition,avatarUrl,displayName = \ | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |         getPersonBox(baseDir,session,wfRequest,personCache, \ | 
					
						
							| 
									
										
										
										
											2019-10-17 15:55:05 +00:00
										 |  |  |                      projectVersion,httpPrefix,nickname,domain,postToBox) | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if nickname=='capabilities': | 
					
						
							|  |  |  |         inboxUrl=capabilityAcquisition | 
					
						
							|  |  |  |         if not capabilityAcquisition: | 
					
						
							|  |  |  |             return 2 | 
					
						
							|  |  |  |     else: | 
					
						
							| 
									
										
										
										
											2019-09-16 11:45:04 +00:00
										 |  |  |         print("inboxUrl: "+str(inboxUrl)) | 
					
						
							|  |  |  |         print("toPersonId: "+str(toPersonId)) | 
					
						
							|  |  |  |         print("sharedInboxUrl: "+str(sharedInboxUrl)) | 
					
						
							| 
									
										
										
										
											2019-09-16 13:06:38 +00:00
										 |  |  |         if inboxUrl: | 
					
						
							|  |  |  |             if inboxUrl.endswith('/actor/inbox'): | 
					
						
							|  |  |  |                 inboxUrl=sharedInboxUrl | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |     if not inboxUrl: | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: missing inboxUrl') | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |         return 3 | 
					
						
							| 
									
										
										
										
											2019-08-04 21:26:31 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: Sending to endpoint '+inboxUrl) | 
					
						
							|  |  |  |                       | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if not pubKey: | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: missing pubkey') | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |         return 4 | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if not toPersonId: | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: missing personId') | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |         return 5 | 
					
						
							|  |  |  |     # sharedInbox and capabilities are optional | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # get the senders private key | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |     privateKeyPem=getPersonKey(nickname,domain,baseDir,'private',debug) | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |     if len(privateKeyPem)==0: | 
					
						
							| 
									
										
										
										
											2019-07-06 13:49:25 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |             print('DEBUG: Private key not found for '+ \ | 
					
						
							|  |  |  |                   nickname+'@'+domain+' in '+baseDir+'/keys/private') | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |         return 6 | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |     if toDomain not in inboxUrl: | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:04:24 +00:00
										 |  |  |             print('DEBUG: '+toDomain+' is not in '+inboxUrl) | 
					
						
							| 
									
										
										
										
											2019-07-05 22:13:20 +00:00
										 |  |  |         return 7 | 
					
						
							| 
									
										
										
										
											2019-08-23 17:03:50 +00:00
										 |  |  |     postPath=inboxUrl.split(toDomain,1)[1] | 
					
						
							| 
									
										
										
										
											2019-08-17 10:15:01 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-16 10:58:31 +00:00
										 |  |  |     addFollowersToPublicPost(postJsonObject) | 
					
						
							|  |  |  |      | 
					
						
							| 
									
										
										
										
											2019-08-17 10:15:01 +00:00
										 |  |  |     # convert json to string so that there are no | 
					
						
							|  |  |  |     # subsequent conversions after creating message body digest | 
					
						
							| 
									
										
										
										
											2019-11-09 21:39:04 +00:00
										 |  |  |     postJsonStr=json.dumps(postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-08-17 10:15:01 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # construct the http header, including the message body digest | 
					
						
							| 
									
										
										
										
											2019-07-05 18:57:19 +00:00
										 |  |  |     signatureHeaderJson = \ | 
					
						
							| 
									
										
										
										
											2019-08-16 13:47:01 +00:00
										 |  |  |         createSignedHeader(privateKeyPem,nickname,domain,port, \ | 
					
						
							|  |  |  |                            toDomain,toPort, \ | 
					
						
							| 
									
										
										
										
											2019-08-17 10:15:01 +00:00
										 |  |  |                            postPath,httpPrefix,withDigest,postJsonStr) | 
					
						
							| 
									
										
										
										
											2019-10-16 14:46:29 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2019-06-30 13:20:23 +00:00
										 |  |  |     # Keep the number of threads being used small | 
					
						
							| 
									
										
										
										
											2019-10-16 18:19:18 +00:00
										 |  |  |     while len(sendThreads)>1000: | 
					
						
							| 
									
										
										
										
											2019-10-04 12:22:56 +00:00
										 |  |  |         print('WARN: Maximum threads reached - killing send thread') | 
					
						
							| 
									
										
										
										
											2019-06-30 15:03:26 +00:00
										 |  |  |         sendThreads[0].kill() | 
					
						
							| 
									
										
										
										
											2019-06-30 13:38:01 +00:00
										 |  |  |         sendThreads.pop(0) | 
					
						
							| 
									
										
										
										
											2019-10-04 12:22:56 +00:00
										 |  |  |         print('WARN: thread killed') | 
					
						
							| 
									
										
										
										
											2019-10-16 18:19:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: starting thread to send post') | 
					
						
							| 
									
										
										
										
											2019-07-18 11:35:48 +00:00
										 |  |  |         pprint(postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |     thr = threadWithTrace(target=threadSendPost, \ | 
					
						
							|  |  |  |                           args=(session, \ | 
					
						
							| 
									
										
										
										
											2019-08-17 10:15:01 +00:00
										 |  |  |                                 postJsonStr, \ | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |                                 federationList, \ | 
					
						
							|  |  |  |                                 inboxUrl,baseDir, \ | 
					
						
							|  |  |  |                                 signatureHeaderJson.copy(), \ | 
					
						
							|  |  |  |                                 postLog, | 
					
						
							|  |  |  |                                 debug),daemon=True) | 
					
						
							| 
									
										
										
										
											2019-06-30 13:20:23 +00:00
										 |  |  |     sendThreads.append(thr) | 
					
						
							| 
									
										
										
										
											2019-10-16 18:19:18 +00:00
										 |  |  |     #thr.start() | 
					
						
							| 
									
										
										
										
											2019-06-30 10:14:02 +00:00
										 |  |  |     return 0 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  | def addToField(activityType: str,postJsonObject: {},debug: bool) -> ({},bool): | 
					
						
							|  |  |  |     """The Follow activity doesn't have a 'to' field and so one
 | 
					
						
							|  |  |  |     needs to be added so that activity distribution happens in a consistent way | 
					
						
							|  |  |  |     Returns true if a 'to' field exists or was added | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if postJsonObject.get('to'): | 
					
						
							|  |  |  |         return postJsonObject,True | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         pprint(postJsonObject) | 
					
						
							|  |  |  |         print('DEBUG: no "to" field when sending to named addresses 2') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     isSameType=False | 
					
						
							|  |  |  |     toFieldAdded=False | 
					
						
							|  |  |  |     if postJsonObject.get('object'): | 
					
						
							|  |  |  |         if isinstance(postJsonObject['object'], str): | 
					
						
							|  |  |  |             if postJsonObject.get('type'): | 
					
						
							|  |  |  |                 if postJsonObject['type']==activityType: | 
					
						
							|  |  |  |                     isSameType=True | 
					
						
							|  |  |  |                     if debug: | 
					
						
							|  |  |  |                         print('DEBUG: "to" field assigned to Follow') | 
					
						
							| 
									
										
										
										
											2019-08-18 16:49:35 +00:00
										 |  |  |                     toAddress=postJsonObject['object'] | 
					
						
							|  |  |  |                     if '/statuses/' in toAddress: | 
					
						
							|  |  |  |                         toAddress=toAddress.split('/statuses/')[0] | 
					
						
							|  |  |  |                     postJsonObject['to']=[toAddress] | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |                     toFieldAdded=True | 
					
						
							|  |  |  |         elif isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |             if postJsonObject['object'].get('type'): | 
					
						
							|  |  |  |                 if postJsonObject['object']['type']==activityType: | 
					
						
							|  |  |  |                     isSameType=True | 
					
						
							|  |  |  |                     if isinstance(postJsonObject['object']['object'], str): | 
					
						
							|  |  |  |                         if debug: | 
					
						
							|  |  |  |                             print('DEBUG: "to" field assigned to Follow') | 
					
						
							| 
									
										
										
										
											2019-08-18 16:49:35 +00:00
										 |  |  |                         toAddress=postJsonObject['object']['object'] | 
					
						
							|  |  |  |                         if '/statuses/' in toAddress: | 
					
						
							|  |  |  |                             toAddress=toAddress.split('/statuses/')[0] | 
					
						
							|  |  |  |                         postJsonObject['object']['to']=[toAddress] | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                         postJsonObject['to']= \ | 
					
						
							|  |  |  |                             [postJsonObject['object']['object']] | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |                         toFieldAdded=True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not isSameType: | 
					
						
							|  |  |  |         return postJsonObject,True | 
					
						
							|  |  |  |     if toFieldAdded: | 
					
						
							|  |  |  |         return postJsonObject,True | 
					
						
							|  |  |  |     return postJsonObject,False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  | def sendToNamedAddresses(session,baseDir: str, \ | 
					
						
							|  |  |  |                          nickname: str, domain: str, port: int, \ | 
					
						
							|  |  |  |                          httpPrefix: str,federationList: [], \ | 
					
						
							|  |  |  |                          sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                          cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                          postJsonObject: {},debug: bool, \ | 
					
						
							|  |  |  |                          projectVersion: str) -> None: | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  |     """sends a post to the specific named addresses in to/cc
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     if not session: | 
					
						
							|  |  |  |         print('WARN: No session for sendToNamedAddresses') | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  |     if not postJsonObject.get('object'): | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-08-20 20:35:15 +00:00
										 |  |  |     if isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |         isProfileUpdate=False | 
					
						
							|  |  |  |         # for actor updates there is no 'to' within the object | 
					
						
							|  |  |  |         if postJsonObject['object'].get('type') and postJsonObject.get('type'): | 
					
						
							| 
									
										
										
										
											2019-08-22 19:53:24 +00:00
										 |  |  |             if postJsonObject['type']=='Update' and \ | 
					
						
							|  |  |  |                (postJsonObject['object']['type']=='Person' or \ | 
					
						
							| 
									
										
										
										
											2019-08-23 20:09:00 +00:00
										 |  |  |                 postJsonObject['object']['type']=='Application' or \ | 
					
						
							| 
									
										
										
										
											2019-10-04 09:23:38 +00:00
										 |  |  |                 postJsonObject['object']['type']=='Group' or \ | 
					
						
							| 
									
										
										
										
											2019-08-22 19:53:24 +00:00
										 |  |  |                 postJsonObject['object']['type']=='Service'): | 
					
						
							| 
									
										
										
										
											2019-08-20 20:35:15 +00:00
										 |  |  |                 # use the original object, which has a 'to' | 
					
						
							|  |  |  |                 recipientsObject=postJsonObject | 
					
						
							|  |  |  |                 isProfileUpdate=True | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         if not isProfileUpdate: | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |             if not postJsonObject['object'].get('to'): | 
					
						
							| 
									
										
										
										
											2019-08-20 20:35:15 +00:00
										 |  |  |                 if debug: | 
					
						
							|  |  |  |                     pprint(postJsonObject) | 
					
						
							|  |  |  |                     print('DEBUG: no "to" field when sending to named addresses') | 
					
						
							|  |  |  |                 if postJsonObject['object'].get('type'):                     | 
					
						
							|  |  |  |                     if postJsonObject['object']['type']=='Follow': | 
					
						
							|  |  |  |                         if isinstance(postJsonObject['object']['object'], str): | 
					
						
							|  |  |  |                             if debug: | 
					
						
							|  |  |  |                                 print('DEBUG: "to" field assigned to Follow') | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                             postJsonObject['object']['to']= \ | 
					
						
							|  |  |  |                                 [postJsonObject['object']['object']] | 
					
						
							| 
									
										
										
										
											2019-08-20 20:35:15 +00:00
										 |  |  |                 if not postJsonObject['object'].get('to'): | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  |             recipientsObject=postJsonObject['object'] | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |     else:  | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |         postJsonObject,fieldAdded=addToField('Follow',postJsonObject,debug) | 
					
						
							| 
									
										
										
										
											2019-08-18 16:49:35 +00:00
										 |  |  |         if not fieldAdded: | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         postJsonObject,fieldAdded=addToField('Like',postJsonObject,debug) | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |         if not fieldAdded: | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |             return | 
					
						
							|  |  |  |         recipientsObject=postJsonObject | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     recipients=[] | 
					
						
							|  |  |  |     recipientType=['to','cc'] | 
					
						
							|  |  |  |     for rType in recipientType: | 
					
						
							| 
									
										
										
										
											2019-08-18 09:39:12 +00:00
										 |  |  |         if not recipientsObject.get(rType): | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2019-08-18 20:54:33 +00:00
										 |  |  |         if isinstance(recipientsObject[rType], list): | 
					
						
							| 
									
										
										
										
											2019-08-18 21:08:38 +00:00
										 |  |  |             if debug: | 
					
						
							| 
									
										
										
										
											2019-08-19 08:58:04 +00:00
										 |  |  |                 pprint(recipientsObject) | 
					
						
							| 
									
										
										
										
											2019-08-18 21:12:37 +00:00
										 |  |  |                 print('recipientsObject: '+str(recipientsObject[rType])) | 
					
						
							| 
									
										
										
										
											2019-08-18 20:54:33 +00:00
										 |  |  |             for address in recipientsObject[rType]: | 
					
						
							| 
									
										
										
										
											2019-08-18 21:15:09 +00:00
										 |  |  |                 if not address: | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2019-08-18 21:12:37 +00:00
										 |  |  |                 if '/' not in address: | 
					
						
							|  |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2019-08-18 20:54:33 +00:00
										 |  |  |                 if address.endswith('#Public'): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 if address.endswith('/followers'): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 recipients.append(address) | 
					
						
							|  |  |  |         elif isinstance(recipientsObject[rType], str): | 
					
						
							|  |  |  |             address=recipientsObject[rType] | 
					
						
							| 
									
										
										
										
											2019-08-18 21:15:09 +00:00
										 |  |  |             if address: | 
					
						
							|  |  |  |                 if '/' in address: | 
					
						
							|  |  |  |                     if address.endswith('#Public'): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     if address.endswith('/followers'): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     recipients.append(address) | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  |     if not recipients: | 
					
						
							| 
									
										
										
										
											2019-08-18 20:54:33 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: no individual recipients') | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-07-15 18:29:30 +00:00
										 |  |  |     if debug: | 
					
						
							| 
									
										
										
										
											2019-08-05 21:14:38 +00:00
										 |  |  |         print('DEBUG: Sending individually addressed posts: '+str(recipients)) | 
					
						
							| 
									
										
										
										
											2019-07-15 18:29:30 +00:00
										 |  |  |     # this is after the message has arrived at the server | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  |     clientToServer=False | 
					
						
							|  |  |  |     for address in recipients: | 
					
						
							|  |  |  |         toNickname=getNicknameFromActor(address) | 
					
						
							|  |  |  |         if not toNickname: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         toDomain,toPort=getDomainFromActor(address) | 
					
						
							|  |  |  |         if not toDomain: | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2019-07-15 18:29:30 +00:00
										 |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |             domainFull=domain | 
					
						
							|  |  |  |             if port: | 
					
						
							|  |  |  |                 if port!=80 and port!=443: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |                     if ':' not in domain: | 
					
						
							|  |  |  |                         domainFull=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |             toDomainFull=toDomain | 
					
						
							|  |  |  |             if toPort: | 
					
						
							|  |  |  |                 if toPort!=80 and toPort!=443: | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |                     if ':' not in toDomain: | 
					
						
							|  |  |  |                         toDomainFull=toDomain+':'+str(toPort) | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |             print('DEBUG: Post sending s2s: '+nickname+'@'+domainFull+ \ | 
					
						
							|  |  |  |                   ' to '+toNickname+'@'+toDomainFull) | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |         cc=[] | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  |         sendSignedJson(postJsonObject,session,baseDir, \ | 
					
						
							|  |  |  |                        nickname,domain,port, \ | 
					
						
							|  |  |  |                        toNickname,toDomain,toPort, \ | 
					
						
							|  |  |  |                        cc,httpPrefix,True,clientToServer, \ | 
					
						
							|  |  |  |                        federationList, \ | 
					
						
							|  |  |  |                        sendThreads,postLog,cachedWebfingers, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                        personCache,debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  | def hasSharedInbox(session,httpPrefix: str,domain: str) -> bool: | 
					
						
							|  |  |  |     """Returns true if the given domain has a shared inbox
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     wfRequest=webfingerHandle(session,domain+'@'+domain,httpPrefix,{}, \ | 
					
						
							|  |  |  |                               None,__version__) | 
					
						
							|  |  |  |     if wfRequest: | 
					
						
							| 
									
										
										
										
											2019-10-17 14:41:47 +00:00
										 |  |  |         if not wfRequest.get('errors'): | 
					
						
							|  |  |  |             return True | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  |     return False | 
					
						
							| 
									
										
										
										
											2019-11-04 10:43:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  | def sendToFollowers(session,baseDir: str, \ | 
					
						
							|  |  |  |                     nickname: str, domain: str, port: int, \ | 
					
						
							|  |  |  |                     httpPrefix: str,federationList: [], \ | 
					
						
							|  |  |  |                     sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                     cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                     postJsonObject: {},debug: bool, \ | 
					
						
							|  |  |  |                     projectVersion: str) -> None: | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |     """sends a post to the followers of the given nickname
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-08-20 21:04:24 +00:00
										 |  |  |     print('sendToFollowers') | 
					
						
							| 
									
										
										
										
											2019-07-16 10:19:04 +00:00
										 |  |  |     if not session: | 
					
						
							|  |  |  |         print('WARN: No session for sendToFollowers') | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |     if not postIsAddressedToFollowers(baseDir,nickname,domain, \ | 
					
						
							|  |  |  |                                       port,httpPrefix,postJsonObject): | 
					
						
							| 
									
										
										
										
											2019-07-15 18:29:30 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('Post is not addressed to followers') | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-08-20 21:04:24 +00:00
										 |  |  |     print('Post is addressed to followers') | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |     grouped=groupFollowersByDomain(baseDir,nickname,domain) | 
					
						
							|  |  |  |     if not grouped: | 
					
						
							| 
									
										
										
										
											2019-07-15 18:29:30 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('Post to followers did not resolve any domains') | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-08-20 21:04:24 +00:00
										 |  |  |     print('Post to followers resolved domains') | 
					
						
							| 
									
										
										
										
											2019-08-26 13:34:41 +00:00
										 |  |  |     print(str(grouped)) | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-15 18:29:30 +00:00
										 |  |  |     # this is after the message has arrived at the server | 
					
						
							| 
									
										
										
										
											2019-07-15 18:20:52 +00:00
										 |  |  |     clientToServer=False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |     # for each instance | 
					
						
							|  |  |  |     for followerDomain,followerHandles in grouped.items(): | 
					
						
							| 
									
										
										
										
											2019-07-16 22:57:45 +00:00
										 |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: follower handles for '+followerDomain) | 
					
						
							|  |  |  |             pprint(followerHandles) | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  |         withSharedInbox=hasSharedInbox(session,httpPrefix,followerDomain) | 
					
						
							|  |  |  |         if debug: | 
					
						
							| 
									
										
										
										
											2019-08-26 17:44:21 +00:00
										 |  |  |             if withSharedInbox: | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  |                 print(followerDomain+' has shared inbox') | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 print(followerDomain+' does not have a shared inbox') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  |         toPort=port | 
					
						
							|  |  |  |         index=0 | 
					
						
							|  |  |  |         toDomain=followerHandles[index].split('@')[1] | 
					
						
							|  |  |  |         if ':' in toDomain: | 
					
						
							|  |  |  |             toPort=toDomain.split(':')[1] | 
					
						
							|  |  |  |             toDomain=toDomain.split(':')[0] | 
					
						
							| 
									
										
										
										
											2019-08-22 19:47:10 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  |         cc='' | 
					
						
							| 
									
										
										
										
											2019-11-07 20:51:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 21:12:53 +00:00
										 |  |  |         if withSharedInbox: | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  |             toNickname=followerHandles[index].split('@')[0] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # if there are more than one followers on the domain | 
					
						
							|  |  |  |             # then send the post to the shared inbox | 
					
						
							|  |  |  |             if len(followerHandles)>1: | 
					
						
							|  |  |  |                 toNickname='inbox' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 21:12:53 +00:00
										 |  |  |             if toNickname!='inbox' and postJsonObject.get('type'): | 
					
						
							|  |  |  |                 if postJsonObject['type']=='Update': | 
					
						
							|  |  |  |                     if postJsonObject.get('object'): | 
					
						
							|  |  |  |                         if isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |                             if postJsonObject['object'].get('type'): | 
					
						
							|  |  |  |                                 if postJsonObject['object']['type']=='Person' or \ | 
					
						
							|  |  |  |                                    postJsonObject['object']['type']=='Application' or \ | 
					
						
							|  |  |  |                                    postJsonObject['object']['type']=='Group' or \ | 
					
						
							|  |  |  |                                    postJsonObject['object']['type']=='Service': | 
					
						
							|  |  |  |                                     print('Sending profile update to shared inbox of '+toDomain) | 
					
						
							|  |  |  |                                     toNickname='inbox' | 
					
						
							| 
									
										
										
										
											2020-02-05 11:46:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  |             if debug: | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                 print('DEBUG: Sending from '+nickname+'@'+domain+ \ | 
					
						
							|  |  |  |                       ' to '+toNickname+'@'+toDomain) | 
					
						
							| 
									
										
										
										
											2019-11-04 13:34:43 +00:00
										 |  |  |             sendSignedJson(postJsonObject,session,baseDir, \ | 
					
						
							|  |  |  |                            nickname,domain,port, \ | 
					
						
							|  |  |  |                            toNickname,toDomain,toPort, \ | 
					
						
							|  |  |  |                            cc,httpPrefix,True,clientToServer, \ | 
					
						
							|  |  |  |                            federationList, \ | 
					
						
							|  |  |  |                            sendThreads,postLog,cachedWebfingers, \ | 
					
						
							|  |  |  |                            personCache,debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             # send to individual followers without using a shared inbox | 
					
						
							|  |  |  |             for handle in followerHandles: | 
					
						
							| 
									
										
										
										
											2019-11-07 19:23:56 +00:00
										 |  |  |                 if debug: | 
					
						
							|  |  |  |                     print('DEBUG: Sending to '+handle) | 
					
						
							| 
									
										
										
										
											2019-08-26 17:42:06 +00:00
										 |  |  |                 toNickname=handle.split('@')[0] | 
					
						
							| 
									
										
										
										
											2019-11-07 19:23:56 +00:00
										 |  |  |                  | 
					
						
							| 
									
										
										
										
											2019-11-07 12:56:00 +00:00
										 |  |  |                 if debug: | 
					
						
							|  |  |  |                     if postJsonObject['type']!='Update': | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                         print('DEBUG: Sending from '+ \ | 
					
						
							|  |  |  |                               nickname+'@'+domain+' to '+ \ | 
					
						
							|  |  |  |                               toNickname+'@'+toDomain) | 
					
						
							| 
									
										
										
										
											2019-11-07 12:56:00 +00:00
										 |  |  |                     else: | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |                         print('DEBUG: Sending profile update from '+ \ | 
					
						
							|  |  |  |                               nickname+'@'+domain+' to '+ \ | 
					
						
							|  |  |  |                               toNickname+'@'+toDomain) | 
					
						
							| 
									
										
										
										
											2019-11-07 21:03:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 12:56:00 +00:00
										 |  |  |                 sendSignedJson(postJsonObject,session,baseDir, \ | 
					
						
							|  |  |  |                                nickname,domain,port, \ | 
					
						
							|  |  |  |                                toNickname,toDomain,toPort, \ | 
					
						
							|  |  |  |                                cc,httpPrefix,True,clientToServer, \ | 
					
						
							|  |  |  |                                federationList, \ | 
					
						
							|  |  |  |                                sendThreads,postLog,cachedWebfingers, \ | 
					
						
							|  |  |  |                                personCache,debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2020-02-05 11:46:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 20:51:29 +00:00
										 |  |  |         time.sleep(4) | 
					
						
							| 
									
										
										
										
											2019-11-04 10:43:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-07 21:16:40 +00:00
										 |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: End of sendToFollowers') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-04 10:43:19 +00:00
										 |  |  | def sendToFollowersThread(session,baseDir: str, \ | 
					
						
							| 
									
										
										
										
											2019-11-29 22:02:16 +00:00
										 |  |  |                           nickname: str,domain: str,port: int, \ | 
					
						
							| 
									
										
										
										
											2019-11-04 10:43:19 +00:00
										 |  |  |                           httpPrefix: str,federationList: [], \ | 
					
						
							|  |  |  |                           sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                           cachedWebfingers: {},personCache: {}, \ | 
					
						
							|  |  |  |                           postJsonObject: {},debug: bool, \ | 
					
						
							|  |  |  |                           projectVersion: str): | 
					
						
							|  |  |  |     """Returns a thread used to send a post to followers
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     sendThread= \ | 
					
						
							|  |  |  |         threadWithTrace(target=sendToFollowers, \ | 
					
						
							|  |  |  |                         args=(session,baseDir, \ | 
					
						
							|  |  |  |                               nickname,domain,port, \ | 
					
						
							|  |  |  |                               httpPrefix,federationList, \ | 
					
						
							|  |  |  |                               sendThreads,postLog, \ | 
					
						
							|  |  |  |                               cachedWebfingers,personCache, \ | 
					
						
							|  |  |  |                               postJsonObject.copy(),debug, \ | 
					
						
							|  |  |  |                               projectVersion),daemon=True) | 
					
						
							|  |  |  |     sendThread.start() | 
					
						
							|  |  |  |     return sendThread | 
					
						
							| 
									
										
										
										
											2019-07-08 13:30:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  | def createInbox(recentPostsCache: {}, \ | 
					
						
							|  |  |  |                 session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                 itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: | 
					
						
							|  |  |  |     return createBoxIndexed(recentPostsCache, \ | 
					
						
							|  |  |  |                             session,baseDir,'inbox',nickname,domain,port,httpPrefix, \ | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                             itemsPerPage,headerOnly,True,ocapAlways,pageNumber) | 
					
						
							| 
									
										
										
										
											2019-08-12 13:22:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-17 14:01:49 +00:00
										 |  |  | def createBookmarksTimeline(session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                             itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |     return createBoxIndexed({},session,baseDir,'tlbookmarks',nickname,domain,port,httpPrefix, \ | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                             itemsPerPage,headerOnly,True,ocapAlways,pageNumber) | 
					
						
							| 
									
										
										
										
											2019-11-17 14:01:49 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-28 16:21:43 +00:00
										 |  |  | def createDMTimeline(session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-08-25 16:09:56 +00:00
										 |  |  |                  itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |     return createBoxIndexed({},session,baseDir,'dm',nickname,domain,port,httpPrefix, \ | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                             itemsPerPage,headerOnly,True,ocapAlways,pageNumber) | 
					
						
							| 
									
										
										
										
											2019-08-25 16:09:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-28 16:21:43 +00:00
										 |  |  | def createRepliesTimeline(session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-09-23 19:53:18 +00:00
										 |  |  |                           itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |     return createBoxIndexed({},session,baseDir,'tlreplies',nickname,domain,port,httpPrefix, \ | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                             itemsPerPage,headerOnly,True,ocapAlways,pageNumber) | 
					
						
							| 
									
										
										
										
											2019-09-23 19:53:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-24 14:49:43 +00:00
										 |  |  | def createBlogsTimeline(session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                         itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: | 
					
						
							|  |  |  |     return createBoxIndexed({},session,baseDir,'tlblogs',nickname,domain,port,httpPrefix, \ | 
					
						
							|  |  |  |                             itemsPerPage,headerOnly,True,ocapAlways,pageNumber) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-28 16:21:43 +00:00
										 |  |  | def createMediaTimeline(session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-09-28 11:29:42 +00:00
										 |  |  |                         itemsPerPage: int,headerOnly: bool,ocapAlways: bool,pageNumber=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |     return createBoxIndexed({},session,baseDir,'tlmedia',nickname,domain,port,httpPrefix, \ | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                             itemsPerPage,headerOnly,True,ocapAlways,pageNumber) | 
					
						
							| 
									
										
										
										
											2019-09-28 11:29:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-28 16:21:43 +00:00
										 |  |  | def createOutbox(session,baseDir: str,nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-12 11:20:59 +00:00
										 |  |  |                  itemsPerPage: int,headerOnly: bool,authorized: bool,pageNumber=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |     return createBoxIndexed({},session,baseDir,'outbox',nickname,domain,port,httpPrefix, \ | 
					
						
							| 
									
										
										
										
											2019-11-18 17:04:45 +00:00
										 |  |  |                             itemsPerPage,headerOnly,authorized,False,pageNumber) | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  | def createModeration(baseDir: str,nickname: str,domain: str,port: int, \ | 
					
						
							|  |  |  |                      httpPrefix: str, \ | 
					
						
							|  |  |  |                      itemsPerPage: int,headerOnly: bool, \ | 
					
						
							|  |  |  |                      ocapAlways: bool,pageNumber=None) -> {}: | 
					
						
							| 
									
										
										
										
											2019-08-12 13:22:17 +00:00
										 |  |  |     boxDir = createPersonDir(nickname,domain,baseDir,'inbox') | 
					
						
							|  |  |  |     boxname='moderation' | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-08-12 13:22:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if not pageNumber: | 
					
						
							|  |  |  |         pageNumber=1 | 
					
						
							| 
									
										
										
										
											2019-11-16 22:09:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 13:22:17 +00:00
										 |  |  |     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'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if isModerator(baseDir,nickname): | 
					
						
							|  |  |  |         moderationIndexFile=baseDir+'/accounts/moderation.txt' | 
					
						
							|  |  |  |         if os.path.isfile(moderationIndexFile): | 
					
						
							|  |  |  |             with open(moderationIndexFile, "r") as f: | 
					
						
							|  |  |  |                 lines = f.readlines() | 
					
						
							|  |  |  |             boxHeader['totalItems']=len(lines) | 
					
						
							|  |  |  |             if headerOnly: | 
					
						
							|  |  |  |                 return boxHeader | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             pageLines=[] | 
					
						
							|  |  |  |             if len(lines)>0: | 
					
						
							|  |  |  |                 endLineNumber=len(lines)-1-int(itemsPerPage*pageNumber) | 
					
						
							|  |  |  |                 if endLineNumber<0: | 
					
						
							|  |  |  |                     endLineNumber=0 | 
					
						
							|  |  |  |                 startLineNumber=len(lines)-1-int(itemsPerPage*(pageNumber-1)) | 
					
						
							|  |  |  |                 if startLineNumber<0: | 
					
						
							|  |  |  |                     startLineNumber=0 | 
					
						
							|  |  |  |                 lineNumber=startLineNumber | 
					
						
							|  |  |  |                 while lineNumber>=endLineNumber: | 
					
						
							|  |  |  |                     pageLines.append(lines[lineNumber].strip('\n')) | 
					
						
							|  |  |  |                     lineNumber-=1 | 
					
						
							| 
									
										
										
										
											2019-11-16 22:09:54 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 13:22:17 +00:00
										 |  |  |             for postUrl in pageLines: | 
					
						
							|  |  |  |                 postFilename=boxDir+'/'+postUrl.replace('/','#')+'.json' | 
					
						
							|  |  |  |                 if os.path.isfile(postFilename): | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |                     postJsonObject=loadJson(postFilename) | 
					
						
							|  |  |  |                     if postJsonObject: | 
					
						
							|  |  |  |                         boxItems['orderedItems'].append(postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-09-17 12:14:36 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-12 13:22:17 +00:00
										 |  |  |     if headerOnly: | 
					
						
							|  |  |  |         return boxHeader | 
					
						
							|  |  |  |     return boxItems | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-14 09:17:50 +00:00
										 |  |  | def getStatusNumberFromPostFilename(filename) -> int: | 
					
						
							|  |  |  |     """Gets the status number from a post filename
 | 
					
						
							|  |  |  |     eg. https:##testdomain.com:8085#users#testuser567#statuses#1562958506952068.json | 
					
						
							|  |  |  |     returns 156295850695206 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if '#statuses#' not in filename: | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     return int(filename.split('#')[-1].replace('.json','')) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-25 16:09:56 +00:00
										 |  |  | def isDM(postJsonObject: {}) -> bool: | 
					
						
							|  |  |  |     """Returns true if the given post is a DM
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if postJsonObject['type']!='Create': | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not postJsonObject.get('object'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2020-02-19 17:57:59 +00:00
										 |  |  |     if postJsonObject['object']['type']!='Note' and \ | 
					
						
							|  |  |  |        postJsonObject['object']['type']!='Article': | 
					
						
							| 
									
										
										
										
											2019-08-25 16:09:56 +00:00
										 |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-11-16 17:29:02 +00:00
										 |  |  |     if postJsonObject['object'].get('moderationStatus'): | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-08-25 16:09:56 +00:00
										 |  |  |     fields=['to','cc'] | 
					
						
							|  |  |  |     for f in fields:         | 
					
						
							|  |  |  |         if not postJsonObject['object'].get(f): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         for toAddress in postJsonObject['object'][f]: | 
					
						
							|  |  |  |             if toAddress.endswith('#Public'): | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  |             if toAddress.endswith('followers'): | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  |     return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  | def isImageMedia(session,baseDir: str,httpPrefix: str, \ | 
					
						
							|  |  |  |                  nickname: str,domain: str,postJsonObject: {}) -> bool: | 
					
						
							| 
									
										
										
										
											2019-09-28 11:29:42 +00:00
										 |  |  |     """Returns true if the given post has attached image media
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-09-28 12:48:33 +00:00
										 |  |  |     if postJsonObject['type']=='Announce': | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |         postJsonAnnounce= \ | 
					
						
							|  |  |  |             downloadAnnounce(session,baseDir,httpPrefix, \ | 
					
						
							|  |  |  |                              nickname,domain,postJsonObject,__version__) | 
					
						
							| 
									
										
										
										
											2019-09-28 16:21:43 +00:00
										 |  |  |         if postJsonAnnounce: | 
					
						
							|  |  |  |             postJsonObject=postJsonAnnounce | 
					
						
							| 
									
										
										
										
											2019-09-28 11:29:42 +00:00
										 |  |  |     if postJsonObject['type']!='Create': | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not postJsonObject.get('object'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-11-16 22:20:16 +00:00
										 |  |  |     if postJsonObject['object'].get('moderationStatus'): | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2020-02-19 17:57:59 +00:00
										 |  |  |     if postJsonObject['object']['type']!='Note' and \ | 
					
						
							|  |  |  |        postJsonObject['object']['type']!='Article': | 
					
						
							| 
									
										
										
										
											2019-09-28 11:29:42 +00:00
										 |  |  |         return False | 
					
						
							|  |  |  |     if not postJsonObject['object'].get('attachment'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not isinstance(postJsonObject['object']['attachment'], list): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     for attach in postJsonObject['object']['attachment']: | 
					
						
							|  |  |  |         if attach.get('mediaType') and attach.get('url'): | 
					
						
							| 
									
										
										
										
											2019-10-23 13:24:09 +00:00
										 |  |  |             if attach['mediaType'].startswith('image/') or \ | 
					
						
							|  |  |  |                attach['mediaType'].startswith('audio/') or \ | 
					
						
							|  |  |  |                attach['mediaType'].startswith('video/'): | 
					
						
							| 
									
										
										
										
											2019-09-28 11:29:42 +00:00
										 |  |  |                 return True | 
					
						
							|  |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-23 19:53:18 +00:00
										 |  |  | def isReply(postJsonObject: {},actor: str) -> bool: | 
					
						
							|  |  |  |     """Returns true if the given post is a reply to the given actor
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-11-09 21:39:04 +00:00
										 |  |  |     if postJsonObject['type']!='Create': | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not postJsonObject.get('object'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2019-11-16 22:18:20 +00:00
										 |  |  |     if postJsonObject['object'].get('moderationStatus'): | 
					
						
							|  |  |  |         return False | 
					
						
							| 
									
										
										
										
											2020-02-19 17:57:59 +00:00
										 |  |  |     if postJsonObject['object']['type']!='Note' and \ | 
					
						
							|  |  |  |        postJsonObject['object']['type']!='Article': | 
					
						
							| 
									
										
										
										
											2019-11-09 21:39:04 +00:00
										 |  |  |         return False | 
					
						
							|  |  |  |     if postJsonObject['object'].get('inReplyTo'): | 
					
						
							|  |  |  |         if postJsonObject['object']['inReplyTo'].startswith(actor): | 
					
						
							|  |  |  |             return True         | 
					
						
							|  |  |  |     if not postJsonObject['object'].get('tag'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not isinstance(postJsonObject['object']['tag'], list): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     for tag in postJsonObject['object']['tag']: | 
					
						
							|  |  |  |         if not tag.get('type'): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         if tag['type']=='Mention': | 
					
						
							|  |  |  |             if not tag.get('href'): | 
					
						
							| 
									
										
										
										
											2019-10-22 19:07:23 +00:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2019-11-09 21:39:04 +00:00
										 |  |  |             if actor in tag['href']: | 
					
						
							|  |  |  |                 return True | 
					
						
							| 
									
										
										
										
											2019-10-22 19:07:23 +00:00
										 |  |  |     return False | 
					
						
							| 
									
										
										
										
											2019-09-23 19:53:18 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 09:22:40 +00:00
										 |  |  | def createBoxIndex(boxDir: str,postsInBoxDict: {}) -> int: | 
					
						
							|  |  |  |     """ Creates an index for the given box
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     postsCtr=0 | 
					
						
							|  |  |  |     postsInPersonInbox=os.scandir(boxDir) | 
					
						
							|  |  |  |     for postFilename in postsInPersonInbox: | 
					
						
							|  |  |  |         postFilename=postFilename.name | 
					
						
							|  |  |  |         if not postFilename.endswith('.json'): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         # extract the status number | 
					
						
							|  |  |  |         statusNumber=getStatusNumberFromPostFilename(postFilename) | 
					
						
							|  |  |  |         if statusNumber: | 
					
						
							|  |  |  |             postsInBoxDict[statusNumber]=os.path.join(boxDir, postFilename) | 
					
						
							|  |  |  |             postsCtr+=1 | 
					
						
							|  |  |  |     return postsCtr | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 09:49:26 +00:00
										 |  |  | def createSharedInboxIndex(baseDir: str,sharedBoxDir: str, \ | 
					
						
							|  |  |  |                            postsInBoxDict: {},postsCtr: int, \ | 
					
						
							|  |  |  |                            nickname: str,domain: str, \ | 
					
						
							|  |  |  |                            ocapAlways: bool) -> int: | 
					
						
							| 
									
										
										
										
											2019-10-20 09:36:07 +00:00
										 |  |  |     """ Creates an index for the given shared inbox
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     handle=nickname+'@'+domain | 
					
						
							|  |  |  |     followingFilename=baseDir+'/accounts/'+handle+'/following.txt' | 
					
						
							|  |  |  |     postsInSharedInbox=os.scandir(sharedBoxDir) | 
					
						
							| 
									
										
										
										
											2019-10-20 09:47:06 +00:00
										 |  |  |     followingHandles=None | 
					
						
							|  |  |  |     for postFilename in postsInSharedInbox:         | 
					
						
							| 
									
										
										
										
											2019-10-20 09:36:07 +00:00
										 |  |  |         postFilename=postFilename.name | 
					
						
							|  |  |  |         if not postFilename.endswith('.json'): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         statusNumber=getStatusNumberFromPostFilename(postFilename) | 
					
						
							|  |  |  |         if not statusNumber: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |                  | 
					
						
							|  |  |  |         sharedInboxFilename=os.path.join(sharedBoxDir, postFilename) | 
					
						
							|  |  |  |         # get the actor from the shared post | 
					
						
							| 
									
										
										
										
											2019-11-23 10:08:00 +00:00
										 |  |  |         postJsonObject=loadJson(sharedInboxFilename,0) | 
					
						
							|  |  |  |         if not postJsonObject: | 
					
						
							|  |  |  |             print('WARN: json load exception createSharedInboxIndex') | 
					
						
							| 
									
										
										
										
											2019-10-20 09:36:07 +00:00
										 |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         actorNickname=getNicknameFromActor(postJsonObject['actor']) | 
					
						
							| 
									
										
										
										
											2019-10-20 09:51:32 +00:00
										 |  |  |         if not actorNickname: | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2019-10-20 09:36:07 +00:00
										 |  |  |         actorDomain,actorPort=getDomainFromActor(postJsonObject['actor']) | 
					
						
							| 
									
										
										
										
											2019-10-20 09:51:32 +00:00
										 |  |  |         if not actorDomain: | 
					
						
							| 
									
										
										
										
											2019-10-20 09:36:07 +00:00
										 |  |  |             continue | 
					
						
							| 
									
										
										
										
											2019-10-20 09:47:06 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 09:36:07 +00:00
										 |  |  |         # is the actor followed by this account? | 
					
						
							| 
									
										
										
										
											2019-10-20 09:47:06 +00:00
										 |  |  |         if not followingHandles: | 
					
						
							|  |  |  |             with open(followingFilename, 'r') as followingFile: | 
					
						
							|  |  |  |                 followingHandles = followingFile.read() | 
					
						
							|  |  |  |         if actorNickname+'@'+actorDomain not in followingHandles: | 
					
						
							| 
									
										
										
										
											2019-10-20 09:36:07 +00:00
										 |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ocapAlways: | 
					
						
							|  |  |  |             capsList=None | 
					
						
							|  |  |  |             # Note: should this be in the Create or the object of a post? | 
					
						
							|  |  |  |             if postJsonObject.get('capability'): | 
					
						
							|  |  |  |                 if isinstance(postJsonObject['capability'], list):                                 | 
					
						
							|  |  |  |                     capsList=postJsonObject['capability'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Have capabilities been granted for the sender? | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |             ocapFilename= \ | 
					
						
							|  |  |  |                 baseDir+'/accounts/'+handle+'/ocap/granted/'+ \ | 
					
						
							|  |  |  |                 postJsonObject['actor'].replace('/','#')+'.json' | 
					
						
							| 
									
										
										
										
											2019-10-20 09:36:07 +00:00
										 |  |  |             if not os.path.isfile(ocapFilename): | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # read the capabilities id | 
					
						
							| 
									
										
										
										
											2019-11-23 10:08:00 +00:00
										 |  |  |             ocapJson=loadJson(ocapFilename,0) | 
					
						
							|  |  |  |             if not ocapJson: | 
					
						
							|  |  |  |                 print('WARN: json load exception createSharedInboxIndex') | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2019-10-20 09:36:07 +00:00
										 |  |  |                 if ocapJson.get('id'): | 
					
						
							|  |  |  |                     if ocapJson['id'] in capsList:                                     | 
					
						
							|  |  |  |                         postsInBoxDict[statusNumber]=sharedInboxFilename | 
					
						
							|  |  |  |                         postsCtr+=1 | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             postsInBoxDict[statusNumber]=sharedInboxFilename | 
					
						
							|  |  |  |             postsCtr+=1 | 
					
						
							|  |  |  |     return postsCtr | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  | def addPostStringToTimeline(postStr: str,boxname: str, \ | 
					
						
							|  |  |  |                             postsInBox: [],boxActor: str) -> bool: | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |     """ is this a valid timeline post?
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     # must be a "Note" or "Announce" type | 
					
						
							| 
									
										
										
										
											2020-02-19 18:11:21 +00:00
										 |  |  |     if '"Note"' in postStr or \ | 
					
						
							|  |  |  |        '"Article"' in postStr or \ | 
					
						
							|  |  |  |        '"Announce"' in postStr or \ | 
					
						
							| 
									
										
										
										
											2019-11-25 10:10:59 +00:00
										 |  |  |        ('"Question"' in postStr and ('"Create"' in postStr or '"Update"' in postStr)): | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |         if boxname=='dm': | 
					
						
							|  |  |  |             if '#Public' in postStr or '/followers' in postStr: | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  |         elif boxname=='tlreplies': | 
					
						
							|  |  |  |             if boxActor not in postStr: | 
					
						
							|  |  |  |                 return False | 
					
						
							| 
									
										
										
										
											2020-02-24 14:39:25 +00:00
										 |  |  |         elif boxname=='tlblogs': | 
					
						
							|  |  |  |             if '"Create"' not in postStr: | 
					
						
							|  |  |  |                 return False | 
					
						
							|  |  |  |             if '"Article"' not in postStr: | 
					
						
							|  |  |  |                 return False | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |         elif boxname=='tlmedia': | 
					
						
							|  |  |  |             if '"Create"' in postStr: | 
					
						
							|  |  |  |                 if 'mediaType' not in postStr or 'image/' not in postStr: | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                     return False | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |         # add the post to the dictionary | 
					
						
							|  |  |  |         postsInBox.append(postStr) | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  | def addPostToTimeline(filePath: str,boxname: str, \ | 
					
						
							|  |  |  |                       postsInBox: [],boxActor: str) -> bool: | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |     """ Reads a post from file and decides whether it is valid
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     with open(filePath, 'r') as postFile: | 
					
						
							|  |  |  |         postStr = postFile.read() | 
					
						
							|  |  |  |         return addPostStringToTimeline(postStr,boxname,postsInBox,boxActor) | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  | def createBoxIndexed(recentPostsCache: {}, \ | 
					
						
							|  |  |  |                      session,baseDir: str,boxname: str, \ | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                      nickname: str,domain: str,port: int,httpPrefix: str, \ | 
					
						
							|  |  |  |                      itemsPerPage: int,headerOnly: bool,authorized :bool, \ | 
					
						
							|  |  |  |                      ocapAlways: bool,pageNumber=None) -> {}: | 
					
						
							|  |  |  |     """Constructs the box feed for a person with the given nickname
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not authorized or not pageNumber: | 
					
						
							|  |  |  |         pageNumber=1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if boxname!='inbox' and boxname!='dm' and \ | 
					
						
							|  |  |  |        boxname!='tlreplies' and boxname!='tlmedia' and \ | 
					
						
							| 
									
										
										
										
											2020-02-24 14:39:25 +00:00
										 |  |  |        boxname!='tlblogs' and \ | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |        boxname!='outbox' and boxname!='tlbookmarks': | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if boxname!='dm' and boxname!='tlreplies' and \ | 
					
						
							| 
									
										
										
										
											2020-02-24 14:39:25 +00:00
										 |  |  |        boxname!='tlmedia' and boxname!='tlblogs' and \ | 
					
						
							|  |  |  |        boxname!='tlbookmarks': | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |         boxDir = createPersonDir(nickname,domain,baseDir,boxname) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         # extract DMs or replies or media from the inbox | 
					
						
							|  |  |  |         boxDir = createPersonDir(nickname,domain,baseDir,'inbox') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     announceCacheDir=baseDir+'/cache/announce/'+nickname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sharedBoxDir=None | 
					
						
							|  |  |  |     if boxname=='inbox' or boxname=='tlreplies' or \ | 
					
						
							| 
									
										
										
										
											2020-02-24 14:39:25 +00:00
										 |  |  |        boxname=='tlmedia' or boxname=='tlblogs': | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |         sharedBoxDir = createPersonDir('inbox',domain,baseDir,boxname) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # bookmarks timeline is like the inbox but has its own separate index | 
					
						
							|  |  |  |     indexBoxName=boxname | 
					
						
							|  |  |  |     if boxname=='tlbookmarks': | 
					
						
							|  |  |  |         indexBoxName='bookmarks' | 
					
						
							| 
									
										
										
										
											2019-11-18 12:43:21 +00:00
										 |  |  |     elif boxname=='dm': | 
					
						
							|  |  |  |         indexBoxName='dm' | 
					
						
							|  |  |  |     elif boxname=='tlreplies': | 
					
						
							|  |  |  |         indexBoxName='tlreplies' | 
					
						
							|  |  |  |     elif boxname=='tlmedia': | 
					
						
							|  |  |  |         indexBoxName='tlmedia' | 
					
						
							| 
									
										
										
										
											2020-02-24 14:39:25 +00:00
										 |  |  |     elif boxname=='tlblogs': | 
					
						
							|  |  |  |         indexBoxName='tlblogs' | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domain=domain+':'+str(port) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     boxActor=httpPrefix+'://'+domain+'/users/'+nickname | 
					
						
							| 
									
										
										
										
											2020-01-19 20:19:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |     pageStr='?page=true' | 
					
						
							|  |  |  |     if pageNumber: | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             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'} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     postsInBox=[] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     indexFilename=baseDir+'/accounts/'+nickname+'@'+domain+'/'+indexBoxName+'.index' | 
					
						
							|  |  |  |     postsCtr=0 | 
					
						
							|  |  |  |     if os.path.isfile(indexFilename): | 
					
						
							|  |  |  |         maxPostCtr=itemsPerPage*pageNumber | 
					
						
							|  |  |  |         with open(indexFilename, 'r') as indexFile: | 
					
						
							|  |  |  |             while postsCtr<maxPostCtr: | 
					
						
							|  |  |  |                 postFilename=indexFile.readline() | 
					
						
							| 
									
										
										
										
											2019-11-18 12:40:38 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 15:04:08 +00:00
										 |  |  |                 if not postFilename: | 
					
						
							|  |  |  |                     postsCtr+=1 | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 12:40:38 +00:00
										 |  |  |                 # Skip through any posts previous to the current page | 
					
						
							|  |  |  |                 if postsCtr<int((pageNumber-1)*itemsPerPage):                     | 
					
						
							| 
									
										
										
										
											2019-11-18 14:12:00 +00:00
										 |  |  |                     postsCtr+=1 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                     continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 12:54:41 +00:00
										 |  |  |                 # if this is a full path then remove the directories | 
					
						
							|  |  |  |                 if '/' in postFilename: | 
					
						
							|  |  |  |                     postFilename=postFilename.split('/')[-1] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                 # filename of the post without any extension or path | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |                 # This should also correspond to any index entry in the posts cache | 
					
						
							| 
									
										
										
										
											2019-11-18 15:04:08 +00:00
										 |  |  |                 postUrl=postFilename.replace('\n','').replace('.json','').strip() | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 postAdded=False | 
					
						
							| 
									
										
										
										
											2019-11-25 10:10:59 +00:00
										 |  |  |                 # is the post cached in memory? | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |                 if recentPostsCache.get('index'): | 
					
						
							|  |  |  |                     if postUrl in recentPostsCache['index']: | 
					
						
							|  |  |  |                         if recentPostsCache['json'].get(postUrl): | 
					
						
							|  |  |  |                             addPostStringToTimeline(recentPostsCache['json'][postUrl], \ | 
					
						
							|  |  |  |                                                     boxname,postsInBox,boxActor) | 
					
						
							|  |  |  |                             postAdded=True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if not postAdded: | 
					
						
							| 
									
										
										
										
											2019-11-25 10:10:59 +00:00
										 |  |  |                     # read the post from file | 
					
						
							| 
									
										
										
										
											2019-11-24 12:12:29 +00:00
										 |  |  |                     fullPostFilename= \ | 
					
						
							|  |  |  |                         locatePost(baseDir,nickname,domain,postUrl,False) | 
					
						
							|  |  |  |                     if fullPostFilename: | 
					
						
							|  |  |  |                         addPostToTimeline(fullPostFilename,boxname,postsInBox,boxActor) | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         print('WARN: unable to locate post '+postUrl) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 14:12:00 +00:00
										 |  |  |                 postsCtr+=1 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Generate first and last entries within header | 
					
						
							|  |  |  |     if postsCtr>0: | 
					
						
							|  |  |  |         lastPage=int(postsCtr/itemsPerPage) | 
					
						
							|  |  |  |         if lastPage<1: | 
					
						
							|  |  |  |             lastPage=1 | 
					
						
							|  |  |  |         boxHeader['last']= \ | 
					
						
							|  |  |  |             httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page='+str(lastPage) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if headerOnly: | 
					
						
							| 
									
										
										
										
											2019-11-18 11:55:27 +00:00
										 |  |  |         boxHeader['totalItems']=len(postsInBox) | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |         prevPageStr='true' | 
					
						
							|  |  |  |         if pageNumber>1: | 
					
						
							|  |  |  |             prevPageStr=str(pageNumber-1) | 
					
						
							|  |  |  |         boxHeader['prev']= \ | 
					
						
							|  |  |  |             httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page='+prevPageStr | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:55:27 +00:00
										 |  |  |         nextPageStr=str(pageNumber+1) | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |         boxHeader['next']= \ | 
					
						
							|  |  |  |             httpPrefix+'://'+domain+'/users/'+nickname+'/'+boxname+'?page='+nextPageStr | 
					
						
							|  |  |  |         return boxHeader | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 11:55:27 +00:00
										 |  |  |     for postStr in postsInBox: | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |         p=None | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             p=json.loads(postStr) | 
					
						
							|  |  |  |         except: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # remove any capability so that it's not displayed | 
					
						
							|  |  |  |         if p.get('capability'): | 
					
						
							|  |  |  |             del p['capability'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Don't show likes, replies or shares (announces) to unauthorized viewers | 
					
						
							|  |  |  |         if not authorized: | 
					
						
							|  |  |  |             if p.get('object'): | 
					
						
							|  |  |  |                 if isinstance(p['object'], dict):                                 | 
					
						
							|  |  |  |                     if p['object'].get('likes'): | 
					
						
							| 
									
										
										
										
											2019-11-25 11:04:27 +00:00
										 |  |  |                         p['likes']={'items': []} | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  |                     if p['object'].get('replies'): | 
					
						
							|  |  |  |                         p['replies']={} | 
					
						
							|  |  |  |                     if p['object'].get('shares'): | 
					
						
							|  |  |  |                         p['shares']={} | 
					
						
							|  |  |  |                     if p['object'].get('bookmarks'): | 
					
						
							|  |  |  |                         p['bookmarks']={} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-18 12:02:55 +00:00
										 |  |  |         boxItems['orderedItems'].append(p) | 
					
						
							| 
									
										
										
										
											2019-11-18 11:28:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return boxItems | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  | def expireCache(baseDir: str,personCache: {}, \ | 
					
						
							|  |  |  |                 httpPrefix: str,archiveDir: str,maxPostsInBox=32000): | 
					
						
							| 
									
										
										
										
											2019-08-20 11:51:29 +00:00
										 |  |  |     """Thread used to expire actors from the cache and archive old posts
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     while True: | 
					
						
							|  |  |  |         # once per day | 
					
						
							|  |  |  |         time.sleep(60*60*24) | 
					
						
							|  |  |  |         expirePersonCache(basedir,personCache) | 
					
						
							|  |  |  |         archivePosts(baseDir,httpPrefix,archiveDir,maxPostsInBox) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  | def archivePosts(baseDir: str,httpPrefix: str,archiveDir: str, \ | 
					
						
							|  |  |  |                  maxPostsInBox=32000) -> None: | 
					
						
							| 
									
										
										
										
											2019-07-12 20:43:55 +00:00
										 |  |  |     """Archives posts for all accounts
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if archiveDir: | 
					
						
							|  |  |  |         if not os.path.isdir(archiveDir): | 
					
						
							|  |  |  |             os.mkdir(archiveDir) | 
					
						
							|  |  |  |     if archiveDir: | 
					
						
							|  |  |  |         if not os.path.isdir(archiveDir+'/accounts'): | 
					
						
							|  |  |  |             os.mkdir(archiveDir+'/accounts') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for subdir, dirs, files in os.walk(baseDir+'/accounts'): | 
					
						
							|  |  |  |         for handle in dirs: | 
					
						
							|  |  |  |             if '@' in handle: | 
					
						
							|  |  |  |                 nickname=handle.split('@')[0] | 
					
						
							|  |  |  |                 domain=handle.split('@')[1] | 
					
						
							|  |  |  |                 archiveSubdir=None | 
					
						
							|  |  |  |                 if archiveDir: | 
					
						
							|  |  |  |                     if not os.path.isdir(archiveDir+'/accounts/'+handle): | 
					
						
							|  |  |  |                         os.mkdir(archiveDir+'/accounts/'+handle)     | 
					
						
							|  |  |  |                     if not os.path.isdir(archiveDir+'/accounts/'+handle+'/inbox'): | 
					
						
							|  |  |  |                         os.mkdir(archiveDir+'/accounts/'+handle+'/inbox')     | 
					
						
							|  |  |  |                     if not os.path.isdir(archiveDir+'/accounts/'+handle+'/outbox'): | 
					
						
							|  |  |  |                         os.mkdir(archiveDir+'/accounts/'+handle+'/outbox') | 
					
						
							|  |  |  |                     archiveSubdir=archiveDir+'/accounts/'+handle+'/inbox' | 
					
						
							| 
									
										
										
										
											2019-07-14 17:02:41 +00:00
										 |  |  |                 archivePostsForPerson(httpPrefix,nickname,domain,baseDir, \ | 
					
						
							| 
									
										
										
										
											2019-07-12 20:43:55 +00:00
										 |  |  |                                       'inbox',archiveSubdir, \ | 
					
						
							|  |  |  |                                       maxPostsInBox) | 
					
						
							|  |  |  |                 if archiveDir: | 
					
						
							|  |  |  |                     archiveSubdir=archiveDir+'/accounts/'+handle+'/outbox' | 
					
						
							| 
									
										
										
										
											2019-07-14 17:02:41 +00:00
										 |  |  |                 archivePostsForPerson(httpPrefix,nickname,domain,baseDir, \ | 
					
						
							| 
									
										
										
										
											2019-07-12 20:43:55 +00:00
										 |  |  |                                       'outbox',archiveSubdir, \ | 
					
						
							|  |  |  |                                       maxPostsInBox) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-14 17:02:41 +00:00
										 |  |  | def archivePostsForPerson(httpPrefix: str,nickname: str,domain: str,baseDir: str, \ | 
					
						
							| 
									
										
										
										
											2019-10-19 10:23:49 +00:00
										 |  |  |                           boxname: str,archiveDir: str,maxPostsInBox=32000) -> None: | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  |     """Retain a maximum number of posts within the given box
 | 
					
						
							| 
									
										
										
										
											2019-06-29 13:44:21 +00:00
										 |  |  |     Move any others to an archive directory | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  |     if boxname!='inbox' and boxname!='outbox': | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-07-12 20:43:55 +00:00
										 |  |  |     if archiveDir: | 
					
						
							|  |  |  |         if not os.path.isdir(archiveDir): | 
					
						
							|  |  |  |             os.mkdir(archiveDir)     | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  |     boxDir = createPersonDir(nickname,domain,baseDir,boxname) | 
					
						
							| 
									
										
										
										
											2019-09-27 12:09:04 +00:00
										 |  |  |     postsInBox=os.scandir(boxDir) | 
					
						
							| 
									
										
										
										
											2019-10-19 10:19:19 +00:00
										 |  |  |     noOfPosts=0 | 
					
						
							|  |  |  |     for f in postsInBox: | 
					
						
							|  |  |  |         noOfPosts+=1 | 
					
						
							| 
									
										
										
										
											2019-07-04 16:24:23 +00:00
										 |  |  |     if noOfPosts<=maxPostsInBox: | 
					
						
							| 
									
										
										
										
											2019-06-29 13:44:21 +00:00
										 |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-09-14 11:18:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-20 11:18:25 +00:00
										 |  |  |     # remove entries from the index | 
					
						
							|  |  |  |     handle=nickname+'@'+domain | 
					
						
							|  |  |  |     indexFilename=baseDir+'/accounts/'+handle+'/'+boxname+'.index' | 
					
						
							|  |  |  |     if os.path.isfile(indexFilename): | 
					
						
							|  |  |  |         indexCtr=0 | 
					
						
							|  |  |  |         # get the existing index entries as a string | 
					
						
							|  |  |  |         newIndex='' | 
					
						
							|  |  |  |         with open(indexFilename, 'r') as indexFile: | 
					
						
							|  |  |  |             for postId in indexFile: | 
					
						
							|  |  |  |                 newIndex+=postId | 
					
						
							|  |  |  |                 indexCtr+=1 | 
					
						
							|  |  |  |                 if indexCtr>=maxPostsInBox: | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |         # save the new index file | 
					
						
							|  |  |  |         if len(newIndex)>0: | 
					
						
							|  |  |  |             indexFile=open(indexFilename,'w+') | 
					
						
							|  |  |  |             if indexFile: | 
					
						
							|  |  |  |                 indexFile.write(newIndex) | 
					
						
							|  |  |  |                 indexFile.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-14 11:18:34 +00:00
										 |  |  |     postsInBoxDict={} | 
					
						
							|  |  |  |     postsCtr=0 | 
					
						
							| 
									
										
										
										
											2019-11-06 13:49:51 +00:00
										 |  |  |     postsInBox=os.scandir(boxDir) | 
					
						
							| 
									
										
										
										
											2019-09-14 11:18:34 +00:00
										 |  |  |     for postFilename in postsInBox: | 
					
						
							| 
									
										
										
										
											2019-09-27 12:09:04 +00:00
										 |  |  |         postFilename=postFilename.name | 
					
						
							| 
									
										
										
										
											2019-09-14 11:18:34 +00:00
										 |  |  |         if not postFilename.endswith('.json'): | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2019-11-06 13:35:25 +00:00
										 |  |  |         # Time of file creation | 
					
						
							| 
									
										
										
										
											2019-11-06 14:50:17 +00:00
										 |  |  |         fullFilename=os.path.join(boxDir,postFilename) | 
					
						
							|  |  |  |         if os.path.isfile(fullFilename): | 
					
						
							|  |  |  |             content=open(fullFilename).read() | 
					
						
							|  |  |  |             if '"published":' in content: | 
					
						
							|  |  |  |                 publishedStr=content.split('"published":')[1] | 
					
						
							|  |  |  |                 if '"' in publishedStr: | 
					
						
							|  |  |  |                     publishedStr=publishedStr.split('"')[1] | 
					
						
							| 
									
										
										
										
											2019-11-06 14:54:17 +00:00
										 |  |  |                     if publishedStr.endswith('Z'): | 
					
						
							|  |  |  |                         postsInBoxDict[publishedStr]=postFilename | 
					
						
							|  |  |  |                         postsCtr+=1 | 
					
						
							| 
									
										
										
										
											2019-09-14 11:18:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     noOfPosts=postsCtr | 
					
						
							|  |  |  |     if noOfPosts<=maxPostsInBox: | 
					
						
							|  |  |  |         return | 
					
						
							| 
									
										
										
										
											2019-11-06 14:50:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-14 11:18:34 +00:00
										 |  |  |     # sort the list in ascending order of date | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |     postsInBoxSorted= \ | 
					
						
							|  |  |  |         OrderedDict(sorted(postsInBoxDict.items(),reverse=False)) | 
					
						
							| 
									
										
										
										
											2019-09-14 17:12:03 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-19 10:10:52 +00:00
										 |  |  |     # directory containing cached html posts | 
					
						
							|  |  |  |     postCacheDir=boxDir.replace('/'+boxname,'/postcache') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 14:54:17 +00:00
										 |  |  |     for publishedStr,postFilename in postsInBoxSorted.items(): | 
					
						
							| 
									
										
										
										
											2019-11-06 14:50:17 +00:00
										 |  |  |         filePath=os.path.join(boxDir,postFilename)         | 
					
						
							| 
									
										
										
										
											2019-09-24 21:16:44 +00:00
										 |  |  |         if not os.path.isfile(filePath): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         if archiveDir: | 
					
						
							|  |  |  |             repliesPath=filePath.replace('.json','.replies') | 
					
						
							| 
									
										
										
										
											2019-11-06 14:50:17 +00:00
										 |  |  |             archivePath=os.path.join(archiveDir,postFilename) | 
					
						
							| 
									
										
										
										
											2019-09-24 21:16:44 +00:00
										 |  |  |             os.rename(filePath,archivePath) | 
					
						
							|  |  |  |             if os.path.isfile(repliesPath): | 
					
						
							|  |  |  |                 os.rename(repliesPath,archivePath) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             deletePost(baseDir,httpPrefix,nickname,domain,filePath,False) | 
					
						
							| 
									
										
										
										
											2019-10-19 10:10:52 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # remove cached html posts | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |         postCacheFilename= \ | 
					
						
							|  |  |  |             os.path.join(postCacheDir,postFilename).replace('.json','.html') | 
					
						
							| 
									
										
										
										
											2019-10-19 10:10:52 +00:00
										 |  |  |         if os.path.isfile(postCacheFilename): | 
					
						
							|  |  |  |             os.remove(postCacheFilename) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-06 14:50:17 +00:00
										 |  |  |         noOfPosts-=1 | 
					
						
							|  |  |  |         if noOfPosts<=maxPostsInBox: | 
					
						
							| 
									
										
										
										
											2019-09-24 21:16:44 +00:00
										 |  |  |             break | 
					
						
							| 
									
										
										
										
											2019-07-03 10:31:02 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  | def getPublicPostsOfPerson(baseDir: str,nickname: str,domain: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-19 13:32:58 +00:00
										 |  |  |                            raw: bool,simple: bool,useTor: bool, \ | 
					
						
							| 
									
										
										
										
											2019-07-19 16:56:55 +00:00
										 |  |  |                            port: int,httpPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                            debug: bool,projectVersion: str) -> None: | 
					
						
							| 
									
										
										
										
											2019-07-03 10:31:02 +00:00
										 |  |  |     """ This is really just for test purposes
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-11-13 10:50:16 +00:00
										 |  |  |     session = createSession(useTor) | 
					
						
							| 
									
										
										
										
											2019-07-03 10:31:02 +00:00
										 |  |  |     personCache={} | 
					
						
							|  |  |  |     cachedWebfingers={} | 
					
						
							|  |  |  |     federationList=[] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-19 13:32:58 +00:00
										 |  |  |     domainFull=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 domainFull=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-19 13:32:58 +00:00
										 |  |  |     handle=httpPrefix+"://"+domainFull+"/@"+nickname | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |     wfRequest = \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |         webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ | 
					
						
							|  |  |  |                         domain,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-03 10:31:02 +00:00
										 |  |  |     if not wfRequest: | 
					
						
							|  |  |  |         sys.exit() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-22 18:36:07 +00:00
										 |  |  |     personUrl,pubKeyId,pubKey,personId,shaedInbox,capabilityAcquisition,avatarUrl,displayName= \ | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  |         getPersonBox(baseDir,session,wfRequest,personCache, \ | 
					
						
							| 
									
										
										
										
											2019-10-17 15:55:05 +00:00
										 |  |  |                      projectVersion,httpPrefix,nickname,domain,'outbox') | 
					
						
							| 
									
										
										
										
											2019-10-18 18:57:34 +00:00
										 |  |  |     wfResult = json.dumps(wfRequest, indent=2, sort_keys=False) | 
					
						
							| 
									
										
										
										
											2019-07-03 10:31:02 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     maxMentions=10 | 
					
						
							|  |  |  |     maxEmoji=10 | 
					
						
							|  |  |  |     maxAttachments=5 | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |     userPosts = \ | 
					
						
							|  |  |  |         getPosts(session,personUrl,30,maxMentions,maxEmoji, \ | 
					
						
							|  |  |  |                  maxAttachments,federationList, \ | 
					
						
							|  |  |  |                  personCache,raw,simple,debug, \ | 
					
						
							|  |  |  |                  projectVersion,httpPrefix,domain) | 
					
						
							| 
									
										
										
										
											2019-07-03 10:31:02 +00:00
										 |  |  |     #print(str(userPosts)) | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def sendCapabilitiesUpdate(session,baseDir: str,httpPrefix: str, \ | 
					
						
							|  |  |  |                            nickname: str,domain: str,port: int, \ | 
					
						
							|  |  |  |                            followerUrl,updateCaps: [], \ | 
					
						
							|  |  |  |                            sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                            cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                            federationList :[],debug :bool, \ | 
					
						
							|  |  |  |                            projectVersion: str) -> int: | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |     """When the capabilities for a follower are changed this
 | 
					
						
							|  |  |  |     sends out an update. followerUrl is the actor of the follower. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     updateJson=capabilitiesUpdate(baseDir,httpPrefix, \ | 
					
						
							|  |  |  |                                   nickname,domain,port, \ | 
					
						
							|  |  |  |                                   followerUrl, \ | 
					
						
							|  |  |  |                                   updateCaps) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not updateJson: | 
					
						
							|  |  |  |         return 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         pprint(updateJson) | 
					
						
							|  |  |  |         print('DEBUG: sending capabilities update from '+ \ | 
					
						
							|  |  |  |               nickname+'@'+domain+' port '+ str(port) + \ | 
					
						
							|  |  |  |               ' to '+followerUrl) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     clientToServer=False | 
					
						
							|  |  |  |     followerNickname=getNicknameFromActor(followerUrl) | 
					
						
							| 
									
										
										
										
											2019-09-02 09:43:43 +00:00
										 |  |  |     if not followerNickname: | 
					
						
							|  |  |  |         print('WARN: unable to find nickname in '+followerUrl) | 
					
						
							|  |  |  |         return 1 | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |     followerDomain,followerPort=getDomainFromActor(followerUrl) | 
					
						
							|  |  |  |     return sendSignedJson(updateJson,session,baseDir, \ | 
					
						
							|  |  |  |                           nickname,domain,port, \ | 
					
						
							|  |  |  |                           followerNickname,followerDomain,followerPort, '', \ | 
					
						
							|  |  |  |                           httpPrefix,True,clientToServer, \ | 
					
						
							|  |  |  |                           federationList, \ | 
					
						
							|  |  |  |                           sendThreads,postLog,cachedWebfingers, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                           personCache,debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-08-02 18:37:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-28 11:29:42 +00:00
										 |  |  | def populateRepliesJson(baseDir: str,nickname: str,domain: str, \ | 
					
						
							|  |  |  |                         postRepliesFilename: str,authorized: bool, \ | 
					
						
							|  |  |  |                         repliesJson: {}) -> None: | 
					
						
							| 
									
										
										
										
											2019-08-02 18:37:23 +00:00
										 |  |  |     # populate the items list with replies | 
					
						
							|  |  |  |     repliesBoxes=['outbox','inbox'] | 
					
						
							|  |  |  |     with open(postRepliesFilename,'r') as repliesFile:  | 
					
						
							|  |  |  |         for messageId in repliesFile: | 
					
						
							|  |  |  |             replyFound=False | 
					
						
							|  |  |  |             # examine inbox and outbox | 
					
						
							|  |  |  |             for boxname in repliesBoxes: | 
					
						
							|  |  |  |                 searchFilename= \ | 
					
						
							|  |  |  |                     baseDir+ \ | 
					
						
							|  |  |  |                     '/accounts/'+nickname+'@'+ \ | 
					
						
							|  |  |  |                     domain+'/'+ \ | 
					
						
							|  |  |  |                     boxname+'/'+ \ | 
					
						
							|  |  |  |                     messageId.replace('\n','').replace('/','#')+'.json' | 
					
						
							|  |  |  |                 if os.path.isfile(searchFilename): | 
					
						
							|  |  |  |                     if authorized or \ | 
					
						
							|  |  |  |                        'https://www.w3.org/ns/activitystreams#Public' in open(searchFilename).read(): | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |                         postJsonObject=loadJson(searchFilename) | 
					
						
							|  |  |  |                         if postJsonObject: | 
					
						
							| 
									
										
										
										
											2019-08-02 18:37:23 +00:00
										 |  |  |                             if postJsonObject['object'].get('cc'):                                                             | 
					
						
							|  |  |  |                                 if authorized or \ | 
					
						
							|  |  |  |                                    ('https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['to'] or \ | 
					
						
							|  |  |  |                                     'https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['cc']): | 
					
						
							|  |  |  |                                     repliesJson['orderedItems'].append(postJsonObject) | 
					
						
							|  |  |  |                                     replyFound=True | 
					
						
							|  |  |  |                             else: | 
					
						
							|  |  |  |                                 if authorized or \ | 
					
						
							|  |  |  |                                    'https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['to']: | 
					
						
							|  |  |  |                                     repliesJson['orderedItems'].append(postJsonObject) | 
					
						
							|  |  |  |                                     replyFound=True | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             # if not in either inbox or outbox then examine the shared inbox | 
					
						
							|  |  |  |             if not replyFound: | 
					
						
							|  |  |  |                 searchFilename= \ | 
					
						
							|  |  |  |                     baseDir+ \ | 
					
						
							|  |  |  |                     '/accounts/inbox@'+ \ | 
					
						
							|  |  |  |                     domain+'/inbox/'+ \ | 
					
						
							|  |  |  |                     messageId.replace('\n','').replace('/','#')+'.json' | 
					
						
							|  |  |  |                 if os.path.isfile(searchFilename): | 
					
						
							|  |  |  |                     if authorized or \ | 
					
						
							|  |  |  |                        'https://www.w3.org/ns/activitystreams#Public' in open(searchFilename).read(): | 
					
						
							|  |  |  |                         # get the json of the reply and append it to the collection | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |                         postJsonObject=loadJson(searchFilename) | 
					
						
							|  |  |  |                         if postJsonObject: | 
					
						
							| 
									
										
										
										
											2019-08-02 18:37:23 +00:00
										 |  |  |                             if postJsonObject['object'].get('cc'):                                                             | 
					
						
							|  |  |  |                                 if authorized or \ | 
					
						
							|  |  |  |                                    ('https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['to'] or \ | 
					
						
							|  |  |  |                                     'https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['cc']): | 
					
						
							|  |  |  |                                     repliesJson['orderedItems'].append(postJsonObject) | 
					
						
							|  |  |  |                             else: | 
					
						
							|  |  |  |                                 if authorized or \ | 
					
						
							|  |  |  |                                    'https://www.w3.org/ns/activitystreams#Public' in postJsonObject['object']['to']: | 
					
						
							|  |  |  |                                     repliesJson['orderedItems'].append(postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-28 16:58:21 +00:00
										 |  |  | def rejectAnnounce(announceFilename: str): | 
					
						
							|  |  |  |     """Marks an announce as rejected
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not os.path.isfile(announceFilename+'.reject'): | 
					
						
							|  |  |  |         rejectAnnounceFile=open(announceFilename+'.reject', "w+") | 
					
						
							|  |  |  |         rejectAnnounceFile.write('\n') | 
					
						
							|  |  |  |         rejectAnnounceFile.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  | def downloadAnnounce(session,baseDir: str,httpPrefix: str, \ | 
					
						
							|  |  |  |                      nickname: str,domain: str, \ | 
					
						
							|  |  |  |                      postJsonObject: {},projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |     """Download the post referenced by an announce
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not postJsonObject.get('object'): | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  |     if not isinstance(postJsonObject['object'], str): | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # get the announced post | 
					
						
							|  |  |  |     announceCacheDir=baseDir+'/cache/announce/'+nickname | 
					
						
							|  |  |  |     if not os.path.isdir(announceCacheDir): | 
					
						
							|  |  |  |         os.mkdir(announceCacheDir) | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |     announceFilename= \ | 
					
						
							|  |  |  |         announceCacheDir+'/'+postJsonObject['object'].replace('/','#')+'.json' | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if os.path.isfile(announceFilename+'.reject'): | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if os.path.isfile(announceFilename): | 
					
						
							|  |  |  |         print('Reading cached Announce content for '+postJsonObject['object']) | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |         postJsonObject=loadJson(announceFilename) | 
					
						
							|  |  |  |         if postJsonObject: | 
					
						
							|  |  |  |             return postJsonObject | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |     else: | 
					
						
							|  |  |  |         asHeader={'Accept': 'application/activity+json; profile="https://www.w3.org/ns/activitystreams"'} | 
					
						
							| 
									
										
										
										
											2019-10-18 09:28:00 +00:00
										 |  |  |         if '/channel/' in postJsonObject['actor']: | 
					
						
							|  |  |  |             asHeader={'Accept': 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'} | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |         actorNickname=getNicknameFromActor(postJsonObject['actor']) | 
					
						
							|  |  |  |         actorDomain,actorPort=getDomainFromActor(postJsonObject['actor']) | 
					
						
							| 
									
										
										
										
											2020-01-18 10:39:51 +00:00
										 |  |  |         if isBlocked(baseDir,nickname,domain,actorNickname,actorDomain): | 
					
						
							|  |  |  |             print('Announce download blocked actor: '+actorNickname+'@'+actorDomain) | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2020-02-05 11:46:05 +00:00
										 |  |  |         objectNickname=getNicknameFromActor(postJsonObject['object']) | 
					
						
							|  |  |  |         objectDomain,objectPort=getDomainFromActor(postJsonObject['object']) | 
					
						
							|  |  |  |         if isBlocked(baseDir,nickname,domain,objectNickname,objectDomain): | 
					
						
							| 
									
										
										
										
											2020-02-19 18:55:29 +00:00
										 |  |  |             if objectNickname and objectDomain: | 
					
						
							|  |  |  |                 print('Announce download blocked object: '+objectNickname+'@'+objectDomain) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 print('Announce download blocked object: '+str(postJsonObject['object'])) | 
					
						
							| 
									
										
										
										
											2020-02-05 11:46:05 +00:00
										 |  |  |             return None | 
					
						
							| 
									
										
										
										
											2020-01-18 10:39:51 +00:00
										 |  |  |         print('Downloading Announce content for '+postJsonObject['object']) | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |         announcedJson= \ | 
					
						
							|  |  |  |             getJson(session,postJsonObject['object'],asHeader, \ | 
					
						
							|  |  |  |                     None,projectVersion,httpPrefix,domain) | 
					
						
							| 
									
										
										
										
											2020-01-19 20:19:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |         if not announcedJson: | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2019-12-04 09:44:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if not isinstance(announcedJson, dict): | 
					
						
							|  |  |  |             print('WARN: announce json is not a dict - '+postJsonObject['object']) | 
					
						
							| 
									
										
										
										
											2019-12-04 09:47:35 +00:00
										 |  |  |             rejectAnnounce(announceFilename) | 
					
						
							| 
									
										
										
										
											2019-12-04 09:44:41 +00:00
										 |  |  |             return None         | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |         if not announcedJson.get('id'): | 
					
						
							|  |  |  |             rejectAnnounce(announceFilename) | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  |         if '/statuses/' not in announcedJson['id']: | 
					
						
							|  |  |  |             rejectAnnounce(announceFilename) | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2019-10-17 22:26:47 +00:00
										 |  |  |         if '/users/' not in announcedJson['id'] and \ | 
					
						
							|  |  |  |            '/channel/' not in announcedJson['id'] and \ | 
					
						
							|  |  |  |            '/profile/' not in announcedJson['id']: | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |             rejectAnnounce(announceFilename) | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  |         if not announcedJson.get('type'): | 
					
						
							|  |  |  |             rejectAnnounce(announceFilename) | 
					
						
							| 
									
										
										
										
											2019-10-29 20:23:49 +00:00
										 |  |  |             #pprint(announcedJson) | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |             return None | 
					
						
							| 
									
										
										
										
											2020-02-19 17:57:59 +00:00
										 |  |  |         if announcedJson['type']!='Note' and \ | 
					
						
							|  |  |  |            announcedJson['type']!='Article': | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |             rejectAnnounce(announceFilename) | 
					
						
							| 
									
										
										
										
											2019-10-29 20:23:49 +00:00
										 |  |  |             #pprint(announcedJson) | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |             return None | 
					
						
							| 
									
										
										
										
											2020-02-05 14:57:10 +00:00
										 |  |  |         if not announcedJson.get('content'): | 
					
						
							|  |  |  |             rejectAnnounce(announceFilename) | 
					
						
							|  |  |  |             return None         | 
					
						
							|  |  |  |         if isFiltered(baseDir,nickname,domain,announcedJson['content']): | 
					
						
							|  |  |  |             rejectAnnounce(announceFilename) | 
					
						
							|  |  |  |             return None | 
					
						
							| 
									
										
										
										
											2020-01-19 20:19:56 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |         # wrap in create to be consistent with other posts | 
					
						
							|  |  |  |         announcedJson= \ | 
					
						
							|  |  |  |             outboxMessageCreateWrap(httpPrefix, \ | 
					
						
							|  |  |  |                                     actorNickname,actorDomain,actorPort, \ | 
					
						
							|  |  |  |                                     announcedJson) | 
					
						
							|  |  |  |         if announcedJson['type']!='Create': | 
					
						
							|  |  |  |             rejectAnnounce(announceFilename) | 
					
						
							| 
									
										
										
										
											2019-10-29 20:23:49 +00:00
										 |  |  |             #pprint(announcedJson) | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # set the id to the original status | 
					
						
							|  |  |  |         announcedJson['id']=postJsonObject['object'] | 
					
						
							|  |  |  |         announcedJson['object']['id']=postJsonObject['object'] | 
					
						
							|  |  |  |         # check that the repeat isn't for a blocked account | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |         attributedNickname= \ | 
					
						
							|  |  |  |             getNicknameFromActor(announcedJson['object']['id']) | 
					
						
							|  |  |  |         attributedDomain,attributedPort= \ | 
					
						
							|  |  |  |             getDomainFromActor(announcedJson['object']['id']) | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |         if attributedNickname and attributedDomain: | 
					
						
							|  |  |  |             if attributedPort: | 
					
						
							|  |  |  |                 if attributedPort!=80 and attributedPort!=443: | 
					
						
							|  |  |  |                     attributedDomain=attributedDomain+':'+str(attributedPort) | 
					
						
							| 
									
										
										
										
											2019-12-12 09:58:06 +00:00
										 |  |  |             if isBlocked(baseDir,nickname,domain, \ | 
					
						
							|  |  |  |                          attributedNickname,attributedDomain): | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |                 rejectAnnounce(announceFilename) | 
					
						
							|  |  |  |                 return None | 
					
						
							|  |  |  |         postJsonObject=announcedJson | 
					
						
							| 
									
										
										
										
											2020-01-15 22:31:04 +00:00
										 |  |  |         replaceYouTube(postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |         if saveJson(postJsonObject,announceFilename): | 
					
						
							|  |  |  |             return postJsonObject | 
					
						
							| 
									
										
										
										
											2019-09-28 16:10:45 +00:00
										 |  |  |     return None | 
					
						
							| 
									
										
										
										
											2019-12-01 13:45:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def mutePost(baseDir: str,nickname: str,domain: str,postId: str, \ | 
					
						
							|  |  |  |              recentPostsCache: {}) -> None: | 
					
						
							|  |  |  |     """ Mutes the given post
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     postFilename=locatePost(baseDir,nickname,domain,postId) | 
					
						
							|  |  |  |     if not postFilename: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     postJsonObject=loadJson(postFilename) | 
					
						
							|  |  |  |     if not postJsonObject: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-01 14:03:18 +00:00
										 |  |  |     print('MUTE: '+postFilename) | 
					
						
							| 
									
										
										
										
											2019-12-01 13:45:30 +00:00
										 |  |  |     muteFile=open(postFilename+'.muted', "w") | 
					
						
							| 
									
										
										
										
											2019-12-01 14:03:18 +00:00
										 |  |  |     if muteFile: | 
					
						
							|  |  |  |         muteFile.write('\n') | 
					
						
							|  |  |  |         muteFile.close() | 
					
						
							| 
									
										
										
										
											2019-12-01 13:45:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # remove cached posts so that the muted version gets created | 
					
						
							|  |  |  |     cachedPostFilename= \ | 
					
						
							| 
									
										
										
										
											2019-12-01 13:55:24 +00:00
										 |  |  |         getCachedPostFilename(baseDir,nickname,domain,postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-12-01 13:45:30 +00:00
										 |  |  |     if cachedPostFilename: | 
					
						
							|  |  |  |         if os.path.isfile(cachedPostFilename): | 
					
						
							| 
									
										
										
										
											2019-12-01 14:34:10 +00:00
										 |  |  |             os.remove(cachedPostFilename) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # if the post is in the recent posts cache then mark it as muted | 
					
						
							| 
									
										
										
										
											2019-12-01 14:43:09 +00:00
										 |  |  |     if recentPostsCache.get('index'): | 
					
						
							|  |  |  |         postId=postJsonObject['id'].replace('/activity','').replace('/','#') | 
					
						
							|  |  |  |         if postId in recentPostsCache['index']: | 
					
						
							|  |  |  |             print('MUTE: '+postId+' is in recent posts cache') | 
					
						
							|  |  |  |             if recentPostsCache['json'].get(postId): | 
					
						
							|  |  |  |                 postJsonObject['muted']=True | 
					
						
							|  |  |  |                 recentPostsCache['json'][postId]=json.dumps(postJsonObject) | 
					
						
							|  |  |  |                 print('MUTE: '+postId+' marked as muted in recent posts cache') | 
					
						
							| 
									
										
										
										
											2019-12-01 13:45:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def unmutePost(baseDir: str,nickname: str,domain: str,postId: str, \ | 
					
						
							|  |  |  |                recentPostsCache: {}) -> None: | 
					
						
							|  |  |  |     """ Unmutes the given post
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     postFilename=locatePost(baseDir,nickname,domain,postId) | 
					
						
							|  |  |  |     if not postFilename: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  |     postJsonObject=loadJson(postFilename) | 
					
						
							|  |  |  |     if not postJsonObject: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-01 14:03:18 +00:00
										 |  |  |     print('UNMUTE: '+postFilename) | 
					
						
							| 
									
										
										
										
											2019-12-01 13:45:30 +00:00
										 |  |  |     muteFilename=postFilename+'.muted' | 
					
						
							|  |  |  |     if os.path.isfile(muteFilename): | 
					
						
							|  |  |  |         os.remove(muteFilename) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # remove cached posts so that it gets recreated | 
					
						
							|  |  |  |     cachedPostFilename= \ | 
					
						
							| 
									
										
										
										
											2019-12-01 13:55:24 +00:00
										 |  |  |         getCachedPostFilename(baseDir,nickname,domain,postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-12-01 13:45:30 +00:00
										 |  |  |     if cachedPostFilename: | 
					
						
							|  |  |  |         if os.path.isfile(cachedPostFilename): | 
					
						
							| 
									
										
										
										
											2019-12-01 15:19:11 +00:00
										 |  |  |             os.remove(cachedPostFilename) | 
					
						
							|  |  |  |     removePostFromCache(postJsonObject,recentPostsCache) |