1
Fork 0
mirror of https://github.com/wlinator/luminara.git synced 2024-10-02 20:23:12 +00:00

Merge pull request #51 from wlinator/coinflip

feat: Add coinflip command with optional prediction
This commit is contained in:
wlinator 2024-09-22 17:00:40 +02:00 committed by GitHub
commit e6b3c87fac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 197 additions and 1 deletions

View file

@ -1 +1,2 @@
data/ data/
.venv/

View file

@ -78,6 +78,18 @@
"case_type_field": "Type:", "case_type_field": "Type:",
"case_type_field_value": "`{0}`", "case_type_field_value": "`{0}`",
"case_type_field_value_with_duration": "`{0} ({1})`", "case_type_field_value_with_duration": "`{0} ({1})`",
"coinflip_correct_prediction_author": "Correct Prediction!",
"coinflip_correct_prediction_description": "the coin landed on **{0}**. You predicted correctly!",
"coinflip_flipping_animation_1": "\ud83e\ude99 Flipping...",
"coinflip_flipping_animation_2": "\ud83e\ude99 Flipping..",
"coinflip_flipping_animation_3": "\ud83e\ude99 Flipping.",
"coinflip_flipping_author": "flipping a Coin",
"coinflip_flipping_description": "the coin is in the air...",
"coinflip_invalid_prediction_description": "please enter a valid prediction ('heads'/'h' or 'tails'/'t').",
"coinflip_result_author": "Coin Flip Result",
"coinflip_result_description": "the coin landed on **{0}**.",
"coinflip_wrong_prediction_author": "Wrong Prediction",
"coinflip_wrong_prediction_description": "the coin landed on **{0}**. Your prediction was incorrect.",
"config_author": "Server Configuration", "config_author": "Server Configuration",
"config_birthday_channel_set": "birthday announcements will be sent in {0}.", "config_birthday_channel_set": "birthday announcements will be sent in {0}.",
"config_birthday_module_already_disabled": "the birthday module was already disabled.", "config_birthday_module_already_disabled": "the birthday module was already disabled.",
@ -150,6 +162,7 @@
"error_actionable_hierarchy_bot": "I don't have permission to perform this action on this user due to role hierarchy.", "error_actionable_hierarchy_bot": "I don't have permission to perform this action on this user due to role hierarchy.",
"error_actionable_hierarchy_user": "you don't have permission to perform this action on this user due to role hierarchy.", "error_actionable_hierarchy_user": "you don't have permission to perform this action on this user due to role hierarchy.",
"error_actionable_self": "you can't perform this action on yourself.", "error_actionable_self": "you can't perform this action on yourself.",
"error_already_flipping_coin_description": "you already have a coinflip running.",
"error_already_playing_blackjack": "you already have a game of blackjack running.", "error_already_playing_blackjack": "you already have a game of blackjack running.",
"error_birthdays_disabled_author": "Birthdays Disabled", "error_birthdays_disabled_author": "Birthdays Disabled",
"error_birthdays_disabled_description": "birthdays are disabled in this server.", "error_birthdays_disabled_description": "birthdays are disabled in this server.",

182
modules/economy/coinflip.py Normal file
View file

@ -0,0 +1,182 @@
import asyncio
import random
import discord
from discord import app_commands
from discord.ext import commands
import lib.format
from lib.client import Luminara
from lib.const import CONST
from lib.exceptions import LumiException
from ui.embeds import Builder
PREDICTION_MAPPING = {
"h": "heads",
"head": "heads",
"heads": "heads",
"t": "tails",
"tail": "tails",
"tails": "tails",
}
COIN_FLIP_DELAY = 0.5 # seconds
class ActiveCoinflips:
def __init__(self):
self._flips: set[int] = set()
def add(self, user_id: int) -> bool:
if user_id in self._flips:
return False
self._flips.add(user_id)
return True
def remove(self, user_id: int) -> None:
self._flips.discard(user_id)
class Coinflip(commands.Cog):
def __init__(self, bot: Luminara) -> None:
self.bot: Luminara = bot
self.coinflip.usage = lib.format.generate_usage(self.coinflip)
self.active_coinflips = ActiveCoinflips()
@commands.command(
name="coinflip",
aliases=["cf"],
)
@commands.guild_only()
async def coinflip(
self,
ctx: commands.Context[Luminara],
*,
prediction: str | None = None,
) -> None:
"""
Flip a coin. Optionally predict the outcome.
Parameters
----------
ctx : commands.Context[Luminara]
The context of the command.
prediction : str, optional
The predicted outcome ('heads', 'h', 'tails', or 't').
"""
await self._coinflip(ctx, prediction)
@app_commands.command(name="coinflip", description="Flip a coin. Optionally predict the outcome.")
@app_commands.guild_only()
@app_commands.choices(
prediction=[
app_commands.Choice(name="Heads", value="heads"),
app_commands.Choice(name="Tails", value="tails"),
],
)
async def coinflip_slash(
self,
interaction: discord.Interaction,
prediction: app_commands.Choice[str] | None = None,
) -> None:
"""
Flip a coin. Optionally predict the outcome.
Parameters
----------
interaction : discord.Interaction
The interaction of the command.
prediction : app_commands.Choice[str], optional
The predicted outcome ('heads' or 'tails').
"""
await self._coinflip(interaction, prediction.value if prediction else None)
async def _coinflip(self, ctx: commands.Context[Luminara] | discord.Interaction, prediction: str | None) -> None:
if isinstance(ctx, commands.Context):
author = ctx.author
reply = ctx.reply
else:
author = ctx.user
reply = ctx.followup.send
if not self.active_coinflips.add(author.id):
raise LumiException(CONST.STRINGS["error_already_flipping_coin_description"])
try:
result = random.choice(["heads", "tails"])
if prediction:
prediction = PREDICTION_MAPPING.get(prediction.lower())
if not prediction:
raise LumiException(CONST.STRINGS["coinflip_invalid_prediction_description"])
flip_embed = Builder.create_embed(
Builder.INFO,
user_name=author.name,
author_text=CONST.STRINGS["coinflip_flipping_author"],
description=CONST.STRINGS["coinflip_flipping_description"],
)
if isinstance(ctx, commands.Context):
flip_message = await reply(embed=flip_embed)
else:
await ctx.response.send_message(embed=flip_embed)
flip_message = await ctx.original_response()
# Add a short delay before starting the coin flip animation
await asyncio.sleep(COIN_FLIP_DELAY)
if isinstance(flip_message, discord.Message):
await self._animate_coin_flip(flip_message, author.name)
if prediction:
predicted_correctly = prediction == result
embed_type = Builder.SUCCESS if predicted_correctly else Builder.ERROR
author_text = CONST.STRINGS[
"coinflip_correct_prediction_author" if predicted_correctly else "coinflip_wrong_prediction_author"
]
description = CONST.STRINGS[
"coinflip_correct_prediction_description"
if predicted_correctly
else "coinflip_wrong_prediction_description"
].format(result)
embed = Builder.create_embed(
embed_type,
user_name=author.name,
author_text=author_text,
description=description,
)
else:
embed = Builder.create_embed(
Builder.INFO,
user_name=author.name,
author_text=CONST.STRINGS["coinflip_result_author"],
description=CONST.STRINGS["coinflip_result_description"].format(result),
)
if flip_message is not None:
await flip_message.edit(embed=embed)
finally:
self.active_coinflips.remove(author.id)
async def _animate_coin_flip(self, flip_message: discord.Message, author_name: str) -> None:
animations = [
CONST.STRINGS["coinflip_flipping_animation_1"],
CONST.STRINGS["coinflip_flipping_animation_2"],
CONST.STRINGS["coinflip_flipping_animation_3"],
]
for animation in animations:
flip_embed = Builder.create_embed(
Builder.INFO,
user_name=author_name,
author_text=CONST.STRINGS["coinflip_flipping_author"],
description=animation,
)
await flip_message.edit(embed=flip_embed)
await asyncio.sleep(COIN_FLIP_DELAY)
async def setup(bot: Luminara) -> None:
await bot.add_cog(Coinflip(bot))

View file

@ -88,7 +88,7 @@ info:
title: Luminara title: Luminara
author: wlinator author: wlinator
license: GNU General Public License v3.0 license: GNU General Public License v3.0
version: "3.0.1" version: "3.1.0"
repository_url: https://git.wlinator.org/Luminara/Lumi repository_url: https://git.wlinator.org/Luminara/Lumi
invite_url: https://discord.com/oauth2/authorize?client_id=1038050427272429588&permissions=8&scope=bot invite_url: https://discord.com/oauth2/authorize?client_id=1038050427272429588&permissions=8&scope=bot