retronews

a featureful fork of the luke8086/retronews hn+lobste.rs tui
Log | Files | Refs | README | LICENSE

commit 3adbc85efe02dcf4de673e175f6b9d8b80e7fdd7
parent ea8e54670bb7452aeb8161a5e61c3524b195a3d3
Author: luke8086 <55237178+luke8086@users.noreply.github.com>
Date:   Thu, 28 Jul 2022 19:56:46 +0000

Add virtual group for starred threads

Diffstat:
Mretronews.py | 69++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
1 file changed, 54 insertions(+), 15 deletions(-)

diff --git a/retronews.py b/retronews.py @@ -116,6 +116,7 @@ GROUP_TABS: list[Group] = [ Group(provider="hn-new", name="", label="New"), Group(provider="hn", name="ask", label="Ask HN"), Group(provider="hn", name="show", label="Show HN"), + Group(provider="starred", name="", label="Starred"), ] @@ -447,6 +448,21 @@ def db_load_read_comments(db: sqlite3.Connection, messages_by_id: dict[str, Mess threads_by_id[row["thread_id"]].read_comments = row["count"] +def db_load_starred_thread_ids(db: sqlite3.Connection, page: int = 1) -> list[str]: + page_size = 30 + offset = (page - 1) * page_size + sql = """ + SELECT id + FROM messages + WHERE id = thread_id AND JSON_EXTRACT(flags, '$.starred') + ORDER BY id + LIMIT ? + OFFSET ? + """ + + return [row["id"] for row in db.execute(sql, (page_size, offset))] + + def app_safe_run(app: AppState, fn: Callable[[], T], flash: Optional[str]) -> Optional[T]: if flash is not None: app_show_flash(app, flash) @@ -523,7 +539,7 @@ def app_load_messages( def app_fetch_threads(app: AppState) -> None: - fn = partial(group_search_threads, app.group) + fn = partial(group_search_threads, app.group, app.db) flash = f"Fetching stories from '{app.group.label}' (page {app.group.page})..." if (messages := app_safe_run(app, fn, flash=flash)) is not None: @@ -823,20 +839,24 @@ def hn_parse_entry(entry: HNEntry, thread_id: str = "", parent_title: str = "") ) +def hn_search_threads_by_id(thread_ids: list[str]) -> list[Message]: + story_tags = ",".join(f"story_{x}" for x in thread_ids) + url = f"https://hn.algolia.com/api/v1/search_by_date?hitsPerPage={len(thread_ids)}&tags=story,({story_tags})" + hits = json.loads(fetch(url))["hits"] + + return [hn_parse_search_hit(hit) for hit in hits] + + def hn_search_threads(group: str = "news", page: int = 1) -> list[Message]: rex = re.compile(r'href="item\?id=(\d+)"') html = fetch(f"https://news.ycombinator.com/{group}?p={page}") - thread_ids = set(match.group(1) for match in rex.finditer(html)) + thread_ids = list(set(match.group(1) for match in rex.finditer(html))) - story_tags = ",".join(f"story_{x}" for x in thread_ids) - url = f"https://hn.algolia.com/api/v1/search_by_date?hitsPerPage=200&tags=story,({story_tags})" - hits = json.loads(fetch(url))["hits"] - - return [hn_parse_search_hit(hit) for hit in hits] + return hn_search_threads_by_id(thread_ids) -def hn_search_new_threads(_: str, page: int = 1) -> list[Message]: +def hn_search_new_threads(page: int = 1) -> list[Message]: url = f"https://hn.algolia.com/api/v1/search_by_date?tags=story&hitsPerPage=30&page={page}" hits = json.loads(fetch(url))["hits"] @@ -853,13 +873,32 @@ def group_advance_page(group: Group, offset: int = 1) -> Group: return dataclasses.replace(group, page=max(1, group.page + offset)) -def group_search_threads(group: Group) -> list[Message]: - searchers: dict[str, Callable[[str, int], list[Message]]] = { - "hn": hn_search_threads, - "hn-new": hn_search_new_threads, - } - searcher = searchers[group.provider] - return searcher(group.name, group.page) +def group_search_starred_threads(db: sqlite3.Connection, page: int = 1) -> list[Message]: + thread_ids = db_load_starred_thread_ids(db, page) + threads_by_provider: dict[str, list[str]] = {} + threads = [] + + for (source_id, provider) in (t.split("@") for t in thread_ids): + threads_by_provider.setdefault(provider, list()).append(source_id) + + for provider, thread_ids in threads_by_provider.items(): + if provider == "hn": + threads += hn_search_threads_by_id(thread_ids) + + threads.sort(key=lambda x: x.date, reverse=True) + + return threads + + +def group_search_threads(group: Group, db: sqlite3.Connection) -> list[Message]: + if group.provider == "hn": + return hn_search_threads(group.name, group.page) + elif group.provider == "hn-new": + return hn_search_new_threads(group.page) + elif group.provider == "starred": + return group_search_starred_threads(db, group.page) + else: + return [] def group_fetch_thread(thread_id: str) -> Message: