Skip to content

Commit

Permalink
coretasks: use sopel.irc.modes to parse modes
Browse files Browse the repository at this point in the history
  • Loading branch information
Exirel committed Jul 1, 2021
1 parent f0ad54e commit afeaad8
Showing 1 changed file with 80 additions and 92 deletions.
172 changes: 80 additions & 92 deletions sopel/coretasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import base64
import collections
import copy
import datetime
import functools
import logging
Expand All @@ -32,7 +33,7 @@

from sopel import loader, module, plugin
from sopel.config import ConfigurationError
from sopel.irc import isupport
from sopel.irc import isupport, modes as ircmodes
from sopel.irc.utils import CapReq, MyInfo
from sopel.tools import events, Identifier, SopelMemory, target, web

Expand Down Expand Up @@ -530,9 +531,6 @@ def _parse_modes(bot, args, clear=False):
LOGGER.debug(
"The server sent a possibly malformed MODE message: %r", args)

modestring = args[1]
params = args[2:]

mapping = {
"v": module.VOICE,
"h": module.HALFOP,
Expand All @@ -543,101 +541,91 @@ def _parse_modes(bot, args, clear=False):
"Y": module.OPER,
}

modes = {}
if not clear:
# Work on a copy for some thread safety
modes.update(channel.modes)

# Process modes
sign = ""
param_idx = 0
chanmodes = bot.isupport.CHANMODES
for char in modestring:
# Are we setting or unsetting
if char in "+-":
sign = char
continue

if char in chanmodes["A"]:
# Type A (beI, etc) have a nick or address param to add/remove
if char not in modes:
modes[char] = set()
if sign == "+":
modes[char].add(params[param_idx])
elif params[param_idx] in modes[char]:
modes[char].remove(params[param_idx])
param_idx += 1
elif char in chanmodes["B"]:
# Type B (k, etc) always have a param
if sign == "+":
modes[char] = params[param_idx]
elif char in modes:
modes.pop(char)
param_idx += 1
elif char in chanmodes["C"]:
# Type C (l, etc) have a param only when setting
if sign == "+":
modes[char] = params[param_idx]
param_idx += 1
elif char in modes:
modes.pop(char)
elif char in chanmodes["D"]:
# Type D (aciLmMnOpqrRst, etc) have no params
if sign == "+":
modes[char] = True
elif char in modes:
modes.pop(char)
elif char in mapping and (
"PREFIX" not in bot.isupport or char in bot.isupport.PREFIX
):
# User privs modes, always have a param
nick = Identifier(params[param_idx])
priv = channel.privileges.get(nick, 0)
# Log a warning if the two privilege-tracking data structures
# get out of sync. That should never happen.
# This is a good place to verify that bot.channels is doing
# what it's supposed to do before ultimately removing the old,
# deprecated bot.privileges structure completely.
ppriv = bot.privileges[channel_name].get(nick, 0)
if priv != ppriv:
LOGGER.warning(
(
"Privilege data error! Please share Sopel's "
"raw log with the developers, if enabled. "
"(Expected %s == %s for %r in %r)"
),
priv,
ppriv,
nick,
channel,
)
value = mapping.get(char)
if value is not None:
if sign == "+":
priv = priv | value
else:
priv = priv & ~value
bot.privileges[channel_name][nick] = priv
channel.privileges[nick] = priv
param_idx += 1
else:
# Might be in a mode block past A/B/C/D, but we don't speak those.
# Send a WHO to ensure no user priv modes we're skipping are lost.
privileges = mapping.keys()
if 'PREFIX' in bot.isupport:
privileges = bot.isupport.PREFIX.keys()

modemessage = ircmodes.ModeMessage(
bot.isupport.CHANMODES,
privileges=set(privileges),
)
modeinfo = modemessage.parse_modestring(args[1], tuple(args[2:]))

# set or update channel's modes
modes = {} if clear else copy.deepcopy(channel.modes)
for letter, mode, is_added, param in modeinfo.modes:
if letter == 'A':
if mode not in modes:
modes[mode] = set()
if is_added:
modes[mode].add(param)
elif param in modes[mode]:
modes[mode].remove(param)
elif letter == 'B':
if is_added:
modes[mode] = param
elif mode in modes:
modes.pop(mode)
elif letter == 'C':
if is_added:
modes[mode] = param
elif mode in modes:
modes.pop(mode)
elif letter == 'D':
if is_added:
modes[mode] = True
elif mode in modes:
modes.pop(mode)

# atomic change of channel's modes
channel.modes = modes

# update user privileges in channel
for privilege, is_added, param in modeinfo.privileges:
# User privs modes, always have a param
nick = Identifier(param)
priv = channel.privileges.get(nick, 0)
# Log a warning if the two privilege-tracking data structures
# get out of sync. That should never happen.
# This is a good place to verify that bot.channels is doing
# what it's supposed to do before ultimately removing the old,
# deprecated bot.privileges structure completely.
ppriv = bot.privileges[channel_name].get(nick, 0)
if priv != ppriv:
LOGGER.warning(
"Unknown MODE message, sending WHO. Message was: %r",
args,
(
"Privilege data error! Please share Sopel's "
"raw log with the developers, if enabled. "
"(Expected %s == %s for %r in %r)"
),
priv,
ppriv,
nick,
channel,
)
_send_who(bot, channel_name)
return
value = mapping[privilege]
if is_added:
priv = priv | value
else:
priv = priv & ~value
bot.privileges[channel_name][nick] = priv
channel.privileges[nick] = priv

if param_idx != len(params):
# log ignored modes (modes Sopel doesn't know how to handle)
if modeinfo.ignored_modes:
LOGGER.warning(
"Too many arguments received for MODE: args=%r chanmodes=%r",
"Unknown MODE message, sending WHO. Message was: %r",
args,
chanmodes,
)
_send_who(bot, channel_name)

channel.modes = modes
# log leftover parameters (too many arguments)
if modeinfo.leftover_params:
LOGGER.warning(
"Too many arguments received for MODE: args=%r chanmodes=%r",
args,
modemessage.chanmodes,
)

LOGGER.info("Updated mode for channel: %s", channel.name)
LOGGER.debug("Channel %r mode: %r", str(channel.name), channel.modes)
Expand Down

0 comments on commit afeaad8

Please sign in to comment.