Clean up type hints everywhere, overhaul bot configuration
All checks were successful
Build Heidi Docker image / build-docker (push) Successful in 2m12s

This commit is contained in:
2025-09-23 20:39:11 +02:00
parent 591f4ea191
commit 75fb627361
6 changed files with 332 additions and 184 deletions

View File

@ -1,11 +1,12 @@
import asyncio
import functools
from typing import Union
from types import CoroutineType
from typing import Any, Callable
import discord
from discord import Interaction, VoiceChannel, Member
from discord import Interaction, Message, VoiceChannel, Member, VoiceClient
from discord.player import FFmpegPCMAudio
from heidi_constants import *
from heidi_constants import SOUNDDIR
print("Debug: Importing heidi_helpers.py")
@ -15,14 +16,15 @@ print("Debug: Importing heidi_helpers.py")
# 1. @enforce_channel(ID) is added to a function, which evaluates to decorate with the channel_id in its closure
# 2. The function is passed to decorate(function),
def enforce_channel(channel_id):
def enforce_channel(channel_id: int):
"""
Only run a function if called from the correct channel.
Only run a function if called from the specified voice channel.
"""
def decorate(function):
def decorate(function: Callable[..., CoroutineType[Any, Any, None]]):
@functools.wraps(function)
async def wrapped(*args, **kwargs):
async def wrapped(*args: *tuple[Interaction, *tuple[Any, ...]], **kwargs: int):
"""
Sends an interaction response if the interaction is not triggered from the heidi_spam channel.
"""
@ -30,7 +32,7 @@ def enforce_channel(channel_id):
# Do not call the decorated function if the channel_id doesn't match
if not interaction.channel_id == channel_id:
await interaction.response.send_message("Heidi sagt: Geh in heidi_spam du dulli", ephemeral=True)
_ = await interaction.response.send_message("Heidi sagt: Geh in heidi_spam du dulli", ephemeral=True)
return
await function(*args, **kwargs)
@ -40,12 +42,34 @@ def enforce_channel(channel_id):
return decorate
# Reactions --------------------------------------------------------------------------------------
def create_author_based_message_predicate(names: list[str]) -> Callable[[Message], bool]:
"""
Create a predicate that determines if a message was written by a certain author.
For usage with on_message_triggers.
"""
def handler(message: Message) -> bool:
for name in names:
if (
isinstance(message.author, Member)
and message.author.nick is not None
and name.lower() in message.author.nick.lower()
):
return True
return False
return handler
# Sounds -----------------------------------------------------------------------------------------
# @todo Normalize volume when playing
async def play_voice_line(
interaction: Union[Interaction, None],
interaction: Interaction | None,
voice_channel: VoiceChannel,
board: str,
sound: str,
@ -54,23 +78,22 @@ async def play_voice_line(
Play a voice line in the specified channel.
"""
try:
open(f"{SOUNDDIR}/{board}/{sound}")
# Check if the file exists
_ = open(f"{SOUNDDIR}/{board}/{sound}")
except IOError:
print(f"Error: Invalid soundfile {SOUNDDIR}/{board}/{sound}!")
if interaction is not None:
await interaction.response.send_message(
f'Heidi sagt: "{board}/{sound}" kanninich finden bruder',
ephemeral=True
_ = await interaction.response.send_message(
f'Heidi sagt: "{board}/{sound}" kanninich finden bruder', ephemeral=True
)
return
if interaction is not None:
await interaction.response.send_message(f'Heidi sagt: "{board}/{sound}"', ephemeral=True)
_ = await interaction.response.send_message(f'Heidi sagt: "{board}/{sound}"', ephemeral=True)
audio_source = discord.FFmpegPCMAudio(
f"{SOUNDDIR}/{board}/{sound}"
) # only works from docker
voice_client = await voice_channel.connect()
# TODO: Normalize volume when playing
audio_source: FFmpegPCMAudio = FFmpegPCMAudio(f"{SOUNDDIR}/{board}/{sound}") # only works from docker
voice_client: VoiceClient = await voice_channel.connect()
voice_client.play(audio_source)
while voice_client.is_playing():
@ -80,7 +103,7 @@ async def play_voice_line(
async def play_voice_line_for_member(
interaction: Union[Interaction, None],
interaction: Interaction | None,
member: Member,
board: str,
sound: str,
@ -97,7 +120,7 @@ async def play_voice_line_for_member(
):
print("User not in (valid) voice channel!")
if interaction is not None:
await interaction.response.send_message("Heidi sagt: Komm in den Channel!", ephemeral=True)
_ = await interaction.response.send_message("Heidi sagt: Komm in den Channel!", ephemeral=True)
return
voice_channel: VoiceChannel = member.voice.channel