Skip to content
Merged
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions packages/bot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@
},
"dependencies": {
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"body-parser": "^1.20.4",
"body-parser": "^2.2.1",
"cors": "^2.8.5",
"fluent-ffmpeg": "^2.1.3",
"follow-redirects": "^1.15.11",
"mime-types": "^2.1.35",
"mime-types": "^3.0.2",
"picocolors": "^1.1.1",
"polka": "^0.5.2"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"test:coverage": "npx c8 npm run test"
},
"devDependencies": {
"@clack/prompts": "^0.7.0",
"@clack/prompts": "^0.11.0",
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-node-resolve": "^16.0.3",
"@rollup/plugin-terser": "^0.4.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,33 @@ test('handleMsg - You should send the payload type media', async () => {
assert.equal(sendFlowSimpleStub.args[0][0], expectedMessage)
})

test('handleMsg - should handle unknown message type with empty answer', async () => {
const messageCtxInComming = {
from: 'some_user_id',
body: 'some_message_body',
}

const dialogFlowContext = new DialogFlowContextCX(null, mockProvider, optionsDX)
dialogFlowContext['createSession'] = stub().resolves('session')
dialogFlowContext['detectIntent'] = stub().resolves({
queryResult: {
responseMessages: [
{
message: 'unknown_type',
someOtherField: { data: 'test' },
},
],
},
})
const expectedMessage = [{ answer: '' }]

dialogFlowContext['sendFlowSimple'] = sendFlowSimpleStub

await dialogFlowContext.handleMsg(messageCtxInComming)
assert.equal(sendFlowSimpleStub.called, true)
assert.equal(sendFlowSimpleStub.args[0][0], expectedMessage)
})

