beta version of waggle dance bot
This commit is contained in:
parent
d7d00452e4
commit
f452eebc09
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
|||
config.json
|
||||
|
||||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
MIT License Copyright (c) <year> <copyright holders>
|
||||
MIT License Copyright (c) 2020 xenofem <xenofematxenodotscience>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
18
README.md
18
README.md
|
@ -1,3 +1,21 @@
|
|||
# waggle-dance
|
||||
|
||||
Discord bot for moving people between voice channels for quick one-on-one matchups
|
||||
|
||||
## Setup
|
||||
|
||||
```bash
|
||||
git clone https://git.xeno.science/xenofem/waggle-dance
|
||||
cd waggle-dance
|
||||
python3 -m venv env
|
||||
source env/bin/activate
|
||||
pip install discord.py
|
||||
cp config.json.example config.json
|
||||
vi config.json
|
||||
python waggle-dance.py
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
- `token`: Discord bot API token
|
||||
- `channel_names`: list of names for voice channels to create
|
||||
|
|
4
config.json.example
Normal file
4
config.json.example
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"channel_names": ["Azalea", "Buttercup", "Cockscomb", "Daffodil", "Forget Me Not", "Gardenia", "Hyacinth", "Iris", "Lily", "Magnolia", "Narcissus", "Petunia", "Rose", "Tulip"],
|
||||
"token": "tOk3N"
|
||||
}
|
143
waggle-dance.py
Executable file
143
waggle-dance.py
Executable file
|
@ -0,0 +1,143 @@
|
|||
#!/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'])
|
Loading…
Reference in a new issue