Discord add reconnecting functionality

Clear rpc on disconnect
Add menu button to reconnect
This commit is contained in:
Constantin Piber
2021-08-27 16:32:55 +02:00
parent 36bc9c62b0
commit b5fd6b4969
3 changed files with 168 additions and 78 deletions

View File

@ -1,71 +1,140 @@
const Discord = require("discord-rpc"); const Discord = require("discord-rpc");
const { dev } = require("electron-is")
const registerCallback = require("../../providers/song-info"); const registerCallback = require("../../providers/song-info");
const rpc = new Discord.Client({
transport: "ipc",
});
// Application ID registered by @semvis123 // Application ID registered by @semvis123
const clientId = "790655993809338398"; const clientId = "790655993809338398";
let clearActivity; /**
* @typedef {Object} Info
* @property {import('discord-rpc').Client} rpc
* @property {boolean} ready
* @property {import('../../providers/song-info').SongInfo} lastSongInfo
*/
/**
* @type {Info}
*/
const info = {
rpc: null,
ready: false,
lastSongInfo: null,
};
/**
* @type {(() => void)[]}
*/
const refreshCallbacks = [];
const resetInfo = () => {
info.rpc = null;
info.ready = false;
clearTimeout(clearActivity);
if (dev()) console.log("discord disconnected");
refreshCallbacks.forEach(cb => cb());
};
module.exports = (win, {activityTimoutEnabled, activityTimoutTime}) => { const connect = () => {
// If the page is ready, register the callback if (info.rpc) {
win.once("ready-to-show", () => { if (dev())
rpc.once("ready", () => { console.log('Attempted to connect with active RPC object');
// Register the callback return;
// }
// We get multiple events
// Next song: PAUSE(n), PAUSE(n+1), PLAY(n+1)
// Skip time: PAUSE(N), PLAY(N)
registerCallback((songInfo) => {
if (songInfo.title.length === 0 && songInfo.artist.length === 0) {
return;
}
// Song information changed, so lets update the rich presence
const activityInfo = {
details: songInfo.title,
state: songInfo.artist,
largeImageKey: "logo",
largeImageText: [
songInfo.uploadDate,
songInfo.views.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " views"
].join(' || '),
};
// stop the clear activity timout info.rpc = new Discord.Client({
clearTimeout(clearActivity); transport: "ipc",
});
info.ready = false;
// clear directly if timeout is 0 info.rpc.once("connected", () => {
if (songInfo.isPaused && activityTimoutEnabled && activityTimoutTime === 0) { if (dev()) console.log("discord connected");
rpc.clearActivity().catch(console.error); refreshCallbacks.forEach(cb => cb());
return; });
} info.rpc.once("ready", () => {
info.ready = true;
if (info.lastSongInfo) updateActivity(info.lastSongInfo)
});
info.rpc.once("disconnected", resetInfo);
if (songInfo.isPaused) { // Startup the rpc client
// Add an idle icon to show that the song is paused info.rpc.login({ clientId }).catch(err => {
activityInfo.smallImageKey = "idle"; resetInfo();
activityInfo.smallImageText = "idle/paused"; if (dev()) console.error(err);
// Set start the timer so the activity gets cleared after a while if enabled
if (activityTimoutEnabled)
clearActivity = setTimeout(() => rpc.clearActivity().catch(console.error), activityTimoutTime || 10000);
} else {
// Add the start and end time of the song
const songStartTime = Date.now() - songInfo.elapsedSeconds * 1000;
activityInfo.startTimestamp = songStartTime;
activityInfo.endTimestamp =
songStartTime + songInfo.songDuration * 1000;
}
rpc.setActivity(activityInfo).catch(console.error);
});
});
// Startup the rpc client
rpc.login({ clientId }).catch(console.error);
}); });
}; };
module.exports.clear = () => rpc.clearActivity(); let clearActivity;
/**
* @type {import('../../providers/song-info').songInfoCallback}
*/
let updateActivity;
module.exports = (win, {activityTimoutEnabled, activityTimoutTime}) => {
// We get multiple events
// Next song: PAUSE(n), PAUSE(n+1), PLAY(n+1)
// Skip time: PAUSE(N), PLAY(N)
updateActivity = songInfo => {
if (songInfo.title.length === 0 && songInfo.artist.length === 0) {
return;
}
info.lastSongInfo = songInfo;
// stop the clear activity timout
clearTimeout(clearActivity);
// stop early if discord connection is not ready
// do this after clearTimeout to avoid unexpected clears
if (!info.rpc || !info.ready) {
return;
}
// clear directly if timeout is 0
if (songInfo.isPaused && activityTimoutEnabled && activityTimoutTime === 0) {
info.rpc.clearActivity().catch(console.error);
return;
}
// Song information changed, so lets update the rich presence
const activityInfo = {
details: songInfo.title,
state: songInfo.artist,
largeImageKey: "logo",
largeImageText: [
songInfo.uploadDate,
songInfo.views.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + " views"
].join(' || '),
};
if (songInfo.isPaused) {
// Add an idle icon to show that the song is paused
activityInfo.smallImageKey = "idle";
activityInfo.smallImageText = "idle/paused";
// Set start the timer so the activity gets cleared after a while if enabled
if (activityTimoutEnabled)
clearActivity = setTimeout(() => info.rpc.clearActivity().catch(console.error), activityTimoutTime || 10000);
} else {
// Add the start and end time of the song
const songStartTime = Date.now() - songInfo.elapsedSeconds * 1000;
activityInfo.startTimestamp = songStartTime;
activityInfo.endTimestamp =
songStartTime + songInfo.songDuration * 1000;
}
info.rpc.setActivity(activityInfo).catch(console.error);
};
// If the page is ready, register the callback
win.once("ready-to-show", () => {
registerCallback(updateActivity);
connect();
});
};
module.exports.clear = () => {
if (info.rpc) info.rpc.clearActivity();
clearTimeout(clearActivity);
};
module.exports.connect = connect;
module.exports.registerRefresh = (cb) => refreshCallbacks.push(cb);
/**
* @type {Info}
*/
module.exports.info = Object.defineProperties({}, Object.keys(info).reduce((o, k) => ({ ...o, [k]: { enumerable: true, get: () => info[k] } }), {}));

