I am trying to write a python wrapper for selenium to send and receive messages on whatsapp web. However, I don’t want people to have to scan the qr authentication code every single time they run the program.
From what I’ve read online, the session data is stored in indexeddb with the name “wawc” and syncs that data to local storage. I have written js scripts to extract the indexeddb keys and values and to inject these into local storage and indexeddb on start up. These appear to work when I look at the chrome dev tools but on reloading the page, I get a logging out screen and then the indexeddb gets cleared.
I’ve seen that some people get around this by specifying the user-data-dir
argument in ChromeOptions but I’d rather do this manually as I’d prefer user data only to be saved for web.whatsapp.com not any other sites.
extract_session.js:
function requestPromise(request) {
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async function openDatabase(dbName) {
const request = indexedDB.open(dbName);
return requestPromise(request);
}
async function getAllFromStore(db, storeName) {
const transaction = db.transaction(storeName, "readonly");
const objectStore = transaction.objectStore(storeName);
return requestPromise(objectStore.getAll());
}
try {
const db = await openDatabase(arguments[0]);
const data = await getAllFromStore(db, arguments[1]);
return JSON.stringify(data);
} catch (error) {
console.log("Failed to extract session:", error)
return null;
}
inject_session.js:
function requestPromise(request) {
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async function openDatabase(dbName) {
const request = indexedDB.open(dbName);
return requestPromise(request);
}
async function putInStore(db, storeName, data) {
const transaction = db.transaction(storeName, "readwrite");
const objectStore = transaction.objectStore(storeName);
for (const item of data) {
const request = objectStore.put(item);
await requestPromise(request);
}
}
try {
const session = JSON.parse(arguments[2]);
const db = await openDatabase(arguments[0]);
await putInStore(db, arguments[1], session);
localStorage.clear()
for (const item of session) {
localStorage.setItem(item.key, item.value);
}
} catch (error) {
console.error("Failed to inject session:", error);
}
Minimal code to recreate the problem:
from selenium.webdriver import Chrome, ChromeOptions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
import os
def _handle_await(parent, locator, timeout, poll_freq):
try:
return WebDriverWait(parent,
timeout,
poll_frequency=poll_freq
).until(EC.presence_of_element_located(locator))
except TimeoutException:
return None
def await_element(parent, locator, timeout=0, poll_freq=0.05):
if timeout == 0:
while True:
res = _handle_await(parent, locator, timeout, poll_freq)
if res is not None:
return res
else:
return _handle_await(parent, locator, timeout, poll_freq)
QR_CODE = (By.CSS_SELECTOR, "canvas[role='img']")
LOGGED_IN = (By.CSS_SELECTOR, "div[title='Chats']")
options = ChromeOptions()
options.set_capability("goog:loggingPrefs", {"browser": "ALL"})
driver = Chrome(options=options)
driver.get("https://web.whatsapp.com/")
await_element(driver, QR_CODE)
if os.path.isfile("session.wa"):
js_code = open("inject_session.js", "r").read()
with open("session.wa", "r", encoding="utf-8") as file:
driver.execute_script(js_code, "wawc", "user", file.read())
driver.refresh()
input()
else:
await_element(driver, LOGGED_IN)
js_code = open("extract_session.js", "r").read()
session_data = driver.execute_script(js_code, "wawc", "user")
with open("session.wa", "w", encoding="utf-8") as file:
file.write(str(session_data))
input()
The first time the code is run, the session.wa file is created and looks fine. Once the code is run again, the local storage and indexeddb appear to be updated correctly but once the page is reloaded, I get this screen:
Whatsapp Logout Image
And then I’m redirected to the main web.whatsapp.com screen with the QR code for login.