oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
71.86k stars 2.56k forks source link

Error from napi: napi_define_properties #12323

Closed JvilleDev closed 6 days ago

JvilleDev commented 6 days ago

How can we reproduce the crash?

I'm doing a venom-bot integration, and sometimes it works great, but now is crashing ._.

Code:

import express from "express";
import sqlite3 from "sqlite3";
import ip from "ip";
import mediaRoutes from "./routes/media";
import { router as dbRoutes, type ResponseType } from "./routes/db";
import venom, { Message } from "venom-bot";
import ffmpeg from "fluent-ffmpeg";
import path from "path";
import fs from "fs";
import ShortUniqueId from "short-unique-id";
import axios from "axios";

const port = 3100;
const db = new sqlite3.Database("./src/data.sqlite");
const app = express();
let qr: string | boolean = false;

// * Running check
app.get("/", (req, res) => {
  res.statusCode = 200;
  res.send("ok!");
});
// ? API Routes
app.use("/api", mediaRoutes);
app.use("/api/db", dbRoutes);

app.get("/api/qr", (req, res) => {
  res.statusCode = 200;
  res.send(qr);
});

// ! -> EXPRESS CONFIG
app.listen(port, () => {
  console.clear();
  console.log(`Server running on http://${ip.address()}:${port}`);
  prepareDb();
});
process.on("exit", () => db.close());

// * DB FUNCTIONS
const prepareDb = (): void => {
  db.serialize(() => {
    db.run(
      "CREATE TABLE IF NOT EXISTS chats (name TEXT NOT NULL, id TEXT PRIMARY KEY)"
    );
    db.run(
      "CREATE TABLE IF NOT EXISTS responses (contains TEXT NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT, answer TEXT NOT NULL, type TEXT NOT NULL)"
    );
    db.run(
      "CREATE TABLE IF NOT EXISTS media (filename TEXT NOT NULL, filetype TEXT NOT NULL, id INTEGER PRIMARY KEY AUTOINCREMENT)"
    );
  });
};

// * VENOM UTILS
async function saveChat(name: string, id: string) {
  db.run("INSERT INTO chats IF NOT EXISTS (name, id) VALUES (?, ?)", [
    name,
    id,
  ]);
}
async function checkResponse(
  text: string
): Promise<{ type: string; answer: string; contains: boolean } | false> {
  try {
    const normalizeText = (str: string) => {
      return str
        .normalize("NFD")
        .replace(/[\u0300-\u036f]/g, "")
        .toLowerCase();
    };

    const rows = await new Promise<ResponseType[]>((resolve, reject) => {
      db.all(
        "SELECT * FROM responses",
        (err: Error | null, rows: ResponseType[]) => {
          if (err) {
            reject(err);
          } else {
            resolve(rows);
          }
        }
      );
    });

    const receivedMessage = normalizeText(text);
    let matchWords: string[] = [];

    rows.forEach((item) => {
      const receivedPartial = normalizeText(item.contains);
      const partialWords = receivedPartial.split(" ");

      partialWords.forEach((word) => {
        const wordRegex = new RegExp(`\\b${word}\\b`, "i");
        if (
          wordRegex.test(receivedMessage) &&
          !matchWords.includes(word)
        ) {
          matchWords.push(word);
        }
      });
    });

    if (matchWords.length > 0) {
      const matchedResponse = rows.find((item) => {
        const normalizedContains = normalizeText(item.contains);
        return normalizedContains.includes(matchWords[0]);
      });

      if (matchedResponse) {
        return {
          type: matchedResponse.type,
          answer: matchedResponse.answer,
          contains: true,
        };
      }
    }

    return false;
  } catch (err) {
    console.error("Error fetching responses:", err);
    return false;
  }
}

