Mercurial > libervia-backend
annotate plugins/plugin_misc_cs.py @ 218:5c68a65548c3
Plugin CS: fixed forgotten debug stuff, CS plugin is now working again
User agent changed to "Salut à Toi XMPP/CS Plugin".
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 27 Dec 2010 18:37:56 +0100 |
parents | 18b0cf49a6f1 |
children | 782319a64ac6 |
rev | line source |
---|---|
101 | 1 #!/usr/bin/python |
2 # -*- coding: utf-8 -*- | |
3 | |
4 """ | |
5 SAT plugin for managing xep-0045 | |
6 Copyright (C) 2009, 2010 Jérôme Poisson (goffi@goffi.org) | |
7 | |
8 This program is free software: you can redistribute it and/or modify | |
9 it under the terms of the GNU General Public License as published by | |
10 the Free Software Foundation, either version 3 of the License, or | |
11 (at your option) any later version. | |
12 | |
13 This program is distributed in the hope that it will be useful, | |
14 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 GNU General Public License for more details. | |
17 | |
18 You should have received a copy of the GNU General Public License | |
19 along with this program. If not, see <http://www.gnu.org/licenses/>. | |
20 """ | |
21 | |
22 from logging import debug, info, warning, error | |
23 from twisted.words.xish import domish | |
24 from twisted.internet import protocol, defer, threads, reactor | |
25 from twisted.words.protocols.jabber import client, jid, xmlstream | |
26 from twisted.words.protocols.jabber import error as jab_error | |
27 from twisted.words.protocols.jabber.xmlstream import IQ | |
28 from twisted.web.client import getPage | |
29 import os.path | |
30 import pdb | |
31 import random | |
32 | |
33 from zope.interface import implements | |
34 | |
35 from wokkel import disco, iwokkel, data_form | |
106 | 36 from tools.xml_tools import XMLUI |
101 | 37 import urllib |
108
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
38 import webbrowser |
101 | 39 |
102 | 40 from BeautifulSoup import BeautifulSoup |
106 | 41 import re |
101 | 42 |
43 | |
44 PLUGIN_INFO = { | |
45 "name": "CouchSurfing plugin", | |
46 "import_name": "CS", | |
47 "type": "Misc", | |
48 "protocols": [], | |
49 "dependencies": [], | |
50 "main": "CS_Plugin", | |
51 "handler": "no", | |
52 "description": _(u"""This plugin allow to manage your CouchSurfing account throught your SàT frontend""") | |
53 } | |
54 | |
218
5c68a65548c3
Plugin CS: fixed forgotten debug stuff, CS plugin is now working again
Goffi <goffi@goffi.org>
parents:
109
diff
changeset
|
55 AGENT = 'Salut à Toi XMPP/CS Plugin' |
101 | 56 |
57 class CS_Plugin(): | |
58 | |
59 params = """ | |
60 <params> | |
61 <individual> | |
62 <category name="CouchSurfing"> | |
63 <param name="Login" type="string" /> | |
64 <param name="Password" type="password" /> | |
65 </category> | |
66 </individual> | |
67 </params> | |
68 """ | |
69 | |
70 def __init__(self, host): | |
71 info(_("Plugin CS initialization")) | |
72 self.host = host | |
73 #parameters | |
74 host.memory.importParams(CS_Plugin.params) | |
75 #menu | |
218
5c68a65548c3
Plugin CS: fixed forgotten debug stuff, CS plugin is now working again
Goffi <goffi@goffi.org>
parents:
109
diff
changeset
|
76 host.importMenu(_("Plugin"), "CouchSurfing", self.menuSelected, help_string = _("Launch CoushSurfing management interface")) |
106 | 77 self.data=self.host.memory.getPrivate('plugin_cs_data') or {} #TODO: delete cookies/data after a while |
107 | 78 self.host.registerGeneralCB("plugin_CS_sendMessage", self.sendMessage) |
108
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
79 self.host.registerGeneralCB("plugin_CS_showUnreadMessages", self.showUnreadMessages) |
102 | 80 |
81 def erroCB(self, e, id): | |
82 """Called when something is going wrong when contacting CS website""" | |
107 | 83 #pdb.set_trace() |
102 | 84 message_data={"reason": "connection error", "message":_(u"Impossible to contact CS website, please check your login/password, connection or try again later")} |
85 self.host.bridge.actionResult("ERROR", id, message_data) | |
86 | |
101 | 87 def menuSelected(self, id, profile): |
88 """Called when the couchsurfing menu item is selected""" | |
89 login = self.host.memory.getParamA("Login", "CouchSurfing", profile_key=profile) | |
90 password = self.host.memory.getParamA("Password", "CouchSurfing", profile_key=profile) | |
91 if not login or not password: | |
92 message_data={"reason": "uncomplete", "message":_(u"You have to fill your CouchSurfing login & password in parameters before using this interface")} | |
93 self.host.bridge.actionResult("ERROR", id, message_data) | |
94 return | |
95 | |
96 post_data = urllib.urlencode({'auth_login[un]':login,'auth_login[pw]':password,'auth_login[action]':'Login...'}) | |
97 | |
106 | 98 if not self.data.has_key(profile): |
99 self.data[profile] = {'cookies':{}} | |
100 else: | |
101 self.data[profile]['cookies'] = {} | |
218
5c68a65548c3
Plugin CS: fixed forgotten debug stuff, CS plugin is now working again
Goffi <goffi@goffi.org>
parents:
109
diff
changeset
|
102 |
107 | 103 d = getPage('http://www.couchsurfing.org/login.html', method='POST', postdata=post_data, headers={'Content-Type':'application/x-www-form-urlencoded'} , agent=AGENT, cookies=self.data[profile]['cookies']) |
106 | 104 d.addCallback(self.__connectionCB, id, profile) |
107 | 105 d.addErrback(self.erroCB, id) |
102 | 106 |
218
5c68a65548c3
Plugin CS: fixed forgotten debug stuff, CS plugin is now working again
Goffi <goffi@goffi.org>
parents:
109
diff
changeset
|
107 #self.host.bridge.actionResult("SUPPRESS", id, {}) |
106 | 108 |
109 | |
110 #pages parsing callbacks | |
111 def savePage(self, name, html): | |
218
5c68a65548c3
Plugin CS: fixed forgotten debug stuff, CS plugin is now working again
Goffi <goffi@goffi.org>
parents:
109
diff
changeset
|
112 f = open ('/tmp/CS_'+name+'.html','w') |
106 | 113 f.write(html) |
114 f.close() | |
115 print "page [%s] sauvee" % name | |
116 #pdb.set_trace() | |
117 | |
118 def __connectionCB(self, html, id, profile): | |
119 print 'Response received' | |
218
5c68a65548c3
Plugin CS: fixed forgotten debug stuff, CS plugin is now working again
Goffi <goffi@goffi.org>
parents:
109
diff
changeset
|
120 #self.savePage('principale',html) |
106 | 121 soup = BeautifulSoup(html) |
122 self.data[profile]['user_nick'] = soup.find('a','item_link',href='/home.html').contents[0] | |
123 self.data[profile]['user_name'] = soup.html.head.title.string.split(' - ')[1] | |
124 #unread messages | |
125 try: | |
126 self.data[profile]['unread_messages'] = int(soup.find(lambda tag: tag.name=='div' and ('class','item_bubble') in tag.attrs and tag.find('a', href="/messages.html?message_status=inbox")).find(text=True)) | |
127 except: | |
128 self.data[profile]['unread_messages'] = 0 | |
129 #unread couchrequest messages | |
130 try: | |
131 self.data[profile]['unread_CR_messages'] = int(soup.find(lambda tag: tag.name=='div' and ('class','item_bubble') in tag.attrs and tag.find('a', href="/couchmanager")).find(text=True)) | |
132 except: | |
133 self.data[profile]['unread_CR_messages'] = 0 | |
134 | |
135 #if we have already the list of friend, no need to make new requests | |
136 if not self.data[profile].has_key('friends'): | |
137 self.data[profile]['friends'] = {} | |
138 d = getPage('http://www.couchsurfing.org/connections.html?type=myfriends&show=10000', agent=AGENT, cookies=self.data[profile]['cookies']) | |
139 d.addCallback(self.__friendsPageCB, id=id, profile=profile) | |
140 d.addErrback(self.erroCB, id) | |
141 else: | |
142 self.host.bridge.actionResult("XMLUI", id, {"type":"window", "xml":self.__buildUI(self.data[profile])}) | |
143 | |
144 def __buildUI(self, data): | |
145 """Build the XML UI of the plugin | |
146 @param data: data store for the profile""" | |
147 user_nick = data['user_nick'] | |
148 user_name = data['user_name'] | |
149 unread_mess = data['unread_messages'] | |
150 unread_CR_mess = data['unread_CR_messages'] | |
151 friends_list = data['friends'].keys() | |
152 friends_list.sort() | |
107 | 153 interface = XMLUI('window','tabs', title='CouchSurfing management') |
106 | 154 interface.addCategory(_("Messages"), "vertical") |
107 | 155 interface.addText(_("G'day %(name)s, you have %(nb_message)i unread message%(plural_mess)s and %(unread_CR_mess)s unread couch request message%(plural_CR)s\nIf you want to send a message, select the recipient(s) in the list below") % {'name':user_name, 'nb_message':unread_mess, 'plural_mess':'s' if unread_mess>1 else '', 'unread_CR_mess': unread_CR_mess, 'plural_CR':'s' if unread_CR_mess>1 else ''}) |
108
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
156 if unread_mess: |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
157 interface.addButton('plugin_CS_showUnreadMessages', 'showUnreadMessages', _('Show unread message%(plural)s in external web browser') % {'plural':'s' if unread_mess>1 else ''}) |
107 | 158 interface.addList(friends_list, 'friends', style=['multi']) |
159 interface.changeLayout('pairs') | |
160 interface.addLabel(_("Subject")) | |
161 interface.addString('subject') | |
162 interface.changeLayout('vertical') | |
163 interface.addLabel(_("Message")) | |
164 interface.addText("(use %name% for contact name and %firstname% for guessed first name)") | |
165 interface.addTextBox('message') | |
166 interface.addButton('plugin_CS_sendMessage', 'sendMessage', _('send'), fields_back=['friends','subject','message']) | |
109
18b0cf49a6f1
Plugin CS minor changes (Events & Couch Search tabs temporarly removed, open_new_tab user to open link in browser)
Goffi <goffi@goffi.org>
parents:
108
diff
changeset
|
167 #interface.addCategory(_("Events"), "vertical") #TODO: coming soon, hopefuly :) |
18b0cf49a6f1
Plugin CS minor changes (Events & Couch Search tabs temporarly removed, open_new_tab user to open link in browser)
Goffi <goffi@goffi.org>
parents:
108
diff
changeset
|
168 #interface.addCategory(_("Couch search"), "vertical") |
106 | 169 return interface.toXml() |
170 | |
171 def __meetingPageCB(self, html): | |
172 """Called when the meeting page has been received""" | |
173 | |
174 def __friendsPageCB(self, html, id, profile): | |
175 """Called when the friends list page has been received""" | |
176 self.savePage('friends',html) | |
107 | 177 soup = BeautifulSoup(html.replace('"formtable width="400','"formtable" width="400"')) #CS html fix #TODO: report the bug to CS dev team |
106 | 178 friends = self.data[profile]['friends'] |
179 for _tr in soup.findAll('tr', {'class':re.compile("^msgRow*")}): #we parse the row with friends infos | |
180 _nobr = _tr.find('nobr') #contain the friend name | |
181 friend_name = unicode(_nobr.string) | |
182 friend_link = u'http://www.couchsurfing.org'+_nobr.parent['href'] | |
183 regex_href = re.compile(r'/connections\.html\?id=([^&]+)') | |
184 a_tag = _tr.find('a',href=regex_href) | |
185 friend_id = regex_href.search(unicode(a_tag)).groups()[0] | |
186 | |
187 debug(_("CS friend found: %(friend_name)s (id: %(friend_id)s, link: %(friend_link)s)") % {'friend_name':friend_name, 'friend_id':friend_id, 'friend_link':friend_link}) | |
188 friends[friend_name] = {'link':friend_link,'id':friend_id} | |
189 a = soup.find('td','barmiddle next').a #is there several pages ? | |
190 if a: | |
191 #yes, we parse the next page | |
192 d = getPage('http://www.couchsurfing.org/'+str(a['href']), agent=AGENT, cookies=self.data[profile]['cookies']) | |
193 d.addCallback(self.__friendsPageCB, id=id, profile=profile) | |
194 d.addErrback(self.erroCB, id) | |
195 else: | |
196 #no, we show the result | |
197 self.host.bridge.actionResult("XMLUI", id, {"type":"window", "xml":self.__buildUI(self.data[profile])}) | |
198 #and save the data | |
199 self.host.memory.setPrivate('plugin_cs_data', self.data) | |
107 | 200 |
201 def __sendMessage(self, answer, subject, message, data, recipient_list, id, profile): | |
202 """Send actually the message | |
203 @param subject: subject of the message | |
204 @param message: body of the message | |
205 @param data: data of the profile | |
206 @param recipient_list: list of friends names, names are removed once message is sent | |
207 @param id: id of the action | |
208 @param profile: profile who launched the action | |
209 """ | |
210 if answer: | |
211 if not 'Here is a copy of the email that was sent' in answer: | |
212 error(_("INTERNAL ERROR: no confirmation of message sent by CS, maybe the site has been modified ?")) | |
213 #TODO: throw a warning to the frontend, saying that maybe the message has not been sent and to contact dev of this plugin | |
214 #debug(_('HTML answer: %s') % answer) | |
215 if recipient_list: | |
216 recipient = recipient_list.pop() | |
217 try: | |
218 friend_id = data['friends'][recipient]['id'] | |
219 except KeyError: | |
220 error('INTERNAL ERROR: unknown friend') | |
221 return #send an error to the frontend | |
222 mess = message.replace('%name%',recipient).replace('%firstname%',recipient.split(' ')[0]) | |
223 info(_('Sending message to %s') % recipient) | |
224 debug(_("\nsubject: %(subject)s\nmessage: \n---\n%(message)s\n---\n\n") % {'subject':subject,'message':mess}) | |
225 post_data = urllib.urlencode({'email[subject]':subject.encode('utf-8'),'email[body]':mess.encode('utf-8'),'email[id]':friend_id,'email[action]':'Send Message','email[replied_id]':'','email[couchsurf]':'','email[directions_to_add]':''}) | |
226 d = getPage("http://www.couchsurfing.org/send_message.html", method='POST', postdata=post_data, headers={'Content-Type':'application/x-www-form-urlencoded'} , agent=AGENT, cookies=data['cookies']) | |
227 d.addCallback(self.__sendMessage, subject, message, data, recipient_list, id, profile) | |
228 d.addErrback(self.erroCB, id) | |
229 else: | |
230 interface = XMLUI('window', title=_('Message sent')) #TODO: create particular actionResult for alerts ? | |
231 interface.addText(_('The message has been sent to every recipients')) | |
232 self.host.bridge.actionResult("XMLUI", id, {"type":"window", "xml":interface.toXml()}) | |
102 | 233 |
107 | 234 def sendMessage(self, id, data, profile): |
235 """Called to send a message to a friend | |
236 @param data: dict with the following keys: | |
237 friend: name of the recipient | |
238 subject: subject of the message | |
239 message: body of the message, with the following special keywords: | |
240 - %name%: name of the friend | |
241 - %firstname%: guessed first name of the friend (currently the first part of the name) | |
242 """ | |
243 if not data['friends']: | |
244 message_data={"reason": "bad data", "message":_(u"There is not recipient selected for this message !")} | |
245 self.host.bridge.actionResult("ERROR", id, message_data) | |
246 return | |
247 friends = data['friends'].split('\t') | |
248 subject = data['subject'] | |
249 message = data['message'] | |
250 print "send message \o/ :) :) :)" | |
251 info(_("sending message to %(friends)s with subject [%(subject)s]" % {'friends':friends, 'subject':subject})) | |
252 self.__sendMessage(None, subject, message, self.data[profile], friends, id, profile) | |
108
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
253 |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
254 def __showUnreadMessages2(self, html, id, profile): |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
255 """Called when the inbox page has been received""" |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
256 #FIXME: that's really too fragile, only works if the unread messages are in the first page, and it would be too resources consuming for the website to DL each time all pages. In addition, the show attribute doesn't work as expected. |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
257 soup = BeautifulSoup(html) |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
258 for tag in soup.findAll(lambda tag: tag.name=='strong' and tag.a and tag.a['href'].startswith('messages.html?message_status=inbox')): |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
259 link = "http://www.couchsurfing.org/"+str(tag.a['href']) |
109
18b0cf49a6f1
Plugin CS minor changes (Events & Couch Search tabs temporarly removed, open_new_tab user to open link in browser)
Goffi <goffi@goffi.org>
parents:
108
diff
changeset
|
260 webbrowser.open_new_tab(link) #TODO: the web browser need to already have CS cookies (i.e. already be opened & logged on CS, or be permanently loggued), a warning to the user should be sent/or a balloon-tip |
108
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
261 |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
262 def showUnreadMessages(self, id, data, profile): |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
263 """Called when user want to see all unread messages in the external browser""" |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
264 d = getPage("http://www.couchsurfing.org/messages.html?message_status=inbox&show=10000", agent=AGENT, cookies=self.data[profile]['cookies']) |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
265 d.addCallback(self.__showUnreadMessages2, id, profile) |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
266 d.addErrback(self.erroCB, id) |
e24e080e6b16
CS plugin: unread messages can now be openned in external web browser
Goffi <goffi@goffi.org>
parents:
107
diff
changeset
|
267 |