n8n-nodes-zalo-nnt
Version:
Unofficial Zalo integration for n8n - Send messages, manage groups, user operations with QR login. No API key required, works via browser simulation.
322 lines • 18 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZaloLoginByQr = void 0;
const n8n_workflow_1 = require("n8n-workflow");
const zca_js_1 = require("zca-js");
const axios_1 = __importDefault(require("axios"));
const branding_1 = require("../shared/branding");
class ZaloLoginByQr {
constructor() {
this.description = {
displayName: 'Zalo Đăng nhập QR by diginno.net',
name: 'zaloLoginByQr',
group: ['Zalo'],
version: 1,
description: 'Đăng nhập Zalo bằng QR code và lưu thông tin vào Credential by diginno.net',
defaults: {
name: 'Zalo Đăng nhập QR by diginno.net',
},
inputs: ['main'],
outputs: ['main'],
icon: 'file:../shared/image.svg',
credentials: [
{
name: 'n8nApiZaloApi',
required: true,
displayName: 'n8n API Account (Required - only API Key & URL)',
},
],
properties: [
branding_1.BRANDING_CONFIG,
{
displayName: '📺 Xem video hướng dẫn chi tiết tại <a href="https://www.youtube.com/watch?v=iwziXxQoW_Q" target="_blank" style="color: #1890ff; text-decoration: underline;">YouTube</a>',
name: 'videoTutorial',
type: 'notice',
default: '',
},
{
displayName: 'Proxy',
name: 'proxy',
type: 'string',
default: '',
placeholder: 'https://user:pass@host:port',
description: 'HTTP proxy to use for Zalo API requests',
},
],
};
}
async execute() {
const returnData = [];
const proxy = this.getNodeParameter('proxy', 0, '');
const timeout = 30;
const fileName = 'zalo-qr-code.png';
let n8nCredential;
try {
n8nCredential = await this.getCredentials('n8nApiZaloApi');
}
catch (error) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'n8n API credentials are required for QR login');
}
console.error('Using n8n account credential for QR login');
try {
const zaloOptions = {
selfListen: true,
logging: true,
};
if (proxy) {
zaloOptions.proxy = proxy;
}
const zalo = new zca_js_1.Zalo(zaloOptions);
console.error('Starting Zalo QR login process...');
const processContext = (context) => {
if (!context) {
console.error('Context is null or undefined');
return;
}
const cookie = context.cookie || '';
const imei = context.imei || '';
const userAgent = context.userAgent || '';
console.error('=== ZALO CREDENTIALS ===');
console.error('Cookie:', cookie
? `Received (length: ${typeof cookie === 'string' ? cookie.length : Array.isArray(cookie) ? cookie.length : 'unknown'})`
: 'None');
console.error('IMEI:', imei ? imei : 'None');
console.error('User Agent:', userAgent ? userAgent : 'None');
console.error('=== END CREDENTIALS ===');
};
const setupEventListeners = (api) => {
console.error('Setting up event listeners to get credentials');
try {
if (typeof api.getContext === 'function') {
const contextResult = api.getContext();
if (contextResult && typeof contextResult.then === 'function') {
contextResult
.then((context) => {
processContext(context);
})
.catch((error) => {
console.error('Error getting context:', error);
});
}
else {
processContext(contextResult);
}
}
else {
console.error('getContext is not a function');
if (api.context) {
console.error('Found context in api object');
processContext(api.context);
}
else {
console.error('No context found in api object');
}
}
}
catch (error) {
console.error('Error in setupEventListeners:', error);
}
};
const qrCodePromise = new Promise(async (resolve, reject) => {
let isResolved = false;
let userDisplayName = '';
let userPhone = '';
const timeoutId = setTimeout(() => {
if (!isResolved) {
isResolved = true;
reject(new n8n_workflow_1.NodeOperationError(this.getNode(), 'Timeout generating QR code. Please try again or check your Zalo connection.'));
}
}, timeout * 1000);
try {
let api = await zalo.loginQR(null, (qrEvent) => {
var _a, _b;
console.error('Received QR event type:', qrEvent ? qrEvent.type : 'no event');
switch (qrEvent.type) {
case 0:
if ((_a = qrEvent === null || qrEvent === void 0 ? void 0 : qrEvent.data) === null || _a === void 0 ? void 0 : _a.image) {
const qrCodeBase64 = qrEvent.data.image;
console.error('QR code generated, length:', qrCodeBase64.length);
if (isResolved)
return;
clearTimeout(timeoutId);
if (qrCodeBase64) {
isResolved = true;
resolve(qrCodeBase64);
}
}
else {
console.error('Could not get QR code from Zalo SDK');
reject(new Error('Could not get QR code'));
}
break;
case 1:
console.error('QR code expired. Please try again.');
break;
case 2:
console.error('=== QR CODE SCANNED ===');
if (qrEvent === null || qrEvent === void 0 ? void 0 : qrEvent.data) {
console.error('User:', qrEvent.data.display_name);
console.error('Avatar:', qrEvent.data.avatar ? 'Yes' : 'No');
userDisplayName = qrEvent.data.display_name || '';
if (qrEvent.data.phone) {
userPhone = qrEvent.data.phone;
}
}
break;
case 3:
console.error('=== QR CODE DECLINED ===');
if ((_b = qrEvent === null || qrEvent === void 0 ? void 0 : qrEvent.data) === null || _b === void 0 ? void 0 : _b.code) {
console.error('Decline code:', qrEvent.data.code);
}
break;
case 4:
console.error('=== GOT LOGIN INFO ===');
if (qrEvent === null || qrEvent === void 0 ? void 0 : qrEvent.data) {
const cookie = qrEvent.data.cookie || [];
const imei = qrEvent.data.imei || '';
const userAgent = qrEvent.data.userAgent || '';
console.error('Cookie received:', cookie.length > 0 ? 'Yes' : 'No');
console.error('IMEI received:', imei ? 'Yes' : 'No');
console.error('User Agent received:', userAgent ? 'Yes' : 'No');
try {
if (cookie.length > 0 || imei || userAgent) {
const now = new Date();
const vietnamTime = new Date(now.getTime());
const day = String(vietnamTime.getDate()).padStart(2, '0');
const month = String(vietnamTime.getMonth() + 1).padStart(2, '0');
const year = vietnamTime.getFullYear();
const hours = String(vietnamTime.getHours()).padStart(2, '0');
const minutes = String(vietnamTime.getMinutes()).padStart(2, '0');
const timeStr = `${day}-${month}-${year} ${hours}:${minutes}`;
let credentialName = userDisplayName || 'Zalo';
if (userPhone && userPhone.length >= 3) {
const last3Digits = userPhone.slice(-3);
credentialName += ` ${last3Digits}`;
}
credentialName += ` ${timeStr}`;
(async () => {
try {
console.error('=== CREATING ZALO CREDENTIAL ===');
const credentialApiData = {
name: credentialName,
type: 'zaloApi',
nodesAccess: [],
data: {
cookie: JSON.stringify(cookie),
imei: imei,
userAgent: userAgent,
proxy: proxy || '',
supportCode: '',
licenseKey: '',
},
};
const n8nApi = await this.getCredentials('n8nApiZaloApi');
const n8nApiUrl = n8nApi.url.replace(/\/+$/, '');
const n8nApiKey = n8nApi.apiKey;
const fullApiUrl = `${n8nApiUrl}/api/v1/credentials`;
console.error(`Creating credential: ${credentialName}`);
console.error(`API URL: ${fullApiUrl}`);
const response = await axios_1.default.post(fullApiUrl, credentialApiData, {
headers: {
'Content-Type': 'application/json',
'X-N8N-API-KEY': n8nApiKey,
},
});
console.error('=== CREDENTIAL CREATED SUCCESSFULLY ===');
console.error('Response:', JSON.stringify(response.data).substring(0, 300));
}
catch (error) {
console.error('=== CREDENTIAL CREATION FAILED ===');
console.error('Error:', error.message);
if (error.response) {
console.error('Status:', error.response.status);
console.error('Data:', JSON.stringify(error.response.data));
}
else if (error.request) {
console.error('No response - check n8n URL and if n8n is running');
}
}
})();
}
else {
console.error('=== NO CREDENTIALS TO SAVE ===');
console.error('No login information available to save');
}
}
catch (fileError) {
console.error('Error saving credentials:', fileError.message);
}
}
break;
default:
console.error('Unknown QR event type:', qrEvent.type);
}
});
console.error('Starting Zalo listener');
api.listener.start();
api.listener.onConnected(() => {
console.error('=== ZALO SDK CONNECTED ===');
setupEventListeners(api);
});
api.listener.onError((error) => {
console.error('=== ZALO ERROR ===', error);
});
api.listener.onMessage((message) => {
console.error('=== ZALO MESSAGE RECEIVED ===');
console.error('Message type:', message.type);
console.error('Message content:', JSON.stringify(message).substring(0, 200) + '...');
if (message.type === 'login_success' || message.type === 'qr_scanned') {
console.error('=== QR CODE SCANNED OR LOGIN SUCCESSFUL ===');
setupEventListeners(api);
}
});
console.error('All event listeners set up');
}
catch (error) {
clearTimeout(timeoutId);
if (!isResolved) {
isResolved = true;
reject(error);
}
}
});
const qrCodeBase64 = await qrCodePromise;
const binaryData = Buffer.from(qrCodeBase64, 'base64');
const newItem = {
json: {
success: true,
message: 'QR code generated successfully. Scan with Zalo app to login. Credentials will be auto-created after successful login.',
fileName,
credentialType: 'n8nApiZaloApi',
},
binary: {
data: await this.helpers.prepareBinaryData(binaryData, fileName, 'image/png'),
},
};
returnData.push(newItem);
if (returnData[0] && returnData[0].json) {
returnData[0].json.credentialInstructions =
'Using n8n API credential. New Zalo credentials will be created automatically after successful QR login.';
returnData[0].json.credentialName = n8nCredential.name || 'Unknown';
returnData[0].json.credentialId = n8nCredential.id || 'Unknown';
returnData[0].json.autoCreateApi =
'Credentials will be created automatically via n8n API after successful login.';
}
return [returnData];
}
catch (error) {
if (this.continueOnFail()) {
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray({ error: error.message }), { itemData: { item: 0 } });
return [executionData];
}
else {
throw error;
}
}
}
}
exports.ZaloLoginByQr = ZaloLoginByQr;
//# sourceMappingURL=ZaloLoginByQr.node.js.map