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
9 changes: 8 additions & 1 deletion docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ sopel.tools
.. automodule:: sopel.tools
:members:
:exclude-members: iteritems, iterkeys, itervalues, raw_input
:private-members: Identifier._lower, Identifier._lower_swapped


sopel.tools.identifiers
-----------------------

.. automodule:: sopel.tools.identifiers
:members:


sopel.tools.web
---------------
Expand Down
7 changes: 4 additions & 3 deletions docs/source/plugin/bot.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ second argument::

bot.say('The bot is now talking!', '#private-channel')

Instead of a string, you can use an instance of :class:`sopel.tools.Identifier`.
Instead of a string, you can use an instance of
:class:`~sopel.tools.identifiers.Identifier`.

If you want to reply to a user in a private message, you can use the trigger's
:attr:`~sopel.trigger.Trigger.nick` attribute as destination::
Expand Down Expand Up @@ -261,8 +262,8 @@ which provides the following information:
if not trigger.sender.is_nick():
# this trigger is from a channel

See :meth:`Identifier.is_nick() <sopel.tools.Identifier.is_nick>` for
more information.
See :meth:`Identifier.is_nick() <sopel.tools.identifiers.Identifier.is_nick>`
for more information.

Getting users in a channel
--------------------------
Expand Down
34 changes: 20 additions & 14 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from sopel import db, irc, logger, plugin, plugins, tools
from sopel.irc import modes
from sopel.plugins import jobs as plugin_jobs, rules as plugin_rules
from sopel.tools import deprecated, Identifier, jobs as tools_jobs
from sopel.tools import deprecated, jobs as tools_jobs
from sopel.trigger import PreTrigger, Trigger


Expand Down Expand Up @@ -81,23 +81,28 @@ def __init__(self, config, daemon=False):
self.modeparser = modes.ModeParser()
"""A mode parser used to parse ``MODE`` messages and modestrings."""

self.channels = tools.SopelIdentifierMemory()
self.channels = tools.SopelIdentifierMemory(
identifier_factory=self.make_identifier,
)
"""A map of the channels that Sopel is in.

The keys are :class:`sopel.tools.Identifier`\\s of the channel names,
and map to :class:`sopel.tools.target.Channel` objects which contain
the users in the channel and their permissions.
The keys are :class:`~sopel.tools.identifiers.Identifier`\\s of the
channel names, and map to :class:`~sopel.tools.target.Channel` objects
which contain the users in the channel and their permissions.
"""

self.users = tools.SopelIdentifierMemory()
self.users = tools.SopelIdentifierMemory(
identifier_factory=self.make_identifier,
)
"""A map of the users that Sopel is aware of.

The keys are :class:`sopel.tools.Identifier`\\s of the nicknames, and
map to :class:`sopel.tools.target.User` instances. In order for Sopel
to be aware of a user, it must share at least one mutual channel.
The keys are :class:`~sopel.tools.identifiers.Identifier`\\s of the
nicknames, and map to :class:`~sopel.tools.target.User` instances. In
order for Sopel to be aware of a user, it must share at least one
mutual channel.
"""

self.db = db.SopelDB(config)
self.db = db.SopelDB(config, identifier_factory=self.make_identifier)
"""The bot's database, as a :class:`sopel.db.SopelDB` instance."""

self.memory = tools.SopelMemory()
Expand Down Expand Up @@ -212,9 +217,10 @@ def has_channel_privilege(self, channel, privilege) -> bool:
>>> bot.has_channel_privilege('#chan', plugin.VOICE)
True

