diff --git a/csp_bot/bot.py b/csp_bot/bot.py index 53ed0d5..ec69f13 100644 --- a/csp_bot/bot.py +++ b/csp_bot/bot.py @@ -1,10 +1,10 @@ -import logging import re import threading import time from csv import reader from datetime import datetime, timedelta from io import StringIO +from logging import getLogger from types import MappingProxyType from typing import Any, Dict, List, Optional, Set, Tuple, Union @@ -29,6 +29,7 @@ BaseCommandModel, HelpCommand, ScheduleCommand, + StatusCommand, ) from .gateway import GatewayChannels, GatewayModule from .structs import ( @@ -39,7 +40,7 @@ ) from .utils import Backend -log = logging.getLogger(__name__) +log = getLogger(__name__) SLACK_ENTITY_REGEX = re.compile("<@.+?>") DISCORD_ENTITY_REGEX = re.compile("<@.+?>") @@ -469,10 +470,14 @@ def extract_bot_commands(self, message: Message, channel: str, text: str, entiti command_runner = self._commands[command] + # TODO generalize by interrogating the command signature if isinstance(command_runner, ScheduleCommand): # Special command, gets access to schedule of commands return command_runner.preexecute(command_instance, self._scheduled, self) - return self._commands[command].preexecute(command_instance) + elif isinstance(command_runner, StatusCommand): + # Special command, gets access to status of commands + return command_runner.preexecute(command_instance, self) + return command_runner.preexecute(command_instance) else: log.info(f"Defaulting to help command from message to bot with no command: {message}") command_runner = self._commands["help"] diff --git a/csp_bot/cli.py b/csp_bot/cli.py index aa0c78e..eee8d21 100644 --- a/csp_bot/cli.py +++ b/csp_bot/cli.py @@ -1,4 +1,4 @@ -import logging +from logging import getLogger from pprint import pprint import csp_gateway @@ -8,7 +8,7 @@ from csp_bot import __version__ -log = logging.getLogger(__name__) +log = getLogger(__name__) __all__ = ( "load", diff --git a/csp_bot/commands/__init__.py b/csp_bot/commands/__init__.py index e0e5821..0318efb 100644 --- a/csp_bot/commands/__init__.py +++ b/csp_bot/commands/__init__.py @@ -11,3 +11,4 @@ from .echo import EchoCommand, EchoCommandModel from .help import HelpCommand, HelpCommandModel from .schedule import ScheduleCommand, ScheduleCommandModel +from .status import StatusCommand, StatusCommandModel diff --git a/csp_bot/commands/echo.py b/csp_bot/commands/echo.py index 444e0c6..4c61f37 100644 --- a/csp_bot/commands/echo.py +++ b/csp_bot/commands/echo.py @@ -1,11 +1,11 @@ -import logging +from logging import getLogger from typing import Optional, Type from csp_bot.structs import BotCommand, Message from .base import BaseCommand, BaseCommandModel, ReplyToOtherCommand -log = logging.getLogger(__name__) +log = getLogger(__name__) class EchoCommand(ReplyToOtherCommand): diff --git a/csp_bot/commands/help.py b/csp_bot/commands/help.py index 0470155..4aff7b7 100644 --- a/csp_bot/commands/help.py +++ b/csp_bot/commands/help.py @@ -1,5 +1,5 @@ -import logging from html import escape, unescape +from logging import getLogger from typing import Mapping, Type import pandas as pd @@ -8,7 +8,7 @@ from .base import BaseCommand, BaseCommandModel, ReplyCommand -log = logging.getLogger(__name__) +log = getLogger(__name__) class HelpCommand(ReplyCommand): diff --git a/csp_bot/commands/schedule.py b/csp_bot/commands/schedule.py index 51343f8..8db5e64 100644 --- a/csp_bot/commands/schedule.py +++ b/csp_bot/commands/schedule.py @@ -1,5 +1,5 @@ -import logging from html import escape +from logging import getLogger from typing import TYPE_CHECKING, Mapping, Type from croniter import CroniterBadCronError, croniter @@ -12,7 +12,7 @@ if TYPE_CHECKING: from csp_bot import Bot -log = logging.getLogger(__name__) +log = getLogger(__name__) class ScheduleCommand(ReplyCommand): diff --git a/csp_bot/commands/status.py b/csp_bot/commands/status.py new file mode 100644 index 0000000..2af86e2 --- /dev/null +++ b/csp_bot/commands/status.py @@ -0,0 +1,69 @@ +from datetime import datetime +from getpass import getuser +from logging import getLogger +from socket import gethostname +from threading import active_count +from typing import TYPE_CHECKING, Optional, Type + +import psutil + +from csp_bot.structs import BotCommand, Message + +from .base import BaseCommand, BaseCommandModel, ReplyCommand + +if TYPE_CHECKING: + from csp_bot import Bot + +log = getLogger(__name__) + +_HOSTNAME = gethostname() +_USER = getuser() + + +class StatusCommand(ReplyCommand): + def command(self) -> str: + return "status" + + def name(self) -> str: + return "status" + + def help(self) -> str: + return "System information. Syntax: /status [/channel ]" + + def preexecute(self, command: BotCommand, bot_instance: "Bot") -> BotCommand: + # TODO: pull out everything except auth keys? + self._adapters = list(bot_instance._adapters.keys()) + return command + + def execute(self, command: BotCommand) -> Optional[Message]: + log.info(f"Status command: {command}") + + message = "" + + # Time information + message += f"Now\n\t{datetime.utcnow()}\n" + + # Adapter information + message += f"Backends:\n\t{', '.join(self._adapters)}\n" + + # Machine information + message += f"CPU\n\t{psutil.cpu_percent()}\n" + message += f"Memory\n\t{psutil.virtual_memory().percent}\n" + message += f"Memory Available\n\t{round(psutil.virtual_memory().available * 100 / psutil.virtual_memory().total, 2)}\n" + message += f"Host\n\t{_HOSTNAME}\n" + message += f"User\n\t{_USER}\n" + + # Process and thread information + current_process = psutil.Process() + message += f"PID\n\t{current_process.pid}\n" + message += f"Active Threads\n\t{active_count()}\n" + + return Message( + msg=message, + channel=command.channel, + backend=command.backend, + ) + + +class StatusCommandModel(BaseCommandModel): + command: Type[BaseCommand] = StatusCommand diff --git a/csp_bot/config/__init__.py b/csp_bot/config/__init__.py index 2c704bb..a4d7cfd 100644 --- a/csp_bot/config/__init__.py +++ b/csp_bot/config/__init__.py @@ -1,4 +1,4 @@ -import logging +from logging import getLogger from pathlib import Path from typing import List, Optional @@ -6,7 +6,7 @@ from csp_bot import __version__ -log = logging.getLogger(__name__) +log = getLogger(__name__) __all__ = ("load_config",) diff --git a/csp_bot/config/commands.yaml b/csp_bot/config/commands.yaml index 127531e..7a92627 100644 --- a/csp_bot/config/commands.yaml +++ b/csp_bot/config/commands.yaml @@ -6,3 +6,6 @@ echo: schedule: _target_: csp_bot.commands.ScheduleCommandModel + +status: + _target_: csp_bot.commands.StatusCommandModel diff --git a/csp_bot/config/conf.yaml b/csp_bot/config/conf.yaml index 88fc347..a0d3718 100644 --- a/csp_bot/config/conf.yaml +++ b/csp_bot/config/conf.yaml @@ -28,6 +28,7 @@ gateway: - /commands/help - /commands/echo - /commands/schedule + - /commands/status settings: PORT: ${port} AUTHENTICATE: ${authenticate} diff --git a/csp_bot/gateway/gateway.py b/csp_bot/gateway/gateway.py index ec3c4c3..bfe4f05 100644 --- a/csp_bot/gateway/gateway.py +++ b/csp_bot/gateway/gateway.py @@ -1,5 +1,5 @@ -import logging from functools import wraps +from logging import getLogger from typing import List from csp import ts @@ -16,7 +16,7 @@ from csp_bot.commands import BaseCommandModel from csp_bot.structs import BotCommand, Message -log = logging.getLogger(__name__) +log = getLogger(__name__) class GatewayChannels(GatewayChannelsBase):