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))
|