]>
jfr.im git - z_archive/twitter.git/blob - twitter/cmdline.py
4 twitter [action] [options]
7 follow add the specified user to your follow list
8 friends get latest tweets from your friends (default action)
9 help print this help text that you are currently reading
10 leave remove the specified user from your following list
11 public get latest public tweets
12 replies get latest replies
13 set set your twitter status
17 -e --email <email> your email to login to twitter
18 -p --password <password> your twitter password
19 -r --refresh run this command forever, polling every once
20 in a while (default: every 5 minutes)
21 -R --refresh-rate <rate> set the refresh rate (in seconds)
22 -f --format <format> specify the output format for status updates
23 -c --config <filename> read username and password from given config
24 file (default ~/.twitter)
26 FORMATS for the --format option
28 default one line per status
29 verbose multiple lines per status, more verbose status info
30 urls nothing but URLs. Dare you click them?
34 The config file should contain a [twitter] header, your email and password
44 from getopt
import getopt
, GetoptError
45 from getpass
import getpass
48 from ConfigParser
import SafeConfigParser
50 from api
import Twitter
, TwitterError
52 AGENT_STR
= "Twitter Command-line Tool"
61 'config_filename': os
.environ
.get('HOME', '') + os
.sep
+ '.twitter',
65 def parse_args(args
, options
):
66 long_opts
= ['email', 'password', 'help', 'format', 'refresh',
67 'refresh-rate', 'config']
68 short_opts
= "e:p:f:h?rR:c:"
69 opts
, extra_args
= getopt(args
, short_opts
, long_opts
)
72 if opt
in ('-e', '--email'):
73 options
['email'] = arg
74 elif opt
in ('-p', '--password'):
75 options
['password'] = arg
76 elif opt
in ('-f', '--format'):
77 options
['format'] = arg
78 elif opt
in ('-r', '--refresh'):
79 options
['refresh'] = True
80 elif opt
in ('-R', '--refresh-rate'):
81 options
['refresh_rate'] = int(arg
)
82 elif opt
in ('-?', '-h', '--help'):
85 elif opt
in ('-c', '--config'):
86 options
['config_filename'] = arg
89 options
['action'] = extra_args
[0]
90 options
['extra_args'] = extra_args
[1:]
92 class StatusFormatter(object):
93 def __call__(self
, status
):
95 status
['user']['screen_name'], status
['text']))
97 class VerboseStatusFormatter(object):
98 def __call__(self
, status
):
99 return (u
"-- %s (%s) on %s\n%s\n" %(
100 status
['user']['screen_name'],
101 status
['user']['location'],
102 status
['created_at'],
105 class URLStatusFormatter(object):
106 urlmatch
= re
.compile(r
'https?://\S+')
107 def __call__(self
, status
):
108 urls
= self
.urlmatch
.findall(status
['text'])
109 return u
'\n'.join(urls
) if urls
else ""
111 class AdminFormatter(object):
112 def __call__(self
, action
, user
):
113 user_str
= u
"%s (%s)" %(user
['screen_name'], user
['name'])
114 if action
== "follow":
115 return u
"You are now following %s.\n" %(user_str)
117 return u
"You are no longer following %s.\n" %(user_str)
119 class VerboseAdminFormatter(object):
120 def __call__(self
, action
, user
):
121 return(u
"-- %s: %s (%s): %s" % (
122 "Following" if action
== "follow" else "Leaving",
127 class URLAdminFormatter(object):
128 def __call__(self
, action
, user
):
129 return("Admin actions do not support the URL formatter")
131 status_formatters
= {
132 'default': StatusFormatter
,
133 'verbose': VerboseStatusFormatter
,
134 'urls': URLStatusFormatter
138 'default': AdminFormatter
,
139 'verbose': VerboseAdminFormatter
,
140 'urls': URLAdminFormatter
143 def get_status_formatter(options
):
144 sf
= status_formatters
.get(options
['format'])
147 "Unknown formatter '%s'" %(options
['format']))
150 def get_admin_formatter(options
):
151 sf
= admin_formatters
.get(options
['format'])
154 "Unknown formatter '%s'" %(options
['format']))
157 class Action(object):
160 class NoSuchAction(Action
):
161 def __call__(self
, twitter
, options
):
162 print >> sys
.stderr
, "No such action: ", options
['action']
165 class StatusAction(Action
):
166 def __call__(self
, twitter
, options
):
167 statuses
= self
.getStatuses(twitter
)
168 sf
= get_status_formatter(options
)
169 for status
in statuses
:
170 statusStr
= sf(status
)
171 if statusStr
.strip():
172 print statusStr
.encode(sys
.stdout
.encoding
, 'replace')
174 class AdminAction(Action
):
175 def __call__(self
, twitter
, options
):
176 if not options
['extra_args'][0]:
177 raise TwitterError("You need to specify a user (screen name)")
178 af
= get_admin_formatter(options
)
180 user
= self
.getUser(twitter
, options
['extra_args'][0])
181 except TwitterError
, e
:
182 print "There was a problem following or leaving the specified user."
183 print " You may be trying to follow a user you are already following;"
184 print " Leaving a user you are not currently following;"
185 print " Or the user may not exist."
190 print af(options
['action'], user
).encode(sys
.stdout
.encoding
, 'replace')
192 class FriendsAction(StatusAction
):
193 def getStatuses(self
, twitter
):
194 return reversed(twitter
.statuses
.friends_timeline())
196 class PublicAction(StatusAction
):
197 def getStatuses(self
, twitter
):
198 return reversed(twitter
.statuses
.public_timeline())
200 class RepliesAction(StatusAction
):
201 def getStatuses(self
, twitter
):
202 return reversed(twitter
.statuses
.replies())
204 class FollowAction(AdminAction
):
205 def getUser(self
, twitter
, user
):
206 return twitter
.notifications
.follow(id=user
)
208 class LeaveAction(AdminAction
):
209 def getUser(self
, twitter
, user
):
210 return twitter
.notifications
.leave(id=user
)
212 class SetStatusAction(Action
):
213 def __call__(self
, twitter
, options
):
214 statusTxt
= (u
" ".join(options
['extra_args'])
215 if options
['extra_args']
216 else unicode(raw_input("message: ")))
217 status
= (statusTxt
.encode('utf8', 'replace'))
218 twitter
.statuses
.update(status
=status
)
220 class HelpAction(Action
):
221 def __call__(self
, twitter
, options
):
225 'follow': FollowAction
,
226 'friends': FriendsAction
,
228 'leave': LeaveAction
,
229 'public': PublicAction
,
230 'replies': RepliesAction
,
231 'set': SetStatusAction
,
234 def loadConfig(filename
):
237 if os
.path
.exists(filename
):
238 cp
= SafeConfigParser()
240 email
= cp
.get('twitter', 'email', None)
241 password
= cp
.get('twitter', 'password', None)
242 return email
, password
245 return main_with_args(sys
.argv
[1:])
247 def main_with_args(args
):
249 parse_args(args
, options
)
250 except GetoptError
, e
:
251 print >> sys
.stderr
, "I can't do that, %s." %(e)
255 email
, password
= loadConfig(options
['config_filename'])
256 if not options
['email']: options
['email'] = email
257 if not options
['password']: options
['password'] = password
259 if options
['refresh'] and options
['action'] not in (
260 'friends', 'public', 'replies'):
261 print >> sys
.stderr
, "You can only refresh the friends, public, or replies actions."
262 print >> sys
.stderr
, "Use 'twitter -h' for help."
265 if options
['email'] and not options
['password']:
266 options
['password'] = getpass("Twitter password: ")
268 twitter
= Twitter(options
['email'], options
['password'], agent
=AGENT_STR
)
269 action
= actions
.get(options
['action'], NoSuchAction
)()
272 doAction
= lambda : action(twitter
, options
)
274 if (options
['refresh'] and isinstance(action
, StatusAction
)):
277 time
.sleep(options
['refresh_rate'])
281 except TwitterError
, e
:
282 print >> sys
.stderr
, e
.args
[0]
283 print >> sys
.stderr
, "Use 'twitter -h' for help."
285 except KeyboardInterrupt: