diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 37cee7cd..9ce87ecc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -94,3 +94,28 @@ jobs: - name: List files with error (open this section to see the list of files that failed this check) run: git diff | grep "diff --git"; exit 1 if: failure() + mypy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: '3.12' + cache: pip + cache-dependency-path: | + requirements.txt + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements.txt + - name: Install mypy + run: python -m pip install mypy + - name: Install types + run: | + python -m pip install \ + types-requests \ + types-psutil \ + types-python-dateutil \ + types-PyYAML + - name: Run mypy + run: python -m mypy . diff --git a/setup.cfg b/setup.cfg index f4aa1d2b..81672e6c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,3 +17,20 @@ disable = skip = venv multi_line_output = 3 + +[mypy] +# TODO: remove disable_error_code +disable_error_code = + union-attr, + arg-type, + typeddict-item, + attr-defined, + assignment, + func-returns-value, + safe-super, + misc +exclude = + venv, + src/db, + tests +explicit_package_bases = True diff --git a/src/api/command.py b/src/api/command.py index 21ab85db..2f0014ec 100644 --- a/src/api/command.py +++ b/src/api/command.py @@ -1,7 +1,7 @@ import enum from abc import ABC, abstractmethod from types import FunctionType -from typing import TYPE_CHECKING, Any, Dict, List +from typing import TYPE_CHECKING, Any, Dict, List, Optional from src import const from src.api.execution_context import ExecutionContext @@ -79,7 +79,7 @@ def store_persistent_state(self, commands_data: Dict[str, Any]): commands_data[self.command_name]["times_called"] = self.times_called commands_data[self.command_name]["max_execution_time"] = self.max_execution_time - async def run(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> None: + async def run(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Optional[str]: if execution_ctx.platform == const.BotBackend.DISCORD: # On Discord platform we are using legacy separate time limit handling for now return await self._run_impl(cmd_line, execution_ctx) @@ -92,12 +92,12 @@ async def run(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Non await Command.send_message(execution_ctx, f"Command '{' '.join(cmd_line)}' took too long to execute") return result - async def _run_impl(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> None: + async def _run_impl(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Optional[str]: if execution_ctx.platform != const.BotBackend.DISCORD: # On Discord platform we are using legacy separate permission handling for now if execution_ctx.permission_level < self.permission_level: await self.send_message(execution_ctx, f"You don't have permission to call command '{cmd_line[0]}'") - return + return None self.times_called += 1 if not self.postpone_execution: cmd_line = (await self.process_variables(execution_ctx, ' '.join(cmd_line), cmd_line)).split(' ') @@ -122,9 +122,8 @@ async def _run_impl(self, cmd_line: List[str], execution_ctx: ExecutionContext) else: raise RuntimeError("invalid implementation type") - @abstractmethod async def _exec(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> str: - pass + raise NotImplementedError("Command executor is not implemented") @staticmethod async def check_args_count(execution_ctx, cmd_line, min=None, max=None): diff --git a/src/api/execution_context.py b/src/api/execution_context.py index c7ec7a1c..b82f4367 100644 --- a/src/api/execution_context.py +++ b/src/api/execution_context.py @@ -32,7 +32,7 @@ def message_author(self) -> str: pass @abstractmethod - def message_author_id(self) -> int: + def message_author_id(self) -> str: pass @abstractmethod diff --git a/src/api/plugin.py b/src/api/plugin.py index b3b25669..7675f85e 100644 --- a/src/api/plugin.py +++ b/src/api/plugin.py @@ -48,6 +48,7 @@ async def update_implementation(self) -> None: """ raise NotImplementedError + @staticmethod def plugin_api_version() -> PluginAPIVersion: """Plugin API version""" return PluginAPIVersion.LATEST diff --git a/src/autoupdate_impl.py b/src/autoupdate_impl.py index 7147d468..ed15a0b8 100644 --- a/src/autoupdate_impl.py +++ b/src/autoupdate_impl.py @@ -70,7 +70,8 @@ def check_updates(context: AutoUpdateContext) -> bool: """Function that performs updates check. It is called periodically""" secret_config = Util.read_config_file(const.SECRET_CONFIG_PATH) if secret_config is None: - return log.error("Failed to read secret config file") + log.error("Failed to read secret config file") + return False mail = Mail(secret_config) old_sha = context.repo.head.object.hexsha try: @@ -80,20 +81,25 @@ def check_updates(context: AutoUpdateContext) -> bool: secret_config.admin_email_list, "Autoupdate error", get_autoupdate_error_message(f"Failed to fetch updates from remote: {e}")) - return log.error(f"Fetch failed: {e}. Skipping this cycle, will try to update on the next one") + log.error(f"Fetch failed: {e}. Skipping this cycle, will try to update on the next one") + return False new_sha = context.repo.remotes.origin.refs['master'].object.name_rev.split()[0] log.debug(f"{old_sha} {new_sha}") if not FF.is_enabled("WALBOT_TEST_AUTO_UPDATE") and old_sha == new_sha: - return log.debug("No new updates") + log.debug("No new updates") + return False bot_cache = importlib.import_module("src.bot_cache").BotCache(True).parse() if bot_cache is None: - return log.warning("Could not read bot cache. Skipping this cycle, will try to update on the next one") + log.warning("Could not read bot cache. Skipping this cycle, will try to update on the next one") + return False if "do_not_update" not in bot_cache.keys(): - return log.warning( + log.warning( "Could not find 'do_not_update' field in bot cache. " "Skipping this cycle, will try to update on the next one") + return False if bot_cache["do_not_update"]: - return log.debug("Automatic update is not permitted. Skipping this cycle, will try to update on the next one") + log.debug("Automatic update is not permitted. Skipping this cycle, will try to update on the next one") + return False context.repo.git.reset("--hard") try: g = git.cmd.Git(const.WALBOT_DIR) diff --git a/src/backend/discord/commands.py b/src/backend/discord/commands.py index 501dd4b2..bce3cc7f 100644 --- a/src/backend/discord/commands.py +++ b/src/backend/discord/commands.py @@ -12,7 +12,7 @@ class DiscordCommandBinding(CommandBinding): - def bind(self, cmd_name: str, command: Command): + def bind(self, cmd_name: str, command: Command): # type:ignore if command.module_name is None: return bc.discord.commands.register_command( @@ -31,9 +31,9 @@ def unbind(self, cmd_name: str): class Commands: def __init__(self) -> None: if not hasattr(self, "data"): - self.data = dict() + self.data: Dict[str, Any] = dict() if not hasattr(self, "aliases"): - self.aliases = dict() + self.aliases: Dict[str, Any] = dict() def update(self, reload: bool = False) -> None: bc.discord.commands = self diff --git a/src/backend/discord/config.py b/src/backend/discord/config.py index 5cea98b8..530a74fa 100644 --- a/src/backend/discord/config.py +++ b/src/backend/discord/config.py @@ -1,4 +1,7 @@ +from typing import Any, Dict + + class DiscordConfig: def __init__(self) -> None: - self.guilds = dict() - self.users = dict() + self.guilds: Dict[str, Any] = dict() + self.users: Dict[str, Any] = dict() diff --git a/src/backend/discord/context.py b/src/backend/discord/context.py index 71c12657..e84e15c2 100644 --- a/src/backend/discord/context.py +++ b/src/backend/discord/context.py @@ -1,6 +1,6 @@ import asyncio import re -from typing import Optional +from typing import Any, Optional import discord @@ -40,7 +40,7 @@ def disable_pings(self, message: str) -> str: if r is None: break t = asyncio.create_task(self.message.guild.fetch_member(int(r.group(1)))) - member = asyncio.run(t) + member: Any = asyncio.run(t) message = const.DISCORD_USER_ID_REGEX.sub(str(member), message, count=1) while True: r = const.DISCORD_ROLE_ID_REGEX.search(message) @@ -57,8 +57,8 @@ def disable_pings(self, message: str) -> str: def message_author(self) -> str: return self.message.author.mention - def message_author_id(self) -> int: - return self.message.author.id + def message_author_id(self) -> str: + return str(self.message.author.id) def channel_name(self) -> str: return self.message.channel.mention diff --git a/src/backend/discord/embed.py b/src/backend/discord/embed.py index ad29e6f3..18559333 100644 --- a/src/backend/discord/embed.py +++ b/src/backend/discord/embed.py @@ -1,5 +1,5 @@ import datetime -from typing import Optional +from typing import Any, Dict, Optional import discord @@ -10,7 +10,7 @@ class DiscordEmbed: """Discord embed constructor""" def __init__(self) -> None: - self._data = dict() + self._data: Dict[str, Any] = dict() def get(self) -> discord.Embed: """Get embed result""" diff --git a/src/backend/discord/instance.py b/src/backend/discord/instance.py index 13218741..392f973e 100644 --- a/src/backend/discord/instance.py +++ b/src/backend/discord/instance.py @@ -4,6 +4,7 @@ import itertools import re import sys +from typing import Optional import discord @@ -203,8 +204,9 @@ async def _process_regular_message(self, message: discord.Message) -> None: message.content = cmd result = await self._process_command(message, cmd_split, silent=True) message.content = msg_content + result = result or "" if not self.config.discord.guilds[message.channel.guild.id].markov_pings: - result = (DiscordExecutionContext(message).disable_pings(result)) or "" + result = (DiscordExecutionContext(message).disable_pings(result)) await message.channel.send(message.author.mention + ' ' + result) elif channel_id in self.config.discord.guilds[message.channel.guild.id].markov_logging_whitelist: # If the message is in a channel that is supposed to log markov chains, doesn't mention the bot then @@ -232,7 +234,7 @@ async def _process_regular_message(self, message: discord.Message) -> None: except discord.HTTPException: pass - async def _process_command(self, message: discord.Message, command=None, silent=False) -> None: + async def _process_command(self, message: discord.Message, command=None, silent=False) -> Optional[str]: if command is None: command = message.content.split(' ') command = list(filter(None, command)) @@ -246,7 +248,7 @@ async def _process_command(self, message: discord.Message, command=None, silent= await message.channel.send( f"Unknown command '{command[0]}', " f"probably you meant '{self._suggest_similar_command(command[0])}'") - return + return None max_exec_time = self.config.commands.data[command[0]].max_execution_time if command[0] in self.config.executor["commands_data"].keys(): max_exec_time = bc.executor.commands[command[0]].max_execution_time diff --git a/src/backend/repl/instance.py b/src/backend/repl/instance.py index 6bd6682e..5b9d73f0 100644 --- a/src/backend/repl/instance.py +++ b/src/backend/repl/instance.py @@ -1,6 +1,7 @@ import asyncio import inspect import socket +from typing import Optional from src import const from src.api.bot_instance import BotInstance @@ -14,7 +15,7 @@ class ReplBotInstance(BotInstance): def __init__(self) -> None: self.channel = None - self.sock = None + self.sock: Optional[socket.socket] = None async def parse_command(self, commands, message) -> str: args = message.split(' ') diff --git a/src/backend/telegram/command.py b/src/backend/telegram/command.py index a5454f45..7f116800 100644 --- a/src/backend/telegram/command.py +++ b/src/backend/telegram/command.py @@ -13,7 +13,7 @@ async def _command_handler(command_name: str, update: Update, context: CallbackContext) -> None: await bc.executor.commands[command_name].run( - [command_name] + context.args, TelegramExecutionContext(update, context)) + [command_name] + (context.args or list()), TelegramExecutionContext(update, context)) @Mail.send_exception_info_to_admin_emails diff --git a/src/backend/telegram/config.py b/src/backend/telegram/config.py index db2a4b5d..2105de79 100644 --- a/src/backend/telegram/config.py +++ b/src/backend/telegram/config.py @@ -1,8 +1,9 @@ import uuid +from typing import Any, Dict, Set class TelegramConfig: def __init__(self) -> None: - self.channel_whitelist = set() + self.channel_whitelist: Set[str] = set() self.passphrase = uuid.uuid4().hex - self.users = dict() + self.users: Dict[str, Any] = dict() diff --git a/src/backend/telegram/context.py b/src/backend/telegram/context.py index 0076a621..55528be4 100644 --- a/src/backend/telegram/context.py +++ b/src/backend/telegram/context.py @@ -1,4 +1,5 @@ import os +from typing import Dict from telegram import Update from telegram.ext import CallbackContext @@ -13,12 +14,12 @@ class TelegramExecutionContext(ExecutionContext): def __init__(self, update: Update, context: CallbackContext) -> None: super().__init__() - self.platform = const.BotBackend.TELEGRAM + self.platform = str(const.BotBackend.TELEGRAM) self.update = update self.context = context self.user = bc.config.telegram.users[update.message.from_user.id] self.permission_level = bc.config.telegram.users[update.message.from_user.id].permission_level - self._replace_patterns = dict() + self._replace_patterns: Dict[str, str] = dict() async def send_message(self, message: str, *args, **kwargs) -> None: if self.silent: @@ -45,7 +46,7 @@ async def reply(self, message: str, *args, **kwargs) -> None: await self.send_message(message, *args, **kwargs, reply_on_msg=True) async def send_direct_message(self, user_id: int, message: str, *args, **kwargs) -> None: - await send_message(user_id, message) + send_message(user_id, message) def _unescape_ping1(self, message: str) -> str: idx = 0 @@ -75,7 +76,7 @@ def message_author(self) -> str: return self.update.message.from_user.mention_markdown_v2() def message_author_id(self) -> str: - return self.update.message.from_user.id + return str(self.update.message.from_user.id) def channel_name(self) -> str: return self.update.message.chat.title or "" diff --git a/src/backend/telegram/instance.py b/src/backend/telegram/instance.py index 6887685a..1b7cd413 100644 --- a/src/backend/telegram/instance.py +++ b/src/backend/telegram/instance.py @@ -83,7 +83,7 @@ def _run(self, args) -> None: counter += 1 if self._is_stopping: log.info("Stopping Telegram instance...") - app.stop() + loop.run_until_complete(app.stop()) log.info("Telegram instance is stopped!") break if counter % const.REMINDER_POLLING_INTERVAL == 0: diff --git a/src/bc.py b/src/bc.py index 472099fd..4cff2579 100644 --- a/src/bc.py +++ b/src/bc.py @@ -58,7 +58,7 @@ class Telegram: def __init__(self) -> None: self.bot_username: 'Optional[str]' = None self.app: 'Optional[telegram.ext.Application]' = None - self.handlers: 'Dict[CommandHandler]' = dict() + self.handlers: 'Dict[str, CommandHandler]' = dict() class Repl: def __init__(self) -> None: @@ -66,7 +66,7 @@ def __init__(self) -> None: class Backend: def __init__(self) -> None: - self._backends: Dict[str, bool] = dict(zip( + self._backends: Dict[const.BotBackend, bool] = dict(zip( [backend for backend in const.BotBackend][1:], itertools.repeat(False))) def set_running(self, backend: const.BotBackend, new_state: bool, user_data_msg: str = "") -> None: @@ -78,7 +78,7 @@ def set_running(self, backend: const.BotBackend, new_state: bool, user_data_msg: log.info(f"Backend controller: {str(backend).title()} instance has stopped!") self._backends[backend] = new_state - def is_running(self, backend: const.BotBackend) -> None: + def is_running(self, backend: const.BotBackend) -> bool: return self._backends[backend] def __init__(self): @@ -98,3 +98,4 @@ def __init__(self): self.plugin_manager = PluginManager(self.executor) self.message_cache = MessageCache() self.be = self.Backend() + self.guilds = None # TODO: move to Discord diff --git a/src/bot_cache.py b/src/bot_cache.py index 1321a507..fd05098c 100644 --- a/src/bot_cache.py +++ b/src/bot_cache.py @@ -28,7 +28,7 @@ def update(self, upd_dict): def parse(self) -> Optional[Dict]: if not os.path.exists(self.path): - return + return None cache = None for _ in range(10): try: @@ -38,10 +38,11 @@ def parse(self) -> Optional[Dict]: if "pid" not in cache or not psutil.pid_exists(int(cache["pid"])): log.warning("Could validate pid from .bot_cache") os.remove(self.path) - return + return None return cache except json.decoder.JSONDecodeError: time.sleep(0.5) + return None def dump_to_file(self) -> None: with open(self.path, 'w') as f: diff --git a/src/cmd/builtin.py b/src/cmd/builtin.py index a601e2d2..ce141d4f 100644 --- a/src/cmd/builtin.py +++ b/src/cmd/builtin.py @@ -136,7 +136,7 @@ async def _uptime(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> """Show bot uptime Example: !uptime""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None result = bc.info.uptime await Command.send_message(execution_ctx, result) return result @@ -149,7 +149,7 @@ async def _help(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> N !help -a (alternative: !help --all) !help """ if not await Command.check_args_count(execution_ctx, cmd_line, min=1): - return + return None parser = CmdArgParser(execution_ctx) parser.add_argument("command_name", action="store", default=None, nargs='?') parser.add_argument("-p", "--plain", action="store_true", default=False) @@ -160,7 +160,7 @@ async def _help(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> N def get_help_for_command(cmd_name: str) -> str: if cmd_name not in bc.executor.commands.keys(): - return + return "" cmd = bc.executor.commands[cmd_name] result = "" if cmd.impl_type == Implementation.FUNCTION: @@ -227,7 +227,7 @@ async def _about(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> !about -vv <- even more verbose """ if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=2): - return + return None parser = CmdArgParser(execution_ctx) parser.add_argument("-v", "--verbose", action="store_const", dest="verbosity", const=1, default=0) parser.add_argument( @@ -242,7 +242,7 @@ async def _shutdown(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Shutdown the bot Example: !shutdown""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None await Command.send_message(execution_ctx, f"{execution_ctx.message_author()} invoked bot shutdown!") subprocess.call([sys.executable, "walbot.py", "stop"]) @@ -250,7 +250,7 @@ async def _restart(self, cmd_line: List[str], execution_ctx: ExecutionContext) - """Restart the bot Example: !restart""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None await Command.send_message(execution_ctx, f"{execution_ctx.message_author()} invoked restarting the bot!") subprocess.call([sys.executable, "walbot.py", "restart"]) @@ -260,7 +260,7 @@ async def _version(self, cmd_line: List[str], execution_ctx: ExecutionContext) - !version !version short""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=2): - return + return None result = bc.info.version if len(cmd_line) == 2 and (cmd_line[1] == 's' or cmd_line[1] == 'short'): result = result[:7] @@ -272,7 +272,7 @@ async def _extexec(self, cmd_line: List[str], execution_ctx: ExecutionContext) - Note: Be careful when you are executing external commands! Example: !extexec uname -a""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None return await Shell.run_and_send_stdout(execution_ctx, ' '.join(cmd_line[1:])) async def _curl(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Optional[str]: @@ -282,13 +282,13 @@ async def _curl(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> O !curl --no-proxy """ if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=3): - return + return None parser = CmdArgParser(execution_ctx) parser.add_argument("url") parser.add_argument("--no-proxy", action="store_false", dest="use_proxy") args = parser.parse_args(cmd_line) if args is None: - return + return None try: r = Util.request(args.url, use_proxy=args.use_proxy) result = r.get_text() @@ -296,12 +296,13 @@ async def _curl(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> O return result except Exception as e: await Command.send_message(execution_ctx, f"Request failed: {e}") + return None async def _wme(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> None: """Send direct message to author with something Example: !wme Hello!""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None result = ' '.join(cmd_line[1:]) if not result: return @@ -312,7 +313,7 @@ async def _getmentioncmd(self, cmd_line: List[str], execution_ctx: ExecutionCont """Get current command which is executed on bot ping Example: !getmentioncmd""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None await Command.send_message(execution_ctx, bc.config.on_mention_command) async def _setmentioncmd(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> None: @@ -321,7 +322,7 @@ async def _setmentioncmd(self, cmd_line: List[str], execution_ctx: ExecutionCont !setmentioncmd ping !setmentioncmd markov""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None command = ' '.join(cmd_line[1:]) bc.config.on_mention_command = command await Command.send_message(execution_ctx, f"Command '{command}' was set on bot mention") @@ -332,7 +333,7 @@ async def _profile(self, cmd_line: List[str], execution_ctx: ExecutionContext) - !profile !profile `@user`""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=2): - return + return None user = "" if execution_ctx.platform == const.BotBackend.DISCORD: if len(cmd_line) == 1: @@ -361,7 +362,7 @@ async def _server(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> """Print information about current server Example: !server""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=2): - return + return None if execution_ctx.platform == const.BotBackend.DISCORD: g = execution_ctx.message.guild e = DiscordEmbed() @@ -388,7 +389,7 @@ async def _message(self, cmd_line: List[str], execution_ctx: ExecutionContext) - """Get message by its order number counting from the newest message Example: !message""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None number = await Util.parse_int( execution_ctx, cmd_line[1], "Message number should be an integer") if number is None: @@ -416,7 +417,7 @@ async def _tts(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> No """Send text-to-speech (TTS) message Example: !tts Hello!""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None text = ' '.join(cmd_line[1:]) await Command.send_message(execution_ctx, text, tts=True) @@ -452,7 +453,7 @@ async def _nick(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> N """Change nickname Usage: !nick walbot""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None new_nick = ' '.join(cmd_line[1:]) try: await execution_ctx.message.guild.me.edit(nick=new_nick) diff --git a/src/cmd/debug.py b/src/cmd/debug.py index acbdf2c1..b5299d4a 100644 --- a/src/cmd/debug.py +++ b/src/cmd/debug.py @@ -40,11 +40,11 @@ def bind(self) -> None: "debug", "eval", const.Permission.ADMIN, Implementation.FUNCTION, subcommand=False, impl_func=self._eval) - async def _dbg(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> None: + async def _dbg(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Optional[str]: """Debug command Example: !dbg """ if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None debug_info = _DebugCommand(cmd_line[1], cmd_line[2:]) result = debug_info.run() await Command.send_message(execution_ctx, result) @@ -55,7 +55,7 @@ async def _eval(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> N Note: Dangerous, use it only if you know what you are doing Example: !eval """ if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None old_stdout = sys.stdout sys.stdout = tmp_stdout = StringIO() try: diff --git a/src/cmd/markov.py b/src/cmd/markov.py index 7604a227..4ac6b136 100644 --- a/src/cmd/markov.py +++ b/src/cmd/markov.py @@ -7,7 +7,7 @@ from src.api.command import BaseCmd, Command, Implementation from src.api.execution_context import ExecutionContext from src.config import bc -from src.utils import Util +from src.utils import Util, null class MarkovCommands(BaseCmd): @@ -62,7 +62,7 @@ async def _markov(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> """Generate message using Markov chain Example: !markov""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1): - return + return None if len(cmd_line) > 1: result = "" for _ in range(const.MAX_MARKOV_ATTEMPTS): @@ -83,7 +83,7 @@ async def _markovgc(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Garbage collect Markov model nodes Example: !markovgc""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None result = bc.markov.collect_garbage() result = f"Garbage collected {len(result)} items: {', '.join(result)}" await Command.send_message(execution_ctx, result) @@ -93,12 +93,12 @@ async def _delmarkov(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Delete all words in Markov model by regex Example: !delmarkov hello""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None regex = ' '.join(cmd_line[1:]) try: removed = bc.markov.del_words(regex) except re.error as e: - return await Command.send_message(execution_ctx, f"Invalid regular expression: {e}") + return null(await Command.send_message(execution_ctx, f"Invalid regular expression: {e}")) await execution_ctx.send_message(f"Deleted {len(removed)} words from model: {removed}", suppress_embeds=True) async def _findmarkov(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> None: @@ -108,12 +108,12 @@ async def _findmarkov(self, cmd_line: List[str], execution_ctx: ExecutionContext !findmarkov hello !findmarkov hello -f""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=3): - return + return None regex = cmd_line[1] try: found = bc.markov.find_words(regex) except re.error as e: - return await Command.send_message(execution_ctx, f"Invalid regular expression: {e}") + return null(await Command.send_message(execution_ctx, f"Invalid regular expression: {e}")) amount = len(found) if not (len(cmd_line) > 2 and cmd_line[2] == '-f' and execution_ctx.permission_level >= const.Permission.MOD.value): @@ -129,12 +129,12 @@ async def _getmarkovword(self, cmd_line: List[str], execution_ctx: ExecutionCont !getmarkovword hello -a <- get amount of found words !getmarkovword hello 0 <- get word by index""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3, max=3): - return + return None regex = cmd_line[1] try: found = bc.markov.find_words(regex) except re.error as e: - return await Command.send_message(execution_ctx, f"Invalid regular expression: {e}") + return null(await Command.send_message(execution_ctx, f"Invalid regular expression: {e}")) amount = len(found) if cmd_line[2] == '-a': result = str(amount) @@ -144,10 +144,10 @@ async def _getmarkovword(self, cmd_line: List[str], execution_ctx: ExecutionCont execution_ctx, cmd_line[2], f"Third parameter '{cmd_line[2]}' should be a valid index") if index is None: - return + return None if not 0 <= index < amount: - return await Command.send_message( - execution_ctx, f"Wrong index in list '{cmd_line[2]}' (should be in range [0..{amount - 1}])") + return null(await Command.send_message( + execution_ctx, f"Wrong index in list '{cmd_line[2]}' (should be in range [0..{amount - 1}])")) result = found[index] await Command.send_message(execution_ctx, result) return result @@ -156,7 +156,7 @@ async def _dropmarkov(self, cmd_line: List[str], execution_ctx: ExecutionContext """Drop Markov database Example: !dropmarkov""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None bc.markov.__init__() await Command.send_message(execution_ctx, "Markov database has been dropped!") @@ -164,7 +164,7 @@ async def _statmarkov(self, cmd_line: List[str], execution_ctx: ExecutionContext """Show stats for Markov module Example: !statmarkov""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None pairs_count = sum(word.total_next for word in bc.markov.model.values()) markov_db_size = os.path.getsize(const.MARKOV_PATH) while markov_db_size == 0: @@ -185,7 +185,7 @@ async def _inspectmarkov(self, cmd_line: List[str], execution_ctx: ExecutionCont """Inspect next words in Markov model for current one Example: !inspectmarkov hello""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=3): - return + return None word = cmd_line[1] if len(cmd_line) > 1 else '' words = bc.markov.get_next_words_list(word) result = f"Next for '{word}':\n" @@ -203,7 +203,7 @@ async def _addmarkovfilter(self, cmd_line: List[str], execution_ctx: ExecutionCo """Add regular expression filter for Markov model Example: !addmarkovfilter regex""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None bc.markov.filters.append(re.compile(cmd_line[1], re.DOTALL)) await Command.send_message(execution_ctx, f"Filter '{cmd_line[1]}' was successfully added for Markov model") @@ -211,7 +211,7 @@ async def _listmarkovfilter(self, cmd_line: List[str], execution_ctx: ExecutionC """Print list of regular expression filters for Markov model Example: !listmarkovfilter""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None result = "" for index, regex in enumerate(bc.markov.filters): result += f"{index} -> `{regex.pattern}`\n" @@ -222,7 +222,7 @@ async def _delmarkovfilter(self, cmd_line: List[str], execution_ctx: ExecutionCo """Delete regular expression filter for Markov model by index Example: !delmarkovfilter 0""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of filter") @@ -238,7 +238,7 @@ async def _addmarkovignoredprefix(self, cmd_line: List[str], execution_ctx: Exec """Add message prefix that should be ignored by Markov model Example: !addmarkovignoredprefix $""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None prefix = ' '.join(cmd_line[1:]) index = bc.config.ids["markov_ignored_prefix"] bc.markov.ignored_prefixes[index] = ' '.join(cmd_line[1:]) @@ -249,7 +249,7 @@ async def _listmarkovignoredprefix(self, cmd_line: List[str], execution_ctx: Exe """List all prefixes that should be ignored by Markov model Example: !listmarkovignoredprefix""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None result = "" for index, prefix in bc.markov.ignored_prefixes.items(): result += f"{index} -> `{prefix}`\n" @@ -259,7 +259,7 @@ async def _delmarkovignoredprefix(self, cmd_line: List[str], execution_ctx: Exec """Delete message prefix that should be ignored by Markov model by its index Example: !delmarkovignoredprefix 0""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of ignored prefix") if index is None: diff --git a/src/cmd/math.py b/src/cmd/math.py index 3861c96b..a1dcc7f0 100644 --- a/src/cmd/math.py +++ b/src/cmd/math.py @@ -81,7 +81,7 @@ async def _calc(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> O !calc 2+2*2 !calc 4/2-1""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None expr = ' '.join(cmd_line[1:]) try: result = str(MathExprEvaluator().evaluate(expr)) @@ -97,7 +97,7 @@ async def _if(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Opt !if 0 It's true;It's false -> It's false """ if not await Command.check_args_count(execution_ctx, cmd_line, min=3): - return + return None condition = cmd_line[1] true = ["true"] @@ -132,12 +132,12 @@ async def _loop(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> O !loop 2 ping !loop 5 echo Hello!""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3): - return + return None subcommand = cmd_line[2:] loop_count = await Util.parse_int( execution_ctx, cmd_line[1], "Loop iterations count should be an integer") if loop_count is None: - return + return None if loop_count <= 0: await Command.send_message(execution_ctx, "Loop iterations count should be greater than 0") result = "" diff --git a/src/cmd/quote.py b/src/cmd/quote.py index 2ed5ba9f..6767caa7 100644 --- a/src/cmd/quote.py +++ b/src/cmd/quote.py @@ -2,7 +2,7 @@ import datetime import random -from typing import List +from typing import List, Optional from src import const from src.api.command import BaseCmd, Command, Implementation @@ -40,7 +40,7 @@ async def _quote(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> !quote !quote 1""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=2): - return + return None if not bc.config.quotes: return await Command.send_message(execution_ctx, "") if len(cmd_line) == 2: @@ -70,7 +70,7 @@ async def _addquote(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Add quote to quotes database Example: !addquote Hello, world!""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None quote = ' '.join(cmd_line[1:]) index = bc.config.ids["quote"] bc.config.quotes[index] = Quote(quote, execution_ctx.message_author()) @@ -78,11 +78,11 @@ async def _addquote(self, cmd_line: List[str], execution_ctx: ExecutionContext) await Command.send_message( execution_ctx, f"Quote '{quote}' was successfully added to quotes database with index {index}") - async def _listquote(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> None: + async def _listquote(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Optional[str]: """Print list of all quotes Example: !listquote""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None result = "" for index, quote in bc.config.quotes.items(): result += f"{index} -> {quote.quote()}\n" @@ -93,7 +93,7 @@ async def _delquote(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Delete quote from quotes database by index Example: !delquote 0""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of quote") if index is None: @@ -108,7 +108,7 @@ async def _setquoteauthor(self, cmd_line: List[str], execution_ctx: ExecutionCon """Set author of quote by its index Example: !setquoteauthor 0 WalBot""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of quote") if index is None: diff --git a/src/cmd/random.py b/src/cmd/random.py index 71392e05..580d7b3d 100644 --- a/src/cmd/random.py +++ b/src/cmd/random.py @@ -27,17 +27,17 @@ async def _random(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> """Get random number in range [left, right] Example: !random 5 10""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3, max=3): - return + return None left = await Util.parse_float( execution_ctx, cmd_line[1], "Left border should be a number") if left is None: - return + return None right = await Util.parse_float( execution_ctx, cmd_line[2], "Right border should be a number") if right is None: - return + return None if left > right: - return Command.send_message(execution_ctx, "Left border should be less or equal than right") + return await Command.send_message(execution_ctx, "Left border should be less or equal than right") if const.INTEGER_NUMBER.fullmatch(cmd_line[1]) and const.INTEGER_NUMBER.fullmatch(cmd_line[2]): result = str(random.randint(int(left), int(right))) # integer random else: @@ -49,7 +49,7 @@ async def _randselect(self, cmd_line: List[str], execution_ctx: ExecutionContext """Get random option among provided strings (split by space) Example: !randselect a b c""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None index = random.randint(1, len(cmd_line) - 1) result = cmd_line[index] await Command.send_message(execution_ctx, result) @@ -59,7 +59,7 @@ async def _randselects(self, cmd_line: List[str], execution_ctx: ExecutionContex """Get random option among provided strings (split by semicolon) Example: !randselects a;b;c""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None options = ' '.join(cmd_line[1:]).split(';') index = random.randint(0, len(options) - 1) result = options[index] diff --git a/src/cmd/reaction.py b/src/cmd/reaction.py index 57c13969..34123065 100644 --- a/src/cmd/reaction.py +++ b/src/cmd/reaction.py @@ -54,7 +54,7 @@ async def _addreaction(self, cmd_line: List[str], execution_ctx: ExecutionContex """Add reaction Example: !addreaction emoji regex""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3): - return + return None bc.config.reactions[bc.config.ids["reaction"]] = Reaction(' '.join(cmd_line[2:]), cmd_line[1]) bc.config.ids["reaction"] += 1 await Command.send_message( @@ -64,7 +64,7 @@ async def _updreaction(self, cmd_line: List[str], execution_ctx: ExecutionContex """Update reaction Example: !updreaction index emoji regex""" if not await Command.check_args_count(execution_ctx, cmd_line, min=4): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should an index (integer)") if index is None: @@ -81,7 +81,7 @@ async def _delreaction(self, cmd_line: List[str], execution_ctx: ExecutionContex Examples: !delreaction index""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reaction") if index is None: @@ -92,11 +92,11 @@ async def _delreaction(self, cmd_line: List[str], execution_ctx: ExecutionContex else: await Command.send_message(execution_ctx, "Invalid index of reaction!") - async def _listreaction(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> str: + async def _listreaction(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Optional[str]: """Print list of reactions Example: !listreaction""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None result = "" for index, reaction in bc.config.reactions.items(): result += f"{index} - {reaction.emoji}: `{reaction.regex}`\n" @@ -107,7 +107,7 @@ async def _addresponse(self, cmd_line: List[str], execution_ctx: ExecutionContex """Add bot response on message that contains particular regex Example: !addresponse regex;text""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None parts = ' '.join(cmd_line[1:]).split(';', 1) if len(parts) < 2: return await Command.send_message( @@ -121,7 +121,7 @@ async def _updresponse(self, cmd_line: List[str], execution_ctx: ExecutionContex """Update bot response Example: !updresponse index regex;text""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should an index (integer)") if index is None: @@ -141,7 +141,7 @@ async def _delresponse(self, cmd_line: List[str], execution_ctx: ExecutionContex Examples: !delresponse index""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of response") if index is None: @@ -155,7 +155,7 @@ async def _listresponse(self, cmd_line: List[str], execution_ctx: ExecutionConte """Print list of responses Example: !listresponse""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None result = "" for index, response in bc.config.responses.items(): result += f"{index} - `{response.regex}`: {response.text}\n" diff --git a/src/cmd/reminder.py b/src/cmd/reminder.py index 7155af65..d8f6d6b0 100644 --- a/src/cmd/reminder.py +++ b/src/cmd/reminder.py @@ -17,7 +17,7 @@ from src.api.reminder import Reminder from src.backend.discord.embed import DiscordEmbed from src.config import bc -from src.utils import Time, Util +from src.utils import Time, Util, null class _ReminderInternals: @@ -127,7 +127,7 @@ async def parse_reminder_args_in(execution_ctx: ExecutionContext, time: str): @staticmethod async def listreminder_common(cmd_line: List[str], execution_ctx: ExecutionContext, is_only_locals=False) -> None: if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=2): - return + return None if len(cmd_line) == 2: count = await Util.parse_int( execution_ctx, cmd_line[1], @@ -278,7 +278,7 @@ async def _reminder(self, cmd_line: List[str], execution_ctx: ExecutionContext) Example: !reminder 1""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -333,7 +333,7 @@ async def _addreminder(self, cmd_line: List[str], execution_ctx: ExecutionContex !addreminder in 5h10m Test reminder 3 """ if not await Command.check_args_count(execution_ctx, cmd_line, min=4): - return + return None text = ' '.join(cmd_line[3:]) if cmd_line[1] == "in": time = await _ReminderInternals.parse_reminder_args_in(execution_ctx, cmd_line[2]) @@ -387,7 +387,7 @@ async def _updreminder(self, cmd_line: List[str], execution_ctx: ExecutionContex !updreminder 0 in 5h10m Test reminder 3 """ if not await Command.check_args_count(execution_ctx, cmd_line, min=5): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -439,7 +439,7 @@ async def _delreminder(self, cmd_line: List[str], execution_ctx: ExecutionContex """Delete reminders by index Example: !delreminder 0 1 2""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None errors = [] passed = [] for i in range(1, len(cmd_line)): @@ -471,7 +471,7 @@ async def _remindme(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Ask bot to ping you when it sends reminder Example: !remindme 1""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -485,7 +485,7 @@ async def _remindwme(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Ask bot to send direct message you when it sends reminder Example: !remindwme 1""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -509,7 +509,7 @@ async def _remindeme(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Ask bot to send you an e-mail when it sends reminder Example: !remindeme 1 """ if not await Command.check_args_count(execution_ctx, cmd_line, min=3, max=3): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -537,7 +537,7 @@ async def _repeatreminder(self, cmd_line: List[str], execution_ctx: ExecutionCon !repeatreminder 1 0 Note: number without postfix is translated to minutes. 0 means disabling repetition""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3, max=3): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -557,37 +557,37 @@ async def _repeatreminder(self, cmd_line: List[str], execution_ctx: ExecutionCon cmd_line[2] = "1y" if cmd_line[2].endswith("h"): - duration = cmd_line[2][:-1] + duration_str = cmd_line[2][:-1] duration = await Util.parse_int( - execution_ctx, duration, "You need to specify amount of days before 'd'. Example: 3d for 3 days") + execution_ctx, duration_str, "You need to specify amount of days before 'd'. Example: 3d for 3 days") if duration is None: return duration *= 60 elif cmd_line[2].endswith("d"): - duration = cmd_line[2][:-1] + duration_str = cmd_line[2][:-1] duration = await Util.parse_int( - execution_ctx, duration, "You need to specify amount of days before 'd'. Example: 3d for 3 days") + execution_ctx, duration_str, "You need to specify amount of days before 'd'. Example: 3d for 3 days") if duration is None: return duration *= 1440 elif cmd_line[2].endswith("w"): - duration = cmd_line[2][:-1] + duration_str = cmd_line[2][:-1] duration = await Util.parse_int( - execution_ctx, duration, "You need to specify amount of days before 'd'. Example: 3d for 3 days") + execution_ctx, duration_str, "You need to specify amount of days before 'd'. Example: 3d for 3 days") if duration is None: return duration *= 10080 elif cmd_line[2].endswith("m"): - duration = cmd_line[2][:-1] + duration_str = cmd_line[2][:-1] duration = await Util.parse_int( - execution_ctx, duration, "You need to specify amount of days before 'm'. Example: 3m for 3 months") + execution_ctx, duration_str, "You need to specify amount of days before 'm'. Example: 3m for 3 months") if duration is None: return bc.config.reminders[index].repeat_interval_measure = "months" elif cmd_line[2].endswith("y"): - duration = cmd_line[2][:-1] + duration_str = cmd_line[2][:-1] duration = await Util.parse_int( - execution_ctx, duration, "You need to specify amount of days before 'y'. Example: 3y for 3 years") + execution_ctx, duration_str, "You need to specify amount of days before 'y'. Example: 3y for 3 years") if duration is None: return bc.config.reminders[index].repeat_interval_measure = "years" @@ -613,7 +613,7 @@ async def _skipreminder(self, cmd_line: List[str], execution_ctx: ExecutionConte Example: !skipreminder 1 Note: only recurring (repeating) reminders are affected by this command""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -627,7 +627,8 @@ async def _skipreminder(self, cmd_line: List[str], execution_ctx: ExecutionConte datetime.datetime.strptime(rem.time, const.REMINDER_DATETIME_FORMAT) + rem.get_next_event_delta(), const.REMINDER_DATETIME_FORMAT) if (rem.remaining_repetitions == 0 or - new_time > datetime.datetime.strptime(rem.limit_repetitions_time, const.REMINDER_DATETIME_FORMAT)): + new_time > datetime.datetime.strptime( # type:ignore + rem.limit_repetitions_time, const.REMINDER_DATETIME_FORMAT)): return await Command.send_message(execution_ctx, f"Repetition limit exceeded for reminder {index}") id_ = bc.config.ids["reminder"] bc.config.reminders[id_] = Reminder( @@ -654,13 +655,13 @@ async def _timeuntilreminder(self, cmd_line: List[str], execution_ctx: Execution """Show time until particular reminder Example: !timeuntilreminder 1""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: - return + return None if index not in bc.config.reminders.keys(): - return await Command.send_message(execution_ctx, "Invalid index of reminder!") + return null(await Command.send_message(execution_ctx, "Invalid index of reminder!")) rem = bc.config.reminders[index] rem_time = datetime.datetime.strptime(rem.time, const.REMINDER_DATETIME_FORMAT) - Time().now() if rem_time < datetime.timedelta(days=1): @@ -677,7 +678,7 @@ async def _setprereminders(self, cmd_line: List[str], execution_ctx: ExecutionCo !setprereminders 1 10 !setprereminders 2 5 10 15""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -705,7 +706,7 @@ async def _addremindernotes(self, cmd_line: List[str], execution_ctx: ExecutionC """Add reminder notes for specific reminder Example: !addremindernotes 1 Some text""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -720,7 +721,7 @@ async def _delremindernotes(self, cmd_line: List[str], execution_ctx: ExecutionC """Delete reminder notes for specific reminder Example: !delremindernotes 1""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=2): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -734,7 +735,7 @@ async def _setreminderchannel(self, cmd_line: List[str], execution_ctx: Executio """Set channel where reminder will be sent Example: !setreminderchannel 1 """ if not await Command.check_args_count(execution_ctx, cmd_line, min=3, max=3): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -753,7 +754,7 @@ async def _repeatreminderfor(self, cmd_line: List[str], execution_ctx: Execution """Limit amount of repetitions for reminder Example: !repeatreminderfor 1 5 <- repeat reminder 1 only 5 times""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3, max=3): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: @@ -775,7 +776,7 @@ async def _repeatreminderuntil(self, cmd_line: List[str], execution_ctx: Executi !repeatreminderuntil 1 2022-12-31 23:59 <- repeat reminder 1 until Dec 31, 2022 23:59 """ if not await Command.check_args_count(execution_ctx, cmd_line, min=3, max=4): - return + return None index = await Util.parse_int( execution_ctx, cmd_line[1], f"Second parameter for '{cmd_line[0]}' should be an index of reminder") if index is None: diff --git a/src/cmd/services.py b/src/cmd/services.py index 915ae9cd..6cc6b4c8 100644 --- a/src/cmd/services.py +++ b/src/cmd/services.py @@ -4,7 +4,7 @@ import sys from typing import List, Optional -from aiogoogletrans import Translator +from aiogoogletrans import Translator # type:ignore from src import const from src.api.command import BaseCmd, Command, Implementation @@ -33,7 +33,7 @@ async def _netcheck(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Check network and proxy settings Usage: !netcheck""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None result = "" try: r = Util.request( @@ -50,12 +50,13 @@ async def _netcheck(self, cmd_line: List[str], execution_ctx: ExecutionContext) except Exception as e: result += "Speedtest: failed with error: " + str(e) + "\n" await Command.send_message(execution_ctx, result) + return result async def _translate(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Optional[str]: """Translate text to specified language Usage: !translate """ if not await Command.check_args_count(execution_ctx, cmd_line, min=3): - return + return None translator = Translator(proxy=Util.proxy.http() or None) dst_language = cmd_line[1] text = " ".join(cmd_line[2:]) @@ -66,12 +67,13 @@ async def _translate(self, cmd_line: List[str], execution_ctx: ExecutionContext) return result except ValueError as e: await Command.send_message(execution_ctx, f"ERROR! Could not translate text: {e}") + return None async def _weather(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Optional[str]: """Get current weather using wttr.in Usage: !weather """ if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None city = ' '.join(cmd_line[1:]) try: r = Util.request(f"https://wttr.in/{city}?format=4") @@ -89,12 +91,13 @@ async def _weather(self, cmd_line: List[str], execution_ctx: ExecutionContext) - result = f"Error while getting weather: {e}" await Command.send_message(execution_ctx, result) return result + return None - async def _weatherforecast(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> None: + async def _weatherforecast(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Optional[str]: """Get weather forecast using wttr.in Usage: !weatherforecast """ if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None city = ' '.join(cmd_line[1:]) try: r = Util.request(f"https://wttr.in/{city}.png?m") @@ -112,3 +115,4 @@ async def _weatherforecast(self, cmd_line: List[str], execution_ctx: ExecutionCo result = f"Error while getting weather: {e}" await Command.send_message(execution_ctx, result) return result + return None diff --git a/src/cmd/string.py b/src/cmd/string.py index c84c232f..cb5a0947 100644 --- a/src/cmd/string.py +++ b/src/cmd/string.py @@ -72,7 +72,7 @@ async def _emojify(self, cmd_line: List[str], execution_ctx: ExecutionContext) - """Emojify text Example: !emojify Hello!""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None text = ' '.join(cmd_line[1:]).lower() result = "" is_emoji = False @@ -93,7 +93,7 @@ async def _demojify(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Demojify text Example: !demojify 🇭 🇪 🇱 🇱 🇴""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None text = ' '.join(cmd_line[1:]) result = "" i = 0 @@ -115,7 +115,7 @@ async def _range(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> !range !range """ if not await Command.check_args_count(execution_ctx, cmd_line, min=2, max=4): - return + return None start, stop, step = 0, 0, 1 if len(cmd_line) == 2: stop = await Util.parse_int( @@ -145,7 +145,7 @@ async def _urlencode(self, cmd_line: List[str], execution_ctx: ExecutionContext) """Urlencode string Example: !urlencode hello, world!""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None result = ' '.join(cmd_line[1:]) result = urllib.request.quote(result.encode("cp1251")) await Command.send_message(execution_ctx, result) @@ -159,12 +159,12 @@ async def _takechars(self, cmd_line: List[str], execution_ctx: ExecutionContext) !takechars -2 hello Result: lo""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None result = ' '.join(cmd_line[2:]) num = await Util.parse_int( execution_ctx, cmd_line[1], f"Second argument of command '{cmd_line[0]}' should be an integer") if num is None: - return + return None if num < 0: result = result[len(result) + num:] else: @@ -180,12 +180,12 @@ async def _dropchars(self, cmd_line: List[str], execution_ctx: ExecutionContext) !dropchars -2 hello Result: hel""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None result = ' '.join(cmd_line[2:]) num = await Util.parse_int( execution_ctx, cmd_line[1], f"Second argument of command '{cmd_line[0]}' should be an integer") if num is None: - return + return None if num < 0: result = result[:len(result) + num] else: @@ -197,7 +197,7 @@ async def _countchars(self, cmd_line: List[str], execution_ctx: ExecutionContext """Calculate length of the message Example: !countchars some text""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None result = str(len(' '.join(cmd_line[1:]))) await Command.send_message(execution_ctx, result) return result @@ -210,16 +210,16 @@ async def _takewords(self, cmd_line: List[str], execution_ctx: ExecutionContext) !takewords -2 a b c Result: b c""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return - result = ' '.join(cmd_line[2:]).split() + return None + result_list = ' '.join(cmd_line[2:]).split() num = await Util.parse_int( execution_ctx, cmd_line[1], f"Second argument of command '{cmd_line[0]}' should be an integer") if num is None: - return + return None if num < 0: - result = ' '.join(result[len(result) + num:]) + result = ' '.join(result_list[len(result_list) + num:]) else: - result = ' '.join(result[:num]) + result = ' '.join(result_list[:num]) await Command.send_message(execution_ctx, result) return result @@ -231,16 +231,16 @@ async def _dropwords(self, cmd_line: List[str], execution_ctx: ExecutionContext) !dropwords -2 a b c Result: a""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return - result = ' '.join(cmd_line[2:]).split() + return None + result_list = ' '.join(cmd_line[2:]).split() num = await Util.parse_int( execution_ctx, cmd_line[1], f"Second argument of command '{cmd_line[0]}' should be an integer") if num is None: - return + return None if num < 0: - result = ' '.join(result[:len(result) + num]) + result = ' '.join(result_list[:len(result_list) + num]) else: - result = ' '.join(result[num:]) + result = ' '.join(result_list[num:]) await Command.send_message(execution_ctx, result) return result @@ -248,7 +248,7 @@ async def _countwords(self, cmd_line: List[str], execution_ctx: ExecutionContext """Count amount of words Example: !countwords some text""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None result = str(len(' '.join(cmd_line).split()) - 1) await Command.send_message(execution_ctx, result) return result @@ -267,16 +267,16 @@ async def _takelines(self, cmd_line: List[str], execution_ctx: ExecutionContext) Result: b c""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return - result = ' '.join(cmd_line[2:]).split('\n') + return None + result_list = ' '.join(cmd_line[2:]).split('\n') num = await Util.parse_int( execution_ctx, cmd_line[1], f"Second argument of command '{cmd_line[0]}' should be an integer") if num is None: - return + return None if num < 0: - result = '\n'.join(result[len(result) + num:]) + result = '\n'.join(result_list[len(result_list) + num:]) else: - result = '\n'.join(result[:num]) + result = '\n'.join(result_list[:num]) await Command.send_message(execution_ctx, result) return result @@ -292,16 +292,16 @@ async def _droplines(self, cmd_line: List[str], execution_ctx: ExecutionContext) c Result: a""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return - result = ' '.join(cmd_line[2:]).split('\n') + return None + result_list = ' '.join(cmd_line[2:]).split('\n') num = await Util.parse_int( execution_ctx, cmd_line[1], f"Second argument of command '{cmd_line[0]}' should be an integer") if num is None: - return + return None if num < 0: - result = '\n'.join(result[:len(result) + num]) + result = '\n'.join(result_list[:len(result_list) + num]) else: - result = '\n'.join(result[num:]) + result = '\n'.join(result_list[num:]) await Command.send_message(execution_ctx, result) return result @@ -309,7 +309,7 @@ async def _countlines(self, cmd_line: List[str], execution_ctx: ExecutionContext """Count amount of lines Example: !countlines some text""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None result = str(len(' '.join(cmd_line).split('\n'))) await Command.send_message(execution_ctx, result) return result @@ -318,7 +318,7 @@ async def _tolower(self, cmd_line: List[str], execution_ctx: ExecutionContext) - """Convert text to lower case Example: !tolower SoMe TeXt""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None result = ' '.join(cmd_line[1:]).lower() await Command.send_message(execution_ctx, result) return result @@ -327,7 +327,7 @@ async def _toupper(self, cmd_line: List[str], execution_ctx: ExecutionContext) - """Convert text to upper case Example: !toupper SoMe TeXt""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None result = ' '.join(cmd_line[1:]).upper() await Command.send_message(execution_ctx, result) return result @@ -336,7 +336,7 @@ async def _join(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> O """Join words with string as separator Example: !join + 1 2 3 -> 1+2+3""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3): - return + return None separator = cmd_line[1] result = separator.join(cmd_line[2:]) await Command.send_message(execution_ctx, result) @@ -346,7 +346,7 @@ async def _eqwords(self, cmd_line: List[str], execution_ctx: ExecutionContext) - """Check if two words are equal or not Example: !eqwords a b""" if not await Command.check_args_count(execution_ctx, cmd_line, min=3, max=3): - return + return None result = "true" if cmd_line[1] == cmd_line[2] else "false" await Command.send_message(execution_ctx, result) return result @@ -355,7 +355,7 @@ async def _eqstrs(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> """Check if two strings separated by ';' are equal or not Example: !eqstrs a;b""" if not await Command.check_args_count(execution_ctx, cmd_line, min=2): - return + return None options = ' '.join(cmd_line[1:]).split(';') if len(options) < 2: return await Command.send_message(execution_ctx, "Too few options to compare") diff --git a/src/cmd/time.py b/src/cmd/time.py index 8f913157..35b84304 100644 --- a/src/cmd/time.py +++ b/src/cmd/time.py @@ -31,7 +31,7 @@ async def _time(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> O !time America/New_York Full timezone database list: """ if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=2): - return + return None timezone = None if len(cmd_line) == 2: timezone = tz.gettz(cmd_line[1]) @@ -50,7 +50,7 @@ async def _tz(self, cmd_line: List[str], execution_ctx: ExecutionContext) -> Non """Get current timezone Usage: !tz""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=1): - return + return None local_tz = Time().now().astimezone().tzinfo await Command.send_message(execution_ctx, f"{local_tz}") @@ -61,7 +61,7 @@ async def _setusertz(self, cmd_line: List[str], execution_ctx: ExecutionContext) !setusertz Europe/Moscow !setusertz America/New_York""" if not await Command.check_args_count(execution_ctx, cmd_line, min=1, max=2): - return + return None timezone = None if len(cmd_line) == 2: timezone = tz.gettz(cmd_line[1]) diff --git a/src/cmdarg_parser.py b/src/cmdarg_parser.py index 1b937999..63eba7c7 100644 --- a/src/cmdarg_parser.py +++ b/src/cmdarg_parser.py @@ -25,7 +25,7 @@ def __init__(self, execution_ctx: ExecutionContext, *args, **kwargs) -> None: self._execution_ctx = execution_ctx self._error = False - def parse_args(self, cmd_line: List[str]) -> Optional[argparse.Namespace]: + def parse_args(self, cmd_line: List[str]) -> Optional[argparse.Namespace]: # type:ignore """Use cmd_line (required argument) as an input. Original argparse method is hidden. Return parsed args in Namespace class. @@ -40,13 +40,15 @@ def parse_args(self, cmd_line: List[str]) -> Optional[argparse.Namespace]: return None return result - def error(self, message) -> None: + def error(self, message) -> None: # type:ignore + # This function should not perform sys.exit """Original argparse method is hidden. Send error to text channel""" bc.discord.background_loop.run_until_complete(self._execution_ctx.send_message(message)) self._error = True - def exit(self, status=0, message=None) -> None: + def exit(self, status=0, message=None) -> None: # type:ignore + # This function should not perform sys.exit """Original argparse method is hidden. Ignore argparse exit calls""" pass diff --git a/src/db/walbot_db.py b/src/db/walbot_db.py index 7268b0d8..3d1ab25e 100644 --- a/src/db/walbot_db.py +++ b/src/db/walbot_db.py @@ -2,9 +2,9 @@ import os from typing import Optional -import pymongo -from pymongo.collection import Collection -from pymongo.errors import ServerSelectionTimeoutError +import pymongo # type:ignore +from pymongo.collection import Collection # type:ignore +from pymongo.errors import ServerSelectionTimeoutError # type:ignore from src.log import log diff --git a/src/emoji.py b/src/emoji.py index 2486825d..fcf4f62d 100644 --- a/src/emoji.py +++ b/src/emoji.py @@ -155,7 +155,7 @@ def get_clock_emoji(time: str) -> Optional[str]: r = const.TIME_24H_REGEX.match(time) if r is None: - return + return None hours, minutes = (int(x) for x in r.groups()) if minutes < 15: minutes = 0 diff --git a/src/executor.py b/src/executor.py index 4a227356..70da2491 100644 --- a/src/executor.py +++ b/src/executor.py @@ -16,7 +16,7 @@ class Executor: def __init__(self) -> None: - self.commands = {} + self.commands: Dict[str, Any] = {} self.binders: Dict[const.BotBackend, CommandBinding] = {} def register_command(self, cmd_name: str, command: Command) -> None: @@ -39,10 +39,10 @@ def load_commands(self) -> None: obj[1] for obj in inspect.getmembers(commands_file, inspect.isclass) if (obj[1].__module__ == module) and issubclass(obj[1], BaseCmd)] if len(commands) == 1: - commands = commands[0] - if "bind" in [func[0] for func in inspect.getmembers(commands, inspect.isfunction) + commands_class = commands[0] + if "bind" in [func[0] for func in inspect.getmembers(commands_class, inspect.isfunction) if not func[0].startswith('_')]: - self.add_module(commands()) + self.add_module(commands_class()) else: log.error(f"Class '{commands.__name__}' does not have bind() function") elif len(commands) > 1: @@ -73,7 +73,7 @@ def store_persistent_state(self, executor_config: Dict[str, Any]): executor_config["custom_commands"][command.command_name] = command def export_help(self, platform: SupportedPlatforms): - modules = defaultdict(dict) + modules: Dict[str, Dict[str, Any]] = defaultdict(dict) for cmd_name, command in self.commands.items(): if command.supported_platforms & platform: modules[command.module_name][cmd_name] = (command.description or "").strip() diff --git a/src/info.py b/src/info.py index 69125c61..69f9738d 100644 --- a/src/info.py +++ b/src/info.py @@ -19,7 +19,7 @@ def _get_repo(self) -> Optional[git.Repo]: try: return git.Repo(search_parent_directories=True) except git.exc.InvalidGitRepositoryError: - return + return None @property def version(self) -> str: @@ -37,7 +37,7 @@ def commit_name(self) -> str: if repo is None: return "" commit = repo.head.commit.message.splitlines()[0].strip() - return commit + return str(commit) @property def branch_name(self) -> str: diff --git a/src/launcher.py b/src/launcher.py index 804b58c0..65520052 100644 --- a/src/launcher.py +++ b/src/launcher.py @@ -14,8 +14,9 @@ import time import zipfile from types import FrameType +from typing import List -import nest_asyncio +import nest_asyncio # type:ignore import psutil from src import const @@ -36,9 +37,9 @@ class Launcher: def _get_argparser(self) -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description='WalBot', formatter_class=argparse.RawTextHelpFormatter) - subparsers = parser.add_subparsers(dest="action") + subparsers_obj = parser.add_subparsers(dest="action") subparsers = { - cmd: subparsers.add_parser( + cmd: subparsers_obj.add_parser( cmd, help=getattr(self, cmd).__doc__, formatter_class=argparse.RawTextHelpFormatter) for cmd in list(filter(lambda _: not _.startswith('_') and not _.startswith('launch_'), dir(self))) } @@ -221,7 +222,7 @@ def start(self, main_bot: bool = True) -> const.ExitStatus: """Start the bot""" if main_bot and self.args.autoupdate: return self.autoupdate() - self.backends = [] + self.backends: List[BotInstance] = [] self._read_configs(main_bot) bc.executor.load_commands() if not self.args.fast_start: @@ -274,6 +275,7 @@ def _stop_bot_process(self, _, main_bot: bool = True) -> const.ExitStatus: log.error("Could not stop the bot (cache file does not exist)") return const.ExitStatus.GENERAL_ERROR bot_cache = BotCache(main_bot).parse() + assert bot_cache is not None, "bot_cache is None" pid = bot_cache["pid"] if pid is None: log.error("Could not stop the bot (cache file does not contain pid)") diff --git a/src/mail.py b/src/mail.py index 62a7c727..daf1614e 100644 --- a/src/mail.py +++ b/src/mail.py @@ -11,7 +11,6 @@ class Mail: def __init__(self, secret_config: SecretConfig) -> None: - self.secrets = None if not all(secret_config.mail.values()): log.error("Email service cannot be initialized. Fill all mail fields in secret.yaml") return diff --git a/src/markov.py b/src/markov.py index cc10a768..f5a1407e 100644 --- a/src/markov.py +++ b/src/markov.py @@ -1,9 +1,9 @@ import random import re from enum import IntEnum -from typing import List, Optional, Set +from typing import Any, Dict, List, Optional, Set -import yaml +import yaml # type:ignore from src import const from src.ff import FF @@ -13,6 +13,7 @@ class MarkovNode: def __init__(self, node_type, word: str = None): self.type = node_type self.word = word + self.next: Dict[Any, int] if not FF.is_enabled("WALBOT_FEATURE_MARKOV_MONGO"): self.next = {None: 0} else: @@ -94,7 +95,8 @@ def find_words(self, regex: str) -> List[str]: def get_next_words_list(self, word: str) -> List[str]: if word not in self.model.keys(): return [] - return sorted(self.model[word].next.items(), key=lambda x: -x[1]) + # TODO: investigate typing issue + return sorted(self.model[word].next.items(), key=lambda x: -x[1]) # type:ignore def generate(self, word: str = "") -> str: if word not in self.model.keys(): @@ -123,7 +125,7 @@ def generate(self, word: str = "") -> str: self.chains_generated += 1 return result - def collect_garbage(self, node: Optional[MarkovNode] = None) -> Set[str]: + def collect_garbage(self, node: Optional[MarkovNode] = None) -> Set[Any]: if not node: node = self.model[""] was = {node} @@ -137,7 +139,7 @@ def collect_garbage(self, node: Optional[MarkovNode] = None) -> Set[str]: if hasattr(node, "word"): result.append(node.word) del self.model[node.word] - return result + return set(result) return was def serialize(self, filename: str, dumper: type = yaml.Dumper) -> None: @@ -269,7 +271,7 @@ def generate(self, word: str = "__markov_null") -> str: return result def collect_garbage(self, node: Optional[MarkovNode] = None) -> Set[str]: - pass + return set() def serialize(self, filename: str, dumper: type = yaml.Dumper) -> None: pass diff --git a/src/plugin.py b/src/plugin.py index cf2c298e..2a919ee7 100644 --- a/src/plugin.py +++ b/src/plugin.py @@ -68,8 +68,7 @@ def _process_module(self, module: Any, reload: bool = True): self._handle_register_error( module, f"Failed to register plugin '{p.get_classname()}'. Error: {e}") else: - self._handle_register_error( - module, f"Class '{p.get_classname()}' does comply with BasePlugin interface") + self._handle_register_error(module, "Class does comply with BasePlugin interface") elif len(plugins) > 1: self._handle_register_error( module, f"Module '{module}' have more than 1 class in it") diff --git a/src/plugins/vq.py b/src/plugins/vq.py index 381f7cf9..3f88ffc0 100644 --- a/src/plugins/vq.py +++ b/src/plugins/vq.py @@ -4,10 +4,10 @@ import urllib.parse from collections import deque from dataclasses import dataclass -from typing import List +from typing import Any, Dict, List import discord -import yt_dlp +import yt_dlp # type:ignore from src import const from src.api.command import BaseCmd, Command, SupportedPlatforms @@ -92,7 +92,7 @@ async def print_yt_info(execution_ctx: ExecutionContext, video_url: str, full_de r = const.YT_VIDEO_REGEX.match(video_url) if r is None: return await Command.send_message(execution_ctx, "Please, provide valid YT link") - ydl_opts = { + ydl_opts: Dict[Any, Any] = { } try: with yt_dlp.YoutubeDL(ydl_opts) as ydl: diff --git a/src/shell.py b/src/shell.py index 3a04e6a3..d74b2562 100644 --- a/src/shell.py +++ b/src/shell.py @@ -76,9 +76,9 @@ async def run_and_send_stdout( result = await Shell.run_async(cmd_line, cwd=cwd, shell=True) if result.exit_code == -1: await execution_ctx.send_message("") - return + return None if result.exit_code != 0: await execution_ctx.send_message(f"") - return + return None await execution_ctx.send_message(result.stdout) return result.stdout diff --git a/src/utils.py b/src/utils.py index 5365b314..54b598e2 100644 --- a/src/utils.py +++ b/src/utils.py @@ -118,18 +118,18 @@ def cut_string(string: str, length: int) -> str: return string class proxy: - def http() -> Optional[str]: + def http() -> str: """Get HTTP proxy from environment""" return ( os.environ.get("http_proxy") or - os.environ.get("HTTP_PROXY") + os.environ.get("HTTP_PROXY") or "" ) - def https() -> Optional[str]: + def https() -> str: """Get HTTPS proxy from environment""" return ( os.environ.get("https_proxy") or - os.environ.get("HTTPS_PROXY") + os.environ.get("HTTPS_PROXY") or "" ) class request: @@ -189,6 +189,6 @@ def now(self) -> datetime.datetime: return datetime.datetime.now(tz=self._tz).replace(microsecond=0) -def null(*args, **kwargs): +def null(*args, **kwargs) -> None: """Drop return value""" - return + return None diff --git a/tests/fixtures/context.py b/tests/fixtures/context.py index d54d064c..afe99fb7 100644 --- a/tests/fixtures/context.py +++ b/tests/fixtures/context.py @@ -25,8 +25,8 @@ def disable_pings(self, message: str) -> str: def message_author(self) -> str: return "" - def message_author_id(self) -> int: - return 0 + def message_author_id(self) -> str: + return "0" def channel_name(self) -> str: return "console" diff --git a/tests/util_tests.py b/tests/util_tests.py index 907636d7..ee43fb39 100644 --- a/tests/util_tests.py +++ b/tests/util_tests.py @@ -1,6 +1,6 @@ import asyncio -import pytest +import pytest # type:ignore from src.utils import Util from tests.fixtures.context import BufferTestExecutionContext diff --git a/tools/mexplorer.py b/tools/mexplorer.py index 7af297b5..42795195 100644 --- a/tools/mexplorer.py +++ b/tools/mexplorer.py @@ -16,8 +16,9 @@ def start(self) -> int: self._process(command.split()) except (EOFError, KeyboardInterrupt): self._quit(None) + return 0 - def _process(self, cmd: str): + def _process(self, cmd: str) -> None: if len(cmd) == 0: pass elif cmd[0] in ("?", "help"): @@ -31,7 +32,7 @@ def _process(self, cmd: str): else: print(f"Unknown command {cmd[0]}") - def _help(self, _): + def _help(self, _) -> None: print("- help : print this message") print("- next : print list of next words") print("- words : list of words in Markov model")