import { resolveMetaIntegration, type MetaAccountParams } from './accounts.ts'
import {
graphItems,
graphRequest,
requireConfirmOrDryRun,
withPageAccessToken,
type JsonObject,
} from './core.ts'
import { boundedLimit } from './validation.ts'
export type PageListParams = MetaAccountParams & {
limit?: number
after?: string
fields?: string
}
export type PageGetParams = MetaAccountParams & {
pageId: string
fields?: string
}
export type PagePostsParams = MetaAccountParams & {
pageId: string
limit?: number
after?: string
fields?: string
}
export type PagePublishParams = MetaAccountParams & {
pageId: string
message: string
link?: string
published?: boolean
dryRun?: boolean
confirm?: boolean
}
const PAGE_LIST_FIELDS = 'id,name,category,tasks,fan_count,instagram_business_account'
const PAGE_GET_FIELDS = 'id,name,category,about,fan_count,link,username,instagram_business_account'
const POST_FIELDS = 'id,message,created_time,permalink_url,shares,is_published'
/** List Facebook Pages the connected user or system user can access. Never hard-codes a Page id. */
export async function listPages(params: PageListParams = {}) {
const data = await graphRequest<JsonObject>({
integration: resolveMetaIntegration(params),
authMode: params.authMode,
tokenSecret: params.tokenSecret,
account: params.account,
path: '/me/accounts',
query: {
fields: params.fields ?? PAGE_LIST_FIELDS,
limit: boundedLimit(params.limit, 25, 100),
after: params.after,
},
})
return graphItems<JsonObject>(data)
}
/** Read one Facebook Page by caller-supplied id. */
export async function getPage(params: PageGetParams) {
const pageId = params.pageId?.trim()
if (!pageId) throw new Error('getPage requires pageId from listPages (or the Page the caller named).')
return graphRequest<JsonObject>({
...params,
path: '/' + encodeURIComponent(pageId),
query: { fields: params.fields ?? PAGE_GET_FIELDS },
})
}
/** List recent posts on a Facebook Page. */
export async function listPosts(params: PagePostsParams) {
const pageId = params.pageId?.trim()
if (!pageId) throw new Error('listPosts requires pageId.')
const data = await graphRequest<JsonObject>({
...params,
path: '/' + encodeURIComponent(pageId) + '/posts',
query: {
fields: params.fields ?? POST_FIELDS,
limit: boundedLimit(params.limit, 10, 50),
after: params.after,
},
})
return graphItems<JsonObject>(data)
}
/** Read one Page post. */
export async function getPost(params: MetaAccountParams & { postId: string; fields?: string }) {
const postId = params.postId?.trim()
if (!postId) throw new Error('getPost requires postId.')
return graphRequest<JsonObject>({
...params,
path: '/' + encodeURIComponent(postId),
query: { fields: params.fields ?? POST_FIELDS },
})
}
/** Preview or publish a Page feed post. Live publish requires confirm: true. */
export async function publishPost(params: PagePublishParams) {
const pageId = params.pageId?.trim()
if (!pageId) throw new Error('publishPost requires pageId from listPages. Do not invent a brand Page id.')
const message = params.message?.trim()
if (!message) throw new Error('publishPost requires message.')
const body: JsonObject = { message }
if (params.link) body.link = params.link
if (params.published === false) body.published = false
const path = '/' + encodeURIComponent(pageId) + '/feed'
const mode = requireConfirmOrDryRun({
dryRun: params.dryRun,
confirm: params.confirm,
action: 'publishPost',
})
if (mode.dryRun) {
return {
dryRun: true as const,
method: 'POST',
path,
pageId,
body,
}
}
return withPageAccessToken({ ...params, pageId }, (pageAccessToken) =>
graphRequest<JsonObject>({
...params,
pageAccessToken,
method: 'POST',
path,
body,
}),
)
}
/**
* Facebook Page helpers.
* @example
* import { listPages } from 'kody:@kody/meta/pages'
* const { items } = await listPages({ limit: 5 })
*/
export default function pages() {
return { listPages, getPage, listPosts, getPost, publishPost }
}