Compare commits
No commits in common. "main" and "dev" have entirely different histories.
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
# Overview
|
# Overview
|
||||||
|
|
||||||
Guava is a Discord music bot with support for multiple different music and video streaming platforms. Guava is a part of >225 Discord servers and currently supports these services:
|
Guava is a Discord music bot with support for multiple different music and video streaming platforms. Guava is a part of >200 Discord servers and currently supports these services:
|
||||||
|
|
||||||
- YouTube
|
- YouTube
|
||||||
- Apple Music
|
- Apple Music
|
||||||
@ -106,11 +106,10 @@ Field | Description | Requirement
|
|||||||
GENIUS_CLIENT_ID | `CLIENT ID`: ID from Genius API Dashboard | **OPTIONAL** - *Used for the /lyrics command*
|
GENIUS_CLIENT_ID | `CLIENT ID`: ID from Genius API Dashboard | **OPTIONAL** - *Used for the /lyrics command*
|
||||||
GENIUS_CLIENT_SECRET | `CLIENT SECRET`: Secret string from Genius API Dashboard | **OPTIONAL** - *Used for the /lyrics command*
|
GENIUS_CLIENT_SECRET | `CLIENT SECRET`: Secret string from Genius API Dashboard | **OPTIONAL** - *Used for the /lyrics command*
|
||||||
|
|
||||||
## AI | OPTIONAL
|
## OPENAI | OPTIONAL
|
||||||
Field | Description | Requirement
|
Field | Description | Requirement
|
||||||
--- | --- | ---
|
--- | --- | ---
|
||||||
SERVICE | Which providers API you will use. Supports `groq` or `openai` | **OPTIONAL** - *Used to support the /autoplay feature*
|
OPENAI_API_KEY | API Key from OpenAI for autoplay recommendations | **OPTIONAL** - *Used to support the /autoplay feature*
|
||||||
API_KEY | API key for the provider you are using | **OPTIONAL** - *Used to support the /autoplay feature*
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
server:
|
server:
|
||||||
port: 2333
|
port: 2333
|
||||||
address: 127.0.0.1
|
address: localhost
|
||||||
http2:
|
http2:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
@ -15,9 +15,9 @@ plugins:
|
|||||||
- ANDROID_VR
|
- ANDROID_VR
|
||||||
- WEB
|
- WEB
|
||||||
- WEBEMBEDDED
|
- WEBEMBEDDED
|
||||||
pot: # https://github.com/iv-org/youtube-trusted-session-generator
|
pot:
|
||||||
token: ""
|
token: "" # Your token data here
|
||||||
visitorData: ""
|
visitorData: "" # Your visitor data here
|
||||||
lavasrc:
|
lavasrc:
|
||||||
providers:
|
providers:
|
||||||
- "scsearch:\"%ISRC%\""
|
- "scsearch:\"%ISRC%\""
|
||||||
@ -29,19 +29,16 @@ plugins:
|
|||||||
applemusic: false
|
applemusic: false
|
||||||
deezer: true
|
deezer: true
|
||||||
yandexmusic: false
|
yandexmusic: false
|
||||||
flowerytts: false
|
flowerytts: falsee
|
||||||
youtube: false
|
youtube: false
|
||||||
deezer:
|
deezer:
|
||||||
masterDecryptionKey: "" # Find on your own
|
masterDecryptionKey: "" # master decryption key from deezer
|
||||||
arl: "" # Guides can be found, use Google
|
|
||||||
formats: [ "MP3_128", "MP3_64" ] # "FLAC", "MP3_320", "MP3_256", & "AAC_64" require premium and valid arl
|
|
||||||
|
|
||||||
lavalink:
|
lavalink:
|
||||||
plugins:
|
plugins:
|
||||||
- dependency: "dev.lavalink.youtube:youtube-plugin:5ce67f60c656e0dc60687ea7b471663c8718ea1c"
|
- dependency: "dev.lavalink.youtube:youtube-plugin:1.11.1"
|
||||||
repository: "https://maven.kikkia.dev/snapshots"
|
snapshot: false
|
||||||
snapshot: true
|
- dependency: "com.github.topi314.lavasrc:lavasrc-plugin:4.3.0"
|
||||||
- dependency: "com.github.topi314.lavasrc:lavasrc-plugin:4.4.2"
|
|
||||||
snapshot: false
|
snapshot: false
|
||||||
|
|
||||||
server:
|
server:
|
||||||
@ -72,22 +69,22 @@ lavalink:
|
|||||||
resamplingQuality: LOW # Quality of resampling operations. Valid values are LOW, MEDIUM and HIGH, where HIGH uses the most CPU.
|
resamplingQuality: LOW # Quality of resampling operations. Valid values are LOW, MEDIUM and HIGH, where HIGH uses the most CPU.
|
||||||
trackStuckThresholdMs: 10000 # The threshold for how long a track can be stuck. A track is stuck if does not return any audio data.
|
trackStuckThresholdMs: 10000 # The threshold for how long a track can be stuck. A track is stuck if does not return any audio data.
|
||||||
useSeekGhosting: true # Seek ghosting is the effect where whilst a seek is in progress, the audio buffer is read from until empty, or until seek is ready.
|
useSeekGhosting: true # Seek ghosting is the effect where whilst a seek is in progress, the audio buffer is read from until empty, or until seek is ready.
|
||||||
youtubePlaylistLoadLimit: 3 # Number of pages at 100 each
|
youtubePlaylistLoadLimit: 6 # Number of pages at 100 each
|
||||||
playerUpdateInterval: 5 # How frequently to send player updates to clients, in seconds
|
playerUpdateInterval: 5 # How frequently to send player updates to clients, in seconds
|
||||||
youtubeSearchEnabled: true
|
youtubeSearchEnabled: true
|
||||||
soundcloudSearchEnabled: true
|
soundcloudSearchEnabled: true
|
||||||
gc-warnings: true
|
gc-warnings: true
|
||||||
# ratelimit:
|
#ratelimit:
|
||||||
# ipBlocks: [""] # list of ip blocks
|
# ipBlocks: [""] # list of ip blocks
|
||||||
# excludedIps: [] # ips which should be explicit excluded from usage by lavalink
|
# excludedIps: [] # ips which should be explicit excluded from usage by lavalink
|
||||||
# strategy: "LoadBalance" # RotateOnBan | LoadBalance | NanoSwitch | RotatingNanoSwitch
|
# strategy: "LoadBalance" # RotateOnBan | LoadBalance | NanoSwitch | RotatingNanoSwitch
|
||||||
# searchTriggersFail: true # Whether a search 429 should trigger marking the ip as failing
|
# searchTriggersFail: true # Whether a search 429 should trigger marking the ip as failing
|
||||||
# retryLimit: -1 # -1 = use default lavaplayer value | 0 = infinity | >0 = retry will happen this numbers times
|
# retryLimit: -1 # -1 = use default lavaplayer value | 0 = infinity | >0 = retry will happen this numbers times
|
||||||
# httpConfig: # Useful for blocking bad-actors from ip-grabbing your music node and attacking it, this way only the http proxy will be attacked
|
#httpConfig: # Useful for blocking bad-actors from ip-grabbing your music node and attacking it, this way only the http proxy will be attacked
|
||||||
# proxyHost: "localhost" # Hostname of the proxy, (ip or domain)
|
#proxyHost: "localhost" # Hostname of the proxy, (ip or domain)
|
||||||
# proxyPort: 3128 # Proxy port, 3128 is the default for squidProxy
|
#proxyPort: 3128 # Proxy port, 3128 is the default for squidProxy
|
||||||
# proxyUser: "" # Optional user for basic authentication fields, leave blank if you don't use basic auth
|
#proxyUser: "" # Optional user for basic authentication fields, leave blank if you don't use basic auth
|
||||||
# proxyPassword: "" # Password for basic authentication
|
#proxyPassword: "" # Password for basic authentication
|
||||||
|
|
||||||
metrics:
|
metrics:
|
||||||
prometheus:
|
prometheus:
|
||||||
@ -97,9 +94,9 @@ metrics:
|
|||||||
sentry:
|
sentry:
|
||||||
dsn: ""
|
dsn: ""
|
||||||
environment: ""
|
environment: ""
|
||||||
# tags:
|
# tags:
|
||||||
# some_key: some_value
|
# some_key: some_value
|
||||||
# another_key: another_value
|
# another_key: another_value
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
file:
|
file:
|
||||||
|
24
code/bot.py
24
code/bot.py
@ -2,6 +2,7 @@ import discord
|
|||||||
from discord.ext import commands, tasks
|
from discord.ext import commands, tasks
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
|
import openai
|
||||||
import lyricsgenius
|
import lyricsgenius
|
||||||
|
|
||||||
import utils.config as config
|
import utils.config as config
|
||||||
@ -19,17 +20,19 @@ class MyBot(commands.Bot):
|
|||||||
)
|
)
|
||||||
|
|
||||||
async def setup_hook(self):
|
async def setup_hook(self):
|
||||||
# Get Spotify, Apple Music, and Genius access tokens/clients
|
# Get Spotify, Apple Music, Genius, and OpenAI access tokens/clients
|
||||||
get_access_token.start()
|
get_access_token.start()
|
||||||
refresh_media_api_key.start()
|
refresh_media_api_key.start()
|
||||||
login_genius.start()
|
login_genius.start()
|
||||||
|
if config.OPENAI_API_KEY:
|
||||||
|
bot.openai = openai.OpenAI(api_key=config.OPENAI_API_KEY)
|
||||||
|
|
||||||
config.LOG.info("Loading cogs...")
|
config.LOG.info("Loading cogs...")
|
||||||
if config.YOUTUBE_SUPPORT:
|
|
||||||
config.LOG.info(
|
config.LOG.info(
|
||||||
"YouTube support is enabled, make sure to set a poToken"
|
"YouTube support is enabled, make sure to set a poToken"
|
||||||
|
if config.YOUTUBE_SUPPORT
|
||||||
|
else "YouTube support is disabled"
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
config.LOG.warn("YouTube support is disabled")
|
|
||||||
for ext in os.listdir("./code/cogs"):
|
for ext in os.listdir("./code/cogs"):
|
||||||
if ext.endswith(".py"):
|
if ext.endswith(".py"):
|
||||||
# Load the OPTIONAL feedback cog
|
# Load the OPTIONAL feedback cog
|
||||||
@ -37,28 +40,28 @@ class MyBot(commands.Bot):
|
|||||||
ext[:-3] == "feedback"
|
ext[:-3] == "feedback"
|
||||||
and config.FEEDBACK_CHANNEL_ID == None
|
and config.FEEDBACK_CHANNEL_ID == None
|
||||||
):
|
):
|
||||||
config.LOG.warn(
|
config.LOG.info(
|
||||||
"Skipped loading feedback cog - channel ID not"
|
"Skipped loading feedback cog - channel ID not"
|
||||||
" provided"
|
" provided"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
# Load the OPTIONAL bug cog
|
# Load the OPTIONAL bug cog
|
||||||
if ext[:-3] == "bug" and config.BUG_CHANNEL_ID == None:
|
if ext[:-3] == "bug" and config.BUG_CHANNEL_ID == None:
|
||||||
config.LOG.warn(
|
config.LOG.info(
|
||||||
"Skipped loading bug cog - channel ID not provided"
|
"Skipped loading bug cog - channel ID not provided"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
# Load the OPTIONAL lyrics cog
|
# Load the OPTIONAL lyrics cog
|
||||||
if ext[:-3] == "lyrics" and config.GENIUS_CLIENT_ID == None:
|
if ext[:-3] == "lyrics" and config.GENIUS_CLIENT_ID == None:
|
||||||
config.LOG.warn(
|
config.LOG.info(
|
||||||
"Skipped loading lyrics cog - Genius API credentials"
|
"Skipped loading lyrics cog - Genius API credentials"
|
||||||
" not provided"
|
" not provided"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
# Load the OPTIONAL autoplay cog
|
# Load the OPTIONAL autoplay cog
|
||||||
if ext[:-3] == "autoplay" and config.AI_CLIENT == None:
|
if ext[:-3] == "autoplay" and config.OPENAI_API_KEY == None:
|
||||||
config.LOG.warn(
|
config.LOG.info(
|
||||||
"Skipped loading autoplay cog - AI API credentials"
|
"Skipped loading autoplay cog - OpenAI API credentials"
|
||||||
" not provided"
|
" not provided"
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
@ -80,7 +83,6 @@ bot = MyBot()
|
|||||||
bot.remove_command("help")
|
bot.remove_command("help")
|
||||||
bot.temp_command_count = {} # command_name: count
|
bot.temp_command_count = {} # command_name: count
|
||||||
bot.autoplay = [] # guild_id, guild_id, etc.
|
bot.autoplay = [] # guild_id, guild_id, etc.
|
||||||
bot.youtube_broken = False
|
|
||||||
|
|
||||||
|
|
||||||
@tasks.loop(minutes=45)
|
@tasks.loop(minutes=45)
|
||||||
|
@ -75,7 +75,9 @@ class Autoplay(commands.Cog):
|
|||||||
)
|
)
|
||||||
await interaction.response.send_message(embed=embed)
|
await interaction.response.send_message(embed=embed)
|
||||||
|
|
||||||
if await add_song_recommendations(self.bot.user, player, 5, inputs):
|
if await add_song_recommendations(
|
||||||
|
self.bot.openai, self.bot.user, player, 5, inputs
|
||||||
|
):
|
||||||
self.bot.autoplay.append(interaction.guild.id)
|
self.bot.autoplay.append(interaction.guild.id)
|
||||||
embed = create_embed(
|
embed = create_embed(
|
||||||
title=":infinity: Autoplay Enabled :infinity:",
|
title=":infinity: Autoplay Enabled :infinity:",
|
||||||
|
@ -56,8 +56,7 @@ commands_and_descriptions = {
|
|||||||
},
|
},
|
||||||
"autoplay": {
|
"autoplay": {
|
||||||
"description": (
|
"description": (
|
||||||
"Keep the music playing forever with automatic song"
|
"Keep the music playing forever with music suggestions from OpenAI"
|
||||||
" recommendations"
|
|
||||||
),
|
),
|
||||||
"arguments": {
|
"arguments": {
|
||||||
"on": "Turn autoplay feature on",
|
"on": "Turn autoplay feature on",
|
||||||
|
@ -245,7 +245,7 @@ class Music(commands.Cog):
|
|||||||
for song in event.player.queue[:10]:
|
for song in event.player.queue[:10]:
|
||||||
inputs[song.title] = song.author
|
inputs[song.title] = song.author
|
||||||
await add_song_recommendations(
|
await add_song_recommendations(
|
||||||
self.bot.user, event.player, 5, inputs
|
self.bot.openai, self.bot.user, event.player, 5, inputs
|
||||||
)
|
)
|
||||||
|
|
||||||
@lavalink.listener(lavalink.events.NodeConnectedEvent)
|
@lavalink.listener(lavalink.events.NodeConnectedEvent)
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
from discord.ext import commands
|
|
||||||
|
|
||||||
|
|
||||||
class Send(commands.Cog):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
@commands.command()
|
|
||||||
@commands.dm_only()
|
|
||||||
@commands.is_owner()
|
|
||||||
async def send(self, ctx, user_id: int, *, message: str):
|
|
||||||
"""Send a message to a user (follow-up on bug reports)"""
|
|
||||||
user = await self.bot.fetch_user(user_id)
|
|
||||||
|
|
||||||
if not user:
|
|
||||||
return await ctx.send("User not found.")
|
|
||||||
|
|
||||||
elif not message:
|
|
||||||
return await ctx.send("No message for user.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
await user.send(message)
|
|
||||||
await ctx.send("Message sent to user.")
|
|
||||||
except Exception as e:
|
|
||||||
await ctx.send("Error sending message to user.")
|
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
|
||||||
await bot.add_cog(Send(bot))
|
|
@ -1,31 +0,0 @@
|
|||||||
from discord.ext import commands
|
|
||||||
from typing import Literal
|
|
||||||
|
|
||||||
|
|
||||||
class Toggle(commands.Cog):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
@commands.command()
|
|
||||||
@commands.dm_only()
|
|
||||||
@commands.is_owner()
|
|
||||||
async def toggle(self, ctx, action: Literal["on", "off"]):
|
|
||||||
"""Toggle YouTube as broken or not"""
|
|
||||||
if action == "on":
|
|
||||||
self.bot.youtube_broken = False
|
|
||||||
return await ctx.send("YouTube has been enabled.")
|
|
||||||
|
|
||||||
if action == "off":
|
|
||||||
self.bot.youtube_broken = True
|
|
||||||
return await ctx.send("YouTube has been marked as broken.")
|
|
||||||
|
|
||||||
@toggle.error
|
|
||||||
async def toggle_error(self, ctx, error):
|
|
||||||
if isinstance(error, commands.BadLiteralArgument):
|
|
||||||
return await ctx.send("Invalid action. Use either 'on' or 'off'.")
|
|
||||||
else:
|
|
||||||
return await ctx.send("An unknown error occurred.")
|
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot):
|
|
||||||
await bot.add_cog(Toggle(bot))
|
|
@ -45,22 +45,6 @@ class Play(commands.Cog):
|
|||||||
embed=embed, ephemeral=True
|
embed=embed, ephemeral=True
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.bot.youtube_broken:
|
|
||||||
embed = create_embed(
|
|
||||||
title="YouTube Broken",
|
|
||||||
description=(
|
|
||||||
"YouTube support is currently broken. This is a known"
|
|
||||||
" issue and is being actively worked on, please try"
|
|
||||||
" again later. Other sources should still be in"
|
|
||||||
" working order. Submit a bug report with "
|
|
||||||
" </bug:1224840889906499626> if issues persist. Sorry"
|
|
||||||
" for the inconvenience."
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return await interaction.response.send_message(
|
|
||||||
embed=embed, ephemeral=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check for custom sources (Apple Music/Spotify)
|
# Check for custom sources (Apple Music/Spotify)
|
||||||
if "music.apple.com" in query:
|
if "music.apple.com" in query:
|
||||||
results, embed = await parse_custom_source(
|
results, embed = await parse_custom_source(
|
||||||
|
@ -1,52 +1,40 @@
|
|||||||
from lavalink import LoadType
|
from lavalink import LoadType
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from utils.config import AI_CLIENT, AI_MODEL
|
|
||||||
|
|
||||||
|
|
||||||
async def add_song_recommendations(
|
async def add_song_recommendations(
|
||||||
bot_user, player, number, inputs, retries: int = 1
|
openai_client, bot_user, player, number, inputs, retries: int = 1
|
||||||
):
|
):
|
||||||
input_list = [f'"{song} by {artist}"' for song, artist in inputs.items()]
|
input_list = [f'"{song} by {artist}"' for song, artist in inputs.items()]
|
||||||
|
|
||||||
completion = (
|
completion = (
|
||||||
AI_CLIENT.chat.completions.create(
|
openai_client.chat.completions.create(
|
||||||
messages=[
|
messages=[
|
||||||
{
|
|
||||||
"role": "system",
|
|
||||||
"content": f"""
|
|
||||||
Given an input list of songs formatted as ["song_name
|
|
||||||
by artist_name", "song_name by artist_name", ...], generate
|
|
||||||
a list of 5 new songs that the user may enjoy based on
|
|
||||||
the input.
|
|
||||||
|
|
||||||
Thoroughly analyze each song in the input list, considering
|
|
||||||
factors such as tempo, beat, mood, genre, lyrical themes,
|
|
||||||
instrumentation, and overall meaning. Use this analysis to
|
|
||||||
recommend 5 songs that closely align with the user's musical
|
|
||||||
preferences.
|
|
||||||
|
|
||||||
The output must be formatted in the exact same way:
|
|
||||||
["song_name by artist_name", "song_name by artist_name", ...].
|
|
||||||
|
|
||||||
If you are unable to find 5 new songs or encounter any issues,
|
|
||||||
return the following list instead: ["NOTHING_FOUND"]. Do
|
|
||||||
not return partial results—either provide 5 songs or return
|
|
||||||
["NOTHING_FOUND"]. Ensure accuracy in song and artist names.
|
|
||||||
|
|
||||||
DO NOT include any additional information or text in the
|
|
||||||
output, it should STRICTLY be either a list of the songs
|
|
||||||
or ["NOTHING_FOUND"].
|
|
||||||
""",
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": f"""
|
"content": f"""
|
||||||
{input_list}
|
BACKGROUND: You're an AI music recommendation system with a knack for understanding
|
||||||
|
user preferences based on provided input. Your task is to generate a list
|
||||||
|
of {number} songs that the user might enjoy, derived from a given list of {number} songs.
|
||||||
|
The input will be in the format of
|
||||||
|
["Song-1-Name by Song-1-Artist", "Song-2-Name by Song-2-Artist", ...]
|
||||||
|
and you need to return a list formatted in the same way.
|
||||||
|
|
||||||
|
When recommending songs, consider the genre, tempo, and mood of the input
|
||||||
|
songs to suggest similar ones that align with the user's tastes. Also, it
|
||||||
|
is important to mix up the artists, don't only give the same artists that
|
||||||
|
are already in the queue. If you cannot find {number} songs that match the
|
||||||
|
criteria or encounter any issues, return the list ["NOTHING FOUND"].
|
||||||
|
|
||||||
|
Please be sure to also only use characters A-Z, a-z, 0-9, and spaces in the
|
||||||
|
song and artist names. Do not include escape/special characters, emojis, or
|
||||||
|
quotes in the output.
|
||||||
|
|
||||||
|
INPUT: {input_list}
|
||||||
""",
|
""",
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
model=AI_MODEL,
|
model="gpt-4o-mini",
|
||||||
)
|
)
|
||||||
.choices[0]
|
.choices[0]
|
||||||
.message.content.strip()
|
.message.content.strip()
|
||||||
@ -59,7 +47,7 @@ async def add_song_recommendations(
|
|||||||
if completion == '["NOTHING FOUND"]':
|
if completion == '["NOTHING FOUND"]':
|
||||||
if retries <= 3:
|
if retries <= 3:
|
||||||
await add_song_recommendations(
|
await add_song_recommendations(
|
||||||
bot_user, player, number, inputs, retries + 1
|
openai_client, bot_user, player, number, inputs, retries + 1
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
@ -8,7 +8,7 @@ import sys
|
|||||||
import discord
|
import discord
|
||||||
import logging
|
import logging
|
||||||
import requests
|
import requests
|
||||||
from groq import Groq
|
from datetime import datetime
|
||||||
from colorlog import ColoredFormatter
|
from colorlog import ColoredFormatter
|
||||||
|
|
||||||
log_level = logging.DEBUG
|
log_level = logging.DEBUG
|
||||||
@ -39,8 +39,7 @@ SPOTIFY_CLIENT_ID = None
|
|||||||
SPOTIFY_CLIENT_SECRET = None
|
SPOTIFY_CLIENT_SECRET = None
|
||||||
GENIUS_CLIENT_ID = None
|
GENIUS_CLIENT_ID = None
|
||||||
GENIUS_CLIENT_SECRET = None
|
GENIUS_CLIENT_SECRET = None
|
||||||
AI_CLIENT = None
|
OPENAI_API_KEY = None
|
||||||
AI_MODEL = None
|
|
||||||
LAVALINK_HOST = None
|
LAVALINK_HOST = None
|
||||||
LAVALINK_PORT = None
|
LAVALINK_PORT = None
|
||||||
LAVALINK_PASSWORD = None
|
LAVALINK_PASSWORD = None
|
||||||
@ -83,13 +82,12 @@ schema = {
|
|||||||
},
|
},
|
||||||
"required": ["genius_client_id", "genius_client_secret"],
|
"required": ["genius_client_id", "genius_client_secret"],
|
||||||
},
|
},
|
||||||
"ai": {
|
"openai": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"service": {"enum": ["openai", "groq"]},
|
"openai_api_key": {"type": "string"},
|
||||||
"api_key": {"type": "string"},
|
|
||||||
},
|
},
|
||||||
"required": ["service"],
|
"required": ["openai_api_key"],
|
||||||
},
|
},
|
||||||
"lavalink": {
|
"lavalink": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@ -146,9 +144,9 @@ genius:
|
|||||||
genius_client_id:
|
genius_client_id:
|
||||||
genius_client_secret:
|
genius_client_secret:
|
||||||
|
|
||||||
ai:
|
openai:
|
||||||
service:
|
openai_api_key:
|
||||||
api_key: """
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
sys.exit(
|
sys.exit(
|
||||||
@ -162,7 +160,7 @@ ai:
|
|||||||
|
|
||||||
# Thouroughly validate all of the options in the config.yaml file
|
# Thouroughly validate all of the options in the config.yaml file
|
||||||
def validate_config(file_contents):
|
def validate_config(file_contents):
|
||||||
global TOKEN, BOT_COLOR, BOT_INVITE_LINK, FEEDBACK_CHANNEL_ID, BUG_CHANNEL_ID, LOG_SONGS, YOUTUBE_SUPPORT, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, GENIUS_CLIENT_ID, GENIUS_CLIENT_SECRET, AI_CLIENT, AI_MODEL, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD
|
global TOKEN, BOT_COLOR, BOT_INVITE_LINK, FEEDBACK_CHANNEL_ID, BUG_CHANNEL_ID, LOG_SONGS, YOUTUBE_SUPPORT, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, GENIUS_CLIENT_ID, GENIUS_CLIENT_SECRET, OPENAI_API_KEY, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD
|
||||||
config = yaml.safe_load(file_contents)
|
config = yaml.safe_load(file_contents)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -272,24 +270,17 @@ def validate_config(file_contents):
|
|||||||
)
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
# If the AI section is present, make sure the API key is valid
|
# If the OPENAI section is present, make sure the API key is valid
|
||||||
#
|
#
|
||||||
|
|
||||||
if "ai" in config:
|
if "openai" in config:
|
||||||
if config["ai"]["service"] == "openai":
|
client = openai.OpenAI(api_key=config["openai"]["openai_api_key"])
|
||||||
client = openai.OpenAI(api_key=config["ai"]["api_key"])
|
|
||||||
model = "gpt-4o-mini"
|
|
||||||
elif config["ai"]["service"] == "groq":
|
|
||||||
client = Groq(api_key=config["ai"]["api_key"])
|
|
||||||
model = "llama-3.3-70b-specdec"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client.models.list()
|
client.models.list()
|
||||||
AI_CLIENT = client
|
OPENAI_API_KEY = config["openai"]["openai_api_key"]
|
||||||
AI_MODEL = model
|
|
||||||
except openai.AuthenticationError:
|
except openai.AuthenticationError:
|
||||||
LOG.critical(
|
LOG.critical(
|
||||||
"Error in config.yaml file: OpenAI/Groq API key is invalid"
|
"Error in config.yaml file: OpenAI API key is invalid"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set appropriate values for all non-optional variables
|
# Set appropriate values for all non-optional variables
|
||||||
|
@ -22,6 +22,5 @@ genius:
|
|||||||
genius_client_id:
|
genius_client_id:
|
||||||
genius_client_secret:
|
genius_client_secret:
|
||||||
|
|
||||||
ai:
|
openai:
|
||||||
service:
|
openai_api_key:
|
||||||
api_key:
|
|
@ -4,7 +4,6 @@ colorlog==6.8.2
|
|||||||
validators==0.28.3
|
validators==0.28.3
|
||||||
openai==1.56.0
|
openai==1.56.0
|
||||||
requests==2.32.3
|
requests==2.32.3
|
||||||
lyricsgenius==3.2.0
|
lyricsgenius==3.0.1
|
||||||
PyYAML==6.0.1
|
PyYAML==6.0.1
|
||||||
jsonschema==4.23.0
|
jsonschema==4.23.0
|
||||||
groq==0.18.0
|
|
Loading…
x
Reference in New Issue
Block a user