143 lines
5.3 KiB
JavaScript
143 lines
5.3 KiB
JavaScript
/**
|
|
* @name AllCallTimeCounter
|
|
* @author Max
|
|
* @description Add call timer to all users in a server voice channel.
|
|
* @source https://github.com/Max-Herbold/AllCallTimersDiscordPlugin/blob/main/AllCallTimeCounter.plugin.js
|
|
* @updateUrl https://raw.githubusercontent.com/Max-Herbold/AllCallTimersDiscordPlugin/main/AllCallTimeCounter.plugin.js
|
|
* @authorLink https://github.com/Max-Herbold
|
|
* @version 1.0.2
|
|
*/
|
|
|
|
module.exports = (_ => {
|
|
class Timer extends window.BdApi.React.Component {
|
|
constructor(props) {
|
|
try {
|
|
super(props);
|
|
this.state = { time_delta: Date.now() - this.props.time };
|
|
} catch (e) { }
|
|
}
|
|
|
|
render() {
|
|
let time = new Date(Date.now() - this.props.time).toISOString().substr(11, 8);
|
|
return window.BdApi.React.createElement("div", {
|
|
className: "timeCounter",
|
|
children: time,
|
|
style: {
|
|
marginTop: -6,
|
|
fontWeight: "bold",
|
|
fontFamily: "monospace",
|
|
fontSize: 11,
|
|
position: "relative",
|
|
}
|
|
});
|
|
}
|
|
|
|
componentDidMount() {
|
|
this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000);
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
clearInterval(this.interval);
|
|
}
|
|
}
|
|
|
|
return class AllCallTimeCounter {
|
|
users = new Map(); // value format: [channelId, lastUpdatedTime]
|
|
|
|
load() { }
|
|
|
|
allUsers(guilds) {
|
|
// return an array of all users in all guilds
|
|
let users = [];
|
|
for (const guildId in guilds) {
|
|
const guild = guilds[guildId];
|
|
for (const userId in guild) {
|
|
users.push(userId);
|
|
}
|
|
}
|
|
return users;
|
|
}
|
|
|
|
updateInternal(userId, channelId) {
|
|
this.users.set(userId, [channelId, Date.now()]);
|
|
}
|
|
|
|
updateSingleUser(userId, channelId) {
|
|
// Used to keep track of currently rendered users in real time
|
|
if (this.users.has(userId) && this.users.get(userId)[0] !== channelId) {
|
|
// User moved to a different channel
|
|
this.updateInternal(userId, channelId);
|
|
} else if (!this.users.has(userId)) {
|
|
// User just joined a channel
|
|
this.updateInternal(userId, channelId);
|
|
}
|
|
}
|
|
|
|
runEverySecond() {
|
|
// Keeps track of users in the background at 1Hz
|
|
const states = this.VoiceStateStore.getAllVoiceStates();
|
|
|
|
const current_users = this.allUsers(states);
|
|
for (let userId in this.users.keys()) {
|
|
if (!current_users.includes(userId)) {
|
|
this.users.delete(userId);
|
|
}
|
|
}
|
|
|
|
// states is an array of {guildId: {userId: {channelId: channelId}}}
|
|
// iterate through all guilds and update the users, check if the user is in the same channel as before
|
|
// if userId is not in any guild it should be deleted from the users object
|
|
for (const guildId in states) {
|
|
let guild = states[guildId];
|
|
for (const userId in guild) {
|
|
const user = guild[userId];
|
|
const { channelId } = user;
|
|
if (channelId) {
|
|
if (this.users.has(userId)) {
|
|
// user is already in the users object
|
|
if (this.users.get(userId)[0] !== channelId) {
|
|
// user changed the channel
|
|
this.updateInternal(userId, channelId);
|
|
}
|
|
} else {
|
|
// user is not in the users object
|
|
this.updateInternal(userId, channelId);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
start() {
|
|
const searchProps = ["renderPrioritySpeaker", "renderIcons", "renderAvatar"];
|
|
const VoiceUser = window.BdApi.Webpack.getAllByPrototypeKeys(...searchProps)[0];
|
|
|
|
this.VoiceStateStore = window.BdApi.Webpack.getStore("VoiceStateStore");
|
|
|
|
window.BdApi.Patcher.after("AllCallTimeCounter", VoiceUser.prototype, "render", (e, _, returnValue) => this.processVoiceUser(e, _, returnValue));
|
|
|
|
// TODO: Hook this to user join/leave events
|
|
this.interval = setInterval(() => this.runEverySecond(), 1000);
|
|
}
|
|
|
|
stop() {
|
|
window.BdApi.Patcher.unpatchAll("AllCallTimeCounter");
|
|
clearInterval(this.interval);
|
|
}
|
|
|
|
createUserTimer(user, parent) {
|
|
const time = this.users.get(user.id)[1]
|
|
const tag = window.BdApi.React.createElement(Timer, { time: time });
|
|
|
|
parent[2].props.children.splice(1, 0, tag);
|
|
}
|
|
|
|
processVoiceUser(e, _, returnValue) {
|
|
const { user } = e.props;
|
|
this.updateSingleUser(user.id, e.props.channelId); // update user entry before trying to render
|
|
const parent = returnValue.props.children.props.children;
|
|
this.createUserTimer(user, parent);
|
|
}
|
|
};
|
|
})();
|