]>
jfr.im git - z_archive/twitter.git/blob - twitter/follow.py
2 twitter-follow [options] <user>
5 Display all following/followers of a user, one user per line.
8 -o --oauth authenticate to Twitter using OAuth (default no)
9 -r --followers display followers of the given user (default)
10 -g --following display users the given user is following
11 -a --api-rate see your current API rate limit status
14 Authenticate to Twitter using OAuth to see following/followers of private
15 profiles and have higher API rate limits. OAuth authentication tokens
16 are stored in the file .twitter-follow_oauth in your home directory.
19 from __future__
import print_function
21 import os
, sys
, time
, calendar
22 from getopt
import gnu_getopt
as getopt
, GetoptError
25 import urllib
.request
as urllib2
26 import http
.client
as httplib
31 # T-Follow (Twitter-Follow) application registered by @stalkr_
32 CONSUMER_KEY
='USRZQfvFFjB6UvZIN2Edww'
33 CONSUMER_SECRET
='AwGAaSzZa5r0TDL8RKCDtffnI9H9mooZUdOa95nw8'
35 from .api
import Twitter
, TwitterError
36 from .oauth
import OAuth
, read_token_file
37 from .oauth_dance
import oauth_dance
38 from .auth
import NoAuth
39 from .util
import Fail
, err
42 def parse_args(args
, options
):
43 """Parse arguments from command-line to set options."""
44 long_opts
= ['help', 'oauth', 'followers', 'following', 'api-rate']
46 opts
, extra_args
= getopt(args
, short_opts
, long_opts
)
49 if opt
in ('-h', '--help'):
52 elif opt
in ('-o', '--oauth'):
53 options
['oauth'] = True
54 elif opt
in ('-r', '--followers'):
55 options
['followers'] = True
56 elif opt
in ('-g', '--following'):
57 options
['followers'] = False
58 elif opt
in ('-a', '--api-rate'):
59 options
['api-rate' ] = True
61 options
['extra_args'] = extra_args
63 def lookup_portion(twitter
, user_ids
):
64 """Resolve a limited list of user ids to screen names."""
66 kwargs
= dict(user_id
=",".join(map(str, user_ids
)), skip_status
=1)
67 for u
in twitter
.users
.lookup(**kwargs
):
68 users
[int(u
['id'])] = u
['screen_name']
71 def lookup(twitter
, user_ids
):
72 """Resolve an entire list of user ids to screen names."""
75 for i
in range(0, len(user_ids
), api_limit
):
79 portion
= lookup_portion(twitter
, user_ids
[i
:][:api_limit
])
80 except TwitterError
as e
:
82 err("Fail: %i API rate limit exceeded" % e
.e
.code
)
83 rate
= twitter
.account
.rate_limit_status()
84 reset
= rate
['reset_time_in_seconds']
85 reset
= time
.asctime(time
.localtime(reset
))
86 delay
= int(rate
['reset_time_in_seconds']
87 - time
.time()) + 5 # avoid race
88 err("Hourly limit of %i requests reached, next reset on "
89 "%s: going to sleep for %i secs"
90 % (rate
['hourly_limit'], reset
, delay
))
94 err("Fail: %i Service currently unavailable, retrying..."
97 err("Fail: %s\nRetrying..." % str(e
)[:500])
99 except urllib2
.URLError
as e
:
100 err("Fail: urllib2.URLError %s - Retrying..." % str(e
))
102 except httplib
.error
as e
:
103 err("Fail: httplib.error %s - Retrying..." % str(e
))
105 except KeyError as e
:
106 err("Fail: KeyError %s - Retrying..." % str(e
))
109 users
.update(portion
)
110 err("Resolving user ids to screen names: %i/%i"
111 % (len(users
), len(user_ids
)))
115 def follow_portion(twitter
, screen_name
, cursor
=-1, followers
=True):
116 """Get a portion of followers/following for a user."""
117 kwargs
= dict(screen_name
=screen_name
, cursor
=cursor
)
119 t
= twitter
.followers
.ids(**kwargs
)
121 t
= twitter
.friends
.ids(**kwargs
)
122 return t
['ids'], t
['next_cursor']
124 def follow(twitter
, screen_name
, followers
=True):
125 """Get the entire list of followers/following for a user."""
131 portion
, cursor
= follow_portion(twitter
, screen_name
, cursor
,
133 except TwitterError
as e
:
135 reason
= ("follow%s of that user are protected"
136 % ("ers" if followers
else "ing"))
137 err("Fail: %i Unauthorized (%s)" % (e
.e
.code
, reason
))
139 elif e
.e
.code
== 400:
140 err("Fail: %i API rate limit exceeded" % e
.e
.code
)
141 rate
= twitter
.account
.rate_limit_status()
142 reset
= rate
['reset_time_in_seconds']
143 reset
= time
.asctime(time
.localtime(reset
))
144 delay
= int(rate
['reset_time_in_seconds']
145 - time
.time()) + 5 # avoid race
146 err("Hourly limit of %i requests reached, next reset on %s: "
147 "going to sleep for %i secs" % (rate
['hourly_limit'],
151 elif e
.e
.code
== 502:
152 err("Fail: %i Service currently unavailable, retrying..."
155 err("Fail: %s\nRetrying..." % str(e
)[:500])
157 except urllib2
.URLError
as e
:
158 err("Fail: urllib2.URLError %s - Retrying..." % str(e
))
160 except httplib
.error
as e
:
161 err("Fail: httplib.error %s - Retrying..." % str(e
))
163 except KeyError as e
:
164 err("Fail: KeyError %s - Retrying..." % str(e
))
168 user_ids
= list(set(user_ids
+ portion
))
170 what
= "follow%s" % ("ers" if followers
else "ing")
171 err("Browsing %s %s, new: %i" % (screen_name
, what
, new
))
178 def rate_limit_status(twitter
):
179 """Print current Twitter API rate limit status."""
180 r
= twitter
.account
.rate_limit_status()
181 print("Remaining API requests: %i/%i (hourly limit)"
182 % (r
['remaining_hits'], r
['hourly_limit']))
183 print("Next reset in %is (%s)"
184 % (int(r
['reset_time_in_seconds'] - time
.time()),
185 time
.asctime(time
.localtime(r
['reset_time_in_seconds']))))
187 def main(args
=sys
.argv
[1:]):
194 parse_args(args
, options
)
195 except GetoptError
as e
:
196 err("I can't do that, %s." % e
)
199 # exit if no user or given, except if asking for API rate
200 if not options
['extra_args'] and not options
['api-rate']:
204 # authenticate using OAuth, asking for token if necessary
206 oauth_filename
= (os
.getenv("HOME", "") + os
.sep
207 + ".twitter-follow_oauth")
208 if not os
.path
.exists(oauth_filename
):
209 oauth_dance("Twitter-Follow", CONSUMER_KEY
, CONSUMER_SECRET
,
211 oauth_token
, oauth_token_secret
= read_token_file(oauth_filename
)
212 auth
= OAuth(oauth_token
, oauth_token_secret
, CONSUMER_KEY
,
217 twitter
= Twitter(auth
=auth
, api_version
='1', domain
='api.twitter.com')
219 if options
['api-rate']:
220 rate_limit_status(twitter
)
223 # obtain list of followers (or following) for every given user
224 for user
in options
['extra_args']:
225 user_ids
, users
= [], {}
227 user_ids
= follow(twitter
, user
, options
['followers'])
228 users
= lookup(twitter
, user_ids
)
229 except KeyboardInterrupt as e
:
235 print(users
[uid
].encode("utf-8"))
237 # print total on stderr to separate from user list on stdout
238 if options
['followers']:
239 err("Total followers for %s: %i" % (user
, len(user_ids
)))
241 err("Total users %s is following: %i" % (user
, len(user_ids
)))