Skip to content

Commit 60425a4

Browse files
committed
Websocket tests
1 parent b82ecc0 commit 60425a4

File tree

9 files changed

+6516
-1692
lines changed

9 files changed

+6516
-1692
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ jspm_packages
44

55
# Serverless directories
66
.serverless
7-
/api/*.js
7+
/api/**/*.js
88
/utils/*.js
99
.vscode/
1010
bin/

api/abstractplay.ts

Lines changed: 44 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ async function verifyAndCorrectCountWithData(metaGame: string, countType: "curre
447447
}
448448

449449
const details = countsData.Item as MetaGameCounts;
450-
450+
451451
// Check if the metaGame nested attribute exists at all
452452
if (!details[metaGame]) {
453453
console.log(`No nested attribute for ${metaGame} in METAGAMES COUNTS, initializing...`);
@@ -461,7 +461,7 @@ async function verifyAndCorrectCountWithData(metaGame: string, countType: "curre
461461
console.log(`Initialized ${countType} for new game ${metaGame} to ${actualCount}`);
462462
return;
463463
}
464-
464+
465465
const storedCount = details[metaGame][countType] || 0;
466466

467467
if (storedCount !== actualCount) {
@@ -2944,22 +2944,22 @@ async function updateStandingChallengeCount(metaGame: any, diff: number) {
29442944
ExpressionAttributeValues: {":n": diff, ":zero": 0},
29452945
UpdateExpression: "set #g.standingchallenges = if_not_exists(#g.standingchallenges, :zero) + :n",
29462946
});
2947-
2947+
29482948
try {
29492949
return await ddbDocClient.send(updateCommand);
29502950
} catch (error: any) {
29512951
if (error.name === 'ValidationException') {
29522952
console.log(`ValidationException updating METAGAMES/COUNTS standing challenges for new game ${metaGame}. Initializing nested attribute...`);
2953-
2953+
29542954
// Fetch current METAGAMES/COUNTS record
29552955
const countsData = await ddbDocClient.send(new GetCommand({
29562956
TableName: process.env.ABSTRACT_PLAY_TABLE,
29572957
Key: { "pk": "METAGAMES", "sk": "COUNTS" }
29582958
}));
2959-
2959+
29602960
// Initialize the nested attribute for this game
29612961
await verifyAndCorrectCountWithData(metaGame, "standingchallenges", 0, countsData);
2962-
2962+
29632963
// Retry the original update
29642964
return await ddbDocClient.send(updateCommand);
29652965
}
@@ -3257,21 +3257,21 @@ async function addToGameLists(type: string, game: Game, now: number, keepgame: b
32573257
ExpressionAttributeValues: {":n": 1, ":zero": 0},
32583258
UpdateExpression: "set #g.currentgames = if_not_exists(#g.currentgames, :zero) + :n"
32593259
});
3260-
3260+
32613261
work.push(
32623262
sendCommandWithRetry(updateCommand).catch(async (error) => {
32633263
if (error.name === 'ValidationException') {
32643264
console.log(`ValidationException updating METAGAMES/COUNTS for new game ${game.metaGame}. Initializing nested attribute...`);
3265-
3265+
32663266
// Fetch current METAGAMES/COUNTS record
32673267
const countsData = await ddbDocClient.send(new GetCommand({
32683268
TableName: process.env.ABSTRACT_PLAY_TABLE,
32693269
Key: { "pk": "METAGAMES", "sk": "COUNTS" }
32703270
}));
3271-
3271+
32723272
// Initialize the nested attribute for this game
32733273
await verifyAndCorrectCountWithData(game.metaGame, "currentgames", 0, countsData);
3274-
3274+
32753275
// Retry the original update
32763276
return sendCommandWithRetry(updateCommand);
32773277
}
@@ -4375,7 +4375,7 @@ function applyMove(
43754375
})));
43764376
}
43774377
}
4378-
4378+
43794379
game.state = engine.serialize();
43804380
if (engine.gameover) {
43814381
game.toMove = "";
@@ -4396,10 +4396,10 @@ function isInterestingComment(comment: string): boolean {
43964396
}
43974397
// Normalize the comment
43984398
const normalized = comment.toLowerCase().trim();
4399-
4399+
44004400
// Remove punctuation for comparison
44014401
const withoutPunctuation = normalized.replace(/[^\w\s]/g, '');
4402-
4402+
44034403
// Common boring phrases (exact matches)
44044404
const boringPhrases = new Set([
44054405
'gg', 'glhf', 'gl', 'hf', 'tagg', 'hi', 'hello', 'hey',
@@ -4410,48 +4410,48 @@ function isInterestingComment(comment: string): boolean {
44104410
'thanks for playing', 'thanks for the game!', 'gg thanks',
44114411
'yoyo', 'yoyo gl', 'yoyo gl hf'
44124412
]);
4413-
4413+
44144414
// Check for exact matches (with or without punctuation)
44154415
if (boringPhrases.has(normalized) || boringPhrases.has(withoutPunctuation)) {
44164416
return false;
44174417
}
4418-
4418+
44194419
// Split into words for further analysis
44204420
const words = withoutPunctuation.split(/\s+/).filter(w => w.length > 0);
4421-
4421+
44224422
// Very short comments with only common game words are boring
44234423
const commonWords = new Set([
44244424
'gg', 'gl', 'hf', 'tagg', 'hi', 'hello', 'yoyo',
44254425
'thanks', 'thx', 'ty', 'wp', 'move', 'pie', 'invoked',
44264426
'good', 'game', 'luck', 'fun', 'for', 'the', 'a', 'to',
44274427
'have', 'sir', 'well', 'played', 'you', 'too'
44284428
]);
4429-
4429+
44304430
if (words.length <= 3 && words.every(w => commonWords.has(w))) {
44314431
return false;
44324432
}
4433-
4433+
44344434
// If we got here, the comment is interesting
44354435
return true;
44364436
}
44374437

44384438
// Helper function to update lastChat and seen for all players of a game
44394439
// Used by both submitComment (for in-game chats) and saveExploration (for completed game comments)
44404440
async function updateLastChatForPlayers(
4441-
gameId: string,
4441+
gameId: string,
44424442
metaGame: string,
44434443
players: {[k: string]: any; id: string}[],
44444444
currentUserId: string,
44454445
allowReAdd: boolean = false // Only true for completed games via saveExploration
44464446
) {
44474447
console.log(`Updating lastChat for all players of game ${gameId}`);
4448-
4448+
44494449
// Lazy-loaded only if needed (when a player doesn't have the game in their list)
44504450
let fullGame: FullGame | undefined;
44514451
let gameEngine: any | undefined;
4452-
4452+
44534453
const now = Date.now();
4454-
4454+
44554455
for (const pid of players.map(p => p.id)) {
44564456
let data: any;
44574457
let user: FullUser | undefined;
@@ -4473,14 +4473,14 @@ async function updateLastChatForPlayers(
44734473
console.log(`Unable to get user data for user ${pid} when updating lastChat`);
44744474
continue; // Don't fail the whole operation
44754475
}
4476-
4476+
44774477
if (user === undefined) {
44784478
console.log(`Unable to get user data for user ${pid} when updating lastChat`);
44794479
continue; // Don't fail the whole operation
44804480
}
4481-
4481+
44824482
const game = user.games?.find(g => g.id === gameId);
4483-
4483+
44844484
if (game !== undefined) {
44854485
game.lastChat = now;
44864486
// if this is the user who added the comment/exploration, also update their `seen`
@@ -4493,7 +4493,7 @@ async function updateLastChatForPlayers(
44934493
} else if (allowReAdd) {
44944494
// Only try to re-add for completed games (when allowReAdd is true)
44954495
console.log(`User ${user.name} does not have a game entry for ${gameId}, re-adding it`);
4496-
4496+
44974497
// Fetch the full game only once (lazy loading)
44984498
if (fullGame === undefined) {
44994499
try {
@@ -4518,7 +4518,7 @@ async function updateLastChatForPlayers(
45184518
console.log(`Failed to get completed game ${gameId} for re-adding to user's list:`, err);
45194519
}
45204520
}
4521-
4521+
45224522
if (fullGame !== undefined && gameEngine !== undefined) {
45234523
const newGame: Game = {
45244524
id: gameId,
@@ -4533,7 +4533,7 @@ async function updateLastChatForPlayers(
45334533
lastChat: now,
45344534
seen: pid === currentUserId ? now + 10 : undefined,
45354535
};
4536-
4536+
45374537
if (!user.games) {
45384538
user.games = [];
45394539
}
@@ -4593,19 +4593,19 @@ async function submitComment(userid: string, pars: { id: string; players?: {[k:
45934593
"comments": comments
45944594
}
45954595
}));
4596-
4596+
45974597
// Check if the new comment is interesting
45984598
const newCommentIsInteresting = isInterestingComment(comment.comment);
4599-
4599+
46004600
// If we didn't have interesting comments before but the new one is interesting,
46014601
// update the GAME record to set commented = 1
46024602
if (pars.metaGame && !hadInterestingCommentBefore && newCommentIsInteresting) {
46034603
try {
46044604
await ddbDocClient.send(new UpdateCommand({
46054605
TableName: process.env.ABSTRACT_PLAY_TABLE,
4606-
Key: {
4607-
"pk": "GAME",
4608-
"sk": pars.metaGame + "#0#" + pars.id
4606+
Key: {
4607+
"pk": "GAME",
4608+
"sk": pars.metaGame + "#0#" + pars.id
46094609
},
46104610
ExpressionAttributeValues: { ":c": 1 },
46114611
UpdateExpression: "set commented = :c",
@@ -4618,7 +4618,7 @@ async function submitComment(userid: string, pars: { id: string; players?: {[k:
46184618
}
46194619
}
46204620
}
4621-
4621+
46224622
// Update lastChat for all players when a comment is added to an in-game chat
46234623
// Note: For completed games, comments go through the exploration system (saveExploration)
46244624
if (pars.players && pars.metaGame) {
@@ -4639,8 +4639,8 @@ async function saveExploration(userid: string, pars: { public: boolean, game: st
46394639
try {
46404640
await ddbDocClient.send(new UpdateCommand({
46414641
TableName: process.env.ABSTRACT_PLAY_TABLE,
4642-
Key: {
4643-
"pk": "COMPLETEDGAMES#" + pars.metaGame,
4642+
Key: {
4643+
"pk": "COMPLETEDGAMES#" + pars.metaGame,
46444644
"sk": pars.gameEnded + "#" + pars.game
46454645
},
46464646
ExpressionAttributeValues: { ":c": pars.updateCommentedFlag },
@@ -4653,7 +4653,7 @@ async function saveExploration(userid: string, pars: { public: boolean, game: st
46534653
// Don't fail the whole operation just because of the flag update
46544654
}
46554655
}
4656-
4656+
46574657
// If we need to update lastChat and seen for all players (completed games only)
46584658
if (pars.updateLastChat && pars.public && pars.players) {
46594659
await updateLastChatForPlayers(
@@ -4664,7 +4664,7 @@ async function saveExploration(userid: string, pars: { public: boolean, game: st
46644664
true // Allow re-adding game to list for completed games
46654665
);
46664666
}
4667-
4667+
46684668
if (!pars.public) {
46694669
await ddbDocClient.send(new PutCommand({
46704670
TableName: process.env.ABSTRACT_PLAY_TABLE,
@@ -7866,8 +7866,8 @@ async function updateCommented(userId: string, pars: {id: string; metaGame: stri
78667866
// For completed games, update COMPLETEDGAMES table
78677867
await ddbDocClient.send(new UpdateCommand({
78687868
TableName: process.env.ABSTRACT_PLAY_TABLE,
7869-
Key: {
7870-
"pk": "COMPLETEDGAMES#" + pars.metaGame,
7869+
Key: {
7870+
"pk": "COMPLETEDGAMES#" + pars.metaGame,
78717871
"sk": pars.gameEnded + "#" + pars.id
78727872
},
78737873
ExpressionAttributeValues: { ":c": pars.commented },
@@ -7879,17 +7879,17 @@ async function updateCommented(userId: string, pars: {id: string; metaGame: stri
78797879
// For current games, update GAME table
78807880
await ddbDocClient.send(new UpdateCommand({
78817881
TableName: process.env.ABSTRACT_PLAY_TABLE,
7882-
Key: {
7883-
"pk": "GAME",
7884-
"sk": pars.metaGame + "#0#" + pars.id
7882+
Key: {
7883+
"pk": "GAME",
7884+
"sk": pars.metaGame + "#0#" + pars.id
78857885
},
78867886
ExpressionAttributeValues: { ":c": pars.commented },
78877887
UpdateExpression: "set commented = :c",
78887888
ConditionExpression: "attribute_exists(pk) AND attribute_exists(sk)"
78897889
}));
78907890
console.log(`Successfully updated commented flag in GAME for game ${pars.id} to ${pars.commented}`);
78917891
}
7892-
7892+
78937893
return {
78947894
statusCode: 200,
78957895
body: JSON.stringify({ success: true }),

api/sockets/connectHandler.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
4+
import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';
5+
6+
interface WebSocketConnectEvent {
7+
requestContext: {
8+
connectionId: string;
9+
authorizer?: {
10+
userId?: string;
11+
email?: string;
12+
[key: string]: any;
13+
};
14+
};
15+
}
16+
17+
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
18+
19+
export const handler = async (event: WebSocketConnectEvent) => {
20+
console.log("Connect event:", JSON.stringify(event));
21+
const { connectionId, authorizer } = event.requestContext;
22+
23+
if (!authorizer?.userId) {
24+
console.error("Missing userId in authorizer context");
25+
return { statusCode: 401 };
26+
}
27+
28+
const userId = authorizer.userId;
29+
30+
await ddb.send(
31+
new PutCommand({
32+
TableName: process.env.ABSTRACT_PLAY_TABLE!,
33+
Item: {
34+
pk: "wsConnections",
35+
sk: connectionId,
36+
37+
connectionId,
38+
userId,
39+
40+
// Optional TTL for auto-cleanup
41+
ttl: Math.floor(Date.now() / 1000) + 3600,
42+
},
43+
})
44+
);
45+
46+
return { statusCode: 200 };
47+
};

0 commit comments

Comments
 (0)