diff --git a/modules/accept_invite.py b/modules/accept_invite.py index 61afb4e6..877a95f6 100644 --- a/modules/accept_invite.py +++ b/modules/accept_invite.py @@ -2,9 +2,8 @@ from src import ModuleManager, utils -@utils.export("serverset", {"setting": "accept-invites", - "help": "Set whether I accept invites on this server", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("serverset", utils.BoolSetting("accept-invites", + "Set whether I accept invites on this server")) class Module(ModuleManager.BaseModule): @utils.hook("received.invite") def on_invite(self, event): diff --git a/modules/auto_mode.py b/modules/auto_mode.py index db503133..93ea303c 100644 --- a/modules/auto_mode.py +++ b/modules/auto_mode.py @@ -5,9 +5,8 @@ from src import ModuleManager, utils -@utils.export("channelset", {"setting": "automode", - "help": "Disable/Enable automode", "validate": utils.bool_or_none, - "example": "on"}) +@utils.export("channelset", utils.BoolSetting( + "automode", "Disable/Enable automode")) class Module(ModuleManager.BaseModule): _name = "AutoMode" diff --git a/modules/birthday.py b/modules/birthday.py index 46a1dde6..563c7a73 100644 --- a/modules/birthday.py +++ b/modules/birthday.py @@ -1,7 +1,7 @@ #--depends-on commands #--depends-on config -import datetime +import datetime, typing from src import ModuleManager, utils DATE_YEAR_FORMAT = "%Y-%m-%d" @@ -30,20 +30,22 @@ def _format(years, dt): return _format_year(dt) else: return _format_noyear(dt) -def _check(s): - parsed = _parse(s) - if parsed: - years, parsed = parsed - return _format(years, parsed) - return None + +class BirthdaySetting(utils.Setting): + def parse(self, value: str) -> typing.Any: + parsed = _parse(value) + if parsed: + years, parsed = parsed + return _format(years, parsed) + return None def _apostrophe(nickname): if nickname[-1].lower() == "s": return "%s'" % nickname return "%s's" % nickname -@utils.export("set", {"setting": "birthday", "help": "Set your birthday", - "validate": _check, "example": "1995-09-15"}) +@utils.export("set", BirthdaySetting("birthday", "Set your birthday", + example="1995-09-15")) class Module(ModuleManager.BaseModule): @utils.hook("received.command.birthday") def birthday(self, event): diff --git a/modules/bot_channel.py b/modules/bot_channel.py index d7c7e8b2..32f3f1fc 100644 --- a/modules/bot_channel.py +++ b/modules/bot_channel.py @@ -2,8 +2,8 @@ from src import ModuleManager, utils -@utils.export("serverset", {"setting": "bot-channel", - "help": "Set main channel", "example": "#bitbot"}) +@utils.export("serverset", utils.Setting("bot-channel", + "Set main channel", example="#bitbot")) class Module(ModuleManager.BaseModule): @utils.hook("received.001") def do_join(self, event): diff --git a/modules/channel_log/__init__.py b/modules/channel_log/__init__.py index 583957a0..48fb93f4 100644 --- a/modules/channel_log/__init__.py +++ b/modules/channel_log/__init__.py @@ -7,9 +7,8 @@ from src import ModuleManager, utils ROOT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) LOGS_DIRECTORY = os.path.join(ROOT_DIRECTORY, "logs") -@utils.export("channelset", {"setting": "log", - "help": "Enable/disable channel logging", "validate": utils.bool_or_none, - "example": "on"}) +@utils.export("channelset", utils.BoolSetting("log", + "Enable/disable channel logging")) class Module(ModuleManager.BaseModule): def _log_file(self, server_name, channel_name): return open(os.path.join(LOGS_DIRECTORY, diff --git a/modules/channel_op.py b/modules/channel_op.py index 02688626..11d5a0d5 100644 --- a/modules/channel_op.py +++ b/modules/channel_op.py @@ -10,21 +10,17 @@ class UserNotFoundException(Exception): class InvalidTimeoutException(Exception): pass -@utils.export("channelset", {"setting": "highlight-spam-threshold", - "help": "Set the number of nicknames in a message that qualifies as spam", - "validate": utils.int_or_none, "example": "10"}) -@utils.export("channelset", {"setting": "highlight-spam-protection", - "help": "Enable/Disable highlight spam protection", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("channelset", {"setting": "highlight-spam-ban", - "help": "Enable/Disable banning highlight spammers " - "instead of just kicking", "validate": utils.bool_or_none, - "example": "on"}) -@utils.export("channelset", {"setting": "ban-format", - "help": "Set ban format ($n = nick, $u = username, $h = hostname)", - "example": "*!$u@$h"}) -@utils.export("serverset", {"setting": "mute-method", - "help": "Set this server's method of muting users", "example": "qmode"}) +@utils.export("channelset", utils.IntSetting("highlight-spam-threshold", + "Set the number of nicknames in a message that qualifies as spam")) +@utils.export("channelset", utils.BoolSetting("highlight-spam-protection", + "Enable/Disable highlight spam protection")) +@utils.export("channelset", utils.BoolSetting("highlight-spam-ban", + "Enable/Disable banning highlight spammers instead of just kicking")) +@utils.export("channelset", utils.Setting("ban-format", + "Set ban format ($n = nick, $u = username, $h = hostname)", + example="*!$u@$h")) +@utils.export("serverset", utils.Setting("mute-method", + "Set this server's method of muting users", example="qmode")) class Module(ModuleManager.BaseModule): _name = "ChanOp" diff --git a/modules/check_mode.py b/modules/check_mode.py index 1ad96e10..67d48172 100644 --- a/modules/check_mode.py +++ b/modules/check_mode.py @@ -6,12 +6,11 @@ LOWHIGH = { "low": "v", "high": "o" } -@utils.export("channelset", {"setting": "mode-low", - "help": "Set which channel mode is considered to be 'low' access", - "example": "v"}) -@utils.export("channelset", {"setting": "mode-high", - "help": "Set which channel mode is considered to be 'high' access", - "example": "o"}) + +@utils.export("channelset", utils.Setting("mode-low", + "Set which channel mode is considered to be 'low' access", example="v")) +@utils.export("channelset", utils.Setting("mode-high", + "Set which channel mode is considered to be 'high' access", example="o")) class Module(ModuleManager.BaseModule): def _check_command(self, event, channel, require_mode): if event["is_channel"] and require_mode: diff --git a/modules/check_urls.py b/modules/check_urls.py index 1ea83ebc..c8e81642 100644 --- a/modules/check_urls.py +++ b/modules/check_urls.py @@ -8,16 +8,12 @@ from src import ModuleManager, utils URL_VIRUSTOTAL = "https://www.virustotal.com/vtapi/v2/url/report" RE_URL = re.compile(r"https?://\S+", re.I) -@utils.export("channelset", {"setting": "check-urls", - "help": "Enable/Disable automatically checking for malicious URLs", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("serverset", {"setting": "check-urls", - "help": "Enable/Disable automatically checking for malicious URLs", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("channelset", {"setting": "check-urls-kick", - "help": "Enable/Disable automatically kicking users that " - "send malicious URLs", "validate": utils.bool_or_none, - "example": "on"}) +@utils.export("channelset", utils.BoolSetting("check-urls", + "Enable/Disable automatically checking for malicious URLs")) +@utils.export("serverset", utils.BoolSetting("check-urls", + "Enable/Disable automatically checking for malicious URLs")) +@utils.export("channelset", utils.BoolSetting("check-urls-kick", + "Enable/Disable automatically kicking users that send malicious URLs")) class Module(ModuleManager.BaseModule): @utils.hook("received.message.channel") def message(self, event): diff --git a/modules/commands/__init__.py b/modules/commands/__init__.py index 46f79ab3..fdb21075 100644 --- a/modules/commands/__init__.py +++ b/modules/commands/__init__.py @@ -1,7 +1,7 @@ #--depends-on config #--depends-on permissions -import re, string, typing +import re, string, traceback, typing from src import EventManager, ModuleManager, utils from . import outs @@ -20,25 +20,28 @@ def _command_method_validate(s): if s.upper() in COMMAND_METHODS: return s.upper() -@utils.export("channelset", {"setting": "command-prefix", - "help": "Set the command prefix used in this channel", "example": "!"}) -@utils.export("serverset", {"setting": "command-prefix", - "help": "Set the command prefix used on this server", "example": "!"}) -@utils.export("serverset", {"setting": "command-method", - "help": "Set the method used to respond to commands", - "validate": _command_method_validate, "example": "NOTICE"}) -@utils.export("channelset", {"setting": "command-method", - "help": "Set the method used to respond to commands", - "validate": _command_method_validate, "example": "NOTICE"}) -@utils.export("channelset", {"setting": "hide-prefix", - "help": "Disable/enable hiding prefix in command reponses", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("channelset", {"setting": "commands", - "help": "Disable/enable responding to commands in-channel", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("channelset", {"setting": "prefixed-commands", - "help": "Disable/enable responding to prefixed commands in-channel", - "validate": utils.bool_or_none, "example": "on"}) +class CommandMethodSetting(utils.Setting): + example = "NOTICE" + def parse(self, value: str) -> typing.Any: + upper = value.upper() + if upper in COMMAND_METHODS: + return upper + return None + +@utils.export("channelset", utils.Setting("command-prefix", + "Set the command prefix used in this channel", example="!")) +@utils.export("serverset", utils.Setting("command-prefix", + "Set the command prefix used on this server", example="!")) +@utils.export("serverset", CommandMethodSetting("command-method", + "Set the method used to respond to commands")) +@utils.export("channelset", CommandMethodSetting("command-method", + "Set the method used to respond to commands")) +@utils.export("channelset", utils.BoolSetting("hide-prefix", + "Disable/enable hiding prefix in command reponses")) +@utils.export("channelset", utils.BoolSetting("commands", + "Disable/enable responding to commands in-channel")) +@utils.export("channelset", utils.BoolSetting("prefixed-commands", + "Disable/enable responding to prefixed commands in-channel")) class Module(ModuleManager.BaseModule): @utils.hook("new.user") @utils.hook("new.channel") diff --git a/modules/config.py b/modules/config.py index 6ef50c05..8c070e0d 100644 --- a/modules/config.py +++ b/modules/config.py @@ -84,19 +84,18 @@ class Module(ModuleManager.BaseModule): if setting_info: value = target.get_setting(require_setting, None) if value == None: - example = setting_info.get("example", "") + example = setting_info.exaple or "" return "Please set %s, e.g.: %sconfig %s %s %s" % ( require_setting, event["command_prefix"], context[0], require_setting, example) def _get_export_setting(self, context): settings = self.exports.get_all(context) - return {setting["setting"].lower(): setting for setting in settings} + return {setting.name.lower(): setting for setting in settings} def _config(self, export_settings, target, setting, value=None): if not value == None: - validation = export_settings[setting].get("validate", lambda x: x) - validated_value = validation(value) + validated_value = export_settings[setting].parse(value) if not validated_value == None: target.set_setting(setting, validated_value) return ConfigResult(ConfigResults.Changed, validated_value) @@ -181,7 +180,7 @@ class Module(ModuleManager.BaseModule): try: result = self._config(export_settings, target, setting, value) except ConfigInvalidValue: - example = export_settings[setting].get("example", None) + example = export_settings[setting].example if not example == None: raise utils.EventError("Invalid value. Example: %s" % example) diff --git a/modules/ctcp.py b/modules/ctcp.py index 58f8d7e5..678cf833 100644 --- a/modules/ctcp.py +++ b/modules/ctcp.py @@ -4,9 +4,8 @@ import datetime from src import IRCBot, ModuleManager, utils -@utils.export("serverset", {"setting": "ctcp-responses", - "help": "Set whether I respond to CTCPs on this server", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("serverset", utils.BoolSetting("ctcp-responses", + "Set whether I respond to CTCPs on this server")) class Module(ModuleManager.BaseModule): @utils.hook("received.ctcp.request.version") def ctcp_version(self, event): diff --git a/modules/ducks.py b/modules/ducks.py index 037c602b..afef16d2 100644 --- a/modules/ducks.py +++ b/modules/ducks.py @@ -9,15 +9,12 @@ NO_DUCK = "There was no duck!" DEFAULT_MIN_MESSAGES = 100 -@utils.export("channelset", {"setting": "ducks-enabled", - "help": "Whether or not to spawn ducks", "validate": utils.bool_or_none, - "example": "on"}) -@utils.export("channelset", {"setting": "ducks-min-messages", - "help": "Minimum messages between ducks spawning", - "validate": utils.int_or_none, "example": "50"}) -@utils.export("channelset", {"setting": "ducks-kick", - "help": "Whether or not to kick someone talking to non-existent ducks", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("channelset", utils.BoolSetting("ducks-enabled", + "Whether or not to spawn ducks")) +@utils.export("channelset", utils.IntSetting("ducks-min-messages", + "Minimum messages between ducks spawning", example="50")) +@utils.export("channelset", utils.BoolSetting("ducks-kick", + "Whether or not to kick someone talking to non-existent ducks")) class Module(ModuleManager.BaseModule): @utils.hook("new.channel") def new_channel(self, event): diff --git a/modules/git_webhooks/__init__.py b/modules/git_webhooks/__init__.py index b76ec4a3..a86eda68 100644 --- a/modules/git_webhooks/__init__.py +++ b/modules/git_webhooks/__init__.py @@ -13,15 +13,12 @@ DEFAULT_EVENT_CATEGORIES = [ "ping", "code", "pr", "issue", "repo" ] -@utils.export("channelset", {"setting": "git-prevent-highlight", - "help": "Enable/disable preventing highlights", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("channelset", {"setting": "git-hide-organisation", - "help": "Hide/show organisation in repository names", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("channelset", {"setting": "git-hide-prefix", - "help": "Hide/show command-like prefix on git webhook outputs", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("channelset", utils.BoolSetting("git-prevent-highlight", + "Enable/disable preventing highlights")) +@utils.export("channelset", utils.BoolSetting("git-hide-organisation", + "Hide/show organisation in repository names")) +@utils.export("channelset", utils.BoolSetting("git-hide-prefix", + "Hide/show command-like prefix on git webhook outputs")) class Module(ModuleManager.BaseModule): _name = "Webhooks" diff --git a/modules/github.py b/modules/github.py index 102f3ab2..633f1a0a 100644 --- a/modules/github.py +++ b/modules/github.py @@ -22,15 +22,13 @@ REGEX_REF = re.compile(r"(?:\S+(?:\/\S+)?)?#\d+") API_ISSUE_URL = "https://api.github.com/repos/%s/%s/issues/%s" API_PULL_URL = "https://api.github.com/repos/%s/%s/pulls/%s" -@utils.export("channelset", {"setting": "github-default-repo", - "help": "Set the default github repo for the current channel", - "example": "jesopo/bitbot"}) -@utils.export("channelset", {"setting": "auto-github", - "help": "Enable/disable automatically getting github issue/PR info", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("channelset", {"setting": "auto-github-cooldown", - "help": "Set amount of seconds between auto-github duplicates", - "validate": utils.int_or_none, "example": "300"}) +@utils.export("channelset", utils.Setting("github-default-repo", + "Set the default github repo for the current channel", + example="jesopo/bitbot")) +@utils.export("channelset", utils.Setting("auto-github", + "Enable/disable automatically getting github issue/PR info")) +@utils.export("channelset", utils.IntSetting("auto-github-cooldown", + "Set amount of seconds between auto-github duplicates", example="300")) class Module(ModuleManager.BaseModule): def _parse_ref(self, channel, ref): repo, _, number = ref.rpartition("#") diff --git a/modules/google.py b/modules/google.py index f365ee90..123c7a58 100644 --- a/modules/google.py +++ b/modules/google.py @@ -9,9 +9,8 @@ from src import ModuleManager, utils URL_GOOGLESEARCH = "https://www.googleapis.com/customsearch/v1" URL_GOOGLESUGGEST = "http://google.com/complete/search" -@utils.export("channelset", {"setting": "google-safesearch", - "help": "Turn safe search off/on", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("channelset", utils.BoolSetting("google-safesearch", + "Turn safe search off/on")) class Module(ModuleManager.BaseModule): @utils.hook("received.command.g", alias_of="google") @utils.hook("received.command.google") diff --git a/modules/greeting.py b/modules/greeting.py index a1005436..9f5b9b78 100644 --- a/modules/greeting.py +++ b/modules/greeting.py @@ -2,9 +2,9 @@ from src import ModuleManager, utils -@utils.export("channelset", {"setting": "greeting", - "help": "Set a greeting to send to users when they join", - "example": "welcome to the channel!"}) +@utils.export("channelset", utils.Setting("greeting", + "Set a greeting to send to users when they join", + example="welcome to the channel!")) class Module(ModuleManager.BaseModule): @utils.hook("received.join") def join(self, event): diff --git a/modules/imgur.py b/modules/imgur.py index fd68ed00..9c9e8c57 100644 --- a/modules/imgur.py +++ b/modules/imgur.py @@ -16,9 +16,8 @@ URL_GALLERY = "https://api.imgur.com/3/gallery/%s" NSFW_TEXT = "(NSFW)" -@utils.export("channelset", {"setting": "auto-imgur", - "help": "Disable/Enable automatically getting info from Imgur URLs", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("channelset", utils.BoolSetting("auto-imgur", + "Disable/Enable automatically getting info from Imgur URLs")) class Module(ModuleManager.BaseModule): def _prefix(self, data): text = "%s: " % data["id"] diff --git a/modules/ip_addresses.py b/modules/ip_addresses.py index e13f59ff..62208504 100644 --- a/modules/ip_addresses.py +++ b/modules/ip_addresses.py @@ -1,6 +1,6 @@ #--depends-on commands -import re, socket +import re, socket, typing from src import ModuleManager, utils import dns.resolver @@ -9,13 +9,14 @@ REGEX_IPv6 = r"(?:(?:[a-f0-9]{1,4}:){2,}|[a-f0-9:]*::)[a-f0-9:]*" REGEX_IPv4 = r"(?:\d{1,3}\.){3}\d{1,3}" REGEX_IP = re.compile("(%s)|(%s)" % (REGEX_IPv4, REGEX_IPv6), re.I) -def _dns_validate(s): - if utils.is_ip(s): - return s - return None +class DnsSetting(utils.Setting): + def parse(self, value: str) -> typing.Any: + if utils.is_ip(value): + return value + return None -@utils.export("serverset", {"setting": "dns-nameserver", - "help": "Set DNS nameserver", "example": "8.8.8.8"}) +@utils.export("serverset", DnsSetting("dns-nameserver", + "Set DNS nameserver", example="8.8.8.8")) class Module(ModuleManager.BaseModule): @utils.hook("received.command.dns", min_args=1) def dns(self, event): diff --git a/modules/ircv3.py b/modules/ircv3.py index 8b3c388f..0628e7e7 100644 --- a/modules/ircv3.py +++ b/modules/ircv3.py @@ -11,7 +11,7 @@ class Module(ModuleManager.BaseModule): supporting_servers = [] for server in self.bot.servers.values(): - if not server.connection_params.hostname == "localhost": + if server.get_setting("ircv3-stats", False): if spec in server.server_capabilities: port = str(server.connection_params.port) if server.connection_params.tls: diff --git a/modules/ircv3_sasl/__init__.py b/modules/ircv3_sasl/__init__.py index c86796b0..b93c9b07 100644 --- a/modules/ircv3_sasl/__init__.py +++ b/modules/ircv3_sasl/__init__.py @@ -1,6 +1,6 @@ #--depends-on config -import base64, hashlib, hmac, uuid +import base64, hashlib, hmac, typing, uuid from src import ModuleManager, utils from . import scram @@ -13,16 +13,16 @@ USERPASS_MECHANISMS = [ "PLAIN" ] -def _validate(s): - mechanism, _, arguments = s.partition(" ") - return {"mechanism": mechanism, "args": arguments} +class SaslSetting(utils.Setting): + def parse(self, value: str) -> typing.Any: + mechanism, _, arguments = value.partition(" ") + return {"mechanism": mechanism, "args": arguments} -@utils.export("serverset", {"setting": "sasl", - "help": "Set the sasl username/password for this server", - "validate": _validate, "example": "PLAIN BitBot:hunter2"}) -@utils.export("serverset", {"setting": "sasl-hard-fail", - "help": "Set whether a SASL failure should cause a disconnect", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("serverset", SaslSetting("sasl", + "Set the sasl username/password for this server", + example="PLAIN BitBot:hunter2")) +@utils.export("serverset", utils.BoolSetting("sasl-hard-fail", + "Set whether a SASL failure should cause a disconnect")) class Module(ModuleManager.BaseModule): def _best_userpass_mechanism(self, mechanisms): for potential_mechanism in USERPASS_MECHANISMS: diff --git a/modules/karma.py b/modules/karma.py index 7650e430..9f23244c 100644 --- a/modules/karma.py +++ b/modules/karma.py @@ -10,12 +10,10 @@ KARMA_DELAY_SECONDS = 3 REGEX_KARMA = re.compile(r"^(.*)(\+{2}|\-{2})$") -@utils.export("channelset", {"setting": "karma-verbose", - "help": "Enable/disable automatically responding to karma changes", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("serverset", {"setting": "karma-nickname-only", - "help": "Enable/disable karma being for nicknames only", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("channelset", utils.BoolSetting("karma-verbose", + "Enable/disable automatically responding to karma changes")) +@utils.export("serverset", utils.BoolSetting("karma-nickname-only", + "Enable/disable karma being for nicknames only")) class Module(ModuleManager.BaseModule): def _karma_str(self, karma): karma_str = str(karma) diff --git a/modules/lastfm.py b/modules/lastfm.py index 2e08bdab..8078dc54 100644 --- a/modules/lastfm.py +++ b/modules/lastfm.py @@ -7,8 +7,8 @@ from src import ModuleManager, utils URL_SCROBBLER = "http://ws.audioscrobbler.com/2.0/" -@utils.export("set", {"setting": "lastfm", "help": "Set last.fm username", - "example": "jesopo"}) +@utils.export("set", utils.Setting("lastfm", "Set last.fm username", + example="jesopo")) class Module(ModuleManager.BaseModule): _name = "last.fm" diff --git a/modules/location.py b/modules/location.py index 3f1eef65..84dcd691 100644 --- a/modules/location.py +++ b/modules/location.py @@ -1,14 +1,21 @@ #--depends-on config +import typing from src import ModuleManager, utils URL_OPENCAGE = "https://api.opencagedata.com/geocode/v1/json" +class LocationSetting(utils.Setting): + _func = None + def parse(self, value: str) -> typing.Any: + return self._func(value) + class Module(ModuleManager.BaseModule): def on_load(self): - self.exports.add("set", {"setting": "location", - "help": "Set your location", "validate": self._get_location, - "example": "London, GB"}) + setting = LocationSetting("location", "Set your location", + example="London, GB") + setting._func = self._get_location + self.exports.add("set", setting) def _get_location(self, s): page = utils.http.request(URL_OPENCAGE, get_params={ diff --git a/modules/nickserv.py b/modules/nickserv.py index ad8330d5..13f449a8 100644 --- a/modules/nickserv.py +++ b/modules/nickserv.py @@ -3,9 +3,8 @@ import base64 from src import EventManager, ModuleManager, utils -@utils.export("serverset", {"setting": "nickserv-password", - "help": "Set the nickserv password for this server", - "example": "hunter2"}) +@utils.export("serverset", utils.Setting("nickserv-password", + "Set the nickserv password for this server", example="hunter2")) class Module(ModuleManager.BaseModule): @utils.hook("received.001", priority=EventManager.PRIORITY_URGENT) def on_connect(self, event): diff --git a/modules/permissions/__init__.py b/modules/permissions/__init__.py index 6003c256..f27033bf 100644 --- a/modules/permissions/__init__.py +++ b/modules/permissions/__init__.py @@ -9,9 +9,8 @@ REQUIRES_IDENTIFY = "You need to be identified to use that command" REQUIRES_IDENTIFY_INTERNAL = ("You need to be identified to use that command " "(/msg %s register | /msg %s identify)") -@utils.export("serverset", {"setting": "identity-mechanism", - "help": "Set the identity mechanism for this server", - "example": "ircv3-account"}) +@utils.export("serverset", utils.Setting("identity-mechanism", + "Set the identity mechanism for this server", example="ircv3-account")) class Module(ModuleManager.BaseModule): @utils.hook("new.user") def new_user(self, event): diff --git a/modules/print_activity.py b/modules/print_activity.py index 45b57e2a..f7e30feb 100644 --- a/modules/print_activity.py +++ b/modules/print_activity.py @@ -4,9 +4,8 @@ import datetime from src import EventManager, ModuleManager, utils -@utils.export("botset", {"setting": "print-motd", - "help": "Set whether I print /motd", "validate": utils.bool_or_none, - "example": "on"}) +@utils.export("botset", + utils.BoolSetting("print-motd", "Set whether I print /motd")) class Module(ModuleManager.BaseModule): def _print(self, event): self.bot.log.info("%s%s | %s", [ diff --git a/modules/pronouns.py b/modules/pronouns.py index e0d9b2c1..f800dad5 100644 --- a/modules/pronouns.py +++ b/modules/pronouns.py @@ -3,8 +3,8 @@ from src import ModuleManager, utils -@utils.export("set", {"setting": "pronouns", "help": "Set your pronouns", - "example": "she/her"}) +@utils.export("set", utils.Setting("pronouns", "Set your pronouns", + example="she/her")) class Module(ModuleManager.BaseModule): @utils.hook("received.command.pronouns") def pronouns(self, event): diff --git a/modules/rest_api.py b/modules/rest_api.py index a436bff3..19deff2d 100644 --- a/modules/rest_api.py +++ b/modules/rest_api.py @@ -110,12 +110,10 @@ class Handler(http.server.BaseHTTPRequestHandler): def log_message(self, format, *args): return -@utils.export("botset", {"setting": "rest-api", - "help": "Enable/disable REST API", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("botset", {"setting": "rest-api-minify", - "help": "Enable/disable REST API minifying", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("botset", + utils.BoolSetting("rest-api", "Enable/disable REST API")) +@utils.export("botset", + utils.BoolSetting("rest-api", "Enable/disable REST API minifying")) class Module(ModuleManager.BaseModule): def on_load(self): global _bot diff --git a/modules/sed.py b/modules/sed.py index 20e85b68..0716d04d 100644 --- a/modules/sed.py +++ b/modules/sed.py @@ -7,12 +7,10 @@ from src import ModuleManager, utils REGEX_SPLIT = re.compile("(?;:.,!?" -@utils.export("set", {"setting": "word-tracking", - "help": "Disable/enable tracking your wordcounts", - "validate": utils.bool_or_none, "example": "on"}) +@utils.export("set", utils.BoolSetting( + "word-tracking", "Disable/enable tracking your wordcounts")) class Module(ModuleManager.BaseModule): def _channel_message(self, user, event): if not user.get_setting("word-tracking", True): diff --git a/modules/youtube.py b/modules/youtube.py index f31b15ec..53460a49 100644 --- a/modules/youtube.py +++ b/modules/youtube.py @@ -18,12 +18,10 @@ URL_YOUTUBESHORT = "https://youtu.be/%s" ARROW_UP = "↑" ARROW_DOWN = "↓" -@utils.export("channelset", {"setting": "auto-youtube", - "help": "Disable/Enable automatically getting info from youtube URLs", - "validate": utils.bool_or_none, "example": "on"}) -@utils.export("channelset", {"setting": "youtube-safesearch", - "help": "Turn safe search off/on", "validate": utils.bool_or_none, - "example": "on"}) +@utils.export("channelset", utils.BoolSetting("auto-youtube", + "Disable/Enable automatically getting info from youtube URLs")) +@utils.export("channelset", utils.BoolSetting("youtube-safesearch", + "Turn safe search off/on")) class Module(ModuleManager.BaseModule): def on_load(self): self.exports.add("search-youtube", self._search_youtube) diff --git a/src/utils/__init__.py b/src/utils/__init__.py index 7e1fce18..5ad057c8 100644 --- a/src/utils/__init__.py +++ b/src/utils/__init__.py @@ -138,21 +138,6 @@ def parse_number(s: str) -> str: raise ValueError("Unknown unit '%s' given to parse_number" % unit) return str(number) -IS_TRUE = ["true", "yes", "on", "y"] -IS_FALSE = ["false", "no", "off", "n"] -def bool_or_none(s: str) -> typing.Optional[bool]: - s = s.lower() - if s in IS_TRUE: - return True - elif s in IS_FALSE: - return False - return None -def int_or_none(s: str) -> typing.Optional[int]: - stripped_s = s.lstrip("0") - if stripped_s.isdigit(): - return int(stripped_s) - return None - def prevent_highlight(nickname: str) -> str: return nickname[0]+"\u200c"+nickname[1:] @@ -243,3 +228,32 @@ def is_ip(s: str) -> bool: def is_main_thread() -> bool: return threading.current_thread() is threading.main_thread() + +class Setting(object): + def __init__(self, name: str, help: str=None, example: str=None): + self.name = name + self.help = help + self.example = example + def parse(self, value: str) -> typing.Any: + return value + +SETTING_TRUE = ["true", "yes", "on", "y"] +SETTING_FALSE = ["false", "no", "off", "n"] +class BoolSetting(Setting): + example = "on" + def parse(self, value: str) -> typing.Any: + value_lower = value.lower() + if value_lower in SETTING_TRUE: + return True + elif value_lower in SETTING_FALSE: + return False + return None + +class IntSetting(Setting): + example = "10" + def parse(self, value: str) -> typing.Any: + stripped = value.lstrip("0") + if stripped.isdigit(): + return int(stripped) + return None +