Compare commits

...

7 Commits

Author SHA1 Message Date
Nemesis d2459492c3 feat(costumes): check if emblems exists 2023-02-19 20:21:29 +01:00
Nemesis 3a4b4f7b51 docs: update readme 2023-02-19 20:15:40 +01:00
Nemesis 2d0f32d01d feat(import): emblems 2023-02-19 20:11:24 +01:00
Nemesis 0b99a0d977 feat(import): costumes 2023-02-19 20:11:19 +01:00
DrMint c00531c7d2 Added duplicate weapon stories checks 2023-02-13 02:02:47 +01:00
Nemesis 72d642ed13 fix: trailing slash in env definition 2023-02-02 00:29:19 +01:00
DrMint c9b2afcdb6
Merge pull request #1 from Accords-Library/feat-weapon-stories-script
feat: add weapon stories import
2023-02-02 00:27:35 +01:00
9 changed files with 345 additions and 4 deletions

View File

@ -1,2 +1,3 @@
STRAPI_BASE_API_URL=https://strapi.accords-library.com|http://localhost:1337/api
STRAPI_GRAPHQL=https://strapi.accords-library.com/graphql|http://localhost:1337/graphql
STRAPI_API_TOKEN=

View File

@ -2,8 +2,50 @@
## Scripts available
### Import all
Models imported in following order:
- `rein-emblems`
- `rein-costumes`
- `weapon-stories`
Usage:
```sh
npm run import:all
```
### NieR Re[in]carnation
#### Costume emblems
Import costume emblems (abyssal, celebratory...) from https://nierrein.guide/ database to Accord's Library database
Model: `rein-emblems`
Usage:
```sh
npm run import:emblems
```
#### Costumes
Import costumes from https://nierrein.guide/ database to Accord's Library database
Model: `rein-costumes`
Usage:
> ⚠️ Importing the emblems before the costumes is mandatory.
```sh
npm run import:costumes
```
#### Weapon stories
Import weapon stories from https://nierrein.guide/ database to Accord's Library database
@ -16,3 +58,9 @@ Usage:
```sh
npm run import:weapon-stories
```
Related checks:
```sh
npm run check:weapon-stories-duplicates
```

View File

@ -2,7 +2,7 @@ import { str, envsafe, url } from 'envsafe';
export const env = envsafe({
STRAPI_BASE_API_URL: url({
devDefault: 'http://127.0.01:1337/api/',
devDefault: 'http://127.0.01:1337/api',
}),
STRAPI_API_TOKEN: str(),
});

View File

