diff --git a/README.md b/README.md
index c3af4191..e734a123 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ To install any cog you want:
| AutoPublisyher | Automatically push news channel messages. | 2.5.0 | ltzmax, AAA3A | ✅ |
| Counting | Count from 1 to infinity! with some customizable settings. | 1.3.3 | ltzmax | ✅ |
| GitHub | GitHub RSS Commit Feeds | 1.0.1 | Obi-Wan3, ltzmax | ✅ |
+| GuessFlag | Play A game of Guess the flag of a country. | 1.0.0 | ltzmax | ✅ |
| lockdown | Let moderators lockdown a channel to prevent messages from being sent. | 1.3.0 | ltzmax | ✅ |
| nbaschedule | Get the current NBA schedule for next game. | 1.0.1 | ltzmax | ✅ |
| NoSpoiler | Prevent using spoiler messages | 1.8.0 | ltzmax | ✅ |
diff --git a/docs/GuessFlag.md b/docs/GuessFlag.md
new file mode 100644
index 00000000..a051d8ff
--- /dev/null
+++ b/docs/GuessFlag.md
@@ -0,0 +1,29 @@
+Guess the flag of a country game.
Win credits to your `[p]bank balance` if you guess the flag correctly and lose credits from your `[p]bank balance` if you guess it wrong.
+
+# [p]guessflagset
+Settings for Guess the Flag game.
+ - Usage: `[p]guessflagset`
+ - Restricted to: `BOT_OWNER`
+## [p]guessflagset view
+View the current settings.
+ - Usage: `[p]guessflagset view`
+## [p]guessflagset creditloss
+Set the amount of credits to lose.
+ - Usage: `[p]guessflagset creditloss `
+## [p]guessflagset creditwin
+Set the amount of credits to win.
+ - Usage: `[p]guessflagset creditwin `
+# [p]guessflag
+Play A game of Guess the flag of a country.
+
+You can only play this game once every 60 seconds.
+
+You have 30 seconds to guess the flag of a country.
+You will win credits if you guess the flag correctly and lose credits if you guess it wrong.
+
+The default credit win amount is 100 and the default credit loss amount is 50 credits but can be changed by the bot owner.
+ - Usage: `[p]guessflag`
+ - Cooldown: `1 per 60.0 seconds`
+## [p]guessflag stats
+View your stats or another user's stats.
+ - Usage: `[p]guessflag stats [user=None]`
diff --git a/guessflag/__init__.py b/guessflag/__init__.py
new file mode 100644
index 00000000..cf9590f8
--- /dev/null
+++ b/guessflag/__init__.py
@@ -0,0 +1,10 @@
+from redbot.core.bot import Red
+
+from .guessflag import GuessFlag
+
+__red_end_user_data_statement__ = "This cog does not persistently store data about users."
+
+
+async def setup(bot: Red) -> None:
+ cog = GuessFlag(bot)
+ await bot.add_cog(cog)
diff --git a/guessflag/guessflag.py b/guessflag/guessflag.py
new file mode 100644
index 00000000..6ebbf520
--- /dev/null
+++ b/guessflag/guessflag.py
@@ -0,0 +1,211 @@
+import asyncio
+import logging
+import random
+
+import aiohttp
+import discord
+import orjson
+from redbot.core import Config, bank, commands
+from redbot.core.errors import BalanceTooHigh
+from redbot.core.utils.chat_formatting import box, humanize_number
+from tabulate import tabulate
+
+log = logging.getLogger("red.maxcogs.guessflag")
+
+
+async def fetch_flags():
+ async with aiohttp.ClientSession() as session:
+ async with session.get("https://restcountries.com/v3.1/all") as response:
+ if response.status == 200:
+ countries = orjson.loads(await response.read())
+ flags = {
+ country["name"]["common"]: country["flags"]["png"] for country in countries
+ }
+ return flags
+ else:
+ log.error(f"Failed to fetch flags: {response.status}")
+ return {}
+
+
+def get_random_flag(flags):
+ country, flag_url = random.choice(list(flags.items()))
+ options = random.sample(list(flags.keys()), 24)
+ if country not in options:
+ options.append(country)
+ random.shuffle(options)
+ return country, flag_url, options
+
+
+class GuessFlag(commands.Cog):
+ """
+ Play A game of Guess the flag of a country.
+
+ Win credits to your `[p]bank balance` if you guess the flag correctly and lose credits from your `[p]bank balance` if you guess it wrong.
+ """
+
+ __version__: Final[str] = "1.0.0"
+ __author__: Final[str] = "MAX"
+ __docs__: Final[str] = "https://github.com/ltzmax/maxcogs/blob/master/docs/GuessFlag.md"
+
+ def __init__(self, bot):
+ self.bot = bot
+ self.flags = None
+ bot.loop.create_task(self.load_flags())
+ self.config = Config.get_conf(self, identifier=9008567, force_registration=True)
+ default_global = {"default_credit_win": 100, "default_credit_loss": 50}
+ default_user = {"stats_won": 0, "stats_lost": 0}
+ self.config.register_user(**default_user)
+ self.config.register_global(**default_global)
+
+ def format_help_for_context(self, ctx: commands.Context) -> str:
+ """Thanks Sinbad!"""
+ pre_processed = super().format_help_for_context(ctx)
+ return f"{pre_processed}\n\nAuthor: {self.__author__}\nCog Version: {self.__version__}\nDocs: {self.__docs__}"
+
+ async def red_delete_data_for_user(self, **kwargs: Any) -> None:
+ """Nothing to delete."""
+ return
+
+ async def load_flags(self):
+ self.flags = await fetch_flags()
+
+ @commands.group()
+ @commands.is_owner()
+ async def guessflagset(self, ctx):
+ """
+ Settings for Guess the Flag game.
+ """
+
+ @guessflagset.command()
+ async def creditwin(self, ctx, amount: commands.Range[int, 50, 1000000000]):
+ """Set the amount of credits to win."""
+ await self.config.default_credit_win.set(amount)
+ await ctx.send(f"Credit win amount set to {amount}.")
+
+ @guessflagset.command()
+ async def creditloss(self, ctx, amount: commands.Range[int, 50, 1000000000]):
+ """Set the amount of credits to lose."""
+ await self.config.default_credit_loss.set(amount)
+ await ctx.send(f"Credit loss amount set to {amount}.")
+
+ @guessflagset.command()
+ async def view(self, ctx):
+ """View the current settings."""
+ credit_win = await self.config.default_credit_win()
+ credit_loss = await self.config.default_credit_loss()
+ await ctx.send(f"Credit win amount: {credit_win}\nCredit loss amount: {credit_loss}")
+
+ @commands.group(invoke_without_command=True)
+ @commands.bot_has_permissions(embed_links=True)
+ @commands.cooldown(1, 60, commands.BucketType.user)
+ async def guessflag(self, ctx):
+ """
+ Play A game of Guess the flag of a country.
+
+ You can only play this game once every 60 seconds.
+
+ You have 30 seconds to guess the flag of a country.
+ You will win credits if you guess the flag correctly and lose credits if you guess it wrong.
+
+ The default credit win amount is 100 and the default credit loss amount is 50 credits but can be changed by the bot owner.
+ """
+ if not self.flags:
+ return await ctx.send("Flags are still loading, please try again later.")
+
+ country, flag_url, options = get_random_flag(self.flags)
+
+ embed = discord.Embed(
+ title="Guess the Flag!",
+ description="You have 30 seconds to guess the flag.",
+ color=await ctx.embed_color(),
+ )
+ embed.set_image(url=flag_url)
+
+ view = GuessFlagView(ctx.author, country, options, self.config)
+ message = await ctx.send(embed=embed, view=view)
+ view.message = message
+
+ @guessflag.command()
+ async def stats(self, ctx, user: discord.User = None):
+ """View your stats or another user's stats."""
+ user = user or ctx.author
+
+ if (
+ not await self.config.user(user).stats_won()
+ and not await self.config.user(user).stats_lost()
+ ):
+ return await ctx.send("You haven't played this game yet.")
+
+ stats_won = await self.config.user(user).stats_won()
+ stats_lost = await self.config.user(user).stats_lost()
+ table = tabulate(
+ [["Won", stats_won], ["Lost", stats_lost]],
+ headers=["Title", "Stats"],
+ tablefmt="simple",
+ )
+ await ctx.send(box(table, lang="prolog"))
+
+
+# Too lazy to make a separate file for this class
+# This class is for the buttons in the GuessFlag game view
+class GuessFlagView(discord.ui.View):
+ def __init__(self, author, correct_country, options, config):
+ super().__init__(timeout=30)
+ self.author = author
+ self.correct_country = correct_country
+ self.config = config
+ for option in options:
+ button = discord.ui.Button(
+ label=option, style=discord.ButtonStyle.primary, custom_id=option
+ )
+ button.callback = self.button_callback
+ self.add_item(button)
+
+ async def on_timeout(self) -> None:
+ for item in self.children:
+ item: discord.ui.Item
+ item.disabled = True
+ try:
+ if self.message:
+ await self.message.edit(view=self)
+ except discord.HTTPException:
+ log.error("Failed to edit message.", exc_info=True)
+ pass
+
+ async def button_callback(self, interaction):
+ if interaction.user != self.author:
+ return await interaction.response.send_message(
+ "This is not your game!", ephemeral=True
+ )
+
+ for item in self.children:
+ item.disabled = True
+ if item.custom_id == self.correct_country:
+ item.style = discord.ButtonStyle.success # Green for correct answer
+ else:
+ item.style = discord.ButtonStyle.danger # Red for wrong answers
+
+ credit = await bank.get_balance(interaction.user)
+ credit_name = await bank.get_currency_name(interaction.user)
+ credit_win = await self.config.default_credit_win()
+ credit_loss = await self.config.default_credit_loss()
+ stats_lost = await self.config.user(interaction.user).stats_lost()
+ stats_won = await self.config.user(interaction.user).stats_won()
+ if interaction.data["custom_id"] == self.correct_country:
+ try:
+ await bank.deposit_credits(interaction.user, credit_win)
+ except BalanceTooHigh as e:
+ return await interaction.response.send_message(
+ "You have reached the maximum balance.", ephemeral=True
+ )
+ await self.config.user(interaction.user).stats_won.set(stats_won + 1)
+ await interaction.response.send_message(
+ f"Correct {interaction.user.mention}! You got {credit_win} {credit_name}."
+ )
+ else:
+ await bank.withdraw_credits(interaction.user, credit_loss)
+ await self.config.user(interaction.user).stats_lost.set(stats_lost + 1)
+ await interaction.response.send_message(
+ f"Wrong answer {interaction.user.mention}! It's {self.correct_country} and you lost {credit_loss} {credit_name}."
+ )
+ await interaction.message.edit(view=self)
diff --git a/guessflag/info.json b/guessflag/info.json
new file mode 100644
index 00000000..56e738cb
--- /dev/null
+++ b/guessflag/info.json
@@ -0,0 +1,26 @@
+{
+ "author": [
+ "max"
+ ],
+ "name": "GuessFlag",
+ "install_msg": "Thanks for installing.",
+ "description": "Win credits to your `[p]bank balance` if you guess the flag correctly and lose credits from your `[p]bank balance` if you guess it wrong.",
+ "short": "Play A game of Guess the flag of a country.",
+ "hidden": false,
+ "min_bot_version": "3.5.13",
+ "tags": [
+ "economy",
+ "credits"
+ ],
+ "permissions": [
+ "manage_messages"
+ ],
+ "requirements": ["tabulate"],
+ "min_python_version": [
+ 3,
+ 9,
+ 0
+ ],
+ "end_user_data_statement": "This cog does not store any End User Data.",
+ "type": "COG"
+}