The ``channel`` argument can be either a :class:`str` or a
:class:`sopel.tools.Identifier`, as long as Sopel joined said channel.
If the channel is unknown, a :exc:`ValueError` will be raised.
The ``channel`` argument can be either a :class:`str` or an
:class:`~sopel.tools.identifiers.Identifier`, as long as Sopel joined
said channel. If the channel is unknown, a :exc:`ValueError` will be
raised.
"""
if channel not in self.channels:
raise ValueError('Unknown channel %s' % channel)
Expand Down Expand Up @@ -987,7 +993,7 @@ def _nick_blocked(self, nick: str) -> bool:
if not bad_nick:
continue
if (re.match(bad_nick + '$', nick, re.IGNORECASE) or
Identifier(bad_nick) == nick):
self.make_identifier(bad_nick) == nick):
return True
return False

Expand Down
2 changes: 1 addition & 1 deletion sopel/config/core_section.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,7 @@ def homedir(self):
:default: ``Sopel: https://sopel.chat/``
"""

nick = ValidatedAttribute('nick', Identifier, default=Identifier('Sopel'))
nick = ValidatedAttribute('nick', default='Sopel')
"""The nickname for the bot.
:default: ``Sopel``
Expand Down
56 changes: 38 additions & 18 deletions sopel/coretasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@

from sopel import config, plugin
from sopel.irc import isupport, utils
from sopel.tools import events, Identifier, jobs, SopelMemory, target
from sopel.tools import events, jobs, SopelMemory, target


LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -143,7 +143,7 @@ def auth_after_register(bot):

# nickserv-based auth method needs to check for current nick
if auth_method == 'nickserv':
if bot.nick != bot.settings.core.nick:
if bot.nick != bot.make_identifier(bot.settings.core.nick):
LOGGER.warning("Sending nickserv GHOST command.")
bot.say(
'GHOST %s %s' % (bot.settings.core.nick, auth_password),
Expand Down Expand Up @@ -347,6 +347,7 @@ def handle_isupport(bot, trigger):
botmode_support = 'BOT' in bot.isupport
namesx_support = 'NAMESX' in bot.isupport
uhnames_support = 'UHNAMES' in bot.isupport
casemapping_support = 'CASEMAPPING' in bot.isupport

# parse ISUPPORT message from server
parameters = {}
Expand All @@ -367,6 +368,11 @@ def handle_isupport(bot, trigger):
if 'PREFIX' in bot.isupport:
bot.modeparser.privileges = set(bot.isupport.PREFIX.keys())

# was CASEMAPPING support status updated?
if not casemapping_support and 'CASEMAPPING' in bot.isupport:
# Re-create the bot's nick with the proper identifier+casemapping
bot.rebuild_nick()

# was BOT mode support status updated?
if not botmode_support and 'BOT' in bot.isupport:
# yes it was! set our mode unless the config overrides it
Expand Down Expand Up @@ -482,9 +488,12 @@ def handle_names(bot, trigger):
channels = re.search(r'(#\S*)', trigger.raw)
if not channels:
return
channel = Identifier(channels.group(1))
channel = bot.make_identifier(channels.group(1))
if channel not in bot.channels:
bot.channels[channel] = target.Channel(channel)
bot.channels[channel] = target.Channel(
channel,
identifier_factory=bot.make_identifier,
)

# This could probably be made flexible in the future, but I don't think
# it'd be worth it.
Expand Down Expand Up @@ -515,7 +524,7 @@ def handle_names(bot, trigger):
if prefix in name:
priv = priv | value

nick = Identifier(name.lstrip(''.join(mapping.keys())))
nick = bot.make_identifier(name.lstrip(''.join(mapping.keys())))
user = bot.users.get(nick)
if user is None:
# The username/hostname will be included in a NAMES reply only if
Expand Down Expand Up @@ -559,7 +568,7 @@ def _parse_modes(bot, args, clear=False):
modes at https://modern.ircdocs.horse/#channel-mode

"""
channel_name = Identifier(args[0])
channel_name = bot.make_identifier(args[0])
if channel_name.is_nick():
# We don't do anything with user modes
LOGGER.debug("Ignoring user modes: %r", args)
Expand Down Expand Up @@ -622,7 +631,7 @@ def _parse_modes(bot, args, clear=False):
# modeinfo.privileges contains only the valid parsed privileges
for privilege, is_added, param in modeinfo.privileges:
# User privs modes, always have a param
nick = Identifier(param)
nick = bot.make_identifier(param)
priv = channel.privileges.get(nick, 0)
value = MODE_PREFIX_PRIVILEGES[privilege]
if is_added:
Expand Down Expand Up @@ -659,7 +668,7 @@ def _parse_modes(bot, args, clear=False):
def track_nicks(bot, trigger):
"""Track nickname changes and maintain our chanops list accordingly."""
old = trigger.nick
new = Identifier(trigger)
new = bot.make_identifier(trigger)

