Voice Message using Discord liek snapchat. Hold the microphone button next to the chat bar to create a voice chat.
// ==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 r###lt = new Float32Array(recordingLength); var offset = 0; for (var i = 0; i < channelBuffer.length; i++) { var buffer = channelBuffer[i]; r###lt.set(buffer, offset); offset += buffer.length; } return r###lt; } function interleave(leftChannel, rightChannel) { var length = leftChannel.length + rightChannel.length; var r###lt = new Float32Array(length); var inputIndex = 0; for (var index = 0; index < length;) { r###lt[index++] = leftChannel[inputIndex]; r###lt[index++] = rightChannel[inputIndex]; inputIndex++; } return r###lt; } 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);