]> jfr.im git - z_archive/twitter.git/blob - twitter/cmdline.py
Merge commit 'origin/master' into twitter_agent
[z_archive/twitter.git] / twitter / cmdline.py
1 """
2 USAGE:
3
4 twitter [action] [options]
5
6 ACTIONS:
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
13 OPTIONS:
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
24 FORMATS 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
30 CONFIG FILES
31
32 The config file should contain your email and password like so:
33
34 [twitter]
35 email: <username>
36 password: <password>
37 """
38
39 import sys
40 import time
41 from getopt import getopt
42 from getpass import getpass
43 import re
44 import os.path
45 from ConfigParser import SafeConfigParser
46
47 from api import Twitter, TwitterError
48
49 options = {
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
60 def 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
87 class StatusFormatter(object):
88 def __call__(self, status):
89 return (u"%s %s" %(
90 status['user']['screen_name'], status['text']))
91
92 class 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
100 class 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
106 formatters = {
107 'default': StatusFormatter,
108 'verbose': VerboseStatusFormatter,
109 'urls': URLStatusFormatter
110 }
111
112 def 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
119 class Action(object):
120 pass
121
122 class NoSuchAction(Action):
123 def __call__(self, twitter, options):
124 print >> sys.stderr, "No such action: ", options['action']
125 sys.exit(1)
126
127 class 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
136 class FriendsAction(StatusAction):
137 def getStatuses(self, twitter):
138 return reversed(twitter.statuses.friends_timeline())
139
140 class PublicAction(StatusAction):
141 def getStatuses(self, twitter):
142 return reversed(twitter.statuses.public_timeline())
143
144 class RepliesAction(StatusAction):
145 def getStatuses(self, twitter):
146 return reversed(twitter.statuses.replies())
147
148 class 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
156 actions = {
157 'friends': FriendsAction,
158 'public': PublicAction,
159 'replies': RepliesAction,
160 'set': SetStatusAction,
161 }
162
163 def 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
173 def main():
174 return main_with_args(sys.argv[1:])
175
176 def 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