131 lines
No EOL
6.1 KiB
TypeScript
131 lines
No EOL
6.1 KiB
TypeScript
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<typeof VideoInfoSchema>;
|
||
|
||
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;
|
||
}
|
||
}; |