Add graceful exits

This sets up a set of ropes and pulleys that signal the `Bot.keepalive`
function to clean things up after a quit signal has been sent. This
allows plugins to define an `on_unload` function to save any important
datas on intentional exit.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2022-05-24 19:16:15 -07:00
parent ffb2d4204e
commit 82e50f86d6
3 changed files with 28 additions and 13 deletions

View File

@@ -1,5 +1,7 @@
import asyncio import asyncio
from functools import partial
import logging import logging
import signal
from .config import ServerConfig from .config import ServerConfig
from .bot import Bot from .bot import Bot
@@ -10,15 +12,18 @@ logging.basicConfig(
) )
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.debug("Loading config")
config = ServerConfig()
config.load("config.toml")
log.debug("Using configuration: %s", config)
async def main(): bot = Bot(config)
log.debug("Loading config")
config = ServerConfig()
config.load("config.toml")
log.debug("Using configuration: %s", config)
server = Bot(config) try:
await server.run() asyncio.run(bot.run())
except KeyboardInterrupt:
log.info("Got ctrl-c")
asyncio.run(main()) finally:
log.info("Quitting, press ctrl-c to quit immediately")
bot.quit()
asyncio.run(bot.keepalive())

View File

@@ -22,6 +22,7 @@ class Bot:
] ]
# TODO - this may not be needed # TODO - this may not be needed
self.__channels: Set[str] = set() self.__channels: Set[str] = set()
self.__quitting = asyncio.Event()
@property @property
def server_config(self) -> ServerConfig: def server_config(self) -> ServerConfig:
@@ -31,6 +32,9 @@ class Bot:
def plugins(self) -> Sequence[plugin.Plugin]: def plugins(self) -> Sequence[plugin.Plugin]:
return self.__plugins return self.__plugins
def quit(self):
self.__quitting.set()
def channel_plugins(self, channel: str) -> Sequence[plugin.Plugin]: def channel_plugins(self, channel: str) -> Sequence[plugin.Plugin]:
return [plugin for plugin in self.plugins if channel in plugin.channels] return [plugin for plugin in self.plugins if channel in plugin.channels]
@@ -127,6 +131,9 @@ class Bot:
async def keepalive(self): async def keepalive(self):
# loop while we're connected, check every second # loop while we're connected, check every second
log.info("Starting keepalive loop") await self.__quitting.wait()
while self.connection.connected: log.info("Shutting down gracefully")
await asyncio.sleep(1.0) # TODO: unload modules
await asyncio.gather(
*[plugin.on_unload(self.connection) for plugin in self.plugins]
)

View File

@@ -51,6 +51,9 @@ class Plugin:
async def on_message(self, conn: IrcProtocol, channel: str, who: Prefix, line: str): async def on_message(self, conn: IrcProtocol, channel: str, who: Prefix, line: str):
pass pass
async def on_unload(self, conn: IrcProtocol):
pass
def load_plugin(server_config: ServerConfig, plugin_config: PluginConfig) -> Plugin: def load_plugin(server_config: ServerConfig, plugin_config: PluginConfig) -> Plugin:
name = plugin_config["module"] name = plugin_config["module"]