Flashii Chat Userscripts
#9630
if you have tried to extend the chat in ways flashwave couldn't be assed to do, post your results here

here is a filesize detection script made for reemo
// ==UserScript==
// @name         Sockchat EEPROM File Sizes
// @description  adds things to eeprom uploads
// @author       szy
// @match        https://chat.flashii.net/
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    function xhr(url) { // fucking stolen from osk and then mutilated
        return new Promise((resolve, reject) => {
            const request = new XMLHttpRequest();
            request.open('GET', url, true);
            request.onload = () => {
                if (request.readyState === 4) {
                    const json = JSON.parse(request.responseText);
                    if (json) {
                        resolve(json);
                    } else {
                        reject(json);
                    }
                }
            };
            request.onerror = () => {
                console.error(`Request ${url} returned onerror`);
                reject();
            };
            request.send();
        });
    }
    function sizeOf(bytes) { // https://stackoverflow.com/a/28120564
        if (bytes == 0) { return "0B"; }
        var e = Math.floor(Math.log(bytes) / Math.log(1024));
        return (bytes/Math.pow(1024, e)).toFixed(2)+'\u200b'+'\u200bKMGTP'.charAt(e)+'B';
    }


    window.addEventListener('umi:ui:message_add', function(ev) {
        var message = ev.detail.element;
        var embeds = message.querySelectorAll('span[title^="//i.flashii.net/"]');
        for (var i = 0; i < embeds.length; i++) {
            const embed = embeds[i];
            try {
                let el = document.createElement('span');
                el.classList.add('szy_eeprom_data');
                el.style.color='#AFF';
                el.textContent=' [...]';
                embed.appendChild(el);
                let href = embed.firstChild.firstChild.href;
                let eeprom = href.replace(/i.flashii.net\/([A-Za-z0-9_-]+)/g, "eeprom.flashii.net/uploads/$1.json");
                xhr(eeprom).then(resp=>{
                    var size = +resp['size'];
                    el.textContent = `[${sizeOf(size)}]`;
                    el.style.color = '#CDD';
                }).catch(err=>{
                    console.error(err);
                    el.textContent='[ERR]';
                    el.style.color='#FAA';
                });
            } catch (ex) {
                console.error(ex);
            }
        }
    });
})();
#9658
thank u suzy i will install monkeyscriptmonkeyscript just to use this female
//i.fii.moe/EEtDbYEICrUbLOFjkwKVQ7OUJWJR-ejl
#11398
i modded szy's script a bit to work with non-eeprom files too, using flash's mii thing which i never heard of until now but it's cute
it also shows the resolution and length if applicable, and for eeprom files, you can hover over the tag to see its original filename

