add translate

This commit is contained in:
2026-01-18 15:47:24 +09:00
parent 6ef8780ac6
commit 8f8b2b7d28
18 changed files with 1049 additions and 153 deletions

View File

@@ -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
}