nowplaying: half-assed lastfm support #4
2 arquivos alterados com 76 adições e 25 exclusões
|
|
@ -4,52 +4,98 @@ import {
|
||||||
ApplicationIntegrationType,
|
ApplicationIntegrationType,
|
||||||
ButtonBuilder,
|
ButtonBuilder,
|
||||||
ButtonStyle,
|
ButtonStyle,
|
||||||
ChatInputCommandInteraction, ContainerBuilder,
|
ChatInputCommandInteraction,
|
||||||
EmbedBuilder,
|
EmbedBuilder,
|
||||||
MessageFlags,
|
MessageFlags,
|
||||||
InteractionContextType, type MessageActionRowComponentBuilder, MessageFlagsBitField,
|
InteractionContextType, type MessageActionRowComponentBuilder,
|
||||||
SlashCommandBuilder, TextDisplayBuilder, SectionBuilder, ThumbnailBuilder, type ButtonInteraction
|
SlashCommandBuilder
|
||||||
} from "discord.js";
|
} from "discord.js";
|
||||||
|
|
||||||
import { getSongOnPreferredProvider, kyzaify, lobotomizedSongButton, musicCache, nowPlayingView, type Song } from "../music.ts"
|
import { getSongOnPreferredProvider, lobotomizedSongButton, musicCache, nowPlayingView } from "../music.ts"
|
||||||
import { type Config } from "../config.ts";
|
import { type Config } from "../config.ts";
|
||||||
import { hash } from "crypto"
|
import { hash } from "crypto"
|
||||||
|
|
||||||
|
async function getNowPlaying(username: string, lastFMApiKey?: string, lastFMFetchLink?: boolean): Promise<{
|
||||||
|
songName: string, artistName: string, link: string
|
||||||
|
} | false | undefined> {
|
||||||
|
if (!lastFMApiKey) {
|
||||||
|
const res = await fetch(`https://api.listenbrainz.org/1/user/${username}/playing-now`).then((res) => res.json());
|
||||||
|
if (!res?.payload) return
|
||||||
|
else if (res.payload.count === 0) return false
|
||||||
|
else {
|
||||||
|
const trackMetadata = res.payload.listens[0].track_metadata
|
||||||
|
return {
|
||||||
|
songName: trackMetadata.artist_name,
|
||||||
|
artistName: trackMetadata.track_name,
|
||||||
|
link: trackMetadata.additional_info.origin_url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const res = await fetch(`https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=${username}&api_key=${lastFMApiKey}&limit=1&format=json`)
|
||||||
|
.then((res) => res.json());
|
||||||
|
if (!res?.recenttracks) return
|
||||||
|
else if (!res.recenttracks?.track?.[0]) return false
|
||||||
|
else {
|
||||||
|
const track = res.recenttracks.track[0]
|
||||||
|
// yes its a string, horror
|
||||||
|
if (track["@attr"]?.nowplaying !== "true") return false
|
||||||
|
// it also doesnt provide a streaming platform url, im sorry i have to do this
|
||||||
|
let link = ""
|
||||||
|
if (lastFMFetchLink) {
|
||||||
|
const page = await (await fetch(track.url)).text()
|
||||||
|
const match = page.match(/class="header-new-playlink"\s+href="(.+?)"/m)
|
||||||
|
if (match) link = match[1]
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
songName: track.name,
|
||||||
|
artistName: track.artist["#text"],
|
||||||
|
link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default class PingCommand extends Command {
|
export default class PingCommand extends Command {
|
||||||
async run(interaction: ChatInputCommandInteraction, config: Config): Promise<void> {
|
async run(interaction: ChatInputCommandInteraction, config: Config): Promise<void> {
|
||||||
await interaction.deferReply()
|
await interaction.deferReply()
|
||||||
const user = interaction.options.getString("user") ?? config.listenbrainzAccount;
|
const user = interaction.options.getString("user") ?? config.listenbrainzAccount;
|
||||||
const lobotomized = interaction.options.getBoolean("lobotomized") ?? config.commandDefaults.nowplaying.lobotomized;
|
const lobotomized = interaction.options.getBoolean("lobotomized") ?? config.commandDefaults.nowplaying.lobotomized;
|
||||||
const usesonglink = interaction.options.getBoolean("usesonglink") ?? config.commandDefaults.nowplaying.useSonglink
|
const useLastFM = interaction.options.getBoolean("uselastfm") ?? config.commandDefaults.nowplaying.useLastFM
|
||||||
const useitunes = interaction.options.getBoolean("useitunes") ?? config.commandDefaults.nowplaying.useItunes
|
let useSonglink = interaction.options.getBoolean("usesonglink") ?? config.commandDefaults.nowplaying.useSonglink
|
||||||
const meow = await fetch(`https://api.listenbrainz.org/1/user/${user}/playing-now`).then((res) => res.json());
|
const useiTunes = interaction.options.getBoolean("useitunes") ?? config.commandDefaults.nowplaying.useItunes
|
||||||
if (!meow) {
|
|
||||||
|
const nowPlaying = await getNowPlaying(user, useLastFM ? config.lastFMApiKey : undefined, !useiTunes)
|
||||||
|
if (typeof nowPlaying === "undefined") {
|
||||||
|
await interaction.followUp("something shat itself!");
|
||||||
|
return;
|
||||||
|
} else if (!nowPlaying) {
|
||||||
|
await interaction.followUp(user + " isn't listening to music");
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
const paramsObj = { entity: "song", term: `${nowPlaying.artistName} ${nowPlaying.songName}` };
|
||||||
|
const searchParams = new URLSearchParams(paramsObj);
|
||||||
|
let { link } = nowPlaying
|
||||||
|
if (useiTunes) {
|
||||||
|
const itunesinfo = (await (await fetch(`https://itunes.apple.com/search?${searchParams.toString()}`)).json()).results[0];
|
||||||
|
link = itunesinfo?.trackViewUrl
|
||||||
|
if (!link) {
|
||||||
await interaction.followUp("something shat itself!");
|
await interaction.followUp("something shat itself!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (meow.payload.count === 0) {
|
|
||||||
await interaction.followUp(user + " isnt listening to music");
|
|
||||||
} else {
|
|
||||||
const track_metadata = meow.payload.listens[0].track_metadata
|
|
||||||
const paramsObj = { entity: "song", term: track_metadata.artist_name + " " + track_metadata.track_name };
|
|
||||||
const searchParams = new URLSearchParams(paramsObj);
|
|
||||||
let link = track_metadata.additional_info.origin_url
|
|
||||||
if (useitunes) {
|
|
||||||
const itunesinfo = (await (await fetch(`https://itunes.apple.com/search?${searchParams.toString()}`)).json()).results[0];
|
|
||||||
link = itunesinfo.trackViewUrl
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!link) useSonglink = false
|
||||||
let preferredApi, songlink, isCached = false
|
let preferredApi, songlink, isCached = false
|
||||||
if (musicCache[link]) {
|
if (link && musicCache[link]) {
|
||||||
preferredApi = musicCache[link].preferredApi
|
preferredApi = musicCache[link].preferredApi
|
||||||
songlink = musicCache[link].songlink
|
songlink = musicCache[link].songlink
|
||||||
isCached = true
|
isCached = true
|
||||||
} else {
|
} else if (useSonglink) {
|
||||||
songlink = await fetch(`https://api.song.link/v1-alpha.1/links?url=${link}`).then(a => a.json())
|
songlink = await fetch(`https://api.song.link/v1-alpha.1/links?url=${link}`).then(a => a.json())
|
||||||
preferredApi = getSongOnPreferredProvider(songlink, link)
|
preferredApi = getSongOnPreferredProvider(songlink, link)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (preferredApi && usesonglink) {
|
if (preferredApi && useSonglink) {
|
||||||
if (!isCached) musicCache[link] ??= {
|
if (!isCached) musicCache[link] ??= {
|
||||||
preferredApi,
|
preferredApi,
|
||||||
songlink
|
songlink
|
||||||
|
|
@ -72,7 +118,7 @@ export default class PingCommand extends Command {
|
||||||
];
|
];
|
||||||
await interaction.followUp({
|
await interaction.followUp({
|
||||||
content: `### ${preferredApi.title.replace(/([#*_~`|])/g, "\\$1")} ${emoji}\n-# by ${preferredApi.artist}`,
|
content: `### ${preferredApi.title.replace(/([#*_~`|])/g, "\\$1")} ${emoji}\n-# by ${preferredApi.artist}`,
|
||||||
components: components,
|
components,
|
||||||
})
|
})
|
||||||
// we dont have infinite emoji slots
|
// we dont have infinite emoji slots
|
||||||
await emoji.delete()
|
await emoji.delete()
|
||||||
|
|
@ -86,9 +132,9 @@ export default class PingCommand extends Command {
|
||||||
} else {
|
} else {
|
||||||
const embedfallback = new EmbedBuilder()
|
const embedfallback = new EmbedBuilder()
|
||||||
.setAuthor({
|
.setAuthor({
|
||||||
name: meow.payload.listens[0].track_metadata.artist_name
|
name: nowPlaying.artistName
|
||||||
})
|
})
|
||||||
.setTitle(meow.payload.listens[0].track_metadata.track_name)
|
.setTitle(nowPlaying.songName)
|
||||||
.setFooter({
|
.setFooter({
|
||||||
text: "song.link proxying was turned off or failed - amy jr",
|
text: "song.link proxying was turned off or failed - amy jr",
|
||||||
});
|
});
|
||||||
|
|
@ -112,11 +158,14 @@ export default class PingCommand extends Command {
|
||||||
.addBooleanOption(option => {
|
.addBooleanOption(option => {
|
||||||
return option.setName("usesonglink").setDescription("use songlink or not").setRequired(false)
|
return option.setName("usesonglink").setDescription("use songlink or not").setRequired(false)
|
||||||
})
|
})
|
||||||
|
.addBooleanOption(option => {
|
||||||
|
return option.setName("uselastfm").setDescription("use last.fm or not").setRequired(false)
|
||||||
|
})
|
||||||
.addBooleanOption(option => {
|
.addBooleanOption(option => {
|
||||||
return option.setName("useitunes").setDescription("use itunes or not").setRequired(false)
|
return option.setName("useitunes").setDescription("use itunes or not").setRequired(false)
|
||||||
})
|
})
|
||||||
.addStringOption(option => {
|
.addStringOption(option => {
|
||||||
return option.setName("user").setDescription("listenbrainz username").setRequired(false)
|
return option.setName("user").setDescription("username").setRequired(false)
|
||||||
})
|
})
|
||||||
.setContexts([
|
.setContexts([
|
||||||
InteractionContextType.BotDM,
|
InteractionContextType.BotDM,
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import { PrismaClient } from "./generated/prisma/index.js";
|
||||||
const configT = z.object({
|
const configT = z.object({
|
||||||
token: z.string(),
|
token: z.string(),
|
||||||
listenbrainzAccount: z.string(),
|
listenbrainzAccount: z.string(),
|
||||||
|
lastFMApiKey: z.string(),
|
||||||
gitapi: z.string(),
|
gitapi: z.string(),
|
||||||
sharkeyInstance: z.string(),
|
sharkeyInstance: z.string(),
|
||||||
radioURL: z.string(),
|
radioURL: z.string(),
|
||||||
|
|
@ -13,7 +14,8 @@ const configT = z.object({
|
||||||
nowplaying: z.object({
|
nowplaying: z.object({
|
||||||
lobotomized: z.boolean(),
|
lobotomized: z.boolean(),
|
||||||
useSonglink: z.boolean(),
|
useSonglink: z.boolean(),
|
||||||
useItunes: z.boolean()
|
useItunes: z.boolean(),
|
||||||
|
useLastFM: z.boolean()
|
||||||
}),
|
}),
|
||||||
pat: z.object({
|
pat: z.object({
|
||||||
speed: z.number(),
|
speed: z.number(),
|
||||||
|
|
|
||||||
Carregando…
Adicionar tabela
Adicionar um link
Referência em uma nova issue