Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
4a64f9b0b2 | |||
5c5a00483f | |||
dd873d5b11 | |||
03e7111604 | |||
b343d0d45b | |||
106692551b | |||
e578077df3 | |||
06034d0b37 | |||
b335e82699 | |||
378e1c9057 | |||
ca9e76d694 | |||
9221d04287 | |||
5c4e55c502 |
@ -25,7 +25,7 @@
|
||||
|
||||
# Overview
|
||||
|
||||
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:
|
||||
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:
|
||||
|
||||
- YouTube
|
||||
- Apple Music
|
||||
@ -106,10 +106,11 @@ Field | Description | Requirement
|
||||
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*
|
||||
|
||||
## OPENAI | OPTIONAL
|
||||
## AI | OPTIONAL
|
||||
Field | Description | Requirement
|
||||
--- | --- | ---
|
||||
OPENAI_API_KEY | API Key from OpenAI for autoplay recommendations | **OPTIONAL** - *Used to support the /autoplay feature*
|
||||
SERVICE | Which providers API you will use. Supports `groq` or `openai` | **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>
|
||||
|
||||
|
211
application.yml
211
application.yml
@ -1,120 +1,123 @@
|
||||
server:
|
||||
port: 2333
|
||||
address: localhost
|
||||
http2:
|
||||
enabled: false
|
||||
port: 2333
|
||||
address: 127.0.0.1
|
||||
http2:
|
||||
enabled: false
|
||||
|
||||
plugins:
|
||||
youtube:
|
||||
enabled: true
|
||||
allowSearch: true
|
||||
allowDirectVideoIds: true
|
||||
allowDirectPlaylistIds: true
|
||||
clients:
|
||||
- MUSIC
|
||||
- ANDROID_VR
|
||||
- WEB
|
||||
- WEBEMBEDDED
|
||||
pot:
|
||||
token: "" # Your token data here
|
||||
visitorData: "" # Your visitor data here
|
||||
lavasrc:
|
||||
providers:
|
||||
- "scsearch:\"%ISRC%\""
|
||||
- "scsearch:%QUERY%"
|
||||
- "dzisrc:\"%ISRC%\""
|
||||
- "dzsearch:%QUERY%"
|
||||
sources:
|
||||
spotify: false
|
||||
applemusic: false
|
||||
deezer: true
|
||||
yandexmusic: false
|
||||
flowerytts: falsee
|
||||
youtube: false
|
||||
deezer:
|
||||
masterDecryptionKey: "" # master decryption key from deezer
|
||||
youtube:
|
||||
enabled: true
|
||||
allowSearch: true
|
||||
allowDirectVideoIds: true
|
||||
allowDirectPlaylistIds: true
|
||||
clients:
|
||||
- MUSIC
|
||||
- ANDROID_VR
|
||||
- WEB
|
||||
- WEBEMBEDDED
|
||||
pot: # https://github.com/iv-org/youtube-trusted-session-generator
|
||||
token: ""
|
||||
visitorData: ""
|
||||
lavasrc:
|
||||
providers:
|
||||
- "scsearch:\"%ISRC%\""
|
||||
- "scsearch:%QUERY%"
|
||||
- "dzisrc:\"%ISRC%\""
|
||||
- "dzsearch:%QUERY%"
|
||||
sources:
|
||||
spotify: false
|
||||
applemusic: false
|
||||
deezer: true
|
||||
yandexmusic: false
|
||||
flowerytts: false
|
||||
youtube: false
|
||||
deezer:
|
||||
masterDecryptionKey: "" # Find on your own
|
||||
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:
|
||||
plugins:
|
||||
- dependency: "dev.lavalink.youtube:youtube-plugin:1.11.1"
|
||||
snapshot: false
|
||||
- dependency: "com.github.topi314.lavasrc:lavasrc-plugin:4.3.0"
|
||||
snapshot: false
|
||||
plugins:
|
||||
- dependency: "dev.lavalink.youtube:youtube-plugin:5ce67f60c656e0dc60687ea7b471663c8718ea1c"
|
||||
repository: "https://maven.kikkia.dev/snapshots"
|
||||
snapshot: true
|
||||
- dependency: "com.github.topi314.lavasrc:lavasrc-plugin:4.4.2"
|
||||
snapshot: false
|
||||
|
||||
server:
|
||||
password: "youshallnotpass"
|
||||
sources:
|
||||
youtube: false
|
||||
bandcamp: true
|
||||
soundcloud: true
|
||||
twitch: true
|
||||
vimeo: true
|
||||
http: true
|
||||
local: false
|
||||
filters:
|
||||
volume: false
|
||||
equalizer: false
|
||||
karaoke: false
|
||||
timescale: false
|
||||
tremolo: false
|
||||
vibrato: false
|
||||
distortion: false
|
||||
rotation: false
|
||||
channelMix: false
|
||||
lowPass: false
|
||||
server:
|
||||
password: "youshallnotpass"
|
||||
sources:
|
||||
youtube: false
|
||||
bandcamp: true
|
||||
soundcloud: true
|
||||
twitch: true
|
||||
vimeo: true
|
||||
http: true
|
||||
local: false
|
||||
filters:
|
||||
volume: false
|
||||
equalizer: false
|
||||
karaoke: false
|
||||
timescale: false
|
||||
tremolo: false
|
||||
vibrato: false
|
||||
distortion: false
|
||||
rotation: false
|
||||
channelMix: false
|
||||
lowPass: false
|
||||
|
||||
bufferDurationMs: 400 # The duration of the NAS buffer. Higher values fare better against longer GC pauses. Duration <= 0 to disable JDA-NAS. Minimum of 40ms, lower values may introduce pauses.
|
||||
frameBufferDurationMs: 8000 # How many milliseconds of audio to keep buffered
|
||||
opusEncodingQuality: 10 # Opus encoder quality. Valid values range from 0 to 10, where 10 is best quality but is the most expensive on the 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.
|
||||
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: 6 # Number of pages at 100 each
|
||||
playerUpdateInterval: 5 # How frequently to send player updates to clients, in seconds
|
||||
youtubeSearchEnabled: true
|
||||
soundcloudSearchEnabled: true
|
||||
gc-warnings: true
|
||||
#ratelimit:
|
||||
# ipBlocks: [""] # list of ip blocks
|
||||
# excludedIps: [] # ips which should be explicit excluded from usage by lavalink
|
||||
# strategy: "LoadBalance" # RotateOnBan | LoadBalance | NanoSwitch | RotatingNanoSwitch
|
||||
# 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
|
||||
#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)
|
||||
#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
|
||||
#proxyPassword: "" # Password for basic authentication
|
||||
bufferDurationMs: 400 # The duration of the NAS buffer. Higher values fare better against longer GC pauses. Duration <= 0 to disable JDA-NAS. Minimum of 40ms, lower values may introduce pauses.
|
||||
frameBufferDurationMs: 8000 # How many milliseconds of audio to keep buffered
|
||||
opusEncodingQuality: 10 # Opus encoder quality. Valid values range from 0 to 10, where 10 is best quality but is the most expensive on the 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.
|
||||
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
|
||||
playerUpdateInterval: 5 # How frequently to send player updates to clients, in seconds
|
||||
youtubeSearchEnabled: true
|
||||
soundcloudSearchEnabled: true
|
||||
gc-warnings: true
|
||||
# ratelimit:
|
||||
# ipBlocks: [""] # list of ip blocks
|
||||
# excludedIps: [] # ips which should be explicit excluded from usage by lavalink
|
||||
# strategy: "LoadBalance" # RotateOnBan | LoadBalance | NanoSwitch | RotatingNanoSwitch
|
||||
# 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
|
||||
# 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)
|
||||
# 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
|
||||
# proxyPassword: "" # Password for basic authentication
|
||||
|
||||
metrics:
|
||||
prometheus:
|
||||
enabled: false
|
||||
endpoint: /metrics
|
||||
prometheus:
|
||||
enabled: false
|
||||
endpoint: /metrics
|
||||
|
||||
sentry:
|
||||
dsn: ""
|
||||
environment: ""
|
||||
# tags:
|
||||
# some_key: some_value
|
||||
# another_key: another_value
|
||||
dsn: ""
|
||||
environment: ""
|
||||
# tags:
|
||||
# some_key: some_value
|
||||
# another_key: another_value
|
||||
|
||||
logging:
|
||||
file:
|
||||
path: ./logs/
|
||||
file:
|
||||
path: ./logs/
|
||||
|
||||
level:
|
||||
root: INFO
|
||||
lavalink: INFO
|
||||
level:
|
||||
root: INFO
|
||||
lavalink: INFO
|
||||
|
||||
request:
|
||||
enabled: true
|
||||
includeClientInfo: true
|
||||
includeHeaders: false
|
||||
includeQueryString: true
|
||||
includePayload: true
|
||||
maxPayloadLength: 10000
|
||||
request:
|
||||
enabled: true
|
||||
includeClientInfo: true
|
||||
includeHeaders: false
|
||||
includeQueryString: true
|
||||
includePayload: true
|
||||
maxPayloadLength: 10000
|
||||
|
||||
logback:
|
||||
rollingpolicy:
|
||||
max-file-size: 1GB
|
||||
max-history: 30
|
||||
logback:
|
||||
rollingpolicy:
|
||||
max-file-size: 1GB
|
||||
max-history: 30
|
||||
|
30
code/bot.py
30
code/bot.py
@ -2,7 +2,6 @@ import discord
|
||||
from discord.ext import commands, tasks
|
||||
import os
|
||||
import requests
|
||||
import openai
|
||||
import lyricsgenius
|
||||
|
||||
import utils.config as config
|
||||
@ -20,19 +19,17 @@ class MyBot(commands.Bot):
|
||||
)
|
||||
|
||||
async def setup_hook(self):
|
||||
# Get Spotify, Apple Music, Genius, and OpenAI access tokens/clients
|
||||
# Get Spotify, Apple Music, and Genius access tokens/clients
|
||||
get_access_token.start()
|
||||
refresh_media_api_key.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(
|
||||
"YouTube support is enabled, make sure to set a poToken"
|
||||
if config.YOUTUBE_SUPPORT
|
||||
else "YouTube support is disabled"
|
||||
)
|
||||
if config.YOUTUBE_SUPPORT:
|
||||
config.LOG.info(
|
||||
"YouTube support is enabled, make sure to set a poToken"
|
||||
)
|
||||
else:
|
||||
config.LOG.warn("YouTube support is disabled")
|
||||
for ext in os.listdir("./code/cogs"):
|
||||
if ext.endswith(".py"):
|
||||
# Load the OPTIONAL feedback cog
|
||||
@ -40,28 +37,28 @@ class MyBot(commands.Bot):
|
||||
ext[:-3] == "feedback"
|
||||
and config.FEEDBACK_CHANNEL_ID == None
|
||||
):
|
||||
config.LOG.info(
|
||||
config.LOG.warn(
|
||||
"Skipped loading feedback cog - channel ID not"
|
||||
" provided"
|
||||
)
|
||||
continue
|
||||
# Load the OPTIONAL bug cog
|
||||
if ext[:-3] == "bug" and config.BUG_CHANNEL_ID == None:
|
||||
config.LOG.info(
|
||||
config.LOG.warn(
|
||||
"Skipped loading bug cog - channel ID not provided"
|
||||
)
|
||||
continue
|
||||
# Load the OPTIONAL lyrics cog
|
||||
if ext[:-3] == "lyrics" and config.GENIUS_CLIENT_ID == None:
|
||||
config.LOG.info(
|
||||
config.LOG.warn(
|
||||
"Skipped loading lyrics cog - Genius API credentials"
|
||||
" not provided"
|
||||
)
|
||||
continue
|
||||
# Load the OPTIONAL autoplay cog
|
||||
if ext[:-3] == "autoplay" and config.OPENAI_API_KEY == None:
|
||||
config.LOG.info(
|
||||
"Skipped loading autoplay cog - OpenAI API credentials"
|
||||
if ext[:-3] == "autoplay" and config.AI_CLIENT == None:
|
||||
config.LOG.warn(
|
||||
"Skipped loading autoplay cog - AI API credentials"
|
||||
" not provided"
|
||||
)
|
||||
continue
|
||||
@ -83,6 +80,7 @@ bot = MyBot()
|
||||
bot.remove_command("help")
|
||||
bot.temp_command_count = {} # command_name: count
|
||||
bot.autoplay = [] # guild_id, guild_id, etc.
|
||||
bot.youtube_broken = False
|
||||
|
||||
|
||||
@tasks.loop(minutes=45)
|
||||
|
@ -75,9 +75,7 @@ class Autoplay(commands.Cog):
|
||||
)
|
||||
await interaction.response.send_message(embed=embed)
|
||||
|
||||
if await add_song_recommendations(
|
||||
self.bot.openai, self.bot.user, player, 5, inputs
|
||||
):
|
||||
if await add_song_recommendations(self.bot.user, player, 5, inputs):
|
||||
self.bot.autoplay.append(interaction.guild.id)
|
||||
embed = create_embed(
|
||||
title=":infinity: Autoplay Enabled :infinity:",
|
||||
|
@ -56,7 +56,8 @@ commands_and_descriptions = {
|
||||
},
|
||||
"autoplay": {
|
||||
"description": (
|
||||
"Keep the music playing forever with music suggestions from OpenAI"
|
||||
"Keep the music playing forever with automatic song"
|
||||
" recommendations"
|
||||
),
|
||||
"arguments": {
|
||||
"on": "Turn autoplay feature on",
|
||||
|
@ -245,7 +245,7 @@ class Music(commands.Cog):
|
||||
for song in event.player.queue[:10]:
|
||||
inputs[song.title] = song.author
|
||||
await add_song_recommendations(
|
||||
self.bot.openai, self.bot.user, event.player, 5, inputs
|
||||
self.bot.user, event.player, 5, inputs
|
||||
)
|
||||
|
||||
@lavalink.listener(lavalink.events.NodeConnectedEvent)
|
||||
|
29
code/cogs/owner/send.py
Normal file
29
code/cogs/owner/send.py
Normal file
@ -0,0 +1,29 @@
|
||||
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))
|
31
code/cogs/owner/toggle.py
Normal file
31
code/cogs/owner/toggle.py
Normal file
@ -0,0 +1,31 @@
|
||||
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,6 +45,22 @@ class Play(commands.Cog):
|
||||
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)
|
||||
if "music.apple.com" in query:
|
||||
results, embed = await parse_custom_source(
|
||||
|
@ -1,40 +1,52 @@
|
||||
from lavalink import LoadType
|
||||
import re
|
||||
|
||||
from utils.config import AI_CLIENT, AI_MODEL
|
||||
|
||||
|
||||
async def add_song_recommendations(
|
||||
openai_client, bot_user, player, number, inputs, retries: int = 1
|
||||
bot_user, player, number, inputs, retries: int = 1
|
||||
):
|
||||
input_list = [f'"{song} by {artist}"' for song, artist in inputs.items()]
|
||||
|
||||
completion = (
|
||||
openai_client.chat.completions.create(
|
||||
AI_CLIENT.chat.completions.create(
|
||||
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",
|
||||
"content": f"""
|
||||
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}
|
||||
{input_list}
|
||||
""",
|
||||
}
|
||||
},
|
||||
],
|
||||
model="gpt-4o-mini",
|
||||
model=AI_MODEL,
|
||||
)
|
||||
.choices[0]
|
||||
.message.content.strip()
|
||||
@ -47,7 +59,7 @@ async def add_song_recommendations(
|
||||
if completion == '["NOTHING FOUND"]':
|
||||
if retries <= 3:
|
||||
await add_song_recommendations(
|
||||
openai_client, bot_user, player, number, inputs, retries + 1
|
||||
bot_user, player, number, inputs, retries + 1
|
||||
)
|
||||
else:
|
||||
return False
|
||||
|
@ -8,7 +8,7 @@ import sys
|
||||
import discord
|
||||
import logging
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from groq import Groq
|
||||
from colorlog import ColoredFormatter
|
||||
|
||||
log_level = logging.DEBUG
|
||||
@ -39,7 +39,8 @@ SPOTIFY_CLIENT_ID = None
|
||||
SPOTIFY_CLIENT_SECRET = None
|
||||
GENIUS_CLIENT_ID = None
|
||||
GENIUS_CLIENT_SECRET = None
|
||||
OPENAI_API_KEY = None
|
||||
AI_CLIENT = None
|
||||
AI_MODEL = None
|
||||
LAVALINK_HOST = None
|
||||
LAVALINK_PORT = None
|
||||
LAVALINK_PASSWORD = None
|
||||
@ -82,12 +83,13 @@ schema = {
|
||||
},
|
||||
"required": ["genius_client_id", "genius_client_secret"],
|
||||
},
|
||||
"openai": {
|
||||
"ai": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"openai_api_key": {"type": "string"},
|
||||
"service": {"enum": ["openai", "groq"]},
|
||||
"api_key": {"type": "string"},
|
||||
},
|
||||
"required": ["openai_api_key"],
|
||||
"required": ["service"],
|
||||
},
|
||||
"lavalink": {
|
||||
"type": "object",
|
||||
@ -144,9 +146,9 @@ genius:
|
||||
genius_client_id:
|
||||
genius_client_secret:
|
||||
|
||||
openai:
|
||||
openai_api_key:
|
||||
"""
|
||||
ai:
|
||||
service:
|
||||
api_key: """
|
||||
)
|
||||
|
||||
sys.exit(
|
||||
@ -160,7 +162,7 @@ openai:
|
||||
|
||||
# Thouroughly validate all of the options in the config.yaml file
|
||||
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, OPENAI_API_KEY, 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, AI_CLIENT, AI_MODEL, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD
|
||||
config = yaml.safe_load(file_contents)
|
||||
|
||||
try:
|
||||
@ -270,17 +272,24 @@ def validate_config(file_contents):
|
||||
)
|
||||
|
||||
#
|
||||
# If the OPENAI section is present, make sure the API key is valid
|
||||
# If the AI section is present, make sure the API key is valid
|
||||
#
|
||||
|
||||
if "openai" in config:
|
||||
client = openai.OpenAI(api_key=config["openai"]["openai_api_key"])
|
||||
if "ai" in config:
|
||||
if config["ai"]["service"] == "openai":
|
||||
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:
|
||||
client.models.list()
|
||||
OPENAI_API_KEY = config["openai"]["openai_api_key"]
|
||||
AI_CLIENT = client
|
||||
AI_MODEL = model
|
||||
except openai.AuthenticationError:
|
||||
LOG.critical(
|
||||
"Error in config.yaml file: OpenAI API key is invalid"
|
||||
"Error in config.yaml file: OpenAI/Groq API key is invalid"
|
||||
)
|
||||
|
||||
# Set appropriate values for all non-optional variables
|
||||
|
@ -22,5 +22,6 @@ genius:
|
||||
genius_client_id:
|
||||
genius_client_secret:
|
||||
|
||||
openai:
|
||||
openai_api_key:
|
||||
ai:
|
||||
service:
|
||||
api_key:
|
@ -4,6 +4,7 @@ colorlog==6.8.2
|
||||
validators==0.28.3
|
||||
openai==1.56.0
|
||||
requests==2.32.3
|
||||
lyricsgenius==3.0.1
|
||||
lyricsgenius==3.2.0
|
||||
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