Skip to content

Commit 9103422

Browse files
authored
Merge pull request #2552 from sopel-irc/bot-identifiermemory-helper
irc, bot, builtins: add & use `AbstractBot.make_identifier_memory()` helper
2 parents 72493dc + 89b8d49 commit 9103422

File tree

7 files changed

+87
-22
lines changed

7 files changed

+87
-22
lines changed

sopel/bot.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,15 @@ def __init__(self, config, daemon=False):
7575
self.modeparser = modes.ModeParser()
7676
"""A mode parser used to parse ``MODE`` messages and modestrings."""
7777

78-
self.channels = tools.SopelIdentifierMemory(
79-
identifier_factory=self.make_identifier,
80-
)
78+
self.channels = self.make_identifier_memory()
8179
"""A map of the channels that Sopel is in.
8280
8381
The keys are :class:`~sopel.tools.identifiers.Identifier`\\s of the
8482
channel names, and map to :class:`~sopel.tools.target.Channel` objects
8583
which contain the users in the channel and their permissions.
8684
"""
8785

88-
self.users = tools.SopelIdentifierMemory(
89-
identifier_factory=self.make_identifier,
90-
)
86+
self.users = self.make_identifier_memory()
9187
"""A map of the users that Sopel is aware of.
9288
9389
The keys are :class:`~sopel.tools.identifiers.Identifier`\\s of the

sopel/builtins/find.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,11 @@
1818

1919
from sopel import plugin
2020
from sopel.formatting import bold
21-
from sopel.tools import SopelIdentifierMemory
2221

2322

2423
def setup(bot):
2524
if 'find_lines' not in bot.memory:
26-
bot.memory['find_lines'] = SopelIdentifierMemory(
27-
identifier_factory=bot.make_identifier,
28-
)
25+
bot.memory['find_lines'] = bot.make_identifier_memory()
2926

3027

3128
def shutdown(bot):
@@ -49,9 +46,7 @@ def collectlines(bot, trigger):
4946

5047
# Add a log for the channel and nick, if there isn't already one
5148
if trigger.sender not in bot.memory['find_lines']:
52-
bot.memory['find_lines'][trigger.sender] = SopelIdentifierMemory(
53-
identifier_factory=bot.make_identifier,
54-
)
49+
bot.memory['find_lines'][trigger.sender] = bot.make_identifier_memory()
5550
if trigger.nick not in bot.memory['find_lines'][trigger.sender]:
5651
bot.memory['find_lines'][trigger.sender][trigger.nick] = deque(maxlen=10)
5752

sopel/builtins/translate.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import requests
1717

18-
from sopel import plugin, tools
18+
from sopel import plugin
1919
from sopel.tools import web
2020

2121

@@ -25,9 +25,7 @@
2525

2626
def setup(bot):
2727
if 'mangle_lines' not in bot.memory:
28-
bot.memory['mangle_lines'] = tools.SopelIdentifierMemory(
29-
identifier_factory=bot.make_identifier,
30-
)
28+
bot.memory['mangle_lines'] = bot.make_identifier_memory()
3129

3230

3331
def shutdown(bot):

sopel/builtins/url.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,7 @@ def setup(bot: Sopel):
133133

134134
# Ensure last_seen_url is in memory
135135
if 'last_seen_url' not in bot.memory:
136-
bot.memory['last_seen_url'] = tools.SopelIdentifierMemory(
137-
identifier_factory=bot.make_identifier,
138-
)
136+
bot.memory['last_seen_url'] = bot.make_identifier_memory()
139137

140138
# Initialize shortened_urls as a dict if it doesn't exist.
141139
if 'shortened_urls' not in bot.memory:

sopel/irc/__init__.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545

4646
from sopel import tools, trigger
4747
from sopel.lifecycle import deprecated
48-
from sopel.tools import identifiers
48+
from sopel.tools import identifiers, memories
4949
from .backends import AsyncioBackend, UninitializedBackend
5050
from .capabilities import Capabilities
5151
from .isupport import ISupport
@@ -227,7 +227,10 @@ def hostmask(self) -> Optional[str]:
227227
# Utility
228228

229229
def make_identifier(self, name: str) -> identifiers.Identifier:
230-
"""Instantiate an Identifier using the bot's context."""
230+
"""Instantiate an Identifier using the bot's context.
231+
232+
.. versionadded:: 8.0
233+
"""
231234
casemapping = {
232235
'ascii': identifiers.ascii_lower,
233236
'rfc1459': identifiers.rfc1459_lower,
@@ -242,6 +245,36 @@ def make_identifier(self, name: str) -> identifiers.Identifier:
242245
chantypes=chantypes,
243246
)
244247

