First commit

This commit is contained in:
Evann Regnault 2022-01-15 18:07:08 +01:00
commit 455ae8c0ee
36 changed files with 677 additions and 0 deletions

35
.gitignore vendored Normal file
View file

@ -0,0 +1,35 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel

8
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

18
package.json Normal file
View file

@ -0,0 +1,18 @@
{
"private": true,
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"dayjs": "^1.10.7",
"next": "latest",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"swr": "^1.0.1",
"redis": "^4.0.1",
"dotenv": "^10.0.0",
"fast-levenshtein": "^3.0.0"
}
}

View file

@ -0,0 +1,21 @@
import {getArtifacts} from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
export default async function artifactHandler({query: {id}}, res) {
let artifact = JSON.parse(await getResultFromRedis(`artifact-${id}`))
if (!artifact) {
let artifacts = JSON.parse(await getResultFromRedis("artifact-all"));
if (!artifacts){
artifacts = await getArtifacts();
addResultToRedis("artifact-all", JSON.stringify(artifacts)).catch(console.error);
}
artifact = artifacts[id];
}
if (artifact) {
res.status(200).json(artifact);
addResultToRedis(`artifact-${id}`, JSON.stringify(artifact)).catch(console.error)
} else {
res.status(404).json({message: `Artifact ${id} doesn't exist`})
}
}

View file

@ -0,0 +1,21 @@
import {getArtifacts} from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
export default async function handler(req, res) {
let t0 = performance.now()
let artifacts = JSON.parse(await getResultFromRedis(`artifact-all`))
if (!artifacts) {
artifacts = await getArtifacts();
}
if (artifacts) {
res.status(200).json(Object.keys(artifacts));
addResultToRedis(`artifact-all`, JSON.stringify(artifacts)).catch(console.error)
} else {
res.status(404).json({message: "An error has occurred"})
}
let t1 = performance.now();
console.debug(`Execution took ${t1-t0} ms`)
}

View file

@ -0,0 +1,23 @@
import {getArtifacts} from "../utils";
import {getClosest} from "../../../../utils/levenshtein";
import {addResultToRedis, getResultFromRedis} from "../../../../utils/redis";
export default async function artifactHandler({query: {id}}, res) {
let artifacts = JSON.parse(await getResultFromRedis('artifact-all'));
if (!artifacts) {
artifacts = await getArtifacts();
addResultToRedis('artifact-all', JSON.stringify(artifacts)).catch(console.error)
}
const closestArtifacts = []
getClosest(id, artifacts).forEach(a => {
closestArtifacts.push(artifacts[a.name])
})
if (closestArtifacts) {
res.status(200).json(closestArtifacts);
} else {
res.status(404).json({message: `Artifact ${id} not found`});
}
}

View file

@ -0,0 +1,5 @@
export async function getArtifacts(){
const res = await fetch("https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/src/data/artifacts/en.json");
const artifacts = JSON.parse(await res.text());
return artifacts;
}

21
pages/api/builds/[id].js Normal file
View file

@ -0,0 +1,21 @@
import {getBuilds} from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
export default async function buildHandler({query: {id}}, res) {
let build = JSON.parse(await getResultFromRedis(`build-${id}`));
if (!build) {
let builds = JSON.parse(await getResultFromRedis('build-all'));
if (!builds){
builds = await getBuilds();
addResultToRedis('build-all', JSON.stringify(builds)).catch(console.error);
}
build = builds[id];
}
if (build) {
res.status(200).json(build);
addResultToRedis(`build-${id}`, JSON.stringify(build)).catch(console.error);
} else {
res.status(404).json({message : `Build for ${id} not found`});
}
}

16
pages/api/builds/index.js Normal file
View file

@ -0,0 +1,16 @@
import {getBuilds} from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
export default async function handler(req, res) {
let builds = JSON.parse(await getResultFromRedis('build-all'));
if (!builds) {
builds = await getBuilds();
addResultToRedis('build-all', JSON.stringify(builds)).catch(console.error);
}
if (builds){
res.status(200).json(Object.keys(builds));
} else {
res.status(404).json({message: "An error has occurred"});
}
}

