| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  | __filename__ = "mastoapiv1.py" | 
					
						
							|  |  |  | __author__ = "Bob Mottram" | 
					
						
							|  |  |  | __license__ = "AGPL3+" | 
					
						
							| 
									
										
										
										
											2024-12-22 23:37:30 +00:00
										 |  |  | __version__ = "1.6.0" | 
					
						
							| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  | __maintainer__ = "Bob Mottram" | 
					
						
							| 
									
										
										
										
											2021-09-10 16:14:50 +00:00
										 |  |  | __email__ = "bob@libreserver.org" | 
					
						
							| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  | __status__ = "Production" | 
					
						
							| 
									
										
										
										
											2021-06-26 11:16:41 +00:00
										 |  |  | __module_group__ = "API" | 
					
						
							| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | import os | 
					
						
							| 
									
										
										
										
											2024-04-10 13:32:03 +00:00
										 |  |  | from utils import string_contains | 
					
						
							| 
									
										
										
										
											2023-12-09 14:18:24 +00:00
										 |  |  | from utils import get_url_from_post | 
					
						
							| 
									
										
										
										
											2021-12-26 15:13:34 +00:00
										 |  |  | from utils import load_json | 
					
						
							| 
									
										
										
										
											2021-12-26 14:08:58 +00:00
										 |  |  | from utils import get_config_param | 
					
						
							| 
									
										
										
										
											2021-12-26 12:02:29 +00:00
										 |  |  | from utils import acct_dir | 
					
						
							| 
									
										
										
										
											2023-07-12 11:08:02 +00:00
										 |  |  | from utils import remove_html | 
					
						
							| 
									
										
										
										
											2023-10-02 12:40:58 +00:00
										 |  |  | from utils import get_attachment_property_value | 
					
						
							| 
									
										
										
										
											2023-10-02 18:56:22 +00:00
										 |  |  | from utils import no_of_accounts | 
					
						
							|  |  |  | from utils import get_status_count | 
					
						
							| 
									
										
										
										
											2023-10-02 20:29:30 +00:00
										 |  |  | from utils import lines_in_file | 
					
						
							| 
									
										
										
										
											2024-05-12 12:35:26 +00:00
										 |  |  | from utils import data_dir | 
					
						
							| 
									
										
										
										
											2024-09-13 12:09:20 +00:00
										 |  |  | from utils import account_is_indexable | 
					
						
							| 
									
										
										
										
											2023-10-02 18:56:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _meta_data_instance_v1(show_accounts: bool, | 
					
						
							|  |  |  |                            instance_title: str, | 
					
						
							|  |  |  |                            instance_description_short: str, | 
					
						
							|  |  |  |                            instance_description: str, | 
					
						
							|  |  |  |                            http_prefix: str, base_dir: str, | 
					
						
							|  |  |  |                            admin_nickname: str, domain: str, domain_full: str, | 
					
						
							|  |  |  |                            registration: bool, system_language: str, | 
					
						
							|  |  |  |                            version: str) -> {}: | 
					
						
							|  |  |  |     """ /api/v1/instance endpoint
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     admin_actor_filename = \ | 
					
						
							| 
									
										
										
										
											2024-05-12 12:35:26 +00:00
										 |  |  |         data_dir(base_dir) + '/' + admin_nickname + '@' + domain + '.json' | 
					
						
							| 
									
										
										
										
											2023-10-02 18:56:22 +00:00
										 |  |  |     if not os.path.isfile(admin_actor_filename): | 
					
						
							|  |  |  |         return {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-20 10:47:58 +00:00
										 |  |  |     admin_actor = load_json(admin_actor_filename) | 
					
						
							| 
									
										
										
										
											2023-10-02 18:56:22 +00:00
										 |  |  |     if not admin_actor: | 
					
						
							|  |  |  |         print('WARN: json load exception _meta_data_instance_v1') | 
					
						
							|  |  |  |         return {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |     rules_list: list[str] = [] | 
					
						
							| 
									
										
										
										
											2024-05-12 12:35:26 +00:00
										 |  |  |     rules_filename = data_dir(base_dir) + '/tos.md' | 
					
						
							| 
									
										
										
										
											2023-10-02 18:56:22 +00:00
										 |  |  |     if os.path.isfile(rules_filename): | 
					
						
							| 
									
										
										
										
											2024-07-13 14:38:11 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             with open(rules_filename, 'r', encoding='utf-8') as fp_rules: | 
					
						
							|  |  |  |                 rules_lines = fp_rules.readlines() | 
					
						
							|  |  |  |                 rule_ctr = 1 | 
					
						
							|  |  |  |                 for line in rules_lines: | 
					
						
							|  |  |  |                     line = line.strip() | 
					
						
							|  |  |  |                     if not line: | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     if line.startswith('#'): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     rules_list.append({ | 
					
						
							|  |  |  |                         'id': str(rule_ctr), | 
					
						
							|  |  |  |                         'text': line | 
					
						
							|  |  |  |                     }) | 
					
						
							|  |  |  |                     rule_ctr += 1 | 
					
						
							|  |  |  |         except OSError: | 
					
						
							|  |  |  |             print('EX: _meta_data_instance_v1 unable to read ' + | 
					
						
							|  |  |  |                   rules_filename) | 
					
						
							| 
									
										
										
										
											2023-10-02 18:56:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     is_bot = False | 
					
						
							|  |  |  |     is_group = False | 
					
						
							|  |  |  |     if admin_actor['type'] == 'Group': | 
					
						
							|  |  |  |         is_group = True | 
					
						
							|  |  |  |     elif admin_actor['type'] != 'Person': | 
					
						
							|  |  |  |         is_bot = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     url = \ | 
					
						
							|  |  |  |         http_prefix + '://' + domain_full + '/@' + \ | 
					
						
							|  |  |  |         admin_actor['preferredUsername'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if show_accounts: | 
					
						
							|  |  |  |         active_accounts = no_of_accounts(base_dir) | 
					
						
							|  |  |  |         local_posts = get_status_count(base_dir) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         active_accounts = 1 | 
					
						
							|  |  |  |         local_posts = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     created_at = '' | 
					
						
							|  |  |  |     if admin_actor.get('published'): | 
					
						
							|  |  |  |         created_at = admin_actor['published'] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-12-09 14:18:24 +00:00
										 |  |  |     url_str = get_url_from_post(admin_actor['icon']['url']) | 
					
						
							|  |  |  |     icon_url = remove_html(url_str) | 
					
						
							|  |  |  |     url_str = get_url_from_post(admin_actor['image']['url']) | 
					
						
							|  |  |  |     image_url = remove_html(url_str) | 
					
						
							| 
									
										
										
										
											2023-10-02 18:56:22 +00:00
										 |  |  |     instance = { | 
					
						
							|  |  |  |         'approval_required': False, | 
					
						
							|  |  |  |         'invites_enabled': False, | 
					
						
							|  |  |  |         'registrations': registration, | 
					
						
							|  |  |  |         'contact_account': { | 
					
						
							|  |  |  |             'acct': admin_actor['preferredUsername'], | 
					
						
							|  |  |  |             'created_at': created_at, | 
					
						
							|  |  |  |             'avatar': icon_url, | 
					
						
							|  |  |  |             'avatar_static': icon_url, | 
					
						
							|  |  |  |             'header': image_url, | 
					
						
							|  |  |  |             'header_static': image_url, | 
					
						
							|  |  |  |             'bot': is_bot, | 
					
						
							|  |  |  |             'discoverable': True, | 
					
						
							|  |  |  |             'group': is_group, | 
					
						
							|  |  |  |             'display_name': admin_actor['name'], | 
					
						
							|  |  |  |             'locked': admin_actor['manuallyApprovesFollowers'], | 
					
						
							|  |  |  |             'note': '<p>Admin of ' + domain + '</p>', | 
					
						
							|  |  |  |             'url': url, | 
					
						
							|  |  |  |             'username': admin_actor['preferredUsername'] | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         'description': instance_description, | 
					
						
							|  |  |  |         'languages': [system_language], | 
					
						
							|  |  |  |         'short_description': instance_description_short, | 
					
						
							|  |  |  |         'stats': { | 
					
						
							|  |  |  |             'domain_count': 2, | 
					
						
							|  |  |  |             'status_count': local_posts, | 
					
						
							|  |  |  |             'user_count': active_accounts | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         'thumbnail': http_prefix + '://' + domain_full + '/login.png', | 
					
						
							|  |  |  |         'title': instance_title, | 
					
						
							|  |  |  |         'uri': domain_full, | 
					
						
							|  |  |  |         'urls': {}, | 
					
						
							|  |  |  |         'version': version, | 
					
						
							|  |  |  |         'rules': rules_list, | 
					
						
							|  |  |  |         'configuration': { | 
					
						
							|  |  |  |             'statuses': { | 
					
						
							|  |  |  |                 'max_media_attachments': 1 | 
					
						
							|  |  |  |             }, | 
					
						
							|  |  |  |             'media_attachments': { | 
					
						
							|  |  |  |                 'supported_mime_types': [ | 
					
						
							|  |  |  |                     'image/jpeg', | 
					
						
							|  |  |  |                     'image/jxl', | 
					
						
							|  |  |  |                     'image/png', | 
					
						
							|  |  |  |                     'image/gif', | 
					
						
							|  |  |  |                     'image/webp', | 
					
						
							|  |  |  |                     'image/avif', | 
					
						
							|  |  |  |                     'image/heic', | 
					
						
							|  |  |  |                     'image/svg+xml', | 
					
						
							|  |  |  |                     'video/webm', | 
					
						
							|  |  |  |                     'video/mp4', | 
					
						
							|  |  |  |                     'video/ogv', | 
					
						
							|  |  |  |                     'audio/ogg', | 
					
						
							|  |  |  |                     'audio/wav', | 
					
						
							|  |  |  |                     'audio/x-wav', | 
					
						
							|  |  |  |                     'audio/x-pn-wave', | 
					
						
							|  |  |  |                     'audio/vnd.wave', | 
					
						
							|  |  |  |                     'audio/opus', | 
					
						
							|  |  |  |                     'audio/speex', | 
					
						
							|  |  |  |                     'audio/x-speex', | 
					
						
							|  |  |  |                     'audio/flac', | 
					
						
							|  |  |  |                     'audio/mpeg' | 
					
						
							|  |  |  |                 ], | 
					
						
							|  |  |  |                 'image_size_limit': 10485760, | 
					
						
							|  |  |  |                 'image_matrix_limit': 16777216, | 
					
						
							|  |  |  |                 'video_size_limit': 41943040, | 
					
						
							|  |  |  |                 'video_frame_rate_limit': 60, | 
					
						
							|  |  |  |                 'video_matrix_limit': 2304000 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return instance | 
					
						
							| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def _get_mast_api_v1id(path: str) -> int: | 
					
						
							| 
									
										
										
										
											2021-01-22 13:32:37 +00:00
										 |  |  |     """Extracts the mastodon Id number from the given path
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     masto_id = None | 
					
						
							|  |  |  |     id_path = '/api/v1/accounts/:' | 
					
						
							|  |  |  |     if not path.startswith(id_path): | 
					
						
							| 
									
										
										
										
											2021-01-22 13:32:37 +00:00
										 |  |  |         return None | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     masto_id_str = path.replace(id_path, '') | 
					
						
							|  |  |  |     if '/' in masto_id_str: | 
					
						
							|  |  |  |         masto_id_str = masto_id_str.split('/')[0] | 
					
						
							|  |  |  |     if masto_id_str.isdigit(): | 
					
						
							|  |  |  |         masto_id = int(masto_id_str) | 
					
						
							|  |  |  |         return masto_id | 
					
						
							| 
									
										
										
										
											2021-01-22 13:32:37 +00:00
										 |  |  |     return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def get_masto_api_v1id_from_nickname(nickname: str) -> int: | 
					
						
							| 
									
										
										
										
											2021-01-22 13:32:37 +00:00
										 |  |  |     """Given an account nickname return the corresponding mastodon id
 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     return int.from_bytes(nickname.encode('utf-8'), 'little') | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  | def _int_to_bytes(num: int) -> str: | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     """Integer conversion
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2021-01-22 13:32:37 +00:00
										 |  |  |     if num == 0: | 
					
						
							|  |  |  |         return b"" | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     return _int_to_bytes(num // 256) + bytes([num % 256]) | 
					
						
							| 
									
										
										
										
											2021-01-22 13:32:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  | def get_nickname_from_masto_api_v1id(masto_id: int) -> str: | 
					
						
							| 
									
										
										
										
											2021-01-22 13:32:37 +00:00
										 |  |  |     """Given the mastodon Id return the nickname
 | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     nickname = _int_to_bytes(masto_id).decode() | 
					
						
							| 
									
										
										
										
											2021-01-22 13:32:37 +00:00
										 |  |  |     return nickname[::-1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-02 11:10:43 +00:00
										 |  |  | def _get_masto_api_v1account(base_dir: str, nickname: str, domain: str, | 
					
						
							| 
									
										
										
										
											2023-10-02 12:11:30 +00:00
										 |  |  |                              show_accounts: bool, broch_mode: bool) -> {}: | 
					
						
							| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  |     """See https://github.com/McKael/mastodon-documentation/
 | 
					
						
							|  |  |  |     blob/master/Using-the-API/API.md#account | 
					
						
							|  |  |  |     Authorization has already been performed | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2023-10-02 11:10:43 +00:00
										 |  |  |     account_dir = acct_dir(base_dir, nickname, domain) | 
					
						
							|  |  |  |     account_filename = account_dir + '.json' | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     if not os.path.isfile(account_filename): | 
					
						
							| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  |         return {} | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     account_json = load_json(account_filename) | 
					
						
							|  |  |  |     if not account_json: | 
					
						
							| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  |         return {} | 
					
						
							| 
									
										
										
										
											2023-12-09 14:18:24 +00:00
										 |  |  |     url_str = get_url_from_post(account_json['icon']['url']) | 
					
						
							|  |  |  |     avatar_url = remove_html(url_str) | 
					
						
							|  |  |  |     url_str = get_url_from_post(account_json['image']['url']) | 
					
						
							|  |  |  |     image_url = remove_html(url_str) | 
					
						
							| 
									
										
										
										
											2023-10-02 10:42:20 +00:00
										 |  |  |     joined_date = "2016-10-05T10:30:00Z" | 
					
						
							|  |  |  |     if account_json.get('published'): | 
					
						
							| 
									
										
										
										
											2023-10-02 10:43:02 +00:00
										 |  |  |         joined_date = account_json['published'] | 
					
						
							| 
									
										
										
										
											2024-09-13 12:09:20 +00:00
										 |  |  |     noindex = not account_is_indexable(account_json) | 
					
						
							| 
									
										
										
										
											2023-10-02 10:54:55 +00:00
										 |  |  |     discoverable = True | 
					
						
							|  |  |  |     if 'discoverable' in account_json: | 
					
						
							|  |  |  |         if account_json['discoverable'] is False: | 
					
						
							|  |  |  |             discoverable = False | 
					
						
							| 
									
										
										
										
											2023-10-02 10:57:31 +00:00
										 |  |  |     group = False | 
					
						
							| 
									
										
										
										
											2023-10-02 20:30:57 +00:00
										 |  |  |     bot = False | 
					
						
							| 
									
										
										
										
											2023-10-02 10:57:31 +00:00
										 |  |  |     if account_json['type'] == 'Group': | 
					
						
							|  |  |  |         group = True | 
					
						
							| 
									
										
										
										
											2023-10-02 20:30:57 +00:00
										 |  |  |     elif account_json['type'] != 'Person': | 
					
						
							| 
									
										
										
										
											2023-10-02 12:23:44 +00:00
										 |  |  |         bot = True | 
					
						
							| 
									
										
										
										
											2023-10-02 11:10:43 +00:00
										 |  |  |     no_of_statuses = 0 | 
					
						
							| 
									
										
										
										
											2023-10-02 12:11:30 +00:00
										 |  |  |     no_of_followers = 0 | 
					
						
							|  |  |  |     no_of_following = 0 | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |     fields: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2023-10-02 14:09:06 +00:00
										 |  |  |     published = None | 
					
						
							| 
									
										
										
										
											2023-10-02 12:11:30 +00:00
										 |  |  |     if show_accounts and not broch_mode: | 
					
						
							| 
									
										
										
										
											2023-10-02 20:29:30 +00:00
										 |  |  |         no_of_followers = lines_in_file(account_dir + '/followers.txt') | 
					
						
							|  |  |  |         no_of_following = lines_in_file(account_dir + '/following.txt') | 
					
						
							| 
									
										
										
										
											2023-10-02 11:10:43 +00:00
										 |  |  |         # count the number of posts | 
					
						
							|  |  |  |         for _, _, files2 in os.walk(account_dir + '/outbox'): | 
					
						
							|  |  |  |             no_of_statuses = len(files2) | 
					
						
							|  |  |  |             break | 
					
						
							| 
									
										
										
										
											2023-10-02 12:40:58 +00:00
										 |  |  |         # get account fields from attachments | 
					
						
							|  |  |  |         if account_json.get('attachment'): | 
					
						
							|  |  |  |             if isinstance(account_json['attachment'], list): | 
					
						
							|  |  |  |                 for tag in account_json['attachment']: | 
					
						
							|  |  |  |                     if not isinstance(tag, dict): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     if not tag.get('name'): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     if not isinstance(tag['name'], str): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     prop_value_name, _ = \ | 
					
						
							|  |  |  |                         get_attachment_property_value(tag) | 
					
						
							|  |  |  |                     if not prop_value_name: | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     if not tag.get(prop_value_name): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     if not isinstance(tag[prop_value_name], str): | 
					
						
							|  |  |  |                         continue | 
					
						
							|  |  |  |                     fields.append({ | 
					
						
							|  |  |  |                         "name": tag['name'], | 
					
						
							|  |  |  |                         "value": tag[prop_value_name], | 
					
						
							|  |  |  |                         "verified_at": None | 
					
						
							|  |  |  |                     }) | 
					
						
							| 
									
										
										
										
											2023-10-02 20:29:30 +00:00
										 |  |  |         published_filename = \ | 
					
						
							|  |  |  |             acct_dir(base_dir, nickname, domain) + '/.last_published' | 
					
						
							|  |  |  |         if os.path.isfile(published_filename): | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 with open(published_filename, 'r', | 
					
						
							|  |  |  |                           encoding='utf-8') as fp_pub: | 
					
						
							|  |  |  |                     published = fp_pub.read() | 
					
						
							|  |  |  |             except OSError: | 
					
						
							|  |  |  |                 print('EX: unable to read last published time 1 ' + | 
					
						
							|  |  |  |                       published_filename) | 
					
						
							| 
									
										
										
										
											2023-10-02 12:40:58 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     masto_account_json = { | 
					
						
							| 
									
										
										
										
											2021-12-29 21:55:09 +00:00
										 |  |  |         "id": get_masto_api_v1id_from_nickname(nickname), | 
					
						
							| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  |         "username": nickname, | 
					
						
							|  |  |  |         "acct": nickname, | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         "display_name": account_json['name'], | 
					
						
							|  |  |  |         "locked": account_json['manuallyApprovesFollowers'], | 
					
						
							| 
									
										
										
										
											2023-10-02 10:42:20 +00:00
										 |  |  |         "created_at": joined_date, | 
					
						
							| 
									
										
										
										
											2023-10-02 12:11:30 +00:00
										 |  |  |         "followers_count": no_of_followers, | 
					
						
							|  |  |  |         "following_count": no_of_following, | 
					
						
							| 
									
										
										
										
											2023-10-02 11:10:43 +00:00
										 |  |  |         "statuses_count": no_of_statuses, | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         "note": account_json['summary'], | 
					
						
							|  |  |  |         "url": account_json['id'], | 
					
						
							| 
									
										
										
										
											2023-07-12 11:08:02 +00:00
										 |  |  |         "avatar": avatar_url, | 
					
						
							|  |  |  |         "avatar_static": avatar_url, | 
					
						
							|  |  |  |         "header": image_url, | 
					
						
							| 
									
										
										
										
											2023-10-02 10:48:22 +00:00
										 |  |  |         "header_static": image_url, | 
					
						
							| 
									
										
										
										
											2023-10-02 10:54:55 +00:00
										 |  |  |         "noindex": noindex, | 
					
						
							| 
									
										
										
										
											2023-10-02 10:57:31 +00:00
										 |  |  |         "discoverable": discoverable, | 
					
						
							| 
									
										
										
										
											2023-10-02 12:23:44 +00:00
										 |  |  |         "group": group, | 
					
						
							|  |  |  |         "bot": bot, | 
					
						
							|  |  |  |         "emojis": [], | 
					
						
							|  |  |  |         "roles": [], | 
					
						
							| 
									
										
										
										
											2023-10-02 12:40:58 +00:00
										 |  |  |         "fields": fields | 
					
						
							| 
									
										
										
										
											2021-01-22 11:29:36 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2023-10-02 14:09:06 +00:00
										 |  |  |     if published: | 
					
						
							|  |  |  |         masto_account_json['last_status_at'] = published | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     return masto_account_json | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-28 17:20:43 +00:00
										 |  |  | def masto_api_v1_response(path: str, calling_domain: str, | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                           ua_str: str, | 
					
						
							| 
									
										
										
										
											2021-12-28 17:20:43 +00:00
										 |  |  |                           authorized: bool, | 
					
						
							|  |  |  |                           http_prefix: str, | 
					
						
							|  |  |  |                           base_dir: str, nickname: str, domain: str, | 
					
						
							|  |  |  |                           domain_full: str, | 
					
						
							|  |  |  |                           onion_domain: str, i2p_domain: str, | 
					
						
							|  |  |  |                           translate: {}, | 
					
						
							|  |  |  |                           registration: bool, | 
					
						
							|  |  |  |                           system_language: str, | 
					
						
							|  |  |  |                           project_version: str, | 
					
						
							| 
									
										
										
										
											2022-01-01 20:36:56 +00:00
										 |  |  |                           custom_emoji: [], | 
					
						
							| 
									
										
										
										
											2021-12-28 17:20:43 +00:00
										 |  |  |                           show_node_info_accounts: bool, | 
					
						
							|  |  |  |                           broch_mode: bool) -> ({}, str): | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     """This is a vestigil mastodon API for the purpose
 | 
					
						
							| 
									
										
										
										
											2023-10-02 20:29:30 +00:00
										 |  |  |        of returning a result to sites like | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |        https://mastopeek.app-dist.eu | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     send_json = None | 
					
						
							|  |  |  |     send_json_str = '' | 
					
						
							|  |  |  |     if not ua_str: | 
					
						
							|  |  |  |         ua_str = '' | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # parts of the api needing authorization | 
					
						
							|  |  |  |     if authorized and nickname: | 
					
						
							|  |  |  |         if path == '/api/v1/accounts/verify_credentials': | 
					
						
							| 
									
										
										
										
											2023-10-02 11:10:43 +00:00
										 |  |  |             send_json = \ | 
					
						
							|  |  |  |                 _get_masto_api_v1account(base_dir, nickname, domain, | 
					
						
							| 
									
										
										
										
											2023-10-02 12:11:30 +00:00
										 |  |  |                                          show_node_info_accounts, | 
					
						
							|  |  |  |                                          broch_mode) | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |             send_json_str = \ | 
					
						
							|  |  |  |                 'masto API account sent for ' + nickname + ' ' + ua_str | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-24 09:02:28 +00:00
										 |  |  |     # information about where the request is coming from | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     calling_info = ' ' + ua_str + ', ' + calling_domain | 
					
						
							| 
									
										
										
										
											2021-10-24 09:02:28 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     # Parts of the api which don't need authorization | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |     masto_id = _get_mast_api_v1id(path) | 
					
						
							|  |  |  |     if masto_id is not None: | 
					
						
							|  |  |  |         path_nickname = get_nickname_from_masto_api_v1id(masto_id) | 
					
						
							|  |  |  |         if path_nickname: | 
					
						
							|  |  |  |             original_path = path | 
					
						
							| 
									
										
										
										
											2024-04-10 13:32:03 +00:00
										 |  |  |             if string_contains(path, | 
					
						
							|  |  |  |                                ('/followers?', '/following?', '/streaming/', | 
					
						
							|  |  |  |                                 '/search?', '/relationships?', '/statuses?')): | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |                 path = path.split('?')[0] | 
					
						
							| 
									
										
										
										
											2022-02-25 10:29:14 +00:00
										 |  |  |             if '/streaming/' in path: | 
					
						
							|  |  |  |                 streaming_msg = \ | 
					
						
							|  |  |  |                     "Error: Streaming API not implemented on this instance" | 
					
						
							|  |  |  |                 send_json = { | 
					
						
							|  |  |  |                     "error": streaming_msg | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 send_json_str = 'masto API streaming response' | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |             if path.endswith('/followers'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |                 send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                 send_json_str = \ | 
					
						
							| 
									
										
										
										
											2021-10-23 18:51:30 +00:00
										 |  |  |                     'masto API followers sent for ' + nickname + \ | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                     calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |             elif path.endswith('/following'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |                 send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                 send_json_str = \ | 
					
						
							| 
									
										
										
										
											2021-10-23 18:51:30 +00:00
										 |  |  |                     'masto API following sent for ' + nickname + \ | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                     calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |             elif path.endswith('/statuses'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |                 send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                 send_json_str = \ | 
					
						
							| 
									
										
										
										
											2021-10-23 18:51:30 +00:00
										 |  |  |                     'masto API statuses sent for ' + nickname + \ | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                     calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |             elif path.endswith('/search'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |                 send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                 send_json_str = \ | 
					
						
							|  |  |  |                     'masto API search sent ' + original_path + \ | 
					
						
							|  |  |  |                     calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |             elif path.endswith('/relationships'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |                 send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                 send_json_str = \ | 
					
						
							|  |  |  |                     'masto API relationships sent ' + original_path + \ | 
					
						
							|  |  |  |                     calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                 send_json = \ | 
					
						
							| 
									
										
										
										
											2023-10-02 11:10:43 +00:00
										 |  |  |                     _get_masto_api_v1account(base_dir, path_nickname, domain, | 
					
						
							| 
									
										
										
										
											2023-10-02 12:11:30 +00:00
										 |  |  |                                              show_node_info_accounts, | 
					
						
							|  |  |  |                                              broch_mode) | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                 send_json_str = \ | 
					
						
							| 
									
										
										
										
											2021-10-23 18:51:30 +00:00
										 |  |  |                     'masto API account sent for ' + nickname + \ | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |                     calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-02 11:54:47 +00:00
										 |  |  |     # NOTE: adding support for '/api/v1/directory seems to create | 
					
						
							|  |  |  |     # federation problems, so avoid implementing that | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     if path.startswith('/api/v1/blocks'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |         send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = \ | 
					
						
							|  |  |  |             'masto API instance blocks sent ' + path + calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/favorites'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |         send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = 'masto API favorites sent ' + path + calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/follow_requests'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |         send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = \ | 
					
						
							|  |  |  |             'masto API follow requests sent ' + path + calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/mutes'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |         send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = \ | 
					
						
							|  |  |  |             'masto API mutes sent ' + path + calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/notifications'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |         send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = \ | 
					
						
							|  |  |  |             'masto API notifications sent ' + path + calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/reports'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |         send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = 'masto API reports sent ' + path + calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/statuses'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |         send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = 'masto API statuses sent ' + path + calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/timelines'): | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json = { | 
					
						
							| 
									
										
										
										
											2021-10-13 11:27:37 +00:00
										 |  |  |             'error': 'This method requires an authenticated user' | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = 'masto API timelines sent ' + path + calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/custom_emojis'): | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json = custom_emoji | 
					
						
							|  |  |  |         send_json_str = \ | 
					
						
							|  |  |  |             'masto API custom emojis sent ' + path + calling_info | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-31 21:18:12 +00:00
										 |  |  |     admin_nickname = get_config_param(base_dir, 'admin') | 
					
						
							|  |  |  |     if admin_nickname and path == '/api/v1/instance': | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         instance_description_short = \ | 
					
						
							| 
									
										
										
										
											2021-12-26 14:08:58 +00:00
										 |  |  |             get_config_param(base_dir, 'instanceDescriptionShort') | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         if not instance_description_short: | 
					
						
							|  |  |  |             instance_description_short = \ | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |                 translate['Yet another Epicyon Instance'] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         instance_description = \ | 
					
						
							| 
									
										
										
										
											2021-12-26 14:08:58 +00:00
										 |  |  |             get_config_param(base_dir, 'instanceDescription') | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         instance_title = get_config_param(base_dir, 'instanceTitle') | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-26 18:29:39 +00:00
										 |  |  |         if calling_domain.endswith('.onion') and onion_domain: | 
					
						
							| 
									
										
										
										
											2021-12-26 10:00:46 +00:00
										 |  |  |             domain_full = onion_domain | 
					
						
							| 
									
										
										
										
											2021-12-25 17:09:22 +00:00
										 |  |  |             http_prefix = 'http' | 
					
						
							| 
									
										
										
										
											2021-12-26 18:29:39 +00:00
										 |  |  |         elif (calling_domain.endswith('.i2p') and i2p_domain): | 
					
						
							| 
									
										
										
										
											2021-12-26 10:00:46 +00:00
										 |  |  |             domain_full = i2p_domain | 
					
						
							| 
									
										
										
										
											2021-12-25 17:09:22 +00:00
										 |  |  |             http_prefix = 'http' | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-25 18:38:19 +00:00
										 |  |  |         if broch_mode: | 
					
						
							| 
									
										
										
										
											2021-12-25 18:32:17 +00:00
										 |  |  |             show_node_info_accounts = False | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json = \ | 
					
						
							| 
									
										
										
										
											2023-10-02 18:56:22 +00:00
										 |  |  |             _meta_data_instance_v1(show_node_info_accounts, | 
					
						
							|  |  |  |                                    instance_title, | 
					
						
							|  |  |  |                                    instance_description_short, | 
					
						
							|  |  |  |                                    instance_description, | 
					
						
							|  |  |  |                                    http_prefix, | 
					
						
							|  |  |  |                                    base_dir, | 
					
						
							|  |  |  |                                    admin_nickname, | 
					
						
							|  |  |  |                                    domain, | 
					
						
							|  |  |  |                                    domain_full, | 
					
						
							|  |  |  |                                    registration, | 
					
						
							|  |  |  |                                    system_language, | 
					
						
							|  |  |  |                                    project_version) | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = 'masto API instance metadata sent ' + ua_str | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/instance/peers'): | 
					
						
							|  |  |  |         # This is just a dummy result. | 
					
						
							|  |  |  |         # Showing the full list of peers would have privacy implications. | 
					
						
							|  |  |  |         # On a large instance you are somewhat lost in the crowd, but on | 
					
						
							|  |  |  |         # small instances a full list of peers would convey a lot of | 
					
						
							|  |  |  |         # information about the interests of a small number of accounts | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json = ['mastodon.social', domain_full] | 
					
						
							|  |  |  |         send_json_str = 'masto API peers metadata sent ' + ua_str | 
					
						
							| 
									
										
										
										
											2021-06-07 19:10:37 +00:00
										 |  |  |     elif path.startswith('/api/v1/instance/activity'): | 
					
						
							| 
									
										
										
										
											2024-12-23 17:45:20 +00:00
										 |  |  |         send_json: list[dict] = [] | 
					
						
							| 
									
										
										
										
											2022-01-02 22:49:32 +00:00
										 |  |  |         send_json_str = 'masto API activity metadata sent ' + ua_str | 
					
						
							|  |  |  |     return send_json, send_json_str |