Skip to content

Instantly share code, notes, and snippets.

@adelriosantiago
Last active January 23, 2024 12:47
Show Gist options
  • Save adelriosantiago/c105e0e2de3e92a7b196530b2a92a728 to your computer and use it in GitHub Desktop.
Save adelriosantiago/c105e0e2de3e92a7b196530b2a92a728 to your computer and use it in GitHub Desktop.
Javascript code to download all audios from a Whatsapp chat

Javascript code to download all audios from a Whatsapp chat

Open a new browser console (F12) in the Whatsapp Web chat room and run this code to download all available audios.

var prefix = "download";

function pad_with_zeroes(number, length) {
    var my_string = '' + number;
    while (my_string.length < length) {
        my_string = '0' + my_string;
    }
    return my_string;
}

var elm = document.getElementsByTagName('audio');
for (var i = 0; i < elm.length; i++) {
  var a = document.createElement('a');
  document.body.appendChild(a);
  a.href = elm[i].src;
  var padNum = pad_with_zeroes(i, 6);
  a.download = prefix + "-" + padNum + "-" + elm[i].src.substr(elm[i].src.lastIndexOf('/') + 1) + '.ogg';
  a.click();
}

This will download all audio blocks that you can click on "Play". Be sure to scroll up to discover all audio blocks. If a block has the "Download" you need to download it first. Use the following code to automatically click on all "Download" buttons.

var elm = document.getElementsByClassName('btn-audio icon icon-audio-download');

for (var i = 0; i < elm.length; i++) {
  elm[i].click();
}
@Quidney
Copy link

Quidney commented Feb 19, 2023

Does this still work?

@Quidney
Copy link

Quidney commented Feb 19, 2023

Both of the codes don't work,
But this can be used as an alternative to the 2nd code:

var downloadButtons = document.querySelectorAll('[data-testid="audio-download"]');

// Loop through the download buttons and click each one with a delay
var delay = 1000; // 1 second
for (var i = 0; i < downloadButtons.length; i++) {
  setTimeout(function(button) {
    button.click();
  }, delay * i, downloadButtons[i]);
}

@Quidney
Copy link

Quidney commented Feb 20, 2023

And this can be used for the first part.

var messageContainers = document.querySelectorAll('[data-testid="msg-container"]');
for (var i = 0; i < messageContainers.length; i++) {
  setTimeout(function(container) {
    // Simulate a mouseover event on the container element
    var mouseoverEvent = new MouseEvent('mouseover', {
      view: window,
      bubbles: true,
      cancelable: true
    });
    container.dispatchEvent(mouseoverEvent);
	setTimeout(function() {
    // Find the triangle element and simulate a click event on it
		var tri = container.querySelector('[data-testid="down-context"]');
		if (tri) {
      
        var clickEvent = new MouseEvent('click', {
          view: window,
          bubbles: true,
          cancelable: true
        });
        tri.dispatchEvent(clickEvent);

        // Wait for the context menu to appear
        setTimeout(function() {
          // Find the download button and simulate a click event on it
          var downloadButton = document.querySelector('li[data-testid="mi-msg-download"] > div._1MZM5');
          if (downloadButton) {
            downloadButton.dispatchEvent(new MouseEvent('click', {
              view: window,
              bubbles: true,
              cancelable: true
            }));
          }
        }, 500);
		}
		},500);
  }, 1000 * i, messageContainers[i]);
}

@Rovack
Copy link

Rovack commented Jan 23, 2024

The above didn't quite work for me, but here's a version based on it, that also scrolls up (as far as WhatsApp web is willing to sync at least), and downloads everything it encounters:

const downloadLabel = 'Download voice message';
const playLabel = 'Play voice message';
const contextMenuLabel = 'Context Menu';
const realDownloadLabel = 'Download';

const messageViewClass = '_5kRIK';

const delay = 1; // 1 second

const alreadyDownloaded = [];

const sleep = (seconds) => new Promise((resolve) => setTimeout(resolve, seconds * 1000));

const findByLabel = (label) => document.querySelectorAll(`[aria-label="${label}"]`);
const getDataId = (button) => button.closest('[data-id]').dataset.id;

const waitForElement = async (label) => {
  let element = null;
  do {
    await sleep(1);
    element = findByLabel(label)?.[0];
  } while (element == null);

  return element;
};

async function downloadVisible() {
  const downloadedIds = []; 
  const downloadButtons = findByLabel(downloadLabel);

  // Loop through the "download" buttons and click each one with a delay
  for (const button of downloadButtons) {
    await sleep(delay);
    button.click();
  }

  // Wait for the downloads to finish.
  let playButtons = [];
  do {
    playButtons = findByLabel(playLabel);
    await sleep(1);
  } while (playButtons.length < downloadButtons.length);

  playButtons = [...playButtons].filter((button) => !alreadyDownloaded.includes(getDataId(button)));

  // Open the menu and download the file for each memo.
  for (const button of playButtons) {
    await sleep(1);

    // Simulate a mouseover event on the message.
    const mouseoverEvent = new MouseEvent('mouseover', {
      view: window,
      bubbles: true,
      cancelable: true
    });
    button.dispatchEvent(mouseoverEvent);

    const contextMenu = await waitForElement(contextMenuLabel);
    console.log('Clicking context menu');
    contextMenu.click();

    const download = await waitForElement(realDownloadLabel);
    download.click();

    downloadedIds.push(getDataId(button));
  }

  if (downloadedIds.length > 0) console.log(`Downloaded ${downloadedIds.length} visible memos`);
  return downloadedIds;
}

const messageView = document.getElementsByClassName(messageViewClass)[0];

while (true) {
    const ids = await downloadVisible();
    alreadyDownloaded.push(...ids);

    console.log('Scrolling...');
    messageView.scrollBy(0, -messageView.offsetHeight);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment