adds initial dotfiles
This commit is contained in:
@@ -0,0 +1,893 @@
|
||||
/**
|
||||
* @name VoiceActivity
|
||||
* @author Neodymium
|
||||
* @version 1.12.3
|
||||
* @description Shows icons and info in popouts, the member list, and more when someone is in a voice channel.
|
||||
* @source https://github.com/Neodymium7/BetterDiscordStuff/blob/main/VoiceActivity/VoiceActivity.plugin.js
|
||||
* @invite fRbsqH87Av
|
||||
*/
|
||||
|
||||
/*@cc_on
|
||||
@if (@_jscript)
|
||||
|
||||
// Offer to self-install for clueless users that try to run this directly.
|
||||
var shell = WScript.CreateObject("WScript.Shell");
|
||||
var fs = new ActiveXObject("Scripting.FileSystemObject");
|
||||
var pathPlugins = shell.ExpandEnvironmentStrings("%APPDATA%\BetterDiscord\plugins");
|
||||
var pathSelf = WScript.ScriptFullName;
|
||||
// Put the user at ease by addressing them in the first person
|
||||
shell.Popup("It looks like you've mistakenly tried to run me directly. \n(Don't do that!)", 0, "I'm a plugin for BetterDiscord", 0x30);
|
||||
if (fs.GetParentFolderName(pathSelf) === fs.GetAbsolutePathName(pathPlugins)) {
|
||||
shell.Popup("I'm in the correct folder already.", 0, "I'm already installed", 0x40);
|
||||
} else if (!fs.FolderExists(pathPlugins)) {
|
||||
shell.Popup("I can't find the BetterDiscord plugins folder.\nAre you sure it's even installed?", 0, "Can't install myself", 0x10);
|
||||
} else if (shell.Popup("Should I copy myself to BetterDiscord's plugins folder for you?", 0, "Do you need some help?", 0x34) === 6) {
|
||||
fs.CopyFile(pathSelf, fs.BuildPath(pathPlugins, fs.GetFileName(pathSelf)), true);
|
||||
// Show the user where to put plugins in the future
|
||||
shell.Exec("explorer " + pathPlugins);
|
||||
shell.Popup("I'm installed!", 0, "Successfully installed", 0x40);
|
||||
}
|
||||
WScript.Quit();
|
||||
|
||||
@else@*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const betterdiscord = new BdApi("VoiceActivity");
|
||||
const react = BdApi.React;
|
||||
|
||||
// styles
|
||||
let _styles = "";
|
||||
function _loadStyle(path, css) {
|
||||
_styles += "/*" + path + "*/\n" + css + "\n";
|
||||
}
|
||||
function styles() {
|
||||
return _styles;
|
||||
}
|
||||
|
||||
// @lib/settings.ts
|
||||
class SettingsManager {
|
||||
settings = betterdiscord.Data.load("settings");
|
||||
listeners = new Set();
|
||||
constructor(defaultSettings) {
|
||||
if (!this.settings) {
|
||||
betterdiscord.Data.save("settings", defaultSettings);
|
||||
this.settings = defaultSettings;
|
||||
return;
|
||||
}
|
||||
if (Object.keys(this.settings) !== Object.keys(defaultSettings)) {
|
||||
this.settings = { ...defaultSettings, ...this.settings };
|
||||
let changed = false;
|
||||
for (const key in this.settings) {
|
||||
if (!(key in defaultSettings)) {
|
||||
delete this.settings[key];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) betterdiscord.Data.save("settings", this.settings);
|
||||
}
|
||||
}
|
||||
addListener(listener) {
|
||||
this.listeners.add(listener);
|
||||
return () => {
|
||||
this.listeners.delete(listener);
|
||||
};
|
||||
}
|
||||
clearListeners() {
|
||||
this.listeners.clear();
|
||||
}
|
||||
useSettingsState(...keys) {
|
||||
let initialState = this.settings;
|
||||
if (keys.length) initialState = Object.fromEntries(keys.map((key) => [key, initialState[key]]));
|
||||
const [state, setState] = react.useState(initialState);
|
||||
react.useEffect(() => {
|
||||
return this.addListener((key, value) => {
|
||||
if (!keys.length || keys.includes(key)) setState((state2) => ({ ...state2, [key]: value }));
|
||||
});
|
||||
}, []);
|
||||
return state;
|
||||
}
|
||||
get(key) {
|
||||
return this.settings[key];
|
||||
}
|
||||
set(key, value) {
|
||||
this.settings[key] = value;
|
||||
betterdiscord.Data.save("settings", this.settings);
|
||||
for (const listener of this.listeners) listener(key, value);
|
||||
}
|
||||
}
|
||||
function buildSettingsPanel(settingsManager, settings) {
|
||||
for (const setting of settings) {
|
||||
setting.value = settingsManager.get(setting.id);
|
||||
}
|
||||
return betterdiscord.UI.buildSettingsPanel({
|
||||
settings,
|
||||
onChange: (_, id, value) => settingsManager.set(id, value)
|
||||
});
|
||||
}
|
||||
|
||||
// @lib/strings.ts
|
||||
const LocaleStore = betterdiscord.Webpack.Stores.LocaleStore;
|
||||
class StringsManager {
|
||||
locales;
|
||||
defaultLocale;
|
||||
strings;
|
||||
constructor(locales, defaultLocale) {
|
||||
this.locales = locales;
|
||||
this.defaultLocale = defaultLocale;
|
||||
this.strings = locales[defaultLocale];
|
||||
}
|
||||
setLocale = () => {
|
||||
this.strings = this.locales[LocaleStore.locale] || this.locales[this.defaultLocale];
|
||||
};
|
||||
subscribe() {
|
||||
this.setLocale();
|
||||
LocaleStore.addChangeListener(this.setLocale);
|
||||
}
|
||||
unsubscribe() {
|
||||
LocaleStore.removeChangeListener(this.setLocale);
|
||||
}
|
||||
get(key) {
|
||||
return this.strings[key] || this.locales[this.defaultLocale][key];
|
||||
}
|
||||
}
|
||||
|
||||
// @lib/changelog.ts
|
||||
function showChangelog(changes, meta) {
|
||||
if (!changes || changes.length == 0) return;
|
||||
const changelogVersion = betterdiscord.Data.load("changelogVersion");
|
||||
if (meta.version === changelogVersion) return;
|
||||
betterdiscord.UI.showChangelogModal({
|
||||
title: meta.name,
|
||||
subtitle: meta.version,
|
||||
changes
|
||||
});
|
||||
betterdiscord.Data.save("changelogVersion", meta.version);
|
||||
}
|
||||
|
||||
// @lib/utils/webpack.ts
|
||||
function getClasses(...classes) {
|
||||
return betterdiscord.Webpack.getModule((m) => betterdiscord.Webpack.Filters.byKeys(...classes)(m) && typeof m[classes[0]] == "string");
|
||||
}
|
||||
function getSelectors(...classes) {
|
||||
const module = getClasses(...classes);
|
||||
if (!module) return void 0;
|
||||
return classes.reduce((obj, className) => {
|
||||
obj[className] = "." + module[className].replaceAll(" ", ".");
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
function getIcon(searchString) {
|
||||
const filter = (m) => betterdiscord.Webpack.Filters.byStrings(searchString, '"svg"')(m) && typeof m === "function";
|
||||
return betterdiscord.Webpack.getModule(filter, {
|
||||
searchExports: true
|
||||
});
|
||||
}
|
||||
function expect(object, options) {
|
||||
if (object) return object;
|
||||
const fallbackMessage = !options.fatal && options.fallback ? " Using fallback value instead." : "";
|
||||
const errorMessage = `Module ${options.name} not found.${fallbackMessage}\n\nContact the plugin developer to inform them of this error.`;
|
||||
betterdiscord.Logger.error(errorMessage);
|
||||
options.onError?.();
|
||||
if (options.fatal) throw new Error(errorMessage);
|
||||
return options.fallback;
|
||||
}
|
||||
function expectModule(options) {
|
||||
return expect(betterdiscord.Webpack.getModule(options.filter, options), options);
|
||||
}
|
||||
function expectWithKey(options) {
|
||||
const [module, key] = betterdiscord.Webpack.getWithKey(options.filter, options);
|
||||
if (module) return [module, key];
|
||||
const fallback = expect(module, options);
|
||||
if (fallback) {
|
||||
const key2 = "__key";
|
||||
return [{ [key2]: fallback }, key2];
|
||||
}
|
||||
return void 0;
|
||||
}
|
||||
function expectSelectors(name, classes) {
|
||||
return expect(getSelectors(...classes), {
|
||||
name
|
||||
});
|
||||
}
|
||||
function expectIcon(name, searchString) {
|
||||
return expect(getIcon(searchString), {
|
||||
name,
|
||||
fallback: (_props) => null
|
||||
});
|
||||
}
|
||||
function byType(type) {
|
||||
return (e) => typeof e === type;
|
||||
}
|
||||
|
||||
// manifest.json
|
||||
const changelog = [
|
||||
{
|
||||
title: "Fixed",
|
||||
type: "fixed",
|
||||
items: [
|
||||
"Fixed friends list icons."
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
// @discord/stores.ts
|
||||
const UserStore = betterdiscord.Webpack.getStore("UserStore");
|
||||
const VoiceStateStore = betterdiscord.Webpack.getStore("VoiceStateStore");
|
||||
const GuildStore = betterdiscord.Webpack.getStore("GuildStore");
|
||||
const ChannelStore = betterdiscord.Webpack.getStore("ChannelStore");
|
||||
const PermissionStore = betterdiscord.Webpack.getStore("PermissionStore");
|
||||
const useStateFromStores = expectModule({
|
||||
filter: betterdiscord.Webpack.Filters.byStrings("useStateFromStores"),
|
||||
name: "Flux",
|
||||
fallback(stores, callback) {
|
||||
return callback();
|
||||
},
|
||||
searchExports: true
|
||||
});
|
||||
|
||||
// modules/discordmodules.tsx
|
||||
function useUserVoiceStateFallback({ userId }) {
|
||||
const voiceState = useStateFromStores(
|
||||
[VoiceStateStore],
|
||||
() => userId && VoiceStateStore.getDiscoverableVoiceStateForUser(userId)
|
||||
);
|
||||
const channel = useStateFromStores([ChannelStore], () => {
|
||||
if (voiceState?.channelId) return ChannelStore.getChannel(voiceState?.channelId);
|
||||
});
|
||||
const visible = useStateFromStores(
|
||||
[PermissionStore],
|
||||
() => channel?.isPrivate() || PermissionStore.can(Permissions.VIEW_CHANNEL, channel)
|
||||
);
|
||||
if (visible) {
|
||||
return {
|
||||
voiceState,
|
||||
voiceChannel: channel
|
||||
};
|
||||
} else return {};
|
||||
}
|
||||
const MemberListItem = expectModule({
|
||||
filter: (m) => m.type?.toString?.().includes("memberListItem"),
|
||||
name: "MemberListItem",
|
||||
searchExports: true
|
||||
});
|
||||
const PrivateChannel = expectWithKey({
|
||||
filter: betterdiscord.Webpack.Filters.byStrings("getTypingUsers", "isMultiUserDM", "getRecipientId"),
|
||||
name: "PrivateChannel",
|
||||
searchExports: true
|
||||
});
|
||||
const PeopleListItem = expectModule({
|
||||
filter: betterdiscord.Webpack.Filters.bySource("peopleListItemRef"),
|
||||
declarationFilter: betterdiscord.Webpack.Filters.byStrings("peopleListItemRef"),
|
||||
name: "PeopleListItem"
|
||||
});
|
||||
const Permissions = expectModule({
|
||||
filter: betterdiscord.Webpack.Filters.byKeys("VIEW_CREATOR_MONETIZATION_ANALYTICS"),
|
||||
searchExports: true,
|
||||
name: "Permissions",
|
||||
fallback: {
|
||||
VIEW_CHANNEL: 1024n
|
||||
}
|
||||
});
|
||||
const memberSelectors = expectSelectors("Children Class", ["avatar", "children", "layout"]);
|
||||
const useUserVoiceState = expectModule({
|
||||
filter: betterdiscord.Webpack.Filters.byStrings("getDiscoverableVoiceState", "getDiscoverableVoiceStateForUser"),
|
||||
name: "useUserVoiceState",
|
||||
fallback: useUserVoiceStateFallback
|
||||
});
|
||||
|
||||
// locales.json
|
||||
const el = {
|
||||
SETTINGS_PROFILE: "Τομέας Προφίλ",
|
||||
SETTINGS_PROFILE_NOTE: "Εμφανίζει τον τομέα προφίλ για την τρέχουσα δραστηριότητα φωνής στα αναδυόμενα χρήστη και στην πλευρικές μπάρες προφίλ των Άμεσων Μηνυμάτων.",
|
||||
SETTINGS_ICONS: "Εικονίδια Λίστας Μελών",
|
||||
SETTINGS_ICONS_NOTE: "Εμφανίζει εικονίδια στη λίστα μελών όταν κάποιος είναι σε κανάλι φωνής.",
|
||||
SETTINGS_DM_ICONS: "Εικονίδια Άμεσων Μηνυμάτων",
|
||||
SETTINGS_DM_ICONS_NOTE: "Εμφανίζει εικονίδια στη λίστα Άμεσων Μηνυμάτων όταν κάποιος είναι σε κανάλι φωνής.",
|
||||
SETTINGS_PEOPLE_ICONS: "Εικονίδια Λίστας Φίλων",
|
||||
SETTINGS_PEOPLE_ICONS_NOTE: "Εμφανίζει εικονίδια στη λίστα φίλων όταν κάποιος είναι σε κανάλι φωνής.",
|
||||
SETTINGS_GUILD_ICONS: "Εικονίδια Συντεχνίας",
|
||||
SETTINGS_GUILD_ICONS_NOTE: "Εμφανίζει εικονίδια στις συντεχνίες ακόμα και αν δεν συμμετέχετε.",
|
||||
SETTINGS_COLOR: "Λίστα Μελών - Χρώμα Εικονιδίου Τρέχοντος Καναλιού",
|
||||
SETTINGS_COLOR_NOTE: "Αλλάζει τα εικονίδια της Λίστας Μελών σε πράσινα όταν ο χρήστης είναι στο δικό σας τρέχον κανάλι φωνής.",
|
||||
SETTINGS_STATUS: "Λίστα Μελών - Εμφάνιση Εικονιδίων Κατάστασης",
|
||||
SETTINGS_STATUS_NOTE: "Αλλάζει τα εικονίδια της Λίστας Μελών όταν ο χρήστης είναι σε Σίγαση, Κώφωση ή έχε ενεργοποιημένο Βίντεο.",
|
||||
SETTINGS_IGNORE: "Αγνόηση",
|
||||
SETTINGS_IGNORE_NOTE: "Προσθέτει μια επιλογή στο Κανάλι Φωνής και στα μενού περιεχομένου Συντεχνίας για αγνόηση αυτού του καναλιού/συντεχνίας στα Εικονίδια Λϊστας Μελών και στα Αναδυόμενα Χρήστη.",
|
||||
CONTEXT_IGNORE: "Αγνόηση στη Δραστηριότητα Φωνής",
|
||||
VOICE_CALL: "Φωνητική Κλήση",
|
||||
PRIVATE_CALL: "Ιδιωτική Κλήση",
|
||||
GROUP_CALL: "Ομαδική Κλήση",
|
||||
LIVE: "Ζωντανά",
|
||||
HEADER: "Σε ένα Κανάλι Φωνής",
|
||||
HEADER_VOICE: "Σε μια Κλήση Φωνής",
|
||||
HEADER_PRIVATE: "Σε μια Ιδιωτική Κλήση",
|
||||
HEADER_GROUP: "Σε μια Ομαδική Κλήση",
|
||||
HEADER_STAGE: "Σε ένα Κανάλι Σταδίου",
|
||||
VIEW: "Προβολή Καναλιού",
|
||||
VIEW_CALL: "Προβολή Κλήσης",
|
||||
JOIN: "Συμμετοχή σε Κανάλι",
|
||||
JOIN_CALL: "Συμμετοχή σε Κλήση",
|
||||
JOIN_DISABLED: "Ήδη σε Κανάλι",
|
||||
JOIN_DISABLED_CALL: "Ήδη σε Κλήση",
|
||||
JOIN_VIDEO: "Συμμετοχή με Βίντεο"
|
||||
};
|
||||
const ru = {
|
||||
SETTINGS_PROFILE: "Раздел в Профиле",
|
||||
SETTINGS_PROFILE_NOTE: "Показывает раздел для текущей голосовой активности в профиле, всплывающих окнах и боковых панелях в ЛС.",
|
||||
SETTINGS_ICONS: "Иконки в Списке Участников",
|
||||
SETTINGS_ICONS_NOTE: "Показывает иконки в списке участников когда пользователь в голосовом канале.",
|
||||
SETTINGS_DM_ICONS: "Иконки в ЛС",
|
||||
SETTINGS_DM_ICONS_NOTE: "Показывает иконки в списке участников ЛС когда пользователь в голосовом канале.",
|
||||
SETTINGS_PEOPLE_ICONS: "Иконки в Списке Друзей",
|
||||
SETTINGS_PEOPLE_ICONS_NOTE: "Показывает иконки в списке друзей когда пользователь в голосовом канале.",
|
||||
SETTINGS_GUILD_ICONS: "Иконки Сервера",
|
||||
SETTINGS_GUILD_ICONS_NOTE: "Показывает голосовые иконки на серверах даже когда вы не участвуете.",
|
||||
SETTINGS_COLOR: "Цвет Иконки Голосовой Активности",
|
||||
SETTINGS_COLOR_NOTE: "Делает иконки в Списке Участников зелёными когда пользователь в голосовом канале вместе с вами.",
|
||||
SETTINGS_STATUS: "Показывать Иконки Статуса",
|
||||
SETTINGS_STATUS_NOTE: "Меняет иконки в Списке Участников когда пользователь Выключил Микрофон/Звук или Начал трансляцию.",
|
||||
SETTINGS_IGNORE: "Ингор",
|
||||
SETTINGS_IGNORE_NOTE: "Добавляет возможность в контекстных меню Голосовых Каналов и Серверов игнорировать этот канал/сервер в Списке Участников и Профилях Пользователей.",
|
||||
CONTEXT_IGNORE: "Игнор (Голосовая Активность)",
|
||||
VOICE_CALL: "Голосовой звонок",
|
||||
PRIVATE_CALL: "Приватный звонок",
|
||||
GROUP_CALL: "Групповой звонок",
|
||||
LIVE: "В ЭФИРЕ",
|
||||
HEADER: "В Голосовом Канале",
|
||||
HEADER_VOICE: "В Голосовом Звонке",
|
||||
HEADER_PRIVATE: "В Приватном Звонке",
|
||||
HEADER_GROUP: "В Групповом Звонке",
|
||||
HEADER_STAGE: "В Канале Трибуны",
|
||||
VIEW: "Просмотреть Канал",
|
||||
VIEW_CALL: "Просмотреть Звонок",
|
||||
JOIN: "Присоединиться к Каналу",
|
||||
JOIN_CALL: "Присоединиться к Звонку",
|
||||
JOIN_DISABLED: "Уже в Канале",
|
||||
JOIN_DISABLED_CALL: "Уже в Звонке",
|
||||
JOIN_VIDEO: "Присоединиться с Видео"
|
||||
};
|
||||
const de = {
|
||||
SETTINGS_PROFILE: "Profilbereich",
|
||||
SETTINGS_PROFILE_NOTE: "Zeigt den Profilbereich für die aktuelle Sprachaktivität in Benutzer-Popouts und DM-Profilseitenleisten an.",
|
||||
SETTINGS_ICONS: "Symbole in der Mitgliederliste",
|
||||
SETTINGS_ICONS_NOTE: "Zeigt Symbole in der Mitgliederliste an, wenn sich jemand in einem Sprachkanal befindet.",
|
||||
SETTINGS_DM_ICONS: "DM-Symbole",
|
||||
SETTINGS_DM_ICONS_NOTE: "Zeigt Symbole in der DM-Liste an, wenn sich jemand in einem Sprachkanal befindet.",
|
||||
SETTINGS_PEOPLE_ICONS: "Symbole in der Freundesliste",
|
||||
SETTINGS_PEOPLE_ICONS_NOTE: "Zeigt Symbole in der Freundesliste an, wenn sich jemand in einem Sprachkanal befindet.",
|
||||
SETTINGS_GUILD_ICONS: "Gildensymbole",
|
||||
SETTINGS_GUILD_ICONS_NOTE: "Zeigt Symbole für Gilden an, auch wenn du nicht teilnimmst.",
|
||||
SETTINGS_COLOR: "Mitgliederliste - Farbe des aktuellen Kanalsymbols",
|
||||
SETTINGS_COLOR_NOTE: "Die Symbole in der Mitgliederliste werden grün, wenn sich der Benutzer in Ihrem aktuellen Sprachkanal befindet.",
|
||||
SETTINGS_STATUS: "Mitgliederliste - Statussymbole anzeigen",
|
||||
SETTINGS_STATUS_NOTE: "Ändert die Symbole in der Mitgliederliste, wenn ein Benutzer stummgeschaltet oder mit aktiviertem Video ist.",
|
||||
SETTINGS_IGNORE: "Ignorieren",
|
||||
SETTINGS_IGNORE_NOTE: "Fügt eine Option in die Kontextmenüs von Sprachkanälen und Gilden hinzu, um diesen Kanal/diese Gilde in der Mitgliederliste und Benutzer-Popouts zu ignorieren.",
|
||||
CONTEXT_IGNORE: "Bei Sprachaktivität ignorieren",
|
||||
VOICE_CALL: "Sprachanruf",
|
||||
PRIVATE_CALL: "Privatanruf",
|
||||
GROUP_CALL: "Gruppenanruf",
|
||||
LIVE: "Live",
|
||||
HEADER: "In einem Sprachkanal",
|
||||
HEADER_VOICE: "In einem Sprachanruf",
|
||||
HEADER_PRIVATE: "In einem privaten Anruf",
|
||||
HEADER_GROUP: "In einem Gruppenruf",
|
||||
HEADER_STAGE: "In einem Stage Kanal",
|
||||
VIEW: "Kanal anzeigen",
|
||||
VIEW_CALL: "Anruf anzeigen",
|
||||
JOIN: "Kanal beitreten",
|
||||
JOIN_CALL: "Anruf beitreten",
|
||||
JOIN_DISABLED: "Bereits im Kanal",
|
||||
JOIN_DISABLED_CALL: "Bereits im Aufruf",
|
||||
JOIN_VIDEO: "Beitreten mit Video"
|
||||
};
|
||||
const fr = {
|
||||
SETTINGS_PROFILE: "Section de profil",
|
||||
SETTINGS_PROFILE_NOTE: "Affiche une section de profile pour l'activité vocale actuelle dans les popouts utilisateur et les profiles latéraux de MP.",
|
||||
SETTINGS_ICONS: "Icônes de la liste de membre",
|
||||
SETTINGS_ICONS_NOTE: "Affiche des icônes dans la liste de membre quand quelqu'un est dans un salon vocal.",
|
||||
SETTINGS_DM_ICONS: "Icônes de MP",
|
||||
SETTINGS_DM_ICONS_NOTE: "Affiche des icônes dans la liste de MP quand quelqu'un est dans un salon vocal.",
|
||||
SETTINGS_PEOPLE_ICONS: "Icônes de la liste d'amis",
|
||||
SETTINGS_PEOPLE_ICONS_NOTE: "Affiche des icônes dans la liste d'amis quand quelqu'un est dans un salon vocal.",
|
||||
SETTINGS_GUILD_ICONS: "Icônes de serveur",
|
||||
SETTINGS_GUILD_ICONS_NOTE: "Affiche des icônes de vocal sur les serveurs même quand vous ne participez pas.",
|
||||
SETTINGS_COLOR: "Liste de membre - Couleur d'icône du salon actuel",
|
||||
SETTINGS_COLOR_NOTE: "Rends les icônes de la liste de membre vertes quand un utilisateur est dans le salon vocal actuel.",
|
||||
SETTINGS_STATUS: "Liste de membre - Afficher les icônes de status",
|
||||
SETTINGS_STATUS_NOTE: "Change les icônes de la liste de membre quand un utilisateur est muet, mis en sourdine, ou a la vidéo activée.",
|
||||
SETTINGS_IGNORE: "Ignorer",
|
||||
SETTINGS_IGNORE_NOTE: "Ajoute une option dans les menus contextuels des salons vocaux et serveurs pour ignorer le dit salon/serveur dans les icônes de liste de membre et popouts utilisateur.",
|
||||
CONTEXT_IGNORE: "Ignorer dans Activité Vocale",
|
||||
VOICE_CALL: "Appel vocal",
|
||||
PRIVATE_CALL: "Appel privé",
|
||||
GROUP_CALL: "Appel de groupe",
|
||||
LIVE: "Live",
|
||||
HEADER: "Dans un salon vocal",
|
||||
HEADER_VOICE: "Dans un appel vocal",
|
||||
HEADER_PRIVATE: "Dans un appel privé",
|
||||
HEADER_GROUP: "Dans un appel de groupe",
|
||||
HEADER_STAGE: "Dans un salon de conférence",
|
||||
VIEW: "Voir le salon",
|
||||
VIEW_CALL: "Voir l'appel",
|
||||
JOIN: "Rejoindre le salon",
|
||||
JOIN_CALL: "Rejoindre l'appel",
|
||||
JOIN_DISABLED: "Déjà dans le salon",
|
||||
JOIN_DISABLED_CALL: "Déjà en appel",
|
||||
JOIN_VIDEO: "Rejoidre avec vidéo",
|
||||
MEMBER: "Membre",
|
||||
MEMBERS: "Membres"
|
||||
};
|
||||
const locales = {
|
||||
"en-US": {
|
||||
SETTINGS_PROFILE: "Profile Section",
|
||||
SETTINGS_PROFILE_NOTE: "Shows profile section for current voice activity in user popouts and DM profile sidebars.",
|
||||
SETTINGS_ICONS: "Member List Icons",
|
||||
SETTINGS_ICONS_NOTE: "Shows icons on the member list when someone is in a voice channel.",
|
||||
SETTINGS_DM_ICONS: "DM Icons",
|
||||
SETTINGS_DM_ICONS_NOTE: "Shows icons on the DM list when someone is in a voice channel.",
|
||||
SETTINGS_PEOPLE_ICONS: "Friends List Icons",
|
||||
SETTINGS_PEOPLE_ICONS_NOTE: "Shows icons on the friends list when someone is in a voice channel.",
|
||||
SETTINGS_GUILD_ICONS: "Server Icons",
|
||||
SETTINGS_GUILD_ICONS_NOTE: "Shows voice icons on servers even when you're not participating.",
|
||||
SETTINGS_COLOR: "Member List - Current Channel Icon Color",
|
||||
SETTINGS_COLOR_NOTE: "Makes the Member List icons green when the user is in your current voice channel.",
|
||||
SETTINGS_STATUS: "Member List - Show Status Icons",
|
||||
SETTINGS_STATUS_NOTE: "Changes the Member List icons when a user is Muted, Deafened, or has Video enabled.",
|
||||
SETTINGS_CURRENT_USER: "Current User Icon",
|
||||
SETTINGS_CURRENT_USER_NOTE: "Toggles displaying a voice channel icon for the current user.",
|
||||
SETTINGS_IGNORE: "Ignore",
|
||||
SETTINGS_IGNORE_NOTE: "Adds an option on Voice Channel and Server context menus to ignore that channel/server in Member List Icons and User Popouts.",
|
||||
CONTEXT_IGNORE: "Ignore in Voice Activity",
|
||||
VOICE_CALL: "Voice Call",
|
||||
PRIVATE_CALL: "Private Call",
|
||||
GROUP_CALL: "Group Call",
|
||||
LIVE: "Live",
|
||||
HEADER: "In a Voice Channel",
|
||||
HEADER_VOICE: "In a Voice Call",
|
||||
HEADER_PRIVATE: "In a Private Call",
|
||||
HEADER_GROUP: "In a Group Call",
|
||||
HEADER_STAGE: "In a Stage Channel",
|
||||
VIEW: "View Channel",
|
||||
VIEW_CALL: "View Call",
|
||||
JOIN: "Join Channel",
|
||||
JOIN_CALL: "Join Call",
|
||||
JOIN_DISABLED: "Already in Channel",
|
||||
JOIN_DISABLED_CALL: "Already in Call",
|
||||
JOIN_VIDEO: "Join With Video",
|
||||
MEMBER: "Member",
|
||||
MEMBERS: "Members"
|
||||
},
|
||||
el: el,
|
||||
ru: ru,
|
||||
de: de,
|
||||
fr: fr
|
||||
};
|
||||
|
||||
// modules/utils.ts
|
||||
const Settings = new SettingsManager({
|
||||
showMemberListIcons: true,
|
||||
showDMListIcons: true,
|
||||
showPeopleListIcons: true,
|
||||
currentChannelColor: true,
|
||||
showStatusIcons: true,
|
||||
currentUserIcon: true,
|
||||
ignoreEnabled: false,
|
||||
ignoredChannels: [],
|
||||
ignoredGuilds: []
|
||||
});
|
||||
const Strings = new StringsManager(locales, "en-US");
|
||||
function groupDMName(members) {
|
||||
if (members.length === 1) {
|
||||
return UserStore.getUser(members[0]).globalName;
|
||||
} else if (members.length > 1) {
|
||||
let name = "";
|
||||
for (let i = 0; i < members.length; i++) {
|
||||
if (i === members.length - 1) name += UserStore.getUser(members[i]).globalName;
|
||||
else name += UserStore.getUser(members[i]).globalName + ", ";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
return "Unnamed";
|
||||
}
|
||||
|
||||
// styles/voiceicon.module.css
|
||||
const css = `
|
||||
.VoiceActivity-voiceicon-icon {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
min-width: 20px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--background-accent);
|
||||
cursor: pointer;
|
||||
svg {
|
||||
padding: 3px;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
.VoiceActivity-voiceicon-iconCurrentCall {
|
||||
background-color: var(--status-positive);
|
||||
}
|
||||
.VoiceActivity-voiceicon-iconLive {
|
||||
height: 16px;
|
||||
border-radius: 16px;
|
||||
background-color: var(--red-400);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
font-weight: 600;
|
||||
font-family: var(--font-display);
|
||||
text-transform: uppercase;
|
||||
& > div {
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
.VoiceActivity-voiceicon-tooltip {
|
||||
.VoiceActivity-voiceicon-header {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.VoiceActivity-voiceicon-subtext {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 3px;
|
||||
& > div {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
.VoiceActivity-voiceicon-tooltipIcon {
|
||||
min-width: 16px;
|
||||
margin-right: 3px;
|
||||
color: var(--interactive-normal);
|
||||
}
|
||||
}
|
||||
.VoiceActivity-voiceicon-iconContainer {
|
||||
margin-left: auto;
|
||||
}
|
||||
.VoiceActivity-voiceicon-peopleListIcon {
|
||||
margin-right: 16px;
|
||||
}`;
|
||||
_loadStyle("voiceicon.module.css", css);
|
||||
const modules_1af761ba = {
|
||||
"icon": "VoiceActivity-voiceicon-icon",
|
||||
"iconCurrentCall": "VoiceActivity-voiceicon-iconCurrentCall",
|
||||
"iconLive": "VoiceActivity-voiceicon-iconLive",
|
||||
"tooltip": "VoiceActivity-voiceicon-tooltip",
|
||||
"header": "VoiceActivity-voiceicon-header",
|
||||
"subtext": "VoiceActivity-voiceicon-subtext",
|
||||
"tooltipIcon": "VoiceActivity-voiceicon-tooltipIcon",
|
||||
"iconContainer": "VoiceActivity-voiceicon-iconContainer",
|
||||
"peopleListIcon": "VoiceActivity-voiceicon-peopleListIcon"
|
||||
};
|
||||
const iconStyles = modules_1af761ba;
|
||||
|
||||
// @discord/icons.tsx
|
||||
const Speaker = expectIcon("Speaker", "M12 3a1 1 0 0 0-1-1h-.06a1 1 0 0 0-.74.32L5.92 7H3a1 1");
|
||||
const Muted = expectIcon(
|
||||
"Muted",
|
||||
"m2.7 22.7 20-20a1 1 0 0 0-1.4-1.4l-20 20a1 1 0 1 0 1.4 1.4ZM10.8 17.32c-.21.21-.1.58.2.62V20H9a1"
|
||||
);
|
||||
const ServerMuted = expectIcon(
|
||||
"ServerMuted",
|
||||
"M21.76.83a5.02 5.02 0 0 1 .78 7.7 5 5 0 0 1-7.07 0 5.02 5.02 0 0 1 0-7.07"
|
||||
);
|
||||
const Deafened = expectIcon(
|
||||
"Deafened",
|
||||
"M22.7 2.7a1 1 0 0 0-1.4-1.4l-20 20a1 1 0 1 0 1.4 1.4l20-20ZM17.06"
|
||||
);
|
||||
const ServerDeafened = expectIcon(
|
||||
"ServerDeafened",
|
||||
"M12.38 1c.38.02.58.45.4.78-.15.3-.3.62-.4.95A.4.4 0 0 1 12 3a9 9 0 0 0-8.95 10h1.87a5"
|
||||
);
|
||||
const Video = expectIcon("Video", "M4 4a3 3 0 0 0-3 3v10a3 3 0 0 0 3 3h11a3 3");
|
||||
const ChannelIcon = expectModule({
|
||||
filter: betterdiscord.Webpack.Filters.combine(
|
||||
betterdiscord.Webpack.Filters.byStrings("isGuildStageVoice", "isGroupDM", "isPrivate"),
|
||||
(m) => !m.toString?.().includes("intl")
|
||||
),
|
||||
name: "ChannelIcon",
|
||||
fallback: (_props) => null
|
||||
});
|
||||
|
||||
// @discord/modules.ts
|
||||
const transitionTo = expectModule({
|
||||
filter: betterdiscord.Webpack.Filters.combine(
|
||||
betterdiscord.Webpack.Filters.byStrings("transitionTo -"),
|
||||
byType("function")
|
||||
),
|
||||
searchExports: true,
|
||||
name: "transitionTo"
|
||||
});
|
||||
|
||||
// components/VoiceIcon.tsx
|
||||
function VoiceIcon(props) {
|
||||
const settingsState = Settings.useSettingsState(
|
||||
"showMemberListIcons",
|
||||
"showDMListIcons",
|
||||
"showPeopleListIcons",
|
||||
"ignoreEnabled",
|
||||
"ignoredChannels",
|
||||
"ignoredGuilds",
|
||||
"currentChannelColor",
|
||||
"showStatusIcons",
|
||||
"currentUserIcon"
|
||||
);
|
||||
const currentUser = UserStore.getCurrentUser();
|
||||
const { voiceState, voiceChannel: channel } = useUserVoiceState({ userId: props.userId });
|
||||
const { voiceState: currentUserVoiceState } = useUserVoiceState({ userId: currentUser?.id });
|
||||
if (props.context === "memberlist" && !settingsState.showMemberListIcons) return null;
|
||||
if (props.context === "dmlist" && !settingsState.showDMListIcons) return null;
|
||||
if (props.context === "peoplelist" && !settingsState.showPeopleListIcons) return null;
|
||||
if (props.userId === currentUser?.id && !settingsState.currentUserIcon) return null;
|
||||
if (!voiceState) return null;
|
||||
const guild = GuildStore.getGuild(channel.guild_id);
|
||||
const ignored = settingsState.ignoredChannels.includes(channel.id) || settingsState.ignoredGuilds.includes(guild?.id);
|
||||
if (settingsState.ignoreEnabled && ignored) return null;
|
||||
let text;
|
||||
let subtext;
|
||||
let channelPath;
|
||||
let className = iconStyles.icon;
|
||||
if (settingsState.currentChannelColor && channel.id === currentUserVoiceState?.channelId)
|
||||
className = `${iconStyles.icon} ${iconStyles.iconCurrentCall}`;
|
||||
if (voiceState.selfStream) className = iconStyles.iconLive;
|
||||
if (guild) {
|
||||
text = guild.name;
|
||||
subtext = channel.name;
|
||||
channelPath = `/channels/${guild.id}/${channel.id}`;
|
||||
} else {
|
||||
text = channel.name;
|
||||
subtext = Strings.get("VOICE_CALL");
|
||||
channelPath = `/channels/@me/${channel.id}`;
|
||||
}
|
||||
switch (channel.type) {
|
||||
case 1:
|
||||
text = UserStore.getUser(channel.recipients[0]).globalName;
|
||||
subtext = Strings.get("PRIVATE_CALL");
|
||||
break;
|
||||
case 3:
|
||||
text = channel.name || groupDMName(channel.recipients);
|
||||
subtext = Strings.get("GROUP_CALL");
|
||||
break;
|
||||
}
|
||||
let Icon = Speaker;
|
||||
if (settingsState.showStatusIcons) {
|
||||
if (voiceState.selfVideo) Icon = Video;
|
||||
else if (voiceState.deaf) Icon = ServerDeafened;
|
||||
else if (voiceState.selfDeaf) Icon = Deafened;
|
||||
else if (voiceState.mute) Icon = ServerMuted;
|
||||
else if (voiceState.selfMute) Icon = Muted;
|
||||
}
|
||||
return BdApi.React.createElement(
|
||||
"div",
|
||||
{
|
||||
className,
|
||||
onClick: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (channelPath) transitionTo?.(channelPath);
|
||||
}
|
||||
},
|
||||
BdApi.React.createElement(
|
||||
betterdiscord.Components.Tooltip,
|
||||
{
|
||||
text: BdApi.React.createElement("div", { className: iconStyles.tooltip }, BdApi.React.createElement("div", { className: iconStyles.header, style: { fontWeight: "600" } }, text), BdApi.React.createElement("div", { className: iconStyles.subtext }, BdApi.React.createElement(
|
||||
ChannelIcon,
|
||||
{
|
||||
className: iconStyles.tooltipIcon,
|
||||
size: "16",
|
||||
width: "16",
|
||||
height: "16",
|
||||
color: "currentColor",
|
||||
channel
|
||||
}
|
||||
), BdApi.React.createElement("div", { style: { fontWeight: "400" } }, subtext)))
|
||||
},
|
||||
(props2) => BdApi.React.createElement("div", { ...props2 }, !voiceState.selfStream ? BdApi.React.createElement(Icon, { size: "14", width: "14", height: "14", color: "currentColor" }) : Strings.get("LIVE"))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// index.tsx
|
||||
class VoiceActivity {
|
||||
meta;
|
||||
contextMenuUnpatches = new Set();
|
||||
constructor(meta) {
|
||||
this.meta = meta;
|
||||
}
|
||||
start() {
|
||||
showChangelog(changelog, this.meta);
|
||||
betterdiscord.DOM.addStyle(
|
||||
styles() + `${memberSelectors?.children}:empty { margin-left: 0; } ${memberSelectors?.children} { display: flex; gap: 8px; } ${memberSelectors?.layout} { width: 100%; }`
|
||||
);
|
||||
Strings.subscribe();
|
||||
this.patchPeopleListItem();
|
||||
this.patchMemberListItem();
|
||||
this.patchPrivateChannel();
|
||||
this.patchChannelContextMenu();
|
||||
this.patchGuildContextMenu();
|
||||
betterdiscord.Patcher.instead(VoiceStateStore, "getDiscoverableVoiceState", (_, [guildId, userId]) => {
|
||||
return VoiceStateStore.getDiscoverableVoiceStateForUser(userId);
|
||||
});
|
||||
}
|
||||
patchMemberListItem() {
|
||||
if (!MemberListItem) return;
|
||||
betterdiscord.Patcher.after(MemberListItem, "type", (_, [props], ret) => {
|
||||
if (!props.user) return ret;
|
||||
const children = ret.props.children;
|
||||
ret.props.children = (childrenProps) => {
|
||||
const childrenRet = children(childrenProps);
|
||||
const target = betterdiscord.Utils.findInTree(childrenRet, (x) => x.props?.avatar && x.props?.decorators, {
|
||||
walkable: ["props", "children"]
|
||||
});
|
||||
const icon = BdApi.React.createElement(VoiceIcon, { userId: props.user.id, context: "memberlist" });
|
||||
Array.isArray(target.props.children) ? target.props.children.unshift(icon) : target.props.children = [icon];
|
||||
return childrenRet;
|
||||
};
|
||||
});
|
||||
}
|
||||
patchPrivateChannel() {
|
||||
if (!PrivateChannel) return;
|
||||
const patchType = (props, ret) => {
|
||||
if (props.channel.type !== 1) return;
|
||||
const target = betterdiscord.Utils.findInTree(ret, (e) => typeof e?.props?.children !== "function", {
|
||||
walkable: ["children", "props"]
|
||||
})?.props?.children ?? ret;
|
||||
if (!target) return;
|
||||
const children = target.props.children;
|
||||
target.props.children = (childrenProps) => {
|
||||
const childrenRet = children(childrenProps);
|
||||
const privateChannel = betterdiscord.Utils.findInTree(childrenRet, (e) => e?.children?.props?.avatar, {
|
||||
walkable: ["children", "props"]
|
||||
});
|
||||
privateChannel.children = [
|
||||
privateChannel.children,
|
||||
BdApi.React.createElement("div", { className: iconStyles.iconContainer }, BdApi.React.createElement(VoiceIcon, { userId: props.user.id, context: "dmlist" }))
|
||||
];
|
||||
return childrenRet;
|
||||
};
|
||||
};
|
||||
let patchedType;
|
||||
betterdiscord.Patcher.after(...PrivateChannel, (_, __, containerRet) => {
|
||||
let target = containerRet.children || containerRet;
|
||||
if (patchedType) {
|
||||
target.type = patchedType;
|
||||
return containerRet;
|
||||
}
|
||||
const original = target.type;
|
||||
patchedType = (props) => {
|
||||
const ret = original(props);
|
||||
patchType(props, ret);
|
||||
return ret;
|
||||
};
|
||||
target.type = patchedType;
|
||||
});
|
||||
}
|
||||
patchPeopleListItem() {
|
||||
if (!PeopleListItem) return;
|
||||
betterdiscord.Patcher.after(PeopleListItem.prototype, "render", (that, _, ret) => {
|
||||
if (!that.props.user) return;
|
||||
const children = ret.props.children;
|
||||
ret.props.children = (childrenProps) => {
|
||||
const childrenRet = children(childrenProps);
|
||||
betterdiscord.Utils.findInTree(childrenRet, (i) => Array.isArray(i), { walkable: ["props", "children"] }).splice(
|
||||
1,
|
||||
0,
|
||||
BdApi.React.createElement("div", { className: `${iconStyles.iconContainer} ${iconStyles.peopleListIcon}` }, BdApi.React.createElement(VoiceIcon, { userId: that.props.user.id, context: "peoplelist" }))
|
||||
);
|
||||
return childrenRet;
|
||||
};
|
||||
});
|
||||
}
|
||||
patchChannelContextMenu() {
|
||||
const unpatch = betterdiscord.ContextMenu.patch("channel-context", (ret, props) => {
|
||||
if (!Settings.get("ignoreEnabled")) return ret;
|
||||
if (props.channel.type !== 2 && props.channel.type !== 13) return ret;
|
||||
const { ignoredChannels } = Settings.useSettingsState("ignoredChannels");
|
||||
const ignored = ignoredChannels.includes(props.channel.id);
|
||||
const menuItem = betterdiscord.ContextMenu.buildItem({
|
||||
type: "toggle",
|
||||
label: Strings.get("CONTEXT_IGNORE"),
|
||||
id: "voiceactivity-ignore",
|
||||
checked: ignored,
|
||||
action: () => {
|
||||
if (ignored) {
|
||||
const newIgnoredChannels = ignoredChannels.filter((id) => id !== props.channel.id);
|
||||
Settings.set("ignoredChannels", newIgnoredChannels);
|
||||
} else {
|
||||
const newIgnoredChannels = [...ignoredChannels, props.channel.id];
|
||||
Settings.set("ignoredChannels", newIgnoredChannels);
|
||||
}
|
||||
}
|
||||
});
|
||||
ret.props.children[3].props.children.splice(4, 0, menuItem);
|
||||
});
|
||||
this.contextMenuUnpatches.add(unpatch);
|
||||
}
|
||||
patchGuildContextMenu() {
|
||||
const unpatch = betterdiscord.ContextMenu.patch("guild-context", (ret, props) => {
|
||||
if (!props.guild) return ret;
|
||||
if (!Settings.get("ignoreEnabled")) return ret;
|
||||
const { ignoredGuilds } = Settings.useSettingsState("ignoredGuilds");
|
||||
const ignored = ignoredGuilds.includes(props.guild.id);
|
||||
const menuItem = betterdiscord.ContextMenu.buildItem({
|
||||
type: "toggle",
|
||||
label: Strings.get("CONTEXT_IGNORE"),
|
||||
id: "voiceactivity-ignore",
|
||||
checked: ignored,
|
||||
action: () => {
|
||||
if (ignored) {
|
||||
const newIgnoredGuilds = ignoredGuilds.filter((id) => id !== props.guild.id);
|
||||
Settings.set("ignoredGuilds", newIgnoredGuilds);
|
||||
} else {
|
||||
const newIgnoredGuilds = [...ignoredGuilds, props.guild.id];
|
||||
Settings.set("ignoredGuilds", newIgnoredGuilds);
|
||||
}
|
||||
}
|
||||
});
|
||||
ret.props.children[2].props.children.push(menuItem);
|
||||
});
|
||||
this.contextMenuUnpatches.add(unpatch);
|
||||
}
|
||||
stop() {
|
||||
betterdiscord.DOM.removeStyle();
|
||||
betterdiscord.Patcher.unpatchAll();
|
||||
this.contextMenuUnpatches.forEach((unpatch) => unpatch());
|
||||
this.contextMenuUnpatches.clear();
|
||||
Strings.unsubscribe();
|
||||
}
|
||||
getSettingsPanel() {
|
||||
return buildSettingsPanel(Settings, [
|
||||
{
|
||||
id: "showMemberListIcons",
|
||||
type: "switch",
|
||||
name: Strings.get("SETTINGS_ICONS"),
|
||||
note: Strings.get("SETTINGS_ICONS_NOTE")
|
||||
},
|
||||
{
|
||||
id: "showDMListIcons",
|
||||
type: "switch",
|
||||
name: Strings.get("SETTINGS_DM_ICONS"),
|
||||
note: Strings.get("SETTINGS_DM_ICONS_NOTE")
|
||||
},
|
||||
{
|
||||
id: "showPeopleListIcons",
|
||||
type: "switch",
|
||||
name: Strings.get("SETTINGS_PEOPLE_ICONS"),
|
||||
note: Strings.get("SETTINGS_PEOPLE_ICONS_NOTE")
|
||||
},
|
||||
{
|
||||
id: "currentChannelColor",
|
||||
type: "switch",
|
||||
name: Strings.get("SETTINGS_COLOR"),
|
||||
note: Strings.get("SETTINGS_COLOR_NOTE")
|
||||
},
|
||||
{
|
||||
id: "showStatusIcons",
|
||||
type: "switch",
|
||||
name: Strings.get("SETTINGS_STATUS"),
|
||||
note: Strings.get("SETTINGS_STATUS_NOTE")
|
||||
},
|
||||
{
|
||||
id: "currentUserIcon",
|
||||
type: "switch",
|
||||
name: Strings.get("SETTINGS_CURRENT_USER"),
|
||||
note: Strings.get("SETTINGS_CURRENT_USER_NOTE")
|
||||
},
|
||||
{
|
||||
id: "ignoreEnabled",
|
||||
type: "switch",
|
||||
name: Strings.get("SETTINGS_IGNORE"),
|
||||
note: Strings.get("SETTINGS_IGNORE_NOTE")
|
||||
}
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = VoiceActivity;
|
||||
|
||||
/*@end@*/
|
||||
Reference in New Issue
Block a user