Update WIPBOT.py

This commit is contained in:
kl3z
2025-08-01 14:18:45 +01:00
committed by GitHub
parent bb38308b43
commit c597b12ffe
+148 -89
View File
@@ -1,3 +1,4 @@
#!pip install discord.py python-dotenv
import discord import discord
from discord.ext import commands from discord.ext import commands
from discord.ui import View, Select, Button, Modal, TextInput from discord.ui import View, Select, Button, Modal, TextInput
@@ -5,6 +6,7 @@ from datetime import datetime, timedelta
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv() load_dotenv()
jogador_em_progresso = None
intents = discord.Intents.default() intents = discord.Intents.default()
intents.message_content = True intents.message_content = True
intents.reactions = True intents.reactions = True
@@ -54,9 +56,71 @@ def format_grupo_embed():
embed.add_field(name=f"{EMOJI_DPS} DPS", value=format_role("DPS"), inline=False) embed.add_field(name=f"{EMOJI_DPS} DPS", value=format_role("DPS"), inline=False)
embed.set_footer(text="Bot created by Kl3z") embed.set_footer(text="Bot created by Kl3z")
return embed return embed
async def criar_grupo_customizado(canal):
global grupo_mensagem_id, inscritos, dungeon_escolhida, dificuldade_escolhida, data_formatada, hora_escolhida, alteracoes_feitas
inscritos = {"Tank": [], "Healer": [], "DPS": []}
classes_escolhidas.clear()
dungeon_escolhida = DUNGEONS[2]
dificuldade_escolhida = "10"
data_formatada = "06/08/2025"
hora_escolhida = "22:00"
alteracoes_feitas = {"dungeon": False, "dificuldade": False, "data": False}
embed = format_grupo_embed()
mensagem = await canal.send(embed=embed, view=DungeonView())
grupo_mensagem_id = mensagem.id
await canal.send(
"Escolher a tua **Role**:",
view=RoleView("Novo jogador", canal.id, grupo_mensagem_id)
)
class StackDropdown(Select):
def __init__(self):
options = [
discord.SelectOption(label="Plate Stack"),
discord.SelectOption(label="Leather Stack"),
discord.SelectOption(label="Mail Stack"),
discord.SelectOption(label="Cloth Stack")
]
super().__init__(placeholder="Escolhe o tipo de stack", options=options)
async def callback(self, interaction: discord.Interaction):
escolha = self.values[0]
forum_channel = discord.utils.get(interaction.guild.channels, name="lfg", type=discord.ChannelType.forum)
if not forum_channel:
await interaction.response.send_message("❌ Canal de fórum 'lfg' não encontrado ou não é do tipo fórum.", ephemeral=True)
return
# Cria o post no fórum
thread = await forum_channel.create_thread(name=escolha, content=escolha)
# Apaga a mensagem original com a combobox
await interaction.message.delete()
# Evita erro de timeout
await interaction.response.defer()
@bot.command(name="bot")
async def bot_command(ctx):
await ctx.send(view=StackView())
class StackView(View):
def __init__(self):
super().__init__()
self.add_item(StackDropdown())
CLASSES_POR_ROLE = { CLASSES_POR_ROLE = {
"Tank": [ "Tank": [
("Protection Paladin", "https://wow.zamimg.com/images/wow/icons/large/spell_holy_avengershield.jpg"), ("Protection Paladin", "https://wow.zamimg.com/images/wow/icons/large/spell_holy_avengershield.jpg"),
("Protection Warrior", "https://wow.zamimg.com/images/wow/icons/large/spell_warrior_protetion_spec.jpg"),
("Blood Death Knight", "https://wow.zamimg.com/images/wow/icons/large/spell_deathknight_bloodpresence.jpg"), ("Blood Death Knight", "https://wow.zamimg.com/images/wow/icons/large/spell_deathknight_bloodpresence.jpg"),
("Vengeance Demon Hunter","https://wow.zamimg.com/images/wow/icons/large/ability_demonhunter_specdps.jpg"), ("Vengeance Demon Hunter","https://wow.zamimg.com/images/wow/icons/large/ability_demonhunter_specdps.jpg"),
("Brewmaster Monk", "https://wow.zamimg.com/images/wow/icons/large/spell_monk_brewmaster_spec.jpg"), ("Brewmaster Monk", "https://wow.zamimg.com/images/wow/icons/large/spell_monk_brewmaster_spec.jpg"),
@@ -113,7 +177,11 @@ class DungeonDropdown(Select):
nova_view = DungeonView() nova_view = DungeonView()
if all(alteracoes_feitas.values()): if all(alteracoes_feitas.values()):
nova_view.clear_items() nova_view.clear_items()
await mensagem.edit(embed=format_grupo_embed(), view=nova_view) await mensagem.edit(embed=format_grupo_embed(), view=nova_view)
if isinstance(interaction.channel, discord.Thread):
await interaction.channel.edit(name=f"{dungeon_escolhida} - Key {dificuldade_escolhida}")
await interaction.response.defer() await interaction.response.defer()
class DificuldadeDropdown(Select): class DificuldadeDropdown(Select):
@@ -128,17 +196,19 @@ class DificuldadeDropdown(Select):
return return
dificuldade_escolhida = self.values[0] dificuldade_escolhida = self.values[0]
alteracoes_feitas["dificuldade"] = True alteracoes_feitas["dificuldade"] = True
mensagem = await interaction.channel.fetch_message(grupo_mensagem_id) mensagem = await interaction.channel.fetch_message(grupo_mensagem_id)
nova_view = DungeonView() nova_view = DungeonView()
if all(alteracoes_feitas.values()): if all(alteracoes_feitas.values()):
nova_view.clear_items() nova_view.clear_items()
await mensagem.edit(embed=format_grupo_embed(), view=nova_view) await mensagem.edit(embed=format_grupo_embed(), view=nova_view)
if isinstance(interaction.channel, discord.Thread):
await interaction.channel.edit(name=f"{dungeon_escolhida} - Key {dificuldade_escolhida}")
await interaction.response.defer() await interaction.response.defer()
class DataModal(Modal, title="Definir Data da Dungeon"): class DataModal(Modal, title="Definir Data da Dungeon"):
dia = TextInput(label="Dia do mês (1-31)", placeholder="Ex: 6", max_length=2) dia = TextInput(label="Dia do mês (1-31)", placeholder="Ex: 6", max_length=2)
hora = TextInput(label="Hora (HH:MM)", placeholder="Ex: 22:30", max_length=5) hora = TextInput(label="Hora (HH:MM)", placeholder="Ex: 22:30", max_length=5)
async def on_submit(self, interaction: discord.Interaction): async def on_submit(self, interaction: discord.Interaction):
global data_formatada, hora_escolhida global data_formatada, hora_escolhida
if alteracoes_feitas["data"]: if alteracoes_feitas["data"]:
@@ -149,7 +219,6 @@ class DataModal(Modal, title="Definir Data da Dungeon"):
if not 1 <= dia_num <= 31: if not 1 <= dia_num <= 31:
raise ValueError("Dia fora do intervalo") raise ValueError("Dia fora do intervalo")
hora_val = datetime.strptime(self.hora.value, "%H:%M").strftime("%H:%M") hora_val = datetime.strptime(self.hora.value, "%H:%M").strftime("%H:%M")
hoje = datetime.now() hoje = datetime.now()
mes = hoje.month mes = hoje.month
ano = hoje.year ano = hoje.year
@@ -159,29 +228,23 @@ class DataModal(Modal, title="Definir Data da Dungeon"):
mes = 1 mes = 1
ano += 1 ano += 1
data_completa = datetime(year=ano, month=mes, day=dia_num) data_completa = datetime(year=ano, month=mes, day=dia_num)
except Exception: except Exception:
await interaction.response.send_message("❌ Data ou hora inválida.", ephemeral=True) await interaction.response.send_message("❌ Data ou hora inválida.", ephemeral=True)
return return
data_formatada = data_completa.strftime("%d/%m/%Y") data_formatada = data_completa.strftime("%d/%m/%Y")
hora_escolhida = hora_val hora_escolhida = hora_val
alteracoes_feitas["data"] = True alteracoes_feitas["data"] = True
mensagem = await interaction.channel.fetch_message(grupo_mensagem_id) mensagem = await interaction.channel.fetch_message(grupo_mensagem_id)
nova_view = DungeonView() nova_view = DungeonView()
if all(alteracoes_feitas.values()): if all(alteracoes_feitas.values()):
nova_view.clear_items() nova_view.clear_items()
await mensagem.edit(embed=format_grupo_embed(), view=nova_view) await mensagem.edit(embed=format_grupo_embed(), view=nova_view)
await interaction.response.defer() await interaction.response.defer()
class BotaoData(Button): class BotaoData(Button):
def __init__(self): def __init__(self):
super().__init__(label="🗓️ Definir Data", style=discord.ButtonStyle.primary) super().__init__(label="🗓️ Definir Data", style=discord.ButtonStyle.primary)
async def callback(self, interaction: discord.Interaction): async def callback(self, interaction: discord.Interaction):
await interaction.response.send_modal(DataModal()) await interaction.response.send_modal(DataModal())
class DungeonView(View): class DungeonView(View):
def __init__(self): def __init__(self):
super().__init__(timeout=None) super().__init__(timeout=None)
@@ -191,26 +254,88 @@ class DungeonView(View):
self.add_item(DificuldadeDropdown()) self.add_item(DificuldadeDropdown())
if not alteracoes_feitas["data"]: if not alteracoes_feitas["data"]:
self.add_item(BotaoData()) self.add_item(BotaoData())
class RoleDropdown(Select):
def __init__(self, jogador, ctx_channel_id, role_msg_id):
self.jogador = jogador
self.ctx_channel_id = ctx_channel_id
self.role_msg_id = role_msg_id
options = []
for role in LIMITES:
if len(inscritos[role]) < LIMITES[role]:
options.append(discord.SelectOption(label=role, description="Escolher esta role"))
placeholder = "Escolhe a tua role" if options else "Todas as roles preenchidas"
super().__init__(placeholder=placeholder, options=options, disabled=(len(options) == 0))
async def callback(self, interaction: discord.Interaction):
global jogador_em_progresso
jogador = interaction.user.display_name
if jogador_em_progresso and jogador_em_progresso != jogador:
await interaction.response.send_message(
f"⏳ Aguarda que {jogador_em_progresso} termine a escolha da classe.",
ephemeral=True
)
return
jogador_em_progresso = jogador
role = self.values[0]
for r in inscritos:
if jogador in inscritos[r]:
inscritos[r].remove(jogador)
if jogador not in inscritos[role] and len(inscritos[role]) < LIMITES[role]:
inscritos[role].append(jogador)
canal = bot.get_channel(self.ctx_channel_id)
if isinstance(canal, discord.Thread):
await canal.join()
await interaction.message.delete()
mensagem = await canal.fetch_message(grupo_mensagem_id)
await mensagem.edit(embed=format_grupo_embed())
await canal.send(
f"{jogador}, agora escolhe a tua classe para **{role}**:",
view=ClasseView(role, jogador, self.ctx_channel_id)
)
await interaction.response.defer()
else:
jogador_em_progresso = None
await interaction.response.send_message("❌ Esta role já está cheia ou ocorreu um erro.", ephemeral=True)
class ClasseDropdown(Select): class ClasseDropdown(Select):
def __init__(self, role, jogador): def __init__(self, role, jogador):
self.jogador = jogador self.jogador = jogador
options = [discord.SelectOption(label=nome, value=nome) for nome, _ in CLASSES_POR_ROLE[role]] options = [discord.SelectOption(label=nome, value=nome) for nome, _ in CLASSES_POR_ROLE[role]]
super().__init__(placeholder=f"Escolhe a classe ({role})", options=options) super().__init__(placeholder=f"Escolhe a classe ({role})", options=options)
async def callback(self, interaction: discord.Interaction): async def callback(self, interaction: discord.Interaction):
global jogador_em_progresso
classes_escolhidas[self.jogador] = self.values[0] classes_escolhidas[self.jogador] = self.values[0]
canal = discord.utils.get(bot.get_all_channels(), id=self.ctx_channel_id) canal = interaction.channel
mensagem = await canal.fetch_message(grupo_mensagem_id) mensagem = await canal.fetch_message(grupo_mensagem_id)
await mensagem.edit(embed=format_grupo_embed()) await mensagem.edit(embed=format_grupo_embed())
await interaction.message.delete()
jogador_em_progresso = None
if any(len(inscritos[r]) < LIMITES[r] for r in LIMITES):
await canal.send(
"Escolher a tua **Role**:",
view=RoleView(interaction.user.display_name, canal.id, grupo_mensagem_id)
)
await interaction.response.defer() await interaction.response.defer()
class ClasseView(View): class ClasseView(View):
def __init__(self, role, jogador, ctx_channel_id): def __init__(self, role, jogador, ctx_channel_id):
super().__init__() super().__init__()
dropdown = ClasseDropdown(role, jogador) dropdown = ClasseDropdown(role, jogador)
dropdown.ctx_channel_id = ctx_channel_id dropdown.ctx_channel_id = ctx_channel_id
self.add_item(dropdown) self.add_item(dropdown)
import asyncio
async def agendar_remocao_thread(thread: discord.Thread, data_str: str, hora_str: str):
try:
alvo = datetime.strptime(f"{data_str} {hora_str}", "%d/%m/%Y %H:%M")
agora = datetime.now()
segundos_ate_apagar = (alvo + timedelta(minutes=30) - agora).total_seconds()
if segundos_ate_apagar > 0:
await asyncio.sleep(segundos_ate_apagar)
await thread.delete()
print(f"[INFO] Thread '{thread.name}' apagada automaticamente após 30 minutos.")
else:
print(f"[INFO] Tempo já passou, thread '{thread.name}' não foi agendada.")
except Exception as e:
print(f"[ERRO] ao agendar remoção: {e}")
@bot.command(name="criargrupo") @bot.command(name="criargrupo")
async def criar_grupo(ctx): async def criar_grupo(ctx):
global grupo_mensagem_id, inscritos, dungeon_escolhida, dificuldade_escolhida, data_formatada, hora_escolhida, alteracoes_feitas global grupo_mensagem_id, inscritos, dungeon_escolhida, dificuldade_escolhida, data_formatada, hora_escolhida, alteracoes_feitas
@@ -221,87 +346,21 @@ async def criar_grupo(ctx):
data_formatada = "06/08/2025" data_formatada = "06/08/2025"
hora_escolhida = "22:00" hora_escolhida = "22:00"
alteracoes_feitas = {"dungeon": False, "dificuldade": False, "data": False} alteracoes_feitas = {"dungeon": False, "dificuldade": False, "data": False}
embed = format_grupo_embed() embed = format_grupo_embed()
mensagem = await ctx.send(embed=embed, view=DungeonView()) mensagem = await ctx.send(embed=embed, view=DungeonView())
grupo_mensagem_id = mensagem.id grupo_mensagem_id = mensagem.id
await mensagem.add_reaction(EMOJI_TANK) if isinstance(ctx.channel, discord.Thread):
await mensagem.add_reaction(EMOJI_HEALER) asyncio.create_task(agendar_remocao_thread(ctx.channel, data_formatada, hora_escolhida))
await mensagem.add_reaction(EMOJI_DPS) role_msg = await ctx.send(
"Escolher a tua **Role**:",
@bot.event view=RoleView(ctx.author.display_name, ctx.channel.id, grupo_mensagem_id)
async def on_raw_reaction_add(payload): )
if payload.message_id != grupo_mensagem_id or payload.user_id == bot.user.id: class RoleView(View):
return def __init__(self, jogador, ctx_channel_id, role_msg_id):
super().__init__(timeout=None)
guild = bot.get_guild(payload.guild_id) self.add_item(RoleDropdown(jogador, ctx_channel_id, role_msg_id))
member = guild.get_member(payload.user_id) or await guild.fetch_member(payload.user_id)
nome = member.display_name
emoji = str(payload.emoji)
role = {EMOJI_TANK: "Tank", EMOJI_HEALER: "Healer", EMOJI_DPS: "DPS"}.get(emoji)
if not role:
return
if len(inscritos[role]) >= LIMITES[role] and nome not in inscritos[role]:
channel = bot.get_channel(payload.channel_id)
mensagem = await channel.fetch_message(payload.message_id)
await mensagem.remove_reaction(emoji, member)
return
for r in inscritos:
if nome in inscritos[r]:
inscritos[r].remove(nome)
inscritos[role].append(nome)
canal = bot.get_channel(payload.channel_id)
mensagem = await canal.fetch_message(payload.message_id)
await mensagem.edit(embed=format_grupo_embed())
if role in CLASSES_POR_ROLE:
try:
dm_channel = await member.create_dm()
await dm_channel.send(f"Olá {member.display_name}, escolhe a tua classe para {role}:",
view=ClasseView(role, nome, payload.channel_id))
except discord.Forbidden:
await canal.send(f"{member.mention}, ativa as DMs para receber opções do bot.")
@bot.event
async def on_raw_reaction_remove(payload):
if payload.message_id != grupo_mensagem_id or payload.user_id == bot.user.id:
return
guild = bot.get_guild(payload.guild_id)
member = guild.get_member(payload.user_id) or await guild.fetch_member(payload.user_id)
nome = member.display_name
emoji = str(payload.emoji)
role = {EMOJI_TANK: "Tank", EMOJI_HEALER: "Healer", EMOJI_DPS: "DPS"}.get(emoji)
if role and nome in inscritos[role]:
inscritos[role].remove(nome)
classes_escolhidas.pop(nome, None)
channel = bot.get_channel(payload.channel_id)
mensagem = await channel.fetch_message(payload.message_id)
await mensagem.edit(embed=format_grupo_embed())
if role in CLASSES_POR_ROLE:
try:
await member.send(f"Escolhe a tua classe:", view=ClasseView(role, nome))
except discord.Forbidden:
await channel.send(f"{member.mention}, ativa as DMs para escolher a classe.")
if (
len(inscritos["Tank"]) == LIMITES["Tank"]
and len(inscritos["Healer"]) == LIMITES["Healer"]
and len(inscritos["DPS"]) == LIMITES["DPS"]
):
await channel.send("🎉 Dungeon Encerrada! Parabéns <@Rixa>, felicidades 🎉")
@bot.event @bot.event
async def on_ready(): async def on_ready():
print(f"Bot ligado como {bot.user}") print(f"Bot ligado como {bot.user}")
if os.getenv("RAILWAY_ENVIRONMENT") is None:
from dotenv import load_dotenv
load_dotenv()
bot.run(os.getenv("DISCORD_TOKEN")) bot.run(os.getenv("DISCORD_TOKEN"))