1 # -*- test-case-name: twisted.words.test.test_basechat -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
6 Base classes for Instance Messenger clients.
9 from twisted.words.im.locals import OFFLINE, ONLINE, AWAY
14 A GUI object that displays a contacts list.
16 @ivar chatui: The GUI chat client associated with this contacts list.
17 @type chatui: L{ChatUI}
19 @ivar contacts: The contacts.
20 @type contacts: C{dict} mapping C{str} to a L{IPerson<interfaces.IPerson>}
23 @ivar onlineContacts: The contacts who are currently online (have a status
24 that is not C{OFFLINE}).
25 @type onlineContacts: C{dict} mapping C{str} to a
26 L{IPerson<interfaces.IPerson>} provider
28 @ivar clients: The signed-on clients.
29 @type clients: C{list} of L{IClient<interfaces.IClient>} providers
31 def __init__(self, chatui):
33 @param chatui: The GUI chat client associated with this contacts list.
34 @type chatui: L{ChatUI}
38 self.onlineContacts = {}
42 def setContactStatus(self, person):
44 Inform the user that a person's status has changed.
46 @param person: The person whose status has changed.
47 @type person: L{IPerson<interfaces.IPerson>} provider
49 if not self.contacts.has_key(person.name):
50 self.contacts[person.name] = person
51 if not self.onlineContacts.has_key(person.name) and \
52 (person.status == ONLINE or person.status == AWAY):
53 self.onlineContacts[person.name] = person
54 if self.onlineContacts.has_key(person.name) and \
55 person.status == OFFLINE:
56 del self.onlineContacts[person.name]
59 def registerAccountClient(self, client):
61 Notify the user that an account client has been signed on to.
63 @param client: The client being added to your list of account clients.
64 @type client: L{IClient<interfaces.IClient>} provider
66 if not client in self.clients:
67 self.clients.append(client)
70 def unregisterAccountClient(self, client):
72 Notify the user that an account client has been signed off or
75 @param client: The client being removed from the list of account
77 @type client: L{IClient<interfaces.IClient>} provider
79 if client in self.clients:
80 self.clients.remove(client)
83 def contactChangedNick(self, person, newnick):
85 Update your contact information to reflect a change to a contact's
88 @param person: The person in your contacts list whose nickname is
90 @type person: L{IPerson<interfaces.IPerson>} provider
92 @param newnick: The new nickname for this person.
96 if self.contacts.has_key(oldname):
97 del self.contacts[oldname]
99 self.contacts[newnick] = person
100 if self.onlineContacts.has_key(oldname):
101 del self.onlineContacts[oldname]
102 self.onlineContacts[newnick] = person
108 A GUI window of a conversation with a specific person.
110 @ivar person: The person who you're having this conversation with.
111 @type person: L{IPerson<interfaces.IPerson>} provider
113 @ivar chatui: The GUI chat client associated with this conversation.
114 @type chatui: L{ChatUI}
116 def __init__(self, person, chatui):
118 @param person: The person who you're having this conversation with.
119 @type person: L{IPerson<interfaces.IPerson>} provider
121 @param chatui: The GUI chat client associated with this conversation.
122 @type chatui: L{ChatUI}
130 Display the ConversationWindow.
132 raise NotImplementedError("Subclasses must implement this method")
137 Hide the ConversationWindow.
139 raise NotImplementedError("Subclasses must implement this method")
142 def sendText(self, text):
144 Send text to the person with whom the user is conversing.
146 @param text: The text to be sent.
149 self.person.sendMessage(text, None)
152 def showMessage(self, text, metadata=None):
154 Display a message sent from the person with whom the user is conversing.
156 @param text: The sent message.
159 @param metadata: Metadata associated with this message.
160 @type metadata: C{dict}
162 raise NotImplementedError("Subclasses must implement this method")
165 def contactChangedNick(self, person, newnick):
167 Change a person's name.
169 @param person: The person whose nickname is changing.
170 @type person: L{IPerson<interfaces.IPerson>} provider
172 @param newnick: The new nickname for this person.
173 @type newnick: C{str}
175 self.person.name = newnick
179 class GroupConversation:
181 A GUI window of a conversation with a group of people.
183 @ivar chatui: The GUI chat client associated with this conversation.
184 @type chatui: L{ChatUI}
186 @ivar group: The group of people that are having this conversation.
187 @type group: L{IGroup<interfaces.IGroup>} provider
189 @ivar members: The names of the people in this conversation.
190 @type members: C{list} of C{str}
192 def __init__(self, group, chatui):
194 @param chatui: The GUI chat client associated with this conversation.
195 @type chatui: L{ChatUI}
197 @param group: The group of people that are having this conversation.
198 @type group: L{IGroup<interfaces.IGroup>} provider
207 Display the GroupConversationWindow.
209 raise NotImplementedError("Subclasses must implement this method")
214 Hide the GroupConversationWindow.
216 raise NotImplementedError("Subclasses must implement this method")
219 def sendText(self, text):
221 Send text to the group.
223 @param: The text to be sent.
226 self.group.sendGroupMessage(text, None)
229 def showGroupMessage(self, sender, text, metadata=None):
231 Display to the user a message sent to this group from the given sender.
233 @param sender: The person sending the message.
236 @param text: The sent message.
239 @param metadata: Metadata associated with this message.
240 @type metadata: C{dict}
242 raise NotImplementedError("Subclasses must implement this method")
245 def setGroupMembers(self, members):
247 Set the list of members in the group.
249 @param members: The names of the people that will be in this group.
250 @type members: C{list} of C{str}
252 self.members = members
255 def setTopic(self, topic, author):
257 Change the topic for the group conversation window and display this
260 @param topic: This group's topic.
263 @param author: The person changing the topic.
266 raise NotImplementedError("Subclasses must implement this method")
269 def memberJoined(self, member):
271 Add the given member to the list of members in the group conversation
272 and displays this to the user.
274 @param member: The person joining the group conversation.
277 if not member in self.members:
278 self.members.append(member)
281 def memberChangedNick(self, oldnick, newnick):
283 Change the nickname for a member of the group conversation and displays
284 this change to the user.
286 @param oldnick: The old nickname.
287 @type oldnick: C{str}
289 @param newnick: The new nickname.
290 @type newnick: C{str}
292 if oldnick in self.members:
293 self.members.remove(oldnick)
294 self.members.append(newnick)
297 def memberLeft(self, member):
299 Delete the given member from the list of members in the group
300 conversation and displays the change to the user.
302 @param member: The person leaving the group conversation.
305 if member in self.members:
306 self.members.remove(member)
314 @type conversations: C{dict} of L{Conversation}
315 @ivar conversations: A cache of all the direct windows.
317 @type groupConversations: C{dict} of L{GroupConversation}
318 @ivar groupConversations: A cache of all the group windows.
320 @type persons: C{dict} with keys that are a C{tuple} of (C{str},
321 L{IAccount<interfaces.IAccount>} provider) and values that are
322 L{IPerson<interfaces.IPerson>} provider
323 @ivar persons: A cache of all the users associated with this client.
325 @type groups: C{dict} with keys that are a C{tuple} of (C{str},
326 L{IAccount<interfaces.IAccount>} provider) and values that are
327 L{IGroup<interfaces.IGroup>} provider
328 @ivar groups: A cache of all the groups associated with this client.
330 @type onlineClients: C{list} of L{IClient<interfaces.IClient>} providers
331 @ivar onlineClients: A list of message sources currently online.
333 @type contactsList: L{ContactsList}
334 @ivar contactsList: A contacts list.
337 self.conversations = {}
338 self.groupConversations = {}
341 self.onlineClients = []
342 self.contactsList = ContactsList(self)
345 def registerAccountClient(self, client):
347 Notify the user that an account has been signed on to.
349 @type client: L{IClient<interfaces.IClient>} provider
350 @param client: The client account for the person who has just signed on.
352 @rtype client: L{IClient<interfaces.IClient>} provider
353 @return: The client, so that it may be used in a callback chain.
355 self.onlineClients.append(client)
356 self.contactsList.registerAccountClient(client)
360 def unregisterAccountClient(self, client):
362 Notify the user that an account has been signed off or disconnected.
364 @type client: L{IClient<interfaces.IClient>} provider
365 @param client: The client account for the person who has just signed
368 self.onlineClients.remove(client)
369 self.contactsList.unregisterAccountClient(client)
372 def getContactsList(self):
374 Get the contacts list associated with this chat window.
376 @rtype: L{ContactsList}
377 @return: The contacts list associated with this chat window.
379 return self.contactsList
382 def getConversation(self, person, Class=Conversation, stayHidden=False):
384 For the given person object, return the conversation window or create
385 and return a new conversation window if one does not exist.
387 @type person: L{IPerson<interfaces.IPerson>} provider
388 @param person: The person whose conversation window we want to get.
390 @type Class: L{IConversation<interfaces.IConversation>} implementor
391 @param: The kind of conversation window we want. If the conversation
392 window for this person didn't already exist, create one of this type.
394 @type stayHidden: C{bool}
395 @param stayHidden: Whether or not the conversation window should stay
398 @rtype: L{IConversation<interfaces.IConversation>} provider
399 @return: The conversation window.
401 conv = self.conversations.get(person)
403 conv = Class(person, self)
404 self.conversations[person] = conv
412 def getGroupConversation(self, group, Class=GroupConversation,
415 For the given group object, return the group conversation window or
416 create and return a new group conversation window if it doesn't exist.
418 @type group: L{IGroup<interfaces.IGroup>} provider
419 @param group: The group whose conversation window we want to get.
421 @type Class: L{IConversation<interfaces.IConversation>} implementor
422 @param: The kind of conversation window we want. If the conversation
423 window for this person didn't already exist, create one of this type.
425 @type stayHidden: C{bool}
426 @param stayHidden: Whether or not the conversation window should stay
429 @rtype: L{IGroupConversation<interfaces.IGroupConversation>} provider
430 @return: The group conversation window.
432 conv = self.groupConversations.get(group)
434 conv = Class(group, self)
435 self.groupConversations[group] = conv
443 def getPerson(self, name, client):
445 For the given name and account client, return an instance of a
446 L{IGroup<interfaces.IPerson>} provider or create and return a new
447 instance of a L{IGroup<interfaces.IPerson>} provider.
450 @param name: The name of the person of interest.
452 @type client: L{IClient<interfaces.IClient>} provider
453 @param client: The client account of interest.
455 @rtype: L{IPerson<interfaces.IPerson>} provider
456 @return: The person with that C{name}.
458 account = client.account
459 p = self.persons.get((name, account))
461 p = account.getPerson(name)
462 self.persons[name, account] = p
466 def getGroup(self, name, client):
468 For the given name and account client, return an instance of a
469 L{IGroup<interfaces.IGroup>} provider or create and return a new instance
470 of a L{IGroup<interfaces.IGroup>} provider.
473 @param name: The name of the group of interest.
475 @type client: L{IClient<interfaces.IClient>} provider
476 @param client: The client account of interest.
478 @rtype: L{IGroup<interfaces.IGroup>} provider
479 @return: The group with that C{name}.
481 # I accept 'client' instead of 'account' in my signature for
482 # backwards compatibility. (Groups changed to be Account-oriented
483 # in CVS revision 1.8.)
484 account = client.account
485 g = self.groups.get((name, account))
487 g = account.getGroup(name)
488 self.groups[name, account] = g
492 def contactChangedNick(self, person, newnick):
494 For the given C{person}, change the C{person}'s C{name} to C{newnick}
495 and tell the contact list and any conversation windows with that
496 C{person} to change as well.
498 @type person: L{IPerson<interfaces.IPerson>} provider
499 @param person: The person whose nickname will get changed.
501 @type newnick: C{str}
502 @param newnick: The new C{name} C{person} will take.
504 oldnick = person.name
505 if self.persons.has_key((oldnick, person.account)):
506 conv = self.conversations.get(person)
508 conv.contactChangedNick(person, newnick)
509 self.contactsList.contactChangedNick(person, newnick)
510 del self.persons[oldnick, person.account]
511 person.name = newnick
512 self.persons[person.name, person.account] = person