aboutsummaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/channel_op.py28
-rw-r--r--modules/define.py5
-rw-r--r--modules/dice.py8
-rw-r--r--modules/eval_python.py2
-rw-r--r--modules/git_webhooks/github.py6
-rw-r--r--modules/healthcheck.py7
-rw-r--r--modules/hostmask_tracking.py40
-rw-r--r--modules/ircv3_typing.py7
-rw-r--r--modules/karma.py6
-rw-r--r--modules/quotes.py28
-rw-r--r--modules/relay.py2
-rw-r--r--modules/sed.py7
-rw-r--r--modules/user_time.py32
-rw-r--r--modules/words.py19
14 files changed, 150 insertions, 47 deletions
diff --git a/modules/channel_op.py b/modules/channel_op.py
index bc307778..98f170bb 100644
--- a/modules/channel_op.py
+++ b/modules/channel_op.py
@@ -51,13 +51,12 @@ class Module(ModuleManager.BaseModule):
event["args_split"][1:])
def _format_hostmask(self, user, s):
- mask_split = s.split("$$")
- for i, mask_part in enumerate(mask_split):
- mask_split[i] = (mask_part.replace("$n", user.nickname)
- .replace("$u", user.username)
- .replace("$h", user.hostname)
- .replace("$a", user.account or ""))
- return "$".join(mask_split)
+ vars = {}
+ vars["n"] = vars["nickname"] = user.nickname
+ vars["u"] = vars["username"] = user.username
+ vars["h"] = vars["hostname"] = user.hostname
+ vars["a"] = vars["account"] = user.account or ""
+ return utils.parse.format_token_replace(s, vars)
def _get_hostmask(self, channel, user):
if not user.account == None:
account_format = channel.get_setting("ban-format-account", None)
@@ -123,13 +122,24 @@ class Module(ModuleManager.BaseModule):
self._kick(event["server"], event["target"], args[0], args[1:])
@utils.hook("received.command.op")
- @utils.hook("received.command.deop")
+ @utils.hook("received.command.up", alias_of="op")
@utils.kwarg("channel_only", True)
@utils.kwarg("require_mode", "o")
@utils.kwarg("require_access", "op")
@utils.kwarg("usage", "[nickname]")
def op(self, event):
- add = event["command"] == "op"
+ self._op(True, event)
+
+ @utils.hook("received.command.deop")
+ @utils.hook("received.command.down", alias_of="deop")
+ @utils.kwarg("channel_only", True)
+ @utils.kwarg("require_mode", "o")
+ @utils.kwarg("require_access", "op")
+ @utils.kwarg("usage", "[nickname]")
+ def deop(self, event):
+ self._op(False, event)
+
+ def _op(self, add, event):
target = event["args_split"][0] if event["args"] else event[
"user"].nickname
event["target"].send_mode("+o" if add else "-o", [target])
diff --git a/modules/define.py b/modules/define.py
index 4e83c65c..6bc37774 100644
--- a/modules/define.py
+++ b/modules/define.py
@@ -36,6 +36,11 @@ class Module(ModuleManager.BaseModule):
word = event["args"]
else:
word = event["target"].buffer.get(from_self=False)
+ if word:
+ word = word.message
+
+ if not word:
+ raise utils.EventError("No phrase provided")
word = word.replace(" ", "+")
success, definition = self._get_definition(word)
diff --git a/modules/dice.py b/modules/dice.py
index cf4f53a2..8e34c9e0 100644
--- a/modules/dice.py
+++ b/modules/dice.py
@@ -4,7 +4,8 @@ import random, re
from src import ModuleManager, utils
ERROR_FORMAT = "Incorrect format! Format must be [number]d[number], e.g. 1d20"
-RE_DICE = re.compile("^([1-9]\d*)?d([1-9]\d*)((?:[-+][1-9]\d{,2})*)$", re.I)
+RE_DICE = re.compile("^([1-9]\d*)?d([1-9]\d*)((?:\s*[-+][1-9]\d{,2})*)\s*$",
+ re.I)
RE_MODIFIERS = re.compile("([-+]\d+)")
MAX_DICE = 6
@@ -18,7 +19,7 @@ class Module(ModuleManager.BaseModule):
def roll_dice(self, event):
args = None
if event["args_split"]:
- args = event["args_split"][0]
+ args = event["args"]
else:
args = "1d6"
@@ -27,7 +28,8 @@ class Module(ModuleManager.BaseModule):
roll = match.group(0)
dice_count = int(match.group(1) or "1")
side_count = int(match.group(2))
- modifiers = RE_MODIFIERS.findall(match.group(3))
+ modifiers_str = "".join(match.group(3).split())
+ modifiers = RE_MODIFIERS.findall(modifiers_str)
if dice_count > 6:
raise utils.EventError("Max number of dice is %s" % MAX_DICE)
diff --git a/modules/eval_python.py b/modules/eval_python.py
index 5454b7d3..eb2b5898 100644
--- a/modules/eval_python.py
+++ b/modules/eval_python.py
@@ -21,6 +21,6 @@ class Module(ModuleManager.BaseModule):
if page and page.data:
event["stdout"].write("%s: %s" % (event["user"].nickname,
- page.decode().rstrip("\n")))
+ page.decode("utf8").rstrip("\n")))
else:
event["stderr"].write("%s: failed to eval" % event["user"].nickname)
diff --git a/modules/git_webhooks/github.py b/modules/git_webhooks/github.py
index 20a3fb74..96dc26c3 100644
--- a/modules/git_webhooks/github.py
+++ b/modules/git_webhooks/github.py
@@ -39,12 +39,14 @@ EVENT_CATEGORIES = {
"pull_request_review_comment/deleted"
],
"issue-minimal": [
- "issues/opened", "issues/closed", "issues/reopened", "issues/deleted"
+ "issues/opened", "issues/closed", "issues/reopened", "issues/deleted",
+ "issues/transferred"
],
"issue": [
"issues/opened", "issues/closed", "issues/reopened", "issues/deleted",
"issues/edited", "issues/assigned", "issues/unassigned",
- "issues/locked", "issues/unlocked", "issue_comment"
+ "issues/locked", "issues/unlocked", "issues/transferred",
+ "issue_comment",
],
"issue-all": [
"issues", "issue_comment"
diff --git a/modules/healthcheck.py b/modules/healthcheck.py
index 0cf2c72b..3b5acfe2 100644
--- a/modules/healthcheck.py
+++ b/modules/healthcheck.py
@@ -9,4 +9,9 @@ class Module(ModuleManager.BaseModule):
@utils.hook("cron")
@utils.kwarg("schedule", "*/10")
def ten_minutes(self, event):
- utils.http.request(self.bot.config["healthcheck-url"])
+ url = self.bot.config["healthcheck-url"]
+ try:
+ utils.http.request(url)
+ except Exception as e:
+ self.log.error("Failed to cal healthcheck-url (%s)", [url],
+ exc_info=True)
diff --git a/modules/hostmask_tracking.py b/modules/hostmask_tracking.py
new file mode 100644
index 00000000..5cf82232
--- /dev/null
+++ b/modules/hostmask_tracking.py
@@ -0,0 +1,40 @@
+from src import ModuleManager, utils
+
+class Module(ModuleManager.BaseModule):
+ _name = "Hostmasks"
+
+ @utils.hook("new.user")
+ def new_user(self, event):
+ userhost = event["user"].userhost()
+ if not userhost == None:
+ known_hostmasks = event["user"].get_setting("known-hostmasks", [])
+ if not userhost in known_hostmasks:
+ known_hostmasks.append(userhost)
+ event["user"].set_setting("known-hostmasks", known_hostmasks)
+
+ @utils.hook("received.command.maskfind")
+ @utils.kwarg("min_args", 1)
+ @utils.kwarg("help", "Find all nicknames that used a given hostmask")
+ @utils.kwarg("usage", "<hostmask>")
+ @utils.kwarg("permission", "maskfind")
+ def maskfind(self, event):
+ all_userhosts = event["server"].get_all_user_settings("known-hostmasks")
+ nicknames = set([])
+ hostmask_str = event["args_split"][0]
+ hostmask = utils.irc.hostmask_parse(hostmask_str)
+
+ searched = 0
+ for nickname, userhosts in all_userhosts:
+ searched += len(userhosts)
+ for userhost in userhosts:
+ if hostmask.match(userhost):
+ nicknames.add((nickname, userhost))
+
+ if nicknames:
+ outs = []
+ for nickname, userhost in sorted(nicknames):
+ outs.append("%s (%s)" % (utils.irc.bold(nickname), userhost))
+ event["stdout"].write("%s (%d/%d): %s" %
+ (hostmask_str, len(nicknames), searched, ", ".join(outs)))
+ else:
+ event["stderr"].write("Hostmask not found")
diff --git a/modules/ircv3_typing.py b/modules/ircv3_typing.py
index ca2fce2b..3c8c9c6d 100644
--- a/modules/ircv3_typing.py
+++ b/modules/ircv3_typing.py
@@ -9,10 +9,13 @@ class Module(ModuleManager.BaseModule):
def _has_tags(self, server):
return server.has_capability(CAP)
+ def _expect_output(self, event):
+ kwarg = event["hook"].get_kwarg("expect_output", None)
+ return kwarg if not kwarg is None else event["expect_output"]
+
@utils.hook("preprocess.command")
def preprocess(self, event):
- if (self._has_tags(event["server"]) and
- event["hook"].get_kwarg("expect_output", True)):
+ if self._has_tags(event["server"]) and self._expect_output(event):
event["target"]._typing = True
event["server"].send(self._tagmsg(event["target_str"], "active"),
immediate=True)
diff --git a/modules/karma.py b/modules/karma.py
index f728e7e1..194bd424 100644
--- a/modules/karma.py
+++ b/modules/karma.py
@@ -7,7 +7,7 @@ from src import EventManager, ModuleManager, utils
KARMA_DELAY_SECONDS = 3
-REGEX_WORD = re.compile(r"^([^(\s,:]+)(?:[:,]\s*)?(\+\+|--)\s*$")
+REGEX_WORD = re.compile(r"^([^(\s,:]+)(?:[:,])?\s*(\+\+|--)\s*$")
REGEX_WORD_START = re.compile(r"^(\+\+|--)(?:\s*)([^(\s,:]+)\s*$")
REGEX_PARENS = re.compile(r"\(([^)]+)\)(\+\+|--)")
@@ -68,8 +68,8 @@ class Module(ModuleManager.BaseModule):
karma_total = self._karma_str(self._get_karma(server, target))
- return True, "%s has given %s %s karma (%s total)" % (
- sender.nickname, target, karma_str, karma_total)
+ return True, "%s now has %s karma (%s from %s)" % (
+ target, karma_total, karma_str, sender.nickname)
@utils.hook("command.regex", pattern=REGEX_WORD)
@utils.hook("command.regex", pattern=REGEX_PARENS)
diff --git a/modules/quotes.py b/modules/quotes.py
index 7a1c0939..f6dd223f 100644
--- a/modules/quotes.py
+++ b/modules/quotes.py
@@ -14,10 +14,10 @@ class Module(ModuleManager.BaseModule):
return category, None
return category, quote.strip()
- def _get_quotes(self, server, category):
- return server.get_setting("quotes-%s" % category, [])
- def _set_quotes(self, server, category, quotes):
- server.set_setting("quotes-%s" % category, quotes)
+ def _get_quotes(self, target, category):
+ return target.get_setting("quotes-%s" % category, [])
+ def _set_quotes(self, target, category, quotes):
+ target.set_setting("quotes-%s" % category, quotes)
@utils.hook("received.command.qadd", alias_of="quoteadd")
@utils.hook("received.command.quoteadd", min_args=1)
@@ -39,6 +39,9 @@ class Module(ModuleManager.BaseModule):
else:
event["stderr"].write("Please provide a category AND quote")
+ def _target_zip(self, target, quotes):
+ return [[u, t, q, target] for u, t, q in quotes]
+
@utils.hook("received.command.qdel", alias_of="quotedel")
@utils.hook("received.command.quotedel", min_args=1)
@utils.kwarg("help", "Delete a given quote from a given category")
@@ -48,27 +51,34 @@ class Module(ModuleManager.BaseModule):
category = category or event["args"].strip()
message = None
- setting = "quotes-%s" % category
- quotes = event["server"].get_setting(setting, [])
+ quotes = self._target_zip(event["server"],
+ self._get_quotes(event["server"], category))
+ if event["is_channel"]:
+ quotes += self._target_zip(event["target"],
+ self._get_quotes(event["target"], category))
+ quotes = sorted(quotes, key=lambda q: q[1])
if not quotes:
raise utils.EventError("Quote category '%s' not found" %
category)
+ found_target = None
if not remove_quote == None:
remove_quote_lower = remove_quote.lower()
- for nickname, time_added, quote in quotes[:]:
+ for nickname, time_added, quote, target in quotes[:]:
if quote.lower() == remove_quote_lower:
quotes.remove([nickname, time_added, quote])
+ found_target = target
message = "Removed quote from '%s'"
break
else:
if quotes:
- quotes.pop(-1)
+ quote = quotes.pop(-1)
+ found_target = quote[-1]
message = "Removed last '%s' quote"
if not message == None:
- event["server"].set_setting(setting, quotes)
+ self._set_quotes(found_target, category, quotes)
event["stdout"].write(message % category)
else:
event["stderr"].write("Quote not found")
diff --git a/modules/relay.py b/modules/relay.py
index 9e266043..4719ee85 100644
--- a/modules/relay.py
+++ b/modules/relay.py
@@ -81,8 +81,6 @@ class Module(ModuleManager.BaseModule):
@utils.kwarg("min_args", 1)
@utils.kwarg("help", "Edit configured relay groups")
@utils.kwarg("usage", "list")
- @utils.kwarg("usage", "add <name>")
- @utils.kwarg("usage", "remove <name>")
@utils.kwarg("usage", "join <name>")
@utils.kwarg("usage", "leave <name>")
@utils.kwarg("permission", "relay")
diff --git a/modules/sed.py b/modules/sed.py
index f3fe4864..af597f81 100644
--- a/modules/sed.py
+++ b/modules/sed.py
@@ -73,7 +73,12 @@ class Module(ModuleManager.BaseModule):
if match:
replace = sed_split[2]
replace = replace.replace("\\/", "/")
- replace = re.sub(SED_AMPERSAND, "\\1%s" % match, replace)
+
+ with utils.deadline():
+ for found in SED_AMPERSAND.finditer(replace):
+ found = found.group(1)
+ replace.replace(found, "%s%s" % (found, match))
+
replace_color = utils.irc.bold(replace)
new_message = re.sub(pattern, replace, message, count)
diff --git a/modules/user_time.py b/modules/user_time.py
index 19ed4b22..48077d81 100644
--- a/modules/user_time.py
+++ b/modules/user_time.py
@@ -15,6 +15,9 @@ class LocationType(enum.Enum):
class Module(ModuleManager.BaseModule):
_name = "Time"
+ def on_load(self):
+ self.exports.add("time-localise", self.time_localise)
+
def _find_setting(self, event):
query = None
target_user = None
@@ -41,6 +44,15 @@ class Module(ModuleManager.BaseModule):
else:
return LocationType.NAME, event["args"], None
+ def _timezoned(self, dt, timezone):
+ dt = dt.astimezone(pytz.timezone(timezone))
+ utc_offset = (dt.utcoffset().total_seconds()/60)/60
+ tz = "UTC"
+ if not utc_offset == 0.0:
+ if utc_offset > 0:
+ tz += "+"
+ tz += "%g" % utc_offset
+ return "%s %s" % (utils.datetime.datetime_human(dt), tz)
@utils.hook("received.command.time")
@utils.kwarg("help", "Get the time for you or someone else")
@@ -51,20 +63,13 @@ class Module(ModuleManager.BaseModule):
type, name, timezone = self._find_setting(event)
if not timezone == None:
- dt = datetime.datetime.now(tz=pytz.timezone(timezone))
- utc_offset = (dt.utcoffset().total_seconds()/60)/60
- tz = "UTC"
- if not utc_offset == 0.0:
- if utc_offset > 0:
- tz += "+"
- tz += "%g" % utc_offset
- human = utils.datetime.datetime_human(dt)
+ human = self._timezoned(datetime.datetime.now(), timezone)
out = None
if type == LocationType.USER:
- out = "Time for %s: %s %s" % (name, human, tz)
+ out = "Time for %s: %s" % (name, human)
else:
- out = "It is %s in %s %s" % (human, name, tz)
+ out = "It is %s in %s" % (human, name)
event["stdout"].write(out)
else:
out = None
@@ -74,3 +79,10 @@ class Module(ModuleManager.BaseModule):
out = NOLOCATION_NAME
event["stderr"].write(out % name)
+
+ def time_localise(self, user, dt):
+ location = user.get_setting("location", None)
+ timezone = "UTC"
+ if not location == None:
+ timezone = location["timezone"]
+ return self._timezoned(dt, timezone)
diff --git a/modules/words.py b/modules/words.py
index c76fb3dc..716b7437 100644
--- a/modules/words.py
+++ b/modules/words.py
@@ -129,6 +129,12 @@ class Module(ModuleManager.BaseModule):
else:
event["stderr"].write("That word is not being tracked")
+ def _get_nickname(self, server, target, nickname):
+ nickname = server.get_user(nickname).nickname
+ if target.get_setting("wordiest-prevent-highlight", True):
+ nickname = utils.prevent_highlight(nickname)
+ return nickname
+
@utils.hook("received.command.wordiest")
def wordiest(self, event):
"""
@@ -137,8 +143,13 @@ class Module(ModuleManager.BaseModule):
"""
channel_query = None
word_prefix = ""
- if event["args_split"]:
- channel_query = event["args_split"][0].lower()
+ if event["args"]:
+ if not event["args_split"][0] == "*":
+ channel_query = event["args_split"][0].lower()
+ elif event["is_channel"]:
+ channel_query = event["target"].name
+
+ if channel_query:
word_prefix = " (%s)" % channel_query
words = event["server"].find_all_user_channel_settings("words")
@@ -150,7 +161,7 @@ class Module(ModuleManager.BaseModule):
user_words[nickname] += word_count
top_10 = utils.top_10(user_words,
- convert_key=lambda nickname:
- event["server"].get_user(nickname).nickname)
+ convert_key=lambda nickname: self._get_nickname(
+ event["server"], event["target"], nickname))
event["stdout"].write("wordiest%s: %s" % (
word_prefix, ", ".join(top_10)))