test.after.each(() => {
unlinkSync(pathFile)
})
Expand Down
2 changes: 1 addition & 1 deletion packages/contexts-dialogflow-cx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"dependencies": {
"@builderbot/bot": "workspace:*",
"@google-cloud/dialogflow-cx": "^4.4.0"
"@google-cloud/dialogflow-cx": "^5.5.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^29.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/contexts-dialogflow/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"dependencies": {
"@builderbot/bot": "workspace:*",
"@google-cloud/dialogflow": "^6.5.0"
"@google-cloud/dialogflow": "^7.4.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^29.0.0",
Expand Down
55 changes: 55 additions & 0 deletions packages/database-mongo/__tests__/mongoAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ test('[MongoAdapter] - init', async () => {
assert.ok(mongoAdapter.db, 'Database connection should be established')
})

test('[MongoAdapter] - init with invalid URI should handle error', async () => {
const invalidAdapter = new MongoAdapter({
dbUri: 'mongodb://invalid:27017',
dbName: 'testDB',
})
// Wait a bit for the constructor's init to fail
await delay(100)
const result = await invalidAdapter.init()
// Should return undefined on error (falsy)
assert.not.ok(result, 'Init should return falsy on error')
})

test('[MongoAdapter] - save', async () => {
const ctx = {
from: '12345',
Expand All @@ -45,13 +57,56 @@ test('[MongoAdapter] - save', async () => {
assert.equal(mongoAdapter.listHistory.length, 1)
})

test('[MongoAdapter] - save multiple documents', async () => {
const initialLength = mongoAdapter.listHistory.length
const ctx1 = {
from: '67890',
body: 'First message',
keyword: ['test'],
}
const ctx2 = {
from: '67890',
body: 'Second message',
keyword: ['test'],
}
await mongoAdapter.save(ctx1)
await mongoAdapter.save(ctx2)
assert.equal(mongoAdapter.listHistory.length, initialLength + 2)
})

test('[MongoAdapter] - getPrevByNumber', async () => {
const from = '12345'
const prevDocument = await mongoAdapter.getPrevByNumber(from)
assert.ok(prevDocument)
assert.equal(prevDocument.from, from)
})

test('[MongoAdapter] - getPrevByNumber returns latest document', async () => {
const from = '67890'
const prevDocument = await mongoAdapter.getPrevByNumber(from)
assert.ok(prevDocument)
assert.equal(prevDocument.from, from)
assert.equal(prevDocument.body, 'Second message')
})

test('[MongoAdapter] - getPrevByNumber returns undefined for non-existent number', async () => {
const from = 'nonexistent99999'
const prevDocument = await mongoAdapter.getPrevByNumber(from)
assert.not.ok(prevDocument, 'Should return undefined for non-existent number')
})

test('[MongoAdapter] - saved document should have date field', async () => {
const from = '12345'
const prevDocument = await mongoAdapter.getPrevByNumber(from)
assert.ok(prevDocument.date, 'Document should have date field')
assert.instance(prevDocument.date, Date)
})

test('[MongoAdapter] - credentials should be stored', () => {
assert.ok(mongoAdapter.credentials.dbUri, 'dbUri should be stored')
assert.equal(mongoAdapter.credentials.dbName, 'testDB', 'dbName should be stored')
})

test.after(async () => {
await mongoServer.stop()
hookClose().then()
Expand Down
2 changes: 1 addition & 1 deletion packages/database-mongo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"homepage": "https://github.com/codigoencasa/bot-whatsapp#readme",
"dependencies": {
"@builderbot/bot": "workspace:*",
"mongodb": "^4.17.2"
"mongodb": "^7.0.0"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^29.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/database-mysql/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"homepage": "https://github.com/codigoencasa/bot-whatsapp#readme",
"dependencies": {
"@builderbot/bot": "workspace:*",
"mysql2": "^2.3.3"
"mysql2": "^3.15.3"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^29.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/manager/__tests__/schemas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ test('createBotSchema - rejects reserved tenantId "active"', () => {
}
const result = validate(createBotSchema, input)
assert.not.ok(result.success)
assert.ok(result.error?.includes('reserved'))
assert.ok(result.error?.includes('reserved') || result.error?.includes('tenantId'))
})

test('createBotSchema - rejects reserved tenantId "health"', () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"@ffmpeg-installer/ffmpeg": "^1.1.0",
"fluent-ffmpeg": "^2.1.3",
"polka": "^0.5.2",
"zod": "^3.23.8"
"zod": "^4.1.13"
},
"peerDependencies": {
"@builderbot/bot": ">=1.0.0"
Expand Down
20 changes: 10 additions & 10 deletions packages/manager/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ const tenantIdSchema = z
.min(1, 'tenantId is required')
.max(50, 'tenantId must be 50 characters or less')
.regex(/^[a-zA-Z0-9-_]+$/, 'tenantId can only contain letters, numbers, hyphens and underscores')
.refine(
(val: string) => !RESERVED_TENANT_IDS.includes(val as (typeof RESERVED_TENANT_IDS)[number]),
(val: string) => ({ message: `"${val}" is a reserved tenantId and cannot be used` })
)
.refine((val: string) => !RESERVED_TENANT_IDS.includes(val as (typeof RESERVED_TENANT_IDS)[number]), {
message: 'is a reserved tenantId and cannot be used',
})

/**
* Schema for creating a new bot
Expand All @@ -31,7 +30,7 @@ export const createBotSchema = z.object({
.min(1024, 'port must be 1024 or higher')
.max(65535, 'port must be 65535 or lower')
.optional(),
providerOptions: z.record(z.any()).optional(),
providerOptions: z.record(z.string(), z.any()).optional(),
})

/**
Expand Down Expand Up @@ -97,10 +96,9 @@ export const createFlowSchema = z.object({
.min(1, 'id is required')
.max(50, 'id must be 50 characters or less')
.regex(/^[a-zA-Z0-9-_]+$/, 'id can only contain letters, numbers, hyphens and underscores')
.refine(
(val: string) => !RESERVED_FLOW_IDS.includes(val as (typeof RESERVED_FLOW_IDS)[number]),
(val: string) => ({ message: `"${val}" is a reserved flow id` })
),
.refine((val: string) => !RESERVED_FLOW_IDS.includes(val as (typeof RESERVED_FLOW_IDS)[number]), {
message: 'is a reserved flow id',
}),
/** Display name for the flow */
name: z.string().min(1).max(100),
/** Keywords that trigger this flow */
Expand Down Expand Up @@ -152,7 +150,9 @@ export function validate<T>(schema: z.ZodSchema<T>, data: unknown): ValidationRe
}
}

const errors = result.error.errors.map((err: z.ZodIssue) => ({
// Zod v4 uses 'issues'
const zodErrors = result.error.issues
const errors = zodErrors.map((err: z.ZodIssue) => ({
field: err.path.join('.') || 'root',
message: err.message,
}))
Expand Down
8 changes: 4 additions & 4 deletions packages/provider-baileys/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@
"@types/node": "^24.10.2",
"@types/qr-image": "^3.2.9",
"@types/sinon": "^17.0.3",
"body-parser": "^1.20.2",
"body-parser": "^2.2.1",
"cors": "^2.8.5",
"jest": "^30.2.0",
"mime-types": "^2.1.35",
"pino": "^7.11.0",
"mime-types": "^3.0.2",
"pino": "^10.1.0",
"polka": "^0.5.2",
"qr-image": "^3.2.0",
"rimraf": "^6.1.2",
Expand All @@ -63,7 +63,7 @@
"ts-jest": "^29.4.6",
"ts-node": "^10.9.2",
"wa-sticker-formatter": "^4.4.4",
"wtfnode": "^0.9.2"
"wtfnode": "^0.10.1"
},
"dependencies": {
"@adiwajshing/keyed-db": "^0.2.4",
Expand Down
4 changes: 2 additions & 2 deletions packages/provider-evolution-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@
"dependencies": {
"@polka/parse": "1.0.0-next.0",
"axios": "^1.13.2",
"body-parser": "^1.20.2",
"body-parser": "^2.2.1",
"file-type": "^19.0.0",
"follow-redirects": "^1.15.6",
"form-data": "^4.0.5",
"mime-types": "^2.1.35",
"mime-types": "^3.0.2",
"polka": "^0.5.2",
"queue-promise": "^2.2.1"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/provider-facebook-messenger/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
},
"dependencies": {
"axios": "^1.13.2",
"mime-types": "^2.1.35",
"mime-types": "^3.0.2",
"polka": "^0.5.2"
}
}
2 changes: 1 addition & 1 deletion packages/provider-instagram/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
},
"dependencies": {
"axios": "^1.13.2",
"mime-types": "^2.1.35",
"mime-types": "^3.0.2",
"polka": "^0.5.2"
}
}
53 changes: 53 additions & 0 deletions packages/provider-meta/__tests__/provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1072,4 +1072,57 @@ describe('#MetaProvider', () => {
expect(result).toContain(extension)
})
})

describe('#sendPresenceUpdate', () => {
test('should send typing_on status by default', async () => {
// Arrange
const fakeRecipient = '1234567890'
jest.spyOn(metaProvider, 'sendMessageToApi').mockResolvedValue({ success: true })

// Act
await metaProvider.sendPresenceUpdate(fakeRecipient)

// Assert
expect(metaProvider.sendMessageToApi).toHaveBeenCalledWith({
messaging_product: 'whatsapp',
recipient_type: 'individual',
to: fakeRecipient,
type: 'typing_on',
})
})

test('should send typing_on status when explicitly specified', async () => {
// Arrange
const fakeRecipient = '1234567890'
jest.spyOn(metaProvider, 'sendMessageToApi').mockResolvedValue({ success: true })

// Act
await metaProvider.sendPresenceUpdate(fakeRecipient, 'typing_on')

// Assert
expect(metaProvider.sendMessageToApi).toHaveBeenCalledWith({
messaging_product: 'whatsapp',
recipient_type: 'individual',
to: fakeRecipient,
type: 'typing_on',
})
})

test('should send typing_off status when specified', async () => {
// Arrange
const fakeRecipient = '1234567890'
jest.spyOn(metaProvider, 'sendMessageToApi').mockResolvedValue({ success: true })

// Act
await metaProvider.sendPresenceUpdate(fakeRecipient, 'typing_off')

// Assert
expect(metaProvider.sendMessageToApi).toHaveBeenCalledWith({
messaging_product: 'whatsapp',
recipient_type: 'individual',
to: fakeRecipient,
type: 'typing_off',
})
})
})
})
4 changes: 2 additions & 2 deletions packages/provider-meta/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@
},
"dependencies": {
"axios": "^1.13.2",
"body-parser": "^1.20.2",
"body-parser": "^2.2.1",
"file-type": "^19.0.0",
"form-data": "^4.0.5",
"mime-types": "^2.1.35",
"mime-types": "^3.0.2",
"polka": "^0.5.2",
"queue-promise": "^2.2.1"
},
Expand Down
1 change: 1 addition & 0 deletions packages/provider-meta/src/interface/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,5 @@ export interface MetaInterface {
sendFile: (to: string, mediaInput: string | null, caption: string, context: string | null) => Promise<any>
sendAudio: (to: string, fileOpus: string, context: string | null) => void
markAsRead: (wa_id: string) => Promise<any>
sendPresenceUpdate: (to: string, status?: 'typing_on' | 'typing_off') => Promise<any>
}
Loading
Loading