Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save sankar4n/e7633789a830d5e9cc420d082f940c39 to your computer and use it in GitHub Desktop.
Save sankar4n/e7633789a830d5e9cc420d082f940c39 to your computer and use it in GitHub Desktop.
WhatsApp Group Contacts Export: This will download the members of group with their phone number, whatsapp name and if contact is stored on phone

WhatsApp Group Contacts Export (Personal Project)

This will download the members of group with their phone number, whatsapp name and if contact is stored on phone, it will export the name as well.

Go to web.whatsapp.com. Copy-paste the code into your browser console.

(async () => {
  const contactFinder = new ContactFinder("YOUR GROUP SEARCH STRING OR GROUP TITLE");
  const members = await contactFinder.getGroupMembers(); // This will return a JS Map Object
  console.log(members);

  // OR 
  await contactFinder.downloadMembersAsCSV(); // This will download the contacts as CSV
})();


This code uses data stored in indexDB by WhatsApp and search through indexDb collections to collect the required data

Side Note : This code snippet is shared only for educational purpose and the code might not be maintained anymore. Developer is not responsible for any Privacy/Security voilations. Also no data is being sent to anywhere, everything is stored and fetched from local browser tab.

Sample Image

class ContactFinder {
#db;
#chatToFind;
#dbName = "model-storage";
#chatsCol = "chat";
#contactCol = "contact";
#groupCol = "participant";
constructor(chatGroupName) {
this.#chatToFind = chatGroupName;
}
async openConnection() {
if (!this.#db) {
const dbName = this.#dbName;
this.#db = await new Promise((resolve, reject) => {
let request = indexedDB.open(dbName);
request.onerror = (event) => {
reject(event);
};
request.onsuccess = (event) => {
resolve(event.target.result);
};
});
}
return this.#db;
}
async #promisifyCol(collection, query, count) {
const db = await this.openConnection();
return new Promise((resolve, reject) => {
const request = db.transaction(collection).objectStore(collection).getAll(query, count);
request.onerror = (event) => {
reject(event);
};
request.onsuccess = (event) => {
resolve(event.target.result);
};
});
}
async #getChats() {
const allChats = await this.#promisifyCol(this.#chatsCol);
const chatToFind = this.#chatToFind;
return allChats.filter((chat) => {
return chat.name && chat.name.includes(chatToFind);
});
}
async #getGroups() {
const chats = (await this.#getChats()).map((chat) => chat.id);
const allGroups = await this.#promisifyCol(this.#groupCol);
return allGroups.filter((group) => {
return group.groupId && chats.includes(group.groupId);
});
}
async #getGroupParticipants() {
const groups = await this.#getGroups();
const map = new Map();
groups.forEach((group) => {
group.participants.forEach((par) => {
const num = par.replace("@c.us", "");
map.set(num, num);
});
});
return map;
}
async #getContacts() {
return this.#promisifyCol(this.#contactCol);
}
async getGroupMembers(includeLeft = false) {
const members = await this.#getGroupParticipants();
const contacts = await this.#getContacts();
contacts.forEach((contact) => {
var num;
if (contact.phoneNumber) {
num = contact.phoneNumber.split("@")[0];
} else if (contact.id) {
num = contact.id.split("@")[0];
}
if (num && members.get(num)) {
members.set(num, {
phoneNum: num,
name: contact.name,
pushname: contact.pushname,
});
}
});
return members;
}
async #getGroupPastParticipants() {
const groups = await this.#getGroups();
const map = new Map();
groups.forEach((group) => {
group.pastParticipants.forEach((par) => {
const num = par.jid.replace("@c.us", "");
map.set(num, num);
});
});
return map;
}
async getGroupPastMembers() {
const members = await this.#getGroupPastParticipants();
const contacts = await this.#getContacts();
contacts.forEach((contact) => {
var num;
if (contact.phoneNumber) {
num = contact.phoneNumber.split("@")[0];
} else if (contact.id) {
num = contact.id.split("@")[0];
}
if (num && members.get(num)) {
members.set(num, {
phoneNum: num,
name: contact.name,
pushname: contact.pushname,
});
}
});
return members;
}
async #downloadAsCSV(fileName = "my_data.csv", csvContent) {
var link = document.createElement("a");
link.setAttribute("href", encodeURI(csvContent));
link.setAttribute("download", fileName);
document.body.appendChild(link); // Required for FF
link.click();
}
async downloadCurrentMembers() {
const members = await this.getGroupMembers();
let csvContent = "data:text/csv;charset=utf-8,";
for (const [key, value] of members.entries()) {
let row = [value]
if (typeof value === 'object') {
row = [value.phoneNum];
if (value.name) row.push(value.name);
if (value.pushname) row.push(value.pushname);
}
csvContent += row.join(",") + "\r\n";
}
await this.#downloadAsCSV('current_members.csv', csvContent)
}
async downloadPastMembers() {
const members = await this.getGroupPastMembers();
let csvContent = "data:text/csv;charset=utf-8,";
for (const [key, value] of members.entries()) {
let row = [value]
if (typeof value === 'object') {
row = [value.phoneNum];
if (value.name) row.push(value.name);
if (value.pushname) row.push(value.pushname);
}
csvContent += row.join(",") + "\r\n";
}
await this.#downloadAsCSV('past_members.csv', csvContent)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment