Last active
August 22, 2020 02:50
-
-
Save namazu510/b1a6929e16ab8439290270a4dd1e86bb to your computer and use it in GitHub Desktop.
showroom-morning-automation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import puppeteer from 'puppeteer' | |
import { sub, format } from 'date-fns' | |
const SR_BASE_URL = 'https://www.showroom-live.com' | |
function timeout(duration: number) { | |
// eslint-disable-next-line promise/avoid-new | |
return new Promise(resolve => setTimeout(resolve, duration)) | |
} | |
let browser: puppeteer.Browser | |
async function getBrowser() { | |
if (browser) { | |
return Promise.resolve(browser) | |
} | |
browser = await puppeteer.launch({ | |
headless: false, | |
// headlessだとUAとか面倒くさい, 普段から入ってるchromeを利用. | |
executablePath: '/opt/google/chrome/google-chrome', | |
// 都度ログインが面倒なのでログイン済みuserDataができる場所 | |
userDataDir: '/home/namazu/.config/puppeteer-chrome' | |
}) | |
return browser | |
} | |
async function getOnliveRoomUrls(isOfficial = true): Promise<string[]> { | |
const browser = await getBrowser() | |
const page = await browser.newPage() | |
await page.goto(`${SR_BASE_URL}/onlive`, { | |
waitUntil: 'networkidle2' | |
}) | |
const onlives: { | |
onlives: { lives: { official_lv: 0 | 1; room_url_key: string } }[] | |
} = await page.evaluate(async () => { | |
const r = await fetch( | |
`/api/live/onlives?skip_serial_code_live=1&_=${Date.parse(new Date().toString())}` | |
) | |
const j = await r.json() | |
return j | |
}) | |
const onliveUrls = onlives.onlives | |
.flatMap(onlives => onlives.lives) | |
.filter(l => (isOfficial ? l.official_lv === 1 : l.official_lv === 0)) | |
.map(l => `${SR_BASE_URL}/${l.room_url_key}`) | |
await page.close() | |
return onliveUrls | |
} | |
async function getStar(openRoomNum: number) { | |
const urls = await getOnliveRoomUrls() | |
// 部屋数 * 30sec + 10sec(ズレ許容分) | |
const openDurationMs = openRoomNum * (30 + 10) * 1000 | |
console.log(format(new Date(), 'MM/dd HH:mm:ss'), '星を取得します', `${openRoomNum}部屋分`) | |
await Promise.all( | |
urls.slice(0, openRoomNum).map(url => | |
(async () => { | |
const browser = await getBrowser() | |
const page = await browser.newPage() | |
console.log(format(new Date(), 'MM/dd HH:mm:ss'), `部屋を開きます`, url) | |
await page.goto(url, { | |
waitUntil: 'domcontentloaded' | |
}) | |
// 取得タイミングで閉じてもいいけど検出が手間なので最後まで待つ. | |
await timeout(openDurationMs) | |
await page.close() | |
})() | |
) | |
) | |
} | |
// --- | |
// 挙動設定領域ここから | |
// --- | |
// モード, trueならライブ開始時刻から逆算して星集め・捨てを実施, falseなら指定時刻に星を指定数取得する. | |
const AUTO = false | |
// この時間に開始する枠で3周ができるように星を捨てる. | |
const AUTO_LIVE_START_DATETIME: null | Date = AUTO ? new Date(2020, 7, 22, 9, 13) : null | |
// trueなら上記時間に間に合うように星を集める. | |
const AUTO_DO_GATHER = true | |
// 星捨ての配信開始前時間(20分枠糖の短い枠なら可能な限り詰める, そうでなければ45や30でよい) | |
const AUTO_DROP_DIFF = 53 | |
// マニュアル起動用, 指定時間に星を取得する | |
const MANUAL_GET_STAR_DATETIME: null | Date = AUTO ? null : new Date(2020, 7, 22, 9, 13) | |
// 何個集めるか, 2周タイテでは99で1溢れるのを防ぐために9にしておかないといけない. | |
const MANUAL_GET_STAR_SET = 9 | |
// --- | |
// 挙動設定領域ここまで | |
// --- | |
function main() { | |
if (AUTO) { | |
if (AUTO_LIVE_START_DATETIME === null) { | |
throw new Error('AUTOなら配信開始時刻を入れて') | |
} | |
automation(AUTO_LIVE_START_DATETIME) | |
} else { | |
if (MANUAL_GET_STAR_DATETIME === null) { | |
throw new Error('MANUALなら取得時刻を入れて') | |
} | |
manual(MANUAL_GET_STAR_DATETIME) | |
} | |
} | |
main() | |
async function manual(getStartDatetime: Date) { | |
console.log('星の取得時間 =>', format(getStartDatetime, 'MM/dd HH:mm:ss')) | |
console.log(`各星を${Math.max(99, MANUAL_GET_STAR_SET * 10)}個ずつ取得します`) | |
let duration = getStartDatetime.getTime() - new Date().getTime() | |
// さらに33秒前倒し(タブを開く時間+取得までのカウント時間を考慮する) | |
duration = duration - 33 * 1000 | |
if (duration < 0) { | |
throw new Error('取得時間を超過している') | |
} | |
setTimeout(() => { | |
void getStar(MANUAL_GET_STAR_SET) | |
}, duration) | |
} | |
async function automation(liveStartDatetime: Date) { | |
console.log('配信開始 =>', format(liveStartDatetime, 'MM/dd HH:mm:ss')) | |
const dropTime = sub(liveStartDatetime, { | |
minutes: AUTO_DROP_DIFF | |
}) | |
console.log('星捨て時 =>', format(dropTime, 'MM/dd HH:mm:ss')) | |
let gatherTime: Date | null = null | |
if (AUTO_DO_GATHER) { | |
gatherTime = sub(dropTime, { | |
minutes: 60 | |
}) | |
console.log('星集め時 =>', format(gatherTime, 'MM/dd HH:mm:ss')) | |
} | |
if (gatherTime !== null) { | |
let duration = gatherTime.getTime() - new Date().getTime() | |
// さらに33秒前倒し(タブを開く時間+取得までのカウント時間を考慮する) | |
duration = duration - 33 * 1000 | |
if (duration < 0) { | |
throw new Error('星集め期限を超過している') | |
} | |
setTimeout(() => { | |
// 配信が落ちることを考慮し13開く | |
void getStar(13) | |
}, duration) | |
} | |
if (dropTime !== null) { | |
let duration = dropTime.getTime() - new Date().getTime() | |
// さらに31秒前倒し(タブを開く時間+取得までのカウント時間を考慮する) | |
duration = duration - 31 * 1000 | |
if (duration < 0) { | |
throw new Error('星捨て期限を超過している') | |
} | |
setTimeout(() => { | |
void getStar(1) | |
}, duration) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment