#!/usr/bin/env python3 import asyncio import discord import json import random import sys def matchings(l): if len(l) == 0: return [] l = l.copy() random.shuffle(l) center = l.pop() result = [] for i in range(len(l)): matching = [(center, l[i])] for j in range(1, (len(l)-1)/2 + 1): matching.append((l[(i-j)%len(l)], l[(i+j)%len(l)])) result.append(matching) return result async def delete_if_possible(channels): for c in channels: try: await c.delete() except: pass with open('config.json') as f: config = json.load(f) client = discord.Client() @client.event async def on_ready(): print('logged in as {0.user}'.format(client), file=sys.stderr) @client.event async def on_message(message): if message.author == client.user: return if not message.content.startswith('!waggle'): return if not message.author.guild_permissions.administrator: await message.channel.send('only administrators can start pollination!') return argv = message.content.split() if len(argv) > 1: try: rounds = int(message.content.split()[1]) except ValueError: await message.channel.send('usage: `!waggle 5` for 5 rounds of pollination, `!waggle` for as many rounds as possible') return else: rounds = None voice_state = message.author.voice if voice_state is None or voice_state.channel is None: await message.channel.send('you need to be in the main gathering voice channel to start pollination') return main_voice_channel = voice_state.channel category = message.channel.category if category is None: await message.channel.send("please re-send this message in a channel in the category where you'd like me to create voice channels") return participants = main_voice_channel.members.copy() if len(participants) % 2 != 0: participants.remove(message.author) await message.channel.send('leaving out {} so we have an even number of participants'.format(message.author.mention)) if len(participants) == 0: await message.channel.send('there are no participants! :(') return mention_all = ' '.join(u.mention for u in participants) schedule = matchings(participants) if rounds: schedule = schedule[:rounds] await message.channel.send('performing {} rounds of pollination'.format(len(schedule))) pollination_channels = [] channel_names = config['channel_names'].copy() exception_count = 0 while len(pollination_channels) < len(participants)/2: if len(channel_names) > 0: name = channel_names.pop(0) else: name = 'pollination-{}'.format(random.randrange(10000)) try: pollination_channels.append(await category.create_voice_channel(name)) exception_count = 0 except discord.Forbidden: await message.channel.send("looks like I'm not allowed to create voice channels :(") await delete_if_possible(pollination_channels) return except discord.HTTPException as e: exception_count += 1 if exception_count > 20: await message.channel.send("I'm trying to create voice channels, but something's wrong: {}".format(e)) await delete_if_possible(pollination_channels) return continue for matching in schedule: announcement = 'next round starting in 30 seconds:\n' announcement += '\n'.join('{0} and {1} in {2}'.format(a.mention, b.mention, c.name) for ((a, b), c) in zip(matching, pollination_channels)) await message.channel.send(announcement) await asyncio.sleep(30) for ((a, b), c) in zip(matching, pollination_channels): try: await a.move_to(c) except e: await message.channel.send('failed to move participant: {}'.format(e)) try: await b.move_to(c) except e: await message.channel.send('failed to move participant: {}'.format(e)) await asyncio.sleep(60*3) await message.channel.send('round ending in 2 minutes {}'.format(mention_all)) await asyncio.sleep(60) await message.channel.send('round ending in 1 minute {}'.format(mention_all)) await asyncio.sleep(30) await message.channel.send('returning to main channel in 30 seconds {}'.format(mention_all)) await asyncio.sleep(30) for u in participants: try: await u.move_to(main_voice_channel) except e: await message.channel.send('failed to move participant: {}'.format(e)) await delete_if_possible(pollination_channels) client.run(config['token'])