From e50d691e5a5694a2d7a5ce422ff284739ba5d5e1 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Sat, 13 Dec 2025 13:05:56 +0000 Subject: [PATCH] Support for virtual locations --- happening.py | 9 ++++- maps.py | 17 +++++---- posts.py | 106 +++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 95 insertions(+), 37 deletions(-) diff --git a/happening.py b/happening.py index 23f972876..13f72ebfd 100644 --- a/happening.py +++ b/happening.py @@ -215,11 +215,11 @@ def save_event_post(base_dir: str, handle: str, post_id: str, def _is_happening_event(tag: {}) -> bool: - """Is this tag an Event or Place ActivityStreams type? + """Is this tag an Event, Place or VirtualLocation ActivityStreams type? """ if not tag.get('type'): return False - if tag['type'] != 'Event' and tag['type'] != 'Place': + if tag['type'] not in ('Event', 'Place', 'VirtualLocation'): return False return True @@ -472,6 +472,11 @@ def _icalendar_day(base_dir: str, nickname: str, domain: str, elif evnt['type'] == 'Place': if evnt.get('name'): event_place = remove_html(evnt['name']) + elif evnt['type'] == 'VirtualLocation': + if evnt.get('url'): + event_place = remove_html(evnt['url']) + if evnt.get('name'): + event_description = evnt['name'].strip() print('icalendar: ' + str(post_id) + ' ' + str(event_start) + ' ' + str(event_description) + ' ' + diff --git a/maps.py b/maps.py index a4e870902..c65e6a43b 100644 --- a/maps.py +++ b/maps.py @@ -39,12 +39,17 @@ def get_location_dict_from_tags(tags: []) -> str: for tag_item in tags: if not tag_item.get('type'): continue - if tag_item['type'] != 'Place': + if tag_item['type'] not in ('Place', 'VirtualLocation'): continue if not tag_item.get('name'): continue if not isinstance(tag_item['name'], str): continue + if tag_item.get('url'): + if not isinstance(tag_item['url'], str): + continue + if not resembles_url(tag_item['url']): + continue return tag_item return None @@ -94,12 +99,10 @@ def _get_location_from_tags(tags: []) -> str: location_str = remove_html(location_str) if locn.get('url'): # location name and link - if isinstance(locn['url'], str): - if resembles_url(locn['url']): - location_str = \ - '' + \ - location_str + '' + location_str = \ + '' + \ + location_str + '' if locn.get('address'): # location name and address if isinstance(locn['address'], str): diff --git a/posts.py b/posts.py index a9a80218b..47fd7fe24 100644 --- a/posts.py +++ b/posts.py @@ -1253,7 +1253,8 @@ def _create_post_s2s(base_dir: str, nickname: str, domain: str, port: int, media_license_url: str, media_creator: str, buy_url: str, chat_url: str, translate: {}, searchable_by: [], - automatic_quote_approval: str) -> {}: + automatic_quote_approval: str, + session) -> {}: """Creates a new server-to-server post """ domain_full = get_full_domain(domain, port) @@ -1354,6 +1355,10 @@ def _create_post_s2s(base_dir: str, nickname: str, domain: str, port: int, # pixelfed/friendica style location representation location = get_location_dict_from_tags(tags) if location: + locn_url = None + if location.get('url'): + locn_url = location['url'] + if location.get('name') and \ location.get('longitude') and \ location.get('latitude'): @@ -1364,17 +1369,31 @@ def _create_post_s2s(base_dir: str, nickname: str, domain: str, port: int, 'latitude': location['latitude'] } elif location.get('name'): - new_post['object']['location'] = { - 'type': 'Place', - 'name': location['name'] - } + # check if the location url looks like a map url + locn_url2 = locn_url + if locn_url2: + _, latitude, longitude = \ + geocoords_from_map_link(locn_url2, 'openstreetmap.org', + session) + if not latitude or not longitude: + locn_url2 = None + + if not locn_url2: + new_post['object']['location'] = { + 'type': 'Place', + 'name': location['name'] + } + else: + new_post['object']['location'] = { + 'type': 'VirtualLocation', + 'name': location['name'], + 'url': location['url'] + } if location.get('address'): if isinstance(location['address'], str): new_post['object']['location']['address'] = location['address'] - if location.get('url'): - if isinstance(location['url'], str): - if resembles_url(location['url']): - new_post['object']['location']['url'] = location['url'] + if locn_url: + new_post['object']['location']['url'] = locn_url if attach_image_filename: new_post['object'] = \ attach_media(base_dir, http_prefix, nickname, domain, port, @@ -1403,7 +1422,8 @@ def _create_post_c2s(base_dir: str, nickname: str, domain: str, port: int, content_license_url: str, media_license_url: str, media_creator: str, buy_url: str, chat_url: str, translate: {}, searchable_by: [], - automatic_quote_approval: str) -> {}: + automatic_quote_approval: str, + session) -> {}: """Creates a new client-to-server post """ domain_full = get_full_domain(domain, port) @@ -1492,6 +1512,10 @@ def _create_post_c2s(base_dir: str, nickname: str, domain: str, port: int, # pixelfed/friendica style location representation location = get_location_dict_from_tags(tags) if location: + locn_url = None + if location.get('url'): + locn_url = location['url'] + if location.get('name') and \ location.get('longitude') and \ location.get('latitude'): @@ -1502,17 +1526,31 @@ def _create_post_c2s(base_dir: str, nickname: str, domain: str, port: int, 'latitude': location['latitude'] } elif location.get('name'): - new_post['location'] = { - 'type': 'Place', - 'name': location['name'] - } + # check if the location url looks like a map url + locn_url2 = locn_url + if locn_url2: + _, latitude, longitude = \ + geocoords_from_map_link(locn_url2, 'openstreetmap.org', + session) + if not latitude or not longitude: + locn_url2 = None + + if not locn_url2: + new_post['location'] = { + 'type': 'Place', + 'name': location['name'] + } + else: + new_post['location'] = { + 'type': 'VirtualLocation', + 'name': location['name'], + 'url': location['url'] + } if location.get('address'): if isinstance(location['address'], str): new_post['object']['location']['address'] = location['address'] - if location.get('url'): - if isinstance(location['url'], str): - if resembles_url(location['url']): - new_post['object']['location']['url'] = location['url'] + if locn_url: + new_post['object']['location']['url'] = locn_url if attach_image_filename: new_post = \ @@ -1617,14 +1655,24 @@ def _create_post_place_and_time(event_date: str, end_date: str, "longitude": longitude } else: - location_tag_json = { - "@context": [ - 'https://www.w3.org/ns/activitystreams', - 'https://w3id.org/security/v1' - ], - "type": "Place", - "name": location - } + if not resembles_url(location.strip()): + location_tag_json = { + "@context": [ + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1' + ], + "type": "Place", + "name": location + } + else: + location_tag_json = { + "@context": [ + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1' + ], + "type": "VirtualLocation", + "url": location + } # add the address if it is available if location_address: location_tag_json['address'] = remove_html(location_address) @@ -1957,7 +2005,8 @@ def create_post_base(base_dir: str, content_license_url, media_license_url, media_creator, buy_url, chat_url, translate, searchable_by_list, - automatic_quote_approval) + automatic_quote_approval, + session) else: new_post = \ _create_post_c2s(base_dir, nickname, domain, port, @@ -1974,7 +2023,8 @@ def create_post_base(base_dir: str, content_license_url, media_license_url, media_creator, buy_url, chat_url, translate, searchable_by_list, - automatic_quote_approval) + automatic_quote_approval, + session) _create_post_mentions(cc_url, new_post, to_recipients, tags)