| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  | __filename__ = "announce.py" | 
					
						
							|  |  |  | __author__ = "Bob Mottram" | 
					
						
							|  |  |  | __license__ = "AGPL3+" | 
					
						
							| 
									
										
										
										
											2019-08-29 13:35:29 +00:00
										 |  |  | __version__ = "1.0.0" | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  | __maintainer__ = "Bob Mottram" | 
					
						
							|  |  |  | __email__ = "bob@freedombone.net" | 
					
						
							|  |  |  | __status__ = "Production" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-19 18:22:43 +00:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2019-10-11 18:03:58 +00:00
										 |  |  | import time | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  | import json | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  | from pprint import pprint | 
					
						
							| 
									
										
										
										
											2019-11-24 13:46:28 +00:00
										 |  |  | from utils import removePostFromCache | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  | from utils import getStatusNumber | 
					
						
							|  |  |  | from utils import createOutboxDir | 
					
						
							| 
									
										
										
										
											2019-07-02 10:43:54 +00:00
										 |  |  | from utils import urlPermitted | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  | from utils import getNicknameFromActor | 
					
						
							|  |  |  | from utils import getDomainFromActor | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  | from utils import locatePost | 
					
						
							| 
									
										
										
										
											2019-10-19 18:21:14 +00:00
										 |  |  | from utils import getCachedPostFilename | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  | from utils import loadJson | 
					
						
							|  |  |  | from utils import saveJson | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  | from posts import sendSignedJson | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  | from posts import getPersonBox | 
					
						
							|  |  |  | from session import postJson | 
					
						
							|  |  |  | from webfinger import webfingerHandle | 
					
						
							|  |  |  | from auth import createBasicAuthHeader | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-24 13:46:28 +00:00
										 |  |  | def outboxAnnounce(recentPostsCache: {}, \ | 
					
						
							|  |  |  |                    baseDir: str,messageJson: {},debug: bool) -> bool: | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |     """ Adds or removes announce entries from the shares collection
 | 
					
						
							|  |  |  |     within a given post | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not messageJson.get('actor'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not messageJson.get('type'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not messageJson.get('object'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if messageJson['type']=='Announce': | 
					
						
							|  |  |  |         if not isinstance(messageJson['object'], str): | 
					
						
							| 
									
										
										
										
											2019-09-02 09:43:43 +00:00
										 |  |  |             return False | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |         nickname=getNicknameFromActor(messageJson['actor']) | 
					
						
							| 
									
										
										
										
											2019-09-02 09:43:43 +00:00
										 |  |  |         if not nickname: | 
					
						
							|  |  |  |             print('WARN: no nickname found in '+messageJson['actor']) | 
					
						
							|  |  |  |             return False | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |         domain,port=getDomainFromActor(messageJson['actor']) | 
					
						
							|  |  |  |         postFilename=locatePost(baseDir,nickname,domain,messageJson['object']) | 
					
						
							|  |  |  |         if postFilename: | 
					
						
							| 
									
										
										
										
											2019-11-24 13:46:28 +00:00
										 |  |  |             updateAnnounceCollection(recentPostsCache,baseDir,postFilename, \ | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |                                      messageJson['actor'],domain,debug) | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |             return True | 
					
						
							|  |  |  |     if messageJson['type']=='Undo': | 
					
						
							|  |  |  |         if not isinstance(messageJson['object'], dict): | 
					
						
							| 
									
										
										
										
											2019-09-02 09:43:43 +00:00
										 |  |  |             return False | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |         if not messageJson['object'].get('type'): | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  |         if messageJson['object']['type']=='Announce': | 
					
						
							|  |  |  |             if not isinstance(messageJson['object']['object'], str): | 
					
						
							| 
									
										
										
										
											2019-09-02 09:43:43 +00:00
										 |  |  |                 return False | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |             nickname=getNicknameFromActor(messageJson['actor']) | 
					
						
							| 
									
										
										
										
											2019-09-02 09:43:43 +00:00
										 |  |  |             if not nickname: | 
					
						
							|  |  |  |                 print('WARN: no nickname found in '+messageJson['actor']) | 
					
						
							|  |  |  |                 return False | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |             domain,port=getDomainFromActor(messageJson['actor']) | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |             postFilename= \ | 
					
						
							|  |  |  |                 locatePost(baseDir,nickname,domain, \ | 
					
						
							|  |  |  |                            messageJson['object']['object']) | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |             if postFilename: | 
					
						
							| 
									
										
										
										
											2019-11-24 13:46:28 +00:00
										 |  |  |                 undoAnnounceCollectionEntry(recentPostsCache, \ | 
					
						
							|  |  |  |                                             baseDir,postFilename, \ | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |                                             messageJson['actor'], \ | 
					
						
							|  |  |  |                                             domain,debug) | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |                 return True | 
					
						
							|  |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-24 13:46:28 +00:00
										 |  |  | def undoAnnounceCollectionEntry(recentPostsCache: {}, \ | 
					
						
							|  |  |  |                                 baseDir: str,postFilename: str, \ | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |                                 actor: str,domain: str,debug: bool) -> None: | 
					
						
							|  |  |  |     """Undoes an announce for a particular actor by removing it from
 | 
					
						
							|  |  |  |     the "shares" collection within a post. Note that the "shares"  | 
					
						
							|  |  |  |     collection has no relation to shared items in shares.py. It's | 
					
						
							|  |  |  |     shares of posts, not shares of physical objects. | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |     postJsonObject=loadJson(postFilename) | 
					
						
							| 
									
										
										
										
											2019-09-30 22:39:02 +00:00
										 |  |  |     if postJsonObject: | 
					
						
							| 
									
										
										
										
											2019-11-19 12:52:27 +00:00
										 |  |  |         # remove any cached version of this announce so that the announce icon is changed | 
					
						
							| 
									
										
										
										
											2019-10-19 18:21:14 +00:00
										 |  |  |         nickname=getNicknameFromActor(actor) | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |         cachedPostFilename= \ | 
					
						
							|  |  |  |             getCachedPostFilename(baseDir,nickname,domain,postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:21:14 +00:00
										 |  |  |         if os.path.isfile(cachedPostFilename): | 
					
						
							|  |  |  |             os.remove(cachedPostFilename) | 
					
						
							| 
									
										
										
										
											2019-11-24 13:46:28 +00:00
										 |  |  |         removePostFromCache(postJsonObject,recentPostsCache) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |         if not postJsonObject.get('type'): | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |             return | 
					
						
							|  |  |  |         if postJsonObject['type']!='Create': | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |             return | 
					
						
							|  |  |  |         if not postJsonObject.get('object'): | 
					
						
							|  |  |  |             if debug: | 
					
						
							|  |  |  |                 pprint(postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-08-01 10:05:31 +00:00
										 |  |  |                 print('DEBUG: post has no object') | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |         if not isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |         if not postJsonObject['object'].get('shares'): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         if not postJsonObject['object']['shares'].get('items'): | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         totalItems=0 | 
					
						
							|  |  |  |         if postJsonObject['object']['shares'].get('totalItems'): | 
					
						
							|  |  |  |             totalItems=postJsonObject['object']['shares']['totalItems'] | 
					
						
							|  |  |  |         itemFound=False | 
					
						
							| 
									
										
										
										
											2019-08-01 12:18:22 +00:00
										 |  |  |         for announceItem in postJsonObject['object']['shares']['items']: | 
					
						
							|  |  |  |             if announceItem.get('actor'): | 
					
						
							|  |  |  |                 if announceItem['actor']==actor: | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |                     if debug: | 
					
						
							|  |  |  |                         print('DEBUG: Announce was removed for '+actor) | 
					
						
							| 
									
										
										
										
											2019-08-01 12:18:22 +00:00
										 |  |  |                     postJsonObject['object']['shares']['items'].remove(announceItem) | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |                     itemFound=True | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |         if itemFound: | 
					
						
							|  |  |  |             if totalItems==1: | 
					
						
							|  |  |  |                 if debug: | 
					
						
							|  |  |  |                     print('DEBUG: shares (announcements) was removed from post') | 
					
						
							| 
									
										
										
										
											2019-08-01 12:18:22 +00:00
										 |  |  |                 del postJsonObject['object']['shares'] | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |                 postJsonObject['object']['shares']['totalItems']= \ | 
					
						
							|  |  |  |                     len(postJsonObject['object']['shares']['items']) | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |             saveJson(postJsonObject,postFilename) | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-24 17:47:25 +00:00
										 |  |  | def updateAnnounceCollection(recentPostsCache: {}, \ | 
					
						
							|  |  |  |                              baseDir: str,postFilename: str, \ | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |                              actor: str,domain: str,debug: bool) -> None: | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |     """Updates the announcements collection within a post
 | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |     Confusingly this is known as "shares", but isn't the | 
					
						
							|  |  |  |     same as shared items within shares.py | 
					
						
							| 
									
										
										
										
											2019-08-01 10:12:10 +00:00
										 |  |  |     It's shares of posts, not shares of physical objects. | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |     postJsonObject=loadJson(postFilename) | 
					
						
							| 
									
										
										
										
											2019-09-30 22:39:02 +00:00
										 |  |  |     if postJsonObject: | 
					
						
							| 
									
										
										
										
											2019-11-19 12:52:27 +00:00
										 |  |  |         # remove any cached version of this announce so that the announce | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |         # icon is changed | 
					
						
							| 
									
										
										
										
											2019-10-19 18:21:14 +00:00
										 |  |  |         nickname=getNicknameFromActor(actor) | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |         cachedPostFilename= \ | 
					
						
							|  |  |  |             getCachedPostFilename(baseDir,nickname,domain,postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:21:14 +00:00
										 |  |  |         if os.path.isfile(cachedPostFilename): | 
					
						
							|  |  |  |             os.remove(cachedPostFilename) | 
					
						
							| 
									
										
										
										
											2019-11-24 13:46:28 +00:00
										 |  |  |         removePostFromCache(postJsonObject,recentPostsCache) | 
					
						
							| 
									
										
										
										
											2019-10-19 18:21:14 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |         if not postJsonObject.get('object'): | 
					
						
							|  |  |  |             if debug: | 
					
						
							|  |  |  |                 pprint(postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-08-01 10:05:31 +00:00
										 |  |  |                 print('DEBUG: post '+announceUrl+' has no object') | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2019-08-01 11:43:22 +00:00
										 |  |  |         if not isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |             return | 
					
						
							| 
									
										
										
										
											2019-08-01 10:05:31 +00:00
										 |  |  |         postUrl=postJsonObject['id'].replace('/activity','')+'/shares' | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |         if not postJsonObject['object'].get('shares'): | 
					
						
							|  |  |  |             if debug: | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |                 print('DEBUG: Adding initial shares (announcements) to '+ \ | 
					
						
							|  |  |  |                       postUrl) | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |             announcementsJson = { | 
					
						
							| 
									
										
										
										
											2019-08-18 11:07:06 +00:00
										 |  |  |                 "@context": "https://www.w3.org/ns/activitystreams", | 
					
						
							| 
									
										
										
										
											2019-08-01 10:05:31 +00:00
										 |  |  |                 'id': postUrl, | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |                 'type': 'Collection', | 
					
						
							|  |  |  |                 "totalItems": 1, | 
					
						
							|  |  |  |                 'items': [{ | 
					
						
							|  |  |  |                     'type': 'Announce', | 
					
						
							|  |  |  |                     'actor': actor                     | 
					
						
							|  |  |  |                 }] | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             postJsonObject['object']['shares']=announcementsJson | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if postJsonObject['object']['shares'].get('items'): | 
					
						
							| 
									
										
										
										
											2019-09-03 09:48:06 +00:00
										 |  |  |                 for announceItem in postJsonObject['object']['shares']['items']: | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |                     if announceItem.get('actor'): | 
					
						
							|  |  |  |                         if announceItem['actor']==actor: | 
					
						
							|  |  |  |                             return | 
					
						
							|  |  |  |                 newAnnounce={ | 
					
						
							|  |  |  |                     'type': 'Announce', | 
					
						
							|  |  |  |                     'actor': actor | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 postJsonObject['object']['shares']['items'].append(newAnnounce) | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |                 postJsonObject['object']['shares']['totalItems']= \ | 
					
						
							|  |  |  |                     len(postJsonObject['object']['shares']['items']) | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 if debug: | 
					
						
							|  |  |  |                     print('DEBUG: shares (announcements) section of post has no items list') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: saving post with shares (announcements) added') | 
					
						
							|  |  |  |             pprint(postJsonObject) | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |         saveJson(postJsonObject,postFilename) | 
					
						
							| 
									
										
										
										
											2019-08-01 09:31:38 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def announcedByPerson(postJsonObject: {}, nickname: str,domain: str) -> bool: | 
					
						
							|  |  |  |     """Returns True if the given post is announced by the given person
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not postJsonObject.get('object'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     if not isinstance(postJsonObject['object'], dict): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     # not to be confused with shared items | 
					
						
							|  |  |  |     if not postJsonObject['object'].get('shares'): | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  |     actorMatch=domain+'/users/'+nickname | 
					
						
							|  |  |  |     for item in postJsonObject['object']['shares']['items']: | 
					
						
							|  |  |  |         if item['actor'].endswith(actorMatch): | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |     return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  | def createAnnounce(session,baseDir: str,federationList: [], \ | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |                    nickname: str, domain: str, port: int, \ | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |                    toUrl: str, ccUrl: str, httpPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |                    objectUrl: str, saveToFile: bool, \ | 
					
						
							|  |  |  |                    clientToServer: bool, \ | 
					
						
							|  |  |  |                    sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                    personCache: {},cachedWebfingers: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                    debug: bool,projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |     """Creates an announce message
 | 
					
						
							|  |  |  |     Typically toUrl will be https://www.w3.org/ns/activitystreams#Public | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |     and ccUrl might be a specific person favorited or repeated and the | 
					
						
							|  |  |  |     followers url objectUrl is typically the url of the message, | 
					
						
							|  |  |  |     corresponding to url or atomUri in createPostBase | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2019-07-09 14:20:23 +00:00
										 |  |  |     if not urlPermitted(objectUrl,federationList,"inbox:write"): | 
					
						
							| 
									
										
										
										
											2019-07-02 10:43:54 +00:00
										 |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |     if ':' in domain: | 
					
						
							|  |  |  |         domain=domain.split(':')[0] | 
					
						
							|  |  |  |     fullDomain=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 fullDomain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     statusNumber,published = getStatusNumber() | 
					
						
							| 
									
										
										
										
											2019-07-06 17:00:22 +00:00
										 |  |  |     newAnnounceId= \ | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |         httpPrefix+'://'+fullDomain+'/users/'+nickname+'/statuses/'+statusNumber | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |     newAnnounce = { | 
					
						
							| 
									
										
										
										
											2019-08-18 11:07:06 +00:00
										 |  |  |         "@context": "https://www.w3.org/ns/activitystreams", | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |         'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, | 
					
						
							|  |  |  |         'atomUri': httpPrefix+'://'+fullDomain+'/users/'+nickname+'/statuses/'+statusNumber, | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |         'cc': [], | 
					
						
							|  |  |  |         'id': newAnnounceId+'/activity', | 
					
						
							|  |  |  |         'object': objectUrl, | 
					
						
							|  |  |  |         'published': published, | 
					
						
							|  |  |  |         'to': [toUrl], | 
					
						
							|  |  |  |         'type': 'Announce' | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if ccUrl: | 
					
						
							|  |  |  |         if len(ccUrl)>0: | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |             newAnnounce['cc']=[ccUrl] | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |     if saveToFile: | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |         outboxDir = createOutboxDir(nickname,domain,baseDir) | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |         filename=outboxDir+'/'+newAnnounceId.replace('/','#')+'.json' | 
					
						
							| 
									
										
										
										
											2019-10-22 11:55:06 +00:00
										 |  |  |         saveJson(newAnnounce,filename) | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     announceNickname=None | 
					
						
							|  |  |  |     announceDomain=None | 
					
						
							|  |  |  |     announcePort=None | 
					
						
							| 
									
										
										
										
											2019-10-17 22:26:47 +00:00
										 |  |  |     if '/users/' in objectUrl or \ | 
					
						
							|  |  |  |        '/channel/' in objectUrl or \ | 
					
						
							|  |  |  |        '/profile/' in objectUrl: | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |         announceNickname=getNicknameFromActor(objectUrl) | 
					
						
							|  |  |  |         announceDomain,announcePort=getDomainFromActor(objectUrl) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if announceNickname and announceDomain: | 
					
						
							|  |  |  |         sendSignedJson(newAnnounce,session,baseDir, \ | 
					
						
							|  |  |  |                        nickname,domain,port, \ | 
					
						
							|  |  |  |                        announceNickname,announceDomain,announcePort, \ | 
					
						
							|  |  |  |                        'https://www.w3.org/ns/activitystreams#Public', \ | 
					
						
							|  |  |  |                        httpPrefix,True,clientToServer,federationList, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                        sendThreads,postLog,cachedWebfingers,personCache, \ | 
					
						
							|  |  |  |                        debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |              | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |     return newAnnounce | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  | def announcePublic(session,baseDir: str,federationList: [], \ | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |                    nickname: str, domain: str, port: int, httpPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |                    objectUrl: str,clientToServer: bool, \ | 
					
						
							|  |  |  |                    sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                    personCache: {},cachedWebfingers: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                    debug: bool,projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  |     """Makes a public announcement
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     fromDomain=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 fromDomain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     toUrl = 'https://www.w3.org/ns/activitystreams#Public' | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |     ccUrl = httpPrefix + '://'+fromDomain+'/users/'+nickname+'/followers' | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |     return createAnnounce(session,baseDir,federationList, \ | 
					
						
							|  |  |  |                           nickname,domain,port, \ | 
					
						
							|  |  |  |                           toUrl,ccUrl,httpPrefix, \ | 
					
						
							|  |  |  |                           objectUrl,True,clientToServer, \ | 
					
						
							|  |  |  |                           sendThreads,postLog, \ | 
					
						
							|  |  |  |                           personCache,cachedWebfingers, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                           debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-02 09:25:29 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  | def repeatPost(session,baseDir: str,federationList: [], \ | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |                nickname: str, domain: str, port: int, httpPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |                announceNickname: str, announceDomain: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |                announcePort: int, announceHttpsPrefix: str, \ | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |                announceStatusNumber: int,clientToServer: bool, \ | 
					
						
							|  |  |  |                sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                personCache: {},cachedWebfingers: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                debug: bool,projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-02 14:02:58 +00:00
										 |  |  |     """Repeats a given status post
 | 
					
						
							| 
									
										
										
										
											2019-07-02 09:52:37 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     announcedDomain=announceDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if announcePort: | 
					
						
							|  |  |  |         if announcePort!=80 and announcePort!=443: | 
					
						
							|  |  |  |             if ':' not in announcedDomain: | 
					
						
							|  |  |  |                 announcedDomain=announcedDomain+':'+str(announcePort) | 
					
						
							| 
									
										
										
										
											2019-07-02 09:52:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-03 19:00:03 +00:00
										 |  |  |     objectUrl = announceHttpsPrefix + '://'+announcedDomain+'/users/'+ \ | 
					
						
							| 
									
										
										
										
											2019-07-03 09:40:27 +00:00
										 |  |  |         announceNickname+'/statuses/'+str(announceStatusNumber) | 
					
						
							| 
									
										
										
										
											2019-07-02 09:52:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-11 17:55:10 +00:00
										 |  |  |     return announcePublic(session,baseDir,federationList, \ | 
					
						
							|  |  |  |                           nickname,domain,port,httpPrefix, \ | 
					
						
							|  |  |  |                           objectUrl,clientToServer, \ | 
					
						
							|  |  |  |                           sendThreads,postLog, \ | 
					
						
							|  |  |  |                           personCache,cachedWebfingers, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                           debug,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-02 09:52:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-12 09:41:57 +00:00
										 |  |  | def undoAnnounce(session,baseDir: str,federationList: [], \ | 
					
						
							|  |  |  |                  nickname: str, domain: str, port: int, \ | 
					
						
							|  |  |  |                  toUrl: str, ccUrl: str, httpPrefix: str, \ | 
					
						
							|  |  |  |                  objectUrl: str, saveToFile: bool, \ | 
					
						
							|  |  |  |                  clientToServer: bool, \ | 
					
						
							|  |  |  |                  sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                  personCache: {},cachedWebfingers: {}, \ | 
					
						
							|  |  |  |                  debug: bool) -> {}: | 
					
						
							|  |  |  |     """Undoes an announce message
 | 
					
						
							|  |  |  |     Typically toUrl will be https://www.w3.org/ns/activitystreams#Public | 
					
						
							|  |  |  |     and ccUrl might be a specific person whose post was repeated and the | 
					
						
							|  |  |  |     objectUrl is typically the url of the message which was repeated, | 
					
						
							|  |  |  |     corresponding to url or atomUri in createPostBase | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not urlPermitted(objectUrl,federationList,"inbox:write"): | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ':' in domain: | 
					
						
							|  |  |  |         domain=domain.split(':')[0] | 
					
						
							|  |  |  |     fullDomain=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 fullDomain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-12 09:41:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     newUndoAnnounce = { | 
					
						
							| 
									
										
										
										
											2019-08-18 11:07:06 +00:00
										 |  |  |         "@context": "https://www.w3.org/ns/activitystreams", | 
					
						
							| 
									
										
										
										
											2019-07-12 09:41:57 +00:00
										 |  |  |         'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, | 
					
						
							|  |  |  |         'type': 'Undo', | 
					
						
							|  |  |  |         'cc': [], | 
					
						
							|  |  |  |         'to': [toUrl], | 
					
						
							|  |  |  |         'object': { | 
					
						
							|  |  |  |             'actor': httpPrefix+'://'+fullDomain+'/users/'+nickname, | 
					
						
							|  |  |  |             'cc': [], | 
					
						
							|  |  |  |             'object': objectUrl, | 
					
						
							|  |  |  |             'to': [toUrl], | 
					
						
							|  |  |  |             'type': 'Announce' | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if ccUrl: | 
					
						
							|  |  |  |         if len(ccUrl)>0: | 
					
						
							|  |  |  |             newUndoAnnounce['object']['cc']=[ccUrl] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     announceNickname=None | 
					
						
							|  |  |  |     announceDomain=None | 
					
						
							|  |  |  |     announcePort=None | 
					
						
							| 
									
										
										
										
											2019-10-17 22:26:47 +00:00
										 |  |  |     if '/users/' in objectUrl or \ | 
					
						
							|  |  |  |        '/channel/' in objectUrl or \ | 
					
						
							|  |  |  |        '/profile/' in objectUrl: | 
					
						
							| 
									
										
										
										
											2019-07-12 09:41:57 +00:00
										 |  |  |         announceNickname=getNicknameFromActor(objectUrl) | 
					
						
							|  |  |  |         announceDomain,announcePort=getDomainFromActor(objectUrl) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if announceNickname and announceDomain: | 
					
						
							|  |  |  |         sendSignedJson(newUndoAnnounce,session,baseDir, \ | 
					
						
							|  |  |  |                        nickname,domain,port, \ | 
					
						
							|  |  |  |                        announceNickname,announceDomain,announcePort, \ | 
					
						
							|  |  |  |                        'https://www.w3.org/ns/activitystreams#Public', \ | 
					
						
							|  |  |  |                        httpPrefix,True,clientToServer,federationList, \ | 
					
						
							|  |  |  |                        sendThreads,postLog,cachedWebfingers,personCache,debug) | 
					
						
							|  |  |  |              | 
					
						
							|  |  |  |     return newUndoAnnounce | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def undoAnnouncePublic(session,baseDir: str,federationList: [], \ | 
					
						
							|  |  |  |                        nickname: str, domain: str, port: int, httpPrefix: str, \ | 
					
						
							|  |  |  |                        objectUrl: str,clientToServer: bool, \ | 
					
						
							|  |  |  |                        sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                        personCache: {},cachedWebfingers: {}, \ | 
					
						
							|  |  |  |                        debug: bool) -> {}: | 
					
						
							|  |  |  |     """Undoes a public announcement
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     fromDomain=domain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if port: | 
					
						
							|  |  |  |         if port!=80 and port!=443: | 
					
						
							|  |  |  |             if ':' not in domain: | 
					
						
							|  |  |  |                 fromDomain=domain+':'+str(port) | 
					
						
							| 
									
										
										
										
											2019-07-12 09:41:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     toUrl = 'https://www.w3.org/ns/activitystreams#Public' | 
					
						
							|  |  |  |     ccUrl = httpPrefix + '://'+fromDomain+'/users/'+nickname+'/followers' | 
					
						
							|  |  |  |     return undoAnnounce(session,baseDir,federationList, \ | 
					
						
							|  |  |  |                         nickname,domain,port, \ | 
					
						
							|  |  |  |                         toUrl,ccUrl,httpPrefix, \ | 
					
						
							|  |  |  |                         objectUrl,True,clientToServer, \ | 
					
						
							|  |  |  |                         sendThreads,postLog, \ | 
					
						
							|  |  |  |                         personCache,cachedWebfingers, \ | 
					
						
							|  |  |  |                         debug) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def undoRepeatPost(session,baseDir: str,federationList: [], \ | 
					
						
							|  |  |  |                    nickname: str, domain: str, port: int, httpPrefix: str, \ | 
					
						
							|  |  |  |                    announceNickname: str, announceDomain: str, \ | 
					
						
							|  |  |  |                    announcePort: int, announceHttpsPrefix: str, \ | 
					
						
							|  |  |  |                    announceStatusNumber: int,clientToServer: bool, \ | 
					
						
							|  |  |  |                    sendThreads: [],postLog: [], \ | 
					
						
							|  |  |  |                    personCache: {},cachedWebfingers: {}, \ | 
					
						
							|  |  |  |                    debug: bool) -> {}: | 
					
						
							|  |  |  |     """Undoes a status post repeat
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     announcedDomain=announceDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if announcePort: | 
					
						
							|  |  |  |         if announcePort!=80 and announcePort!=443: | 
					
						
							|  |  |  |             if ':' not in announcedDomain: | 
					
						
							|  |  |  |                 announcedDomain=announcedDomain+':'+str(announcePort) | 
					
						
							| 
									
										
										
										
											2019-07-12 09:41:57 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     objectUrl = announceHttpsPrefix + '://'+announcedDomain+'/users/'+ \ | 
					
						
							|  |  |  |         announceNickname+'/statuses/'+str(announceStatusNumber) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return undoAnnouncePublic(session,baseDir,federationList, \ | 
					
						
							|  |  |  |                               nickname,domain,port,httpPrefix, \ | 
					
						
							|  |  |  |                               objectUrl,clientToServer, \ | 
					
						
							|  |  |  |                               sendThreads,postLog, \ | 
					
						
							|  |  |  |                               personCache,cachedWebfingers, \ | 
					
						
							|  |  |  |                               debug) | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-20 09:16:03 +00:00
										 |  |  | def sendAnnounceViaServer(baseDir: str,session, \ | 
					
						
							|  |  |  |                           fromNickname: str,password: str, | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |                           fromDomain: str,fromPort: int, \ | 
					
						
							|  |  |  |                           httpPrefix: str,repeatObjectUrl: str, \ | 
					
						
							|  |  |  |                           cachedWebfingers: {},personCache: {}, \ | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |                           debug: bool,projectVersion: str) -> {}: | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |     """Creates an announce message via c2s
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     if not session: | 
					
						
							|  |  |  |         print('WARN: No session for sendAnnounceViaServer') | 
					
						
							|  |  |  |         return 6 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     withDigest=True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     fromDomainFull=fromDomain | 
					
						
							| 
									
										
										
										
											2019-08-16 20:35:11 +00:00
										 |  |  |     if fromPort: | 
					
						
							|  |  |  |         if fromPort!=80 and fromPort!=443: | 
					
						
							|  |  |  |             if ':' not in fromDomain: | 
					
						
							|  |  |  |                 fromDomainFull=fromDomain+':'+str(fromPort) | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     toUrl = 'https://www.w3.org/ns/activitystreams#Public' | 
					
						
							|  |  |  |     ccUrl = httpPrefix + '://'+fromDomainFull+'/users/'+fromNickname+'/followers' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     statusNumber,published = getStatusNumber() | 
					
						
							|  |  |  |     newAnnounceId= \ | 
					
						
							| 
									
										
										
										
											2019-11-03 15:27:29 +00:00
										 |  |  |         httpPrefix+'://'+fromDomainFull+'/users/'+ \ | 
					
						
							|  |  |  |         fromNickname+'/statuses/'+statusNumber | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |     newAnnounceJson = { | 
					
						
							| 
									
										
										
										
											2019-08-18 11:07:06 +00:00
										 |  |  |         "@context": "https://www.w3.org/ns/activitystreams", | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |         'actor': httpPrefix+'://'+fromDomainFull+'/users/'+fromNickname, | 
					
						
							|  |  |  |         'atomUri': newAnnounceId, | 
					
						
							|  |  |  |         'cc': [ccUrl], | 
					
						
							|  |  |  |         'id': newAnnounceId+'/activity', | 
					
						
							|  |  |  |         'object': repeatObjectUrl, | 
					
						
							|  |  |  |         'published': published, | 
					
						
							|  |  |  |         'to': [toUrl], | 
					
						
							|  |  |  |         'type': 'Announce' | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     handle=httpPrefix+'://'+fromDomainFull+'/@'+fromNickname | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # lookup the inbox for the To handle | 
					
						
							| 
									
										
										
										
											2019-08-14 20:12:27 +00:00
										 |  |  |     wfRequest = \ | 
					
						
							|  |  |  |         webfingerHandle(session,handle,httpPrefix,cachedWebfingers, \ | 
					
						
							|  |  |  |                         fromDomain,projectVersion) | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |     if not wfRequest: | 
					
						
							|  |  |  |         if debug: | 
					
						
							|  |  |  |             print('DEBUG: announce 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 19:07:45 +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 | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     authHeader=createBasicAuthHeader(fromNickname,password) | 
					
						
							|  |  |  |       | 
					
						
							|  |  |  |     headers = {'host': fromDomain, \ | 
					
						
							| 
									
										
										
										
											2019-11-09 21:39:04 +00:00
										 |  |  |                'Content-type': 'application/json', \ | 
					
						
							| 
									
										
										
										
											2019-07-16 19:07:45 +00:00
										 |  |  |                'Authorization': authHeader} | 
					
						
							|  |  |  |     postResult = \ | 
					
						
							|  |  |  |         postJson(session,newAnnounceJson,[],inboxUrl,headers,"inbox:write") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if debug: | 
					
						
							|  |  |  |         print('DEBUG: c2s POST announce success') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return newAnnounceJson |