Как я могу исправить проблему Electron: Ошибка: ENOTDIR, а не каталог?

Я впервые использую электрон. Я столкнулся с проблемой при создании приложения. Все делаю по документации, использую электронно-форж. npm run make запускается без проблем, но после попытки запуска установки или файла .exe возникает такая ошибка. Я искренне не понимаю в чем проблема, помогите Я использую vk и tg api, когда была готова только часть с vk, все прошло без проблем, но после загрузки @mtproto/core

Uncaught Exception:
Error: ENOTDIR, not а directory
  at createError(node:e1ectron/js2c/node_init:2:2095)
  at t.mkdirSync (node:e1ectron/js2c/node_init:2:16249)
  at module.exports.sync
(C:\Users\kazik\Desktop\social_network_analyzer\out\social-network-analyzer-win32-x64...6)
  at set all
(C:\Users\kazik\Desktop\social_network_analyzer\out\social-network-analyzer-win32-x64...12)
  at new Configstore
(C:\Users\kazik\Desktop\social_network_analyzer\out\social-network-analyzer-win32-x64...13)
  at getLocaIStorage
(C:\Users\kazik\Desktop\social_network_analyzer\out\social-network-analyzer-win32-x64...24)
  at new Storage
(C:\Users\kazik\Desktop\social_network_analyzer\out\social-network-analyzer-win32-x64...45)
  at new <anonymous>
(C:\Users\kazik\Desktop\social_network_analyzer\out\social-network-analyzer-win32-x64...22)
  at new API
(C:\Users\kazik\Desktop\social_network_analyzer\out\social-network-analyzer-win32-x64...20)
  at 0bject <anonymous>
(C:\Users\kazik\Desktop\social_network_analyzer\out\social-network-analyzer-win32-x64...13)

Но когда я запускаю приложение npm start, оно запускается и работает нормально.

вот мой package.json

{
  "name": "social_network_analyzer",
  "version": "1.0.0",
  "description": "app for analyze social networks for Press and Mass Communications Committee",
  "main": "main.js",
  "scripts": {
    "start": "electron-forge start",
    "package": "electron-forge package",
    "make": "electron-forge make"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/KazikS/social_network_analyzer"
  },
  "author": "sabkazz",
  "license": "ISC",
  "bugs": {
    "url": "https://github.com/KazikS/social_network_analyzer/issues"
  },
  "homepage": "https://github.com/KazikS/social_network_analyzer#readme",
  "devDependencies": {
    "@electron-forge/cli": "^7.4.0",
    "@electron-forge/maker-deb": "^7.4.0",
    "@electron-forge/maker-rpm": "^7.4.0",
    "@electron-forge/maker-squirrel": "^7.4.0",
    "@electron-forge/maker-zip": "^7.4.0",
    "@electron-forge/plugin-auto-unpack-natives": "^7.4.0",
    "@electron-forge/plugin-fuses": "^7.4.0",
    "@electron/fuses": "^1.8.0",
    "electron": "^31.1.0"
  },
  "dependencies": {
    "@mtproto/core": "^6.3.0",
    "axios": "^1.7.2",
    "electron-squirrel-startup": "^1.0.1",
    "mtproto": "^0.0.1",
    "scandir": "^0.0.4"
  }
}

вот мой main.js

const { app, BrowserWindow, ipcMain } = require("electron");
const path = require("node:path");
const axios = require("axios");
const api = require("./telegram/tgApi");
const { access } = require("node:fs");
const auth = require("./telegram/auth");
const getUser = require("./telegram/getUser");

let win;

const createWindow = () => {
  win = new BrowserWindow({
    width: 1000,
    height: 1000,
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
      contextIsolation: true,
      enableRemoteModule: false,
      nodeIntegration: false,
    },
  });
  win.loadFile("index.html");
  win.webContents.openDevTools();
  checkUserStatus();
};

async function checkUserStatus() {
  const user = await getUser();
  console.info(user);
  win.webContents.send("user-status", user);
}

app.whenReady().then(() => {
  createWindow();

  app.on("activate", () => {
    if (BrowserWindow.getAllWindows().length === 0) createWindow();
  });
  app.on("window-all-closed", () => {
    if (process.platform !== "darwin") app.quit();
  });
});

