Skip to content

Commit 614d1f0

Browse files
committed
feat: major update in Counting
1 parent ce23128 commit 614d1f0

4 files changed

Lines changed: 533 additions & 227 deletions

File tree

cogs/codebuddy_quiz.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,14 @@ async def on_message(self, message: discord.Message):
133133
# Notify user about quest completion
134134
try:
135135
quest_embed = discord.Embed(
136-
title="Daily Quest Completed!",
137-
description=f"{message.author.mention} You completed your daily quest!\n\n**Rewards Earned:**\n• 1 Streak Freeze\n• 1 Bonus Hint\n\nUse `?inventory` to check your rewards!",
136+
title="Quest Completed!",
137+
description=(
138+
f"{message.author.mention} You completed the **Quiz** quest!\n\n"
139+
"**Rewards Earned:**\n"
140+
"• **0.2** Streak Freeze\n"
141+
"• **0.5** Save\n\n"
142+
"Use `?inventory` to check your items!"
143+
),
138144
color=0x000000
139145
)
140146
await message.channel.send(embed=quest_embed)

cogs/counting.py

Lines changed: 139 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@
33
from discord import app_commands
44
import aiosqlite
55
from utils.codebuddy_database import DB_PATH
6+
from utils.codebuddy_database import (
7+
add_guild_save_units,
8+
get_guild_save_units,
9+
get_user_save_units,
10+
increment_quest_counting_count,
11+
try_use_guild_save,
12+
try_use_user_save,
13+
)
614
import ast
715
import operator
8-
import random
916
import asyncio
1017
import time
1118
from typing import Optional
@@ -454,6 +461,19 @@ async def on_message(self, message):
454461
# Side effects after commit to avoid duplicate reactions on retries.
455462
self._enqueue_reaction(message, "✅")
456463

464+
# Daily quest progress: count 5 numbers (best-effort).
465+
try:
466+
quest_completed = await increment_quest_counting_count(message.author.id)
467+
if quest_completed:
468+
await message.channel.send(
469+
f"Daily quest completed, {message.author.mention}! "
470+
"You earned **0.2** Streak Freeze and **0.5** Save. "
471+
"Use `?inventory` to check your items.",
472+
delete_after=15,
473+
)
474+
except Exception:
475+
pass
476+
457477
# Highscore marker: react ✅+🏆 when reaching/topping the record
458478
if next_count >= high_score:
459479
await self._mark_highscore_message(message, next_count, high_score)
@@ -497,136 +517,136 @@ async def on_message_delete(self, message: discord.Message):
497517
)
498518

499519
async def fail_count(self, message, current_count, reason):
500-
# 1. Send initial message
501-
await message.add_reaction("❌")
502-
status_msg = await message.channel.send(
503-
f"{reason} {message.author.mention} messed up at {current_count}!\n"
504-
"🎲 **Rolling the Dice of Fate...**\n"
505-
"React with 🎲 to help roll! (Need 2 reactions in 60s)"
506-
)
507-
await status_msg.add_reaction("🎲")
520+
# Replace dice mechanic with save mechanic:
521+
# 1) Use a personal save if available.
522+
# 2) Else use a guild save if available.
523+
# 3) Else the count is ruined (reset to 0).
508524

509-
# 2. Wait for reactions
510-
reactions_collected = False
511525
try:
512-
end_time = asyncio.get_event_loop().time() + 60
513-
while True:
514-
# Check current count
515-
status_msg = await message.channel.fetch_message(status_msg.id)
516-
reaction = discord.utils.get(status_msg.reactions, emoji="🎲")
517-
518-
# If bot reacted, count is at least 1. We need 2 total.
519-
if reaction and reaction.count >= 2:
520-
reactions_collected = True
521-
break
522-
523-
timeout = end_time - asyncio.get_event_loop().time()
524-
if timeout <= 0:
525-
break
526-
527-
try:
528-
# Wait for any reaction on this message
529-
await self.bot.wait_for(
530-
'reaction_add',
531-
check=lambda r, u: r.message.id == status_msg.id and str(r.emoji) == "🎲",
532-
timeout=timeout
533-
)
534-
except asyncio.TimeoutError:
535-
break
526+
await message.add_reaction("❌")
536527
except Exception:
537-
pass # Proceed if something fails
528+
pass
538529