# Give debug message, and PM the owner, if the bot's own nick changes.
if old == bot.nick and new != bot.nick:
Expand Down Expand Up @@ -705,7 +714,7 @@ def track_part(bot, trigger):
@plugin.priority('medium')
def track_kick(bot, trigger):
"""Track users kicked from channels."""
nick = Identifier(trigger.args[1])
nick = bot.make_identifier(trigger.args[1])
channel = trigger.sender
_remove_from_channel(bot, nick, channel)
LOGGER.info(
Expand Down Expand Up @@ -748,7 +757,9 @@ def _send_who(bot, channel):
# We might be on an old network, but we still care about keeping our
# user list updated
bot.write(['WHO', channel])
bot.channels[Identifier(channel)].last_who = datetime.datetime.utcnow()

channel_id = bot.make_identifier(channel)
bot.channels[channel_id].last_who = datetime.datetime.utcnow()


@plugin.interval(30)
Expand Down Expand Up @@ -793,7 +804,10 @@ def track_join(bot, trigger):

# is it a new channel?
if channel not in bot.channels:
bot.channels[channel] = target.Channel(channel)
bot.channels[channel] = target.Channel(
channel,
identifier_factory=bot.make_identifier,
)

# did *we* just join?
if trigger.nick == bot.nick:
Expand Down Expand Up @@ -836,7 +850,8 @@ def track_quit(bot, trigger):

LOGGER.info("User quit: %s", trigger.nick)

if trigger.nick == bot.settings.core.nick and trigger.nick != bot.nick:
configured_nick = bot.make_identifier(bot.settings.core.nick)
if trigger.nick == configured_nick and trigger.nick != bot.nick:
# old nick is now available, let's change nick again
bot.change_current_nick(bot.settings.core.nick)
auth_after_register(bot)
Expand Down Expand Up @@ -1229,7 +1244,7 @@ def blocks(bot, trigger):
}

masks = set(s for s in bot.config.core.host_blocks if s != '')
nicks = set(Identifier(nick)
nicks = set(bot.make_identifier(nick)
for nick in bot.config.core.nick_blocks
if nick != '')
text = trigger.group().split()
Expand Down Expand Up @@ -1266,10 +1281,11 @@ def blocks(bot, trigger):

elif len(text) == 4 and text[1] == "del":
if text[2] == "nick":
if Identifier(text[3]) not in nicks:
nick = bot.make_identifier(text[3])
if nick not in nicks:
bot.reply(STRINGS['no_nick'] % (text[3]))
return
nicks.remove(Identifier(text[3]))
nicks.remove(nick)
bot.config.core.nick_blocks = [str(n) for n in nicks]
bot.config.save()
bot.reply(STRINGS['success_del'] % (text[3]))
Expand Down Expand Up @@ -1352,8 +1368,8 @@ def recv_whox(bot, trigger):


def _record_who(bot, channel, user, host, nick, account=None, away=None, modes=None):
nick = Identifier(nick)
channel = Identifier(channel)
nick = bot.make_identifier(nick)
channel = bot.make_identifier(channel)
if nick not in bot.users:
usr = target.User(nick, user, host)
bot.users[nick] = usr
Expand Down Expand Up @@ -1383,7 +1399,11 @@ def _record_who(bot, channel, user, host, nick, account=None, away=None, modes=N
for c in modes:
priv = priv | mapping[c]
if channel not in bot.channels:
bot.channels[channel] = target.Channel(channel)
bot.channels[channel] = target.Channel(
channel,
identifier_factory=bot.make_identifier,
)

bot.channels[channel].add_user(usr, privs=priv)


Expand Down
Loading