diff --git a/packages/bsky/proto/bsky.proto b/packages/bsky/proto/bsky.proto index d7f0e6fed98..d21ced06a1d 100644 --- a/packages/bsky/proto/bsky.proto +++ b/packages/bsky/proto/bsky.proto @@ -140,7 +140,8 @@ message GetFollowersRequest { message FollowInfo { string uri = 1; - string did = 2; + string actor_did = 2; + string subject_did = 3; } message GetFollowersResponse { @@ -230,13 +231,37 @@ message GetActorLikesResponse { // - return number of likes on subjects A, B, C... // - post or feed generator hydration `likeCount` field message GetLikeCountsRequest { - repeated string uris = 1; + repeated RecordRef refs = 1; } message GetLikeCountsResponse { repeated int32 counts = 1; } +// +// Interactions +// +message GetInteractionCountsRequest { + repeated RecordRef refs = 1; +} + +message GetInteractionCountsResponse { + repeated int32 likes = 1; + repeated int32 reposts = 2; + repeated int32 replies = 3; +} + +message GetCountsForUsersRequest { + repeated string dids = 1; +} + +message GetCountsForUsersResponse { + repeated int32 posts = 1; + repeated int32 reposts = 2; + repeated int32 following = 3; + repeated int32 followers = 4; +} + // // Reposts // @@ -244,10 +269,9 @@ message GetLikeCountsResponse { // - return repost uris where subject uri is subject A // - `getReposts` list for a post message GetRepostsBySubjectRequest { - string subject_uri = 1; - optional string subject_cid = 2; - int32 limit = 3; - string cursor = 4; + RecordRef subject = 1; + int32 limit = 2; + string cursor = 3; } message GetRepostsBySubjectResponse { @@ -287,7 +311,7 @@ message GetActorRepostsResponse { // - return number of reposts on subject A // - post or feed generator hydration `repostCount` field message GetRepostCountsRequest { - repeated string uris = 1; + repeated RecordRef refs = 1; } message GetRepostCountsResponse { @@ -726,7 +750,9 @@ message GetTimelineResponse { message FeedItem { string uri = 1; - string repost = 2; + string cid = 2; + string repost = 3; + string repost_cid = 4; } // - Return recent post uris from users in list A @@ -813,7 +839,7 @@ message GetFollowSuggestionsResponse { // - Return post reply count with uris A, B, C… // - All feed hydration message GetPostReplyCountsRequest { - repeated string uris = 1; + repeated RecordRef refs = 1; } message GetPostReplyCountsResponse { @@ -883,10 +909,13 @@ message UpdateTakedownRequest { message UpdateTakedownResponse {} + // Ping message PingRequest {} message PingResponse {} + + service Service { // Records rpc GetBlockRecords(GetBlockRecordsRequest) returns (GetBlockRecordsResponse); @@ -920,6 +949,10 @@ service Service { rpc GetActorReposts(GetActorRepostsRequest) returns (GetActorRepostsResponse); rpc GetRepostCounts(GetRepostCountsRequest) returns (GetRepostCountsResponse); + // Interaction Counts + rpc GetInteractionCounts(GetInteractionCountsRequest) returns (GetInteractionCountsResponse); + rpc GetCountsForUsers(GetCountsForUsersRequest) returns (GetCountsForUsersResponse); + // Profile rpc GetActors(GetActorsRequest) returns (GetActorsResponse); rpc GetDidsByHandles(GetDidsByHandlesRequest) returns (GetDidsByHandlesResponse); diff --git a/packages/bsky/src/api/app/bsky/feed/getRepostedBy.ts b/packages/bsky/src/api/app/bsky/feed/getRepostedBy.ts index 61d6f433b4d..10a58c623d8 100644 --- a/packages/bsky/src/api/app/bsky/feed/getRepostedBy.ts +++ b/packages/bsky/src/api/app/bsky/feed/getRepostedBy.ts @@ -35,7 +35,7 @@ const skeleton = async (inputs: { }): Promise => { const { ctx, params } = inputs const res = await ctx.hydrator.dataplane.getRepostsBySubject({ - subjectUri: params.uri, + subject: { uri: params.uri, cid: params.cid }, cursor: params.cursor, limit: params.limit, }) diff --git a/packages/bsky/src/data-plane/gen/bsky_connect.ts b/packages/bsky/src/data-plane/gen/bsky_connect.ts index ed35f836cd8..8bf8e9592c3 100644 --- a/packages/bsky/src/data-plane/gen/bsky_connect.ts +++ b/packages/bsky/src/data-plane/gen/bsky_connect.ts @@ -3,7 +3,7 @@ /* eslint-disable */ // @ts-nocheck -import { GetActorFeedsRequest, GetActorFeedsResponse, GetActorFollowsActorsRequest, GetActorFollowsActorsResponse, GetActorLikesRequest, GetActorLikesResponse, GetActorListsRequest, GetActorListsResponse, GetActorMutesActorRequest, GetActorMutesActorResponse, GetActorMutesActorViaListRequest, GetActorMutesActorViaListResponse, GetActorRepostsRequest, GetActorRepostsResponse, GetActorsRequest, GetActorsResponse, GetAuthorFeedRequest, GetAuthorFeedResponse, GetBidirectionalBlockRequest, GetBidirectionalBlockResponse, GetBidirectionalBlockViaListRequest, GetBidirectionalBlockViaListResponse, GetBlobTakedownRequest, GetBlobTakedownResponse, GetBlockExistenceRequest, GetBlockExistenceResponse, GetBlocklistSubscriptionRequest, GetBlocklistSubscriptionResponse, GetBlocklistSubscriptionsRequest, GetBlocklistSubscriptionsResponse, GetBlockRecordsRequest, GetBlockRecordsResponse, GetBlocksRequest, GetBlocksResponse, GetDidsByHandlesRequest, GetDidsByHandlesResponse, GetFeedGeneratorRecordsRequest, GetFeedGeneratorRecordsResponse, GetFeedGeneratorStatusRequest, GetFeedGeneratorStatusResponse, GetFollowCountsRequest, GetFollowCountsResponse, GetFollowerCountsRequest, GetFollowerCountsResponse, GetFollowersRequest, GetFollowersResponse, GetFollowRecordsRequest, GetFollowRecordsResponse, GetFollowsRequest, GetFollowsResponse, GetFollowSuggestionsRequest, GetFollowSuggestionsResponse, GetLabelsRequest, GetLabelsResponse, GetLatestRevRequest, GetLatestRevResponse, GetLikeCountsRequest, GetLikeCountsResponse, GetLikeRecordsRequest, GetLikeRecordsResponse, GetLikesByActorAndSubjectsRequest, GetLikesByActorAndSubjectsResponse, GetLikesBySubjectRequest, GetLikesBySubjectResponse, GetListBlockRecordsRequest, GetListBlockRecordsResponse, GetListCountRequest, GetListCountResponse, GetListFeedRequest, GetListFeedResponse, GetListItemRecordsRequest, GetListItemRecordsResponse, GetListMembershipRequest, GetListMembershipResponse, GetListMembersRequest, GetListMembersResponse, GetListRecordsRequest, GetListRecordsResponse, GetMutelistSubscriptionRequest, GetMutelistSubscriptionResponse, GetMutelistSubscriptionsRequest, GetMutelistSubscriptionsResponse, GetMutesRequest, GetMutesResponse, GetNotificationSeenRequest, GetNotificationSeenResponse, GetNotificationsRequest, GetNotificationsResponse, GetPostCountsRequest, GetPostCountsResponse, GetPostRecordsRequest, GetPostRecordsResponse, GetPostReplyCountsRequest, GetPostReplyCountsResponse, GetProfileRecordsRequest, GetProfileRecordsResponse, GetRelationshipsRequest, GetRelationshipsResponse, GetRepostCountsRequest, GetRepostCountsResponse, GetRepostRecordsRequest, GetRepostRecordsResponse, GetRepostsByActorAndSubjectsRequest, GetRepostsByActorAndSubjectsResponse, GetRepostsBySubjectRequest, GetRepostsBySubjectResponse, GetSuggestedFeedsRequest, GetSuggestedFeedsResponse, GetThreadGateRecordsRequest, GetThreadGateRecordsResponse, GetThreadRequest, GetThreadResponse, GetTimelineRequest, GetTimelineResponse, GetUnreadNotificationCountRequest, GetUnreadNotificationCountResponse, MuteActorListRequest, MuteActorListResponse, MuteActorRequest, MuteActorResponse, PingRequest, PingResponse, SearchActorsRequest, SearchActorsResponse, SearchPostsRequest, SearchPostsResponse, UnmuteActorListRequest, UnmuteActorListResponse, UnmuteActorRequest, UnmuteActorResponse, UpdateNotificationSeenRequest, UpdateNotificationSeenResponse, UpdateTakedownRequest, UpdateTakedownResponse } from "./bsky_pb.ts"; +import { GetActorFeedsRequest, GetActorFeedsResponse, GetActorFollowsActorsRequest, GetActorFollowsActorsResponse, GetActorLikesRequest, GetActorLikesResponse, GetActorListsRequest, GetActorListsResponse, GetActorMutesActorRequest, GetActorMutesActorResponse, GetActorMutesActorViaListRequest, GetActorMutesActorViaListResponse, GetActorRepostsRequest, GetActorRepostsResponse, GetActorsRequest, GetActorsResponse, GetAuthorFeedRequest, GetAuthorFeedResponse, GetBidirectionalBlockRequest, GetBidirectionalBlockResponse, GetBidirectionalBlockViaListRequest, GetBidirectionalBlockViaListResponse, GetBlobTakedownRequest, GetBlobTakedownResponse, GetBlockExistenceRequest, GetBlockExistenceResponse, GetBlocklistSubscriptionRequest, GetBlocklistSubscriptionResponse, GetBlocklistSubscriptionsRequest, GetBlocklistSubscriptionsResponse, GetBlockRecordsRequest, GetBlockRecordsResponse, GetBlocksRequest, GetBlocksResponse, GetCountsForUsersRequest, GetCountsForUsersResponse, GetDidsByHandlesRequest, GetDidsByHandlesResponse, GetFeedGeneratorRecordsRequest, GetFeedGeneratorRecordsResponse, GetFeedGeneratorStatusRequest, GetFeedGeneratorStatusResponse, GetFollowCountsRequest, GetFollowCountsResponse, GetFollowerCountsRequest, GetFollowerCountsResponse, GetFollowersRequest, GetFollowersResponse, GetFollowRecordsRequest, GetFollowRecordsResponse, GetFollowsRequest, GetFollowsResponse, GetFollowSuggestionsRequest, GetFollowSuggestionsResponse, GetInteractionCountsRequest, GetInteractionCountsResponse, GetLabelsRequest, GetLabelsResponse, GetLatestRevRequest, GetLatestRevResponse, GetLikeCountsRequest, GetLikeCountsResponse, GetLikeRecordsRequest, GetLikeRecordsResponse, GetLikesByActorAndSubjectsRequest, GetLikesByActorAndSubjectsResponse, GetLikesBySubjectRequest, GetLikesBySubjectResponse, GetListBlockRecordsRequest, GetListBlockRecordsResponse, GetListCountRequest, GetListCountResponse, GetListFeedRequest, GetListFeedResponse, GetListItemRecordsRequest, GetListItemRecordsResponse, GetListMembershipRequest, GetListMembershipResponse, GetListMembersRequest, GetListMembersResponse, GetListRecordsRequest, GetListRecordsResponse, GetMutelistSubscriptionRequest, GetMutelistSubscriptionResponse, GetMutelistSubscriptionsRequest, GetMutelistSubscriptionsResponse, GetMutesRequest, GetMutesResponse, GetNotificationSeenRequest, GetNotificationSeenResponse, GetNotificationsRequest, GetNotificationsResponse, GetPostCountsRequest, GetPostCountsResponse, GetPostRecordsRequest, GetPostRecordsResponse, GetPostReplyCountsRequest, GetPostReplyCountsResponse, GetProfileRecordsRequest, GetProfileRecordsResponse, GetRelationshipsRequest, GetRelationshipsResponse, GetRepostCountsRequest, GetRepostCountsResponse, GetRepostRecordsRequest, GetRepostRecordsResponse, GetRepostsByActorAndSubjectsRequest, GetRepostsByActorAndSubjectsResponse, GetRepostsBySubjectRequest, GetRepostsBySubjectResponse, GetSuggestedFeedsRequest, GetSuggestedFeedsResponse, GetThreadGateRecordsRequest, GetThreadGateRecordsResponse, GetThreadRequest, GetThreadResponse, GetTimelineRequest, GetTimelineResponse, GetUnreadNotificationCountRequest, GetUnreadNotificationCountResponse, MuteActorListRequest, MuteActorListResponse, MuteActorRequest, MuteActorResponse, PingRequest, PingResponse, SearchActorsRequest, SearchActorsResponse, SearchPostsRequest, SearchPostsResponse, UnmuteActorListRequest, UnmuteActorListResponse, UnmuteActorRequest, UnmuteActorResponse, UpdateNotificationSeenRequest, UpdateNotificationSeenResponse, UpdateTakedownRequest, UpdateTakedownResponse } from "./bsky_pb.ts"; import { MethodKind } from "@bufbuild/protobuf"; /** @@ -236,6 +236,26 @@ export const Service = { O: GetRepostCountsResponse, kind: MethodKind.Unary, }, + /** + * Interaction Counts + * + * @generated from rpc bsky.Service.GetInteractionCounts + */ + getInteractionCounts: { + name: "GetInteractionCounts", + I: GetInteractionCountsRequest, + O: GetInteractionCountsResponse, + kind: MethodKind.Unary, + }, + /** + * @generated from rpc bsky.Service.GetCountsForUsers + */ + getCountsForUsers: { + name: "GetCountsForUsers", + I: GetCountsForUsersRequest, + O: GetCountsForUsersResponse, + kind: MethodKind.Unary, + }, /** * Profile * diff --git a/packages/bsky/src/data-plane/gen/bsky_pb.ts b/packages/bsky/src/data-plane/gen/bsky_pb.ts index 34c0250de82..4ca1e4b2085 100644 --- a/packages/bsky/src/data-plane/gen/bsky_pb.ts +++ b/packages/bsky/src/data-plane/gen/bsky_pb.ts @@ -1063,9 +1063,14 @@ export class FollowInfo extends Message { uri = ""; /** - * @generated from field: string did = 2; + * @generated from field: string actor_did = 2; */ - did = ""; + actorDid = ""; + + /** + * @generated from field: string subject_did = 3; + */ + subjectDid = ""; constructor(data?: PartialMessage) { super(); @@ -1076,7 +1081,8 @@ export class FollowInfo extends Message { static readonly typeName = "bsky.FollowInfo"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ { no: 1, name: "uri", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "did", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "actor_did", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "subject_did", kind: "scalar", T: 9 /* ScalarType.STRING */ }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): FollowInfo { @@ -1712,9 +1718,9 @@ export class GetActorLikesResponse extends Message { */ export class GetLikeCountsRequest extends Message { /** - * @generated from field: repeated string uris = 1; + * @generated from field: repeated bsky.RecordRef refs = 1; */ - uris: string[] = []; + refs: RecordRef[] = []; constructor(data?: PartialMessage) { super(); @@ -1724,7 +1730,7 @@ export class GetLikeCountsRequest extends Message { static readonly runtime: typeof proto3 = proto3; static readonly typeName = "bsky.GetLikeCountsRequest"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "uris", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + { no: 1, name: "refs", kind: "message", T: RecordRef, repeated: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): GetLikeCountsRequest { @@ -1781,6 +1787,188 @@ export class GetLikeCountsResponse extends Message { } } +/** + * + * Interactions + * + * + * @generated from message bsky.GetInteractionCountsRequest + */ +export class GetInteractionCountsRequest extends Message { + /** + * @generated from field: repeated bsky.RecordRef refs = 1; + */ + refs: RecordRef[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "bsky.GetInteractionCountsRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "refs", kind: "message", T: RecordRef, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): GetInteractionCountsRequest { + return new GetInteractionCountsRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): GetInteractionCountsRequest { + return new GetInteractionCountsRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): GetInteractionCountsRequest { + return new GetInteractionCountsRequest().fromJsonString(jsonString, options); + } + + static equals(a: GetInteractionCountsRequest | PlainMessage | undefined, b: GetInteractionCountsRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(GetInteractionCountsRequest, a, b); + } +} + +/** + * @generated from message bsky.GetInteractionCountsResponse + */ +export class GetInteractionCountsResponse extends Message { + /** + * @generated from field: repeated int32 likes = 1; + */ + likes: number[] = []; + + /** + * @generated from field: repeated int32 reposts = 2; + */ + reposts: number[] = []; + + /** + * @generated from field: repeated int32 replies = 3; + */ + replies: number[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "bsky.GetInteractionCountsResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "likes", kind: "scalar", T: 5 /* ScalarType.INT32 */, repeated: true }, + { no: 2, name: "reposts", kind: "scalar", T: 5 /* ScalarType.INT32 */, repeated: true }, + { no: 3, name: "replies", kind: "scalar", T: 5 /* ScalarType.INT32 */, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): GetInteractionCountsResponse { + return new GetInteractionCountsResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): GetInteractionCountsResponse { + return new GetInteractionCountsResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): GetInteractionCountsResponse { + return new GetInteractionCountsResponse().fromJsonString(jsonString, options); + } + + static equals(a: GetInteractionCountsResponse | PlainMessage | undefined, b: GetInteractionCountsResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(GetInteractionCountsResponse, a, b); + } +} + +/** + * @generated from message bsky.GetCountsForUsersRequest + */ +export class GetCountsForUsersRequest extends Message { + /** + * @generated from field: repeated string dids = 1; + */ + dids: string[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "bsky.GetCountsForUsersRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "dids", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): GetCountsForUsersRequest { + return new GetCountsForUsersRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): GetCountsForUsersRequest { + return new GetCountsForUsersRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): GetCountsForUsersRequest { + return new GetCountsForUsersRequest().fromJsonString(jsonString, options); + } + + static equals(a: GetCountsForUsersRequest | PlainMessage | undefined, b: GetCountsForUsersRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(GetCountsForUsersRequest, a, b); + } +} + +/** + * @generated from message bsky.GetCountsForUsersResponse + */ +export class GetCountsForUsersResponse extends Message { + /** + * @generated from field: repeated int32 posts = 1; + */ + posts: number[] = []; + + /** + * @generated from field: repeated int32 reposts = 2; + */ + reposts: number[] = []; + + /** + * @generated from field: repeated int32 following = 3; + */ + following: number[] = []; + + /** + * @generated from field: repeated int32 followers = 4; + */ + followers: number[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "bsky.GetCountsForUsersResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "posts", kind: "scalar", T: 5 /* ScalarType.INT32 */, repeated: true }, + { no: 2, name: "reposts", kind: "scalar", T: 5 /* ScalarType.INT32 */, repeated: true }, + { no: 3, name: "following", kind: "scalar", T: 5 /* ScalarType.INT32 */, repeated: true }, + { no: 4, name: "followers", kind: "scalar", T: 5 /* ScalarType.INT32 */, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): GetCountsForUsersResponse { + return new GetCountsForUsersResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): GetCountsForUsersResponse { + return new GetCountsForUsersResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): GetCountsForUsersResponse { + return new GetCountsForUsersResponse().fromJsonString(jsonString, options); + } + + static equals(a: GetCountsForUsersResponse | PlainMessage | undefined, b: GetCountsForUsersResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(GetCountsForUsersResponse, a, b); + } +} + /** * - return repost uris where subject uri is subject A * - `getReposts` list for a post @@ -1789,22 +1977,17 @@ export class GetLikeCountsResponse extends Message { */ export class GetRepostsBySubjectRequest extends Message { /** - * @generated from field: string subject_uri = 1; - */ - subjectUri = ""; - - /** - * @generated from field: optional string subject_cid = 2; + * @generated from field: bsky.RecordRef subject = 1; */ - subjectCid?: string; + subject?: RecordRef; /** - * @generated from field: int32 limit = 3; + * @generated from field: int32 limit = 2; */ limit = 0; /** - * @generated from field: string cursor = 4; + * @generated from field: string cursor = 3; */ cursor = ""; @@ -1816,10 +1999,9 @@ export class GetRepostsBySubjectRequest extends Message [ - { no: 1, name: "subject_uri", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "subject_cid", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, - { no: 3, name: "limit", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, - { no: 4, name: "cursor", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 1, name: "subject", kind: "message", T: RecordRef }, + { no: 2, name: "limit", kind: "scalar", T: 5 /* ScalarType.INT32 */ }, + { no: 3, name: "cursor", kind: "scalar", T: 9 /* ScalarType.STRING */ }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): GetRepostsBySubjectRequest { @@ -2111,9 +2293,9 @@ export class GetActorRepostsResponse extends Message { */ export class GetRepostCountsRequest extends Message { /** - * @generated from field: repeated string uris = 1; + * @generated from field: repeated bsky.RecordRef refs = 1; */ - uris: string[] = []; + refs: RecordRef[] = []; constructor(data?: PartialMessage) { super(); @@ -2123,7 +2305,7 @@ export class GetRepostCountsRequest extends Message { static readonly runtime: typeof proto3 = proto3; static readonly typeName = "bsky.GetRepostCountsRequest"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ - { no: 1, name: "uris", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + { no: 1, name: "refs", kind: "message", T: RecordRef, repeated: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): GetRepostCountsRequest { @@ -5115,10 +5297,20 @@ export class FeedItem extends Message { uri = ""; /** - * @generated from field: string repost = 2; + * @generated from field: string cid = 2; + */ + cid = ""; + + /** + * @generated from field: string repost = 3; */ repost = ""; + /** + * @generated from field: string repost_cid = 4; + */ + repostCid = ""; + constructor(data?: PartialMessage) { super(); proto3.util.initPartial(data, this); @@ -5128,7 +5320,9 @@ export class FeedItem extends Message { static readonly typeName = "bsky.FeedItem"; static readonly fields: FieldList = proto3.util.newFieldList(() => [ { no: 1, name: "uri", kind: "scalar", T: 9 /* ScalarType.STRING */ }, - { no: 2, name: "repost", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "cid", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "repost", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 4, name: "repost_cid", kind: "scalar", T: 9 /* ScalarType.STRING */ }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): FeedItem { @@ -5631,9 +5825,9 @@ export class GetFollowSuggestionsResponse extends Message { /** - * @generated from field: repeated string uris = 1; + * @generated from field: repeated bsky.RecordRef refs = 1; */ - uris: string[] = []; + refs: RecordRef[] = []; constructor(data?: PartialMessage) { super(); @@ -5643,7 +5837,7 @@ export class GetPostReplyCountsRequest extends Message [ - { no: 1, name: "uris", kind: "scalar", T: 9 /* ScalarType.STRING */, repeated: true }, + { no: 1, name: "refs", kind: "message", T: RecordRef, repeated: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): GetPostReplyCountsRequest { diff --git a/packages/bsky/src/data-plane/server/routes/follows.ts b/packages/bsky/src/data-plane/server/routes/follows.ts index d9755de9064..6a801fd71dc 100644 --- a/packages/bsky/src/data-plane/server/routes/follows.ts +++ b/packages/bsky/src/data-plane/server/routes/follows.ts @@ -33,6 +33,7 @@ export default (db: Database): Partial> => ({ .select([ 'follow.uri as uri', 'follow.cid as cid', + 'follow.creator as creatorDid', 'follow.subjectDid as subjectDid', 'follow.sortAt as sortAt', ]) @@ -47,7 +48,11 @@ export default (db: Database): Partial> => ({ const followers = await followersReq.execute() return { - followers: followers.map((f) => ({ uri: f.uri, did: f.subjectDid })), + followers: followers.map((f) => ({ + uri: f.uri, + actorDid: f.creatorDid, + subjectDid: f.subjectDid, + })), cursor: keyset.packFromResult(followers), } }, @@ -63,6 +68,7 @@ export default (db: Database): Partial> => ({ .select([ 'follow.uri as uri', 'follow.cid as cid', + 'follow.creator as creatorDid', 'follow.subjectDid as subjectDid', 'follow.sortAt as sortAt', ]) @@ -78,7 +84,11 @@ export default (db: Database): Partial> => ({ const follows = await followsReq.execute() return { - follows: follows.map((f) => ({ uri: f.uri, did: f.subjectDid })), + follows: follows.map((f) => ({ + uri: f.uri, + actorDid: f.creatorDid, + subjectDid: f.subjectDid, + })), cursor: keyset.packFromResult(follows), } }, diff --git a/packages/bsky/src/data-plane/server/routes/index.ts b/packages/bsky/src/data-plane/server/routes/index.ts index eb7c9cb57b7..0fa4cb6d4ef 100644 --- a/packages/bsky/src/data-plane/server/routes/index.ts +++ b/packages/bsky/src/data-plane/server/routes/index.ts @@ -5,6 +5,7 @@ import blocks from './blocks' import feedGens from './feed-gens' import feeds from './feeds' import follows from './follows' +import interactions from './interactions' import labels from './labels' import likes from './likes' import lists from './lists' @@ -28,6 +29,7 @@ export default (db: Database) => (router: ConnectRouter) => ...feedGens(db), ...feeds(db), ...follows(db), + ...interactions(db), ...labels(db), ...likes(db), ...lists(db), diff --git a/packages/bsky/src/data-plane/server/routes/interactions.ts b/packages/bsky/src/data-plane/server/routes/interactions.ts new file mode 100644 index 00000000000..3d3ddc7f124 --- /dev/null +++ b/packages/bsky/src/data-plane/server/routes/interactions.ts @@ -0,0 +1,40 @@ +import { keyBy } from '@atproto/common' +import { ServiceImpl } from '@connectrpc/connect' +import { Service } from '../../gen/bsky_connect' +import { Database } from '../db' + +export default (db: Database): Partial> => ({ + async getInteractionCounts(req) { + const uris = req.refs.map((ref) => ref.uri) + if (uris.length === 0) { + return { likes: [], replies: [], reposts: [] } + } + const res = await db.db + .selectFrom('post_agg') + .where('uri', 'in', uris) + .selectAll() + .execute() + const byUri = keyBy(res, 'uri') + return { + likes: uris.map((uri) => byUri[uri]?.likeCount ?? 0), + replies: uris.map((uri) => byUri[uri]?.replyCount ?? 0), + reposts: uris.map((uri) => byUri[uri]?.repostCount ?? 0), + } + }, + async getCountsForUsers(req) { + if (req.dids.length === 0) { + return { followers: [], following: [], posts: [] } + } + const res = await db.db + .selectFrom('profile_agg') + .selectAll() + .where('did', 'in', req.dids) + .execute() + const byDid = keyBy(res, 'did') + return { + followers: req.dids.map((uri) => byDid[uri]?.followersCount ?? 0), + following: req.dids.map((uri) => byDid[uri]?.followsCount ?? 0), + posts: req.dids.map((uri) => byDid[uri]?.postsCount ?? 0), + } + }, +}) diff --git a/packages/bsky/src/data-plane/server/routes/likes.ts b/packages/bsky/src/data-plane/server/routes/likes.ts index 881037dd069..99d7d1aabb5 100644 --- a/packages/bsky/src/data-plane/server/routes/likes.ts +++ b/packages/bsky/src/data-plane/server/routes/likes.ts @@ -85,16 +85,17 @@ export default (db: Database): Partial> => ({ }, async getLikeCounts(req) { - if (req.uris.length === 0) { + const uris = req.refs.map((ref) => ref.uri) + if (uris.length === 0) { return { counts: [] } } const res = await db.db .selectFrom('post_agg') - .where('uri', 'in', req.uris) + .where('uri', 'in', uris) .selectAll() .execute() const byUri = keyBy(res, 'uri') - const counts = req.uris.map((uri) => byUri[uri]?.likeCount ?? 0) + const counts = uris.map((uri) => byUri[uri]?.likeCount ?? 0) return { counts } }, }) diff --git a/packages/bsky/src/data-plane/server/routes/posts.ts b/packages/bsky/src/data-plane/server/routes/posts.ts index 5cd58dd4aa6..a65c86646ee 100644 --- a/packages/bsky/src/data-plane/server/routes/posts.ts +++ b/packages/bsky/src/data-plane/server/routes/posts.ts @@ -5,16 +5,17 @@ import { Database } from '../db' export default (db: Database): Partial> => ({ async getPostReplyCounts(req) { - if (req.uris.length === 0) { + const uris = req.refs.map((ref) => ref.uri) + if (uris.length === 0) { return { counts: [] } } const res = await db.db .selectFrom('post_agg') .select(['uri', 'replyCount']) - .where('uri', 'in', req.uris) + .where('uri', 'in', uris) .execute() const byUri = keyBy(res, 'uri') - const counts = req.uris.map((uri) => byUri[uri]?.replyCount ?? 0) + const counts = uris.map((uri) => byUri[uri]?.replyCount ?? 0) return { counts } }, async getPostCounts(req) { diff --git a/packages/bsky/src/data-plane/server/routes/reposts.ts b/packages/bsky/src/data-plane/server/routes/reposts.ts index f0251d0c667..8dfdccdc6b9 100644 --- a/packages/bsky/src/data-plane/server/routes/reposts.ts +++ b/packages/bsky/src/data-plane/server/routes/reposts.ts @@ -6,12 +6,12 @@ import { TimeCidKeyset, paginate } from '../db/pagination' export default (db: Database): Partial> => ({ async getRepostsBySubject(req) { - const { subjectUri, cursor, limit } = req + const { subject, cursor, limit } = req const { ref } = db.db.dynamic let builder = db.db .selectFrom('repost') - .where('repost.subject', '=', subjectUri) + .where('repost.subject', '=', subject?.uri ?? '') .selectAll('repost') const keyset = new TimeCidKeyset(ref('repost.sortAt'), ref('repost.cid')) @@ -76,16 +76,17 @@ export default (db: Database): Partial> => ({ }, async getRepostCounts(req) { - if (req.uris.length === 0) { + const uris = req.refs.map((ref) => ref.uri) + if (uris.length === 0) { return { counts: [] } } const res = await db.db .selectFrom('post_agg') - .where('uri', 'in', req.uris) + .where('uri', 'in', uris) .selectAll() .execute() const byUri = keyBy(res, 'uri') - const counts = req.uris.map((uri) => byUri[uri]?.repostCount ?? 0) + const counts = uris.map((uri) => byUri[uri]?.repostCount ?? 0) return { counts } }, }) diff --git a/packages/bsky/src/hydration/actor.ts b/packages/bsky/src/hydration/actor.ts index 38c7096f891..abd9ff17cc4 100644 --- a/packages/bsky/src/hydration/actor.ts +++ b/packages/bsky/src/hydration/actor.ts @@ -119,16 +119,12 @@ export class ActorHydrator { } async getProfileAggregates(dids: string[]): Promise { - const [followers, follows, posts] = await Promise.all([ - this.dataplane.getFollowerCounts({ dids }), - this.dataplane.getFollowCounts({ dids }), - this.dataplane.getPostCounts({ dids }), - ]) + const counts = await this.dataplane.getCountsForUsers({ dids }) return dids.reduce((acc, did, i) => { return acc.set(did, { - followers: followers.counts[i] ?? 0, - follows: follows.counts[i] ?? 0, - posts: posts.counts[i] ?? 0, + followers: counts.followers[i] ?? 0, + follows: counts.following[i] ?? 0, + posts: counts.posts[i] ?? 0, }) }, new HydrationMap()) } diff --git a/packages/bsky/src/hydration/feed.ts b/packages/bsky/src/hydration/feed.ts index ecb4defd6fd..4f9827fde7a 100644 --- a/packages/bsky/src/hydration/feed.ts +++ b/packages/bsky/src/hydration/feed.ts @@ -85,16 +85,13 @@ export class FeedHydrator { } async getPostAggregates(uris: string[]): Promise { - const [likes, reposts, replies] = await Promise.all([ - this.dataplane.getLikeCounts({ uris }), - this.dataplane.getRepostCounts({ uris }), - this.dataplane.getPostReplyCounts({ uris }), - ]) + const refs = uris.map((uri) => ({ uri })) + const counts = await this.dataplane.getInteractionCounts({ refs }) return uris.reduce((acc, uri, i) => { return acc.set(uri, { - likes: likes.counts[i] ?? 0, - reposts: reposts.counts[i] ?? 0, - replies: replies.counts[i] ?? 0, + likes: counts.likes[i] ?? 0, + reposts: counts.reposts[i] ?? 0, + replies: counts.replies[i] ?? 0, }) }, new HydrationMap()) } @@ -129,7 +126,8 @@ export class FeedHydrator { } async getFeedGenAggregates(uris: string[]): Promise { - const likes = await this.dataplane.getLikeCounts({ uris }) + const refs = uris.map((uri) => ({ uri })) + const likes = await this.dataplane.getLikeCounts({ refs }) return uris.reduce((acc, uri, i) => { return acc.set(uri, { likes: likes.counts[i] ?? 0,