├── .gitignore ├── APIs ├── api.py ├── chat_gpt.py ├── elevenlabs_api.py ├── reddit.py └── translate_api.py ├── Einzelvideos ├── automod.py ├── bild_datei_embed.py ├── bot_inviter.py ├── chat_export.py ├── easypil.py ├── get_channel.py ├── hex_color.py ├── level_system.py ├── radio.py ├── special_dropdowns.py ├── werte_an_klassen_übergeben.py └── youtube_notification.py ├── README.md ├── Template ├── bot_class.py ├── cogs │ └── base.py ├── example.env └── main.py ├── Tutorialreihe ├── part02_events.py ├── part03_slash_commands.py ├── part04_embeds.py ├── part05_cogs.py ├── part06_bot_status.py ├── part07_tasks.py ├── part08_error_handling.py ├── part09_checks.py ├── part10_cooldowns.py ├── part11_buttons.py ├── part12_context_menu.py ├── part13_modals.py ├── part14_wait_for_event.py ├── part15_select_menu_dropdown.py ├── part16_paginator.py ├── part17_autocomplete.py ├── part18_interaction_followup.py ├── part19_command_groups.py ├── part20_original_response.py ├── part21_custom_checks.py ├── part22_global_checks.py ├── part23_option_decorator.py ├── part24_converter.py ├── part26_webhooks.py ├── part27_audio_recording.py └── part28_view_wait.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .idea 3 | .env 4 | venv -------------------------------------------------------------------------------- /APIs/api.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | import requests 6 | import random 7 | import os 8 | 9 | from pprint import pprint 10 | 11 | 12 | class API(commands.Cog): 13 | def __init__(self, bot): 14 | self.bot = bot 15 | 16 | @slash_command() 17 | async def keks(self, ctx): 18 | key = os.getenv("API_KEY") 19 | 20 | params = { 21 | "q": "keks", 22 | "key": key, 23 | "limit": "10", 24 | "client_key": "discord_bot", 25 | "media_filter": "gif" 26 | } 27 | result = requests.get("https://tenor.googleapis.com/v2/search", params=params) 28 | data = result.json() 29 | # pprint(data) 30 | 31 | number = random.randint(0, 9) 32 | url = data['results'][number]['media_formats']['gif']['url'] 33 | 34 | embed = discord.Embed( 35 | title="Keks", 36 | color=discord.Color.yellow() 37 | ) 38 | embed.set_image(url=url) 39 | embed.set_footer(text="Via Tenor") 40 | await ctx.respond(embed=embed) 41 | 42 | 43 | def setup(bot): 44 | bot.add_cog(API(bot)) 45 | -------------------------------------------------------------------------------- /APIs/chat_gpt.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command, Option 4 | 5 | import openai 6 | openai.api_key = "KEY" # hier den api key einfügen 7 | 8 | 9 | class GPT(commands.Cog): 10 | def __init__(self, bot): 11 | self.bot = bot 12 | 13 | @slash_command() 14 | async def gpt(self, ctx, text: Option(str)): 15 | await ctx.defer() 16 | result = openai.ChatCompletion.create( 17 | model="gpt-3.5-turbo", 18 | messages=[ 19 | { 20 | "role": "system", 21 | "content": "Du bist ein Jugendlicher, der in jeden Satz Füllworte wie 'sheesh', 'bruh', 'digga' und 'ehrenlos' einbaut" 22 | }, 23 | {"role": "user", "content": text} 24 | ], 25 | max_tokens=250 26 | ) 27 | embed = discord.Embed( 28 | color=discord.Color.blurple(), 29 | description=result["choices"][0]["message"]["content"] 30 | ) 31 | await ctx.respond(embed=embed) 32 | 33 | 34 | def setup(bot: discord.Bot): 35 | bot.add_cog(GPT(bot)) 36 | -------------------------------------------------------------------------------- /APIs/elevenlabs_api.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | import discord 4 | import elevenlabs 5 | from discord.commands import slash_command, option 6 | from discord.ext import commands 7 | 8 | 9 | elevenlabs.set_api_key("") # hier den api key einfügen 10 | CHOICES = [ 11 | discord.OptionChoice(name=voice.name, value=voice.voice_id) for voice in elevenlabs.voices() 12 | if voice.category != "premade" 13 | ][:25] 14 | 15 | 16 | class ElevenLabs(commands.Cog): 17 | def __init__(self, bot): 18 | self.bot = bot 19 | 20 | @slash_command() 21 | @option("text") 22 | @option("voice", choices=CHOICES) 23 | async def voice(self, ctx, text: str, voice: str): 24 | await ctx.defer() 25 | 26 | output = elevenlabs.generate( 27 | text=text, 28 | voice=elevenlabs.Voice( 29 | voice_id=voice, 30 | settings=elevenlabs.VoiceSettings( 31 | stability=0.71, similarity_boost=0.5, style=0.0, use_speaker_boost=True 32 | ), 33 | ), 34 | model="eleven_multilingual_v2", 35 | ) 36 | 37 | await ctx.respond( 38 | file=discord.File(io.BytesIO(output), filename="voice.mp3") 39 | ) 40 | 41 | 42 | def setup(bot): 43 | bot.add_cog(ElevenLabs(bot)) 44 | -------------------------------------------------------------------------------- /APIs/reddit.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import asyncpraw 4 | import discord 5 | from discord.ext import commands 6 | from discord.commands import slash_command 7 | 8 | 9 | class Reddit(commands.Cog): 10 | def __init__(self, bot): 11 | self.bot = bot 12 | 13 | @slash_command() 14 | async def meme(self, ctx): 15 | await ctx.defer() 16 | 17 | # hier die daten von reddit einfügen: https://www.reddit.com/prefs/apps/ 18 | async with asyncpraw.Reddit( 19 | client_id="", 20 | client_secret="", 21 | username="", 22 | password="", 23 | user_agent='' 24 | ) as reddit: 25 | subreddit = await reddit.subreddit("ich_iel") 26 | hot = subreddit.hot(limit=25) 27 | 28 | all_posts = [] 29 | async for post in hot: 30 | if not post.is_video: 31 | all_posts.append(post) 32 | 33 | random_post = random.choice(all_posts) 34 | 35 | embed = discord.Embed(title=random_post.title, color=discord.Color.blurple()) 36 | embed.set_image(url=random_post.url) 37 | 38 | await ctx.respond(embed=embed) 39 | 40 | 41 | def setup(bot): 42 | bot.add_cog(Reddit(bot)) 43 | -------------------------------------------------------------------------------- /APIs/translate_api.py: -------------------------------------------------------------------------------- 1 | # Für diesen Code muss der Message Content Intent im Discord Dev Portal 2 | # und in der Main-Datei aktiviert sein. 3 | 4 | import discord 5 | from discord.ext import commands 6 | from discord.commands import message_command 7 | 8 | import aiohttp # Alternative: https://pypi.org/project/deepl/ 9 | 10 | 11 | class Translate(commands.Cog): 12 | def __init__(self, bot): 13 | self.bot = bot 14 | self.session = None 15 | 16 | @commands.Cog.listener() 17 | async def on_ready(self): 18 | self.session = aiohttp.ClientSession() 19 | 20 | @message_command() 21 | async def translate(self, ctx, message): 22 | await ctx.defer() 23 | params = { 24 | "auth_key": "", # Hier den API Key einfügen: https://www.deepl.com/de/pro-api 25 | "text": message.content, 26 | "target_lang": "DE" 27 | } 28 | 29 | async with self.session.get( 30 | "https://api-free.deepl.com/v2/translate", params=params 31 | ) as response: 32 | result = await response.json() 33 | 34 | lang = result["translations"][0]["detected_source_language"] 35 | text = result["translations"][0]["text"] 36 | 37 | embed = discord.Embed(description=text) 38 | embed.set_author(name=message.author, icon_url=message.author.display_avatar.url) 39 | embed.set_footer(text=f"Übersetzt aus: {lang}") 40 | 41 | await ctx.respond(embed=embed) 42 | 43 | 44 | def setup(bot): 45 | bot.add_cog(Translate(bot)) 46 | -------------------------------------------------------------------------------- /Einzelvideos/automod.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.commands import slash_command, Option 3 | from discord.ext import commands 4 | 5 | from datetime import timedelta 6 | 7 | 8 | invite_links = ["*.gg/*", "*discord.com/invite*", "*discord.gg*"] 9 | 10 | 11 | class Base(commands.Cog): 12 | def __init__(self, bot): 13 | self.bot = bot 14 | 15 | @slash_command() 16 | @discord.guild_only() 17 | async def automod(self, ctx: discord.ApplicationContext, log_channel: Option(discord.TextChannel)): 18 | actions = [ 19 | discord.AutoModAction( 20 | action_type=discord.AutoModActionType.block_message, 21 | metadata=discord.AutoModActionMetadata(), 22 | ), 23 | discord.AutoModAction( 24 | action_type=discord.AutoModActionType.send_alert_message, 25 | metadata=discord.AutoModActionMetadata(channel_id=log_channel.id), 26 | ), 27 | discord.AutoModAction( 28 | action_type=discord.AutoModActionType.timeout, 29 | metadata=discord.AutoModActionMetadata(timeout_duration=timedelta(hours=1)), 30 | ), 31 | ] 32 | 33 | await ctx.guild.create_auto_moderation_rule( 34 | name="Anti Invite", 35 | event_type=discord.AutoModEventType.message_send, 36 | trigger_type=discord.AutoModTriggerType.keyword, 37 | trigger_metadata=discord.AutoModTriggerMetadata(keyword_filter=invite_links), 38 | enabled=True, 39 | actions=actions 40 | ) 41 | await ctx.respond("✅ Erfolgreich eingerichtet.", ephemeral=True) 42 | 43 | 44 | def setup(bot): 45 | bot.add_cog(Base(bot)) 46 | -------------------------------------------------------------------------------- /Einzelvideos/bild_datei_embed.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command() 11 | async def bild(self, ctx): 12 | embed = discord.Embed( 13 | title="Lecker", 14 | color=discord.Color.blue() 15 | ) 16 | file = discord.File(f"cookie.png", filename="image.png") 17 | embed.set_image(url="attachment://image.png") 18 | await ctx.respond(embed=embed, file=file) 19 | 20 | 21 | def setup(bot): 22 | bot.add_cog(Base(bot)) 23 | -------------------------------------------------------------------------------- /Einzelvideos/bot_inviter.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | 4 | 5 | class Base(commands.Cog): 6 | def __init__(self, bot): 7 | self.bot = bot 8 | 9 | @commands.Cog.listener() 10 | async def on_guild_join(self, guild): 11 | try: 12 | integrations = await guild.integrations() 13 | except discord.Forbidden: 14 | return 15 | 16 | for integration in integrations: 17 | if isinstance(integration, discord.BotIntegration): 18 | if integration.application.user == self.bot.user: 19 | try: 20 | await integration.user.send("Danke fürs Einladen!") 21 | except discord.Forbidden: 22 | return 23 | break 24 | 25 | 26 | def setup(bot): 27 | bot.add_cog(Base(bot)) 28 | -------------------------------------------------------------------------------- /Einzelvideos/chat_export.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command, Option 4 | 5 | import chat_exporter 6 | import io 7 | 8 | 9 | class Base(commands.Cog): 10 | def __init__(self, bot): 11 | self.bot = bot 12 | 13 | @slash_command() 14 | async def export(self, ctx, anzahl: Option(int, default=100)): 15 | await ctx.defer() 16 | transcript = await chat_exporter.export( 17 | ctx.channel, 18 | limit=anzahl, 19 | bot=self.bot, 20 | tz_info="Europe/Berlin" 21 | ) 22 | 23 | file = discord.File( 24 | io.BytesIO(transcript.encode()), 25 | filename="transcript.html" 26 | ) 27 | 28 | msg = await ctx.send(file=file) 29 | link = await chat_exporter.link(msg) 30 | 31 | embed = discord.Embed( 32 | description=f"Hier ist der Link zum [Transkript]({link})", 33 | color=discord.Color.blurple() 34 | ) 35 | 36 | await ctx.respond(embed=embed) 37 | 38 | 39 | def setup(bot): 40 | bot.add_cog(Base(bot)) 41 | -------------------------------------------------------------------------------- /Einzelvideos/easypil.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | from easy_pil import Editor, Font, load_image_async 6 | 7 | 8 | class Image(commands.Cog): 9 | def __init__(self, bot): 10 | self.bot = bot 11 | 12 | @slash_command() 13 | async def rank(self, ctx): 14 | background = Editor("space.png").resize((800, 250)) 15 | 16 | avatar = await load_image_async(ctx.author.display_avatar.url) 17 | circle_avatar = Editor(avatar).resize((200, 200)).circle_image() 18 | background.paste(circle_avatar, (25, 25)) 19 | 20 | big_text = Font.poppins(size=50, variant="bold") 21 | small_text = Font.poppins(size=30, variant="regular") 22 | 23 | background.text((490, 50), f"{ctx.author}", color="white", font=big_text, align="center") 24 | background.text( 25 | (490, 125), "Du hast 55 XP und bist Level 2", color="#00ced1", font=small_text, align="center" 26 | ) 27 | 28 | file = discord.File(fp=background.image_bytes, filename='rank.png') 29 | return await ctx.respond(file=file) 30 | 31 | 32 | def setup(bot): 33 | bot.add_cog(Image(bot)) 34 | -------------------------------------------------------------------------------- /Einzelvideos/get_channel.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.Cog.listener() 11 | async def on_ready(self): 12 | channel_id = 123456789 # Hier Channel ID einfügen 13 | 14 | # Variante 1 15 | channel = self.bot.get_channel(channel_id) 16 | 17 | # Variante 2 18 | try: 19 | channel = await self.bot.fetch_channel(channel_id) 20 | except discord.Forbidden: 21 | print("Keine Rechte") 22 | return 23 | 24 | # Variante 3 25 | channel = await discord.utils.get_or_fetch(self.bot, "channel", channel_id, default=None) 26 | 27 | if channel is not None: 28 | await channel.send("Kekse sind lecker!") 29 | else: 30 | print("Channel wurde nicht gefunden :(") 31 | 32 | @slash_command() 33 | async def say(self, ctx): 34 | channel_id = 123456789 # Hier Channel ID einfügen 35 | 36 | # Variante 4 37 | channel = ctx.guild.get_channel(channel_id) # Geht auch mit fetch 38 | 39 | # Variante 5 40 | channel = discord.utils.get(ctx.guild.text_channels, id=channel_id) # Geht auch mit dem Namen des Channels 41 | 42 | if channel is not None: 43 | await channel.send("Kekse sind schmackhaft!") 44 | await ctx.respond("Nachricht gesendet.") 45 | else: 46 | await ctx.respond("Channel wurde nicht gefunden :(") 47 | 48 | 49 | def setup(bot): 50 | bot.add_cog(Base(bot)) 51 | -------------------------------------------------------------------------------- /Einzelvideos/hex_color.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command, Option 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command() 11 | async def embed(self, ctx, hexcode: Option(str)): 12 | hex_string = f"0x{hexcode}" 13 | color = int(hex_string, 16) 14 | 15 | embed = discord.Embed( 16 | description="Dies ist ein sehr cooler Text" 17 | "\n\n🔹 Kekse sind lecker", 18 | color=color 19 | ) 20 | embed.set_thumbnail(url=ctx.author.display_avatar) 21 | await ctx.respond(embed=embed) 22 | 23 | 24 | def setup(bot): 25 | bot.add_cog(Base(bot)) 26 | -------------------------------------------------------------------------------- /Einzelvideos/level_system.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | import aiosqlite 6 | import random 7 | 8 | 9 | class LevelSystem(commands.Cog): 10 | def __init__(self, bot): 11 | self.bot = bot 12 | self.DB = "level.db" 13 | 14 | @staticmethod 15 | def get_level(xp): 16 | lvl = 1 17 | amount = 100 18 | 19 | while True: 20 | xp -= amount 21 | if xp < 0: 22 | return lvl 23 | lvl += 1 24 | amount += 100 25 | 26 | @commands.Cog.listener() 27 | async def on_ready(self): 28 | async with aiosqlite.connect(self.DB) as db: 29 | await db.execute( 30 | """ 31 | CREATE TABLE IF NOT EXISTS users ( 32 | user_id INTEGER PRIMARY KEY, 33 | msg_count INTEGER DEFAULT 0, 34 | xp INTEGER DEFAULT 0 35 | )""" 36 | ) 37 | 38 | async def check_user(self, user_id): 39 | async with aiosqlite.connect(self.DB) as db: 40 | await db.execute("INSERT OR IGNORE INTO users (user_id) VALUES (?)", (user_id,)) 41 | await db.commit() 42 | 43 | async def get_xp(self, user_id): 44 | await self.check_user(user_id) 45 | async with aiosqlite.connect(self.DB) as db: 46 | async with db.execute("SELECT xp FROM users WHERE user_id = ?", (user_id,)) as cursor: 47 | result = await cursor.fetchone() 48 | 49 | return result[0] 50 | 51 | @commands.Cog.listener() 52 | async def on_message(self, message): 53 | if message.author.bot: 54 | return 55 | if not message.guild: 56 | return 57 | xp = random.randint(10, 20) 58 | 59 | await self.check_user(message.author.id) 60 | async with aiosqlite.connect(self.DB) as db: 61 | await db.execute( 62 | "UPDATE users SET msg_count = msg_count + 1, xp = xp + ? WHERE user_id = ?", (xp, message.author.id) 63 | ) 64 | await db.commit() 65 | 66 | # check level up 67 | new_xp = await self.get_xp(message.author.id) 68 | 69 | old_level = self.get_level(new_xp - xp) 70 | new_level = self.get_level(new_xp) 71 | 72 | if old_level == new_level: 73 | return 74 | 75 | if new_level == 2: 76 | role = message.guild.get_role(123456789) # hier role ID einfügen 77 | await message.author.add_roles(role) 78 | await message.channel.send(f"Level Up! Du hast die Rolle {role.mention} erhalten!") 79 | 80 | @slash_command() 81 | async def rank(self, ctx): 82 | xp = await self.get_xp(ctx.author.id) 83 | lvl = self.get_level(xp) 84 | await ctx.respond(f"Du hast **{xp}** XP und bist Level **{lvl}**") 85 | 86 | @slash_command() 87 | async def leaderboard(self, ctx): 88 | desc = "" 89 | counter = 1 90 | async with aiosqlite.connect(self.DB) as db: 91 | async with db.execute( 92 | "SELECT user_id, xp FROM users WHERE msg_count > 0 ORDER BY xp DESC LIMIT 10" 93 | ) as cursor: 94 | async for user_id, xp in cursor: 95 | desc += f"{counter}. <@{user_id}> - {xp} XP\n" 96 | counter += 1 97 | 98 | embed = discord.Embed( 99 | title="Rangliste", 100 | description=desc, 101 | color=discord.Color.yellow() 102 | ) 103 | embed.set_thumbnail(url=self.bot.user.avatar.url) 104 | await ctx.respond(embed=embed) 105 | 106 | 107 | def setup(bot): 108 | bot.add_cog(LevelSystem(bot)) 109 | -------------------------------------------------------------------------------- /Einzelvideos/radio.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class Radio(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command(description="Starte das Radio") 11 | async def play(self, ctx): 12 | if ctx.author.voice is None: 13 | return await ctx.respond("Du musst erst einem Voice Channel beitreten.") 14 | 15 | if not ctx.author.voice.channel.permissions_for(ctx.guild.me).connect: 16 | return await ctx.respond("Ich habe keine Rechte, um deinem Channel beizutreten.") 17 | 18 | if ctx.voice_client is None: 19 | await ctx.author.voice.channel.connect() # Bot ist in keinem Voice Channel 20 | else: 21 | await ctx.voice_client.move_to(ctx.author.voice.channel) # Bot ist schon in einem anderen Voice Channel 22 | 23 | if ctx.voice_client.is_playing(): 24 | ctx.voice_client.stop() 25 | 26 | ctx.voice_client.play( 27 | discord.FFmpegPCMAudio("https://streams.ilovemusic.de/iloveradio1.mp3") 28 | ) 29 | await ctx.respond("Das Radio wurde gestartet") 30 | 31 | @slash_command(description="Stoppe das Radio") 32 | async def leave(self, ctx): 33 | if ctx.voice_client is None: 34 | return await ctx.respond("Ich bin mit keinem Voice Channel verbunden.") 35 | 36 | await ctx.voice_client.disconnect() 37 | await ctx.respond("Bis bald") 38 | 39 | 40 | def setup(bot): 41 | bot.add_cog(Radio(bot)) 42 | -------------------------------------------------------------------------------- /Einzelvideos/special_dropdowns.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command() 11 | async def select(self, ctx): 12 | await ctx.respond(view=Dropdown()) 13 | 14 | 15 | def setup(bot): 16 | bot.add_cog(Base(bot)) 17 | 18 | 19 | class Dropdown(discord.ui.View): 20 | @discord.ui.role_select(placeholder="Wähle Rollen aus", min_values=1, max_values=3) 21 | async def role_callback(self, select, interaction): 22 | mentions = [f"{roles.mention}" for roles in select.values] 23 | role_list = ", ".join(mentions) 24 | await interaction.response.send_message(f"Du hast folgende Rollen ausgewählt: {role_list}") 25 | 26 | @discord.ui.channel_select(placeholder="Wähle einen Channel", min_values=1, max_values=1) 27 | async def channel_callback(self, select, interaction): 28 | await interaction.response.send_message(f"Du hast {select.values[0].mention} gewählt.") 29 | 30 | @discord.ui.user_select(placeholder="Wähle einen User", min_values=1, max_values=1) 31 | async def user_callback(self, select, interaction): 32 | await interaction.response.send_message(f"Du hast {select.values[0].mention} gewählt.") 33 | -------------------------------------------------------------------------------- /Einzelvideos/werte_an_klassen_übergeben.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class Button(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command() 11 | async def button1(self, ctx): 12 | await ctx.respond("Klicke hier", view=TutorialView(ctx.author)) 13 | 14 | 15 | def setup(bot): 16 | bot.add_cog(Button(bot)) 17 | 18 | 19 | class TutorialView(discord.ui.View): 20 | def __init__(self, user): 21 | self.user = user 22 | super().__init__(timeout=None) 23 | 24 | @discord.ui.button(label="Keks", style=discord.ButtonStyle.primary, emoji="🍪", custom_id="keks") 25 | async def button_callback1(self, button, interaction): 26 | if self.user.id != interaction.user.id: 27 | await interaction.response.send_message("Du darfst diesen Button nicht benutzen!", ephemeral=True) 28 | return 29 | 30 | await interaction.response.send_message(f"{interaction.client.user.name} mag Kekse", ephemeral=True) 31 | 32 | @discord.ui.button(label="Pizza", style=discord.ButtonStyle.primary, emoji="🍕", custom_id="pizza") 33 | async def button_callback2(self, button, interaction): 34 | button.disabled = True 35 | await interaction.response.edit_message(view=self) 36 | -------------------------------------------------------------------------------- /Einzelvideos/youtube_notification.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands, tasks 2 | 3 | import scrapetube 4 | 5 | 6 | class Youtube(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | self.channels = { 10 | "CodingKeks": f"https://youtube.com/@codingkeks", 11 | "GigaChad": f"https://youtube.com/@pycord" 12 | } 13 | self.videos = {} 14 | 15 | @commands.Cog.listener() 16 | async def on_ready(self): 17 | self.check.start() 18 | 19 | @tasks.loop(seconds=60) 20 | async def check(self): 21 | discord_channel = self.bot.get_channel(123456789) # hier channel id einfügen 22 | 23 | for channel_name in self.channels: 24 | videos = scrapetube.get_channel(channel_url=self.channels[channel_name], limit=5) 25 | video_ids = [video["videoId"] for video in videos] 26 | 27 | if self.check.current_loop == 0: 28 | self.videos[channel_name] = video_ids 29 | continue 30 | 31 | for video_id in video_ids: 32 | if video_id not in self.videos[channel_name]: 33 | url = f"https://youtu.be/{video_id}" 34 | await discord_channel.send(f"**{channel_name}** hat ein Video hochgeladen\n\n{url}") 35 | 36 | self.videos[channel_name] = video_ids 37 | 38 | 39 | def setup(bot): 40 | bot.add_cog(Youtube(bot)) 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tutorial Bot 2 | Hier findest du den Source Code zu meinem Discord Bot Tutorial. Für diese Tutorialreihe benutze ich [Pycord](https://github.com/Pycord-Development/pycord). 3 | Das ist eine Python-Bibliothek, die auf [discord.py](https://github.com/Rapptz/discord.py) basiert und mit der wir auf die Discord API zugreifen. 4 | 5 | ## Info 6 | - Die [`main.py`](https://github.com/tibue99/tutorial-bot/blob/main/Template/main.py) Datei ist für die meisten Folgen des Tutorials gleich, deswegen enthalten die meisten Ordner nur die Cog-Datei. 7 | - Im [`Template`](https://github.com/tibue99/tutorial-bot/tree/main/Template) Ordner findest du die [`main.py`](https://github.com/tibue99/tutorial-bot/blob/main/Template/main.py) Datei und eine Vorlage für die grundlegende Code-Struktur des Bots. 8 | - In Tutorials werde ich oft mit dieser Vorlage beginnen, damit ich die Grundlagen nicht in jeder Folge wiederhole. 9 | 10 | ## Setup 11 | 1. Erstelle einen Bot im [Discord Developer Portal](https://discord.com/developers/applications/) 12 | 2. Erstelle eine `.env` Datei, in die du den Bot Token einfügst 13 | ``` 14 | TOKEN = 123456789abcde 15 | ``` 16 | 3. Installiere die Python Packages aus der `requirements.txt` Datei -------------------------------------------------------------------------------- /Template/bot_class.py: -------------------------------------------------------------------------------- 1 | import os 2 | import discord 3 | import ezcord 4 | from dotenv import load_dotenv 5 | 6 | 7 | class Bot(ezcord.Bot): 8 | def __init__(self): 9 | intents = discord.Intents.default() 10 | super().__init__(intents=intents) 11 | 12 | self.load_cogs() 13 | 14 | async def on_ready(self): 15 | print(f"{self.user} ist online") 16 | 17 | 18 | if __name__ == "__main__": 19 | load_dotenv() 20 | bot = Bot() 21 | bot.run(os.getenv("TOKEN")) 22 | -------------------------------------------------------------------------------- /Template/cogs/base.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot: discord.Bot): 8 | self.bot = bot 9 | 10 | @slash_command(description="hello") 11 | async def hello(self, ctx: discord.ApplicationContext): 12 | await ctx.respond(f"Hey {ctx.author.mention}") 13 | 14 | 15 | def setup(bot: discord.Bot): 16 | bot.add_cog(Base(bot)) 17 | -------------------------------------------------------------------------------- /Template/example.env: -------------------------------------------------------------------------------- 1 | TOKEN=# Discord Bot Token 2 | -------------------------------------------------------------------------------- /Template/main.py: -------------------------------------------------------------------------------- 1 | import discord 2 | import os 3 | from dotenv import load_dotenv 4 | 5 | intents = discord.Intents.default() 6 | 7 | bot = discord.Bot(intents=intents) 8 | 9 | 10 | @bot.event 11 | async def on_ready(): 12 | print(f"{bot.user} ist online") 13 | 14 | 15 | if __name__ == "__main__": 16 | for filename in os.listdir("cogs"): 17 | if filename.endswith(".py"): 18 | bot.load_extension(f"cogs.{filename[:-3]}") 19 | 20 | load_dotenv() 21 | bot.run(os.getenv("TOKEN")) 22 | -------------------------------------------------------------------------------- /Tutorialreihe/part02_events.py: -------------------------------------------------------------------------------- 1 | import discord 2 | 3 | intents = discord.Intents.default() 4 | intents.message_content = True # wird für msg.content benötigt 5 | 6 | bot = discord.Bot(intents=intents) 7 | 8 | 9 | @bot.event 10 | async def on_ready(): 11 | print(f"{bot.user} ist online") 12 | 13 | 14 | @bot.event 15 | async def on_message(msg): 16 | if msg.author.bot: 17 | return 18 | 19 | await msg.channel.send("Du stinkst") 20 | 21 | 22 | @bot.event 23 | async def on_message_delete(msg): 24 | await msg.channel.send(f"Eine Nachricht von {msg.author} wurde gelöscht: {msg.content}") 25 | 26 | 27 | bot.run("") # hier token einfügen 28 | -------------------------------------------------------------------------------- /Tutorialreihe/part03_slash_commands.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.commands import Option 3 | 4 | intents = discord.Intents.default() 5 | 6 | bot = discord.Bot( 7 | intents=intents, 8 | debug_guilds=[123456789] # hier server id einfügen 9 | ) 10 | 11 | 12 | @bot.event 13 | async def on_ready(): 14 | print(f"{bot.user} ist online") 15 | 16 | 17 | @bot.slash_command(description="Grüße einen User") 18 | async def greet(ctx, user: Option(discord.User, "Der User, den du grüßen möchtest")): 19 | await ctx.respond(f"Hallo {user.mention}") 20 | 21 | 22 | @bot.slash_command(description="Lass den Bot eine Nachricht senden") 23 | async def say( 24 | ctx, 25 | text: Option(str, "Der Text, den du senden möchtest"), 26 | channel: Option(discord.TextChannel, "Der Channel, in den du die Nachricht senden möchtest") 27 | ): 28 | await channel.send(text) 29 | await ctx.respond("Nachricht gesendet", ephemeral=True) 30 | 31 | 32 | bot.run("") # hier bot token einfügen 33 | -------------------------------------------------------------------------------- /Tutorialreihe/part04_embeds.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.commands import Option 3 | 4 | intents = discord.Intents.default() 5 | 6 | bot = discord.Bot( 7 | intents=intents, 8 | debug_guilds=[123456789] # hier server id einfügen 9 | ) 10 | 11 | 12 | @bot.event 13 | async def on_ready(): 14 | print(f"{bot.user} ist online") 15 | 16 | 17 | @bot.slash_command(name="userinfo", description="Zeige Infos über einen User") 18 | async def info( 19 | ctx, 20 | alter: Option(int, "Das Alter", min_value=1, max_value=99), 21 | user: Option(discord.Member, "Gib einen User an", default=None), 22 | ): 23 | if user is None: 24 | user = ctx.author 25 | 26 | embed = discord.Embed( 27 | title=f"Infos über {user.name}", 28 | description=f"Hier siehst du alle Details über {user.mention}", 29 | color=discord.Color.blue() 30 | ) 31 | 32 | time = discord.utils.format_dt(user.created_at, "R") 33 | 34 | embed.add_field(name="Account erstellt", value=time, inline=False) 35 | embed.add_field(name="ID", value=user.id) 36 | embed.add_field(name="Alter", value=alter) 37 | 38 | embed.set_thumbnail(url=ctx.author.display_avatar.url) 39 | embed.set_footer(text="Das ist ein Footer") 40 | 41 | await ctx.respond(embed=embed) 42 | 43 | 44 | bot.run("") # hier bot token einfügen 45 | -------------------------------------------------------------------------------- /Tutorialreihe/part05_cogs.py: -------------------------------------------------------------------------------- 1 | # Für dieses Beispiel muss der Server Member Intent im Dev Portal und in der Main-Datei aktiviert sein 2 | # 3 | # intents = discord.Intents.default() 4 | # intents.members = True 5 | # 6 | # bot = discord.Bot( 7 | # intents=intents, 8 | # debug_guilds=[123456789], # hier server id einfügen 9 | # ) 10 | 11 | import discord 12 | from discord.ext import commands 13 | from discord.commands import slash_command 14 | 15 | 16 | class Base(commands.Cog): 17 | def __init__(self, bot): 18 | self.bot = bot 19 | 20 | @slash_command() 21 | async def greet(self, ctx): 22 | await ctx.respond(f"Hey {ctx.author.mention}") 23 | 24 | @commands.Cog.listener() 25 | async def on_member_join(self, member): 26 | embed = discord.Embed( 27 | title="Willkommen", 28 | description=f"Hey {member.mention}", 29 | color=discord.Color.orange() 30 | ) 31 | 32 | channel = await self.bot.fetch_channel(123456789) # hier channel id einfügen 33 | await channel.send(embed=embed) 34 | 35 | 36 | def setup(bot): 37 | bot.add_cog(Base(bot)) 38 | -------------------------------------------------------------------------------- /Tutorialreihe/part06_bot_status.py: -------------------------------------------------------------------------------- 1 | # In der Main-Datei kann der Status direkt beim Start gesetzt werden 2 | # 3 | # intents = discord.Intents.default() 4 | # activity = discord.Activity(type=discord.ActivityType.watching, name="Coding Keks") 5 | # status = discord.Status.dnd 6 | 7 | # bot = discord.Bot( 8 | # intents=intents, 9 | # debug_guilds=[123456789], # hier server id einfügen 10 | # activity=activity, 11 | # status=status 12 | # ) 13 | 14 | 15 | import discord 16 | from discord.ext import commands 17 | from discord.commands import slash_command, Option 18 | 19 | 20 | class Commands(commands.Cog): 21 | def __init__(self, bot): 22 | self.bot = bot 23 | 24 | @slash_command() 25 | async def activity( 26 | self, ctx, 27 | typ: Option(str, choices=["game", "stream"]), 28 | name: Option(str) 29 | ): 30 | if typ == "game": 31 | act = discord.Game(name=name) 32 | else: 33 | act = discord.Streaming( 34 | name=name, 35 | url="https://www.twitch.tv/keks" 36 | ) 37 | 38 | await self.bot.change_presence(activity=act, status=discord.Status.online) 39 | await ctx.respond("Status wurde geändert!") 40 | 41 | 42 | def setup(bot): 43 | bot.add_cog(Commands(bot)) 44 | -------------------------------------------------------------------------------- /Tutorialreihe/part07_tasks.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands, tasks 2 | from datetime import time, timezone 3 | 4 | 5 | class Task(commands.Cog): 6 | def __init__(self, bot): 7 | self.bot = bot 8 | 9 | @commands.Cog.listener() 10 | async def on_ready(self): 11 | self.keks.start() 12 | self.time_task.start() 13 | 14 | @tasks.loop(seconds=5) 15 | async def keks(self): 16 | if self.keks.current_loop == 0: 17 | return 18 | 19 | channel = await self.bot.fetch_channel(123456789) # hier channel id einfügen 20 | await channel.send("Keks") 21 | 22 | @tasks.loop( 23 | time=time(22, 0, tzinfo=timezone.utc) 24 | ) 25 | async def time_task(self): 26 | print("Es ist 22:00 Uhr UTC") 27 | 28 | 29 | def setup(bot): 30 | bot.add_cog(Task(bot)) 31 | -------------------------------------------------------------------------------- /Tutorialreihe/part08_error_handling.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command, Option 4 | 5 | 6 | class Admin(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command(description="Kicke einen Member") 11 | async def kick(self, ctx, member: Option(discord.Member, "Wähle einen Member")): 12 | try: 13 | await member.kick() 14 | except discord.Forbidden: 15 | await ctx.respond("Ich habe keine Berechtigung, um diesen Member zu kicken") 16 | return 17 | await ctx.respond(f"{member.mention} wurde gekickt!") 18 | 19 | @commands.Cog.listener() 20 | async def on_application_command_error(self, ctx, error): 21 | await ctx.respond(f"Es ist ein Fehler aufgetreten: ```{error}```") 22 | raise error 23 | 24 | 25 | def setup(bot): 26 | bot.add_cog(Admin(bot)) 27 | -------------------------------------------------------------------------------- /Tutorialreihe/part09_checks.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command, Option 4 | 5 | 6 | class Admin(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command(description="Kicke einen Member") 11 | @discord.default_permissions(administrator=True, kick_members=True) 12 | @discord.guild_only() 13 | async def kick(self, ctx, member: Option(discord.Member, "Wähle einen Member")): 14 | try: 15 | await member.kick() 16 | except discord.Forbidden: 17 | await ctx.respond("Ich habe keine Berechtigung, um diesen Member zu kicken", ephemeral=True) 18 | return 19 | await ctx.respond(f"{member.mention} wurde gekickt!") 20 | 21 | @slash_command() 22 | @commands.has_permissions(administrator=True) 23 | async def hallo(self, ctx): 24 | await ctx.respond("Hey") 25 | 26 | @commands.Cog.listener() 27 | async def on_application_command_error(self, ctx, error): 28 | if isinstance(error, commands.CheckFailure): 29 | await ctx.respond(f"Nur Admins dürfen diesen Befehl ausführen!", ephemeral=True) 30 | return 31 | 32 | await ctx.respond(f"Es ist ein Fehler aufgetreten: ```{error}```", ephemeral=True) 33 | raise error 34 | 35 | 36 | def setup(bot): 37 | bot.add_cog(Admin(bot)) 38 | -------------------------------------------------------------------------------- /Tutorialreihe/part10_cooldowns.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands 2 | from discord.commands import slash_command 3 | 4 | 5 | class Cooldown(commands.Cog): 6 | def __init__(self, bot): 7 | self.bot = bot 8 | 9 | @slash_command() 10 | @commands.cooldown(1, 60, commands.BucketType.user) 11 | async def hey(self, ctx): 12 | await ctx.respond("Hey") 13 | 14 | @staticmethod 15 | def convert_time(seconds: int) -> str: 16 | if seconds < 60: 17 | return f"{round(seconds)} Sekunden" 18 | minutes = seconds / 60 19 | if minutes < 60: 20 | return f"{round(minutes)} Minuten" 21 | hours = minutes / 60 22 | return f"{round(hours)} Stunden" 23 | 24 | @commands.Cog.listener() 25 | async def on_application_command_error(self, ctx, error): 26 | if isinstance(error, commands.CommandOnCooldown): 27 | seconds = ctx.command.get_cooldown_retry_after(ctx) 28 | final_time = self.convert_time(seconds) 29 | 30 | await ctx.respond(f"Du musst noch {final_time} warten.", ephemeral=True) 31 | 32 | 33 | def setup(bot): 34 | bot.add_cog(Cooldown(bot)) 35 | -------------------------------------------------------------------------------- /Tutorialreihe/part11_buttons.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class Button(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.Cog.listener() 11 | async def on_ready(self): 12 | self.bot.add_view(TutorialView()) 13 | 14 | @slash_command() 15 | async def button1(self, ctx): 16 | await ctx.respond("Klicke hier", view=TutorialView()) 17 | 18 | @slash_command() 19 | async def button2(self, ctx): 20 | button = TutorialButton("Kekse sind cool") 21 | view = discord.ui.View() 22 | view.add_item(button) 23 | 24 | await ctx.respond("Klicke hier", view=view) 25 | 26 | @slash_command() 27 | async def url_button(self, ctx): 28 | button = discord.ui.Button(label="GitHub", url="https://github.com/tibue99") 29 | view = discord.ui.View() 30 | view.add_item(button) 31 | 32 | await ctx.respond("Klicke hier", view=view) 33 | 34 | 35 | def setup(bot): 36 | bot.add_cog(Button(bot)) 37 | 38 | 39 | class TutorialView(discord.ui.View): 40 | def __init__(self): 41 | super().__init__(timeout=None) 42 | 43 | @discord.ui.button(label="Keks", style=discord.ButtonStyle.primary, emoji="🍪", custom_id="keks", row=2) 44 | async def button_callback1(self, button, interaction): 45 | await interaction.response.send_message("Keks", ephemeral=True) 46 | 47 | @discord.ui.button(label="Pizza", style=discord.ButtonStyle.primary, emoji="🍕", custom_id="pizza", row=1) 48 | async def button_callback2(self, button, interaction): 49 | button.disabled = True 50 | 51 | # Alle Buttons deaktivieren 52 | # for child in self.children: 53 | # child.disabled = True 54 | 55 | await interaction.response.edit_message(view=self) 56 | 57 | 58 | class TutorialButton(discord.ui.Button): 59 | def __init__(self, label): 60 | super().__init__(label=label, style=discord.ButtonStyle.green) 61 | 62 | async def callback(self, interaction): 63 | await interaction.response.send_message("Hey!", ephemeral=True) 64 | -------------------------------------------------------------------------------- /Tutorialreihe/part12_context_menu.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import message_command, user_command 4 | 5 | 6 | class Context(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @message_command(name="Zeige die ID") 11 | async def get_id(self, ctx, message): 12 | await ctx.respond(f"Hier ist die Message ID: {message.id}") 13 | 14 | @user_command() 15 | async def stups(self, ctx, member: discord.Member): 16 | await ctx.respond(f"{ctx.author.mention} hat {member.mention} angestupst!") 17 | 18 | 19 | def setup(bot): 20 | bot.add_cog(Context(bot)) 21 | -------------------------------------------------------------------------------- /Tutorialreihe/part13_modals.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class ModalCog(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command() 11 | async def modal(self, ctx): 12 | modal = TutorialModal(title="Erstelle ein Embed") 13 | await ctx.send_modal(modal) 14 | 15 | @slash_command() 16 | async def button_modal(self, ctx): 17 | await ctx.respond("Hey", view=TutorialView()) 18 | 19 | 20 | def setup(bot): 21 | bot.add_cog(ModalCog(bot)) 22 | 23 | 24 | class TutorialModal(discord.ui.Modal): 25 | def __init__(self, *args, **kwargs): 26 | super().__init__( 27 | discord.ui.InputText( 28 | label="Embed Titel", 29 | placeholder="Placeholder" 30 | ), 31 | discord.ui.InputText( 32 | label="Embed Beschreibung", 33 | placeholder="Placeholder", 34 | style=discord.InputTextStyle.long 35 | ), 36 | *args, 37 | **kwargs 38 | ) 39 | 40 | async def callback(self, interaction): 41 | embed = discord.Embed( 42 | title=self.children[0].value, 43 | description=self.children[1].value, 44 | color=discord.Color.green() 45 | ) 46 | await interaction.response.send_message(embed=embed) 47 | 48 | 49 | class TutorialView(discord.ui.View): 50 | @discord.ui.button(label="Klicke hier") 51 | async def button_callback(self, button, interaction): 52 | await interaction.response.send_modal(TutorialModal(title="Erstelle ein Embed")) 53 | -------------------------------------------------------------------------------- /Tutorialreihe/part14_wait_for_event.py: -------------------------------------------------------------------------------- 1 | # Für dieses Beispiel muss der Message Content Intent im Dev Portal und in der Main-Datei aktiviert sein 2 | # 3 | # intents = discord.Intents.default() 4 | # intents.message_content = True 5 | # 6 | # bot = discord.Bot( 7 | # intents=intents, 8 | # debug_guilds=[123456789], # hier server id einfügen 9 | # ) 10 | 11 | from discord.ext import commands 12 | from discord.commands import slash_command 13 | 14 | import asyncio 15 | 16 | 17 | class Base(commands.Cog): 18 | def __init__(self, bot): 19 | self.bot = bot 20 | 21 | @slash_command() 22 | async def wait(self, ctx): 23 | await ctx.respond("Gib eine Zahl ein.") 24 | 25 | def check(message): 26 | return message.author == ctx.author and message.content.isdigit() 27 | 28 | try: 29 | answer = await self.bot.wait_for("message", check=check, timeout=5.0) 30 | except asyncio.TimeoutError: 31 | await ctx.send_followup(f"Bruh, du warst zu langsam!") 32 | return 33 | 34 | await answer.reply(f"Du hast die Zahl {answer.content} eingegeben.") 35 | 36 | 37 | def setup(bot): 38 | bot.add_cog(Base(bot)) 39 | -------------------------------------------------------------------------------- /Tutorialreihe/part15_select_menu_dropdown.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | options = [ 7 | discord.SelectOption(label="Python", description="Python Beschreibung", emoji="👑"), 8 | discord.SelectOption(label="Java", description="Java Beschreibung", emoji="💻"), 9 | discord.SelectOption(label="Javascript", description="Javascript Beschreibung", emoji="🚩", value="JS") 10 | ] 11 | 12 | keks = discord.SelectOption(label="Keks", emoji="🍪") 13 | 14 | 15 | class Dropdown(commands.Cog): 16 | def __init__(self, bot): 17 | self.bot = bot 18 | 19 | @commands.Cog.listener() 20 | async def on_ready(self): 21 | self.bot.add_view(TutorialView()) 22 | 23 | @slash_command() 24 | async def select1(self, ctx): 25 | await ctx.respond("Wähle Programmiersprachen aus", view=TutorialView()) 26 | 27 | @slash_command() 28 | async def select2(self, ctx): 29 | select = TutorialSelect() 30 | select.append_option(keks) 31 | 32 | view = discord.ui.View(timeout=None) 33 | view.add_item(select) 34 | 35 | await ctx.respond(view=view) 36 | 37 | 38 | def setup(bot): 39 | bot.add_cog(Dropdown(bot)) 40 | 41 | 42 | class TutorialSelect(discord.ui.Select): 43 | def __init__(self): 44 | super().__init__( 45 | min_values=1, 46 | max_values=1, 47 | placeholder="Triff eine Auswahl", 48 | options=options 49 | ) 50 | 51 | async def callback(self, interaction): 52 | await interaction.response.send_message(f"Du hast {self.values[0]} gewählt") 53 | 54 | 55 | class TutorialView(discord.ui.View): 56 | def __init__(self): 57 | super().__init__(timeout=None) 58 | 59 | @discord.ui.select( 60 | min_values=1, 61 | max_values=2, 62 | placeholder="Triff eine Auswahl", 63 | options=options, 64 | custom_id="keks" 65 | ) 66 | async def select_callback(self, select, interaction): 67 | if "Python" in select.values: 68 | labels = [option.label for option in select.options] 69 | if "Keks" not in labels: 70 | select.append_option(keks) 71 | else: 72 | select.disabled = True 73 | 74 | await interaction.response.edit_message(view=self) 75 | else: 76 | s = "" 77 | for auswahl in select.values: 78 | s += f"- {auswahl}\n" 79 | 80 | await interaction.response.send_message(f"Du hast folgendes ausgewählt:\n{s}") 81 | -------------------------------------------------------------------------------- /Tutorialreihe/part16_paginator.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | from discord.ext.pages import Paginator, Page 5 | 6 | 7 | class Base(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | 11 | @slash_command() 12 | async def page(self, ctx): 13 | pages = [ 14 | Page(embeds=[discord.Embed(title="Keks", color=discord.Color.yellow())]), 15 | Page(content="Du stinkst") 16 | ] 17 | paginator = Paginator(pages=pages, author_check=True, disable_on_timeout=True) 18 | # paginator.remove_button("first") 19 | # paginator.remove_button("last") 20 | 21 | await paginator.respond(ctx.interaction) 22 | 23 | @slash_command() 24 | async def memberlist(self, ctx): 25 | members = ctx.guild.members 26 | pages = [] 27 | description = "" 28 | 29 | for index, member in enumerate(members): 30 | description += f"`{index + 1}.` {member}\n" 31 | 32 | if (index + 1) % 10 == 0 or index == len(members) - 1: 33 | embed = discord.Embed(title="Member List", description=description, color=discord.Color.green()) 34 | if ctx.guild.icon: 35 | embed.set_thumbnail(url=ctx.guild.icon.url) 36 | pages.append(embed) 37 | description = "" 38 | 39 | paginator = Paginator(pages=pages) 40 | await paginator.respond(ctx.interaction) 41 | 42 | 43 | def setup(bot): 44 | bot.add_cog(Base(bot)) 45 | -------------------------------------------------------------------------------- /Tutorialreihe/part17_autocomplete.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.utils import basic_autocomplete 3 | from discord.ext import commands 4 | from discord.commands import slash_command, Option 5 | 6 | food = ["Pizza", "Pommes", "Döner"] 7 | 8 | 9 | def get_food(ctx: discord.AutocompleteContext): 10 | if "99" in ctx.interaction.user.display_name: 11 | return food + ["Kekse"] 12 | 13 | return food 14 | 15 | 16 | class Base(commands.Cog): 17 | def __init__(self, bot): 18 | self.bot = bot 19 | 20 | @slash_command() 21 | async def essen( 22 | self, ctx, auswahl: Option(str, autocomplete=basic_autocomplete(get_food)) 23 | ): 24 | await ctx.respond(f"Du hast ✨ **{auswahl}** ✨ gewählt") 25 | 26 | 27 | def setup(bot): 28 | bot.add_cog(Base(bot)) 29 | -------------------------------------------------------------------------------- /Tutorialreihe/part18_interaction_followup.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command() 11 | async def button(self, ctx): 12 | await ctx.respond(view=TutorialView(), ephemeral=True) 13 | 14 | 15 | def setup(bot): 16 | bot.add_cog(Base(bot)) 17 | 18 | 19 | class TutorialView(discord.ui.View): 20 | @discord.ui.button(label="Keks", style=discord.ButtonStyle.primary, emoji="🍪") 21 | async def button_callback(self, button, interaction): 22 | await interaction.response.edit_message(content="Keks1") 23 | await interaction.followup.send("Keks2", ephemeral=True) 24 | -------------------------------------------------------------------------------- /Tutorialreihe/part19_command_groups.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import SlashCommandGroup 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | eat = SlashCommandGroup("eat", description="Essen ist lecker") 11 | 12 | @eat.command() 13 | async def cookie(self, ctx): 14 | await ctx.respond("Du hast dir einen Keks gegönnt") 15 | 16 | @eat.command() 17 | async def pizza(self, ctx): 18 | await ctx.respond("Du hast dir eine Pizza gegönnt") 19 | 20 | give = SlashCommandGroup( 21 | "give", 22 | default_member_permissions=discord.Permissions(administrator=True) 23 | ) 24 | 25 | keks = give.create_subgroup("keks") 26 | 27 | @keks.command() 28 | async def schoko(self, ctx): 29 | await ctx.respond("Ein legendärer Schokokeks wurde vergeben") 30 | 31 | @keks.command() 32 | async def subway(self, ctx): 33 | await ctx.respond("Subway Cookie wurde vergeben!!!") 34 | 35 | 36 | def setup(bot): 37 | bot.add_cog(Base(bot)) 38 | -------------------------------------------------------------------------------- /Tutorialreihe/part20_original_response.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands 2 | from discord.commands import slash_command 3 | 4 | 5 | class Base(commands.Cog): 6 | def __init__(self, bot): 7 | self.bot = bot 8 | 9 | @slash_command() 10 | async def reaction(self, ctx): 11 | interaction = await ctx.respond("Hey") 12 | message = await interaction.original_response() 13 | await message.add_reaction("🍪") 14 | 15 | @slash_command() 16 | async def edit(self, ctx): 17 | interaction = await ctx.respond("Hey", ephemeral=True) 18 | await interaction.edit_original_response(content="🍪") 19 | 20 | 21 | def setup(bot): 22 | bot.add_cog(Base(bot)) 23 | -------------------------------------------------------------------------------- /Tutorialreihe/part21_custom_checks.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | async def custom_check(ctx): 7 | return ctx.author.id == 123456789 # hier user ID einfügen 8 | 9 | 10 | class Base(commands.Cog): 11 | def __init__(self, bot): 12 | self.bot = bot 13 | 14 | @slash_command() 15 | @commands.check(custom_check) 16 | async def hallo(self, ctx): 17 | await ctx.respond("Hey") 18 | 19 | @commands.Cog.listener() 20 | async def on_application_command_error(self, ctx, error): 21 | if isinstance(error, discord.CheckFailure): 22 | await ctx.respond(f"Du bist nicht würdig genug, um diesem Befehl zu nutzen!", ephemeral=True) 23 | return 24 | 25 | await ctx.respond(f"Es ist ein Fehler aufgetreten: ```{error}```", ephemeral=True) 26 | raise error 27 | 28 | 29 | def setup(bot): 30 | bot.add_cog(Base(bot)) 31 | -------------------------------------------------------------------------------- /Tutorialreihe/part22_global_checks.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @staticmethod 11 | async def bot_check(ctx): 12 | if ctx.author.id != 123456789: # hier user ID einfügen 13 | await ctx.respond("Du bist nicht würdig genug, um diesen Befehl zu nutzen!") 14 | return False 15 | return True 16 | 17 | @staticmethod 18 | async def cog_check(ctx): 19 | if ctx.author.voice is None: 20 | await ctx.respond("Tritt erst einem Voice Channel bei.") 21 | return False 22 | return True 23 | 24 | @slash_command() 25 | async def hey(self, ctx): 26 | await ctx.respond("Hey") 27 | 28 | @commands.Cog.listener() 29 | async def on_application_command_error(self, ctx, error): 30 | if isinstance(error, discord.CheckFailure): 31 | return 32 | raise error 33 | 34 | 35 | def setup(bot): 36 | bot.add_cog(Base(bot)) 37 | -------------------------------------------------------------------------------- /Tutorialreihe/part23_option_decorator.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.commands import slash_command, option 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command() 11 | @option("user", description="Wähle einen User") 12 | @option("auswahl", description="Beschreibung", choices=["Ja", "Nein"]) 13 | async def hello(self, ctx, user: discord.User, auswahl: str = "Ja"): 14 | await ctx.respond(f"{auswahl}, {user.mention}") 15 | 16 | 17 | def setup(bot): 18 | bot.add_cog(Base(bot)) 19 | -------------------------------------------------------------------------------- /Tutorialreihe/part24_converter.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.commands import slash_command, option 3 | from discord.ext import commands 4 | from discord.ext.commands import ColorConverter, EmojiConverter 5 | 6 | 7 | class Base(commands.Cog): 8 | def __init__(self, bot): 9 | self.bot = bot 10 | 11 | @slash_command() 12 | @option("color", type=ColorConverter) 13 | async def converter(self, ctx: discord.ApplicationContext, color: discord.Color): 14 | embed = discord.Embed( 15 | title="Titel", 16 | color=color 17 | ) 18 | await ctx.respond(embed=embed, ephemeral=True) 19 | 20 | @slash_command() 21 | @option("emoji", type=EmojiConverter) 22 | async def emoji(self, ctx: discord.ApplicationContext, emoji: discord.Emoji): 23 | embed = discord.Embed( 24 | title=f"Titel {emoji}", 25 | ) 26 | await ctx.respond(embed=embed, ephemeral=True) 27 | 28 | @commands.Cog.listener() 29 | async def on_application_command_error(self, ctx, error): 30 | if isinstance(error, commands.BadColorArgument): 31 | await ctx.respond("Du hast eine ungültige Farbe gewählt.", ephemeral=True) 32 | return 33 | if isinstance(error, commands.EmojiNotFound): 34 | await ctx.respond("Du hast ein ungültiges Emoji gewählt.", ephemeral=True) 35 | return 36 | raise error 37 | 38 | 39 | def setup(bot): 40 | bot.add_cog(Base(bot)) 41 | -------------------------------------------------------------------------------- /Tutorialreihe/part26_webhooks.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.commands import slash_command 3 | from discord.ext import commands 4 | 5 | import aiohttp 6 | 7 | 8 | webhook_url = "" # hier die webhook URL einfügen 9 | 10 | 11 | class Webhook(commands.Cog): 12 | def __init__(self, bot): 13 | self.bot = bot 14 | 15 | @slash_command() 16 | async def webhook1(self, ctx): 17 | async with aiohttp.ClientSession() as session: 18 | webhook = discord.Webhook.from_url( 19 | webhook_url, 20 | session=session, 21 | bot_token=self.bot.http.token, 22 | ) 23 | # webhook = await webhook.fetch() 24 | # print(webhook.is_partial()) 25 | 26 | await webhook.send( 27 | "Keks", 28 | username=ctx.user.global_name, 29 | avatar_url=ctx.user.display_avatar.url, 30 | wait=True, 31 | ) 32 | 33 | @slash_command() 34 | async def webhook2(self, ctx): 35 | final_webhook = None 36 | for webhook in await ctx.channel.webhooks(): 37 | if webhook.url == webhook_url: 38 | final_webhook = webhook 39 | 40 | if final_webhook is None: 41 | return await ctx.respond("Webhook nicht gefunden") 42 | 43 | msg = await final_webhook.send( 44 | "Keks", 45 | username=ctx.user.global_name, 46 | avatar_url=ctx.user.display_avatar.url, 47 | wait=True 48 | ) 49 | await msg.add_reaction("🍪") 50 | 51 | @slash_command() 52 | async def webhook3(self, ctx): 53 | webhook = await ctx.channel.create_webhook(name="Timo") 54 | await webhook.send( 55 | "Keks", 56 | username=ctx.user.global_name, 57 | avatar_url=ctx.user.display_avatar.url, 58 | wait=True, 59 | view=TutorialView() 60 | ) 61 | 62 | 63 | def setup(bot): 64 | bot.add_cog(Webhook(bot)) 65 | 66 | 67 | class TutorialView(discord.ui.View): 68 | @discord.ui.button(label="Fortnite?") 69 | async def callback(self, button, interaction): 70 | await interaction.response.send_message("Fortnite!", ephemeral=True) 71 | -------------------------------------------------------------------------------- /Tutorialreihe/part27_audio_recording.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord import slash_command 4 | 5 | 6 | async def end_recording(sink: discord.sinks.WaveSink, channel: discord.TextChannel): 7 | await sink.vc.disconnect() 8 | files = [] 9 | for user_id, audio in sink.audio_data.items(): 10 | files.append(discord.File(audio.file, f"{user_id}.{sink.encoding}")) 11 | 12 | await channel.send(files=files) 13 | 14 | 15 | class Base(commands.Cog): 16 | def __init__(self, bot): 17 | self.bot = bot 18 | 19 | @slash_command() 20 | async def record(self, ctx: discord.ApplicationContext): 21 | if not ctx.user.voice: 22 | await ctx.respond("Du bist in keinem Voice Channel!") 23 | 24 | vc = await ctx.user.voice.channel.connect() 25 | vc.start_recording(discord.sinks.WaveSink(), end_recording, ctx.channel) 26 | await ctx.respond(view=StopRecordingView(vc)) 27 | 28 | 29 | def setup(bot): 30 | bot.add_cog(Base(bot)) 31 | 32 | 33 | class StopRecordingView(discord.ui.View): 34 | def __init__(self, vc): 35 | self.vc = vc 36 | super().__init__(timeout=None) 37 | 38 | @discord.ui.button(label="Stop", emoji="🔴") 39 | async def callback(self, _, interaction: discord.Interaction): 40 | self.vc.stop_recording() 41 | self.disable_all_items() 42 | await interaction.edit(view=self) 43 | -------------------------------------------------------------------------------- /Tutorialreihe/part28_view_wait.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord import slash_command 4 | 5 | 6 | class Base(commands.Cog): 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @slash_command() 11 | async def greet(self, ctx: discord.ApplicationContext): 12 | view = TutorialView() 13 | await ctx.respond(view=view) 14 | await view.wait() 15 | print(view.value) 16 | 17 | 18 | def setup(bot): 19 | bot.add_cog(Base(bot)) 20 | 21 | 22 | class TutorialView(discord.ui.View): 23 | def __init__(self): 24 | super().__init__(disable_on_timeout=True) 25 | self.value = None 26 | 27 | @discord.ui.button(emoji="1️⃣") 28 | async def one(self, _, interaction: discord.Interaction): 29 | self.value = 1 30 | await interaction.respond("Du hast **1** gewählt") 31 | self.stop() 32 | 33 | @discord.ui.button(label="2️⃣") 34 | async def two(self, _, interaction: discord.Interaction): 35 | self.value = 2 36 | await interaction.respond("Du hast **2** gewählt") 37 | self.stop() 38 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | py-cord 2 | python-dotenv 3 | aiosqlite 4 | ezcord 5 | 6 | # Requirements für einzelne Videos 7 | chat-exporter 8 | easy-pil 9 | scrapetube 10 | 11 | # Requirements für API-Videos 12 | aiohttp 13 | asyncpraw 14 | elevenlabs 15 | openai 16 | --------------------------------------------------------------------------------