From 60a1f8b82707e4cb4263c81ddd2c35289cc4cff6 Mon Sep 17 00:00:00 2001 From: xenofem Date: Wed, 8 Jul 2020 02:51:51 -0400 Subject: [PATCH] add functionality for people to block other users they don't want to interact with --- .gitignore | 1 + waggle-dance.py | 140 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 122 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index 18cee6e..3a1de92 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ config.json +blocks.json # ---> Python # Byte-compiled / optimized / DLL files diff --git a/waggle-dance.py b/waggle-dance.py index c6878c5..2ae078c 100755 --- a/waggle-dance.py +++ b/waggle-dance.py @@ -4,20 +4,90 @@ import asyncio import discord import json import random +import re import sys +BLOCKS_FILE = 'blocks.json' + +def get_blocks(): + try: + with open(BLOCKS_FILE) as f: + return {int(k): v for (k, v) in json.load(f).items()} + except FileNotFoundError: + return {} + +def add_block(requester, target): + blocks = get_blocks() + if requester not in blocks: + blocks[requester] = [] + if target not in blocks[requester]: + blocks[requester].append(target) + with open(BLOCKS_FILE, 'w') as f: + json.dump(blocks, f) + +def remove_block(requester, target): + blocks = get_blocks() + if requester not in blocks: + return + while target in blocks[requester]: + blocks[requester].remove(target) + if len(blocks[requester]) == 0: + del blocks[requester] + with open(BLOCKS_FILE, 'w') as f: + json.dump(blocks, f) + def matchings(l): - if len(l) == 0: + count = len(l) + if count == 0: return [] l = l.copy() random.shuffle(l) - center = l.pop() + ids = set(u.id for u in l) + + blocks = get_blocks() + block_pairs = set() + for r in blocks: + if r in ids: + for t in blocks[r]: + if t in ids: + block_pairs.add((min(r,t), max(r,t))) + disjoint_pairs = [] + while True: + paired = set(u for pair in disjoint_pairs for u in pair) + remaining = set(p for p in block_pairs if p[0] not in paired and p[1] not in paired) + if len(remaining) == 0: + break + disjoint_pairs.append(remaining.pop()) + + circle = [None]*(count - 1) + center = None + for i in range(count // 2): + if len(disjoint_pairs) != 0: + p = disjoint_pairs.pop() + l = [u for u in l if u.id not in p] + u0 = p[0] + u1 = p[1] + else: + u0 = l.pop() + u1 = l.pop() + + circle[i] = u0 + if i != count // 2 - 1: + circle[-(i+1)] = u1 + else: + center = u1 + 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)])) + for i in range(len(circle)): + matching = [(center, circle[i])] + for j in range(1, (len(circle)-1)//2 + 1): + matching.append((circle[(i-j)%len(circle)], circle[(i+j)%len(circle)])) result.append(matching) + + random.shuffle(result) + for i in range(len(result)): + random.shuffle(result[i]) + result = [matching for matching in result if all((min(match[0].id,match[1].id), max(match[0].id,match[1].id)) not in block_pairs for match in matching)] return result async def delete_if_possible(channels): @@ -39,15 +109,7 @@ with open('config.json') as 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 - +async def handle_guild_message(message): if not message.content.startswith('!waggle'): return @@ -81,10 +143,6 @@ async def on_message(message): 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) @@ -92,6 +150,10 @@ async def on_message(message): if rounds: schedule = schedule[:rounds] + if len(schedule) == 0: + await message.channel.send("there aren't enough people for pollination right now :(") + return + pollination_channels = [] channel_names = config['channel_names'].copy() exception_count = 0 @@ -147,4 +209,44 @@ async def on_message(message): await delete_if_possible(pollination_channels) +async def handle_dm(message): + requester = message.author.id + match = re.search('@([^#]+)#([0-9a-f]+)', message.content) + if match: + handle = match.group(0) + name = match.group(1) + discriminator = match.group(2) + matching_users = [u for u in client.users if u.name == name and u.discriminator == discriminator] + if len(matching_users) == 0: + await message.channel.send("sorry, I can't find user {}".format(handle)) + else: + target = matching_users[0].id + if target in get_blocks().get(requester, []): + await message.channel.send("removing {} from your list of blocked users for pollination".format(handle)) + remove_block(requester, target) + else: + await message.channel.send("adding {} to your list of blocked users for pollination".format(handle)) + add_block(requester, target) + + blocks = get_blocks().get(requester, []) + if len(blocks) == 0: + await message.channel.send("you currently don't have anyone blocked for pollination") + else: + await message.channel.send("your current list of blocked users for pollination is:\n{}".format('\n'.join('@{}#{}'.format(u.name, u.discriminator) for u in [client.get_user(uid) for uid in blocks] if u is not None))) + await message.channel.send("to block or unblock a user, DM me their handle, like @creep#0000. to see this message and your list of blocked users, DM me something random. buzz buzz!") + +@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 isinstance(message.channel, discord.TextChannel): + await handle_guild_message(message) + elif isinstance(message.channel, discord.DMChannel): + await handle_dm(message) + client.run(config['token'])