Skip to content

Commit 8cedff3

Browse files
committed
test: add check-format command and run it in CI
1 parent 92be6d7 commit 8cedff3

File tree

14 files changed

+263
-193
lines changed

14 files changed

+263
-193
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ jobs:
2929
- name: Check types
3030
run: npm run check-types
3131

32+
- name: Check formatting
33+
run: npm run check-format
34+
3235
test:
3336
runs-on: ubuntu-latest
3437
steps:

.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
build
2+
data
3+
node_modules
File renamed without changes.

README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,14 @@ Si besoin de stocker des settings basique, la table `kv` est disponible avec l'a
122122
Les clés doivent être des `string`, les valeurs peuvent être n'importe quoi, sachant que ce sera sérialisé / désérialisé de JSON (donc pas de données circulaires, pas de fonctions).
123123

124124
```typescript
125-
import { KeyValue } from "#src/database/";
125+
import { KeyValue } from '#src/database/';
126126

127-
await KeyValue.set('MyCron-LastRunResult', 'https://github.com/ES-Community/bot/issues/17');
127+
await KeyValue.set(
128+
'MyCron-LastRunResult',
129+
'https://github.com/ES-Community/bot/issues/17',
130+
);
128131
const myLastResult = await KeyValue.get('MyCron-LastRunResult');
129132

130133
if (result === myLastResult) return;
131134
notify(result);
132-
```
135+
```

knexfile.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,32 @@ module.exports = {
77
development: {
88
client: 'better-sqlite3',
99
connection: {
10-
filename: './dev.sqlite3'
10+
filename: './dev.sqlite3',
1111
},
12-
useNullAsDefault: true
12+
useNullAsDefault: true,
1313
},
1414

1515
staging: {
1616
client: 'better-sqlite3',
1717
connection: {
18-
filename: './staging.sqlite3'
18+
filename: './staging.sqlite3',
1919
},
20-
useNullAsDefault: true
20+
useNullAsDefault: true,
2121
},
2222

2323
production: {
2424
client: 'better-sqlite3',
2525
connection: {
26-
filename: './prod.sqlite3'
26+
filename: './prod.sqlite3',
2727
},
28-
useNullAsDefault: true
28+
useNullAsDefault: true,
2929
},
30-
30+
3131
test: {
3232
client: 'better-sqlite3',
3333
connection: {
34-
filename: './test.sqlite3'
34+
filename: './test.sqlite3',
3535
},
36-
useNullAsDefault: true
36+
useNullAsDefault: true,
3737
},
3838
};

migrations/20220916142133_InitDatabase.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@
22
* @param { import("knex").Knex } knex
33
* @returns { Promise<void> }
44
*/
5-
exports.up = function(knex) {
6-
return knex.schema
7-
.createTable('kv', table => {
8-
table.string('key').notNullable();
9-
table.primary('key');
10-
table.json('value');
11-
});
5+
exports.up = function (knex) {
6+
return knex.schema.createTable('kv', (table) => {
7+
table.string('key').notNullable();
8+
table.primary('key');
9+
table.json('value');
10+
});
1211
};
1312

1413
/**
1514
* @param { import("knex").Knex } knex
1615
* @returns { Promise<void> }
1716
*/
18-
exports.down = function(knex) {
17+
exports.down = function (knex) {
1918
return knex.schema.dropTable('kv');
2019
};

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
"private": true,
55
"scripts": {
66
"build": "tsc -p tsconfig.prod.json",
7+
"check-format": "prettier --check .",
78
"check-types": "tsc --noEmit",
89
"heroku-postbuild": "npm run build",
9-
"format": "prettier --write src tests",
10+
"format": "prettier --write .",
1011
"lint": "eslint src --ext ts",
1112
"lint-fix": "npm run lint -- --fix",
1213
"start": "nodemon --watch \"src/**/*.ts\" -e ts --exec \"ts-node -r dotenv/config src/bot.ts | pino-pretty\"",
13-
"test": "npm run test-only && npm run lint && npm run check-types",
14+
"test": "npm run test-only && npm run lint && npm run check-types && npm run check-format",
1415
"test-only": "jest",
1516
"test-coverage": "jest --coverage"
1617
},

src/crons/EpicGames.ts

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export default new Cron({
2929

3030
for (const game of games) {
3131
context.logger.info(`Found a new offered game (${game.title})`);
32-
32+
3333
const message = new MessageEmbed({ title: game.title, url: game.link });
3434
game.thumbnail && message.setThumbnail(game.thumbnail);
3535
game.banner && message.setImage(game.banner);
@@ -71,13 +71,13 @@ interface EpicGamesProducts {
7171
productSlug: string;
7272
catalogNs: {
7373
mappings: {
74-
pageSlug: string,
75-
pageType: 'productHome' | 'offer'
74+
pageSlug: string;
75+
pageType: 'productHome' | 'offer';
7676
}[];
7777
};
7878
offerMappings: {
79-
pageSlug: string,
80-
pageType: 'productHome' | 'offer'
79+
pageSlug: string;
80+
pageType: 'productHome' | 'offer';
8181
}[];
8282
urlSlug: string;
8383
price: {
@@ -217,30 +217,36 @@ export async function getOfferedGames(
217217

218218
return games.map<Game>((game) => {
219219
const discount = game.price.lineOffers[0].appliedRules[0];
220-
let slug = game.productSlug
221-
|| game.offerMappings?.find(i => i.pageType === 'productHome')?.pageSlug
222-
|| game.catalogNs.mappings?.find(i => i.pageType === 'productHome')?.pageSlug
223-
|| (!/^[0-9a-f]+$/.test(game.urlSlug) && game.urlSlug)
224-
|| '';
225-
220+
let slug =
221+
game.productSlug ||
222+
game.offerMappings?.find((i) => i.pageType === 'productHome')?.pageSlug ||
223+
game.catalogNs.mappings?.find((i) => i.pageType === 'productHome')
224+
?.pageSlug ||
225+
(!/^[0-9a-f]+$/.test(game.urlSlug) && game.urlSlug) ||
226+
'';
227+
226228
if (!slug) {
227229
logger.error(game, 'No slug foundable');
228230
}
229-
231+
230232
// Sanitize the slug (e.g. sludge-life/home -> sludge-life).
231233
const slugSlashIndex = slug.indexOf('/');
232234
if (slugSlashIndex >= 0) {
233235
slug = slug.slice(0, slugSlashIndex);
234236
}
235-
237+
236238
const link = slug
237239
? `https://www.epicgames.com/store/fr/p/${slug}`
238240
: 'https://store.epicgames.com/fr/free-games';
239-
240-
let thumbnail = game.keyImages.find((image) => image.type === 'Thumbnail')?.url;
241+
242+
let thumbnail = game.keyImages.find(
243+
(image) => image.type === 'Thumbnail',
244+
)?.url;
241245
thumbnail = thumbnail && encodeURI(thumbnail);
242-
243-
let banner = game.keyImages.find((image) => image.type === 'OfferImageWide')?.url;
246+
247+
let banner = game.keyImages.find(
248+
(image) => image.type === 'OfferImageWide',
249+
)?.url;
244250
banner = banner && encodeURI(banner);
245251

