forked from viveksantayana/geas-bot
190 lines
9.7 KiB
Python
190 lines
9.7 KiB
Python
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)) |