Overview
One thing that always bugged me was if someone says something like “Oh wow you wouldn’t believe what happ- actually nevermind”. I am far too curious and I usually want to know what was going to be said. Secondly I’ve seen this on WhatsApp too, especially in community groups / other where its usually something like:
My curiosity the better of me, I figured there should be some way that I can see these messages since they were there originally sent to my machine and then removed from it. So I decided to build a chrome extension to solve this problem.
TL;DR — You can see the code and get the CRX extension at: https://github.com/AndrewMohawk/WhatsAppDoc
Please note this is *NOT* a stable piece of code to be used for anything other than education
The Extension
First I tried to find extensions that did this, there were a few sideloadable “WhatsApp” APKs, and I wasn’t super comfortable running extensions I didnt know in my browser and definitely not sideloading an app on my phone, so I figured I’d have to cook one up myself.
I quickly ran through building an extension over at Google — https://developer.chrome.com/extensions/getstarted and looked at being able to modify pages. I figured, this can’t be too hard, I’ll look for some DOM changes and then prevent it from happening.
Unfortunately of course, the days of simple webapps are long behind us with WhatsApp web using complex JS to load things in and out as well as websockets to do all the backend controls as well as all the encryption. So I had to figure out how WhatsApp web actually worked. I stumbled upon this awesome resource by Ahmed Alsuwaidi over at https://github.com/aalsuwaidi/wabot/wiki/WhatsApp-Web that explains many of the functions that I would need to understand to be able to load it.
I wont get into the many many tests I did to get it working, but essentially it broke down into a few parts:
- Injecting your JS into the web app
- Waiting for the webapp (WhatsApp Web) to load — since it takes a varied amount of time to start depending on your phone and browser connections.
- Capturing the “Store” object which holds various items like messages, contacts, etc
- Creating a copy of the messages we have as well as a listener for any new incoming messages
- Looking for any deleted messages and modifying them to show the old text message
Injecting Code
This is mostly automatically done for you in the extension, you merely need to tell the extension which pages it can modify and which code to run:
1 2 3 4 | "content_scripts": [{ "js": ["inject.js"], "matches": ["https://web.whatsapp.com/*"] }], |
Then your inject.js can simply add the code to the page:
1 2 3 4 5 6 7 8 | function injectScript(file, node) { var th = document.getElementsByTagName(node)[0]; var s = document.createElement('script'); s.setAttribute('type', 'text/javascript'); s.setAttribute('src', file); th.appendChild(s); } injectScript( chrome.extension.getURL('content.js'), 'body'); |
The Store Object
Once you have code injected you can directly start modifying the application. When you start looking around at various projects you can find many examples of people natively using the “store” object within WhatsApp Web, this object holds various things, but specifically the messages as objects. Unfortunately in a recent update to the application the store was obfuscated, but thankfully some people much smarter than I have found a great way to get that object back, specifically if you look at https://github.com/aalsuwaidi/wabot/wiki/WhatsApp-Web under the heading “Webpack and finding the store”
Having the store object gives you all the message data that is currently available within the application (ie, our precious.)
Waiting for the application to Load
There are a number of different plugins that all basically have a longish timeout before they load, hoping that whatsapp has finished loading, but I found a better way to do it by comparing if the store object has messages loaded in it and once this is completed to start my plugin:
var WhatsAppWebLoaded = setInterval(function() { Store = _requireById(store_id).default; if (Store.Msg.models.length > 1) { clearInterval(WhatsAppWebLoaded); setTimeout(function() { console.log("Starting with " + Store.Msg.models.length + " messages in model."); init(Store.Msg.models.length); console.log("Loaded!"); }, 3000); } else { console.log("Waiting for app..."); } }, 1000); |
The rest of the application
From here on it was fairly basic and the extension runs in the following manner:
- Make a duplicate of the real message store as “DiffStore”
- Mark all deleted messages as already seen, since we cant bring back something we have never seen
- Override the “push” function for the WhatsApp message store so that we add any incoming messages to our DiffStore
- Every second look for new deleted messages
- If a deleted message is found change the “This message has been deleted” to the previous message and log it to the console
Caveats
Different types of messages
I only built this to capture text messages, not images/video/audio/files but its possible to monitor for those as well. To do this one would need to go and pull the linked data (such as a voice note or image file), store it somewhere and then if deleted return it to the client with a link to the original. Its hacky and messy and I think that if someone deletes an image its probably okay. On the other hand all these types do give you a thumbnail that you can use and is returned as a Base64 encoded image, which you can decode even simply online with something like http://freeonlinetools24.com/base64-image
Messages arent instantly shown after deleted
Because of the way the application works and my laziness you will need to hover over the deleted message, check the console or tab out and tab back into WhatsApp web to get it to show the deleted message.
Leaving WhatsApp web open for a long time without the tab in focus can break things
The message store will refresh when you bring the tab back in focus and cause an inconsistency between the DiffStore as well as the original WhatsApp message store, you will need to simply reload the page to keep them balanced
Its coded badly
Yeah, I got nothing for you, you are welcome to make PRs or just redo it yourself :) This was just me mucking around to figure out how it worked and I would in no way encourage anyone to code like I do!
-AM