Audio notifications, per-character sounds and auto-refreshing character metadata
// ==UserScript==
// @name Mucklet Audio Notifier
// @name:de Mucklet Audio Benachrichtigungen
// @namespace https://forum.wolfery.com/u/felinex/
// @version 2.1.0
// @description Audio notifications, per-character sounds and auto-refreshing character metadata
// @description:de Audio-Benachrichtigungen, Sounds pro Charakter und automatische Aktualisierung von Charakter-Metadaten
// @icon https://mucklet.com/mucklet-logo.svg
// @license All Rights Reserved
// @author Felinex Gloomfort
// @match https://wolfery.com/*
// @match https://test.wolfery.com/*
// @match https://mucklet.com/*
// @match https://*.mucklet.com/*
// @grant none
// ==/UserScript==
(function() {"use strict";console.log("[AudioNotifier] Script loaded (v2.1.0).");const e="https://actions.google.com/sounds/v1/alarms/beep_short.ogg",t="AudioNotifierSettings",n=1048576;let a=0,r=0,o=!1,i=!1;const l={muteWhenWindowFocused:!1,muteForActiveChar:!1,playOnFocusedChar:!0,soundDefault:null,soundMention:null,soundPrivate:null,soundFocusChar:null,charSounds:{}};let c=function(){try{const e=localStorage.getItem(t);if(!e)return{...l,charSounds:{}};const n=JSON.parse(e);return{...l,...n,charSounds:y(n.charSounds)}}catch(e){return console.error("[AudioNotifier] Failed to load settings",e),{...l,charSounds:{}}}}(),s=!1,d=!1,u=0,m=null;function h(e){return String(e||"").trim().replace(/\s+/g," ").toLowerCase()}function p(e){return"name:"+h(e)}function f(e){return"id:"+String(e)}function y(e){const t={},n=e&&"object"==typeof e?e:{};return Object.keys(n).forEach(e=>{const a=function(e,t){return t&&"object"==typeof t&&!Array.isArray(t)?{name:String(t.name||e.replace(/^name:/,"")).trim(),charId:null!=t.charId?String(t.charId):null!=t.id?String(t.id):null,avatar:t.avatar||null,sound:t.sound||null,alwaysPlay:!!t.alwaysPlay}:{name:String(e.replace(/^name:/,"")).trim(),charId:null,avatar:null,sound:"string"==typeof t?t:null,alwaysPlay:!1}}(e,n[e]),r=a.charId?f(a.charId):p(a.name||e);t[r]=a}),t}function g(){try{localStorage.setItem(t,JSON.stringify(c))}catch(e){console.error("[AudioNotifier] Failed to save settings",e)}}function v(e){try{return window.app&&"function"==typeof window.app.getModule?window.app.getModule(e):null}catch(e){return null}}function C(e){(function(e){return!!m||!!function(e){const t=v("playerTabs");if(!t||"function"!=typeof t.addTab)return!1;if(t.getTabs&&t.getTabs().get&&t.getTabs().get("audioNotifier"))return!1;let n=null,a=null;function r(t){let n=null;return{render:a=>(n=document.createElement("button"),n.type="button",n.className="iconbtn medium default-400 audio-notifier-player-tab-button",n.title=e.tabTitle,n.setAttribute("aria-label",e.tabTitle),n.innerHTML='<i class="fa fa-volume-up" aria-hidden="true"></i>',n.addEventListener("click",e=>{t(),e.stopPropagation()}),a.appendChild(n),n),unrender(){n&&n.parentNode&&n.parentNode.removeChild(n),n=null}}}function o(){return{render:t=>(n=t,a=b(e),t.appendChild(a),W(),a),unrender(){a&&a.parentNode&&a.parentNode.removeChild(a),a=null,n=null}}}try{return t.addTab({id:"audioNotifier",sortOrder:1e4,tabFactory:e=>r(e),factory:(t,n,a)=>({component:o(),title:e.title})}),m={mode:"hook",dispose:()=>{"function"==typeof t.removeTab&&t.removeTab("audioNotifier")}},console.log("[AudioNotifier] Registered via playerTabs.addTab()"),!0}catch(e){return console.debug("[AudioNotifier] playerTabs.addTab() failed:",e),!1}}(e)})(e)||(u++,u<40&&setTimeout(()=>C(e),500))}function b(e,t){const a=document.createElement("div");a.className="pad-bottom-l audio-notifier-panel",a.style.padding="16px";const r=document.createElement("div");r.style.display="flex",r.style.alignItems="center",r.style.justifyContent="space-between",r.style.gap="8px",r.style.marginTop="12px",r.style.marginBottom="12px";const o=document.createElement("h3");if(o.className="margin-bottom-m",o.style.margin="0",o.textContent=e.title,r.appendChild(o),"function"==typeof t){const n=document.createElement("button");n.type="button",n.className="btn small",n.textContent=e.closeBtn,n.addEventListener("click",()=>t()),r.appendChild(n)}a.appendChild(r),a.appendChild(w(e.muteWhenFocused,c.muteWhenWindowFocused,e=>{c.muteWhenWindowFocused=e,g()})),a.appendChild(w(e.muteForActiveChar,c.muteForActiveChar,e=>{c.muteForActiveChar=e,g()})),a.appendChild(w(e.playOnFocusCmd,c.playOnFocusedChar,e=>{c.playOnFocusedChar=e,g()}));const i=document.createElement("h3");i.className="margin-bottom-s",i.style.marginTop="15px",i.textContent=e.hdrDefault,a.appendChild(i),a.appendChild(S(e.soundDefault,"soundDefault",e));const l=document.createElement("h3");return l.className="margin-bottom-s",l.style.marginTop="15px",l.textContent=e.hdrCategories,a.appendChild(l),a.appendChild(S(e.soundMention,"soundMention",e,!0)),a.appendChild(S(e.soundPrivate,"soundPrivate",e,!0)),a.appendChild(S(e.soundFocusChar,"soundFocusChar",e,!0)),a.appendChild(function(e){const t=document.createElement("div");t.style.marginTop="15px";const a=document.createElement("h3");a.className="margin-bottom-s",a.textContent=e.hdrCharSounds,t.appendChild(a);const r=document.createElement("div");r.className="margin-bottom-s",r.style.fontSize="12px",r.style.opacity="0.8",r.textContent=e.searchHint,t.appendChild(r);const o=document.createElement("div");o.style.display="grid",o.style.gridTemplateColumns="1fr auto",o.style.gap="5px",o.style.marginBottom="6px";const i=document.createElement("input");i.type="text",i.placeholder=e.charSearchPlaceholder,l=i,l.style.flex="1",l.style.padding="4px",l.style.background="#1d2233",l.style.color="#fff",l.style.border="1px solid #303753",l.style.borderRadius="4px",l.style.minWidth="0";var l;const s=document.createElement("button");s.className="btn small",s.textContent=e.searchBtn,o.appendChild(i),o.appendChild(s),t.appendChild(o);const d=document.createElement("div");d.style.marginBottom="10px",t.appendChild(d);const u=document.createElement("div");t.appendChild(u);const m=document.createElement("input");m.type="file",m.accept="audio/*",m.style.display="none",t.appendChild(m);let h=null;const y=async()=>{const t=i.value.trim();if(!t)return;d.innerHTML=`<div style="font-size:12px;opacity:.8;padding:4px 0;">${e.searching}</div>`;v(await x(t))};function v(t){d.innerHTML="",t.length?t.slice(0,8).forEach(t=>{const n=document.createElement("div");n.style.display="grid",n.style.gridTemplateColumns="28px 1fr auto",n.style.gap="6px",n.style.alignItems="center",n.style.padding="4px 0",n.style.borderBottom="1px solid rgba(255,255,255,0.05)";const a=document.createElement("div");if(a.style.width="28px",a.style.height="28px",a.style.borderRadius="4px",a.style.overflow="hidden",a.style.background="rgba(255,255,255,0.08)",t.avatar){const e=document.createElement("img");e.src=t.avatar,e.alt=t.name,e.style.width="100%",e.style.height="100%",e.style.objectFit="cover",a.appendChild(e)}const r=document.createElement("div");r.style.minWidth="0";const o=document.createElement("div");o.style.fontSize="12px",o.style.fontWeight="600",o.style.whiteSpace="nowrap",o.style.overflow="hidden",o.style.textOverflow="ellipsis",o.textContent=t.name;const i=document.createElement("div");i.style.fontSize="11px",i.style.opacity="0.75",i.style.whiteSpace="nowrap",i.style.overflow="hidden",i.style.textOverflow="ellipsis",i.textContent=t.charId?`ID ${t.charId}`:e.byName,r.appendChild(o),r.appendChild(i);const l=document.createElement("button");l.className="btn small",l.textContent=e.chooseBtn,l.onclick=()=>{h=t,m.click()},n.appendChild(a),n.appendChild(r),n.appendChild(l),d.appendChild(n)}):d.innerHTML=`<div style="font-size:12px;opacity:.8;padding:4px 0;">${e.noResults}</div>`}function C(){u.innerHTML="";const t=Object.keys(c.charSounds);t.length&&(t.sort((e,t)=>{const n=c.charSounds[e]?.name||"",a=c.charSounds[t]?.name||"";return n.localeCompare(a)}),t.forEach(t=>{const a=c.charSounds[t];if(!a)return;const r=document.createElement("div");r.className="audio-notifier-char-row",r.style.display="grid",r.style.gridTemplateColumns="28px 1fr auto",r.style.gap="6px",r.style.alignItems="center",r.style.background="rgba(0,0,0,0.18)",r.style.padding="5px",r.style.borderRadius="4px",r.style.marginBottom="5px",r.dataset.entryKey=t,r.dataset.charId=a.charId?String(a.charId):"",r.dataset.charName=a.name||"";const o=document.createElement("div");if(o.style.width="28px",o.style.height="28px",o.style.borderRadius="4px",o.style.overflow="hidden",o.style.background="rgba(255,255,255,0.08)",a.avatar){const e=document.createElement("img");e.className="audio-notifier-char-row-avatar",e.src=a.avatar,e.alt=a.name||t,e.style.width="100%",e.style.height="100%",e.style.objectFit="cover",o.appendChild(e)}const i=document.createElement("div");i.style.minWidth="0";const l=document.createElement("div");l.className="audio-notifier-char-row-title",l.style.fontSize="12px",l.style.fontWeight="600",l.style.whiteSpace="nowrap",l.style.overflow="hidden",l.style.textOverflow="ellipsis",l.textContent=a.name||t;const s=document.createElement("div");s.style.fontSize="11px",s.style.opacity="0.75",s.style.whiteSpace="nowrap",s.style.overflow="hidden",s.style.textOverflow="ellipsis",s.textContent=a.charId?`ID ${a.charId}`:e.byName;const d=document.createElement("label");d.className="checkbox",d.style.display="flex",d.style.alignItems="center",d.style.gap="4px",d.style.fontSize="11px",d.style.marginTop="2px";const m=document.createElement("input");m.type="checkbox",m.checked=!!a.alwaysPlay,m.addEventListener("change",e=>{a.alwaysPlay=e.target.checked,g()});const h=document.createElement("span");h.textContent=e.alwaysPlay,d.appendChild(m),d.appendChild(h),i.appendChild(l),i.appendChild(s),i.appendChild(d);const p=document.createElement("div");p.style.display="flex",p.style.gap="4px",p.style.alignItems="center";const f=document.createElement("input");f.type="file",f.accept="audio/*",f.style.display="none",f.addEventListener("change",t=>{const r=t.target.files&&t.target.files[0];if(!r)return;if(r.size>n)return alert(e.sizeWarning);const o=new FileReader;o.onloadend=()=>{a.sound=o.result,g(),C(),f.value=""},o.readAsDataURL(r)});const y=document.createElement("button");y.className="btn small",y.textContent=e.replaceBtn,y.onclick=()=>f.click();const v=document.createElement("button");v.className="btn small primary",v.textContent=e.testBtn,v.onclick=()=>z(a.sound||L("soundDefault"),!0);const b=document.createElement("button");b.className="btn small error",b.textContent=e.resetBtn,b.onclick=()=>{delete c.charSounds[t],g(),C()},p.appendChild(f),p.appendChild(y),p.appendChild(v),p.appendChild(b),r.appendChild(o),r.appendChild(i),r.appendChild(p),u.appendChild(r)}))}return s.addEventListener("click",y),i.addEventListener("keydown",e=>{"Enter"===e.key&&(e.preventDefault(),y())}),m.addEventListener("change",t=>{const a=t.target.files&&t.target.files[0];if(!a||!h)return;if(a.size>n)return alert(e.sizeWarning);const r=new FileReader;r.onloadend=()=>{const e=h,t=e.charId?f(e.charId):p(e.name);c.charSounds[t]={name:e.name,charId:e.charId||null,avatar:e.avatar||null,sound:r.result,alwaysPlay:!1},g(),h=null,m.value="",d.innerHTML="",i.value="",C()},r.readAsDataURL(a)}),C(),t}(e)),a}function w(e,t,n){const a=document.createElement("div");a.className="margin-bottom-m";const r=document.createElement("label");r.className="checkbox",r.style.display="flex",r.style.alignItems="center",r.style.cursor="pointer";const o=document.createElement("input");o.type="checkbox",o.checked=t,o.addEventListener("change",e=>n(e.target.checked));const i=document.createElement("span");return i.textContent=e,i.style.marginLeft="8px",r.appendChild(o),r.appendChild(i),a.appendChild(r),a}function S(e,t,a,r=!1){const o=document.createElement("div");o.className="margin-bottom-m",o.style.display="flex",o.style.alignItems="center",o.style.justifyContent="space-between",o.style.gap="5px";const i=document.createElement("span");i.style.flex="1",i.style.fontSize="14px";const l=()=>{const n=null!==c[t];i.innerHTML=`${e} [${n?"Custom":r?"Fallback":"System Default"}]`};l();const s=document.createElement("div");s.style.display="flex",s.style.gap="5px";const d=document.createElement("input");d.type="file",d.accept="audio/*",d.style.display="none";const u=document.createElement("button");u.className="btn small",u.textContent=a.uploadBtn,u.onclick=()=>d.click();const m=document.createElement("button");m.className="btn small primary",m.textContent=a.testBtn,m.onclick=()=>z(L(t),!0);const h=document.createElement("button");return h.className="btn small error",h.textContent=a.resetBtn,h.onclick=()=>{c[t]=null,g(),l()},d.addEventListener("change",e=>{const r=e.target.files&&e.target.files[0];if(!r)return;if(r.size>n)return alert(a.sizeWarning);const o=new FileReader;o.onloadend=()=>{c[t]=o.result,g(),l(),z(c[t],!0),d.value=""},o.readAsDataURL(r)}),s.appendChild(d),s.appendChild(u),s.appendChild(h),s.appendChild(m),o.appendChild(i),o.appendChild(s),o}async function x(e){const t=String(e||"").trim();if(!t)return[];const n=function(){const e=v("player")||window.player||window.muckletClient?.player||null,t=v("charsAwake")||window.charsAwake||window.muckletClient?.charsAwake||null;return e&&t?{player:e,charsAwake:t}:null}();if(!n)return console.debug("[AudioNotifier] SearchChar modules not available"),[];const{player:a,charsAwake:r}=n;let o=null;if(t.split(/\s+/).length>1&&a?.getPlayer)try{o=await a.getPlayer().call("getChar",{charName:t})}catch(e){(function(e){return!(!e||"core.charNotFound"!==e.code&&"core.charNotFound"!==e.errorCode&&"core.charNotFound"!==e.message)})(e)||console.debug("[AudioNotifier] getChar failed",e)}const i="function"==typeof a?.getActiveChar?a.getActiveChar():null,l="function"==typeof r?.getWatches?r.getWatches():null,c=l&&l.props?Object.keys(l.props).map(e=>l.props[e]?.char).filter(Boolean):[];return A([o?[o]:null,"function"==typeof a?.getChars?a.getChars():null,i?.inRoom?.chars||null,"function"==typeof r?.getCollection?r.getCollection():null,c]).filter(e=>e&&function(e,t){const n=k(I(e)),a=k(t).split(/\s+/).filter(Boolean);return!(!n||!a.length)&&a.every(e=>n.includes(e))}(e,t)).map(e=>N(e)).filter(Boolean).sort(function(e){const t=k(e);return(e,n)=>{const a=k(e.name||""),r=k(n.name||""),o=a===t?1:0,i=r===t?1:0;if(o!==i)return i-o;const l=a.startsWith(t)?1:0,c=r.startsWith(t)?1:0;if(l!==c)return c-l;const s=a.indexOf(t),d=r.indexOf(t),u=-1===s?9999:s,m=-1===d?9999:d;return u!==m?u-m:a.localeCompare(r)}}(t)).slice(0,10)}function A(e){const t=[],n=new Set;for(const a of e)for(const e of E(a)){if(!e)continue;const a=null!=e.id?String(e.id):null,r=I(e),o=a?`id:${a}`:`name:${k(r)}`;o&&!n.has(o)&&(n.add(o),t.push(e))}return t}function E(e){return e?Array.isArray(e)?e.filter(Boolean):"function"==typeof e.toArray?e.toArray().filter(Boolean):e.props&&"object"==typeof e.props?Object.values(e.props).filter(Boolean):e.models&&Array.isArray(e.models)?e.models.filter(Boolean):e.items&&Array.isArray(e.items)?e.items.filter(Boolean):[]:[]}function N(e){if(!e)return null;const t=`${e.name||""} ${e.surname||""}`.trim()||e.fullName||e.label||null;return t?{name:t,charId:null!=e.id?String(e.id):null,avatar:F(e)||null}:null}function I(e){return e&&(`${e.name||""} ${e.surname||""}`.trim()||e.fullName||e.label)||""}function k(e){return String(e||"").normalize("NFD").replace(/[\u0300-\u036f]/g,"").toLowerCase().replace(/\s+/g," ").trim()}function F(e){const t=[e&&e.avatar&&e.avatar.href,e&&e.avatar&&e.avatar.url,e&&e.avatar&&e.avatar.src,e&&e.icon&&e.icon.href,e&&e.icon&&e.icon.url,e&&e.icon&&e.icon.src,e&&e.avatarUrl,e&&e.iconUrl,e&&e.avatar,e&&e.icon,e&&e.image&&e.image.href,e&&e.image&&e.image.url,e&&e.imageUrl,e&&e.portrait&&e.portrait.href,e&&e.img&&e.img.href,e&&e.img&&e.img.url,e&&e.imgUrl].filter(e=>"string"==typeof e&&e.trim());for(const e of t){const t=T(e);if(t)return t}return null}function T(e){const t=String(e||"").trim();return t?/^https?:\/\/file\.wolfery\.com\/core\/char\/avatar\//i.test(t)?B(t):/^https?:\/\/file\.wolfery\.com\/core\/char\/img\//i.test(t)?B(t.replace(/\/core\/char\/img\//i,"/core/char/avatar/")):/^\/core\/char\/avatar\//i.test(t)?B("https://file.wolfery.com"+t):/^\/core\/char\/img\//i.test(t)?B("https://file.wolfery.com"+t.replace(/\/core\/char\/img\//i,"/core/char/avatar/")):/^[a-z0-9]+$/i.test(t)?`https://file.wolfery.com/core/char/avatar/${t}?thumb=l`:/\/char\/avatar\//i.test(t)?B(t):/\/char\/img\//i.test(t)?B(t.replace(/\/char\/img\//i,"/char/avatar/")):null:null}function B(e){const t=String(e||"").replace(/[?&]thumb=[^&]+/gi,"");return t+(t.includes("?")?"&thumb=l":"?thumb=l")}function L(t){return c[t]?c[t]:c.soundDefault?c.soundDefault:e}async function O(e){if(!e)return null;const t={name:String(e.name||"").trim(),charId:null!=e.charId?String(e.charId):null,avatar:e.avatar||null,sound:e.sound||null,alwaysPlay:!!e.alwaysPlay},n=await async function(e){const t=v("player"),n=v("charsAwake"),a=t&&"function"==typeof t.getActiveChar?t.getActiveChar():null,r=A(["function"==typeof t?.getChars?t.getChars():null,a?.inRoom?.chars||null,"function"==typeof n?.getCollection?n.getCollection():null]);if(e.charId){const t=r.find(t=>String(t?.id)===String(e.charId));if(t)return N(t)}if(e.name){const t=(await x(e.name)).find(t=>h(t.name)===h(e.name));if(t)return t}return null}(t);return n?{...t,name:n.name||t.name,charId:n.charId||t.charId,avatar:n.avatar||t.avatar}:t}function P(e){const t=e.target;if(!(t instanceof HTMLImageElement))return;if(!t.classList.contains("audio-notifier-char-row-avatar"))return;const n=t.closest(".audio-notifier-char-row");if(!(n instanceof HTMLElement))return;const a=n.dataset.entryKey||"";let r=a?c.charSounds[a]:null;r||(r=D(n)),r&&O(r).then(e=>{if(!e)return;const o=a||(r.charId?f(r.charId):p(r.name)),i=e.charId?f(e.charId):p(e.name||o);o!==i&&delete c.charSounds[o],c.charSounds[i]=e,g(),M(n,i,e),e.avatar&&t.src!==e.avatar&&(t.src=e.avatar)}).catch(e=>{console.debug("[AudioNotifier] Failed to refresh avatar after image error",e)})}function D(e){const t=e.dataset.charId||"",n=e.dataset.charName||"";if(t){const e=c.charSounds[f(t)];if(e)return e}if(n){const e=c.charSounds[p(n)];if(e)return e}return null}function W(){document.querySelectorAll(".audio-notifier-char-row").forEach(e=>{if(!(e instanceof HTMLElement))return;const t=e.dataset.entryKey||"",n=c.charSounds[t]||D(e);if(!n)return;M(e,n.charId?f(n.charId):p(n.name||t),n)})}function M(e,t,n){e.dataset.entryKey=t,e.dataset.charId=n.charId?String(n.charId):"",e.dataset.charName=n.name||"";const a=e.querySelector(".audio-notifier-char-row-title");a&&(a.textContent=n.name||t);const r=e.querySelector(".audio-notifier-char-row-avatar");r instanceof HTMLImageElement&&n.avatar&&(r.src=n.avatar,r.alt=n.name||t)}function z(t,n=!1){const r=Date.now();if(n||!(r-a<1e3)){n||(a=r);try{const n=new Audio(t||e);n.volume=.5,n.play().catch(e=>{console.error("[AudioNotifier] Playback failed. Browser blocking autoplay?",e)})}catch(e){console.error("[AudioNotifier] Invalid audio source",e)}}}document.addEventListener("click",function e(){new Audio("data:audio/wav;base64,UklGRigAAABXQVZFZm10IBIAAAABAAEARKwAAIhYAQACABAAAABkYXRhAgAAAAEA").play().catch(()=>{}),document.removeEventListener("click",e)},{once:!0}),window.addEventListener("beforeunload",function(){if(m&&"function"==typeof m.dispose)try{m.dispose()}catch(e){console.debug("[AudioNotifier] Failed to dispose custom UI",e)}m=null}),setTimeout(function e(){if(r++,void 0===window.app)return void(r<60&&setTimeout(e,500));const t=v("charLog");t?(o||(!function(){const e=navigator.language.startsWith("de");C({title:e?"Audio Notifier (Benutzerskript)":"Audio Notifier (Userscript)",tabTitle:e?"Audio Notifier öffnen":"Open Audio Notifier",muteWhenFocused:e?"Ton deaktivieren, wenn das Fenster fokussiert ist":"Disable sound when the window is focused",muteForActiveChar:e?"Ton für aktiven Charakter deaktivieren (Ignoriert bei Fokusverlust)":"Disable sound for active character (Ignored if window loses focus)",playOnFocusCmd:e?"Ton abspielen, wenn fokussierter Charakter schreibt (/focus)":"Play sound when a focused character writes (/focus)",hdrDefault:e?"1. Standard-Sound":"1. Default Sound",soundDefault:e?"Allgemeiner Ton":"Global Default Sound",hdrCategories:e?"2. Spezifische Kategorien (Optional)":"2. Specific Categories (Optional)",soundMention:e?"Erwähnungen":"Mentions",soundPrivate:e?"Private Nachrichten":"Private Messages",soundFocusChar:e?"Fokussierte Charaktere":"Focused characters",hdrCharSounds:e?"3. Sound pro Charakter":"3. Per-character sounds",charSearchPlaceholder:e?"Name suchen...":"Search name...",searchBtn:e?"Suchen":"Search",chooseBtn:"Sound",uploadBtn:e?"Wählen":"Browse",replaceBtn:e?"Neu":"Replace",testBtn:"▶️",resetBtn:"✖",closeBtn:e?"Schließen":"Close",sizeWarning:e?"Datei zu groß (max 1MB).":"File too large (max 1MB).",noResults:e?"Keine Treffer":"No results",searching:e?"Suche...":"Searching...",searchHint:e?"Suche per Name, speichere ID + Avatar.":"Search by name, store ID + avatar.",alwaysPlay:e?"Immer":"Always",byId:"ID",byName:"Name",noSound:e?"Kein Sound":"No sound"})}(),o=!0),i||(!function(e){e.addEventModifier({id:"userscriptSoundNotifier",sortOrder:50,callback:(e,t,n)=>{if(n.muted)return;if(e.char&&e.char.id===t.id)return;const a=document.hasFocus();if(c.muteWhenWindowFocused&&a)return;if(c.muteForActiveChar&&a){const e=v("player"),n=e&&"function"==typeof e.getActiveChar?e.getActiveChar():null;if(n&&n.id===t.id)return}let r=function(e){if(!e)return null;const t=null!=e.id?String(e.id):null;if(t){const e=f(t);if(c.charSounds[e])return e}const n=h(`${e.name||""} ${e.surname||""}`.trim()),a=h(e.name||"");return Object.keys(c.charSounds).find(e=>{const r=c.charSounds[e];if(!r)return!1;if(r.charId&&t&&String(r.charId)===t)return!0;const o=h(r.name||e.replace(/^name:/,""));return o&&(o===n||o===a)})||null}(e.char);r&&e.char&&(r=function(e,t){const n=c.charSounds[e];if(!n||!t)return e;const a=`${t.name||""} ${t.surname||""}`.trim()||n.name,r=null!=t.id?String(t.id):n.charId,o=F(t)||n.avatar;if(n.name=a,n.charId=r,n.avatar=o,r){const t=f(r);if(t!==e)return c.charSounds[t]={...n},delete c.charSounds[e],g(),t}return g(),e}(r,e.char));const o=r?c.charSounds[r]:null;let i=!1,l=null;if(o&&o.alwaysPlay&&o.sound&&function(e){return["say","pose","ooc","whisper","message","address"].includes(e.type)}(e)&&(i=!0,l=o.sound),!i&&n.mentioned&&(i=!0,l=L("soundMention")),!i&&function(e,t,n){return!!["whisper","message","address"].includes(e.type)&&(!!n.targeted||(!(!e.target||e.target.id!==t.id)||!!Array.isArray(e.targets)&&e.targets.some(e=>e&&e.id===t.id)))}(e,t,n)&&(i=!0,l=L("soundPrivate")),!i&&c.playOnFocusedChar&&e.char){const n=v("charFocus");n&&"function"==typeof n.hasFocus&&n.hasFocus(t.id,e.char.id)&&(i=!0,l=L("soundFocusChar"))}i&&o&&o.sound&&(l=o.sound),i&&z(l)}})}(t),i=!0),s||(s=!0,function(){const e=Object.entries(c.charSounds||{});if(!e.length)return;Promise.all(e.map(async([e,t])=>{const n=await O(t);return n?[e,n]:null})).then(e=>{let t=!1;for(const n of e){if(!n)continue;const[e,a]=n,r=a.charId?f(a.charId):p(a.name||e),o=JSON.stringify(c.charSounds[e]||null),i=JSON.stringify(a);e!==r&&(delete c.charSounds[e],t=!0),o==i&&c.charSounds[r]||(c.charSounds[r]=a,t=!0)}t&&g(),W()}).catch(e=>{console.debug("[AudioNotifier] Failed to refresh stored character metadata",e)})}()),d||(d=!0,document.addEventListener("error",P,!0),setTimeout(W,1200))):r<60&&setTimeout(e,500)},1e3);})();