Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[🐛]Al usar base de datos postgres no ingresa al flujo de respuesta esperada #1102

Open
Kira1414 opened this issue Oct 14, 2024 · 4 comments
Labels
bug Something isn't working no-issue-activity

Comments

@Kira1414
Copy link

¿Que versión estas usando?

v2

¿Sobre que afecta?

Flujo de palabras (Flow)

Describe tu problema

Hola,

De antemano pido disculpas si lo estoy haciendo mal, es la primera vez que hago esto.

No sé si solo pasa en linux o solo sea un caso atipico pero sucede que al encadenar respuestas y almacenar el comportamiento de la respuesta no sigue el flujo, por ejemplo:

const registerFlow = addKeyword<Provider, Database>(['register', utils.setEvent('REGISTER_FLOW')]) .addAnswer(What is your name?, { capture: true }, async (ctx, { state }) => { await state.update({ name: ctx.body }) }) .addAnswer('What is your age?', { capture: true }, async (ctx, { state }) => { await state.update({ age: ctx.body }) }) .addAction(async (_, { flowDynamic, state }) => { await flowDynamic(${state.get('name')}, thanks for your information!: Your age: ${state.get('age')}) })

En el anterior código, cuando el usuario escribe register, el bot responde correctamente preguntando What is your name?, pero al usuario responder con el nombre, ahí se detiene el flujo.

Descubrí que el problema estaba en el archivo base-ts-baileys-postgres/node_modules/@builderbot/database-postgres/dist/index.cjs, mas exactamente en el método save porque en él al armar el array n, se está pasando un parametro t.phone y al parecer este no existe, el que existe es el parámetro t.from

Reproducir error

No response

Información Adicional

No response

@Kira1414 Kira1414 added the bug Something isn't working label Oct 14, 2024
@emilianoduran
Copy link

Tuve el mismo problema.

@SantiagoGaonaC
Copy link

SantiagoGaonaC commented Oct 22, 2024

Tengo exactamente el mismo problema
Al parecer es acá

export type HistoryEntry = {
ref: string
keyword?: string
answer: string
refSerialize: string
from: string
options?: Record<string, any>
}

Pero lo que se espera en la tabla es:

CREATE TABLE IF NOT EXISTS history (
id SERIAL PRIMARY KEY,
ref VARCHAR(255) NOT NULL,
keyword VARCHAR(255),
answer TEXT NOT NULL,
refSerialize TEXT NOT NULL,
phone VARCHAR(255) DEFAULT NULL,
options JSONB,
created_at TIMESTAMP DEFAULT current_timestamp,
updated_in TIMESTAMP,
contact_id INTEGER REFERENCES contact(id)
)`

Un ejemplo donde lo inserta:

async save(ctx: HistoryEntry): Promise<void> {
const values = [ctx.ref, ctx.keyword, ctx.answer, ctx.refSerialize, ctx.from, JSON.stringify(ctx.options)]
const query = `SELECT save_or_update_history_and_contact($1, $2, $3, $4, $5, $6)`

Al parecer el error vino de la PR: #1094 en este momento se seguía manteniendo en el dominio phone pero lo que hicieron fue cambiarlo a from, a día de hoy la tabla se sigue construyendo con el campo phone @leifermendez
Sería cambiarlo a from el campo en la creación de la base de datos, pero tal vez puede romperle a algunos

@SantiagoGaonaC
Copy link

Dejo el código corregido del adapter, cambios en las lineas 52 y 62 ctx.phone a ctx.from, ya que viene como tal from y no viene un phone, algo así sería el json que entra con el type HistoryEntry a las funciones:

📝 Saving history entry: {
  ref: 'ans_8a771232-7f84-4077-85f3-7182c4e2128b',
  keyword: 'key_5a23aa89-395e-4c39-12af-cdf791a4cb03',
  answer: '__call_action__',
  options: {
    media: undefined,
    buttons: [],
    capture: false,
    delay: 0,
    idle: undefined,
    ref: undefined,
    nested: [],
    keyword: {},
    callback: true
  },
  refSerialize: 'bc0a8804f59ee93780e7b4fe65e347af',
  from: 'NUMERODECELULAR(borrado para el ejemplo)´
}

No viene un campo phone como tal

Interface:

export type HistoryEntry = {
    ref: string
    keyword?: string
    answer: string
    refSerialize: string
    phone?: string
    options?: Record<string, any>
    from: string
}

Adapter:

import { MemoryDB } from "@builderbot/bot";
import pg from "pg";

import type { Contact, Credential, HistoryEntry } from "./types";

class PostgreSQLAdapter extends MemoryDB {
    db: any;
    listHistory: HistoryEntry[] = [];
    credentials: Credential = { host: "localhost", user: "", database: "", password: null, port: 5432 };

    constructor(_credentials: Credential) {
        super();
        this.credentials = _credentials;
        this.init().then();
    }

    async init(): Promise<boolean | undefined> {
        try {
            const { Pool } = pg;
            const pool = new Pool(this.credentials);
            const db = await pool.connect();
            this.db = db;
            console.log("🆗 Successful DB Connection");
            this.checkTableExistsAndSP();
            return true;
        } catch (error) {
            console.log("Error", error);
            throw error;
        }
    }

    async getPrevByNumber(from: string): Promise<HistoryEntry | undefined> {
        const query = "SELECT * FROM public.history WHERE phone = $1 ORDER BY created_at DESC LIMIT 1";
        try {
            const result = await this.db.query(query, [from]);
            const row = result.rows[0];

            if (row) {
                row["refSerialize"] = row.refserialize;
                delete row.refserialize;
            }

            return row;
        } catch (error) {
            console.error("Error getting previous entry by number:", error);
            throw error;
        }
    }

    async save(ctx: HistoryEntry): Promise<void> {
        console.log("📝 Saving history entry:", ctx);
        const values = [ctx.ref, ctx.keyword, ctx.answer, ctx.refSerialize, ctx.from, JSON.stringify(ctx.options)];
        const query = "SELECT save_or_update_history_and_contact($1, $2, $3, $4, $5, $6)";

        try {
            await this.db.query(query, values);
        } catch (error) {
            console.error("Error registering history entry:", error);
            throw error;
        }
        this.listHistory.push(ctx);
    }

    async getContact(ctx: HistoryEntry): Promise<Contact | undefined> {
        const from = ctx.from;
        const query = "SELECT * FROM public.contact WHERE phone = $1 LIMIT 1";
        try {
            const result = await this.db.query(query, [from]);
            return result.rows[0];
        } catch (error) {
            if (error instanceof Error) {
                console.error("Error getting contact by number:", error.message);
            }
            throw error;
        }
    }

    async saveContact(ctx: any): Promise<void> {
        // action: u (Actualiza el valor de ctx.values), a (Agrega). Agrega por defecto.
        const _contact = await this.getContact(ctx);
        let jsValues = {};

        if ((ctx?.action ?? "a") === "a") {
            jsValues = { ..._contact?.values, ...(ctx?.values ?? {}) };
        } else {
            jsValues = ctx?.values ?? {};
        }

        const values = [ctx.from, JSON.stringify(jsValues)];
        const query = "SELECT save_or_update_contact($1, $2)";

        try {
            await this.db.query(query, values);
        } catch (error) {
            console.error("🚫 Error saving or updating contact:", error);
            throw error;
        }
    }

    async checkTableExistsAndSP(): Promise<void> {
        const contact = `
            CREATE TABLE IF NOT EXISTS contact (
                id SERIAL PRIMARY KEY,
                phone VARCHAR(255) DEFAULT NULL,
                created_at TIMESTAMP DEFAULT current_timestamp,
                updated_in TIMESTAMP,
                last_interaction TIMESTAMP,
                values JSONB
            )`;
        try {
            await this.db.query(contact);
        } catch (error) {
            console.error("🚫 Error creating the contact table:", error);
            throw error;
        }

        const history = `
            CREATE TABLE IF NOT EXISTS history (
                id SERIAL PRIMARY KEY,
                ref VARCHAR(255) NOT NULL,
                keyword VARCHAR(255),
                answer TEXT NOT NULL,
                refSerialize TEXT NOT NULL,
                phone VARCHAR(255) DEFAULT NULL,
                options JSONB,
                created_at TIMESTAMP DEFAULT current_timestamp,
                updated_in TIMESTAMP,
                contact_id INTEGER REFERENCES contact(id)
            )`;
        try {
            await this.db.query(history);
        } catch (error) {
            console.error("🚫 Error creating the history table:", error);
            throw error;
        }

        await this.createSP();
    }

    async createSP(): Promise<void> {
        const sp_suc = `
        CREATE OR REPLACE FUNCTION save_or_update_contact(
            in_phone VARCHAR(255),
            in_values JSONB
        )
        RETURNS VOID AS
        $$
        DECLARE
            contact_cursor refcursor := 'cur_contact';
            contact_id INT;
        BEGIN
            SELECT id INTO contact_id FROM contact WHERE phone = in_phone;
        
            IF contact_id IS NULL THEN
                INSERT INTO contact (phone, "values")
                VALUES (in_phone, in_values);
            ELSE
                UPDATE contact SET "values" = in_values, updated_in = current_timestamp
                WHERE id = contact_id;
            END IF;
        END;
        $$ LANGUAGE plpgsql;`;

        try {
            await this.db.query(sp_suc);
        } catch (error) {
            console.error("🚫 Error creating the stored procedure for contact:", error);
            throw error;
        }

        const sp_suhc = `
        CREATE OR REPLACE FUNCTION save_or_update_history_and_contact(
            in_ref VARCHAR(255),
            in_keyword VARCHAR(255),
            in_answer TEXT,
            in_refserialize TEXT,
            in_phone VARCHAR(255),
            in_options JSONB
        )
        RETURNS VOID AS
        $$
        DECLARE
            _contact_id INT;
        BEGIN
            SELECT id INTO _contact_id FROM contact WHERE phone = in_phone;
        
            IF _contact_id IS NULL THEN
                INSERT INTO contact (phone)
                VALUES (in_phone)
                RETURNING id INTO _contact_id;
            ELSE
                UPDATE contact SET last_interaction = current_timestamp WHERE id = _contact_id;
            END IF;
        
            INSERT INTO history (ref, keyword, answer, refserialize, phone, options, contact_id, created_at)
            VALUES (in_ref, in_keyword, in_answer, in_refserialize, in_phone, in_options, _contact_id, current_timestamp);
        
        END;
        $$ LANGUAGE plpgsql;`;

        try {
            await this.db.query(sp_suhc);
        } catch (error) {
            console.error("🚫 Error creating the stored procedure for history:", error);
            throw error;
        }
    }
}

export { PostgreSQLAdapter };
``

Copy link
Contributor

¿Alguna novedad sobre esta ISSUE?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working no-issue-activity
Projects
None yet
Development

No branches or pull requests

3 participants