193 lines
6 KiB
Python
193 lines
6 KiB
Python
import configparser
|
|
import datetime
|
|
import logging
|
|
from typing import Callable, List, Optional
|
|
|
|
from .client import Client
|
|
from .command import *
|
|
from .message import LiveMessage, Message
|
|
from .room import Room
|
|
from .util import *
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
__all__ = ["Bot", "BotConstructor"]
|
|
|
|
class Bot(Client):
|
|
ALIASES: List[str] = []
|
|
|
|
PING_REPLY: str = "Pong!"
|
|
HELP_GENERAL: Optional[str] = None
|
|
HELP_SPECIFIC: Optional[List[str]] = None
|
|
KILL_REPLY: str = "/me dies"
|
|
RESTART_REPLY: str = "/me restarts"
|
|
|
|
GENERAL_SECTION = "general"
|
|
ROOMS_SECTION = "rooms"
|
|
|
|
def __init__(self,
|
|
config: configparser.ConfigParser,
|
|
config_file: str,
|
|
) -> None:
|
|
self.config = config
|
|
self.config_file = config_file
|
|
|
|
nick = self.config[self.GENERAL_SECTION].get("nick")
|
|
if nick is None:
|
|
logger.warn(("'nick' not set in config file. Defaulting to empty"
|
|
" nick"))
|
|
nick = ""
|
|
|
|
cookie_file = self.config[self.GENERAL_SECTION].get("cookie_file")
|
|
if cookie_file is None:
|
|
logger.warn(("'cookie_file' not set in config file. Using no cookie"
|
|
" file."))
|
|
|
|
super().__init__(nick, cookie_file=cookie_file)
|
|
|
|
self._commands: List[Command] = []
|
|
|
|
self.start_time = datetime.datetime.now()
|
|
|
|
def save_config(self) -> None:
|
|
with open(self.config_file, "w") as f:
|
|
self.config.write(f)
|
|
|
|
async def started(self) -> None:
|
|
for room, password in self.config[self.ROOMS_SECTION].items():
|
|
if password is None:
|
|
await self.join(room)
|
|
else:
|
|
await self.join(room, password=password)
|
|
|
|
# Registering commands
|
|
|
|
def register(self, command: Command) -> None:
|
|
self._commands.append(command)
|
|
|
|
def register_general(self,
|
|
name: str,
|
|
cmdfunc: GeneralCommandFunction,
|
|
args: bool = True
|
|
) -> None:
|
|
command = GeneralCommand(name, cmdfunc, args)
|
|
self.register(command)
|
|
|
|
def register_specific(self,
|
|
name: str,
|
|
cmdfunc: SpecificCommandFunction,
|
|
args: bool = True
|
|
) -> None:
|
|
command = SpecificCommand(name, cmdfunc, args)
|
|
self.register(command)
|
|
|
|
# Processing commands
|
|
|
|
async def process_commands(self,
|
|
room: Room,
|
|
message: LiveMessage,
|
|
aliases: List[str] = []
|
|
) -> None:
|
|
nicks = [room.session.nick] + aliases
|
|
data = CommandData.from_string(message.content)
|
|
|
|
if data is not None:
|
|
logger.debug(f"Processing command from {message.content!r}")
|
|
for command in self._commands:
|
|
await command.run(room, message, nicks, data)
|
|
|
|
async def on_send(self, room: Room, message: LiveMessage) -> None:
|
|
await self.process_commands(room, message, aliases=self.ALIASES)
|
|
|
|
# Help util
|
|
|
|
def format_help(self, room: Room, lines: List[str]) -> str:
|
|
text = "\n".join(lines)
|
|
params = {
|
|
"nick": room.session.nick,
|
|
"mention": room.session.mention,
|
|
"atmention": room.session.atmention,
|
|
}
|
|
return text.format(**params)
|
|
|
|
# Botrulez
|
|
|
|
def register_botrulez(self,
|
|
ping: bool = True,
|
|
help_: bool = True,
|
|
uptime: bool = True,
|
|
kill: bool = False,
|
|
restart: bool = False,
|
|
) -> None:
|
|
if ping:
|
|
self.register_general("ping", self.cmd_ping, args=False)
|
|
self.register_specific("ping", self.cmd_ping, args=False)
|
|
|
|
if help_:
|
|
if self.HELP_GENERAL is None and self.HELP_SPECIFIC is None:
|
|
logger.warn(("HELP_GENERAL and HELP_SPECIFIC are None, but the"
|
|
" help command is enabled"))
|
|
self.register_general("help", self.cmd_help_general, args=False)
|
|
self.register_specific("help", self.cmd_help_specific, args=False)
|
|
|
|
if uptime:
|
|
self.register_specific("uptime", self.cmd_uptime, args=False)
|
|
|
|
if kill:
|
|
self.register_specific("kill", self.cmd_kill, args=False)
|
|
|
|
if restart:
|
|
self.register_specific("restart", self.cmd_restart, args=False)
|
|
|
|
async def cmd_ping(self,
|
|
room: Room,
|
|
message: LiveMessage,
|
|
args: ArgumentData
|
|
) -> None:
|
|
await message.reply(self.PING_REPLY)
|
|
|
|
async def cmd_help_general(self,
|
|
room: Room,
|
|
message: LiveMessage,
|
|
args: ArgumentData
|
|
) -> None:
|
|
if self.HELP_GENERAL is not None:
|
|
await message.reply(self.format_help(room, [self.HELP_GENERAL]))
|
|
|
|
async def cmd_help_specific(self,
|
|
room: Room,
|
|
message: LiveMessage,
|
|
args: SpecificArgumentData
|
|
) -> None:
|
|
if self.HELP_SPECIFIC is not None:
|
|
await message.reply(self.format_help(room, self.HELP_SPECIFIC))
|
|
|
|
async def cmd_uptime(self,
|
|
room: Room,
|
|
message: LiveMessage,
|
|
args: SpecificArgumentData
|
|
) -> None:
|
|
time = format_time(self.start_time)
|
|
delta = format_delta(datetime.datetime.now() - self.start_time)
|
|
text = f"/me has been up since {time} UTC ({delta})"
|
|
await message.reply(text)
|
|
|
|
async def cmd_kill(self,
|
|
room: Room,
|
|
message: LiveMessage,
|
|
args: SpecificArgumentData
|
|
) -> None:
|
|
logger.info(f"Killed in &{room.name} by {message.sender.atmention}")
|
|
await message.reply(self.KILL_REPLY)
|
|
await self.part(room)
|
|
|
|
async def cmd_restart(self,
|
|
room: Room,
|
|
message: LiveMessage,
|
|
args: SpecificArgumentData
|
|
) -> None:
|
|
logger.info(f"Restarted in &{room.name} by {message.sender.atmention}")
|
|
await message.reply(self.RESTART_REPLY)
|
|
await self.stop()
|
|
|
|
BotConstructor = Callable[[configparser.ConfigParser, str], Bot]
|