Got rid of WAClient, deprecated code. Prep for V3

Layered classes based on hierarchy as well.
This commit is contained in:
Adhiraj
2020-08-16 17:51:29 +05:30
parent 1360bef9bb
commit 002d304041
23 changed files with 803 additions and 869 deletions

28
src/Tests/Common.ts Normal file
View File

@@ -0,0 +1,28 @@
import { WAConnection, MessageLogLevel, MessageOptions, MessageType } from '../WAConnection/WAConnection'
import * as assert from 'assert'
import fs from 'fs/promises'
require ('dotenv').config () // dotenv to load test jid
export const testJid = process.env.TEST_JID || '1234@s.whatsapp.net' // set TEST_JID=xyz@s.whatsapp.net in a .env file in the root directory
export async function sendAndRetreiveMessage(conn: WAConnection, content, type: MessageType, options: MessageOptions = {}) {
const response = await conn.sendMessage(testJid, content, type, options)
const messages = await conn.loadConversation(testJid, 10, null, true)
const message = messages.find (m => m.key.id === response.key.id)
assert.ok(message)
return message
}
export function WAConnectionTest(name: string, func: (conn: WAConnection) => void) {
describe(name, () => {
const conn = new WAConnection()
conn.logLevel = MessageLogLevel.info
before(async () => {
const file = './auth_info.json'
await conn.connectSlim(file)
await fs.writeFile(file, JSON.stringify(conn.base64EncodedAuthInfo(), null, '\t'))
})
after(() => conn.close())
func(conn)
})
}

89
src/Tests/Tests.Binary.ts Normal file
View File

@@ -0,0 +1,89 @@
import { strict as assert } from 'assert'
import Encoder from '../Binary/Encoder'
import Decoder from '../Binary/Decoder'
describe('Binary Coding Tests', () => {
const testVectors: [string, Object][] = [
[
'f806092f5a0a10f804f80234fc6c0a350a1b39313735323938373131313740732e77686174736170702e6e657410011a143345423030393637354537454433374141424632122b0a292a7069616e6f20726f6f6d2074696d696e6773206172653a2a0a20363a3030414d2d31323a3030414d18b3faa7f3052003f80234fc4c0a410a1b39313735323938373131313740732e77686174736170702e6e657410001a20304643454335333330463634393239433645394132434646443242433845414418bdfaa7f305c00101f80234fc930a350a1b39313735323938373131313740732e77686174736170702e6e657410011a14334542303033433742353339414644303937353312520a50536f727279206672656e2c204920636f756c646e277420756e6465727374616e6420274c69627261272e2054797065202768656c702720746f206b6e6f77207768617420616c6c20492063616e20646f18c1faa7f3052003f80234fc540a410a1b39313735323938373131313740732e77686174736170702e6e657410001a20413132333042384436423041314437393345433241453245413043313638443812090a076c69627261727918c2faa7f305',
[
'action',
{ last: 'true', add: 'before' },
[
[
'message',
null,
{
key: { remoteJid: '917529871117@s.whatsapp.net', fromMe: true, id: '3EB009675E7ED37AABF2' },
message: { conversation: '*piano room timings are:*\n 6:00AM-12:00AM' },
messageTimestamp: '1584004403',
status: 'DELIVERY_ACK',
},
],
[
'message',
null,
{
key: {
remoteJid: '917529871117@s.whatsapp.net',
fromMe: false,
id: '0FCEC5330F64929C6E9A2CFFD2BC8EAD',
},
messageTimestamp: '1584004413',
messageStubType: 'REVOKE',
},
],
[
'message',
null,
{
key: { remoteJid: '917529871117@s.whatsapp.net', fromMe: true, id: '3EB003C7B539AFD09753' },
message: {
conversation:
"Sorry fren, I couldn't understand 'Libra'. Type 'help' to know what all I can do",
},
messageTimestamp: '1584004417',
status: 'DELIVERY_ACK',
},
],
[
'message',
null,
{
key: {
remoteJid: '917529871117@s.whatsapp.net',
fromMe: false,
id: 'A1230B8D6B0A1D793EC2AE2EA0C168D8',
},
message: { conversation: 'library' },
messageTimestamp: '1584004418',
},
],
],
],
],
[
'f8063f2dfafc0831323334353637385027fc0431323334f801f80228fc0701020304050607',
[
'picture',
{jid: '12345678@c.us', id: '1234'},
[['image', null, Buffer.from([1,2,3,4,5,6,7])]]
]
]
]
const encoder = new Encoder()
const decoder = new Decoder()
it('should decode strings', () => {
testVectors.forEach(pair => {
const buff = Buffer.from(pair[0], 'hex')
const decoded = decoder.read(buff)
assert.deepEqual(JSON.stringify(decoded), JSON.stringify(pair[1]))
const encoded = encoder.write(decoded)
assert.deepEqual(encoded, buff)
})
console.log('all coding tests passed')
})
})

102
src/Tests/Tests.Connect.ts Normal file
View File

@@ -0,0 +1,102 @@
import * as assert from 'assert'
import * as QR from 'qrcode-terminal'
import {WAConnection} from '../WAConnection/WAConnection'
import { AuthenticationCredentialsBase64 } from '../WAConnection/Constants'
import { createTimeout } from '../WAConnection/Utils'
describe('QR Generation', () => {
it('should generate QR', async () => {
const conn = new WAConnection()
let calledQR = false
conn.onReadyForPhoneAuthentication = ([ref, curveKey, clientID]) => {
assert.ok(ref, 'ref nil')
assert.ok(curveKey, 'curve key nil')
assert.ok(clientID, 'client ID nil')
calledQR = true
}
await assert.rejects(async () => conn.connectSlim(null, 5000), 'should have failed connect')
assert.equal(calledQR, true, 'QR not called')
})
})
describe('Test Connect', () => {
let auth: AuthenticationCredentialsBase64
it('should connect', async () => {
console.log('please be ready to scan with your phone')
const conn = new WAConnection()
const user = await conn.connectSlim(null)
assert.ok(user)
assert.ok(user.id)
conn.close()
auth = conn.base64EncodedAuthInfo()
})
it('should re-generate QR & connect', async () => {
const conn = new WAConnection()
conn.onReadyForPhoneAuthentication = async ([ref, publicKey, clientID]) => {
for (let i = 0; i < 2; i++) {
console.log ('called QR ' + i + ' times')
await createTimeout (3000)
ref = await conn.generateNewQRCode ()
}
const str = ref + ',' + publicKey + ',' + clientID
QR.generate(str, { small: true })
}
const user = await conn.connectSlim(null)
assert.ok(user)
assert.ok(user.id)
conn.close()
})
it('should reconnect', async () => {
const conn = new WAConnection()
const [user, chats, contacts] = await conn.connect(auth, 20*1000)
assert.ok(user)
assert.ok(user.id)
assert.ok(chats)
const chatArray = chats.all()
if (chatArray.length > 0) {
assert.ok(chatArray[0].jid)
assert.ok(chatArray[0].count !== null)
if (chatArray[0].messages.length > 0) {
assert.ok(chatArray[0].messages[0])
}
}
assert.ok(contacts)
if (contacts.length > 0) {
assert.ok(contacts[0].jid)
}
await conn.logout()
await assert.rejects(async () => conn.connectSlim(auth), 'reconnect should have failed')
})
})
describe ('Pending Requests', async () => {
it('should queue requests when closed', async () => {
const conn = new WAConnection ()
conn.pendingRequestTimeoutMs = null
await conn.connectSlim ()
await createTimeout (2000)
conn.close ()
const task: Promise<any> = new Promise ((resolve, reject) => {
conn.query(['query', 'Status', conn.userMetaData.id])
.then (json => resolve(json))
.catch (error => reject ('should not have failed, got error: ' + error))
})
await createTimeout (2000)
await conn.connectSlim ()
const json = await task
assert.ok (json.status)
conn.close ()
})
})

59
src/Tests/Tests.Groups.ts Normal file
View File

@@ -0,0 +1,59 @@
import { MessageType, GroupSettingChange, createTimeout, ChatModification } from '../WAConnection/WAConnection'
import * as assert from 'assert'
import { WAConnectionTest, testJid } from './Common'
WAConnectionTest('Groups', (conn) => {
let gid: string
it('should create a group', async () => {
const response = await conn.groupCreate('Cool Test Group', [testJid])
gid = response.gid
console.log('created group: ' + JSON.stringify(response))
})
it('should retreive group invite code', async () => {
const code = await conn.groupInviteCode(gid)
assert.ok(code)
assert.strictEqual(typeof code, 'string')
})
it('should retreive group metadata', async () => {
const metadata = await conn.groupMetadata(gid)
assert.strictEqual(metadata.id, gid)
assert.strictEqual(metadata.participants.filter((obj) => obj.id.split('@')[0] === testJid.split('@')[0]).length, 1)
})
it('should update the group description', async () => {
const newDesc = 'Wow this was set from Baileys'
await conn.groupUpdateDescription (gid, newDesc)
await createTimeout (1000)
const metadata = await conn.groupMetadata(gid)
assert.strictEqual(metadata.desc, newDesc)
})
it('should send a message on the group', async () => {
await conn.sendMessage(gid, 'hello', MessageType.text)
})
it('should update the subject', async () => {
const subject = 'V Cool Title'
await conn.groupUpdateSubject(gid, subject)
const metadata = await conn.groupMetadata(gid)
assert.strictEqual(metadata.subject, subject)
})
it('should update the group settings', async () => {
await conn.groupSettingChange (gid, GroupSettingChange.messageSend, true)
await createTimeout (5000)
await conn.groupSettingChange (gid, GroupSettingChange.settingsChange, true)
})
it('should remove someone from a group', async () => {
await conn.groupRemove(gid, [testJid])
})
it('should leave the group', async () => {
await conn.groupLeave(gid)
await conn.groupMetadataMinimal (gid)
})
it('should archive the group', async () => {
await conn.modifyChat(gid, ChatModification.archive)
})
it('should delete the group', async () => {
await conn.deleteChat(gid)
})
})

View File

@@ -0,0 +1,68 @@
import { MessageType, Mimetype, createTimeout } from '../WAConnection/WAConnection'
import fs from 'fs/promises'
import * as assert from 'assert'
import { WAConnectionTest, testJid, sendAndRetreiveMessage } from './Common'
WAConnectionTest('Messages', (conn) => {
it('should send a text message', async () => {
const message = await sendAndRetreiveMessage(conn, 'hello fren', MessageType.text)
assert.strictEqual(message.message.conversation, 'hello fren')
})
it('should forward a message', async () => {
let messages = await conn.loadConversation (testJid, 1)
await conn.forwardMessage (testJid, messages[0], true)
messages = await conn.loadConversation (testJid, 1)
const message = messages[0]
const content = message.message[ Object.keys(message.message)[0] ]
assert.equal (content?.contextInfo?.isForwarded, true)
})
it('should send a link preview', async () => {
const content = await conn.generateLinkPreview ('hello this is from https://www.github.com/adiwajshing/Baileys')
const message = await sendAndRetreiveMessage(conn, content, MessageType.text)
const received = message.message.extendedTextMessage
assert.strictEqual(received.text, content.text)
assert.ok (received.canonicalUrl)
assert.ok (received.title)
assert.ok (received.jpegThumbnail)
})
it('should quote a message', async () => {
const messages = await conn.loadConversation(testJid, 2)
const message = await sendAndRetreiveMessage(conn, 'hello fren 2', MessageType.extendedText, {
quoted: messages[0],
})
assert.strictEqual(message.message.extendedTextMessage.contextInfo.stanzaId, messages[0].key.id)
})
it('should send a gif', async () => {
const content = await fs.readFile('./Media/ma_gif.mp4')
const message = await sendAndRetreiveMessage(conn, content, MessageType.video, { mimetype: Mimetype.gif })
await conn.downloadAndSaveMediaMessage(message,'./Media/received_vid')
})
it('should send an image', async () => {
const content = await fs.readFile('./Media/meme.jpeg')
const message = await sendAndRetreiveMessage(conn, content, MessageType.image)
await conn.downloadMediaMessage(message)
//const message2 = await sendAndRetreiveMessage (conn, 'this is a quote', MessageType.extendedText)
})
it('should send an image & quote', async () => {
const messages = await conn.loadConversation(testJid, 1)
const content = await fs.readFile('./Media/meme.jpeg')
const message = await sendAndRetreiveMessage(conn, content, MessageType.image, { quoted: messages[0] })
await conn.downloadMediaMessage(message) // check for successful decoding
assert.strictEqual(message.message.imageMessage.contextInfo.stanzaId, messages[0].key.id)
})
it('should send a text message & delete it', async () => {
const message = await sendAndRetreiveMessage(conn, 'hello fren', MessageType.text)
await createTimeout (2000)
await conn.deleteMessage (testJid, message.key)
})
it('should clear the most recent message', async () => {
const messages = await conn.loadConversation (testJid, 1)
await createTimeout (2000)
await conn.clearMessage (messages[0].key)
})
})

