geas-bot/app/cogs/PitchMenu.py

190 lines
9.7 KiB
Python
Raw Normal View History

import os
import pymongo
import discord
from discord.ext import commands, tasks
from bot import dbClient, p, state, gameTimes, gameTime, timeSlotList, dbFindTimeslot, dbLookupRole, syncGames
pitchState = {}
def pitchListening():
l = []
for guild in pitchState:
for slot in pitchState[guild]:
l.append(pitchState[guild][slot]['menuMessage'].id)
return l
emojiList = [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'🔟',
'🇦',
'🇧',
'🇨',
'🇩',
'🇪',
'🇫',
'🇬',
'🇭',
'🇮',
'🇯'
]
class PitchMenu(commands.Cog, name='Pitch Menu Commands'):
def __init__(self,client):
self.client = client
# Pitch Run Command
@commands.has_permissions(administrator=True)
@commands.group(name='pitch', aliases=['pitches'], description='The command to run pitches. It has two subcommands. Syntax: `pitch {run|clear} {wed|sunaft|suneve|oneshot|other}`')
async def pitch(self, ctx):
if ctx.invoked_subcommand is None:
await ctx.send(f'Invalid subcommand. Please use either `{p}pitch run` or `{p}pitch clear`')
@pitch.command(name='run', aliases=['start','generate','setup'], description='Subcommand to set up pitches. Syntax: `pitch run {wed|sunaft|suneve|oneshot|other}`')
async def pitch_run(self, ctx, arg):
if arg.lower() not in ['all', 'other', 'wed', 'sunaft', 'suneve', 'oneshot']:
raise commands.CommandError('Invalid argument. {arg} is not a valid flag for the command.')
syncGames(ctx.guild)
await ctx.message.delete()
await ctx.channel.trigger_typing()
## Constructing Pitch State Dictionary
dbName = str(ctx.guild.id)
db = dbClient[dbName]
colName = gameTime(arg.lower())
if dbName not in pitchState:
pitchState[dbName] = {}
if colName not in pitchState[dbName]:
pitchState[dbName][colName] = {}
pitchState[dbName][colName]['entries'] = []
# Try database queries
try:
cur = db[colName].find().sort('game')
for entry in cur:
gameDict = {}
gameDict['game'] = entry['game']
gameDict['gm'] = await ctx.guild.fetch_member(entry['gm'])
gameDict['role'] = discord.utils.find(lambda m: m.id == entry['role'],ctx.guild.roles)
gameDict['capacity'] = entry['capacity'] if entry['capacity'] != None else 5
gameDict['signups'] = 0
cat = discord.utils.find(lambda m: m.id == entry['category'], ctx.guild.categories)
tFirst = None
tPos = len(ctx.guild.channels)
for t in cat.text_channels:
if t.position <= tPos:
tFirst = t
tPos = t.position
gameDict['textchannel'] = tFirst
pitchState[dbName][colName]['entries'].append(dict(gameDict))
# Infer from server if database fails
except:
for r in ctx.guild.roles:
if r.name.startswith(colName):
gameDict = {}
gameDict['game'] = r.name.split(': ',maxsplit=1)[1]
gameDict['role'] = r
gameDict['capacity'] = 5
gameDict['signups'] = 0
cat = discord.utils.find(lambda m: m.name == r.name, ctx.guild.categories)
for p in cat.overwrites:
if isinstance(p,discord.Member) and cat.overwrites[p].manage_channels:
gameDict['gm'] = p
break
tFirst = None
tPos = len(ctx.guild.channels)
for t in cat.text_channels:
if t.position <= tPos:
tFirst = t
tPos = t.position
gameDict['textchannel'] = t
pitchState[dbName][colName]['entries'].append(dict(gameDict))
pitchState[dbName][colName]['entries'].sort(key= lambda m: m['game'])
# Begin Constructing the Menu
pitchState[dbName][colName]['headerMessage'] = await ctx.channel.send(f'**Game listing for {colName}**\n_ _\nThe following are the games that are being pitched. Please select your game by clicking on the emoji reaction at the bottom of the menu.\n_ _')
for e in pitchState[dbName][colName]['entries']:
e['message'] = await ctx.channel.send(f'{emojiList[pitchState[dbName][colName]["entries"].index(e)]} **{e["game"]}** (GM {e["gm"].mention}). {e["capacity"] - e["signups"]} spaces remaining.')
pitchState[dbName][colName]['menuMessage'] = await ctx.channel.send('_ _\n**Please select a game from the above list by clicking on the corresponding emoji reaction below.**')
for option in pitchState[dbName][colName]['entries']:
await pitchState[dbName][colName]['menuMessage'].add_reaction(emojiList[pitchState[dbName][colName]["entries"].index(option)])
@pitch.command(name='clear', aliases=['end','cancel','reset','delete'], description='Subcommand to clear pitches. {wed|sunaft|suneve|oneshot|other}')
async def pitch_clear(self, ctx, arg):
if arg.lower() not in ['all', 'other', 'wed', 'sunaft', 'suneve', 'oneshot']:
raise commands.CommandError(f'Invalid argument. {arg} is not a valid flag for the command.')
await ctx.message.delete()
await ctx.channel.trigger_typing()
dbName = str(ctx.guild.id)
db = dbClient[dbName]
colName = gameTime(arg.lower())
for e in pitchState[dbName][colName]['entries']:
await e['message'].delete()
await pitchState[dbName][colName]['menuMessage'].delete()
await pitchState[dbName][colName]['headerMessage'].delete()
await ctx.send(f'Pitch menu for {colName} has been reset.')
pitchState[dbName][colName].clear
# Emoji Reaction Event Listeners
@commands.Cog.listener()
async def on_raw_reaction_add(self, payload):
if payload.user_id != self.client.user.id:
if payload.message_id in pitchListening():
guildID = str(payload.guild_id)
guild = discord.utils.find(lambda g: g.id == payload.guild_id, self.client.guilds)
channel = discord.utils.find(lambda c: c.id == payload.channel_id, guild.channels)
author = await guild.fetch_member(payload.user_id)
for slot in pitchState[guildID]:
if pitchState[guildID][slot]['menuMessage'].id == payload.message_id:
break
message = await channel.fetch_message(pitchState[guildID][slot]['menuMessage'].id)
for reaction in message.reactions:
if reaction.emoji != payload.emoji.name:
i = emojiList.index(reaction.emoji)
if author in await reaction.users().flatten():
await reaction.remove(author)
i = emojiList.index(payload.emoji.name)
e = pitchState[guildID][slot]['entries'][i]
playerRole = discord.utils.find(lambda p: p.name == 'Players',guild.roles)
await author.add_roles(playerRole,e['role'])
e['signups'] += 1
contentString = f'{emojiList[i]} **{e["game"]}** (GM {e["gm"].mention}). {e["capacity"] - e["signups"] if e["signups"] <= e["capacity"] else 0} {"space" if e["capacity"] - e["signups"] == 1 else "spaces"} remaining.'
await e['message'].edit(content=f'~~{contentString}~~' if e['signups'] >= e['capacity'] else contentString)
await e['textchannel'].send(f'{author.mention} has joined the game.')
# Emoji Un-React Event Listener
@commands.Cog.listener()
async def on_raw_reaction_remove(self, payload):
if payload.user_id != self.client.user.id:
if payload.message_id in pitchListening():
guildID = str(payload.guild_id)
guild = discord.utils.find(lambda g: g.id == payload.guild_id, self.client.guilds)
channel = discord.utils.find(lambda c: c.id == payload.channel_id, guild.channels)
author = await guild.fetch_member(payload.user_id)
for slot in pitchState[guildID]:
if pitchState[guildID][slot]['menuMessage'].id == payload.message_id:
break
message = await channel.fetch_message(pitchState[guildID][slot]['menuMessage'].id)
i = emojiList.index(payload.emoji.name)
e = pitchState[guildID][slot]['entries'][i]
e['signups'] -= 1
contentString = f'{emojiList[i]} **{e["game"]}** (GM {e["gm"].mention}). {e["capacity"] - e["signups"] if e["signups"] <= e["capacity"] else 0} {"space" if e["capacity"] - e["signups"] == 1 else "spaces"} remaining.'
await e['message'].edit(content=f'~~{contentString}~~' if e['signups'] >= e['capacity'] else contentString)
await e['textchannel'].send(f'{author.mention} has left the game.')
await author.remove_roles(e['role'])
isPlayer = False
for role in author.roles:
if role.name.split(': ',maxsplit=1)[0] in timeSlotList():
isPlayer = True
break
if not isPlayer:
playerRole = discord.utils.find(lambda p: p.name == 'Players',guild.roles)
await author.remove_roles(playerRole)
def setup(client):
client.add_cog(PitchMenu(client))