···92929393## Retrieving Twitter followers
94949595-The `followers` command retrieves details of every follower of the specified account. You can use it to retrieve your own followers, or you can pass a screen_name to pull the followers for another account.
9595+The `followers` command retrieves details of every follower of the specified accounts. You can use it to retrieve your own followers, or you can pass one or more screen names to pull the followers for other accounts.
96969797The following command pulls your followers and saves them in a SQLite database file called `twitter.db`:
9898···102102103103To retrieve followers for another account, use:
104104105105- $ twitter-to-sqlite followers twitter.db --screen_name=cleopaws
105105+ $ twitter-to-sqlite followers twitter.db cleopaws
106106+107107+This command also accepts the `--ids`, `--sql` and `--attach` options.
106108107109See [Analyzing my Twitter followers with Datasette](https://simonwillison.net/2018/Jan/28/analyzing-my-twitter-followers/) for the original inspiration for this command.
108108-109110110111## Retrieving friends
111112
+57-38
twitter_to_sqlite/cli.py
···8989 type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
9090 required=True,
9191)
9292+@add_identifier_options
9293@click.option(
9394 "-a",
9495 "--auth",
···9697 default="auth.json",
9798 help="Path to auth.json token file",
9899)
9999-@click.option("--user_id", help="Numeric user ID")
100100-@click.option("--screen_name", help="Screen name")
100100+@click.option("--ids", is_flag=True, help="Treat input as user IDs, not screen names")
101101@click.option("--silent", is_flag=True, help="Disable progress bar")
102102-def followers(db_path, auth, user_id, screen_name, silent):
103103- "Save followers for specified user (defaults to authenticated user)"
104104- _shared_friends_followers(db_path, auth, user_id, screen_name, silent, "followers")
102102+def followers(db_path, identifiers, attach, sql, auth, ids, silent):
103103+ "Save followers for specified users (defaults to authenticated user)"
104104+ _shared_friends_followers(
105105+ db_path, identifiers, attach, sql, auth, ids, silent, "followers"
106106+ )
105107106108107107-def _shared_friends_followers(db_path, auth, user_id, screen_name, silent, noun):
109109+def _shared_friends_followers(
110110+ db_path, identifiers, attach, sql, auth, ids, silent, noun
111111+):
108112 assert noun in ("friends", "followers")
109113 auth = json.load(open(auth))
110114 session = utils.session_for_auth(auth)
111115 db = utils.open_database(db_path)
112112- fetched = []
113113- # Get the follower count, so we can have a progress bar
114114- count = 0
115116116116- profile = utils.get_profile(db, session, user_id, screen_name)
117117- screen_name = profile["screen_name"]
118118- user_id = profile["id"]
117117+ identifiers = utils.resolve_identifiers(db, identifiers, attach, sql)
119118120120- save_users_kwargs = {}
121121- if noun == "followers":
122122- save_users_kwargs["followed_id"] = user_id
123123- elif noun == "friends":
124124- save_users_kwargs["follower_id"] = user_id
119119+ if not identifiers:
120120+ profile = utils.get_profile(db, session)
121121+ identifiers = [profile["screen_name"]]
122122+123123+ for identifier in identifiers:
124124+ if ids:
125125+ kwargs = {"user_id": identifier}
126126+ else:
127127+ kwargs = {"screen_name": identifier}
128128+129129+ fetched = []
130130+ # Get the follower count, so we can have a progress bar
131131+ count = 0
132132+133133+ profile = utils.get_profile(db, session, **kwargs)
134134+ screen_name = profile["screen_name"]
135135+ user_id = profile["id"]
136136+137137+ save_users_kwargs = {}
138138+ if noun == "followers":
139139+ save_users_kwargs["followed_id"] = user_id
140140+ elif noun == "friends":
141141+ save_users_kwargs["follower_id"] = user_id
125142126126- def go(update):
127127- for users_chunk in utils.fetch_user_list_chunks(
128128- session, user_id, screen_name, noun=noun
129129- ):
130130- fetched.extend(users_chunk)
131131- utils.save_users(db, users_chunk, **save_users_kwargs)
132132- update(len(users_chunk))
143143+ def go(update):
144144+ for users_chunk in utils.fetch_user_list_chunks(
145145+ session, user_id, screen_name, noun=noun
146146+ ):
147147+ fetched.extend(users_chunk)
148148+ utils.save_users(db, users_chunk, **save_users_kwargs)
149149+ update(len(users_chunk))
133150134134- if not silent:
135135- count = profile["{}_count".format(noun)]
136136- with click.progressbar(
137137- length=count,
138138- label="Importing {:,} {}s for @{}".format(count, noun, screen_name),
139139- ) as bar:
140140- go(bar.update)
141141- else:
142142- go(lambda x: None)
151151+ if not silent:
152152+ count = profile["{}_count".format(noun)]
153153+ with click.progressbar(
154154+ length=count,
155155+ label="Importing {:,} {} for @{}".format(count, noun, screen_name),
156156+ ) as bar:
157157+ go(bar.update)
158158+ else:
159159+ go(lambda x: None)
143160144161145162@cli.command()
···148165 type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
149166 required=True,
150167)
168168+@add_identifier_options
151169@click.option(
152170 "-a",
153171 "--auth",
···155173 default="auth.json",
156174 help="Path to auth.json token file",
157175)
158158-@click.option("--user_id", help="Numeric user ID")
159159-@click.option("--screen_name", help="Screen name")
176176+@click.option("--ids", is_flag=True, help="Treat input as user IDs, not screen names")
160177@click.option("--silent", is_flag=True, help="Disable progress bar")
161161-def friends(db_path, auth, user_id, screen_name, silent):
162162- "Save friends for specified user (defaults to authenticated user)"
163163- _shared_friends_followers(db_path, auth, user_id, screen_name, silent, "friends")
178178+def friends(db_path, identifiers, attach, sql, auth, ids, silent):
179179+ "Save friends for specified users (defaults to authenticated user)"
180180+ _shared_friends_followers(
181181+ db_path, identifiers, attach, sql, auth, ids, silent, "friends"
182182+ )
164183165184166185@cli.command()