internal: make most core.handlers async

add some typing annotations on inputs as well, most are not necessary
right now but the end goal is to make poezio internals much more
async-friendly than it currently is.
This commit is contained in:
mathieui 2022-02-06 17:37:13 +01:00
parent 8847f3e7de
commit 911cf335a1
3 changed files with 102 additions and 105 deletions

View file

@ -4,7 +4,7 @@ XMPP-related handlers for the Core class
import logging import logging
from typing import Optional from typing import Optional, Union
import asyncio import asyncio
import curses import curses
@ -21,7 +21,7 @@ from os import path
import pyasn1.codec.der.decoder import pyasn1.codec.der.decoder
import pyasn1.codec.der.encoder import pyasn1.codec.der.encoder
import pyasn1_modules.rfc2459 import pyasn1_modules.rfc2459
from slixmpp import InvalidJID, JID, Message from slixmpp import InvalidJID, JID, Message, Iq, Presence
from slixmpp.xmlstream.stanzabase import StanzaBase, ElementBase from slixmpp.xmlstream.stanzabase import StanzaBase, ElementBase
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
@ -159,67 +159,57 @@ class HandlerCore:
return None return None
def on_carbon_received(self, message): async def on_carbon_received(self, message: Message):
""" """
Carbon <received/> received Carbon <received/> received
""" """
def ignore_message(recv):
log.debug('%s has category conference, ignoring carbon',
recv['from'].server)
def receive_message(recv):
recv['to'] = self.core.xmpp.boundjid.full
if recv['receipt']:
return self.on_receipt(recv)
self.on_normal_message(recv)
recv = message['carbon_received'] recv = message['carbon_received']
is_muc_pm = self.is_known_muc_pm(recv, recv['from']) is_muc_pm = self.is_known_muc_pm(recv, recv['from'])
if is_muc_pm: if is_muc_pm:
log.debug('%s sent a MUC-PM, ignoring carbon', recv['from']) log.debug('%s sent a MUC-PM, ignoring carbon', recv['from'])
return elif is_muc_pm is None:
if is_muc_pm is None: is_muc = self.core.xmpp.plugin['xep_0030'].has_identity(
fixes.has_identity(
self.core.xmpp,
recv['from'].bare, recv['from'].bare,
identity='conference', node='conference',
on_true=functools.partial(ignore_message, recv), )
on_false=functools.partial(receive_message, recv)) if is_muc:
return log.debug('%s has category conference, ignoring carbon',
recv['from'].server)
else:
recv['to'] = self.core.xmpp.boundjid.full
if recv['receipt']:
await self.on_receipt(recv)
else:
await self.on_normal_message(recv)
else: else:
receive_message(recv) recv['to'] = self.core.xmpp.boundjid.full
await self.on_normal_message(recv)
def on_carbon_sent(self, message): async def on_carbon_sent(self, message: Message):
""" """
Carbon <sent/> received Carbon <sent/> received
""" """
def groupchat_private_message(sent):
self.on_groupchat_private_message(sent, sent=True)
def send_message(sent):
sent['from'] = self.core.xmpp.boundjid.full
self.on_normal_message(sent)
sent = message['carbon_sent'] sent = message['carbon_sent']
is_muc_pm = self.is_known_muc_pm(sent, sent['to']) is_muc_pm = self.is_known_muc_pm(sent, sent['to'])
if is_muc_pm: if is_muc_pm:
groupchat_private_message(sent) await self.on_groupchat_private_message(sent, sent=True)
return elif is_muc_pm is None:
if is_muc_pm is None: is_muc = self.core.xmpp.plugin['xep_0030'].has_identity(
fixes.has_identity(
self.core.xmpp,
sent['to'].bare, sent['to'].bare,
identity='conference', node='conference',
on_true=functools.partial(groupchat_private_message, sent), )
on_false=functools.partial(send_message, sent)) if is_muc:
await self.on_groupchat_private_message(sent, sent=True)
else:
sent['from'] = self.core.xmpp.boundjid.full
await self.on_normal_message(sent)
else: else:
send_message(sent) sent['from'] = self.core.xmpp.boundjid.full
await self.on_normal_message(sent)
### Invites ### ### Invites ###
def on_groupchat_invitation(self, message): async def on_groupchat_invitation(self, message: Message):
""" """
Mediated invitation received Mediated invitation received
""" """
@ -243,11 +233,11 @@ class HandlerCore:
logger.log_roster_change(inviter.full, 'invited you to %s' % jid.full) logger.log_roster_change(inviter.full, 'invited you to %s' % jid.full)
self.core.pending_invites[jid.bare] = inviter.full self.core.pending_invites[jid.bare] = inviter.full
def on_groupchat_decline(self, decline): async def on_groupchat_decline(self, decline):
"Mediated invitation declined; skip for now" "Mediated invitation declined; skip for now"
pass pass
def on_groupchat_direct_invitation(self, message): async def on_groupchat_direct_invitation(self, message: Message):
""" """
Direct invitation received Direct invitation received
""" """
@ -280,7 +270,7 @@ class HandlerCore:
### "classic" messages ### ### "classic" messages ###
def on_message(self, message): async def on_message(self, message: Message):
""" """
When receiving private message from a muc OR a normal message When receiving private message from a muc OR a normal message
(from one of our contacts) (from one of our contacts)
@ -291,19 +281,19 @@ class HandlerCore:
return return
# Differentiate both type of messages, and call the appropriate handler. # Differentiate both type of messages, and call the appropriate handler.
if self.is_known_muc_pm(message, message['from']): if self.is_known_muc_pm(message, message['from']):
self.on_groupchat_private_message(message, sent=False) await self.on_groupchat_private_message(message, sent=False)
return else:
self.on_normal_message(message) await self.on_normal_message(message)
def on_encrypted_message(self, message): async def on_encrypted_message(self, message: Message):
""" """
When receiving an encrypted message When receiving an encrypted message
""" """
if message["body"]: if message["body"]:
return # Already being handled by on_message. return # Already being handled by on_message.
self.on_message(message) await self.on_message(message)
def on_error_message(self, message): async def on_error_message(self, message: Message):
""" """
When receiving any message with type="error" When receiving any message with type="error"
""" """
@ -332,7 +322,7 @@ class HandlerCore:
tab.add_message(InfoMessage(error)) tab.add_message(InfoMessage(error))
self.core.refresh_window() self.core.refresh_window()
def on_normal_message(self, message): async def on_normal_message(self, message: Message):
""" """
When receiving "normal" messages (not a private message from a When receiving "normal" messages (not a private message from a
muc participant) muc participant)
@ -388,7 +378,7 @@ class HandlerCore:
else: else:
self.core.refresh_window() self.core.refresh_window()
async def on_0084_avatar(self, msg): async def on_0084_avatar(self, msg: Message):
jid = msg['from'].bare jid = msg['from'].bare
contact = roster[jid] contact = roster[jid]
if not contact: if not contact:
@ -438,7 +428,7 @@ class HandlerCore:
exc_info=True) exc_info=True)
return return
async def on_vcard_avatar(self, pres): async def on_vcard_avatar(self, pres: Presence):
jid = pres['from'].bare jid = pres['from'].bare
contact = roster[jid] contact = roster[jid]
if not contact: if not contact:
@ -474,7 +464,7 @@ class HandlerCore:
log.debug( log.debug(
'Failed writing %ss avatar to cache:', jid, exc_info=True) 'Failed writing %ss avatar to cache:', jid, exc_info=True)
def on_nick_received(self, message): async def on_nick_received(self, message: Message):
""" """
Called when a pep notification for a user nickname Called when a pep notification for a user nickname
is received is received
@ -488,7 +478,7 @@ class HandlerCore:
else: else:
contact.name = '' contact.name = ''
def on_groupchat_message(self, message): async def on_groupchat_message(self, message: Message):
""" """
Triggered whenever a message is received from a multi-user chat room. Triggered whenever a message is received from a multi-user chat room.
""" """
@ -553,6 +543,7 @@ class HandlerCore:
log.debug('Unable to correct a message', exc_info=True) log.debug('Unable to correct a message', exc_info=True)
if not replaced: if not replaced:
ui_msg: Union[InfoMessage, PMessage]
# Messages coming from MUC barejid (Server maintenance, IRC mode # Messages coming from MUC barejid (Server maintenance, IRC mode
# changes from biboumi, etc.) are displayed as info messages. # changes from biboumi, etc.) are displayed as info messages.
highlight = False highlight = False
@ -601,20 +592,20 @@ class HandlerCore:
and self.core.own_nick != message['from'].resource): and self.core.own_nick != message['from'].resource):
curses.beep() curses.beep()
def on_muc_own_nickchange(self, muc): def on_muc_own_nickchange(self, muc: tabs.MucTab):
"We changed our nick in a MUC" "We changed our nick in a MUC"
for tab in self.core.get_tabs(tabs.PrivateTab): for tab in self.core.get_tabs(tabs.PrivateTab):
if tab.parent_muc == muc: if tab.parent_muc == muc:
tab.own_nick = muc.own_nick tab.own_nick = muc.own_nick
def on_groupchat_private_message(self, message, sent): async def on_groupchat_private_message(self, message: Message, sent: bool):
""" """
We received a Private Message (from someone in a Muc) We received a Private Message (from someone in a Muc)
""" """
jid = message['to'] if sent else message['from'] jid = message['to'] if sent else message['from']
with_nick = jid.resource with_nick = jid.resource
if not with_nick: if not with_nick:
self.on_groupchat_message(message) await self.on_groupchat_message(message)
return return
room_from = jid.bare room_from = jid.bare
@ -660,33 +651,33 @@ class HandlerCore:
### Chatstates ### ### Chatstates ###
def on_chatstate_active(self, message): async def on_chatstate_active(self, message: Message):
self._on_chatstate(message, "active") await self._on_chatstate(message, "active")
def on_chatstate_inactive(self, message): async def on_chatstate_inactive(self, message: Message):
self._on_chatstate(message, "inactive") await self._on_chatstate(message, "inactive")
def on_chatstate_composing(self, message): async def on_chatstate_composing(self, message: Message):
self._on_chatstate(message, "composing") await self._on_chatstate(message, "composing")
def on_chatstate_paused(self, message): async def on_chatstate_paused(self, message: Message):
self._on_chatstate(message, "paused") await self._on_chatstate(message, "paused")
def on_chatstate_gone(self, message): async def on_chatstate_gone(self, message: Message):
self._on_chatstate(message, "gone") await self._on_chatstate(message, "gone")
def _on_chatstate(self, message, state): async def _on_chatstate(self, message: Message, state: str):
if message['type'] == 'chat': if message['type'] == 'chat':
if not self._on_chatstate_normal_conversation(message, state): if not await self._on_chatstate_normal_conversation(message, state):
tab = self.core.tabs.by_name_and_class(message['from'].full, tab = self.core.tabs.by_name_and_class(message['from'].full,
tabs.PrivateTab) tabs.PrivateTab)
if not tab: if not tab:
return return
self._on_chatstate_private_conversation(message, state) await self._on_chatstate_private_conversation(message, state)
elif message['type'] == 'groupchat': elif message['type'] == 'groupchat':
self.on_chatstate_groupchat_conversation(message, state) await self.on_chatstate_groupchat_conversation(message, state)
def _on_chatstate_normal_conversation(self, message, state): async def _on_chatstate_normal_conversation(self, message: Message, state: str):
tab = self.core.get_conversation_by_jid(message['from'], False) tab = self.core.get_conversation_by_jid(message['from'], False)
if not tab: if not tab:
return False return False
@ -702,7 +693,7 @@ class HandlerCore:
self.core.refresh_tab_win() self.core.refresh_tab_win()
return True return True
def _on_chatstate_private_conversation(self, message, state): async def _on_chatstate_private_conversation(self, message: Message, state: str):
""" """
Chatstate received in a private conversation from a MUC Chatstate received in a private conversation from a MUC
""" """
@ -719,7 +710,7 @@ class HandlerCore:
_composing_tab_state(tab, state) _composing_tab_state(tab, state)
self.core.refresh_tab_win() self.core.refresh_tab_win()
def on_chatstate_groupchat_conversation(self, message, state): async def on_chatstate_groupchat_conversation(self, message: Message, state: str):
""" """
Chatstate received in a MUC Chatstate received in a MUC
""" """
@ -745,7 +736,7 @@ class HandlerCore:
return '%s: %s' % (error_condition, return '%s: %s' % (error_condition,
error_text) if error_text else error_condition error_text) if error_text else error_condition
def on_version_result(self, iq): def on_version_result(self, iq: Iq):
""" """
Handle the result of a /version command. Handle the result of a /version command.
""" """
@ -762,7 +753,7 @@ class HandlerCore:
'an unknown platform')) 'an unknown platform'))
self.core.information(version, 'Info') self.core.information(version, 'Info')
def on_bookmark_result(self, iq): def on_bookmark_result(self, iq: Iq):
""" """
Handle the result of a /bookmark commands. Handle the result of a /bookmark commands.
""" """
@ -774,7 +765,7 @@ class HandlerCore:
### subscription-related handlers ### ### subscription-related handlers ###
def on_roster_update(self, iq): async def on_roster_update(self, iq: Iq):
""" """
The roster was received. The roster was received.
""" """
@ -793,7 +784,7 @@ class HandlerCore:
if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab): if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab):
self.core.refresh_window() self.core.refresh_window()
def on_subscription_request(self, presence): async def on_subscription_request(self, presence: Presence):
"""subscribe received""" """subscribe received"""
jid = presence['from'].bare jid = presence['from'].bare
contact = roster[jid] contact = roster[jid]
@ -816,7 +807,7 @@ class HandlerCore:
if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab): if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab):
self.core.refresh_window() self.core.refresh_window()
def on_subscription_authorized(self, presence): async def on_subscription_authorized(self, presence: Presence):
"""subscribed received""" """subscribed received"""
jid = presence['from'].bare jid = presence['from'].bare
contact = roster[jid] contact = roster[jid]
@ -831,7 +822,7 @@ class HandlerCore:
if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab): if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab):
self.core.refresh_window() self.core.refresh_window()
def on_subscription_remove(self, presence): async def on_subscription_remove(self, presence: Presence):
"""unsubscribe received""" """unsubscribe received"""
jid = presence['from'].bare jid = presence['from'].bare
contact = roster[jid] contact = roster[jid]
@ -844,7 +835,7 @@ class HandlerCore:
if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab): if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab):
self.core.refresh_window() self.core.refresh_window()
def on_subscription_removed(self, presence): async def on_subscription_removed(self, presence: Presence):
"""unsubscribed received""" """unsubscribed received"""
jid = presence['from'].bare jid = presence['from'].bare
contact = roster[jid] contact = roster[jid]
@ -865,7 +856,7 @@ class HandlerCore:
### Presence-related handlers ### ### Presence-related handlers ###
def on_presence(self, presence): async def on_presence(self, presence: Presence):
if presence.match('presence/muc'): if presence.match('presence/muc'):
return return
jid = presence['from'] jid = presence['from']
@ -892,7 +883,7 @@ class HandlerCore:
tab.refresh() tab.refresh()
self.core.doupdate() self.core.doupdate()
def on_presence_error(self, presence): async def on_presence_error(self, presence: Presence):
jid = presence['from'] jid = presence['from']
contact = roster[jid.bare] contact = roster[jid.bare]
if not contact: if not contact:
@ -901,7 +892,7 @@ class HandlerCore:
contact.error = presence['error']['text'] or presence['error']['type'] + ': ' + presence['error']['condition'] contact.error = presence['error']['text'] or presence['error']['type'] + ': ' + presence['error']['condition']
# TODO: reset chat states status on presence error # TODO: reset chat states status on presence error
def on_got_offline(self, presence): async def on_got_offline(self, presence: Presence):
""" """
A JID got offline A JID got offline
""" """
@ -933,7 +924,7 @@ class HandlerCore:
if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab): if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab):
self.core.refresh_window() self.core.refresh_window()
def on_got_online(self, presence): async def on_got_online(self, presence: Presence):
""" """
A JID got online A JID got online
""" """
@ -972,7 +963,7 @@ class HandlerCore:
if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab): if isinstance(self.core.tabs.current_tab, tabs.RosterInfoTab):
self.core.refresh_window() self.core.refresh_window()
def on_groupchat_presence(self, presence): async def on_groupchat_presence(self, presence: Presence):
""" """
Triggered whenever a presence stanza is received from a user in a multi-user chat room. Triggered whenever a presence stanza is received from a user in a multi-user chat room.
Display the presence on the room window and update the Display the presence on the room window and update the
@ -986,14 +977,14 @@ class HandlerCore:
### Connection-related handlers ### ### Connection-related handlers ###
def on_failed_connection(self, error): async def on_failed_connection(self, error: str):
""" """
We cannot contact the remote server We cannot contact the remote server
""" """
self.core.information( self.core.information(
"Connection to remote server failed: %s" % (error, ), 'Error') "Connection to remote server failed: %s" % (error, ), 'Error')
def on_session_end(self, event): async def on_session_end(self, event):
""" """
Called when a session is terminated (e.g. due to a manual disconnect or a 0198 resume fail) Called when a session is terminated (e.g. due to a manual disconnect or a 0198 resume fail)
""" """
@ -1002,7 +993,7 @@ class HandlerCore:
for tab in self.core.get_tabs(tabs.MucTab): for tab in self.core.get_tabs(tabs.MucTab):
tab.disconnect() tab.disconnect()
def on_session_resumed(self, event): async def on_session_resumed(self, event):
""" """
Called when a session is successfully resumed by 0198 Called when a session is successfully resumed by 0198
""" """
@ -1037,7 +1028,7 @@ class HandlerCore:
""" """
self.core.information("Reconnecting in %d seconds..." % (event), 'Info') self.core.information("Reconnecting in %d seconds..." % (event), 'Info')
def on_stream_error(self, event): async def on_stream_error(self, event):
""" """
When we receive a stream error When we receive a stream error
""" """
@ -1046,7 +1037,7 @@ class HandlerCore:
if event: if event:
self.core.last_stream_error = (time.time(), event) self.core.last_stream_error = (time.time(), event)
def on_failed_all_auth(self, event): async def on_failed_all_auth(self, event):
""" """
Authentication failed Authentication failed
""" """
@ -1054,7 +1045,7 @@ class HandlerCore:
'Error') 'Error')
self.core.legitimate_disconnect = True self.core.legitimate_disconnect = True
def on_no_auth(self, event): async def on_no_auth(self, event):
""" """
Authentication failed (no mech) Authentication failed (no mech)
""" """
@ -1062,14 +1053,14 @@ class HandlerCore:
"Authentication failed, no login method available.", 'Error') "Authentication failed, no login method available.", 'Error')
self.core.legitimate_disconnect = True self.core.legitimate_disconnect = True
def on_connected(self, event): async def on_connected(self, event):
""" """
Remote host responded, but we are not yet authenticated Remote host responded, but we are not yet authenticated
""" """
self.core.information("Connected to server.", 'Info') self.core.information("Connected to server.", 'Info')
self.core.legitimate_disconnect = False self.core.legitimate_disconnect = False
def on_session_start(self, event): async def on_session_start(self, event):
""" """
Called when we are connected and authenticated Called when we are connected and authenticated
""" """
@ -1103,7 +1094,7 @@ class HandlerCore:
### Other handlers ### ### Other handlers ###
def on_status_codes(self, message): async def on_status_codes(self, message: Message):
""" """
Handle groupchat messages with status codes. Handle groupchat messages with status codes.
Those are received when a room configuration change occurs. Those are received when a room configuration change occurs.
@ -1182,7 +1173,7 @@ class HandlerCore:
if modif: if modif:
self.core.refresh_window() self.core.refresh_window()
def on_groupchat_subject(self, message): async def on_groupchat_subject(self, message: Message):
""" """
Triggered when the topic is changed. Triggered when the topic is changed.
""" """
@ -1239,7 +1230,7 @@ class HandlerCore:
room_from, tabs.MucTab) is self.core.tabs.current_tab: room_from, tabs.MucTab) is self.core.tabs.current_tab:
self.core.refresh_window() self.core.refresh_window()
def on_receipt(self, message): async def on_receipt(self, message):
""" """
When a delivery receipt is received (XEP-0184) When a delivery receipt is received (XEP-0184)
""" """
@ -1261,13 +1252,13 @@ class HandlerCore:
except AckError: except AckError:
log.debug('Error while receiving an ack', exc_info=True) log.debug('Error while receiving an ack', exc_info=True)
def on_data_form(self, message): async def on_data_form(self, message: Message):
""" """
When a data form is received When a data form is received
""" """
self.core.information(str(message)) self.core.information(str(message))
def on_attention(self, message): async def on_attention(self, message: Message):
""" """
Attention probe received. Attention probe received.
""" """
@ -1284,7 +1275,7 @@ class HandlerCore:
tab.state = "attention" tab.state = "attention"
self.core.refresh_tab_win() self.core.refresh_tab_win()
def outgoing_stanza(self, stanza): def outgoing_stanza(self, stanza: StanzaBase):
""" """
We are sending a new stanza, write it in the xml buffer if needed. We are sending a new stanza, write it in the xml buffer if needed.
""" """
@ -1315,7 +1306,7 @@ class HandlerCore:
self.core.tabs.current_tab.refresh() self.core.tabs.current_tab.refresh()
self.core.doupdate() self.core.doupdate()
def incoming_stanza(self, stanza): def incoming_stanza(self, stanza: StanzaBase):
""" """
We are receiving a new stanza, write it in the xml buffer if needed. We are receiving a new stanza, write it in the xml buffer if needed.
""" """

