add translate
This commit is contained in:
@@ -242,3 +242,96 @@ export async function getRecord(did: string, collection: string, rkey: string):
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// Constants for search
|
||||
const SEARCH_TIMEOUT_MS = 5000
|
||||
const MAX_SEARCH_LENGTH = 20
|
||||
|
||||
// Search posts that link to a URL
|
||||
export async function searchPostsForUrl(url: string): Promise<SearchPost[]> {
|
||||
// Use public.api.bsky.app for search
|
||||
const endpoint = 'https://public.api.bsky.app'
|
||||
|
||||
// Extract search-friendly patterns from URL
|
||||
const searchQueries: string[] = []
|
||||
|
||||
try {
|
||||
const urlObj = new URL(url)
|
||||
const pathWithDomain = urlObj.host + urlObj.pathname.replace(/\/$/, '')
|
||||
|
||||
// Limit length for search
|
||||
if (pathWithDomain.length <= MAX_SEARCH_LENGTH) {
|
||||
searchQueries.push(pathWithDomain)
|
||||
} else {
|
||||
// Truncate to max length
|
||||
searchQueries.push(pathWithDomain.slice(0, MAX_SEARCH_LENGTH))
|
||||
}
|
||||
|
||||
// Also try shorter path
|
||||
const pathParts = urlObj.pathname.split('/').filter(Boolean)
|
||||
if (pathParts.length >= 1) {
|
||||
const shortPath = urlObj.host + '/' + pathParts[0]
|
||||
if (shortPath.length <= MAX_SEARCH_LENGTH) {
|
||||
searchQueries.push(shortPath)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
searchQueries.push(url.slice(0, MAX_SEARCH_LENGTH))
|
||||
}
|
||||
|
||||
const allPosts: SearchPost[] = []
|
||||
const seenUris = new Set<string>()
|
||||
|
||||
for (const query of searchQueries) {
|
||||
try {
|
||||
const controller = new AbortController()
|
||||
const timeoutId = setTimeout(() => controller.abort(), SEARCH_TIMEOUT_MS)
|
||||
|
||||
const res = await fetch(
|
||||
`${endpoint}/xrpc/app.bsky.feed.searchPosts?q=${encodeURIComponent(query)}&limit=20`,
|
||||
{ signal: controller.signal }
|
||||
)
|
||||
clearTimeout(timeoutId)
|
||||
|
||||
if (!res.ok) continue
|
||||
|
||||
const data = await res.json()
|
||||
const posts = (data.posts || []).filter((post: SearchPost) => {
|
||||
const embedUri = (post.record as { embed?: { external?: { uri?: string } } })?.embed?.external?.uri
|
||||
const text = (post.record as { text?: string })?.text || ''
|
||||
return embedUri === url || text.includes(url) || embedUri?.includes(url.replace(/\/$/, ''))
|
||||
})
|
||||
|
||||
for (const post of posts) {
|
||||
if (!seenUris.has(post.uri)) {
|
||||
seenUris.add(post.uri)
|
||||
allPosts.push(post)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Timeout or network error
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by date (newest first)
|
||||
allPosts.sort((a, b) => {
|
||||
const aDate = (a.record as { createdAt?: string })?.createdAt || ''
|
||||
const bDate = (b.record as { createdAt?: string })?.createdAt || ''
|
||||
return new Date(bDate).getTime() - new Date(aDate).getTime()
|
||||
})
|
||||
|
||||
return allPosts
|
||||
}
|
||||
|
||||
// Search post type
|
||||
export interface SearchPost {
|
||||
uri: string
|
||||
cid: string
|
||||
author: {
|
||||
did: string
|
||||
handle: string
|
||||
displayName?: string
|
||||
avatar?: string
|
||||
}
|
||||
record: unknown
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user