beta version of waggle dance bot
This commit is contained in:
		
							parent
							
								
									d7d00452e4
								
							
						
					
					
						commit
						f452eebc09
					
				
					 5 changed files with 169 additions and 2 deletions
				
			
		
							
								
								
									
										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 | ||||
|  |  | |||
							
								
								
									
										20
									
								
								README.md
									
										
									
									
									
								
							
							
						
						
									
										20
									
								
								README.md
									
										
									
									
									
								
							|  | @ -1,3 +1,21 @@ | |||
| # waggle-dance | ||||
| 
 | ||||
| Discord bot for moving people between voice channels for quick one-on-one matchups | ||||
| 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue