diff --git a/readme.md b/readme.md index 700421c..2ea098e 100644 --- a/readme.md +++ b/readme.md @@ -133,11 +133,11 @@ First add accounts from file: ```sh # twscrape add_accounts # line_format should have "username", "password", "email", "email_password" tokens -# tokens delimeter should be same as an file +# note: tokens delimeter should be same as an file twscrape add_accounts accounts.txt username:password:email:email_password ``` -The call login: +Then call login: ```sh twscrape login_accounts @@ -157,6 +157,20 @@ twscrape accounts # user3 False False None 120 Login error ``` +### Re-login accounts + +It is possible to re-login specific accounts: + +```sh +twscrape relogin user1 user2 +``` + +Or retry login for all failed logins: + +```sh +twscrape relogin_failed +``` + ### Use different accounts file Useful if using a different set of accounts for different actions diff --git a/twscrape/accounts_pool.py b/twscrape/accounts_pool.py index e5dfc9e..1e05687 100644 --- a/twscrape/accounts_pool.py +++ b/twscrape/accounts_pool.py @@ -129,6 +129,33 @@ class AccountsPool: logger.info(f"[{i}/{len(accounts)}] Logging in {x.username} - {x.email}") await self.login(x) + async def relogin(self, usernames: str | list[str]): + usernames = usernames if isinstance(usernames, list) else [usernames] + usernames = list(set(usernames)) + if not usernames: + logger.warning("No usernames provided") + return + + qs = f""" + UPDATE accounts SET + active = false, + locks = json_object(), + last_used = NULL, + error_msg = NULL, + headers = json_object(), + cookies = json_object(), + user_agent = "{UserAgent().safari}" + WHERE username IN ({','.join([f'"{x}"' for x in usernames])}) + """ + + await execute(self._db_file, qs) + await self.login_all() + + async def relogin_failed(self): + qs = "SELECT username FROM accounts WHERE active = false AND error_msg IS NOT NULL" + rs = await fetchall(self._db_file, qs) + await self.relogin([x["username"] for x in rs]) + async def set_active(self, username: str, active: bool): qs = "UPDATE accounts SET active = :active WHERE username = :username" await execute(self._db_file, qs, {"username": username, "active": active}) @@ -244,6 +271,10 @@ class AccountsPool: old_time = datetime(1970, 1, 1).replace(tzinfo=timezone.utc) items = sorted(items, key=lambda x: x["username"].lower()) items = sorted(items, key=lambda x: x["active"], reverse=True) - items = sorted(items, key=lambda x: x["last_used"] or old_time, reverse=True) + items = sorted( + items, + key=lambda x: x["last_used"] or old_time if x["total_req"] > 0 else old_time, + reverse=True, + ) # items = sorted(items, key=lambda x: x["total_req"], reverse=True) return items diff --git a/twscrape/cli.py b/twscrape/cli.py index 1712b06..1443833 100644 --- a/twscrape/cli.py +++ b/twscrape/cli.py @@ -69,6 +69,14 @@ async def main(args): await pool.login_all() return + if args.command == "relogin_failed": + await pool.relogin_failed() + return + + if args.command == "relogin": + await pool.relogin(args.usernames) + return + fn = args.command + "_raw" if args.raw else args.command fn = getattr(api, fn, None) if fn is None: @@ -130,6 +138,9 @@ def run(): add_accounts.add_argument("file_path", help="File with accounts") add_accounts.add_argument("line_format", help="args of Pool.add_account splited by same delim") subparsers.add_parser("login_accounts", help="Login accounts") + relogin = subparsers.add_parser("relogin", help="Re-login selected accounts") + relogin.add_argument("usernames", nargs="+", default=[], help="Usernames to re-login") + subparsers.add_parser("relogin_failed", help="Retry login for failed accounts") c_lim("search", "Search for tweets", "query", "Search query") c_one("tweet_details", "Get tweet details", "tweet_id", "Tweet ID", int) diff --git a/twscrape/login.py b/twscrape/login.py index 790d14f..1cd6fa9 100644 --- a/twscrape/login.py +++ b/twscrape/login.py @@ -154,7 +154,7 @@ async def next_login_task(client: AsyncClient, acc: Account, rep: Response): prev = rep.json() assert "flow_token" in prev, f"flow_token not in {rep.text}" - # logger.debug(f"login tasks: {[x['subtask_id'] for x in data['subtasks']]}") + logger.debug(f"login tasks: {[x['subtask_id'] for x in prev['subtasks']]}") for x in prev["subtasks"]: task_id = x["subtask_id"]