From 138b17cdb35da9b31fbea9c61e4a0e5bcab8b87e Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Thu, 24 Feb 2011 20:02:18 +0100 Subject: [PATCH] In normal conversations: Send composing and active chat states and display the state of the remote contact --- src/connection.py | 1 + src/core.py | 65 ++++++++++++++++++++++++++++++++++++++++------- src/tabs.py | 29 +++++++++++++++++++-- src/windows.py | 7 ++++- 4 files changed, 90 insertions(+), 12 deletions(-) diff --git a/src/connection.py b/src/connection.py index a8bd491f..c12680b8 100644 --- a/src/connection.py +++ b/src/connection.py @@ -55,6 +55,7 @@ class Connection(sleekxmpp.ClientXMPP): self.register_plugin('xep_0030') self.register_plugin('xep_0045') self.register_plugin('xep_0004') + self.register_plugin('xep_0085') if config.get('send_poezio_info', 'true') == 'true': info = {'name':'poezio', 'version':'0.7.5-dev'} diff --git a/src/core.py b/src/core.py index 88f3fd07..d0714c39 100644 --- a/src/core.py +++ b/src/core.py @@ -170,7 +170,11 @@ class Core(object): self.xmpp.add_event_handler("changed_status", self.on_presence) self.xmpp.add_event_handler("changed_subscription", self.on_changed_subscription) self.xmpp.add_event_handler("message_xform", self.on_data_form) - + self.xmpp.add_event_handler("chatstate_active", self.on_chatstate_active) + self.xmpp.add_event_handler("chatstate_composing", self.on_chatstate_composing) + self.xmpp.add_event_handler("chatstate_paused", self.on_chatstate_paused) + self.xmpp.add_event_handler("chatstate_gone", self.on_chatstate_gone) + self.xmpp.add_event_handler("chatstate_inactive", self.on_chatstate_inactive) self.information(_('Welcome to poezio!')) self.refresh_window() @@ -214,6 +218,32 @@ class Core(object): """ self.information('%s' % messsage) + def on_chatstate_active(self, message): + if message['type'] == 'chat': # normal conversation + self.on_chatstate_normal_conversation("active") + + def on_chatstate_inactive(self, message): + if message['type'] == 'chat': # normal conversation + self.on_chatstate_normal_conversation("inactive") + + def on_chatstate_composing(self, message): + if message['type'] == 'chat': + self.on_chatstate_normal_conversation("composing") + + def on_chatstate_paused(self, message): + if message['type'] == 'chat': + self.on_chatstate_normal_conversation("paused") + + def on_chatstate_gone(self, message): + if message['type'] == 'chat': + self.on_chatstate_normal_conversation("gone") + + def on_chatstate_normal_conversation(self, state): + tab = self.get_tab_of_conversation_with_jid(message['from'], False) + if not tab: + return + tab.chatstate = state + def open_new_form(self, form, on_cancel, on_send, **kwargs): """ Open a new tab containing the form @@ -577,6 +607,25 @@ class Core(object): if tab.get_name() == tab_name: self.command_win('%s' % (tab.nb,)) + def get_tab_of_conversation_with_jid(self, jid, create=True): + """ + From a JID, get the tab containing the conversation with it. + If none already exist, and create is "True", we create it + and return it. Otherwise, we return None + """ + # We first check if we have a conversation opened with this precise resource + conversation = self.get_tab_by_name(jid.full, tabs.ConversationTab) + if not conversation: + # If not, we search for a conversation with the bare jid + conversation = self.get_tab_by_name(jid.bare, tabs.ConversationTab) + if not conversation: + if create: + # We create the conversation with the bare Jid if nothing was found + conversation = self.open_conversation_window(jid.bare, False) + else: + conversation = None + return conversation + def on_normal_message(self, message): """ When receiving "normal" messages (from someone in our roster) @@ -585,19 +634,17 @@ class Core(object): body = message['body'] if not body: return - # We first check if we have a conversation opened with this precise resource - conversation = self.get_tab_by_name(jid.full, tabs.ConversationTab) - if not conversation: - # If not, we search for a conversation with the bare jid - conversation = self.get_tab_by_name(jid.bare, tabs.ConversationTab) - if not conversation: - # We create the conversation with the bare Jid if nothing was found - conversation = self.open_conversation_window(jid.bare, False) + conversation = self.get_tab_of_conversation_with_jid(jid, create=True) if roster.get_contact_by_jid(jid.bare): remote_nick = roster.get_contact_by_jid(jid.bare).get_name() or jid.user else: remote_nick = jid.user conversation.get_room().add_message(body, None, remote_nick, False, theme.COLOR_REMOTE_USER) + if conversation.remote_wants_chatstates is None: + if message['chat_state']: + conversation.remote_wants_chatstates = True + else: + conversation.remote_wants_chatstates = False logger.log_message(jid.bare, remote_nick, body) if self.current_tab() is not conversation: conversation.set_color_state(theme.COLOR_TAB_PRIVATE) diff --git a/src/tabs.py b/src/tabs.py index 1a27eb8e..68501c7a 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -225,6 +225,10 @@ class ChatTab(Tab): def __init__(self, core, room): Tab.__init__(self, core) self._room = room + self.remote_wants_chatstates = None # change this to True or False when + # we know that the remote user wants chatstates, or not. + # None means we don’t know yet, and we send only "active" chatstates + self.chatstate = None # can be "active", "composing", "paused", "gone", "inactive" self.key_func['M-/'] = self.last_words_completion self.key_func['^M'] = self.on_enter self.commands['say'] = (self.command_say, @@ -355,6 +359,10 @@ class MucTab(ChatTab, TabWithInfoWin): def __init__(self, core, room): ChatTab.__init__(self, core, room) TabWithInfoWin.__init__(self) + self.remote_wants_chatstates = True + # We send active, composing and paused states to the MUC because + # the chatstate may or may not be filtered by the MUC, + # that’s not our problem. self.topic_win = windows.Topic() self.text_win = windows.TextWin() room.add_window(self.text_win) @@ -1103,7 +1111,12 @@ class ConversationTab(ChatTab, TabWithInfoWin): self.complete_commands(self.input) def command_say(self, line): - muc.send_private_message(self.core.xmpp, self.get_name(), line) + msg = self.core.xmpp.make_message(self.get_name()) + msg['type'] = 'chat' + msg['body'] = line + if self.remote_wants_chatstates is not False: + msg['chat_state'] = 'active' + msg.send() self.core.add_message_to_text_buffer(self.get_room(), line, None, self.core.own_nick) logger.log_message(JID(self.get_name()).bare, self.core.own_nick, line) @@ -1131,7 +1144,7 @@ class ConversationTab(ChatTab, TabWithInfoWin): self.resize() self.text_win.refresh(self._room) self.upper_bar.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name())) - self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self._room, self.text_win) + self.info_header.refresh(self.get_name(), roster.get_contact_by_jid(self.get_name()), self._room, self.text_win, self.chatstate) self.info_win.refresh(informations) self.tab_win.refresh(tabs, tabs[0]) self.input.refresh() @@ -1152,7 +1165,19 @@ class ConversationTab(ChatTab, TabWithInfoWin): if key in self.key_func: self.key_func[key]() return False + empty_before = self.input.get_text() == '' self.input.do_command(key) + if not self.input.get_text() and not empty_before: + msg = self.core.xmpp.make_message(self.get_name()) + msg['type'] = 'chat' + msg['chat_state'] = 'active' + msg.send() + elif self.input.get_text() and empty_before: + msg = self.core.xmpp.make_message(self.get_name()) + msg['type'] = 'chat' + msg['chat_state'] = 'composing' + log.debug('MSG:%s\n' % msg) + msg.send() return False def on_lose_focus(self): diff --git a/src/windows.py b/src/windows.py index aab254c0..dc30d541 100644 --- a/src/windows.py +++ b/src/windows.py @@ -282,7 +282,7 @@ class ConversationInfoWin(InfoWin): def resize(self, height, width, y, x, stdscr): self._resize(height, width, y, x, stdscr) - def refresh(self, jid, contact, text_buffer, window): + def refresh(self, jid, contact, text_buffer, window, chatstate): # contact can be None, if we receive a message # from someone not in our roster. In this case, we display # only the maximum information from the message we can get. @@ -305,6 +305,7 @@ class ConversationInfoWin(InfoWin): self.write_contact_informations(contact) self.write_resource_information(resource) self.print_scroll_position(window) + self.write_chatstate(chatstate) self.finish_line(theme.COLOR_INFORMATION_BAR) self._refresh() @@ -339,6 +340,10 @@ class ConversationInfoWin(InfoWin): self.addstr(jid.full, common.curses_color_pair(theme.COLOR_CONVERSATION_NAME)) self.addstr('] ', common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + def write_chatstate(self, state): + if state: + self.addstr(' %s' % (state,), common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + class ConversationStatusMessageWin(InfoWin): """ The upper bar displaying the status message of the contact