Skip to content
This repository has been archived by the owner on Nov 7, 2023. It is now read-only.

Login with phoneNumber #816

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ CLIENT_VERSION='4.0.0'
# INSTANCE CONFIGURATION
# ==================================
INSTANCE_MAX_RETRY_QR=2
NATIVE_MOBILE_AUTH=false

# ==================================
# DATABASE CONFIGURATION
Expand Down
5 changes: 3 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,17 @@ FROM node:19-alpine

ARG _WORKDIR=/home/node/app
ARG PORT=3333
ENV MONGODB_URL mongodb://mongo:27017/whatsapp_api

USER root
RUN apk add git

WORKDIR ${_WORKDIR}

ADD . ${_WORKDIR}
RUN yarn install
RUN npm install

USER node
EXPOSE ${PORT}

CMD yarn start
CMD npm start
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Maintained by Jeanlucafp
This API is a forked from @salman0ansari and maintained by me.

# Archive Notice 🔒
After three years, I've decided to archive this open-source WhatsApp API project. Your support and contributions have been incredible!

Expand Down
16 changes: 6 additions & 10 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ services:
mongodb:
container_name: mongodb
image: mongo:latest
restart: unless-stopped
ports:
- 27017:27017
volumes:
Expand All @@ -15,21 +14,18 @@ services:
context: .
dockerfile: Dockerfile
args:
- PORT=${PORT}
- PORT=3333
depends_on:
- mongodb
restart: unless-stopped
env_file: .env
ports:
- ${PORT}:${PORT}
- 3333:3333
environment:
- TOKEN=${TOKEN}
- PORT=${PORT}
- MONGODB_ENABLED=${MONGODB_ENABLED}
- PORT=3333
- MONGODB_ENABLED=true
- MONGODB_URL=mongodb://mongodb:27017
- WEBHOOK_ENABLED=${WEBHOOK_ENABLED}
- WEBHOOK_URL=${WEBHOOK_URL}
- WEBHOOK_BASE64=${WEBHOOK_BASE64}
- WEBHOOK_ENABLED=false
- LOG_LEVEL=trace
volumes:
- ./:/home/node/app
- /home/node/app/node_modules/
Expand Down
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,21 @@
"author": "Mohd Salman Ansari <[email protected]>",
"license": "MIT",
"dependencies": {
"@whiskeysockets/baileys": "^6.1.0",
"@adiwajshing/keyed-db": "^0.2.4",
"@whiskeysockets/baileys": "^6.7.7",
"axios": "^1.1.3",
"dotenv": "^16.0.3",
"ejs": "^3.1.7",
"express": "^4.18.2",
"express": "^4.21.0",
"express-exception-handler": "^1.3.23",
"libphonenumber-js": "^1.10.45",
"link-preview-js": "^3.0.0",
"mongodb": "^4.12.1",
"mongoose": "^6.7.4",
"multer": "^1.4.5-lts.1",
"pino": "^8.7.0",
"qrcode": "^1.5.1",
"sharp": "^0.30.5",
"sharp": "^0.32.6",
"uuid": "^9.0.0"
},
"devDependencies": {
Expand All @@ -54,7 +55,7 @@
"husky": "^8.0.2",
"lint-staged": "^13.0.4",
"mocha": "^10.1.0",
"nodemon": "^2.0.20",
"nodemon": "^3.1.4",
"prettier": "^2.8.0",
"supertest": "^6.3.1"
}
Expand Down
100 changes: 100 additions & 0 deletions src/api/class/audit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
const useMongoDBAuthState = require('../helper/mongoAuthState');
const logger = require('pino')()

function statusHandler(numberStatus) {
switch (numberStatus) {
case 1:
return 'pending'
case 3:
return 'played'
default:
return 'unknown'
}
}

class AuditMessageType {
constructor(data) {
this.key = data.key;
this.remoteJid = data.key.remoteJid;
this.identificator = data.id;
this.id = data.key.id;
this.messag = data.message;
this.status = statusHandler(data.status);
this.messageTimestamp = data.messageTimestamp;
}
}

