Added link previews

This commit is contained in:
Adhiraj
2020-07-14 13:30:15 +05:30
parent 3f84325bad
commit 8ce1e2da08
5 changed files with 45 additions and 28 deletions

View File

@@ -1,5 +1,5 @@
import WAConnection from '../WAConnection/WAConnection'
import { MessageStatus, MessageStatusUpdate, PresenceUpdate, Presence, ChatModification, WABroadcastListInfo, WAUrlInfo } from './Constants'
import { MessageStatusUpdate, PresenceUpdate, Presence, WABroadcastListInfo } from './Constants'
import {
WAMessage,
WANode,
@@ -7,7 +7,6 @@ import {
WAFlag,
MessageLogLevel,
} from '../WAConnection/Constants'
import { proto } from '../../WAMessage/WAMessage'
export default class WhatsAppWebBase extends WAConnection {
/** Set the callback for message status updates (when a message is delivered, read etc.) */
@@ -186,13 +185,4 @@ export default class WhatsAppWebBase extends WAConnection {
const json = ['action', {epoch: this.msgCount.toString(), type: 'set'}, nodes]
return this.queryExpecting200(json, [WAMetric.group, WAFlag.ignore]) as Promise<{status: number}>
}
/** Query a string to check if it has a url, if it does, return required info */
async urlQuery (text: string) {
const query = ['query', {type: 'url', url: text, epoch: this.msgCount.toString()}, null]
const response = await this.queryExpecting200 (query, [26, WAFlag.ignore])
if (response[1]) {
response[1].jpegThumbnail = response[2]
}
return response[1] as WAUrlInfo
}
}

View File

@@ -136,3 +136,4 @@ export interface WALocationMessage {
}
export type WAContactMessage = proto.ContactMessage
export type WAMessageKey = proto.IMessageKey
export type WATextMessage = proto.ExtendedTextMessage

View File

@@ -12,9 +12,11 @@ import {
WAMessageKey,
ChatModification,
MessageInfo,
WATextMessage,
WAUrlInfo,
} from './Constants'
import { generateMessageID, sha256, hmacSign, aesEncrypWithIV, randomBytes } from '../WAConnection/Utils'
import { WAMessageContent, WAMetric, WAFlag, WANode, WAMessage } from '../WAConnection/Constants'
import { WAMessageContent, WAMetric, WAFlag, WANode, WAMessage, WAMessageProto } from '../WAConnection/Constants'
import { validateJIDForSending, generateThumbnail, getMediaKeys } from './Utils'
import { proto } from '../../WAMessage/WAMessage'
@@ -100,6 +102,23 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups {
const actual = await this.loadConversation (jid, 1, index)
return actual[0]
}
/** Query a string to check if it has a url, if it does, return required extended text message */
async generateLinkPreview (text: string) {
const query = ['query', {type: 'url', url: text, epoch: this.msgCount.toString()}, null]
const response = await this.queryExpecting200 (query, [26, WAFlag.ignore])
if (response[1]) response[1].jpegThumbnail = response[2]
const data = response[1] as WAUrlInfo
const content = {text} as WATextMessage
content.canonicalUrl = data['canonical-url']
content.matchedText = data['matched-text']
content.jpegThumbnail = data.jpegThumbnail
content.description = data.description
content.title = data.title
content.previewType = 0
return content
}
/**
* Search WhatsApp messages with a given text string
* @param txt the search string
@@ -170,21 +189,24 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups {
}
async sendMessage(
id: string,
message: string | WALocationMessage | WAContactMessage | Buffer,
message: string | WATextMessage | WALocationMessage | WAContactMessage | Buffer,
type: MessageType,
options: MessageOptions = {},
) {
if (options.validateID === true || !('validateID' in options)) {
validateJIDForSending (id)
}
let m: any = {}
let m: WAMessageContent = {}
switch (type) {
case MessageType.text:
case MessageType.extendedText:
if (typeof message !== 'string') {
throw new Error('expected message to be a string')
if (typeof message === 'string') {
m.extendedTextMessage = {text: message}
} else if ('text' in message) {
m.extendedTextMessage = message as WATextMessage
} else {
throw new Error ('message needs to be a string or object with property \'text\'')
}
m.extendedTextMessage = { text: message }
break
case MessageType.location:
case MessageType.liveLocation:
@@ -197,7 +219,7 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups {
m = await this.prepareMediaMessage(message as Buffer, type, options)
break
}
return this.sendGenericMessage(id, m as WAMessageContent, options)
return this.sendGenericMessage(id, m, options)
}
/** Prepare a media message for sending */
protected async prepareMediaMessage(buffer: Buffer, mediaType: MessageType, options: MessageOptions = {}) {
@@ -283,7 +305,7 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups {
}
}
message[key].caption = options?.caption
message[key].jpegThumbnail = options?.thumbnail
if (!message[key].jpegThumbnail) message[key].jpegThumbnail = options?.thumbnail
const messageJSON = {
key: {
@@ -293,7 +315,8 @@ export default class WhatsAppWebMessages extends WhatsAppWebGroups {
},
message: message,
messageTimestamp: timestamp,
participant: id.includes('@g.us') ? this.userMetaData.id : null
participant: id.includes('@g.us') ? this.userMetaData.id : null,
status: WAMessageProto.proto.WebMessageInfo.WEB_MESSAGE_INFO_STATUS.PENDING
}
const json = ['action', {epoch: this.msgCount.toString(), type: 'relay'}, [['message', null, messageJSON]]]
const response = await this.queryExpecting200(json, [WAMetric.message, WAFlag.ignore], null, messageJSON.key.id)

View File

@@ -33,6 +33,14 @@ WAClientTest('Messages', (client) => {
const message = await sendAndRetreiveMessage(client, 'hello fren', MessageType.text)
assert.strictEqual(message.message.conversation, 'hello fren')
})
it('should send a link preview', async () => {
const content = await client.generateLinkPreview ('hello this is from https://www.github.com/adiwajshing/Baileys')
const message = await sendAndRetreiveMessage(client, content, MessageType.text)
const received = message.message.extendedTextMessage
assert.strictEqual(received.text, content.text)
fs.writeFileSync ('Media/received-thumb.jpeg', content.jpegThumbnail)
})
it('should quote a message', async () => {
const messages = await client.loadConversation(testJid, 2)
const message = await sendAndRetreiveMessage(client, 'hello fren 2', MessageType.extendedText, {
@@ -109,12 +117,6 @@ WAClientTest('Misc', (client) => {
it('should return the stories', async () => {
await client.getStories()
})
it('should return a preview', async () => {
const info = await client.urlQuery ('fren have you seen https://www.github.com/adiwajshing/Baileys')
assert.equal (info["matched-text"], 'https://www.github.com/adiwajshing/Baileys')
await assert.rejects (() => client.urlQuery('oh hello there'))
})
it('should return the profile picture', async () => {
const response = await client.getProfilePicture(testJid)
assert.ok(response)

View File

@@ -60,8 +60,9 @@ const extractVideoThumb = async (
})
}) as Promise<void>
/** generates a thumbnail for a given media, if required */
export const compressImage = async (buffer: Buffer) => sharp(buffer).resize(48, 48).jpeg().toBuffer()
/** generates a thumbnail for a given media, if required */
export async function generateThumbnail(buffer: Buffer, mediaType: MessageType, info: MessageOptions) {
if (info.thumbnail === null || info.thumbnail) {
// don't do anything if the thumbnail is already provided, or is null
@@ -69,7 +70,7 @@ export async function generateThumbnail(buffer: Buffer, mediaType: MessageType,
throw new Error('audio messages cannot have thumbnails')
}
} else if (mediaType === MessageType.image || mediaType === MessageType.sticker) {
const buff = await sharp(buffer).resize(48, 48).jpeg().toBuffer()
const buff = await compressImage (buffer)
info.thumbnail = buff.toString('base64')
} else if (mediaType === MessageType.video) {
const filename = './' + randomBytes(5).toString('hex') + '.mp4'