diff --git a/README.md b/README.md index a1b8da6..3eaa371 100644 --- a/README.md +++ b/README.md @@ -72,24 +72,11 @@ FEEDBACK_CHANNEL_ID | `CHANNEL ID`: Discord channel for feedback messages to be BUG_CHANNEL_ID | `CHANNEL ID`: Discord channel for bug messages to be sent to | **OPTIONAL** SPOTIFY_CLIENT_ID | Client ID from Spotify Developer account | **REQUIRED** SPOTIFY_CLIENT_SECRET | Client Secret from Spotify Developer account | **REQUIRED** -APPLE_MUSIC_KEY | See the `note` below to get a media api key without a developer account | **REQUIRED** OPENAI_API_KEY | API Key from OpenAI for autoplay recommendations | **REQUIRED** HOST | Host address for your Lavalink node | **REQUIRED** PORT | Port for your Lavalink node | **REQUIRED** PASSWORD | Password to authenticate into the Lavalink node | **REQUIRED** -
-
-NOTE: Media API Key - -1. Go to https://music.apple.com -2. Open the debuger tab in dev tools -3. Regex this `"(?(ey[\w-]+)\.([\w-]+)\.([\w-]+))"` -4. Copy the entire token from the JS file - -
-
- # Lavalink Information As previously state, a Lavalink node running at least `v4` with the LavaSrc plugin is required. Due to the plugin requirement, it is unlikely that you will be able to use a free/public Lavalink node. diff --git a/code/bot.py b/code/bot.py index aafeb0d..2864e85 100644 --- a/code/bot.py +++ b/code/bot.py @@ -6,6 +6,7 @@ import openai import utils.config as config from utils.command_tree import Tree +from utils.media_api_key import get_media_api_key class MyBot(commands.Bot): @@ -19,6 +20,7 @@ class MyBot(commands.Bot): async def setup_hook(self): get_access_token.start() + refresh_media_api_key.start() config.LOG.info("Loading cogs...") for ext in os.listdir("./code/cogs"): if ext.endswith(".py"): @@ -59,6 +61,18 @@ async def get_access_token(): bot.spotify_headers = {"Authorization": f"Bearer {access_token}"} +@tasks.loop(hours=24) +async def refresh_media_api_key(): + media_api_key = get_media_api_key() + if media_api_key is not None: + bot.apple_headers = { + "Authorization": f"Bearer {media_api_key}", + "Origin": "https://apple.com", + } + else: + bot.apple_headers = None + + if __name__ == "__main__": config.load_config() bot.run(config.TOKEN) diff --git a/code/cogs/play.py b/code/cogs/play.py index 6ae7686..1c04793 100644 --- a/code/cogs/play.py +++ b/code/cogs/play.py @@ -7,17 +7,12 @@ import re import requests from cogs.music import Music, LavalinkVoiceClient -from utils.config import BOT_COLOR, APPLE_MUSIC_KEY +from utils.config import BOT_COLOR from utils.custom_sources import SpotifySource, AppleSource url_rx = re.compile(r"https?://(?:www\.)?.+") -apple_headers = { - "Authorization": f"Bearer {APPLE_MUSIC_KEY}", - "Origin": "https://apple.com", -} - class Play(commands.Cog): def __init__(self, bot): @@ -45,18 +40,26 @@ class Play(commands.Cog): ### if "music.apple.com" in query: + if not self.bot.apple_headers: + embed = discord.Embed( + title="Apple Music Error", + description="Apple Music support seems to be broken at the moment. Please try again and fill out a bug report with if this continues to happen.", + color=BOT_COLOR, + ) + return await interaction.response.send_message(embed=embed, ephemeral=True) + embed = discord.Embed(color=BOT_COLOR) if "/playlist/" in query and "?i=" not in query: playlist_id = query.split("/playlist/")[1].split("/")[1] # Get all of the tracks in the playlist (limit at 250) playlist_url = f"https://api.music.apple.com/v1/catalog/us/playlists/{playlist_id}/tracks?limit=100" - response = requests.get(playlist_url, headers=apple_headers) + response = requests.get(playlist_url, headers=self.bot.apple_headers) if response.status_code == 200: playlist = response.json() # Get the general playlist info (name, artwork) playlist_info_url = f"https://api.music.apple.com/v1/catalog/us/playlists/{playlist_id}" - playlist_info = requests.get(playlist_info_url, headers=apple_headers) + playlist_info = requests.get(playlist_info_url, headers=self.bot.apple_headers) playlist_info = playlist_info.json() try: artwork_url = playlist_info["data"][0]["attributes"]["artwork"]["url"].replace( @@ -90,7 +93,7 @@ class Play(commands.Cog): if "/album/" in query and "?i=" not in query: album_id = query.split("/album/")[1].split("/")[1] album_url = f"https://api.music.apple.com/v1/catalog/us/albums/{album_id}" - response = requests.get(album_url, headers=apple_headers) + response = requests.get(album_url, headers=self.bot.apple_headers) if response.status_code == 200: album = response.json() @@ -117,7 +120,7 @@ class Play(commands.Cog): if "/album/" in query and "?i=" in query: song_id = query.split("/album/")[1].split("?i=")[1] song_url = f"https://api.music.apple.com/v1/catalog/us/songs/{song_id}" - response = requests.get(song_url, headers=apple_headers) + response = requests.get(song_url, headers=self.bot.apple_headers) if response.status_code == 200: song = response.json() diff --git a/code/utils/config.py b/code/utils/config.py index d411653..f319792 100644 --- a/code/utils/config.py +++ b/code/utils/config.py @@ -4,7 +4,6 @@ import os import validators import sys import discord -import openai import logging from colorlog import ColoredFormatter @@ -31,7 +30,6 @@ FEEDBACK_CHANNEL_ID = None BUG_CHANNEL_ID = None SPOTIFY_CLIENT_ID = None SPOTIFY_CLIENT_SECRET = None -APPLE_MUSIC_KEY = None OPENAI_API_KEY = None LAVALINK_HOST = None LAVALINK_PORT = None @@ -71,10 +69,6 @@ def load_config(): "SPOTIFY_CLIENT_SECRET": "", } - config["APPLE_MUSIC"] = { - "APPLE_MUSIC_KEY": "", - } - config["OPENAI"] = { "OPENAI_API_KEY": "", } @@ -101,7 +95,7 @@ Validate all of the options in the config.ini file. def validate_config(file_contents): - global TOKEN, BOT_COLOR, BOT_INVITE_LINK, FEEDBACK_CHANNEL_ID, BUG_CHANNEL_ID, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, APPLE_MUSIC_KEY, OPENAI_API_KEY, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD + global TOKEN, BOT_COLOR, BOT_INVITE_LINK, FEEDBACK_CHANNEL_ID, BUG_CHANNEL_ID, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, OPENAI_API_KEY, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD config = configparser.ConfigParser() config.read_string(file_contents) @@ -111,7 +105,7 @@ def validate_config(file_contents): errors = 0 # Make sure all sections are present - if ["BOT_INFO", "SPOTIFY", "APPLE_MUSIC", "OPENAI", "LAVALINK"] != config.sections(): + if ["BOT_INFO", "SPOTIFY", "OPENAI", "LAVALINK"] != config.sections(): sys.exit( LOG.critical( "Missing sections in config.ini file. Delete the file and re-run the bot to generate a blank config.ini file." @@ -132,13 +126,6 @@ def validate_config(file_contents): ) ) - if ["apple_music_key"] != config.options("APPLE_MUSIC"): - sys.exit( - LOG.critical( - "Missing options in APPLE_MUSIC section of config.ini file. Delete the file and re-run the bot to generate a blank config.ini file." - ) - ) - if ["openai_api_key"] != config.options("OPENAI"): sys.exit( LOG.critical( @@ -187,7 +174,6 @@ def validate_config(file_contents): TOKEN = config["BOT_INFO"]["TOKEN"] SPOTIFY_CLIENT_ID = config["SPOTIFY"]["SPOTIFY_CLIENT_ID"] SPOTIFY_CLIENT_SECRET = config["SPOTIFY"]["SPOTIFY_CLIENT_SECRET"] - APPLE_MUSIC_KEY = config["APPLE_MUSIC"]["APPLE_MUSIC_KEY"] OPENAI_API_KEY = config["OPENAI"]["OPENAI_API_KEY"] LAVALINK_HOST = config["LAVALINK"]["HOST"] LAVALINK_PORT = config["LAVALINK"]["PORT"] @@ -207,7 +193,7 @@ Validate all of the environment variables. def validate_env_vars(): - global TOKEN, BOT_COLOR, BOT_INVITE_LINK, FEEDBACK_CHANNEL_ID, BUG_CHANNEL_ID, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, APPLE_MUSIC_KEY, OPENAI_API_KEY, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD + global TOKEN, BOT_COLOR, BOT_INVITE_LINK, FEEDBACK_CHANNEL_ID, BUG_CHANNEL_ID, SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET, OPENAI_API_KEY, LAVALINK_HOST, LAVALINK_PORT, LAVALINK_PASSWORD hex_pattern_one = "^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$" hex_pattern_two = "^([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$" @@ -215,7 +201,7 @@ def validate_env_vars(): errors = 0 # Make sure all required variables are present in the environment - required_vars = ["TOKEN", "BOT_COLOR", "BOT_INVITE_LINK", "SPOTIFY_CLIENT_ID", "SPOTIFY_CLIENT_SECRET", "APPLE_MUSIC_KEY", "OPENAI_API_KEY", "LAVALINK_HOST", "LAVALINK_PORT", "LAVALINK_PASSWORD"] + required_vars = ["TOKEN", "BOT_COLOR", "BOT_INVITE_LINK", "SPOTIFY_CLIENT_ID", "SPOTIFY_CLIENT_SECRET", "OPENAI_API_KEY", "LAVALINK_HOST", "LAVALINK_PORT", "LAVALINK_PASSWORD"] for var in required_vars: if var not in os.environ: @@ -267,7 +253,6 @@ def validate_env_vars(): TOKEN = os.environ["TOKEN"] SPOTIFY_CLIENT_ID = os.environ["SPOTIFY_CLIENT_ID"] SPOTIFY_CLIENT_SECRET = os.environ["SPOTIFY_CLIENT_SECRET"] - APPLE_MUSIC_KEY = os.environ["APPLE_MUSIC_KEY"] OPENAI_API_KEY = os.environ["OPENAI_API_KEY"] LAVALINK_HOST = os.environ["LAVALINK_HOST"] LAVALINK_PORT = os.environ["LAVALINK_PORT"] diff --git a/code/utils/media_api_key.py b/code/utils/media_api_key.py new file mode 100644 index 0000000..b738302 --- /dev/null +++ b/code/utils/media_api_key.py @@ -0,0 +1,28 @@ +import requests +import re + +from utils.config import LOG + +""" +Search through the JS files on the Apple Music website to pull +a media API key +""" + + +def get_media_api_key(): + url = "https://music.apple.com" + response = requests.get(url) + + js_files = re.findall(r"assets/(.*?\.js)", response.text) + + # Look for `const Ga="TOKEN HERE"` + for js_file in js_files: + response = requests.get(f"{url}/assets/{js_file}") + match = re.search(r"const Ga=\"(.*?)\"", response.text) + if match: + return match.group(1) + + LOG.error( + "Failed to find media API key. Apple Music support will not work until a key is found or manually set." + ) + return None diff --git a/docker-compose.yaml b/docker-compose.yaml index 0446ada..69953c1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -10,7 +10,6 @@ services: - BUG_CHANNEL_ID= - SPOTIFY_CLIENT_ID= - SPOTIFY_CLIENT_SECRET= - - APPLE_MUSIC_KEY= - OPENAI_API_KEY= - LAVALINK_HOST= - LAVALINK_PORT=