@ -0,0 +1,120 @@
import 'dotenv/config';
import { NIERREIN_GUIDE_API_URL, NIERREIN_GUIDE_CDN_URL } from '../../config.mjs';
import { env } from '../../env.mjs';
import slugg from 'slugg';
let currentIndex = 1;
const { data: emblems } = await fetch(
`${env.STRAPI_BASE_API_URL}/rein-emblems`,
{
headers: {
Authorization:
`bearer ${env.STRAPI_API_TOKEN}`,
},
}
)
.then((res) => res.json())
.catch((error) => {
console.error(error);
process.exit(1);
});
if (!emblems || emblems.length === 0) {
console.error(`Got 0 emblems from "${env.STRAPI_BASE_API_URL}/rein-emblems". You may want to run "npm run import:emblems".`)
process.exit(1);
}
let costumes = [];
try {
console.log('Fetching NieR Re[in]carnation costumes...')
costumes = await fetch(`${NIERREIN_GUIDE_API_URL}/costumes`)
.then((response) => response.json())
} catch (error) {
console.error(error)
process.exit(1);
}
console.log(`${costumes.length} costumes fetched from "${NIERREIN_GUIDE_API_URL}/costumes"`)
if (costumes.length === 0) {
console.error(`Got 0 costume from "${NIERREIN_GUIDE_API_URL}/costumes". Their database is probably in the process of being updated. Try again in 10 minutes.`)
process.exit(1);
}
for (const costume of costumes) {
if (!currentIndex > 1) continue
console.log(`Uploading n°${currentIndex}/${costumes.length} costumes.`);
// Get costume image blob for the sprite
const file = await fetch(`${NIERREIN_GUIDE_CDN_URL}${costume.image_path_base}full.png`).then(
(response) => response.blob()
);
const body = new FormData();
let costumeEmblem = {}
if (costume.emblem_id) {
costumeEmblem = emblems.find((emblem) => emblem.attributes.slug === slugg(costume.emblem.name))
}
// Create the weapon-stories entry
body.append(
"data",
JSON.stringify({
slug: costume.slug,
translations: [
{
language: {
connect: [2], // en
},
name: costume.title,
description:
costume.description?.replaceAll(
"\\n",
"<br>"
),
},
],
emblem: {
connect: [costumeEmblem.id].filter(Boolean),
},
})
);
// Add the weapon image blob
body.append("files.sprite", file, `${costume.slug}.png`);
const response = await fetch(
`${env.STRAPI_BASE_API_URL}/rein-costumes`,
{
method: "POST",
body,
headers: {
Authorization:
`bearer ${env.STRAPI_API_TOKEN}`,
},
}
)
.then((res) => res.json())
.catch((err) => err?.json());
currentIndex++;
if (response?.error) {
if (response.error.message === "This attribute must be unique") {
console.warn(
`[DUPLICATE] ${costume.name} (${costume.slug}) already exists.`
);
continue
}
console.error(`[ERROR] ${costume.title} (${costume.slug}):`, response.error.message)
} else {
console.log(`[ADDED] "${costume.title}" (${costume.slug})`);
}
}

View File

@ -0,0 +1,83 @@
import 'dotenv/config';
import { NIERREIN_GUIDE_API_URL } from '../../config.mjs';
import { env } from '../../env.mjs';
import slugg from 'slugg';
let currentIndex = 1;
let emblems = [];
try {
console.log('Fetching NieR Re[in]carnation emblems...')
emblems = await fetch(`${NIERREIN_GUIDE_API_URL}/emblems`)
.then((response) => response.json())
} catch (error) {
console.error(error)
process.exit(1);
}
console.log(`${emblems.length} emblems fetched from "${NIERREIN_GUIDE_API_URL}/emblems"`)
if (emblems.length === 0) {
console.error(`Got 0 emblem from "${NIERREIN_GUIDE_API_URL}/emblems". Their database is probably in the process of being updated. Try again in 10 minutes.`)
process.exit(1);
}
for (const emblem of emblems) {
if (!currentIndex > 1) continue
console.log(`Uploading n°${currentIndex}/${emblems.length} emblems.`);
const body = new FormData();
const description = `
${emblem.main_message?.replaceAll("\\n", "<br>")}<br>${emblem.small_messages?.replaceAll("\\n", "<br>")}
`.trim()
// Create the emblem entry
body.append(
"data",
JSON.stringify({
slug: slugg(emblem.name),
translations: [
{
language: {
connect: [2], // en
},
name: emblem.name,
description,
},
],
})
);
const response = await fetch(
`${env.STRAPI_BASE_API_URL}/rein-emblems`,
{
method: "POST",
body,
headers: {
Authorization:
`bearer ${env.STRAPI_API_TOKEN}`,
},
}
)
.then((res) => res.json())
.catch((err) => err?.json());
currentIndex++;
if (response?.error) {
if (response.error.message === "This attribute must be unique") {
console.warn(
`[DUPLICATE] ${emblem.name} already exists.`
);
continue
}
console.error(`[ERROR] ${emblem.name}:`, response.error.message)
} else {
console.log(`[ADDED] "${emblem.name}"`);
}
}

View File

