feat(plugins/community/chess): add plugin (#1215) [skip ci]

This commit is contained in:
Simon Lecoq
2022-09-05 22:42:25 -04:00
committed by GitHub
parent d6636c00fe
commit 2239bc9297
18 changed files with 406 additions and 0 deletions

View File

@@ -52,3 +52,4 @@ ignore$
^\Qsource/templates/terminal/fonts.css\E$
^\Qsource/templates/terminal/partials/screenshot.ejs\E$
^\Qtests/mocks/api/github/rest/emojis/get.mjs\E$
^\Qtests/mocks/api/axios/get/lichess.mjs\E$

View File

@@ -174,6 +174,7 @@ libxml
libxmljs
libxss
libxtst
lichess
linux
lng
localhost
@@ -236,6 +237,8 @@ params
patchnote
pdated
pened
PGN
Pgn
PGP
playcount
playlists

View File

@@ -90,3 +90,7 @@ place_id: ".*"
# ignore long runs of a single character:
\b([A-Za-z])\g{-1}{3,}\b
# Chess patterns
\brnbqkbnr\b
\bRNBQKBNR\b

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -12,6 +12,8 @@ on:
required: true
METRICS_TOKEN_PERSONAL:
required: true
CHESS_TOKEN:
required: true
PAGESPEED_TOKEN:
required: true
GOOGLE_MAP_TOKEN:

14
package-lock.json generated
View File

@@ -19,6 +19,7 @@
"@primer/css": "^20.4.3",
"@primer/octicons": "^17.5.0",
"axios": "^0.27.2",
"chess.js": "*",
"clipboard": "^2.0.11",
"color": "^4.2.3",
"compression": "^1.7.4",
@@ -57,6 +58,7 @@
"libxmljs2": "^0.30.1"
},
"optionalDependencies": {
"chess.js": "^1.0.0-alpha.0",
"gifencoder": "^2.0.1",
"libxmljs2": "^0.30.1",
"node-chartist": "^1.0.5"
@@ -3125,6 +3127,12 @@
"entities": "^4.3.0"
}
},
"node_modules/chess.js": {
"version": "1.0.0-alpha.0",
"resolved": "https://registry.npmjs.org/chess.js/-/chess.js-1.0.0-alpha.0.tgz",
"integrity": "sha512-nwEDlNEOZR/FaKW1DZLYtjgJrtAlxBW/6Fp4Hk8QXqpCCLrXweOU6/vKfJ/jNHT2gPqasxi0/ZPslcWAAdzozg==",
"optional": true
},
"node_modules/chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
@@ -12435,6 +12443,12 @@
}
}
},
"chess.js": {
"version": "1.0.0-alpha.0",
"resolved": "https://registry.npmjs.org/chess.js/-/chess.js-1.0.0-alpha.0.tgz",
"integrity": "sha512-nwEDlNEOZR/FaKW1DZLYtjgJrtAlxBW/6Fp4Hk8QXqpCCLrXweOU6/vKfJ/jNHT2gPqasxi0/ZPslcWAAdzozg==",
"optional": true
},
"chownr": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",

View File

