]> jfr.im git - z_archive/twitter.git/blame_incremental - twitter/cmdline.py
Add reply action (patch by Wes Devauld)
[z_archive/twitter.git] / twitter / cmdline.py
... / ...
CommitLineData
1"""
2USAGE:
3
4 twitter [action] [options]
5
6ACTIONS:
7
8 friends get latest tweets from your friends (default action)
9 public get latest public tweets
10 replies get latest replies
11 set set your twitter status
12
13OPTIONS:
14
15 -e --email <email> your email to login to twitter
16 -p --password <password> your twitter password
17 -r --refresh run this command forever, polling every once
18 in a while (default: every 5 minutes)
19 -R --refresh-rate <rate> set the refresh rate (in seconds)
20 -f --format <format> specify the output format for status updates
21 -c --config <filename> read username and password from given config
22 file (default ~/.twitter)
23
24FORMATS for the --format option
25
26 default one line per status
27 verbose multiple lines per status, more verbose status info
28 urls nothing but URLs. Dare you click them?
29
30CONFIG FILES
31
32 The config file should contain your email and password like so:
33
34[twitter]
35email: <username>
36password: <password>
37"""
38
39import sys
40import time
41from getopt import getopt
42from getpass import getpass
43import re
44import os.path
45from ConfigParser import SafeConfigParser
46
47from api import Twitter, TwitterError
48
49options = {
50 'email': None,
51 'password': None,
52 'action': 'friends',
53 'refresh': False,
54 'refresh_rate': 600,
55 'format': 'default',
56 'config_filename': os.environ.get('HOME', '') + os.sep + '.twitter',
57 'extra_args': []
58}
59
60def parse_args(args, options):
61 long_opts = ['email', 'password', 'help', 'format', 'refresh',
62 'refresh-rate', 'config']
63 short_opts = "e:p:f:h?rR:c:"
64 opts, extra_args = getopt(args, short_opts, long_opts)
65
66 for opt, arg in opts:
67 if opt in ('-e', '--email'):
68 options['email'] = arg
69 elif opt in ('-p', '--password'):
70 options['password'] = arg
71 elif opt in ('-f', '--format'):
72 options['format'] = arg
73 elif opt in ('-r', '--refresh'):
74 options['refresh'] = True
75 elif opt in ('-R', '--refresh-rate'):
76 options['refresh_rate'] = int(arg)
77 elif opt in ('-?', '-h', '--help'):
78 print __doc__
79 sys.exit(0)
80 elif opt in ('-c', '--config'):
81 options['config_filename'] = arg
82
83 if extra_args:
84 options['action'] = extra_args[0]
85 options['extra_args'] = extra_args[1:]
86
87class StatusFormatter(object):
88 def __call__(self, status):
89 return (u"%s %s" %(
90 status['user']['screen_name'], status['text']))
91
92class VerboseStatusFormatter(object):
93 def __call__(self, status):
94 return (u"-- %s (%s) on %s\n%s\n" %(
95 status['user']['screen_name'],
96 status['user']['location'],
97 status['created_at'],
98 status['text']))
99
100class URLStatusFormatter(object):
101 urlmatch = re.compile(r'https?://\S+')
102 def __call__(self, status):
103 urls = self.urlmatch.findall(status['text'])
104 return u'\n'.join(urls) if urls else ""
105
106formatters = {
107 'default': StatusFormatter,
108 'verbose': VerboseStatusFormatter,
109 'urls': URLStatusFormatter
110}
111
112def get_status_formatter(options):
113 sf = formatters.get(options['format'])
114 if (not sf):
115 raise TwitterError(
116 "Unknown formatter '%s'" %(options['format']))
117 return sf()
118
119class Action(object):
120 pass
121
122class NoSuchAction(Action):
123 def __call__(self, twitter, options):
124 print >> sys.stderr, "No such action: ", options['action']
125 sys.exit(1)
126
127class StatusAction(Action):
128 def __call__(self, twitter, options):
129 statuses = self.getStatuses(twitter)
130 sf = get_status_formatter(options)
131 for status in statuses:
132 statusStr = sf(status)
133 if statusStr.strip():
134 print statusStr.encode(sys.stdout.encoding, 'replace')
135
136class FriendsAction(StatusAction):
137 def getStatuses(self, twitter):
138 return reversed(twitter.statuses.friends_timeline())
139
140class PublicAction(StatusAction):
141 def getStatuses(self, twitter):
142 return reversed(twitter.statuses.public_timeline())
143
144class RepliesAction(StatusAction):
145 def getStatuses(self, twitter):
146 return reversed(twitter.statuses.replies())
147
148class SetStatusAction(Action):
149 def __call__(self, twitter, options):
150 statusTxt = (u" ".join(options['extra_args'])
151 if options['extra_args']
152 else unicode(raw_input("message: ")))
153 status = (statusTxt.encode('utf8', 'replace'))
154 twitter.statuses.update(status=status)
155
156actions = {
157 'friends': FriendsAction,
158 'public': PublicAction,
159 'replies': RepliesAction,
160 'set': SetStatusAction,
161}
162
163def loadConfig(filename):
164 email = None
165 password = None
166 if os.path.exists(filename):
167 cp = SafeConfigParser()
168 cp.read([filename])
169 email = cp.get('twitter', 'email', None)
170 password = cp.get('twitter', 'password', None)
171 return email, password
172
173def main():
174 return main_with_args(sys.argv[1:])
175
176def main_with_args(args):
177 parse_args(args, options)
178
179 email, password = loadConfig(options['config_filename'])
180 if not options['email']: options['email'] = email
181 if not options['password']: options['password'] = password
182
183 if options['refresh'] and options['action'] == 'set':
184 print >> sys.stderr, "You can't repeatedly set your status, silly"
185 print >> sys.stderr, "Use 'twitter -h' for help."
186 sys.exit(1)
187 if options['email'] and not options['password']:
188 options['password'] = getpass("Twitter password: ")
189 twitter = Twitter(options['email'], options['password'])
190 action = actions.get(options['action'], NoSuchAction)()
191 try:
192 doAction = lambda : action(twitter, options)
193 if (options['refresh']):
194 while True:
195 doAction()
196 time.sleep(options['refresh_rate'])
197 else:
198 doAction()
199 except TwitterError, e:
200 print >> sys.stderr, e.args[0]
201 print >> sys.stderr, "Use 'twitter -h' for help."
202 sys.exit(1)
203 except KeyboardInterrupt:
204 pass
205