539-
# 3. Determine Outcome
540-
outcome_msg = ""
541-
new_count = 0
542-
new_last_user_id = None
543-
544-
dice_db_ops = [] # List of DB operations to perform (query, args)
530+
if not message.guild:
531+
return
545532

546-
if not reactions_collected:
547-
# TIMEOUT / NOT ENOUGH REACTIONS -> RESET
548-
new_count = 0
549-
new_last_user_id = None
550-
outcome_msg = "⏳ **Time's up!** Not enough people helped roll the dice.\n💥 **Reset!** The count goes back to 0."
551-
552-
dice_db_ops.append(("""
553-
UPDATE counting_config
533+
guild_id = message.guild.id
534+
user_id = message.author.id
535+
536+
# Clear this user's warnings so a saved mistake doesn't soft-lock them.
537+
try:
538+
await self._set_warning_count(guild_id, user_id, 0)
539+
except Exception:
540+
pass
541+
542+
used_personal = False
543+
used_guild = False
544+
try:
545+
used_personal = await try_use_user_save(user_id)
546+
except Exception:
547+
used_personal = False
548+
549+
if not used_personal:
550+
try:
551+
used_guild = await try_use_guild_save(guild_id)
552+
except Exception:
553+
used_guild = False
554+
555+
if used_personal or used_guild:
556+
try:
557+
remaining_user_units = await get_user_save_units(user_id)
558+
except Exception:
559+
remaining_user_units = 0
560+
try:
561+
remaining_guild_units = await get_guild_save_units(guild_id)
562+
except Exception:
563+
remaining_guild_units = 0
564+
565+
source = "your" if used_personal else "the server's"
566+
await message.channel.send(
567+
f"{reason} {message.author.mention} messed up at **{current_count}**, "
568+
f"but {source} save was used — the count is **saved**.\n"
569+
f"Next number is **{current_count + 1}**.\n"
570+
f"Your saves: **{remaining_user_units/10:.1f}** • Server saves: **{remaining_guild_units/10:.1f}**"
571+
)
572+
return
573+
574+
# No saves: ruin the count (reset to 0)
575+
db_ops = [
576+
(
577+
"""
578+
UPDATE counting_config
554579
SET current_count = 0, last_user_id = NULL
555580
WHERE guild_id = ?
556-
""", (message.guild.id,)))
557-
558-
dice_db_ops.append(("""
581+
""",
582+
(guild_id,),
583+
),
584+
(
585+
"""
559586
INSERT INTO counting_stats (user_id, guild_id, total_counts, ruined_counts)
560587
VALUES (?, ?, 0, 1)
561588
ON CONFLICT(user_id, guild_id) DO UPDATE SET ruined_counts = ruined_counts + 1
562-
""", (message.author.id, message.guild.id)))
589+
""",
590+
(user_id, guild_id),
591+
),
592+
]
563593

564-
else:
565-
# REACTIONS COLLECTED -> ROLL DICE
566-
dice_roll = random.randint(1, 6)
567-
outcome_msg = f"🎲 **Dice Roll: {dice_roll}**\n"
568-
569-
if dice_roll in [2, 4, 6]:
570-
# SAVE
571-
new_count = current_count
572-
outcome_msg += "✨ **Saved!** The count continues!"
573-
# No update to config needed except maybe verifying it?
574-
# Actually if saved, we do NOTHING to counting_config.
575-
elif dice_roll == 3:
576-
# RESET
577-
new_count = 0
578-
new_last_user_id = None
579-
outcome_msg += "💥 **Reset!** The count goes back to 0."
580-
elif dice_roll == 1:
581-
# -10 Penalty
582-
new_count = max(0, current_count - 10)
583-
new_last_user_id = None
584-
outcome_msg += "🔻 **-10 Penalty!** The count drops by 10."
585-
elif dice_roll == 5:
586-
# -5 Penalty
587-
new_count = max(0, current_count - 5)
588-
new_last_user_id = None
589-
outcome_msg += "🔻 **-5 Penalty!** The count drops by 5."
590-
591-
if dice_roll not in [2, 4, 6]:
592-
dice_db_ops.append(("""
593-
UPDATE counting_config
594-
SET current_count = ?, last_user_id = ?
595-
WHERE guild_id = ?
596-
""", (new_count, new_last_user_id, message.guild.id)))
597-
598-
dice_db_ops.append(("""
599-
INSERT INTO counting_stats (user_id, guild_id, total_counts, ruined_counts)
600-
VALUES (?, ?, 0, 1)
601-
ON CONFLICT(user_id, guild_id) DO UPDATE SET ruined_counts = ruined_counts + 1
602-
""", (message.author.id, message.guild.id)))
594+
retries = 3
595+
while retries > 0:
596+
try:
597+
async with aiosqlite.connect(DB_PATH, timeout=30.0) as db:
598+
for sql, args in db_ops:
599+
await db.execute(sql, args)
600+
await db.commit()
601+
break
602+
except aiosqlite.OperationalError as e:
603+
if "locked" in str(e).lower():
604+
retries -= 1
605+
await asyncio.sleep(0.3)
606+
continue
607+
break
603608

