Added /recommend which recommands songs based on users song history. Fixed it so that Amber should auto-join your voice channel when you /play as long as you're already in a voice channel.
This commit is contained in:
parent
5a0cf3855d
commit
d53887dd1a
1
.gitignore
vendored
1
.gitignore
vendored
@ -169,3 +169,4 @@ cython_debug/
|
|||||||
|
|
||||||
# PyPI configuration file
|
# PyPI configuration file
|
||||||
.pypirc
|
.pypirc
|
||||||
|
/user_data.json
|
158
audio.py
158
audio.py
@ -1,26 +1,71 @@
|
|||||||
import discord
|
import discord
|
||||||
import yt_dlp
|
import yt_dlp
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import random
|
||||||
|
import requests
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
voice_clients = {} # Track active voice connections
|
voice_clients = {} # Track active voice connections
|
||||||
music_queues = {} # Per-guild song queue
|
music_queues = {} # Per-guild song queue
|
||||||
current_tracks = {} # Currently playing tracks
|
current_tracks = {} # Currently playing tracks
|
||||||
volumes = {} # Volume levels per guild
|
volumes = {} # Volume levels per guild
|
||||||
default_volume = 0.5 # Default volume (50%)
|
default_volume = 0.05 # Default volume (5%)
|
||||||
|
USER_DATA_FILE = 'user_data.json'
|
||||||
|
YOUTUBE_API_KEY = os.getenv("YOUTUBE_API_KEY") # Replace with your API key
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- Utility Functions ----------
|
||||||
|
def load_user_data():
|
||||||
|
if not os.path.exists(USER_DATA_FILE):
|
||||||
|
return {}
|
||||||
|
with open(USER_DATA_FILE, 'r') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def save_user_data(data):
|
||||||
|
with open(USER_DATA_FILE, 'w') as f:
|
||||||
|
json.dump(data, f, indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
def update_user_history(user_id, song_title, artist):
|
||||||
|
user_data = load_user_data()
|
||||||
|
if str(user_id) not in user_data:
|
||||||
|
user_data[str(user_id)] = {"history": []}
|
||||||
|
|
||||||
|
# Add the song to the user's history
|
||||||
|
user_data[str(user_id)]["history"].append({"title": song_title, "artist": artist})
|
||||||
|
save_user_data(user_data)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- Playback Functions ----------
|
||||||
|
async def join_voice(interaction: discord.Interaction):
|
||||||
|
if interaction.user.voice is None or interaction.user.voice.channel is None:
|
||||||
|
await interaction.response.send_message("You need to be in a voice channel for me to join.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
channel = interaction.user.voice.channel
|
||||||
|
guild_id = interaction.guild.id
|
||||||
|
|
||||||
|
if guild_id not in voice_clients:
|
||||||
|
voice_clients[guild_id] = await channel.connect()
|
||||||
|
await interaction.followup.send(f"✅ **Joined {channel.name}.**")
|
||||||
|
return voice_clients[guild_id]
|
||||||
|
|
||||||
|
|
||||||
async def play_audio(interaction: discord.Interaction, query: str):
|
async def play_audio(interaction: discord.Interaction, query: str):
|
||||||
guild_id = interaction.guild.id
|
guild_id = interaction.guild.id
|
||||||
|
|
||||||
if guild_id not in voice_clients:
|
# Ensure Amber is connected to a voice channel
|
||||||
await interaction.response.send_message("Amber is not connected to a voice channel.")
|
if guild_id not in voice_clients or not voice_clients[guild_id].is_connected():
|
||||||
return
|
voice_client = await join_voice(interaction)
|
||||||
|
if voice_client is None: # If join failed
|
||||||
voice_client = voice_clients[guild_id]
|
return
|
||||||
|
else:
|
||||||
if not voice_client.is_connected():
|
voice_client = voice_clients[guild_id]
|
||||||
await interaction.response.send_message("Amber is not in a voice channel.")
|
|
||||||
return
|
|
||||||
|
|
||||||
await interaction.response.defer()
|
await interaction.response.defer()
|
||||||
|
|
||||||
@ -38,6 +83,10 @@ async def play_audio(interaction: discord.Interaction, query: str):
|
|||||||
|
|
||||||
song_url = info['url']
|
song_url = info['url']
|
||||||
title = info.get('title', 'Unknown Title')
|
title = info.get('title', 'Unknown Title')
|
||||||
|
artist = info.get('uploader', 'Unknown Artist')
|
||||||
|
|
||||||
|
# Log the song in the user's history
|
||||||
|
update_user_history(interaction.user.id, title, artist)
|
||||||
|
|
||||||
# Add the song to the queue
|
# Add the song to the queue
|
||||||
if guild_id not in music_queues:
|
if guild_id not in music_queues:
|
||||||
@ -100,6 +149,28 @@ async def stop_audio(interaction: discord.Interaction):
|
|||||||
await interaction.response.send_message("❌ **No music is playing.**")
|
await interaction.response.send_message("❌ **No music is playing.**")
|
||||||
|
|
||||||
|
|
||||||
|
async def pause_audio(interaction: discord.Interaction):
|
||||||
|
"""Pauses the currently playing song."""
|
||||||
|
guild_id = interaction.guild.id
|
||||||
|
|
||||||
|
if guild_id in voice_clients and voice_clients[guild_id].is_playing():
|
||||||
|
voice_clients[guild_id].pause() # Pause playback
|
||||||
|
await interaction.response.send_message("⏸️ **Music paused.**")
|
||||||
|
else:
|
||||||
|
await interaction.response.send_message("❌ **No music is currently playing.**")
|
||||||
|
|
||||||
|
|
||||||
|
async def resume_audio(interaction: discord.Interaction):
|
||||||
|
"""Resumes playback of a paused song."""
|
||||||
|
guild_id = interaction.guild.id
|
||||||
|
|
||||||
|
if guild_id in voice_clients and voice_clients[guild_id].is_paused():
|
||||||
|
voice_clients[guild_id].resume() # Resume playback
|
||||||
|
await interaction.response.send_message("▶️ **Music resumed.**")
|
||||||
|
else:
|
||||||
|
await interaction.response.send_message("❌ **No paused music to resume.**")
|
||||||
|
|
||||||
|
|
||||||
async def set_volume(interaction: discord.Interaction, level: int):
|
async def set_volume(interaction: discord.Interaction, level: int):
|
||||||
guild_id = interaction.guild.id
|
guild_id = interaction.guild.id
|
||||||
|
|
||||||
@ -120,22 +191,6 @@ async def set_volume(interaction: discord.Interaction, level: int):
|
|||||||
await interaction.response.send_message(f"🔊 **Volume set to {level}%**.")
|
await interaction.response.send_message(f"🔊 **Volume set to {level}%**.")
|
||||||
|
|
||||||
|
|
||||||
async def join_voice(interaction: discord.Interaction):
|
|
||||||
if interaction.user.voice is None or interaction.user.voice.channel is None:
|
|
||||||
await interaction.response.send_message("You need to be in a voice channel for me to join.")
|
|
||||||
return
|
|
||||||
|
|
||||||
channel = interaction.user.voice.channel
|
|
||||||
guild_id = interaction.guild.id
|
|
||||||
|
|
||||||
# Connect to the voice channel
|
|
||||||
if guild_id not in voice_clients:
|
|
||||||
voice_clients[guild_id] = await channel.connect()
|
|
||||||
await interaction.response.send_message(f"✅ **Joined {channel.name}.**")
|
|
||||||
else:
|
|
||||||
await interaction.response.send_message("❌ **I am already in a voice channel.**")
|
|
||||||
|
|
||||||
|
|
||||||
async def leave_voice(interaction: discord.Interaction):
|
async def leave_voice(interaction: discord.Interaction):
|
||||||
guild_id = interaction.guild.id
|
guild_id = interaction.guild.id
|
||||||
|
|
||||||
@ -149,3 +204,54 @@ async def leave_voice(interaction: discord.Interaction):
|
|||||||
await interaction.response.send_message("❌ **I am not connected to any voice channel.**")
|
await interaction.response.send_message("❌ **I am not connected to any voice channel.**")
|
||||||
else:
|
else:
|
||||||
await interaction.response.send_message("❌ **I am not connected to any voice channel.**")
|
await interaction.response.send_message("❌ **I am not connected to any voice channel.**")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------- Recommendations ----------
|
||||||
|
def fetch_related_songs(song_title, artist):
|
||||||
|
"""
|
||||||
|
Fetch related songs using the YouTube Data API v3.
|
||||||
|
Returns a list of recommendations in the format:
|
||||||
|
[ "Title - URL" ]
|
||||||
|
"""
|
||||||
|
query = f"{song_title} {artist} related songs"
|
||||||
|
url = "https://www.googleapis.com/youtube/v3/search"
|
||||||
|
params = {
|
||||||
|
"q": query,
|
||||||
|
"key": YOUTUBE_API_KEY,
|
||||||
|
"type": "video",
|
||||||
|
"part": "snippet",
|
||||||
|
"maxResults": 5
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.get(url, params=params)
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
return [
|
||||||
|
f'{item["snippet"]["title"]} - https://www.youtube.com/watch?v={item["id"]["videoId"]}'
|
||||||
|
for item in data.get("items", [])
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
print(f"Error fetching related songs: {response.status_code} {response.text}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def generate_recommendations(user_id):
|
||||||
|
"""
|
||||||
|
Generate recommendations using the last song the user listened to.
|
||||||
|
"""
|
||||||
|
user_data = load_user_data()
|
||||||
|
user_history = user_data.get(str(user_id), {}).get("history", [])
|
||||||
|
|
||||||
|
if not user_history:
|
||||||
|
return ["No recommendations yet. Start playing some songs!"]
|
||||||
|
|
||||||
|
# Use the last played song to fetch recommendations
|
||||||
|
last_song = user_history[-1]
|
||||||
|
title = last_song["title"]
|
||||||
|
artist = last_song["artist"]
|
||||||
|
recommendations = fetch_related_songs(title, artist)
|
||||||
|
|
||||||
|
if recommendations:
|
||||||
|
return recommendations
|
||||||
|
else:
|
||||||
|
return ["No recommendations could be fetched at this time."]
|
||||||
|
23
commands.py
23
commands.py
@ -1,6 +1,6 @@
|
|||||||
import discord
|
import discord
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from audio import play_audio, stop_audio, set_volume, join_voice, leave_voice
|
from audio import play_audio, stop_audio, set_volume, join_voice, leave_voice, pause_audio, resume_audio, generate_recommendations
|
||||||
|
|
||||||
|
|
||||||
async def setup_commands(client, guild_id=None):
|
async def setup_commands(client, guild_id=None):
|
||||||
@ -20,8 +20,29 @@ async def setup_commands(client, guild_id=None):
|
|||||||
async def stop(interaction: discord.Interaction):
|
async def stop(interaction: discord.Interaction):
|
||||||
await stop_audio(interaction)
|
await stop_audio(interaction)
|
||||||
|
|
||||||
|
@client.tree.command(name="pause", description="Pause the current song.")
|
||||||
|
async def pause(interaction: discord.Interaction):
|
||||||
|
await pause_audio(interaction)
|
||||||
|
|
||||||
|
@client.tree.command(name="resume", description="Resume the paused song.")
|
||||||
|
async def resume(interaction: discord.Interaction):
|
||||||
|
await resume_audio(interaction)
|
||||||
|
|
||||||
|
|
||||||
@client.tree.command(name="volume", description="Set playback volume.")
|
@client.tree.command(name="volume", description="Set playback volume.")
|
||||||
async def volume(interaction: discord.Interaction, level: int):
|
async def volume(interaction: discord.Interaction, level: int):
|
||||||
await set_volume(interaction, level)
|
await set_volume(interaction, level)
|
||||||
|
|
||||||
|
@client.tree.command(name="recommend", description="Get song recommendations based on your listening history.")
|
||||||
|
async def recommend(interaction: discord.Interaction):
|
||||||
|
user_id = interaction.user.id
|
||||||
|
recommendations = generate_recommendations(user_id)
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="🎵 Recommended Songs",
|
||||||
|
description="\n".join(recommendations),
|
||||||
|
color=discord.Color.green()
|
||||||
|
)
|
||||||
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
await client.tree.sync()
|
await client.tree.sync()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user