@ -0,0 +1,53 @@
import "dotenv/config";
const response = await fetch(`${process.env.STRAPI_GRAPHQL}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `bearer ${process.env.STRAPI_API_TOKEN}`,
},
body: JSON.stringify({
query: `{
weaponStories(pagination: { limit: -1 }) {
data {
id
attributes {
slug
name { name }
}
}
}
}`,
}),
});
const normalizeName = (name) => {
return name
.toLowerCase()
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.replace(/[']/g, "'");
};
const weapons = (await response.json()).data.weaponStories.data;
const nameMap = new Map();
for (const weapon of weapons) {
for (const { name } of weapon.attributes.name) {
if (name === undefined || name === null) {
console.warn(name, "is nullable", weapon);
}
const normalizedName = normalizeName(name);
if (nameMap.has(normalizedName))
console.warn(`
Duplicate names ${normalizedName} in:
1. ${nameMap.get(normalizedName).attributes.slug}
2. ${weapon.attributes.slug}`);
else {
nameMap.set(normalizedName, weapon);
}
}
}

View File

@ -6,6 +6,18 @@ let currentIndex = 1;
let weapons = [];
const slugSpecialCases = new Map([
["phoenix-lance", "phoenix-spear"],
["iron-will", "hymir-finger"],
["the-devil-queen", "widow-death"],
["kains-sword", "kaines-sword"],
["spear-of-the-usurper", "robber-king"],
["ancient-overlord", "kingsblood"],
["fang-of-the-twins", "twins-fang"],
["dragoon-lance", "knight-vow"],
["faith", "nobuyoshi"],
]);
try {
console.log('Fetching NieR Re[in]carnation weapons...')
weapons = await fetch(`${NIERREIN_GUIDE_API_URL}/weapons`)
@ -30,6 +42,11 @@ for (const weapon of weapons) {
(response) => response.blob()
);
// Change slug if the weapon is known with a different name in accords-library
if (slugSpecialCases.has(weapon.slug)) {
weapon.slug = slugSpecialCases.get(weapon.slug);
}
const body = new FormData();
// Create the weapon-stories entry

16
package-lock.json generated
View File

@ -10,7 +10,11 @@
"license": "MIT",
"dependencies": {
"dotenv": "^16.0.3",
"envsafe": "^2.0.3"
"envsafe": "^2.0.3",
"slugg": "^1.2.1"
},
"engines": {
"node": ">=18.13.0"
}
},
"node_modules/dotenv": {
@ -28,6 +32,11 @@
"engines": {
"node": ">=10"
}
},
"node_modules/slugg": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/slugg/-/slugg-1.2.1.tgz",
"integrity": "sha512-yzUY7d53sdY3wp6rGCc54fn+cel0aNsdG5lvRVN8bvlAXYQI2fIo36AAOVjArOaMzlidHYyAYqriHzuos93h+Q=="
}
},
"dependencies": {
@ -40,6 +49,11 @@
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/envsafe/-/envsafe-2.0.3.tgz",
"integrity": "sha512-51lek8h5btjXhXUDW//Pno7HPydM1WQzltOb1/ONRKdwB2QkfAURnN4Qn0cJ5LzGvX2Km1ReR2O3K3Bqq9sBMg=="
},
"slugg": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/slugg/-/slugg-1.2.1.tgz",
"integrity": "sha512-yzUY7d53sdY3wp6rGCc54fn+cel0aNsdG5lvRVN8bvlAXYQI2fIo36AAOVjArOaMzlidHYyAYqriHzuos93h+Q=="
}
}
}

View File

@ -8,10 +8,15 @@
"description": "",
"license": "MIT",
"scripts": {
"import:weapon-stories": "node ./nier-reincarnation/weapon-stories/index.mjs"
"import:all": "npm run import:emblems && npm run import:costumes && npm run import:weapon-stories",
"check:weapon-stories-duplicates": "node ./nier-reincarnation/weapon-stories/find-duplicates.mjs",
"import:weapon-stories": "node ./nier-reincarnation/weapon-stories/index.mjs",
"import:emblems": "node ./nier-reincarnation/emblems/index.mjs",
"import:costumes": "node ./nier-reincarnation/costumes/index.mjs"
},
"dependencies": {
"dotenv": "^16.0.3",
"envsafe": "^2.0.3"
"envsafe": "^2.0.3",
"slugg": "^1.2.1"
}
}