import z from "zod"; import { aiController } from "./ai"; import { dateController } from "./date"; import { googleDriveController, LogRowData, LogRowDataSchema } from "./googleDrive"; import { fileController } from "./file"; import path, { join } from "path"; import fs from "fs"; import { createCustomError } from "./error"; import { storageController } from "./storage"; import { CLOUD_STORAGE_MASTER_FOLDER_NAME, DATE_FORMAT, DATETIME_FORMAT, DOCUMENT_MIMETYPE, OWNERS_FILE_NAME, YM_FORMAT } from "../../serverConfig"; import { hubspotController, OwnerSchema } from "./hubspot"; import { fuzzyMatchController } from "./fuzzyMatch"; const VideoInfoSchema = z.looseObject({ id: z.string(), title: z.string(), starts_at: z.string(), ends_at: z.string(), access_permission: z.string(), host: z.object({ login_id: z.string(), user_name: z.string(), }), speech_recognition: z.object({ raw: z.string(), }) }); type VideoInfo = z.infer; export const MiiTelWebhookSchema = z.object({ video: VideoInfoSchema, }); const GOOGLE_DRIVE_FOLDER_ID = process.env.GOOGLE_DRIVE_FOLDER_ID || ''; const MIITEL_REQUEST_LOG_FOLDER_ID = process.env.MIITEL_REQUEST_LOG_FOLDER_ID || ''; const MINUTES_CREATION_HISTORY_FOLDER_ID = process.env.MINUTES_CREATION_HISTORY_FOLDER_ID || ''; const MIITEL_URL = process.env.MIITEL_URL || ''; const HUBSPOT_COMPANY_URL = process.env.HUBSPOT_COMPANY_URL || ''; const FILE_PATH = join(__dirname, "../files/"); let outputPath = ''; export const processRequest = async (videoInfo: VideoInfo) => { try { const videoId = videoInfo.id; const title = videoInfo.title; const startsAt = videoInfo.starts_at; const endsAt = videoInfo.ends_at; const accessPermission = videoInfo.access_permission; const hostId = videoInfo.host.login_id; const hostName = videoInfo.host.user_name; const speechRecognition = videoInfo.speech_recognition.raw; if (accessPermission !== "EVERYONE" || !title.includes("様") || title.includes("社内")) return; // ===== Init ===== const googleAuth = await googleDriveController.getAuth(); const driveClient = googleDriveController.getDriveClient(googleAuth); const docsClient = googleDriveController.getDocsClient(googleAuth); const sheetsClient = googleDriveController.getSheetsClient(googleAuth); const jstStartsAt = dateController.convertToJst(startsAt); const jstEndsAt = dateController.convertToJst(endsAt); const fileName = fileController.createMinutesFileName(title, hostName, jstStartsAt); const videoUrl = `${MIITEL_URL}app/video/${videoId}`; // ===== Save Request Log to Google Drive ===== if (!fs.existsSync(FILE_PATH)) fs.mkdirSync(FILE_PATH, { recursive: true }); outputPath = path.join(FILE_PATH, fileName + '.zip'); const createZip = await fileController.createZip(videoInfo, outputPath, fileName); if(!createZip) throw createCustomError("CREATE_ZIP_FILE_FAILED"); const logFileId = await googleDriveController.uploadFile(driveClient, outputPath, MIITEL_REQUEST_LOG_FOLDER_ID, `${fileName}.zip`, "application/zip"); if(!logFileId) throw createCustomError("UPLOAD_LOG_FAILED"); // ===== Generate Minutes ===== const minutes = await aiController.generateMinutes(speechRecognition); if (!minutes) throw createCustomError("AI_GENERATION_FAILED"); let content = `会議履歴URL:${videoUrl}\n`; content += `担当者:${hostName}\n\n`; content += minutes; // ===== Upload To Google Drive ===== const documentId = await googleDriveController.createNewFile(driveClient, GOOGLE_DRIVE_FOLDER_ID, fileName, DOCUMENT_MIMETYPE); if (!documentId) throw createCustomError("CREATE_NEW_DOCUMENT_FAILED"); const result = await googleDriveController.addContentToDocs(docsClient, documentId, content); if(!result) throw createCustomError("UPLOAD_MINUTES_FAILED"); // ===== Create Meeting Log at Hubspot ===== const ownersJson = await storageController.loadJsonFromGCS(CLOUD_STORAGE_MASTER_FOLDER_NAME, OWNERS_FILE_NAME); if(!ownersJson) throw createCustomError("GET_OWNERS_FAILED"); const parsedOwners = z.array(OwnerSchema).safeParse(JSON.parse(ownersJson)); if(!parsedOwners.success) throw createCustomError("ZOD_FAILED"); const ownerId = hubspotController.searchOwnerIdByEmail(hostId, parsedOwners.data); const extractedCompanyName = fileController.extractCompanyNameFromTitle(title); const matchedCompany = await fuzzyMatchController.searchMatchedCompany(extractedCompanyName); if(matchedCompany) await hubspotController.createMeetingLog(matchedCompany.id, title, ownerId, minutes, startsAt, endsAt); // ===== Apeend Log To SpreadSheet ===== const currentYearMonth = dateController.getCurrentJstTime(YM_FORMAT); const sheetId = await googleDriveController.getLogSheetId(driveClient, sheetsClient, MINUTES_CREATION_HISTORY_FOLDER_ID, currentYearMonth); if(!sheetId) throw createCustomError("GET_SHEET_ID_FAILED"); const currentJstDateTimeStr = dateController.getCurrentJstTime(DATETIME_FORMAT); const currentJstDateStr = dateController.getCurrentJstTime(DATE_FORMAT); const rowData: LogRowData = LogRowDataSchema.parse({ timestamp: currentJstDateTimeStr, meetingDate: currentJstDateStr, title: title, matchedCompanyName: matchedCompany?.name ?? '', ownerName: hostName, meetingUrl: videoUrl, documentUrl: `https://docs.google.com/document/d/${documentId}/edit`, hubspotUrl: matchedCompany ? `${HUBSPOT_COMPANY_URL}/${matchedCompany.id}` : '', }); const insertResult = await googleDriveController.insertRowToSheet(sheetsClient, sheetId, Object.values(rowData)); if(!insertResult) throw createCustomError("INSERT_ROW_FAILED"); fs.unlinkSync(outputPath); } catch (error) { fs.unlinkSync(outputPath); throw error; } };