From 91c72137549c0e5c0a4fa03b9a7cb6c376affd44 Mon Sep 17 00:00:00 2001 From: Cameron Pfiffer Date: Wed, 4 Mar 2026 13:00:07 -0800 Subject: [PATCH] feat: implement full pagination for sync command to retrieve all unread notifications --- src/void_tools/cli.py | 98 +++++++++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/src/void_tools/cli.py b/src/void_tools/cli.py index 7212ef3..a9f433d 100644 --- a/src/void_tools/cli.py +++ b/src/void_tools/cli.py @@ -88,24 +88,39 @@ def cmd_sync(args): memfs_users_dir = os.path.expanduser(f"~/.letta/agents/{agent_id}/memory/users") try: - response = client.app.bsky.notification.list_notifications({'limit': args.limit}) - inbox = [] mapping = {} + cursor = None + stop_pagination = False - for i, notif in enumerate(response.notifications): - if args.unread_only and notif.is_read: - continue + while not stop_pagination: + params = {'limit': min(50, args.limit) if not args.unread_only else 50} + if cursor: + params['cursor'] = cursor - local_id = f"notif_{i}" - handle = notif.author.handle - item = { - "id": local_id, - "type": notif.reason, - "author": f"@{handle}", - } - if hasattr(notif.record, 'text'): - item["text"] = notif.record.text + response = client.app.bsky.notification.list_notifications(params) + + if not response.notifications: + break + + for notif in response.notifications: + if args.unread_only and notif.is_read: + stop_pagination = True + break + + if not args.unread_only and len(inbox) >= args.limit: + stop_pagination = True + break + + local_id = f"notif_{len(inbox)}" + handle = notif.author.handle + item = { + "id": local_id, + "type": notif.reason, + "author": f"@{handle}", + } + if hasattr(notif.record, 'text'): + item["text"] = notif.record.text # Attempt to load memory block safe_handle = handle.replace(".", "_").replace("-", "_") @@ -126,35 +141,36 @@ def cmd_sync(args): else: item["memory_block"] = "No prior records found." - # Fetch thread context for replies, quotes, or mentions - if notif.reason in ['reply', 'quote', 'mention']: - try: - # Depending on the notification type, we might need to look up the thread from the subject or the uri - thread_uri = notif.uri - if notif.reason == 'quote' and hasattr(notif.record, 'embed') and hasattr(notif.record.embed, 'record'): - # If we want the context of the quote, the uri is still the post itself, the parent trace handles the quote subject usually - pass + # Fetch thread context for replies, quotes, or mentions + if notif.reason in ['reply', 'quote', 'mention']: + try: + thread_uri = notif.uri + if notif.reason == 'quote' and hasattr(notif.record, 'embed') and hasattr(notif.record.embed, 'record'): + pass + + thread_resp = client.app.bsky.feed.get_post_thread({'uri': thread_uri, 'depth': 0, 'parent_height': 5}) - thread_resp = client.app.bsky.feed.get_post_thread({'uri': thread_uri, 'depth': 0, 'parent_height': 5}) - - context = [] - # Traverse parents recursively - curr = thread_resp.thread - while hasattr(curr, 'parent') and curr.parent: - curr = curr.parent - if hasattr(curr, 'post') and hasattr(curr.post, 'record') and hasattr(curr.post.record, 'text'): - context.insert(0, { - "author": f"@{curr.post.author.handle}", - "text": curr.post.record.text - }) - - if context: - item["thread_context"] = context - except Exception as e: - item["thread_context"] = f"Failed to fetch context: {str(e)}" + context = [] + curr = thread_resp.thread + while hasattr(curr, 'parent') and curr.parent: + curr = curr.parent + if hasattr(curr, 'post') and hasattr(curr.post, 'record') and hasattr(curr.post.record, 'text'): + context.insert(0, { + "author": f"@{curr.post.author.handle}", + "text": curr.post.record.text + }) + + if context: + item["thread_context"] = context + except Exception as e: + item["thread_context"] = f"Failed to fetch context: {str(e)}" + + inbox.append(item) + mapping[local_id] = notif.uri - inbox.append(item) - mapping[local_id] = notif.uri + if stop_pagination or not response.cursor: + break + cursor = response.cursor with open("inbox.yaml", "w") as f: yaml.dump(inbox, f, sort_keys=False, default_flow_style=False) -- 2.51.2