enable debug mode.. when enabled also server communication is logged to /var/log...
[enigma2-plugins.git] / dreamirc / src / e2chat.py
1 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
2 # See LICENSE for details.
3
4 #
5
6 """Base classes for Instance Messenger clients."""
7 from enigma import *
8 from Screens.Screen import Screen
9
10 from Components.Pixmap import *
11 from Components.Pixmap import Pixmap
12 from Components.ActionMap import ActionMap, NumberActionMap
13 from Components.ScrollLabel import ScrollLabel
14 from Components.GUIComponent import *
15 from Components.MenuList import MenuList
16 from Components.Input import Input
17 from Components.Label import Label
18 from Components.config import *
19 from Components.ConfigList import ConfigList
20 from Plugins.Plugin import PluginDescriptor
21 from Tools.NumericalTextInput import *
22 from Tools.Directories import *
23
24 from locals import OFFLINE, ONLINE, AWAY
25 import dreamIRCTools
26 from enigma import *
27 from dreamIRCTools import *
28 #from myScrollLabel import *
29 #from dreamIRCMainMenu import *
30
31 class ContactsList:
32     """A GUI object that displays a contacts list"""
33     def __init__(self, chatui):
34         """
35         @param chatui: ???
36         @type chatui: L{ChatUI}
37         """
38         self.chatui = chatui
39         self.contacts = {}
40         self.onlineContacts = {}
41         self.clients = []
42         
43     def setContactStatus(self, person):
44         """Inform the user that a person's status has changed.
45
46         @type person: L{Person<interfaces.IPerson>}
47         """
48         if not self.contacts.has_key(person.name):
49             self.contacts[person.name] = person
50         if not self.onlineContacts.has_key(person.name) and \
51             (person.status == ONLINE or person.status == AWAY):
52             self.onlineContacts[person.name] = person
53         if self.onlineContacts.has_key(person.name) and \
54            person.status == OFFLINE:
55             del self.onlineContacts[person.name]
56
57     def registerAccountClient(self, client):
58         """Notify the user that an account client has been signed on to.
59
60         @type client: L{Client<interfaces.IClient>}
61         """
62         if not client in self.clients:
63             self.clients.append(client)
64
65     def unregisterAccountClient(self, client):
66         """Notify the user that an account client has been signed off
67         or disconnected from.
68
69         @type client: L{Client<interfaces.IClient>}
70          """
71         if client in self.clients:
72             self.clients.remove(client)
73
74     def contactChangedNick(self, person, newnick):
75         oldname = person.name
76         if self.contacts.has_key(oldname):
77             del self.contacts[oldname]
78             person.name = newnick
79             self.contacts[newnick] = person
80             if self.onlineContacts.has_key(oldname):
81                 del self.onlineContacts[oldname]
82                 self.onlineContacts[newnick] = person
83
84
85 class Conversation:
86     """A GUI window of a conversation with a specific person"""
87     def __init__(self, person, chatui):
88         """
89         @type person: L{Person<interfaces.IPerson>}
90         @type chatui: L{ChatUI}
91         """
92         self.chatui = chatui
93         self.person = person
94         self.pipe = MessagePipe()
95         self.timer=eTimer()
96         self.timer.timeout.get().append(self.sendOutPipe)
97         self.timer.start(100)
98
99     def show(self):
100         """Displays the ConversationWindow"""
101  #       raise NotImplementedError("Subclasses must implement this method")
102
103     def hide(self):
104         """Hides the ConversationWindow"""
105 #        raise NotImplementedError("Subclasses must implement this method")
106
107     def sendText(self, text):
108         """Sends text to the person with whom the user is conversing.
109         @returntype: L{Deferred<twisted.internet.defer.Deferred>}
110         """
111         self.person.sendMessage(text, None)
112         self.pipe.add("%s" % text)
113         self.pipe.clearOutText()
114 #        print"<%s> %s" % (self.nickname, text)
115
116     def sendOutPipe(self):
117         if len(str(self.pipe.getOutText())) > 0:
118             if (self.pipe.getOutText()=="/QUIT"):
119                 print "/quit detected...."
120                 self.pipe.clearOutText()
121                 self.person.bye()
122             else:
123                 print "sending chat : %s" % str(self.pipe.getOutText())
124                 self.sendText(str(self.pipe.getOutText()))
125                 self.pipe.clearOutText()
126
127     def showMessage(self, text, metadata=None):
128         """Display a message sent from the person with whom she is conversing
129
130         @type text: string
131         @type metadata: dict
132         """
133         self.pipe.add("<%s> %s" % (self.person.name, text))
134 #        raise NotImplementedError("Subclasses must implement this method")
135
136     def contactChangedNick(self, person, newnick):
137         """Change a person's name.
138
139         @type person: L{Person<interfaces.IPerson>}
140         @type newnick: string
141         """
142         self.person.name = newnick
143         self.pipe.add("-!- %s is now known as %s" % (person.name, newnick))
144 #        self.blist.updateBuddyWindow()
145
146     def serverMsg(self, message):
147         """Displays a serverMsg in the group conversation window
148
149         @type message: string
150         """
151         self.pipe.add("-!- %s " % (message))
152 #        self.blist.updateBuddyWindow()
153
154 class GroupConversation:
155     """A conversation with a group of people."""
156     def __init__(self, group, chatui):
157         """
158         @type group: L{Group<interfaces.IGroup>}
159         @param chatui: ???
160         @type chatui: L{ChatUI}
161         """
162         self.chatui = chatui
163         self.group = group
164         self.members = []
165         self.pipe = MessagePipe()
166 #        self.blist=ChatWindow()
167         self.timer=eTimer()
168         self.timer.timeout.get().append(self.sendOutPipe)
169         self.timer.start(100)
170         
171     def show(self):
172         """Displays the GroupConversationWindow."""
173 #        raise NotImplementedError("Subclasses must implement this method")
174
175     def hide(self):
176         """Hides the GroupConversationWindow."""
177 #        raise NotImplementedError("Subclasses must implement this method")
178
179     def sendText(self, text):
180         """Sends text to the group.
181         @type text: string
182         @returntype: L{Deferred<twisted.internet.defer.Deferred>}
183         """
184         self.group.sendGroupMessage(text, None)
185         self.pipe.add("%s" % text)
186         self.pipe.clearOutText()
187 #        print "nach im sending nach sending : %s" % str(self.pipe.getOutText())
188         
189     
190     def sendOutPipe(self):
191         if len(str(self.pipe.getOutText())) > 0:
192                 if (self.pipe.getOutText()=="/QUIT"):
193                         print "/quit detected...."
194                         self.pipe.clearOutText()
195                         self.group.bye()
196                 else:
197                         print "sending group chat : %s" % str(self.pipe.getOutText())
198                         self.sendText(str(self.pipe.getOutText()))
199                         self.pipe.clearOutText()
200             
201     def showGroupMessage(self, sender, text, metadata=None):
202         """Displays to the user a message sent to this group from the given sender
203         @type sender: string (XXX: Not Person?)
204         @type text: string
205         @type metadata: dict
206         """
207         self.pipe.add("<%s/%s> %s" % (sender, self.group.name, text))
208
209     def setGroupMembers(self, members):
210         """Sets the list of members in the group and displays it to the user
211         """
212         self.members = members
213         self.refreshMemberList()
214
215     def setTopic(self, topic, author):
216         """Displays the topic (from the server) for the group conversation window
217
218         @type topic: string
219         @type author: string (XXX: Not Person?)
220         """
221         self.pipe.add("-!- %s set the topic of %s to: %s" % (author, self.group.name, topic))
222 #        print "-!- %s set the topic of %s to: %s" % (author, self.group.name, topic)        
223
224     def serverMsg(self, message):
225         """Displays a serverMsg in the group conversation window
226
227         @type message: string
228         """
229         self.pipe.add("-!- %s " % (message))
230
231     def memberJoined(self, member):
232         """Adds the given member to the list of members in the group conversation
233         and displays this to the user
234
235         @type member: string (XXX: Not Person?)
236         """
237         if not member in self.members:
238             self.members.append(member)
239         self.pipe.add("-!- %s joined %s" % (member, self.group.name))
240         self.refreshMemberList()
241
242     def memberChangedNick(self, oldnick, newnick):
243         """Changes the oldnick in the list of members to newnick and displays this
244         change to the user
245
246         @type oldnick: string
247         @type newnick: string
248         """
249         if oldnick in self.members:
250             self.members.remove(oldnick)
251             self.members.append(newnick)
252             #self.chatui.contactChangedNick(oldnick, newnick)
253         self.pipe.add("-!- %s is now known as %s in %s" % (oldnick, newnick, self.group.name))
254         self.refreshMemberList()
255
256     def memberLeft(self, member):
257         """Deletes the given member from the list of members in the group
258         conversation and displays the change to the user
259
260         @type member: string
261         """
262         if member in self.members:
263             self.members.remove(member)
264         self.pipe.add("-!- %s left %s" % (member, self.group.name))
265         self.refreshMemberList()
266         
267         
268     def refreshMemberList(self):
269         self.pipe.clearBuddyList()
270         self.members.sort(lambda x,y: cmp(string.lower(x), string.lower(y)))
271         self.pipe.getCannelName(self.group.name)
272         for member in self.members:
273             self.pipe.buildBuddyList(str(member))
274 #        print "Channel : #%s" % self.group.name
275         print "Buddylist of #%s : \n%s" % (self.group.name, self.pipe.showBuddyList())
276 #        self.pipe.showBuddyList()
277 #        self.pipe.updateBuddyWindow(self.pipe.showBuddyList())
278         self.pipe.updateBuddyWindow()
279         
280 class ChatUI:
281     """A GUI chat client"""
282     def __init__(self):
283         self.conversations = {}      # cache of all direct windows
284         self.groupConversations = {} # cache of all group windows
285         self.persons = {}            # keys are (name, client)
286         self.groups = {}             # cache of all groups
287         self.onlineClients = []      # list of message sources currently online
288         self.contactsList = ContactsList(self)
289         self.pipe = MessagePipe()
290         self.helper = ""
291
292     def registerAccountClient(self, client):
293         """Notifies user that an account has been signed on to.
294
295         @type client: L{Client<interfaces.IClient>}
296         @returns: client, so that I may be used in a callback chain
297         """
298         self.pipe.debug("signing onto %s" % client.accountName)
299         self.onlineClients.append(client)
300         self.contactsList.registerAccountClient(client)
301         self.helper=client
302         self.pipe.debug(" --- %s ---" % self.helper)
303         self.pipe.add("signing onto %s" % client)
304         self.pipe.add("signing onto %s" % client.accountName)
305         return client
306
307     def unregisterAccountClient(self, client):
308         """Notifies user that an account has been signed off or disconnected
309
310         @type client: L{Client<interfaces.IClient>}
311         """
312         self.pipe.debug("signing off from %s" % client.accountName)
313         self.onlineClients.remove(client)
314         self.contactsList.unregisterAccountClient(client)
315
316     def remClient(self):
317         """Notifies user that an account has been signed off or disconnected
318
319         @type client: L{Client<interfaces.IClient>}
320         """
321         self.pipe.debug(" --- %s ---" % self.helper)
322         self.pipe.debug("signing off from %s"  % self.helper.accountName)
323         self.pipe.add("signing off %s" % helper)
324         self.pipe.add("signing off %s" % helper.accountName)        
325         self.onlineClients.remove(helper)
326         self.contactsList.unregisterAccountClient(helper)
327
328     def getContactsList(self):
329         """
330         @returntype: L{ContactsList}
331         """
332         self.pipe.debug("contactlist = %s" % self.contactsList)
333         return self.contactsList
334
335
336     def getConversation(self, person, Class=Conversation, stayHidden=0):
337         """For the given person object, returns the conversation window
338         or creates and returns a new conversation window if one does not exist.
339
340         @type person: L{Person<interfaces.IPerson>}
341         @type Class: L{Conversation<interfaces.IConversation>} class
342         @type stayHidden: boolean
343
344         @returntype: L{Conversation<interfaces.IConversation>}
345         """
346         conv = self.conversations.get(person)
347         if not conv:
348             conv = Class(person, self)
349             self.conversations[person] = conv
350         if stayHidden:
351             conv.hide()
352         else:
353             conv.show()
354         return conv
355
356     def getGroupConversation(self,group,Class=GroupConversation,stayHidden=0):
357         """For the given group object, returns the group conversation window or
358         creates and returns a new group conversation window if it doesn't exist
359
360         @type group: L{Group<interfaces.IGroup>}
361         @type Class: L{Conversation<interfaces.IConversation>} class
362         @type stayHidden: boolean
363
364         @returntype: L{GroupConversation<interfaces.IGroupConversation>}
365         """
366         conv = self.groupConversations.get(group)
367         if not conv:
368             conv = Class(group, self)
369             self.groupConversations[group] = conv
370         if stayHidden:
371             conv.hide()
372         else:
373             conv.show()
374 #        print "[dreamIRC] : " , conv
375         return conv
376
377     def getPerson(self, name, client):
378         """For the given name and account client, returns the instance of the
379         AbstractPerson subclass, or creates and returns a new AbstractPerson
380         subclass of the type Class
381
382         @type name: string
383         @type client: L{Client<interfaces.IClient>}
384
385         @returntype: L{Person<interfaces.IPerson>}
386         """
387         account = client.account
388         p = self.persons.get((name, account))
389         if not p:
390             p = account.getPerson(name)
391             self.persons[name, account] = p
392         return p
393
394     def getGroup(self, name, client):
395         """For the given name and account client, returns the instance of the
396         AbstractGroup subclass, or creates and returns a new AbstractGroup
397         subclass of the type Class
398
399         @type name: string
400         @type client: L{Client<interfaces.IClient>}
401
402         @returntype: L{Group<interfaces.IGroup>}
403         """
404         # I accept 'client' instead of 'account' in my signature for
405         # backwards compatibility.  (Groups changed to be Account-oriented
406         # in CVS revision 1.8.)
407         account = client.account
408         g = self.groups.get((name, account))
409         if not g:
410             g = account.getGroup(name)
411             self.groups[name, account] = g
412 #        self.pipe.add("joined %s" % g)
413         return g
414
415     def contactChangedNick(self, oldnick, newnick):
416         """For the given person, changes the person's name to newnick, and
417         tells the contact list and any conversation windows with that person
418         to change as well.
419
420         @type oldnick: string
421         @type newnick: string
422         """
423         if self.persons.has_key((person.name, person.account)):
424             conv = self.conversations.get(person)
425             if conv:
426                 conv.contactChangedNick(person, newnick)
427
428             self.contactsList.contactChangedNick(person, newnick)
429
430             del self.persons[person.name, person.account]
431             person.name = newnick
432             self.persons[person.name, person.account] = person
433
434     def sendOutPipe(self):
435         print "groupchat %s" % self.pipe.OutText
436         if len(self.pipe.OutText()) > 0:
437             self.sendText(self.pipe.OutText())
438             self.pipe.clearOutText()