Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add sendPhotoVideoViaTyping function #2678

Merged
merged 1 commit into from
Mar 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,11 @@ await client
});


// Send by injecting keystrokes into WhatsApp, thus maintaining the typing indicator
await client.sendTextViaTyping('[email protected]', '👋 Hello from venom!');
// Send text message by injecting keystrokes into WhatsApp, thus maintaining the typing indicator
let success = await client.sendTextViaTyping('[email protected]', '👋 Hello from venom!');

// Send photo or video by injecting keystrokes
let success = await client.sendPhotoVideoViaTyping('[email protected]', 'path/to/file.jpg', 'Pretty sunset');

// Send location
await client
Expand Down
127 changes: 127 additions & 0 deletions src/api/layers/AutomateLayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { ListenerLayer } from './listener.layer';
import { Mutex } from 'async-mutex';
import { Browser, Page } from 'puppeteer';
import { CreateConfig } from '../../config/create-config';

export class AutomateLayer extends ListenerLayer {
private typingMutex: Mutex;

constructor(
public browser: Browser,
public page: Page,
session?: string,
options?: CreateConfig
) {
super(browser, page, session, options);
this.typingMutex = new Mutex();
}

private async selectChatViaTyping(chatId: string): Promise<boolean> {
let xpath = '//*[@id="side"]/div[1]/div/div[2]/div[2]/div/div[1]/p';
let ids = await this.page.evaluate(() => {
return WAPI.getAllChatIds();
});
if (!ids.includes(chatId)) {
return false;
}

try {
let search_element = await this.page.waitForXPath(xpath, {
timeout: 1000
});
// @ts-ignore
await search_element.click();
let phone_number = chatId.replace('@c.us', '');
await this.page.keyboard.type(' ' + phone_number + '\n', { delay: 200 });
await this.page.waitForTimeout(3000);
return true;
} catch (error) {
return false;
}
}

private async typeMultiLine(content: string): Promise<void> {
content = content.replace('\r', '\n');

Check failure

Code scanning / CodeQL

Incomplete string escaping or encoding High

This replaces only the first occurrence of '\r'.
const lines = content.split(/\r?\n/);

for (let index = 0; index < lines.length; index++) {
let line = lines[index];
await this.page.keyboard.type(line, { delay: 200 });
await this.page.keyboard.down('Shift');
// Press the Enter key while the Shift key is down
await this.page.keyboard.press('Enter');
// Release the Shift key
await this.page.keyboard.up('Shift');
}
}

/**
* Sends a photo or video to given chat, by injecting keystrokes
* @param to chat id: [email protected]
* @param fileName full path to media file
* @param caption media caption
*/
public async sendPhotoVideoViaTyping(
to: string,
fileName: string,
caption: string = ''
): Promise<boolean> {
let release = await this.typingMutex.acquire();
let success = await this.selectChatViaTyping(to);
if (!success) {
release();
return false;
}

let plus_button_xpath =
'//*[@id="main"]/footer/div[1]/div/span[2]/div/div[1]/div[2]/div/div/div/span';
try {
let plus_button = await this.page.waitForXPath(plus_button_xpath);
// @ts-ignore
await plus_button.click();
} catch (error) {
release();
return false;
}

for (let i = 0; i < 5; i++) {
await this.page.keyboard.press('ArrowUp');
await this.page.waitForTimeout(500);
}

const [fileChooser] = await Promise.all([
this.page.waitForFileChooser(),
this.page.evaluate(() => {
// @ts-ignore
document.activeElement.click();
})
]);
await fileChooser.accept([fileName]);

await this.page.waitForTimeout(1000);
await this.typeMultiLine(caption);
await this.page.keyboard.press('Enter');

release();
return true;
}

/**
* Sends a text message to given chat, by injecting keystrokes
* @param to chat id: [email protected]
* @param content text message
*/
public async sendTextViaTyping(to: string, content: string) {
let release = await this.typingMutex.acquire();
let success = await this.selectChatViaTyping(to);
if (success) {
release();
return false;
}

await this.typeMultiLine(content);
await this.page.keyboard.press('Enter');
release();
return true;
}
}
48 changes: 2 additions & 46 deletions src/api/layers/sender.layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,20 @@ import {
import { filenameFromMimeType } from '../helpers/filename-from-mimetype';
import { Message, SendFileResult, SendStickerResult } from '../model';
import { ChatState } from '../model/enum';
import { ListenerLayer } from './listener.layer';
import { AutomateLayer } from './AutomateLayer';
import { Scope, checkValuesSender } from '../helpers/layers-interface';
import { Mutex } from 'async-mutex';

let obj: Scope;

export class SenderLayer extends ListenerLayer {
private typingMutex: Mutex;
export class SenderLayer extends AutomateLayer {
constructor(
public browser: Browser,
public page: Page,
session?: string,
options?: CreateConfig
) {
super(browser, page, session, options);
this.typingMutex = new Mutex();
}

public async createCommunity(name: string, description: string) {
Expand Down Expand Up @@ -366,48 +364,6 @@ export class SenderLayer extends ListenerLayer {
});
}

public async sendTextViaTyping(
to: string,
content: string
): Promise<boolean> {
const xpath = '//*[@id="side"]/div[1]/div/div[2]/div[2]/div/div[1]/p';
let ids = await this.page.evaluate(() => {
return WAPI.getAllChatIds();
});
if (!ids.includes(to)) {
return false;
}

let release = await this.typingMutex.acquire();
try {
let search_element = await this.page.waitForXPath(xpath);
// @ts-ignore
await search_element.click();
let phone_number = to.replace('@c.us', '');
await this.page.keyboard.type(' ' + phone_number + '\n', { delay: 200 });

content = content.replace('\r', '\n');
const lines = content.split(/\r?\n/);

for (let index = 0; index < lines.length; index++) {
let line = lines[index];
await this.page.keyboard.type(line, { delay: 200 });
await this.page.keyboard.down('Shift');
// Press the Enter key while the Shift key is down
await this.page.keyboard.press('Enter');
// Release the Shift key
await this.page.keyboard.up('Shift');
}

await this.page.keyboard.press('Enter');
release();
return true;
} catch (e) {
release();
return false;
}
}

/**
* Sends a text message to given chat
* @param to chat id: [email protected]
Expand Down
Loading