View File

@ -1,28 +1,38 @@
const { setOptions } = require("../../config/plugins"); const { setOptions } = require("../../config/plugins");
const { edit } = require("../../config"); const { edit } = require("../../config");
const { clear } = require("./back"); const { clear, info, connect, registerRefresh } = require("./back");
module.exports = (win, options) => [ let hasRegisterred = false;
{
label: "Clear activity", module.exports = (win, options, refreshMenu) => {
click: () => { if (!hasRegisterred) {
clear(); registerRefresh(refreshMenu);
hasRegisterred = true;
}
return [
{
label: info.rpc !== null ? "Connected" : "Reconnect",
enabled: info.rpc === null,
click: connect,
}, },
}, {
{ label: "Clear activity",
label: "Clear activity after timeout", click: clear,
type: "checkbox",
checked: options.activityTimoutEnabled,
click: (item) => {
options.activityTimoutEnabled = item.checked;
setOptions('discord', options);
}, },
}, {
{ label: "Clear activity after timeout",
label: "Set timeout time in config", type: "checkbox",
click: () => { checked: options.activityTimoutEnabled,
click: (item) => {
options.activityTimoutEnabled = item.checked;
setOptions('discord', options);
},
},
{
label: "Set timeout time in config",
// open config.json // open config.json
edit(); click: edit,
}, },
}, ];
]; };

View File

@ -40,6 +40,9 @@ const getArtist = async (win) => {
} }
// Fill songInfo with empty values // Fill songInfo with empty values
/**
* @typedef {songInfo} SongInfo
*/
const songInfo = { const songInfo = {
title: "", title: "",
artist: "", artist: "",
@ -71,6 +74,14 @@ const handleData = async (responseText, win) => {
const callbacks = []; const callbacks = [];
// This function will allow plugins to register callback that will be triggered when data changes // This function will allow plugins to register callback that will be triggered when data changes
/**
* @callback songInfoCallback
* @param {songInfo} songInfo
* @returns {void}
*/
/**
* @param {songInfoCallback} callback
*/
const registerCallback = (callback) => { const registerCallback = (callback) => {
callbacks.push(callback); callbacks.push(callback);
}; };