Skip to content

Commit

Permalink
feat: Added WPP.call.offer function
Browse files Browse the repository at this point in the history
  • Loading branch information
edgardmessias committed Mar 29, 2023
1 parent 240d07b commit aa53282
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/call/functions/index.ts
Expand Up @@ -15,3 +15,4 @@
*/

export { rejectCall } from './rejectCall';
export { sendCallOffer } from './sendCallOffer';
165 changes: 165 additions & 0 deletions src/call/functions/sendCallOffer.ts
@@ -0,0 +1,165 @@
/*!
* Copyright 2023 WPPConnect Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { assertWid } from '../../assert';
import { WPPError } from '../../util';
import {
functions,
multidevice,
UserPrefs,
websocket,
Wid,
} from '../../whatsapp';

export interface CallOfferOptions {
isVideo?: boolean;
}

/**
* Send a call offer
*
* This method only will send a call offer, but there are no audio/video
*
* @example
* ```javascript
* // Send a call offer
* WPP.call.sendCallOffer('[number]@c.us');
* // Send a video call offer
* WPP.call.sendCallOffer('[number]@c.us', {isVideo: true});
* ```
*/
export async function sendCallOffer(
to: string | Wid,
options: CallOfferOptions = {}
): Promise<any> {
options = Object.assign<CallOfferOptions, CallOfferOptions>(
{ isVideo: false },
options
);

const toWid = assertWid(to);

if (!toWid.isUser()) {
throw new WPPError(
'call_is_not_user',
`The ${toWid} is not a user to call`,
{
to,
}
);
}

const callId = functions.randomHex(16).substr(0, 64);
const me = UserPrefs.assertGetMe();

const content = [
websocket.smax('audio', { enc: 'opus', rate: '16000' }, null),
websocket.smax('audio', { enc: 'opus', rate: '8000' }, null),
];

if (options.isVideo) {
content.push(
websocket.smax(
'video',
{
orientation: '0',
screen_width: '1920',
screen_height: '1080',
device_orientation: '0',
enc: 'vp8',
dec: 'vp8',
},
null
)
);
}

content.push(
...[
websocket.smax('net', { medium: '3' }, null),
websocket.smax(
'capability',
{ ver: '1' },
new Uint8Array([1, 4, 255, 131, 207, 4])
),
websocket.smax('encopt', { keygen: '2' }, null),
]
);

const fanList = await functions.getFanOutList({ wids: [toWid] });
await websocket.ensureE2ESessions(fanList);

let shouldHaveIdentity = false;
const destination = await Promise.all(
fanList.map(async (wid) => {
const encKey = self.crypto.getRandomValues(new Uint8Array(32)).buffer;

const { type, ciphertext } = await functions.encryptMsgProtobuf(wid, 0, {
call: {
callKey: new Uint8Array(encKey),
},
});

shouldHaveIdentity = shouldHaveIdentity || type === 'pkmsg';

return websocket.smax(
'to',
{
jid: wid.toString({ legacy: true }),
},
[
websocket.smax(
'enc',
{
v: '2',
type: type,
count: '0',
},
ciphertext
),
]
);
})
);
content.push(websocket.smax('destination', {}, destination));

if (shouldHaveIdentity) {
const identity = await multidevice.adv.getADVEncodedIdentity();
content.push(websocket.smax('device-identity', undefined, identity));
}

const node = websocket.smax(
'call',
{
to: toWid.toString({ legacy: true }),
id: functions.randomHex(8),
},
[
websocket.smax(
'offer',
{
'call-id': callId,
'call-creator': me.toString({ legacy: true }),
},
content
),
]
);

const response = await websocket.sendSmaxStanza(node);

return response;
}
35 changes: 35 additions & 0 deletions src/whatsapp/functions/encryptMsgProtobuf.ts
@@ -0,0 +1,35 @@
/*!
* Copyright 2023 WPPConnect Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { exportModule } from '../exportModule';
import { Wid } from '../misc';

/**
* @whatsapp 309029 >= 2.2312.7
*/
export declare function encryptMsgProtobuf(
wid: Wid,
retryCount: number,
protoMessage: any
): Promise<{ type: string; ciphertext: Uint8Array }>;

exportModule(
exports,
{
encryptMsgProtobuf: 'encryptMsgProtobuf',
},
(m) => m.encryptMsgProtobuf
);
1 change: 1 addition & 0 deletions src/whatsapp/functions/index.ts
Expand Up @@ -27,6 +27,7 @@ export * from './editBusinessProfile';
export * from './encodeMaybeMediaType';
export * from './encryptAndSendGroupMsg';
export * from './encryptAndSendMsg';
export * from './encryptMsgProtobuf';
export * from './fetchLinkPreview';
export * from './findChat';
export * from './findFirstWebLink';
Expand Down

0 comments on commit aa53282

Please sign in to comment.