Add docstrings to Bot functions
This commit is contained in:
parent
eb9cc4f9bd
commit
6a15e1a948
2 changed files with 180 additions and 1 deletions
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
## Next version
|
||||
|
||||
Nothing yet
|
||||
- add docstrings to `Bot`
|
||||
- change `KILL_REPLY` and `RESTART_REPLY` to be optional in `Bot`
|
||||
|
||||
## 1.1.3 (2019-04-19)
|
||||
|
||||
|
|
|
|||
178
yaboli/bot.py
178
yaboli/bot.py
|
|
@ -14,6 +14,36 @@ logger = logging.getLogger(__name__)
|
|||
__all__ = ["Bot", "BotConstructor"]
|
||||
|
||||
class Bot(Client):
|
||||
"""
|
||||
A Bot is a Client that responds to commands and uses a config file to
|
||||
automatically set its nick and join rooms.
|
||||
|
||||
The config file is loaded as a ConfigParser by the run() or run_modulebot()
|
||||
functions and has the following structure:
|
||||
|
||||
A "general" section which contains:
|
||||
- nick - the default nick of the bot (set to the empty string if you don't
|
||||
want to set a nick)
|
||||
- cookie_file (optional) - the file the cookie should be saved in
|
||||
|
||||
A "rooms" section which contains a list of rooms that the bot should
|
||||
automatically join. This section is optional if you overwrite started().
|
||||
The room list should have the format "roomname" or "roomname = password".
|
||||
|
||||
A bot has the following attributes:
|
||||
- ALIASES - list of alternate nicks the bot responds to (see
|
||||
process_commands())
|
||||
- PING_REPLY - used by cmd_ping()
|
||||
- HELP_GENERAL - used by cmd_help_general()
|
||||
- HELP_SPECIFIC - used by cmd_help_specific()
|
||||
- KILL_REPLY - used by cmd_kill()
|
||||
- RESTART_REPLY - used by cmd_restart()
|
||||
- GENERAL_SECTION - the name of the "general" section in the config file
|
||||
(see above) (default: "general")
|
||||
- ROOMS_SECTION - the name of the "rooms" section in the config file (see
|
||||
above) (default: "rooms")
|
||||
"""
|
||||
|
||||
ALIASES: List[str] = []
|
||||
|
||||
PING_REPLY: str = "Pong!"
|
||||
|
|
@ -50,10 +80,26 @@ class Bot(Client):
|
|||
self.start_time = datetime.datetime.now()
|
||||
|
||||
def save_config(self) -> None:
|
||||
"""
|
||||
Save the current state of self.config to the file passed in __init__ as
|
||||
the config_file parameter.
|
||||
|
||||
Usually, this is the file that self.config was loaded from (if you use
|
||||
run or run_modulebot).
|
||||
"""
|
||||
|
||||
with open(self.config_file, "w") as f:
|
||||
self.config.write(f)
|
||||
|
||||
async def started(self) -> None:
|
||||
"""
|
||||
This Client function is overwritten in order to join all the rooms
|
||||
listed in the "rooms" section of self.config.
|
||||
|
||||
If you need to overwrite this function but want to keep the auto-join
|
||||
functionality, make sure to await super().started().
|
||||
"""
|
||||
|
||||
for room, password in self.config[self.ROOMS_SECTION].items():
|
||||
if password is None:
|
||||
await self.join(room)
|
||||
|
|
@ -63,6 +109,12 @@ class Bot(Client):
|
|||
# Registering commands
|
||||
|
||||
def register(self, command: Command) -> None:
|
||||
"""
|
||||
Register a Command (from the yaboli.command submodule).
|
||||
|
||||
Usually, you don't have to call this function yourself.
|
||||
"""
|
||||
|
||||
self._commands.append(command)
|
||||
|
||||
def register_general(self,
|
||||
|
|
@ -70,6 +122,23 @@ class Bot(Client):
|
|||
cmdfunc: GeneralCommandFunction,
|
||||
args: bool = True
|
||||
) -> None:
|
||||
"""
|
||||
Register a function as general bot command (i. e. no @mention of the
|
||||
bot nick after the !command). This function will be called by
|
||||
process_commands() when the bot encounters a matching command.
|
||||
|
||||
name - the name of the command (If you want your command to be !hello,
|
||||
the name is "hello".)
|
||||
|
||||
cmdfunc - the function that is called with the Room, LiveMessage and
|
||||
ArgumentData when the bot encounters a matching command
|
||||
|
||||
args - whether the command may have arguments (If set to False, the
|
||||
ArgumentData's has_args() function must also return False for the
|
||||
command function to be called. If set to True, all ArgumentData is
|
||||
valid.)
|
||||
"""
|
||||
|
||||
command = GeneralCommand(name, cmdfunc, args)
|
||||
self.register(command)
|
||||
|
||||
|
|
@ -78,6 +147,21 @@ class Bot(Client):
|
|||
cmdfunc: SpecificCommandFunction,
|
||||
args: bool = True
|
||||
) -> None:
|
||||
"""
|
||||
Register a function as specific bot command (i. e. @mention of the bot
|
||||
nick after the !command is required). This function will be called by
|
||||
process_commands() when the bot encounters a matching command.
|
||||
|
||||
name - the name of the command (see register_general() for an
|
||||
explanation)
|
||||
|
||||
cmdfunc - the function that is called with the Room, LiveMessage and
|
||||
SpecificArgumentData when the bot encounters a matching command
|
||||
|
||||
args - whether the command may have arguments (see register_general()
|
||||
for an explanation)
|
||||
"""
|
||||
|
||||
command = SpecificCommand(name, cmdfunc, args)
|
||||
self.register(command)
|
||||
|
||||
|
|
@ -88,6 +172,13 @@ class Bot(Client):
|
|||
message: LiveMessage,
|
||||
aliases: List[str] = []
|
||||
) -> None:
|
||||
"""
|
||||
If the message contains a command, call all matching command functions
|
||||
that were previously registered.
|
||||
|
||||
This function is usually called by the overwritten on_send() function.
|
||||
"""
|
||||
|
||||
nicks = [room.session.nick] + aliases
|
||||
data = CommandData.from_string(message.content)
|
||||
|
||||
|
|
@ -97,11 +188,31 @@ class Bot(Client):
|
|||
await command.run(room, message, nicks, data)
|
||||
|
||||
async def on_send(self, room: Room, message: LiveMessage) -> None:
|
||||
"""
|
||||
This Client function is overwritten in order to automatically call
|
||||
process_commands() with self.ALIASES.
|
||||
|
||||
If you need to overwrite this function, make sure to await
|
||||
process_commands() with self.ALIASES somewhere in your function, or
|
||||
await super().on_send().
|
||||
"""
|
||||
|
||||
await self.process_commands(room, message, aliases=self.ALIASES)
|
||||
|
||||
# Help util
|
||||
|
||||
def format_help(self, room: Room, lines: List[str]) -> str:
|
||||
"""
|
||||
Format a list of strings into a string, replacing certain placeholders
|
||||
with the actual values.
|
||||
|
||||
This function uses the str.format() function to replace the following:
|
||||
|
||||
- {nick} - the bot's current nick
|
||||
- {mention} - the bot's current nick, run through mention()
|
||||
- {atmention} - the bot's current nick, run through atmention()
|
||||
"""
|
||||
|
||||
text = "\n".join(lines)
|
||||
params = {
|
||||
"nick": room.session.nick,
|
||||
|
|
@ -119,6 +230,36 @@ class Bot(Client):
|
|||
kill: bool = False,
|
||||
restart: bool = False,
|
||||
) -> None:
|
||||
"""
|
||||
Register the commands necessary for the bot to conform to the botrulez
|
||||
(https://github.com/jedevc/botrulez). Also includes a few optional
|
||||
botrulez commands that are disabled by default.
|
||||
|
||||
- ping - register general and specific cmd_ping()
|
||||
- help_ - register cmd_help_general() and cmd_help_specific()
|
||||
- uptime - register specific cmd_uptime
|
||||
- kill - register specific cmd_kill (disabled by default)
|
||||
- uptime - register specific cmd_uptime (disabled by default)
|
||||
|
||||
All commands are registered with args=False.
|
||||
|
||||
If you want to implement your own versions of these commands, it is
|
||||
recommended that you set the respective argument to False in your call
|
||||
to register_botrulez(), overwrite the existing command functions or
|
||||
create your own, and then register them manually.
|
||||
|
||||
For help, that might look something like this, if you've written a
|
||||
custom specific help that takes extra arguments but are using the
|
||||
botrulez general help:
|
||||
|
||||
self.register_botrulez(help_=False)
|
||||
self.register_general("help", self.cmd_help_general, args=False)
|
||||
self.register_specific("help", self.cmd_help_custom)
|
||||
|
||||
In case you're asking, the help_ parameter has an underscore at the end
|
||||
so it doesn't overlap the help() function.
|
||||
"""
|
||||
|
||||
if ping:
|
||||
self.register_general("ping", self.cmd_ping, args=False)
|
||||
self.register_specific("ping", self.cmd_ping, args=False)
|
||||
|
|
@ -144,6 +285,10 @@ class Bot(Client):
|
|||
message: LiveMessage,
|
||||
args: ArgumentData
|
||||
) -> None:
|
||||
"""
|
||||
Reply with self.PING_REPLY.
|
||||
"""
|
||||
|
||||
await message.reply(self.PING_REPLY)
|
||||
|
||||
async def cmd_help_general(self,
|
||||
|
|
@ -151,6 +296,10 @@ class Bot(Client):
|
|||
message: LiveMessage,
|
||||
args: ArgumentData
|
||||
) -> None:
|
||||
"""
|
||||
Reply with self.HELP_GENERAL, if it is not None. Uses format_help().
|
||||
"""
|
||||
|
||||
if self.HELP_GENERAL is not None:
|
||||
await message.reply(self.format_help(room, [self.HELP_GENERAL]))
|
||||
|
||||
|
|
@ -159,6 +308,10 @@ class Bot(Client):
|
|||
message: LiveMessage,
|
||||
args: SpecificArgumentData
|
||||
) -> None:
|
||||
"""
|
||||
Reply with self.HELP_SPECIFIC, if it is not None. Uses format_help().
|
||||
"""
|
||||
|
||||
if self.HELP_SPECIFIC is not None:
|
||||
await message.reply(self.format_help(room, self.HELP_SPECIFIC))
|
||||
|
||||
|
|
@ -167,6 +320,15 @@ class Bot(Client):
|
|||
message: LiveMessage,
|
||||
args: SpecificArgumentData
|
||||
) -> None:
|
||||
"""
|
||||
Reply with the bot's uptime in the format specified by the botrulez.
|
||||
|
||||
This uses the time that the Bot was first started, not the time the
|
||||
respective Room was created. A !restart (see register_botrulez()) will
|
||||
reset the bot uptime, but leaving and re-joining a room or losing
|
||||
connection won't.
|
||||
"""
|
||||
|
||||
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})"
|
||||
|
|
@ -177,6 +339,13 @@ class Bot(Client):
|
|||
message: LiveMessage,
|
||||
args: SpecificArgumentData
|
||||
) -> None:
|
||||
"""
|
||||
Remove the bot from this room.
|
||||
|
||||
If self.KILL_REPLY is not None, replies with that before leaving the
|
||||
room.
|
||||
"""
|
||||
|
||||
logger.info(f"Killed in &{room.name} by {message.sender.atmention}")
|
||||
|
||||
if self.KILL_REPLY is not None:
|
||||
|
|
@ -189,6 +358,15 @@ class Bot(Client):
|
|||
message: LiveMessage,
|
||||
args: SpecificArgumentData
|
||||
) -> None:
|
||||
"""
|
||||
Restart the whole Bot.
|
||||
|
||||
This is done by stopping the Bot, since the run() or run_modulebot()
|
||||
functions start the Bot in a while True loop.
|
||||
|
||||
If self.RESTART_REPLY is not None, replies with that before restarting.
|
||||
"""
|
||||
|
||||
logger.info(f"Restarted in &{room.name} by {message.sender.atmention}")
|
||||
|
||||
if self.RESTART_REPLY is not None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue