From 1d6a76987dafa0aa7efe2f5347e1c6f3a1198381 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 2 Dec 2023 18:24:53 +0000 Subject: [PATCH] Proof of concept using Link response headers for pagination. --- client.py | 21 ++++++++++++++++++++- mastodonochrome | 8 +++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/client.py b/client.py index e7640f1..e83f42f 100644 --- a/client.py +++ b/client.py @@ -1,5 +1,6 @@ import calendar import os +import re import requests import string import time @@ -26,6 +27,7 @@ class Client: def set_instance(self, instance): self.instance = instance self.urls = { + None: '', 'auth': "https://" + instance + "/oauth/", 'api': "https://" + instance + "/api/v1/", } @@ -53,7 +55,7 @@ class Client: self.log_response = log_response - def method(self, method, path, base, params): + def method(self, method, path, base, params, links={}): headers = {} if self.bearer_token is not None: headers['Authorization'] = 'Bearer ' + self.bearer_token @@ -61,6 +63,13 @@ class Client: self.log_response(rsp) if rsp.status_code != 200: raise HTTPError(rsp) + linkhdr = rsp.headers.get('Link', '') + while len(linkhdr) > 0: + m = re.match(r'<([^>]+)>\s*;\s*rel="([^"]+)"(?:,\s*)?', linkhdr) + if m is None: + break + links[m.group(2)] = m.group(1) + linkhdr = linkhdr[m.end():] return rsp.json() def get(self, path, base='api', **params): @@ -68,6 +77,16 @@ class Client: def post(self, path, base='api', **params): return self.method(requests.post, path, base, params) + def get_incremental(self, path, base='api', **params): + params.setdefault('limit', 32) + while True: + links = {} + data = self.method(requests.get, path, base, params, links) + yield from data + if 'next' not in links: + break + base, path = None, links['next'] + def get_url(self, path, base='api', **params): r = requests.Request(method="GET", url=self.urls[base] + path, params=params) diff --git a/mastodonochrome b/mastodonochrome index 13048e7..823681d 100755 --- a/mastodonochrome +++ b/mastodonochrome @@ -22,10 +22,16 @@ class TimelineUI(client.Client): class MentionsUI(client.Client): def run(self): - for item in reversed(self.get("notifications", limit=40)): + g = self.get_incremental("notifications") + things = [] + for item in g: if item['type'] != 'mention': continue p = client.Status(item['status']) + things.append(p) + if len(things) >= 40: + break + for p in reversed(things): for thing in p.text(): for line in thing.render(80): print(line.ecma48()) -- 2.30.2