UNPKG

n8n-nodes-naver-sms

Version:
214 lines (213 loc) 9.92 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NaverSms = void 0; const n8n_workflow_1 = require("n8n-workflow"); const crypto_1 = __importDefault(require("crypto")); class NaverSms { constructor() { this.description = { displayName: 'Naver SMS', name: 'naverSms', icon: 'fa:envelope', group: ['output'], version: 1, description: 'Send SMS/LMS/MMS via Naver Cloud Platform', defaults: { name: 'Naver SMS' }, inputs: ["main" /* NodeConnectionType.Main */], outputs: ["main" /* NodeConnectionType.Main */], credentials: [{ name: 'naverSensApi', required: true }], properties: [ { displayName: 'Message Type', name: 'type', type: 'options', options: [ { name: 'SMS (90바이트, 한글45자)', value: 'SMS' }, { name: 'LMS (2000바이트, 한글1000자)', value: 'LMS' }, { name: 'MMS (첨부파일 가능)', value: 'MMS' }, ], default: 'SMS', }, { displayName: 'To (수신번호, 하이픈없이 숫자만)', name: 'to', type: 'string', required: true, default: '', }, { displayName: 'Content (메시지 내용)', name: 'content', type: 'string', typeOptions: { rows: 4 }, required: true, default: '', }, { displayName: 'Subject (LMS/MMS 제목, 선택사항)', name: 'subject', type: 'string', default: '', displayOptions: { show: { type: ['LMS', 'MMS'] }, }, }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; let creds; try { creds = await this.getCredentials('naverSensApi'); } catch (error) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), '네이버 SENS API 인증 정보를 가져올 수 없습니다.'); } // 필수 인증 정보 검증 if (!creds.accessKey || !creds.secretKey || !creds.serviceId || !creds.from) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), '네이버 SENS API 인증 정보가 완전하지 않습니다. Access Key, Secret Key, Service ID, From 번호를 모두 설정해주세요.'); } const baseUrl = 'https://sens.apigw.ntruss.com'; for (let i = 0; i < items.length; i++) { // 변수들을 try 블록 외부에서 선언 let type = ''; let to = ''; let content = ''; let subject = ''; let cleanTo = ''; let timestamp = ''; let uri = ''; let signature = ''; let body = {}; try { type = this.getNodeParameter('type', i); to = this.getNodeParameter('to', i); content = this.getNodeParameter('content', i); subject = this.getNodeParameter('subject', i, ''); // 입력 검증 if (!to || !content) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `아이템 ${i + 1}: 수신번호와 메시지 내용은 필수입니다.`); } // 전화번호 형식 검증 (간단한 검증) const phoneRegex = /^01[0-9]{8,9}$/; cleanTo = to.replace(/[^0-9]/g, ''); if (!phoneRegex.test(cleanTo)) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `아이템 ${i + 1}: 올바르지 않은 전화번호 형식입니다. (예: 01012345678)`); } // 메시지 길이 검증 (네이버 SMS는 EUC-KR 기준으로 바이트 계산) const getEuckrByteLength = (str) => { let byteLength = 0; for (let i = 0; i < str.length; i++) { const charCode = str.charCodeAt(i); if (charCode <= 0x7F) { // ASCII 문자 (1바이트) byteLength += 1; } else if (charCode <= 0x7FF) { // 한글 및 기타 2바이트 문자 byteLength += 2; } else { // 3바이트 이상 유니코드 문자도 EUC-KR에서는 2바이트로 처리 byteLength += 2; } } return byteLength; }; const contentBytes = getEuckrByteLength(content); if (type === 'SMS' && contentBytes > 90) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `아이템 ${i + 1}: SMS는 90바이트를 초과할 수 없습니다. 현재: ${contentBytes}바이트 (EUC-KR 기준)`); } else if ((type === 'LMS' || type === 'MMS') && contentBytes > 2000) { throw new n8n_workflow_1.NodeOperationError(this.getNode(), `아이템 ${i + 1}: LMS/MMS는 2000바이트를 초과할 수 없습니다. 현재: ${contentBytes}바이트 (EUC-KR 기준)`); } timestamp = Date.now().toString(); uri = `/sms/v2/services/${creds.serviceId}/messages`; const sigStr = `POST ${uri}\n${timestamp}\n${creds.accessKey}`; signature = crypto_1.default .createHmac('sha256', creds.secretKey) .update(sigStr) .digest('base64'); body = { type, contentType: 'COMM', countryCode: '82', from: creds.from, content, messages: [{ to: cleanTo, content }], }; // LMS/MMS인 경우 제목 추가 if (subject && (type === 'LMS' || type === 'MMS')) { body.subject = subject; body.messages[0].subject = subject; } const response = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}${uri}`, headers: { 'Content-Type': 'application/json; charset=utf-8', 'x-ncp-apigw-timestamp': timestamp, 'x-ncp-iam-access-key': creds.accessKey, 'x-ncp-apigw-signature-v2': signature, }, body, json: true, }); // 응답 데이터에 요청 정보 추가 const responseData = { ...response, requestInfo: { type, to: cleanTo, content, subject, timestamp: new Date(parseInt(timestamp)).toISOString(), }, }; returnData.push({ json: responseData }); } catch (error) { if (error instanceof n8n_workflow_1.NodeOperationError) { throw error; } // HTTP 에러의 경우 더 자세한 정보 출력 let errorMessage = error instanceof Error ? error.message : String(error); if (error && typeof error === 'object' && 'response' in error) { const httpError = error; const responseData = httpError.response?.data || httpError.response || {}; const statusCode = httpError.response?.status || 'unknown'; console.log('=== NAVER SENS API 에러 디버깅 정보 ==='); console.log('Status Code:', statusCode); console.log('Request URL:', `${baseUrl}${uri}`); console.log('Request Headers:', { 'Content-Type': 'application/json; charset=utf-8', 'x-ncp-apigw-timestamp': timestamp, 'x-ncp-iam-access-key': creds.accessKey, 'x-ncp-apigw-signature-v2': signature, }); console.log('Request Body:', JSON.stringify(body, null, 2)); console.log('Response Data:', JSON.stringify(responseData, null, 2)); console.log('====================================='); errorMessage = `HTTP ${statusCode}: ${JSON.stringify(responseData)}`; } throw new n8n_workflow_1.NodeOperationError(this.getNode(), `아이템 ${i + 1} 처리 중 오류 발생: ${errorMessage}`); } } return [returnData]; } } exports.NaverSms = NaverSms;