bot/ircbot.py

115 lines
3.2 KiB
Python
Executable file

import asyncio
import os
import sys
import pydle
from discord_webhook import DiscordEmbed
import commands as botcommands
class DiscordAuthor:
def __init__(self, nick):
self.nick = nick
self.global_name = nick
class DiscordContext:
def __init__(self, bot, target, source, message):
self._bot = bot
self._target = target
self._source = source
self._message = message
self.author = DiscordAuthor(source)
def typing(self):
class NilTyping:
async def __aenter__(self):
return self
async def __aexit__(self, *exc):
return None
return NilTyping()
async def reply(self, m):
lines = m.strip().split("\n")
lines = [f"{self._source}: {line}" for line in lines]
await self._bot.message(self._target, "\n".join(lines))
class DiscordImplBot(pydle.Client):
def __init__(self, channel, nickname, *, push_messages=None, prefix="!"):
super().__init__(nickname, realname=nickname)
self._channel = channel
self._cmd_prefix = prefix
self._cmds = {}
self._push_messages = push_messages
self._push_messages_task = None
async def on_connect(self):
await self.join(self._channel)
if self._push_messages is not None:
self._push_messages_task = asyncio.create_task(self._handle_push_messages())
async def on_disconnect(self):
if self._push_messages_task is not None:
self._push_messages_task.cancel()
self._push_messages_task = None
async def _handle_push_messages(self):
async for message in self._push_messages():
if isinstance(message, DiscordEmbed):
await self.notice(self._channel, f"{message.title} {message.url}")
else:
await self.notice(self._channel, message)
async def on_message(self, target, source, message):
# Don't respond to our own messages, as this leads to a positive feedback loop.
if source == self.nickname:
return
if not message.startswith(self._cmd_prefix):
return
cmd_fn = self._cmds.get(message.removeprefix(self._cmd_prefix))
if not cmd_fn:
return
ctx = DiscordContext(self, target, source, message)
await cmd_fn(ctx)
# Discord API: Register a new command
def command(self, *, name=None, description=None):
def _reg_cmd(handler):
nonlocal name
name = name or handler.__name__
self._cmds[name] = handler
return _reg_cmd
def main(*, server, channel, nick, mqtt_host):
def push_messages():
return botcommands.run_events(mqtt_host)
bot = DiscordImplBot(
channel,
nick,
push_messages=push_messages,
)
botcommands.setup(bot, mqtt_host)
bot.run(server, tls=True)
if __name__ == "__main__":
mqtt_host = os.getenv("MQTT_HOST")
if not mqtt_host:
print("MQTT_HOST unset")
sys.exit(1)
main(
server="irc.libera.chat",
channel="#bitlair-bot-test",
nick="Bitlair",
mqtt_host=mqtt_host,
)