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 new file mode 100644 index 000000000..8ca25ef1f --- /dev/null +++ b/socket/@types/bannerRequest.ts @@ -0,0 +1,43 @@ +export interface CampaignIdOptionType { + [_: string]: [number, string, string]; +} + +export interface Campaign { + 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 BanPausedCampaign { + [key: string]: string[]; +} + +export interface CampaignObjectValue { + campaignName: string; + optionType: number; + campaignDescription: string; +} +export interface CampaignObject { + [key: string]: CampaignObjectValue; +} 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/@types/shared.ts b/socket/@types/shared.ts new file mode 100644 index 000000000..cfe317b6a --- /dev/null +++ b/socket/@types/shared.ts @@ -0,0 +1,9 @@ +export interface CreatorStatus { + url: string; + previousBannerName: string; + programType: string; +} + +export interface Banner { + [key: string]: string; +} diff --git a/socket/app.ts b/socket/app.ts index 6d3e578be..7cf9857d7 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 requestBanner from './lib/requestBanner'; +import { CreatorStatus } from './@types/shared'; +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(){ +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: CreatorStatus = { + 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', async (msg: [string, number, string]) => { + const clientUrl = msg[0]; + const HISTORY = msg[1]; + const programType = msg[2]; + + requestMessage.url = clientUrl; + + if (process.env.NODE_ENV === 'development') { + console.log('NEW CLIENT'); + socket.emit('host pass', SOCKET_HOST); + + await requestBanner(socket, requestMessage); + return; + } + 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); + await requestBanner(socket, requestMessage); + } + }); + + // *********************************************************** + // 접속 종료시 발생되는 이벤트인 "disconnect" 이벤트 핸들러 + socket.on('disconnect', () => { + // 접속종료시 + delete socketInfo[SOCKET_ID]; // socketsInfo에서 접속종료한 clientID 삭제 + SOCKET_ID = ''; + }); + + // *********************************************************** + // 송출될 광고 재요청 이벤트 : "re-render" 이벤트 핸들러 + socket.on('re-render', async (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; + await requestBanner(socket, requestMessage); + }); + + // *********************************************************** + // + socket.on('pageOn', async (msg: [string, string]) => { + const clientUrl = msg[0]; + const programType = msg[1]; + requestMessage.url = clientUrl; + requestMessage.previousBannerName = ''; + requestMessage.programType = programType; + await requestBanner(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/lib/bannerSelection.ts b/socket/lib/bannerSelection.ts new file mode 100644 index 000000000..d8ac19aea --- /dev/null +++ b/socket/lib/bannerSelection.ts @@ -0,0 +1,381 @@ +/* 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, + CampaignObject, +} from '../@types/bannerRequest'; +import { CreatorStatus, Banner } from '../@types/shared'; +import query from '../models/query'; + +class BannerSelection { + fullUrl: string; + cutUrl: string; + previousBannerName: string; + programType: string; + timestamp: string; + campaignObject: CampaignObject; + creatorId: string; + banOrPausedCampaigns: BanPausedCampaign; + constructor(requestMessage: CreatorStatus) { + this.fullUrl = requestMessage.url; + this.cutUrl = `/${this.fullUrl.split('/')[4]}`; + this.previousBannerName = requestMessage.previousBannerName; + this.programType = requestMessage.programType; + this.timestamp = new Date().toLocaleString(); + this.campaignObject = {}; + this.creatorId = ''; + this.banOrPausedCampaigns = {}; + } + + 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]); + } + resolve(false); + }) + .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) { + const creatorGameId = twitchGameIdResult[0].gameId; + return creatorGameId; + } + + // 트위치 방송중이 아니며, 아프리카티비 방송 중 -> 아프리카 tv 카테고리 + // 즉, 동시 송출 중이라면, twitch 카테고리를 우선시 함. @by hwasurr + if (!(twitchGameIdResult.length > 0) && afreecaGameIdResult.length > 0) { + const creatorGameId = afreecaGameIdResult[0].gameId; + return 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 selectedActiveCampaign = filteredCampaigns.length !== 0 ? filteredCampaigns[0] : ''; + return selectedActiveCampaign; + }; + + getCampaign = async ([creatorId, gameId]: string[]): Promise => { + console.log( + `-----------------------Id : ${creatorId} / ${this.timestamp}---------------------------`, + ); + let selectedActiveCampaign = ''; + const [creatorCampaigns, activeCampaigns] = await Promise.all([ + this.getCreatorCampaignList(), + this.getActiveCampaigns(), + ]); + this.banOrPausedCampaigns = await this.getBanOrPausedCampaignsByCreator(); + // ********************************************************* + // 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인 -> ON 상태 필터링 + const activeCreatorCampaigns = creatorCampaigns.filter((campaignId: string) => + activeCampaigns.includes(campaignId), + ); + + // 현재 ON상태인 크리에이터 개인에게 할당된(크리에이터 에게 송출) 캠페인 목록이 있는 경우 + if (activeCreatorCampaigns.length !== 0) { + const filteredCampaigns = this.filterCampaign( + activeCreatorCampaigns, + this.banOrPausedCampaigns, + ); + if (filteredCampaigns) { + selectedActiveCampaign = filteredCampaigns[this.getRandomInt(filteredCampaigns.length)]; + console.log('방송인 에게만 송출될 캠페인 : ', selectedActiveCampaign); + } + } 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, + this.banOrPausedCampaigns, + ); + selectedActiveCampaign = filteredCampaigns[this.getRandomInt(filteredCampaigns.length)]; + } + return selectedActiveCampaign; + }; + + getBanner = async (selectedActiveCampaign: string): Promise => { + // RETRUN 섹션 + const OPTION_TYPE_LIVE_BANNER_CAMPAIGN = 1; // 생방송 라이브 배너광고 캠페인의 경우 + const OPTION_TYPE_CPS_CAMPAIGN = 3; // 판매형광고(CPS) 캠페인의 경우 + const campaignToStream = { + bannerSrc: '', + campaignId: '', + linkToChatBot: '', + }; + let linkToChatBot; + let onadDefaultBanner = ''; + if ( + selectedActiveCampaign && + this.campaignObject[selectedActiveCampaign].optionType === OPTION_TYPE_LIVE_BANNER_CAMPAIGN + ) { + // 송출될 캠페인의 link 를 가져와 트위치 챗봇에 챗봇광고 이벤트를 에밋하기 위한 정보를 가져온다. + console.log( + `${this.creatorId} / 광고될 캠페인은 ${selectedActiveCampaign} / ${this.timestamp}`, + ); + linkToChatBot = await this.getLinkName(selectedActiveCampaign); + } else if ( + selectedActiveCampaign && + this.campaignObject[selectedActiveCampaign].optionType === OPTION_TYPE_CPS_CAMPAIGN + ) { + console.log( + `${this.creatorId} / 광고될 캠페인은 ${selectedActiveCampaign} / ${this.timestamp}`, + ); + linkToChatBot = await this.getMerchandiseSiteUrl(selectedActiveCampaign); + } else { + // 송출할 배너가 없어서 기본 배너 검색 + onadDefaultBanner = await this.getDefaultBanner(this.banOrPausedCampaigns); + if (onadDefaultBanner) { + // 기본 배너 송출 + console.log(`${this.creatorId} 기본 배너 송출 / ${this.timestamp}`); + linkToChatBot = await this.getLinkName(onadDefaultBanner); + } else { + console.log(`${this.creatorId} / 켜져있는 광고 없음 / ${this.timestamp}`); + return campaignToStream; + } + } + + if ( + this.previousBannerName && + selectedActiveCampaign === this.previousBannerName.split(',')[0] + ) { + campaignToStream.campaignId = selectedActiveCampaign; + campaignToStream.linkToChatBot = linkToChatBot; + return campaignToStream; + } + const bannerSrc = await this.getBannerSrc(selectedActiveCampaign || onadDefaultBanner); + + campaignToStream.bannerSrc = bannerSrc; + campaignToStream.campaignId = selectedActiveCampaign || onadDefaultBanner; + campaignToStream.linkToChatBot = linkToChatBot; + return campaignToStream; + }; +} + +export default BannerSelection; diff --git a/socket/public/client.ts b/socket/lib/client.ts similarity index 75% rename from socket/public/client.ts rename to socket/lib/client.ts index 654d9cb80..c1a48e0a1 100644 --- a/socket/public/client.ts +++ b/socket/lib/client.ts @@ -16,7 +16,6 @@ function isVideo(src: string): boolean { hiddenEventHandler(socket, THIS_URL, programType); imageClicker(socket, THIS_URL, programType); - setInterval(() => { $('.default').toggleClass('top'); setTimeout(() => { @@ -24,7 +23,6 @@ setInterval(() => { }, 10000); }, 600000); - let socketHost = ''; socket.emit('new client', [THIS_URL, history, programType]); @@ -45,27 +43,34 @@ socket.on('duplicate', (DESTINATION_URL: string) => { }); socket.on('img receive', (msg: string[]) => { - if ($('.img-area').find('.banner-area').length === 1 && isVideo(msg[0])) { // 기존 배너 있고 mp4일 때 - $('.banner-area').fadeOut(1000, () => { - $('.img-area').empty().append(` + if ($('.img-area').find('.banner-area').length === 1 && isVideo(msg[0])) { + // 기존 배너 있고 mp4일 때 + $('.banner-area') + .fadeOut(1000, () => { + $('.img-area').empty().append(` `); - setTimeout(() => { - $('.marquee').fadeOut(1000); - }, 9000); - }).fadeIn(1000); - } else if ($('.img-area').find('.banner-area').length === 1 && isVideo(msg[0]) === false) { // 기존 배너 있고 이미지일 때 - $('.banner-area').fadeOut(1000, () => { - $('.img-area').empty().append(` + setTimeout(() => { + $('.marquee').fadeOut(1000); + }, 9000); + }) + .fadeIn(1000); + } else if ($('.img-area').find('.banner-area').length === 1 && isVideo(msg[0]) === false) { + // 기존 배너 있고 이미지일 때 + $('.banner-area') + .fadeOut(1000, () => { + $('.img-area').empty().append(` `); - setTimeout(() => { - $('.marquee').fadeOut(1000); - }, 9000); - }).fadeIn(1000); - } else if (isVideo(msg[0])) { // 기존 배너없고 mp4일 때 + setTimeout(() => { + $('.marquee').fadeOut(1000); + }, 9000); + }) + .fadeIn(1000); + } else if (isVideo(msg[0])) { + // 기존 배너없고 mp4일 때 $('.img-area').empty().append(`