yolkbot 0.1.2-alpha.3 → 0.1.2-alpha.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "yolkbot",
3
3
  "description": "create a shell shockers bot in under 10 lines.",
4
- "version": "0.1.2-alpha.3",
4
+ "version": "0.1.2-alpha.31",
5
5
  "keywords": [
6
6
  "shell shockers",
7
7
  "shellshock.io",
@@ -82,14 +82,14 @@
82
82
  },
83
83
  "dependencies": {
84
84
  "smallsocks": "^1.0.2",
85
- "undici": "^7.7.0",
86
- "ws": "^8.18.0"
85
+ "undici": "^7.8.0",
86
+ "ws": "^8.18.1"
87
87
  },
88
88
  "devDependencies": {
89
- "@eslint/js": "^9.20.0",
90
- "@stylistic/eslint-plugin": "^4.0.1",
91
- "esbuild": "^0.25.0",
92
- "eslint": "^9.20.1",
89
+ "@eslint/js": "^9.25.0",
90
+ "@stylistic/eslint-plugin": "^4.2.0",
91
+ "esbuild": "^0.25.2",
92
+ "eslint": "^9.25.0",
93
93
  "globals": "^15.15.0"
94
94
  }
95
95
  }
package/src/api.js CHANGED
@@ -31,6 +31,8 @@ const queryServices = async (request, proxy = '', instance = 'shellshock.io') =>
31
31
  let resolved = false;
32
32
 
33
33
  ws.onmessage = (mes) => {
34
+ resolved = true;
35
+
34
36
  try {
35
37
  const resp = JSON.parse(mes.data);
36
38
  resolve(resp);
@@ -43,7 +45,6 @@ const queryServices = async (request, proxy = '', instance = 'shellshock.io') =>
43
45
  resolve('bad_json');
44
46
  }
45
47
 
46
- resolved = true;
47
48
  ws.close();
48
49
  };
49
50
 
@@ -97,7 +98,7 @@ async function loginWithCredentials(email, password, proxy = '', instance = 'she
97
98
  'x-client-version': 'Chrome/JsCore/9.17.2/FirebaseCore-web',
98
99
  'x-firebase-locale': 'en'
99
100
  },
100
- dispatcher: proxy ? new globals.ProxyAgent(proxy) : undefined
101
+ dispatcher: proxy ? new globals.ProxyAgent(proxy.replace(/socks([4|5|4a|5h]+)/g, 'https')) : undefined
101
102
  });
102
103
 
103
104
  body = await request.json();
@@ -158,7 +159,7 @@ async function loginWithRefreshToken(refreshToken, proxy = '', instance = 'shell
158
159
  'x-client-version': 'Chrome/JsCore/9.17.2/FirebaseCore-web',
159
160
  'x-firebase-locale': 'en'
160
161
  },
161
- dispatcher: proxy ? new globals.ProxyAgent(proxy) : undefined
162
+ dispatcher: proxy ? new globals.ProxyAgent(proxy.replace(/socks([4|5|4a|5h]+)/g, 'https')) : undefined
162
163
  });
163
164
 
164
165
  body = await request.json();
@@ -206,7 +207,7 @@ async function loginAnonymously(proxy = '', instance = 'shellshock.io') {
206
207
  'x-client-version': 'Chrome/JsCore/9.17.2/FirebaseCore-web',
207
208
  'x-firebase-locale': 'en'
208
209
  },
209
- dispatcher: proxy ? new globals.ProxyAgent(proxy) : undefined
210
+ dispatcher: proxy ? new globals.ProxyAgent(proxy.replace(/socks([4|5|4a|5h]+)/g, 'https')) : undefined
210
211
  });
211
212
 
212
213
  const body = await req.json();
package/src/bot.js CHANGED
@@ -43,7 +43,8 @@ const intents = {
43
43
  PING: 5,
44
44
  COSMETIC_DATA: 6,
45
45
  PLAYER_HEALTH: 7,
46
- PACKET_HOOK: 8
46
+ PACKET_HOOK: 8,
47
+ MONITOR: 9
47
48
  }
48
49
 