ipcMain.handle("analyze", async (event, { url, startDate, endDate }) => {
  try {
    if (url.includes("vk.com")) {
      return analyzeVK(url, startDate, endDate);
    } else if (url.includes("t.me")) {
      return analyzeTelegram(url, startDate, endDate);
    } else {
      throw new Error("Unsupported URL");
    }
  } catch (error) {
    console.error("Error in analyze handler:", error);
    throw error;
  }
});

//VK
async function analyzeVK(url, startDate, endDate) {
  const token =
    "*********************************************************";
  const apiVersion = "5.131";
  const groupId = await extractGroupId(url, token, apiVersion);
  let offset = 0;
  let count = 100;
  let allPosts = [];
  let hasMorePosts = true;
  while (hasMorePosts) {
    const response = await axios.get(`https://api.vk.com/method/wall.get`, {
      params: {
        owner_id: `-${groupId}`,
        count: count,
        offset: offset,
        access_token: token,
        v: apiVersion,
      },
    });
    const posts = response.data.response.items;
    if (posts.length < count) {
      hasMorePosts = false;
    }
    allPosts = allPosts.concat(posts);
    offset += count;
  }

  const start = new Date(startDate);
  const end = new Date(endDate);
  start.setHours(0, 0, 0, 0);
  end.setHours(23, 59, 59, 999);

  const filteredPosts = allPosts.filter((post) => {
    const postDate = new Date(post.date * 1000);
    return postDate >= start && postDate <= end;
  });
  const totalLikes = filteredPosts.reduce(
    (acc, post) => acc + (post.likes ? post.likes.count : 0),
    0
  );
  const totalViews = filteredPosts.reduce(
    (acc, post) => acc + (post.views ? post.views.count : 0),
    0
  );
  const dateDiff = Math.abs(end - start);
  const weeks = dateDiff / (1000 * 60 * 60 * 24 * 7);
  const avgPostsPerWeek = filteredPosts.length / weeks;
  console.info(
    "Views count: " +
      totalViews +
      "\n" +
      "Likes count: " +
      totalLikes +
      "\n" +
      "Per week" +
      avgPostsPerWeek +
      " " +
      weeks +
      " " +
      filteredPosts.length
  );
  return { filteredPosts, totalLikes, totalViews, avgPostsPerWeek };
}

//telegram

ipcMain.handle("update_config", async (event, data) => {
  console.info(data);
  auth(data.phone, data.code, data.password);

});


async function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function analyzeTelegram(url, startDate, endDate) {
  const channelUsername = extractChannelUsername(url);
  let allMessages = [];
  let offsetId = 0;
  let hasMoreMessages = true;

  try {
    const channelInfo = await api.call("contacts.resolveUsername", {
      username: channelUsername,
    });
    const channelId = channelInfo.chats[0].id;
    const accessHash = channelInfo.chats[0].access_hash;

    while (hasMoreMessages) {
      console.info(`Fetching messages with offset_id: ${offsetId}`);
      try {
        const messages = await api.call("messages.getHistory", {
          peer: {
            _: "inputPeerChannel",
            channel_id: channelId,
            access_hash: accessHash,
          },
          offset_id: offsetId,
          offset_date: 0,
          add_offset: 0,
          limit: 100,
          max_id: 0,
          min_id: 0,
          hash: 0,
        });

        if (messages.messages.length === 0) {
          console.info("No more messages to fetch");
          hasMoreMessages = false;
        } else {
          allMessages = allMessages.concat(messages.messages);
          offsetId = messages.messages[messages.messages.length - 1].id;
          console.info(
            `Fetched ${messages.messages.length} messages, next offset_id: ${offsetId}`
          );
        }
      } catch (error) {
        if (error.error_code === 420) {
          const waitTime =
            parseInt(error.error_message.split("_").pop(), 10) * 1000;
          console.info(`Flood wait detected, waiting for ${waitTime} ms`);
          await delay(waitTime);
        } else {
          throw error;
        }
      }
    }

    console.info("All messages:", allMessages);

    const start = new Date(startDate);
    const end = new Date(endDate);
    start.setHours(0, 0, 0, 0);
    end.setHours(23, 59, 59, 999);

    const filteredMessages = allMessages.filter((msg) => {
      const msgDate = new Date(msg.date * 1000);
      return msgDate >= start && msgDate <= end;
    });

    console.info("Filtered messages:", filteredMessages);

    let totalMessages = filteredMessages.length;
    let totalViews = 0;
    let totalReactions = 0;

    filteredMessages.forEach((msg) => {
      if (msg.views) totalViews += msg.views;
      if (msg.reactions && msg.reactions.results) {
        totalReactions += msg.reactions.results.length;
      }
    });

    const dateDiff = Math.abs(end - start);
    const weeks = dateDiff / (1000 * 60 * 60 * 24 * 7);
    const avgPostsPerWeek = filteredMessages.length / weeks;
    return { totalMessages, totalReactions, totalViews, avgPostsPerWeek };
  } catch (error) {
    console.error("Invalid query:", error);
    throw error;
  }
}