604-
# EXECUTE DB OPS with Retry
605-
if dice_db_ops:
606-
retries = 3
607-
while retries > 0:
608-
try:
609-
async with aiosqlite.connect(DB_PATH, timeout=30.0) as db:
610-
for sql, args in dice_db_ops:
611-
await db.execute(sql, args)
612-
await db.commit()
613-
break # Success
614-
except aiosqlite.OperationalError as e:
615-
if "locked" in str(e):
616-
retries -= 1
617-
await asyncio.sleep(0.5)
618-
else:
619-
print(f"Error saving count fail state: {e}")
620-
break
621-
622-
# 4. Edit message
623-
await status_msg.edit(content=f"{reason} {message.author.mention} messed up at {current_count}!\n{outcome_msg}\nNext number is **{new_count + 1}**.")
624-
625-
# If the count was actually changed (ruined/reset/penalty), clear warnings and remove highscore marker.
626-
count_ruined = new_count != current_count
627-
if count_ruined and message.guild and isinstance(message.channel, discord.TextChannel):
628-
await self._clear_all_warnings(message.guild.id)
629-
await self._clear_highscore_marker_if_any(message.guild.id, message.channel)
609+
await message.channel.send(
610+
f"{reason} {message.author.mention} messed up at **{current_count}**. "
611+
"No saves were available — the count is **ruined** and has been reset to **0**.\n"
612+
"Next number is **1**."
613+
)
614+
615+
if isinstance(message.channel, discord.TextChannel):
616+
await self._clear_all_warnings(guild_id)
617+
await self._clear_highscore_marker_if_any(guild_id, message.channel)
618+
619+
620+
@commands.command(name="donateguild", aliases=["dg"])
621+
async def donate_guild(self, ctx: commands.Context):
622+
"""Donate 1 personal save to the guild pool (guild receives 0.5 save)."""
623+
if not ctx.guild:
624+
return await ctx.send("Server only command.")
625+
626+
user_id = ctx.author.id
627+
guild_id = ctx.guild.id
628+
629+
# Need at least 1.0 save (10 units) to donate.
630+
user_units = await get_user_save_units(user_id)
631+
if user_units < 10:
632+
return await ctx.send(
633+
f"You need **1.0** save to donate. Your saves: **{user_units/10:.1f}**"
634+
)
635+
636+
# Consume 1.0 personal save
637+
used = await try_use_user_save(user_id)
638+
if not used:
639+
return await ctx.send("Couldn't donate right now (try again).")
640+
641+
# Guild receives 0.5 save (5 units)
642+
await add_guild_save_units(guild_id, 5)
643+
644+
new_user_units = await get_user_save_units(user_id)
645+
new_guild_units = await get_guild_save_units(guild_id)
646+
await ctx.send(
647+
f"Donated **1.0** save to the server pool. Server gained **0.5** save.\n"
648+
f"Your saves: **{new_user_units/10:.1f}** • Server saves: **{new_guild_units/10:.1f}**"
649+
)
630650

631651
@commands.hybrid_command(name="highscoretable", aliases=["highscores"], help="Show recent counting highscores")
632652
async def highscore_table(self, ctx: commands.Context):

0 commit comments

Comments
 (0)