JustPaste
HomeCategoriesAboutDonateContactTerms of UsePrivacy Policy
JustPaste

Free online notepad — write and share instantly

Navigate

  • Home
  • Timeline
  • Categories

Info

  • About
  • Donate
  • Contact

Legal

  • Terms of Use
  • Privacy Policy

© 2026 JustPaste.app. All rights reserved.

Made with ♥ by JustPaste

Updated Sharty Steganography Decoder | JustPaste.app
27 days ago13 views
👨‍💻Programming

Updated Sharty Steganography Decoder

// ==UserScript==
// @name         Sharty Steganography Decoder
// @namespace    janny - made by 4737, i love you bro
// @version      1.3
// @description  Adds steganography decoding functionality to the sharty file approval queue and posts
// @author       grok + claude
// @match        https://soyjak.st/*
// @match        https://www.soyjak.st/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const $ = window.jQuery || window.$;

    // Shared with the modal opened from thread view, so both the
    // file-approval scanner and the per-post button agree on which
    // extensions are worth scanning.
    const SUPPORTED_EXTENSIONS = ['.png', '.jpeg', '.jpg', '.webp'];

    function hasSupportedExtension(url) {
        const lower = url.toLowerCase();
        return SUPPORTED_EXTENSIONS.some(ext => lower.endsWith(ext));
    }

    // ---------------------------------------------------------------------
    // Shared canvas helpers (same logic previously duplicated inline in
    // okaygoyim's slider/decode handlers, now used by both the file
    // approval scanner AND the new in-thread popup modal). Because these
    // always load from soyjak.st itself (same-origin), there's no CORS
    // issue here, unlike the external gen-statis.github.io decoder page.
    // ---------------------------------------------------------------------

    function renderStegPreview(imgEl, sourceUrl, bits) {
        try {
            if (bits === 1) {
                imgEl.src = imgEl.dataset.originalSrc || imgEl.src;
                return;
            }
            const canvas = document.createElement("canvas");
            const ctx = canvas.getContext("2d");
            const img = new Image();
            img.crossOrigin = "Anonymous";
            img.onload = function() {
                try {
                    canvas.width = img.width;
                    canvas.height = img.height;
                    ctx.drawImage(img, 0, 0);
                    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                    doUnhideImage(imageData, bits - 1);
                    ctx.putImageData(imageData, 0, 0);
                    imgEl.src = canvas.toDataURL();
                } catch (err) {
                    console.error('Error processing image for steganography:', err);
                }
            };
            img.onerror = function() {
                console.error('Failed to load image for steganography:', sourceUrl);
            };
            img.src = sourceUrl;
        } catch (err) {
            console.error('Error in renderStegPreview:', err);
        }
    }

    function decodeStegMessage(sourceUrl, onDone) {
        try {
            const canvas = document.createElement("canvas");
            const ctx = canvas.getContext("2d");
            const img = new Image();
            img.crossOrigin = "Anonymous";
            img.onload = function() {
                try {
                    canvas.width = img.width;
                    canvas.height = img.height;
                    ctx.drawImage(img, 0, 0);
                    const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                    const binaryMessage = [];
                    const pixel = imageData.data;
                    for (let i = 0, n = pixel.length; i < n; i += 4) {
                        for (let offset = 0; offset < 3; offset++) {
                            binaryMessage.push(pixel[i + offset] % 2);
                        }
                    }
                    let output = "";
                    for (let i = 0; i < binaryMessage.length; i += 8) {
                        if (i + 7 >= binaryMessage.length) break;
                        let c = 0;
                        for (let j = 0; j < 8; j++) {
                            c <<= 1;
                            c |= binaryMessage[i + j];
                        }
                        output += String.fromCharCode(c);
                    }
                    onDone(null, output);
                } catch (err) {
                    onDone(err);
                }
            };
            img.onerror = function() {
                onDone(new Error('Failed to load image: ' + sourceUrl));
            };
            img.src = sourceUrl;
        } catch (err) {
            onDone(err);
        }
    }

    function doUnhideImage(stegdata, bits) {
        try {
            const stegpix = stegdata.data;
            const w = stegdata.width;
            const h = stegdata.height;

            for (let y = 0; y < h; y++) {
                const stegy = y * w;
                for (let x = 0; x < w; x++) {
                    const stegidx = 4 * (stegy + x);
                    // red
                    stegpix[stegidx] = (stegpix[stegidx] << (8 - bits)) & 0xff;
                    // green
                    stegpix[stegidx + 1] = (stegpix[stegidx + 1] << (8 - bits)) & 0xff;
                    // blue
                    stegpix[stegidx + 2] = (stegpix[stegidx + 2] << (8 - bits)) & 0xff;
                }
            }
        } catch (err) {
            console.error('Error in doUnhideImage function:', err);
        }
    }

    // ---------------------------------------------------------------------
    // File approval queue scanner (unchanged behavior, just now calling
    // the shared helpers above instead of duplicated inline code)
    // ---------------------------------------------------------------------

    function okaygoyim(scope) {
        try {
            $(scope).find(".file-approval-item").each((i, item) => {
                const file = $(item).find(".file-info")[0];
                const thumbImg = $(item).find(".file-thumbnail img")[0];
                const sourceLink = $(item).find("a[target='_blank']")[0];

                if (!file || !thumbImg || !sourceLink) {
                    console.debug('Skipping item due to missing elements:', { file, thumbImg, sourceLink });
                    return;
                }

                const sourceUrl = sourceLink.href;
                if (!hasSupportedExtension(sourceUrl)) {
                    console.debug('Skipping item due to unsupported extension:', sourceUrl);
                    return;
                }

                const existingP = $(file).find("p").filter((i, el) => $(el).text() === "Steganography Scan:").get(0);
                const existingSlideContainer = file.querySelector(".slidecontainer");
                const existingFlexDiv = file.querySelector("div[style*='flex']");

                if (existingP && existingSlideContainer && existingFlexDiv) {
                    console.debug('Skipping item: elements already exist');
                    return;
                }

                const p = existingP || document.createElement("p");
                if (!existingP) p.textContent = "Steganography Scan:";

                const slideContainer = existingSlideContainer || document.createElement("div");
                if (!existingSlideContainer) slideContainer.className = "slidecontainer";

                const rangeInput = slideContainer.querySelector(".slider") || document.createElement("input");
                if (!slideContainer.querySelector(".slider")) {
                    rangeInput.type = "range";
                    rangeInput.min = "1";
                    rangeInput.max = "8";
                    rangeInput.value = "1";
                    rangeInput.className = "slider";
                    rangeInput.style.width = "99%";
                    slideContainer.appendChild(rangeInput);
                }

                const flexDiv = existingFlexDiv || document.createElement("div");
                if (!existingFlexDiv) {
                    flexDiv.style.display = "flex";
                    flexDiv.style.justifyContent = "center";
                }

                const decodeButton = flexDiv.querySelector(".decode-button") || document.createElement("input");
                if (!flexDiv.querySelector(".decode-button")) {
                    decodeButton.className = "decode-button";
                    decodeButton.type = "button";
                    decodeButton.value = "Decode";
                    decodeButton.disabled = false;
                    flexDiv.appendChild(decodeButton);
                }

                if (!existingP) file.appendChild(p);
                if (!existingSlideContainer) file.appendChild(slideContainer);
                if (!existingFlexDiv) file.appendChild(flexDiv);

                if (!thumbImg.dataset.originalSrc) {
                    thumbImg.dataset.originalSrc = thumbImg.src;
                }

                $(rangeInput).off("input").on("input", function() {
                    const bits = parseInt(this.value);
                    renderStegPreview(thumbImg, sourceLink.href, bits);
                });

                $(decodeButton).off("click").on("click", function() {
                    decodeButton.value = "Copying...";
                    decodeButton.disabled = true;
                    decodeStegMessage(sourceLink.href, (err, output) => {
                        decodeButton.value = "Decode";
                        decodeButton.disabled = false;
                        if (err) {
                            console.error('Error decoding image:', err);
                            return;
                        }
                        navigator.clipboard.writeText(output).then(() => {
                            alert("Decoded message copied to clipboard.");
                        }).catch(clipErr => {
                            alert("Failed to copy decoded message to clipboard.");
                            console.error('Clipboard error:', clipErr);
                        });
                    });
                });

                addStegLink(item);
            });
        } catch (err) {
            console.error('Error in okaygoyim function:', err);
        }
    }

    // ---------------------------------------------------------------------
    // In-thread popup modal (new) — replaces the old link out to the
    // external gen-statis.github.io page, which hit CORS errors because
    // it had to fetch a cross-origin image into a canvas. This modal runs
    // on soyjak.st itself, so the image fetch is same-origin and works
    // exactly like the file-approval slider does.
    // ---------------------------------------------------------------------

    let modalEl = null;

    function ensureModal() {
        if (modalEl) return modalEl;

        if (!$("#steg-modal-styles").length) {
            $("<style>", { id: "steg-modal-styles" }).text(`
                .steg-modal-overlay {
                    position: fixed;
                    top: 0; left: 0; right: 0; bottom: 0;
                    background: rgba(0, 0, 0, 0.6);
                    z-index: 99999;
                    display: none;
                    align-items: center;
                    justify-content: center;
                }
                .steg-modal-overlay.open { display: flex; }
                .steg-modal-box {
                    background: #fff;
                    border-radius: 6px;
                    padding: 12px;
                    max-width: 90vw;
                    max-height: 90vh;
                    overflow: auto;
                    box-shadow: 0 4px 24px rgba(0,0,0,0.4);
                    text-align: center;
                }
                .steg-modal-box h3 {
                    margin: 0 0 8px 0;
                    font-size: 15px;
                    display: flex;
                    justify-content: space-between;
                    align-items: center;
                    gap: 12px;
                }
                .steg-modal-close {
                    cursor: pointer;
                    font-weight: bold;
                    border: none;
                    background: none;
                    font-size: 18px;
                    line-height: 1;
                }
                .steg-modal-img {
                    max-width: 70vw;
                    max-height: 60vh;
                    display: block;
                    margin: 0 auto 8px auto;
                }
                .steg-modal-status {
                    font-size: 12px;
                    color: #666;
                    min-height: 16px;
                }
            `).appendTo("head");
        }

        const overlay = $("<div>", { class: "steg-modal-overlay" });
        const box = $("<div>", { class: "steg-modal-box" });
        const header = $("<h3>").append(
            $("<span>").text("Steganography Scan"),
            $("<button>", { class: "steg-modal-close", type: "button" }).text("×")
        );
        const img = $("<img>", { class: "steg-modal-img" });
        const slideContainer = $("<div>", { class: "slidecontainer" });
        const rangeInput = $("<input>", { type: "range", min: "1", max: "8", value: "1", class: "slider" }).css("width", "99%");
        slideContainer.append(rangeInput);
        const flexDiv = $("<div>").css({ display: "flex", justifyContent: "center", gap: "8px" });
        const decodeButton = $("<input>", { type: "button", value: "Decode", class: "decode-button" });
        flexDiv.append(decodeButton);
        const status = $("<div>", { class: "steg-modal-status" });

        box.append(header, img, slideContainer, flexDiv, status);
        overlay.append(box);
        $("body").append(overlay);

        function close() {
            overlay.removeClass("open");
        }

        overlay.on("click", function(e) {
            if (e.target === overlay[0]) close();
        });
        header.find(".steg-modal-close").on("click", close);
        $(document).on("keydown", function(e) {
            if (e.key === "Escape") close();
        });

        modalEl = { overlay, img: img[0], rangeInput: rangeInput[0], decodeButton: decodeButton[0], status: status[0], close };
        return modalEl;
    }

    function openStegModal(sourceUrl) {
        try {
            const modal = ensureModal();

            modal.img.dataset.originalSrc = sourceUrl;
            modal.img.src = sourceUrl;
            modal.rangeInput.value = "1";
            modal.status.textContent = "";

            $(modal.rangeInput).off("input").on("input", function() {
                const bits = parseInt(this.value);
                renderStegPreview(modal.img, sourceUrl, bits);
            });

            $(modal.decodeButton).off("click").on("click", function() {
                modal.decodeButton.value = "Copying...";
                modal.decodeButton.disabled = true;
                modal.status.textContent = "Decoding...";
                decodeStegMessage(sourceUrl, (err, output) => {
                    modal.decodeButton.value = "Decode";
                    modal.decodeButton.disabled = false;
                    if (err) {
                        modal.status.textContent = "Failed to decode image.";
                        console.error('Error decoding image:', err);
                        return;
                    }
                    navigator.clipboard.writeText(output).then(() => {
                        modal.status.textContent = "Copied to clipboard.";
                    }).catch(clipErr => {
                        modal.status.textContent = "Failed to copy to clipboard.";
                        console.error('Clipboard error:', clipErr);
                    });
                });
            });

            modal.overlay.addClass("open");
        } catch (err) {
            console.error('Error in openStegModal:', err);
        }
    }

    function addStegLink(item) {
        try {
            const url = window.location.href;
            if (!url.match(/^https:\/\/(www\.)?soyjak\.st\/mod\.php\?\/.*/)) {
                return;
            }
            const fileInfoP = $(item).find(".fileinfo");
            if (fileInfoP.length) {
                fileInfoP.each((i, p) => {
                    if ($(p).find("a.steg-link").length) {
                        return;
                    }

                    const downloadLink = $(p).find("a.filename-download-link");
                    const imgOpsLink = $(p).find("a[target='_blank']");
                    if (downloadLink.length && imgOpsLink.length) {
                        const imageSource = downloadLink.attr("href");
                        if (!imageSource || !hasSupportedExtension(imageSource)) {
                            console.debug('Skipping steg link due to unsupported extension:', imageSource);
                            return;
                        }

                        const baseUrl = url.startsWith("https://www.soyjak.st") ? "https://www.soyjak.st" : "https://soyjak.st";
                        const fullImageUrl = imageSource.startsWith("http") ? imageSource : `${baseUrl}${imageSource}`;

                        const stegLink = $("<a>")
                            .addClass("steg-link")
                            .attr("href", "#")
                            .text("[Steganography]")
                            .css("margin-left", "5px")
                            .on("click", function(e) {
                                e.preventDefault();
                                openStegModal(fullImageUrl);
                            });
                        imgOpsLink.after(stegLink);
                    }
                });
            }
        } catch (err) {
            console.error('Error in addStegLink function:', err);
        }
    }

    try {
        addStegLink(document);

        $(document).on("new_post", (e, post) => {
            try {
                addStegLink(post);
            } catch (err) {
                console.error('Error in new_post handler:', err);
            }
        });

        const url = window.location.href;
        if (url.match(/^https:\/\/(www\.)?soyjak\.st\/mod\.php\?\/file_approval$/)) {
            okaygoyim(document);
            // Reduce polling frequency to 500ms to lower performance impact
            window.setInterval(() => {
                try {
                    okaygoyim(document);
                } catch (err) {
                    console.error('Error in interval handler:', err);
                }
            }, 500);
        }
    } catch (err) {
        console.error('Error in main script execution:', err);
    }
})();
← Back to timeline