async function extractGroupId(url, token, apiVersion) {
  const match = url.match(/vk\.com/(?:public|club)(\d+)/);
  if (match) {
    return match[1];
  }
  const groupNameMatch = url.match(/vk\.com/(.+)/);
  const groupName = groupNameMatch ? groupNameMatch[1] : null;
  if (!groupName) {
    throw new Error("Invalid group URL");
  }
  const response = await axios.get("https://api.vk.com/method/groups.getById", {
    params: {
      group_id: groupName,
      access_token: token,
      v: apiVersion,
    },
  });
  const groupId = response.data.response[0].id;
  return groupId;
}

function extractChannelUsername(url) {
  const regex = /(?:https?://)?(?:t\.me|telegram\.me)/([a-zA-Z0-9_]+)/;
  const match = url.match(regex);
  return match ? match[1] : null;
}

Я пытался переустановить node_modules, заменить path.resolve(__dirname, "needed file") на './needed file', удалить nodeIntegration: false в webPrefences, переустановить Windows...

🤔 А знаете ли вы, что...
JavaScript поддерживает работу с графикой и аудио, что позволяет создавать мультимедийные веб-приложения.


80
1

Ответ:

Решено

В конструкторе класса API моего файла tgApi.js я изменил это:

storageOptions: {
        path: path.join(__dirname, 'session.json'),
      },

к этому:

const sessionPath = app.isPackaged
    ? path.join(app.getPath('userData'), 'session.json')
    : path.join(__dirname, 'session.json');
...
storageOptions: {
        path: sessionPath,
      },

Итак, исправленная версия выглядит так:

const path = require('path');
const MTProto = require('@mtproto/core');
const { sleep } = require('@mtproto/core/src/utils/common');
const { app } = require('electron');

class API {
  constructor() {
    const sessionPath = app.isPackaged
    ? path.join(app.getPath('userData'), 'session.json')
    : path.join(__dirname, 'session.json');

    this.mtproto = new MTProto({
      api_id: ****,
      api_hash: "****",

      storageOptions: {
        path: sessionPath,
      },
    });
  }

  async call(method, params, options = {}) {
    try {
      const result = await this.mtproto.call(method, params, options);

      return result;
    } catch (error) {
      console.info(`${method} error:`, error);

      const { error_code, error_message } = error;

      if (error_code === 420) {
        const seconds = Number(error_message.split('FLOOD_WAIT_')[1]);
        const ms = seconds * 1000;

        await sleep(ms);

        return this.call(method, params, options);
      }

      if (error_code === 303) {
        const [type, dcIdAsString] = error_message.split('_MIGRATE_');

        const dcId = Number(dcIdAsString);

        // If auth.sendCode call on incorrect DC need change default DC, because
        // call auth.signIn on incorrect DC return PHONE_CODE_EXPIRED error
        if (type === 'PHONE') {
          await this.mtproto.setDefaultDc(dcId);
        } else {
          Object.assign(options, { dcId });
        }

        return this.call(method, params, options);
      }

      return Promise.reject(error);
    }
  }
}

const api = new API();

module.exports = api;