246252
return {

src/crons/GoG.ts

Lines changed: 56 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,34 @@
1-
import { MessageEmbed } from "discord.js";
2-
import got from "got";
3-
import { Logger } from "pino";
1+
import { MessageEmbed } from 'discord.js';
2+
import got from 'got';
3+
import { Logger } from 'pino';
44

5-
import { Cron, findTextChannelByName } from "../framework";
6-
import { parse } from "node-html-parser";
5+
import { Cron, findTextChannelByName } from '../framework';
6+
import { parse } from 'node-html-parser';
77

88
const dateFmtOptions: Intl.DateTimeFormatOptions = {
9-
timeZone: "Europe/Paris",
10-
year: "numeric",
11-
month: "long",
12-
day: "numeric",
13-
hour: "numeric",
14-
minute: "numeric"
9+
timeZone: 'Europe/Paris',
10+
year: 'numeric',
11+
month: 'long',
12+
day: 'numeric',
13+
hour: 'numeric',
14+
minute: 'numeric',
1515
};
1616

1717
export default new Cron({
1818
enabled: true,
19-
name: "GoG",
19+
name: 'GoG',
2020
description:
21-
"Vérifie tous les jours à 17h30 (Paris) si GoG offre un jeu (promotion gratuite) et alerte dans #jeux",
21+
'Vérifie tous les jours à 17h30 (Paris) si GoG offre un jeu (promotion gratuite) et alerte dans #jeux',
2222
schedule: '30 17 * * *',
2323
// schedule: "* * * * *", // switch for testing
2424
async handle(context) {
2525
const game = await getOfferedGame(context.logger);
2626
if (!game) {
2727
return;
2828
}
29-
30-
const channel = findTextChannelByName(context.client.channels, "jeux");
31-
29+
30+
const channel = findTextChannelByName(context.client.channels, 'jeux');
31+
3232
await channel.send(
3333
new MessageEmbed()
3434
.setTitle(game.title)
@@ -37,15 +37,15 @@ export default new Cron({
3737
.setThumbnail(game.thumbnail)
3838
.setImage(game.banner)
3939
.addField(
40-
"Fin",
41-
game.discountEndDate.toLocaleDateString("fr-FR", dateFmtOptions),
42-
true
40+
'Fin',
41+
game.discountEndDate.toLocaleDateString('fr-FR', dateFmtOptions),
42+
true,
4343
)
44-
.addField("Prix", `${game.originalPrice}€ → **Gratuit**`)
45-
.addField("Note", `⭐ ${game.rating}`)
46-
.setTimestamp()
44+
.addField('Prix', `${game.originalPrice}€ → **Gratuit**`)
45+
.addField('Note', `⭐ ${game.rating}`)
46+
.setTimestamp(),
4747
);
48-
}
48+
},
4949
});
5050

5151
/**
@@ -70,39 +70,53 @@ interface Game {
7070
async function getOfferedGame(logger: Logger): Promise<Game | null> {
7171
const GOG_GOT_OPTIONS = {
7272
headers: {
73-
"Accept-Language": "fr,fr-FR;q=0.8,fr-FR;q=0.7,en-US;q=0.5,en-US;q=0.3,en;q=0.2"
74-
}
73+
'Accept-Language':
74+
'fr,fr-FR;q=0.8,fr-FR;q=0.7,en-US;q=0.5,en-US;q=0.3,en;q=0.2',
75+
},
7576
};
76-
const { body: homeBody } = await got<string>("https://www.gog.com/#giveaway", GOG_GOT_OPTIONS);
77-
77+
const { body: homeBody } = await got<string>(
78+
'https://www.gog.com/#giveaway',
79+
GOG_GOT_OPTIONS,
80+
);
81+
7882
if (!homeBody) return null;
7983
const html = parse(homeBody);
8084
if (!html) return null;
81-
const SEOLink = html.querySelector("a#giveaway");
85+
const SEOLink = html.querySelector('a#giveaway');
8286
if (!SEOLink) return null;
8387
const endTimestamp = Number(
8488
html
85-
.querySelector(".giveaway-banner__footer gog-countdown-timer")
86-
?.getAttribute("end-date")
89+
.querySelector('.giveaway-banner__footer gog-countdown-timer')
90+
?.getAttribute('end-date'),
8791
);
88-
92+
8993
const { body: gameBody } = await got<string>(
90-
`https://www.gog.com${SEOLink.getAttribute("ng-href")}`,
91-
GOG_GOT_OPTIONS
94+
`https://www.gog.com${SEOLink.getAttribute('ng-href')}`,
95+
GOG_GOT_OPTIONS,
9296
);
9397
if (!gameBody) return null;
9498
const gameHTML = parse(gameBody);
9599
if (!gameHTML) return null;
96-
97-
const ldJSONNode = gameHTML.querySelector("script[type=\"application/ld+json\"]");
98-
const gameJSON = JSON.parse(ldJSONNode?.innerHTML ?? "");
100+
101+
const ldJSONNode = gameHTML.querySelector(
102+
'script[type="application/ld+json"]',
103+
);
104+
const gameJSON = JSON.parse(ldJSONNode?.innerHTML ?? '');
99105
if (!gameJSON) return null;
100-
101-
const description = gameHTML.querySelector(".content-summary-item__description")?.innerText ?? "";
102-
const banner = gameHTML.querySelector("head meta[property=\"og:image\"]")?.getAttribute("content") ?? "";
103-
104-
logger.info({ data: { gameJSON, description, banner } }, "Offered games response");
105-
106+
107+
const description =
108+
gameHTML.querySelector('.content-summary-item__description')?.innerText ??
109+
'';
110+
const banner =
111+
gameHTML
112+
.querySelector('head meta[property="og:image"]')
113+
?.getAttribute('content') ?? '';
114+
115+
logger.info(
116+
{ data: { gameJSON, description, banner } },
117+
'Offered games response',
118+
);
119+
106120
return {
107121
title: gameJSON.name,
108122
description,

0 commit comments

Comments
 (0)