Skip to content

Built for people who want to own their automations. Join the waitlist for an invite.

Package listing

@kody/meta

src/pages.ts

140 lines · 4.0 KB · TypeScript
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 }
}