class AuditMessages {
constructor(id) {
this.messages = [];
this.collectionName = 'audit_messages';
this.id = id;
}

find(query) {
const collection = mongoClient.db('whatsapp-api').collection(this.collectionName)
return useMongoDBAuthState(collection)
.then(({ find }) => {
return find(query)
})
}

findHistory(remoteJid) {
const findes = this.messages.filter(message => message.remoteJid === remoteJid)
if (findes.length > 0) {
return new Promise(resolve => resolve(findes))
}

const collection = mongoClient.db('whatsapp-api').collection(this.collectionName)
return useMongoDBAuthState(collection)
.then(({ find }) => {
return find({ remoteJid })
})
}

append(message) {
const messageStruct = new AuditMessageType({
key: message.key,
message: message.message,
status: message.status,
messageTimestamp: message.messageTimestamp,
id: this.id,
remoteJid: message.key.remoteJid,
});

const collection = mongoClient.db('whatsapp-api').collection(this.collectionName)
useMongoDBAuthState(collection)
.then(({ insertData }) => {
insertData(messageStruct)
.then((result) => {
logger.info('Audit message requested save saved', result)
this.messages.push(messageStruct);
})
})
}

update(message) {
const remoteJid = message?.key?.remoteJid
if (remoteJid) {
this.findHistory(remoteJid)
.then(entities => {
const collection = mongoClient.db('whatsapp-api').collection(this.collectionName)
const id = message.key.id;
entities.forEach(entitie => {
if (entitie.id === id) {
useMongoDBAuthState(collection).then(({ updateOne }) => {
updateOne(
{ id: entitie.id, remoteJid: entitie.remoteJid },
{ $set: { status: statusHandler(message.update.status), updateAt: new Date() } }
)
})

}

})
})
}
}
}

exports.AuditMessages = new AuditMessages();
94 changes: 87 additions & 7 deletions src/api/class/instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ const pino = require('pino')
const {
default: makeWASocket,
DisconnectReason,
PHONENUMBER_MCC
} = require('@whiskeysockets/baileys')
const { unlinkSync } = require('fs')
const { v4: uuidv4 } = require('uuid')
const path = require('path')
const processButton = require('../helper/processbtn')
const generateVC = require('../helper/genVc')
const Chat = require('../models/chat.model')
Expand All @@ -16,11 +15,15 @@ const config = require('../../config/config')
const downloadMessage = require('../helper/downloadMsg')
const logger = require('pino')()
const useMongoDBAuthState = require('../helper/mongoAuthState')
const { AuditMessages } = require('./audit')
const libPhonenumber = require('libphonenumber-js')
const TypeBot = require('./typebot')

