waggle-dance/waggle-dance.py

144 lines
5.5 KiB
Python
Raw Normal View History

2020-06-26 03:04:11 -04:00
#!/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])]
2020-06-26 17:51:49 -04:00
for j in range(1, (len(l)-1)//2 + 1):
2020-06-26 03:04:11 -04:00
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:
2020-06-26 04:13:03 -04:00
await message.channel.send("usage: `!waggle 6` for at most 6 rounds of pollination, `!waggle` for as many rounds as possible until everyone's met everyone else")
2020-06-26 03:04:11 -04:00
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]
pollination_channels = []
channel_names = config['channel_names'].copy()
exception_count = 0
2020-06-26 17:51:49 -04:00
while len(pollination_channels) < len(participants)//2:
2020-06-26 03:04:11 -04:00
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
2020-06-26 03:37:20 -04:00
await message.channel.send("{} Welcome to pollination! You'll be randomly paired up, moved into separate voice channels, spend some time chatting, and then move to a new channel and meet somebody else. Each round will last 5 minutes, and there'll be {} rounds total. I'll announce when your time is nearly up so you can wrap up your conversations, and I'll automatically move you to a different voice channel at the start of each new round. **Feel free to leave at any time if you need to. You won't get moved into new voice channels if you disconnect from voice.** If you find yourself alone in a channel, the person you were paired with for that round may have left; just relax and take a break for 5 minutes. Have fun!".format(mention_all, len(schedule)))
2020-06-26 03:04:11 -04:00
for matching in schedule:
2020-06-26 03:37:20 -04:00
announcement = 'Next round starting in 30 seconds:\n'
2020-06-26 03:04:11 -04:00
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:
2020-06-26 03:37:20 -04:00
print('failed to move participant {}: {}'.format(a, e), file=sys.stderr)
2020-06-26 03:04:11 -04:00
try:
await b.move_to(c)
except e:
2020-06-26 03:37:20 -04:00
print('failed to move participant {}: {}'.format(b, e), file=sys.stderr)
2020-06-26 03:04:11 -04:00
await asyncio.sleep(60*3)
2020-06-26 03:37:20 -04:00
await message.channel.send('Round ending in 2 minutes {}'.format(mention_all))
2020-06-26 03:04:11 -04:00
await asyncio.sleep(60)
2020-06-26 03:37:20 -04:00
await message.channel.send('Round ending in 1 minute {}'.format(mention_all))
2020-06-26 03:04:11 -04:00
await asyncio.sleep(30)
2020-06-26 03:37:20 -04:00
await message.channel.send('Returning to main channel in 30 seconds {}'.format(mention_all))
2020-06-26 03:04:11 -04:00
await asyncio.sleep(30)
for u in participants:
try:
await u.move_to(main_voice_channel)
except e:
2020-06-26 03:37:20 -04:00
print('failed to move participant {}: {}'.format(u, e), file=sys.stderr)
2020-06-26 03:04:11 -04:00
await delete_if_possible(pollination_channels)
client.run(config['token'])