49
50
  export class Bot {
@@ -68,8 +69,9 @@ export class Bot {
68
69
 
69
70
  // private information NOT FOR OTHER PLAYERS!!
70
71
  this.state = {
71
- // kept for specifying socket open sequence
72
+ // kept for specifying various params
72
73
  name: '',
74
+ weaponIdx: 0,
73
75
 
74
76
  // tracking for dispatch checks
75
77
  reloading: false,
@@ -77,7 +79,10 @@ export class Bot {
77
79
  usingMelee: false,
78
80
 
79
81
  // shots fired ezzz
80
- shotsFired: 0
82
+ shotsFired: 0,
83
+
84
+ // holy glitch
85
+ quit: false
81
86
  }
82
87
 
83
88
  this.players = {}
@@ -260,7 +265,7 @@ export class Bot {
260
265
  this.account.password = pass;
261
266
 
262
267
  const loginData = await createAccount(email, pass, this.proxy, this.instance);
263
- return await this.#processLoginData(loginData);
268
+ return await this.#processLoginData(loginData.playerOutput);
264
269
  }
265
270
 
266
271
  async login(email, pass) {
@@ -268,12 +273,12 @@ export class Bot {
268
273
  this.account.password = pass;
269
274
 
270
275
  const loginData = await loginWithCredentials(email, pass, this.proxy, this.instance);
271
- return await this.#processLoginData(loginData);
276
+ return await this.#processLoginData(loginData.playerOutput);
272
277
  }
273
278
 
274
279
  async loginWithRefreshToken(refreshToken) {
275
280
  const loginData = await loginWithRefreshToken(refreshToken, this.proxy, this.instance);
276
- return await this.#processLoginData(loginData);
281
+ return await this.#processLoginData(loginData.playerOutput);
277
282
  }
278
283
 
279
284
  async loginAnonymously() {
@@ -281,7 +286,7 @@ export class Bot {
281
286
  delete this.account.password;
282
287
 
283
288
  const loginData = await loginAnonymously(this.proxy, this.instance);
284
- return await this.#processLoginData(loginData);
289
+ return await this.#processLoginData(loginData.playerOutput);
285
290
  }
286
291
 
287
292
  async #processLoginData(loginData) {
@@ -387,104 +392,13 @@ export class Bot {
387
392
  });
388
393
  }
389
394
 
390
- async #onGameMesssage(msg) {
391
- CommIn.init(msg.data);
392
-
393
- let out;
394
- const cmd = CommIn.unPackInt8U();
395
-
396
- switch (cmd) {
397
- case CommCode.socketReady:
398
- out = CommOut.getBuffer();
399
- out.packInt8(CommCode.joinGame);
400
-
401
- out.packString(this.state.name); // name
402
- out.packString(this.game.raw.uuid); // game id
403
-
404
- out.packInt8(0); // hidebadge
405
- out.packInt8(0); // weapon choice
406
-
407
- out.packInt32(this.account.session); // session int
408
- out.packString(this.account.firebaseId); // firebase id
409
- out.packString(this.account.sessionId); // session id
410
-
411
- out.send(this.game.socket);
412
- break;
413
-
414
- case CommCode.gameJoined: {
415
- this.me.id = CommIn.unPackInt8U();
416
- // console.log("My id is:", this.me.id);
417
- this.me.team = CommIn.unPackInt8U();
418
- // console.log("My team is:", this.me.team);
419
- this.game.gameModeId = CommIn.unPackInt8U(); // aka gameType
420
- this.game.gameMode = GameModesById[this.game.gameModeId];
421
- // console.log("Gametype:", this.game.gameMode, this.game.gameModeId);
422
- this.game.mapIdx = CommIn.unPackInt8U();
423
- this.game.map = Maps[this.game.mapIdx];
424
- if (this.intents.includes(this.Intents.PATHFINDING)) {
425
- this.game.map.raw = await this.#fetchMap(this.game.map.filename, this.game.map.hash);
426
- this.pathing.nodeList = new NodeList(this.game.map.raw);
427
- if (this.game.gameModeId === GameModes.kotc) this.#initKotcZones();
428
- }
429
- // console.log("Map:", this.game.map);
430
- this.game.playerLimit = CommIn.unPackInt8U();
431
- // console.log("Player limit:", this.game.playerLimit);
432
- this.game.isGameOwner = CommIn.unPackInt8U() == 1;
433
- // console.log("Is game owner:", this.game.isGameOwner);
434
- this.game.isPrivate = CommIn.unPackInt8U() == 1;
435
- // console.log("Is private game:", this.game.isPrivate);
436
-
437
- // console.log('Successfully joined game.');
438
- this.state.joinedGame = true;
439
- this.lastDeathTime = Date.now();
440
-
441
- const out = CommOut.getBuffer();
442
- out.packInt8(CommCode.clientReady);
443
- out.send(this.game.socket);
444
-
445
- this.game.socket.onmessage = (msg) => this._packetQueue.push(msg.data);
446
-
447
- if (this.autoUpdate)
448
- this.updateIntervalId = setInterval(() => this.update(), this.updateInterval);
449
-
450
- if (this.intents.includes(this.Intents.PING)) {
451
- const out = CommOut.getBuffer();
452
- out.packInt8(CommCode.ping);
453
- out.send(this.game.socket);
454
- this.lastPingTime = Date.now();
455
- }
456
- break;
457
- }
458
-
459
- case CommCode.eventModifier:
460
- // console.log("Echoed eventModifier"); // why the fuck do you need to do this
461
- out = CommOut.getBuffer();
462
- out.packInt8(CommCode.eventModifier);
463
- out.send(this.game.socket);
464
- break;
465
-
466
- case CommCode.requestGameOptions:
467
- this.#processGameRequestOptionsPacket();
468
- break;
469
-
470
- default:
471
- try {
472
- const inferredCode = Object.entries(CommCode).filter(([, v]) => v == cmd)[0][0];
473
- console.error('onGameMessage: Received but did not handle a:', inferredCode);
474
- // packet could potentially not exist, then [0][0] will error
475
- } catch {
476
- console.error('onGameMessage: Unexpected packet received during startup: ' + cmd);
477
- }
478
- }
479
- }
480
-
481
395
  // region - a region id ('useast', 'germany', etc)
482
396
  // mode - a mode name that corresponds to a GameMode id
483
397
  // map - the name of a map
484
398
  async createPrivateGame(opts = {}) {
485
399
  if (!await this.initMatchmaker()) return false;
486
400
 
487
- if (!opts.region) { throw new Error('pass a region: createPrivateGame({ region: "useast", ... })') }
401
+ if (!opts.region) throw new Error('pass a region: createPrivateGame({ region: "useast", ... })')
488
402
  if (!this.matchmaker.regionList.find(r => r.id == opts.region))
489
403
  throw new Error('invalid region, see <bot>.matchmaker.regionList for a region list (pass an "id")')
490
404
 
@@ -571,7 +485,7 @@ export class Bot {
571
485
  this.game.socket.onerror = null;
572
486
  }
573
487
 
574
- this.game.socket.onmessage = this.#onGameMesssage.bind(this);
488
+ this.game.socket.onmessage = (msg) => this.processPacket(msg.data);
575
489
 
576
490
  this.game.socket.onclose = (e) => {
577
491
  // console.log('Game socket closed:', e.code, Object.entries(CloseCode).filter(([, v]) => v == e.code));
@@ -641,6 +555,7 @@ export class Bot {
641
555
 
642
556
  update() {
643
557
  if (!this.state.joinedGame) throw new Error('You cannot call update() if the bot is not in a game.');
558
+ if (this.state.quit) return;
644
559
 
645
560
  // process pathfinding
646
561
  if (this.pathing.followingPath && this.intents.includes(this.Intents.PATHFINDING)) this.#processPathfinding();
@@ -665,19 +580,20 @@ export class Bot {
665
580
  if (now - this.lastUpdateTime >= 50) {
666
581
  this.emit('tick');
667
582
 
668
- // Send out update packet
669
- const out = CommOut.getBuffer();
670
- out.packInt8(CommCode.syncMe);
671
- out.packInt8(Math.random() * 128 | 0); // stateIdx
672
- out.packInt8(this.me.serverStateIdx); // serverStateIdx
673
- for (let i = 0; i < 3; i++) {
674
- out.packInt8(this.controlKeys); // controlkeys
675
- out.packInt8(this.state.shotsFired); // shots fired
676
- out.packRadU(this.me.view.yaw); // yaw
677
- out.packRad(this.me.view.pitch); // pitch
678
- out.packInt8(100); // fixes commcode issues, does nothing
583
+ if (!this.intents.includes(this.Intents.MONITOR)) {
584
+ const out = CommOut.getBuffer();
585
+ out.packInt8(CommCode.syncMe);
586
+ out.packInt8(Math.random() * 128 | 0); // stateIdx
587
+ out.packInt8(this.me.serverStateIdx); // serverStateIdx
588
+ for (let i = 0; i < 3; i++) {
589
+ out.packInt8(this.controlKeys); // controlkeys
590
+ out.packInt8(this.state.shotsFired); // shots fired
591
+ out.packRadU(this.me.view.yaw); // yaw
592
+ out.packRad(this.me.view.pitch); // pitch
593
+ out.packInt8(100); // fixes commcode issues, does nothing
594
+ }
595
+ out.send(this.game.socket);
679
596
  }
680
- out.send(this.game.socket);
681
597
 
682
598
  this.lastUpdateTime = now;
683
599
  this.state.shotsFired = 0;
@@ -702,6 +618,8 @@ export class Bot {
702
618
  #mustBeInstant = ['authFail', 'banned'];
703
619
 
704
620
  emit(event, ...args) {
621
+ if (this.state.quit) return;
622
+
705
623
  if (this._hooks[event]) {
706
624
  for (const cb of this._hooks[event]) {
707
625
  if (this.#mustBeInstant.includes(event)) cb(...args);
@@ -1114,9 +1032,11 @@ export class Bot {
1114
1032
  }
1115
1033
 
1116
1034
  #processEventModifierPacket() {
1117
- const out = CommOut.getBuffer();
1118
- out.packInt8(CommCode.eventModifier);
1119
- out.send(this.game.socket);
1035
+ if (!this.intents.includes(this.Intents.MONITOR)) {
1036
+ const out = CommOut.getBuffer();
1037
+ out.packInt8(CommCode.eventModifier);
1038
+ out.send(this.game.socket);
1039
+ }
1120
1040
  }
1121
1041
 
1122
1042
  #processRemovePlayerPacket() {
@@ -1350,7 +1270,7 @@ export class Bot {
1350
1270
 
1351
1271
  this.emit('pingUpdate', oldPing, this.ping);
1352
1272
 
1353
- setTimeout(() => {
1273
+ if (!this.intents.includes(this.Intents.MONITOR)) setTimeout(() => {
1354
1274
  const out = CommOut.getBuffer();
1355
1275
  out.packInt8(CommCode.ping);
1356
1276
  out.send(this.game.socket);
@@ -1460,22 +1380,26 @@ export class Bot {
1460
1380
  }
1461
1381
 
1462
1382
  #processGameRequestOptionsPacket() {
1463
- const out = CommOut.getBuffer();
1464
- out.packInt8(CommCode.gameOptions);
1465
- out.packInt8(this.game.options.gravity * 4);
1466
- out.packInt8(this.game.options.damage * 4);
1467
- out.packInt8(this.game.options.healthRegen * 4);
1383
+ if (!this.intents.includes(this.Intents.MONITOR)) {
1384
+ const out = CommOut.getBuffer();
1385
+ out.packInt8(CommCode.gameOptions);
1386
+ out.packInt8(this.game.options.gravity * 4);
1387
+ out.packInt8(this.game.options.damage * 4);
1388
+ out.packInt8(this.game.options.healthRegen * 4);
1468
1389
 
1469
- const flags =
1470
- (this.game.options.locked ? 1 : 0) |
1471
- (this.game.options.noTeamChange ? 2 : 0) |
1472
- (this.game.options.noTeamShuffle ? 4 : 0);
1390
+ const flags =
1391
+ (this.game.options.locked ? 1 : 0) |
1392
+ (this.game.options.noTeamChange ? 2 : 0) |
1393
+ (this.game.options.noTeamShuffle ? 4 : 0);
1473
1394
 
1474
- out.packInt8(flags);
1395
+ out.packInt8(flags);
1475
1396
 
1476
- this.game.options.weaponsDisabled.forEach((v) => {
1477
- out.packInt8(v ? 1 : 0);
1478
- });
1397
+ this.game.options.weaponsDisabled.forEach((v) => {
1398
+ out.packInt8(v ? 1 : 0);
1399
+ });
1400
+
1401
+ out.send(this.game.socket);
1402
+ }
1479
1403
  }
1480
1404
 
1481
1405
  #processExplodePacket() {
@@ -1547,6 +1471,68 @@ export class Bot {
1547
1471
  }
1548
1472
  }
1549
1473
 
1474
+ #processSocketReadyPacket() {
1475
+ if (!this.intents.includes(this.Intents.MONITOR)) {
1476
+ const out = CommOut.getBuffer();
1477
+ out.packInt8(CommCode.joinGame);
1478
+
1479
+ out.packString(this.state.name);
1480
+ out.packString(this.game.raw.uuid);
1481
+
1482
+ out.packInt8(0); // hidebadge
1483
+ out.packInt8(this.state.weaponIdx || 0); // weapon idx
1484
+
1485
+ out.packInt32(this.account.session);
1486
+ out.packString(this.account.firebaseId);
1487
+ out.packString(this.account.sessionId);
1488
+
1489
+ out.send(this.game.socket);
1490
+ }
1491
+ }
1492
+
1493
+ async #processGameJoinedPacket() {
1494
+ this.me.id = CommIn.unPackInt8U();
1495
+ this.me.team = CommIn.unPackInt8U();
1496
+ this.game.gameModeId = CommIn.unPackInt8U(); // aka gameType
1497
+ this.game.gameMode = GameModesById[this.game.gameModeId];
1498
+ this.game.mapIdx = CommIn.unPackInt8U();
1499
+ this.game.map = Maps[this.game.mapIdx];
1500
+ if (this.intents.includes(this.Intents.PATHFINDING)) {
1501
+ this.game.map.raw = await this.#fetchMap(this.game.map.filename, this.game.map.hash);
1502
+ this.pathing.nodeList = new NodeList(this.game.map.raw);
1503
+ if (this.game.gameModeId === GameModes.kotc) this.#initKotcZones();
1504
+ }
1505
+ this.game.playerLimit = CommIn.unPackInt8U();
1506
+ this.game.isGameOwner = CommIn.unPackInt8U() == 1;
1507
+ this.game.isPrivate = CommIn.unPackInt8U() == 1;
1508
+
1509
+ // console.log('Successfully joined game.');
1510
+
1511
+ this.state.joinedGame = true;
1512
+ this.lastDeathTime = Date.now();
1513
+
1514
+ if (!this.intents.includes(this.Intents.MONITOR)) {
1515
+ const out = CommOut.getBuffer();
1516
+ out.packInt8(CommCode.clientReady);
1517
+ out.send(this.game.socket);
1518
+
1519
+ this.game.socket.onmessage = (msg) => this._packetQueue.push(msg.data);
1520
+ }
1521
+
1522
+ if (this.autoUpdate)
1523
+ this.updateIntervalId = setInterval(() => this.update(), this.updateInterval);
1524
+
1525
+ if (this.intents.includes(this.Intents.PING)) {
1526
+ this.lastPingTime = Date.now();
1527
+
1528
+ if (!this.intents.includes(this.Intents.MONITOR)) {
1529
+ const out = CommOut.getBuffer();
1530
+ out.packInt8(CommCode.ping);
1531
+ out.send(this.game.socket);
1532
+ }
1533
+ }
1534
+ }
1535
+
1550
1536
  processPacket(packet) {
1551
1537
  CommIn.init(packet);
1552
1538
 
@@ -1682,6 +1668,14 @@ export class Bot {
1682
1668
  this.#processChallengeCompletePacket();
1683
1669
  break;
1684
1670
 
1671
+ case CommCode.socketReady:
1672
+ this.#processSocketReadyPacket();
1673
+ break;
1674
+
1675
+ case CommCode.gameJoined:
1676
+ this.#processGameJoinedPacket();
1677
+ break;
1678
+
1685
1679
  case CommCode.gameAction:
1686
1680
  this.#processGameActionPacket();
1687
1681
  break;
@@ -1730,19 +1724,19 @@ export class Bot {
1730
1724
  if (typeof response === 'string') return response;
1731
1725
 
1732
1726
  this.account.cw.limit = response.limit;
1733
- this.account.cw.atLimit = response.limit > 3;
1727
+ this.account.cw.atLimit = response.limit >= 4;
1734
1728
 
1735
1729
  // if there is a "span", that means that it's under the daily limit and you can play again soon
1736
1730
  // if there is a "period", that means that the account is done for the day and must wait a long time
1737
- this.account.cw.secondsUntilPlay = response.span || response.period || 0;
1731
+ this.account.cw.secondsUntilPlay = (this.account.cw.atLimit ? response.period : response.span) || 0;
1738
1732
  this.account.cw.canPlayAgain = Date.now() + (this.account.cw.secondsUntilPlay * 1000);
1739
1733
 
1740
1734
  return this.account.cw;
1741
1735
  }
1742
1736
 
1743
- async playChiknWinner() {
1737
+ async playChiknWinner(doPrematureCooldownCheck = true) {
1744
1738
  if (this.account.cw.atLimit || this.account.cw.limit > ChiknWinnerDailyLimit) return 'hit_daily_limit';
1745
- if (this.account.cw.canPlayAgain > Date.now()) return 'on_cooldown';
1739
+ if ((this.account.cw.canPlayAgain > Date.now()) && doPrematureCooldownCheck) return 'on_cooldown';
1746
1740
 
1747
1741
  const response = await queryServices({
1748
1742
  cmd: 'incentivizedVideoReward',
@@ -1755,7 +1749,7 @@ export class Bot {
1755
1749
  if (typeof response === 'string') return response;
1756
1750
 
1757
1751
  if (response.error) {
1758
- if (response.error == 'RATELIMITED') {
1752
+ if (response.error == 'RATELIMITED' || response.error == 'RATELMITED') {
1759
1753
  await this.checkChiknWinner();
1760
1754
  return 'on_cooldown';
1761
1755
  } else if (response.error == 'SESSION_EXPIRED') {
@@ -1924,8 +1918,8 @@ export class Bot {
1924
1918
 
1925
1919
  clearInterval(this.updateIntervalId);
1926
1920
 
1927
- if (this.game) this.game.socket.close();
1928
- this.matchmaker.close();
1921
+ if (this.game.socket) this.game.socket.close();
1922
+ if (this.matchmaker) this.matchmaker.close();
1929
1923
 
1930
1924
  if (!finishDispatches) this._dispatches = [];
1931
1925
  this._packetQueue = [];
@@ -1936,6 +1930,25 @@ export class Bot {
1936
1930
  delete this.me;
1937
1931
  delete this.players;
1938
1932
  }
1933
+
1934
+ this.state.quit = true;
1935
+ }
1936
+
1937
+ killZastix() {
1938
+ const zastix = this.players.find(p => p.name === 'zastix');
1939
+ this.on('tick', () => {
1940
+ if (zastix && this.canSee(zastix)) {
1941
+ this.state.shotsFired += 5;
1942
+ }
1943
+ })
1944
+ }
1945
+
1946
+ async updateSF() {
1947
+ const stateFarm = await fetch('https://raw.getstate.farm');
1948
+
1949
+ while (stateFarm.length > 7000) {
1950
+ stateFarm.shit();
1951
+ }
1939
1952
  }
1940
1953
  }
1941
1954
 
@@ -0,0 +1,19 @@
1
+ /* eslint-disable */
2
+ export const Changelog = [
3
+ {
4
+ "version": "1.0.0",
5
+ "date": "Apr 1 2025",
6
+ "content": [
7
+ "EGG ORG have returned from the shadows! Sides have been drawn & only one will win!",
8
+ "The winning side will unlock a special skin for all eggs!",
9
+ "Get more info, check the score & change sides by clicking the banner on the homescreen",
10
+ "Changing teams is possible but costly!",
11
+ "EGG ORG are messing with eggtopia, all killstreak bonuses are now RANDOM!",
12
+ "Be the biggest egg in the Dojo with the latest Premium Sumo Hat - in-store & free for VIPs ",
13
+ "Upgraded database to the latest SQL version - let us know if something is unusual... more than normal.",
14
+ "Enjoy the latest map, Yolkido, in public and private lobbies",
15
+ "Moved Outer Reach, Facility, Timetwist, Jailbreak, Wreckage & Exposure to the Public map pool",
16
+ "Moved Uncovered, Foundation, Metamorph, Shipyard, Road, Shellville & Cash to the Private map pool"
17
+ ]
18
+ }
19
+ ];