mirror of
https://github.com/misenhower/splatoon3.ink.git
synced 2026-04-26 07:49:22 -05:00
Add basic Twitter functionality
This commit is contained in:
parent
4fac5b67cc
commit
7a6677c9f6
5
.env.example
Normal file
5
.env.example
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# Twitter API parameters
|
||||
TWITTER_CONSUMER_KEY=
|
||||
TWITTER_CONSUMER_SECRET=
|
||||
TWITTER_ACCESS_TOKEN_KEY=
|
||||
TWITTER_ACCESS_TOKEN_SECRET=
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -14,6 +14,7 @@ dist-ssr
|
|||
coverage
|
||||
*.local
|
||||
|
||||
.env
|
||||
docker-compose.override.yml
|
||||
|
||||
/cypress/videos/
|
||||
|
|
|
|||
17
app/index.mjs
Normal file
17
app/index.mjs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import dotenv from 'dotenv';
|
||||
import { sendTweets } from './twitter/index.mjs';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const actions = {
|
||||
twitter: sendTweets,
|
||||
}
|
||||
|
||||
const command = process.argv[2];
|
||||
const action = actions[command];
|
||||
if (action) {
|
||||
action();
|
||||
} else {
|
||||
console.error(`Unrecognized command: ${command}`);
|
||||
}
|
||||
|
||||
5
app/twitter/Media.mjs
Normal file
5
app/twitter/Media.mjs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export default class Media
|
||||
{
|
||||
file = null;
|
||||
type = 'image/png';
|
||||
}
|
||||
5
app/twitter/Tweet.mjs
Normal file
5
app/twitter/Tweet.mjs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
export default class Tweet
|
||||
{
|
||||
status = null;
|
||||
media = [];
|
||||
}
|
||||
30
app/twitter/TwitterClient.mjs
Normal file
30
app/twitter/TwitterClient.mjs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { TwitterApi } from "twitter-api-v2";
|
||||
import Tweet from "./Tweet.mjs";
|
||||
|
||||
export default class TwitterClient
|
||||
{
|
||||
/** @var {TwitterApi} */
|
||||
#api;
|
||||
|
||||
constructor() {
|
||||
this.#api = new TwitterApi({
|
||||
appKey: process.env.TWITTER_CONSUMER_KEY,
|
||||
appSecret: process.env.TWITTER_CONSUMER_SECRET,
|
||||
accessToken: process.env.TWITTER_ACCESS_TOKEN_KEY,
|
||||
accessSecret: process.env.TWITTER_ACCESS_TOKEN_SECRET,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Tweet} tweet
|
||||
*/
|
||||
async send(tweet) {
|
||||
// Upload images
|
||||
let mediaIds = await Promise.all(
|
||||
tweet.media.map(m => this.#api.v1.uploadMedia(m.file, { mimeType: m.type}))
|
||||
);
|
||||
|
||||
// Send tweet
|
||||
await this.#api.v1.tweet(tweet.status, { media_ids: mediaIds });
|
||||
}
|
||||
}
|
||||
28
app/twitter/TwitterManager.mjs
Normal file
28
app/twitter/TwitterManager.mjs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import TweetGenerator from "./generators/TweetGenerator.mjs";
|
||||
import TwitterClient from "./TwitterClient.mjs";
|
||||
|
||||
export default class TwitterManager
|
||||
{
|
||||
/** @var {TweetGenerator[]} */
|
||||
generators;
|
||||
|
||||
/** @var {TwitterClient} */
|
||||
client;
|
||||
|
||||
constructor(generators = []) {
|
||||
this.generators = generators;
|
||||
this.client = new TwitterClient;
|
||||
}
|
||||
|
||||
async sendTweets() {
|
||||
for (let generator of this.generators) {
|
||||
if (!(await generator.shouldTweet())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let tweet = await generator.getTweet();
|
||||
|
||||
await this.client.send(tweet);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
app/twitter/generators/CountdownTweet.mjs
Normal file
34
app/twitter/generators/CountdownTweet.mjs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import TweetGenerator from "./TweetGenerator.mjs";
|
||||
import { captureScreenshot } from "../../screenshots/captureScreenshot.mjs";
|
||||
import Media from "../Media.mjs";
|
||||
|
||||
const releaseDate = new Date('2022-09-09');
|
||||
|
||||
export default class CountdownTweet extends TweetGenerator
|
||||
{
|
||||
shouldTweet() {
|
||||
const now = new Date;
|
||||
const cutoff = new Date('2022-09-10');
|
||||
|
||||
return now < cutoff;
|
||||
}
|
||||
|
||||
_getStatus() {
|
||||
const now = new Date;
|
||||
const ms = releaseDate - now;
|
||||
let days = Math.max(0, Math.ceil(ms / 1000 / 60 / 60 / 24));
|
||||
|
||||
switch (days) {
|
||||
case 0: return 'Splatoon 3 is NOW RELEASED! https://splatoon3.ink #Splatoon3';
|
||||
case 1: return 'Splatoon 3 releases in 1 DAY! https://splatoon3.ink #Splatoon3';
|
||||
default: return `Splatoon 3 releases in ${days} days! https://splatoon3.ink #Splatoon3`;
|
||||
}
|
||||
}
|
||||
|
||||
async _getMedia() {
|
||||
let media = new Media;
|
||||
media.file = await captureScreenshot('countdown');
|
||||
|
||||
return media;
|
||||
}
|
||||
}
|
||||
29
app/twitter/generators/TweetGenerator.mjs
Normal file
29
app/twitter/generators/TweetGenerator.mjs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import Tweet from '../Tweet.mjs';
|
||||
|
||||
export default class TweetGenerator
|
||||
{
|
||||
async shouldTweet() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async getTweet() {
|
||||
const tweet = new Tweet;
|
||||
tweet.status = await this._getStatus();
|
||||
|
||||
let media = await this._getMedia();
|
||||
if (media && !Array.isArray(media)) {
|
||||
media = [media];
|
||||
}
|
||||
tweet.media = media;
|
||||
|
||||
return tweet;
|
||||
}
|
||||
|
||||
async _getStatus () {
|
||||
//
|
||||
}
|
||||
|
||||
async _getMedia() {
|
||||
//
|
||||
}
|
||||
}
|
||||
10
app/twitter/index.mjs
Normal file
10
app/twitter/index.mjs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import CountdownTweet from "./generators/CountdownTweet.mjs";
|
||||
import TwitterManager from "./TwitterManager.mjs"
|
||||
|
||||
export function sendTweets() {
|
||||
const manager = new TwitterManager([
|
||||
new CountdownTweet,
|
||||
]);
|
||||
|
||||
return manager.sendTweets();
|
||||
}
|
||||
25
package-lock.json
generated
25
package-lock.json
generated
|
|
@ -8,9 +8,11 @@
|
|||
"name": "splatoon3.ink",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.0.2",
|
||||
"ecstatic": "^4.1.4",
|
||||
"pinia": "^2.0.13",
|
||||
"puppeteer": "^17.0.0",
|
||||
"twitter-api-v2": "^1.12.5",
|
||||
"vue": "^3.2.33",
|
||||
"vue-router": "^4.0.14"
|
||||
},
|
||||
|
|
@ -837,6 +839,14 @@
|
|||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.0.2",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.2.tgz",
|
||||
"integrity": "sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/ecstatic": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-4.1.4.tgz",
|
||||
|
|
@ -2905,6 +2915,11 @@
|
|||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/twitter-api-v2": {
|
||||
"version": "1.12.5",
|
||||
"resolved": "https://registry.npmjs.org/twitter-api-v2/-/twitter-api-v2-1.12.5.tgz",
|
||||
"integrity": "sha512-kgsEjRfm2kdvAgqXd5druYYxykXEXamae6V/TDyYnws0MClcYBlbtOyGn/HPXEfn2NylbQrjDwkCLaD3qkRgMA=="
|
||||
},
|
||||
"node_modules/type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
|
@ -3762,6 +3777,11 @@
|
|||
"esutils": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "16.0.2",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.2.tgz",
|
||||
"integrity": "sha512-JvpYKUmzQhYoIFgK2MOnF3bciIZoItIIoryihy0rIA+H4Jy0FmgyKYAHCTN98P5ybGSJcIFbh6QKeJdtZd1qhA=="
|
||||
},
|
||||
"ecstatic": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-4.1.4.tgz",
|
||||
|
|
@ -5109,6 +5129,11 @@
|
|||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"twitter-api-v2": {
|
||||
"version": "1.12.5",
|
||||
"resolved": "https://registry.npmjs.org/twitter-api-v2/-/twitter-api-v2-1.12.5.tgz",
|
||||
"integrity": "sha512-kgsEjRfm2kdvAgqXd5druYYxykXEXamae6V/TDyYnws0MClcYBlbtOyGn/HPXEfn2NylbQrjDwkCLaD3qkRgMA=="
|
||||
},
|
||||
"type-check": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@
|
|||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview --port 5050",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
|
||||
"twitter": "node app/index.mjs twitter"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^16.0.2",
|
||||
"ecstatic": "^4.1.4",
|
||||
"pinia": "^2.0.13",
|
||||
"puppeteer": "^17.0.0",
|
||||
"twitter-api-v2": "^1.12.5",
|
||||
"vue": "^3.2.33",
|
||||
"vue-router": "^4.0.14"
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user