fix invites

This commit is contained in:
Devin Zuczek
2026-07-23 18:09:16 -04:00
parent f39db8a15e
commit 82f011c0b0
10 changed files with 883 additions and 8 deletions
+49
View File
@@ -15,6 +15,55 @@ export enum RoomInstanceType {
Clubhouse = 5,
}
/**
* The `Type` byte on a messaging `Message` — how the client dispatches a message it
* receives (a game invite renders the join prompt, a text message the chat bubble, …).
* Distinct from the notify hub's {@link NotificationType}: a message is delivered *as*
* a `MessageReceived` (NotificationType 2) notification whose payload is a `Message`,
* and this enum is that inner `Message.Type`. Mirrors the reference's `MessageType`.
*/
export enum MessageType {
GameInvite = 0,
GameInviteDeclined = 1,
GameJoinFailed = 2,
PartyActivitySwitch = 3,
FriendInvite = 4,
VoteToKick = 5,
GameInviteV2 = 6,
PartyActivitySwitchV2 = 7,
RequestGameInvite = 10,
RequestGameInviteDeclined = 11,
FriendStatusOnline = 20,
TextMessage = 30,
FriendRequestAccepted = 40,
PlayerCheer = 50,
PlayerCheerAnonymous = 51,
RoomCoOwnerAdded = 60,
RoomCoOwnerRemoved = 61,
RoomCoOwnerInvited = 62,
CreatorPublishedNewRoom = 70,
PlayerAttendingEvent = 80,
PlayerEventInvitation = 81,
DeprecatedGroupInvitation = 90,
DeprecatedPlayerJoinedGroup = 91,
CoachMessage = 100,
NewRoomComments = 110,
PartyUpRequest = 120,
FriendIntroduction = 130,
ClubMemberInvited = 200,
ClubModeratorInvited = 201,
ClubCoownerInvited = 202,
VirtualClubAnnouncementRoomPublished = 100000,
VirtualClubAnnouncementInventionPublished = 100001,
VirtualClubAnnouncementGeneric = 100002,
VirtualClubAnnouncementPlayerEventPublished = 100003,
VirtualClubAnnouncementClub = 100004,
VirtualClubAnnouncementPlayer = 100005,
VirtualClubAnnouncementCode = 100006,
VirtualClubAnnouncementPhoto = 100007,
VirtualRoomNotification = 100008,
}
/** A room's (or image's) visibility, matching the client's `RoomAccessibility`. */
export enum Accessibility {
Private = 0,
+2 -1
View File
@@ -1,4 +1,4 @@
export { RoomInstanceType, Accessibility, Role } from './enums'
export { RoomInstanceType, Accessibility, Role, MessageType } from './enums'
export * from './accounts-db'
export * from './clubs-db'
export * from './images-db'
@@ -7,3 +7,4 @@ export * from './rooms-db'
export * from './room-instance-db'
export * from './presence-db'
export * from './gifts-db'
export * from './relationships-db'
+56
View File
@@ -0,0 +1,56 @@
/**
* Read-only access to the friendship graph on the shared `recflare` D1 database.
*
* The `relationship` table's schema and every mutation are owned by the `api` worker
* (apps/api/src/relationships-db.ts, migrations/0001_relationship.sql). This module is
* the shared *reader* other workers need: the `match` worker looks up a player's friends
* to push a presence update to them when the player changes rooms. It only SELECTs the
* three columns that identify a friendship (requester/target/type), so it stays
* decoupled from the favorited/ignored/muted flag columns api layers on top.
*/
/**
* `relationship_type` for a mutual friendship — mirror of api's `RelationshipType.Friend`.
* Pending requests (1 sent / 2 received) and bare ignore/mute rows (0) are not friends.
*/
const FRIEND_RELATIONSHIP_TYPE = 3
/**
* The account ids of a player's mutual friends. Exactly one relationship row exists per
* unordered pair, with the player on either side, so this reads both directions and
* returns whichever id isn't the player. Non-friend rows are excluded; the order is
* unspecified.
*/
export async function getFriendIds(db: D1Database, playerId: number): Promise<number[]> {
const { results } = await db
.prepare(
`SELECT requester_id, target_id FROM relationship
WHERE relationship_type = ?2 AND (requester_id = ?1 OR target_id = ?1)`
)
.bind(playerId, FRIEND_RELATIONSHIP_TYPE)
.all<{ requester_id: number; target_id: number }>()
return results.map((r) => (r.requester_id === playerId ? r.target_id : r.requester_id))
}
/**
* Whether two players are mutual friends. The single relationship row for the pair sits
* in either direction, so both are checked. A targeted single-row read — cheaper than
* {@link getFriendIds} when all you need is "are these two friends?" (e.g. gating a
* follow-a-friend matchmake).
*/
export async function areFriends(
db: D1Database,
playerId: number,
otherId: number
): Promise<boolean> {
const row = await db
.prepare(
`SELECT 1 AS ok FROM relationship
WHERE relationship_type = ?3
AND ((requester_id = ?1 AND target_id = ?2) OR (requester_id = ?2 AND target_id = ?1))
LIMIT 1`
)
.bind(playerId, otherId, FRIEND_RELATIONSHIP_TYPE)
.first<{ ok: number }>()
return row !== null
}