updated on Thu Jan 19 20:01:47 UTC 2012
[aur-mirror.git] / gnome-shell-extensions-pidgin / extension.js
blobf68a02dd1c4744397b65ec4dad4c16e44128e454
1 /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
3 /*
4  * Pidgin GnomeShell Integration.
5  *
6  * Credits to the author of Gajim extension as this extension code was modified
7  * from it.
8  *
9  */
11 const DBus = imports.dbus;
12 const GLib = imports.gi.GLib;
13 const Lang = imports.lang;
14 const St = imports.gi.St;
15 const Main = imports.ui.main;
16 const MessageTray = imports.ui.messageTray;
17 const Shell = imports.gi.Shell;
18 const TelepathyClient = imports.ui.telepathyClient;
19 const Tp = imports.gi.TelepathyGLib;
21 const Gettext = imports.gettext.domain('gnome-shell-extensions');
22 const _ = Gettext.gettext;
24 function wrappedText(text, sender, timestamp, direction, chat) {
25     let currentTime = (Date.now() / 1000);
26         let type = Tp.ChannelTextMessageType.NORMAL;
28     if (timestamp == null) {
29         timestamp = currentTime;
30     }
31         
32         text = _fixText(text);
33         if (chat && direction != TelepathyClient.NotificationDirection.SENT){
34                 text = sender + ": " + text;
35         }
36         if (text.substr(0, 3) == '/me' && direction != TelepathyClient.NotificationDirection.SENT) {
37                 text = text.substr(4);
38                 type = Tp.ChannelTextMessageType.ACTION;
39         }
41     return {
42         text: text,
43                 messageType: type,
44         sender: sender,
45         timestamp: timestamp,
46         direction: direction
47     };
51 function PidginNotification(source) {
52     this._init(source);
55 function _fixText(text) {
56     // remove all tags
57     let _text = text.replace(/<\/?[^>]+(>|$)/g, "");
58     return _text;
61 PidginNotification.prototype = {
62     __proto__: TelepathyClient.ChatNotification.prototype
65 function PidginChatNotification(source) {
66     this._init(source);
69 PidginChatNotification.prototype = {
70     __proto__: TelepathyClient.ChatNotification.prototype
73 function Source(client, account, author, initialMessage, conversation, chat, flag) {
74     this._init(client, account, author, initialMessage, conversation, chat, flag);
77 Source.prototype = {
78     __proto__: MessageTray.Source.prototype,
80     _init: function(client, account, author, initialMessage, conversation, chat, flag) {
82         let proxy = client.proxy();
83         this._client = client;
84         this._author = author;
85         this._authors = {};
86         this._authors[author] = true;
87         this._account = account;
88         this._conversation = conversation;
89         this._chat = chat;
90         this._initialMessage = initialMessage;
91         this._initialFlag = flag;
92         this._iconUri = null;
93         this._presence = 'online';
94         this.isChat = true;
95         if (chat) {
96             this._notification = new PidginChatNotification(this);
97         } else {
98             this._notification = new PidginNotification(this);
99         }
100         this._notification.setUrgency(MessageTray.Urgency.HIGH);
101         this._notification.enableScrolling(true);
103         proxy.PurpleConversationGetTitleRemote(this._conversation, Lang.bind(this, this._async_set_title));
104         if (chat) proxy.PurpleConvChatRemote(this._conversation, Lang.bind(this, this._async_set_conversation_im));
105     },
107     _async_set_author_buddy: function (author_buddy) {
108         let proxy = this._client.proxy();
109         this._author_buddy = author_buddy;
110         proxy.PurpleConvImRemote(this._conversation, Lang.bind(this, this._async_set_conversation_im));
111     },
113     _async_set_conversation_im: function (conversation_im) {
114         let proxy = this._client.proxy();
115         this._conversation_im = conversation_im;
116         proxy.PurpleBuddyGetIconRemote(this._author_buddy, Lang.bind(this, this._async_get_icon));
117     },
119     _async_set_title: function (title) {
120         let proxy = this._client.proxy();
121         this.title = _fixText(title);
122         if (!this._chat) {
123             proxy.PurpleFindBuddyRemote(this._account, this._author, Lang.bind(this, this._async_set_author_buddy))
124         } else {
125             this._start();
126         }
127     },
129     _async_get_icon: function (iconobj) {
130         let proxy = this._client.proxy();
131         if (iconobj) {
132             proxy.PurpleBuddyIconGetFullPathRemote(iconobj, Lang.bind(this, this._async_set_icon));
133         } else {
134             this._start();
135         }
136     },
138     _async_set_icon: function (iconpath) {
139         this._iconUri = 'file://' + iconpath;
140         this._start();
141     },
143     _start: function () {
145         let proxy = this._client.proxy();
146         MessageTray.Source.prototype._init.call(this, this.title);
148         this._setSummaryIcon(this.createNotificationIcon());
150         Main.messageTray.add(this);
151         this.pushNotification(this._notification);
153         let direction = null;
154         if (this._initialFlag == 1) {
155             direction = TelepathyClient.NotificationDirection.SENT;
156         } else if (this._initialFlag == 2) {
157             direction = TelepathyClient.NotificationDirection.RECEIVED;
158         }
159         
160         let message = wrappedText(this._initialMessage, this._author, null, direction, this._chat);
161         this._notification.appendMessage(message, false);
163         if (this._chat) {
164             this._messageDisplayedId = proxy.connect('DisplayedChatMsg', Lang.bind(this, this._onDisplayedChatMessage));
165         } else {
166             this._buddyStatusChangeId = proxy.connect('BuddyStatusChanged', Lang.bind(this, this._onBuddyStatusChange));
167             this._buddySignedOffId = proxy.connect('BuddySignedOff', Lang.bind(this, this._onBuddySignedOff));
168             this._buddySignedOnId = proxy.connect('BuddySignedOn', Lang.bind(this, this._onBuddySignedOn));
169             this._messageDisplayedId = proxy.connect('DisplayedImMsg', Lang.bind(this, this._onDisplayedImMessage));
170         }
171         this._deleteConversationId = proxy.connect('DeletingConversation', Lang.bind(this, this._onDeleteConversation));
173         this.notify();
174     },
176     destroy: function () {
177         let proxy = this._client.proxy();
178         proxy.disconnect(this._buddyStatusChangeId);
179         proxy.disconnect(this._buddySignedOffId);
180         proxy.disconnect(this._buddySignedOnId);
181         proxy.disconnect(this._deleteConversationId);
182         proxy.disconnect(this._messageDisplayedId);
183         MessageTray.Source.prototype.destroy.call(this);
184     },
185     
187     createNotificationIcon: function() {
188         let iconBox = new St.Bin({ style_class: 'avatar-box' });
189         iconBox._size = this.ICON_SIZE;
191         if (!this._iconUri) {
192             iconBox.child = new St.Icon({ icon_name: 'avatar-default',
193                                           icon_type: St.IconType.FULLCOLOR,
194                                           icon_size: iconBox._size });
195         } else {
196             let textureCache = St.TextureCache.get_default();
197             iconBox.child = textureCache.load_uri_async(this._iconUri, iconBox._size, iconBox._size);
198         }
199         return iconBox;
200     },
202     open: function(notification) {
203         let app = Shell.AppSystem.get_default().get_app('pidgin.desktop');
204         app.activate_window(null, global.get_current_time());
205         if (this._chat) {
206             this.destroy();
207         }
208     },
210     notify: function () {
211         let proxy = this._client.proxy();
212         proxy.PurpleConversationHasFocusRemote(this._conversation, Lang.bind(this, this._async_notify));
213     },
215     _async_notify: function (stats) {
216         if (!stats) {
217             MessageTray.Source.prototype.notify.call(this, this._notification);
218             this._notification.scrollTo(St.Side.BOTTOM);
219         }
220     },
222     respond: function(text) {
223         let proxy = this._client.proxy();
224         let _text = GLib.markup_escape_text(text, -1);
225         if(this._chat){
226                 proxy.PurpleConvChatSendRemote(this._conversation_im, _text);
227         }
228         else{
229                 proxy.PurpleConvImSendRemote(this._conversation_im, _text);
230         }
231     },
233     _onBuddyStatusChange: function (emitter, buddy, old_status_id, new_status_id) {
234         if (!this.title) return;
236         let self = this;
238         let proxy = this._client.proxy();
240         if (buddy != this._author_buddy) return;
242         let presenceInfo = {};
244         let notify_presence = function () {
246             let presence = presenceInfo.presence;
247             let message = presenceInfo.message;
248             if (self._presence == presence) return;
249     
250             let title = self.title;
251             let presenceMessage, shouldNotify;
252             if (presence == "away") {
253                 presenceMessage = _("%s is away.").format(title);
254             } else if (presence == "available") {
255                 presenceMessage = _("%s is available.").format(title);
256             } else if (presence == "dnd") {
257                 presenceMessage = _("%s is busy.").format(title);
258             } else {
259                 return;
260             }
261     
262             self._presence = presence;
263     
264             if (message)
265                 presenceMessage += ' <i>(' + _fixText(message) + ')</i>';
266     
267             self._notification.appendPresence(presenceMessage, false);
268         };
270         let set_presence_message = function (message) {
271             presenceInfo.message = message;
272             notify_presence();
273         };
274         let set_presence = function (presence) {
275             presenceInfo.presence = presence;
276             proxy.PurpleStatusGetAttrStringRemote(new_status_id, 'message', set_presence_message);
277         };
279         proxy.PurpleStatusGetIdRemote(new_status_id, set_presence);
281     },
283     _onBuddySignedOff: function(emitter, buddy) {
284         if (buddy != this._author_buddy) return;
286         let shouldNotify = this._presence != 'offline';
287         let presenceMessage = _("%s is offline.").format(this.title);
288         this._notification.appendPresence(presenceMessage, shouldNotify);
289         this._presence = 'offline';
290         if (shouldNotify) 
291             this.notify();
292     },
294     _onBuddySignedOn: function(emitter, buddy) {
295         if (buddy != this._author_buddy) return;
297         let shouldNotify = this._presence == 'offline';
298         let presenceMessage = _("%s is online.").format(this.title);
299         this._notification.appendPresence(presenceMessage, shouldNotify);
300         this._presence = 'online';
301         if (shouldNotify) 
302             this.notify();
303     },
305     _onDeleteConversation: function(emitter, conversation) {
306         if (conversation != this._conversation) return;
307         this.destroy();
308     },
310     _onDisplayedChatMessage: function(emitter, account, author, text, conversation, flag) {
311         global.log(flag);
312         if (text && (this._conversation == conversation) && (flag & 3) == 2) {
313             // accept messages from people who sent us something with our nick in it
314             if ((flag & 32) == 32) {
315                 this._authors[author] = true;
316             }
317             if (author in this._authors) {
318                 let message = wrappedText(text, author, null, TelepathyClient.NotificationDirection.RECEIVED, this._chat);
319                 this._notification.appendMessage(message, false);
320                 this.notify();
321             }
322         }
323         else if(flag == 1){
324             let message = wrappedText(text, author, null, TelepathyClient.NotificationDirection.SENT, this._chat);
325             this._notification.appendMessage(message, false);
326             this.notify();
327         }
329     },
331     _onDisplayedImMessage: function(emitter, account, author, text, conversation, flag) {
333         if (text && (this._conversation == conversation)) {
334             let direction = null;
335             if (flag == 1) {
336                 direction = TelepathyClient.NotificationDirection.SENT;
337             } else if (flag == 2) {
338                 direction = TelepathyClient.NotificationDirection.RECEIVED;
339             }
340             if (direction != null) {
341                 let message = wrappedText(text, author, null, direction);
342                 this._notification.appendMessage(message, false);
343             }
345             if (direction == TelepathyClient.NotificationDirection.RECEIVED) {
346                 this.notify();
347             }
348         }
350     }
354 const PidginIface = {
355     name: 'im.pidgin.purple.PurpleInterface',
356     properties: [],
357     methods: [
358         {name: 'PurpleGetIms', inSignature: '', outSignature: 'ai'},
359         {name: 'PurpleAccountsGetAllActive', inSignature: '', outSignature: 'ai'},
360         {name: 'PurpleConversationGetType', inSignature: 'i', outSignature: 'u'},
361         {name: 'PurpleFindBuddies', inSignature: 'is', outSignature: 'ai'},
362         {name: 'PurpleFindBuddy', inSignature: 'is', outSignature: 'i'},
363         {name: 'PurpleAccountGetAlias', inSignature: 'i', outSignature: 's'},
364         {name: 'PurpleAccountGetNameForDisplay', inSignature: 'i', outSignature: 's'},
365         {name: 'PurpleBuddyGetAlias', inSignature: 'i', outSignature: 's'},
366         {name: 'PurpleBuddyGetName', inSignature: 'i', outSignature: 's'},
367         {name: 'PurpleStatusGetId', inSignature: 'i', outSignature: 's'},
368         {name: 'PurpleStatusGetAttrString', inSignature: 'is', outSignature: 's'},
369         {name: 'PurpleBuddyIconGetFullPath', inSignature: 'i', outSignature: 's'},
370         {name: 'PurpleBuddyGetIcon', inSignature: 'i', outSignature: 'i'},
371         {name: 'PurpleConvImSend', inSignature: 'is', outSignature: ''},
372         {name: 'PurpleConvChatSend', inSignature: 'is', outSignature: ''},
373         {name: 'PurpleConvIm', inSignature: 'i', outSignature: 'i'},
374         {name: 'PurpleConvChat', inSignature: 'i', outSignature: 'i'},
375         {name: 'PurpleConvImGetIcon', inSignature: 'i', outSignature: 'i'},
376         {name: 'PurpleConversationGetName', inSignature: 'i', outSignature: 's'},
377         {name: 'PurpleConversationGetAccount', inSignature: 'i', outSignature: 's'},
378         {name: 'PurpleConversationGetMessageHistory', inSignature: 'i', outSignature: 'ai'},
379         {name: 'PurpleConversationMessageGetMessage', inSignature: 'i', outSignature: 's'},
380         {name: 'PurpleConversationGetTitle', inSignature: 'i', outSignature: 's'},
381         {name: 'PurpleConversationHasFocus', inSignature: 'i', outSignature: 'b'}
382     ],
383     signals: [
384         {name: 'ReceivedImMsg', inSignature: 'issiu'},
385         {name: 'ReceivedChatMsg', inSignature: 'issiu'},
386         {name: 'DisplayedImMsg', inSignature: 'issiu'},
387         {name: 'DisplayedChatMsg', inSignature: 'issiu'},
388         {name: 'SentImMsg', inSignature: 'iss'},
389         {name: 'SentChatMsg', inSignature: 'iss'},
390         {name: 'BuddyStatusChanged', inSignature: 'iii'}, // ????
391         {name: 'BuddySignedOff', inSignature: 'i'},
392         {name: 'BuddySignedOn', inSignature: 'i'},
393         {name: 'DeletingConversation', inSignature: 'i'},
394         {name: 'ConversationCreated', inSignature: 'i'}
395     ]
398 let Pidgin = DBus.makeProxyClass(PidginIface);
400 function PidginClient() {
401     this._init();
404 PidginClient.prototype = {
405     _init: function() {
406         this._sources = {};
407         this._proxy = new Pidgin(DBus.session, 'im.pidgin.purple.PurpleService', '/im/pidgin/purple/PurpleObject');
408         this._displayedImMsgId = 0;
409         this._displayedChatMsgId = 0;
410     },
412     enable: function() {
413         this._displayedImMsgId = this._proxy.connect('DisplayedImMsg', Lang.bind(this, this._messageDisplayed));
414         this._displayedChatMsgId = this._proxy.connect('DisplayedChatMsg', Lang.bind(this, this._chatMessageDisplayed));        
415     },
416     
417     disable: function() {
418         if (this._displayedImMsgId > 0) {
419             this._proxy.disconnect(this._displayedImMsgId);
420             this._displayedImMsgId = 0;
421         }
422         
423         if (this._displayedChatMsgId > 0) {
424             this._proxy.disconnect(this._displayedChatMsgId);
425             this._displayedChatMsgId = 0;
426         }
427         
428         for (let key in this._sources) {
429             if (this._sources.hasOwnProperty(key))
430                 this._sources[key].destroy();
431         }
432     },
434     proxy: function () {
435         return this._proxy;
436     },
438     _chatMessageDisplayed: function(emitter, account, author, message, conversation, flag) {
440         // only trigger on chat message received with nick
441         if (flag != (2 | 32)) return;
443         if (conversation) {
444             let source = this._sources[conversation];
445             if (!source) {
446                 source = new Source(this, account, author, message, conversation, true, 2);
447                 source.connect('destroy', Lang.bind(this,
448                     function() {
449                         delete this._sources[conversation];
450                     }
451                 ));
452             }
453             this._sources[conversation] = source;
454         }
455     },
457     _messageDisplayed: function(emitter, account, author, message, conversation, flag) {
459         // only trigger on message received/message sent
460         if (flag != 2 && flag != 1) return;
462         if (conversation) {
463             let source = this._sources[conversation];
464             if (!source) {
465                 source = new Source(this, account, author, message, conversation, false, flag);
466                 source.connect('destroy', Lang.bind(this, 
467                     function() {
468                         delete this._sources[conversation];
469                     }
470                 ));
471             }
472             this._sources[conversation] = source;
473         }
474     }
477 function init(metadata) {
478     imports.gettext.bindtextdomain('gnome-shell-extensions', metadata.localedir);
479     return new PidginClient();