From 43a26de389abfb7da469942acfa1cef0dc55af46 Mon Sep 17 00:00:00 2001 From: konsumlamm Date: Mon, 28 Sep 2020 17:43:30 +0200 Subject: Refactor project to use the command extension Restart bot when an exception occurs Add .idea/ to .gitignore --- bot.py | 167 +++++++++++++++-------------------------------------------------- 1 file changed, 39 insertions(+), 128 deletions(-) (limited to 'bot.py') diff --git a/bot.py b/bot.py index 88153cc..75f8aea 100644 --- a/bot.py +++ b/bot.py @@ -1,19 +1,13 @@ -import asyncio -import discord -import config -from command_utils import command, CommandClient -from random import shuffle, choice -from time import sleep +from os import getenv +from commands import answer, await_n, bot_commands +import config -async def await_n(lst): - lst = list(asyncio.create_task(task) for task in lst) - if lst: - done, _ = await asyncio.wait(lst) - return list(task.result() for task in done) +import discord +from discord.ext import commands -class Client(CommandClient): +class Cupido(commands.Bot): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.meta_channel = None @@ -23,45 +17,38 @@ class Client(CommandClient): async def on_ready(self): print(f'the bot {config.NAME} is logged in as "{self.user}"') - @command( - names = ('help', 'hepl', 'h', '?'), - description = 'display this help message', - is_help = True - ) - async def help(self, ctx): - command_doc = '\n'.join( - f' * {config.COMMAND_PREFIX.strip()} {c.names[0]:15} - {c.description}' - for c in self.get_commands()) - await ctx.answer(f'''``` -{config.HELP_TEXT}\nThese are all available commands:\n{command_doc}```''') - async def create_category(self, ctx): category = await ctx.guild.create_category(config.CATEGORY_CHANNEL_NAME) - await ctx.answer(f'info: category created "{category}" ({category.id})') + await answer(ctx, f'info: category created "{category}" ({category.id})') return category async def create_lobby(self, ctx): - lobby = await ctx.guild.create_voice_channel(config.LOBBY_CHANNEL_NAME, topic=config.LOBBY_CHANNEL_TOPIC, category=self.meta_channel) - await ctx.answer(f'info: voice channel created "{lobby}" ({lobby.id})') + lobby = await ctx.guild.create_voice_channel( + config.LOBBY_CHANNEL_NAME, + topic=config.LOBBY_CHANNEL_TOPIC, + category=self.meta_channel, + ) + await answer(ctx, f'info: voice channel created "{lobby}" ({lobby.id})') return lobby def get_meta_channel(self, ctx): - return (self.meta_channel - or discord.utils.get(ctx.guild.categories, - name=config.CATEGORY_CHANNEL_NAME)) + return self.meta_channel or discord.utils.get(ctx.guild.categories, name=config.CATEGORY_CHANNEL_NAME) def get_lobby_channel(self, ctx, meta_channel): return (self.lobby_channel - or discord.utils.get(ctx.guild.voice_channels, - name=config.LOBBY_CHANNEL_NAME, - category=meta_channel)) + or discord.utils.get( + ctx.guild.voice_channels, + name=config.LOBBY_CHANNEL_NAME, + category=meta_channel + )) def get_pair_channels(self, ctx, meta_channel): return (self.pair_channels or sorted((channel for channel in ctx.guild.voice_channels if channel.category == meta_channel and channel.name.isdigit()), - key=lambda c: c.name)) + key=lambda c: c.name, + )) async def destroy_pair_channels(self, ctx, meta_channel): await await_n(channel.delete() for channel in self.get_pair_channels(ctx, meta_channel)) @@ -74,110 +61,34 @@ class Client(CommandClient): futures.append(ctx.guild.create_voice_channel(str(i), category=meta_channel)) return await await_n(futures) - @command( - names = ('init', 'create', 'inti', 'craete', 'cretae', 'c', 'i', '+'), - description = 'create a new lobby' - ) - async def init(self, ctx): - self.meta_channel = ( - self.get_meta_channel(ctx) - or await self.create_category(ctx) - ) - self.lobby_channel = ( - self.get_lobby_channel(ctx, self.meta_channel) - or await self.create_lobby(ctx) - ) - - @command( - names = ('destroy', 'kill', 'desctruction', 'genocide', '-'), - description = f'destruct all {config.NAME} channels' - ) - async def destroy(self, ctx): - futures = [] - meta_channel = self.get_meta_channel(ctx) - for channel in (self.get_lobby_channel(ctx, meta_channel), meta_channel): - if channel: - futures.append(channel.delete()) - await await_n(futures) - self.lobby_channel = None - self.meta_channel = None - self.pair_channels = [] - async def get_channels(self, ctx): meta_channel = self.get_meta_channel(ctx) lobby_channel = self.get_lobby_channel(ctx, meta_channel) if meta_channel is None or lobby_channel is None: - await ctx.answer('error: cannot start shuffling, you need to initialize channels') - await self.help(ctx) + await answer(ctx, 'error: cannot start shuffling, you need to initialize channels') return None return meta_channel, lobby_channel - @command( - names = ('shuffle', 'start', 'run', 'strat', 'rnu'), - description = 'start shuffling' - ) - async def shuffle(self, ctx): - channels = await self.get_channels(ctx) - if not channels: return - meta_channel, lobby_channel = channels - members = lobby_channel.members[:] - slots = len(members) >> 1 - self.pair_channels = await self.create_pair_channels(ctx, meta_channel, slots) - slots = [] - for i, _ in enumerate(self.pair_channels): - slots.append(i) - slots.append(i) - shuffle(slots) - futures = [] - for slot in slots: - member = members.pop() - if member is None: break - futures.append(member.move_to(self.pair_channels[slot])) - if members: - futures.append(members.pop().move_to(choice(self.pair_channels))) - await await_n(futures) - - @command( - names = ('stop', 'quit', 'exit', 'abort', 'back', 'return'), - description = 'move everyone back to lobby' - ) - async def stop(self, ctx): - channels = await self.get_channels(ctx) - if not channels: return - meta_channel, lobby_channel = channels - pair_channels = self.get_pair_channels(ctx, meta_channel) - futures = [] - for channel in pair_channels: - for member in channel.members: - futures.append(member.move_to(lobby_channel)) - await await_n(futures) - await self.destroy_pair_channels(ctx, meta_channel) - - @command( - names = ('loop',), - description = 'repeat "shuffle" and "stop" (default: 3) times and (default: 120) seconds' - ) - async def loop(self, ctx): - if len(ctx.args) >= 1 and ctx.args[0].isdigit(): - n = int(ctx.args[0]) - else: - n = 3 - if len(ctx.args) >= 2 and ctx.args[1].isdigit(): - t = int(ctx.args[1]) - else: - t = 120 - await ctx.answer(f'repeat shuffling {n} times and each {t} seconds') - for _ in range(n): - await self.shuffle(ctx) - sleep(t) - await self.stop(ctx) - -if __name__ == '__main__': - from os import getenv +def main(): token = getenv(config.TOKEN_ENV_VAR) if token is None: print('error: no token was given') exit(1) - bot = Client(activity=discord.Game(name=config.GAME_STATUS)) + bot = Cupido(activity=discord.Game(name=config.GAME_STATUS), command_prefix=config.COMMAND_PREFIX) + bot.remove_command('help') + for cmd in bot_commands: + bot.add_command(cmd) bot.run(token) + + +if __name__ == '__main__': + while True: + try: + main() + except Exception as e: + print(f'[DEBUG] encountered exception: {type(e)}: {e}') + if e.args == ('Event loop is closed',): + print('[DEBUG] quit...') + break + print('[DEBUG] restarting...') -- cgit v1.2.3-70-g09d2