Drill agent into feed apis

This commit is contained in:
Eric Bailey
2024-04-25 10:58:08 -05:00
parent 38aa11adea
commit 1061710255
8 changed files with 177 additions and 54 deletions
+17 -4
View File
@@ -1,15 +1,28 @@
import {
AppBskyFeedDefs,
AppBskyFeedGetAuthorFeed as GetAuthorFeed,
BskyAgent,
} from '@atproto/api'
import {FeedAPI, FeedAPIResponse} from './types'
import {getAgent} from '#/state/session'
export class AuthorFeedAPI implements FeedAPI {
constructor(public params: GetAuthorFeed.QueryParams) {}
agent: BskyAgent
params: GetAuthorFeed.QueryParams
constructor({
agent,
feedParams,
}: {
agent: BskyAgent
feedParams: GetAuthorFeed.QueryParams
}) {
this.agent = agent
this.params = feedParams
}
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
const res = await getAgent().getAuthorFeed({
const res = await this.agent.getAuthorFeed({
...this.params,
limit: 1,
})
@@ -23,7 +36,7 @@ export class AuthorFeedAPI implements FeedAPI {
cursor: string | undefined
limit: number
}): Promise<FeedAPIResponse> {
const res = await getAgent().getAuthorFeed({
const res = await this.agent.getAuthorFeed({
...this.params,
cursor,
limit,
+23 -7
View File
@@ -2,18 +2,30 @@ import {
AppBskyFeedDefs,
AppBskyFeedGetFeed as GetCustomFeed,
AtpAgent,
BskyAgent,
} from '@atproto/api'
import {getContentLanguages} from '#/state/preferences/languages'
import {getAgent} from '#/state/session'
import {FeedAPI, FeedAPIResponse} from './types'
export class CustomFeedAPI implements FeedAPI {
constructor(public params: GetCustomFeed.QueryParams) {}
agent: BskyAgent
params: GetCustomFeed.QueryParams
constructor({
agent,
feedParams,
}: {
agent: BskyAgent
feedParams: GetCustomFeed.QueryParams
}) {
this.agent = agent
this.params = feedParams
}
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
const contentLangs = getContentLanguages().join(',')
const res = await getAgent().app.bsky.feed.getFeed(
const res = await this.agent.app.bsky.feed.getFeed(
{
...this.params,
limit: 1,
@@ -31,15 +43,19 @@ export class CustomFeedAPI implements FeedAPI {
limit: number
}): Promise<FeedAPIResponse> {
const contentLangs = getContentLanguages().join(',')
const agent = getAgent()
const res = agent.session
? await getAgent().app.bsky.feed.getFeed(
const res = this.agent.session
? await this.agent.app.bsky.feed.getFeed(
{
...this.params,
cursor,
limit,
},
{headers: {'Accept-Language': contentLangs}},
{
headers: {
'Accept-Language': contentLangs,
},
},
)
: await loggedOutFetch({...this.params, cursor, limit})
if (res.success) {
+9 -5
View File
@@ -1,12 +1,16 @@
import {AppBskyFeedDefs} from '@atproto/api'
import {AppBskyFeedDefs, BskyAgent} from '@atproto/api'
import {FeedAPI, FeedAPIResponse} from './types'
import {getAgent} from '#/state/session'
export class FollowingFeedAPI implements FeedAPI {
constructor() {}
agent: BskyAgent
constructor({agent}: {agent: BskyAgent}) {
this.agent = agent
}
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
const res = await getAgent().getTimeline({
const res = await this.agent.getTimeline({
limit: 1,
})
return res.data.feed[0]
@@ -19,7 +23,7 @@ export class FollowingFeedAPI implements FeedAPI {
cursor: string | undefined
limit: number
}): Promise<FeedAPIResponse> {
const res = await getAgent().getTimeline({
const res = await this.agent.getTimeline({
cursor,
limit,
})
+18 -9
View File
@@ -1,8 +1,9 @@
import {AppBskyFeedDefs} from '@atproto/api'
import {FeedAPI, FeedAPIResponse} from './types'
import {FollowingFeedAPI} from './following'
import {CustomFeedAPI} from './custom'
import {AppBskyFeedDefs, BskyAgent} from '@atproto/api'
import {PROD_DEFAULT_FEED} from '#/lib/constants'
import {CustomFeedAPI} from './custom'
import {FollowingFeedAPI} from './following'
import {FeedAPI, FeedAPIResponse} from './types'
// HACK
// the feed API does not include any facilities for passing down
@@ -26,19 +27,27 @@ export const FALLBACK_MARKER_POST: AppBskyFeedDefs.FeedViewPost = {
}
export class HomeFeedAPI implements FeedAPI {
agent: BskyAgent
following: FollowingFeedAPI
discover: CustomFeedAPI
usingDiscover = false
itemCursor = 0
constructor() {
this.following = new FollowingFeedAPI()
this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')})
constructor({agent}: {agent: BskyAgent}) {
this.agent = agent
this.following = new FollowingFeedAPI({agent})
this.discover = new CustomFeedAPI({
agent,
feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
})
}
reset() {
this.following = new FollowingFeedAPI()
this.discover = new CustomFeedAPI({feed: PROD_DEFAULT_FEED('whats-hot')})
this.following = new FollowingFeedAPI({agent: this.agent})
this.discover = new CustomFeedAPI({
agent: this.agent,
feedParams: {feed: PROD_DEFAULT_FEED('whats-hot')},
})
this.usingDiscover = false
this.itemCursor = 0
}
+17 -4
View File
@@ -1,15 +1,28 @@
import {
AppBskyFeedDefs,
AppBskyFeedGetActorLikes as GetActorLikes,
BskyAgent,
} from '@atproto/api'
import {FeedAPI, FeedAPIResponse} from './types'
import {getAgent} from '#/state/session'
export class LikesFeedAPI implements FeedAPI {
constructor(public params: GetActorLikes.QueryParams) {}
agent: BskyAgent
params: GetActorLikes.QueryParams
constructor({
agent,
feedParams,
}: {
agent: BskyAgent
feedParams: GetActorLikes.QueryParams
}) {
this.agent = agent
this.params = feedParams
}
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
const res = await getAgent().getActorLikes({
const res = await this.agent.getActorLikes({
...this.params,
limit: 1,
})
@@ -23,7 +36,7 @@ export class LikesFeedAPI implements FeedAPI {
cursor: string | undefined
limit: number
}): Promise<FeedAPIResponse> {
const res = await getAgent().getActorLikes({
const res = await this.agent.getActorLikes({
...this.params,
cursor,
limit,
+17 -4
View File
@@ -1,15 +1,28 @@
import {
AppBskyFeedDefs,
AppBskyFeedGetListFeed as GetListFeed,
BskyAgent,
} from '@atproto/api'
import {FeedAPI, FeedAPIResponse} from './types'
import {getAgent} from '#/state/session'
export class ListFeedAPI implements FeedAPI {
constructor(public params: GetListFeed.QueryParams) {}
agent: BskyAgent
params: GetListFeed.QueryParams
constructor({
agent,
feedParams,
}: {
agent: BskyAgent
feedParams: GetListFeed.QueryParams
}) {
this.agent = agent
this.params = feedParams
}
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
const res = await getAgent().app.bsky.feed.getListFeed({
const res = await this.agent.app.bsky.feed.getListFeed({
...this.params,
limit: 1,
})
@@ -23,7 +36,7 @@ export class ListFeedAPI implements FeedAPI {
cursor: string | undefined
limit: number
}): Promise<FeedAPIResponse> {
const res = await getAgent().app.bsky.feed.getListFeed({
const res = await this.agent.app.bsky.feed.getListFeed({
...this.params,
cursor,
limit,
+72 -17
View File
@@ -1,31 +1,51 @@
import {AppBskyFeedDefs, AppBskyFeedGetTimeline} from '@atproto/api'
import {AppBskyFeedDefs, AppBskyFeedGetTimeline, BskyAgent} from '@atproto/api'
import shuffle from 'lodash.shuffle'
import {timeout} from 'lib/async/timeout'
import {getContentLanguages} from '#/state/preferences/languages'
import {FeedParams} from '#/state/queries/post-feed'
import {bundleAsync} from 'lib/async/bundle'
import {timeout} from 'lib/async/timeout'
import {feedUriToHref} from 'lib/strings/url-helpers'
import {FeedTuner} from '../feed-manip'
import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types'
import {FeedParams} from '#/state/queries/post-feed'
import {FeedTunerFn} from '../feed-manip'
import {getAgent} from '#/state/session'
import {getContentLanguages} from '#/state/preferences/languages'
import {FeedAPI, FeedAPIResponse, ReasonFeedSource} from './types'
const REQUEST_WAIT_MS = 500 // 500ms
const POST_AGE_CUTOFF = 60e3 * 60 * 24 // 24hours
export class MergeFeedAPI implements FeedAPI {
agent: BskyAgent
params: FeedParams
feedTuners: FeedTunerFn[]
following: MergeFeedSource_Following
customFeeds: MergeFeedSource_Custom[] = []
feedCursor = 0
itemCursor = 0
sampleCursor = 0
constructor(public params: FeedParams, public feedTuners: FeedTunerFn[]) {
this.following = new MergeFeedSource_Following(this.feedTuners)
constructor({
agent,
feedParams,
feedTuners,
}: {
agent: BskyAgent
feedParams: FeedParams
feedTuners: FeedTunerFn[]
}) {
this.agent = agent
this.params = feedParams
this.feedTuners = feedTuners
this.following = new MergeFeedSource_Following({
agent: this.agent,
feedTuners: this.feedTuners,
})
}
reset() {
this.following = new MergeFeedSource_Following(this.feedTuners)
this.following = new MergeFeedSource_Following({
agent: this.agent,
feedTuners: this.feedTuners,
})
this.customFeeds = []
this.feedCursor = 0
this.itemCursor = 0
@@ -33,7 +53,12 @@ export class MergeFeedAPI implements FeedAPI {
if (this.params.mergeFeedSources) {
this.customFeeds = shuffle(
this.params.mergeFeedSources.map(
feedUri => new MergeFeedSource_Custom(feedUri, this.feedTuners),
feedUri =>
new MergeFeedSource_Custom({
agent: this.agent,
feedUri,
feedTuners: this.feedTuners,
}),
),
)
} else {
@@ -42,7 +67,7 @@ export class MergeFeedAPI implements FeedAPI {
}
async peekLatest(): Promise<AppBskyFeedDefs.FeedViewPost> {
const res = await getAgent().getTimeline({
const res = await this.agent.getTimeline({
limit: 1,
})
return res.data.feed[0]
@@ -136,12 +161,23 @@ export class MergeFeedAPI implements FeedAPI {
}
class MergeFeedSource {
agent: BskyAgent
feedTuners: FeedTunerFn[]
sourceInfo: ReasonFeedSource | undefined
cursor: string | undefined = undefined
queue: AppBskyFeedDefs.FeedViewPost[] = []
hasMore = true
constructor(public feedTuners: FeedTunerFn[]) {}
constructor({
agent,
feedTuners,
}: {
agent: BskyAgent
feedTuners: FeedTunerFn[]
}) {
this.agent = agent
this.feedTuners = feedTuners
}
get numReady() {
return this.queue.length
@@ -203,7 +239,7 @@ class MergeFeedSource_Following extends MergeFeedSource {
cursor: string | undefined,
limit: number,
): Promise<AppBskyFeedGetTimeline.Response> {
const res = await getAgent().getTimeline({cursor, limit})
const res = await this.agent.getTimeline({cursor, limit})
// run the tuner pre-emptively to ensure better mixing
const slices = this.tuner.tune(res.data.feed, {
dryRun: false,
@@ -215,10 +251,25 @@ class MergeFeedSource_Following extends MergeFeedSource {
}
class MergeFeedSource_Custom extends MergeFeedSource {
agent: BskyAgent
minDate: Date
feedUri: string
constructor(public feedUri: string, public feedTuners: FeedTunerFn[]) {
super(feedTuners)
constructor({
agent,
feedUri,
feedTuners,
}: {
agent: BskyAgent
feedUri: string
feedTuners: FeedTunerFn[]
}) {
super({
agent,
feedTuners,
})
this.agent = agent
this.feedUri = feedUri
this.sourceInfo = {
$type: 'reasonFeedSource',
uri: feedUri,
@@ -233,13 +284,17 @@ class MergeFeedSource_Custom extends MergeFeedSource {
): Promise<AppBskyFeedGetTimeline.Response> {
try {
const contentLangs = getContentLanguages().join(',')
const res = await getAgent().app.bsky.feed.getFeed(
const res = await this.agent.app.bsky.feed.getFeed(
{
cursor,
limit,
feed: this.feedUri,
},
{headers: {'Accept-Language': contentLangs}},
{
headers: {
'Accept-Language': contentLangs,
},
},
)
// NOTE
// some custom feeds fail to enforce the pagination limit
+4 -4
View File
@@ -379,7 +379,7 @@ function createApi({
feedParams,
feedTuners,
}: {
agent?: BskyAgent
agent: BskyAgent
feedDesc: FeedDescriptor
feedParams: FeedParams
feedTuners: FeedTunerFn[]
@@ -398,10 +398,10 @@ function createApi({
return new FollowingFeedAPI({agent})
} else if (feedDesc.startsWith('author')) {
const [_, actor, filter] = feedDesc.split('|')
return new AuthorFeedAPI({agent, actor, filter})
return new AuthorFeedAPI({agent, feedParams: {actor, filter}})
} else if (feedDesc.startsWith('likes')) {
const [_, actor] = feedDesc.split('|')
return new LikesFeedAPI({agent, actor})
return new LikesFeedAPI({agent, feedParams: {actor}})
} else if (feedDesc.startsWith('feedgen')) {
const [_, feed] = feedDesc.split('|')
return new CustomFeedAPI({
@@ -410,7 +410,7 @@ function createApi({
})
} else if (feedDesc.startsWith('list')) {
const [_, list] = feedDesc.split('|')
return new ListFeedAPI({agent, list})
return new ListFeedAPI({agent, feedParams: {list}})
} else {
// shouldnt happen
return new FollowingFeedAPI({agent})