Add spotify playlist tracking

This commit is contained in:
baz 2025-08-18 00:50:40 +01:00
parent a7e35351d3
commit 9130494b00
6 changed files with 262 additions and 3 deletions

View File

@ -4,4 +4,9 @@ discordToken=
igdbClientId=
igdbClientSecret=
igdbAccessToken=
googleplacesapikey=
googleplacesapikey=
spotifyClientId=
spotifyClientSecret=
spotifyAccessToken=
spotifyPlaylistTracking=
spotifyPlaylistChannel=

4
.gitignore vendored
View File

@ -4,4 +4,6 @@ config.json
*.sqlite
backups
*.idea
tempbeattimeline.png
tempbeattimeline.png
playlistinfo.json
playlistContent.json

View File

@ -0,0 +1,127 @@
const { EmbedBuilder } = require('@discordjs/builders');
const fs = require('fs');
async function getAllPlaylistTracks() {
let allTracks = [];
let offset = 0;
const limit = 100;
let total = 0;
do {
await fetch(
`https://api.spotify.com/v1/playlists/${process.env.spotifyPlaylistTracking}/tracks?limit=${limit}&offset=${offset}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.spotifyAccessToken}`,
},
},
)
.then(response => response.json())
.then(response => {
allTracks = allTracks.concat(response.items);
total = response.total;
offset += limit;
})
.catch(err => {
console.log(err);
});
} while (allTracks.length < total);
return allTracks;
}
let seenTrackIds = new Set();
async function PostNewPlaylistUpdates(client) {
if (!process.env.spotifyPlaylistTracking || !process.env.spotifyPlaylistChannel || !process.env.spotifyAccessToken) {
return;
}
if (fs.existsSync('./playlistContent.json')) {
seenTrackIds = new Set(JSON.parse(fs.readFileSync('./playlistContent.json', 'utf-8')));
}
else {
seenTrackIds = new Set();
}
const tracks = await getAllPlaylistTracks();
if (tracks.length <= 1) return;
const currentIds = tracks.map(item => item.track.id);
let newTracks;
if (seenTrackIds.size > 0)
{
newTracks = currentIds.filter(id => !seenTrackIds.has(id));
} else {
newTracks = currentIds;
}
const channel = await client.channels.cache.get(process.env.spotifyPlaylistChannel);
for (const trackID of newTracks) {
// Send discord embeds;
const track = tracks.find(item => item.track.id === trackID);
console.log(track);
const embed = new EmbedBuilder()
.setColor(0x1db954)
.setTitle(`${track.track.name} added!`)
.setURL(track.track.external_urls.spotify)
.setFooter({ text: 'The Ochulus • 100 Games Challenge', iconURL: client.user.avatarURL() })
.setTimestamp();
if (track.added_by) {
await fetch(
`https://api.spotify.com/v1/users/${track.added_by.id}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.spotifyAccessToken}`,
},
},
)
.then(response => response.json())
.then(response => {
if (response.images.length > 0) {
embed.setAuthor({ name: `${response.display_name} added a new song!`, iconURL: response.images[0].url });
}
else {
embed.setAuthor({ name: `${response.display_name} added a new song!` });
}
})
.catch(err => {
console.log(err);
});
}
embed.addFields({ name: 'Title', value: `[${track.track.name}](${track.track.external_urls.spotify})`, inline: true });
const artists = track.track.artists.map(artist => `[${artist.name}](${artist.external_urls.spotify})`).join(', ');
embed.addFields({ name: 'Artists', value: artists, inline: true });
embed.addFields({ name: 'Album', value: `[${track.track.album.name}](${track.track.album.external_urls.spotify})`, inline: true });
if (track.track.album.images.length > 0)
{
embed.setThumbnail(track.track.album.images[0].url);
}
await channel.send({ embeds: [embed] });
}
seenTrackIds = new Set(currentIds);
fs.writeFileSync('./playlistContent.json', JSON.stringify([...seenTrackIds]));
}
module.exports = {
getAllPlaylistTracks,
PostNewPlaylistUpdates,
};