21
pages/api/builds/utils.js Normal file
View file

@ -0,0 +1,21 @@
const AsyncFunction = Object.getPrototypeOf(async function () {
}).constructor;
export async function getBuilds() {
let builds = await new AsyncFunction(
(await (await fetch(
"https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/src/data/build.js",
)).text()).replace("export const builds =", "return"),
)();
Object.values(builds).forEach(b => {
let d = [];
for (let c in b["roles"]) {
b["roles"][c]["name"] = c;
d.push(b["roles"][c]);
}
b["roles"] = d;
})
return builds;
}

View file

@ -0,0 +1,21 @@
import {getCharacters} from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
export default async function characterHandler({query: {id}}, res) {
let character = JSON.parse(await getResultFromRedis(`character-${id}`));
if (!character) {
let characters = JSON.parse(await getResultFromRedis(`character-all`));
if (!characters) {
characters = await getCharacters();
addResultToRedis('character-all', JSON.stringify(characters)).catch(console.error);
}
character = characters[id];
}
if (character){
res.status(200).json(character);
addResultToRedis(`character-${id}`, JSON.stringify(character)).catch(console.error);
} else {
res.status(404).json({message: `Character ${id} doesn't exist`});
}
}

View file

@ -0,0 +1,15 @@
import {getCharacters} from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
export default async function handler(req, res) {
let characters = JSON.parse(await getResultFromRedis('character-all'));
if (!characters){
characters = await getCharacters();
addResultToRedis('character-all', JSON.stringify(characters)).catch(console.error)
}
if (characters) {
res.status(200).json(Object.keys(characters));
} else {
res.status(404).json({message: "An error has occurred"});
}
}

View file

@ -0,0 +1,23 @@
import {getCharacters} from "../utils";
import {getClosest} from "../../../../utils/levenshtein";
import {addResultToRedis, getResultFromRedis} from "../../../../utils/redis";
export default async function artifactHandler({query: {id}}, res) {
let characters = JSON.parse(await getResultFromRedis('character-all'));
if (!characters) {
characters = await getCharacters();
addResultToRedis('character-all', JSON.stringify(characters)).catch(console.error)
}
const closestCharacters = []
getClosest(id, characters).forEach(a => {
closestCharacters.push(characters[a.name])
})
if (closestCharacters) {
res.status(200).json(closestCharacters);
} else {
res.status(404).json({message: `Character ${id} not found`});
}
}

View file

@ -0,0 +1,37 @@
import {weapons} from "../weapons/utils";
import {getElements} from "../elements/utils";
import {getItems} from "../items/utils";
import {getBuilds} from "../builds/utils";
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
export async function getCharacters() {
const builds = await getBuilds();
let func = (await (await fetch(
"https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/src/data/characters.js",
)).text());
func = func.substr(func.indexOf("export const characters ="));
const characters = await new AsyncFunction(
"weapons",
"elements",
"itemList",
func.replace("export const characters = ", "return"),
)(weapons, await getElements(), await getItems());
for (let chr in characters){
characters[chr].builds = builds[chr]['roles'];
for (let y in characters[chr]['ascension']) {
for (let x in characters[chr]['ascension'][y].items) {
if (!characters[chr]['ascension'][y]["items"][x]["amount"]) {
delete characters[chr]['ascension'][y]["items"].splice(x, 1);
break;
}
}
}
}
return characters;
}

10
pages/api/domains/[id].js Normal file
View file

@ -0,0 +1,10 @@
import {getDomains} from "./utils";
export default async function domainHandler({query : {id}}, res) {
const domains = await getDomains();
if (domains[id]) {
res.status(200).json(domains[id]);
} else {
res.status(404).json({message: `Domain ${id} doesn't exist`});
}
}