@@ -81,6 +81,7 @@
"transform": {}
},
"optionalDependencies": {
"chess.js": "^1.0.0-alpha.0",
"gifencoder": "^2.0.1",
"libxmljs2": "^0.30.1",
"node-chartist": "^1.0.5"

View File

@@ -1043,6 +1043,30 @@
},
})
: null),
//Chess
...(set.plugins.enabled.chess
? ({
chess: {
platform: options["chess.platform"] || "(chess platform)",
meta: {
Event: "Casual Correspondence game",
Date: faker.date.recent().toISOString().substring(0, 10),
White: options["chess.user"],
Black: faker.internet.userName(),
WhiteElo: faker.datatype.number(3000),
BlackElo: faker.datatype.number(3000),
},
animation: { size: 40, delay: 3, duration: 0.6 },
result: { white: faker.datatype.number(3), get black() { return this.white + faker.helpers.arrayElement([-1, +1]) } },
moves: [
{color: "w", piece: "p", from: "f2", to: "f4", san: "f4", flags: "b"},
{color: "b", piece: "p", from: "c7", to: "c5", san: "c5", flags: "b"},
{color: "w", piece: "p", from: "e2", to: "e4", san: "e4", flags: "b"},
{color: "b", piece: "p", from: "d7", to: "d6", san: "d6", flags: "n"},
]
},
})
: null),
//Activity
...(set.plugins.enabled.activity
? ({

View File

@@ -0,0 +1,12 @@
<!--header-->
<!--/header-->
## ➡️ Available options
<!--options-->
<!--/options-->
## Examples workflows
<!--examples-->
<!--/examples-->

View File

@@ -0,0 +1,10 @@
- name: Last chess game from lichess.org
uses: lowlighter/metrics@latest
with:
filename: metrics.plugin.chess.svg
token: NOT_NEEDED
base: ""
plugin_chess: yes
plugin_chess_token: ${{ secrets.CHESS_TOKEN }}
plugin_chess_platform: lichess.org

View File

@@ -0,0 +1,48 @@
//Imports
import { Chess } from "chess.js"
//Setup
export default async function({login, q, imports, data, account}, {enabled = false, token = "", extras = false} = {}) {
//Plugin execution
try {
//Check if plugin is enabled and requirements are met
if ((!q.chess) || (!imports.metadata.plugins.chess.enabled(enabled, {extras})))
return null
//Load inputs
const {user, platform, animation} = imports.metadata.plugins.chess.inputs({data, account, q})
for (const [key, defaulted] of Object.entries({size:40, delay:1, duration:4})) {
if (Number.isNaN(Number(animation[key])))
animation[key] = defaulted
if (animation[key] < 0)
animation[key] = defaulted
}
//Fetch PGN
console.debug(`metrics/compute/${login}/plugins > chess > fetching last game from ${platform}`)
let PGN
switch (platform) {
case "lichess.org":
PGN = (await imports.axios.get(`https://lichess.org/api/games/user/${user}?max=1`, {headers: {Authorization: `Bearer ${token}`}})).data
break
case "":
throw {error: {message: "Unspecified platform"}}
default:
throw {error: {message: `Unsupported platform "${platform}"`}}
}
//Parse PGN
const board = new Chess()
board.loadPgn(PGN)
const moves = board.history({verbose: true})
const meta = board.header()
const result = Object.fromEntries(meta.Result.split("-").map((score, i) => [i ? "black" : "white", Number(score)]))
//Results
return {platform, meta, moves, animation, result}
}
//Handle errors
catch (error) {
throw imports.format.error(error)
}
}

View File

@@ -0,0 +1,61 @@
name: ♟️ Chess
category: community
description: |
This plugin displays the last game you played on a supported chess platform.
disclaimer: |
This plugin is not affiliated, associated, authorized, endorsed by, or in any way officially connected with any of the supported provider.
All product and company names are trademarks™ or registered® trademarks of their respective holders.
examples:
default: https://github.com/lowlighter/metrics/blob/examples/metrics.plugin.chess.svg
authors:
- lowlighter
supports:
- user
- organization
- repository
scopes: []
inputs:
plugin_chess:
description: |
Enable chess plugin
type: boolean
default: no
plugin_chess_token:
description: |
Chess platform token
type: token
default: ""
extras:
- metrics.api.chess.any
plugin_chess_user:
description: |
AniList login
type: string
default: .user.login
preset: no
plugin_chess_platform:
description: |
Chess platform
type: string
default: ""
values:
- lichess.org
plugin_chess_animation:
description: |
Animation settings
- `size` is the size of a single chessboard square in pixels (board will be 8 times larger)
- `delay` is the delay before starting animation (in seconds)
- `duration` is the duration of the animation of a move (in seconds)
type: json
default: |
{
"size": 40,
"delay": 3,
"duration": 0.6
}

View File

@@ -37,6 +37,7 @@
"achievements",
"screenshot",
"code",
"chess",
"sponsors",
"poopmap",
"fortune"

View File

@@ -0,0 +1,194 @@
<% if (plugins.chess) { %>
<section>
<h2 class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M1.5 3.25c0-.966.784-1.75 1.75-1.75h2.5c.966 0 1.75.784 1.75 1.75v2.5A1.75 1.75 0 015.75 7.5h-2.5A1.75 1.75 0 011.5 5.75v-2.5zM3.25 3a.25.25 0 00-.25.25v2.5c0 .138.112.25.25.25h2.5A.25.25 0 006 5.75v-2.5A.25.25 0 005.75 3h-2.5zM1.5 10.25c0-.966.784-1.75 1.75-1.75h2.5c.966 0 1.75.784 1.75 1.75v2.5a1.75 1.75 0 01-1.75 1.75h-2.5a1.75 1.75 0 01-1.75-1.75v-2.5zM3.25 10a.25.25 0 00-.25.25v2.5c0 .138.112.25.25.25h2.5a.25.25 0 00.25-.25v-2.5a.25.25 0 00-.25-.25h-2.5zM8.5 3.25c0-.966.784-1.75 1.75-1.75h2.5c.966 0 1.75.784 1.75 1.75v2.5a1.75 1.75 0 01-1.75 1.75h-2.5A1.75 1.75 0 018.5 5.75v-2.5zM10.25 3a.25.25 0 00-.25.25v2.5c0 .138.112.25.25.25h2.5a.25.25 0 00.25-.25v-2.5a.25.25 0 00-.25-.25h-2.5zM8.5 10.25c0-.966.784-1.75 1.75-1.75h2.5c.966 0 1.75.784 1.75 1.75v2.5a1.75 1.75 0 01-1.75 1.75h-2.5a1.75 1.75 0 01-1.75-1.75v-2.5zm1.75-.25a.25.25 0 00-.25.25v2.5c0 .138.112.25.25.25h2.5a.25.25 0 00.25-.25v-2.5a.25.25 0 00-.25-.25h-2.5z"></path></svg>
Last chess game
</h2>
<% if (plugins.chess.error) { %>
<div class="row">
<section>
<div class="field error">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1113.657 2.343 8 8 0 012.343 13.657zM6.03 4.97a.75.75 0 00-1.06 1.06L6.94 8 4.97 9.97a.75.75 0 101.06 1.06L8 9.06l1.97 1.97a.75.75 0 101.06-1.06L9.06 8l1.97-1.97a.75.75 0 10-1.06-1.06L8 6.94 6.03 4.97z"></path></svg>
<%= plugins.chess.error.message %>
</div>
</section>
</div>
<% } else { %>
<div class="row largeable-align-start">
<section class="largeable-column-fields">
<div class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M2.002 2.725a.75.75 0 01.797-.699C8.79 2.42 13.58 7.21 13.974 13.201a.75.75 0 11-1.497.098 10.502 10.502 0 00-9.776-9.776.75.75 0 01-.7-.798zM2 13a1 1 0 112 0 1 1 0 01-2 0zm.84-5.95a.75.75 0 00-.179 1.489c2.509.3 4.5 2.291 4.8 4.8a.75.75 0 101.49-.178A7.003 7.003 0 002.838 7.05z"></path></svg>
From <%= plugins.chess.platform %>
</div>
<div class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M10.5 5a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0zm.061 3.073a4 4 0 10-5.123 0 6.004 6.004 0 00-3.431 5.142.75.75 0 001.498.07 4.5 4.5 0 018.99 0 .75.75 0 101.498-.07 6.005 6.005 0 00-3.432-5.142z"></path></svg>
<%= plugins.chess.meta["White"] %> <% if (plugins.chess.meta["WhiteElo"]) { %>(<%= plugins.chess.meta["WhiteElo"] %> ELO)<% } %>
</div>
<div class="field">
<% if (plugins.chess.result.white > plugins.chess.result.black) { %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M3.217 6.962A3.75 3.75 0 010 3.25v-.5C0 1.784.784 1 1.75 1h1.356c.228-.585.796-1 1.462-1h6.864a1.57 1.57 0 011.462 1h1.356c.966 0 1.75.784 1.75 1.75v.5a3.75 3.75 0 01-3.217 3.712 5.014 5.014 0 01-2.771 3.117l.144 1.446c.005.05.03.12.114.204.086.087.217.17.373.227.283.103.618.274.89.568.285.31.467.723.467 1.226v.75h1.25a.75.75 0 110 1.5H2.75a.75.75 0 010-1.5H4v-.75c0-.503.182-.916.468-1.226.27-.294.606-.465.889-.568a1.03 1.03 0 00.373-.227c.084-.085.109-.153.114-.204l.144-1.446a5.014 5.014 0 01-2.77-3.117zM3 2.5H1.75a.25.25 0 00-.25.25v.5c0 .98.626 1.813 1.5 2.122V2.5zm4.457 7.97l-.12 1.204c-.093.925-.858 1.47-1.467 1.691a.764.764 0 00-.3.176c-.037.04-.07.093-.07.21v.75h5v-.75c0-.117-.033-.17-.07-.21a.763.763 0 00-.3-.176c-.609-.221-1.374-.766-1.466-1.69l-.12-1.204a5.052 5.052 0 01-1.087 0zM13 5.373V2.5h1.25a.25.25 0 01.25.25v.5A2.25 2.25 0 0113 5.372zM4.5 1.568c0-.037.03-.068.068-.068h6.864c.037 0 .068.03.068.068V5.5a3.5 3.5 0 11-7 0V1.568z"></path></svg>
<% } else { %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 5.5a2.5 2.5 0 100 5 2.5 2.5 0 000-5zM4 8a4 4 0 118 0 4 4 0 01-8 0z"></path></svg>
<% } %>
<%= plugins.chess.result.white %> victor<%= s(plugins.chess.result.white, "y") %>
</div>
</section>
<section class="largeable-column-fields">
<div class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M4.75 0a.75.75 0 01.75.75V2h5V.75a.75.75 0 011.5 0V2h1.25c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0113.25 16H2.75A1.75 1.75 0 011 14.25V3.75C1 2.784 1.784 2 2.75 2H4V.75A.75.75 0 014.75 0zm0 3.5h8.5a.25.25 0 01.25.25V6h-11V3.75a.25.25 0 01.25-.25h2zm-2.25 4v6.75c0 .138.112.25.25.25h10.5a.25.25 0 00.25-.25V7.5h-11z"></path></svg>
<%= plugins.chess.meta["Date"] %>
</div>
<div class="field">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path d="M4.243 4.757a3.757 3.757 0 115.851 3.119 6.006 6.006 0 013.9 5.339.75.75 0 01-.715.784H2.721a.75.75 0 01-.714-.784 6.006 6.006 0 013.9-5.34 3.753 3.753 0 01-1.664-3.118z"></path></svg>
<%= plugins.chess.meta["Black"] %> <% if (plugins.chess.meta["BlackElo"]) { %>(<%= plugins.chess.meta["BlackElo"] %> ELO)<% } %>
</div>
<div class="field">
<% if (plugins.chess.result.black > plugins.chess.result.white) { %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M3.217 6.962A3.75 3.75 0 010 3.25v-.5C0 1.784.784 1 1.75 1h1.356c.228-.585.796-1 1.462-1h6.864a1.57 1.57 0 011.462 1h1.356c.966 0 1.75.784 1.75 1.75v.5a3.75 3.75 0 01-3.217 3.712 5.014 5.014 0 01-2.771 3.117l.144 1.446c.005.05.03.12.114.204.086.087.217.17.373.227.283.103.618.274.89.568.285.31.467.723.467 1.226v.75h1.25a.75.75 0 110 1.5H2.75a.75.75 0 010-1.5H4v-.75c0-.503.182-.916.468-1.226.27-.294.606-.465.889-.568a1.03 1.03 0 00.373-.227c.084-.085.109-.153.114-.204l.144-1.446a5.014 5.014 0 01-2.77-3.117zM3 2.5H1.75a.25.25 0 00-.25.25v.5c0 .98.626 1.813 1.5 2.122V2.5zm4.457 7.97l-.12 1.204c-.093.925-.858 1.47-1.467 1.691a.764.764 0 00-.3.176c-.037.04-.07.093-.07.21v.75h5v-.75c0-.117-.033-.17-.07-.21a.763.763 0 00-.3-.176c-.609-.221-1.374-.766-1.466-1.69l-.12-1.204a5.052 5.052 0 01-1.087 0zM13 5.373V2.5h1.25a.25.25 0 01.25.25v.5A2.25 2.25 0 0113 5.372zM4.5 1.568c0-.037.03-.068.068-.068h6.864c.037 0 .068.03.068.068V5.5a3.5 3.5 0 11-7 0V1.568z"></path></svg>
<% } else { %>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8z"></path></svg>
<% } %>
<%= plugins.chess.result.black %> victor<%= s(plugins.chess.result.black, "y") %>
</div>
</section>
</div>
<div class="chess-board">
<% {
//Utilities functions
const pieces = {
w: {p: "♙", r: "♖", n: "♘", b: "♗", q: "♕", k: "♔"},
b: {p: "♟&#xFE0E;", r: "♜", n: "♞", b: "♝", q: "♛", k: "♚"},
}
const init = ["rnbqkbnr", "pppppppp", "", "", "", "", "PPPPPPPP", "RNBQKBNR"]
const column = p => ["a", "b", "c", "d", "e", "f", "g", "h"].indexOf(p[0])
const row = p => ["8", "7", "6", "5", "4", "3", "2", "1"].indexOf(p[1])
const animation = plugins.chess.animation
const size = animation.size
//Empty board
for (let i = 0; i < 8; i++) { %>
<div class="chess-row">
<% for (let j = 0; j < 8; j++) { %>
<div class="chess-square <%= (i + j) % 2 ? 'black' : 'white' %>"></div>
<% } %>
</div>
<% }
//Initial board state
for (let i = 0; i < init.length; i++) { for (let j = 0; j < init[i].length; j++) {
const color = /[A-Z]/.test(init[i][j]) ? "w" : "b", piece = init[i][j].toLocaleLowerCase() %>
<div class="chess-move">
<div class="init" style="left: <%= j*size %>px; top: <%= i*size %>px;"><%- pieces[color][piece] %></div>
</div>
<% }}
//Draw moves
const moves = plugins.chess.moves
for (let i = 0; i < moves.length; i++) { const {color, piece, from, to} = moves[i] %>
<div class="chess-move chess-move-<%= i %>">
<div class="from" style="left: <%= column(from)*size %>px; top: <%= row(from)*size %>px;"><%- pieces[color][piece] %></div>
<div class="to" style="left: <%= column(from)*size %>px; top: <%= row(from)*size %>px;"><%- pieces[color][piece] %></div>
<style>
@keyframes chess-move-<%= i %>-from {
100% {
opacity: 1;
color: transparent;
background-color: <%= (row(from)+column(from))%2 ? "#C5A076" : "#ECDAB9" %>;
}
}
.chess-move-<%= i %> .from {
animation-name: chess-move-<%= i %>-from;
animation-delay: <%= i * animation.duration + animation.delay %>s;
}
@keyframes chess-move-<%= i %>-to {
1% {
opacity: 1;
}
99% {
background-color: transparent;
}
100% {
opacity: 1;
background-color: <%= (row(to)+column(to))%2 ? "#C5A076" : "#ECDAB9" %>;
left: <%= column(to)*size %>px;
top: <%= row(to)*size %>px;
}
}
.chess-move-<%= i %> .to {
animation-name: chess-move-<%= i %>-to;
animation-delay: <%= i * animation.duration + animation.delay %>s;
}
</style>
</div>
<% } %>
<style>
.chess-board {
width: <%= 8*size %>px;
height: <%= 8*size %>px;
position: relative;
margin: 6px auto 0;
}
.chess-square, .chess-move .from, .chess-move .to, .chess-move .init {
width: <%= size %>px;
height: <%= size %>px;
float: left;
}
.chess-square.white {
background-color: #ECDAB9;
}
.chess-square.black {
background-color: #C5A076;
}
.chess-clock {
width: <%= 8*size %>px;
height: 4px;
margin: auto;
background-color: #58A6FF40;
margin-bottom: 6px;
}
.chess-clock .chess-time {
width: 0%;
height: 100%;
background-color: #58A6FF;
animation-fill-mode: forwards;
animation-name: chess-clock;
animation-delay: <%= animation.delay %>s;
animation-duration: <%= moves.length*animation.duration %>s;
animation-timing-function: linear;
}
@keyframes chess-clock {
100% {
width: 100%;
}
}
.chess-move {
position: absolute;
top: 0;
left: 0;
}
.chess-move .from, .chess-move .to, .chess-move .init {
position: absolute;
display: flex;
align-items: center;
justify-content: center;
font-size: <%= 0.8*size %>px;
animation-fill-mode: forwards;
opacity: 0;
}
.chess-move .init {
opacity: 1;
}
.chess-move .to {
animation-duration: <%= animation.duration %>s;
}
</style>
<% } %>
</div>
<div class="chess-clock">
<div class="chess-time"></div>
</div>
<% } %>
</section>
<% } %>

View File

@@ -0,0 +1,30 @@
/**Mocked data */
export default function({faker, url, options, login = faker.internet.userName()}) {
//Wakatime api
if (/^https:..lichess.org.api.games.user.*$/.test(url)) {
console.debug(`metrics/compute/mocks > mocking lichess api result > ${url}`)
return ({
status: 200,
data: `
[Event "It (cat.17)"]
[Site "Wijk aan Zee (Netherlands)"]
[Date "1999.??.??"]
[Round "?"]
[White "Garry Kasparov"]
[Black "Veselin Topalov"]
[Result "1-0"]
1. e4 d6 2. d4 Nf6 3. Nc3 g6 4. Be3 Bg7 5. Qd2 c6 6. f3 b5 7. Nge2 Nbd7 8. Bh6
Bxh6 9. Qxh6 Bb7 10. a3 e5 11. O-O-O Qe7 12. Kb1 a6 13. Nc1 O-O-O 14. Nb3 exd4
15. Rxd4 c5 16. Rd1 Nb6 17. g3 Kb8 18. Na5 Ba8 19. Bh3 d5 20. Qf4+ Ka7 21. Rhe1
d4 22. Nd5 Nbxd5 23. exd5 Qd6 24. Rxd4 cxd4 25. Re7+ Kb6 26. Qxd4+ Kxa5 27. b4+
Ka4 28. Qc3 Qxd5 29. Ra7 Bb7 30. Rxb7 Qc4 31. Qxf6 Kxa3 32. Qxa6+ Kxb4 33. c3+
Kxc3 34. Qa1+ Kd2 35. Qb2+ Kd1 36. Bf1 Rd2 37. Rd7 Rxd7 38. Bxc4 bxc4 39. Qxh8
Rd3 40. Qa8 c3 41. Qa4+ Ke1 42. f4 f5 43. Kc1 Rd2 44. Qa7 1-0`.trim()
})
}
}

View File

@@ -11,5 +11,6 @@
"TWITTER_TOKEN":"MOCKED_TOKEN",
"GOOGLE_MAP_TOKEN": "MOCKED_TOKEN",
"STOCK_TOKEN":"MOCKED_TOKEN",
"CHESS_TOKEN":"MOCKED_TOKEN",
"POOPMAP_TOKEN":"MOCKED_TOKEN"
}