From 7e9b8cedf16da42d7a338f5b3ac24c33564c85c1 Mon Sep 17 00:00:00 2001 From: leejineun Date: Fri, 23 Jul 2021 11:31:21 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=EC=BF=BC=EB=A6=AC=EB=B6=84=EB=A6=AC=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- socket/@types/bannerRequest.ts | 36 ++ socket/@types/db.ts | 1 + socket/app.ts | 228 +++++++------ socket/{public => lib}/callImg.ts | 328 ++++++++----------- socket/{public => lib}/client.ts | 0 socket/{public => lib}/hiddenEventHandler.ts | 0 socket/{public => lib}/imageClicker.ts | 0 socket/{public => lib}/programIdentifier.ts | 0 socket/models/query.ts | 68 ++++ 9 files changed, 369 insertions(+), 292 deletions(-) create mode 100644 socket/@types/bannerRequest.ts rename socket/{public => lib}/callImg.ts (57%) rename socket/{public => lib}/client.ts (100%) rename socket/{public => lib}/hiddenEventHandler.ts (100%) rename socket/{public => lib}/imageClicker.ts (100%) rename socket/{public => lib}/programIdentifier.ts (100%) create mode 100644 socket/models/query.ts diff --git a/socket/@types/bannerRequest.ts b/socket/@types/bannerRequest.ts new file mode 100644 index 000000000..097fa3095 --- /dev/null +++ b/socket/@types/bannerRequest.ts @@ -0,0 +1,36 @@ +export interface CampaignIdOptionType { + [_: string]: [number, string, string]; +} + +export interface TimeData { + startDate: Date; + finDate: Date; + campaignId: string; + campaignName: string; + selectedTime: Date; + optionType: number; + campaignDescription: string; +} + +export interface ReturnDate { + [key: string]: Date; +} + +export interface LinkJson { + linkName: string; + linkTo: string; + primary: boolean; +} + +export interface CreatorIds { + creatorId: string; + creatorTwitchId?: string; + afreecaId?: string; + adChatAgreement: number; +} + +export interface BannerRequest { + url: string; + previousBannerName: string; + programType: string; +} diff --git a/socket/@types/db.ts b/socket/@types/db.ts index 09534cc99..6092454b1 100644 --- a/socket/@types/db.ts +++ b/socket/@types/db.ts @@ -5,3 +5,4 @@ export interface QueryResult { error?: MysqlError | null; result: any; } + diff --git a/socket/app.ts b/socket/app.ts index 6d3e578be..b09f9b99d 100644 --- a/socket/app.ts +++ b/socket/app.ts @@ -9,19 +9,24 @@ import http from 'http'; import socketio, { Socket } from 'socket.io'; import nodeSchedule from 'node-schedule'; import doQuery from './models/doQuery'; -import callImg from './public/callImg'; +import callImg from './lib/callImg'; +import { BannerRequest } from './@types/bannerRequest'; +import query from './models/query'; const app = express(); const httpServer = http.createServer(app); const io = socketio(httpServer); -process.env.NODE_ENV = (process.env.NODE_ENV && (process.env.NODE_ENV).trim().toLowerCase() === 'production') ? 'production' : 'development'; +process.env.NODE_ENV = + process.env.NODE_ENV && process.env.NODE_ENV.trim().toLowerCase() === 'production' + ? 'production' + : 'development'; + +app.use('/lib', express.static(`${__dirname}/lib`)); +app.use('/public', express.static(`${__dirname}/public`)); -app.use('/public', express.static(`${__dirname}/public`)); // 디렉토리 정적으로 고정하는 부분 -// view engine app.set('views', `${__dirname}/views`); app.set('view engine', 'ejs'); -app.engine('html', require('ejs').renderFile); app.get('/', (req: express.Request, res: express.Response) => { res.sendStatus(200); @@ -39,111 +44,134 @@ app.get('/duplicate', (req, res) => { res.render('duplicate.ejs'); }); -app.get('/banner/:id', (req, res, next) => { // /banner/:id로 라우팅 +app.get('/banner/:id', (req, res, next) => { + // /banner/:id로 라우팅 res.render('client.ejs'); }); // nodemon --expose-gc file_dir // 20초에 한 번마다 garbage collector -setInterval(function(){ - global.gc(); -}, 20000); +// setInterval(function(){ +// global.gc(); +// }, 20000); interface SocketInfo { [key: string]: string; } -( - function () { - const SOCKET_HOST = process.env.SOCKET_HOSTNAME; - const socketInfo: SocketInfo = {}; - io.on('connection', (socket: Socket) => { - let SOCKET_ID: string = socket.id; - const urlArray: Array = Object.values(socketInfo); - - // *********************************************************** - // 스케쥴링 - const rule = new nodeSchedule.RecurrenceRule(); // 스케쥴러 객체 생성 - rule.hour = new nodeSchedule.Range(0, 23); // cronTask 시간지정 - rule.minute = [0, 10, 20, 30, 40, 50]; // cronTask 실행되는 분(minute) - // cronTask - nodeSchedule.scheduleJob(rule, () => { // 스케쥴러를 통해 10분마다 db에 배너정보 전송 - socket.emit('re-render at client', {}); - }); - - // *********************************************************** - // 첫 입장시 발생되는 이벤트인 "new client" 이벤트 핸들러 - socket.on('new client', (msg: [string, number, string]) => { - const CLIENT_URL = msg[0]; - const HISTORY = msg[1]; - const programType = msg[2]; - if (process.env.NODE_ENV === 'development') { - console.log('SOCKET ON'); - socket.emit('host pass', SOCKET_HOST); - callImg(socket, [CLIENT_URL, '', programType]); - } else if ( - // 트위치 스튜디오 또는 아프리카 프릭샷으로 접속하지 않았으면서 - !['afreeca-freecshot', 'twitch-studio'].includes(programType) - // history.length가 1이 아닌 경우에 잘못된 접속 처리 - && HISTORY !== 1) { - const DESTINATION_URL = `${SOCKET_HOST}/browserWarn`; - socket.emit('browser warning', DESTINATION_URL); - } - - if (urlArray.includes(CLIENT_URL)) { - const DESTINATION_URL = `${SOCKET_HOST}/duplicate`; - socket.emit('duplicate', DESTINATION_URL); - } else { - socketInfo[SOCKET_ID] = CLIENT_URL; - socket.emit('host pass', SOCKET_HOST); - callImg(socket, [CLIENT_URL, '', programType]); - } - }); - - // *********************************************************** - // 접속 종료시 발생되는 이벤트인 "disconnect" 이벤트 핸들러 - socket.on('disconnect', () => { // 접속종료시 - delete socketInfo[SOCKET_ID]; // socketsInfo에서 접속종료한 clientID 삭제 - SOCKET_ID = ''; - }); - - // *********************************************************** - // 송출될 광고 재요청 이벤트 : "re-render" 이벤트 핸들러 - socket.on('re-render', (msg: [string, string, string]) => { - callImg(socket, msg); - }); - - // *********************************************************** - // - socket.on('pageOn', (msg: [string, string]) => { - const CLIENT_URL = msg[0]; - const programType = msg[1]; - callImg(socket, [CLIENT_URL, '', programType]); - }); - - // 배너 더블 클릭을 통해 ON/OFF 조절 - // bannerVisible 에 상태 삽입 - socket.on('pageActive handler', (msg: [string, boolean, string]) => { - // 배너창을 띄웠을 때는 state = 1 - // 배너창 숨겼을 때는 state = 0 - const clientUrl = msg[0]; - const state = msg[1] === true ? 1 : 0; - const program = msg[2]; - const activeQuery = 'INSERT INTO bannerVisible (advertiseUrl, visibleState, program, type) VALUES (?, ?, ?, 0);'; - doQuery(activeQuery, [clientUrl, state, program]); - }); - - // 배너 더블 클릭을 통해 ON/OFF 조절 - // bannerVisible 에 상태 삽입 - socket.on('banner click', (msg: [string, boolean, string]) => { - const clientUrl = msg[0]; - const state = msg[1] === true ? 1 : 0; - const program = msg[2]; - const hiddenQuery = 'INSERT INTO bannerVisible (advertiseUrl, visibleState, program, type) VALUES (?, ?, ?, 1);'; - doQuery(hiddenQuery, [clientUrl, state, program]); - }); - // *********************************************************** +(function () { + const SOCKET_HOST = process.env.SOCKET_HOSTNAME; + const socketInfo: SocketInfo = {}; + io.on('connection', (socket: Socket) => { + let SOCKET_ID: string = socket.id; + const urlArray: Array = Object.values(socketInfo); + const requestMessage: BannerRequest = { + url: '', + previousBannerName: '', + programType: '', + }; + // *********************************************************** + // 스케쥴링 + const rule = new nodeSchedule.RecurrenceRule(); // 스케쥴러 객체 생성 + rule.hour = new nodeSchedule.Range(0, 23); // cronTask 시간지정 + rule.minute = [0, 10, 20, 30, 40, 50]; // cronTask 실행되는 분(minute) + // cronTask + nodeSchedule.scheduleJob(rule, () => { + // 스케쥴러를 통해 10분마다 db에 배너정보 전송 + socket.emit('re-render at client', {}); + }); + + // *********************************************************** + // 첫 입장시 발생되는 이벤트인 "new client" 이벤트 핸들러 + socket.on('new client', (msg: [string, number, string]) => { + const clientUrl = msg[0]; + const HISTORY = msg[1]; + const programType = msg[2]; + + requestMessage.url = clientUrl; + requestMessage.previousBannerName = ''; + requestMessage.programType = programType; + + if (process.env.NODE_ENV === 'development') { + console.log('SOCKET ON'); + socket.emit('host pass', SOCKET_HOST); + + callImg(socket, requestMessage); + } else if ( + // 트위치 스튜디오 또는 아프리카 프릭샷으로 접속하지 않았으면서 + !['afreeca-freecshot', 'twitch-studio'].includes(programType) && + // history.length가 1이 아닌 경우에 잘못된 접속 처리 + HISTORY !== 1 + ) { + const DESTINATION_URL = `${SOCKET_HOST}/browserWarn`; + socket.emit('browser warning', DESTINATION_URL); + } + + if (urlArray.includes(clientUrl)) { + const DESTINATION_URL = `${SOCKET_HOST}/duplicate`; + socket.emit('duplicate', DESTINATION_URL); + } else { + socketInfo[SOCKET_ID] = clientUrl; + socket.emit('host pass', SOCKET_HOST); + callImg(socket, requestMessage); + } + }); + + // *********************************************************** + // 접속 종료시 발생되는 이벤트인 "disconnect" 이벤트 핸들러 + socket.on('disconnect', () => { + // 접속종료시 + delete socketInfo[SOCKET_ID]; // socketsInfo에서 접속종료한 clientID 삭제 + SOCKET_ID = ''; + }); + + // *********************************************************** + // 송출될 광고 재요청 이벤트 : "re-render" 이벤트 핸들러 + socket.on('re-render', (msg: [string, string, string]) => { + const clientUrl = msg[0]; + const previousBannerName = msg[1]; + const programType = msg[2]; + + requestMessage.url = clientUrl; + requestMessage.previousBannerName = previousBannerName; + requestMessage.programType = programType; + callImg(socket, requestMessage); + }); + + // *********************************************************** + // + socket.on('pageOn', (msg: [string, string]) => { + const clientUrl = msg[0]; + const programType = msg[1]; + requestMessage.url = clientUrl; + requestMessage.previousBannerName = ''; + requestMessage.programType = programType; + callImg(socket, requestMessage); + }); + + // 배너 더블 클릭을 통해 ON/OFF 조절 + // bannerVisible 에 상태 삽입 + socket.on('pageActive handler', (msg: [string, boolean, string]) => { + // 배너창을 띄웠을 때는 state = 1 + // 배너창 숨겼을 때는 state = 0 + const clientUrl = msg[0]; + const state = msg[1] === true ? 1 : 0; + const program = msg[2]; + const { insertBannerVisibleBannerClick } = query; + doQuery(insertBannerVisibleBannerClick, [clientUrl, state, program]); + }); + + // 배너 더블 클릭을 통해 ON/OFF 조절 + // bannerVisible 에 상태 삽입 + socket.on('banner click', (msg: [string, boolean, string]) => { + const clientUrl = msg[0]; + const state = msg[1] === true ? 1 : 0; + const program = msg[2]; + const { insertBannerVisiblePageApi } = query; + doQuery(insertBannerVisiblePageApi, [clientUrl, state, program]); }); - }()); + // *********************************************************** + }); +})(); httpServer.listen(3002, () => { console.log(`node_websocket server on ${process.env.NODE_ENV} mode`); diff --git a/socket/public/callImg.ts b/socket/lib/callImg.ts similarity index 57% rename from socket/public/callImg.ts rename to socket/lib/callImg.ts index 6945870b3..0d42cd20a 100644 --- a/socket/public/callImg.ts +++ b/socket/lib/callImg.ts @@ -1,15 +1,27 @@ import { Socket } from 'socket.io'; import doQuery from '../models/doQuery'; +import { + CampaignIdOptionType, + TimeData, + ReturnDate, + LinkJson, + CreatorIds, +} from '../@types/bannerRequest'; +import query from '../models/query'; -function callImg(socket: Socket, msg: string[]): void { - const fullUrl: string = msg[0]; +interface Request { + url: string; + previousBannerName: string; + programType: string; +} + +function callImg(socket: Socket, request: Request): void { + const fullUrl: string = request.url; const cutUrl = `/${fullUrl.split('/')[4]}`; - const prevBannerName: string = msg[1]; - const programType: string = msg[2]; + const { previousBannerName } = request; + const { programType } = request; const getTime: string = new Date().toLocaleString(); - interface CampaignIdOptionType { - [_: string]: [number, string, string]; - } + const campaignObject: CampaignIdOptionType = {}; let myCampaignId: string; @@ -18,23 +30,20 @@ function callImg(socket: Socket, msg: string[]): void { const getCreatorCampaignList = (creatorId: string): Promise => { console.log(`${creatorId} / 특정 방송인 송출 광고 조회 / ${getTime}`); - const campaignListQuery = ` - SELECT campaignList - FROM creatorCampaign - WHERE creatorId = ? - `; + const { creatorCampaign } = query; return new Promise((resolve, reject) => { - doQuery(campaignListQuery, [creatorId]) - .then((row) => { + doQuery(creatorCampaign, [creatorId]) + .then(row => { if (row.result[0].length !== 0) { const jsonData = JSON.parse(row.result[0].campaignList); resolve(jsonData.campaignList); } }) - .catch((errorData) => { + .catch(errorData => { errorData.point = 'getCreatorCampaignList()'; - errorData.description = 'creatorCampaign table에서 creator에게 계약된 campaignList 가져오는 과정.'; + errorData.description = + 'creatorCampaign table에서 creator에게 계약된 campaignList 가져오는 과정.'; reject(errorData); }); }); @@ -44,41 +53,28 @@ function callImg(socket: Socket, msg: string[]): void { const getOnCampaignList = (): Promise => { // 광고주 상태가 켜져있고, 캠페인이 켜져있으며 일일예산을 소진하지 않은 LIVE생방송배너광고(CPM+CPC) 이거나, // 광고주 상태가 켜져있고, 캠페인이 켜져있는 판매형광고(CPS)만 가져온다. - const campaignListQuery = ` - SELECT campaignId, campaignName, optionType, startDate, finDate, selectedTime, campaignDescription, cashAmount - FROM campaign - LEFT JOIN marketerInfo ON campaign.marketerId = marketerInfo.marketerId - LEFT JOIN marketerDebit ON campaign.marketerId = marketerDebit.marketerId - WHERE - (marketerInfo.marketerContraction = 1 AND campaign.onOff = 1 AND campaign.optionType = 1 AND campaign.limitState = 0) - OR (marketerInfo.marketerContraction = 1 AND campaign.onOff = 1 AND campaign.optionType = 3) - `; - interface TimeData { - startDate: Date; - finDate: Date; - campaignId: string; - campaignName: string; - selectedTime: Date; - optionType: number; - campaignDescription: string; - } - interface ReturnDate { - [key: string]: Date; - } + const { activeCampaign } = query; + return new Promise((resolve, reject) => { - doQuery(campaignListQuery) - .then((row) => { + doQuery(activeCampaign) + .then(row => { const filteredDate: ReturnDate = {}; const campaignIdList: string[] = []; const nowDate = new Date(); - row.result.map( - (data: TimeData) => { - if (data.startDate && data.startDate < nowDate && (data.finDate > nowDate || !data.finDate)) { - filteredDate[data.campaignId] = data.selectedTime; - campaignObject[data.campaignId] = [data.optionType, data.campaignName, data.campaignDescription]; - } + row.result.map((data: TimeData) => { + if ( + data.startDate && + data.startDate < nowDate && + (data.finDate > nowDate || !data.finDate) + ) { + filteredDate[data.campaignId] = data.selectedTime; + campaignObject[data.campaignId] = [ + data.optionType, + data.campaignName, + data.campaignDescription, + ]; } - ); + }); Object.values(filteredDate).map((value: Date, index: number) => { const jsonData = JSON.parse(value.toLocaleString()); if (jsonData.time.includes(nowDate.getHours())) { @@ -87,7 +83,7 @@ function callImg(socket: Socket, msg: string[]): void { }); resolve(campaignIdList); }) - .catch((errorData) => { + .catch(errorData => { errorData.point = 'getOnCampaignList()'; errorData.description = 'campaign table에서 현재 ON 되어있는 campaignList 가져오는 과정.'; reject(errorData); @@ -96,17 +92,17 @@ function callImg(socket: Socket, msg: string[]): void { }; // 하나의 categoryId 에 해당하는 캠페인 리스트를 반환하는 Promise const getCategoryCampaignList = async (categoryId: string): Promise => { - const campaignListQuery = 'SELECT campaignList FROM categoryCampaign WHERE categoryId = ?'; + const { categoryCampaign } = query; return new Promise((resolve, reject) => { - doQuery(campaignListQuery, [categoryId]) - .then((row) => { + doQuery(categoryCampaign, [categoryId]) + .then(row => { if (row.result.length > 0) { const jsonData = JSON.parse(row.result[0].campaignList); resolve(jsonData.campaignList); } resolve([]); }) - .catch((errorData) => { + .catch(errorData => { errorData.point = 'getCategoryCampaignList()'; errorData.description = '하나의 categoryId를 통하여 계약된 campaignList 가져오는 과정.'; reject(errorData); @@ -116,32 +112,12 @@ function callImg(socket: Socket, msg: string[]): void { const getGameId = async (creatorId: string): Promise => { console.log(`${creatorId} / get gameid / ${getTime}`); - const getGameIdQuery = ` - SELECT gameId FROM twitchStreamDetail AS tsd - WHERE streamId = ( - SELECT streamId FROM twitchStream - JOIN creatorInfo ON creatorTwitchOriginalId = streamerId - WHERE creatorId = ? - ORDER BY startedAt DESC LIMIT 1 - ) AND time > DATE_SUB(NOW(), INTERVAL 10 MINUTE) - ORDER BY tsd.time DESC LIMIT 1;`; - - const getAfreecaGameIdQuery = ` - SELECT broadCategory AS gameId FROM AfreecaBroadDetail AS ABD - WHERE broadId = ( - SELECT broadId FROM AfreecaBroad JOIN creatorInfo ON afreecaId = userId - WHERE creatorId = ? ORDER BY broadStartedAt DESC LIMIT 1 - ) AND createdAt > DATE_SUB(NOW(), INTERVAL 10 MINUTE) - ORDER BY createdAt DESC LIMIT 1 - `; - + const { twitchGameIdForCreator } = query; + const { afreecaGameIdForCreator } = query; try { - const [ - { result: twitchGameIdResult }, - { result: afreecaGameIdResult } - ] = await Promise.all([ - doQuery(getGameIdQuery, [creatorId]), - doQuery(getAfreecaGameIdQuery, [creatorId]) + const [{ result: twitchGameIdResult }, { result: afreecaGameIdResult }] = await Promise.all([ + doQuery(twitchGameIdForCreator, [creatorId]), + doQuery(afreecaGameIdForCreator, [creatorId]), ]); // 트위치 방송 중인 경우, 트위치 현재 진행 중 카테고리 @@ -154,6 +130,7 @@ function callImg(socket: Socket, msg: string[]): void { if (!(twitchGameIdResult.length > 0) && afreecaGameIdResult.length > 0) { myGameId = afreecaGameIdResult[0].gameId; } + return myGameId; } catch (errorData) { errorData.point = 'getGameId()'; @@ -168,21 +145,6 @@ function callImg(socket: Socket, msg: string[]): void { * @param gameId gameId * @param creatorId creatorId */ - const insertTwitchGameUnchecked = (gameId: string, creatorId: string) => { - const insertTwitchGameUncheckedQuery = 'INSERT IGNORE INTO twitchGame_unchecked(gameId, creatorId) values(?,?)'; - return new Promise((resolve, reject) => { - doQuery(insertTwitchGameUncheckedQuery, [gameId, creatorId]) - .then((row) => { - console.log(`새로운 game : ${gameId} 추가 / cretorId ${creatorId} / at : ${getTime}`); - resolve(row.result); - }) - .catch((errorData) => { - errorData.point = 'insertTwitchGameUnchecked()'; - errorData.description = 'twitchGameUnchecked 새게임 insert 과정'; - reject(errorData); - }); - }); - }; // "하나의 gameId" + "카테고리무관" 에 해당하는 모든 캠페인 리스트를 반환하는 Promise const getGameCampaignList = async (gameId: string, isDefault = false): Promise => { @@ -195,14 +157,11 @@ function callImg(socket: Socket, msg: string[]): void { returnList = await getCategoryCampaignList(gameId); return returnList; } - await Promise.all([ - getCategoryCampaignList(gameId), - getCategoryCampaignList(CATEGORY_WHATEVER) - ]) + await Promise.all([getCategoryCampaignList(gameId), getCategoryCampaignList(CATEGORY_WHATEVER)]) .then(([campaignList1, campaignList2]) => { returnList = campaignList1.concat(campaignList2); }) - .catch((errorData) => { + .catch(errorData => { errorData.point = 'getGameCampaignList()'; errorData.description = 'categoryCampaign에서 각각의 categoryId에 따른 캠페인 가져오기'; }); @@ -211,20 +170,16 @@ function callImg(socket: Socket, msg: string[]): void { }; const getBanList = (creatorId: string): Promise => { - const selectQuery = ` - SELECT banList, pausedList - FROM creatorCampaign - WHERE creatorId = ? - `; + const { banAndPausedCampaign } = query; return new Promise((resolve, reject) => { - doQuery(selectQuery, [creatorId]) - .then((row) => { + doQuery(banAndPausedCampaign, [creatorId]) + .then(row => { const banList = JSON.parse(row.result[0].banList).campaignList; const pausedList = JSON.parse(row.result[0].pausedList).campaignList; resolve({ banList, pausedList }); }) - .catch((errorData) => { + .catch(errorData => { errorData.point = 'getBanList()'; errorData.description = '해당 creator의 banList를 가져오는 과정'; reject(errorData); @@ -233,20 +188,14 @@ function callImg(socket: Socket, msg: string[]): void { }; const getBannerSrc = (campaignId: string): Promise => { - const selectQuery = ` - SELECT br.bannerSrcUrl AS bannerSrc - FROM campaign - JOIN bannerRegistered as br - ON br.bannerId = campaign.bannerId - WHERE campaign.campaignId = ? - `; + const { bannerSrc } = query; return new Promise((resolve, reject) => { - doQuery(selectQuery, [campaignId]) - .then((row) => { + doQuery(bannerSrc, [campaignId]) + .then(row => { console.log(row.result[0].bannerSrc); resolve(row.result[0].bannerSrc); }) - .catch((errorData) => { + .catch(errorData => { errorData.point = 'getBannerSrc()'; errorData.description = '하나의 campaignId를 통해 bannerSrc를 가져오는 과정'; reject(errorData); @@ -254,21 +203,11 @@ function callImg(socket: Socket, msg: string[]): void { }); }; - interface LinkJson { - linkName: string; - linkTo: string; - primary: boolean; - } - const getLinkName = (campaignId: string): Promise => { - const selectQuery = ` - SELECT links - FROM linkRegistered - WHERE linkId = (SELECT connectedLinkId FROM campaign WHERE campaignId = ?) - `; + const { linkUrl } = query; return new Promise((resolve, reject) => { - doQuery(selectQuery, [campaignId]) - .then((row) => { + doQuery(linkUrl, [campaignId]) + .then(row => { let linkName = ''; const links = JSON.parse(row.result[0].links); links.links.map((data: LinkJson) => { @@ -278,7 +217,7 @@ function callImg(socket: Socket, msg: string[]): void { }); resolve(linkName); }) - .catch((errorData) => { + .catch(errorData => { errorData.point = 'getLinkName()'; errorData.description = '하나의 campaignId를 통해 linkName 가져오는 과정'; reject(errorData); @@ -292,11 +231,8 @@ function callImg(socket: Socket, msg: string[]): void { * @returns 상품 사이트 URL 문자열 */ const getMerchandiseSiteUrl = async (campaignId: string): Promise => { - const query = `SELECT itemSiteUrl - FROM merchandiseMallItems - JOIN campaign on campaign.merchandiseId = merchandiseMallItems.merchandiseId - WHERE campaignId = ?`; - const { result } = await doQuery(query, [campaignId]); + const { onadMallItemUrl } = query; + const { result } = await doQuery(onadMallItemUrl, [campaignId]); if (!(result.length > 0)) return ''; return result[0].itemSiteUrl; }; @@ -306,34 +242,33 @@ function callImg(socket: Socket, msg: string[]): void { return Math.floor(Math.random() * (max - 0)) + 0; // 최댓값은 제외, 최솟값은 포함 }; - async function getBanner( - [creatorId, gameId]: string[] - ): Promise<[string | boolean, string | boolean, string | boolean]> { + async function getBanner([creatorId, gameId]: string[]): Promise< + [string | boolean, string | boolean, string | boolean] + > { console.log(`-----------------------Id : ${creatorId} / ${getTime}---------------------------`); let linkToChatBot; - const [creatorCampaignList, onCampaignList, checkList] = await Promise.all( - [ - getCreatorCampaignList(creatorId), - getOnCampaignList(), - getBanList(creatorId) - ] - ); + const [creatorCampaignList, onCampaignList, checkList] = await Promise.all([ + getCreatorCampaignList(creatorId), + getOnCampaignList(), + getBanList(creatorId), + ]); // ********************************************************* // 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인 -> ON 상태 필터링 - const onCreatorcampaignList = creatorCampaignList.filter( - (campaignId: any) => onCampaignList.includes(campaignId) + const onCreatorcampaignList = creatorCampaignList.filter((campaignId: any) => + onCampaignList.includes(campaignId), ); // 현재 ON상태인 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인 목록이 있는 경우 if (onCreatorcampaignList.length !== 0) { - const extractPausedCampaignList = onCreatorcampaignList - .filter((campaignId: any) => !checkList.pausedList.includes(campaignId)); // 일시정지 배너 거르기. - const extractBanCampaignList = extractPausedCampaignList - .filter((campaignId: any) => !checkList.banList.includes(campaignId)); // 마지막에 banList를 통해 거르기. + const extractPausedCampaignList = onCreatorcampaignList.filter( + (campaignId: any) => !checkList.pausedList.includes(campaignId), + ); // 일시정지 배너 거르기. + const extractBanCampaignList = extractPausedCampaignList.filter( + (campaignId: any) => !checkList.banList.includes(campaignId), + ); // 마지막에 banList를 통해 거르기. if (extractBanCampaignList) { - const returnCampaignId = extractBanCampaignList[ - getRandomInt(extractBanCampaignList.length) - ]; + const returnCampaignId = + extractBanCampaignList[getRandomInt(extractBanCampaignList.length)]; myCampaignId = returnCampaignId; console.log('방송인 에게만 송출될 캠페인 : ', myCampaignId); } @@ -341,13 +276,18 @@ function callImg(socket: Socket, msg: string[]): void { // ********************************************************* // 현재 ON상태인 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인이 없는 경우 const categoryCampaignList = await getGameCampaignList(gameId); - console.log(`${creatorId} 방송인에게만 송출될 광고 없음. 카테고리 선택형 및 노출우선형 광고 검색 / ${getTime}`); - const onCategorycampaignList = categoryCampaignList - .filter((campaignId) => onCampaignList.includes(campaignId)); - const extractPausedCampaignList = onCategorycampaignList - .filter((campaignId) => !checkList.pausedList.includes(campaignId)); // 일시정지 배너 거르기. - const extractBanCampaignList = extractPausedCampaignList - .filter((campaignId) => !checkList.banList.includes(campaignId)); // 마지막에 banList를 통해 거르기. + console.log( + `${creatorId} 방송인에게만 송출될 광고 없음. 카테고리 선택형 및 노출우선형 광고 검색 / ${getTime}`, + ); + const onCategorycampaignList = categoryCampaignList.filter(campaignId => + onCampaignList.includes(campaignId), + ); + const extractPausedCampaignList = onCategorycampaignList.filter( + campaignId => !checkList.pausedList.includes(campaignId), + ); // 일시정지 배너 거르기. + const extractBanCampaignList = extractPausedCampaignList.filter( + campaignId => !checkList.banList.includes(campaignId), + ); // 마지막에 banList를 통해 거르기. const returnCampaignId = extractBanCampaignList[getRandomInt(extractBanCampaignList.length)]; myCampaignId = returnCampaignId; } @@ -369,12 +309,15 @@ function callImg(socket: Socket, msg: string[]): void { // 기본배너 검색 const categoryCampaignList = await getGameCampaignList('-1', true); console.log(`${creatorId} 기본 배너 검색 / ${getTime}`); - const onCategorycampaignList = categoryCampaignList - .filter((campaignId) => onCampaignList.includes(campaignId)); - const extractPausedCampaignList = onCategorycampaignList - .filter((campaignId) => !checkList.pausedList.includes(campaignId)); // 일시정지 배너 거르기. - const extractBanCampaignList = extractPausedCampaignList - .filter((campaignId) => !checkList.banList.includes(campaignId)); // 마지막에 banList를 통해 거르기. + const onCategorycampaignList = categoryCampaignList.filter(campaignId => + onCampaignList.includes(campaignId), + ); + const extractPausedCampaignList = onCategorycampaignList.filter( + campaignId => !checkList.pausedList.includes(campaignId), + ); // 일시정지 배너 거르기. + const extractBanCampaignList = extractPausedCampaignList.filter( + campaignId => !checkList.banList.includes(campaignId), + ); // 마지막에 banList를 통해 거르기. const returnCampaignId = extractBanCampaignList[getRandomInt(extractBanCampaignList.length)]; myCampaignId = returnCampaignId; if (myCampaignId) { @@ -390,34 +333,25 @@ function callImg(socket: Socket, msg: string[]): void { } // 이전 송출하던 배너와 현재 배너가 같은 경우 - if (prevBannerName && myCampaignId === prevBannerName.split(',')[0]) { + if (previousBannerName && myCampaignId === previousBannerName.split(',')[0]) { return [false, myCampaignId, linkToChatBot]; } const bannerSrc = await getBannerSrc(myCampaignId); return [bannerSrc, myCampaignId, linkToChatBot]; } - interface CreatorIds { - creatorId: string; - creatorTwitchId?: string; - afreecaId?: string; - adChatAgreement: number; - } + async function getCreatorData(): Promise { - const initQuery = ` - SELECT creatorId, creatorTwitchId, adChatAgreement, afreecaId - FROM creatorInfo - WHERE advertiseUrl = ? - `; + const { creatorByUrl } = query; return new Promise((resolve, reject) => { - doQuery(initQuery, [cutUrl]) - .then((row) => { + doQuery(creatorByUrl, [cutUrl]) + .then(row => { if (row.result[0]) { resolve(row.result[0]); } else { socket.emit('url warning', []); } }) - .catch((errorData) => { + .catch(errorData => { errorData.point = 'getCreatorData()'; errorData.description = 'getCreatorData 불러오는 과정'; reject(errorData); @@ -426,19 +360,23 @@ function callImg(socket: Socket, msg: string[]): void { } function writeToDb(campaignId: string, creatorId: string, program: string): void { - const writeQuery = 'INSERT INTO campaignTimestamp (campaignId, creatorId, program) VALUES (?, ?, ?);'; - doQuery(writeQuery, [campaignId, creatorId, program]); + const { insertTimestamp } = query; + doQuery(insertTimestamp, [campaignId, creatorId, program]); } async function init(): Promise { const CREATOR_DATA = await getCreatorData(); - if (CREATOR_DATA.creatorId) { // 새로운 배너가 송출되는 경우 + if (CREATOR_DATA.creatorId) { + // 새로운 배너가 송출되는 경우 myGameId = await getGameId(CREATOR_DATA.creatorId); const bannerInfo = await getBanner([CREATOR_DATA.creatorId, myGameId]); - const checkOptionType = typeof bannerInfo[1] === 'string' ? campaignObject[bannerInfo[1]][0] : null; - const campaignName = typeof bannerInfo[1] === 'string' ? campaignObject[bannerInfo[1]][1] : null; + const checkOptionType = + typeof bannerInfo[1] === 'string' ? campaignObject[bannerInfo[1]][0] : null; + const campaignName = + typeof bannerInfo[1] === 'string' ? campaignObject[bannerInfo[1]][1] : null; const linkName = typeof bannerInfo[1] === 'string' ? bannerInfo[2] : null; - const campaignDescription = typeof bannerInfo[1] === 'string' ? campaignObject[bannerInfo[1]][2] : null; + const campaignDescription = + typeof bannerInfo[1] === 'string' ? campaignObject[bannerInfo[1]][2] : null; const descriptionToChat: string | boolean | null = campaignDescription || linkName; // 챗봇 관련 필요 정보 @@ -446,19 +384,23 @@ function callImg(socket: Socket, msg: string[]): void { const myAdChatAgreement = CREATOR_DATA.adChatAgreement; // 트위치 챗봇 동의 및 옵션타입 cpm+cpc인 경우에 챗봇으로 데이터 전송 const CHATBOT_ALLOWED_CAMPAIGN_OPTION_TYPE = [1, 3]; - if (myAdChatAgreement === 1 - && checkOptionType - && CHATBOT_ALLOWED_CAMPAIGN_OPTION_TYPE.includes(checkOptionType)) { + if ( + myAdChatAgreement === 1 && + checkOptionType && + CHATBOT_ALLOWED_CAMPAIGN_OPTION_TYPE.includes(checkOptionType) + ) { if (myCreatorTwitchId) { // 챗봇은 트위치 한정. 트위치 아이디가 없는 경우 에미팅 하지 않도록 추가 // @by hwasurr 21.01.08 - console.log(`${CREATOR_DATA.creatorId} / next-campaigns-twitch-chatbot Emitting ${myCreatorTwitchId} / ${getTime}`); + console.log( + `${CREATOR_DATA.creatorId} / next-campaigns-twitch-chatbot Emitting ${myCreatorTwitchId} / ${getTime}`, + ); socket.broadcast.emit('next-campaigns-twitch-chatbot', { campaignId: myCampaignId, creatorId: CREATOR_DATA.creatorId, creatorTwitchId: myCreatorTwitchId, campaignName, - descriptionToChat + descriptionToChat, }); } } @@ -472,7 +414,9 @@ function callImg(socket: Socket, msg: string[]): void { if (myCampaignId) { writeToDb(myCampaignId, CREATOR_DATA.creatorId, programType); } - console.log(`${CREATOR_DATA.creatorId} / 같은 캠페인 송출 중이어서 재호출 안함 / ${getTime}`); + console.log( + `${CREATOR_DATA.creatorId} / 같은 캠페인 송출 중이어서 재호출 안함 / ${getTime}`, + ); } } } diff --git a/socket/public/client.ts b/socket/lib/client.ts similarity index 100% rename from socket/public/client.ts rename to socket/lib/client.ts diff --git a/socket/public/hiddenEventHandler.ts b/socket/lib/hiddenEventHandler.ts similarity index 100% rename from socket/public/hiddenEventHandler.ts rename to socket/lib/hiddenEventHandler.ts diff --git a/socket/public/imageClicker.ts b/socket/lib/imageClicker.ts similarity index 100% rename from socket/public/imageClicker.ts rename to socket/lib/imageClicker.ts diff --git a/socket/public/programIdentifier.ts b/socket/lib/programIdentifier.ts similarity index 100% rename from socket/public/programIdentifier.ts rename to socket/lib/programIdentifier.ts diff --git a/socket/models/query.ts b/socket/models/query.ts new file mode 100644 index 000000000..16b2a5492 --- /dev/null +++ b/socket/models/query.ts @@ -0,0 +1,68 @@ +interface Query { + [key: string]: string; +} + +const query: Query = { + creatorCampaign: 'SELECT campaignList FROM creatorCampaign WHERE creatorId = ?;', + activeCampaign: ` + SELECT campaignId, campaignName, optionType, startDate, finDate, selectedTime, campaignDescription, cashAmount + FROM campaign + LEFT JOIN marketerInfo ON campaign.marketerId = marketerInfo.marketerId + LEFT JOIN marketerDebit ON campaign.marketerId = marketerDebit.marketerId + WHERE + (marketerInfo.marketerContraction = 1 AND campaign.onOff = 1 AND campaign.optionType = 1 AND campaign.limitState = 0) + OR (marketerInfo.marketerContraction = 1 AND campaign.onOff = 1 AND campaign.optionType = 3) + `, + categoryCampaign: 'SELECT campaignList FROM categoryCampaign WHERE categoryId = ?', + twitchGameIdForCreator: ` + SELECT gameId FROM twitchStreamDetail AS tsd + WHERE streamId = ( + SELECT streamId FROM twitchStream + JOIN creatorInfo ON creatorTwitchOriginalId = streamerId + WHERE creatorId = ? + ORDER BY startedAt DESC LIMIT 1 + ) AND time > DATE_SUB(NOW(), INTERVAL 10 MINUTE) + ORDER BY tsd.time DESC LIMIT 1;`, + afreecaGameIdForCreator: ` + SELECT broadCategory AS gameId FROM AfreecaBroadDetail AS ABD + WHERE broadId = ( + SELECT broadId FROM AfreecaBroad JOIN creatorInfo ON afreecaId = userId + WHERE creatorId = ? ORDER BY broadStartedAt DESC LIMIT 1 + ) AND createdAt > DATE_SUB(NOW(), INTERVAL 10 MINUTE) + ORDER BY createdAt DESC LIMIT 1 + `, + banAndPausedCampaign: ` + SELECT banList, pausedList + FROM creatorCampaign + WHERE creatorId = ? + `, + bannerSrc: ` + SELECT br.bannerSrcUrl AS bannerSrc + FROM campaign + JOIN bannerRegistered as br + ON br.bannerId = campaign.bannerId + WHERE campaign.campaignId = ? + `, + linkUrl: ` + SELECT links + FROM linkRegistered + WHERE linkId = (SELECT connectedLinkId FROM campaign WHERE campaignId = ?) +`, + onadMallItemUrl: `SELECT itemSiteUrl + FROM merchandiseMallItems + JOIN campaign on campaign.merchandiseId = merchandiseMallItems.merchandiseId + WHERE campaignId = ?`, + creatorByUrl: ` + SELECT creatorId, creatorTwitchId, adChatAgreement, afreecaId + FROM creatorInfo + WHERE advertiseUrl = ? +`, + insertTimestamp: + 'INSERT INTO campaignTimestamp (campaignId, creatorId, program) VALUES (?, ?, ?);', + insertBannerVisiblePageApi: + 'INSERT INTO bannerVisible (advertiseUrl, visibleState, program, type) VALUES (?, ?, ?, 1);', + insertBannerVisibleBannerClick: + 'INSERT INTO bannerVisible (advertiseUrl, visibleState, program, type) VALUES (?, ?, ?, 0);', +}; + +export default query; From 89d9aa9e71bfb2f71bd45234ee904f83e82221ec Mon Sep 17 00:00:00 2001 From: leejineun Date: Wed, 28 Jul 2021 10:17:18 +0900 Subject: [PATCH 2/6] =?UTF-8?q?requstBanner=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + live-commerce/@types/data.ts | 16 +- socket/@types/bannerRequest.ts | 8 +- socket/@types/shared.ts | 5 + socket/app.ts | 20 +- socket/lib/requestBanner copy.ts | 373 ++++++++++++++++++++ socket/lib/{callImg.ts => requestBanner.ts} | 103 ++---- socket/lib/requestBannerTest.ts | 27 ++ socket/lib/requestBannerV2.ts | 75 ++++ socket/models/query.ts | 4 +- socket/views/client.ejs | 2 +- 11 files changed, 545 insertions(+), 90 deletions(-) create mode 100644 socket/@types/shared.ts create mode 100644 socket/lib/requestBanner copy.ts rename socket/lib/{callImg.ts => requestBanner.ts} (82%) create mode 100644 socket/lib/requestBannerTest.ts create mode 100644 socket/lib/requestBannerV2.ts diff --git a/.gitignore b/.gitignore index 98090adc2..7eb6989d1 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,5 @@ sleepAccount/ # python __pycache__ __pypackages__/ + +connectionPool.ts diff --git a/live-commerce/@types/data.ts b/live-commerce/@types/data.ts index d8ff77141..70e7d908d 100644 --- a/live-commerce/@types/data.ts +++ b/live-commerce/@types/data.ts @@ -45,17 +45,17 @@ export interface QueryResult { } export interface RankingData { - nickname:string; - total:number; + nickname: string; + total: number; } -export interface AudioEncoding { - speakingRate:number; - audioEncoding: 'MP3' | undefined | null +export interface AudioEncoding { + speakingRate: number; + audioEncoding: 'MP3' | undefined | null; } export interface Voice { - languageCode:string; - name:string; - ssmlGender : "FEMALE" | "SSML_VOICE_GENDER_UNSPECIFIED" | "MALE" | "NEUTRAL" | null | undefined + languageCode: string; + name: string; + ssmlGender: 'FEMALE' | 'SSML_VOICE_GENDER_UNSPECIFIED' | 'MALE' | 'NEUTRAL' | null | undefined; } diff --git a/socket/@types/bannerRequest.ts b/socket/@types/bannerRequest.ts index 097fa3095..e022e4b01 100644 --- a/socket/@types/bannerRequest.ts +++ b/socket/@types/bannerRequest.ts @@ -2,7 +2,7 @@ export interface CampaignIdOptionType { [_: string]: [number, string, string]; } -export interface TimeData { +export interface Campaign { startDate: Date; finDate: Date; campaignId: string; @@ -29,8 +29,6 @@ export interface CreatorIds { adChatAgreement: number; } -export interface BannerRequest { - url: string; - previousBannerName: string; - programType: string; +export interface BanPausedCampaign { + [key: string]: string[]; } diff --git a/socket/@types/shared.ts b/socket/@types/shared.ts new file mode 100644 index 000000000..e6d9a0cf5 --- /dev/null +++ b/socket/@types/shared.ts @@ -0,0 +1,5 @@ +export interface CreatorStatus { + url: string; + previousBannerName: string; + programType: string; +} diff --git a/socket/app.ts b/socket/app.ts index b09f9b99d..4816c1812 100644 --- a/socket/app.ts +++ b/socket/app.ts @@ -9,8 +9,8 @@ import http from 'http'; import socketio, { Socket } from 'socket.io'; import nodeSchedule from 'node-schedule'; import doQuery from './models/doQuery'; -import callImg from './lib/callImg'; -import { BannerRequest } from './@types/bannerRequest'; +import requestBanner from './lib/requestBannerV2'; +import { CreatorStatus } from './@types/shared'; import query from './models/query'; const app = express(); @@ -64,7 +64,7 @@ interface SocketInfo { io.on('connection', (socket: Socket) => { let SOCKET_ID: string = socket.id; const urlArray: Array = Object.values(socketInfo); - const requestMessage: BannerRequest = { + const requestMessage: CreatorStatus = { url: '', previousBannerName: '', programType: '', @@ -82,7 +82,7 @@ interface SocketInfo { // *********************************************************** // 첫 입장시 발생되는 이벤트인 "new client" 이벤트 핸들러 - socket.on('new client', (msg: [string, number, string]) => { + socket.on('new client', async (msg: [string, number, string]) => { const clientUrl = msg[0]; const HISTORY = msg[1]; const programType = msg[2]; @@ -95,7 +95,7 @@ interface SocketInfo { console.log('SOCKET ON'); socket.emit('host pass', SOCKET_HOST); - callImg(socket, requestMessage); + await requestBanner(socket, requestMessage); } else if ( // 트위치 스튜디오 또는 아프리카 프릭샷으로 접속하지 않았으면서 !['afreeca-freecshot', 'twitch-studio'].includes(programType) && @@ -112,7 +112,7 @@ interface SocketInfo { } else { socketInfo[SOCKET_ID] = clientUrl; socket.emit('host pass', SOCKET_HOST); - callImg(socket, requestMessage); + await requestBanner(socket, requestMessage); } }); @@ -126,7 +126,7 @@ interface SocketInfo { // *********************************************************** // 송출될 광고 재요청 이벤트 : "re-render" 이벤트 핸들러 - socket.on('re-render', (msg: [string, string, string]) => { + socket.on('re-render', async (msg: [string, string, string]) => { const clientUrl = msg[0]; const previousBannerName = msg[1]; const programType = msg[2]; @@ -134,18 +134,18 @@ interface SocketInfo { requestMessage.url = clientUrl; requestMessage.previousBannerName = previousBannerName; requestMessage.programType = programType; - callImg(socket, requestMessage); + await requestBanner(socket, requestMessage); }); // *********************************************************** // - socket.on('pageOn', (msg: [string, string]) => { + socket.on('pageOn', async (msg: [string, string]) => { const clientUrl = msg[0]; const programType = msg[1]; requestMessage.url = clientUrl; requestMessage.previousBannerName = ''; requestMessage.programType = programType; - callImg(socket, requestMessage); + await requestBanner(socket, requestMessage); }); // 배너 더블 클릭을 통해 ON/OFF 조절 diff --git a/socket/lib/requestBanner copy.ts b/socket/lib/requestBanner copy.ts new file mode 100644 index 000000000..f8585ebf7 --- /dev/null +++ b/socket/lib/requestBanner copy.ts @@ -0,0 +1,373 @@ +/* eslint-disable no-console */ +/* eslint-disable import/first */ + +import dotenv from 'dotenv'; + +dotenv.config(); + +import doQuery from '../models/doQuery'; +import { + Campaign, + ReturnDate, + LinkJson, + CreatorIds, + BanPausedCampaign, +} from '../@types/bannerRequest'; +import { CreatorStatus } from '../@types/shared'; +import query from '../models/query'; + +class RequestBanner { + fullUrl: string; + cutUrl: string; + previousBannerName: string; + programType: string; + timestamp: string; + campaignObject: any; + myCampaignId: string; + creatorGameId: string; + creatorId: string; + constructor(request: CreatorStatus) { + this.fullUrl = request.url; + this.cutUrl = `/${this.fullUrl.split('/')[4]}`; + this.previousBannerName = request.previousBannerName; + this.programType = request.programType; + this.timestamp = new Date().toLocaleString(); + this.campaignObject = {}; + this.myCampaignId = ''; + this.creatorGameId = ''; + this.creatorId = ''; + } + + getCreatorIdAndChatAgreement = async (): Promise => { + const { creatorByUrl } = query; + return new Promise((resolve, reject) => { + doQuery(creatorByUrl, [this.cutUrl]) + .then(row => { + if (row.result[0]) { + const creator = row.result[0]; + this.creatorId = creator.creatorId; + resolve(row.result[0]); + } else { + // socket.emit('url warning', []); + } + }) + .catch(errorData => { + console.log(errorData); + reject(errorData); + }); + }); + }; + + getGameId = async (): Promise => { + console.log(`${this.creatorId} / get gameid / ${this.timestamp}`); + const { assignedTwitchGameId } = query; + const { assignedAfreecaGameId } = query; + try { + const [{ result: twitchGameIdResult }, { result: afreecaGameIdResult }] = await Promise.all([ + doQuery(assignedTwitchGameId, [this.creatorId]), + doQuery(assignedAfreecaGameId, [this.creatorId]), + ]); + + // 트위치 방송 중인 경우, 트위치 현재 진행 중 카테고리 + if (twitchGameIdResult.length > 0) { + this.creatorGameId = twitchGameIdResult[0].gameId; + } + + // 트위치 방송중이 아니며, 아프리카티비 방송 중 -> 아프리카 tv 카테고리 + // 즉, 동시 송출 중이라면, twitch 카테고리를 우선시 함. @by hwasurr + if (!(twitchGameIdResult.length > 0) && afreecaGameIdResult.length > 0) { + this.creatorGameId = afreecaGameIdResult[0].gameId; + return this.creatorGameId; + } + return ''; + } catch (errorData) { + errorData.point = 'getGameId()'; + errorData.description = 'TWITCHSTREAMDETAIL 또는 AFREECABROAD 에서 GAMEID 가져오기'; + throw errorData; + } + }; + + getCreatorCampaignList = (): Promise => { + console.log(`${this.creatorId} / 특정 방송인 송출 광고 조회 / ${this.timestamp}`); + + const { creatorCampaign } = query; + return new Promise((resolve, reject) => { + doQuery(creatorCampaign, [this.creatorId]) + .then(row => { + if (row.result[0].length !== 0) { + const creatorCampaigns = JSON.parse(row.result[0].campaignList); + resolve(creatorCampaigns.campaignList); + } + }) + .catch(errorData => { + reject(errorData); + }); + }); + }; + + getActiveCampaigns = (): Promise => { + // 광고주 상태가 켜져있고, 캠페인이 켜져있으며 일일예산을 소진하지 않은 LIVE생방송배너광고(CPM+CPC) 이거나, + // 광고주 상태가 켜져있고, 캠페인이 켜져있는 판매형광고(CPS)만 가져온다. + const { activeCampaign } = query; + + return new Promise((resolve, reject) => { + doQuery(activeCampaign) + .then(row => { + const filteredDate: ReturnDate = {}; + const campaignIds: string[] = []; + const nowDate = new Date(); + row.result.map((data: Campaign) => { + if ( + data.startDate && + data.startDate < nowDate && + (data.finDate > nowDate || !data.finDate) + ) { + filteredDate[data.campaignId] = data.selectedTime; + this.campaignObject[data.campaignId] = { + optionType: data.optionType, + campaignName: data.campaignName, + campaignDescription: data.campaignDescription, + }; + } + }); + Object.values(filteredDate).map((value: Date, index: number) => { + const activeTime = JSON.parse(value.toLocaleString()); + if (activeTime.time.includes(nowDate.getHours())) { + campaignIds.push(Object.keys(filteredDate)[index]); + } + }); + resolve(campaignIds); + }) + .catch(errorData => { + reject(errorData); + }); + }); + }; + + getBanOrPausedCampaignsByCreator = (): Promise => { + const { banAndPausedCampaign } = query; + + return new Promise((resolve, reject) => { + doQuery(banAndPausedCampaign, [this.creatorId]) + .then(row => { + const bannedCampaigns = JSON.parse(row.result[0].banList).campaignList; + const pausedCampaigns = JSON.parse(row.result[0].pausedList).campaignList; + resolve({ bannedCampaigns, pausedCampaigns }); + }) + .catch(errorData => { + reject(errorData); + }); + }); + }; + + getCategoryCampaigns = async (categoryId: string): Promise => { + const { categoryCampaign } = query; + return new Promise((resolve, reject) => { + doQuery(categoryCampaign, [categoryId]) + .then(row => { + if (row.result.length > 0) { + const categoryCampaigns = JSON.parse(row.result[0].campaignList); + resolve(categoryCampaigns.campaignList); + } + resolve([]); + }) + .catch(errorData => { + reject(errorData); + }); + }); + }; + + getGameCampaigns = async (gameId: string, isDefault = false): Promise => { + // ************************************************************ + // 트위치 카테고리의 경우 + const CATEGORY_WHATEVER = '14'; + + let campaigns: string[] = []; + if (isDefault) { + campaigns = await this.getCategoryCampaigns(gameId); + return campaigns; + } + await Promise.all([ + this.getCategoryCampaigns(gameId), + this.getCategoryCampaigns(CATEGORY_WHATEVER), + ]) + .then(([categoryCampaignByGameId, AnyCategoryCampaign]) => { + campaigns = categoryCampaignByGameId.concat(AnyCategoryCampaign); + }) + .catch(errorData => { + console.log(errorData); + }); + + return Array.from(new Set(campaigns)); + }; + + getBannerSrc = (campaignId: string): Promise => { + const { bannerSrc } = query; + return new Promise((resolve, reject) => { + doQuery(bannerSrc, [campaignId]) + .then(row => { + resolve(row.result[0].bannerSrc); + }) + .catch(errorData => { + reject(errorData); + }); + }); + }; + + getLinkName = (campaignId: string): Promise => { + const { linkUrl } = query; + return new Promise((resolve, reject) => { + doQuery(linkUrl, [campaignId]) + .then(row => { + let linkName = ''; + const links = JSON.parse(row.result[0].links); + links.links.map((data: LinkJson) => { + if (data.primary === true) { + linkName = data.linkName; + } + }); + resolve(linkName); + }) + .catch(errorData => { + reject(errorData); + }); + }); + }; + + getMerchandiseSiteUrl = async (campaignId: string): Promise => { + const { onadMallItemUrl } = query; + const { result } = await doQuery(onadMallItemUrl, [campaignId]); + if (!(result.length > 0)) return ''; + return result[0].itemSiteUrl; + }; + + getRandomInt = (length: number): number => { + const max = Math.floor(length); + return Math.floor(Math.random() * (max - 0)) + 0; // 최댓값은 제외, 최솟값은 포함 + }; + + markTimestamp = (campaignId: string, creatorId: string, program: string): void => { + const { insertTimestamp } = query; + doQuery(insertTimestamp, [campaignId, creatorId, program]); + }; + + filterCampaign = ( + activeCreatorCampaigns: string[], + banOrPausedCampaigns: BanPausedCampaign, + ): string[] => { + const extractPausedCampaignList = activeCreatorCampaigns.filter( + (campaignId: string) => !banOrPausedCampaigns.pausedCampaigns.includes(campaignId), + ); // 일시정지 배너 거르기. + const filterdCampaigns = extractPausedCampaignList.filter( + (campaignId: string) => !banOrPausedCampaigns.bannedCampaigns.includes(campaignId), + ); // 마지막에 banList를 통해 거르기. + return filterdCampaigns; + }; + + getDefaultBanner = async (banOrPausedCampaigns: BanPausedCampaign): Promise => { + const categoryCampaignList = await this.getGameCampaigns('-1', true); + console.log(`${this.creatorId} 기본 배너 검색 / ${this.timestamp}`); + const onCategorycampaignList = categoryCampaignList.filter(campaignId => + categoryCampaignList.includes(campaignId), + ); + + const filteredCampaigns = this.filterCampaign(onCategorycampaignList, banOrPausedCampaigns); + + const selectedCampaign = filteredCampaigns[this.getRandomInt(filteredCampaigns.length)]; + // this.myCampaignId = selectedCampaign; + return selectedCampaign; + }; + + getBanner = async ([creatorId, gameId]: string[]): Promise => { + console.log( + `-----------------------Id : ${creatorId} / ${this.timestamp}---------------------------`, + ); + let selectedCampaign; + let linkToChatBot; + const [creatorCampaigns, activeCampaigns, banOrPausedCampaigns] = await Promise.all([ + this.getCreatorCampaignList(), + this.getActiveCampaigns(), + this.getBanOrPausedCampaignsByCreator(), + ]); + // ********************************************************* + // 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인 -> ON 상태 필터링 + const activeCreatorCampaigns = creatorCampaigns.filter((campaignId: string) => + activeCampaigns.includes(campaignId), + ); + + // 현재 ON상태인 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인 목록이 있는 경우 + if (activeCreatorCampaigns.length !== 0) { + const filteredCampaigns = this.filterCampaign(activeCreatorCampaigns, banOrPausedCampaigns); + if (filteredCampaigns) { + selectedCampaign = filteredCampaigns[this.getRandomInt(filteredCampaigns.length)]; + // this.myCampaignId = selectedCampaign; + console.log('방송인 에게만 송출될 캠페인 : ', this.myCampaignId); + } + } else { + // ********************************************************* + // 현재 ON상태인 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인이 없는 경우 + const categoryCampaignList = await this.getGameCampaigns(gameId); + console.log( + `${creatorId} 방송인에게만 송출될 광고 없음. 카테고리 선택형 및 노출우선형 광고 검색 / ${this.timestamp}`, + ); + const onCategorycampaignList = categoryCampaignList.filter(campaignId => + activeCampaigns.includes(campaignId), + ); + const filteredCampaigns = this.filterCampaign(onCategorycampaignList, banOrPausedCampaigns); + + selectedCampaign = filteredCampaigns[this.getRandomInt(filteredCampaigns.length)]; + // this.myCampaignId = selectedCampaign; + } + + // ********************************************************* + // RETRUN 섹션 + const OPTION_TYPE_LIVE_BANNER_CAMPAIGN = 1; // 생방송 라이브 배너광고 캠페인의 경우 + const OPTION_TYPE_CPS_CAMPAIGN = 3; // 판매형광고(CPS) 캠페인의 경우 + const campaignToStream = { + bannerSrc: '', + campaignId: '', + linkToChatBot: '', + }; + if ( + this.myCampaignId && + this.campaignObject[this.myCampaignId].optionType === OPTION_TYPE_LIVE_BANNER_CAMPAIGN + ) { + // 송출될 캠페인의 link 를 가져와 트위치 챗봇에 챗봇광고 이벤트를 에밋하기 위한 정보를 가져온다. + console.log(`${creatorId} / 광고될 캠페인은 ${this.myCampaignId} / ${this.timestamp}`); + linkToChatBot = await this.getLinkName(this.myCampaignId); + } else if ( + this.myCampaignId && + this.campaignObject[this.myCampaignId].optionType === OPTION_TYPE_CPS_CAMPAIGN + ) { + console.log(`${creatorId} / 광고될 캠페인은 ${this.myCampaignId} / ${this.timestamp}`); + linkToChatBot = await this.getMerchandiseSiteUrl(this.myCampaignId); + } else { + // 송출할 배너가 없어서 기본 배너 검색 + const defaultBanner = await this.getDefaultBanner(banOrPausedCampaigns); + if (defaultBanner) { + // 기본 배너 송출 + console.log(`${creatorId} 기본 배너 송출 / ${this.timestamp}`); + linkToChatBot = await this.getLinkName(this.myCampaignId); + } else { + // 기본 배너도 꺼둔 경우 + // socket.emit('img clear', []); + console.log(`${creatorId} / 켜져있는 광고 없음 / ${this.timestamp}`); + return campaignToStream; + } + } + + // 이전 송출하던 배너와 현재 배너가 같은 경우 + if (this.previousBannerName && this.myCampaignId === this.previousBannerName.split(',')[0]) { + campaignToStream.campaignId = this.myCampaignId; + campaignToStream.linkToChatBot = linkToChatBot; + return campaignToStream; + } + const bannerSrc = await this.getBannerSrc(this.myCampaignId); + campaignToStream.bannerSrc = bannerSrc; + campaignToStream.campaignId = this.myCampaignId; + campaignToStream.linkToChatBot = linkToChatBot; + return campaignToStream; + }; +} + +export default RequestBanner; diff --git a/socket/lib/callImg.ts b/socket/lib/requestBanner.ts similarity index 82% rename from socket/lib/callImg.ts rename to socket/lib/requestBanner.ts index 0d42cd20a..3eeaddc73 100644 --- a/socket/lib/callImg.ts +++ b/socket/lib/requestBanner.ts @@ -1,26 +1,25 @@ +/* eslint-disable no-console */ +/* eslint-disable import/first */ + import { Socket } from 'socket.io'; import doQuery from '../models/doQuery'; import { CampaignIdOptionType, - TimeData, + Campaign, ReturnDate, LinkJson, CreatorIds, + BanPausedCampaign, } from '../@types/bannerRequest'; +import { CreatorStatus } from '../@types/shared'; import query from '../models/query'; -interface Request { - url: string; - previousBannerName: string; - programType: string; -} - -function callImg(socket: Socket, request: Request): void { +function requestBanner(socket: Socket, request: CreatorStatus): void { const fullUrl: string = request.url; const cutUrl = `/${fullUrl.split('/')[4]}`; const { previousBannerName } = request; const { programType } = request; - const getTime: string = new Date().toLocaleString(); + const timestamp: string = new Date().toLocaleString(); const campaignObject: CampaignIdOptionType = {}; @@ -28,28 +27,23 @@ function callImg(socket: Socket, request: Request): void { let myGameId: string; // creatorId를 전달받아 creatorCampaign과 onff List를 도출. const getCreatorCampaignList = (creatorId: string): Promise => { - console.log(`${creatorId} / 특정 방송인 송출 광고 조회 / ${getTime}`); + console.log(`${creatorId} / 특정 방송인 송출 광고 조회 / ${timestamp}`); const { creatorCampaign } = query; - return new Promise((resolve, reject) => { doQuery(creatorCampaign, [creatorId]) .then(row => { if (row.result[0].length !== 0) { - const jsonData = JSON.parse(row.result[0].campaignList); - resolve(jsonData.campaignList); + const creatorCampaignList = JSON.parse(row.result[0].campaignList); + resolve(creatorCampaignList.campaignList); } }) .catch(errorData => { - errorData.point = 'getCreatorCampaignList()'; - errorData.description = - 'creatorCampaign table에서 creator에게 계약된 campaignList 가져오는 과정.'; reject(errorData); }); }); }; - // 켜져있는 광고 const getOnCampaignList = (): Promise => { // 광고주 상태가 켜져있고, 캠페인이 켜져있으며 일일예산을 소진하지 않은 LIVE생방송배너광고(CPM+CPC) 이거나, // 광고주 상태가 켜져있고, 캠페인이 켜져있는 판매형광고(CPS)만 가져온다. @@ -61,7 +55,7 @@ function callImg(socket: Socket, request: Request): void { const filteredDate: ReturnDate = {}; const campaignIdList: string[] = []; const nowDate = new Date(); - row.result.map((data: TimeData) => { + row.result.map((data: Campaign) => { if ( data.startDate && data.startDate < nowDate && @@ -84,8 +78,6 @@ function callImg(socket: Socket, request: Request): void { resolve(campaignIdList); }) .catch(errorData => { - errorData.point = 'getOnCampaignList()'; - errorData.description = 'campaign table에서 현재 ON 되어있는 campaignList 가져오는 과정.'; reject(errorData); }); }); @@ -103,21 +95,19 @@ function callImg(socket: Socket, request: Request): void { resolve([]); }) .catch(errorData => { - errorData.point = 'getCategoryCampaignList()'; - errorData.description = '하나의 categoryId를 통하여 계약된 campaignList 가져오는 과정.'; reject(errorData); }); }); }; const getGameId = async (creatorId: string): Promise => { - console.log(`${creatorId} / get gameid / ${getTime}`); - const { twitchGameIdForCreator } = query; - const { afreecaGameIdForCreator } = query; + console.log(`${creatorId} / get gameid / ${timestamp}`); + const { assignedTwitchGameId } = query; + const { assignedAfreecaGameId } = query; try { const [{ result: twitchGameIdResult }, { result: afreecaGameIdResult }] = await Promise.all([ - doQuery(twitchGameIdForCreator, [creatorId]), - doQuery(afreecaGameIdForCreator, [creatorId]), + doQuery(assignedTwitchGameId, [creatorId]), + doQuery(assignedAfreecaGameId, [creatorId]), ]); // 트위치 방송 중인 경우, 트위치 현재 진행 중 카테고리 @@ -139,37 +129,29 @@ function callImg(socket: Socket, request: Request): void { } }; - /** - * @deprecated 2021.01.11 by hwasurr - * store uncehcked game - * @param gameId gameId - * @param creatorId creatorId - */ - // "하나의 gameId" + "카테고리무관" 에 해당하는 모든 캠페인 리스트를 반환하는 Promise const getGameCampaignList = async (gameId: string, isDefault = false): Promise => { // ************************************************************ // 트위치 카테고리의 경우 const CATEGORY_WHATEVER = '14'; - let returnList: string[] = []; + let campaigns: string[] = []; if (isDefault) { - returnList = await getCategoryCampaignList(gameId); - return returnList; + campaigns = await getCategoryCampaignList(gameId); + return campaigns; } await Promise.all([getCategoryCampaignList(gameId), getCategoryCampaignList(CATEGORY_WHATEVER)]) .then(([campaignList1, campaignList2]) => { - returnList = campaignList1.concat(campaignList2); + campaigns = campaignList1.concat(campaignList2); }) .catch(errorData => { - errorData.point = 'getGameCampaignList()'; - errorData.description = 'categoryCampaign에서 각각의 categoryId에 따른 캠페인 가져오기'; + console.log(errorData); }); - return Array.from(new Set(returnList)); + return Array.from(new Set(campaigns)); }; - const getBanList = (creatorId: string): Promise => { + const getBanList = (creatorId: string): Promise => { const { banAndPausedCampaign } = query; return new Promise((resolve, reject) => { @@ -180,8 +162,6 @@ function callImg(socket: Socket, request: Request): void { resolve({ banList, pausedList }); }) .catch(errorData => { - errorData.point = 'getBanList()'; - errorData.description = '해당 creator의 banList를 가져오는 과정'; reject(errorData); }); }); @@ -192,12 +172,9 @@ function callImg(socket: Socket, request: Request): void { return new Promise((resolve, reject) => { doQuery(bannerSrc, [campaignId]) .then(row => { - console.log(row.result[0].bannerSrc); resolve(row.result[0].bannerSrc); }) .catch(errorData => { - errorData.point = 'getBannerSrc()'; - errorData.description = '하나의 campaignId를 통해 bannerSrc를 가져오는 과정'; reject(errorData); }); }); @@ -218,8 +195,6 @@ function callImg(socket: Socket, request: Request): void { resolve(linkName); }) .catch(errorData => { - errorData.point = 'getLinkName()'; - errorData.description = '하나의 campaignId를 통해 linkName 가져오는 과정'; reject(errorData); }); }); @@ -245,7 +220,9 @@ function callImg(socket: Socket, request: Request): void { async function getBanner([creatorId, gameId]: string[]): Promise< [string | boolean, string | boolean, string | boolean] > { - console.log(`-----------------------Id : ${creatorId} / ${getTime}---------------------------`); + console.log( + `-----------------------Id : ${creatorId} / ${timestamp}---------------------------`, + ); let linkToChatBot; const [creatorCampaignList, onCampaignList, checkList] = await Promise.all([ getCreatorCampaignList(creatorId), @@ -254,17 +231,17 @@ function callImg(socket: Socket, request: Request): void { ]); // ********************************************************* // 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인 -> ON 상태 필터링 - const onCreatorcampaignList = creatorCampaignList.filter((campaignId: any) => + const onCreatorcampaignList = creatorCampaignList.filter((campaignId: string) => onCampaignList.includes(campaignId), ); // 현재 ON상태인 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인 목록이 있는 경우 if (onCreatorcampaignList.length !== 0) { const extractPausedCampaignList = onCreatorcampaignList.filter( - (campaignId: any) => !checkList.pausedList.includes(campaignId), + (campaignId: string) => !checkList.pausedList.includes(campaignId), ); // 일시정지 배너 거르기. const extractBanCampaignList = extractPausedCampaignList.filter( - (campaignId: any) => !checkList.banList.includes(campaignId), + (campaignId: string) => !checkList.banList.includes(campaignId), ); // 마지막에 banList를 통해 거르기. if (extractBanCampaignList) { const returnCampaignId = @@ -277,7 +254,7 @@ function callImg(socket: Socket, request: Request): void { // 현재 ON상태인 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인이 없는 경우 const categoryCampaignList = await getGameCampaignList(gameId); console.log( - `${creatorId} 방송인에게만 송출될 광고 없음. 카테고리 선택형 및 노출우선형 광고 검색 / ${getTime}`, + `${creatorId} 방송인에게만 송출될 광고 없음. 카테고리 선택형 및 노출우선형 광고 검색 / ${timestamp}`, ); const onCategorycampaignList = categoryCampaignList.filter(campaignId => onCampaignList.includes(campaignId), @@ -299,16 +276,16 @@ function callImg(socket: Socket, request: Request): void { if (myCampaignId && campaignObject[myCampaignId][0] === OPTION_TYPE_LIVE_BANNER_CAMPAIGN) { // 송출될 캠페인의 link 를 가져와 트위치 챗봇에 챗봇광고 이벤트를 에밋하기 위한 정보를 가져온다. - console.log(`${creatorId} / 광고될 캠페인은 ${myCampaignId} / ${getTime}`); + console.log(`${creatorId} / 광고될 캠페인은 ${myCampaignId} / ${timestamp}`); linkToChatBot = await getLinkName(myCampaignId); } else if (myCampaignId && campaignObject[myCampaignId][0] === OPTION_TYPE_CPS_CAMPAIGN) { - console.log(`${creatorId} / 광고될 캠페인은 ${myCampaignId} / ${getTime}`); + console.log(`${creatorId} / 광고될 캠페인은 ${myCampaignId} / ${timestamp}`); linkToChatBot = await getMerchandiseSiteUrl(myCampaignId); } else { // 송출할 광고 없다. // 기본배너 검색 const categoryCampaignList = await getGameCampaignList('-1', true); - console.log(`${creatorId} 기본 배너 검색 / ${getTime}`); + console.log(`${creatorId} 기본 배너 검색 / ${timestamp}`); const onCategorycampaignList = categoryCampaignList.filter(campaignId => onCampaignList.includes(campaignId), ); @@ -322,12 +299,12 @@ function callImg(socket: Socket, request: Request): void { myCampaignId = returnCampaignId; if (myCampaignId) { // 기본 배너 송출 - console.log(`${creatorId} 기본 배너 송출 / ${getTime}`); + console.log(`${creatorId} 기본 배너 송출 / ${timestamp}`); linkToChatBot = await getLinkName(myCampaignId); } else { // 기본 배너도 꺼둔 경우 socket.emit('img clear', []); - console.log(`${creatorId} / 켜져있는 광고 없음 / ${getTime}`); + console.log(`${creatorId} / 켜져있는 광고 없음 / ${timestamp}`); return [false, false, false]; } } @@ -352,8 +329,6 @@ function callImg(socket: Socket, request: Request): void { } }) .catch(errorData => { - errorData.point = 'getCreatorData()'; - errorData.description = 'getCreatorData 불러오는 과정'; reject(errorData); }); }); @@ -393,7 +368,7 @@ function callImg(socket: Socket, request: Request): void { // 챗봇은 트위치 한정. 트위치 아이디가 없는 경우 에미팅 하지 않도록 추가 // @by hwasurr 21.01.08 console.log( - `${CREATOR_DATA.creatorId} / next-campaigns-twitch-chatbot Emitting ${myCreatorTwitchId} / ${getTime}`, + `${CREATOR_DATA.creatorId} / next-campaigns-twitch-chatbot Emitting ${myCreatorTwitchId} / ${timestamp}`, ); socket.broadcast.emit('next-campaigns-twitch-chatbot', { campaignId: myCampaignId, @@ -415,7 +390,7 @@ function callImg(socket: Socket, request: Request): void { writeToDb(myCampaignId, CREATOR_DATA.creatorId, programType); } console.log( - `${CREATOR_DATA.creatorId} / 같은 캠페인 송출 중이어서 재호출 안함 / ${getTime}`, + `${CREATOR_DATA.creatorId} / 같은 캠페인 송출 중이어서 재호출 안함 / ${timestamp}`, ); } } @@ -424,4 +399,4 @@ function callImg(socket: Socket, request: Request): void { init(); } -export default callImg; +export default requestBanner; diff --git a/socket/lib/requestBannerTest.ts b/socket/lib/requestBannerTest.ts new file mode 100644 index 000000000..0fed6f6f7 --- /dev/null +++ b/socket/lib/requestBannerTest.ts @@ -0,0 +1,27 @@ +import socketio, { Socket } from 'socket.io'; +import RequestBanner from './requestBanner copy'; +import { CreatorStatus } from '../@types/shared'; + +const requestMessage: CreatorStatus = { + url: 'http://localhost:3002/banner/onad', + previousBannerName: '', + programType: '', +}; + +const test = new RequestBanner(requestMessage); + +const getCreatorIdAndChatAgreementTest = async () => { + const creatorId = await test.getCreatorIdAndChatAgreement(); + console.log(creatorId.creatorId); +}; + +const getOnCampaignListTest = async () => { + const campaigns = await test.getActiveCampaigns(); + console.log(campaigns); +}; +// const getCreatorDataTest = async () => { +// const creatorId = await test.getCreatorData(); +// console.log(creatorId); +// }; + +getCreatorIdAndChatAgreementTest(); diff --git a/socket/lib/requestBannerV2.ts b/socket/lib/requestBannerV2.ts new file mode 100644 index 000000000..538c469b0 --- /dev/null +++ b/socket/lib/requestBannerV2.ts @@ -0,0 +1,75 @@ +/* eslint-disable no-console */ +/* eslint-disable import/first */ + +import { Socket } from 'socket.io'; +import RequestBanner from './requestBanner copy'; +import { CreatorStatus } from '../@types/shared'; + +const requestBanner = async (socket: Socket, requestMessage: CreatorStatus) => { + const requestModule = new RequestBanner(requestMessage); + const creatorIdAndChatAgreement = await requestModule.getCreatorIdAndChatAgreement(); + const { creatorId } = creatorIdAndChatAgreement; + + let creatorGameId; + if (creatorId) { + creatorGameId = await requestModule.getGameId(); + const bannerInfo = await requestModule.getBanner([creatorId, creatorGameId]); + + const checkOptionType = bannerInfo.campaignId + ? requestModule.campaignObject[bannerInfo.campaignId].optionType + : null; + const campaignName = bannerInfo.campaignId + ? requestModule.campaignObject[bannerInfo.campaignId].campaignName + : null; + const linkName = bannerInfo.campaignId ? bannerInfo.linkToChatBot : null; + const campaignDescription = bannerInfo.campaignId + ? requestModule.campaignObject[bannerInfo.campaignId].campaignDescription + : null; + const descriptionToChat: string | boolean | null = campaignDescription || linkName; + + const { creatorTwitchId } = creatorIdAndChatAgreement; + const { adChatAgreement } = creatorIdAndChatAgreement; + // 트위치 챗봇 동의 및 옵션타입 cpm+cpc인 경우에 챗봇으로 데이터 전송 + const CHATBOT_ALLOWED_CAMPAIGN_OPTION_TYPE = [1, 3]; + if ( + adChatAgreement === 1 && + checkOptionType && + CHATBOT_ALLOWED_CAMPAIGN_OPTION_TYPE.includes(checkOptionType) + ) { + if (creatorTwitchId) { + // 챗봇은 트위치 한정. 트위치 아이디가 없는 경우 에미팅 하지 않도록 추가 + // @by hwasurr 21.01.08 + console.log( + `${creatorId} / next-campaigns-twitch-chatbot Emitting ${creatorTwitchId} / {timestamp}`, + ); + socket.broadcast.emit('next-campaigns-twitch-chatbot', { + campaignId: requestModule.myCampaignId, + creatorId, + creatorTwitchId, + campaignName, + descriptionToChat, + }); + } + } + console.log(bannerInfo.optionType && bannerInfo.campaignName); + if (bannerInfo.optionType && bannerInfo.campaignName) { + requestModule.markTimestamp(requestModule.myCampaignId, creatorId, requestModule.programType); + console.log(bannerInfo.bannerSrc, [bannerInfo.campaignName, creatorId]); + socket.emit('img receive', [bannerInfo.bannerSrc, [bannerInfo.campaignName, creatorId]]); + // to chatbot + } else { + if (requestModule.myCampaignId) { + requestModule.markTimestamp( + requestModule.myCampaignId, + creatorId, + requestModule.programType, + ); + } + console.log( + `${creatorId} / 같은 캠페인 송출 중이어서 재호출 안함 / ${requestModule.timestamp}`, + ); + } + } +}; + +export default requestBanner; diff --git a/socket/models/query.ts b/socket/models/query.ts index 16b2a5492..323b24581 100644 --- a/socket/models/query.ts +++ b/socket/models/query.ts @@ -14,7 +14,7 @@ const query: Query = { OR (marketerInfo.marketerContraction = 1 AND campaign.onOff = 1 AND campaign.optionType = 3) `, categoryCampaign: 'SELECT campaignList FROM categoryCampaign WHERE categoryId = ?', - twitchGameIdForCreator: ` + assignedTwitchGameId: ` SELECT gameId FROM twitchStreamDetail AS tsd WHERE streamId = ( SELECT streamId FROM twitchStream @@ -23,7 +23,7 @@ const query: Query = { ORDER BY startedAt DESC LIMIT 1 ) AND time > DATE_SUB(NOW(), INTERVAL 10 MINUTE) ORDER BY tsd.time DESC LIMIT 1;`, - afreecaGameIdForCreator: ` + assignedAfreecaGameId: ` SELECT broadCategory AS gameId FROM AfreecaBroadDetail AS ABD WHERE broadId = ( SELECT broadId FROM AfreecaBroad JOIN creatorInfo ON afreecaId = userId diff --git a/socket/views/client.ejs b/socket/views/client.ejs index c5f92d389..a947e1b04 100644 --- a/socket/views/client.ejs +++ b/socket/views/client.ejs @@ -60,7 +60,7 @@ const exports = {}; - +