From 2cd1a65408c4d6a49b1fad923f7e24d9fefacda5 Mon Sep 17 00:00:00 2001 From: xenofem Date: Fri, 25 Mar 2022 15:37:33 -0400 Subject: [PATCH] bad therapy bot --- LICENSE | 19 +++++++++ README.md | 21 ++++++++++ bot.py | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 bot.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a54809c --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +MIT License Copyright (c) 2022 xenofem + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next +paragraph) shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d9820bf --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Mikage Bot + +Discord bot to give questionable elevator therapy + +## Discord setup + +Required permissions: +- Manage channels +- Read messages/view channels +- Send messages + + +## Setup + + git clone https://git.xeno.science/xenofem/mikage-bot + cd mikage-bot + python3 -m venv env + source env/bin/activate + pip install py-cord + echo > token + python bot.py diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..3895324 --- /dev/null +++ b/bot.py @@ -0,0 +1,116 @@ +import discord + +import asyncio +import json +import sys + +STATE_FILE = 'state.json' +CHANNEL_NAME = 'therapy-elevator' +DELETION_SECONDS = 300 + +SETUP_MESSAGE = 'Welcome to Nemuro Memorial Hall. Please give your name and class year for our records.' +INITIAL_MESSAGE = 'All right, please begin.' +BUTTERFLIES = ['🦋', '🥐', 'Deeper. Go deeper.' '🐛', '🍃'] +FINAL_MESSAGE = 'I understand. Your only choice is to revolutionize the world. The path you must take has been prepared for you.' +ROSE = '🌹' + +async def try_send(channel, message): + try: + await channel.send(message) + except discord.DiscordException as e: + print(f"error sending message in channel {channel}: {e}", file=sys.stderr) + +def text_only(channels): + return [channel for channel in channels if isinstance(channel, discord.TextChannel)] + +class MikageClient(discord.Client): + def __init__(self): + super().__init__() + try: + with open(STATE_FILE) as f: + self.state = json.load(f) + except: + self.state = {} + self.save_state() + + def save_state(self): + with open(STATE_FILE, 'w') as f: + json.dump(self.state, f) + + async def on_ready(self): + print(f"logged in as {self.user}", file=sys.stderr) + for guild in self.guilds: + await self.initialize_elevator(guild) + + async def on_guild_join(self, guild): + print(f"joined new guild: {guild}", file=sys.stderr) + await self.initialize_elevator(guild) + + async def initialize_elevator(self, guild): + guild_id = str(guild.id) + if guild_id in self.state and guild.get_channel(self.state[guild_id]['channel']) is not None: + return + try: + (top_category, channels) = guild.by_category()[0] + channel = await guild.create_text_channel(CHANNEL_NAME, category=top_category) + await channel.move(category=top_category, beginning=True) + self.state[guild_id] = {'channel': channel.id} + self.save_state() + await channel.send(SETUP_MESSAGE) + except discord.DiscordException as e: + print(f"error initializing elevator for guild {guild}: {e}", file=sys.stderr) + return + + async def on_message(self, message): + if message.guild is None: + return + guild_id = str(message.guild.id) + if guild_id not in self.state or message.channel.id != self.state[guild_id]['channel']: + return + if message.author == self.user: + return + + categories = message.guild.by_category() + (category_index, (category, channels)) = [entry for entry in enumerate(categories) if message.channel in entry[1][1]][0] + channels = text_only(channels) + channel_index = channels.index(message.channel) + + if category_index == 0 and channel_index == 0: + await try_send(message.channel, INITIAL_MESSAGE) + + if channel_index == len(channels) - 1: + if category_index == len(categories) - 1: + return + target_category = categories[category_index+1][0] + target_position = 0 + else: + target_category = category + target_position = channel_index+1 + try: + await message.channel.move(category=target_category, beginning=True, offset=target_position) + except discord.DiscordException as e: + print(f"error moving channel {message.channel} in guild {message.guild}: {e}", file=sys.stderr) + await try_send(message.channel, "Dammit, the elevator's stuck!") + return + + if target_position == 0: + await try_send(message.channel, BUTTERFLIES[int(category_index*len(BUTTERFLIES)/(len(categories)-1))]) + + final_category_channels = text_only(message.guild.by_category()[-1][1]) + if len(final_category_channels) > 0 and message.channel == final_category_channels[-1]: + await try_send(message.channel, FINAL_MESSAGE) + await try_send(message.channel, ROSE) + await asyncio.sleep(DELETION_SECONDS) + try: + await message.channel.delete() + del self.state[guild_id] + self.save_state() + except discord.DiscordException as e: + print(f"error deleting elevator for guild {message.guild}: {e}", file=sys.stderr) + return + await self.initialize_elevator(message.guild) + +client = MikageClient() +with open('token') as f: + token = f.read().strip() +client.run(token)