Add nxapi-powered NSO client

This commit is contained in:
Matt Isenhower 2022-09-09 14:41:39 -07:00
parent eea6984e64
commit 9bd16d2e41
6 changed files with 2251 additions and 39 deletions

View File

@ -1,3 +1,9 @@
# Nintendo API
NINTENDO_TOKEN=
# User agent string
USER_AGENT=
# Twitter API parameters # Twitter API parameters
TWITTER_CONSUMER_KEY= TWITTER_CONSUMER_KEY=
TWITTER_CONSUMER_SECRET= TWITTER_CONSUMER_SECRET=

45
app/common/ValueCache.mjs Normal file
View File

@ -0,0 +1,45 @@
import fs from 'fs/promises';
export default class ValueCache
{
constructor(key) {
this._key = key;
}
get path() {
return `storage/${this._key}.json`;
}
async _getRawItem() {
try {
let data = await fs.readFile(this.path);
return JSON.parse(data);
} catch (e) {
return null;
}
}
async get() {
let item = await this._getRawItem();
// If the cached value is already expired, return null
if (item && item.expires && item.expires < new Date) {
return null;
}
return item;
}
async getData() {
let item = await this.get();
return item && item.data;
}
async setData(data, expires = null) {
let serialized = JSON.stringify({ expires, data }, undefined, 2);
await fs.writeFile(this.path, serialized);
}
}

75
app/nso/NsoClient.mjs Normal file
View File

@ -0,0 +1,75 @@
import CoralApi from 'nxapi/coral';
import { addUserAgent } from "nxapi";
import ValueCache from '../common/ValueCache.mjs';
let _nxapiInitialized = false;
function initializeNxapi() {
if (!_nxapiInitialized) {
addUserAgent(process.env.USER_AGENT);
}
_nxapiInitialized = true;
}
export default class NsoClient
{
constructor(nintendoToken = null) {
initializeNxapi();
this.nintendoToken = nintendoToken || process.env.NINTENDO_TOKEN;
this.coralCache = new ValueCache('coral');
}
_calculateCacheExpiry(expiresIn) {
let expires = Date.now() + expiresIn * 1000;
// Expire 5min early to make sure we have time to execute requests
return expires - 5 * 60 * 1000;
}
async getCoralApi() {
let data = await this.coralCache.getData();
if (!data) {
data = await this._createCoralSession();
}
return CoralApi.createWithSavedToken(data);
}
async _createCoralSession() {
console.debug('Creating Coral session');
let { data } = await CoralApi.createWithSessionToken(this.nintendoToken);
let expires = this._calculateCacheExpiry(data.credential.expiresIn);
console.debug(`Caching Coral session until: ${expires}`);
await this.coralCache.setData(data, expires);
return data;
}
async getWebServiceToken(id) {
let tokenCache = new ValueCache(`webservicetoken.${id}`);
let token = await tokenCache.getData();
if (!token) {
token = await this._createWebServiceToken(id, tokenCache);
}
return token.accessToken;
}
async _createWebServiceToken(id, tokenCache) {
let coral = await this.getCoralApi();
console.debug(`Creating web service token for ID ${id}`);
let { result } = await coral.getWebServiceToken(id);
let expires = this._calculateCacheExpiry(result.expiresIn);
console.debug(`Caching web service token for ID ${id} until: ${expires}`);
await tokenCache.setData(result, expires);
return result;
}
}

2161
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
"cron": "^2.1.0", "cron": "^2.1.0",
"dotenv": "^16.0.2", "dotenv": "^16.0.2",
"ecstatic": "^4.1.4", "ecstatic": "^4.1.4",
"nxapi": "^1.4.0",
"pinia": "^2.0.13", "pinia": "^2.0.13",
"puppeteer": "^17.0.0", "puppeteer": "^17.0.0",
"twitter-api-v2": "^1.12.5", "twitter-api-v2": "^1.12.5",

2
storage/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore