diff --git a/daemon.py b/daemon.py index caa4bffe1..a0f1ad9e2 100644 --- a/daemon.py +++ b/daemon.py @@ -416,6 +416,7 @@ from qrcode import save_domain_qrcode from importFollowing import run_import_following_watchdog from maps import map_format_from_tagmaps_path from relationships import get_moved_feed +from relationships import get_inactive_feed from relationships import update_moved_actors import os @@ -14529,6 +14530,146 @@ class PubServer(BaseHTTPRequestHandler): return True return False + def _show_inactive_feed(self, authorized: bool, + calling_domain: str, referer_domain: str, + path: str, base_dir: str, http_prefix: str, + domain: str, port: int, getreq_start_time, + proxy_type: str, cookie: str, + debug: str, curr_session, + dormant_months: int) -> bool: + """Shows the inactive accounts feed + """ + following = \ + get_inactive_feed(base_dir, domain, port, path, + http_prefix, authorized, + dormant_months, + FOLLOWS_PER_PAGE) + if following: + if self._request_http(): + page_number = 1 + if '?page=' not in path: + search_path = path + # get a page of following, not the summary + following = \ + get_inactive_feed(base_dir, domain, port, path, + http_prefix, authorized, + dormant_months, + FOLLOWS_PER_PAGE) + else: + page_number_str = path.split('?page=')[1] + if ';' in page_number_str: + page_number_str = page_number_str.split(';')[0] + if '#' in page_number_str: + page_number_str = page_number_str.split('#')[0] + if len(page_number_str) > 5: + page_number_str = "1" + if page_number_str.isdigit(): + page_number = int(page_number_str) + search_path = path.split('?page=')[0] + get_person = \ + person_lookup(domain, + search_path.replace('/inactive', ''), + base_dir) + if get_person: + curr_session = \ + self._establish_session("show_inactive_feed", + curr_session, proxy_type) + if not curr_session: + self._404() + return True + + access_keys = self.server.access_keys + city = None + timezone = None + if '/users/' in path: + nickname = path.split('/users/')[1] + if '/' in nickname: + nickname = nickname.split('/')[0] + if self.server.key_shortcuts.get(nickname): + access_keys = \ + self.server.key_shortcuts[nickname] + + city = get_spoofed_city(self.server.city, + base_dir, nickname, domain) + if self.server.account_timezone.get(nickname): + timezone = \ + self.server.account_timezone.get(nickname) + content_license_url = \ + self.server.content_license_url + shared_items_federated_domains = \ + self.server.shared_items_federated_domains + bold_reading = False + if self.server.bold_reading.get(nickname): + bold_reading = True + msg = \ + html_profile(self.server.signing_priv_key_pem, + self.server.rss_icon_at_top, + self.server.icons_as_buttons, + self.server.default_timeline, + self.server.recent_posts_cache, + self.server.max_recent_posts, + self.server.translate, + self.server.project_version, + base_dir, http_prefix, + authorized, + get_person, 'inactive', + curr_session, + self.server.cached_webfingers, + self.server.person_cache, + self.server.yt_replace_domain, + self.server.twitter_replacement_domain, + self.server.show_published_date_only, + self.server.newswire, + self.server.theme_name, + self.server.dormant_months, + self.server.peertube_instances, + self.server.allow_local_network_access, + self.server.text_mode_banner, + self.server.debug, + access_keys, city, + self.server.system_language, + self.server.max_like_count, + shared_items_federated_domains, + following, + page_number, + FOLLOWS_PER_PAGE, + self.server.cw_lists, + self.server.lists_enabled, + content_license_url, + timezone, bold_reading).encode('utf-8') + msglen = len(msg) + self._set_headers('text/html', + msglen, cookie, calling_domain, False) + self._write(msg) + fitness_performance(getreq_start_time, + self.server.fitness, + '_GET', '_show_inactive_feed', + debug) + return True + else: + if self._secure_mode(curr_session, proxy_type): + msg_str = json.dumps(following, + ensure_ascii=False) + msg_str = self._convert_domains(calling_domain, + referer_domain, + msg_str) + msg = msg_str.encode('utf-8') + msglen = len(msg) + accept_str = self.headers['Accept'] + protocol_str = \ + get_json_content_from_accept(accept_str) + self._set_headers(protocol_str, msglen, + None, calling_domain, False) + self._write(msg) + fitness_performance(getreq_start_time, + self.server.fitness, + '_GET', '_show_inactive_feed json', + debug) + else: + self._404() + return True + return False + def _show_followers_feed(self, authorized: bool, calling_domain: str, referer_domain: str, path: str, base_dir: str, http_prefix: str, @@ -19331,6 +19472,25 @@ class PubServer(BaseHTTPRequestHandler): '_GET', 'show moved 4 done', self.server.debug) + if self._show_inactive_feed(authorized, + calling_domain, referer_domain, + self.path, + self.server.base_dir, + self.server.http_prefix, + self.server.domain, + self.server.port, + getreq_start_time, + proxy_type, + cookie, self.server.debug, + curr_session, + self.server.dormant_months): + self.server.getreq_busy = False + return + + fitness_performance(getreq_start_time, self.server.fitness, + '_GET', 'show inactive 5 done', + self.server.debug) + if self._show_followers_feed(authorized, calling_domain, referer_domain, self.path, diff --git a/relationships.py b/relationships.py index 3e86b26c2..f41b79453 100644 --- a/relationships.py +++ b/relationships.py @@ -8,6 +8,8 @@ __status__ = "Production" __module_group__ = "Core" import os +from utils import get_user_paths +from utils import is_dormant from utils import acct_dir from utils import valid_nickname from utils import get_full_domain @@ -290,3 +292,168 @@ def update_moved_actors(base_dir: str, debug: bool) -> None: except OSError: print('EX: update_moved_actors unable to save ' + moved_accounts_filename) + + +def _get_inactive_accounts(base_dir: str, nickname: str, domain: str, + dormant_months: int) -> []: + """returns a list of inactive accounts + """ + # get the list of followers + followers_filename = \ + acct_dir(base_dir, nickname, domain) + '/followers.txt' + followers_str = '' + try: + with open(followers_filename, 'r', + encoding='utf-8') as fp_follow: + followers_str = fp_follow.read() + except OSError: + print('EX: get_moved_accounts unable to read ' + + followers_filename) + followers_list = followers_str.split('\n') + + result = [] + users_list = get_user_paths() + for handle in followers_list: + if handle in result: + continue + if '@' in handle: + follower_nickname = handle.split('@')[0] + follower_domain = handle.split('@')[1] + found = False + for http_prefix in ('https://', 'http://'): + for users_str in users_list: + actor = \ + http_prefix + follower_domain + users_str + \ + follower_nickname + if is_dormant(base_dir, nickname, domain, actor, + dormant_months): + result.append(handle) + found = True + break + if found: + break + elif '://' in handle: + actor = handle + if is_dormant(base_dir, nickname, domain, actor, + dormant_months): + result.append(handle) + return result + + +def get_inactive_feed(base_dir: str, domain: str, port: int, path: str, + http_prefix: str, authorized: bool, + dormant_months: int, + follows_per_page=12) -> {}: + """Returns the inactive accounts feed from GET requests. + """ + # Don't show inactive accounts to non-authorized viewers + if not authorized: + follows_per_page = 0 + + if '/inactive' not in path: + return None + if '?page=' not in path: + path = path.replace('/inactive', '/inactive?page=true') + # handle page numbers + header_only = True + page_number = None + if '?page=' in path: + page_number = path.split('?page=')[1] + if len(page_number) > 5: + page_number = "1" + if page_number == 'true' or not authorized: + page_number = 1 + else: + try: + page_number = int(page_number) + except BaseException: + print('EX: get_inactive_feed unable to convert to int ' + + str(page_number)) + path = path.split('?page=')[0] + header_only = False + + if not path.endswith('/inactive'): + return None + nickname = None + if path.startswith('/users/'): + nickname = \ + path.replace('/users/', '', 1).replace('/inactive', '') + if path.startswith('/@'): + nickname = path.replace('/@', '', 1).replace('/inactive', '') + if not nickname: + return None + if not valid_nickname(domain, nickname): + return None + + domain = get_full_domain(domain, port) + + lines = _get_inactive_accounts(base_dir, nickname, domain, + dormant_months) + + if header_only: + first_str = \ + local_actor_url(http_prefix, nickname, domain) + \ + '/moved?page=1' + id_str = \ + local_actor_url(http_prefix, nickname, domain) + '/inactive' + total_str = str(len(lines.items())) + following = { + '@context': 'https://www.w3.org/ns/activitystreams', + 'first': first_str, + 'id': id_str, + 'orderedItems': [], + 'totalItems': total_str, + 'type': 'OrderedCollection' + } + return following + + if not page_number: + page_number = 1 + + next_page_number = int(page_number + 1) + id_str = \ + local_actor_url(http_prefix, nickname, domain) + \ + '/inactive?page=' + str(page_number) + part_of_str = \ + local_actor_url(http_prefix, nickname, domain) + '/inactive' + following = { + '@context': 'https://www.w3.org/ns/activitystreams', + 'id': id_str, + 'orderedItems': [], + 'partOf': part_of_str, + 'totalItems': 0, + 'type': 'OrderedCollectionPage' + } + + handle_domain = domain + handle_domain = remove_domain_port(handle_domain) + curr_page = 1 + page_ctr = 0 + total_ctr = 0 + for handle, new_handle in lines.items(): + # nickname@domain + page_ctr += 1 + total_ctr += 1 + if curr_page == page_number: + line2_lower = handle.lower() + line2 = remove_eol(line2_lower) + nick = line2.split('@')[0] + dom = line2.split('@')[1] + if not nick.startswith('!'): + # person actor + url = local_actor_url(http_prefix, nick, dom) + else: + # group actor + url = http_prefix + '://' + dom + '/c/' + nick + following['orderedItems'].append(url) + if page_ctr >= follows_per_page: + page_ctr = 0 + curr_page += 1 + following['totalItems'] = total_ctr + last_page = int(total_ctr / follows_per_page) + last_page = max(last_page, 1) + if next_page_number > last_page: + following['next'] = \ + local_actor_url(http_prefix, nickname, domain) + \ + '/inactive?page=' + str(last_page) + return following diff --git a/translations/ar.json b/translations/ar.json index aebe2413b..7fdaffb60 100644 --- a/translations/ar.json +++ b/translations/ar.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "معاينة المشاركات على شاشة الملف الشخصي", "Reverse timelines": "عكس الجداول الزمنية", "Moved": "انتقل", - "Move": "يتحرك" + "Move": "يتحرك", + "Inactive": "غير نشط" } diff --git a/translations/bn.json b/translations/bn.json index 7354466bd..17e35a662 100644 --- a/translations/bn.json +++ b/translations/bn.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "প্রোফাইল স্ক্রিনে পোস্টের পূর্বরূপ দেখুন", "Reverse timelines": "বিপরীত সময়রেখা", "Moved": "সরানো হয়েছে", - "Move": "সরান" + "Move": "সরান", + "Inactive": "নিষ্ক্রিয়" } diff --git a/translations/ca.json b/translations/ca.json index a93d01f78..0ea9bf927 100644 --- a/translations/ca.json +++ b/translations/ca.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Previsualitza les publicacions a la pantalla del perfil", "Reverse timelines": "Cronologia inversa", "Moved": "Mogut", - "Move": "Moure's" + "Move": "Moure's", + "Inactive": "Inactiu" } diff --git a/translations/cy.json b/translations/cy.json index 127cc8b11..2e9e04e3c 100644 --- a/translations/cy.json +++ b/translations/cy.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Rhagolwg postiadau ar sgrin proffil", "Reverse timelines": "Gwrthdroi llinellau amser", "Moved": "Wedi symud", - "Move": "Symud" + "Move": "Symud", + "Inactive": "Anactif" } diff --git a/translations/de.json b/translations/de.json index dfdba1016..5c7b7edad 100644 --- a/translations/de.json +++ b/translations/de.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Vorschau von Beiträgen auf dem Profilbildschirm", "Reverse timelines": "Umgekehrte Zeitlinien", "Moved": "Gerührt", - "Move": "Bewegen" + "Move": "Bewegen", + "Inactive": "Inaktiv" } diff --git a/translations/el.json b/translations/el.json index ed3a0a7af..e461d7db2 100644 --- a/translations/el.json +++ b/translations/el.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Προεπισκόπηση αναρτήσεων στην οθόνη προφίλ", "Reverse timelines": "Αντίστροφα χρονοδιαγράμματα", "Moved": "Μετακινήθηκε", - "Move": "Κίνηση" + "Move": "Κίνηση", + "Inactive": "Αδρανής" } diff --git a/translations/en.json b/translations/en.json index a3a3bf8b3..eced2c68a 100644 --- a/translations/en.json +++ b/translations/en.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Preview posts on profile screen", "Reverse timelines": "Reverse timelines", "Moved": "Moved", - "Move": "Move" + "Move": "Move", + "Inactive": "Inactive" } diff --git a/translations/es.json b/translations/es.json index c7b75bcf2..67e1d8494 100644 --- a/translations/es.json +++ b/translations/es.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Vista previa de publicaciones en la pantalla de perfil", "Reverse timelines": "Líneas de tiempo inversas", "Moved": "Movida", - "Move": "Muevete" + "Move": "Muevete", + "Inactive": "Αδρανής" } diff --git a/translations/fa.json b/translations/fa.json index 469a984b6..cee03a88d 100644 --- a/translations/fa.json +++ b/translations/fa.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "پیش نمایش پست ها در صفحه نمایه", "Reverse timelines": "جدول های زمانی معکوس", "Moved": "منتقل شد", - "Move": "حرکت" + "Move": "حرکت", + "Inactive": "غیر فعال" } diff --git a/translations/fr.json b/translations/fr.json index a17d18394..fd4685774 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Prévisualiser les messages sur l'écran de profil", "Reverse timelines": "Chronologies inversées", "Moved": "Déplacée", - "Move": "Déplacer" + "Move": "Déplacer", + "Inactive": "Inactive" } diff --git a/translations/ga.json b/translations/ga.json index 19e0b43f6..e6d63cfbc 100644 --- a/translations/ga.json +++ b/translations/ga.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Réamhamhairc postálacha ar an scáileán próifíle", "Reverse timelines": "Amlínte droim ar ais", "Moved": "Ar athraíodh a ionad", - "Move": "Bog" + "Move": "Bog", + "Inactive": "Neamhghníomhach" } diff --git a/translations/hi.json b/translations/hi.json index ba02685dd..053b98cbd 100644 --- a/translations/hi.json +++ b/translations/hi.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "प्रोफ़ाइल स्क्रीन पर पोस्ट का पूर्वावलोकन करें", "Reverse timelines": "रिवर्स टाइमलाइन", "Moved": "ले जाया गया", - "Move": "कदम" + "Move": "कदम", + "Inactive": "निष्क्रिय" } diff --git a/translations/it.json b/translations/it.json index f7f8cda26..037f6ba3c 100644 --- a/translations/it.json +++ b/translations/it.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Visualizza l'anteprima dei post nella schermata del profilo", "Reverse timelines": "Invertire le tempistiche", "Moved": "Mosso", - "Move": "Spostare" + "Move": "Spostare", + "Inactive": "Non attivo" } diff --git a/translations/ja.json b/translations/ja.json index ea6e57eea..7e6c24107 100644 --- a/translations/ja.json +++ b/translations/ja.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "プロフィール画面で投稿をプレビュー", "Reverse timelines": "逆タイムライン", "Moved": "移動しました", - "Move": "動く" + "Move": "動く", + "Inactive": "非活性" } diff --git a/translations/ko.json b/translations/ko.json index 25f478338..9c1009e68 100644 --- a/translations/ko.json +++ b/translations/ko.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "프로필 화면에서 게시물 미리보기", "Reverse timelines": "역방향 타임라인", "Moved": "움직이는", - "Move": "이동하다" + "Move": "이동하다", + "Inactive": "비활성" } diff --git a/translations/ku.json b/translations/ku.json index 5b54daa6b..e7d793c11 100644 --- a/translations/ku.json +++ b/translations/ku.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Mesajên li ser ekrana profîlê pêşdîtin", "Reverse timelines": "Reverse timelines", "Moved": "Moved", - "Move": "Barkirin" + "Move": "Barkirin", + "Inactive": "Bêkar" } diff --git a/translations/nl.json b/translations/nl.json index b37327e50..b9395c1b6 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Bekijk berichten op het profielscherm", "Reverse timelines": "Omgekeerde tijdlijnen", "Moved": "Verhuisd", - "Move": "Beweging" + "Move": "Beweging", + "Inactive": "Inactief" } diff --git a/translations/oc.json b/translations/oc.json index 6d501052f..80932cde5 100644 --- a/translations/oc.json +++ b/translations/oc.json @@ -600,5 +600,6 @@ "Preview posts on profile screen": "Preview posts on profile screen", "Reverse timelines": "Reverse timelines", "Moved": "Moved", - "Move": "Move" + "Move": "Move", + "Inactive": "Inactive" } diff --git a/translations/pl.json b/translations/pl.json index 44ecbbc80..6f90633c6 100644 --- a/translations/pl.json +++ b/translations/pl.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Podgląd postów na ekranie profilu", "Reverse timelines": "Odwróć ramy czasowe", "Moved": "Przeniósł", - "Move": "Przenosić" + "Move": "Przenosić", + "Inactive": "Nieaktywny" } diff --git a/translations/pt.json b/translations/pt.json index a7ea17e39..e0c19ea9a 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Visualizar postagens na tela do perfil", "Reverse timelines": "Cronogramas reversos", "Moved": "Mudou-se", - "Move": "Jogada" + "Move": "Jogada", + "Inactive": "Inativa" } diff --git a/translations/ru.json b/translations/ru.json index 3536c1dbb..470d07b7f 100644 --- a/translations/ru.json +++ b/translations/ru.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Предварительный просмотр сообщений на экране профиля", "Reverse timelines": "Обратные сроки", "Moved": "Взолнованный", - "Move": "Шаг" + "Move": "Шаг", + "Inactive": "Неактивный" } diff --git a/translations/sw.json b/translations/sw.json index 1670892db..47a64cf34 100644 --- a/translations/sw.json +++ b/translations/sw.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Hakiki machapisho kwenye skrini ya wasifu", "Reverse timelines": "Обратные сроки", "Moved": "Imehamishwa", - "Move": "Sogeza" + "Move": "Sogeza", + "Inactive": "Isiyotumika" } diff --git a/translations/tr.json b/translations/tr.json index 63bc3d975..6ddeffb9b 100644 --- a/translations/tr.json +++ b/translations/tr.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Gönderileri profil ekranında önizleyin", "Reverse timelines": "Обратные сроки", "Moved": "Etkilenmiş", - "Move": "Hareket" + "Move": "Hareket", + "Inactive": "etkin değil" } diff --git a/translations/uk.json b/translations/uk.json index 361cec1c6..cb3478733 100644 --- a/translations/uk.json +++ b/translations/uk.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "Попередній перегляд дописів на екрані профілю", "Reverse timelines": "Обратные сроки", "Moved": "Переїхав", - "Move": "рухатися" + "Move": "рухатися", + "Inactive": "Неактивний" } diff --git a/translations/yi.json b/translations/yi.json index 72ef11def..ab2ddb7cd 100644 --- a/translations/yi.json +++ b/translations/yi.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "פאָרויסיקע ווייַזונג אַרטיקלען אויף פּראָפיל פאַרשטעלן", "Reverse timelines": "פאַרקערט טיימליינז", "Moved": "אריבערגעפארן", - "Move": "מאַך" + "Move": "מאַך", + "Inactive": "ינאַקטיוו" } diff --git a/translations/zh.json b/translations/zh.json index 29c62ee29..672144b6e 100644 --- a/translations/zh.json +++ b/translations/zh.json @@ -604,5 +604,6 @@ "Preview posts on profile screen": "在个人资料屏幕上预览帖子", "Reverse timelines": "倒转时间线", "Moved": "אריבערגעפארן", - "Move": "移动" + "Move": "移动", + "Inactive": "无效" } diff --git a/utils.py b/utils.py index 496da8ea9..8eb0eb085 100644 --- a/utils.py +++ b/utils.py @@ -2240,7 +2240,8 @@ def _get_reserved_words() -> str: 'minimal', 'search', 'eventdelete', 'searchemoji', 'catalog', 'conversationId', 'mention', 'http', 'https', 'ipfs', 'ipns', - 'ontologies', 'data', 'postedit', 'moved') + 'ontologies', 'data', 'postedit', 'moved', + 'inactive') def get_nickname_validation_pattern() -> str: diff --git a/webapp_profile.py b/webapp_profile.py index be13f3cab..bfcac1048 100644 --- a/webapp_profile.py +++ b/webapp_profile.py @@ -688,6 +688,7 @@ def html_profile(signing_priv_key_pem: str, following_button = 'button' moved_button = 'button' moved_button = 'button' + inactive_button = 'button' followers_button = 'button' roles_button = 'button' skills_button = 'button' @@ -699,6 +700,8 @@ def html_profile(signing_priv_key_pem: str, following_button = 'buttonselected' elif selected == 'moved': moved_button = 'buttonselected' + elif selected == 'inactive': + inactive_button = 'buttonselected' elif selected == 'followers': followers_button = 'buttonselected' elif selected == 'roles': @@ -993,6 +996,8 @@ def html_profile(signing_priv_key_pem: str, if show_moved_accounts: menu_moved = \ html_hide_from_screen_reader('⌂') + ' ' + translate['Moved'] + menu_inactive = \ + html_hide_from_screen_reader('💤') + ' ' + translate['Inactive'] menu_logout = \ html_hide_from_screen_reader('❎') + ' ' + translate['Logout'] if not show_moved_accounts: @@ -1008,6 +1013,7 @@ def html_profile(signing_priv_key_pem: str, menu_edit: user_path_str + '/editprofile', menu_followers: user_path_str + '/followers#timeline', menu_moved: user_path_str + '/moved#timeline', + menu_inactive: user_path_str + '/inactive#timeline', menu_logout: '/logout' } if not is_group: @@ -1053,6 +1059,11 @@ def html_profile(signing_priv_key_pem: str, '/followers#buttonheader" tabindex="2">' + \ '' + profile_str += \ + ' ' + \ + '' if not is_group: if show_moved_accounts: profile_str += \ @@ -1161,6 +1172,17 @@ def html_profile(signing_priv_key_pem: str, selected, users_path, page_number, max_items_per_page, dormant_months, debug, signing_priv_key_pem) + if authorized and selected == 'inactive': + profile_str += \ + _html_profile_following(translate, base_dir, http_prefix, + authorized, nickname, + domain, session, + cached_webfingers, + person_cache, extra_json, + project_version, ["block"], + selected, users_path, page_number, + max_items_per_page, dormant_months, debug, + signing_priv_key_pem) if not is_group: if selected == 'roles': profile_str += \