View file

@ -0,0 +1,10 @@
import {getDomains} from "./utils";
export default async function handler(req, res) {
const domains = await getDomains();
if (domains) {
res.status(200).json(Object.keys(domains));
} else {
res.status(404).json({message: "An error has occurred"});
}
}

View file

@ -0,0 +1,11 @@
const AsyncFunction = Object.getPrototypeOf(async function () {
}).constructor;
export async function getDomains() {
return await new AsyncFunction(
(await (await fetch(
"https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/src/data/domain.js",
)).text()).replace("export const domains =", "return"),
)();
}

View file

@ -0,0 +1,12 @@
import {getElements} from "./utils.js";
export default async function elementsHandler({ query: { id } }, res) {
const element = await getElements();
console.log(element, id);
if (element[id]) {
res.status(200).json(element[id]);
} else {
res.status(404).json({ message: `Element ${id} not found` });
}
}

View file

@ -0,0 +1,10 @@
import {getElements} from "./utils.js";
export default async function handler(req, res) {
const elements = await getElements();
if (elements) {
res.status(200).json(Object.keys(elements));
} else {
res.status(404).json({ message: 'An error occurred' });
}
}

View file

@ -0,0 +1,25 @@
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
const elementColor = {
anemo: 0x7fffd4,
cryo: 0xd6fffa,
dendro: 0x7de91c,
electro: 0xbf00ff,
geo: 0xffbf00,
hydro: 0x0f5e9c,
pyro: 0xe35822
}
export async function getElements() {
let elements = await new AsyncFunction(
(await (await fetch(
"https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/src/data/elements.js",
)).text()).replace("export const elements =", "return"),
)();
Object.keys(elements).forEach(k => {
elements[k].color = elementColor[k];
})
return elements;
}

View file

@ -0,0 +1,13 @@
import {compareEndTimes, isCurrentEvent} from "../../../utils/time";
import {getEvents} from "./utils";
export default async function currentEvents(req, res){
const allEvents = await getEvents();
let currents = allEvents.flat().map(isCurrentEvent).filter(x=>x);
currents = currents.sort(compareEndTimes);
if (currents) {
res.status(200).json(currents);
} else {
res.status(404).json({message: "There is no event currently going"});
}
}

10
pages/api/events/index.js Normal file
View file

@ -0,0 +1,10 @@
import {getEvents} from "./utils";
export default async function handler(req, res) {
const events = await getEvents();
if (events) {
res.status(200).json(events);
} else {
res.status(404).json({message: "An error has occured"});
}
}

View file

@ -0,0 +1,13 @@
import {getEvents} from "./utils";
import {compareStartTimes, isUpcomingEvent} from "../../../utils/time";
export default async function upcomingEvents(req, res){
const allEvents = await getEvents();
let upcoming = allEvents.flat().map(isUpcomingEvent).filter(x=>x);
upcoming = upcoming.sort(compareStartTimes);
if (upcoming){
res.status(200).json(upcoming);
} else {
res.status(404).json({message: "There is no upcoming event"});
}
}

12
pages/api/events/utils.js Normal file
View file

@ -0,0 +1,12 @@
import {isCurrentEvent} from "../../../utils/time";
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
export async function getEvents(){
const allEvents = await new AsyncFunction(
(await (await fetch(
"https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/src/data/timeline.js",
)).text()).replace("export const eventsData = ", "return"),
)();
return allEvents;
}

5
pages/api/index.js Normal file
View file

@ -0,0 +1,5 @@
const fs = require('fs')
export default function handle(req, res) {
const dirs = fs.readdirSync('./pages/api').filter(x=>x != "index.js")
res.status(200).json(dirs)
}

21
pages/api/items/[id].js Normal file
View file

