Я впервые использую электрон. Я столкнулся с проблемой при создании приложения. Все делаю по документации, использую электронно-форж. 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 поддерживает работу с графикой и аудио, что позволяет создавать мультимедийные веб-приложения.
В конструкторе класса 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;