]> jfr.im git - z_archive/twitter.git/blame_incremental - twitter/cmdline.py
Agent feature was not actually working. Prepare fix for 1.0.1. Still waiting on Twitt...
[z_archive/twitter.git] / twitter / cmdline.py
... / ...
CommitLineData
1"""
2USAGE:
3
4 twitter [action] [options]
5
6ACTIONS:
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
14
15OPTIONS:
16
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)
25
26FORMATS for the --format option
27
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?
31
32CONFIG FILES
33
34 The config file should contain a [twitter] header, your email and password
35 like so:
36
37[twitter]
38email: <username>
39password: <password>
40"""
41
42import sys
43import time
44from getopt import getopt, GetoptError
45from getpass import getpass
46import re
47import os.path
48from ConfigParser import SafeConfigParser
49
50from api import Twitter, TwitterError
51
52# Wait on Twitter people to provide this string...
53AGENT_STR = None
54
55options = {
56 'email': None,
57 'password': None,
58 'action': 'friends',
59 'refresh': False,
60 'refresh_rate': 600,
61 'format': 'default',
62 'config_filename': os.environ.get('HOME', '') + os.sep + '.twitter',
63 'extra_args': []
64}
65
66def parse_args(args, options):
67 long_opts = ['email', 'password', 'help', 'format', 'refresh',
68 'refresh-rate', 'config']
69 short_opts = "e:p:f:h?rR:c:"
70 opts, extra_args = getopt(args, short_opts, long_opts)
71
72 for opt, arg in opts:
73 if opt in ('-e', '--email'):
74 options['email'] = arg
75 elif opt in ('-p', '--password'):
76 options['password'] = arg
77 elif opt in ('-f', '--format'):
78 options['format'] = arg
79 elif opt in ('-r', '--refresh'):
80 options['refresh'] = True
81 elif opt in ('-R', '--refresh-rate'):
82 options['refresh_rate'] = int(arg)
83 elif opt in ('-?', '-h', '--help'):
84 print __doc__
85 sys.exit(0)
86 elif opt in ('-c', '--config'):
87 options['config_filename'] = arg
88
89 if extra_args:
90 options['action'] = extra_args[0]
91 options['extra_args'] = extra_args[1:]
92
93class StatusFormatter(object):
94 def __call__(self, status):
95 return (u"%s %s" %(
96 status['user']['screen_name'], status['text']))
97
98class VerboseStatusFormatter(object):
99 def __call__(self, status):
100 return (u"-- %s (%s) on %s\n%s\n" %(
101 status['user']['screen_name'],
102 status['user']['location'],
103 status['created_at'],
104 status['text']))
105
106class URLStatusFormatter(object):
107 urlmatch = re.compile(r'https?://\S+')
108 def __call__(self, status):
109 urls = self.urlmatch.findall(status['text'])
110 return u'\n'.join(urls) if urls else ""
111
112class AdminFormatter(object):
113 def __call__(self, action, user):
114 user_str = u"%s (%s)" %(user['screen_name'], user['name'])
115 if action == "follow":
116 return u"You are now following %s.\n" %(user_str)
117 else:
118 return u"You are no longer following %s.\n" %(user_str)
119
120class VerboseAdminFormatter(object):
121 def __call__(self, action, user):
122 return(u"-- %s: %s (%s): %s" % (
123 "Following" if action == "follow" else "Leaving",
124 user['screen_name'],
125 user['name'],
126 user['url']))
127
128class URLAdminFormatter(object):
129 def __call__(self, action, user):
130 return("Admin actions do not support the URL formatter")
131
132status_formatters = {
133 'default': StatusFormatter,
134 'verbose': VerboseStatusFormatter,
135 'urls': URLStatusFormatter
136}
137
138admin_formatters = {
139 'default': AdminFormatter,
140 'verbose': VerboseAdminFormatter,
141 'urls': URLAdminFormatter
142}
143
144def get_status_formatter(options):
145 sf = status_formatters.get(options['format'])
146 if (not sf):
147 raise TwitterError(
148 "Unknown formatter '%s'" %(options['format']))
149 return sf()
150
151def get_admin_formatter(options):
152 sf = admin_formatters.get(options['format'])
153 if (not sf):
154 raise TwitterError(
155 "Unknown formatter '%s'" %(options['format']))
156 return sf()
157
158class Action(object):
159 pass
160
161class NoSuchAction(Action):
162 def __call__(self, twitter, options):
163 print >> sys.stderr, "No such action: ", options['action']
164 sys.exit(1)
165
166class StatusAction(Action):
167 def __call__(self, twitter, options):
168 statuses = self.getStatuses(twitter)
169 sf = get_status_formatter(options)
170 for status in statuses:
171 statusStr = sf(status)
172 if statusStr.strip():
173 print statusStr.encode(sys.stdout.encoding, 'replace')
174
175class AdminAction(Action):
176 def __call__(self, twitter, options):
177 if not options['extra_args'][0]:
178 raise TwitterError("You need to specify a user (screen name)")
179 af = get_admin_formatter(options)
180 try:
181 user = self.getUser(twitter, options['extra_args'][0])
182 except TwitterError, e:
183 print "There was a problem following or leaving the specified user."
184 print " You may be trying to follow a user you are already following;"
185 print " Leaving a user you are not currently following;"
186 print " Or the user may not exist."
187 print " Sorry."
188 print
189 print e
190 else:
191 print af(options['action'], user).encode(sys.stdout.encoding, 'replace')
192
193class FriendsAction(StatusAction):
194 def getStatuses(self, twitter):
195 return reversed(twitter.statuses.friends_timeline())
196
197class PublicAction(StatusAction):
198 def getStatuses(self, twitter):
199 return reversed(twitter.statuses.public_timeline())
200
201class RepliesAction(StatusAction):
202 def getStatuses(self, twitter):
203 return reversed(twitter.statuses.replies())
204
205class FollowAction(AdminAction):
206 def getUser(self, twitter, user):
207 return twitter.notifications.follow(id=user)
208
209class LeaveAction(AdminAction):
210 def getUser(self, twitter, user):
211 return twitter.notifications.leave(id=user)
212
213class SetStatusAction(Action):
214 def __call__(self, twitter, options):
215 statusTxt = (u" ".join(options['extra_args'])
216 if options['extra_args']
217 else unicode(raw_input("message: ")))
218 status = (statusTxt.encode('utf8', 'replace'))
219 twitter.statuses.update(status=status)
220
221class HelpAction(Action):
222 def __call__(self, twitter, options):
223 print __doc__
224
225actions = {
226 'follow': FollowAction,
227 'friends': FriendsAction,
228 'help': HelpAction,
229 'leave': LeaveAction,
230 'public': PublicAction,
231 'replies': RepliesAction,
232 'set': SetStatusAction,
233}
234
235def loadConfig(filename):
236 email = None
237 password = None
238 if os.path.exists(filename):
239 cp = SafeConfigParser()
240 cp.read([filename])
241 email = cp.get('twitter', 'email', None)
242 password = cp.get('twitter', 'password', None)
243 return email, password
244
245def main():
246 return main_with_args(sys.argv[1:])
247
248def main_with_args(args):
249 try:
250 parse_args(args, options)
251 except GetoptError, e:
252 print >> sys.stderr, "I can't do that, %s." %(e)
253 print >> sys.stderr
254 sys.exit(1)
255
256 email, password = loadConfig(options['config_filename'])
257 if not options['email']: options['email'] = email
258 if not options['password']: options['password'] = password
259
260 if options['refresh'] and options['action'] not in (
261 'friends', 'public', 'replies'):
262 print >> sys.stderr, "You can only refresh the friends, public, or replies actions."
263 print >> sys.stderr, "Use 'twitter -h' for help."
264 sys.exit(1)
265
266 if options['email'] and not options['password']:
267 options['password'] = getpass("Twitter password: ")
268
269 twitter = Twitter(options['email'], options['password'], agent=AGENT_STR)
270 action = actions.get(options['action'], NoSuchAction)()
271
272 try:
273 doAction = lambda : action(twitter, options)
274
275 if (options['refresh'] and isinstance(action, StatusAction)):
276 while True:
277 doAction()
278 time.sleep(options['refresh_rate'])
279 else:
280 doAction()
281
282 except TwitterError, e:
283 print >> sys.stderr, e.args[0]
284 print >> sys.stderr, "Use 'twitter -h' for help."
285 sys.exit(1)
286 except KeyboardInterrupt:
287 pass
288