Some checks failed
Build Heidi Docker image / build-docker (push) Failing after 28s
129 lines
4.0 KiB
Python
129 lines
4.0 KiB
Python
import asyncio
|
|
import functools
|
|
from types import CoroutineType
|
|
from typing import Any, Callable
|
|
|
|
from discord import Interaction, Message, VoiceChannel, Member, VoiceClient
|
|
from discord.player import FFmpegPCMAudio
|
|
|
|
from heidi_constants import SOUNDDIR
|
|
|
|
print("Debug: Importing heidi_helpers.py")
|
|
|
|
|
|
# Checks -----------------------------------------------------------------------------------------
|
|
|
|
|
|
# 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: int):
|
|
"""
|
|
Only run a function if called from the specified voice channel.
|
|
"""
|
|
|
|
def decorate(function: Callable[..., CoroutineType[Any, Any, None]]):
|
|
|
|
@functools.wraps(function)
|
|
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.
|
|
"""
|
|
interaction: Interaction = args[0]
|
|
|
|
# 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)
|
|
return
|
|
|
|
await function(*args, **kwargs)
|
|
|
|
return wrapped
|
|
|
|
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 -----------------------------------------------------------------------------------------
|
|
|
|
|
|
async def play_voice_line(
|
|
interaction: Interaction | None,
|
|
voice_channel: VoiceChannel,
|
|
board: str,
|
|
sound: str,
|
|
) -> None:
|
|
"""
|
|
Play a voice line in the specified channel.
|
|
"""
|
|
try:
|
|
# 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
|
|
)
|
|
return
|
|
|
|
if interaction is not None:
|
|
_ = await interaction.response.send_message(f'Heidi sagt: "{board}/{sound}"', ephemeral=True)
|
|
|
|
# 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():
|
|
await asyncio.sleep(1)
|
|
|
|
await voice_client.disconnect()
|
|
|
|
|
|
async def play_voice_line_for_member(
|
|
interaction: Interaction | None,
|
|
member: Member,
|
|
board: str,
|
|
sound: str,
|
|
) -> None:
|
|
"""
|
|
Play a voice line in the member's current channel.
|
|
"""
|
|
# Member needs to be in voice channel to hear audio (Heidi needs to know the channel to join)
|
|
if (
|
|
member is None
|
|
or member.voice is None
|
|
or member.voice.channel is None
|
|
or not isinstance(member.voice.channel, VoiceChannel)
|
|
):
|
|
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)
|
|
return
|
|
|
|
voice_channel: VoiceChannel = member.voice.channel
|
|
|
|
await play_voice_line(interaction, voice_channel, board, sound)
|