async function handleMessage(msg: Message, client: any) {
  const text = msg.body;
  await saveChat(msg.from, msg.chat.name ? msg.chat.name : msg.from);
  if (!msg.fromMe) {
    if (msg.type === "chat") {
      try {
        const response = await checkResponse(text);
        if (response) {
          client.sendText(msg.from, response.answer);
        } else {
          client.sendText(msg.from, "Por favor, dame un momento.");
        }
      } catch (err) {
        console.error("Error checking response:", err);
      }
    } else {
      try {
        const mediaBuffer = await client.decryptFile(msg);
        const id = new ShortUniqueId({ length: 5 });

        const tmpFilePath = path.join(
          __dirname,
          "tmp",
          `${Date.now()}_${id.rnd()}.${msg.mimetype.split("/")[1]}`
        );
        fs.writeFileSync(tmpFilePath, mediaBuffer);

        const mp3FilePath = path.join(
          __dirname,
          "tmp",
          `${Date.now()}_${id.rnd()}.mp3`
        );
        await new Promise<void>((resolve, reject) => {
          ffmpeg(tmpFilePath)
            .format("mp3")
            .audioCodec("libmp3lame")
            .audioBitrate(128)
            .on("end", () => {
              fs.unlinkSync(tmpFilePath);
              resolve();
            })
            .on("error", (err) => reject(err))
            .save(mp3FilePath);
        });

        const mp3Buffer = fs.readFileSync(mp3FilePath);
        const contentType = "audio/mpeg";

        const axiosConfig = {
          headers: {
            Authorization: `Bearer ${process.env.WIT_TOKEN}`,
            "Content-Type": contentType,
          },
          params: {
            n: 1,
          },
        };

        const response = await axios.post(
          "https://api.wit.ai/speech",
          mp3Buffer,
          axiosConfig
        );

        if (!response.data) {
          throw new Error("Failed to transcribe media");
        }

        const witResponses = response.data
          .split("\r\n")
          .filter(Boolean)
          .map(JSON.parse);
        const lastWitResponse = witResponses[witResponses.length - 1];
        const transcription = lastWitResponse.text || "sdfkfljhsflaksjdhfl";

        try {
          const response = await checkResponse(transcription);
          if (response) {
            client.sendText(msg.from, response.answer);
          } else {
            console.error("No valid response found");
          }
        } catch (err) {
          console.error("Error checking response:", err);
        }
        fs.unlinkSync(mp3FilePath);
      } catch (err) {
        console.error("Error transcribing media:", err);
      }
    }
  }
}

// * VENOM
venom
  .create({ session: "venom-bot", catchQR: (qrCode) => (qr = qrCode) })
  .then((client) => start(client))
  .catch((erro) => console.log(erro));
function start(client: any) {
  client.onMessage((message: venom.Message) => {
    handleMessage(message, client);
  });
}

package.json

{
  "name": "backend",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "bun --watch src/index.ts",
    "start": "bun src/index.ts",
    "node-start": "tsc && node --env-file=.env ./src/dist/index.js",
    "node-dev": "tsc-watch --silent --noClear -p tsconfig.json --onSuccess \"node --env-file=.env ./src/dist/index.js\""
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@ffmpeg-installer/ffmpeg": "^1.1.0",
    "axios": "^1.7.2",
    "express": "^4.19.2",
    "fluent-ffmpeg": "^2.1.3",
    "ip": "^2.0.1",
    "multer": "^1.4.5-lts.1",
    "node-fetch": "^3.3.2",
    "short-unique-id": "^5.2.0",
    "sqlite3": "^5.1.7",
    "typescript": "^5.5.3",
    "venom-bot": "^5.1.0"
  },
  "devDependencies": {
    "@types/axios": "^0.14.0",
    "@types/express": "^4.17.21",
    "@types/fluent-ffmpeg": "^2.1.24",
    "@types/multer": "^1.4.11",
    "tsc-watch": "^6.2.0"
  }
}

Aditional info:

Stack Trace (bun.report)

Bun v1.1.17 (bb66bba) on windows x86_64 [AutoCommand]

panic: napi: napi_define_properties Error::Error

JvilleDev commented 6 days ago

For some reason, now is good ._.