diff --git a/file-1711615972612.oga b/file-1711615972612.oga new file mode 100644 index 0000000..c06627a Binary files /dev/null and b/file-1711615972612.oga differ diff --git a/file-1711616007252.oga b/file-1711616007252.oga new file mode 100644 index 0000000..2d1bafe Binary files /dev/null and b/file-1711616007252.oga differ diff --git a/file-1711616171723.oga b/file-1711616171723.oga new file mode 100644 index 0000000..d263e4c Binary files /dev/null and b/file-1711616171723.oga differ diff --git a/file-1711616227382.oga b/file-1711616227382.oga new file mode 100644 index 0000000..6208eef Binary files /dev/null and b/file-1711616227382.oga differ diff --git a/file-1711616252320.oga b/file-1711616252320.oga new file mode 100644 index 0000000..1a167e7 Binary files /dev/null and b/file-1711616252320.oga differ diff --git a/file-1711616395289.oga b/file-1711616395289.oga new file mode 100644 index 0000000..2734e5a Binary files /dev/null and b/file-1711616395289.oga differ diff --git a/file-1711616438382.oga b/file-1711616438382.oga new file mode 100644 index 0000000..f425311 Binary files /dev/null and b/file-1711616438382.oga differ diff --git a/package.json b/package.json index bcbf2ed..87eca91 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,13 @@ "license": "ISC", "dependencies": { "@builderbot-plugins/telegram": "^1.0.0", - "@builderbot/bot": "1.0.23-alpha.0", - "@builderbot/provider-baileys": "1.0.23-alpha.0", + "@builderbot/bot": "1.0.27-alpha.0", + "@builderbot/provider-baileys": "1.0.27-alpha.0", + "@ffmpeg-installer/ffmpeg": "^1.1.0", "date-fns": "^3.3.1", "date-fns-tz": "3.0.0-beta.2", "dotenv": "^16.4.2", + "fluent-ffmpeg": "^2.1.2", "multer": "1.4.5-lts.1", "openai": "^4.27.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4fe23e..ec80687 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,13 +7,16 @@ settings: dependencies: '@builderbot-plugins/telegram': specifier: ^1.0.0 - version: 1.0.0(@builderbot/bot@1.0.23-alpha.0)(polka@0.5.2)(telegraf@4.15.3) + version: 1.0.0(@builderbot/bot@1.0.27-alpha.0)(polka@0.5.2)(telegraf@4.15.3) '@builderbot/bot': - specifier: 1.0.23-alpha.0 - version: 1.0.23-alpha.0 + specifier: 1.0.27-alpha.0 + version: 1.0.27-alpha.0 '@builderbot/provider-baileys': - specifier: 1.0.23-alpha.0 - version: 1.0.23-alpha.0 + specifier: 1.0.27-alpha.0 + version: 1.0.27-alpha.0 + '@ffmpeg-installer/ffmpeg': + specifier: ^1.1.0 + version: 1.1.0 date-fns: specifier: ^3.3.1 version: 3.3.1 @@ -23,6 +26,9 @@ dependencies: dotenv: specifier: ^16.4.2 version: 16.4.5 + fluent-ffmpeg: + specifier: ^2.1.2 + version: 2.1.2 multer: specifier: 1.4.5-lts.1 version: 1.4.5-lts.1 @@ -66,14 +72,14 @@ packages: engines: {node: '>=4'} dev: false - /@builderbot-plugins/telegram@1.0.0(@builderbot/bot@1.0.23-alpha.0)(polka@0.5.2)(telegraf@4.15.3): + /@builderbot-plugins/telegram@1.0.0(@builderbot/bot@1.0.27-alpha.0)(polka@0.5.2)(telegraf@4.15.3): resolution: {integrity: sha512-NmcJCvSpyZHvtS76XhJMGO49Q88iW7lKgCPvLTI1amTt+80ZSiTrz3neOfoRv/Ko2tOK9bNT9vcSCUtT2sSweA==} peerDependencies: '@builderbot/bot': '>=1.0.0' polka: '>=0.5.2' telegraf: '=4.15.3' dependencies: - '@builderbot/bot': 1.0.23-alpha.0 + '@builderbot/bot': 1.0.27-alpha.0 body-parser: 1.20.2 dotenv: 16.4.1 polka: 0.5.2 @@ -82,21 +88,25 @@ packages: - supports-color dev: false - /@builderbot/bot@1.0.23-alpha.0: - resolution: {integrity: sha512-R3uodj0d+FrsqsgaBa/SOdzK9uRfTLSEpg4WF7wcObYeAjNTiDEp7lTMrPkokpROx16VF21xhEYnMRy92RULuQ==} + /@builderbot/bot@1.0.27-alpha.0: + resolution: {integrity: sha512-qYm8VT8nWj/bRKE/iEbapdmKsWXTRMrgmHT7PTdiPZZUsqOQTmclGFrrAp9dW5pEQ5rSTeb5njQnR79Tq059Zw==} dependencies: '@ffmpeg-installer/ffmpeg': 1.1.0 + body-parser: 1.20.2 + cors: 2.8.5 fluent-ffmpeg: 2.1.2 follow-redirects: 1.15.5 mime-types: 2.1.35 picocolors: 1.0.0 + polka: 0.5.2 sharp: 0.33.2 transitivePeerDependencies: - debug + - supports-color dev: false - /@builderbot/provider-baileys@1.0.23-alpha.0: - resolution: {integrity: sha512-bHvznizxYPUsxJ38cr45w0lBnVTqzAXuzxtn1JqV1s8cRFM+4N0mlmtn8oSN6Z8+xIr5KK6EqXvCyaAjheIVBw==} + /@builderbot/provider-baileys@1.0.27-alpha.0: + resolution: {integrity: sha512-/QAxr0E+wNV7JaVhpbSv39v04E9zpdmtRpPlDi08FXNyXsPxMj1hWlilWIAESVI1/dYRcvv91LkYw4ONOTbNtw==} dependencies: '@ffmpeg-installer/ffmpeg': 1.1.0 '@types/polka': 0.5.7 @@ -1367,6 +1377,14 @@ packages: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} dev: false + /cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + dev: false + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2917,6 +2935,11 @@ packages: hasBin: true dev: false + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + /web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} diff --git a/src/flows/index.ts b/src/flows/index.ts index 82f9187..bdffa0e 100644 --- a/src/flows/index.ts +++ b/src/flows/index.ts @@ -4,11 +4,13 @@ import { welcomeFlow } from "./welcome.flow"; import { flowSeller } from "./seller.flow"; import { flowSchedule } from "./schedule.flow"; import { flowConfirm } from "./confirm.flow"; +import { voiceFlow } from "./voice.flow"; export default createFlow([ welcomeFlow, flowSeller, flowSchedule, - flowConfirm + flowConfirm, + voiceFlow ]) \ No newline at end of file diff --git a/src/flows/seller.flow.ts b/src/flows/seller.flow.ts index 37d4d05..b89d208 100644 --- a/src/flows/seller.flow.ts +++ b/src/flows/seller.flow.ts @@ -1,6 +1,6 @@ import { addKeyword, EVENTS } from "@builderbot/bot"; import { generateTimer } from "../utils/generateTimer"; -import { getHistoryParse, handleHistory } from "../utils/handleHistory"; +import { getHistory, getHistoryParse, handleHistory } from "../utils/handleHistory"; import AIClass from "../services/ai"; import { getFullCurrentDate } from "src/utils/currentDate"; import { pdfQuery } from "src/services/pdf"; @@ -43,13 +43,14 @@ export const generatePromptSeller = (history: string, database: string) => { const flowSeller = addKeyword(EVENTS.ACTION) .addAnswer(`⏱️`) - .addAction(async (ctx, { state, flowDynamic, extensions }) => { + .addAction(async (_, { state, flowDynamic, extensions }) => { try { const ai = extensions.ai as AIClass + const lastMessage = getHistory(state).at(-1) const history = getHistoryParse(state) - const dataBase = await pdfQuery(ctx.body) + const dataBase = await pdfQuery(lastMessage.content) console.log({ dataBase }) const promptInfo = generatePromptSeller(history, dataBase) diff --git a/src/flows/voice.flow.ts b/src/flows/voice.flow.ts new file mode 100644 index 0000000..89c70b0 --- /dev/null +++ b/src/flows/voice.flow.ts @@ -0,0 +1,26 @@ +import { EVENTS, addKeyword } from "@builderbot/bot"; +import { BaileysProvider } from "@builderbot/provider-baileys"; +import AIClass from "src/services/ai"; +import { processAudio } from "src/utils/process"; +import { welcomeFlow } from "./welcome.flow"; +import mainLayer from "src/layers/main.layer"; +import { handleHistory } from "src/utils/handleHistory"; + +const voiceFlow = addKeyword(EVENTS.VOICE_NOTE) + .addAction(async (_, { flowDynamic }) => { + await flowDynamic(`dame un momento para esucharte...`) + }) + .addAction(async (ctx: any, { provider, extensions, gotoFlow, state }) => { + try{ + const ai = extensions.ai as AIClass + const pathVoice = await provider.saveFile(ctx) + const mp3Path = await processAudio(pathVoice) + const text = await ai.voiceToText(mp3Path) + await handleHistory({ content: text, role: 'user' }, state) + return gotoFlow(welcomeFlow) + }catch(e){ + console.log(`Err`,e)} + }) + .addAction(mainLayer) + +export { voiceFlow } \ No newline at end of file diff --git a/src/layers/conversational.layer.ts b/src/layers/conversational.layer.ts index 26b8f68..64db8a8 100644 --- a/src/layers/conversational.layer.ts +++ b/src/layers/conversational.layer.ts @@ -5,5 +5,6 @@ import { handleHistory } from "../utils/handleHistory"; * Su funcion es almancenar en el state todos los mensajes que el usuario escriba */ export default async ({ body }: BotContext, { state, }: BotMethods) => { + if(body.includes('_event_')) return await handleHistory({ content: body, role: 'user' }, state) } \ No newline at end of file diff --git a/src/layers/main.layer.ts b/src/layers/main.layer.ts index 87e1f18..ae3e7f0 100644 --- a/src/layers/main.layer.ts +++ b/src/layers/main.layer.ts @@ -19,15 +19,15 @@ Por favor, analiza la siguiente conversación y determina la intención del usua export default async (_: BotContext, { state, gotoFlow, extensions }: BotMethods) => { const ai = extensions.ai as AIClass const history = getHistoryParse(state) - const prompt = PROMPT_DISCRIMINATOR + const prompt = PROMPT_DISCRIMINATOR.replace('{HISTORY}', history) - console.log(prompt.replace('{HISTORY}', history)) + console.log(prompt) const { prediction } = await ai.determineChatFn([ { role: 'system', - content: prompt.replace('{HISTORY}', history) + content: prompt } ]) diff --git a/src/utils/process.ts b/src/utils/process.ts new file mode 100644 index 0000000..9f9cf0a --- /dev/null +++ b/src/utils/process.ts @@ -0,0 +1,27 @@ +import ffmpeg from "fluent-ffmpeg"; +import {join} from "path"; +import {tmpdir} from "os"; +import { path as ffmpegPath } from '@ffmpeg-installer/ffmpeg'; +ffmpeg.setFfmpegPath(ffmpegPath); + +const convertOggMp3 = async (inputStream: string, outStream: string) => { + return new Promise((resolve) => { + ffmpeg(inputStream) + .audioQuality(96) + .toFormat("mp3") + .save(outStream) + .on("progress", () => null) + .on("end", () => { + resolve(true); + }); + }); +}; + +/** + * Return mp3 path + */ +export const processAudio = async (pathOgg: string ) => { + const pathTmpMp3 = join(tmpdir(),`voice-note-${Date.now()}.mp3`); + await convertOggMp3(pathOgg, pathTmpMp3); + return pathTmpMp3 +} \ No newline at end of file