chore: cleanup, ci workflows and formatting (#127064)

* ci: setup workflows, dependabot, labeler and commit linting

* docs: update README, community files and issue templates

* chore: configure prettier, editorconfig and format codebase

* chore: restore changes lost during rebase

* docs: adjust url for new logo

* fix: align badges

* chore: resolve conflicts and re-add temp lint
This commit is contained in:
2^1 2026-03-10 00:45:12 +01:00 • committed by GitHub
commit 807efd502d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 6790 additions and 3547 deletions

View file

@ -5,7 +5,7 @@ exports.WhatsWebURL = 'https://web.whatsapp.com/';
exports.DefaultOptions = {
puppeteer: {
headless: true,
defaultViewport: null
defaultViewport: null,
},
webVersion: '2.3000.1017054665',
webVersionCache: {
@ -15,7 +15,8 @@ exports.DefaultOptions = {
qrMaxRetries: 0,
takeoverOnConflict: false,
takeoverTimeoutMs: 0,
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
userAgent:
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
ffmpegPath: 'ffmpeg',
bypassCSP: false,
proxyAuthentication: undefined,
@ -34,7 +35,7 @@ exports.DefaultOptions = {
exports.Status = {
INITIALIZING: 0,
AUTHENTICATING: 1,
READY: 3
READY: 3,
};
/**
@ -72,7 +73,7 @@ exports.Events = {
BATTERY_CHANGED: 'change_battery',
INCOMING_CALL: 'call',
REMOTE_SESSION_SAVED: 'remote_session_saved',
VOTE_UPDATE: 'vote_update'
VOTE_UPDATE: 'vote_update',
};
/**
@ -148,7 +149,7 @@ exports.GroupNotificationTypes = {
exports.ChatTypes = {
SOLO: 'solo',
GROUP: 'group',
UNKNOWN: 'unknown'
UNKNOWN: 'unknown',
};
/**
@ -168,7 +169,7 @@ exports.WAState = {
TOS_BLOCK: 'TOS_BLOCK',
UNLAUNCHED: 'UNLAUNCHED',
UNPAIRED: 'UNPAIRED',
UNPAIRED_IDLE: 'UNPAIRED_IDLE'
UNPAIRED_IDLE: 'UNPAIRED_IDLE',
};
/**

View file

@ -5,8 +5,12 @@ exports.ExposeAuthStore = () => {
window.AuthStore.AppState = window.require('WAWebSocketModel').Socket;
window.AuthStore.Cmd = window.require('WAWebCmd').Cmd;
window.AuthStore.Conn = window.require('WAWebConnModel').Conn;
window.AuthStore.OfflineMessageHandler = window.require('WAWebOfflineHandler').OfflineMessageHandler;
window.AuthStore.PairingCodeLinkUtils = window.require('WAWebAltDeviceLinkingApi');
window.AuthStore.OfflineMessageHandler = window.require(
'WAWebOfflineHandler',
).OfflineMessageHandler;
window.AuthStore.PairingCodeLinkUtils = window.require(
'WAWebAltDeviceLinkingApi',
);
window.AuthStore.Base64Tools = window.require('WABase64');
window.AuthStore.RegistrationUtils = {
...window.require('WAWebCompanionRegClientUtils'),
@ -14,4 +18,4 @@ exports.ExposeAuthStore = () => {
...window.require('WAWebUserPrefsInfoStore'),
...window.require('WAWebSignalStoreApi'),
};
};
};

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,6 @@
* Interface Controller
*/
class InterfaceController {
constructor(props) {
this.pupPage = props.pupPage;
}
@ -15,8 +14,12 @@ class InterfaceController {
*/
async openChatWindow(chatId) {
return await this.pupPage.evaluate(async (chatId) => {
const chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
return await (window.require('WAWebCmd').Cmd).openChatBottom({'chat':chat});
const chat = await window.WWebJS.getChat(chatId, {
getAsModel: false,
});
return await window
.require('WAWebCmd')
.Cmd.openChatBottom({ chat: chat });
}, chatId);
}
@ -25,9 +28,11 @@ class InterfaceController {
* @param {string} chatId ID of the chat drawer that will be opened
*/
async openChatDrawer(chatId) {
await this.pupPage.evaluate(async chatId => {
let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
await (window.require('WAWebCmd').Cmd).openDrawerMid(chat);
await this.pupPage.evaluate(async (chatId) => {
let chat = await window.WWebJS.getChat(chatId, {
getAsModel: false,
});
await window.require('WAWebCmd').Cmd.openDrawerMid(chat);
}, chatId);
}
@ -36,9 +41,11 @@ class InterfaceController {
* @param {string} chatId ID of the chat search that will be opened
*/
async openChatSearch(chatId) {
await this.pupPage.evaluate(async chatId => {
let chat = await window.WWebJS.getChat(chatId, { getAsModel: false });
await (window.require('WAWebCmd').Cmd).chatSearch(chat);
await this.pupPage.evaluate(async (chatId) => {
let chat = await window.WWebJS.getChat(chatId, {
getAsModel: false,
});
await window.require('WAWebCmd').Cmd.chatSearch(chat);
}, chatId);
}
@ -48,10 +55,24 @@ class InterfaceController {
*/
async openChatWindowAt(msgId) {
await this.pupPage.evaluate(async (msgId) => {
const msg = (window.require('WAWebCollections')).Msg.get(msgId) || (await (window.require('WAWebCollections')).Msg.getMessagesById([msgId]))?.messages?.[0];
const chat = (window.require('WAWebCollections')).Chat.get(msg.id.remote) ?? await (window.require('WAWebCollections')).Chat.find(msg.id.remote);
const searchContext = await (window.require('WAWebChatMessageSearch')).getSearchContext(chat, msg.id);
await (window.require('WAWebCmd').Cmd).openChatAt({ chat: chat, msgContext: searchContext });
const msg =
window.require('WAWebCollections').Msg.get(msgId) ||
(
await window
.require('WAWebCollections')
.Msg.getMessagesById([msgId])
)?.messages?.[0];
const chat =
window.require('WAWebCollections').Chat.get(msg.id.remote) ??
(await window
.require('WAWebCollections')
.Chat.find(msg.id.remote));
const searchContext = await window
.require('WAWebChatMessageSearch')
.getSearchContext(chat, msg.id);
await window
.require('WAWebCmd')
.Cmd.openChatAt({ chat: chat, msgContext: searchContext });
}, msgId);
}
@ -60,9 +81,15 @@ class InterfaceController {
* @param {string} msgId ID of the message drawer that will be opened
*/
async openMessageDrawer(msgId) {
await this.pupPage.evaluate(async msgId => {
const msg = (window.require('WAWebCollections')).Msg.get(msgId) || (await (window.require('WAWebCollections')).Msg.getMessagesById([msgId]))?.messages?.[0];
await (window.require('WAWebCmd').Cmd).msgInfoDrawer(msg);
await this.pupPage.evaluate(async (msgId) => {
const msg =
window.require('WAWebCollections').Msg.get(msgId) ||
(
await window
.require('WAWebCollections')
.Msg.getMessagesById([msgId])
)?.messages?.[0];
await window.require('WAWebCmd').Cmd.msgInfoDrawer(msg);
}, msgId);
}
@ -71,7 +98,9 @@ class InterfaceController {
*/
async closeRightDrawer() {
await this.pupPage.evaluate(async () => {
await window.require('WAWebDrawerManager').DrawerManager.closeDrawerRight();
await window
.require('WAWebDrawerManager')
.DrawerManager.closeDrawerRight();
});
}
@ -80,8 +109,11 @@ class InterfaceController {
*/
async getFeatures() {
return await this.pupPage.evaluate(() => {
if(!(window.require('WAWebCollections')).Features) throw new Error('This version of Whatsapp Web does not support features');
return (window.require('WAWebCollections')).Features.F;
if (!window.require('WAWebCollections').Features)
throw new Error(
'This version of Whatsapp Web does not support features',
);
return window.require('WAWebCollections').Features.F;
});
}
@ -91,8 +123,13 @@ class InterfaceController {
*/
async checkFeatureStatus(feature) {
return await this.pupPage.evaluate((feature) => {
if(!(window.require('WAWebCollections')).Features) throw new Error('This version of Whatsapp Web does not support features');
return (window.require('WAWebCollections')).Features.supportsFeature(feature);
if (!window.require('WAWebCollections').Features)
throw new Error(
'This version of Whatsapp Web does not support features',
);
return window
.require('WAWebCollections')
.Features.supportsFeature(feature);
}, feature);
}
@ -102,9 +139,14 @@ class InterfaceController {
*/
async enableFeatures(features) {
await this.pupPage.evaluate((features) => {
if(!(window.require('WAWebCollections')).Features) throw new Error('This version of Whatsapp Web does not support features');
if (!window.require('WAWebCollections').Features)
throw new Error(
'This version of Whatsapp Web does not support features',
);
for (const feature in features) {
(window.require('WAWebCollections')).Features.setFeature(features[feature], true);
window
.require('WAWebCollections')
.Features.setFeature(features[feature], true);
}
}, features);
}
@ -115,9 +157,14 @@ class InterfaceController {
*/
async disableFeatures(features) {
await this.pupPage.evaluate((features) => {
if(!(window.require('WAWebCollections')).Features) throw new Error('This version of Whatsapp Web does not support features');
if (!window.require('WAWebCollections').Features)
throw new Error(
'This version of Whatsapp Web does not support features',
);
for (const feature in features) {
(window.require('WAWebCollections')).Features.setFeature(features[feature], false);
window
.require('WAWebCollections')
.Features.setFeature(features[feature], false);
}
}, features);
}

View file

@ -20,4 +20,4 @@ async function exposeFunctionIfAbsent(page, name, fn) {
await page.exposeFunction(name, fn);
}
module.exports = {exposeFunctionIfAbsent};
module.exports = { exposeFunctionIfAbsent };

View file

@ -13,15 +13,20 @@ const has = (o, k) => Object.prototype.hasOwnProperty.call(o, k);
*/
class Util {
constructor() {
throw new Error(`The ${this.constructor.name} class may not be instantiated.`);
throw new Error(
`The ${this.constructor.name} class may not be instantiated.`,
);
}
static generateHash(length) {
var result = '';
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
result += characters.charAt(
Math.floor(Math.random() * charactersLength),
);
}
return result;
}
@ -49,7 +54,7 @@ class Util {
/**
* Formats a image to webp
* @param {MessageMedia} media
*
*
* @returns {Promise<MessageMedia>} media in webp format
*/
static async formatImageToWebpSticker(media, pupPage) {
@ -68,7 +73,7 @@ class Util {
/**
* Formats a video to webp
* @param {MessageMedia} media
*
*
* @returns {Promise<MessageMedia>} media in webp format
*/
static async formatVideoToWebpSticker(media) {
@ -79,13 +84,13 @@ class Util {
const tempFile = path.join(
tmpdir(),
`${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`
`${Crypto.randomBytes(6).readUIntLE(0, 6).toString(36)}.webp`,
);
const stream = new (require('stream').Readable)();
const buffer = Buffer.from(
media.data.replace(`data:${media.mimetype};base64,`, ''),
'base64'
'base64',
);
stream.push(buffer);
stream.push(null);
@ -100,7 +105,7 @@ class Util {
'libwebp',
'-vf',
// eslint-disable-next-line no-useless-escape
'scale=\'iw*min(300/iw\,300/ih)\':\'ih*min(300/iw\,300/ih)\',format=rgba,pad=300:300:\'(300-iw)/2\':\'(300-ih)/2\':\'#00000000\',setsar=1,fps=10',
"scale='iw*min(300/iw\,300/ih)':'ih*min(300/iw\,300/ih)',format=rgba,pad=300:300:'(300-iw)/2':'(300-ih)/2':'#00000000',setsar=1,fps=10",
'-loop',
'0',
'-ss',
@ -132,8 +137,8 @@ class Util {
/**
* Sticker metadata.
* @typedef {Object} StickerMetadata
* @property {string} [name]
* @property {string} [author]
* @property {string} [name]
* @property {string} [author]
* @property {string[]} [categories]
*/
@ -141,7 +146,7 @@ class Util {
* Formats a media to webp
* @param {MessageMedia} media
* @param {StickerMetadata} metadata
*
*
* @returns {Promise<MessageMedia>} media in webp format
*/
static async formatToWebpSticker(media, metadata, pupPage) {
@ -151,8 +156,7 @@ class Util {
webpMedia = await this.formatImageToWebpSticker(media, pupPage);
else if (media.mimetype.includes('video'))
webpMedia = await this.formatVideoToWebpSticker(media);
else
throw new Error('Invalid media format');
else throw new Error('Invalid media format');
if (metadata.name || metadata.author) {
const img = new webp.Image();
@ -161,8 +165,17 @@ class Util {
const packname = metadata.name;
const author = metadata.author;
const categories = metadata.categories || [''];
const json = { 'sticker-pack-id': stickerPackId, 'sticker-pack-name': packname, 'sticker-pack-publisher': author, 'emojis': categories };
let exifAttr = Buffer.from([0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00]);
const json = {
'sticker-pack-id': stickerPackId,
'sticker-pack-name': packname,
'sticker-pack-publisher': author,
emojis: categories,
};
let exifAttr = Buffer.from([
0x49, 0x49, 0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00,
0x41, 0x57, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,
0x00, 0x00,
]);
let jsonBuffer = Buffer.from(JSON.stringify(json), 'utf8');
let exif = Buffer.concat([exifAttr, jsonBuffer]);
exif.writeUIntLE(jsonBuffer.length, 14, 4);