diff --git a/ircrobots/glob.py b/ircrobots/glob.py index f5eebdf..70070d9 100644 --- a/ircrobots/glob.py +++ b/ircrobots/glob.py @@ -18,3 +18,37 @@ def _collapse(pattern: str) -> str: i += 1 return out +def _match(pattern: str, s: str): + i, j = 0, 0 + + i_backup = -1 + j_backup = -1 + while j < len(s): + p = (pattern[i:] or [None])[0] + + if p == "*": + i += 1 + i_backup = i + j_backup = j + + elif p in ["?", s[j]]: + i += 1 + j += 1 + + else: + if i_backup == -1: + return False + else: + j_backup += 1 + j = j_backup + i = i_backup + + return i == len(pattern) + +class Glob(object): + def __init__(self, pattern: str): + self._pattern = pattern + def match(self, s: str) -> bool: + return _match(self._pattern, s) +def compile(pattern: str): + return Glob(_collapse(pattern)) diff --git a/ircrobots/matching/params.py b/ircrobots/matching/params.py index 9826916..8326c18 100644 --- a/ircrobots/matching/params.py +++ b/ircrobots/matching/params.py @@ -1,7 +1,8 @@ -from re import compile +from re import compile as re_compile from typing import Optional, Pattern from irctokens import Hostmask from ..interface import IMatchResponseParam, IMatchResponseHostmask, IServer +from ..glob import Glob, compile as glob_compile from .. import formatting class Any(IMatchResponseParam): @@ -52,7 +53,7 @@ class Regex(IMatchResponseParam): self._pattern: Optional[Pattern] = None def match(self, server: IServer, arg: str) -> bool: if self._pattern is None: - self._pattern = compile(self._value) + self._pattern = re_compile(self._value) return bool(self._pattern.search(arg)) class Nickname(IMatchResponseHostmask): @@ -67,3 +68,12 @@ class Nickname(IMatchResponseHostmask): if self._folded is None: self._folded = server.casefold(self._nickname) return self._folded == server.casefold(hostmask.nickname) + +class Mask(IMatchResponseHostmask): + def __init__(self, mask: str): + self._mask = mask + self._compiled = Optional[Glob] + def match(self, server: IServer, hostmask: Hostmask): + if self._compiled is None: + self._compiled = glob_compile(self._mask) + return self._compiled.match(str(hostmask))