Quality improvements
This commit is contained in:
parent
282c44a09e
commit
39fa0fda72
1 changed files with 51 additions and 51 deletions
102
bot.py
102
bot.py
|
|
@ -1,8 +1,9 @@
|
|||
import discord
|
||||
from discord import app_commands
|
||||
from discord.ext import commands, tasks
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, time as dt_time
|
||||
import json
|
||||
import logging
|
||||
import random
|
||||
import re
|
||||
import pytz
|
||||
|
|
@ -11,13 +12,18 @@ from dotenv import load_dotenv
|
|||
|
||||
load_dotenv()
|
||||
TOKEN = os.getenv('DISCORD_BOT_TOKEN')
|
||||
GUILD_IDS = [int(gid.strip()) for gid in os.getenv('GUILD_IDS', '').split(',') if gid.strip()]
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.members = True
|
||||
bot = commands.Bot(command_prefix=commands.when_mentioned, intents=intents)
|
||||
|
||||
TICKETS_FILE = 'tickets.json'
|
||||
GUILD_IDS = [922622021110730753, 988258730061725738]
|
||||
TZ_UTC = pytz.UTC
|
||||
TZ_LOCAL = pytz.timezone('America/Edmonton')
|
||||
|
||||
|
||||
def load_tickets_data():
|
||||
|
|
@ -25,10 +31,10 @@ def load_tickets_data():
|
|||
with open(TICKETS_FILE, 'r') as f:
|
||||
try:
|
||||
data = json.load(f)
|
||||
print("Loaded tickets data:", data)
|
||||
log.info("Loaded tickets data: %s", data)
|
||||
return data
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error loading JSON data: {e}")
|
||||
log.error("Error loading JSON data: %s", e)
|
||||
return {}
|
||||
return {}
|
||||
|
||||
|
|
@ -38,12 +44,13 @@ tickets_data = load_tickets_data()
|
|||
def save_tickets_data():
|
||||
with open(TICKETS_FILE, 'w') as f:
|
||||
json.dump(tickets_data, f, indent=4)
|
||||
print("Saved tickets data:", tickets_data)
|
||||
log.info("Saved tickets data: %s", tickets_data)
|
||||
|
||||
|
||||
def reload_tickets_data():
|
||||
global tickets_data
|
||||
tickets_data = load_tickets_data()
|
||||
def _format_ticket_expiry(ticket: dict) -> str:
|
||||
exp_utc = datetime.fromisoformat(ticket["expiration"]).astimezone(TZ_UTC)
|
||||
exp_local = exp_utc.astimezone(TZ_LOCAL)
|
||||
return f"- Expires on {exp_utc.strftime('%Y-%m-%d %H:%M:%S')} UTC / {exp_local.strftime('%Y-%m-%d %H:%M:%S')} {exp_local.strftime('%Z')}"
|
||||
|
||||
|
||||
async def ticket_type_autocomplete(interaction: discord.Interaction, current: str):
|
||||
|
|
@ -51,7 +58,7 @@ async def ticket_type_autocomplete(interaction: discord.Interaction, current: st
|
|||
return [
|
||||
app_commands.Choice(name=t, value=t)
|
||||
for t in ticket_types
|
||||
if current.lower() in t
|
||||
if t.startswith(current.lower())
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -74,16 +81,17 @@ async def assign_ticket(interaction: discord.Interaction, member: discord.Member
|
|||
if member_id not in tickets_data:
|
||||
tickets_data[member_id] = {"tickets": []}
|
||||
|
||||
expiration_date = datetime.utcnow() + timedelta(days=365)
|
||||
expiration_date = datetime.now(TZ_UTC) + timedelta(days=365)
|
||||
tickets_data[member_id]["tickets"].append({
|
||||
"type": "normal",
|
||||
"expiration": expiration_date.isoformat()
|
||||
})
|
||||
expiration_message = f", which expires on {expiration_date.strftime('%Y-%m-%d %H:%M:%S')} UTC."
|
||||
|
||||
save_tickets_data()
|
||||
reload_tickets_data()
|
||||
await interaction.response.send_message(f"Assigned a ticket to {member.mention}{expiration_message}.", ephemeral=True)
|
||||
await interaction.response.send_message(
|
||||
f"Assigned a ticket to {member.mention}, which expires on {expiration_date.strftime('%Y-%m-%d %H:%M:%S')} UTC.",
|
||||
ephemeral=True
|
||||
)
|
||||
|
||||
|
||||
@tpa.command(name="redeem_ticket", description="Redeem a ticket for a member")
|
||||
|
|
@ -103,15 +111,15 @@ async def redeem_ticket(interaction: discord.Interaction, member: discord.Member
|
|||
await interaction.response.send_message(f"{member.mention} does not have any tickets.", ephemeral=True)
|
||||
return
|
||||
|
||||
for i, ticket in enumerate(tickets_data[member_id]["tickets"]):
|
||||
if ticket["type"] == ticket_type:
|
||||
del tickets_data[member_id]["tickets"][i]
|
||||
save_tickets_data()
|
||||
reload_tickets_data()
|
||||
await interaction.response.send_message(f"Redeemed a {ticket_type} ticket for {member.mention}.", ephemeral=True)
|
||||
return
|
||||
tickets = tickets_data[member_id]["tickets"]
|
||||
ticket_to_remove = next((t for t in tickets if t["type"] == ticket_type), None)
|
||||
if ticket_to_remove is None:
|
||||
await interaction.response.send_message(f"{member.mention} does not have any {ticket_type} tickets to redeem.", ephemeral=True)
|
||||
return
|
||||
|
||||
await interaction.response.send_message(f"{member.mention} does not have any {ticket_type} tickets to redeem.", ephemeral=True)
|
||||
tickets.remove(ticket_to_remove)
|
||||
save_tickets_data()
|
||||
await interaction.response.send_message(f"Redeemed a {ticket_type} ticket for {member.mention}.", ephemeral=True)
|
||||
|
||||
|
||||
@tpa.command(name="check_ticket", description="Check a member's tickets")
|
||||
|
|
@ -127,18 +135,14 @@ async def check_ticket(interaction: discord.Interaction, member: discord.Member)
|
|||
normal_tickets = [t for t in ticket_info if t["type"] == "normal"]
|
||||
golden_tickets = [t for t in ticket_info if t["type"] == "golden"]
|
||||
|
||||
tz_utc = pytz.UTC
|
||||
tz_mst = pytz.timezone('America/Edmonton')
|
||||
lines = []
|
||||
if normal_tickets:
|
||||
lines.append(f"{member.mention} has {len(normal_tickets)} normal ticket(s):")
|
||||
lines.extend(_format_ticket_expiry(t) for t in normal_tickets)
|
||||
if golden_tickets:
|
||||
lines.append(f"{member.mention} has {len(golden_tickets)} golden ticket(s).")
|
||||
|
||||
normal_message = f"{member.mention} has {len(normal_tickets)} normal tickets:\n"
|
||||
for ticket in normal_tickets:
|
||||
expiration_date_utc = datetime.fromisoformat(ticket["expiration"]).astimezone(tz_utc)
|
||||
expiration_date_mst = expiration_date_utc.astimezone(tz_mst)
|
||||
normal_message += f"- Expires on {expiration_date_utc.strftime('%Y-%m-%d %H:%M:%S')} UTC / {expiration_date_mst.strftime('%Y-%m-%d %H:%M:%S')} MST\n"
|
||||
|
||||
golden_message = f"{member.mention} has {len(golden_tickets)} golden tickets."
|
||||
|
||||
await interaction.response.send_message(f"{normal_message}{golden_message}", ephemeral=True)
|
||||
await interaction.response.send_message("\n".join(lines), ephemeral=True)
|
||||
|
||||
|
||||
@tp.command(name="tickets", description="Check your own tickets")
|
||||
|
|
@ -153,12 +157,10 @@ async def tickets(interaction: discord.Interaction):
|
|||
golden_tickets = [t for t in ticket_info if t["type"] == "golden"]
|
||||
|
||||
lines = []
|
||||
if len(normal_tickets) > 0:
|
||||
if normal_tickets:
|
||||
lines.append(f"You have {len(normal_tickets)} normal ticket(s):")
|
||||
for ticket in normal_tickets:
|
||||
expiration_date = datetime.fromisoformat(ticket["expiration"])
|
||||
lines.append(f"- Expires on {expiration_date.strftime('%Y-%m-%d %H:%M:%S')} UTC")
|
||||
if len(golden_tickets) > 0:
|
||||
lines.extend(_format_ticket_expiry(t) for t in normal_tickets)
|
||||
if golden_tickets:
|
||||
lines.append(f"You have {len(golden_tickets)} golden ticket(s).")
|
||||
|
||||
await interaction.response.send_message("\n".join(lines), ephemeral=True)
|
||||
|
|
@ -182,7 +184,6 @@ def parse_and_roll(notation: str):
|
|||
if term in ('adv', 'dis'):
|
||||
rolls = [random.randint(1, 20), random.randint(1, 20)]
|
||||
kept = max(rolls) if term == 'adv' else min(rolls)
|
||||
dropped = min(rolls) if term == 'adv' else max(rolls)
|
||||
subtotal = kept * multiplier
|
||||
rolls_str = ', '.join(f"**{r}**" if r == kept else f"~~{r}~~" for r in rolls)
|
||||
lines.append(f"`{prefix}{term}:` [{rolls_str}] = **{subtotal}**")
|
||||
|
|
@ -220,34 +221,33 @@ async def handle_roll(interaction: discord.Interaction, notation: str):
|
|||
|
||||
|
||||
@bot.tree.command(name="roll", description="Roll dice using dice notation (e.g. 2d20+1d4+3)")
|
||||
@app_commands.describe(notation="Dice notation, e.g. 2d20+1d4+3")
|
||||
async def roll_cmd(interaction: discord.Interaction, notation: str):
|
||||
await handle_roll(interaction, notation)
|
||||
@app_commands.describe(dice="Dice notation, e.g. 2d20+1d4+3")
|
||||
async def roll_cmd(interaction: discord.Interaction, dice: str):
|
||||
await handle_roll(interaction, dice)
|
||||
|
||||
|
||||
@bot.tree.command(name="r", description="Roll dice using dice notation (e.g. 2d20+1d4+3)")
|
||||
@app_commands.describe(notation="Dice notation, e.g. 2d20+1d4+3")
|
||||
async def r_cmd(interaction: discord.Interaction, notation: str):
|
||||
await handle_roll(interaction, notation)
|
||||
@app_commands.describe(dice="Dice notation, e.g. 2d20+1d4+3")
|
||||
async def r_cmd(interaction: discord.Interaction, dice: str):
|
||||
await handle_roll(interaction, dice)
|
||||
|
||||
|
||||
@tasks.loop(hours=24)
|
||||
@tasks.loop(time=dt_time(hour=0, minute=0, tzinfo=TZ_UTC))
|
||||
async def check_expired_tickets():
|
||||
tz = pytz.UTC
|
||||
current_time = datetime.now(tz)
|
||||
now = datetime.now(TZ_UTC)
|
||||
to_remove = []
|
||||
|
||||
for member_id, ticket_info in tickets_data.items():
|
||||
tickets_data[member_id]["tickets"] = [
|
||||
ticket for ticket in ticket_info["tickets"]
|
||||
if ticket["type"] == "golden" or current_time < datetime.fromisoformat(ticket["expiration"]).astimezone(tz)
|
||||
if ticket["type"] == "golden" or now < datetime.fromisoformat(ticket["expiration"]).astimezone(TZ_UTC)
|
||||
]
|
||||
|
||||
if not tickets_data[member_id]["tickets"]:
|
||||
to_remove.append(member_id)
|
||||
|
||||
for member_id in to_remove:
|
||||
print(f"Removing expired tickets for user ID: {member_id}")
|
||||
log.info("Removing expired tickets for user ID: %s", member_id)
|
||||
del tickets_data[member_id]
|
||||
|
||||
save_tickets_data()
|
||||
|
|
@ -261,12 +261,12 @@ async def setup_hook():
|
|||
guild = discord.Object(id=guild_id)
|
||||
bot.tree.copy_global_to(guild=guild)
|
||||
await bot.tree.sync(guild=guild)
|
||||
print(f"Synced commands to guild {guild_id}")
|
||||
log.info("Synced commands to guild %s", guild_id)
|
||||
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
print(f"Logged in as {bot.user.name}")
|
||||
log.info("Logged in as %s", bot.user.name)
|
||||
check_expired_tickets.start()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue