forked from platypush/platypush
[irc] Plugin rename/refactor.
The `chat.irc` plugin is now `irc`.
This commit is contained in:
parent
1ba85231d8
commit
7637890a54
6 changed files with 207 additions and 104 deletions
|
@ -1,5 +0,0 @@
|
|||
``chat.irc``
|
||||
============
|
||||
|
||||
.. automodule:: platypush.plugins.chat.irc
|
||||
:members:
|
5
docs/source/platypush/plugins/irc.rst
Normal file
5
docs/source/platypush/plugins/irc.rst
Normal file
|
@ -0,0 +1,5 @@
|
|||
``irc``
|
||||
=======
|
||||
|
||||
.. automodule:: platypush.plugins.irc
|
||||
:members:
|
|
@ -22,7 +22,6 @@ Plugins
|
|||
platypush/plugins/camera.ir.mlx90640.rst
|
||||
platypush/plugins/camera.pi.rst
|
||||
platypush/plugins/camera.pi.legacy.rst
|
||||
platypush/plugins/chat.irc.rst
|
||||
platypush/plugins/chat.telegram.rst
|
||||
platypush/plugins/clipboard.rst
|
||||
platypush/plugins/config.rst
|
||||
|
@ -55,6 +54,7 @@ Plugins
|
|||
platypush/plugins/http.webpage.rst
|
||||
platypush/plugins/ifttt.rst
|
||||
platypush/plugins/inspect.rst
|
||||
platypush/plugins/irc.rst
|
||||
platypush/plugins/joystick.rst
|
||||
platypush/plugins/kafka.rst
|
||||
platypush/plugins/lastfm.rst
|
||||
|
|
|
@ -2,6 +2,7 @@ import os
|
|||
from typing import Sequence, Dict, Tuple, Union, Optional
|
||||
|
||||
from platypush.plugins import RunnablePlugin, action
|
||||
from platypush.plugins.chat import ChatPlugin
|
||||
from platypush.schemas.irc import (
|
||||
IRCServerSchema,
|
||||
IRCServerStatusSchema,
|
||||
|
@ -9,10 +10,9 @@ from platypush.schemas.irc import (
|
|||
)
|
||||
|
||||
from ._bot import IRCBot
|
||||
from .. import ChatPlugin
|
||||
|
||||
|
||||
class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
|
||||
class IrcPlugin(RunnablePlugin, ChatPlugin):
|
||||
"""
|
||||
IRC integration.
|
||||
|
||||
|
@ -48,7 +48,7 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
|
|||
try:
|
||||
self._bots: Dict[Tuple[str, int], IRCBot] = {
|
||||
(server_conf['server'], server_conf['port']): IRCBot(**server_conf)
|
||||
for server_conf in IRCServerSchema().load(servers, many=True)
|
||||
for server_conf in IRCServerSchema().load(servers, many=True) # type: ignore
|
||||
}
|
||||
except Exception as e:
|
||||
self.logger.warning(f'Could not load IRC server configuration: {e}')
|
||||
|
@ -57,15 +57,15 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
|
|||
|
||||
@property
|
||||
def _bots_by_server(self) -> Dict[str, IRCBot]:
|
||||
return {bot.server: bot for srv, bot in self._bots.items()}
|
||||
return {bot.server: bot for bot in self._bots.values()}
|
||||
|
||||
@property
|
||||
def _bots_by_server_and_port(self) -> Dict[Tuple[str, int], IRCBot]:
|
||||
return {(bot.server, bot.port): bot for srv, bot in self._bots.items()}
|
||||
return {(bot.server, bot.port): bot for bot in self._bots.values()}
|
||||
|
||||
@property
|
||||
def _bots_by_alias(self) -> Dict[str, IRCBot]:
|
||||
return {bot.alias: bot for srv, bot in self._bots.items() if bot.alias}
|
||||
return {bot.alias: bot for bot in self._bots.values() if bot.alias}
|
||||
|
||||
def main(self):
|
||||
self._connect()
|
||||
|
@ -88,7 +88,7 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
|
|||
|
||||
def _get_bot(self, server: Union[str, Tuple[str, int]]) -> IRCBot:
|
||||
if isinstance(server, (tuple, list, set)):
|
||||
bot = self._bots_by_server_and_port[tuple(server)]
|
||||
bot = self._bots_by_server_and_port[tuple(server)] # type: ignore
|
||||
else:
|
||||
bot = self._bots_by_alias.get(server, self._bots_by_server.get(server))
|
||||
|
||||
|
@ -184,22 +184,24 @@ class ChatIrcPlugin(RunnablePlugin, ChatPlugin):
|
|||
"""
|
||||
bot = self._get_bot(server)
|
||||
channel_name = channel
|
||||
channel = bot.channels.get(channel)
|
||||
assert channel, f'Not connected to channel {channel}'
|
||||
return IRCChannelSchema().dump(
|
||||
ch = bot.channels.get(channel)
|
||||
assert ch, f'Not connected to channel {channel}'
|
||||
return dict(
|
||||
IRCChannelSchema().dump(
|
||||
{
|
||||
'is_invite_only': channel.is_invite_only(),
|
||||
'is_moderated': channel.is_moderated(),
|
||||
'is_protected': channel.is_protected(),
|
||||
'is_secret': channel.is_secret(),
|
||||
'is_invite_only': ch.is_invite_only(),
|
||||
'is_moderated': ch.is_moderated(),
|
||||
'is_protected': ch.is_protected(),
|
||||
'is_secret': ch.is_secret(),
|
||||
'name': channel_name,
|
||||
'modes': channel.modes,
|
||||
'opers': list(channel.opers()),
|
||||
'owners': channel.owners(),
|
||||
'users': list(channel.users()),
|
||||
'voiced': list(channel.voiced()),
|
||||
'modes': ch.modes,
|
||||
'opers': list(ch.opers()),
|
||||
'owners': ch.owners(),
|
||||
'users': list(ch.users()),
|
||||
'voiced': list(ch.voiced()),
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@action
|
||||
def send_ctcp_message(
|
|
@ -8,32 +8,61 @@ import select
|
|||
import socket
|
||||
import ssl as _ssl
|
||||
import struct
|
||||
from typing import Iterable, Optional, Type, Sequence, Dict, Tuple
|
||||
from typing import Generator, Iterable, Optional, Type, Sequence, Dict, Tuple
|
||||
|
||||
import irc.bot
|
||||
from irc.client import Connection, Event as _IRCEvent, ServerConnection, DCCConnection, ip_numstr_to_quad, \
|
||||
ip_quad_to_numstr
|
||||
from irc.client import (
|
||||
Connection,
|
||||
Event as _IRCEvent,
|
||||
ServerConnection,
|
||||
DCCConnection,
|
||||
ip_numstr_to_quad,
|
||||
ip_quad_to_numstr,
|
||||
)
|
||||
from irc.connection import Factory as ConnectionFactory
|
||||
from irc.events import codes as irc_event_codes
|
||||
|
||||
from platypush.context import get_bus
|
||||
from platypush.message.event.irc import IRCEvent, IRCChannelJoinEvent, IRCChannelKickEvent, IRCDisconnectEvent, \
|
||||
IRCModeEvent, IRCNickChangeEvent, IRCPartEvent, IRCQuitEvent, IRCConnectEvent, IRCPrivateMessageEvent, \
|
||||
IRCPublicMessageEvent, IRCDCCRequestEvent, IRCDCCMessageEvent, IRCDCCFileRequestEvent, IRCCTCPMessageEvent, \
|
||||
IRCDCCFileRecvCompletedEvent, IRCDCCFileRecvCancelledEvent, IRCDCCFileSendCompletedEvent, \
|
||||
IRCDCCFileSendCancelledEvent
|
||||
from platypush.message.event.irc import (
|
||||
IRCEvent,
|
||||
IRCChannelJoinEvent,
|
||||
IRCChannelKickEvent,
|
||||
IRCDisconnectEvent,
|
||||
IRCModeEvent,
|
||||
IRCNickChangeEvent,
|
||||
IRCPartEvent,
|
||||
IRCQuitEvent,
|
||||
IRCConnectEvent,
|
||||
IRCPrivateMessageEvent,
|
||||
IRCPublicMessageEvent,
|
||||
IRCDCCRequestEvent,
|
||||
IRCDCCMessageEvent,
|
||||
IRCDCCFileRequestEvent,
|
||||
IRCCTCPMessageEvent,
|
||||
IRCDCCFileRecvCompletedEvent,
|
||||
IRCDCCFileRecvCancelledEvent,
|
||||
IRCDCCFileSendCompletedEvent,
|
||||
IRCDCCFileSendCancelledEvent,
|
||||
)
|
||||
|
||||
|
||||
class IRCBot(irc.bot.SingleServerIRCBot):
|
||||
def __init__(
|
||||
self, server: str, port: int, nickname: str, alias: str,
|
||||
self,
|
||||
server: str,
|
||||
port: int,
|
||||
nickname: str,
|
||||
alias: str,
|
||||
channels: Iterable[str],
|
||||
response_timeout: Optional[float],
|
||||
dcc_file_transfer_timeout: Optional[float],
|
||||
dcc_accept_timeout: Optional[float],
|
||||
dcc_downloads_dir: str, realname: Optional[str] = None,
|
||||
password: Optional[str] = None, ssl: bool = False,
|
||||
ipv6: bool = False, stop_message: Optional[str] = None,
|
||||
dcc_downloads_dir: str,
|
||||
realname: Optional[str] = None,
|
||||
password: Optional[str] = None,
|
||||
ssl: bool = False,
|
||||
ipv6: bool = False,
|
||||
stop_message: Optional[str] = None,
|
||||
dcc_ip_whitelist: Optional[Sequence[str]] = None,
|
||||
dcc_ip_blacklist: Optional[Sequence[str]] = None,
|
||||
dcc_nick_whitelist: Optional[Sequence[str]] = None,
|
||||
|
@ -47,8 +76,10 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
connection_factory.family = socket.AF_INET6
|
||||
|
||||
super().__init__(
|
||||
server_list=[(server, port)], nickname=nickname,
|
||||
realname=realname, connect_factory=connection_factory
|
||||
server_list=[(server, port)],
|
||||
nickname=nickname,
|
||||
realname=realname,
|
||||
connect_factory=connection_factory,
|
||||
)
|
||||
|
||||
self.server = server
|
||||
|
@ -78,7 +109,9 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
def stop_message(self) -> Optional[str]:
|
||||
return self._stop_message
|
||||
|
||||
def _post_event(self, connection: Connection, output_event_type: Type[IRCEvent], **kwargs):
|
||||
def _post_event(
|
||||
self, connection: Connection, output_event_type: Type[IRCEvent], **kwargs
|
||||
):
|
||||
if isinstance(connection, ServerConnection):
|
||||
kwargs['server'] = connection.server
|
||||
kwargs['port'] = connection.port
|
||||
|
@ -89,26 +122,39 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
get_bus().post(event)
|
||||
|
||||
def on_join(self, connection: ServerConnection, event: _IRCEvent):
|
||||
self._post_event(connection, IRCChannelJoinEvent, channel=event.target, nick=event.source.nick)
|
||||
self._post_event(
|
||||
connection,
|
||||
IRCChannelJoinEvent,
|
||||
channel=event.target,
|
||||
nick=event.source.nick,
|
||||
)
|
||||
super()._on_join(connection, event)
|
||||
|
||||
def on_kick(self, connection: ServerConnection, event: _IRCEvent):
|
||||
self._post_event(
|
||||
connection, IRCChannelKickEvent, channel=event.target,
|
||||
source_nick=event.arguments[1], target_nick=event.arguments[0]
|
||||
connection,
|
||||
IRCChannelKickEvent,
|
||||
channel=event.target,
|
||||
source_nick=event.arguments[1],
|
||||
target_nick=event.arguments[0],
|
||||
)
|
||||
|
||||
super()._on_kick(connection, event)
|
||||
|
||||
def on_nick(self, connection: ServerConnection, event: _IRCEvent):
|
||||
self._post_event(
|
||||
connection, IRCNickChangeEvent, before=event.source.nick, after=event.target.nick
|
||||
connection,
|
||||
IRCNickChangeEvent,
|
||||
before=event.source.nick,
|
||||
after=event.target.nick,
|
||||
)
|
||||
super()._on_nick(connection, event)
|
||||
|
||||
def on_mode(self, connection: ServerConnection, event: _IRCEvent):
|
||||
self._post_event(
|
||||
connection, IRCModeEvent, mode=event.arguments[0],
|
||||
connection,
|
||||
IRCModeEvent,
|
||||
mode=event.arguments[0],
|
||||
source=event.source.nick,
|
||||
target_=(event.arguments[1] if len(event.arguments) > 1 else None),
|
||||
channel=event.target,
|
||||
|
@ -140,18 +186,22 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
|
||||
def on_pubmsg(self, connection: ServerConnection, event: _IRCEvent):
|
||||
self._post_event(
|
||||
connection, IRCPublicMessageEvent,
|
||||
text=event.arguments[0], nick=event.source.nick,
|
||||
connection,
|
||||
IRCPublicMessageEvent,
|
||||
text=event.arguments[0],
|
||||
nick=event.source.nick,
|
||||
channel=event.target,
|
||||
mentions_me=self._mentions_me(connection, event.arguments[0])
|
||||
mentions_me=self._mentions_me(connection, event.arguments[0]),
|
||||
)
|
||||
|
||||
def on_privmsg(self, connection: ServerConnection, event: _IRCEvent):
|
||||
self._post_event(
|
||||
connection, IRCPrivateMessageEvent,
|
||||
text=event.arguments[0], nick=event.source.nick,
|
||||
connection,
|
||||
IRCPrivateMessageEvent,
|
||||
text=event.arguments[0],
|
||||
nick=event.source.nick,
|
||||
channel=event.target,
|
||||
mentions_me=self._mentions_me(connection, event.arguments[0])
|
||||
mentions_me=self._mentions_me(connection, event.arguments[0]),
|
||||
)
|
||||
|
||||
def on_dccchat(self, connection: DCCConnection, event: _IRCEvent):
|
||||
|
@ -168,7 +218,9 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
|
||||
nick = event.source.nick
|
||||
if not self._is_dcc_connection_request_allowed(address=address, nick=nick):
|
||||
self.logger.info(f'Refused DCC connection from address={address} nick={nick}')
|
||||
self.logger.info(
|
||||
f'Refused DCC connection from address={address} nick={nick}'
|
||||
)
|
||||
connection.disconnect('Unauthorized peer')
|
||||
return
|
||||
|
||||
|
@ -194,15 +246,23 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
conn_map = getattr(self, f'_dcc_{proc_type}_procs', {})
|
||||
conn_map.pop((addr, port), None)
|
||||
|
||||
def _process_dcc_recv(self, connection: DCCConnection, filename: str, address: str, port: int, size: int):
|
||||
def _process_dcc_recv(
|
||||
self,
|
||||
connection: DCCConnection,
|
||||
filename: str,
|
||||
address: str,
|
||||
port: int,
|
||||
size: int,
|
||||
):
|
||||
pathlib.Path(self.dcc_downloads_dir).mkdir(parents=True, exist_ok=True)
|
||||
filename = os.path.abspath(os.path.join(self.dcc_downloads_dir, filename))
|
||||
assert filename.startswith(self.dcc_downloads_dir), (
|
||||
'Attempt to save a file outside the downloads directory'
|
||||
)
|
||||
assert filename.startswith(
|
||||
self.dcc_downloads_dir
|
||||
), 'Attempt to save a file outside the downloads directory'
|
||||
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock, \
|
||||
open(filename, 'wb') as f:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock, open(
|
||||
filename, 'wb'
|
||||
) as f:
|
||||
try:
|
||||
sock.connect((address, port))
|
||||
processed_bytes = 0
|
||||
|
@ -214,15 +274,23 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
sock.send(f'{socket.htonl(processed_bytes)}'.encode())
|
||||
|
||||
self._post_event(
|
||||
connection, IRCDCCFileRecvCompletedEvent, address=address,
|
||||
port=port, file=filename, size=size
|
||||
connection,
|
||||
IRCDCCFileRecvCompletedEvent,
|
||||
address=address,
|
||||
port=port,
|
||||
file=filename,
|
||||
size=size,
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.error(f'DCC transfer error from {address}:{port}: {e}')
|
||||
self.logger.exception(e)
|
||||
self._post_event(
|
||||
connection, IRCDCCFileRecvCancelledEvent, address=address,
|
||||
port=port, file=filename, error=str(e)
|
||||
connection,
|
||||
IRCDCCFileRecvCancelledEvent,
|
||||
address=address,
|
||||
port=port,
|
||||
file=filename,
|
||||
error=str(e),
|
||||
)
|
||||
finally:
|
||||
connection.disconnect()
|
||||
|
@ -265,22 +333,33 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
buf = f.read(4096)
|
||||
|
||||
self._post_event(
|
||||
connection, IRCDCCFileSendCompletedEvent,
|
||||
file=filename, address=address, port=transfer_port
|
||||
connection,
|
||||
IRCDCCFileSendCompletedEvent,
|
||||
file=filename,
|
||||
address=address,
|
||||
port=transfer_port,
|
||||
)
|
||||
finally:
|
||||
client_sock.close()
|
||||
dcc_sock.close()
|
||||
except Exception as e:
|
||||
self.logger.error(f'DCC transfer error to {connection.peeraddress}:{connection.peerport}: {e}')
|
||||
self.logger.error(
|
||||
f'DCC transfer error to {connection.peeraddress}:{connection.peerport}: {e}'
|
||||
)
|
||||
self.logger.exception(e)
|
||||
self._post_event(
|
||||
connection, IRCDCCFileSendCancelledEvent, address=connection.peeraddress,
|
||||
port=connection.peerport, file=filename, error=str(e)
|
||||
connection,
|
||||
IRCDCCFileSendCancelledEvent,
|
||||
address=connection.peeraddress,
|
||||
port=connection.peerport,
|
||||
file=filename,
|
||||
error=str(e),
|
||||
)
|
||||
finally:
|
||||
connection.disconnect()
|
||||
self._dcc_proc_completion_queue.put(('send', connection.localaddress, connection.localport))
|
||||
self._dcc_proc_completion_queue.put(
|
||||
('send', connection.localaddress, connection.localport)
|
||||
)
|
||||
|
||||
def _set_accept_timeout(self, sock: socket.socket):
|
||||
if self.dcc_accept_timeout:
|
||||
|
@ -293,12 +372,10 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
if proc and proc.is_alive():
|
||||
proc.terminate()
|
||||
|
||||
def _accept_dcc_file_request(self, connection: DCCConnection, ctcp_msg: str, nick: str):
|
||||
ctcp_msg = [
|
||||
token.strip()
|
||||
for token in ctcp_msg.split(' ')
|
||||
if token
|
||||
]
|
||||
def _accept_dcc_file_request(
|
||||
self, connection: DCCConnection, ctcp_msg: str, nick: str
|
||||
):
|
||||
ctcp_msg = [token.strip() for token in ctcp_msg.split(' ') if token]
|
||||
|
||||
filename = ' '.join(ctcp_msg[2:-3])
|
||||
address, port, size = ctcp_msg[-3:]
|
||||
|
@ -315,22 +392,26 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
return
|
||||
|
||||
self._post_event(
|
||||
connection, IRCDCCFileRequestEvent,
|
||||
connection,
|
||||
IRCDCCFileRequestEvent,
|
||||
address=address,
|
||||
port=port, file=filename,
|
||||
size=size, nick=nick
|
||||
port=port,
|
||||
file=filename,
|
||||
size=size,
|
||||
nick=nick,
|
||||
)
|
||||
|
||||
# Accept the file request
|
||||
# (if we're here then the peer is whitelisted/not blacklisted)
|
||||
self._dcc_recv_procs[(address, port)] = multiprocessing.Process(
|
||||
target=self._process_dcc_recv, kwargs={
|
||||
target=self._process_dcc_recv,
|
||||
kwargs={
|
||||
'connection': connection,
|
||||
'address': address,
|
||||
'port': port,
|
||||
'filename': filename,
|
||||
'size': size,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
self._dcc_recv_procs[(address, port)].start()
|
||||
|
@ -342,7 +423,7 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
|
||||
try:
|
||||
msg = [
|
||||
body[(0 if i == 0 else sep_pos[i - 1] + 1):pos].decode().strip()
|
||||
body[(0 if i == 0 else sep_pos[i - 1] + 1) : pos].decode().strip()
|
||||
for i, pos in enumerate(sep_pos)
|
||||
][1:]
|
||||
except (ValueError, TypeError):
|
||||
|
@ -361,10 +442,15 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
|
||||
def on_dccmsg(self, connection: DCCConnection, event: _IRCEvent):
|
||||
ctcp_header = b'CTCP_MESSAGE'
|
||||
if event.arguments[0][:len(ctcp_header)] == ctcp_header:
|
||||
if event.arguments[0][: len(ctcp_header)] == ctcp_header:
|
||||
return self._handle_ctcp_message(connection, event)
|
||||
|
||||
self._post_event(connection, IRCDCCMessageEvent, address=event.source, body=event.arguments[0])
|
||||
self._post_event(
|
||||
connection,
|
||||
IRCDCCMessageEvent,
|
||||
address=event.source,
|
||||
body=event.arguments[0],
|
||||
)
|
||||
|
||||
def on_disconnect(self, connection: Connection, event: _IRCEvent):
|
||||
self._post_event(connection, IRCDisconnectEvent)
|
||||
|
@ -385,7 +471,9 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
q.put(event)
|
||||
|
||||
def _on_generic_event(self, _, event: _IRCEvent):
|
||||
self.logger.debug(f'Received raw unhandled IRC event on {self.server}: {event.__dict__}')
|
||||
self.logger.debug(
|
||||
f'Received raw unhandled IRC event on {self.server}: {event.__dict__}'
|
||||
)
|
||||
|
||||
def _is_dcc_connection_request_allowed(self, address: str, nick: str) -> bool:
|
||||
if self.dcc_ip_whitelist and address not in self.dcc_ip_whitelist:
|
||||
|
@ -397,7 +485,10 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
if self.dcc_nick_blacklist and nick in self.dcc_nick_blacklist:
|
||||
return False
|
||||
|
||||
if self.dcc_max_connections and len(self._dcc_recv_procs) >= self.dcc_max_connections:
|
||||
if (
|
||||
self.dcc_max_connections
|
||||
and len(self._dcc_recv_procs) >= self.dcc_max_connections
|
||||
):
|
||||
self.logger.warning(
|
||||
f'Refused new DCC connection: maximum number of concurrent '
|
||||
f'connections ({self.dcc_max_connections}) reached'
|
||||
|
@ -407,29 +498,35 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
|
||||
return True
|
||||
|
||||
def dcc_file_transfer(self, file: str, nick: str, bind_address: Optional[str] = None):
|
||||
def dcc_file_transfer(
|
||||
self, file: str, nick: str, bind_address: Optional[str] = None
|
||||
):
|
||||
conn: DCCConnection = self.dcc('chat')
|
||||
bind_address = (bind_address, 0) if bind_address else None
|
||||
conn.listen(bind_address)
|
||||
conn.passive = False
|
||||
self.connection.privmsg(
|
||||
nick,
|
||||
f'\x01DCC CHAT chat {ip_quad_to_numstr(conn.localaddress)} {conn.localport}\x01'
|
||||
f'\x01DCC CHAT chat {ip_quad_to_numstr(conn.localaddress)} {conn.localport}\x01',
|
||||
)
|
||||
|
||||
send_proc = self._dcc_send_procs[(conn.localaddress, conn.localport)] = multiprocessing.Process(
|
||||
send_proc = self._dcc_send_procs[
|
||||
(conn.localaddress, conn.localport)
|
||||
] = multiprocessing.Process(
|
||||
target=self._process_dcc_send,
|
||||
kwargs={
|
||||
'connection': conn,
|
||||
'filename': file,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
send_proc.start()
|
||||
send_proc.join()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def event_queue(self, event_type: str) -> multiprocessing.Queue:
|
||||
def event_queue(
|
||||
self, event_type: str
|
||||
) -> Generator[multiprocessing.Queue, None, None]:
|
||||
q = self._pending_requests[event_type] = multiprocessing.Queue()
|
||||
try:
|
||||
yield q
|
||||
|
@ -438,7 +535,9 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
self._pending_requests.pop(event_type, None)
|
||||
|
||||
def start(self):
|
||||
self._dcc_processor = multiprocessing.Process(target=self._dcc_connect_processor)
|
||||
self._dcc_processor = multiprocessing.Process(
|
||||
target=self._dcc_connect_processor
|
||||
)
|
||||
self._dcc_processor.start()
|
||||
|
||||
for event_code in irc_event_codes.keys():
|
||||
|
@ -452,7 +551,9 @@ class IRCBot(irc.bot.SingleServerIRCBot):
|
|||
|
||||
def stop(self, msg: Optional[str] = None):
|
||||
msg = msg or self.stop_message
|
||||
for addr, port in list(self._dcc_recv_procs.keys()) + list(self._dcc_send_procs.keys()):
|
||||
for addr, port in list(self._dcc_recv_procs.keys()) + list(
|
||||
self._dcc_send_procs.keys()
|
||||
):
|
||||
self._stop_dcc_connection(addr, port)
|
||||
|
||||
if self._dcc_processor and self._dcc_processor.is_alive():
|
|
@ -18,7 +18,7 @@ manifest:
|
|||
platypush.message.event.irc.IRCDCCFileRecvCancelledEvent: when a DCC file download is cancelled
|
||||
platypush.message.event.irc.IRCDCCFileSendCompletedEvent: when a DCC file upload is completed
|
||||
platypush.message.event.irc.IRCDCCFileSendCancelledEvent: when a DCC file upload is cancelled
|
||||
package: platypush.plugins.chat.irc
|
||||
package: platypush.plugins.irc
|
||||
type: plugin
|
||||
install:
|
||||
apt:
|
Loading…
Reference in a new issue