-
Notifications
You must be signed in to change notification settings - Fork 1
/
handler.js
361 lines (332 loc) · 14.1 KB
/
handler.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
// Dependencies
const { GoogleSpreadsheet } = require('google-spreadsheet');
const axios = require('axios');
// Credentials
const { COD_RAPID_API_KEY } = process.env;
const { COD_RAPID_API_HOST } = process.env;
const config = {
headers: {
'x-rapidapi-key': COD_RAPID_API_KEY,
'x-rapidapi-host': COD_RAPID_API_HOST,
useQueryString: true,
},
};
// Global Variables
const THIS_WEEKS_GAME_MODE = 'wz';
const TOURNAMENT_TIME_WINDOW = 7500; // 2hr5min
const QUERY_DELAY = 10200;
const DOC = new GoogleSpreadsheet('1rTrJACi-IsX2B194ZWlkwNLi5jE_09PFNzhr6-iUc-8');
const UTC_TO_EST_DST = 14400; // EST is 4 hours behind UTC during daylight savings
const LAMBDA_TIME_NOW = Math.floor(+new Date() / 1000);
// Helper Functions
function normalizePlayerName(playerName) {
if (typeof playerName === 'undefined') {
return 'empty_cell';
}
const result = playerName.replace('#', '%23');
return result;
}
function normalizePlatformName(platformName) {
if (typeof platformName === 'undefined') {
return 'empty_cell';
}
switch (platformName) {
case '1':
return 'battle';
case '2':
return 'xbl';
case '3':
return 'psn';
default:
console.log('Error: Incorrect Platform ID Type');
return 'Incorrect Platform ID Type'
}
}
function normalizeTime(time12h) {
const [time, modifier] = time12h.split(' ');
// eslint-disable-next-line prefer-const
let [hours, minutes] = time.split(':');
if (hours === '12') {
hours = '00';
}
if (modifier === 'PM') {
hours = parseInt(hours, 10) + 12;
}
return `${hours}:${minutes}`;
}
function sleep(miliseconds) {
const currentTime = new Date().getTime();
while (currentTime + miliseconds >= new Date().getTime()) {
// empty
}
}
function placementPointsConverter(n) {
switch (n) {
case 1:
return 20;
case 2:
return 15;
case 3:
return 10;
case 4:
return 7;
case 5:
return 5;
default:
return 0;
}
}
function sumObjectsByKey(...objs) {
return objs.reduce((a, b) => {
for (const k in b) {
if (b.hasOwnProperty(k)) a[k] = (a[k] || 0) + b[k];
}
return a;
}, {});
}
// Main Function
module.exports.run = async () => {
// Main Function Outputs
let totalTeamKDR;
let bestGameKills;
let bestGamePP;
let secondBestGameKills;
let secondBestGamePP;
let totalScore;
// Google Sheets Credentials and Parameters
await DOC.useServiceAccountAuth({
client_email: process.env.CLIENT_EMAIL,
private_key: process.env.GS_PRIVATE_KEY,
});
await DOC.loadInfo();
const sheet = DOC.sheetsByIndex[0];
const rows = await sheet.getRows();
let tournamentForm;
let currentRow;
for (let i = 0; i < rows.length; i += 1) {
currentRow = rows[i];
try { // Try to read Date and Start Time, if it cant be read skip row
let timeToQuery = new Date(`${currentRow.Date} ${normalizeTime(currentRow['Start Time'])}`).getTime() / 1000;
timeToQuery += UTC_TO_EST_DST + QUERY_DELAY;
if (LAMBDA_TIME_NOW >= timeToQuery && typeof currentRow['Total Team KDR'] === 'undefined') {
console.log(`Pulling row ${i + 2} to be processed.`);
console.log(`Tournament ID: ${currentRow['Tournament ID']}`);
console.log(`EST Start Time = ${currentRow.Date}, ${currentRow['Start Time']}`);
console.log('Unix Epoch Start Time = ', new Date(`${currentRow.Date} ${normalizeTime(currentRow['Start Time'])}`).getTime() / 1000 + UTC_TO_EST_DST);
console.log(`Team Name = "${currentRow['Team Name']}"`);
tournamentForm = {
playerOne: {
playerName: normalizePlayerName(currentRow['Account 1']),
playerPlatform: normalizePlatformName(currentRow['ID Type 1']),
startTime: new Date(`${currentRow.Date} ${normalizeTime(currentRow['Start Time'])}`).getTime() / 1000 + UTC_TO_EST_DST,
},
playerTwo: {
playerName: normalizePlayerName(currentRow['Account 2']),
playerPlatform: normalizePlatformName(currentRow['ID Type 2']),
startTime: new Date(`${currentRow.Date} ${normalizeTime(currentRow['Start Time'])}`).getTime() / 1000 + UTC_TO_EST_DST,
},
playerThree: {
playerName: normalizePlayerName(currentRow['Account 3']),
playerPlatform: normalizePlatformName(currentRow['ID Type 3']),
startTime: new Date(`${currentRow.Date} ${normalizeTime(currentRow['Start Time'])}`).getTime() / 1000 + UTC_TO_EST_DST,
},
playerFour: {
playerName: normalizePlayerName(currentRow['Account 4']),
playerPlatform: normalizePlatformName(currentRow['ID Type 4']),
startTime: new Date(`${currentRow.Date} ${normalizeTime(currentRow['Start Time'])}`).getTime() / 1000 + UTC_TO_EST_DST,
},
};
break;
}
} catch {
console.log(`Row Number ${i + 2}: 'Date' or 'Start Time' fields appear to empty, skipping row...`);
}
}
if (typeof tournamentForm === 'undefined') {
console.log('No records ready to process at this time. Returning...');
return;
}
// Delete empty vacant player slots in tournamentForm
for (const key in tournamentForm) {
if (tournamentForm[key].playerName === 'empty_cell') {
delete tournamentForm[key];
}
}
// Create playerNameLst and playerPlatformLst
const playerNameLst = [];
const playerPlatformLst = [];
for (const key in tournamentForm) {
playerNameLst.push(tournamentForm[key].playerName);
playerPlatformLst.push(tournamentForm[key].playerPlatform);
}
const teamSize = playerNameLst.length;
// In the even at tournamentForm is submitted with no users on it
if (teamSize === 0) {
console.log('Error: No players were found in Google Sheets');
currentRow['Total Team KDR'] = 'ERROR: No players were found in Google Sheets';
await currentRow.save();
return;
}
const tournamentStart = tournamentForm.playerOne.startTime;
const tournamentStop = tournamentForm.playerOne.startTime + TOURNAMENT_TIME_WINDOW;
console.log(`Scoring all matches >= ${tournamentStart} AND < ${tournamentStop}`);
console.log(`Players on Team: ${teamSize}`);
console.log('Now processing scores for players:', playerNameLst);
console.log('With the ID Types of:', playerPlatformLst);
/* BEGIN Compute Total Team KDR */
let warZoneStats;
totalTeamKDR = 0;
for (let i = 0; i < teamSize; i += 1) {
try {
sleep(1000);
warZoneStats = await axios.get(
`https://call-of-duty-modern-warfare.p.rapidapi.com/warzone/
${playerNameLst[i]}/${playerPlatformLst[i]}`, config,
);
totalTeamKDR += parseFloat(warZoneStats.data.br.kdRatio);
} catch (error) {
currentRow['Total Team KDR'] = 'ERROR: Account is set to private OR Invalid Account OR ID Type';
await currentRow.save();
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.config);
console.log(error.response.status);
console.log(error.response.headers);
} else {
// The request was made and the server responded with a 2xx status code
console.log("Error: User's account is set to private, or invalid username or platform_id, HALTING PROCESS");
console.log('CoD Rapid API Response:', warZoneStats.data);
}
return;
}
}
console.log('Total Team KDR = ', totalTeamKDR);
/* END Compute Total Team KDR */
/* BEGIN Find all matches within time window AND gameType == THIS_WEEKS_GAME_MODE */
let warZoneMatches;
const matchIdPool = [];
// matchIdPool = all the matches played within
// the tournament window AND has the correct gameType
const warZoneMatchHistory = []; // stores the last 20 match history for each player
const qualifiedwarZoneMatchHistory = []; // stores the match history for each player inside the tournament window AND correct gameType
for (let i = 0; i < teamSize; i += 1) {
try {
sleep(1000);
warZoneMatches = await axios.get(`https://call-of-duty-modern-warfare.p.rapidapi.com/warzone-matches/
${playerNameLst[i]}/${playerPlatformLst[i]}`, config);
} catch (error) {
console.log(error);
}
warZoneMatchHistory.push(warZoneMatches.data.matches);
}
for (let i = 0; i < warZoneMatchHistory.length; i += 1) {
for (let j = 0; j < warZoneMatchHistory[i].length; j += 1) {
if (warZoneMatchHistory[i][j].utcStartSeconds >= tournamentStart
&& warZoneMatchHistory[i][j].utcStartSeconds < tournamentStop
&& warZoneMatchHistory[i][j].gameType === THIS_WEEKS_GAME_MODE) {
matchIdPool.push(warZoneMatchHistory[i][j].matchID);
qualifiedwarZoneMatchHistory.push(warZoneMatchHistory[i][j]);
}
}
}
/* END Find all matches within time window AND gameType == THIS_WEEKS_GAME_MODE */
/* BEGIN Verify all team members were present in each match */
// A matchID has a 1 to 1 relationship with its player
// therefore, a matchID must be repeated as manytimes, as players on the
// team. For example, there exists a team of two. A matchID must exist in
// the matchIdPool two times, to prove the players were present in the same
// game.
// Remove duplicate matchIDs
const uniqueMatchIdPoolSet = new Set(matchIdPool);
// Write de-duped matchIDs to an array
const uniqueMatchIdPoolLst = Array.from(uniqueMatchIdPoolSet);
// Convert uniqueMatchIdPoolLst to objects, with values of 0
const matchesPlayedAsTeam = uniqueMatchIdPoolLst.reduce(
(a, b) => (a[b] = 0, a), {});
// For each unique matchId in matchesPlayedAsTeam count how many times
// they occur in the matchIdPoool
for (const key in matchesPlayedAsTeam) {
for (let i = 0; i < matchIdPool.length; i+=1) {
if (key === matchIdPool[i]) {
matchesPlayedAsTeam[key] += 1;
}
}
}
// A match will only be scored IF and ONLY IF it occurred in the
// matchIdPool as many times, as there are players on the team
for (const key in matchesPlayedAsTeam) {
if (matchesPlayedAsTeam[key] !== teamSize) {
delete matchesPlayedAsTeam[key];
}
}
const matchesQualifiedToBeScored = Object.keys(matchesPlayedAsTeam);
if (matchesQualifiedToBeScored.length < 2) {
console.log('ERROR: Less than two matches qualified to be scored, HALTING PROCESS');
currentRow['Total Team KDR'] = 'ERROR: Less than two matches qualified to be scored';
await currentRow.save();
return;
}
/* END Verify all team members were present in each match */
/* BEGIN Score all matches that qualify to be scored */
const killsTracker = matchesQualifiedToBeScored.reduce(
(a, b) => (a[b] = 0, a), {});
const teamPlacementTracker = matchesQualifiedToBeScored.reduce(
(a, b) => (a[b] = 0, a), {});
const teamPlacementsPointsTracker = matchesQualifiedToBeScored.reduce(
(a, b) => (a[b] = 0, a), {});
for (let i = 0; i < qualifiedwarZoneMatchHistory.length; i += 1) {
for (let j = 0; j < matchesQualifiedToBeScored.length; j += 1) {
if (qualifiedwarZoneMatchHistory[i].matchID === matchesQualifiedToBeScored[j]) {
killsTracker[qualifiedwarZoneMatchHistory[i].matchID]
+= qualifiedwarZoneMatchHistory[i].playerStats.kills;
teamPlacementTracker[qualifiedwarZoneMatchHistory[i].matchID] = qualifiedwarZoneMatchHistory[i].playerStats.teamPlacement;
teamPlacementsPointsTracker[qualifiedwarZoneMatchHistory[i].matchID] = placementPointsConverter(
qualifiedwarZoneMatchHistory[i].playerStats.teamPlacement);
}
}
}
console.log('Team Placement for each match = ', teamPlacementTracker);
console.log('Team Placement Points for each match = ', teamPlacementsPointsTracker);
console.log('Total Team Kills for each match = ', killsTracker);
const finalScores = sumObjectsByKey(killsTracker, teamPlacementsPointsTracker);
console.log('Final Tournament Scores for this team = ', finalScores);
/* END Score all matches that qualify to be scored */
/* BEGIN Build Scorecard to be OUTPUT */
// Find MatchID for best game and write it to bestGame
const finalScoresWorkingCopy = finalScores;
const bestGame = Object.keys(finalScoresWorkingCopy).reduce(
(a, b) => (finalScoresWorkingCopy[a] > finalScoresWorkingCopy[b] ? a : b));
delete finalScoresWorkingCopy[bestGame];
bestGameKills = killsTracker[bestGame];
const bestGameTeamPlacement = teamPlacementTracker[bestGame];
bestGamePP = teamPlacementsPointsTracker[bestGame];
console.log("--------This team's best game is", bestGame, '--------');
console.log('Their Total Kills for this game = ', bestGameKills);
console.log('Their Team placement for this game is = ', bestGameTeamPlacement);
console.log('Their Placement Points for this game is = ', bestGamePP);
console.log('The Total Best Game Score is = ', bestGameKills + bestGamePP, '\n');
// Find MatchID for second best game and write it to secondBestGame
const secondBestGame = Object.keys(finalScoresWorkingCopy).reduce((a, b) => (finalScoresWorkingCopy[a] > finalScoresWorkingCopy[b] ? a : b));
delete finalScoresWorkingCopy[secondBestGame];
secondBestGameKills = killsTracker[secondBestGame];
const secondBestGameTeamPlacement = teamPlacementTracker[secondBestGame];
secondBestGamePP = teamPlacementsPointsTracker[secondBestGame];
console.log("--------This team's seccond best game is", secondBestGame, '--------');
console.log('Their Total Kills for this game = ', secondBestGameKills);
console.log('Their Team placement for this game is = ', secondBestGameTeamPlacement);
console.log('Their Placement Points for this game is = ', secondBestGamePP);
console.log('The Total Second Best Game Score is = ', secondBestGameKills + secondBestGamePP, '\n');
totalScore = bestGameKills + secondBestGameKills + bestGamePP + secondBestGamePP;
console.log('Total Score =', totalScore, '\n');
/* END Build Scorecard to be OUTPUT */
/* BEGIN Write to Google Sheet */
currentRow['Total Team KDR'] = totalTeamKDR;
currentRow['Best Game Kills'] = bestGameKills;
currentRow['Best Game PP'] = bestGamePP;
currentRow['Second Best Game Kills'] = secondBestGameKills;
currentRow['Second Best Game PP'] = secondBestGamePP;
currentRow['Total Score'] = totalScore;
await currentRow.save();
/* END Write to Google Sheet */
};