mirror of https://gitlab.com/bashrc2/epicyon
				
				
				
			
		
			
				
	
	
		
			236 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			236 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			Python
		
	
	
| __filename__ = "metadata.py"
 | |
| __author__ = "Bob Mottram"
 | |
| __license__ = "AGPL3+"
 | |
| __version__ = "1.3.0"
 | |
| __maintainer__ = "Bob Mottram"
 | |
| __email__ = "bob@libreserver.org"
 | |
| __status__ = "Production"
 | |
| __module_group__ = "Metadata"
 | |
| 
 | |
| import os
 | |
| from utils import is_account_dir
 | |
| from utils import load_json
 | |
| from utils import no_of_accounts
 | |
| from utils import no_of_active_accounts_monthly
 | |
| 
 | |
| 
 | |
| def _get_status_count(base_dir: str) -> int:
 | |
|     """Get the total number of posts
 | |
|     """
 | |
|     status_ctr = 0
 | |
|     accounts_dir = base_dir + '/accounts'
 | |
|     for _, dirs, _ in os.walk(accounts_dir):
 | |
|         for acct in dirs:
 | |
|             if not is_account_dir(acct):
 | |
|                 continue
 | |
|             acct_dir = os.path.join(accounts_dir, acct + '/outbox')
 | |
|             for _, _, files2 in os.walk(acct_dir):
 | |
|                 status_ctr += len(files2)
 | |
|                 break
 | |
|         break
 | |
|     return status_ctr
 | |
| 
 | |
| 
 | |
| def meta_data_node_info(base_dir: str,
 | |
|                         about_url: str,
 | |
|                         terms_of_service_url: str,
 | |
|                         registration: bool, version: str,
 | |
|                         showAccounts: bool) -> {}:
 | |
|     """ /nodeinfo/2.0 endpoint
 | |
|     Also see https://socialhub.activitypub.rocks/t/
 | |
|     fep-f1d5-nodeinfo-in-fediverse-software/1190/4
 | |
| 
 | |
|     Note that there are security considerations with this. If an adversary
 | |
|     sees a lot of accounts and "local" posts then the instance may be
 | |
|     considered a higher priority target.
 | |
|     Also exposure of the version number and number of accounts could be
 | |
|     sensitive
 | |
|     """
 | |
|     if showAccounts:
 | |
|         active_accounts = no_of_accounts(base_dir)
 | |
|         active_accounts_monthly = no_of_active_accounts_monthly(base_dir, 1)
 | |
|         active_accounts_half_year = no_of_active_accounts_monthly(base_dir, 6)
 | |
|         local_posts = _get_status_count(base_dir)
 | |
|     else:
 | |
|         active_accounts = 1
 | |
|         active_accounts_monthly = 1
 | |
|         active_accounts_half_year = 1
 | |
|         local_posts = 1
 | |
| 
 | |
|     nodeinfo = {
 | |
|         'openRegistrations': registration,
 | |
|         'protocols': ['activitypub'],
 | |
|         'software': {
 | |
|             'name': 'epicyon',
 | |
|             'version': version
 | |
|         },
 | |
|         'documents': {
 | |
|             'about': about_url,
 | |
|             'terms': terms_of_service_url
 | |
|         },
 | |
|         'usage': {
 | |
|             'localPosts': local_posts,
 | |
|             'users': {
 | |
|                 'activeHalfyear': active_accounts_half_year,
 | |
|                 'activeMonth': active_accounts_monthly,
 | |
|                 'total': active_accounts
 | |
|             }
 | |
|         },
 | |
|         'version': '2.0'
 | |
|     }
 | |
|     return nodeinfo
 | |
| 
 | |
| 
 | |
| def meta_data_instance(showAccounts: 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 = \
 | |
|         base_dir + '/accounts/' + admin_nickname + '@' + domain + '.json'
 | |
|     if not os.path.isfile(admin_actor_filename):
 | |
|         return {}
 | |
| 
 | |
|     admin_actor = load_json(admin_actor_filename, 0)
 | |
|     if not admin_actor:
 | |
|         print('WARN: json load exception meta_data_instance')
 | |
|         return {}
 | |
| 
 | |
|     rules_list = []
 | |
|     rules_filename = \
 | |
|         base_dir + '/accounts/tos.md'
 | |
|     if os.path.isfile(rules_filename):
 | |
|         with open(rules_filename, 'r') 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
 | |
| 
 | |
|     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 showAccounts:
 | |
|         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']
 | |
| 
 | |
|     instance = {
 | |
|         'approval_required': False,
 | |
|         'invites_enabled': False,
 | |
|         'registrations': registration,
 | |
|         'contact_account': {
 | |
|             'acct': admin_actor['preferredUsername'],
 | |
|             'created_at': created_at,
 | |
|             'avatar': admin_actor['icon']['url'],
 | |
|             'avatar_static': admin_actor['icon']['url'],
 | |
|             'header': admin_actor['image']['url'],
 | |
|             'header_static': admin_actor['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/svg+xml',
 | |
|                     'video/webm',
 | |
|                     'video/mp4',
 | |
|                     'video/ogv',
 | |
|                     'audio/ogg',
 | |
|                     '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
 | |
| 
 | |
| 
 | |
| def metadata_custom_emoji(base_dir: str,
 | |
|                           http_prefix: str, domain_full: str) -> {}:
 | |
|     """Returns the custom emoji
 | |
|     Endpoint /api/v1/custom_emojis
 | |
|     See https://docs.joinmastodon.org/methods/instance/custom_emojis
 | |
|     """
 | |
|     result = []
 | |
|     emojis_url = http_prefix + '://' + domain_full + '/emoji'
 | |
|     for _, _, files in os.walk(base_dir + '/emoji'):
 | |
|         for fname in files:
 | |
|             if len(fname) < 3:
 | |
|                 continue
 | |
|             if fname[0].isdigit() or fname[1].isdigit():
 | |
|                 continue
 | |
|             if not fname.endswith('.png'):
 | |
|                 continue
 | |
|             url = os.path.join(emojis_url, fname)
 | |
|             result.append({
 | |
|                 "shortcode": fname.replace('.png', ''),
 | |
|                 "url": url,
 | |
|                 "static_url": url,
 | |
|                 "visible_in_picker": True
 | |
|             })
 | |
|         break
 | |
|     return result
 |