@ -0,0 +1,21 @@
import { getItems } from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
import {getArtifacts} from "../artifacts/utils";
export default async function itemsHandler({ query: { id } }, res) {
let item = JSON.parse(await getResultFromRedis(`item-${id}`))
if (!item) {
let items = JSON.parse(await getResultFromRedis("item-all"));
if (!items){
items = await getItems();
addResultToRedis("item-all", JSON.stringify(items)).catch(console.error);
}
item = items[id]
}
if (item) {
res.status(200).json(item);
addResultToRedis(`item-${id}`, JSON.stringify(item)).catch(console.error)
} else {
res.status(404).json({ message: `Item ${id} not found` });
}
}

16
pages/api/items/index.js Normal file
View file

@ -0,0 +1,16 @@
import { getItems } from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
export default async function handler(req, res) {
let items = JSON.parse(await getResultFromRedis('item-all'))
if (!items) {
items = await getItems();
}
if (items) {
res.status(200).json(Object.keys(items));
addResultToRedis('item-all', JSON.stringify(items)).catch(console.error)
} else {
res.status(404).json({ message: 'An error occurred' });
}
}

View file

@ -0,0 +1,23 @@
import {getItems} from "../utils";
import {getClosest} from "../../../../utils/levenshtein";
import {addResultToRedis, getResultFromRedis} from "../../../../utils/redis";
export default async function itemHandler({query: {id}}, res) {
let items = JSON.parse(await getResultFromRedis('item-all'));
if (!items) {
items = await getItems();
addResultToRedis('item-all', JSON.stringify(items)).catch(console.error)
}
const closestItems = []
getClosest(id, items).forEach(a => {
closestItems.push(items[a.name])
})
if (closestItems) {
res.status(200).json(closestItems);
} else {
res.status(404).json({message: `Item ${id} not found`});
}
}

9
pages/api/items/utils.js Normal file
View file

@ -0,0 +1,9 @@
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
export async function getItems() {
return await new AsyncFunction(
(await (await fetch(
"https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/src/data/itemList.js",
)).text()).replace("export const itemList =", "return"),
)();
}

21
pages/api/weapons/[id].js Normal file
View file

@ -0,0 +1,21 @@
import { getWeapons } from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
export default async function weaponsHandler({ query: { id } }, res) {
let weapon = JSON.parse(await getResultFromRedis(`weapon-${id}`));
if (!weapon) {
let weapons = JSON.parse(await getResultFromRedis("weapon-all"));
if (!weapons){
weapons = await getWeapons();
addResultToRedis("weapon-all", JSON.stringify(weapons)).catch(console.error);
}
weapon = weapons[id];
}
if (weapon) {
res.status(200).json(weapon);
addResultToRedis(`weapon-${id}`, JSON.stringify(weapon)).catch(console.error);
} else {
res.status(404).json({ message: `Item ${id} not found` });
}
}

View file

@ -0,0 +1,16 @@
import { getWeapons } from "./utils";
import {addResultToRedis, getResultFromRedis} from "../../../utils/redis";
export default async function handler(req, res) {
let weapons = JSON.parse(await getResultFromRedis("weapon-all"));
if (!weapons)
weapons = await getWeapons();
if (weapons) {
res.status(200).json(Object.keys(weapons));
addResultToRedis("weapon-all", JSON.stringify(weapons)).catch(console.error);
} else {
res.status(404).json({ message: 'An error has occurred' });
}
}

View file

@ -0,0 +1,23 @@
import {getWeapons} from "../utils";
import {getClosest} from "../../../../utils/levenshtein";
import {addResultToRedis, getResultFromRedis} from "../../../../utils/redis";
export default async function weaponsHandler({query: {id}}, res) {
let weapons = JSON.parse(await getResultFromRedis('weapon-all'));
if (!weapons) {
weapons = await getWeapons();
addResultToRedis('weapon-all', JSON.stringify(weapons)).catch(console.error)
}
const closestWeapons = []
getClosest(id, weapons).forEach(w => {
closestWeapons.push(weapons[w.name])
})
if (closestWeapons) {
res.status(200).json(closestWeapons);
} else {
res.status(404).json({message: `Weapon ${id} not found`});
}
}

