diff --git a/src/css/screenshots.scss b/src/css/screenshots.scss index 8c87fd6..4e8991e 100644 --- a/src/css/screenshots.scss +++ b/src/css/screenshots.scss @@ -25,4 +25,9 @@ padding: 0.5rem 0.75rem; background: rgba($black-bis, 0.5); } + + .featured-merchandise { + transform: scale(2) rotate(-1.4deg); + min-width: 173px; + } } diff --git a/src/js/components/screenshots/splatnetgear/SplatNetGear.vue b/src/js/components/screenshots/splatnetgear/SplatNetGear.vue new file mode 100644 index 0000000..3fd9cfa --- /dev/null +++ b/src/js/components/screenshots/splatnetgear/SplatNetGear.vue @@ -0,0 +1,63 @@ + + + diff --git a/src/js/screenshots.js b/src/js/screenshots.js index 7d463f1..697f8a9 100644 --- a/src/js/screenshots.js +++ b/src/js/screenshots.js @@ -8,8 +8,10 @@ import VueRouter from 'vue-router'; Vue.use(VueRouter); import Schedules from './components/screenshots/schedules/Schedules.vue'; +import SplatNetGear from './components/screenshots/splatnetgear/SplatNetGear.vue'; const routes = [ { path: '/schedules/:startTime', component: Schedules }, + { path: '/splatNetGear/:startTime/:endTime', component: SplatNetGear }, ]; const router = new VueRouter({ routes }); diff --git a/src/updater/screenshots.js b/src/updater/screenshots.js index 72828a7..ceab46b 100644 --- a/src/updater/screenshots.js +++ b/src/updater/screenshots.js @@ -22,7 +22,8 @@ async function captureScreenshot(options) { // Create a new page and set the viewport const page = await browser.newPage(); - page.setViewport(viewport); + let thisViewport = Object.assign({}, viewport, options.viewport); + page.setViewport(thisViewport); // Navigate to the URL await page.goto(options.url, { @@ -46,6 +47,14 @@ function captureScheduleScreenshot(startTime) { return captureScreenshot({ url }); } +function captureGearScreenshot(startTime, endTime) { + let url = new URL(htmlUrl); + url.hash = `/splatNetGear/${startTime}/${endTime}`; + + return captureScreenshot({ url, viewport: { height: 660 } }); +} + module.exports = { captureScheduleScreenshot, + captureGearScreenshot, } diff --git a/src/updater/twitter.js b/src/updater/twitter.js index f87ac86..d8a9d15 100644 --- a/src/updater/twitter.js +++ b/src/updater/twitter.js @@ -52,6 +52,28 @@ function updateLastTweetTime(key, time) { fs.writeFileSync(lastTweetTimesPath, JSON.stringify(lastTweetTimes)); } +function postMediaTweet(imageData, statusText) { + return new Promise((resolve, reject) => { + // Upload the image + client.post('media/upload', { media: imageData }, (error, media, response) => { + if (error) + return reject(error); + + let status = { + status: statusText, + media_ids: media.media_id_string, + }; + + client.post('statuses/update', status, (error, tweet, response) => { + if (error) + return reject(error); + + resolve(tweet); + }); + }); + }); +} + /** * Twitter posts */ @@ -63,6 +85,7 @@ async function maybePostTweets() { } await maybePostScheduleTweet(); + await maybePostGearTweet(); } /** @@ -103,33 +126,78 @@ async function maybePostScheduleTweet() { updateLastTweetTime(key, time); } -function postScheduleTweet(startTime) { - return new Promise(async (resolve, reject) => { - // Generate the image - let imageData = await screenshots.captureScheduleScreenshot(startTime); +async function postScheduleTweet(startTime) { + // Generate the image + let imageData = await screenshots.captureScheduleScreenshot(startTime); - // Upload the image - client.post('media/upload', { media: imageData }, (error, media, response) => { - if (error) - return reject(error); + // Post the tweet + return await postMediaTweet(imageData, 'Current Splatoon 2 map rotation, via https://splatoon2.ink #splatoon2'); +} - let status = { - status: 'Current Splatoon 2 map rotation, via https://splatoon2.ink #splatoon2', - media_ids: media.media_id_string, - }; +/** + * Gear tweets + */ - client.post('statuses/update', status, (error, tweet, response) => { - if (error) - return reject(error); +function getMerchandises() { + let merchandisesPath = `${dataPath}/merchandises.json`; + if (!fs.existsSync(merchandisesPath)) { + console.warn('Twitter: Gear: merchandises.json does not exist'); + return []; + } + return JSON.parse(fs.readFileSync(merchandisesPath)).merchandises; +} - resolve(tweet); - }); - }); - }); +async function maybePostGearTweet() { + const key = 'gear'; + + // What time are we posting the schedule for? + let time = getTopOfCurrentHour(); + + // Make sure we have merchandises + let merchandises = getMerchandises(); + if (!merchandises.length) { + console.info('Twitter: Gear: No merchandises'); + return; + } + + // This one is a little different: we only have end_times for merchandise items, so we + // need to track the latest end_time we've posted + let endTimes = merchandises.map(m => m.end_time); + let lastEndTime = Math.max(...endTimes); + + // Have we already posted for this time? + if (!shouldTweet(key, lastEndTime)) { + console.info('Twitter: Gear: Already posted for this hour', lastEndTime); + return; + } + + // Everything looks good, let's post a tweet + await postGearTweet(time, lastEndTime); + console.info('Twitter: Gear: Posted latest SplatNet gear'); + + // Update the last tweet time + updateLastTweetTime(key, lastEndTime); +} + +async function postGearTweet(startTime, endTime) { + // Generate the image + let imageData = await screenshots.captureGearScreenshot(startTime, endTime); + + // Generate the text + let gearText; + let merchandise = getMerchandises().find(g => g.end_time == endTime); + if (!merchandise) + return; + gearText = `Up now on SplatNet: ${merchandise.gear.name} with ${merchandise.skill.name}, via https://splatoon2.ink #splatoon2 #splatnet2`; + + // Post the tweet + return await postMediaTweet(imageData, gearText); } module.exports = { maybePostTweets, + postScheduleTweet, + postGearTweet, } require('make-runnable/custom')({ printOutputFrame: false });