Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions sopel/irc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
from sopel.tools import identifiers
from .backends import AsyncioBackend
from .isupport import ISupport
from .utils import CapReq, safe
from .utils import CapReq

if TYPE_CHECKING:
from sopel.config import Config
Expand Down Expand Up @@ -563,7 +563,6 @@ def write(self, args: Iterable[str], text: Optional[str] = None) -> None:
if self.backend is None:
raise RuntimeError(ERR_BACKEND_NOT_INITIALIZED)

args = [safe(arg) for arg in args]
self.backend.send_command(*args, text=text)

# IRC Commands
Expand Down Expand Up @@ -867,7 +866,7 @@ def say(
# update recipient metadata
flood_left = recipient_stack['flood_left'] - 1
recipient_stack['flood_left'] = max(0, flood_left)
recipient_stack['messages'].append((time.time(), safe(text)))
recipient_stack['messages'].append((time.time(), text))

# Now that we've sent the first part, we need to send the rest if
# requested. Doing so recursively seems simpler than iteratively.
Expand Down
22 changes: 11 additions & 11 deletions sopel/irc/abstract_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ def prepare_command(self, *args: str, text: Optional[str] = None) -> str:
and can be sent as-is.
"""
max_length = unicode_max_length = 510
raw_command = ' '.join(args)
raw_command = ' '.join(safe(arg) for arg in args)
if text is not None:
raw_command = '{args} :{text}'.format(args=raw_command,
text=safe(text))
Expand All @@ -151,7 +151,7 @@ def send_ping(self, host: str) -> None:
A ``PING`` command should be sent at a regular interval to make sure
the server knows the IRC connection is still active.
"""
self.send_command('PING', safe(host))
self.send_command('PING', host)

def send_pong(self, host: str) -> None:
"""Send a ``PONG`` command to the server.
Expand All @@ -161,14 +161,14 @@ def send_pong(self, host: str) -> None:
A ``PONG`` command must be sent each time the server sends a ``PING``
command to the client.
"""
self.send_command('PONG', safe(host))
self.send_command('PONG', host)

def send_nick(self, nick: str) -> None:
"""Send a ``NICK`` command with a ``nick``.

:param nick: nickname to take
"""
self.send_command('NICK', safe(nick))
self.send_command('NICK', nick)

def send_user(self, user: str, mode: str, nick: str, name: str) -> None:
"""Send a ``USER`` command with a ``user``.
Expand All @@ -178,14 +178,14 @@ def send_user(self, user: str, mode: str, nick: str, name: str) -> None:
:param nick: nickname associated with this user
:param name: "real name" for the user
"""
self.send_command('USER', safe(user), mode, safe(nick), text=name)
self.send_command('USER', user, mode, nick, text=name)

def send_pass(self, password: str) -> None:
"""Send a ``PASS`` command with a ``password``.

:param password: password for authentication
"""
self.send_command('PASS', safe(password))
self.send_command('PASS', password)

def send_join(self, channel: str, password: Optional[str] = None) -> None:
"""Send a ``JOIN`` command to ``channel`` with optional ``password``.
Expand All @@ -194,17 +194,17 @@ def send_join(self, channel: str, password: Optional[str] = None) -> None:
:param password: optional password for protected channels
"""
if password is None:
self.send_command('JOIN', safe(channel))
self.send_command('JOIN', channel)
else:
self.send_command('JOIN', safe(channel), safe(password))
self.send_command('JOIN', channel, password)

def send_part(self, channel: str, reason: Optional[str] = None) -> None:
"""Send a ``PART`` command to ``channel``.

:param channel: the channel to part
:param text: optional text for leaving the channel
"""
self.send_command('PART', safe(channel), text=reason)
self.send_command('PART', channel, text=reason)

def send_quit(self, reason: Optional[str] = None) -> None:
"""Send a ``QUIT`` command.
Expand All @@ -228,15 +228,15 @@ def send_kick(
:param nick: nickname to kick from the ``channel``
:param reason: optional reason for the kick
"""
self.send_command('KICK', safe(channel), safe(nick), text=reason)
self.send_command('KICK', channel, nick, text=reason)

def send_privmsg(self, dest: str, text: str) -> None:
"""Send a ``PRIVMSG`` command to ``dest`` with ``text``.

:param dest: nickname or channel name
:param text: the text to send
"""
self.send_command('PRIVMSG', safe(dest), text=text)
self.send_command('PRIVMSG', dest, text=text)

def send_notice(self, dest: str, text: str) -> None:
"""Send a ``NOTICE`` command to ``dest`` with ``text``.
Expand Down
27 changes: 27 additions & 0 deletions test/irc/test_irc_abstract_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,33 @@ def test_prepare_command_text_too_long():
assert result == expected


def test_prepare_command_command_safe():
backend = MockIRCBackend(BotCollector())

result = backend.prepare_command(
"PRIVMSG\r\nMODE #sopel +o Mallory\r\nPRIVMSG", "#sopel", text="Hello"
)
assert result == "PRIVMSGMODE #sopel +o MalloryPRIVMSG #sopel :Hello\r\n"


def test_prepare_command_target_safe():
backend = MockIRCBackend(BotCollector())

result = backend.prepare_command(
"PRIVMSG", "#sopel\r\nMODE #sopel +o Mallory\r\nPRIVMSG #sopel", text="Hello"
)
assert result == "PRIVMSG #sopelMODE #sopel +o MalloryPRIVMSG #sopel :Hello\r\n"


def test_prepare_command_text_safe():
backend = MockIRCBackend(BotCollector())

result = backend.prepare_command(
"PRIVMSG", "#sopel", text="Hello\r\nMODE #sopel +o Mallory"
)
assert result == "PRIVMSG #sopel :HelloMODE #sopel +o Mallory\r\n"


def test_send_command():
bot = BotCollector()
backend = MockIRCBackend(bot)
Expand Down