epicyon/metadata.py

235 lines
7.3 KiB
Python
Raw Normal View History

2020-04-03 16:59:12 +00:00
__filename__ = "metadata.py"
__author__ = "Bob Mottram"
__license__ = "AGPL3+"
2021-01-26 10:07:42 +00:00
__version__ = "1.2.0"
2020-04-03 16:59:12 +00:00
__maintainer__ = "Bob Mottram"
2021-09-10 16:14:50 +00:00
__email__ = "bob@libreserver.org"
2020-04-03 16:59:12 +00:00
__status__ = "Production"
2021-06-15 15:08:12 +00:00
__module_group__ = "Metadata"
2019-11-13 10:32:12 +00:00
2019-11-13 12:45:41 +00:00
import os
2021-12-26 18:46:43 +00:00
from utils import is_account_dir
2021-12-26 15:13:34 +00:00
from utils import load_json
from utils import noOfAccounts
2019-11-13 15:19:43 +00:00
from utils import noOfActiveAccountsMonthly
2019-11-13 10:32:12 +00:00
2020-04-03 16:59:12 +00:00
2021-12-25 16:17:53 +00:00
def _getStatusCount(base_dir: str) -> int:
2021-06-05 09:22:35 +00:00
"""Get the total number of posts
"""
statusCtr = 0
2021-12-25 16:17:53 +00:00
accountsDir = base_dir + '/accounts'
2021-06-05 09:22:35 +00:00
for subdir, dirs, files in os.walk(accountsDir):
for acct in dirs:
2021-12-26 18:46:43 +00:00
if not is_account_dir(acct):
2021-06-05 09:22:35 +00:00
continue
2021-12-26 12:02:29 +00:00
acct_dir = os.path.join(accountsDir, acct + '/outbox')
for subdir2, dirs2, files2 in os.walk(acct_dir):
2021-06-05 09:22:35 +00:00
statusCtr += len(files2)
break
break
return statusCtr
2021-12-25 16:17:53 +00:00
def metaDataNodeInfo(base_dir: str,
2021-05-04 09:39:15 +00:00
aboutUrl: str,
termsOfServiceUrl: str,
registration: bool, version: str,
showAccounts: bool) -> {}:
2019-11-13 10:32:12 +00:00
""" /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
2019-11-13 10:32:12 +00:00
"""
if showAccounts:
2021-12-25 16:17:53 +00:00
activeAccounts = noOfAccounts(base_dir)
activeAccountsMonthly = noOfActiveAccountsMonthly(base_dir, 1)
activeAccountsHalfYear = noOfActiveAccountsMonthly(base_dir, 6)
localPosts = _getStatusCount(base_dir)
else:
activeAccounts = 1
activeAccountsMonthly = 1
activeAccountsHalfYear = 1
2021-06-05 09:22:35 +00:00
localPosts = 1
2020-04-03 16:59:12 +00:00
nodeinfo = {
2019-11-13 10:32:12 +00:00
'openRegistrations': registration,
'protocols': ['activitypub'],
'software': {
'name': 'epicyon',
'version': version
},
2021-05-04 09:39:15 +00:00
'documents': {
'about': aboutUrl,
'terms': termsOfServiceUrl
},
2019-11-13 10:32:12 +00:00
'usage': {
2021-06-05 09:22:35 +00:00
'localPosts': localPosts,
2019-11-13 10:32:12 +00:00
'users': {
2019-11-13 15:19:43 +00:00
'activeHalfyear': activeAccountsHalfYear,
'activeMonth': activeAccountsMonthly,
'total': activeAccounts
2019-11-13 10:32:12 +00:00
}
},
'version': '2.0'
}
return nodeinfo
2019-11-13 12:45:41 +00:00
2020-04-03 16:59:12 +00:00
2021-06-05 09:22:35 +00:00
def metaDataInstance(showAccounts: bool,
instanceTitle: str,
2020-04-03 16:59:12 +00:00
instanceDescriptionShort: str,
instanceDescription: str,
2021-12-25 17:09:22 +00:00
http_prefix: str, base_dir: str,
2021-12-26 10:00:46 +00:00
adminNickname: str, domain: str, domain_full: str,
2021-12-25 23:03:28 +00:00
registration: bool, system_language: str,
2019-11-13 12:45:41 +00:00
version: str) -> {}:
""" /api/v1/instance endpoint
"""
2020-04-03 16:59:12 +00:00
adminActorFilename = \
2021-12-25 16:17:53 +00:00
base_dir + '/accounts/' + adminNickname + '@' + domain + '.json'
2019-11-13 12:45:41 +00:00
if not os.path.isfile(adminActorFilename):
return {}
2021-12-26 15:13:34 +00:00
adminActor = load_json(adminActorFilename, 0)
2019-11-13 12:45:41 +00:00
if not adminActor:
print('WARN: json load exception metaDataInstance')
2019-11-13 12:45:41 +00:00
return {}
rulesList = []
rulesFilename = \
2021-12-25 16:17:53 +00:00
base_dir + '/accounts/tos.md'
if os.path.isfile(rulesFilename):
with open(rulesFilename, 'r') as fp:
rulesLines = fp.readlines()
ruleCtr = 1
for line in rulesLines:
line = line.strip()
if not line:
continue
if line.startswith('#'):
continue
rulesList.append({
'id': str(ruleCtr),
'text': line
})
ruleCtr += 1
2020-04-03 16:59:12 +00:00
isBot = False
isGroup = False
if adminActor['type'] == 'Group':
isGroup = True
elif adminActor['type'] != 'Person':
2020-04-03 16:59:12 +00:00
isBot = True
url = \
2021-12-26 10:00:46 +00:00
http_prefix + '://' + domain_full + '/@' + \
2020-04-03 16:59:12 +00:00
adminActor['preferredUsername']
2020-03-22 21:16:02 +00:00
2021-06-05 09:22:35 +00:00
if showAccounts:
2021-12-25 16:17:53 +00:00
activeAccounts = noOfAccounts(base_dir)
localPosts = _getStatusCount(base_dir)
2021-06-05 09:22:35 +00:00
else:
activeAccounts = 1
localPosts = 1
createdAt = ''
if adminActor.get('published'):
createdAt = adminActor['published']
2020-04-03 16:59:12 +00:00
instance = {
2019-11-13 12:55:37 +00:00
'approval_required': False,
'invites_enabled': False,
'registrations': registration,
2020-03-22 20:36:19 +00:00
'contact_account': {
'acct': adminActor['preferredUsername'],
'created_at': createdAt,
2020-03-22 20:36:19 +00:00
'avatar': adminActor['icon']['url'],
'avatar_static': adminActor['icon']['url'],
'header': adminActor['image']['url'],
'header_static': adminActor['image']['url'],
'bot': isBot,
'discoverable': True,
'group': isGroup,
'display_name': adminActor['name'],
2020-03-22 20:36:19 +00:00
'locked': adminActor['manuallyApprovesFollowers'],
2021-05-04 10:17:06 +00:00
'note': '<p>Admin of ' + domain + '</p>',
2020-04-03 16:59:12 +00:00
'url': url,
2020-03-22 20:36:19 +00:00
'username': adminActor['preferredUsername']
2019-11-13 12:45:41 +00:00
},
'description': instanceDescription,
2021-12-25 23:03:28 +00:00
'languages': [system_language],
2019-11-13 12:45:41 +00:00
'short_description': instanceDescriptionShort,
'stats': {
'domain_count': 2,
2021-06-05 09:22:35 +00:00
'status_count': localPosts,
'user_count': activeAccounts
2019-11-13 12:45:41 +00:00
},
2021-12-26 10:00:46 +00:00
'thumbnail': http_prefix + '://' + domain_full + '/login.png',
2019-11-13 12:45:41 +00:00
'title': instanceTitle,
2021-12-26 10:00:46 +00:00
'uri': domain_full,
2019-11-13 12:45:41 +00:00
'urls': {},
'version': version,
'rules': rulesList,
'configuration': {
'statuses': {
'max_media_attachments': 1
},
'media_attachments': {
'supported_mime_types': [
'image/jpeg',
'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
}
}
2019-11-13 12:45:41 +00:00
}
2020-03-22 21:16:02 +00:00
2019-11-13 12:45:41 +00:00
return instance
2021-05-27 22:08:49 +00:00
2021-12-25 16:17:53 +00:00
def metadataCustomEmoji(base_dir: str,
2021-12-26 10:00:46 +00:00
http_prefix: str, domain_full: str) -> {}:
2021-05-27 22:08:49 +00:00
"""Returns the custom emoji
Endpoint /api/v1/custom_emojis
See https://docs.joinmastodon.org/methods/instance/custom_emojis
"""
result = []
2021-12-26 10:00:46 +00:00
emojisUrl = http_prefix + '://' + domain_full + '/emoji'
2021-12-25 16:17:53 +00:00
for subdir, dirs, files in os.walk(base_dir + '/emoji'):
2021-05-27 22:08:49 +00:00
for f in files:
2021-05-27 22:19:52 +00:00
if len(f) < 3:
continue
if f[0].isdigit() or f[1].isdigit():
2021-05-27 22:08:49 +00:00
continue
if not f.endswith('.png'):
continue
url = os.path.join(emojisUrl, f)
result.append({
"shortcode": f.replace('.png', ''),
"url": url,
"static_url": url,
"visible_in_picker": True
})
break
return result