View file

@ -0,0 +1,47 @@
import { getItems } from "../items/utils";
const AsyncFunction = Object.getPrototypeOf(async function () { }).constructor;
export const weapons = {
sword: {
id: "sword",
name: "Sword",
},
bow: {
id: "bow",
name: "Bow",
},
polearm: {
id: "polearm",
name: "Polearm",
},
claymore: {
id: "claymore",
name: "Claymore",
},
catalyst: {
id: "catalyst",
name: "Catalyst",
},
};
export async function getWeapons() {
const data = JSON.parse(await (await fetch("https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/src/data/weapons/en.json")).text());
let func = (await (await fetch(
"https://raw.githubusercontent.com/MadeBaruna/paimon-moe/main/src/data/weaponList.js",
)).text());
func = func.slice(func.indexOf("export const weaponList ="));
let weaponList = await new AsyncFunction("weapons", "itemList",
func.replace("export const weaponList = ", "return"),
)(weapons, await getItems());
Object.values(weaponList).forEach(x => {
weaponList[x.id].extras = data[x.id];
})
return weaponList;
}

18
utils/levenshtein.js Normal file
View file

@ -0,0 +1,18 @@
const levenshtein = require("fast-levenshtein");
export function getClosest(input, list, limit=5){
let t0 = performance.now();
const things = []
for (let s in list) {
let ns = s.replaceAll("_"," ");
if (ns.toLowerCase().includes(input.toLowerCase())){
things.push({name: s, dist: 0})
continue;
}
things.push({name: s, dist: levenshtein.get(input, ns)})
}
things.sort((a,b) => a.dist - b.dist);
let t1 = performance.now();
console.debug(`Levenshtein took ${t1-t0} ms`)
return things.splice(0,limit);
}

15
utils/redis.js Normal file
View file

@ -0,0 +1,15 @@
const redis = require("redis");
const client = redis.createClient("redis://localhost:6379");
client.connect();
export async function getResultFromRedis(key){
if (await client.exists(key)) {
return await client.get(key)
}
return null;
}
export async function addResultToRedis(key, value){
await client.set(key, value, {EX: 600});
}

52
utils/time.js Normal file
View file

@ -0,0 +1,52 @@
const dayjs = require('dayjs');
export const getTimeDifferenceAsia = () => {
const now = dayjs();
const local = now.utcOffset();
const serverTime = now.utcOffset(8);
return serverTime - local;
};
function convertToNow(event){
let start;
if (event["timezoneDependent"]) {
start = dayjs(event.start, 'YYYY-MM-DD HH:mm:ss').subtract(getTimeDifferenceAsia(), 'minute');
} else {
start = dayjs(event.start, 'YYYY-MM-DD HH:mm:ss').subtract(0, 'minute');
}
const end = dayjs(event.end, 'YYYY-MM-DD HH:mm:ss').subtract(0, 'minute');
return [start, end];
}
export function isCurrentEvent(event) {
const [start, end] = convertToNow(event);
const timeSinceStart = -start.diff(dayjs(), 'day', true);
const timeUntilEnd = end.diff(dayjs(), 'day', true);
if (timeSinceStart > 0 && timeUntilEnd > 0){
event["startTimestamp"] = start.unix();
event["endTimestamp"] = end.unix();
return event;
}
}
export function isUpcomingEvent(event){
const [start, ] = convertToNow(event);
const timeUntilStart = start.diff(dayjs(), 'day', true);
if (timeUntilStart > 0){
event["startTimestamp"] = start.unix();
event["endTimestamp"] = 0;
return event;
}
}
export function compareStartTimes(a, b) {
return dayjs(a.start).diff(dayjs(b.start), 'day', true);
}
export function compareEndTimes(a, b) {
return dayjs(a.end).diff(dayjs(b.end), 'day', true);
}