g1mp/plugins/asynchronious.py

186 lines
5.8 KiB
Python
Raw Normal View History

2025-02-13 04:55:42 +00:00
# -*- coding: utf-8 -*-
2025-02-13 06:35:15 +00:00
"""
2025-02-13 04:55:42 +00:00
======================================================
2025-02-13 06:35:15 +00:00
:mod:`irc3.plugins.asynchronous` Asynchronous Events
2025-02-13 04:55:42 +00:00
======================================================
2025-02-13 06:35:15 +00:00
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.
2025-02-13 04:55:42 +00:00
Usage
=====
2025-02-13 06:35:15 +00:00
Subclass `~irc3.asynchronous.AsyncEvents` to create custom asynchronous event handlers.
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
Example:
2025-02-13 04:55:42 +00:00
class MyPlugin:
def __init__(self, bot):
self.bot = bot
self.whois = Whois(bot)
2025-02-13 06:35:15 +00:00
async def do_whois(self):
2025-02-13 04:55:42 +00:00
whois = await self.whois(nick='gawel')
if int(whois['idle']) / 60 > 10:
self.bot.privmsg('gawel', 'Wake up dude')
.. warning::
2025-02-13 06:35:15 +00:00
Always verify `result['timeout']` to ensure a response was received before the timeout.
2025-02-13 04:55:42 +00:00
"""
2025-02-13 07:00:16 +00:00
from .asynchronous import AsyncEvents
2025-02-13 06:35:15 +00:00
from irc3 import utils
from irc3 import dec
2025-02-13 04:55:42 +00:00
class Whois(AsyncEvents):
2025-02-13 06:35:15 +00:00
"""Asynchronously handle WHOIS responses to gather user details.
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
Attributes:
timeout (int): Default timeout for WHOIS response in seconds.
send_line (str): IRC command template for WHOIS requests.
"""
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
timeout = 20
2025-02-13 04:55:42 +00:00
send_line = 'WHOIS {nick} {nick}'
events = (
{'match': r"(?i)^:\S+ 301 \S+ {nick} :(?P<away>.*)"},
{
'match': (
2025-02-13 06:35:15 +00:00
r"(?i)^:\S+ 311 \S+ {nick} (?P<username>\S+) (?P<host>\S+) "
r". :(?P<realname>.*)"
2025-02-13 04:55:42 +00:00
)
},
{
2025-02-13 06:35:15 +00:00
'match': r"(?i)^:\S+ 312 \S+ {nick} (?P<server>\S+) :(?P<server_desc>.*)"
2025-02-13 04:55:42 +00:00
},
{'match': r"(?i)^:\S+ 317 \S+ {nick} (?P<idle>[0-9]+).*"},
2025-02-13 06:35:15 +00:00
{'match': r"(?i)^:\S+ 319 \S+ {nick} :(?P<channels>.*)", 'multi': True},
2025-02-13 04:55:42 +00:00
{
'match': (
2025-02-13 06:35:15 +00:00
r"(?i)^:\S+ 330 \S+ {nick} (?P<account>\S+) :(?P<account_desc>.*)"
2025-02-13 04:55:42 +00:00
)
},
{'match': r"(?i)^:\S+ 671 \S+ {nick} :(?P<connection>.*)"},
{
2025-02-13 06:35:15 +00:00
'match': r"(?i)^:\S+ (?P<retcode>(318|401)) \S+ (?P<nick>{nick}) :.*",
'final': True,
2025-02-13 04:55:42 +00:00
},
)
def process_results(self, results=None, **value):
2025-02-13 06:35:15 +00:00
"""Aggregate and structure WHOIS results into a consolidated dictionary.
2025-02-13 04:55:42 +00:00
Args:
2025-02-13 06:35:15 +00:00
results (list): Collected event responses.
**value: Accumulated data from event processing.
2025-02-13 04:55:42 +00:00
Returns:
2025-02-13 06:35:15 +00:00
dict: Structured user information with success status.
2025-02-13 04:55:42 +00:00
"""
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):
2025-02-13 06:35:15 +00:00
"""Handle WHO responses for channel user listings.
Attributes:
send_line (str): IRC command template for WHO requests.
"""
2025-02-13 04:55:42 +00:00
send_line = 'WHO {channel}'
events = (
{
'match': (
2025-02-13 06:35:15 +00:00
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>.*)"
2025-02-13 04:55:42 +00:00
),
2025-02-13 06:35:15 +00:00
'multi': True,
2025-02-13 04:55:42 +00:00
},
2025-02-13 06:35:15 +00:00
{'match': r"(?i)^:\S+ (?P<retcode>(315|401)) \S+ {channel} :.*", 'final': True},
2025-02-13 04:55:42 +00:00
)
def process_results(self, results=None, **value):
2025-02-13 06:35:15 +00:00
"""Compile WHO channel results into a list of users.
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
Args:
results (list): Raw event response data.
**value: Extracted key-value pairs from responses.
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
Returns:
dict: Processed result with user list and success status.
"""
2025-02-13 04:55:42 +00:00
users = []
for res in results:
if 'retcode' in res:
value.update(res)
else:
2025-02-13 06:35:15 +00:00
res['mask'] = utils.IrcString(f"{res['nick']}!{res['user']}@{res['host']}")
2025-02-13 04:55:42 +00:00
users.append(res)
value['users'] = users
value['success'] = value.get('retcode') == '315'
return value
@dec.plugin
class Async:
2025-02-13 06:35:15 +00:00
"""Expose asynchronous IRC command interfaces for plugin usage."""
2025-02-13 04:55:42 +00:00
def __init__(self, context):
2025-02-13 06:35:15 +00:00
"""Initialize with the bot context and register async commands."""
2025-02-13 04:55:42 +00:00
self.context = context
self.context.async_cmds = self
self.async_whois = Whois(context)
self.async_who_channel = WhoChannel(context)
2025-02-13 06:35:15 +00:00
def send_message(self, target: str, message: str):
"""Send a message to a target (channel or user).
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
Args:
target (str): Recipient channel or nickname.
message (str): Message content to send.
2025-02-13 04:55:42 +00:00
"""
2025-02-13 06:35:15 +00:00
self.context.privmsg(target, message)
2025-02-13 04:55:42 +00:00
@dec.extend
2025-02-13 06:35:15 +00:00
def whois(self, nick: str, timeout: int = 20):
"""Initiate a WHOIS query for a nickname.
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
Args:
nick (str): Nickname to query.
timeout (int): Response timeout in seconds.
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
Returns:
Awaitable[dict]: WHOIS result data.
2025-02-13 04:55:42 +00:00
"""
2025-02-13 06:35:15 +00:00
return self.async_whois(nick=nick.lower(), timeout=timeout)
2025-02-13 04:55:42 +00:00
@dec.extend
2025-02-13 06:35:15 +00:00
def who(self, target: str, timeout: int = 20):
"""Perform a WHO query on a channel or user.
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
Args:
target (str): Channel or nickname to query.
timeout (int): Response timeout in seconds.
2025-02-13 04:55:42 +00:00
2025-02-13 06:35:15 +00:00
Returns:
Awaitable[dict] | None: WHO results for channels, else None.
2025-02-13 04:55:42 +00:00
"""
2025-02-13 06:35:15 +00:00
target = target.lower()
if target.startswith('#'):
return self.async_who_channel(channel=target, timeout=timeout)
return None