Skip to content

Commit

Permalink
Interaction Gating (#1561)
Browse files Browse the repository at this point in the history
* lexicons for block lists

* reorg blockset functionality into graph service, impl block/mute filtering

* apply filterBlocksAndMutes() throughout appview except feeds

* update local feeds to pass through cleanFeedSkeleton(), offload block/mute application

* impl for grabbing block/mute details by did pair

* refactor getActorInfos away, use actor service

* experiment with moving getFeedGenerators over to a pipeline

* move getPostThread over to a pipeline

* move feeds over to pipelines

* move suggestions and likes over to pipelines

* move reposted-by, follows, followers over to pipelines, tidy author feed and post thread

* remove old block/mute checks

* unify post presentation logic

* move profiles endpoints over to pipelines

* tidy

* tidy

* misc fixes

* unify some profile hydration/presentation in appview

* profile detail, split hydration and presentation, misc fixes

* unify feed hydration w/ profile hydration

* unify hydration step for embeds, tidy application of labels

* setup indexing of list-blocks in bsky appview

* apply list-blocks, impl getListBlocks, tidy getList, tests

* tidy

* update pds proxy snaps

* update pds proxy snaps

* fix snap

* make algos return feed items, save work in getFeed

* misc changes, tidy

* tidy

* fix aturi import

* initial lexicons for interaction-gating

* add interactions view to post views

* codegen

* model bad reply/interaction check state on posts

* initial impl for checking bad reply or interaction on write

* omit invalid interactions from post thread

* support not-found list in interaction view

* hydrate can-reply state on threads

* present interaction views on posts

* misc fixes, update snaps

* tidy/reorg

* tidy

* split interaction gating into separate record in lexicon

* switch interaction-gating impl to use separate record type

* allow checking reply gate w/ root post deletion

* fix

* initial gating tests

* tighten gated reply views, tests

* reply-gating list rule tests

* allow custom post rkeys within window

* hoist actors out of composeThread()

* tidy

* update thread gate lexicons, codegen

* lex fix

* rename gate to threadgate in bsky, update views

* lex fix

* improve terminology around reply validation

* fix down migration

* remove thread gates on actor unindexing

* add back .prettierignore

* tidy

* run ci on all prs

* syntax

* run ci on all prs

* format

* fix snap

---------

Co-authored-by: Devin Ivy <[email protected]>
  • Loading branch information
dholms and devinivy authored Sep 14, 2023
1 parent 77749ab commit 9879ca9
Show file tree
Hide file tree
Showing 36 changed files with 2,135 additions and 28 deletions.
24 changes: 22 additions & 2 deletions lexicons/app/bsky/feed/defs.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"labels": {
"type": "array",
"items": { "type": "ref", "ref": "com.atproto.label.defs#label" }
}
},
"threadgate": { "type": "ref", "ref": "#threadgateView" }
}
},
"viewerState": {
Expand Down Expand Up @@ -86,7 +87,8 @@
"type": "union",
"refs": ["#threadViewPost", "#notFoundPost", "#blockedPost"]
}
}
},
"viewer": { "type": "ref", "ref": "#viewerThreadState" }
}
},
"notFoundPost": {
Expand Down Expand Up @@ -114,6 +116,12 @@
"viewer": { "type": "ref", "ref": "app.bsky.actor.defs#viewerState" }
}
},
"viewerThreadState": {
"type": "object",
"properties": {
"canReply": { "type": "boolean" }
}
},
"generatorView": {
"type": "object",
"required": ["uri", "cid", "did", "creator", "displayName", "indexedAt"],
Expand Down Expand Up @@ -158,6 +166,18 @@
"properties": {
"repost": { "type": "string", "format": "at-uri" }
}
},
"threadgateView": {
"type": "object",
"properties": {
"uri": { "type": "string", "format": "at-uri" },
"cid": { "type": "string", "format": "cid" },
"record": { "type": "unknown" },
"lists": {
"type": "array",
"items": { "type": "ref", "ref": "app.bsky.graph.defs#listViewBasic" }
}
}
}
}
}
43 changes: 43 additions & 0 deletions lexicons/app/bsky/feed/threadgate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"lexicon": 1,
"id": "app.bsky.feed.threadgate",
"defs": {
"main": {
"type": "record",
"key": "tid",
"description": "Defines interaction gating rules for a thread. The rkey of the threadgate record should match the rkey of the thread's root post.",
"record": {
"type": "object",
"required": ["post", "createdAt"],
"properties": {
"post": { "type": "string", "format": "at-uri" },
"allow": {
"type": "array",
"maxLength": 5,
"items": {
"type": "union",
"refs": ["#mentionRule", "#followingRule", "#listRule"]
}
},
"createdAt": { "type": "string", "format": "datetime" }
}
}
},
"mentionRule": {
"type": "object",
"description": "Allow replies from actors mentioned in your post."
},
"followingRule": {
"type": "object",
"description": "Allow replies from actors you follow."
},
"listRule": {
"type": "object",
"description": "Allow replies from actors on a list.",
"required": ["list"],
"properties": {
"list": { "type": "string", "format": "at-uri" }
}
}
}
}
69 changes: 69 additions & 0 deletions packages/api/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ import * as AppBskyFeedGetTimeline from './types/app/bsky/feed/getTimeline'
import * as AppBskyFeedLike from './types/app/bsky/feed/like'
import * as AppBskyFeedPost from './types/app/bsky/feed/post'
import * as AppBskyFeedRepost from './types/app/bsky/feed/repost'
import * as AppBskyFeedThreadgate from './types/app/bsky/feed/threadgate'
import * as AppBskyGraphBlock from './types/app/bsky/graph/block'
import * as AppBskyGraphDefs from './types/app/bsky/graph/defs'
import * as AppBskyGraphFollow from './types/app/bsky/graph/follow'
Expand Down Expand Up @@ -228,6 +229,7 @@ export * as AppBskyFeedGetTimeline from './types/app/bsky/feed/getTimeline'
export * as AppBskyFeedLike from './types/app/bsky/feed/like'
export * as AppBskyFeedPost from './types/app/bsky/feed/post'
export * as AppBskyFeedRepost from './types/app/bsky/feed/repost'
export * as AppBskyFeedThreadgate from './types/app/bsky/feed/threadgate'
export * as AppBskyGraphBlock from './types/app/bsky/graph/block'
export * as AppBskyGraphDefs from './types/app/bsky/graph/defs'
export * as AppBskyGraphFollow from './types/app/bsky/graph/follow'
Expand Down Expand Up @@ -1204,13 +1206,15 @@ export class FeedNS {
like: LikeRecord
post: PostRecord
repost: RepostRecord
threadgate: ThreadgateRecord

constructor(service: AtpServiceClient) {
this._service = service
this.generator = new GeneratorRecord(service)
this.like = new LikeRecord(service)
this.post = new PostRecord(service)
this.repost = new RepostRecord(service)
this.threadgate = new ThreadgateRecord(service)
}

describeFeedGenerator(
Expand Down Expand Up @@ -1623,6 +1627,71 @@ export class RepostRecord {
}
}

export class ThreadgateRecord {
_service: AtpServiceClient

constructor(service: AtpServiceClient) {
this._service = service
}

async list(
params: Omit<ComAtprotoRepoListRecords.QueryParams, 'collection'>,
): Promise<{
cursor?: string
records: { uri: string; value: AppBskyFeedThreadgate.Record }[]
}> {
const res = await this._service.xrpc.call('com.atproto.repo.listRecords', {
collection: 'app.bsky.feed.threadgate',
...params,
})
return res.data
}

async get(
params: Omit<ComAtprotoRepoGetRecord.QueryParams, 'collection'>,
): Promise<{
uri: string
cid: string
value: AppBskyFeedThreadgate.Record
}> {
const res = await this._service.xrpc.call('com.atproto.repo.getRecord', {
collection: 'app.bsky.feed.threadgate',
...params,
})
return res.data
}

async create(
params: Omit<
ComAtprotoRepoCreateRecord.InputSchema,
'collection' | 'record'
>,
record: AppBskyFeedThreadgate.Record,
headers?: Record<string, string>,
): Promise<{ uri: string; cid: string }> {
record.$type = 'app.bsky.feed.threadgate'
const res = await this._service.xrpc.call(
'com.atproto.repo.createRecord',
undefined,
{ collection: 'app.bsky.feed.threadgate', ...params, record },
{ encoding: 'application/json', headers },
)
return res.data
}

async delete(
params: Omit<ComAtprotoRepoDeleteRecord.InputSchema, 'collection'>,
headers?: Record<string, string>,
): Promise<void> {
await this._service.xrpc.call(
'com.atproto.repo.deleteRecord',
undefined,
{ collection: 'app.bsky.feed.threadgate', ...params },
{ headers },
)
}
}

export class GraphNS {
_service: AtpServiceClient
block: BlockRecord
Expand Down
97 changes: 97 additions & 0 deletions packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4390,6 +4390,10 @@ export const schemaDict = {
ref: 'lex:com.atproto.label.defs#label',
},
},
threadgate: {
type: 'ref',
ref: 'lex:app.bsky.feed.defs#threadgateView',
},
},
},
viewerState: {
Expand Down Expand Up @@ -4486,6 +4490,10 @@ export const schemaDict = {
],
},
},
viewer: {
type: 'ref',
ref: 'lex:app.bsky.feed.defs#viewerThreadState',
},
},
},
notFoundPost: {
Expand Down Expand Up @@ -4534,6 +4542,14 @@ export const schemaDict = {
},
},
},
viewerThreadState: {
type: 'object',
properties: {
canReply: {
type: 'boolean',
},
},
},
generatorView: {
type: 'object',
required: ['uri', 'cid', 'did', 'creator', 'displayName', 'indexedAt'],
Expand Down Expand Up @@ -4619,6 +4635,29 @@ export const schemaDict = {
},
},
},
threadgateView: {
type: 'object',
properties: {
uri: {
type: 'string',
format: 'at-uri',
},
cid: {
type: 'string',
format: 'cid',
},
record: {
type: 'unknown',
},
lists: {
type: 'array',
items: {
type: 'ref',
ref: 'lex:app.bsky.graph.defs#listViewBasic',
},
},
},
},
},
},
AppBskyFeedDescribeFeedGenerator: {
Expand Down Expand Up @@ -5615,6 +5654,63 @@ export const schemaDict = {
},
},
},
AppBskyFeedThreadgate: {
lexicon: 1,
id: 'app.bsky.feed.threadgate',
defs: {
main: {
type: 'record',
key: 'tid',
description:
"Defines interaction gating rules for a thread. The rkey of the threadgate record should match the rkey of the thread's root post.",
record: {
type: 'object',
required: ['post', 'createdAt'],
properties: {
post: {
type: 'string',
format: 'at-uri',
},
allow: {
type: 'array',
maxLength: 5,
items: {
type: 'union',
refs: [
'lex:app.bsky.feed.threadgate#mentionRule',
'lex:app.bsky.feed.threadgate#followingRule',
'lex:app.bsky.feed.threadgate#listRule',
],
},
},
createdAt: {
type: 'string',
format: 'datetime',
},
},
},
},
mentionRule: {
type: 'object',
description: 'Allow replies from actors mentioned in your post.',
},
followingRule: {
type: 'object',
description: 'Allow replies from actors you follow.',
},
listRule: {
type: 'object',
description: 'Allow replies from actors on a list.',
required: ['list'],
properties: {
list: {
type: 'string',
format: 'at-uri',
},
},
},
},
},
AppBskyGraphBlock: {
lexicon: 1,
id: 'app.bsky.graph.block',
Expand Down Expand Up @@ -6932,6 +7028,7 @@ export const ids = {
AppBskyFeedLike: 'app.bsky.feed.like',
AppBskyFeedPost: 'app.bsky.feed.post',
AppBskyFeedRepost: 'app.bsky.feed.repost',
AppBskyFeedThreadgate: 'app.bsky.feed.threadgate',
AppBskyGraphBlock: 'app.bsky.graph.block',
AppBskyGraphDefs: 'app.bsky.graph.defs',
AppBskyGraphFollow: 'app.bsky.graph.follow',
Expand Down
Loading

0 comments on commit 9879ca9

Please sign in to comment.