/* eslint-disable @typescript-eslint/no-explicit-any */ "use server" import { z } from 'zod'; // 환경 변수 타입 정의 const MESSENGER_BASE_URL = process.env.MESSENGER_BASE_URL || 'https://api.messenger.com'; const ACCESS_TOKEN = process.env.MESSENGER_ACCESS_TOKEN; const DEVICE_ID = process.env.MESSENGER_DEVICE_ID; // 공통 헤더 생성 함수 function createHeaders(additionalHeaders?: Record) { const headers: Record = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': `Bearer ${ACCESS_TOKEN}`, 'x-device-id': DEVICE_ID!, 'x-device-type': 'relation', ...additionalHeaders }; return headers; } // 응답 타입 정의 type ApiResponse = { success: boolean; data?: T; error?: string; statusCode?: number; }; // 에러 핸들링 함수 function handleApiError(error: any, endpoint: string): ApiResponse { console.error(`Knox Messenger API Error (${endpoint}):`, error); return { success: false, error: error.message || `Failed to call ${endpoint}`, statusCode: error.status || 500 }; } // ============================================================================= // 연락처 관리 API // ============================================================================= // 연락처 추가 스키마 const addContactSchema = z.object({ contactList: z.array(z.object({ userID: z.number() })) }); // 연락처 추가 export async function addContact(data: z.infer): Promise> { try { const validated = addContactSchema.parse(data); const response = await fetch(`${MESSENGER_BASE_URL}/messenger/contact/api/v1.0/contact/o1/user`, { method: 'POST', headers: createHeaders(), body: JSON.stringify(validated) }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to add contact', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'addContact'); } } // 연락처 삭제 스키마 const deleteContactSchema = z.object({ contactList: z.array(z.object({ userID: z.number() })) }); // 연락처 삭제 export async function deleteContact(data: z.infer): Promise> { try { const validated = deleteContactSchema.parse(data); const response = await fetch(`${MESSENGER_BASE_URL}/messenger/contact/api/v1.0/contact/o1/user/delete`, { method: 'POST', headers: createHeaders(), body: JSON.stringify(validated) }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to delete contact', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'deleteContact'); } } // 연락처 목록 조회 export async function getContactList(): Promise> { try { const response = await fetch(`${MESSENGER_BASE_URL}/messenger/contact/api/v1.0/contact/o1/list`, { method: 'GET', headers: createHeaders() }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to get contact list', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'getContactList'); } } // 사용자 검색 스키마 const searchUserSchema = z.object({ singleIdList: z.array(z.object({ singleId: z.string() })) }); // 사용자 검색 export async function searchUserByLoginId(data: z.infer): Promise> { try { const validated = searchUserSchema.parse(data); const response = await fetch(`${MESSENGER_BASE_URL}/messenger/contact/api/v1.0/profile/o1/search/loginid`, { method: 'POST', headers: createHeaders(), body: JSON.stringify(validated) }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to search user', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'searchUserByLoginId'); } } // ============================================================================= // 장치 관리 API // ============================================================================= // 장치 등록 export async function registerDevice(): Promise> { try { const response = await fetch(`${MESSENGER_BASE_URL}/messenger/contact/api/v1.0/device/o1/reg`, { method: 'GET', headers: createHeaders() }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to register device', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'registerDevice'); } } // 장치 해지 스키마 const deregisterDeviceSchema = z.object({ deviceServerID: z.number() }); // 장치 해지 export async function deregisterDevice(data: z.infer): Promise> { try { const validated = deregisterDeviceSchema.parse(data); const response = await fetch(`${MESSENGER_BASE_URL}/messenger/contact/api/v1.0/device/o1/delete`, { method: 'POST', headers: createHeaders(), body: JSON.stringify(validated) }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to deregister device', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'deregisterDevice'); } } // 장치 사용 정보 조회 스키마 const getDeviceUseInfoSchema = z.object({ singleIdList: z.array(z.object({ singleId: z.string() })) }); // 장치 사용 정보 조회 export async function getDeviceUseInfo(data: z.infer): Promise> { try { const validated = getDeviceUseInfoSchema.parse(data); const response = await fetch(`${MESSENGER_BASE_URL}/messenger/contact/api/v1.0/device/o1/use/info`, { method: 'POST', headers: createHeaders(), body: JSON.stringify(validated) }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to get device use info', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'getDeviceUseInfo'); } } // ============================================================================= // 파일 관리 API // ============================================================================= // 파일 서버 시간 및 키 조회 스키마 const getFileServerTimeSchema = z.object({ word: z.string() }); // 파일 서버 시간 및 키 조회 export async function getFileServerTime(data: z.infer): Promise> { try { const validated = getFileServerTimeSchema.parse(data); const response = await fetch(`${MESSENGER_BASE_URL}/messenger/file/api/v1.0/file/v1/getCurrentTime?word=${validated.word}`, { method: 'GET', headers: createHeaders({ 'x-access-token': ACCESS_TOKEN! }) }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to get file server time', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'getFileServerTime'); } } // 파일 업로드 스키마 const uploadFileSchema = z.object({ filename: z.string(), fileData: z.instanceof(FormData).or(z.any()) }); // 파일 업로드 export async function uploadFile(data: z.infer): Promise> { try { const validated = uploadFileSchema.parse(data); const response = await fetch(`${MESSENGER_BASE_URL}/messenger/file/api/v1.0/file/v1s/file/${validated.filename}`, { method: 'PUT', headers: createHeaders({ 'x-access-token': ACCESS_TOKEN!, 'Content-Type': 'multipart/form-data' }), body: validated.fileData }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to upload file', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'uploadFile'); } } // 파일 다운로드 스키마 const downloadFileSchema = z.object({ fileId: z.string() }); // 파일 다운로드 export async function downloadFile(data: z.infer): Promise> { try { const validated = downloadFileSchema.parse(data); const response = await fetch(`${MESSENGER_BASE_URL}/messenger/file/api/v1.0/file/v1s/file/${validated.fileId}`, { method: 'GET', headers: createHeaders({ 'x-access-token': ACCESS_TOKEN! }) }); if (!response.ok) { return { success: false, error: 'Failed to download file', statusCode: response.status }; } const blob = await response.blob(); return { success: true, data: blob }; } catch (error) { return handleApiError(error, 'downloadFile'); } } // ============================================================================= // 메시지 관리 API // ============================================================================= // 메시지 서버 키 조회 export async function getMessageServerKeys(): Promise> { try { const response = await fetch(`${MESSENGER_BASE_URL}/messenger/msgctx/api/v1.0/key/getkeys`, { method: 'GET', headers: createHeaders() }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to get message server keys', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'getMessageServerKeys'); } } // 대화방 참여자 정보 조회 스키마 const getChatMembersSchema = z.object({ chatroomId: z.number() }); // 대화방 참여자 정보 조회 export async function getChatMembers(data: z.infer): Promise> { try { const validated = getChatMembersSchema.parse(data); const response = await fetch(`${MESSENGER_BASE_URL}/messenger/msgctx/api/v1.0/chat/activemember?chatroomId=${validated.chatroomId}`, { method: 'GET', headers: createHeaders() }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to get chat members', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'getChatMembers'); } } // 대화방 생성 스키마 const createChatroomSchema = z.object({ requestId: z.number(), chatType: z.number(), // 0: 1:1, 1: GROUP, 2: BROADCAST GROUP, 5: BROADCAST SINGLE receivers: z.array(z.number()), chatroomTitle: z.string().optional() }); // 대화방 생성 export async function createChatroom(data: z.infer): Promise> { try { const validated = createChatroomSchema.parse(data); // TODO: 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/createChatroomRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to create chatroom', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'createChatroom'); } } // 메시지 발송 스키마 const sendMessageSchema = z.object({ requestId: z.number(), chatroomId: z.number(), chatMessageParams: z.array(z.object({ msgId: z.number(), msgType: z.number(), // 0: TEXT, 1: MEDIA, 7: RTF, 8: NCUSTOM chatMsg: z.string(), msgTtl: z.number().optional() })) }); // 메시지 발송 export async function sendMessage(data: z.infer): Promise> { try { const validated = sendMessageSchema.parse(data); // 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/chatRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to send message', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'sendMessage'); } } // 대화상대 초대 스키마 const inviteUserSchema = z.object({ requestId: z.number(), chatroomId: z.number(), invitingMembers: z.array(z.number()) }); // 대화상대 초대 export async function inviteUser(data: z.infer): Promise> { try { const validated = inviteUserSchema.parse(data); // 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/inviteRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to invite user', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'inviteUser'); } } // 대화상대 내보내기 스키마 const removeUserSchema = z.object({ requestId: z.number(), chatroomId: z.number(), removingMembers: z.array(z.number()) }); // 대화상대 내보내기 export async function removeUser(data: z.infer): Promise> { try { const validated = removeUserSchema.parse(data); // 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/removeMemberRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to remove user', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'removeUser'); } } // 대화방 삭제 스키마 const deleteChatroomSchema = z.object({ requestId: z.number(), chatroomId: z.number() }); // 대화방 삭제 export async function deleteChatroom(data: z.infer): Promise> { try { const validated = deleteChatroomSchema.parse(data); // 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/destroyChatroomRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to delete chatroom', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'deleteChatroom'); } } // 메시지 발신취소 스키마 const recallMessageSchema = z.object({ requestId: z.number(), chatroomId: z.number(), msgId: z.number() }); // 메시지 발신취소 export async function recallMessage(data: z.infer): Promise> { try { const validated = recallMessageSchema.parse(data); // 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/recallMessageRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to recall message', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'recallMessage'); } } // 대화방명 변경 스키마 const changeChatroomNameSchema = z.object({ requestId: z.number(), chatroomId: z.number(), chatroomTitle: z.string() }); // 대화방명 변경 export async function changeChatroomName(data: z.infer): Promise> { try { const validated = changeChatroomNameSchema.parse(data); // 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/changeChatroomMetaRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to change chatroom name', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'changeChatroomName'); } } // 방장 변경 스키마 const changeOwnerSchema = z.object({ requestId: z.number(), chatroomId: z.number(), newOwnerUserId: z.number() }); // 방장 변경 export async function changeOwner(data: z.infer): Promise> { try { const validated = changeOwnerSchema.parse(data); // 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/changeOwnerRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to change owner', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'changeOwner'); } } // 대화방 나가기 스키마 const leaveChatroomSchema = z.object({ requestId: z.number(), chatroomId: z.number() }); // 대화방 나가기 export async function leaveChatroom(data: z.infer): Promise> { try { const validated = leaveChatroomSchema.parse(data); // 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/endChatRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to leave chatroom', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'leaveChatroom'); } } // 메시지 읽음 카운트 조회 스키마 const getMessageReadCountSchema = z.object({ requestId: z.number(), chatroomId: z.number(), msgId: z.number() }); // 메시지 읽음 카운트 조회 export async function getMessageReadCount(data: z.infer): Promise> { try { const validated = getMessageReadCountSchema.parse(data); // 실제 구현에서는 메시지 서버 키를 사용하여 암호화해야 함 const encryptedBody = JSON.stringify(validated); // 임시로 평문 사용 const response = await fetch(`${MESSENGER_BASE_URL}/messenger/message/api/v1.0/message/messageReadCountRequest`, { method: 'POST', headers: createHeaders(), body: encryptedBody }); const result = await response.json(); if (!response.ok) { return { success: false, error: result.message || 'Failed to get message read count', statusCode: response.status }; } return { success: true, data: result }; } catch (error) { return handleApiError(error, 'getMessageReadCount'); } } // ============================================================================= // 유틸리티 함수 // ============================================================================= // 현재 시간을 밀리초로 반환 (requestId 생성용) export async function generateRequestId(): Promise { return Date.now(); } // 메시지 ID 생성 (각 메시지마다 고유한 ID 필요) export async function generateMessageId(): Promise { return Date.now() + Math.floor(Math.random() * 1000); } // 헬퍼 함수 - 간단한 텍스트 메시지 발송 export async function sendTextMessage(chatroomId: number, message: string): Promise> { const requestId = await generateRequestId(); const msgId = await generateMessageId(); return sendMessage({ requestId, chatroomId, chatMessageParams: [{ msgId, msgType: 0, // TEXT chatMsg: message, msgTtl: 7200 // 2시간 }] }); } // 헬퍼 함수 - 그룹 대화방 생성 export async function createGroupChatroom(userIds: number[], title?: string): Promise> { const requestId = await generateRequestId(); return createChatroom({ requestId, chatType: 2, // BROADCAST GROUP receivers: userIds, chatroomTitle: title }); } // 헬퍼 함수 - 1:1 대화방 생성 export async function createPrivateChatroom(userId: number): Promise> { const requestId = await generateRequestId(); return createChatroom({ requestId, chatType: 5, // BROADCAST SINGLE receivers: [userId] }); }