View File

@ -0,0 +1,69 @@
const { SlashCommandBuilder, EmbedBuilder } = require('discord.js');
const axios = require('axios');
const fs = require('fs');
const { getAllPlaylistTracks } = require('./postnewplaylistupdates.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('trackspotifyplaylist')
.setDescription('Tracks for changes in a spotify playlist and posts updates in the channel this message is sent')
.addStringOption(option => option.setName('playlisturl').setDescription('A link to the playlist to track.').setRequired(true)),
async execute(interaction) {
await interaction.deferReply();
const playlistURL = interaction.options.getString('playlisturl');
const lastIndexOf = playlistURL.lastIndexOf('/');
const playlistID = playlistURL.substr(lastIndexOf + 1);
const embed = new EmbedBuilder()
.setColor(0xFFD700)
.setFooter({ text: 'The Ochulus • 100 Games Challenge', iconURL: interaction.client.user.avatarURL() })
.setTimestamp();
await fetch(
`https://api.spotify.com/v1/playlists/${playlistID}`,
{
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.spotifyAccessToken}`,
},
},
)
.then(response => response.json())
.then(response => {
embed.setColor(0x1db954);
embed.setTitle(`Now tracking ${response.name}`);
embed.setURL(response.external_urls.spotify);
if (response.images) {
embed.setThumbnail(`${response.images[0].url}`);
}
embed.setDescription(`There are currently ${response.tracks.total} tracks in the playlist.`);
console.log(response);
process.env.spotifyPlaylistTracking = response.id;
process.env.spotifyPlaylistChannel = interaction.channelId;
// Save to file
const list = [];
list.push(process.env.spotifyPlaylistTracking);
list.push(process.env.spotifyPlaylistChannel);
fs.writeFileSync('./playlistinfo.json', JSON.stringify(list));
})
.catch(err => {
embed.setColor(0xFF0000);
embed.setTitle('Unable to track playlist');
console.error(err);
});
if (process.env.spotifyPlaylistTracking) {
const allTracks = await getAllPlaylistTracks();
const ids = allTracks.map(item => item.track.id);
fs.writeFileSync('./playlistContent.json', JSON.stringify([...ids]));
}
await interaction.editReply({ embeds: [embed] });
},
};

View File

@ -10,6 +10,10 @@ const { igdb } = require('./igdb.js');
const { backupDatabase } = require('./databaseHelperFunctions.js');
new igdb();
const { Spotify } = require('./spotify.js');
new Spotify();
const { PostNewPlaylistUpdates } = require('./commands/fun/postnewplaylistupdates.js');
// Create a new client instance
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
@ -68,4 +72,17 @@ setInterval(() => {
backupDatabase();
}, 86000000);
backupDatabase();
backupDatabase();
if (fs.existsSync('./playlistinfo.json')) {
const info = JSON.parse(fs.readFileSync('./playlistinfo.json'));
process.env.spotifyPlaylistTracking = info[0];
process.env.spotifyPlaylistChannel = info[1];
}
setInterval(() => {
PostNewPlaylistUpdates(client);
}, 60000);
PostNewPlaylistUpdates(client);

39
spotify.js Normal file
View File

@ -0,0 +1,39 @@
class Spotify {
constructor() {
// make a new token every hour
setInterval(() => {
this.makeClientCred();
}, 3600000);
this.makeClientCred();
}
async makeClientCred() {
console.log('Making a spotify token');
const response = await fetch('https://accounts.spotify.com/api/token',
{
method: 'POST',
headers: {
'Authorization': 'Basic ' + (new Buffer.from(process.env.spotifyClientId + ':' + process.env.spotifyClientSecret).toString('base64')),
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
grant_type: 'client_credentials',
}),
},
);
const data = await response.json();
if (response.status != 200) {
console.log('Failed with ', data.status, data.body);
return;
}
process.env.spotifyAccessToken = data.access_token;
}
}
module.exports = {
Spotify,
};