merged other changes.
[gwibber.git] / gwibber / microblog / twitter.py
1
2 """
3
4 Twitter interface for Gwibber
5 SegPhault (Ryan Paul) - 12/22/2007
6
7 """
8
9 from . import can, support
10 import urllib2, urllib, base64, re, simplejson
11 import gettext
12 _ = gettext.lgettext
13
14
15 PROTOCOL_INFO = {
16   "name": "Twitter",
17   "version": 0.1,
18   
19   "config": [
20     "private:password",
21     "username",
22     "message_color",
23     "receive_enabled",
24     "send_enabled",
25     "search_enabled",
26     "receive_count",
27   ],
28
29   "features": [
30     can.SEND,
31     can.RECEIVE,
32     can.SEARCH,
33     can.TAG,
34     can.REPLY,
35     can.RESPONSES,
36     can.DELETE,
37     can.RETWEET,
38     can.LIKE,
39     #can.THREAD,
40     can.THREAD_REPLY,
41     can.SEARCH_URL,
42     can.USER_MESSAGES,
43   ],
44 }
45
46 NICK_PARSE = re.compile("\B@([A-Za-z0-9_]+|@[A-Za-z0-9_]$)")
47 HASH_PARSE = re.compile("\B#([A-Za-z0-9_\-]+|@[A-Za-z0-9_\-]$)")
48
49 class Message:
50   def __init__(self, client, data):
51    try:
52     self.client = client
53     self.account = client.account
54     self.protocol = client.account["protocol"]
55     self.username = client.account["username"]
56     self.bgcolor = "message_color"
57     self.id = data["id"] or ''
58     self.time = support.parse_time(data["created_at"])
59     self.is_private  = False
60
61     if "user" in data:
62       user = data["user"]
63       self.reply_nick = data["in_reply_to_screen_name"]
64       self.reply_url = "https://twitter.com/%s/statuses/%s" % (data["in_reply_to_screen_name"], data["in_reply_to_status_id"])
65       self.reply_id = data["in_reply_to_status_id"]
66     elif "sender" in data:
67       user = data["sender"]
68       self.reply_nick = None
69       self.reply_url = None
70     elif "name" in data:
71       user = data
72
73     self.sender = user["name"]
74     self.sender_nick = user["screen_name"]
75     self.sender_id = user["id"]
76     self.sender_location = user["location"]
77     self.sender_followers_count = user["followers_count"]
78     self.image = user["profile_image_url"]
79     self.url = "https://twitter.com/%s/statuses/%s" % (user["screen_name"], data["id"])
80     self.profile_url = "gwibber:user/%s/%s" % (self.account.id, user["screen_name"])
81     self.external_profile_url = "https://twitter.com/%s" % user["screen_name"]
82
83     if "text" in data:
84       self.text = data["text"]
85       self.html_string = '<span class="text">%s</span>' % \
86           HASH_PARSE.sub('#<a class="inlinehash" href="gwibber:tag/\\1">\\1</a>',
87           NICK_PARSE.sub('@<a class="inlinenick" href="gwibber:user/'+self.account.id+'/\\1">\\1</a>',
88           support.linkify(self.text)))
89       self.is_reply = re.compile("@%s[\W]+|@%s$" % (self.username, self.username)).search(self.text)
90       self.reply_nick = ''
91       self.reply_url = ''
92     else:
93       # if reached a protected gwibber:user tab then do some things differently
94       if "name" in data:
95         self.url = self.profile_url = self.external_profile_url = "https://twitter.com/%s" % data["screen_name"]
96         self.is_reply = False
97         if data["protected"] == True:
98           self.text = _("This user has protected their updates.") + ' ' + _("You need to send a request before you can view this person's timeline.") + ' ' + _("Send request...")
99           self.html_string = '<p><b>' + _("This user has protected their updates.") + '</b><p>' + _("You need to send a request before you can view this person's timeline.") + '<p><a href="' + self.url + '">' + _("Send request...") + '</a>'
100         else:
101           self.text = self.html_string = ''
102
103     if "in_reply_to_screen_name" in data and "in_reply_to_status_id" in data and data["in_reply_to_status_id"]:
104       self.reply_nick = data["in_reply_to_screen_name"]
105       self.reply_url = "https://twitter.com/%s/statuses/%s" % (self.reply_nick, data["in_reply_to_status_id"])
106    except Exception:
107     from traceback import format_exc
108     print(format_exc())
109
110 class SearchResult:
111   def __init__(self, client, data, query = None):
112     self.client = client
113     self.account = client.account
114     self.protocol = client.account["protocol"]
115     self.username = client.account["username"]
116     self.sender = data["from_user"]
117     self.sender_nick = data["from_user"]
118     self.sender_id = data["from_user_id"]
119     self.time = support.parse_time(data["created_at"])
120     self.text = data["text"]
121     self.id = data["id"]
122     self.image = data["profile_image_url"]
123     self.bgcolor = "message_color"
124     self.url = "https://twitter.com/%s/statuses/%s" % (data["from_user"], data["id"])
125     self.profile_url = "gwibber:user/%s/%s" % (self.account.id, data["from_user"])
126     self.external_profile_url = "https://twitter.com/%s" % data["from_user"]
127
128     if query: html = support.highlight_search_results(self.text, query)
129     else: html = self.text
130     
131     self.html_string = '<span class="text">%s</span>' % \
132       HASH_PARSE.sub('#<a class="inlinehash" href="gwibber:tag/\\1">\\1</a>',
133       NICK_PARSE.sub('@<a class="inlinenick" href="gwibber:user/'+self.account.id+'/\\1">\\1</a>',
134         support.linkify(self.text)))
135
136     self.is_reply = re.compile("@%s[\W]+|@%s$" % (self.username, self.username)).search(self.text) 
137
138 class Client:
139   def __init__(self, acct):
140     self.account = acct
141
142   def send_enabled(self):
143     return self.account["send_enabled"] and \
144       self.account["username"] != None and \
145       self.account["private:password"] != None
146
147   def receive_enabled(self):
148     return self.account["receive_enabled"] and \
149       self.account["username"] != None and \
150       self.account["private:password"] != None
151
152   def get_auth(self):
153     return "Basic %s" % base64.encodestring(
154       ("%s:%s" % (self.account["username"], self.account["private:password"]))).strip()
155
156   def connect(self, url, data = None):
157     return urllib2.urlopen(urllib2.Request(
158       url, data, headers = {"Authorization": self.get_auth()})).read()
159
160   def get_messages(self):
161     return simplejson.loads(self.connect(
162       "https://twitter.com/statuses/friends_timeline.json" +'?'+
163       urllib.urlencode({"count": self.account["receive_count"] or "50"})))
164
165   def get_user_messages(self, screen_name):
166     try:
167       return simplejson.loads(self.connect(
168         "https://twitter.com/statuses/user_timeline/"+ screen_name + ".json" +'?'+
169           urllib.urlencode({"count": self.account["receive_count"] or "50"})))
170     except Exception:
171       profile = [simplejson.loads(self.connect(
172         "https://twitter.com/users/show/"+ screen_name +".json"))]
173       return profile
174
175   def get_replies(self):
176     return simplejson.loads(self.connect(
177       "https://twitter.com/statuses/replies.json" +'?'+
178         urllib.urlencode({"count": self.account["receive_count"] or "50"})))
179
180   def get_direct_messages(self):
181     return simplejson.loads(self.connect(
182       "https://twitter.com/direct_messages.json"))
183
184   def get_search_data(self, query):
185     return simplejson.loads(urllib2.urlopen(
186       urllib2.Request("http://search.twitter.com/search.json",
187         urllib.urlencode({"q": query}))).read())
188
189   def search(self, query):
190     for data in self.get_search_data(query)["results"]:
191       yield SearchResult(self, data, query)
192
193   def search_url(self, query):
194     urls = support.unshorten_url(query)
195     for data in self.get_search_data(" OR ".join(urls))["results"]:
196       if any(item in data["text"] for item in urls):
197         yield SearchResult(self, data, query)
198
199   def tag(self, query):
200     for data in self.get_search_data("#%s" % query)["results"]:
201       yield SearchResult(self, data, "#%s" % query)
202
203   def responses(self):
204     for data in self.get_replies():
205       yield Message(self, data)
206
207     for data in self.get_direct_messages():
208       m = Message(self, data)
209       m.is_private = True
210       yield m
211
212   def receive(self):
213     for data in self.get_messages():
214       yield Message(self, data)
215
216   def user_messages(self, screen_name):
217     for data in self.get_user_messages(screen_name):
218       yield Message(self, data)
219
220   def delete(self, message):
221     return simplejson.loads(self.connect(
222       "https://twitter.com/statuses/destroy/%s.json" % message.id, {}))
223
224   def like(self, message):
225     return simplejson.loads(self.connect(
226       "https://twitter.com/favorites/create/%s.json" % message.id, {}))
227
228   def send(self, message):
229     data = simplejson.loads(self.connect(
230       "https://twitter.com/statuses/update.json",
231         urllib.urlencode({"status":message, "source": "gwibbernet"})))
232     return Message(self, data)
233
234   def send_thread(self, message, target):
235     data = simplejson.loads(self.connect(
236       "https://twitter.com/statuses/update.json",
237         urllib.urlencode({"status":message,
238           "in_reply_to_status_id":target.id, "source": "gwibbernet"})))
239     return Message(self, data)
240