// ==UserScript==
// @name         Sockchat Embed File Sizes
// @description  adds things to embeds
// @author       szy, osk
// @match        https://chat.flashii.net/
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    function xhr(url) {
        return new Promise((resolve, reject) => {
            const request = new XMLHttpRequest();
            request.open('GET', url, true);
            request.onload = () => {
                if (request.readyState === 4) {
                    const json = JSON.parse(request.responseText);
                    if (json) {
                        resolve(json);
                    } else {
                        reject(json);
                    }
                }
            };
            request.onerror = () => {
                console.error(`Request ${url} returned onerror`);
                reject();
            };
            request.send();
        });
    }
    function sizeOf(bytes) { // https://stackoverflow.com/a/28120564
        if (bytes === 0) { return '0B'; }
        const e = Math.floor(Math.log(bytes) / Math.log(1024));
        return (bytes/Math.pow(1024, e)).toFixed(e === 0 ? 0 : 2)+'\u200b'+'\u200bKMGTP'.charAt(e)+'B';
    }
    function ParseMS(oms) {
        const roms = Math.round(oms);

        const ms = roms % 1000;
        const s = Math.floor(roms / 1000) % 60;
        const m = Math.floor(roms / 60000);

        return {
            ms: ms.toString().padStart(3, '0'),
            s: s.toString().padStart(2, '0'),
            m: m.toString()
        };
    }


    window.addEventListener('umi:ui:message_add', function(ev) {
        const embedButtons = ev.detail.element.querySelectorAll('a[onclick^="Umi.Parser.SockChatBBcode.Embed"]');
      
        for (const embedButton of embedButtons) {
            const embed = embedButton.parentElement;
            let link = embed.getAttribute('title');
            if (link.startsWith('//')) { link = `https:${ link }`; } // Mii 500s on //domain.name URIs
          
            try {
                const el = document.createElement('span');
                el.classList.add('eeprom_extended_data');
                el.style.color = '#AFF';
                el.textContent = ' [...]';
                embed.appendChild(el);
              
                xhr(`https://uiharu.flashii.net/metadata?url=${ encodeURIComponent(link) }`).then((resp) => {
                    if (resp.errorMessage) {
                        throw resp.errorMessage;
                    }
                  
                    if (!resp.media) {
                        if (resp.content_type) {
                            throw `Not a media file; detected as ${ resp.content_type.string }`;
                        }
                        throw 'Not a media file; content type unknown';
                    }
                  
                    let size = resp.media.size || 0;
                  
                    if (resp.eeprom_file_info) {
                        size = resp.eeprom_file_info.size;
                        el.title = resp.eeprom_file_info.name;
                    }
                  
                    el.style.color = '#A0F5B8';
                    if (size >= 128*1024) { el.style.color = '#BAE9C7'; }
                    if (size >= 512*1024) { el.style.color = '#CDD'; }
                    if (size >= 1024*1024) { el.style.color = '#CCA1F4'; }
                    if (size >= 4*1024*1024) { el.style.color = '#DB5FF1'; }
                    if (size >= 10*1024*1024) { el.style.color = '#EC32A4'; }
                  
                    let elText = sizeOf(size);
                  
                    if (resp.is_image && resp.width && resp.height) {
                        elText += `・${ resp.width }x${ resp.height }`;
                    }
                    if (resp.is_video && resp.width && resp.height && resp.media.duration) {
                        const p = ParseMS(resp.media.duration * 1000);
                        elText += `・${ resp.width }x${ resp.height }・${ p.m }:${ p.s }<span style="font-size: 70%">.${ p.ms }</span>`;
                    }
                    if (resp.is_audio && resp.media.duration) {
                        const p = ParseMS(resp.media.duration * 1000);
                        elText += `・${ p.m }:${ p.s }<span style="font-size: 70%">.${ p.ms }</span>`;
                    }
                  
                    el.innerHTML = ` [${ elText }]`;
                }).catch((err) => {
                    console.error(err);
                    el.textContent = '[ERR]';
                    el.style.color = '#FAA';
                    el.title = err || 'Network error while requesting metadata';
                })
            } catch (ex) {
                console.error(ex)
            }
        }
    });
})();
https://kagari.moe/outer_assets/flashii/signature.png https://kagari.moe/outer_assets/flashii/signature-sites.pnghttps://kagari.moe/outer_assets/flashii/signature-osksh.pnghttps://kagari.moe/outer_assets/flashii/signature-kagarimoe.pnghttps://kagari.moe/outer_assets/flashii/signature-social.pnghttps://kagari.moe/outer_assets/flashii/signature-twitter.pnghttps://kagari.moe/outer_assets/flashii/signature-github.pnghttps://kagari.moe/outer_assets/flashii/signature-blog.png
#12957
oh yeah ive totally had this one laying around too
double shiftclick to delete a message

// ==UserScript==
// @name        Flashii Chat Delete 
// @namespace   Violentmonkey Scripts
// @match       https://chat.flashii.net/
// @grant       none
// @version     1.0
// @author      osk
// @description 2/25/2021, 11:07:26 PM
// ==/UserScript==
document.addEventListener('click', function(e) {
  if (!e.shiftKey) { return; }
  if (e.target.closest('.message')) {
    if (e.target.closest('.message').classList.contains('deleting')) {
      Umi.Server.SendMessage(`/delmsg ${ e.target.closest('.message').id.replace('message-', '') }`);
    } else {
      e.target.closest('.message').classList.add('deleting');
      e.target.closest('.message').setAttribute('style', 'background-color: #400;');
    }	  
  }
});
https://kagari.moe/outer_assets/flashii/signature.png https://kagari.moe/outer_assets/flashii/signature-sites.pnghttps://kagari.moe/outer_assets/flashii/signature-osksh.pnghttps://kagari.moe/outer_assets/flashii/signature-kagarimoe.pnghttps://kagari.moe/outer_assets/flashii/signature-social.pnghttps://kagari.moe/outer_assets/flashii/signature-twitter.pnghttps://kagari.moe/outer_assets/flashii/signature-github.pnghttps://kagari.moe/outer_assets/flashii/signature-blog.png
#12958

you can also replace the Umi.Server.SendMessage with Umi.UI.View.SetText if you dont want it to delete instantly, this will just fill in the command to do so but not send it

this makes the script break a little (since the red mark never goes away) and is redundant with how it requires two clicks anyway but i like it more that way