class WhatsAppInstance {
socketConfig = {
defaultQueryTimeoutMs: undefined,
printQRInTerminal: false,
mobile: config.instance.useMobileAuth,
logger: pino({
level: config.log.level,
}),
Expand All @@ -37,6 +40,7 @@ class WhatsAppInstance {
messages: [],
qrRetry: 0,
customWebhook: '',
typebot: null
}

axiosInstance = axios.create({
Expand Down Expand Up @@ -71,15 +75,22 @@ class WhatsAppInstance {

async init() {
this.collection = mongoClient.db('whatsapp-api').collection(this.key)
const { state, saveCreds } = await useMongoDBAuthState(this.collection)
this.authState = { state: state, saveCreds: saveCreds }
const { state, saveCreds, writeData } = await useMongoDBAuthState(this.collection)
this.authState = { state: state, saveCreds: saveCreds, writeData }
this.socketConfig.auth = this.authState.state
this.socketConfig.browser = Object.values(config.browser)
this.instance.sock = makeWASocket(this.socketConfig)
this.setHandler()
return this
}

async activeTypeBot(apiHost, typebotname, saveData = true) {
const typebot = new TypeBot(apiHost, typebotname, this)
this.instance.typebot = typebot
if (saveData)
await this.authState.writeData(typebot.getObjectToSave(), 'typebot')
}

setHandler() {
const sock = this.instance.sock
// on credentials update save state
Expand Down Expand Up @@ -302,13 +313,40 @@ class WhatsAppInstance {
)
)
await this.SendWebhook('message', webhookData, this.key)

if (this.instance.typebot) {
// extendedTextMessage quando recebe mensagem do web-whatsapp
if (messageType === 'extendedTextMessage') {
await this.instance.typebot.startTypebot({
message: msg.message.extendedTextMessage.text,
remoteJid: msg.key.remoteJid
})
}

// conversation quando recebe mensagem do celular
if (messageType === 'conversation') {
await this.instance.typebot.startTypebot({
message: msg.message.conversation,
remoteJid: msg.key.remoteJid
})
}

if (messageType === 'templateButtonReplyMessage') {
await this.instance.typebot.startTypebot({
message: TypeBot.giveMeTextButtonAndIGiveUId(msg.message.templateButtonReplyMessage.selectedDisplayText),
remoteJid: msg.key.remoteJid
})
}
}
})
})

sock?.ev.on('messages.update', async (messages) => {
//console.log('messages.update')
//console.dir(messages);
messages.forEach(element => {
AuditMessages.update(element)
})
})

sock?.ws.on('CB:call', async (data) => {
if (data.content) {
if (data.content.find((e) => e.tag === 'offer')) {
Expand Down Expand Up @@ -429,6 +467,10 @@ class WhatsAppInstance {
phone_connected: this.instance?.online,
webhookUrl: this.instance.customWebhook,
user: this.instance?.online ? this.instance.sock?.user : {},
typebot: this.instance?.typebot ? {
apiHost: this.instance.typebot.apiHost,
typebotName: this.instance.typebot.typebotName,
} : {},
}
}

Expand Down Expand Up @@ -471,8 +513,9 @@ class WhatsAppInstance {
async sendUrlMediaFile(to, url, type, mimeType, caption = '') {
await this.verifyId(this.getWhatsAppId(to))

const receiver = this.getWhatsAppId(to)
const data = await this.instance.sock?.sendMessage(
this.getWhatsAppId(to),
receiver,
{
[type]: {
url: url,
Expand Down Expand Up @@ -1000,6 +1043,43 @@ class WhatsAppInstance {
logger.error('Error react message failed')
}
}

async enterRegistrationCode(code) {
const response = await this.instance.sock?.register(code.replace(/["']/g, '').trim().toLowerCase())
logger.info('Successfully registered your phone number.')
return response
}

async sendCodeRegistrationToWhatsappNumber(phoneNumber) {
const parsedNumber = libPhonenumber.parsePhoneNumberWithError(phoneNumber)
if (!parsedNumber.isValid()) {
throw new Error('Invalid phone number: ' + phoneNumber)
}

const registration = {}
registration.phoneNumber = parsedNumber.format('E.164')
registration.phoneNumberCountryCode = parsedNumber.countryCallingCode
registration.phoneNumberNationalNumber = parsedNumber.nationalNumber
const mcc = PHONENUMBER_MCC[parsedNumber.countryCallingCode]
if(!mcc) {
throw new Error('Could not find MCC for phone number: ' + phoneNumber + '\nPlease specify the MCC manually.')
}

registration.phoneNumberMobileCountryCode = mcc
registration.identityId = Buffer.from(registration.phoneNumber, 'utf-8')
registration.backupToken = Buffer.from('1243', 'utf-8')
await this.instance.sock?.requestRegistrationCode(registration)
}

async requestMobileAuthCode(phoneNumber) {
if (!config.instance.useMobileAuth) {
throw new Error('Cannot use pairing code with mobile api')
}
logger.info(`request pairing code for : ${phoneNumber}`)
const _code = await this.instance.sock?.requestPairingCode(phoneNumber)
logger.info(`Pairing code: ${_code}`)
return _code
}
}

exports.WhatsAppInstance = WhatsAppInstance
Loading