From 2cc68d13bd5b688a829cd4e98dc8f86aaee75f6b Mon Sep 17 00:00:00 2001 From: Matt Isenhower Date: Thu, 8 Dec 2022 19:47:56 -0800 Subject: [PATCH] Rework social post manager to support multiple client types --- app/social/StatusGeneratorManager.mjs | 62 +++++++++---------- app/social/clients/Client.mjs | 16 +++++ app/social/clients/FileWriter.mjs | 28 +++++++++ app/social/{ => clients}/TwitterClient.mjs | 14 +++-- app/social/generators/SalmonRunGearStatus.mjs | 4 +- app/social/generators/StatusGenerator.mjs | 32 +++------- app/social/index.mjs | 32 ++++++++-- 7 files changed, 120 insertions(+), 68 deletions(-) create mode 100644 app/social/clients/Client.mjs create mode 100644 app/social/clients/FileWriter.mjs rename app/social/{ => clients}/TwitterClient.mjs (82%) diff --git a/app/social/StatusGeneratorManager.mjs b/app/social/StatusGeneratorManager.mjs index d5d8f79..c001868 100644 --- a/app/social/StatusGeneratorManager.mjs +++ b/app/social/StatusGeneratorManager.mjs @@ -1,58 +1,54 @@ -import fs from 'fs/promises'; -import mkdirp from 'mkdirp'; +import prefixedConsole from "../common/prefixedConsole.mjs"; import ScreenshotHelper from "../screenshots/ScreenshotHelper.mjs"; import StatusGenerator from "./generators/StatusGenerator.mjs"; -import TwitterClient from "./TwitterClient.mjs"; export default class StatusGeneratorManager { /** @type {StatusGenerator[]} */ generators; - /** @type {TwitterClient} */ - client; + /** @type {Client[]} */ + clients; /** @type {ScreenshotHelper} */ screenshotHelper; - constructor(generators = []) { + console(generator = null, client = null) { + let prefixes = ['Social', generator?.name, client?.name].filter(s => s); + return prefixedConsole(...prefixes); + } + + constructor(generators = [], clients = []) { this.generators = generators; - this.client = new TwitterClient; + this.clients = clients; this.screenshotHelper = new ScreenshotHelper; } - async sendStatuses() { + async shouldPost(generator) { + for (let client of this.clients) { + if (await generator.shouldPost(client)) { + return true; + } + } + + return false; + } + + async sendStatuses(force = false) { for (let generator of this.generators) { - if (!(await generator.shouldPost())) { + if (!force && !(await this.shouldPost(generator))) { + this.console(generator).info('No status to post, skipping'); + continue; } - await generator.sendStatus(this.screenshotHelper, this.client); - } - - await this.screenshotHelper.close(); - } - - async testStatuses() { - for (let generator of this.generators) { - let dir = 'temp'; - await mkdirp(dir); - let status = await generator.getStatus(this.screenshotHelper); - let imgFilename = `temp/${generator.key}.png`; - await fs.writeFile(imgFilename, status.media[0].file); - - let text = [ - 'Status:', - status.status, - '', - 'Alt text:', - status.media[0].altText, - ].join('\n'); - - let textFilename = `temp/${generator.key}.txt`; - await fs.writeFile(textFilename, text); + for (let client of this.clients) { + this.console(generator, client).info('Posting...'); + await client.send(status, generator); + await generator.updatelastPostCache(client); + } } await this.screenshotHelper.close(); diff --git a/app/social/clients/Client.mjs b/app/social/clients/Client.mjs new file mode 100644 index 0000000..66ffc03 --- /dev/null +++ b/app/social/clients/Client.mjs @@ -0,0 +1,16 @@ +import Status from "../Status.mjs"; +import StatusGenerator from "../generators/StatusGenerator.mjs"; + +export default class Client +{ + key; + name; + + /** + * @param {Status} status + * @param {StatusGenerator} generator + */ + async send(status, generator) { + // + } +} diff --git a/app/social/clients/FileWriter.mjs b/app/social/clients/FileWriter.mjs new file mode 100644 index 0000000..653dc1c --- /dev/null +++ b/app/social/clients/FileWriter.mjs @@ -0,0 +1,28 @@ +import fs from 'fs/promises'; +import mkdirp from 'mkdirp'; +import Client from "./Client.mjs"; + +export default class FileWriter extends Client { + key = 'file'; + name = 'FileWriter'; + + dir = 'temp'; + + async send(status, generator) { + await mkdirp(this.dir); + + let imgFilename = `${this.dir}/${generator.key}.png`; + await fs.writeFile(imgFilename, status.media[0].file); + + let text = [ + 'Status:', + status.status, + '', + 'Alt text:', + status.media[0].altText, + ].join('\n'); + + let textFilename = `${this.dir}/${generator.key}.txt`; + await fs.writeFile(textFilename, text); + } +} diff --git a/app/social/TwitterClient.mjs b/app/social/clients/TwitterClient.mjs similarity index 82% rename from app/social/TwitterClient.mjs rename to app/social/clients/TwitterClient.mjs index e87b270..36ed682 100644 --- a/app/social/TwitterClient.mjs +++ b/app/social/clients/TwitterClient.mjs @@ -1,12 +1,17 @@ import { TwitterApi } from "twitter-api-v2"; -import Status from "./Status.mjs"; +import Client from "./Client.mjs"; -export default class TwitterClient +export default class TwitterClient extends Client { + key = 'twitter'; + name = 'Twitter'; + /** @var {TwitterApi} */ #api; constructor() { + super(); + this.#api = new TwitterApi({ appKey: process.env.TWITTER_CONSUMER_KEY, appSecret: process.env.TWITTER_CONSUMER_SECRET, @@ -15,10 +20,7 @@ export default class TwitterClient }); } - /** - * @param {Status} status - */ - async send(status) { + async send(status, generator) { // Upload images let mediaIds = await Promise.all( status.media.map(async m => { diff --git a/app/social/generators/SalmonRunGearStatus.mjs b/app/social/generators/SalmonRunGearStatus.mjs index 988246c..92654c9 100644 --- a/app/social/generators/SalmonRunGearStatus.mjs +++ b/app/social/generators/SalmonRunGearStatus.mjs @@ -19,10 +19,10 @@ export default class SalmonRunGearStatus extends StatusGenerator return gear?.__splatoon3ink_id; } - async shouldPost() { + async shouldPost(client) { let gear = await this.getGear(); - let cachedId = await this.lastPostCache.getData(); + let cachedId = await this.lastPostCache(client).getData(); return gear?.__splatoon3ink_id && gear?.__splatoon3ink_id !== cachedId; } diff --git a/app/social/generators/StatusGenerator.mjs b/app/social/generators/StatusGenerator.mjs index 45c78e6..8f87a5a 100644 --- a/app/social/generators/StatusGenerator.mjs +++ b/app/social/generators/StatusGenerator.mjs @@ -3,7 +3,7 @@ import { createPinia, setActivePinia } from 'pinia'; import { useCoopDataStore, useFestivalsDataStore, useGearDataStore, useSchedulesDataStore } from '../../../src/stores/data.mjs'; import prefixedConsole from '../../common/prefixedConsole.mjs'; import Status from '../Status.mjs'; -import TwitterClient from '../TwitterClient.mjs'; +import Client from '../clients/Client.mjs'; import ScreenshotHelper from '../../screenshots/ScreenshotHelper.mjs'; import { getTopOfCurrentHour } from '../../common/util.mjs'; import { useTimeStore } from '../../../src/stores/time.mjs'; @@ -16,15 +16,13 @@ export default class StatusGenerator /** @type {Console} */ get console() { - this._console ??= prefixedConsole('Twitter', this.name); + this._console ??= prefixedConsole('Social', this.name); return this._console; } - get lastPostCache() { - this._lastPostCache ??= new ValueCache(`twitter.${this.key}`); - - return this._lastPostCache; + lastPostCache(client) { + return new ValueCache(`social.${client.key}.${this.key}`); } async preparePinia() { @@ -50,33 +48,23 @@ export default class StatusGenerator return useTimeStore().now; } - async shouldPost() { + async shouldPost(client) { let currentTime = await this.getDataTime(); - let cachedTime = await this.lastPostCache.getData(); + let cachedTime = await this.lastPostCache(client).getData(); return currentTime && (!cachedTime || (currentTime > cachedTime)); } - async updatelastPostCache() { + async updatelastPostCache(client) { let currentTime = await this.getDataTime(); - await this.lastPostCache.setData(currentTime); - } - - /** - * @param {ScreenshotHelper} screenshotHelper - * @param {TwitterClient} twitterClient - */ - async sendStatus(screenshotHelper, twitterClient) { - let status = await this.getStatus(screenshotHelper); - - await twitterClient.send(status); - - await this.updatelastPostCache(); + await this.lastPostCache(client).setData(currentTime); } /** @param {ScreenshotHelper} screenshotHelper */ async getStatus(screenshotHelper) { + this.console.log('Generating status...'); + const status = new Status; status.status = await this._getStatus(); diff --git a/app/social/index.mjs b/app/social/index.mjs index 9356e8a..16eba6f 100644 --- a/app/social/index.mjs +++ b/app/social/index.mjs @@ -1,3 +1,5 @@ +import FileWriter from "./clients/FileWriter.mjs"; +import TwitterClient from "./clients/TwitterClient.mjs"; import DailyDropGearStatus from "./generators/DailyDropGearStatus.mjs"; import RegularGearStatus from "./generators/RegularGearStatus.mjs"; import SalmonRunGearStatus from "./generators/SalmonRunGearStatus.mjs"; @@ -6,21 +8,41 @@ import SchedulesStatus from "./generators/SchedulesStatus.mjs"; import SplatfestResultsStatus from "./generators/SplatfestResultsStatus.mjs"; import StatusGeneratorManager from "./StatusGeneratorManager.mjs" -export function defaultTwitterManager() { - return new StatusGeneratorManager([ +function defaultStatusGenerators() { + return [ new SchedulesStatus, new DailyDropGearStatus, new RegularGearStatus, new SalmonRunStatus, new SalmonRunGearStatus, new SplatfestResultsStatus, - ]); + ]; +} + +function defaultClients() { + return [ + new TwitterClient, + ]; +} + +export function defaultStatusGeneratorManager() { + return new StatusGeneratorManager( + defaultStatusGenerators(), + defaultClients(), + ); +} + +export function testStatusGeneratorManager() { + return new StatusGeneratorManager( + defaultStatusGenerators(), + [new FileWriter], + ); } export function sendStatuses() { - return defaultTwitterManager().sendStatuses(); + return defaultStatusGeneratorManager().sendStatuses(); } export function testStatuses() { - return defaultTwitterManager().testStatuses(); + return testStatusGeneratorManager().sendStatuses(true); }