Implementing featured tags

main
bashrc 2026-07-22 17:55:52 +01:00
parent 9f03aa51ce
commit 5c8c675509
5 changed files with 125 additions and 162 deletions

View File

@ -13,6 +13,7 @@ from src.httpcodes import write2
from src.httpcodes import http_404 from src.httpcodes import http_404
from src.httpheaders import set_headers from src.httpheaders import set_headers
from src.posts import json_pin_post from src.posts import json_pin_post
from src.utils import remove_html
from src.utils import text_in_file from src.utils import text_in_file
from src.utils import convert_domains from src.utils import convert_domains
from src.utils import get_json_content_from_accept from src.utils import get_json_content_from_accept
@ -21,7 +22,6 @@ from src.utils import load_json
from src.utils import remove_eol from src.utils import remove_eol
from src.follow import get_following_feed from src.follow import get_following_feed
from src.data import is_a_file from src.data import is_a_file
from src.data import is_a_dir
from src.data import load_list from src.data import load_list
from src.data import load_string from src.data import load_string
@ -207,7 +207,7 @@ def get_featured_tags_collection(self, calling_domain: str,
} }
featured_tags_filename: str = \ featured_tags_filename: str = \
account_dir + '/featured_hashtags.txt' account_dir + '/featured_hashtags.txt'
if is_a_dir(account_dir) and is_a_file(featured_tags_filename): if is_a_file(featured_tags_filename):
hashtags_str: str = \ hashtags_str: str = \
load_string(featured_tags_filename, load_string(featured_tags_filename,
'EX: unable to load featured hashtags ' + 'EX: unable to load featured hashtags ' +
@ -221,7 +221,10 @@ def get_featured_tags_collection(self, calling_domain: str,
tag = tag.strip().replace('#', '') tag = tag.strip().replace('#', '')
if not tag: if not tag:
continue continue
url = http_prefix + '://' + domain_full + '/tagged/' + tag tag = remove_html(tag)
if not tag:
continue
url = http_prefix + '://' + domain_full + '/tags/' + tag
tag_dict = { tag_dict = {
'href': url, 'href': url,
'name': '#' + tag, 'name': '#' + tag,

View File

@ -1503,7 +1503,6 @@ def _profile_post_bio(actor_json: {}, fields: {},
check_name_and_bio: bool) -> bool: check_name_and_bio: bool) -> bool:
""" HTTP POST change user bio """ HTTP POST change user bio
""" """
featured_tags = get_featured_hashtags(actor_json) + ' '
actor_json['tag']: list[dict] = [] actor_json['tag']: list[dict] = []
if fields.get('bio'): if fields.get('bio'):
if fields['bio'] != actor_json['summary']: if fields['bio'] != actor_json['summary']:
@ -1519,11 +1518,6 @@ def _profile_post_bio(actor_json: {}, fields: {},
domain_full, domain_full,
bio_str, [], actor_tags, bio_str, [], actor_tags,
translate) translate)
if actor_tags:
for _, tag in actor_tags.items():
if tag['name'] + ' ' in featured_tags:
continue
actor_json['tag'].append(tag)
actor_changed = True actor_changed = True
else: else:
if check_name_and_bio: if check_name_and_bio:
@ -1531,7 +1525,6 @@ def _profile_post_bio(actor_json: {}, fields: {},
else: else:
if check_name_and_bio: if check_name_and_bio:
redirect_path = '/welcome_profile' redirect_path = '/welcome_profile'
set_featured_hashtags(actor_json, featured_tags, True)
return actor_changed, redirect_path return actor_changed, redirect_path
@ -1575,22 +1568,20 @@ def _profile_post_alsoknownas(actor_json: {}, fields: {},
return actor_changed return actor_changed
def _profile_post_featured_hashtags(actor_json: {}, fields: {}, def _profile_post_featured_hashtags(base_dir: str, nickname: str, domain: str,
actor_changed: bool) -> bool: fields: {}) -> None:
""" HTTP POST featured hashtags on edit profile screen """ HTTP POST featured hashtags on edit profile screen
""" """
featured_hashtags = get_featured_hashtags(actor_json) featured_hashtags = get_featured_hashtags(base_dir, nickname, domain)
if fields.get('featuredHashtags'): if fields.get('featuredHashtags'):
fields['featuredHashtags'] = remove_html(fields['featuredHashtags']) fields['featuredHashtags'] = remove_html(fields['featuredHashtags'])
if featured_hashtags != fields['featuredHashtags']: if featured_hashtags != fields['featuredHashtags']:
set_featured_hashtags(actor_json, set_featured_hashtags(base_dir, nickname, domain,
fields['featuredHashtags']) fields['featuredHashtags'])
actor_changed = True
else: else:
if featured_hashtags: if featured_hashtags:
set_featured_hashtags(actor_json, '') set_featured_hashtags(base_dir, nickname, domain, '')
actor_changed = True return
return actor_changed
def _profile_post_occupation(actor_json: {}, fields: {}, def _profile_post_occupation(actor_json: {}, fields: {},
@ -3184,9 +3175,8 @@ def profile_edit(self, calling_domain: str, cookie: str,
_profile_post_occupation(actor_json, fields, _profile_post_occupation(actor_json, fields,
actor_changed) actor_changed)
actor_changed = \ _profile_post_featured_hashtags(base_dir, nickname, domain,
_profile_post_featured_hashtags(actor_json, fields, fields)
actor_changed)
actor_changed = \ actor_changed = \
_profile_post_alsoknownas(actor_json, fields, _profile_post_alsoknownas(actor_json, fields,

View File

@ -41,6 +41,7 @@ from src.media import process_meta_data
from src.flags import is_image_file from src.flags import is_image_file
from src.timeFunctions import date_utcnow from src.timeFunctions import date_utcnow
from src.timeFunctions import get_current_time_int from src.timeFunctions import get_current_time_int
from src.utils import resembles_url
from src.utils import get_preferred_username from src.utils import get_preferred_username
from src.utils import string_starts_with from src.utils import string_starts_with
from src.utils import is_yggdrasil_address from src.utils import is_yggdrasil_address
@ -612,6 +613,8 @@ def _create_person_base(base_dir: str, nickname: str, domain: str, port: int,
if new_person.get('roles'): if new_person.get('roles'):
del new_person['roles'] del new_person['roles']
del new_person['tag'] del new_person['tag']
del new_person['featuredCollections']
del new_person['featuredTags']
del new_person['availability'] del new_person['availability']
del new_person['followers'] del new_person['followers']
del new_person['following'] del new_person['following']
@ -2292,86 +2295,90 @@ def valid_sending_actor(session, base_dir: str,
return True return True
def get_featured_hashtags(actor_json: {}) -> str: def get_featured_hashtags(base_dir: str, nickname: str, domain: str) -> str:
"""returns a string containing featured hashtags """returns a string containing featured hashtags
""" """
account_dir: str = acct_dir(base_dir, nickname, domain)
featured_tags_filename: str = account_dir + '/featured_hashtags.txt'
if is_a_file(featured_tags_filename):
hashtags_str: str = \
load_string(featured_tags_filename,
'EX: unable to load featured hashtags ' +
featured_tags_filename)
if hashtags_str:
return hashtags_str.strip()
return ''
def get_featured_hashtags_as_html_remote(signing_priv_key_pem: str,
session, tags_url: str,
as_header: {},
http_prefix: str,
mitm_servers: [],
host_domain: str,
debug: bool) -> str:
"""returns a html string containing featured hashtags
on remote instance
"""
result: str = '' result: str = ''
if not actor_json.get('tag'):
return result
if not isinstance(actor_json['tag'], list):
return result
ctr: int = 0 ctr: int = 0
for tag_dict in actor_json['tag']: quiet: bool = True
if not tag_dict.get('type'): featured_hashtags_json = \
get_json(signing_priv_key_pem, session, tags_url, as_header,
None, debug, mitm_servers, __version__, http_prefix,
host_domain, 20, quiet)
if get_json_valid(featured_hashtags_json):
if 'items' not in featured_hashtags_json:
return ''
if not isinstance(featured_hashtags_json['items'], list):
return ''
for tag_dict in featured_hashtags_json['items']:
if 'type' not in tag_dict:
continue
if 'name' not in tag_dict:
continue
if 'href' not in tag_dict:
continue continue
if not isinstance(tag_dict['type'], str): if not isinstance(tag_dict['type'], str):
continue continue
if not tag_dict['type'].endswith('Hashtag'):
continue
if not tag_dict.get('name'):
continue
if not isinstance(tag_dict['name'], str): if not isinstance(tag_dict['name'], str):
continue continue
if not tag_dict.get('href'):
continue
if not isinstance(tag_dict['href'], str): if not isinstance(tag_dict['href'], str):
continue continue
tag_name = tag_dict['name']
if not tag_name:
continue
if tag_name.startswith('#'):
tag_name = tag_name[1:]
if not tag_name:
continue
tag_url = remove_html(tag_dict['href']) tag_url = remove_html(tag_dict['href'])
if '://' not in tag_url: if not resembles_url(tag_url):
continue continue
if not valid_hash_tag(tag_name): tag_name = remove_html(tag_dict['name'])
tag_name = tag_name.replace('#', '')
if not tag_name or not tag_url:
continue continue
result += '#' + tag_name + ' ' result += \
'<a href="' + tag_url + '" ' + \
'class="mention hashtag" rel="tag" ' + \
'tabindex="10">#' + tag_name + '</a> '
ctr += 1 ctr += 1
if ctr >= 10: if ctr >= 10:
break break
return result.strip() return result
def get_featured_hashtags_as_html(actor_json: {}, def get_featured_hashtags_as_html(base_dir: str, nickname: str, domain: str,
profile_description: str) -> str: http_prefix: str, domain_full: str) -> str:
"""returns a html string containing featured hashtags """returns a html string containing featured hashtags
""" """
hashtags_str = get_featured_hashtags(base_dir, nickname, domain)
separator = ' '
if ',' in hashtags_str:
separator = ','
hashtags_list: list[str] = hashtags_str.split(separator)
result: str = '' result: str = ''
if not actor_json.get('tag'):
return result
if not isinstance(actor_json['tag'], list):
return result
ctr: int = 0 ctr: int = 0
for tag_dict in actor_json['tag']: for tag in hashtags_list:
if not tag_dict.get('type'): tag_name = tag.strip().replace('#', '')
continue
if not isinstance(tag_dict['type'], str):
continue
if not tag_dict['type'].endswith('Hashtag'):
continue
if not tag_dict.get('name'):
continue
if not isinstance(tag_dict['name'], str):
continue
if not tag_dict.get('href'):
continue
if not isinstance(tag_dict['href'], str):
continue
tag_name = tag_dict['name']
if not tag_name: if not tag_name:
continue continue
if tag_name.startswith('#'): tag_url = http_prefix + '://' + domain_full + '/tags/' + tag_name
tag_name = tag_name[1:]
if not tag_name:
continue
if '/tags/' + tag_name + '"' in profile_description:
continue
if ' #' + tag_name in profile_description:
continue
tag_url = remove_html(tag_dict['href'])
if '://' not in tag_url: if '://' not in tag_url:
continue continue
if not valid_hash_tag(tag_name): if not valid_hash_tag(tag_name):
@ -2389,53 +2396,17 @@ def get_featured_hashtags_as_html(actor_json: {},
return result return result
def set_featured_hashtags(actor_json: {}, hashtags: str, def set_featured_hashtags(base_dir: str, nickname: str, domain: str,
append: bool = False) -> None: hashtags: str) -> None:
"""sets featured hashtags """sets featured hashtags
""" """
separator_str = ' ' account_dir: str = acct_dir(base_dir, nickname, domain)
separators = (',', ' ') if not is_a_dir(account_dir):
for separator_str in separators: return
if separator_str in hashtags: featured_tags_filename: str = account_dir + '/featured_hashtags.txt'
break save_string(hashtags, featured_tags_filename,
tag_list = hashtags.split(separator_str) 'EX: set_featured_hashtags unabel to save ' +
result: list[str] = [] featured_tags_filename)
tags_used: list[str] = []
actor_id = actor_json['id']
actor_domain = actor_id.split('://')[1]
if '/' in actor_domain:
actor_domain = actor_domain.split('/')[0]
actor_url = \
actor_id.split('://')[0] + '://' + actor_domain
for tag_str in tag_list:
if not tag_str:
continue
if not tag_str.startswith('#'):
tag_str = '#' + tag_str
if tag_str in tags_used:
continue
url = actor_url + '/tags/' + tag_str.replace('#', '')
result.append({
"name": tag_str,
"type": "Hashtag",
"href": url
})
tags_used.append(tag_str)
if len(result) >= 10:
break
# add any non-hashtags to the result
if actor_json.get('tag'):
for tag_dict in actor_json['tag']:
if not tag_dict.get('type'):
continue
if not isinstance(tag_dict['type'], str):
continue
if tag_dict['type'] != 'Hashtag':
result.append(tag_dict)
if not append:
actor_json['tag'] = result
else:
actor_json['tag'] += result
def update_memorial_flags(base_dir: str, person_cache: {}) -> None: def update_memorial_flags(base_dir: str, person_cache: {}) -> None:

View File

@ -124,8 +124,6 @@ from src.follow import add_follower_of_person
from src.follow import unfollow_account from src.follow import unfollow_account
from src.follow import unfollower_of_account from src.follow import unfollower_of_account
from src.follow import send_follow_request from src.follow import send_follow_request
from src.person import set_featured_hashtags
from src.person import get_featured_hashtags
from src.person import create_person from src.person import create_person
from src.person import create_group from src.person import create_group
from src.person import set_display_nickname from src.person import set_display_nickname
@ -8885,23 +8883,6 @@ def _test_xor_hashes():
assert result == expected assert result == expected
def _test_featured_tags() -> None:
print('featured_tags')
actor_json = {
"id": "https://somesite/users/somenick"
}
featured_tags = '#dog #cat'
set_featured_hashtags(actor_json, featured_tags)
assert actor_json.get('tag')
assert len(actor_json['tag']) == 2
result = get_featured_hashtags(actor_json)
if result != featured_tags:
pprint(actor_json)
print('result: ' + result)
print('expected: ' + featured_tags)
assert result == featured_tags
def _test_remove_tag() -> None: def _test_remove_tag() -> None:
print('remove_tag') print('remove_tag')
test_html = 'This is a test' test_html = 'This is a test'
@ -9945,7 +9926,6 @@ def run_all_tests():
_test_is_right_to_left() _test_is_right_to_left()
_test_format_mixed_rtl() _test_format_mixed_rtl()
_test_remove_tag() _test_remove_tag()
_test_featured_tags()
_test_xor_hashes() _test_xor_hashes()
_test_convert_markdown() _test_convert_markdown()
_test_remove_style() _test_remove_style()

View File

@ -61,6 +61,7 @@ from src.languages import get_actor_languages
from src.skills import get_skills from src.skills import get_skills
from src.theme import get_themes_list from src.theme import get_themes_list
from src.person import get_featured_hashtags_as_html from src.person import get_featured_hashtags_as_html
from src.person import get_featured_hashtags_as_html_remote
from src.person import get_featured_hashtags from src.person import get_featured_hashtags
from src.person import person_box_json from src.person import person_box_json
from src.person import get_actor_json from src.person import get_actor_json
@ -444,8 +445,27 @@ def html_profile_after_search(authorized: bool,
add_emoji_to_display_name(session, base_dir, http_prefix, add_emoji_to_display_name(session, base_dir, http_prefix,
nickname, domain, nickname, domain,
profile_description, False, translate) profile_description, False, translate)
featured_hashtags: str = \ domain_full: str = get_full_domain(domain, port)
get_featured_hashtags_as_html(profile_json, profile_description) featured_hashtags: str = ''
if profile_json.get('featuredTags'):
if isinstance(profile_json['featuredTags'], str):
if search_domain == domain:
# account on this instance
featured_hashtags = \
get_featured_hashtags_as_html(base_dir,
search_nickname, domain,
http_prefix, domain_full)
else:
# account on remote instance
tags_url: str = profile_json['featuredTags']
featured_hashtags = \
get_featured_hashtags_as_html_remote(signing_priv_key_pem,
session, tags_url,
as_header,
http_prefix,
mitm_servers,
domain,
debug)
outbox_url: str = None outbox_url: str = None
if not profile_json.get('outbox'): if not profile_json.get('outbox'):
if debug: if debug:
@ -550,8 +570,6 @@ def html_profile_after_search(authorized: bool,
art_site_url, art_site_url,
donate_url) donate_url)
domain_full: str = get_full_domain(domain, port)
follow_is_permitted: bool = True follow_is_permitted: bool = True
if not profile_json.get('followers'): if not profile_json.get('followers'):
# no followers collection specified within actor # no followers collection specified within actor
@ -1302,7 +1320,8 @@ def html_profile(signing_priv_key_pem: str,
if profile_description: if profile_description:
profile_description = standardize_text(profile_description) profile_description = standardize_text(profile_description)
featured_hashtags: str = \ featured_hashtags: str = \
get_featured_hashtags_as_html(profile_json, profile_description) get_featured_hashtags_as_html(base_dir, nickname, domain,
http_prefix, domain_full)
posts_button: str = 'button' posts_button: str = 'button'
following_button: str = 'button' following_button: str = 'button'
moved_button: str = 'button' moved_button: str = 'button'
@ -3717,7 +3736,7 @@ def html_edit_profile(server, translate: {},
elif actor_json.get('copiedTo'): elif actor_json.get('copiedTo'):
if isinstance(actor_json['copiedTo'], str): if isinstance(actor_json['copiedTo'], str):
moved_to = remove_html(actor_json['copiedTo']) moved_to = remove_html(actor_json['copiedTo'])
featured_hashtags = get_featured_hashtags(actor_json) featured_hashtags = get_featured_hashtags(base_dir, nickname, domain)
donate_url = get_donation_url(actor_json) donate_url = get_donation_url(actor_json)
website_url = get_website(actor_json, translate) website_url = get_website(actor_json, translate)
gemini_link = get_gemini_link(actor_json) gemini_link = get_gemini_link(actor_json)