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