Wolfery Audio Notifier

Plays a sound when a mention occurs, or when you are whispered, directly messaged, or addressed.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         Wolfery Audio Notifier
// @name:de      Wolfery Audio Benachrichtigungen
// @namespace    https://forum.wolfery.com/u/felinex/
// @version      1.5
// @description  Plays a sound when a mention occurs, or when you are whispered, directly messaged, or addressed.
// @description:de Spielt einen Sound ab, wenn eine Erwähnung (Mention) auftaucht, man angeflüstert, direkt angeschrieben (message) oder adressiert wird.
// @icon         https://static.f-list.net/images/eicon/wolfery.png
// @license      All Rights Reserved
// @author       Felinex Gloomfort
// @match        https://wolfery.com/*
// @match        https://test.wolfery.com/*
// @match        https://*.mucklet.com/*
// @icon
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    console.log("[AudioNotifier] Script loaded.");

    const SOUND_URL = 'https://actions.google.com/sounds/v1/alarms/beep_short.ogg';
    const audio = new Audio(SOUND_URL);
    audio.volume = 0.5;

    let lastPlay = 0;
    let initAttempts = 0;

    function init() {
        initAttempts++;
        if (typeof window.app === 'undefined') {
            if (initAttempts < 60) {
                setTimeout(init, 500);
            } else {
                console.error("[AudioNotifier] ERROR: window.app was not found.");
            }
            return;
        }

        console.log(`[AudioNotifier] window.app found! Trying to load module 'charLog' (Attempt ${initAttempts})...`);

        // Using getModule to fetch the module once the client initialized it.
        // This avoids the "App.require may only be called from an AppModule's constructor" error.
        const charLog = window.app.getModule('charLog');

        if (!charLog) {
            // Module is not ready yet, keep waiting
            if (initAttempts < 60) {
                setTimeout(init, 500);
            } else {
                console.error("[AudioNotifier] ERROR: 'charLog' module could not be loaded even after 30 seconds.");
            }
            return;
        }

        console.log("[AudioNotifier] Module 'charLog' successfully loaded. Adding EventModifier...");

        try {
            charLog.addEventModifier({
                id: 'userscriptSoundNotifier',
                sortOrder: 100,
                callback: (ev, ctrl, mod) => {

                    console.log(`[AudioNotifier] Event received: ${ev.type}`, { event: ev, mod: mod, ctrlId: ctrl.id });

                    // Exclude muted events and own character's actions
                    if (mod.muted) return;
                    if (ev.char && ev.char.id === ctrl.id) return;

                    // 1. Check for mentions (Set by the system when highlight triggers match)
                    const isMention = !!mod.mentioned;

                    // 2. Target check (Is the character the target of a whisper/message/address?)
                    // We manually check the target attributes of the original event
                    let isTargetedToMe = false;
                    const isDirectType = ['whisper', 'message', 'address'].includes(ev.type);

                    if (isDirectType) {
                        // Either it is explicitly in the mod object (set by previous modifiers)
                        if (mod.targeted) {
                            isTargetedToMe = true;
                        }
                        // Or we look for the character directly in the event targets
                        else if (ev.target && ev.target.id === ctrl.id) {
                            isTargetedToMe = true;
                        }
                        else if (ev.targets && Array.isArray(ev.targets)) {
                            // For group messages (multiple targets)
                            if (ev.targets.some(t => t.id === ctrl.id)) {
                                isTargetedToMe = true;
                            }
                        }
                    }

                    console.log(`[AudioNotifier] Evaluation: Mention=${isMention}, Targeted=${isTargetedToMe}`);

                    // Trigger sound
                    if (isMention || isTargetedToMe) {
                        playSound();
                    }
                }
            });
            console.log("[AudioNotifier] EventModifier successfully registered!");
        } catch (err) {
            console.error("[AudioNotifier] ERROR registering EventModifier:", err);
        }
    }

    function playSound() {
        const now = Date.now();
        if (now - lastPlay < 1000) return; // Spam protection / throttling

        lastPlay = now;

        audio.play().then(() => {
            console.log("[AudioNotifier] Sound played!");
        }).catch(e => {
            console.error("[AudioNotifier] ERROR playing sound. Browser blocking autoplay? Click anywhere on the page.", e);
        });
    }

    // Catch first click to unlock browser autoplay restrictions
    document.addEventListener('click', function unlockAudio() {
        audio.play().then(() => { audio.pause(); audio.currentTime = 0; }).catch(() => {});
        document.removeEventListener('click', unlockAudio);
    }, { once: true });

    // Wait briefly for the DOM loading process to begin before starting
    setTimeout(init, 1000);
})();