import { randomBytes } from 'bare-crypto'
const SOURCES = ['mastodon', 'atproto', 'instagram', 'local']
class FeedStore {
constructor (bee) {
this.bee = bee
}
_genId () {
return randomBytes(8).toString('hex')
}
_padTimestamp (ts) {
return String(ts).padStart(16, '0')
}
async add (opts = {}) {
const id = opts.id || this._genId()
const now = Date.now()
const item = {
id,
source: opts.source || 'local',
author: opts.author || 'unknown',
authorId: opts.authorId || opts.author || 'unknown',
authorPublicKey: opts.authorPublicKey || null,
text: opts.text || '',
timestamp: opts.timestamp || now,
type: opts.type || 'post',
tags: opts.tags || [],
url: opts.url || null,
mediaUrl: opts.mediaUrl || null,
mediaType: opts.mediaType || null,
metadata: opts.metadata || {},
syndicated: opts.syndicated || null,
anproto: opts.anproto || null
}
const batch = this.bee.batch()
await batch.put(`item/${id}`, item)
await batch.put(`timeline/${this._padTimestamp(item.timestamp)}/${id}`, { id })
await batch.put(`source/${item.source}/${this._padTimestamp(item.timestamp)}/${id}`, { id })
await batch.put(`author/${item.authorId}/${this._padTimestamp(item.timestamp)}/${id}`, { id })
await batch.flush()
return item
}
async get (id) {
const node = await this.bee.get(`item/${id}`)
return node ? node.value : null
}
async timeline (opts = {}) {
const { limit = 50, before, after, source } = opts
const items = []
const rangeOpts = {}
if (source) {
rangeOpts.gte = `source/${source}/`
rangeOpts.lte = `source/${source}/~`
if (before) rangeOpts.lt = `source/${source}/${this._padTimestamp(before)}/`
} else {
rangeOpts.gte = 'timeline/'
rangeOpts.lte = 'timeline/~'
if (before) rangeOpts.lt = `timeline/${this._padTimestamp(before)}/`
if (after) rangeOpts.gt = `timeline/${this._padTimestamp(after)}/~`
}
// Read in reverse (newest first)
rangeOpts.reverse = true
for await (const node of this.bee.createReadStream(rangeOpts)) {
if (!node.value) continue
const item = await this.get(node.value.id)
if (!item) continue
items.push(item)
if (items.length >= limit) break
}
return items
}
}
export { FeedStore, SOURCES }