paint-brush
Browser Extensions: Taking Back What’s Yoursby@vedantsopinions
1,395 reads
1,395 reads

Browser Extensions: Taking Back What’s Yours

by Vedant AgarwalaDecember 30th, 2018
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Recently, I had the misfortune to look for a rental house. Besides parsing through multiple properties on property rental portals, I had to contact agents. For <strong>every </strong>property that I liked <em>(and had a chance to get)</em>:

Companies Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Browser Extensions: Taking Back What’s Yours
Vedant Agarwala HackerNoon profile picture

I made a chrome extension to send WhatsApp messages to all phone numbers on a webpage, without saving a contact on the phone.

GitHub Link.

Recently, I had the misfortune to look for a rental house. Besides parsing through multiple properties on property rental portals, I had to contact agents. For every property that I liked (and had a chance to get):

I had to copy the agent’s phone number from the computer screen to my phone, wait for WhatsApp to sync contacts, mentally note the listing name from the computer, and only then send a message from my phone.

And, I would have to do it 79 times:

agents to contact

Too much manual labour; there had to be a better solution.

After some homework, I settled on making a chrome extension that would parse every webpage, look for phone numbers and send a message with a single click. Some iterations later, it was able to send messages to every number, with a link to the corresponding property, with a single click. The extension also stores the phone numbers it has already sent to, to prevent repeat messages:

After running my extension

Technical Implementation

I figured I could run custom javascript in web.whatsapp.com to send a message. But there was no way to open a chatbox of a new user who’s number is not saved. I could pass in query params to web.whatsapp.com with a contact and a message, something like https://web.whatsapp.com/send?phone=1234556789&text=Hey. This would open the chatbox to the number +1 23456789 with ’Hey’ in the chatbox. Then, perform a click on the send button and voila! The message is sent.

With the above plan, I broke it into the following sub-tasks:

  1. Parse every page for phone numbers
  2. Append a WhatsApp icon to those numbers
  3. Search for the hyperlink before that phone number.
  4. Open WhatsApp web on click of that icon, with a pre-filled message to the phone number containing the found hyperlink.
  5. Add a_“send to all”_ action to send messages to all contacts in the background.
  6. A script that runs in web.whatsapp.com to wait for the chatbox (or the invalid number popup) to open and send the message.
  7. Sending the message is a long running task, so show the status like: sending, sent, phone invalid.
  8. Store the numbers already sent so that I do not accidentally send multiple messages to the same number.

I used the awesome libphonenumber-js library and some javascript to find phone numbers, and add the WhatsApp icon.

On click, I open the WhatsApp web tab using the chrome extension API. It was a little tricky because the content script that added the icon, had to send a message to the background script to open the tab along with parameters. With some recursive code I could find the closest hyperlink before the phone number. Almost always this would be the link to the property the agent has put up (who my script would message).

This marked the half-way point: I could send a WhatsApp message from the browser to an unsaved contact without touching my phone.

Then, for WhatsApp Web, I wrote some async /await code. It had to wait for the web-app to load; specifically, it waited for either for the chat box to open, or the ‘Phone number shared via url is invalid’ popup to show up. This part was really fun- instead of writing indented anonymous/arrow functions, I wrote linear await statements. I passed Sandi Metz’ squint test:

“Squint you eyes. Lean back. Look for changes in shape. Look for changes in colour. Shape reflects cycolmetric complexity — lots of nested conditionals. Colour reflects differing levels of abstraction, different types of things are in a mish mash. The squint test can help!”

Remaining part was simple: use the chrome APIs to send messages between the content and background scripts. The chrome APIs to store data between page refreshes was simple enough as well.

I really liked how I added functions to my created element and just called them from different parts in the code. For example, see the setStatus function:













// in 1 file:function addSendToWaToNode(node, phoneNumber) {const sendToWaNode = document.createElement('span');...sendToWaNode.setStatus = (status, save = false) => {sendToWaNode.lastChild.textContent = status;if (save) {chrome.storage.sync.set({ [waNumber]: status }, function() {console.log(`saved ${waNumber} : ${status}`);});}}}




// in another file, I could use it like:phoneSpans = document.querySelectorAll(`.${WA_SPAN_CLASS_NAME}`);span = phoneSpans[0];span.setStatus('sent', true);

Finally, the long awaited production run!

I think I contacted 80 agents instead of 30 just because it was so easy.

Was nice to write javascript for chrome extension because I didn’t have to worry about compatibility issues for different browsers and could liberally use ES6 features (const, async, await, arrow functions, string interpolation).

Initially, I kept my code organized and functions small (~5 lines), but I kind of gave up on it in the end. I was still homeless you know.

One thing I sorely missed was the native support of tests. I read people made node apps and some framework like code to support testing for their extensions. But there no out-of-the-box solution, so I just let it go for this project.

Parting thoughts

Instead of spending a few hours to manually message like 80 agents, I spent a week to write this chrome extension. But then there would be nothing to blog (or brag) about. Nevertheless, I’d do it again.

The extension is currently in beta. It needs some more UI, error handling to become a general purpose extension. Not sure if I should publish it on the chrome web store. Tell me if you would like to use something like this.

I really liked the idea of extensions. It’s like adding features that the app creators don’t have time for; or they thought of them as bugs, not features. Rest assured, there are more extensions to come. Extension requests open 😉