#13176

I'll probably start making some large changes to the chat code sometime soon so I'd like to know what kinda methods scripts hook into so I can keep some kind of compatiblity with them in mind.

The monolithic Umi.Protocol.SockLegacy.Protocol.Instance.SendMessage is already operating on backwards compat, and Umi.Server.SendMessage too technically since I changed the casing.

With this I'd also like to fully push through the namespace name change from Umi to Mami so it's in line with the current name of the project (third name change so far!), backwards compatibility shit would obviously still use the Umi namespace because it'd be in vain otherwise.

I have the following APIs already listed:

  • window.addEventListener(’umi:connect’, handler(event))
  • window.addEventListener(’umi:message_add’, handler(event: { detail: messageInfo }))
  • window.addEventListener(’umi:ui:message_add’, handler(event: { detail: { element: messageElement, message: messageInfo } }))
  • Umi.Server.SendMessage(text)
  • Umi.Protocol.SockLegacy.Protocol.Instance.SendMessage(text)
  • Umi.Parser.SockChatBBcode.EmbedStub()
  • Umi.UI.View.SetText(text)
  • Umi.UI.Menus.Add(baseId, title, initiallyHidden)
  • Umi.UI.Menus.Get(baseId, icon)

There's also querySelectorAll(&#039;a[onclick^=&quot;Umi.Parser.SockChatBBcode.Embed&quot;]&#039;) which is used in the file size script, but that functionality will be built in pretty much at the same time as the HTML that this tries to pick up on would be removed. Though in the mean time, that's where EmbedStub comes in. Let me know if any you use are missing and I'll see if they're within reason. I'll keep the list in this post updated.

https://sig.flash.moe/signature.png
#13434
For users of the Pictochat/Nintendo DS soundpack, a while ago I noticed that the sound for incoming and outgoing messages had been swapped and I fixed it. If this fucks up your mental, here's the fix for You.

// ==UserScript==
// @name     Unfix Pictochat sounds
// @version  2024021001
// @grant    none
// @match    https://chat.flashii.net/
// ==/UserScript==

window.addEventListener('umi:connect', function() {
    const picto = unsafeWindow.mami.sound.packs.get('nds');
    picto.setEventSound('incoming', 'nds:server');
    picto.setEventSound('server', 'nds:incoming');
});


Update I swapped the wrong ones because I don't use the pack updated the script oops thanks osk !!!!
https://sig.flash.moe/signature.png
#13437

thank you i can finally go back

https://cockdickball.in/media/button.gif
#13438
edited osks deletion script to have an automatic timeout to the deletion dialog.


// ==UserScript==
// @name        Flashii Chat Delete 
// @namespace   Violentmonkey Scripts
// @match       https://chat.flashii.net/
// @grant       none
// @version     1.0
// @author      osk
// @author      geb
// @description 2/25/2021, 11:07:26 PM 
// @description 19:38 21/10/2022 added a timeout to the message deletion dialog -geb
// ==/UserScript==



document.addEventListener('click', function(e) {
  function clearTarget() {
    e.target.closest('.message').setAttribute('style', 'transition: background-color 250ms ease-out')
    e.target.closest('.message').classList.remove('deleting')
  }
  if (!e.shiftKey) { return; }
  if (e.target.closest('.message')) {
    if (e.target.closest('.message').classList.contains('deleting')) {
      Umi.Server.SendMessage(`/delmsg ${ e.target.closest('.message').id.replace('message-', '') }`);
    } else {
      e.target.closest('.message').classList.add('deleting');
      e.target.closest('.message').setAttribute('style', 'background-color: #400; transition: background-color 250ms ease-out');
      setTimeout(() => {  clearTarget() }, 5000);
    }	  
  }
});
https://cockdickball.in/media/button.gif
#13823
shitty autoembed
// ==UserScript==
// @name Sockchat Autoembed
// @version 0.15
// @description Autoembed stuff that has an embed button
// @author saikuru0
// @match https://chat.flashii.net/
// @grant none
// ==/UserScript==

const observer = new MutationObserver(mutations => {
  mutations.forEach(mutation => {
    mutation.addedNodes.forEach(node => {
      clickEmbed(node);
    });
  });
});

observer.observe(document, { childList: true, subtree: true });

function clickEmbed(targetDiv) {
  const links = targetDiv.querySelectorAll("div.message__container>div.message__text>span>a.markup__link");
  links.forEach(link => {
    if (link.textContent == "Embed") {
      link.click();
    }
  });
}
https://saikuru.net/sig