Skip to content

Commit

Permalink
Add followerRule threadgate
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelbsky committed Feb 5, 2025
1 parent 61dc0d6 commit 0c8884d
Show file tree
Hide file tree
Showing 15 changed files with 268 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/silver-horses-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@atproto/bsky": patch
---

Add followerRule threadgate
12 changes: 11 additions & 1 deletion lexicons/app/bsky/feed/threadgate.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@
"maxLength": 5,
"items": {
"type": "union",
"refs": ["#mentionRule", "#followingRule", "#listRule"]
"refs": [
"#mentionRule",
"#followerRule",
"#followingRule",
"#listRule"
]
}
},
"createdAt": { "type": "string", "format": "datetime" },
Expand All @@ -41,6 +46,11 @@
"description": "Allow replies from actors mentioned in your post.",
"properties": {}
},
"followerRule": {
"type": "object",
"description": "Allow replies from actors who follow you.",
"properties": {}
},
"followingRule": {
"type": "object",
"description": "Allow replies from actors you follow.",
Expand Down
6 changes: 6 additions & 0 deletions packages/api/src/client/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7339,6 +7339,7 @@ export const schemaDict = {
type: 'union',
refs: [
'lex:app.bsky.feed.threadgate#mentionRule',
'lex:app.bsky.feed.threadgate#followerRule',
'lex:app.bsky.feed.threadgate#followingRule',
'lex:app.bsky.feed.threadgate#listRule',
],
Expand All @@ -7365,6 +7366,11 @@ export const schemaDict = {
description: 'Allow replies from actors mentioned in your post.',
properties: {},
},
followerRule: {
type: 'object',
description: 'Allow replies from actors who follow you.',
properties: {},
},
followingRule: {
type: 'object',
description: 'Allow replies from actors you follow.',
Expand Down
18 changes: 18 additions & 0 deletions packages/api/src/client/types/app/bsky/feed/threadgate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface Record {
post: string
allow?: (
| MentionRule
| FollowerRule
| FollowingRule
| ListRule
| { $type: string; [k: string]: unknown }
Expand Down Expand Up @@ -51,6 +52,23 @@ export function validateMentionRule(v: unknown): ValidationResult {
return lexicons.validate('app.bsky.feed.threadgate#mentionRule', v)
}

/** Allow replies from actors who follow you. */
export interface FollowerRule {
[k: string]: unknown
}

export function isFollowerRule(v: unknown): v is FollowerRule {
return (
isObj(v) &&
hasProp(v, '$type') &&
v.$type === 'app.bsky.feed.threadgate#followerRule'
)
}

export function validateFollowerRule(v: unknown): ValidationResult {
return lexicons.validate('app.bsky.feed.threadgate#followerRule', v)
}

/** Allow replies from actors you follow. */
export interface FollowingRule {
[k: string]: unknown
Expand Down
13 changes: 12 additions & 1 deletion packages/bsky/src/data-plane/server/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,20 +100,29 @@ export const violatesThreadGate = async (
) => {
const {
canReply,
allowFollower,
allowFollowing,
allowListUris = [],
} = parseThreadGate(replierDid, ownerDid, rootPost, gate)
if (canReply) {
return false
}
if (!allowFollowing && !allowListUris?.length) {
if (!allowFollower && !allowFollowing && !allowListUris?.length) {
return true
}
const { ref } = db.dynamic
const nullResult = sql<null>`${null}`
const check = await db
.selectFrom(valuesList([replierDid]).as(sql`subject (did)`))
.select([
allowFollower
? db
.selectFrom('follow')
.where('subjectDid', '=', ownerDid)
.whereRef('creator', '=', ref('subject.did'))
.select('subjectDid')
.as('isFollower')
: nullResult.as('isFollower'),
allowFollowing
? db
.selectFrom('follow')
Expand All @@ -136,6 +145,8 @@ export const violatesThreadGate = async (

if (allowFollowing && check?.isFollowed) {
return false
} else if (allowFollower && check?.isFollower) {
return false
} else if (allowListUris.length && check?.isInList) {
return false
}
Expand Down
6 changes: 6 additions & 0 deletions packages/bsky/src/lexicon/lexicons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7339,6 +7339,7 @@ export const schemaDict = {
type: 'union',
refs: [
'lex:app.bsky.feed.threadgate#mentionRule',
'lex:app.bsky.feed.threadgate#followerRule',
'lex:app.bsky.feed.threadgate#followingRule',
'lex:app.bsky.feed.threadgate#listRule',
],
Expand All @@ -7365,6 +7366,11 @@ export const schemaDict = {
description: 'Allow replies from actors mentioned in your post.',
properties: {},
},
followerRule: {
type: 'object',
description: 'Allow replies from actors who follow you.',
properties: {},
},
followingRule: {
type: 'object',
description: 'Allow replies from actors you follow.',
Expand Down
18 changes: 18 additions & 0 deletions packages/bsky/src/lexicon/types/app/bsky/feed/threadgate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface Record {
post: string
allow?: (
| MentionRule
| FollowerRule
| FollowingRule
| ListRule
| { $type: string; [k: string]: unknown }
Expand Down Expand Up @@ -51,6 +52,23 @@ export function validateMentionRule(v: unknown): ValidationResult {
return lexicons.validate('app.bsky.feed.threadgate#mentionRule', v)
}

/** Allow replies from actors who follow you. */
export interface FollowerRule {
[k: string]: unknown
}

export function isFollowerRule(v: unknown): v is FollowerRule {
return (
isObj(v) &&
hasProp(v, '$type') &&
v.$type === 'app.bsky.feed.threadgate#followerRule'
)
}

export function validateFollowerRule(v: unknown): ValidationResult {
return lexicons.validate('app.bsky.feed.threadgate#followerRule', v)
}

/** Allow replies from actors you follow. */
export interface FollowingRule {
[k: string]: unknown
Expand Down
4 changes: 4 additions & 0 deletions packages/bsky/src/views/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1163,12 +1163,16 @@ export class Views {
const ownerDid = creatorFromUri(rootUriStr)
const {
canReply,
allowFollower,
allowFollowing,
allowListUris = [],
} = parseThreadGate(viewer, ownerDid, rootPost ?? null, gate)
if (canReply) {
return false
}
if (allowFollower && state.profileViewers?.get(ownerDid)?.following) {
return false
}
if (allowFollowing && state.profileViewers?.get(ownerDid)?.followedBy) {
return false
}
Expand Down
13 changes: 11 additions & 2 deletions packages/bsky/src/views/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '../lexicon/types/app/bsky/feed/postgate'
import {
Record as GateRecord,
isFollowerRule,
isFollowingRule,
isListRule,
isMentionRule,
Expand All @@ -28,6 +29,7 @@ export const parseThreadGate = (
}

const allowMentions = gate.allow.some(isMentionRule)
const allowFollower = gate.allow.some(isFollowerRule)
const allowFollowing = gate.allow.some(isFollowingRule)
const allowListUris = gate.allow?.filter(isListRule).map((item) => item.list)

Expand All @@ -39,15 +41,22 @@ export const parseThreadGate = (
)
})
if (isMentioned) {
return { canReply: true, allowMentions, allowFollowing, allowListUris }
return {
canReply: true,
allowMentions,
allowFollower,
allowFollowing,
allowListUris,
}
}
}
return { allowMentions, allowFollowing, allowListUris }
return { allowMentions, allowFollower, allowFollowing, allowListUris }
}

type ParsedThreadGate = {
canReply?: boolean
allowMentions?: boolean
allowFollower?: boolean
allowFollowing?: boolean
allowListUris?: string[]
}
Expand Down
21 changes: 21 additions & 0 deletions packages/bsky/tests/views/__snapshots__/threadgating.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@ Object {
}
`;

exports[`views with thread gating applies gate for follower rule. 1`] = `
Object {
"cid": "cids(0)",
"lists": Array [],
"record": Object {
"$type": "app.bsky.feed.threadgate",
"allow": Array [
Object {
"$type": "app.bsky.feed.threadgate#followerRule",
},
],
"createdAt": "1970-01-01T00:00:00.000Z",
"post": "record(1)",
},
"uri": "record(0)",
}
`;

exports[`views with thread gating applies gate for following rule. 1`] = `
Object {
"cid": "cids(0)",
Expand Down Expand Up @@ -123,6 +141,9 @@ Object {
Object {
"$type": "app.bsky.feed.threadgate#mentionRule",
},
Object {
"$type": "app.bsky.feed.threadgate#followerRule",
},
Object {
"$type": "app.bsky.feed.threadgate#followingRule",
},
Expand Down
Loading

0 comments on commit 0c8884d

Please sign in to comment.