-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbot.py
More file actions
164 lines (146 loc) · 6.08 KB
/
bot.py
File metadata and controls
164 lines (146 loc) · 6.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
import sys
import os
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
import discord
from discord.ext import commands
from discord import app_commands
from dotenv import load_dotenv
import asyncio
from time import monotonic
# --- Load environment variables ---
load_dotenv()
TOKEN = os.getenv("DISCORD_TOKEN")
GUILD_ID = int(os.getenv("GUILD_ID"))
ADMINS_ROLE_ID = int(os.getenv("ADMINS_ROLE_ID", 0))
CAPTAINS_ROLE_ID = int(os.getenv("CAPTAINS_ROLE_ID", 0))
# --- Discord bot setup ---
intents = discord.Intents.default()
intents.members = True
intents.message_content = True
bot = commands.Bot(command_prefix="!", intents=intents)
# ================================================================
# 🕒 GLOBAL COOLDOWN (Admins bypass, applies to all slash commands)
# ================================================================
THROTTLE_SECONDS = 8.0
_last_use_by_user: dict[int, float] = {}
def is_admin_user(interaction: discord.Interaction) -> bool:
"""Admins include anyone with Admin role ID or Administrator permission."""
if not interaction.guild:
return False
if getattr(interaction.user, "guild_permissions", None) and interaction.user.guild_permissions.administrator:
return True
if ADMINS_ROLE_ID and discord.utils.get(interaction.user.roles, id=ADMINS_ROLE_ID):
return True
return False
@bot.event
async def on_interaction(interaction: discord.Interaction):
"""
Intercepts all interactions. If it's a slash command,
apply the cooldown for non-admin users before processing.
"""
# Make sure it’s a slash command (and not autocomplete, button, etc.)
if not interaction.command:
return await bot.process_application_commands(interaction)
# Skip admins entirely
if is_admin_user(interaction):
return await bot.process_application_commands(interaction)
uid = interaction.user.id
now = monotonic()
last = _last_use_by_user.get(uid, 0.0)
wait_for = THROTTLE_SECONDS - (now - last)
if wait_for > 0:
try:
await interaction.response.send_message(
f"⏳ You’re using commands too quickly! Please wait **{wait_for:.1f} seconds**.",
ephemeral=True
)
except discord.InteractionResponded:
await interaction.followup.send(
f"⏳ You’re using commands too quickly! Please wait **{wait_for:.1f} seconds**.",
ephemeral=True
)
return # Don't run the command
# Update cooldown and process command
_last_use_by_user[uid] = now
await bot.process_application_commands(interaction)
# ================================================================
# 🤖 BOT READY EVENT
# ================================================================
@bot.event
async def on_ready():
print(f"✅ Logged in as {bot.user} (ID: {bot.user.id})")
try:
guild = discord.Object(id=GUILD_ID)
bot.tree.copy_global_to(guild=guild)
await bot.tree.sync(guild=guild)
print(f"🔄 Synced slash commands to guild {GUILD_ID}")
except Exception as e:
print(f"⚠️ Failed to sync commands: {e}")
# ================================================================
# ⚠️ GLOBAL ERROR HANDLER
# ================================================================
@bot.tree.error
async def on_app_command_error(interaction: discord.Interaction, error):
if isinstance(error, app_commands.MissingRole):
try:
await interaction.response.send_message(
"🚫 You don’t have permission to use this command.",
ephemeral=True
)
except discord.InteractionResponded:
await interaction.followup.send(
"🚫 You don’t have permission to use this command.",
ephemeral=True
)
return
try:
await interaction.response.send_message(
"⚠️ An unexpected error occurred while running this command.",
ephemeral=True
)
except discord.InteractionResponded:
await interaction.followup.send(
"⚠️ An unexpected error occurred while running this command.",
ephemeral=True
)
print(f"Command Error: {error!r}")
# ================================================================
# 🧩 MAIN ENTRY POINT
# ================================================================
async def main():
async with bot:
for cog_name in [
"cogs.startweek",
"cogs.clearschedule",
"cogs.salary",
"cogs.updateuser",
"cogs.profile",
"cogs.teaminfo",
"cogs.help", # List available commands and a short description
"cogs.propose", # Propose Match Day and Time
"cogs.confirm", # Confirm proposed Match Day and Time
"cogs.refresh", # Refreshes the .csv to match the Google Sheet
# "cogs.transactions"
"cogs.add", # Adds player into roster spot
"cogs.drop", # Removed player from roster spot
"cogs.sub", # Applies Team role for certain duration
"cogs.trade", # Adds opposing captain to chat and initiates the trade
"cogs.waiverclaim", # Adds waiver claims to be automated, adding/removing/and putting on a team
"cogs.unretire",
"cogs.retire",
"cogs.settoken",
"cogs.token",
"cogs.sendmessage"
]:
try:
await bot.load_extension(cog_name)
print(f"✅ Cog '{cog_name.split('.')[-1]}' loaded successfully")
except Exception as e:
print(f"❌ Failed to load '{cog_name}': {e}")
print("🚀 Starting bot connection to Discord...")
await bot.start(TOKEN)
# ================================================================
# ▶️ RUN
# ================================================================
if __name__ == "__main__":
asyncio.run(main())