View file

@ -11,6 +11,7 @@ There are two different instances of a ConversationTab:
the time. the time.
""" """
import asyncio
import curses import curses
import logging import logging
from datetime import datetime from datetime import datetime
@ -209,7 +210,9 @@ class ConversationTab(OneToOneTab):
if not msg['body']: if not msg['body']:
return return
self.set_last_sent_message(msg, correct=correct) self.set_last_sent_message(msg, correct=correct)
self.core.handler.on_normal_message(msg) asyncio.ensure_future(
self.core.handler.on_normal_message(msg)
)
# Our receipts slixmpp hack # Our receipts slixmpp hack
msg._add_receipt = True # type: ignore msg._add_receipt = True # type: ignore
msg.send() msg.send()

View file

@ -10,6 +10,7 @@ both participants nicks. It also has slightly different features than
the ConversationTab (such as tab-completion on nicks from the room). the ConversationTab (such as tab-completion on nicks from the room).
""" """
import asyncio
import curses import curses
import logging import logging
from datetime import datetime from datetime import datetime
@ -239,7 +240,9 @@ class PrivateTab(OneToOneTab):
if not msg['body']: if not msg['body']:
return return
self.set_last_sent_message(msg, correct=correct) self.set_last_sent_message(msg, correct=correct)
self.core.handler.on_groupchat_private_message(msg, sent=True) asyncio.ensure_future(
self.core.handler.on_groupchat_private_message(msg, sent=True)
)
# Our receipts slixmpp hack # Our receipts slixmpp hack
msg._add_receipt = True # type: ignore msg._add_receipt = True # type: ignore
msg.send() msg.send()