From 36ec373e838291a1cb48a608339255c318812b48 Mon Sep 17 00:00:00 2001 From: cadenshokat Date: Tue, 13 May 2025 00:05:45 -0400 Subject: [PATCH] Add List members functionality. Changes to: api.py cli.py test_api.py --- tests/test_api.py | 1 + twscrape/api.py | 18 ++++++++++++++++++ twscrape/cli.py | 1 + 3 files changed, 20 insertions(+) diff --git a/tests/test_api.py b/tests/test_api.py index 97c5f9d..6c8ef57 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -21,6 +21,7 @@ class MockedError(Exception): "user_tweets_and_replies", "list_timeline", "trends", + "list_members", ] diff --git a/twscrape/api.py b/twscrape/api.py index da9f17f..022621e 100644 --- a/twscrape/api.py +++ b/twscrape/api.py @@ -25,6 +25,7 @@ OP_UserMedia = "vFPc2LVIu7so2uA_gHQAdg/UserMedia" OP_Bookmarks = "-LGfdImKeQz0xS_jjUwzlA/Bookmarks" OP_GenericTimelineById = "CT0YFEFf5GOYa5DJcxM91w/GenericTimelineById" +OP_ListMembers = "CDYIXKGINDLWntNdeXVUgQ" GQL_URL = "https://x.com/i/api/graphql" GQL_FEATURES = { # search values here (view source) https://x.com/ @@ -514,3 +515,20 @@ async def bookmarks(self, limit=-1, kv: KV = None): async for rep in gen: for x in parse_tweets(rep.json(), limit): yield x + + # list members of a List + + async def list_members_raw(self, list_id: int, limit: int = -1, kv: KV = None): + # Raw query for list members + op = OP_ListMembers + kv = {"listId": str(list_id), "count": 20, **(kv or {})} + async with aclosing(self._gql_items(op, kv, limit=limit)) as gen: + async for page in gen: + yield page + + async def list_members(self, list_id: int, limit: int = -1, kv: KV = None): + # Parse the list members from the raw query + async with aclosing(self.list_members_raw(list_id, limit=limit, kv=kv)) as gen: + async for page in gen: + for user in parse_users(page.json(), limit): + yield user diff --git a/twscrape/cli.py b/twscrape/cli.py index 6b3d66f..45eebd3 100644 --- a/twscrape/cli.py +++ b/twscrape/cli.py @@ -196,6 +196,7 @@ def c_lim(name: str, msg: str, a_name: str, a_msg: str, a_type: type = str): c_lim("user_media", "Get user's media", "user_id", "User ID", int) c_lim("list_timeline", "Get tweets from list", "list_id", "List ID", int) c_lim("trends", "Get trends", "trend_id", "Trend ID or name", str) + c_lim("list_members", "Get List members by list ID", "list_id", "List ID", int) args = p.parse_args() if args.command is None: