eslint: upgrade to version 9 + tests: remove tests
This commit is contained in:
@@ -1,10 +0,0 @@
|
||||
# Ignore artifacts:
|
||||
lib
|
||||
coverage
|
||||
*.lock
|
||||
.eslintrc.json
|
||||
src/WABinary/index.ts
|
||||
WAProto
|
||||
Example/Example.ts
|
||||
docs
|
||||
proto-extract
|
||||
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"extends": ["@whiskeysockets", "plugin:prettier/recommended"],
|
||||
"plugins": ["prettier"],
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"ignorePatterns": ["src/Tests/*"],
|
||||
"rules": {
|
||||
"camelcase": "off",
|
||||
"indent": "off",
|
||||
"@typescript-eslint/no-explicit-any": [
|
||||
"warn",
|
||||
{
|
||||
"ignoreRestArgs": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-inferrable-types": ["warn"],
|
||||
"@typescript-eslint/no-redundant-type-constituents": ["warn"],
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": ["warn"],
|
||||
"no-restricted-syntax": "off",
|
||||
"keyword-spacing": "off",
|
||||
"implicit-arrow-linebreak": ["off"],
|
||||
"space-before-function-paren": [
|
||||
"error",
|
||||
{
|
||||
"anonymous": "always",
|
||||
"named": "never",
|
||||
"asyncArrow": "always"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"caughtErrors": "none"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
nodeLinker: node-modules
|
||||
@@ -0,0 +1,72 @@
|
||||
import { defineConfig, globalIgnores } from "eslint/config";
|
||||
import prettier from "eslint-plugin-prettier";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import js from "@eslint/js";
|
||||
import { FlatCompat } from "@eslint/eslintrc";
|
||||
import base from "@whiskeysockets/eslint-config";
|
||||
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all
|
||||
});
|
||||
|
||||
export default defineConfig([globalIgnores([
|
||||
"src/Tests/*",
|
||||
"**/lib",
|
||||
"**/coverage",
|
||||
"**/*.lock",
|
||||
"**/.eslintrc.json",
|
||||
"src/WABinary/index.ts",
|
||||
"**/WAProto",
|
||||
"Example/Example.ts",
|
||||
"**/docs",
|
||||
"**/proto-extract",
|
||||
]),
|
||||
...base,
|
||||
{
|
||||
extends: compat.extends("plugin:prettier/recommended"),
|
||||
|
||||
plugins: {
|
||||
prettier,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
ecmaVersion: 5,
|
||||
sourceType: "module",
|
||||
|
||||
parserOptions: {
|
||||
project: "./tsconfig.json",
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
camelcase: "off",
|
||||
indent: "off",
|
||||
|
||||
"@typescript-eslint/no-explicit-any": ["warn", {
|
||||
ignoreRestArgs: true,
|
||||
}],
|
||||
|
||||
"@typescript-eslint/no-inferrable-types": ["warn"],
|
||||
"@typescript-eslint/no-redundant-type-constituents": ["warn"],
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": ["warn"],
|
||||
"no-restricted-syntax": "off",
|
||||
"keyword-spacing": "off",
|
||||
"implicit-arrow-linebreak": ["off"],
|
||||
|
||||
"space-before-function-paren": ["error", {
|
||||
anonymous: "always",
|
||||
named: "never",
|
||||
asyncArrow: "always",
|
||||
}],
|
||||
|
||||
"@typescript-eslint/no-unused-vars": ["error", {
|
||||
caughtErrors: "none",
|
||||
}],
|
||||
},
|
||||
}]);
|
||||
+7
-8
@@ -43,13 +43,15 @@
|
||||
"@hapi/boom": "^9.1.3",
|
||||
"async-mutex": "^0.5.0",
|
||||
"axios": "^1.6.0",
|
||||
"libsignal": "github:WhiskeySockets/libsignal-node",
|
||||
"libsignal": "git+https://github.com/whiskeysockets/libsignal-node",
|
||||
"music-metadata": "^11.7.0",
|
||||
"pino": "^9.6",
|
||||
"protobufjs": "^7.2.4",
|
||||
"ws": "^8.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.31.0",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/ws": "^8.0.0",
|
||||
@@ -57,18 +59,18 @@
|
||||
"@typescript-eslint/parser": "^8.32.0",
|
||||
"@whiskeysockets/eslint-config": "github:whiskeysockets/eslint-config",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"eslint": "^8.0.0",
|
||||
"eslint": "^9.31.0",
|
||||
"eslint-config-prettier": "^10.1.2",
|
||||
"eslint-plugin-prettier": "^5.4.0",
|
||||
"jest": "^29.7.0",
|
||||
"jimp": "^1.6.0",
|
||||
"jiti": "^2.4.2",
|
||||
"json": "^11.0.0",
|
||||
"link-preview-js": "^3.0.0",
|
||||
"open": "^8.4.2",
|
||||
"prettier": "^3.5.3",
|
||||
"protobufjs-cli": "^1.1.3",
|
||||
"release-it": "^15.10.3",
|
||||
"sharp": "^0.34.2",
|
||||
"ts-jest": "^29.3.2",
|
||||
"tsx": "^4.20.3",
|
||||
"typedoc": "^0.27.9",
|
||||
@@ -79,7 +81,7 @@
|
||||
"audio-decode": "^2.1.3",
|
||||
"jimp": "^1.6.0",
|
||||
"link-preview-js": "^3.0.0",
|
||||
"sharp": "^0.34.2"
|
||||
"sharp": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"audio-decode": {
|
||||
@@ -90,12 +92,9 @@
|
||||
},
|
||||
"link-preview-js": {
|
||||
"optional": true
|
||||
},
|
||||
"sharp": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@1.22.19",
|
||||
"packageManager": "yarn@4.9.2",
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ const _gcLimit = 10000
|
||||
|
||||
async function _asyncQueueExecutor(queue: Array<QueueJob<any>>, cleanup: () => void): Promise<void> {
|
||||
let offt = 0
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
|
||||
while (true) {
|
||||
const limit = Math.min(queue.length, _gcLimit)
|
||||
for (let i = offt; i < limit; i++) {
|
||||
|
||||
+10
-10
@@ -1095,9 +1095,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
|
||||
// Handles newsletter notifications
|
||||
async function handleNewsletterNotification(node: BinaryNode) {
|
||||
const from = node.attrs.from
|
||||
const [child] = getAllBinaryNodeChildren(node)
|
||||
const author = node.attrs.participant
|
||||
const from = node.attrs.from!
|
||||
const child = getAllBinaryNodeChildren(node)[0]!
|
||||
const author = node.attrs.participant!
|
||||
|
||||
logger.info({ from, child }, 'got newsletter notification')
|
||||
|
||||
@@ -1105,7 +1105,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
case 'reaction':
|
||||
const reactionUpdate = {
|
||||
id: from,
|
||||
server_id: child.attrs.message_id,
|
||||
server_id: child.attrs.message_id!,
|
||||
reaction: {
|
||||
code: getBinaryNodeChildString(child, 'reaction'),
|
||||
count: 1
|
||||
@@ -1117,7 +1117,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
case 'view':
|
||||
const viewUpdate = {
|
||||
id: from,
|
||||
server_id: child.attrs.message_id,
|
||||
server_id: child.attrs.message_id!,
|
||||
count: parseInt(child.content?.toString() || '0', 10)
|
||||
}
|
||||
ev.emit('newsletter.view', viewUpdate)
|
||||
@@ -1127,9 +1127,9 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
const participantUpdate = {
|
||||
id: from,
|
||||
author,
|
||||
user: child.attrs.jid,
|
||||
action: child.attrs.action,
|
||||
new_role: child.attrs.role
|
||||
user: child.attrs.jid!,
|
||||
action: child.attrs.action!,
|
||||
new_role: child.attrs.role!
|
||||
}
|
||||
ev.emit('newsletter-participants.update', participantUpdate)
|
||||
break
|
||||
@@ -1168,7 +1168,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
fromMe: false
|
||||
},
|
||||
message: messageProto,
|
||||
messageTimestamp: +child.attrs.t
|
||||
messageTimestamp: +child.attrs.t!
|
||||
})
|
||||
await upsertMessage(fullMessage, 'append')
|
||||
logger.info('Processed plaintext newsletter message')
|
||||
@@ -1229,7 +1229,7 @@ export const makeMessagesRecvSocket = (config: SocketConfig) => {
|
||||
if (update.jid && update.user) {
|
||||
ev.emit('newsletter-participants.update', {
|
||||
id: update.jid,
|
||||
author: node.attrs.from,
|
||||
author: node.attrs.from!,
|
||||
user: update.user,
|
||||
new_role: 'ADMIN',
|
||||
action: 'promote'
|
||||
|
||||
@@ -47,7 +47,8 @@ import {
|
||||
} from '../WABinary'
|
||||
import { USyncQuery, USyncUser } from '../WAUSync'
|
||||
import { makeGroupsSocket } from './groups'
|
||||
import { makeNewsletterSocket, NewsletterSocket } from './newsletter'
|
||||
import type { NewsletterSocket } from './newsletter'
|
||||
import { makeNewsletterSocket } from './newsletter'
|
||||
|
||||
export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
const {
|
||||
@@ -307,7 +308,7 @@ export const makeMessagesSocket = (config: SocketConfig) => {
|
||||
const msgId = await relayMessage(meJid, protocolMessage, {
|
||||
additionalAttributes: {
|
||||
category: 'peer',
|
||||
// eslint-disable-next-line camelcase
|
||||
|
||||
push_priority: 'high_force'
|
||||
}
|
||||
})
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { Boom } from '@hapi/boom'
|
||||
import { BinaryNode } from '../WABinary'
|
||||
import type { BinaryNode } from '../WABinary'
|
||||
import { getBinaryNodeChild, S_WHATSAPP_NET } from '../WABinary'
|
||||
|
||||
const wMexQuery = (
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { NewsletterCreateResponse, WAMediaUpload } from '../Types'
|
||||
import { NewsletterMetadata, NewsletterUpdate, QueryIds, XWAPaths } from '../Types'
|
||||
import type { NewsletterMetadata, NewsletterUpdate } from '../Types'
|
||||
import { QueryIds, XWAPaths } from '../Types'
|
||||
import { generateProfilePicture } from '../Utils/messages-media'
|
||||
import { getBinaryNodeChild } from '../WABinary'
|
||||
import { GroupsSocket } from './groups'
|
||||
import type { GroupsSocket } from './groups'
|
||||
import { executeWMexQuery as genericExecuteWMexQuery } from './mex'
|
||||
|
||||
const parseNewsletterCreateResponse = (response: NewsletterCreateResponse): NewsletterMetadata => {
|
||||
|
||||
@@ -487,7 +487,7 @@ export const makeSocket = (config: SocketConfig) => {
|
||||
attrs: {
|
||||
jid: authState.creds.me.id,
|
||||
stage: 'companion_hello',
|
||||
// eslint-disable-next-line camelcase
|
||||
|
||||
should_show_push_notification: 'true'
|
||||
},
|
||||
content: [
|
||||
|
||||
@@ -1,206 +0,0 @@
|
||||
import { AccountSettings, ChatMutation, Contact, InitialAppStateSyncOptions } from '../Types'
|
||||
import { unixTimestampSeconds } from '../Utils'
|
||||
import { processSyncAction } from '../Utils/chat-utils'
|
||||
import logger from '../Utils/logger'
|
||||
|
||||
describe('App State Sync Tests', () => {
|
||||
const me: Contact = { id: randomJid() }
|
||||
// case when initial sync is off
|
||||
it('should return archive=false event', () => {
|
||||
const jid = randomJid()
|
||||
const index = ['archive', jid]
|
||||
|
||||
const CASES: ChatMutation[][] = [
|
||||
[
|
||||
{
|
||||
index,
|
||||
syncAction: {
|
||||
value: {
|
||||
archiveChatAction: {
|
||||
archived: false,
|
||||
messageRange: {
|
||||
lastMessageTimestamp: unixTimestampSeconds()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
index,
|
||||
syncAction: {
|
||||
value: {
|
||||
archiveChatAction: {
|
||||
archived: true,
|
||||
messageRange: {
|
||||
lastMessageTimestamp: unixTimestampSeconds()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
index,
|
||||
syncAction: {
|
||||
value: {
|
||||
archiveChatAction: {
|
||||
archived: false,
|
||||
messageRange: {
|
||||
lastMessageTimestamp: unixTimestampSeconds()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
for (const mutations of CASES) {
|
||||
const events = processSyncAction(mutations, me, undefined, logger)
|
||||
expect(events['chats.update']).toHaveLength(1)
|
||||
const event = events['chats.update']?.[0]
|
||||
expect(event.archive).toEqual(false)
|
||||
}
|
||||
})
|
||||
// case when initial sync is on
|
||||
// and unarchiveChats = true
|
||||
it('should not fire any archive event', () => {
|
||||
const jid = randomJid()
|
||||
const index = ['archive', jid]
|
||||
const now = unixTimestampSeconds()
|
||||
|
||||
const CASES: ChatMutation[][] = [
|
||||
[
|
||||
{
|
||||
index,
|
||||
syncAction: {
|
||||
value: {
|
||||
archiveChatAction: {
|
||||
archived: true,
|
||||
messageRange: {
|
||||
lastMessageTimestamp: now - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
index,
|
||||
syncAction: {
|
||||
value: {
|
||||
archiveChatAction: {
|
||||
archived: false,
|
||||
messageRange: {
|
||||
lastMessageTimestamp: now + 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
index,
|
||||
syncAction: {
|
||||
value: {
|
||||
archiveChatAction: {
|
||||
archived: true,
|
||||
messageRange: {
|
||||
lastMessageTimestamp: now + 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
index,
|
||||
syncAction: {
|
||||
value: {
|
||||
archiveChatAction: {
|
||||
archived: false,
|
||||
messageRange: {
|
||||
lastMessageTimestamp: now + 11
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
const ctx: InitialAppStateSyncOptions = {
|
||||
recvChats: {
|
||||
[jid]: { lastMsgRecvTimestamp: now }
|
||||
},
|
||||
accountSettings: { unarchiveChats: true }
|
||||
}
|
||||
|
||||
for (const mutations of CASES) {
|
||||
const events = processSyncActions(mutations, me, ctx, logger)
|
||||
expect(events['chats.update']?.length).toBeFalsy()
|
||||
}
|
||||
})
|
||||
|
||||
// case when initial sync is on
|
||||
// with unarchiveChats = true & unarchiveChats = false
|
||||
it('should fire archive=true events', () => {
|
||||
const jid = randomJid()
|
||||
const index = ['archive', jid]
|
||||
const now = unixTimestampSeconds()
|
||||
|
||||
const CASES: { settings: AccountSettings; mutations: ChatMutation[] }[] = [
|
||||
{
|
||||
settings: { unarchiveChats: true },
|
||||
mutations: [
|
||||
{
|
||||
index,
|
||||
syncAction: {
|
||||
value: {
|
||||
archiveChatAction: {
|
||||
archived: true,
|
||||
messageRange: {
|
||||
lastMessageTimestamp: now
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
settings: { unarchiveChats: false },
|
||||
mutations: [
|
||||
{
|
||||
index,
|
||||
syncAction: {
|
||||
value: {
|
||||
archiveChatAction: {
|
||||
archived: true,
|
||||
messageRange: {
|
||||
lastMessageTimestamp: now - 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
for (const { mutations, settings } of CASES) {
|
||||
const ctx: InitialAppStateSyncOptions = {
|
||||
recvChats: {
|
||||
[jid]: { lastMsgRecvTimestamp: now }
|
||||
},
|
||||
accountSettings: settings
|
||||
}
|
||||
const events = processSyncActions(mutations, me, ctx, logger)
|
||||
expect(events['chats.update']).toHaveLength(1)
|
||||
const event = events['chats.update']?.[0]
|
||||
expect(event.archive).toEqual(true)
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,330 +0,0 @@
|
||||
import { proto } from '../../WAProto'
|
||||
import { Chat, WAMessageKey, WAMessageStatus, WAMessageStubType, WAMessageUpdate } from '../Types'
|
||||
import { delay, generateMessageID, makeEventBuffer, toNumber, unixTimestampSeconds } from '../Utils'
|
||||
import logger from '../Utils/logger'
|
||||
import { randomJid } from './utils'
|
||||
|
||||
describe('Event Buffer Tests', () => {
|
||||
let ev: ReturnType<typeof makeEventBuffer>
|
||||
beforeEach(() => {
|
||||
const _logger = logger.child({})
|
||||
_logger.level = 'trace'
|
||||
ev = makeEventBuffer(_logger)
|
||||
})
|
||||
|
||||
it('should buffer a chat upsert & update event', async () => {
|
||||
const chatId = randomJid()
|
||||
|
||||
const chats: Chat[] = []
|
||||
|
||||
ev.on('chats.upsert', c => chats.push(...c))
|
||||
ev.on('chats.update', () => fail('should not emit update event'))
|
||||
|
||||
ev.buffer()
|
||||
await Promise.all([
|
||||
(async () => {
|
||||
ev.buffer()
|
||||
await delay(100)
|
||||
ev.emit('chats.upsert', [{ id: chatId, conversationTimestamp: 123, unreadCount: 1 }])
|
||||
const flushed = ev.flush()
|
||||
expect(flushed).toBeFalsy()
|
||||
})(),
|
||||
(async () => {
|
||||
ev.buffer()
|
||||
await delay(200)
|
||||
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 124, unreadCount: 1 }])
|
||||
const flushed = ev.flush()
|
||||
expect(flushed).toBeFalsy()
|
||||
})()
|
||||
])
|
||||
|
||||
const flushed = ev.flush()
|
||||
expect(flushed).toBeTruthy()
|
||||
|
||||
expect(chats).toHaveLength(1)
|
||||
expect(chats[0].conversationTimestamp).toEqual(124)
|
||||
expect(chats[0].unreadCount).toEqual(2)
|
||||
})
|
||||
|
||||
it('should overwrite a chats.delete event', async () => {
|
||||
const chatId = randomJid()
|
||||
const chats: Partial<Chat>[] = []
|
||||
|
||||
ev.on('chats.update', c => chats.push(...c))
|
||||
ev.on('chats.delete', () => fail('not should have emitted'))
|
||||
|
||||
ev.buffer()
|
||||
|
||||
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 123, unreadCount: 1 }])
|
||||
ev.emit('chats.delete', [chatId])
|
||||
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 124, unreadCount: 1 }])
|
||||
|
||||
ev.flush()
|
||||
|
||||
expect(chats).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('should overwrite a chats.update event', async () => {
|
||||
const chatId = randomJid()
|
||||
const chatsDeleted: string[] = []
|
||||
|
||||
ev.on('chats.delete', c => chatsDeleted.push(...c))
|
||||
ev.on('chats.update', () => fail('not should have emitted'))
|
||||
|
||||
ev.buffer()
|
||||
|
||||
ev.emit('chats.update', [{ id: chatId, conversationTimestamp: 123, unreadCount: 1 }])
|
||||
ev.emit('chats.delete', [chatId])
|
||||
|
||||
ev.flush()
|
||||
|
||||
expect(chatsDeleted).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('should release a conditional update at the right time', async () => {
|
||||
const chatId = randomJid()
|
||||
const chatId2 = randomJid()
|
||||
const chatsUpserted: Chat[] = []
|
||||
const chatsSynced: Chat[] = []
|
||||
|
||||
ev.on('chats.upsert', c => chatsUpserted.push(...c))
|
||||
ev.on('messaging-history.set', c => chatsSynced.push(...c.chats))
|
||||
ev.on('chats.update', () => fail('not should have emitted'))
|
||||
|
||||
ev.buffer()
|
||||
ev.emit('chats.update', [
|
||||
{
|
||||
id: chatId,
|
||||
archived: true,
|
||||
conditional(buff) {
|
||||
if (buff.chatUpserts[chatId]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
ev.emit('chats.update', [
|
||||
{
|
||||
id: chatId2,
|
||||
archived: true,
|
||||
conditional(buff) {
|
||||
if (buff.historySets.chats[chatId2]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
ev.flush()
|
||||
|
||||
ev.buffer()
|
||||
ev.emit('chats.upsert', [
|
||||
{
|
||||
id: chatId,
|
||||
conversationTimestamp: 123,
|
||||
unreadCount: 1,
|
||||
muteEndTime: 123
|
||||
}
|
||||
])
|
||||
ev.emit('messaging-history.set', {
|
||||
chats: [
|
||||
{
|
||||
id: chatId2,
|
||||
conversationTimestamp: 123,
|
||||
unreadCount: 1,
|
||||
muteEndTime: 123
|
||||
}
|
||||
],
|
||||
contacts: [],
|
||||
messages: [],
|
||||
isLatest: false
|
||||
})
|
||||
ev.flush()
|
||||
|
||||
expect(chatsUpserted).toHaveLength(1)
|
||||
expect(chatsUpserted[0].id).toEqual(chatId)
|
||||
expect(chatsUpserted[0].archived).toEqual(true)
|
||||
expect(chatsUpserted[0].muteEndTime).toEqual(123)
|
||||
|
||||
expect(chatsSynced).toHaveLength(1)
|
||||
expect(chatsSynced[0].id).toEqual(chatId2)
|
||||
expect(chatsSynced[0].archived).toEqual(true)
|
||||
})
|
||||
|
||||
it('should discard a conditional update', async () => {
|
||||
const chatId = randomJid()
|
||||
const chatsUpserted: Chat[] = []
|
||||
|
||||
ev.on('chats.upsert', c => chatsUpserted.push(...c))
|
||||
ev.on('chats.update', () => fail('not should have emitted'))
|
||||
|
||||
ev.buffer()
|
||||
ev.emit('chats.update', [
|
||||
{
|
||||
id: chatId,
|
||||
archived: true,
|
||||
conditional(buff) {
|
||||
if (buff.chatUpserts[chatId]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
ev.emit('chats.upsert', [
|
||||
{
|
||||
id: chatId,
|
||||
conversationTimestamp: 123,
|
||||
unreadCount: 1,
|
||||
muteEndTime: 123
|
||||
}
|
||||
])
|
||||
|
||||
ev.flush()
|
||||
|
||||
expect(chatsUpserted).toHaveLength(1)
|
||||
expect(chatsUpserted[0].archived).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should overwrite a chats.update event with a history event', async () => {
|
||||
const chatId = randomJid()
|
||||
let chatRecv: Chat | undefined
|
||||
|
||||
ev.on('messaging-history.set', ({ chats }) => {
|
||||
chatRecv = chats[0]
|
||||
})
|
||||
ev.on('chats.update', () => fail('not should have emitted'))
|
||||
|
||||
ev.buffer()
|
||||
|
||||
ev.emit('messaging-history.set', {
|
||||
chats: [{ id: chatId, conversationTimestamp: 123, unreadCount: 1 }],
|
||||
messages: [],
|
||||
contacts: [],
|
||||
isLatest: true
|
||||
})
|
||||
ev.emit('chats.update', [{ id: chatId, archived: true }])
|
||||
|
||||
ev.flush()
|
||||
|
||||
expect(chatRecv).toBeDefined()
|
||||
expect(chatRecv?.archived).toBeTruthy()
|
||||
})
|
||||
|
||||
it('should buffer message upsert events', async () => {
|
||||
const messageTimestamp = unixTimestampSeconds()
|
||||
const msg: proto.IWebMessageInfo = {
|
||||
key: {
|
||||
remoteJid: randomJid(),
|
||||
id: generateMessageID(),
|
||||
fromMe: false
|
||||
},
|
||||
messageStubType: WAMessageStubType.CIPHERTEXT,
|
||||
messageTimestamp
|
||||
}
|
||||
|
||||
const msgs: proto.IWebMessageInfo[] = []
|
||||
|
||||
ev.on('messages.upsert', c => {
|
||||
msgs.push(...c.messages)
|
||||
expect(c.type).toEqual('notify')
|
||||
})
|
||||
|
||||
ev.buffer()
|
||||
ev.emit('messages.upsert', { messages: [proto.WebMessageInfo.fromObject(msg)], type: 'notify' })
|
||||
|
||||
msg.messageTimestamp = unixTimestampSeconds() + 1
|
||||
msg.messageStubType = undefined
|
||||
msg.message = { conversation: 'Test' }
|
||||
ev.emit('messages.upsert', { messages: [proto.WebMessageInfo.fromObject(msg)], type: 'notify' })
|
||||
ev.emit('messages.update', [{ key: msg.key, update: { status: WAMessageStatus.READ } }])
|
||||
|
||||
ev.flush()
|
||||
|
||||
expect(msgs).toHaveLength(1)
|
||||
expect(msgs[0].message).toBeTruthy()
|
||||
expect(toNumber(msgs[0].messageTimestamp!)).toEqual(messageTimestamp)
|
||||
expect(msgs[0].status).toEqual(WAMessageStatus.READ)
|
||||
})
|
||||
|
||||
it('should buffer a message receipt update', async () => {
|
||||
const msg: proto.IWebMessageInfo = {
|
||||
key: {
|
||||
remoteJid: randomJid(),
|
||||
id: generateMessageID(),
|
||||
fromMe: false
|
||||
},
|
||||
messageStubType: WAMessageStubType.CIPHERTEXT,
|
||||
messageTimestamp: unixTimestampSeconds()
|
||||
}
|
||||
|
||||
const msgs: proto.IWebMessageInfo[] = []
|
||||
|
||||
ev.on('messages.upsert', c => msgs.push(...c.messages))
|
||||
ev.on('message-receipt.update', () => fail('should not emit'))
|
||||
|
||||
ev.buffer()
|
||||
ev.emit('messages.upsert', { messages: [proto.WebMessageInfo.fromObject(msg)], type: 'notify' })
|
||||
ev.emit('message-receipt.update', [
|
||||
{
|
||||
key: msg.key,
|
||||
receipt: {
|
||||
userJid: randomJid(),
|
||||
readTimestamp: unixTimestampSeconds()
|
||||
}
|
||||
}
|
||||
])
|
||||
|
||||
ev.flush()
|
||||
|
||||
expect(msgs).toHaveLength(1)
|
||||
expect(msgs[0].userReceipt).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('should buffer multiple status updates', async () => {
|
||||
const key: WAMessageKey = {
|
||||
remoteJid: randomJid(),
|
||||
id: generateMessageID(),
|
||||
fromMe: false
|
||||
}
|
||||
|
||||
const msgs: WAMessageUpdate[] = []
|
||||
|
||||
ev.on('messages.update', c => msgs.push(...c))
|
||||
|
||||
ev.buffer()
|
||||
ev.emit('messages.update', [{ key, update: { status: WAMessageStatus.DELIVERY_ACK } }])
|
||||
ev.emit('messages.update', [{ key, update: { status: WAMessageStatus.READ } }])
|
||||
|
||||
ev.flush()
|
||||
|
||||
expect(msgs).toHaveLength(1)
|
||||
expect(msgs[0].update.status).toEqual(WAMessageStatus.READ)
|
||||
})
|
||||
|
||||
it('should remove chat unread counter', async () => {
|
||||
const msg: proto.IWebMessageInfo = {
|
||||
key: {
|
||||
remoteJid: '12345@s.whatsapp.net',
|
||||
id: generateMessageID(),
|
||||
fromMe: false
|
||||
},
|
||||
message: {
|
||||
conversation: 'abcd'
|
||||
},
|
||||
messageTimestamp: unixTimestampSeconds()
|
||||
}
|
||||
|
||||
const chats: Partial<Chat>[] = []
|
||||
|
||||
ev.on('chats.update', c => chats.push(...c))
|
||||
|
||||
ev.buffer()
|
||||
ev.emit('messages.upsert', { messages: [proto.WebMessageInfo.fromObject(msg)], type: 'notify' })
|
||||
ev.emit('chats.update', [{ id: msg.key.remoteJid!, unreadCount: 1, conversationTimestamp: msg.messageTimestamp }])
|
||||
ev.emit('messages.update', [{ key: msg.key, update: { status: WAMessageStatus.READ } }])
|
||||
|
||||
ev.flush()
|
||||
|
||||
expect(chats[0].unreadCount).toBeUndefined()
|
||||
})
|
||||
})
|
||||
@@ -1,77 +0,0 @@
|
||||
import { addTransactionCapability, delay } from '../Utils'
|
||||
import logger from '../Utils/logger'
|
||||
import { makeMockSignalKeyStore } from './utils'
|
||||
|
||||
logger.level = 'trace'
|
||||
|
||||
describe('Key Store w Transaction Tests', () => {
|
||||
const rawStore = makeMockSignalKeyStore()
|
||||
const store = addTransactionCapability(rawStore, logger, {
|
||||
maxCommitRetries: 1,
|
||||
delayBetweenTriesMs: 10
|
||||
})
|
||||
|
||||
it('should use transaction cache when mutated', async () => {
|
||||
const key = '123'
|
||||
const value = new Uint8Array(1)
|
||||
const ogGet = rawStore.get
|
||||
await store.transaction(async () => {
|
||||
await store.set({ session: { [key]: value } })
|
||||
|
||||
rawStore.get = () => {
|
||||
throw new Error('should not have been called')
|
||||
}
|
||||
|
||||
const { [key]: stored } = await store.get('session', [key])
|
||||
expect(stored).toEqual(new Uint8Array(1))
|
||||
})
|
||||
|
||||
rawStore.get = ogGet
|
||||
})
|
||||
|
||||
it('should not commit a failed transaction', async () => {
|
||||
const key = 'abcd'
|
||||
await expect(
|
||||
store.transaction(async () => {
|
||||
await store.set({ session: { [key]: new Uint8Array(1) } })
|
||||
throw new Error('fail')
|
||||
})
|
||||
).rejects.toThrowError('fail')
|
||||
|
||||
const { [key]: stored } = await store.get('session', [key])
|
||||
expect(stored).toBeUndefined()
|
||||
})
|
||||
|
||||
it('should handle overlapping transactions', async () => {
|
||||
// promise to let transaction 2
|
||||
// know that transaction 1 has started
|
||||
let promiseResolve: () => void
|
||||
const promise = new Promise<void>(resolve => {
|
||||
promiseResolve = resolve
|
||||
})
|
||||
|
||||
store.transaction(async () => {
|
||||
await store.set({
|
||||
session: {
|
||||
'1': new Uint8Array(1)
|
||||
}
|
||||
})
|
||||
// wait for the other transaction to start
|
||||
await delay(5)
|
||||
// reolve the promise to let the other transaction continue
|
||||
promiseResolve()
|
||||
})
|
||||
|
||||
await store.transaction(async () => {
|
||||
await promise
|
||||
await delay(5)
|
||||
|
||||
expect(store.isInTransaction()).toBe(true)
|
||||
})
|
||||
|
||||
expect(store.isInTransaction()).toBe(false)
|
||||
// ensure that the transaction were committed
|
||||
const { ['1']: stored } = await store.get('session', ['1'])
|
||||
expect(stored).toEqual(new Uint8Array(1))
|
||||
})
|
||||
})
|
||||
@@ -1,163 +0,0 @@
|
||||
import { makeLibSignalRepository } from '../Signal/libsignal'
|
||||
import { SignalAuthState, SignalDataTypeMap } from '../Types'
|
||||
import { Curve, generateRegistrationId, generateSignalPubKey, signedKeyPair } from '../Utils'
|
||||
|
||||
// TODO: should move to libsignal
|
||||
|
||||
describe('Signal Tests', () => {
|
||||
it('should correctly encrypt/decrypt 1 message', async () => {
|
||||
const user1 = makeUser()
|
||||
const user2 = makeUser()
|
||||
|
||||
const msg = Buffer.from('hello there!')
|
||||
|
||||
await prepareForSendingMessage(user1, user2)
|
||||
|
||||
const result = await user1.repository.encryptMessage({ jid: user2.jid, data: msg })
|
||||
|
||||
const dec = await user2.repository.decryptMessage({ jid: user1.jid, ...result })
|
||||
|
||||
expect(dec).toEqual(msg)
|
||||
})
|
||||
|
||||
it('should correctly override a session', async () => {
|
||||
const user1 = makeUser()
|
||||
const user2 = makeUser()
|
||||
|
||||
const msg = Buffer.from('hello there!')
|
||||
|
||||
for (let preKeyId = 2; preKeyId <= 3; preKeyId++) {
|
||||
await prepareForSendingMessage(user1, user2, preKeyId)
|
||||
|
||||
const result = await user1.repository.encryptMessage({ jid: user2.jid, data: msg })
|
||||
|
||||
const dec = await user2.repository.decryptMessage({ jid: user1.jid, ...result })
|
||||
|
||||
expect(dec).toEqual(msg)
|
||||
}
|
||||
})
|
||||
|
||||
it('should correctly encrypt/decrypt multiple messages', async () => {
|
||||
const user1 = makeUser()
|
||||
const user2 = makeUser()
|
||||
|
||||
const msg = Buffer.from('hello there!')
|
||||
|
||||
await prepareForSendingMessage(user1, user2)
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const result = await user1.repository.encryptMessage({ jid: user2.jid, data: msg })
|
||||
|
||||
const dec = await user2.repository.decryptMessage({ jid: user1.jid, ...result })
|
||||
|
||||
expect(dec).toEqual(msg)
|
||||
}
|
||||
})
|
||||
|
||||
it('should encrypt/decrypt messages from group', async () => {
|
||||
const groupId = '123456@g.us'
|
||||
const participants = [...Array(5)].map(makeUser)
|
||||
|
||||
const msg = Buffer.from('hello there!')
|
||||
|
||||
const sender = participants[0]
|
||||
const enc = await sender.repository.encryptGroupMessage({
|
||||
group: groupId,
|
||||
meId: sender.jid,
|
||||
data: msg
|
||||
})
|
||||
|
||||
for (const participant of participants) {
|
||||
if (participant === sender) {
|
||||
continue
|
||||
}
|
||||
|
||||
await participant.repository.processSenderKeyDistributionMessage({
|
||||
item: {
|
||||
groupId,
|
||||
axolotlSenderKeyDistributionMessage: enc.senderKeyDistributionMessage
|
||||
},
|
||||
authorJid: sender.jid
|
||||
})
|
||||
|
||||
const dec = await participant.repository.decryptGroupMessage({
|
||||
group: groupId,
|
||||
authorJid: sender.jid,
|
||||
msg: enc.ciphertext
|
||||
})
|
||||
expect(dec).toEqual(msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
type User = ReturnType<typeof makeUser>
|
||||
|
||||
function makeUser() {
|
||||
const store = makeTestAuthState()
|
||||
const jid = `${Math.random().toString().replace('.', '')}@s.whatsapp.net`
|
||||
const repository = makeLibSignalRepository(store)
|
||||
return { store, jid, repository }
|
||||
}
|
||||
|
||||
async function prepareForSendingMessage(sender: User, receiver: User, preKeyId = 2) {
|
||||
const preKey = Curve.generateKeyPair()
|
||||
await sender.repository.injectE2ESession({
|
||||
jid: receiver.jid,
|
||||
session: {
|
||||
registrationId: receiver.store.creds.registrationId,
|
||||
identityKey: generateSignalPubKey(receiver.store.creds.signedIdentityKey.public),
|
||||
signedPreKey: {
|
||||
keyId: receiver.store.creds.signedPreKey.keyId,
|
||||
publicKey: generateSignalPubKey(receiver.store.creds.signedPreKey.keyPair.public),
|
||||
signature: receiver.store.creds.signedPreKey.signature
|
||||
},
|
||||
preKey: {
|
||||
keyId: preKeyId,
|
||||
publicKey: generateSignalPubKey(preKey.public)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
await receiver.store.keys.set({
|
||||
'pre-key': {
|
||||
[preKeyId]: preKey
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function makeTestAuthState(): SignalAuthState {
|
||||
const identityKey = Curve.generateKeyPair()
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const store: { [_: string]: any } = {}
|
||||
return {
|
||||
creds: {
|
||||
signedIdentityKey: identityKey,
|
||||
registrationId: generateRegistrationId(),
|
||||
signedPreKey: signedKeyPair(identityKey, 1)
|
||||
},
|
||||
keys: {
|
||||
get(type, ids) {
|
||||
const data: { [_: string]: SignalDataTypeMap[typeof type] } = {}
|
||||
for (const id of ids) {
|
||||
const item = store[getUniqueId(type, id)]
|
||||
if (typeof item !== 'undefined') {
|
||||
data[id] = item
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
},
|
||||
set(data) {
|
||||
for (const type in data) {
|
||||
for (const id in data[type]) {
|
||||
store[getUniqueId(type, id)] = data[type][id]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getUniqueId(type: string, id: string) {
|
||||
return `${type}.${id}`
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
import { readFileSync } from 'fs'
|
||||
import { proto } from '../../WAProto'
|
||||
import { DownloadableMessage, MediaType } from '../Types'
|
||||
import { downloadContentFromMessage } from '../Utils'
|
||||
|
||||
jest.setTimeout(20_000)
|
||||
|
||||
type TestVector = {
|
||||
type: MediaType
|
||||
message: DownloadableMessage
|
||||
plaintext: Buffer
|
||||
}
|
||||
|
||||
const TEST_VECTORS: TestVector[] = [
|
||||
{
|
||||
type: 'image',
|
||||
message: proto.Message.ImageMessage.decode(
|
||||
Buffer.from(
|
||||
'Ck1odHRwczovL21tZy53aGF0c2FwcC5uZXQvZC9mL0FwaHR4WG9fWXZZcDZlUVNSa0tjOHE5d2ozVUpleWdoY3poM3ExX3I0ektnLmVuYxIKaW1hZ2UvanBlZyIgKTuVFyxDc6mTm4GXPlO3Z911Wd8RBeTrPLSWAEdqW8MomcUBQiB7wH5a4nXMKyLOT0A2nFgnnM/DUH8YjQf8QtkCIekaSkogTB+BXKCWDFrmNzozY0DCPn0L4VKd7yG1ZbZwbgRhzVc=',
|
||||
'base64'
|
||||
)
|
||||
),
|
||||
plaintext: readFileSync('./Media/cat.jpeg')
|
||||
},
|
||||
{
|
||||
type: 'image',
|
||||
message: proto.Message.ImageMessage.decode(
|
||||
Buffer.from(
|
||||
'Ck1odHRwczovL21tZy53aGF0c2FwcC5uZXQvZC9mL0Ftb2tnWkphNWF6QWZxa3dVRzc0eUNUdTlGeWpjMmd5akpqcXNmMUFpZEU5LmVuYxIKaW1hZ2UvanBlZyIg8IS5TQzdzcuvcR7F8HMhWnXmlsV+GOo9JE1/t2k+o9Yoz6o6QiA7kDk8j5KOEQC0kDFE1qW7lBBDYhm5z06N3SirfUj3CUog/CjYF8e670D5wUJwWv2B2mKzDEo8IJLStDv76YmtPfs=',
|
||||
'base64'
|
||||
)
|
||||
),
|
||||
plaintext: readFileSync('./Media/icon.png')
|
||||
}
|
||||
]
|
||||
|
||||
describe('Media Download Tests', () => {
|
||||
it('should download a full encrypted media correctly', async () => {
|
||||
for (const { type, message, plaintext } of TEST_VECTORS) {
|
||||
const readPipe = await downloadContentFromMessage(message, type)
|
||||
|
||||
let buffer = Buffer.alloc(0)
|
||||
for await (const read of readPipe) {
|
||||
buffer = Buffer.concat([buffer, read])
|
||||
}
|
||||
|
||||
expect(buffer).toEqual(plaintext)
|
||||
}
|
||||
})
|
||||
|
||||
it('should download an encrypted media correctly piece', async () => {
|
||||
for (const { type, message, plaintext } of TEST_VECTORS) {
|
||||
// check all edge cases
|
||||
const ranges = [
|
||||
{ startByte: 51, endByte: plaintext.length - 100 }, // random numbers
|
||||
{ startByte: 1024, endByte: 2038 }, // larger random multiples of 16
|
||||
{ startByte: 1, endByte: plaintext.length - 1 } // borders
|
||||
]
|
||||
for (const range of ranges) {
|
||||
const readPipe = await downloadContentFromMessage(message, type, range)
|
||||
|
||||
let buffer = Buffer.alloc(0)
|
||||
for await (const read of readPipe) {
|
||||
buffer = Buffer.concat([buffer, read])
|
||||
}
|
||||
|
||||
const hex = buffer.toString('hex')
|
||||
const expectedHex = plaintext.slice(range.startByte || 0, range.endByte || undefined).toString('hex')
|
||||
expect(hex).toBe(expectedHex)
|
||||
|
||||
console.log('success on ', range)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,34 +0,0 @@
|
||||
import { WAMessageContent } from '../Types'
|
||||
import { normalizeMessageContent } from '../Utils'
|
||||
|
||||
describe('Messages Tests', () => {
|
||||
it('should correctly unwrap messages', () => {
|
||||
const CONTENT = { imageMessage: {} }
|
||||
expectRightContent(CONTENT)
|
||||
expectRightContent({
|
||||
ephemeralMessage: { message: CONTENT }
|
||||
})
|
||||
expectRightContent({
|
||||
viewOnceMessage: {
|
||||
message: {
|
||||
ephemeralMessage: { message: CONTENT }
|
||||
}
|
||||
}
|
||||
})
|
||||
expectRightContent({
|
||||
viewOnceMessage: {
|
||||
message: {
|
||||
viewOnceMessageV2: {
|
||||
message: {
|
||||
ephemeralMessage: { message: CONTENT }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function expectRightContent(content: WAMessageContent) {
|
||||
expect(normalizeMessageContent(content)).toHaveProperty('imageMessage')
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -1,35 +0,0 @@
|
||||
import { jidEncode } from '../WABinary'
|
||||
|
||||
export function randomJid() {
|
||||
return jidEncode(Math.floor(Math.random() * 1000000), Math.random() < 0.5 ? 's.whatsapp.net' : 'g.us')
|
||||
}
|
||||
|
||||
export function makeMockSignalKeyStore(): SignalKeyStore {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const store: { [_: string]: any } = {}
|
||||
|
||||
return {
|
||||
get(type, ids) {
|
||||
const data: { [_: string]: SignalDataTypeMap[typeof type] } = {}
|
||||
for (const id of ids) {
|
||||
const item = store[getUniqueId(type, id)]
|
||||
if (typeof item !== 'undefined') {
|
||||
data[id] = item
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
},
|
||||
set(data) {
|
||||
for (const type in data) {
|
||||
for (const id in data[type]) {
|
||||
store[getUniqueId(type, id)] = data[type][id]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getUniqueId(type: string, id: string) {
|
||||
return `${type}.${id}`
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export interface GroupMetadata {
|
||||
addressingMode: 'pn' | 'lid'
|
||||
owner: string | undefined
|
||||
ownerJid?: string | undefined
|
||||
owner_country_code: string
|
||||
owner_country_code?: string | undefined
|
||||
subject: string
|
||||
/** group subject owner */
|
||||
subjectOwner?: string
|
||||
|
||||
@@ -32,6 +32,7 @@ import type { ILogger } from './logger'
|
||||
const getTmpFilesDirectory = () => tmpdir()
|
||||
|
||||
const getImageProcessingLibrary = async () => {
|
||||
//@ts-ignore
|
||||
const [jimp, sharp] = await Promise.all([import('jimp').catch(() => {}), import('sharp').catch(() => {})])
|
||||
|
||||
if (sharp) {
|
||||
@@ -152,7 +153,7 @@ export const extractImageThumb = async (bufferOrFilePath: Readable | Buffer | st
|
||||
}
|
||||
}
|
||||
} else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
|
||||
const jimp = await lib.jimp.default.Jimp.read(bufferOrFilePath)
|
||||
const jimp = await (lib.jimp.Jimp as any).read(bufferOrFilePath)
|
||||
const dimensions = {
|
||||
width: jimp.width,
|
||||
height: jimp.height
|
||||
@@ -200,7 +201,7 @@ export const generateProfilePicture = async (
|
||||
})
|
||||
.toBuffer()
|
||||
} else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
|
||||
const jimp = await lib.jimp.default.Jimp.read(buffer)
|
||||
const jimp = await (lib.jimp.Jimp as any).read(buffer)
|
||||
const min = Math.min(jimp.width, jimp.height)
|
||||
const cropped = jimp.crop({ x: 0, y: 0, w: min, h: min })
|
||||
|
||||
@@ -242,7 +243,8 @@ export async function getAudioDuration(buffer: Buffer | string | Readable) {
|
||||
*/
|
||||
export async function getAudioWaveform(buffer: Buffer | string | Readable, logger?: ILogger) {
|
||||
try {
|
||||
const { default: decoder } = await eval("import('audio-decode')")
|
||||
// @ts-ignore
|
||||
const { default: decoder } = await import('audio-decode')
|
||||
let audioData: Buffer
|
||||
if (Buffer.isBuffer(buffer)) {
|
||||
audioData = buffer
|
||||
@@ -311,7 +313,7 @@ export const getStream = async (item: WAMediaUpload, opts?: AxiosRequestConfig)
|
||||
const urlStr = item.url.toString()
|
||||
|
||||
if (urlStr.startsWith('data:')) {
|
||||
const buffer = Buffer.from(urlStr.split(',')[1], 'base64')
|
||||
const buffer = Buffer.from(urlStr.split(',')[1]!, 'base64')
|
||||
return { stream: toReadable(buffer), type: 'buffer' } as const
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
generateThumbnail,
|
||||
getAudioDuration,
|
||||
getAudioWaveform,
|
||||
getRawMediaUploadData,
|
||||
getRawMediaUploadData,
|
||||
type MediaDownloadOptions
|
||||
} from './messages-media'
|
||||
|
||||
@@ -177,7 +177,8 @@ export const prepareWAMessageMedia = async (
|
||||
await fs.unlink(filePath)
|
||||
|
||||
const obj = WAProto.Message.fromObject({
|
||||
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
||||
// todo: add more support here
|
||||
[`${mediaType}Message`]: (MessageTypeProto as any)[mediaType].fromObject({
|
||||
url: mediaUrl,
|
||||
directPath,
|
||||
fileSha256,
|
||||
|
||||
Reference in New Issue
Block a user