Micro-optimize MUC presence handling
Keep all presence before status code=110 in a buffer, then batch-process them when receiving our initial join.
This commit is contained in:
parent
68e3cdcd49
commit
dcdc970acd
1 changed files with 179 additions and 130 deletions
|
@ -36,6 +36,7 @@ from poezio.core.structs import Completion, Status
|
|||
|
||||
|
||||
NS_MUC_USER = 'http://jabber.org/protocol/muc#user'
|
||||
STATUS_XPATH = '{%s}x/{%s}status' % (NS_MUC_USER, NS_MUC_USER)
|
||||
|
||||
|
||||
class MucTab(ChatTab):
|
||||
|
@ -55,6 +56,7 @@ class MucTab(ChatTab):
|
|||
self.own_user = None
|
||||
self.name = jid
|
||||
self.password = password
|
||||
self.presence_buffer = []
|
||||
self.users = []
|
||||
self.privates = [] # private conversations
|
||||
self.topic = ''
|
||||
|
@ -566,6 +568,7 @@ class MucTab(ChatTab):
|
|||
current_status.show)
|
||||
|
||||
def leave_room(self, message):
|
||||
self.presence_buffer = []
|
||||
if self.joined:
|
||||
info_col = dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
|
||||
char_quit = get_theme().CHAR_QUIT
|
||||
|
@ -1072,36 +1075,71 @@ class MucTab(ChatTab):
|
|||
0)
|
||||
|
||||
def handle_presence(self, presence):
|
||||
from_nick = presence['from'].resource
|
||||
from_room = presence['from'].bare
|
||||
xpath = '{%s}x/{%s}status' % (NS_MUC_USER, NS_MUC_USER)
|
||||
"""
|
||||
Handle MUC presence
|
||||
"""
|
||||
status_codes = set()
|
||||
for status_code in presence.xml.findall(xpath):
|
||||
for status_code in presence.xml.findall(STATUS_XPATH):
|
||||
status_codes.add(status_code.attrib['code'])
|
||||
if not self.joined:
|
||||
if '110' in status_codes:
|
||||
self.process_presence_buffer(presence)
|
||||
else:
|
||||
self.presence_buffer.append(presence)
|
||||
return
|
||||
else:
|
||||
try:
|
||||
self.handle_presence_joined(presence, status_codes)
|
||||
except PresenceError:
|
||||
self.core.room_error(presence, presence['from'].bare)
|
||||
if self.core.current_tab() is self:
|
||||
self.text_win.refresh()
|
||||
self.user_win.refresh_if_changed(self.users)
|
||||
self.info_header.refresh(self, self.text_win, user=self.own_user)
|
||||
self.input.refresh()
|
||||
self.core.doupdate()
|
||||
|
||||
# Check if it's not an error presence.
|
||||
if presence['type'] == 'error':
|
||||
return self.core.room_error(presence, from_room)
|
||||
affiliation = presence['muc']['affiliation']
|
||||
show = presence['show']
|
||||
status = presence['status']
|
||||
role = presence['muc']['role']
|
||||
jid = presence['muc']['jid']
|
||||
typ = presence['type']
|
||||
def process_presence_buffer(self, last_presence):
|
||||
"""
|
||||
Batch-process all the initial presences
|
||||
"""
|
||||
deterministic = config.get_by_tabname('deterministic_nick_colors', self.name)
|
||||
if not self.joined: # user in the room BEFORE us.
|
||||
# ignore redondant presence message, see bug #1509
|
||||
if (from_nick not in [user.nick for user in self.users]
|
||||
and typ != "unavailable"):
|
||||
|
||||
for stanza in self.presence_buffer:
|
||||
try:
|
||||
self.handle_presence_unjoined(stanza, deterministic)
|
||||
except PresenceError as e:
|
||||
self.core.room_error(e.presence, e.presence['from'].bare)
|
||||
self.handle_presence_unjoined(last_presence, deterministic, own=True)
|
||||
self.users.sort()
|
||||
# Enable the self ping event, to regularly check if we
|
||||
# are still in the room.
|
||||
self.enable_self_ping_event()
|
||||
if self.core.current_tab() is not self:
|
||||
self.refresh_tab_win()
|
||||
self.core.current_tab().input.refresh()
|
||||
self.core.doupdate()
|
||||
|
||||
def handle_presence_unjoined(self, presence, deterministic, own=False):
|
||||
"""
|
||||
Presence received while we are not in the room (before code=110)
|
||||
"""
|
||||
from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence(presence)
|
||||
user_color = self.search_for_color(from_nick)
|
||||
new_user = User(from_nick, affiliation, show,
|
||||
status, role, jid, deterministic, user_color)
|
||||
bisect.insort_left(self.users, new_user)
|
||||
self.users.append(new_user)
|
||||
self.core.events.trigger('muc_join', presence, self)
|
||||
if '110' in status_codes or self.own_nick == from_nick:
|
||||
# second part of the condition is a workaround for old
|
||||
# ejabberd or every gateway in the world that just do
|
||||
# not send a 110 status code with the presence
|
||||
if own:
|
||||
status_codes = set()
|
||||
for status_code in presence.xml.findall(STATUS_XPATH):
|
||||
status_codes.add(status_code.attrib['code'])
|
||||
self.own_join(from_nick, new_user, status_codes)
|
||||
|
||||
def own_join(self, from_nick, new_user, status_codes):
|
||||
"""
|
||||
Handle the last presence we received, entering the room
|
||||
"""
|
||||
self.own_nick = from_nick
|
||||
self.own_user = new_user
|
||||
self.joined = True
|
||||
|
@ -1135,6 +1173,7 @@ class MucTab(ChatTab):
|
|||
'info_col': info_col,
|
||||
}
|
||||
self.add_message(enable_message, typ=2)
|
||||
self.core.enable_private_tabs(self.name, enable_message)
|
||||
if '201' in status_codes:
|
||||
self.add_message(
|
||||
'\x19%(info_col)s}Info: The room '
|
||||
|
@ -1155,15 +1194,12 @@ class MucTab(ChatTab):
|
|||
{'info_col': info_col,
|
||||
'warn_col': warn_col},
|
||||
typ=0)
|
||||
if self.core.current_tab() is not self:
|
||||
self.refresh_tab_win()
|
||||
self.core.current_tab().input.refresh()
|
||||
self.core.doupdate()
|
||||
self.core.enable_private_tabs(self.name, enable_message)
|
||||
# Enable the self ping event, to regularly check if we
|
||||
# are still in the room.
|
||||
self.enable_self_ping_event()
|
||||
else:
|
||||
|
||||
def handle_presence_joined(self, presence, status_codes):
|
||||
"""
|
||||
Handle new presences when we are already in the room
|
||||
"""
|
||||
from_nick, from_room, affiliation, show, status, role, jid, typ = dissect_presence(presence)
|
||||
change_nick = '303' in status_codes
|
||||
kick = '307' in status_codes and typ == 'unavailable'
|
||||
ban = '301' in status_codes and typ == 'unavailable'
|
||||
|
@ -1205,12 +1241,6 @@ class MucTab(ChatTab):
|
|||
else:
|
||||
self.on_user_change_status(user, from_nick, from_room,
|
||||
affiliation, role, show, status)
|
||||
if self.core.current_tab() is self:
|
||||
self.text_win.refresh()
|
||||
self.user_win.refresh_if_changed(self.users)
|
||||
self.info_header.refresh(self, self.text_win, user=self.own_user)
|
||||
self.input.refresh()
|
||||
self.core.doupdate()
|
||||
|
||||
def on_non_member_kicked(self):
|
||||
"""We have been kicked because the MUC is members-only"""
|
||||
|
@ -1727,3 +1757,22 @@ class MucTab(ChatTab):
|
|||
def on_self_ping_failed(self, iq):
|
||||
self.command_cycle("the MUC server is not responding")
|
||||
self.core.refresh_window()
|
||||
|
||||
class PresenceError(Exception): pass
|
||||
|
||||
def dissect_presence(presence):
|
||||
"""
|
||||
Extract relevant information from a presence
|
||||
"""
|
||||
from_nick = presence['from'].resource
|
||||
from_room = presence['from'].bare
|
||||
# Check if it's not an error presence.
|
||||
if presence['type'] == 'error':
|
||||
raise PresenceError(presence)
|
||||
affiliation = presence['muc']['affiliation']
|
||||
show = presence['show']
|
||||
status = presence['status']
|
||||
role = presence['muc']['role']
|
||||
jid = presence['muc']['jid']
|
||||
typ = presence['type']
|
||||
return from_nick, from_room, affiliation, show, status, role, jid, typ
|
||||
|
|
Loading…
Reference in a new issue