186 lines
5.8 KiB
Python
186 lines
5.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
======================================================
|
|
:mod:`irc3.plugins.asynchronous` Asynchronous Events
|
|
======================================================
|
|
|
|
Provides asynchronous handling for various IRC events including WHOIS, WHO queries,
|
|
and channel management through non-blocking operations.
|
|
|
|
Features:
|
|
- **WHOIS Support**: Retrieve detailed user information from the server.
|
|
- **WHO Queries**: Fetch channel users with their respective flags.
|
|
- **CTCP Handling**: Manage custom Client-to-Client Protocol requests.
|
|
- **Channel Topic Management**: Get or modify channel topics efficiently.
|
|
- **Ban List Handling**: Query active bans on a channel.
|
|
|
|
Usage
|
|
=====
|
|
Subclass `~irc3.asynchronous.AsyncEvents` to create custom asynchronous event handlers.
|
|
|
|
Example:
|
|
class MyPlugin:
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
self.whois = Whois(bot)
|
|
|
|
async def do_whois(self):
|
|
whois = await self.whois(nick='gawel')
|
|
if int(whois['idle']) / 60 > 10:
|
|
self.bot.privmsg('gawel', 'Wake up dude')
|
|
|
|
.. warning::
|
|
Always verify `result['timeout']` to ensure a response was received before the timeout.
|
|
"""
|
|
|
|
from .asynchronous import AsyncEvents
|
|
from irc3 import utils
|
|
from irc3 import dec
|
|
|
|
|
|
class Whois(AsyncEvents):
|
|
"""Asynchronously handle WHOIS responses to gather user details.
|
|
|
|
Attributes:
|
|
timeout (int): Default timeout for WHOIS response in seconds.
|
|
send_line (str): IRC command template for WHOIS requests.
|
|
"""
|
|
|
|
timeout = 20
|
|
send_line = 'WHOIS {nick} {nick}'
|
|
|
|
events = (
|
|
{'match': r"(?i)^:\S+ 301 \S+ {nick} :(?P<away>.*)"},
|
|
{
|
|
'match': (
|
|
r"(?i)^:\S+ 311 \S+ {nick} (?P<username>\S+) (?P<host>\S+) "
|
|
r". :(?P<realname>.*)"
|
|
)
|
|
},
|
|
{
|
|
'match': r"(?i)^:\S+ 312 \S+ {nick} (?P<server>\S+) :(?P<server_desc>.*)"
|
|
},
|
|
{'match': r"(?i)^:\S+ 317 \S+ {nick} (?P<idle>[0-9]+).*"},
|
|
{'match': r"(?i)^:\S+ 319 \S+ {nick} :(?P<channels>.*)", 'multi': True},
|
|
{
|
|
'match': (
|
|
r"(?i)^:\S+ 330 \S+ {nick} (?P<account>\S+) :(?P<account_desc>.*)"
|
|
)
|
|
},
|
|
{'match': r"(?i)^:\S+ 671 \S+ {nick} :(?P<connection>.*)"},
|
|
{
|
|
'match': r"(?i)^:\S+ (?P<retcode>(318|401)) \S+ (?P<nick>{nick}) :.*",
|
|
'final': True,
|
|
},
|
|
)
|
|
|
|
def process_results(self, results=None, **value):
|
|
"""Aggregate and structure WHOIS results into a consolidated dictionary.
|
|
|
|
Args:
|
|
results (list): Collected event responses.
|
|
**value: Accumulated data from event processing.
|
|
|
|
Returns:
|
|
dict: Structured user information with success status.
|
|
"""
|
|
channels = []
|
|
for res in results:
|
|
channels.extend(res.pop('channels', '').split())
|
|
value.update(res)
|
|
value['channels'] = channels
|
|
value['success'] = value.get('retcode') == '318'
|
|
return value
|
|
|
|
|
|
class WhoChannel(AsyncEvents):
|
|
"""Handle WHO responses for channel user listings.
|
|
|
|
Attributes:
|
|
send_line (str): IRC command template for WHO requests.
|
|
"""
|
|
|
|
send_line = 'WHO {channel}'
|
|
|
|
events = (
|
|
{
|
|
'match': (
|
|
r"(?i)^:\S+ 352 \S+ {channel} (?P<user>\S+) (?P<host>\S+) "
|
|
r"(?P<server>\S+) (?P<nick>\S+) (?P<modes>\S+) "
|
|
r":(?P<hopcount>\S+) (?P<realname>.*)"
|
|
),
|
|
'multi': True,
|
|
},
|
|
{'match': r"(?i)^:\S+ (?P<retcode>(315|401)) \S+ {channel} :.*", 'final': True},
|
|
)
|
|
|
|
def process_results(self, results=None, **value):
|
|
"""Compile WHO channel results into a list of users.
|
|
|
|
Args:
|
|
results (list): Raw event response data.
|
|
**value: Extracted key-value pairs from responses.
|
|
|
|
Returns:
|
|
dict: Processed result with user list and success status.
|
|
"""
|
|
users = []
|
|
for res in results:
|
|
if 'retcode' in res:
|
|
value.update(res)
|
|
else:
|
|
res['mask'] = utils.IrcString(f"{res['nick']}!{res['user']}@{res['host']}")
|
|
users.append(res)
|
|
value['users'] = users
|
|
value['success'] = value.get('retcode') == '315'
|
|
return value
|
|
|
|
|
|
@dec.plugin
|
|
class Async:
|
|
"""Expose asynchronous IRC command interfaces for plugin usage."""
|
|
|
|
def __init__(self, context):
|
|
"""Initialize with the bot context and register async commands."""
|
|
self.context = context
|
|
self.context.async_cmds = self
|
|
self.async_whois = Whois(context)
|
|
self.async_who_channel = WhoChannel(context)
|
|
|
|
def send_message(self, target: str, message: str):
|
|
"""Send a message to a target (channel or user).
|
|
|
|
Args:
|
|
target (str): Recipient channel or nickname.
|
|
message (str): Message content to send.
|
|
"""
|
|
self.context.privmsg(target, message)
|
|
|
|
@dec.extend
|
|
def whois(self, nick: str, timeout: int = 20):
|
|
"""Initiate a WHOIS query for a nickname.
|
|
|
|
Args:
|
|
nick (str): Nickname to query.
|
|
timeout (int): Response timeout in seconds.
|
|
|
|
Returns:
|
|
Awaitable[dict]: WHOIS result data.
|
|
"""
|
|
return self.async_whois(nick=nick.lower(), timeout=timeout)
|
|
|
|
@dec.extend
|
|
def who(self, target: str, timeout: int = 20):
|
|
"""Perform a WHO query on a channel or user.
|
|
|
|
Args:
|
|
target (str): Channel or nickname to query.
|
|
timeout (int): Response timeout in seconds.
|
|
|
|
Returns:
|
|
Awaitable[dict] | None: WHO results for channels, else None.
|
|
"""
|
|
target = target.lower()
|
|
if target.startswith('#'):
|
|
return self.async_who_channel(channel=target, timeout=timeout)
|
|
return None |