195 lines
5.8 KiB
Diff
195 lines
5.8 KiB
Diff
diff --git a/.dockerignore b/.dockerignore
|
|
new file mode 100644
|
|
index 0000000..3c3629e
|
|
--- /dev/null
|
|
+++ b/.dockerignore
|
|
@@ -0,0 +1 @@
|
|
+node_modules
|
|
diff --git a/Dockerfile b/Dockerfile
|
|
new file mode 100644
|
|
index 0000000..993c83d
|
|
--- /dev/null
|
|
+++ b/Dockerfile
|
|
@@ -0,0 +1,11 @@
|
|
+FROM node:18-alpine
|
|
+
|
|
+WORKDIR /app
|
|
+
|
|
+COPY package.json yarn.lock ./
|
|
+RUN yarn install
|
|
+
|
|
+COPY . .
|
|
+
|
|
+EXPOSE 3000
|
|
+CMD ["yarn", "start"]
|
|
diff --git a/scripts/publish.ts b/scripts/publish.ts
|
|
new file mode 100644
|
|
index 0000000..966edcf
|
|
--- /dev/null
|
|
+++ b/scripts/publish.ts
|
|
@@ -0,0 +1,64 @@
|
|
+import dotenv from 'dotenv'
|
|
+import { AtpAgent, BlobRef, AppBskyFeedDefs } from '@atproto/api'
|
|
+import fs from 'fs/promises'
|
|
+import { ids } from '../src/lexicon/lexicons'
|
|
+
|
|
+const run = async () => {
|
|
+ dotenv.config()
|
|
+
|
|
+ const handle = process.env.FEEDGEN_HANDLE
|
|
+ const password = process.env.FEEDGEN_PASSWORD
|
|
+ const recordName = process.env.FEEDGEN_RECORD_NAME || 'app'
|
|
+ const displayName = process.env.FEEDGEN_DISPLAY_NAME || 'App Feed'
|
|
+ const description = process.env.FEEDGEN_DESCRIPTION || 'Automated App Feed'
|
|
+ const avatar = process.env.FEEDGEN_AVATAR
|
|
+ const service = process.env.FEEDGEN_SERVICE_URL || 'https://syu.is'
|
|
+
|
|
+ if (!handle || !password) {
|
|
+ throw new Error('Please provide FEEDGEN_HANDLE and FEEDGEN_PASSWORD environment variables')
|
|
+ }
|
|
+
|
|
+ if (!process.env.FEEDGEN_SERVICE_DID && !process.env.FEEDGEN_HOSTNAME) {
|
|
+ throw new Error('Please provide a hostname in the .env file')
|
|
+ }
|
|
+
|
|
+ const feedGenDid =
|
|
+ process.env.FEEDGEN_SERVICE_DID ?? `did:web:${process.env.FEEDGEN_HOSTNAME}`
|
|
+
|
|
+ const agent = new AtpAgent({ service })
|
|
+ await agent.login({ identifier: handle, password })
|
|
+
|
|
+ let avatarRef: BlobRef | undefined
|
|
+ if (avatar) {
|
|
+ let encoding: string
|
|
+ if (avatar.endsWith('png')) {
|
|
+ encoding = 'image/png'
|
|
+ } else if (avatar.endsWith('jpg') || avatar.endsWith('jpeg')) {
|
|
+ encoding = 'image/jpeg'
|
|
+ } else {
|
|
+ throw new Error('expected png or jpeg')
|
|
+ }
|
|
+ const img = await fs.readFile(avatar)
|
|
+ const blobRes = await agent.api.com.atproto.repo.uploadBlob(img, {
|
|
+ encoding,
|
|
+ })
|
|
+ avatarRef = blobRes.data.blob
|
|
+ }
|
|
+
|
|
+ await agent.api.com.atproto.repo.putRecord({
|
|
+ repo: agent.session?.did ?? '',
|
|
+ collection: ids.AppBskyFeedGenerator,
|
|
+ rkey: recordName,
|
|
+ record: {
|
|
+ did: feedGenDid,
|
|
+ displayName: displayName,
|
|
+ description: description,
|
|
+ avatar: avatarRef,
|
|
+ createdAt: new Date().toISOString(),
|
|
+ },
|
|
+ })
|
|
+
|
|
+ console.log('All done 🎉')
|
|
+}
|
|
+
|
|
+run()
|
|
diff --git a/src/algos/app.ts b/src/algos/app.ts
|
|
new file mode 100644
|
|
index 0000000..2376be9
|
|
--- /dev/null
|
|
+++ b/src/algos/app.ts
|
|
@@ -0,0 +1,35 @@
|
|
+import { QueryParams } from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
|
|
+import { AppContext } from '../config'
|
|
+
|
|
+// max 15 chars
|
|
+export const shortname = 'app'
|
|
+
|
|
+export const handler = async (ctx: AppContext, params: QueryParams) => {
|
|
+ let builder = ctx.db
|
|
+ .selectFrom('post')
|
|
+ .selectAll()
|
|
+ .orderBy('indexedAt', 'desc')
|
|
+ .orderBy('cid', 'desc')
|
|
+ .limit(params.limit)
|
|
+
|
|
+ if (params.cursor) {
|
|
+ const timeStr = new Date(parseInt(params.cursor, 10)).toISOString()
|
|
+ builder = builder.where('post.indexedAt', '<', timeStr)
|
|
+ }
|
|
+ const res = await builder.execute()
|
|
+
|
|
+ const feed = res.map((row) => ({
|
|
+ post: row.uri,
|
|
+ }))
|
|
+
|
|
+ let cursor: string | undefined
|
|
+ const last = res.at(-1)
|
|
+ if (last) {
|
|
+ cursor = new Date(last.indexedAt).getTime().toString(10)
|
|
+ }
|
|
+
|
|
+ return {
|
|
+ cursor,
|
|
+ feed,
|
|
+ }
|
|
+}
|
|
diff --git a/src/algos/index.ts b/src/algos/index.ts
|
|
index b7ee48a..102cb93 100644
|
|
--- a/src/algos/index.ts
|
|
+++ b/src/algos/index.ts
|
|
@@ -4,11 +4,13 @@ import {
|
|
OutputSchema as AlgoOutput,
|
|
} from '../lexicon/types/app/bsky/feed/getFeedSkeleton'
|
|
import * as whatsAlf from './whats-alf'
|
|
+import * as app from './app'
|
|
|
|
type AlgoHandler = (ctx: AppContext, params: QueryParams) => Promise<AlgoOutput>
|
|
|
|
const algos: Record<string, AlgoHandler> = {
|
|
[whatsAlf.shortname]: whatsAlf.handler,
|
|
+ [app.shortname]: app.handler,
|
|
}
|
|
|
|
export default algos
|
|
diff --git a/src/index.ts b/src/index.ts
|
|
index c3bd006..1e7f0b5 100644
|
|
--- a/src/index.ts
|
|
+++ b/src/index.ts
|
|
@@ -24,6 +24,8 @@ const run = async () => {
|
|
console.log(
|
|
`🤖 running feed generator at http://${server.cfg.listenhost}:${server.cfg.port}`,
|
|
)
|
|
+ console.log('Supported algos:', Object.keys(require('./algos').default))
|
|
+ console.log('Publisher DID:', server.cfg.publisherDid)
|
|
}
|
|
|
|
const maybeStr = (val?: string) => {
|
|
diff --git a/src/methods/feed-generation.ts b/src/methods/feed-generation.ts
|
|
index b887413..34c5148 100644
|
|
--- a/src/methods/feed-generation.ts
|
|
+++ b/src/methods/feed-generation.ts
|
|
@@ -10,7 +10,7 @@ export default function (server: Server, ctx: AppContext) {
|
|
const feedUri = new AtUri(params.feed)
|
|
const algo = algos[feedUri.rkey]
|
|
if (
|
|
- feedUri.hostname !== ctx.cfg.publisherDid ||
|
|
+ //feedUri.hostname !== ctx.cfg.publisherDid ||
|
|
feedUri.collection !== 'app.bsky.feed.generator' ||
|
|
!algo
|
|
) {
|
|
diff --git a/src/subscription.ts b/src/subscription.ts
|
|
index 0422a03..d591ef9 100644
|
|
--- a/src/subscription.ts
|
|
+++ b/src/subscription.ts
|
|
@@ -19,10 +19,6 @@ export class FirehoseSubscription extends FirehoseSubscriptionBase {
|
|
|
|
const postsToDelete = ops.posts.deletes.map((del) => del.uri)
|
|
const postsToCreate = ops.posts.creates
|
|
- .filter((create) => {
|
|
- // only alf-related posts
|
|
- return create.record.text.toLowerCase().includes('alf')
|
|
- })
|
|
.map((create) => {
|
|
// map alf-related posts to a db row
|
|
return {
|