248+
def make_identifier_memory(self) -> memories.SopelIdentifierMemory:
249+
"""Instantiate a SopelIdentifierMemory using the bot's context.
250+
251+
This is a shortcut for :class:`~.memories.SopelIdentifierMemory`\'s most
252+
common use case, which requires remembering to pass the ``bot``\'s own
253+
:meth:`make_identifier` method so the ``SopelIdentifierMemory`` will
254+
cast its keys to :class:`~.tools.identifiers.Identifier`\\s that are
255+
compatible with what the bot tracks internally and sends with
256+
:class:`~.trigger.Trigger`\\s when a plugin callable runs.
257+
258+
Calling this method is equivalent to the following::
259+
260+
from sopel.tools import memories
261+
262+
memories.SopelIdentifierMemory(
263+
identifier_factory=bot.make_identifier,
264+
)
265+
266+
.. versionadded:: 8.0
267+
268+
.. seealso::
269+
270+
The :mod:`.tools.memories` module describes how to use
271+
:class:`~.tools.memories.SopelIdentifierMemory` and its siblings.
272+
273+
"""
274+
return memories.SopelIdentifierMemory(
275+
identifier_factory=self.make_identifier,
276+
)
277+
245278
def safe_text_length(self, recipient: str) -> int:
246279
"""Estimate a safe text length for an IRC message.
247280

test/test_coretasks.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,36 @@ def test_handle_isupport_casemapping(mockbot):
340340
assert mockbot.nick.lower() == 'test[a]'
341341

342342

343+
def test_handle_isupport_casemapping_identifiermemory(mockbot):
344+
# create a nick that needs casemapping
345+
rfc1459 = 'Test[a]'
346+
347+
# create `SopelIdentifierMemory` w/bot's helper method and add the nick
348+
memory = mockbot.make_identifier_memory()
349+
memory[rfc1459] = rfc1459
350+
351+
# check default behavior (`rfc1459` casemapping)
352+
assert memory['test{a}'] == rfc1459
353+
assert memory['Test[a]'] == rfc1459
354+
355+
# now the bot "connects" to a server using `CASEMAPPING=ascii`
356+
mockbot.on_message(
357+
':irc.example.com 005 Sopel '
358+
'CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQScgimnprstz '
359+
'CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 '
360+
'NETWORK=example STATUSMSG=@+ CALLERID=g CASEMAPPING=ascii '
361+
':are supported by this server')
362+
363+
# CASEMAPPING token change doesn't affect previously existing Identifiers...
364+
assert memory['Test{a}'] == rfc1459
365+
# ...so we have to create a new nick that will casemap differently now
366+
ascii = 'Test[b]'
367+
memory[ascii] = ascii
368+
assert len(memory) == 2
369+
assert memory['test[b]'] == ascii
370+
assert 'test{b}' not in memory
371+
372+
343373
def test_handle_isupport_chantypes(mockbot):
344374
# check default behavior (chantypes allows #, &, +, and !)
345375
assert not mockbot.make_identifier('#channel').is_nick()

test/test_irc.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@ def bot(tmpconfig, botfactory):
2929
return botfactory(tmpconfig)
3030

3131

32+
def test_make_identifier(bot):
33+
nick = bot.make_identifier('Test[a]')
34+
assert 'test{a}' == nick
35+
36+
37+
def test_make_identifier_memory(bot):
38+
memory = bot.make_identifier_memory()
39+
memory['Test[a]'] = True
40+
assert memory['test{a}'] is True
41+
42+
memory['test{a}'] = False
43+
assert len(memory) == 1
44+
assert memory['Test[a]'] is False
45+
46+
3247
def prefix_length(bot):
3348
# ':', nick, '!', '~', ident/username, '@', maximum hostname length, <0x20>
3449
return 1 + len(bot.nick) + 1 + 1 + len(bot.user) + 1 + 63 + 1

0 commit comments

Comments
 (0)