diff --git a/.env.template b/.env.template deleted file mode 100644 index e0ed7b7599035e927ab80d67dd0e7d4ea4a2777f..0000000000000000000000000000000000000000 --- a/.env.template +++ /dev/null @@ -1,9 +0,0 @@ -# General -DISCORD_TOKEN=<Bot Token> -DISCORD_ACTIVITY=<What should be shown, Bot is playing right now> -DISCORD_PROD=<True, if running in an productive environment, otherwise False (neccessary for a couple of features like private threads, that are only available on our productive environment)> - -# IDs -DISCORD_ELM_STREET_CHANNEL=<ID of elm street channel> -DISCORD_HALLOWEEN_CATEGORY=<ID of Halloween category> - diff --git a/.gitignore b/.gitignore index 4ac064f1cd7569e5a87fe9cab128d1881f3e7cbf..438cdf0832136ed56299f6bb586abd9266caad9e 100644 --- a/.gitignore +++ b/.gitignore @@ -117,3 +117,4 @@ GitHub.sublime-settings .history /data/*.json !/data/story.json +/config.json diff --git a/extensions/elm_street.py b/deprecated/extensions/elm_street.py similarity index 100% rename from extensions/elm_street.py rename to deprecated/extensions/elm_street.py diff --git a/images/20220928_142617.jpg b/deprecated/images/20220928_142617.jpg similarity index 100% rename from images/20220928_142617.jpg rename to deprecated/images/20220928_142617.jpg diff --git a/images/20220928_143119.jpg b/deprecated/images/20220928_143119.jpg similarity index 100% rename from images/20220928_143119.jpg rename to deprecated/images/20220928_143119.jpg diff --git a/images/20220928_143134.jpg b/deprecated/images/20220928_143134.jpg similarity index 100% rename from images/20220928_143134.jpg rename to deprecated/images/20220928_143134.jpg diff --git a/images/20220928_151142.jpg b/deprecated/images/20220928_151142.jpg similarity index 100% rename from images/20220928_151142.jpg rename to deprecated/images/20220928_151142.jpg diff --git a/images/20220928_151227.jpg b/deprecated/images/20220928_151227.jpg similarity index 100% rename from images/20220928_151227.jpg rename to deprecated/images/20220928_151227.jpg diff --git a/images/20220928_151233.jpg b/deprecated/images/20220928_151233.jpg similarity index 100% rename from images/20220928_151233.jpg rename to deprecated/images/20220928_151233.jpg diff --git a/images/20220928_151251.jpg b/deprecated/images/20220928_151251.jpg similarity index 100% rename from images/20220928_151251.jpg rename to deprecated/images/20220928_151251.jpg diff --git a/images/20220928_151535.jpg b/deprecated/images/20220928_151535.jpg similarity index 100% rename from images/20220928_151535.jpg rename to deprecated/images/20220928_151535.jpg diff --git a/images/20220928_152325.jpg b/deprecated/images/20220928_152325.jpg similarity index 100% rename from images/20220928_152325.jpg rename to deprecated/images/20220928_152325.jpg diff --git a/images/20220928_152359.jpg b/deprecated/images/20220928_152359.jpg similarity index 100% rename from images/20220928_152359.jpg rename to deprecated/images/20220928_152359.jpg diff --git a/images/20220928_152754.jpg b/deprecated/images/20220928_152754.jpg similarity index 100% rename from images/20220928_152754.jpg rename to deprecated/images/20220928_152754.jpg diff --git a/images/20220928_152812.jpg b/deprecated/images/20220928_152812.jpg similarity index 100% rename from images/20220928_152812.jpg rename to deprecated/images/20220928_152812.jpg diff --git a/images/20220928_152925.jpg b/deprecated/images/20220928_152925.jpg similarity index 100% rename from images/20220928_152925.jpg rename to deprecated/images/20220928_152925.jpg diff --git a/images/20220928_153911.jpg b/deprecated/images/20220928_153911.jpg similarity index 100% rename from images/20220928_153911.jpg rename to deprecated/images/20220928_153911.jpg diff --git a/images/20220928_154705.jpg b/deprecated/images/20220928_154705.jpg similarity index 100% rename from images/20220928_154705.jpg rename to deprecated/images/20220928_154705.jpg diff --git a/images/20220928_155339.jpg b/deprecated/images/20220928_155339.jpg similarity index 100% rename from images/20220928_155339.jpg rename to deprecated/images/20220928_155339.jpg diff --git a/images/20220928_160658.jpg b/deprecated/images/20220928_160658.jpg similarity index 100% rename from images/20220928_160658.jpg rename to deprecated/images/20220928_160658.jpg diff --git a/images/20220928_184010.jpg b/deprecated/images/20220928_184010.jpg similarity index 100% rename from images/20220928_184010.jpg rename to deprecated/images/20220928_184010.jpg diff --git a/images/Felskapelle.jpg b/deprecated/images/Felskapelle.jpg similarity index 100% rename from images/Felskapelle.jpg rename to deprecated/images/Felskapelle.jpg diff --git a/images/Midjourney-Herbsthaus1_.png b/deprecated/images/Midjourney-Herbsthaus1_.png similarity index 100% rename from images/Midjourney-Herbsthaus1_.png rename to deprecated/images/Midjourney-Herbsthaus1_.png diff --git a/images/Midjourney-Herbsthaus2.png b/deprecated/images/Midjourney-Herbsthaus2.png similarity index 100% rename from images/Midjourney-Herbsthaus2.png rename to deprecated/images/Midjourney-Herbsthaus2.png diff --git a/images/Midjourney-Herbsthaus3.png b/deprecated/images/Midjourney-Herbsthaus3.png similarity index 100% rename from images/Midjourney-Herbsthaus3.png rename to deprecated/images/Midjourney-Herbsthaus3.png diff --git a/images/Midjourney-Tuergesicht.png b/deprecated/images/Midjourney-Tuergesicht.png similarity index 100% rename from images/Midjourney-Tuergesicht.png rename to deprecated/images/Midjourney-Tuergesicht.png diff --git a/images/wildeGestallten.jpg b/deprecated/images/wildeGestallten.jpg similarity index 100% rename from images/wildeGestallten.jpg rename to deprecated/images/wildeGestallten.jpg diff --git a/extensions/click_game.py b/extensions/click_game.py new file mode 100644 index 0000000000000000000000000000000000000000..e7aa064c17b72f31c72dbc1e962ffabbb722bea9 --- /dev/null +++ b/extensions/click_game.py @@ -0,0 +1,167 @@ +import json +import os +import random +from asyncio import sleep +from copy import deepcopy +from os.path import exists +from random import SystemRandom +from typing import Union, Dict +from datetime import datetime, timedelta + +import discord +from discord import app_commands, Guild, Interaction, ButtonStyle, File, Message, Embed +from discord.app_commands import Choice +from discord.ext import commands, tasks +from discord.utils import escape_markdown +from dotenv import load_dotenv + +from utils import send_dm + +load_dotenv() + + +@app_commands.guild_only() +class ClickGame(commands.Cog): + def __init__(self, bot): + self.bot = bot + self.config: Dict = bot.config["extensions"][__name__.split(".")[-1]] + self.bot.view_manager.register("on_click", self.on_click) + self.msg_count: int = 0 + self.last_sent: datetime = datetime(1970, 1, 1) + + @commands.Cog.listener() + async def on_message(self, message: Message): + if message.author == self.bot.user: + return + + if message.channel.id not in self.config["channels"]: + return + + self.msg_count += 1 + if self.msg_count < self.config["min_msg_count"]: + print("Too few messages sent.") + return + + if self.last_sent + timedelta(minutes=self.config["cooldown"]) > datetime.now(): + print("Too early, sorry.") + return + + if random.random() > self.config["probability"]: + print("Nope, better luck next time!") + return + + await self.send_message(message.channel) + + async def send_message(self, channel) -> None: + img = self.get_random_image() + plus_emoji, minus_emoji = self.get_random_emojis() + file = discord.File(f"images/{img}", filename=img) + embed = Embed(title="Vorsicht!", description=self.get_random_description(plus_emoji, minus_emoji)) + embed.set_image(url=f"attachment://{img}") + await channel.send(file=file, embed=embed, view=self.get_view(plus_emoji, minus_emoji)) + + self.last_sent = datetime.now() + self.msg_count = 0 + + def get_random_image(self) -> str: + images = os.listdir("images/") + return random.choice(images) + + def get_random_emojis(self): + plus_emoji = random.choice(self.config["emojis"]) + while True: + minus_emoji = random.choice(self.config["emojis"]) + if plus_emoji != minus_emoji: + return plus_emoji, minus_emoji + + def get_random_description(self, plus_emoji, minus_emoji): + texts = [ + f"Oh nein, pass auf! Klicke auf {plus_emoji}, um das Monster in die Flucht zu schlagen! Klicke auf {minus_emoji} um das Monster zu stärken!", + f"Oh nein, pass auf! Klicke auf {minus_emoji} um das Monster zu stärken! Klicke auf {plus_emoji}, um das Monster in die Flucht zu schlagen!"] + return random.choice(texts) + + @app_commands.command(name="leaderboard", + description="Zeigt das Leaderboard der Elm Street Sammlerinnen-Gemeinschaft an.") + @app_commands.choices(show=[Choice(name='10', value=10), Choice(name='all', value=0)]) + @app_commands.guild_only() + async def cmd_leaderboard(self, interaction: Interaction, show: int = 10): + await interaction.response.defer(ephemeral=True) + leaderboard = await self.get_leaderboard(interaction.guild, max_entries=show) + await interaction.followup.send(content=leaderboard, ephemeral=True) + + async def on_click(self, button: discord.ui.Button, interaction: Interaction, value=None): + if value > 0: + await interaction.response.send_message( + f"Super! Du hast das Monster geschwächt und erhältst zur Belohnung {value} Punkte.", ephemeral=True) + button.value -= 1 + elif value < 0: + await interaction.response.send_message( + f"Oh nein! Du hast das Monster gestärkt. Es greift dich an und du bekommst {value} Punkte abgezogen.", + ephemeral=True) + button.value += 1 + else: + interaction.response.send_message("Du warst zu spät. Glücklicherweise wurde das Monster bereits besiegt.", + ephemeral=True) + + if button.value == 0 and interaction.message: + await interaction.message.delete() + + def get_view(self, plus_emoji, minus_emoji): + buttons = [ + {"style": ButtonStyle.gray, "value": 5, "custom_id": "click_game:plus", "emoji": plus_emoji}, + {"style": ButtonStyle.gray, "value": -5, "custom_id": "click_game:minus", "emoji": minus_emoji} + ] + return self.bot.view_manager.view(buttons, "on_click") + + async def get_leaderboard(self, guild: Guild, max_entries: int = 10): + message = f"**__Elm-Street Leaderboard__**\n\n" \ + f"Wie süß bist du wirklich??\n" \ + f"{':jack_o_lantern: ' * 8}\n\n" \ + f"```md\n" \ + f"Rank. | Items | User\n" \ + f"==================================================\n" + + place = 0 + + ready = False + last_score = -1 + for player_id, player_data in sorted(self.players.items(), key=lambda item: item[1]["sweets"], reverse=True): + value = player_data["sweets"] + member = await guild.fetch_member(int(player_id)) + try: + if last_score != value: + place += 1 + last_score = value + if 0 < max_entries < place: + if ready: + break + message += f"{str(place).rjust(4)}. | {str(value).rjust(5)} | " + message += f"{member.display_name}#{member.discriminator}\n" + except: + pass + + message += f"```" + + return message + + @tasks.loop(minutes=5) + async def increase_courage(self): + pass + + @increase_courage.before_loop + async def before_increase(self): + pass + # await sleep(10) + + # @commands.Cog.listener(name="on_voice_state_update") + # async def voice_state_changed(self, member, before, after): + # if not after.channel: + # voice_channel_left = before.channel + # if len(voice_channel_left.members) == 0 and \ + # voice_channel_left.category_id == self.halloween_category_id and \ + # not self.get_group_by_voice_id(voice_channel_left.id): + # await voice_channel_left.delete() + + +async def setup(bot: commands.Bot) -> None: + await bot.add_cog(ClickGame(bot)) diff --git a/halloween.py b/halloween.py index a1ef61d6b9ca9b6bf84125c588546f632459e7f8..5cbe3c9e1810d08be2f044a9301cbc967c95c8bd 100644 --- a/halloween.py +++ b/halloween.py @@ -1,26 +1,22 @@ -import os +import json from discord import Intents, Game from discord.ext import commands -from dotenv import load_dotenv -from typing import List +from typing import Dict from view_manager import ViewManager -# .env file is necessary in the same directory, that contains several strings. -load_dotenv() - class HalloweenBot(commands.Bot): - def __init__(self, *args, initial_extensions: List[str], **kwargs): + def __init__(self, *args, config: Dict, **kwargs): super().__init__(*args, **kwargs) - self.is_prod = os.getenv("DISCORD_PROD") == "True" - self.initial_extensions: List[str] = initial_extensions + self.config = config + self.is_prod = config["is_prod"] self.view_manager: ViewManager = ViewManager(self) self.persistent_views_added: bool = False async def setup_hook(self) -> None: - for extension in self.initial_extensions: + for extension, _ in self.config["extensions"].items(): await self.load_extension(f"extensions.{extension}") await self.tree.sync() @@ -28,7 +24,12 @@ class HalloweenBot(commands.Bot): self.view_manager.on_ready() -extensions = ["elm_street"] -bot = HalloweenBot(command_prefix='!', help_command=None, activity=Game(os.getenv('DISCORD_ACTIVITY')), - intents=Intents.all(), initial_extensions=extensions) -bot.run(os.getenv('DISCORD_TOKEN')) +def load_config(): + fp = open("config.json", mode="r") + return json.load(fp) + + +config = load_config() +bot = HalloweenBot(command_prefix='!', help_command=None, activity=Game(config["activity"]), + intents=Intents.all(), config=config) +bot.run(config["token"]) diff --git a/images/img01.png b/images/img01.png new file mode 100644 index 0000000000000000000000000000000000000000..7945985f0d2dddd26ee1346ee0a79fc058c65190 Binary files /dev/null and b/images/img01.png differ diff --git a/images/img02.png b/images/img02.png new file mode 100644 index 0000000000000000000000000000000000000000..36fd22528423cf5849df588ad0c022c704b5de27 Binary files /dev/null and b/images/img02.png differ diff --git a/images/img03.png b/images/img03.png new file mode 100644 index 0000000000000000000000000000000000000000..c8b9a51bc978f151dc8ff86227be0713cb64a28c Binary files /dev/null and b/images/img03.png differ diff --git a/requirements.txt b/requirements.txt index d21f5e18b67b4e58dcd174f300a7f8a86480e1fe..95a91b54dd7a0bbddd2dd386b51c6b822e0c590d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,10 @@ -discord.py==2.0.1 -python-dotenv==0.21.0 +aiohttp==3.8.5 +aiosignal==1.3.1 +async-timeout==4.0.3 +attrs==23.1.0 +charset-normalizer==3.2.0 +discord.py==2.3.2 +frozenlist==1.4.0 +idna==3.4 +multidict==6.0.4 +yarl==1.9.2