4.6. Struktur des Revolt Bots

Über das graphische Interface von Revolt wird ein Bottoken generiert. Dieser Bottoken dient dem Bot als Zugang zum zugehörigen Nutzeraccount. Um den BotToken dem Code verfügbar zu machen wird er in der Umgebungsvariable BOT_TOKEN gespeichert. Dieser Nutzeraccount kann wieder über das graphische Interface von Revolt einem Server hinzugefügt werden. Wird der Bot auf einem Host gestarted ist dieser über seinen Nutzeraccount online.

async def main():
    bot_token = os.getenv('BOT_TOKEN')
    if not bot_token:
        print("BOT_TOKEN environment not set, exiting")
        sys.exit(1)
    async with aiohttp.ClientSession() as session:
        print("Session started, trying to start Client")
        client = Client(session, bot_token)
        print("Waiting for Client to start")
        await client.start()
        signal.signal(signal.SIGINT, lambda sig,frame: client.stop())
    signal.pause()


asyncio.run(main())

Wird eine Nachricht in einen Textkanal in einem Server, dem der Bot zugehörig ist, versendet, empfängt der Bot diese auch. Diese Nachrichten werden behandelt und falls es ein gültiges Kommando war wird eine Antwort in den Textkanal der Nachricht geschickt. Enthätlt die Antwort ein Bild wird dieses temporär auf dem Host gespeichert.

class Client(revolt.Client):
    async def on_message(self, message: revolt.Message):
        if(message.author.id == self.user.id):
            return
        command: BotCommand
        message_text: str = message.content.strip()
        if(message_text.startswith('qr!')): 
            message_text = message_text.removeprefix('qr!')
            message_parameters = [param for param in message_text.split(' ') if param]
            try: 
                command = BotCommand(message_parameters)
                response = command_handler(command)
            except (BotCommandError) as error:
                await message.channel.send(f'Error: {error}')
                await message.channel.send(help_page())
                raise error
            if (response.response_file == None):
                await message.channel.send(content=response.response_text)
            else:
                hash = hashlib.sha256()
                hash.update(" ".join(command.args).encode("utf-8"))
                os.makedirs(Path("/tmp/qr_scanner"),exist_ok=True)
                outfile = Path(f"/tmp/qr_scanner/{hash.hexdigest()}.png")
                response.response_file.save(outfile)
                file = revolt.File(file=str(outfile),filename='qr_code.png')
                await message.channel.send(content=response.response_text, attachments=[file])

Die Nachrichten werden darauf geprüft ob sie das für den Bot definierte Präfix(qr!) verwenden. Verwendet die Nachricht das Präfix und wurde nicht vom Bot selbst versendet wird der Rest der Nachricht nach den zulässigen Kommandos untersucht und in ein Objekt gespeichert.

class BotCommandType(Enum):
    HELP='help'
    LINK='link'
    TEXT='text'


class BotCommand:
    def __init__(self,parameters: list[str]):
        if not parameters: 
            raise BotCommandError(f'empty command')
        try:
            self.command_type = BotCommandType(parameters[0])
        except ValueError:
            raise BotCommandError(f'No command named: {parameters[0]}')
        self.args: list[str] = parameters[1:] if len(parameters)>1 else []

Das dem Bot gegebene Kommando wird dem vordefinierten Kommando Typ nach behandelt und das Ergebnis in ein Object gespeichert. Dieses Ergebnis Object enthält die Daten die am Ende versendet werden.

class BotResponse:
    def __init__(self, response_text:str, response_file: Image.Image):
        self.response_text = response_text
        self.response_file = response_file


def command_handler(command:BotCommand):
    match command.command_type:
        case BotCommandType.LINK:
            return link_handler(command.args)
        case BotCommandType.TEXT:
            return text_handler(command.args)
        case BotCommandType.HELP:
            return BotResponse(help_page(),None)

Ein Kommando vom Typ Link wandelt den in der Nachricht mitgegebenen Link in einen QR-Code um und versendet ihn als Bild.

def link_handler(link_args):
    if(len(link_args) <= 0):
        raise BotCommandError(f'No Link given')
    qrcode_generator = QRCodeGenerator()
    image = qrcode_generator.generate_qr_code(link_args[0])
    return BotResponse(f'QR-code generated from link: {link_args[0]}', image)

Ein Kommando vom Typ Text speichert den in der Nachricht mitgegebnen Text auf einen Dateiserver und wandelt den Link zur Texdatei in einen Qr-Code um und versendet ihn als Bild.

def text_handler(text_args):
    if(len(text_args) <= 0):
        raise BotCommandError(f'No Text given')
    text = ' '.join(text_args)
    data = {'string': f'<hl>{text}<hl>'}
    remote_qr = json.loads("{\"success\" : false}")
    #Connect to King-Nikita's Server
    server_request = ServerRequest('https://hs.nokt.is/qr/generate/')
    response_text = server_request.send_request(params=data)
    try:
        remote_qr = json.loads(response_text)
    except json.JSONDecodeError:
        print('Input is not valid JSON')
    if remote_qr['success']:
        qrcode_generator = QRCodeGenerator(fill_color="#000000", back_color="#ffffff")
        link = remote_qr['url']
        image = qrcode_generator.generate_qr_code(link)
        return BotResponse(f'Your text is now saved under the following link: {link}\nQR-code generated from link: ', image)
    else:
        return BotResponse('Problems with request to fileserver', None)

Ein Kommando vom Typ Help versendet einen Text der erklärt wie die Kommandos des Bots zu verwenden sind. Dieser Text wird auch zusammen mit einer Fehlermeldung versendet wenn beim Behandeln des Kommandos ein Fehler aufgetreten ist.

def help_page()-> str:
    return(f'''This bot is used for converting links into QR-codes for sharing.
    It can also be used to share simple text files with others.

    Available commands for this bot: 
    qr!link *linkToBeConverted* ; Converts one link into a QR-code and provides as a png picture
    qr!text *Text to be converted* ; Saves the text on a fileserver and converts the link to the file to a QR-code and provides it as a png picture
    qr!help; Displays this message

    The commands need the necessary arguments to work.
    ''')