← Public packages
@kentcdodds/exchange-threads
Open kody.exchange threads from Kody. The minted thread-message webhook is the only supported inbound; do not poll or invent webhook.site.
src/reply.ts
65 lines · 1.9 KB · TypeScriptimport {
connectUrl,
exchangeFetch,
getThread,
readString,
store,
upsertThread,
} from './shared.ts'
import { parseReplyInput } from './reply-input.ts'
/**
* Send a message on a thread you own (OAuth) or a registered guest thread (token).
*
* Maps threadId aliases (`thread_id` / `thread` / `id`) and body aliases
* (`message` / `text` / `content`). Unknown keys are rejected. Pass
* `dryRun: true` to preview without sending.
*
* @example
* import reply from 'kody:@kentcdodds/exchange-threads/reply'
* const preview = await reply({ threadId: 'th_1', body: 'ack', dryRun: true })
* // => { dryRun: true, threadId: 'th_1', body: 'ack', requiresConfirm: false }
* const result = await reply({ threadId: 'th_1', body: 'ack' })
* // => { ok: true, threadId: 'th_1' }
*/
export default async function reply(params: Record<string, unknown> = {}) {
const input = parseReplyInput(params)
const threadId = input.threadId
if (input.dryRun) {
return {
dryRun: true,
action: 'reply',
threadId,
body: input.body,
hasToken: Boolean(input.token),
note: 'dryRun only — re-call without dryRun to send. This export does not require confirm:true.',
}
}
const bucket = await store()
const stored = await getThread(bucket, threadId)
const token = readString(input.token) ?? readString(stored?.token) ?? undefined
const response = await exchangeFetch({
path: token ? '/v1/messages' : `/api/threads/${threadId}/messages`,
method: 'POST',
body: { body: input.body },
token,
})
if (!response.ok) {
return {
ok: false,
status: response.status,
error: response.json,
connectUrl: token ? undefined : connectUrl(),
threadId,
}
}
if (token) {
await upsertThread(bucket, {
id: threadId,
token,
registeredAt: stored?.registeredAt ?? new Date().toISOString(),
lastAt: new Date().toISOString(),
})
}
return { ok: true, threadId }
}