@atproto/bsky 0.1.7 → 0.2.1
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/CHANGELOG.md +58 -0
- package/dist/api/app/bsky/actor/getProfile.js +1 -1
- package/dist/api/app/bsky/actor/getProfile.js.map +1 -1
- package/dist/api/app/bsky/actor/getProfiles.js +1 -1
- package/dist/api/app/bsky/actor/getProfiles.js.map +1 -1
- package/dist/api/app/bsky/actor/getSuggestions.js +2 -2
- package/dist/api/app/bsky/actor/getSuggestions.js.map +1 -1
- package/dist/api/app/bsky/embed/getEmbedExternalView.d.ts.map +1 -1
- package/dist/api/app/bsky/embed/getEmbedExternalView.js +132 -53
- package/dist/api/app/bsky/embed/getEmbedExternalView.js.map +1 -1
- package/dist/api/app/bsky/feed/getAuthorFeed.d.ts.map +1 -1
- package/dist/api/app/bsky/feed/getAuthorFeed.js +4 -2
- package/dist/api/app/bsky/feed/getAuthorFeed.js.map +1 -1
- package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js +2 -2
- package/dist/api/app/bsky/graph/getSuggestedFollowsByActor.js.map +1 -1
- package/dist/api/app/bsky/notification/constants.d.ts +4 -1
- package/dist/api/app/bsky/notification/constants.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/constants.js +3 -0
- package/dist/api/app/bsky/notification/constants.js.map +1 -1
- package/dist/api/app/bsky/notification/getGroupedNotifications.d.ts +1 -1
- package/dist/api/app/bsky/notification/getGroupedNotifications.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/getGroupedNotifications.js +44 -26
- package/dist/api/app/bsky/notification/getGroupedNotifications.js.map +1 -1
- package/dist/api/app/bsky/notification/grouping/spotlight.d.ts +1 -1
- package/dist/api/app/bsky/notification/grouping/spotlight.d.ts.map +1 -1
- package/dist/api/app/bsky/notification/grouping/spotlight.js +114 -80
- package/dist/api/app/bsky/notification/grouping/spotlight.js.map +1 -1
- package/dist/api/app/bsky/notification/listActivitySubscriptions.js +3 -2
- package/dist/api/app/bsky/notification/listActivitySubscriptions.js.map +1 -1
- package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.d.ts.map +1 -1
- package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js +16 -148
- package/dist/api/app/bsky/unspecced/getAtmosphereExploreTab.js.map +1 -1
- package/dist/api/internal/bsky/actor/getProfiles.js +4 -1
- package/dist/api/internal/bsky/actor/getProfiles.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +43 -0
- package/dist/config.js.map +1 -1
- package/dist/data-plane/server/indexing/index.d.ts +3 -1
- package/dist/data-plane/server/indexing/index.d.ts.map +1 -1
- package/dist/data-plane/server/indexing/index.js +2 -0
- package/dist/data-plane/server/indexing/index.js.map +1 -1
- package/dist/data-plane/server/indexing/plugins/profile-link.d.ts +8 -0
- package/dist/data-plane/server/indexing/plugins/profile-link.d.ts.map +1 -0
- package/dist/data-plane/server/indexing/plugins/profile-link.js +30 -0
- package/dist/data-plane/server/indexing/plugins/profile-link.js.map +1 -0
- package/dist/data-plane/server/routes/records.d.ts.map +1 -1
- package/dist/data-plane/server/routes/records.js +1 -0
- package/dist/data-plane/server/routes/records.js.map +1 -1
- package/dist/hydration/actor.d.ts +4 -1
- package/dist/hydration/actor.d.ts.map +1 -1
- package/dist/hydration/actor.js +11 -0
- package/dist/hydration/actor.js.map +1 -1
- package/dist/hydration/external.d.ts +67 -64
- package/dist/hydration/external.d.ts.map +1 -1
- package/dist/hydration/external.js +79 -133
- package/dist/hydration/external.js.map +1 -1
- package/dist/hydration/hydrator.d.ts +53 -14
- package/dist/hydration/hydrator.d.ts.map +1 -1
- package/dist/hydration/hydrator.js +422 -105
- package/dist/hydration/hydrator.js.map +1 -1
- package/dist/lexicons/app/bsky/actor/defs.defs.d.ts +25 -0
- package/dist/lexicons/app/bsky/actor/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/actor/defs.defs.js +16 -0
- package/dist/lexicons/app/bsky/actor/defs.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/actor/link.d.ts +3 -0
- package/dist/lexicons/app/bsky/actor/link.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/actor/link.defs.d.ts +120 -0
- package/dist/lexicons/app/bsky/actor/link.defs.d.ts.map +1 -0
- package/dist/lexicons/app/bsky/actor/link.defs.js +39 -0
- package/dist/lexicons/app/bsky/actor/link.defs.js.map +1 -0
- package/dist/lexicons/app/bsky/actor/link.js +6 -0
- package/dist/lexicons/app/bsky/actor/link.js.map +1 -0
- package/dist/lexicons/app/bsky/actor/profile.defs.d.ts +14 -0
- package/dist/lexicons/app/bsky/actor/profile.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/actor/profile.defs.js +3 -0
- package/dist/lexicons/app/bsky/actor/profile.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/actor.d.ts +1 -0
- package/dist/lexicons/app/bsky/actor.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/actor.js +1 -0
- package/dist/lexicons/app/bsky/actor.js.map +1 -1
- package/dist/lexicons/app/bsky/authFullApp.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/authFullApp.defs.js +1 -0
- package/dist/lexicons/app/bsky/authFullApp.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/authManageProfile.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/authManageProfile.defs.js +1 -0
- package/dist/lexicons/app/bsky/authManageProfile.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.d.ts +13 -3
- package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.js +11 -3
- package/dist/lexicons/app/bsky/embed/getEmbedExternalView.defs.js.map +1 -1
- package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.d.ts +62 -3
- package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.d.ts.map +1 -1
- package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.js +53 -2
- package/dist/lexicons/app/bsky/notification/getGroupedNotifications.defs.js.map +1 -1
- package/dist/lexicons/tools/ozone/inbox/defs.defs.d.ts +14 -1
- package/dist/lexicons/tools/ozone/inbox/defs.defs.d.ts.map +1 -1
- package/dist/lexicons/tools/ozone/inbox/defs.defs.js +6 -1
- package/dist/lexicons/tools/ozone/inbox/defs.defs.js.map +1 -1
- package/dist/lexicons/tools/ozone/inbox/getNotificationPreferences.defs.d.ts +1 -0
- package/dist/lexicons/tools/ozone/inbox/getNotificationPreferences.defs.d.ts.map +1 -1
- package/dist/lexicons/tools/ozone/inbox/getNotificationPreferences.defs.js +1 -0
- package/dist/lexicons/tools/ozone/inbox/getNotificationPreferences.defs.js.map +1 -1
- package/dist/lexicons/tools/ozone/inbox/putNotificationPreferences.defs.d.ts +7 -2
- package/dist/lexicons/tools/ozone/inbox/putNotificationPreferences.defs.d.ts.map +1 -1
- package/dist/lexicons/tools/ozone/inbox/putNotificationPreferences.defs.js +4 -1
- package/dist/lexicons/tools/ozone/inbox/putNotificationPreferences.defs.js.map +1 -1
- package/dist/proto/bsky_connect.d.ts +10 -1
- package/dist/proto/bsky_connect.d.ts.map +1 -1
- package/dist/proto/bsky_connect.js +10 -1
- package/dist/proto/bsky_connect.js.map +1 -1
- package/dist/proto/bsky_pb.d.ts +34 -0
- package/dist/proto/bsky_pb.d.ts.map +1 -1
- package/dist/proto/bsky_pb.js +60 -0
- package/dist/proto/bsky_pb.js.map +1 -1
- package/dist/telemetry/events.d.ts +35 -0
- package/dist/telemetry/events.d.ts.map +1 -1
- package/dist/telemetry/events.js +68 -0
- package/dist/telemetry/events.js.map +1 -1
- package/dist/util/external/index.d.ts +8 -0
- package/dist/util/external/index.d.ts.map +1 -0
- package/dist/util/external/index.js +50 -0
- package/dist/util/external/index.js.map +1 -0
- package/dist/util/{standard-site.d.ts → external/standard-site.d.ts} +30 -4
- package/dist/util/external/standard-site.d.ts.map +1 -0
- package/dist/util/{standard-site.js → external/standard-site.js} +73 -4
- package/dist/util/external/standard-site.js.map +1 -0
- package/dist/views/external.js +1 -1
- package/dist/views/external.js.map +1 -1
- package/dist/views/index.d.ts +11 -7
- package/dist/views/index.d.ts.map +1 -1
- package/dist/views/index.js +94 -43
- package/dist/views/index.js.map +1 -1
- package/dist/views/profile-link-rules.d.ts +10 -0
- package/dist/views/profile-link-rules.d.ts.map +1 -0
- package/dist/views/profile-link-rules.js +48 -0
- package/dist/views/profile-link-rules.js.map +1 -0
- package/dist/views/types.d.ts +2 -0
- package/dist/views/types.d.ts.map +1 -1
- package/dist/views/types.js.map +1 -1
- package/package.json +7 -7
- package/dist/util/standard-site.d.ts.map +0 -1
- package/dist/util/standard-site.js.map +0 -1
|
@@ -74,7 +74,7 @@ const hydration = async (input) => {
|
|
|
74
74
|
const { ctx, params, skeleton } = input;
|
|
75
75
|
const { suggestedDids } = skeleton;
|
|
76
76
|
if (params.hydrateCtx.features.checkGate(params.hydrateCtx.features.Gate.SuggestedUsersSocialProofEnable)) {
|
|
77
|
-
return ctx.hydrator.hydrateProfilesDetailed(suggestedDids, params.hydrateCtx);
|
|
77
|
+
return ctx.hydrator.hydrateProfilesDetailed(suggestedDids, params.hydrateCtx, { knownFollowersDids: suggestedDids, activitySubscriptions: true });
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
80
80
|
return ctx.hydrator.hydrateProfiles(suggestedDids, params.hydrateCtx);
|
|
@@ -89,7 +89,7 @@ const noBlocksOrMutes = (input) => {
|
|
|
89
89
|
const presentation = (input) => {
|
|
90
90
|
const { ctx, hydration, skeleton } = input;
|
|
91
91
|
const { suggestedDids, contentLanguage } = skeleton;
|
|
92
|
-
const suggestions = mapDefined(suggestedDids, (did) => ctx.views.
|
|
92
|
+
const suggestions = mapDefined(suggestedDids, (did) => ctx.views.profile(did, hydration));
|
|
93
93
|
return {
|
|
94
94
|
recIdStr: skeleton.recIdStr,
|
|
95
95
|
contentLanguage,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getSuggestedFollowsByActor.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/graph/getSuggestedFollowsByActor.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"getSuggestedFollowsByActor.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/graph/getSuggestedFollowsByActor.ts"],"names":[],"mappings":"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","sourcesContent":["import { mapDefined, noUndefinedVals } from '@atproto/common'\nimport type { Client, DidString } from '@atproto/lex'\nimport {\n type Headers as HeadersMap,\n InternalServerError,\n InvalidRequestError,\n type Server,\n} from '@atproto/xrpc-server'\nimport type { AppContext } from '../../../../context.js'\nimport type { HydrateCtx, Hydrator } from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type RulesFnInput,\n type SkeletonFnInput,\n createPipeline,\n} from '../../../../pipeline.js'\nimport { getAtprotoPassthroughHeaders } from '../../../../util/headers.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\n\nexport default function (server: Server, ctx: AppContext) {\n const getSuggestedFollowsByActor = createPipeline(\n skeleton,\n hydration,\n noBlocksOrMutes,\n presentation,\n )\n server.add(app.bsky.graph.getSuggestedFollowsByActor, {\n auth: ctx.authVerifier.standardOptional,\n handler: async ({ auth, params, req, signal }) => {\n const viewer = auth.credentials.iss\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n labelers,\n viewer,\n features: ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({\n viewer,\n req,\n }),\n ),\n })\n\n const useIris = hydrateCtx.features.checkGate(\n hydrateCtx.features.Gate.SuggestedUsersIrisEnable,\n )\n const client = useIris ? ctx.irisClient : ctx.suggestionsClient\n\n if (!client) {\n return {\n encoding: 'application/json',\n body: { suggestions: [] },\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n }\n }\n\n const headers = noUndefinedVals({\n 'accept-language': req.headers['accept-language'],\n ...getAtprotoPassthroughHeaders(req),\n })\n\n const { contentLanguage, ...body } = await getSuggestedFollowsByActor(\n { ...params, hydrateCtx, headers, signal },\n ctx,\n )\n\n return {\n encoding: 'application/json',\n body,\n headers: {\n ...(contentLanguage ? { 'content-language': contentLanguage } : null),\n ...resHeaders({ labelers: hydrateCtx.labelers }),\n },\n }\n },\n })\n}\n\nconst skeleton = async (\n input: SkeletonFnInput<Context, Params>,\n): Promise<SkeletonState> => {\n const { params, ctx } = input\n\n const useIris = params.hydrateCtx.features.checkGate(\n params.hydrateCtx.features.Gate.SuggestedUsersIrisEnable,\n )\n const client = useIris ? ctx.irisClient : ctx.suggestionsClient\n\n // handled above already, this branch should not be reached\n if (!client) {\n throw new InternalServerError('Suggestions service not configured')\n }\n\n const [relativeToDid] = await ctx.hydrator.actor.getDids([params.actor])\n if (!relativeToDid) {\n throw new InvalidRequestError('Actor not found')\n }\n\n const res = await client.xrpc(app.bsky.unspecced.getSuggestionsSkeleton, {\n params: {\n viewer: params.hydrateCtx.viewer ?? undefined,\n relativeToDid,\n },\n headers: params.headers,\n signal: params.signal,\n })\n\n return {\n recIdStr: res.body.recIdStr,\n suggestedDids: res.body.actors.map((a) => a.did),\n contentLanguage: res.headers.get('content-language') ?? undefined,\n }\n}\n\nconst hydration = async (\n input: HydrationFnInput<Context, Params, SkeletonState>,\n) => {\n const { ctx, params, skeleton } = input\n const { suggestedDids } = skeleton\n if (\n params.hydrateCtx.features.checkGate(\n params.hydrateCtx.features.Gate.SuggestedUsersSocialProofEnable,\n )\n ) {\n return ctx.hydrator.hydrateProfilesDetailed(\n suggestedDids,\n params.hydrateCtx,\n { knownFollowersDids: suggestedDids, activitySubscriptions: true },\n )\n } else {\n return ctx.hydrator.hydrateProfiles(suggestedDids, params.hydrateCtx)\n }\n}\n\nconst noBlocksOrMutes = (\n input: RulesFnInput<Context, Params, SkeletonState>,\n) => {\n const { ctx, skeleton, hydration } = input\n skeleton.suggestedDids = skeleton.suggestedDids.filter(\n (did) =>\n !ctx.views.viewerBlockExists(did, hydration) &&\n !ctx.views.viewerMuteExists(did, hydration),\n )\n return skeleton\n}\n\nconst presentation = (\n input: PresentationFnInput<Context, Params, SkeletonState>,\n) => {\n const { ctx, hydration, skeleton } = input\n const { suggestedDids, contentLanguage } = skeleton\n const suggestions = mapDefined(suggestedDids, (did) =>\n ctx.views.profile(did, hydration),\n )\n return {\n recIdStr: skeleton.recIdStr,\n contentLanguage,\n suggestions,\n }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n suggestionsClient: Client | undefined\n irisClient: Client | undefined\n}\n\ntype Params = app.bsky.graph.getSuggestedFollowsByActor.$Params & {\n hydrateCtx: HydrateCtx\n headers: HeadersMap\n signal: AbortSignal\n}\n\ntype SkeletonState = {\n suggestedDids: DidString[]\n recIdStr?: string\n contentLanguage?: string\n}\n"]}
|
|
@@ -31,7 +31,10 @@ export declare const APPVIEW_NOTIFICATION_REASON: {
|
|
|
31
31
|
readonly UNVERIFIED: 'unverified';
|
|
32
32
|
readonly VERIFIED: 'verified';
|
|
33
33
|
readonly MULTI_POST_LIKE: 'multi-post-like';
|
|
34
|
+
readonly MULTI_POST_REPOST: 'multi-post-repost';
|
|
35
|
+
readonly MULTI_POST_LIKE_VIA_REPOST: 'multi-post-like-via-repost';
|
|
36
|
+
readonly MULTI_POST_REPOST_VIA_REPOST: 'multi-post-repost-via-repost';
|
|
34
37
|
};
|
|
35
|
-
export type AppviewNotificationReason =
|
|
38
|
+
export type AppviewNotificationReason = (typeof APPVIEW_NOTIFICATION_REASON)[keyof typeof APPVIEW_NOTIFICATION_REASON];
|
|
36
39
|
export declare const MARK_READ_GENERIC = "mark-read-generic";
|
|
37
40
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,mBAAmB;aAC9B,aAAa,EAAE,eAAe;aAC9B,MAAM,EAAE,QAAQ;aAChB,WAAW,EAAE,aAAa;aAC1B,IAAI,EAAE,MAAM;aACZ,eAAe,EAAE,iBAAiB;aAClC,OAAO,EAAE,SAAS;aAClB,KAAK,EAAE,OAAO;aACd,KAAK,EAAE,OAAO;aACd,MAAM,EAAE,QAAQ;aAChB,iBAAiB,EAAE,mBAAmB;aACtC,kBAAkB,EAAE,oBAAoB;aACxC,eAAe,EAAE,iBAAiB;aAClC,UAAU,EAAE,YAAY;aACxB,QAAQ,EAAE,UAAU;CACZ,CAAA;AAEV,MAAM,MAAM,2BAA2B,GACrC,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAA;AAEhE,eAAO,MAAM,2BAA2B;4BAnBvB,eAAe;qBACtB,QAAQ;0BACH,aAAa;mBACpB,MAAM;8BACK,iBAAiB;sBACzB,SAAS;oBACX,OAAO;oBACP,OAAO;qBACN,QAAQ;gCACG,mBAAmB;iCAClB,oBAAoB;8BACvB,iBAAiB;yBACtB,YAAY;uBACd,UAAU;8BAQH,iBAAiB;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,mBAAmB;aAC9B,aAAa,EAAE,eAAe;aAC9B,MAAM,EAAE,QAAQ;aAChB,WAAW,EAAE,aAAa;aAC1B,IAAI,EAAE,MAAM;aACZ,eAAe,EAAE,iBAAiB;aAClC,OAAO,EAAE,SAAS;aAClB,KAAK,EAAE,OAAO;aACd,KAAK,EAAE,OAAO;aACd,MAAM,EAAE,QAAQ;aAChB,iBAAiB,EAAE,mBAAmB;aACtC,kBAAkB,EAAE,oBAAoB;aACxC,eAAe,EAAE,iBAAiB;aAClC,UAAU,EAAE,YAAY;aACxB,QAAQ,EAAE,UAAU;CACZ,CAAA;AAEV,MAAM,MAAM,2BAA2B,GACrC,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAA;AAEhE,eAAO,MAAM,2BAA2B;4BAnBvB,eAAe;qBACtB,QAAQ;0BACH,aAAa;mBACpB,MAAM;8BACK,iBAAiB;sBACzB,SAAS;oBACX,OAAO;oBACP,OAAO;qBACN,QAAQ;gCACG,mBAAmB;iCAClB,oBAAoB;8BACvB,iBAAiB;yBACtB,YAAY;uBACd,UAAU;8BAQH,iBAAiB;gCACf,mBAAmB;yCACV,4BAA4B;2CAC1B,8BAA8B;CACpD,CAAA;AAEV,MAAM,MAAM,yBAAyB,GACnC,CAAC,OAAO,2BAA2B,CAAC,CAAC,MAAM,OAAO,2BAA2B,CAAC,CAAA;AAGhF,eAAO,MAAM,iBAAiB,sBAAsB,CAAA"}
|
|
@@ -18,6 +18,9 @@ export const NOTIFICATION_REASON = {
|
|
|
18
18
|
export const APPVIEW_NOTIFICATION_REASON = {
|
|
19
19
|
...NOTIFICATION_REASON,
|
|
20
20
|
MULTI_POST_LIKE: 'multi-post-like',
|
|
21
|
+
MULTI_POST_REPOST: 'multi-post-repost',
|
|
22
|
+
MULTI_POST_LIKE_VIA_REPOST: 'multi-post-like-via-repost',
|
|
23
|
+
MULTI_POST_REPOST_VIA_REPOST: 'multi-post-repost-via-repost',
|
|
21
24
|
};
|
|
22
25
|
// Not a real notification the users see, but used to mark as read across user devices.
|
|
23
26
|
export const MARK_READ_GENERIC = 'mark-read-generic';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/constants.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,aAAa,EAAE,eAAe;IAC9B,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,aAAa;IAC1B,IAAI,EAAE,MAAM;IACZ,eAAe,EAAE,iBAAiB;IAClC,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;IAChB,iBAAiB,EAAE,mBAAmB;IACtC,kBAAkB,EAAE,oBAAoB;IACxC,eAAe,EAAE,iBAAiB;IAClC,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;CACZ,CAAA;AAKV,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,GAAG,mBAAmB;IACtB,eAAe,EAAE,iBAAiB;
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/constants.ts"],"names":[],"mappings":"AAAA,mCAAmC;AACnC,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,aAAa,EAAE,eAAe;IAC9B,MAAM,EAAE,QAAQ;IAChB,WAAW,EAAE,aAAa;IAC1B,IAAI,EAAE,MAAM;IACZ,eAAe,EAAE,iBAAiB;IAClC,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,QAAQ;IAChB,iBAAiB,EAAE,mBAAmB;IACtC,kBAAkB,EAAE,oBAAoB;IACxC,eAAe,EAAE,iBAAiB;IAClC,UAAU,EAAE,YAAY;IACxB,QAAQ,EAAE,UAAU;CACZ,CAAA;AAKV,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,GAAG,mBAAmB;IACtB,eAAe,EAAE,iBAAiB;IAClC,iBAAiB,EAAE,mBAAmB;IACtC,0BAA0B,EAAE,4BAA4B;IACxD,4BAA4B,EAAE,8BAA8B;CACpD,CAAA;AAKV,uFAAuF;AACvF,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAAmB,CAAA","sourcesContent":["// As they come from the dataplane.\nexport const NOTIFICATION_REASON = {\n CONTACT_MATCH: 'contact-match',\n FOLLOW: 'follow',\n FOLLOW_BACK: 'follow-back',\n LIKE: 'like',\n LIKE_VIA_REPOST: 'like-via-repost',\n MENTION: 'mention',\n QUOTE: 'quote',\n REPLY: 'reply',\n REPOST: 'repost',\n REPOST_VIA_REPOST: 'repost-via-repost',\n STARTERPACK_JOINED: 'starterpack-joined',\n SUBSCRIBED_POST: 'subscribed-post',\n UNVERIFIED: 'unverified',\n VERIFIED: 'verified',\n} as const\n\nexport type DataplaneNotificationReason =\n (typeof NOTIFICATION_REASON)[keyof typeof NOTIFICATION_REASON]\n\nexport const APPVIEW_NOTIFICATION_REASON = {\n ...NOTIFICATION_REASON,\n MULTI_POST_LIKE: 'multi-post-like',\n MULTI_POST_REPOST: 'multi-post-repost',\n MULTI_POST_LIKE_VIA_REPOST: 'multi-post-like-via-repost',\n MULTI_POST_REPOST_VIA_REPOST: 'multi-post-repost-via-repost',\n} as const\n\nexport type AppviewNotificationReason =\n (typeof APPVIEW_NOTIFICATION_REASON)[keyof typeof APPVIEW_NOTIFICATION_REASON]\n\n// Not a real notification the users see, but used to mark as read across user devices.\nexport const MARK_READ_GENERIC = 'mark-read-generic'\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Server } from '@atproto/xrpc-server';
|
|
2
2
|
import type { AppContext } from '../../../../context.js';
|
|
3
|
-
export declare const getNextRawLimit: (itemsFetched: number, groupsBuilt: number, groupsNeeded: number) => number;
|
|
3
|
+
export declare const getNextRawLimit: (itemsFetched: number, groupsBuilt: number, groupsNeeded: number, minRawLimit: number, maxRawItems: number) => number;
|
|
4
4
|
export default function (server: Server, ctx: AppContext): void;
|
|
5
5
|
//# sourceMappingURL=getGroupedNotifications.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getGroupedNotifications.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/getGroupedNotifications.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;
|
|
1
|
+
{"version":3,"file":"getGroupedNotifications.d.ts","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/getGroupedNotifications.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAuCxD,eAAO,MAAM,eAAe,iBACZ,MAAM,eACP,MAAM,gBACL,MAAM,eACP,MAAM,eACN,MAAM,KAClB,MAoBF,CAAA;AAED,MAAM,CAAC,OAAO,WAAW,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,UAAU,QAqCvD"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
|
-
import { mapDefined } from '@atproto/common';
|
|
2
|
+
import { dedupeStrs, mapDefined } from '@atproto/common';
|
|
3
3
|
import { asDatetimeString, } from '@atproto/lex';
|
|
4
4
|
import { AtUri } from '@atproto/syntax';
|
|
5
5
|
import { app } from '../../../../lexicons/index.js';
|
|
6
6
|
import { createPipeline, } from '../../../../pipeline.js';
|
|
7
7
|
import { NotificationFeed } from '../../../../proto/bsky_pb.js';
|
|
8
|
+
import { events } from '../../../../telemetry/events.js';
|
|
8
9
|
import { uriToDid } from '../../../../util/uris.js';
|
|
9
10
|
import { resHeaders } from '../../../util.js';
|
|
10
11
|
import { NOTIFICATION_REASON } from './constants.js';
|
|
@@ -12,27 +13,23 @@ import { buildGroups, parseRawNotification, } from './grouping/grouping.js';
|
|
|
12
13
|
import { compareNotificationGroupsNewestFirst, isNotificationRead, } from './grouping/shared.js';
|
|
13
14
|
import { buildSpotlight } from './grouping/spotlight.js';
|
|
14
15
|
import { delayCursor, shouldFilterForNeedsReview, shouldFilterHiddenThreadTag, shouldFilterReplyByThreadgate, } from './util.js';
|
|
15
|
-
const
|
|
16
|
-
const MAX_RAW_ITEMS = 3000;
|
|
17
|
-
const MAX_READS = 3;
|
|
18
|
-
const MAX_GROUP_ITEMS_WITH_RELATED_VIEWS = 10;
|
|
19
|
-
export const getNextRawLimit = (itemsFetched, groupsBuilt, groupsNeeded) => {
|
|
16
|
+
export const getNextRawLimit = (itemsFetched, groupsBuilt, groupsNeeded, minRawLimit, maxRawItems) => {
|
|
20
17
|
// How many raw items we still can fetch until hit the max.
|
|
21
|
-
const remainingItems =
|
|
18
|
+
const remainingItems = maxRawItems - itemsFetched;
|
|
22
19
|
if (remainingItems <= 0)
|
|
23
20
|
return 0;
|
|
24
21
|
// Defensive fallback to avoid a division by 0 later.
|
|
25
22
|
// This is for the scenario where fetched items but couldn't build any groups with them
|
|
26
23
|
// (let's say all notifications are missing data and are filtered out, shouldn't happen in practice).
|
|
27
24
|
if (groupsBuilt === 0)
|
|
28
|
-
return Math.min(
|
|
25
|
+
return Math.min(minRawLimit, remainingItems);
|
|
29
26
|
// How many groups still to be built - note that we might still fit items into existing groups.
|
|
30
27
|
const groupsMissing = Math.max(0, groupsNeeded - groupsBuilt);
|
|
31
28
|
const averagePerGroup = itemsFetched / groupsBuilt;
|
|
32
29
|
// Estimate additional raw notifications from the average group size.
|
|
33
30
|
const estimatedRawItems = Math.ceil(averagePerGroup * groupsMissing);
|
|
34
31
|
// Keep the estimate above the minimum batch and within the total raw-item budget.
|
|
35
|
-
return Math.min(remainingItems, Math.max(
|
|
32
|
+
return Math.min(remainingItems, Math.max(minRawLimit, estimatedRawItems));
|
|
36
33
|
};
|
|
37
34
|
export default function (server, ctx) {
|
|
38
35
|
const getGroupedNotifications = createPipeline(skeleton, hydration, rules, presentation);
|
|
@@ -76,8 +73,8 @@ const skeleton = async (input) => {
|
|
|
76
73
|
let groups = [];
|
|
77
74
|
let nextCursor;
|
|
78
75
|
let itemsFetched = 0;
|
|
79
|
-
let rawLimit =
|
|
80
|
-
for (let read = 0; read <
|
|
76
|
+
let rawLimit = ctx.cfg.notificationsV2MinRawLimit;
|
|
77
|
+
for (let read = 0; read < ctx.cfg.notificationsV2MaxReads; read++) {
|
|
81
78
|
const response = await ctx.hydrator.dataplane.getNotificationsV2({
|
|
82
79
|
actorDid: params.hydrateCtx.viewer,
|
|
83
80
|
feed,
|
|
@@ -124,16 +121,27 @@ const skeleton = async (input) => {
|
|
|
124
121
|
if (!shouldContinueBuildingPage)
|
|
125
122
|
break;
|
|
126
123
|
cursor = response.cursor;
|
|
127
|
-
rawLimit = getNextRawLimit(itemsFetched, groups.length, params.limit);
|
|
124
|
+
rawLimit = getNextRawLimit(itemsFetched, groups.length, params.limit, ctx.cfg.notificationsV2MinRawLimit, ctx.cfg.notificationsV2MaxRawItems);
|
|
128
125
|
if (rawLimit === 0)
|
|
129
126
|
break;
|
|
130
127
|
}
|
|
131
|
-
return
|
|
128
|
+
return {
|
|
129
|
+
...buildSpotlight(items, groups, nextCursor, params.limit, seenAt),
|
|
130
|
+
itemsFetched,
|
|
131
|
+
};
|
|
132
132
|
};
|
|
133
133
|
const hydration = async (input) => {
|
|
134
134
|
const { ctx, params, skeleton } = input;
|
|
135
135
|
const notifs = skeleton.groups.flatMap((group) => group.items.map((item) => item.raw));
|
|
136
|
-
|
|
136
|
+
// Optimization: instead of fetching known followers for all users in all notifications,
|
|
137
|
+
// only fetch for the notifications type that want it (follow type), and only for the ones
|
|
138
|
+
// that appear before truncation.
|
|
139
|
+
const knownFollowersDids = dedupeStrs(skeleton.groups.flatMap((group) => group.kind === NOTIFICATION_REASON.FOLLOW
|
|
140
|
+
? truncateRelatedViewsItems(group.items, ctx.cfg.notificationsV2MaxGroupItemsWithRelatedViews).map((item) => item.actorDid)
|
|
141
|
+
: []));
|
|
142
|
+
return ctx.hydrator.hydrateGroupedNotifications(notifs, params.hydrateCtx, {
|
|
143
|
+
knownFollowersDids,
|
|
144
|
+
});
|
|
137
145
|
};
|
|
138
146
|
const rules = (input) => {
|
|
139
147
|
const { ctx, params, skeleton, hydration } = input;
|
|
@@ -240,6 +248,8 @@ const presentation = (input) => {
|
|
|
240
248
|
const { ctx, params, skeleton, hydration } = input;
|
|
241
249
|
const defs = app.bsky.notification.getGroupedNotifications;
|
|
242
250
|
const groups = mapDefined(skeleton.groups, (group) => ctx.views.notificationGroup(group, hydration));
|
|
251
|
+
const maxGroupItems = ctx.cfg.notificationsV2MaxGroupItemsWithRelatedViews;
|
|
252
|
+
// Build related profiles, records.
|
|
243
253
|
const profileDids = new Set();
|
|
244
254
|
const recordUris = new Set();
|
|
245
255
|
for (const { kind } of groups) {
|
|
@@ -248,31 +258,34 @@ const presentation = (input) => {
|
|
|
248
258
|
defs.likeViaRepostGroup.$isTypeOf(kind) ||
|
|
249
259
|
defs.repostViaRepostGroup.$isTypeOf(kind)) {
|
|
250
260
|
recordUris.add(kind.post);
|
|
251
|
-
for (const item of truncateRelatedViewsItems(kind.items)) {
|
|
261
|
+
for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {
|
|
252
262
|
profileDids.add(item.actor);
|
|
253
263
|
}
|
|
254
264
|
}
|
|
255
|
-
else if (defs.multiPostLikeGroup.$isTypeOf(kind)
|
|
265
|
+
else if (defs.multiPostLikeGroup.$isTypeOf(kind) ||
|
|
266
|
+
defs.multiPostRepostGroup.$isTypeOf(kind) ||
|
|
267
|
+
defs.multiPostLikeViaRepostGroup.$isTypeOf(kind) ||
|
|
268
|
+
defs.multiPostRepostViaRepostGroup.$isTypeOf(kind)) {
|
|
256
269
|
profileDids.add(kind.actor);
|
|
257
|
-
for (const item of truncateRelatedViewsItems(kind.items)) {
|
|
270
|
+
for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {
|
|
258
271
|
recordUris.add(item.post);
|
|
259
272
|
}
|
|
260
273
|
}
|
|
261
274
|
else if (defs.generatorLikeGroup.$isTypeOf(kind)) {
|
|
262
275
|
recordUris.add(kind.generator);
|
|
263
|
-
for (const item of truncateRelatedViewsItems(kind.items)) {
|
|
276
|
+
for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {
|
|
264
277
|
profileDids.add(item.actor);
|
|
265
278
|
}
|
|
266
279
|
}
|
|
267
280
|
else if (defs.followGroup.$isTypeOf(kind)) {
|
|
268
|
-
for (const item of truncateRelatedViewsItems(kind.items)) {
|
|
281
|
+
for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {
|
|
269
282
|
profileDids.add(item.actor);
|
|
270
283
|
if (item.starterPack)
|
|
271
284
|
recordUris.add(item.starterPack);
|
|
272
285
|
}
|
|
273
286
|
}
|
|
274
287
|
else if (defs.subscribedPostGroup.$isTypeOf(kind)) {
|
|
275
|
-
for (const item of truncateRelatedViewsItems(kind.items)) {
|
|
288
|
+
for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {
|
|
276
289
|
profileDids.add(item.actor);
|
|
277
290
|
recordUris.add(item.post);
|
|
278
291
|
}
|
|
@@ -296,14 +309,14 @@ const presentation = (input) => {
|
|
|
296
309
|
profileDids.add(kind.actor);
|
|
297
310
|
}
|
|
298
311
|
}
|
|
299
|
-
|
|
312
|
+
const body = {
|
|
300
313
|
groups,
|
|
301
314
|
relatedViews: [
|
|
302
315
|
...mapDefined([...profileDids], (did) => {
|
|
303
|
-
const view = ctx.views.
|
|
316
|
+
const view = ctx.views.profileBasic(did, hydration);
|
|
304
317
|
if (!view)
|
|
305
318
|
return;
|
|
306
|
-
return app.bsky.actor.defs.
|
|
319
|
+
return app.bsky.actor.defs.profileViewBasic.$build(view);
|
|
307
320
|
}),
|
|
308
321
|
...mapDefined([...recordUris], (uri) => {
|
|
309
322
|
const collection = new AtUri(uri).collection;
|
|
@@ -314,10 +327,10 @@ const presentation = (input) => {
|
|
|
314
327
|
return view;
|
|
315
328
|
}
|
|
316
329
|
else if (collection === app.bsky.graph.starterpack.$type) {
|
|
317
|
-
const view = ctx.views.
|
|
330
|
+
const view = ctx.views.starterPackBasic(uri, hydration);
|
|
318
331
|
if (!view)
|
|
319
332
|
return;
|
|
320
|
-
return app.bsky.graph.defs.
|
|
333
|
+
return app.bsky.graph.defs.starterPackViewBasic.$build(view);
|
|
321
334
|
}
|
|
322
335
|
else if (collection === app.bsky.feed.generator.$type) {
|
|
323
336
|
const view = ctx.views.feedGenerator(uri, hydration);
|
|
@@ -330,8 +343,13 @@ const presentation = (input) => {
|
|
|
330
343
|
cursor: skeleton.cursor,
|
|
331
344
|
seenAt: params.seenAt,
|
|
332
345
|
};
|
|
346
|
+
events.groupedNotifications({
|
|
347
|
+
itemsFetched: skeleton.itemsFetched,
|
|
348
|
+
itemsReturned: groups.reduce((count, group) => count + group.count, 0),
|
|
349
|
+
});
|
|
350
|
+
return body;
|
|
333
351
|
};
|
|
334
|
-
const truncateRelatedViewsItems = (items) => items.slice(0,
|
|
352
|
+
const truncateRelatedViewsItems = (items, maxItems) => items.slice(0, maxItems);
|
|
335
353
|
const normalizeFeed = (feed) => {
|
|
336
354
|
switch (feed) {
|
|
337
355
|
case 'people-i-follow':
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getGroupedNotifications.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/getGroupedNotifications.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto'\nimport { mapDefined } from '@atproto/common'\nimport {\n type AtUriString,\n type DatetimeString,\n type DidString,\n asDatetimeString,\n} from '@atproto/lex'\nimport { AtUri } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { ServerConfig } from '../../../../config.js'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtxWithViewer,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type RulesFnInput,\n type SkeletonFnInput,\n createPipeline,\n} from '../../../../pipeline.js'\nimport { NotificationFeed } from '../../../../proto/bsky_pb.js'\nimport { uriToDid } from '../../../../util/uris.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\nimport { NOTIFICATION_REASON } from './constants.js'\nimport {\n type NotificationGroup,\n type NotificationItem,\n type RawNotification,\n buildGroups,\n parseRawNotification,\n} from './grouping/grouping.js'\nimport {\n compareNotificationGroupsNewestFirst,\n isNotificationRead,\n} from './grouping/shared.js'\nimport { buildSpotlight } from './grouping/spotlight.js'\nimport {\n delayCursor,\n shouldFilterForNeedsReview,\n shouldFilterHiddenThreadTag,\n shouldFilterReplyByThreadgate,\n} from './util.js'\n\nconst MIN_RAW_LIMIT = 300\nconst MAX_RAW_ITEMS = 3000\nconst MAX_READS = 3\nconst MAX_GROUP_ITEMS_WITH_RELATED_VIEWS = 10\n\nexport const getNextRawLimit = (\n itemsFetched: number,\n groupsBuilt: number,\n groupsNeeded: number,\n): number => {\n // How many raw items we still can fetch until hit the max.\n const remainingItems = MAX_RAW_ITEMS - itemsFetched\n if (remainingItems <= 0) return 0\n\n // Defensive fallback to avoid a division by 0 later.\n // This is for the scenario where fetched items but couldn't build any groups with them\n // (let's say all notifications are missing data and are filtered out, shouldn't happen in practice).\n if (groupsBuilt === 0) return Math.min(MIN_RAW_LIMIT, remainingItems)\n\n // How many groups still to be built - note that we might still fit items into existing groups.\n const groupsMissing = Math.max(0, groupsNeeded - groupsBuilt)\n\n const averagePerGroup = itemsFetched / groupsBuilt\n\n // Estimate additional raw notifications from the average group size.\n const estimatedRawItems = Math.ceil(averagePerGroup * groupsMissing)\n\n // Keep the estimate above the minimum batch and within the total raw-item budget.\n return Math.min(remainingItems, Math.max(MIN_RAW_LIMIT, estimatedRawItems))\n}\n\nexport default function (server: Server, ctx: AppContext) {\n const getGroupedNotifications = createPipeline(\n skeleton,\n hydration,\n rules,\n presentation,\n )\n server.add(app.bsky.notification.getGroupedNotifications, {\n auth: ctx.authVerifier.standard,\n handler: async ({ params, auth, req }) => {\n const viewer = auth.credentials.iss\n const features = ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n )\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n labelers,\n viewer,\n features,\n })\n const lastSeenRes = await ctx.hydrator.dataplane.getNotificationSeen({\n actorDid: viewer,\n })\n const seenAt = lastSeenRes.timestamp\n ? asDatetimeString(lastSeenRes.timestamp.toJson() as string)\n : undefined\n const body = await getGroupedNotifications(\n { ...params, hydrateCtx, seenAt },\n ctx,\n )\n return {\n encoding: 'application/json',\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n body,\n }\n },\n })\n}\n\nconst skeleton = async (\n input: SkeletonFnInput<Context, Params>,\n): Promise<SkeletonState> => {\n const { ctx, params } = input\n const now = Date.now()\n const feed = normalizeFeed(params.feed)\n const seenAt = params.seenAt ? Date.parse(params.seenAt) : undefined\n const delayedCursor = delayCursor(undefined, ctx.cfg.notificationsDelayMs)\n let cursor = params.cursor\n if (!cursor || Date.parse(cursor) > Date.parse(delayedCursor)) {\n cursor = delayedCursor\n }\n\n const items: NotificationItem[] = []\n let groups: NotificationGroup[] = []\n let nextCursor: string | undefined\n let itemsFetched = 0\n let rawLimit = MIN_RAW_LIMIT\n\n for (let read = 0; read < MAX_READS; read++) {\n const response = await ctx.hydrator.dataplane.getNotificationsV2({\n actorDid: params.hydrateCtx.viewer,\n feed,\n cursor,\n limit: rawLimit,\n })\n itemsFetched += response.notifications.length\n\n for (const notification of response.notifications) {\n const notif = parseRawNotification(notification)\n if (!notif) continue\n const uri = notif.uri\n const actorDid = uriToDid(uri)\n const indexedAt = notif.indexedAt\n // Hashing keeps IDs within the lexicon's length limit for long record URIs.\n const id = createHash('sha256')\n .update(`${indexedAt}\\0${uri}`)\n .digest('base64url')\n\n let groupingKey: string = notif.reasonSubject || uri\n if (notif.reason === NOTIFICATION_REASON.FOLLOW) {\n groupingKey = NOTIFICATION_REASON.FOLLOW\n }\n if (notif.reason === NOTIFICATION_REASON.SUBSCRIBED_POST) {\n groupingKey = actorDid\n }\n\n items.push({\n raw: notif,\n id,\n groupingKey,\n actorDid,\n })\n }\n\n const groupingResult = buildGroups(\n items,\n params.limit,\n now,\n seenAt,\n ctx.cfg.notificationsV2Algorithm,\n feed,\n )\n groups = groupingResult.groups\n\n // After fetching a page from the dataplane and attempting to group, 2 situations can happen:\n // 1. The first unconsumed notification would create an additional group beyond params.limit.\n // result.cursor points before that notification; use it so the next request processes it.\n // 2. Every fetched notification was consumed into the groups.\n // result.cursor is undefined, so use response.cursor to fetch the next dataplane page.\n nextCursor = groupingResult.cursor ?? (response.cursor || undefined)\n\n const groupingFoundUnconsumedNotification =\n groupingResult.cursor !== undefined\n const dataplaneHasMoreNotifications = !!response.cursor\n const shouldContinueBuildingPage =\n !groupingFoundUnconsumedNotification && dataplaneHasMoreNotifications\n if (!shouldContinueBuildingPage) break\n\n cursor = response.cursor\n rawLimit = getNextRawLimit(itemsFetched, groups.length, params.limit)\n if (rawLimit === 0) break\n }\n\n return buildSpotlight(items, groups, nextCursor, params.limit, seenAt)\n}\n\nconst hydration = async (\n input: HydrationFnInput<Context, Params, SkeletonState>,\n) => {\n const { ctx, params, skeleton } = input\n const notifs = skeleton.groups.flatMap((group) =>\n group.items.map((item) => item.raw),\n )\n return ctx.hydrator.hydrateGroupedNotifications(notifs, params.hydrateCtx)\n}\n\nconst rules = (\n input: RulesFnInput<Context, Params, SkeletonState>,\n): SkeletonState => {\n const { ctx, params, skeleton, hydration } = input\n const seenAt = params.seenAt ? Date.parse(params.seenAt) : undefined\n const groups = mapDefined(\n skeleton.groups,\n (group): NotificationGroup | undefined => {\n const items = group.items.filter((item) => {\n const notif = item.raw\n const uri = notif.uri\n const did = item.actorDid\n if (\n ctx.views.viewerBlockExists(did, hydration) ||\n ctx.views.viewerMuteExists(did, hydration)\n ) {\n return false\n }\n if (\n shouldFilterReplyByThreadgate(\n notif.reason,\n uri,\n params.hydrateCtx.viewer,\n hydration,\n ctx.views,\n )\n ) {\n return false\n }\n if (\n shouldFilterHiddenThreadTag(\n notif.reason,\n uri,\n did,\n hydration,\n ctx.cfg.threadTagsHide,\n )\n ) {\n return false\n }\n if (\n shouldFilterForNeedsReview(\n notif.reason,\n did,\n uri,\n hydration,\n ctx.views,\n )\n ) {\n return false\n }\n\n // After hydration, notificationAvailable checks whether each notification’s required records exist.\n // If its actor, post, repost, follow, or starter pack is missing or blocked, rules removes that notification from the returned group.\n return notificationAvailable(notif, hydration, ctx.views)\n })\n\n const newest = items[0]\n const oldest = items.at(-1)\n\n // If it has no more elements, drop this group.\n if (!newest || !oldest) return\n\n // Filtering may remove notifications from this group; build a new group with only the remaining items and their updated metadata.\n return {\n ...group,\n id: newest.id,\n items,\n actorDids: items.map((item) => item.actorDid),\n itemCount: items.length,\n indexedAt: newest.raw.indexedAt,\n firstIndexedAt: oldest.raw.indexedAt,\n isRead: isNotificationRead(newest.raw.indexedAt, seenAt),\n }\n },\n )\n\n groups.sort(compareNotificationGroupsNewestFirst)\n\n return { ...skeleton, groups }\n}\n\n// Checks that the notification’s actor and required records exist and referenced posts aren’t viewer-blocked.\nconst notificationAvailable = (\n notif: RawNotification,\n state: HydrationState,\n views: Views,\n): boolean => {\n const uri = notif.uri\n const actor = state.actors?.get(uriToDid(uri))\n if (!actor) return false\n\n const postAvailable = (postUri: AtUriString) =>\n !!state.posts?.get(postUri) &&\n !!state.actors?.get(uriToDid(postUri)) &&\n !views.viewerBlockExists(uriToDid(postUri), state)\n\n const starterPackAvailable = (starterPackUri: AtUriString) =>\n !!state.starterPacks?.get(starterPackUri) &&\n !!state.actors?.get(uriToDid(starterPackUri))\n\n switch (notif.reason) {\n case NOTIFICATION_REASON.LIKE: {\n const subjectUri = notif.reasonSubject\n if (!state.likes?.get(uri)) return false\n if (new AtUri(subjectUri).collection === app.bsky.feed.generator.$type) {\n return (\n !!state.feedgens?.get(subjectUri) &&\n !!state.actors?.get(uriToDid(subjectUri))\n )\n }\n return postAvailable(subjectUri)\n }\n case NOTIFICATION_REASON.REPOST:\n return !!state.reposts?.get(uri) && postAvailable(notif.reasonSubject)\n case NOTIFICATION_REASON.LIKE_VIA_REPOST:\n case NOTIFICATION_REASON.REPOST_VIA_REPOST: {\n const recordInfo =\n notif.reason === NOTIFICATION_REASON.LIKE_VIA_REPOST\n ? state.likes?.get(uri)\n : state.reposts?.get(uri)\n const postUri = state.reposts?.get(notif.reasonSubject)?.record.subject\n .uri\n return !!recordInfo && !!postUri && postAvailable(postUri)\n }\n case NOTIFICATION_REASON.FOLLOW:\n case NOTIFICATION_REASON.FOLLOW_BACK:\n return !!state.follows?.get(uri)\n case NOTIFICATION_REASON.REPLY:\n return (\n postAvailable(uri) && !!state.posts?.get(uri)?.record.reply?.parent.uri\n )\n case NOTIFICATION_REASON.QUOTE:\n case NOTIFICATION_REASON.MENTION:\n case NOTIFICATION_REASON.SUBSCRIBED_POST:\n return postAvailable(uri)\n case NOTIFICATION_REASON.VERIFIED:\n case NOTIFICATION_REASON.UNVERIFIED:\n // Both notifications survive deletion of the verification record.\n return true\n case NOTIFICATION_REASON.STARTERPACK_JOINED:\n return (\n !!actor.profile &&\n !!actor.profileCid &&\n starterPackAvailable(notif.reasonSubject)\n )\n case NOTIFICATION_REASON.CONTACT_MATCH:\n return !!actor.profile && !!actor.profileCid\n default:\n return false\n }\n}\n\nconst presentation = (\n input: PresentationFnInput<Context, Params, SkeletonState>,\n): app.bsky.notification.getGroupedNotifications.$OutputBody => {\n const { ctx, params, skeleton, hydration } = input\n const defs = app.bsky.notification.getGroupedNotifications\n const groups = mapDefined(skeleton.groups, (group) =>\n ctx.views.notificationGroup(group, hydration),\n )\n const profileDids = new Set<DidString>()\n const recordUris = new Set<AtUriString>()\n for (const { kind } of groups) {\n if (\n defs.likeGroup.$isTypeOf(kind) ||\n defs.repostGroup.$isTypeOf(kind) ||\n defs.likeViaRepostGroup.$isTypeOf(kind) ||\n defs.repostViaRepostGroup.$isTypeOf(kind)\n ) {\n recordUris.add(kind.post)\n for (const item of truncateRelatedViewsItems(kind.items)) {\n profileDids.add(item.actor)\n }\n } else if (defs.multiPostLikeGroup.$isTypeOf(kind)) {\n profileDids.add(kind.actor)\n for (const item of truncateRelatedViewsItems(kind.items)) {\n recordUris.add(item.post)\n }\n } else if (defs.generatorLikeGroup.$isTypeOf(kind)) {\n recordUris.add(kind.generator)\n for (const item of truncateRelatedViewsItems(kind.items)) {\n profileDids.add(item.actor)\n }\n } else if (defs.followGroup.$isTypeOf(kind)) {\n for (const item of truncateRelatedViewsItems(kind.items)) {\n profileDids.add(item.actor)\n if (item.starterPack) recordUris.add(item.starterPack)\n }\n } else if (defs.subscribedPostGroup.$isTypeOf(kind)) {\n for (const item of truncateRelatedViewsItems(kind.items)) {\n profileDids.add(item.actor)\n recordUris.add(item.post)\n }\n } else if (\n defs.replyNotification.$isTypeOf(kind) ||\n defs.quoteNotification.$isTypeOf(kind) ||\n defs.mentionNotification.$isTypeOf(kind)\n ) {\n recordUris.add(kind.post)\n if (kind.parent) recordUris.add(kind.parent)\n } else if (\n defs.followBackNotification.$isTypeOf(kind) ||\n defs.starterPackJoinedNotification.$isTypeOf(kind)\n ) {\n profileDids.add(kind.actor)\n if (kind.starterPack) recordUris.add(kind.starterPack)\n } else if (\n defs.verifiedNotification.$isTypeOf(kind) ||\n defs.unverifiedNotification.$isTypeOf(kind) ||\n defs.contactMatchNotification.$isTypeOf(kind)\n ) {\n profileDids.add(kind.actor)\n }\n }\n return {\n groups,\n relatedViews: [\n ...mapDefined([...profileDids], (did) => {\n const view = ctx.views.profileDetailed(did, hydration)\n if (!view) return\n return app.bsky.actor.defs.profileViewDetailed.$build(view)\n }),\n ...mapDefined([...recordUris], (uri) => {\n const collection = new AtUri(uri).collection\n if (collection === app.bsky.feed.post.$type) {\n const view = ctx.views.maybePost(uri, hydration)\n if (!view) return\n return view\n } else if (collection === app.bsky.graph.starterpack.$type) {\n const view = ctx.views.starterPack(uri, hydration)\n if (!view) return\n return app.bsky.graph.defs.starterPackView.$build(view)\n } else if (collection === app.bsky.feed.generator.$type) {\n const view = ctx.views.feedGenerator(uri, hydration)\n if (!view) return\n return app.bsky.feed.defs.generatorView.$build(view)\n }\n }),\n ],\n cursor: skeleton.cursor,\n seenAt: params.seenAt,\n }\n}\n\nconst truncateRelatedViewsItems = <T extends unknown[]>(\n items: T,\n): T[number][] => items.slice(0, MAX_GROUP_ITEMS_WITH_RELATED_VIEWS)\n\nconst normalizeFeed = (feed: string): NotificationFeed => {\n switch (feed) {\n case 'people-i-follow':\n return NotificationFeed.PEOPLE_I_FOLLOW\n case 'conversations':\n return NotificationFeed.CONVERSATIONS\n case 'followers':\n return NotificationFeed.FOLLOWERS\n case 'activity':\n return NotificationFeed.ACTIVITY\n default:\n return NotificationFeed.ALL\n }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n cfg: ServerConfig\n}\n\ntype Params = app.bsky.notification.getGroupedNotifications.$Params & {\n hydrateCtx: HydrateCtxWithViewer\n seenAt?: DatetimeString\n}\n\ntype SkeletonState = {\n groups: NotificationGroup[]\n cursor?: string\n}\n"]}
|
|
1
|
+
{"version":3,"file":"getGroupedNotifications.js","sourceRoot":"","sources":["../../../../../src/api/app/bsky/notification/getGroupedNotifications.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto'\nimport { dedupeStrs, mapDefined } from '@atproto/common'\nimport {\n type AtUriString,\n type DatetimeString,\n type DidString,\n asDatetimeString,\n} from '@atproto/lex'\nimport { AtUri } from '@atproto/syntax'\nimport type { Server } from '@atproto/xrpc-server'\nimport type { ServerConfig } from '../../../../config.js'\nimport type { AppContext } from '../../../../context.js'\nimport type {\n HydrateCtxWithViewer,\n HydrationState,\n Hydrator,\n} from '../../../../hydration/hydrator.js'\nimport { app } from '../../../../lexicons/index.js'\nimport {\n type HydrationFnInput,\n type PresentationFnInput,\n type RulesFnInput,\n type SkeletonFnInput,\n createPipeline,\n} from '../../../../pipeline.js'\nimport { NotificationFeed } from '../../../../proto/bsky_pb.js'\nimport { events } from '../../../../telemetry/events.js'\nimport { uriToDid } from '../../../../util/uris.js'\nimport type { Views } from '../../../../views/index.js'\nimport { resHeaders } from '../../../util.js'\nimport { NOTIFICATION_REASON } from './constants.js'\nimport {\n type NotificationGroup,\n type NotificationItem,\n type RawNotification,\n buildGroups,\n parseRawNotification,\n} from './grouping/grouping.js'\nimport {\n compareNotificationGroupsNewestFirst,\n isNotificationRead,\n} from './grouping/shared.js'\nimport { buildSpotlight } from './grouping/spotlight.js'\nimport {\n delayCursor,\n shouldFilterForNeedsReview,\n shouldFilterHiddenThreadTag,\n shouldFilterReplyByThreadgate,\n} from './util.js'\n\nexport const getNextRawLimit = (\n itemsFetched: number,\n groupsBuilt: number,\n groupsNeeded: number,\n minRawLimit: number,\n maxRawItems: number,\n): number => {\n // How many raw items we still can fetch until hit the max.\n const remainingItems = maxRawItems - itemsFetched\n if (remainingItems <= 0) return 0\n\n // Defensive fallback to avoid a division by 0 later.\n // This is for the scenario where fetched items but couldn't build any groups with them\n // (let's say all notifications are missing data and are filtered out, shouldn't happen in practice).\n if (groupsBuilt === 0) return Math.min(minRawLimit, remainingItems)\n\n // How many groups still to be built - note that we might still fit items into existing groups.\n const groupsMissing = Math.max(0, groupsNeeded - groupsBuilt)\n\n const averagePerGroup = itemsFetched / groupsBuilt\n\n // Estimate additional raw notifications from the average group size.\n const estimatedRawItems = Math.ceil(averagePerGroup * groupsMissing)\n\n // Keep the estimate above the minimum batch and within the total raw-item budget.\n return Math.min(remainingItems, Math.max(minRawLimit, estimatedRawItems))\n}\n\nexport default function (server: Server, ctx: AppContext) {\n const getGroupedNotifications = createPipeline(\n skeleton,\n hydration,\n rules,\n presentation,\n )\n server.add(app.bsky.notification.getGroupedNotifications, {\n auth: ctx.authVerifier.standard,\n handler: async ({ params, auth, req }) => {\n const viewer = auth.credentials.iss\n const features = ctx.featureGatesClient.scope(\n ctx.featureGatesClient.parseUserContextFromHandler({ viewer, req }),\n )\n const labelers = ctx.reqLabelers(req)\n const hydrateCtx = await ctx.hydrator.createContext({\n labelers,\n viewer,\n features,\n })\n const lastSeenRes = await ctx.hydrator.dataplane.getNotificationSeen({\n actorDid: viewer,\n })\n const seenAt = lastSeenRes.timestamp\n ? asDatetimeString(lastSeenRes.timestamp.toJson() as string)\n : undefined\n const body = await getGroupedNotifications(\n { ...params, hydrateCtx, seenAt },\n ctx,\n )\n return {\n encoding: 'application/json',\n headers: resHeaders({ labelers: hydrateCtx.labelers }),\n body,\n }\n },\n })\n}\n\nconst skeleton = async (\n input: SkeletonFnInput<Context, Params>,\n): Promise<SkeletonState> => {\n const { ctx, params } = input\n const now = Date.now()\n const feed = normalizeFeed(params.feed)\n const seenAt = params.seenAt ? Date.parse(params.seenAt) : undefined\n const delayedCursor = delayCursor(undefined, ctx.cfg.notificationsDelayMs)\n let cursor = params.cursor\n if (!cursor || Date.parse(cursor) > Date.parse(delayedCursor)) {\n cursor = delayedCursor\n }\n\n const items: NotificationItem[] = []\n let groups: NotificationGroup[] = []\n let nextCursor: string | undefined\n let itemsFetched = 0\n let rawLimit = ctx.cfg.notificationsV2MinRawLimit\n\n for (let read = 0; read < ctx.cfg.notificationsV2MaxReads; read++) {\n const response = await ctx.hydrator.dataplane.getNotificationsV2({\n actorDid: params.hydrateCtx.viewer,\n feed,\n cursor,\n limit: rawLimit,\n })\n itemsFetched += response.notifications.length\n\n for (const notification of response.notifications) {\n const notif = parseRawNotification(notification)\n if (!notif) continue\n const uri = notif.uri\n const actorDid = uriToDid(uri)\n const indexedAt = notif.indexedAt\n // Hashing keeps IDs within the lexicon's length limit for long record URIs.\n const id = createHash('sha256')\n .update(`${indexedAt}\\0${uri}`)\n .digest('base64url')\n\n let groupingKey: string = notif.reasonSubject || uri\n if (notif.reason === NOTIFICATION_REASON.FOLLOW) {\n groupingKey = NOTIFICATION_REASON.FOLLOW\n }\n if (notif.reason === NOTIFICATION_REASON.SUBSCRIBED_POST) {\n groupingKey = actorDid\n }\n\n items.push({\n raw: notif,\n id,\n groupingKey,\n actorDid,\n })\n }\n\n const groupingResult = buildGroups(\n items,\n params.limit,\n now,\n seenAt,\n ctx.cfg.notificationsV2Algorithm,\n feed,\n )\n groups = groupingResult.groups\n\n // After fetching a page from the dataplane and attempting to group, 2 situations can happen:\n // 1. The first unconsumed notification would create an additional group beyond params.limit.\n // result.cursor points before that notification; use it so the next request processes it.\n // 2. Every fetched notification was consumed into the groups.\n // result.cursor is undefined, so use response.cursor to fetch the next dataplane page.\n nextCursor = groupingResult.cursor ?? (response.cursor || undefined)\n\n const groupingFoundUnconsumedNotification =\n groupingResult.cursor !== undefined\n const dataplaneHasMoreNotifications = !!response.cursor\n const shouldContinueBuildingPage =\n !groupingFoundUnconsumedNotification && dataplaneHasMoreNotifications\n if (!shouldContinueBuildingPage) break\n\n cursor = response.cursor\n rawLimit = getNextRawLimit(\n itemsFetched,\n groups.length,\n params.limit,\n ctx.cfg.notificationsV2MinRawLimit,\n ctx.cfg.notificationsV2MaxRawItems,\n )\n if (rawLimit === 0) break\n }\n\n return {\n ...buildSpotlight(items, groups, nextCursor, params.limit, seenAt),\n itemsFetched,\n }\n}\n\nconst hydration = async (\n input: HydrationFnInput<Context, Params, SkeletonState>,\n) => {\n const { ctx, params, skeleton } = input\n const notifs = skeleton.groups.flatMap((group) =>\n group.items.map((item) => item.raw),\n )\n\n // Optimization: instead of fetching known followers for all users in all notifications,\n // only fetch for the notifications type that want it (follow type), and only for the ones\n // that appear before truncation.\n const knownFollowersDids = dedupeStrs(\n skeleton.groups.flatMap((group) =>\n group.kind === NOTIFICATION_REASON.FOLLOW\n ? truncateRelatedViewsItems(\n group.items,\n ctx.cfg.notificationsV2MaxGroupItemsWithRelatedViews,\n ).map((item) => item.actorDid)\n : [],\n ),\n )\n\n return ctx.hydrator.hydrateGroupedNotifications(notifs, params.hydrateCtx, {\n knownFollowersDids,\n })\n}\n\nconst rules = (\n input: RulesFnInput<Context, Params, SkeletonState>,\n): SkeletonState => {\n const { ctx, params, skeleton, hydration } = input\n const seenAt = params.seenAt ? Date.parse(params.seenAt) : undefined\n const groups = mapDefined(\n skeleton.groups,\n (group): NotificationGroup | undefined => {\n const items = group.items.filter((item) => {\n const notif = item.raw\n const uri = notif.uri\n const did = item.actorDid\n if (\n ctx.views.viewerBlockExists(did, hydration) ||\n ctx.views.viewerMuteExists(did, hydration)\n ) {\n return false\n }\n if (\n shouldFilterReplyByThreadgate(\n notif.reason,\n uri,\n params.hydrateCtx.viewer,\n hydration,\n ctx.views,\n )\n ) {\n return false\n }\n if (\n shouldFilterHiddenThreadTag(\n notif.reason,\n uri,\n did,\n hydration,\n ctx.cfg.threadTagsHide,\n )\n ) {\n return false\n }\n if (\n shouldFilterForNeedsReview(\n notif.reason,\n did,\n uri,\n hydration,\n ctx.views,\n )\n ) {\n return false\n }\n\n // After hydration, notificationAvailable checks whether each notification’s required records exist.\n // If its actor, post, repost, follow, or starter pack is missing or blocked, rules removes that notification from the returned group.\n return notificationAvailable(notif, hydration, ctx.views)\n })\n\n const newest = items[0]\n const oldest = items.at(-1)\n\n // If it has no more elements, drop this group.\n if (!newest || !oldest) return\n\n // Filtering may remove notifications from this group; build a new group with only the remaining items and their updated metadata.\n return {\n ...group,\n id: newest.id,\n items,\n actorDids: items.map((item) => item.actorDid),\n itemCount: items.length,\n indexedAt: newest.raw.indexedAt,\n firstIndexedAt: oldest.raw.indexedAt,\n isRead: isNotificationRead(newest.raw.indexedAt, seenAt),\n }\n },\n )\n\n groups.sort(compareNotificationGroupsNewestFirst)\n\n return { ...skeleton, groups }\n}\n\n// Checks that the notification’s actor and required records exist and referenced posts aren’t viewer-blocked.\nconst notificationAvailable = (\n notif: RawNotification,\n state: HydrationState,\n views: Views,\n): boolean => {\n const uri = notif.uri\n const actor = state.actors?.get(uriToDid(uri))\n if (!actor) return false\n\n const postAvailable = (postUri: AtUriString) =>\n !!state.posts?.get(postUri) &&\n !!state.actors?.get(uriToDid(postUri)) &&\n !views.viewerBlockExists(uriToDid(postUri), state)\n\n const starterPackAvailable = (starterPackUri: AtUriString) =>\n !!state.starterPacks?.get(starterPackUri) &&\n !!state.actors?.get(uriToDid(starterPackUri))\n\n switch (notif.reason) {\n case NOTIFICATION_REASON.LIKE: {\n const subjectUri = notif.reasonSubject\n if (!state.likes?.get(uri)) return false\n if (new AtUri(subjectUri).collection === app.bsky.feed.generator.$type) {\n return (\n !!state.feedgens?.get(subjectUri) &&\n !!state.actors?.get(uriToDid(subjectUri))\n )\n }\n return postAvailable(subjectUri)\n }\n case NOTIFICATION_REASON.REPOST:\n return !!state.reposts?.get(uri) && postAvailable(notif.reasonSubject)\n case NOTIFICATION_REASON.LIKE_VIA_REPOST:\n case NOTIFICATION_REASON.REPOST_VIA_REPOST: {\n const recordInfo =\n notif.reason === NOTIFICATION_REASON.LIKE_VIA_REPOST\n ? state.likes?.get(uri)\n : state.reposts?.get(uri)\n const postUri = state.reposts?.get(notif.reasonSubject)?.record.subject\n .uri\n return !!recordInfo && !!postUri && postAvailable(postUri)\n }\n case NOTIFICATION_REASON.FOLLOW:\n case NOTIFICATION_REASON.FOLLOW_BACK:\n return !!state.follows?.get(uri)\n case NOTIFICATION_REASON.REPLY:\n return (\n postAvailable(uri) && !!state.posts?.get(uri)?.record.reply?.parent.uri\n )\n case NOTIFICATION_REASON.QUOTE:\n case NOTIFICATION_REASON.MENTION:\n case NOTIFICATION_REASON.SUBSCRIBED_POST:\n return postAvailable(uri)\n case NOTIFICATION_REASON.VERIFIED:\n case NOTIFICATION_REASON.UNVERIFIED:\n // Both notifications survive deletion of the verification record.\n return true\n case NOTIFICATION_REASON.STARTERPACK_JOINED:\n return (\n !!actor.profile &&\n !!actor.profileCid &&\n starterPackAvailable(notif.reasonSubject)\n )\n case NOTIFICATION_REASON.CONTACT_MATCH:\n return !!actor.profile && !!actor.profileCid\n default:\n return false\n }\n}\n\nconst presentation = (\n input: PresentationFnInput<Context, Params, SkeletonState>,\n): app.bsky.notification.getGroupedNotifications.$OutputBody => {\n const { ctx, params, skeleton, hydration } = input\n const defs = app.bsky.notification.getGroupedNotifications\n const groups = mapDefined(skeleton.groups, (group) =>\n ctx.views.notificationGroup(group, hydration),\n )\n\n const maxGroupItems = ctx.cfg.notificationsV2MaxGroupItemsWithRelatedViews\n\n // Build related profiles, records.\n const profileDids = new Set<DidString>()\n const recordUris = new Set<AtUriString>()\n for (const { kind } of groups) {\n if (\n defs.likeGroup.$isTypeOf(kind) ||\n defs.repostGroup.$isTypeOf(kind) ||\n defs.likeViaRepostGroup.$isTypeOf(kind) ||\n defs.repostViaRepostGroup.$isTypeOf(kind)\n ) {\n recordUris.add(kind.post)\n for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {\n profileDids.add(item.actor)\n }\n } else if (\n defs.multiPostLikeGroup.$isTypeOf(kind) ||\n defs.multiPostRepostGroup.$isTypeOf(kind) ||\n defs.multiPostLikeViaRepostGroup.$isTypeOf(kind) ||\n defs.multiPostRepostViaRepostGroup.$isTypeOf(kind)\n ) {\n profileDids.add(kind.actor)\n for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {\n recordUris.add(item.post)\n }\n } else if (defs.generatorLikeGroup.$isTypeOf(kind)) {\n recordUris.add(kind.generator)\n for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {\n profileDids.add(item.actor)\n }\n } else if (defs.followGroup.$isTypeOf(kind)) {\n for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {\n profileDids.add(item.actor)\n if (item.starterPack) recordUris.add(item.starterPack)\n }\n } else if (defs.subscribedPostGroup.$isTypeOf(kind)) {\n for (const item of truncateRelatedViewsItems(kind.items, maxGroupItems)) {\n profileDids.add(item.actor)\n recordUris.add(item.post)\n }\n } else if (\n defs.replyNotification.$isTypeOf(kind) ||\n defs.quoteNotification.$isTypeOf(kind) ||\n defs.mentionNotification.$isTypeOf(kind)\n ) {\n recordUris.add(kind.post)\n if (kind.parent) recordUris.add(kind.parent)\n } else if (\n defs.followBackNotification.$isTypeOf(kind) ||\n defs.starterPackJoinedNotification.$isTypeOf(kind)\n ) {\n profileDids.add(kind.actor)\n if (kind.starterPack) recordUris.add(kind.starterPack)\n } else if (\n defs.verifiedNotification.$isTypeOf(kind) ||\n defs.unverifiedNotification.$isTypeOf(kind) ||\n defs.contactMatchNotification.$isTypeOf(kind)\n ) {\n profileDids.add(kind.actor)\n }\n }\n\n const body = {\n groups,\n relatedViews: [\n ...mapDefined([...profileDids], (did) => {\n const view = ctx.views.profileBasic(did, hydration)\n if (!view) return\n return app.bsky.actor.defs.profileViewBasic.$build(view)\n }),\n ...mapDefined([...recordUris], (uri) => {\n const collection = new AtUri(uri).collection\n if (collection === app.bsky.feed.post.$type) {\n const view = ctx.views.maybePost(uri, hydration)\n if (!view) return\n return view\n } else if (collection === app.bsky.graph.starterpack.$type) {\n const view = ctx.views.starterPackBasic(uri, hydration)\n if (!view) return\n return app.bsky.graph.defs.starterPackViewBasic.$build(view)\n } else if (collection === app.bsky.feed.generator.$type) {\n const view = ctx.views.feedGenerator(uri, hydration)\n if (!view) return\n return app.bsky.feed.defs.generatorView.$build(view)\n }\n }),\n ],\n cursor: skeleton.cursor,\n seenAt: params.seenAt,\n }\n\n events.groupedNotifications({\n itemsFetched: skeleton.itemsFetched,\n itemsReturned: groups.reduce((count, group) => count + group.count, 0),\n })\n\n return body\n}\n\nconst truncateRelatedViewsItems = <T extends unknown[]>(\n items: T,\n maxItems: number,\n): T[number][] => items.slice(0, maxItems)\n\nconst normalizeFeed = (feed: string): NotificationFeed => {\n switch (feed) {\n case 'people-i-follow':\n return NotificationFeed.PEOPLE_I_FOLLOW\n case 'conversations':\n return NotificationFeed.CONVERSATIONS\n case 'followers':\n return NotificationFeed.FOLLOWERS\n case 'activity':\n return NotificationFeed.ACTIVITY\n default:\n return NotificationFeed.ALL\n }\n}\n\ntype Context = {\n hydrator: Hydrator\n views: Views\n cfg: ServerConfig\n}\n\ntype Params = app.bsky.notification.getGroupedNotifications.$Params & {\n hydrateCtx: HydrateCtxWithViewer\n seenAt?: DatetimeString\n}\n\ntype SkeletonState = {\n groups: NotificationGroup[]\n cursor?: string\n itemsFetched: number\n}\n"]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { GroupingResult, NotificationGroup, NotificationItem } from './grouping.js';
|
|
2
|
-
export declare
|
|
2
|
+
export declare function buildSpotlight(items: NotificationItem[], groups: NotificationGroup[], cursor: string | undefined, limit: number, seenAt: number | undefined): GroupingResult;
|
|
3
3
|
//# sourceMappingURL=spotlight.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spotlight.d.ts","sourceRoot":"","sources":["../../../../../../src/api/app/bsky/notification/grouping/spotlight.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"spotlight.d.ts","sourceRoot":"","sources":["../../../../../../src/api/app/bsky/notification/grouping/spotlight.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,cAAc,EACd,iBAAiB,EACjB,gBAAgB,EACjB,MAAM,eAAe,CAAA;AAiCtB,wBAAgB,cAAc,CAC5B,KAAK,EAAE,gBAAgB,EAAE,EACzB,MAAM,EAAE,iBAAiB,EAAE,EAC3B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GAAG,SAAS,GACzB,cAAc,CAyGhB"}
|