Skip to content

Commit

Permalink
Add basic Twitter functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
misenhower committed Sep 1, 2022
1 parent 4fac5b6 commit 7a6677c
Show file tree
Hide file tree
Showing 12 changed files with 193 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Twitter API parameters
TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET=
TWITTER_ACCESS_TOKEN_KEY=
TWITTER_ACCESS_TOKEN_SECRET=
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dist-ssr
coverage
*.local

.env
docker-compose.override.yml

/cypress/videos/
Expand Down
17 changes: 17 additions & 0 deletions app/index.mjs
Original file line number Diff line number Diff line change
@@ -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 changes: 5 additions & 0 deletions app/twitter/Media.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default class Media
{
file = null;
type = 'image/png';
}
5 changes: 5 additions & 0 deletions app/twitter/Tweet.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default class Tweet
{
status = null;
media = [];
}
30 changes: 30 additions & 0 deletions app/twitter/TwitterClient.mjs
Original file line number Diff line number Diff line change
@@ -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 changes: 28 additions & 0 deletions app/twitter/TwitterManager.mjs
Original file line number Diff line number Diff line change
@@ -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 changes: 34 additions & 0 deletions app/twitter/generators/CountdownTweet.mjs
Original file line number Diff line number Diff line change
@@ -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 changes: 29 additions & 0 deletions app/twitter/generators/TweetGenerator.mjs
Original file line number Diff line number Diff line change
@@ -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 changes: 10 additions & 0 deletions app/twitter/index.mjs
Original file line number Diff line number Diff line change
@@ -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 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down

0 comments on commit 7a6677c

Please sign in to comment.