mirror of
https://github.com/FranP-code/Baileys.git
synced 2025-10-13 00:32:22 +00:00
* fix: registration + update mobile version * feat: add captcha support for registration * fix: jid protocol address missing device * fix: linting errors
251 lines
7.4 KiB
TypeScript
251 lines
7.4 KiB
TypeScript
/* eslint-disable camelcase */
|
|
import axios, { AxiosRequestConfig } from 'axios'
|
|
import { MOBILE_REGISTRATION_ENDPOINT, MOBILE_TOKEN, MOBILE_USERAGENT, REGISTRATION_PUBLIC_KEY } from '../Defaults'
|
|
import { KeyPair, SignedKeyPair, SocketConfig } from '../Types'
|
|
import { aesEncryptGCM, Curve, md5 } from '../Utils/crypto'
|
|
import { jidEncode } from '../WABinary'
|
|
import { makeBusinessSocket } from './business'
|
|
|
|
function urlencode(str: string) {
|
|
return str.replace(/-/g, '%2d').replace(/_/g, '%5f').replace(/~/g, '%7e')
|
|
}
|
|
|
|
const validRegistrationOptions = (config: RegistrationOptions) => config?.phoneNumberCountryCode &&
|
|
config.phoneNumberNationalNumber &&
|
|
config.phoneNumberMobileCountryCode
|
|
|
|
export const makeRegistrationSocket = (config: SocketConfig) => {
|
|
const sock = makeBusinessSocket(config)
|
|
|
|
const register = async(code: string) => {
|
|
if(!validRegistrationOptions(config.auth.creds.registration)) {
|
|
throw new Error('please specify the registration options')
|
|
}
|
|
|
|
const result = await mobileRegister({ ...sock.authState.creds, ...sock.authState.creds.registration as RegistrationOptions, code }, config.options)
|
|
|
|
sock.authState.creds.me = {
|
|
id: jidEncode(result.login!, 's.whatsapp.net'),
|
|
name: '~'
|
|
}
|
|
|
|
sock.authState.creds.registered = true
|
|
sock.ev.emit('creds.update', sock.authState.creds)
|
|
|
|
return result
|
|
}
|
|
|
|
const requestRegistrationCode = async(registrationOptions?: RegistrationOptions) => {
|
|
registrationOptions = registrationOptions || config.auth.creds.registration
|
|
if(!validRegistrationOptions(registrationOptions)) {
|
|
throw new Error('Invalid registration options')
|
|
}
|
|
|
|
sock.authState.creds.registration = registrationOptions
|
|
|
|
sock.ev.emit('creds.update', sock.authState.creds)
|
|
|
|
return mobileRegisterCode({ ...config.auth.creds, ...registrationOptions }, config.options)
|
|
}
|
|
|
|
return {
|
|
...sock,
|
|
register,
|
|
requestRegistrationCode,
|
|
}
|
|
}
|
|
|
|
// Backup_token: Base64.getEncoder().encodeToString(Arrays.copyOfRange(Base64.getDecoder().decode(UUID.randomUUID().toString().replace('-','')),0,15))
|
|
|
|
export interface RegistrationData {
|
|
registrationId: number
|
|
signedPreKey: SignedKeyPair
|
|
noiseKey: KeyPair
|
|
signedIdentityKey: KeyPair
|
|
identityId: Buffer
|
|
phoneId: string
|
|
deviceId: string
|
|
backupToken: Buffer
|
|
}
|
|
|
|
export interface RegistrationOptions {
|
|
/** your phone number */
|
|
phoneNumber?: string
|
|
/** the country code of your phone number */
|
|
phoneNumberCountryCode: string
|
|
/** your phone number without country code */
|
|
phoneNumberNationalNumber: string
|
|
/** the country code of your mobile network
|
|
* @see {@link https://de.wikipedia.org/wiki/Mobile_Country_Code}
|
|
*/
|
|
phoneNumberMobileCountryCode: string
|
|
/** the network code of your mobile network
|
|
* @see {@link https://de.wikipedia.org/wiki/Mobile_Network_Code}
|
|
*/
|
|
phoneNumberMobileNetworkCode: string
|
|
/**
|
|
* How to send the one time code
|
|
*/
|
|
method?: 'sms' | 'voice' | 'captcha'
|
|
/**
|
|
* The captcha code if it was requested
|
|
*/
|
|
captcha?: string
|
|
}
|
|
|
|
export type RegistrationParams = RegistrationData & RegistrationOptions
|
|
|
|
function convertBufferToUrlHex(buffer: Buffer) {
|
|
var id = ''
|
|
|
|
buffer.forEach((x) => {
|
|
// encode random identity_id buffer as percentage url encoding
|
|
id += `%${x.toString(16).padStart(2, '0').toLowerCase()}`
|
|
})
|
|
|
|
return id
|
|
}
|
|
|
|
export function registrationParams(params: RegistrationParams) {
|
|
const e_regid = Buffer.alloc(4)
|
|
e_regid.writeInt32BE(params.registrationId)
|
|
|
|
const e_skey_id = Buffer.alloc(3)
|
|
e_skey_id.writeInt16BE(params.signedPreKey.keyId)
|
|
|
|
params.phoneNumberCountryCode = params.phoneNumberCountryCode.replace('+', '').trim()
|
|
params.phoneNumberNationalNumber = params.phoneNumberNationalNumber.replace(/[/-\s)(]/g, '').trim()
|
|
|
|
return {
|
|
cc: params.phoneNumberCountryCode,
|
|
in: params.phoneNumberNationalNumber,
|
|
Rc: '0',
|
|
lg: 'en',
|
|
lc: 'GB',
|
|
mistyped: '6',
|
|
authkey: Buffer.from(params.noiseKey.public).toString('base64url'),
|
|
e_regid: e_regid.toString('base64url'),
|
|
e_keytype: 'BQ',
|
|
e_ident: Buffer.from(params.signedIdentityKey.public).toString('base64url'),
|
|
// e_skey_id: e_skey_id.toString('base64url'),
|
|
e_skey_id: 'AAAA',
|
|
e_skey_val: Buffer.from(params.signedPreKey.keyPair.public).toString('base64url'),
|
|
e_skey_sig: Buffer.from(params.signedPreKey.signature).toString('base64url'),
|
|
fdid: params.phoneId,
|
|
network_ratio_type: '1',
|
|
expid: params.deviceId,
|
|
simnum: '1',
|
|
hasinrc: '1',
|
|
pid: Math.floor(Math.random() * 1000).toString(),
|
|
id: convertBufferToUrlHex(params.identityId),
|
|
backup_token: convertBufferToUrlHex(params.backupToken),
|
|
token: md5(Buffer.concat([MOBILE_TOKEN, Buffer.from(params.phoneNumberNationalNumber)])).toString('hex'),
|
|
fraud_checkpoint_code: params.captcha,
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Requests a registration code for the given phone number.
|
|
*/
|
|
export function mobileRegisterCode(params: RegistrationParams, fetchOptions?: AxiosRequestConfig) {
|
|
return mobileRegisterFetch('/code', {
|
|
params: {
|
|
...registrationParams(params),
|
|
mcc: `${params.phoneNumberMobileCountryCode}`.padStart(3, '0'),
|
|
mnc: `${params.phoneNumberMobileNetworkCode || '001'}`.padStart(3, '0'),
|
|
sim_mcc: '000',
|
|
sim_mnc: '000',
|
|
method: params?.method || 'sms',
|
|
reason: '',
|
|
hasav: '1'
|
|
},
|
|
...fetchOptions,
|
|
})
|
|
}
|
|
|
|
export function mobileRegisterExists(params: RegistrationParams, fetchOptions?: AxiosRequestConfig) {
|
|
return mobileRegisterFetch('/exist', {
|
|
params: registrationParams(params),
|
|
...fetchOptions
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Registers the phone number on whatsapp with the received OTP code.
|
|
*/
|
|
export async function mobileRegister(params: RegistrationParams & { code: string }, fetchOptions?: AxiosRequestConfig) {
|
|
//const result = await mobileRegisterFetch(`/reg_onboard_abprop?cc=${params.phoneNumberCountryCode}&in=${params.phoneNumberNationalNumber}&rc=0`)
|
|
|
|
return mobileRegisterFetch('/register', {
|
|
params: { ...registrationParams(params), code: params.code.replace('-', '') },
|
|
...fetchOptions,
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Encrypts the given string as AEAD aes-256-gcm with the public whatsapp key and a random keypair.
|
|
*/
|
|
export function mobileRegisterEncrypt(data: string) {
|
|
const keypair = Curve.generateKeyPair()
|
|
const key = Curve.sharedKey(keypair.private, REGISTRATION_PUBLIC_KEY)
|
|
|
|
const buffer = aesEncryptGCM(Buffer.from(data), new Uint8Array(key), Buffer.alloc(12), Buffer.alloc(0))
|
|
|
|
return Buffer.concat([Buffer.from(keypair.public), buffer]).toString('base64url')
|
|
}
|
|
|
|
export async function mobileRegisterFetch(path: string, opts: AxiosRequestConfig = {}) {
|
|
let url = `${MOBILE_REGISTRATION_ENDPOINT}${path}`
|
|
|
|
if(opts.params) {
|
|
const parameter = [] as string[]
|
|
|
|
for(const param in opts.params) {
|
|
if(opts.params[param] !== null && opts.params[param] !== undefined) {
|
|
parameter.push(param + '=' + urlencode(opts.params[param]))
|
|
}
|
|
}
|
|
|
|
url += `?${parameter.join('&')}`
|
|
delete opts.params
|
|
}
|
|
|
|
if(!opts.headers) {
|
|
opts.headers = {}
|
|
}
|
|
|
|
opts.headers['User-Agent'] = MOBILE_USERAGENT
|
|
|
|
const response = await axios(url, opts)
|
|
|
|
var json = response.data
|
|
|
|
if(response.status > 300 || json.reason) {
|
|
throw json
|
|
}
|
|
|
|
if(json.status && !['ok', 'sent'].includes(json.status)) {
|
|
throw json
|
|
}
|
|
|
|
return json as ExistsResponse
|
|
}
|
|
|
|
|
|
export interface ExistsResponse {
|
|
status: 'fail' | 'sent'
|
|
voice_length?: number
|
|
voice_wait?: number
|
|
sms_length?: number
|
|
sms_wait?: number
|
|
reason?: 'incorrect' | 'missing_param' | 'code_checkpoint'
|
|
login?: string
|
|
flash_type?: number
|
|
ab_hash?: string
|
|
ab_key?: string
|
|
exp_cfg?: string
|
|
lid?: string
|
|
image_blob?: string
|
|
audio_blob?: string
|
|
}
|