DiscordVMSG

Voice Message using Discord liek snapchat. Hold the microphone button next to the chat bar to create a voice chat.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         DiscordVMSG
// @namespace    None
// @version      0.2
// @language     English
// @description  Voice Message using Discord liek snapchat. Hold the microphone button next to the chat bar to create a voice chat.
// @author       xPythh
// @match        https://discord.com/channels/*
// @grant        none
// ==/UserScript==
    var checkIfReadyInterval;
    var microphonePicture;
    var startedAt;
    var isTalking = false;

    var leftchannel = [];
    var rightchannel = [];
    var recorder = null;
    var recordingLength = 0;
    var volume = null;
    var mediaStream = null;
    var sampleRate = 48000; // voice pitch need to be edited according to microphone, increase if its too dark, or decrease it if too hight
    var context = null;
    var voiceBlob;

    var localStorage;
    // Chat Bar is dynamic and "reinitialised" between each browsing page, so we need to constantly insert it, if doesn't exist
    function attachMicrophoneButton()
    {
        if (!document.getElementById("chatTalkButton"))
        {
            var mainDiv = document.createElement('div');// Main Button Div
            mainDiv.id = "chatTalkButton";
            mainDiv.classList.add('button-3AYNKb');
            mainDiv.classList.add('button-318s1X');

            var mainButton = document.createElement('button');// Button itself
            mainButton.type = "button";
            mainButton.classList.add('button-14-BFJ');
            mainButton.classList.add('enabled-2cQ-u7');
            mainButton.classList.add('lookBlank-3eh9lL');

            var secondDiv = document.createElement('div');// Div containing the Logo
            secondDiv.classList.add('contents-18-Yxp');

            var buttonLogo = new Image(); // Microphone Logo
            var cantTalk = document.querySelector(".channelTextAreaDisabled-8rmlrp");
            var canSendFiles = document.querySelector(".attachButton-2WznTc");
            if (!cantTalk && canSendFiles) {
                buttonLogo.src = "https://i.imgur.com/FdJV697.png"; // Can send files and talk in chat
            } else {
                buttonLogo.src = "https://i.imgur.com/2o7DvgF.png"; // Can't send messages / Files
            }
            buttonLogo.setAttribute('draggable', false);
            microphonePicture = buttonLogo;
            secondDiv.appendChild(buttonLogo);
            mainButton.appendChild(secondDiv);
            mainDiv.appendChild(mainButton);

            // Check if the chatbox exists before creating any element (Else will break up Discord Interface)
            var chatTools = document.querySelector(".buttons-3JBrkn");
            if (chatTools)
            {
                var microphoneButton = document.body.appendChild(mainDiv);
                chatTools.insertBefore(microphoneButton, chatTools.firstChild);
                if (!cantTalk) microphoneButton.addEventListener('mousedown', function(event)
                {
                  isTalking = true;
                  startedAt = Date.now();
                  microphonePicture.src = "https://i.imgur.com/bx2hCl3.gif";
                  startRecording();
                });
                // Passing trought document, then user can leave the button rectangle
                document.addEventListener('mouseup', function(event)
                {
                    if (!isTalking) return;
                    microphonePicture.src = "https://i.imgur.com/FdJV697.png";
                    stopRecording();
                    if (Date.now() - startedAt < 500) return; // Message too short, let's not send that

                    var xhr = new XMLHttpRequest();
                    var fd = new FormData();
                    fd.append("audio_data", voiceBlob , `VMSG_${Date.now() / 1000}.wav`);
                    var channelsData = location.href.match(/channels\/([\w@]+)\/(\d+)/);
                    var channelId = channelsData[2]
                    xhr.open("POST",`https://discord.com/api/v8/channels/${channelId}/messages`, true);
                    xhr.setRequestHeader("Authorization", JSON.parse(localStorage.token));
                    xhr.send(fd);

                    // Reinitialise Every vocal variables
                    isTalking = false;
                    voiceBlob = null;
                    leftchannel = [];
                    rightchannel = [];
                    recordingLength = 0;
                });
            }
        }
    }


    function flattenArray(channelBuffer, recordingLength) {
            var result = new Float32Array(recordingLength);
            var offset = 0;
            for (var i = 0; i < channelBuffer.length; i++) {
                var buffer = channelBuffer[i];
                result.set(buffer, offset);
                offset += buffer.length;
            }
            return result;
        }
    function interleave(leftChannel, rightChannel) {
            var length = leftChannel.length + rightChannel.length;
            var result = new Float32Array(length);
            var inputIndex = 0;
            for (var index = 0; index < length;) {
                result[index++] = leftChannel[inputIndex];
                result[index++] = rightChannel[inputIndex];
                inputIndex++;
            }
            return result;
        }
    function writeUTFBytes(view, offset, string) {
        for (var i = 0; i < string.length; i++) view.setUint8(offset + i, string.charCodeAt(i));
    }
    function startRecording() {
            navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
            navigator.getUserMedia({audio: true},
            function (e) {
                window.AudioContext = window.AudioContext || window.webkitAudioContext;
                context = new AudioContext();
                mediaStream = context.createMediaStreamSource(e);
                var bufferSize = 2048;
                var numberOfInputChannels = 2;
                var numberOfOutputChannels = 2;
                if (context.createScriptProcessor) {
                    recorder = context.createScriptProcessor(bufferSize, numberOfInputChannels, numberOfOutputChannels);
                } else {
                    recorder = context.createJavaScriptNode(bufferSize, numberOfInputChannels, numberOfOutputChannels);
                }

                recorder.onaudioprocess = function (e) {
                    leftchannel.push(new Float32Array(e.inputBuffer.getChannelData(0)));
                    rightchannel.push(new Float32Array(e.inputBuffer.getChannelData(1)));
                    recordingLength += bufferSize;
                }
                mediaStream.connect(recorder);
                recorder.connect(context.destination);
            },function (e) {});
    }
    function stopRecording() {
        if (!recorder) return;
         recorder.disconnect(context.destination);
         mediaStream.disconnect(recorder);
         var leftBuffer = flattenArray(leftchannel, recordingLength);
         var rightBuffer = flattenArray(rightchannel, recordingLength);
         var interleaved = interleave(leftBuffer, rightBuffer);
         var buffer = new ArrayBuffer(44 + interleaved.length * 2);
         var view = new DataView(buffer);
         writeUTFBytes(view, 0, 'RIFF');
         view.setUint32(4, 44 + interleaved.length * 2, true);
         writeUTFBytes(view, 8, 'WAVE');
         writeUTFBytes(view, 12, 'fmt ');
         view.setUint32(16, 16, true); // chunkSize
         view.setUint16(20, 1, true); // wFormatTag
         view.setUint16(22, 2, true); // wChannels: stereo (2 channels)
         view.setUint32(24, sampleRate, true); // dwSamplesPerSec
         view.setUint32(28, sampleRate * 4, true); // dwAvgBytesPerSec
         view.setUint16(32, 4, true); // wBlockAlign
         view.setUint16(34, 16, true); // wBitsPerSample
         writeUTFBytes(view, 36, 'data');
         view.setUint32(40, interleaved.length * 2, true);
         var index = 44;
         var volume = 1;
         for (var i = 0; i < interleaved.length; i++) {
            view.setInt16(index, interleaved[i] * (0x7FFF * volume), true);
            index += 2;
         }
            voiceBlob = new Blob([view], { type: 'audio/wav' });
        }
    function checkIfReady() {
        if (document.querySelector(".buttons-3JBrkn"))
        {
          window.dispatchEvent(new Event('beforeunload'));
          localStorage = document.body.appendChild(document.createElement('iframe')).contentWindow.localStorage;
          clearInterval(checkIfReadyInterval)
          setInterval(attachMicrophoneButton,125);
        }
    }
    checkIfReadyInterval = setInterval(checkIfReady,1000);