import { parseAuth } from './auth.ts'
import { intercomRequest } from './client.ts'
import { compactDefined, extractListItems, mapArticle } from './models.ts'
import { getMe } from './me.ts'
import type {
DryRunResult,
IntercomArticle,
IntercomAuthInput,
IntercomPageInfo,
JsonRecord,
MutationInput,
} from './types.ts'
import {
clampInt,
optionalBoolean,
optionalString,
requireRecord,
requireString,
} from './types.ts'
export type ListArticlesInput = IntercomAuthInput & {
perPage?: number
page?: number
}
export type GetArticleInput = IntercomAuthInput & {
id: string
}
export type CreateArticleInput = IntercomAuthInput &
MutationInput & {
title: string
body?: string
authorId?: string
state?: string
parentId?: number | string
parentType?: string
}
export type UpdateArticleInput = IntercomAuthInput &
MutationInput & {
id: string
title?: string
body?: string
authorId?: string
state?: string
parentId?: number | string
parentType?: string
}
export type ArticleListResult = {
items: Array<IntercomArticle>
pageInfo: IntercomPageInfo
}
async function resolveAuthorId(input: IntercomAuthInput & { authorId?: string }): Promise<string> {
if (input.authorId) return requireString(input.authorId, 'authorId')
const me = await getMe(input)
return me.id
}
function articleBody(
input: CreateArticleInput | UpdateArticleInput,
authorId: string,
): JsonRecord {
return compactDefined({
title: 'title' in input ? input.title : undefined,
body: input.body,
author_id: authorId,
state: input.state,
parent_id: input.parentId,
parent_type: input.parentType,
})
}
/**
* List Intercom Help Center articles.
* @example
* import { listArticles } from 'kody:@kody/intercom/articles'
* const { items } = await listArticles({ perPage: 10 })
*/
export async function listArticles(input: ListArticlesInput = {}): Promise<ArticleListResult> {
const result = await intercomRequest<unknown>({
...input,
operation: 'articles.read',
path: '/articles',
query: compactDefined({
per_page: clampInt(input.perPage, 1, 150, 20),
page: input.page === undefined ? undefined : clampInt(input.page, 1, 10_000, 1, 'page'),
}),
})
if ('dryRun' in result) {
throw new Error('listArticles is read-only.')
}
return {
items: extractListItems(result.data)
.map(mapArticle)
.filter((item): item is IntercomArticle => Boolean(item)),
pageInfo: result.pageInfo,
}
}
/**
* Get one Intercom article by id.
* @example
* import { getArticle } from 'kody:@kody/intercom/articles'
* const article = await getArticle({ id })
*/
export async function getArticle(input: GetArticleInput): Promise<IntercomArticle> {
const id = requireString(input.id, 'id')
const result = await intercomRequest<unknown>({
...input,
operation: 'articles.read',
path: `/articles/${encodeURIComponent(id)}`,
})
if ('dryRun' in result) {
throw new Error('getArticle is read-only.')
}
const mapped = mapArticle(result.data)
if (!mapped) throw new Error('Intercom did not return an article id.')
return mapped
}
/**
* Create an Intercom article. Requires `confirm: true`, or use `dryRun: true`.
* When `authorId` is omitted, the connected `/me` admin is used.
* @example
* import { createArticle } from 'kody:@kody/intercom/articles'
* const preview = await createArticle({ title: 'Reset your password', dryRun: true })
*/
export async function createArticle(
input: CreateArticleInput,
): Promise<IntercomArticle | DryRunResult> {
const title = requireString(input.title, 'title')
const authorId = input.dryRun
? input.authorId ?? '<connected-admin>'
: await resolveAuthorId(input)
const result = await intercomRequest<unknown>({
...input,
operation: 'articles.write',
method: 'POST',
path: '/articles',
body: articleBody({ ...input, title }, authorId),
})
if ('dryRun' in result) return result
const mapped = mapArticle(result.data)
if (!mapped) throw new Error('Intercom did not return a created article id.')
return mapped
}
/**
* Update an Intercom article. Requires `confirm: true`, or use `dryRun: true`.
* @example
* import { updateArticle } from 'kody:@kody/intercom/articles'
* const preview = await updateArticle({ id, title: 'Updated title', dryRun: true })
*/
export async function updateArticle(
input: UpdateArticleInput,
): Promise<IntercomArticle | DryRunResult> {
const id = requireString(input.id, 'id')
const authorId = input.dryRun
? input.authorId ?? '<connected-admin>'
: input.authorId
? requireString(input.authorId, 'authorId')
: undefined
const result = await intercomRequest<unknown>({
...input,
operation: 'articles.write',
method: 'PUT',
path: `/articles/${encodeURIComponent(id)}`,
body: compactDefined({
title: input.title,
body: input.body,
author_id: authorId,
state: input.state,
parent_id: input.parentId,
parent_type: input.parentType,
}),
})
if ('dryRun' in result) return result
const mapped = mapArticle(result.data)
if (!mapped) throw new Error('Intercom did not return an updated article id.')
return mapped
}
export function parseListArticles(params: Record<string, unknown>): ListArticlesInput {
const input = requireRecord(params, 'list-articles')
return {
...parseAuth(input),
perPage: input.perPage as number | undefined,
page: input.page as number | undefined,
}
}
export function parseGetArticle(params: Record<string, unknown>): GetArticleInput {
const input = requireRecord(params, 'get-article')
return { ...parseAuth(input), id: requireString(input.id, 'id') }
}
export function parseCreateArticle(params: Record<string, unknown>): CreateArticleInput {
const input = requireRecord(params, 'create-article')
return {
...parseAuth(input),
title: requireString(input.title, 'title'),
body: optionalString(input.body, 'body'),
authorId: optionalString(input.authorId, 'authorId'),
state: optionalString(input.state, 'state'),
parentId: input.parentId as number | string | undefined,
parentType: optionalString(input.parentType, 'parentType'),
confirm: optionalBoolean(input.confirm, 'confirm'),
dryRun: optionalBoolean(input.dryRun, 'dryRun'),
}
}
export function parseUpdateArticle(params: Record<string, unknown>): UpdateArticleInput {
const input = requireRecord(params, 'update-article')
return {
...parseAuth(input),
id: requireString(input.id, 'id'),
title: optionalString(input.title, 'title'),
body: optionalString(input.body, 'body'),
authorId: optionalString(input.authorId, 'authorId'),
state: optionalString(input.state, 'state'),
parentId: input.parentId as number | string | undefined,
parentType: optionalString(input.parentType, 'parentType'),
confirm: optionalBoolean(input.confirm, 'confirm'),
dryRun: optionalBoolean(input.dryRun, 'dryRun'),
}
}
/**
* Intercom article helpers. Writes require `confirm: true` or `dryRun: true`.
* @example
* import { listArticles } from 'kody:@kody/intercom/articles'
* const { items } = await listArticles({ perPage: 10 })
*/
export default async function articlesEntrypoint(params: Record<string, unknown> = {}) {
return listArticles(parseListArticles(params))
}