113
src/Tests/Tests.Misc.ts Normal file
View File

@@ -0,0 +1,113 @@
import { MessageType, Presence, ChatModification, promiseTimeout, createTimeout } from '../WAConnection/WAConnection'
import fs from 'fs/promises'
import * as assert from 'assert'
import fetch from 'node-fetch'
import { WAConnectionTest, testJid } from './Common'
WAConnectionTest('Presence', (conn) => {
it('should update presence', async () => {
const presences = Object.values(Presence)
for (const i in presences) {
const response = await conn.updatePresence(testJid, presences[i])
assert.strictEqual(response.status, 200)
await createTimeout(1500)
}
})
})
WAConnectionTest('Misc', (conn) => {
it('should tell if someone has an account on WhatsApp', async () => {
const response = await conn.isOnWhatsApp(testJid)
assert.strictEqual(response, true)
const responseFail = await conn.isOnWhatsApp('abcd@s.whatsapp.net')
assert.strictEqual(responseFail, false)
})
it('should return the status', async () => {
const response = await conn.getStatus(testJid)
assert.strictEqual(typeof response.status, 'string')
})
it('should update status', async () => {
const newStatus = 'v cool status'
const response = await conn.getStatus()
assert.strictEqual(typeof response.status, 'string')
await createTimeout (1000)
await conn.setStatus (newStatus)
const response2 = await conn.getStatus()
assert.equal (response2.status, newStatus)
await createTimeout (1000)
await conn.setStatus (response.status) // update back
})
it('should return the stories', async () => {
await conn.getStories()
})
it('should change the profile picture', async () => {
await createTimeout (5000)
const ppUrl = await conn.getProfilePicture(conn.userMetaData.id)
const fetched = await fetch(ppUrl, { headers: { Origin: 'https://web.whatsapp.com' } })
const buff = await fetched.buffer ()
const newPP = await fs.readFile ('./Media/cat.jpeg')
const response = await conn.updateProfilePicture (conn.userMetaData.id, newPP)
await createTimeout (10000)
await conn.updateProfilePicture (conn.userMetaData.id, buff) // revert back
})
it('should return the profile picture', async () => {
const response = await conn.getProfilePicture(testJid)
assert.ok(response)
assert.rejects(conn.getProfilePicture('abcd@s.whatsapp.net'))
})
it('should send typing indicator', async () => {
const response = await conn.updatePresence(testJid, Presence.composing)
assert.ok(response)
})
it('should mark a chat unread', async () => {
await conn.sendReadReceipt(testJid, null, 'unread')
})
it('should archive & unarchive', async () => {
await conn.modifyChat (testJid, ChatModification.archive)
await createTimeout (2000)
await conn.modifyChat (testJid, ChatModification.unarchive)
})
it('should pin & unpin a chat', async () => {
const response = await conn.modifyChat (testJid, ChatModification.pin)
await createTimeout (2000)
await conn.modifyChat (testJid, ChatModification.unpin, {stamp: response.stamp})
})
it('should mute & unmute a chat', async () => {
const mutedate = new Date (new Date().getTime() + 8*60*60*1000) // 8 hours in the future
await conn.modifyChat (testJid, ChatModification.mute, {stamp: mutedate})
await createTimeout (2000)
await conn.modifyChat (testJid, ChatModification.unmute, {stamp: mutedate})
})
it('should return search results', async () => {
const jids = [null, testJid]
for (let i in jids) {
const response = await conn.searchMessages('Hello', jids[i], 25, 1)
assert.ok (response.messages)
assert.ok (response.messages.length >= 0)
}
})
})
WAConnectionTest('Events', (conn) => {
it('should deliver a message', async () => {
const waitForUpdate = () =>
new Promise((resolve) => {
conn.setOnMessageStatusChange((update) => {
if (update.ids.includes(response.key.id)) {
resolve()
}
})
})
const response = await conn.sendMessage(testJid, 'My Name Jeff', MessageType.text)
await promiseTimeout(15000, waitForUpdate())
})
})