61 lines
2.3 KiB
JavaScript
61 lines
2.3 KiB
JavaScript
import { randomUUID } from 'node:crypto';
|
|
import WebSocket from 'ws';
|
|
import { searchSpotify, playlistTool } from './spotify.mjs';
|
|
|
|
const spotifyUri = /^spotify:(track|artist|album|playlist|episode):[A-Za-z0-9]{22}$/;
|
|
|
|
export function deviceTools(client, signal) {
|
|
const pending = new Map();
|
|
signal.addEventListener('abort', () => {
|
|
for (const { reject, timeout } of pending.values()) {
|
|
clearTimeout(timeout);
|
|
reject(new Error('device_disconnected'));
|
|
}
|
|
pending.clear();
|
|
}, { once: true });
|
|
|
|
function request(name, args) {
|
|
if (signal.aborted || client.readyState !== WebSocket.OPEN) throw new Error('device_disconnected');
|
|
const id = randomUUID();
|
|
return new Promise((resolve, reject) => {
|
|
const timeout = setTimeout(() => {
|
|
pending.delete(id);
|
|
reject(new Error('device_tool_timeout'));
|
|
}, 17000);
|
|
pending.set(id, { resolve, reject, timeout });
|
|
client.send(JSON.stringify({ type: 'device_tool', id, name, arguments: args }));
|
|
});
|
|
}
|
|
|
|
return {
|
|
accept(message) {
|
|
if (message?.type !== 'device_tool_result') return false;
|
|
const call = pending.get(message.id);
|
|
if (!call) return true;
|
|
clearTimeout(call.timeout);
|
|
pending.delete(message.id);
|
|
call.resolve(message.result ?? { error: 'empty_device_result' });
|
|
return true;
|
|
},
|
|
async execute(name, args) {
|
|
if (name === 'spotify_playlist') return playlistTool(args, signal);
|
|
const enqueue = name === 'spotify_control' && args.action === 'enqueue';
|
|
if (name !== 'spotify_play' && !enqueue) return request(name, args);
|
|
let selected;
|
|
if (args.uri) {
|
|
if (!spotifyUri.test(args.uri)) return { error: 'invalid_spotify_uri' };
|
|
selected = { uri: args.uri };
|
|
} else {
|
|
try {
|
|
selected = (await searchSpotify(args.query, signal, enqueue ? 'track' : args.type))[0];
|
|
} catch {
|
|
return { error: 'spotify_search_unavailable', hint: 'Подключи Spotify в пульте Telegram и повтори запрос.' };
|
|
}
|
|
}
|
|
if (!selected) return { error: 'spotify_not_found' };
|
|
const result = await request(name, enqueue ? { action: 'enqueue', uri: selected.uri } : { uri: selected.uri });
|
|
return { ...result, selection: selected };
|
|
},
|
|
};
|
|
}
|