import {
listChannelMappings,
readCategoryId,
type ChannelMapping,
} from './discord-map.ts'
import {
listBotRecords,
listKnownSecretNames,
normalizeName,
secretNameFor,
urlHostOf,
type BotSummary,
} from './registry.ts'
export type ListResult = {
categoryId: string
channels: ChannelMapping[]
bots: BotSummary[]
}
/**
* List registered Grok Bots without full webhook URLs.
*
* @returns Roster summaries with urlHost, pendingSecret, and channelId when mapped
* @example
* import list from 'kody:@kentcdodds/grok-bot/list'
* const roster = await list()
* // => { categoryId, channels, bots: [{ id, name, aliases, secretName, pendingSecret, urlHost, channelId }] }
*/
export default async function list(): Promise<ListResult> {
const [records, secretNames, categoryId, channels] = await Promise.all([
listBotRecords(),
listKnownSecretNames(),
readCategoryId(),
listChannelMappings(),
])
const bots: BotSummary[] = records.map((record) => ({
id: record.id,
name: record.name,
aliases: record.aliases,
secretName: record.secretName,
pendingSecret: secretNames ? !secretNames.has(record.secretName) : true,
urlHost: urlHostOf(record.url),
...(record.channelId ? { channelId: record.channelId } : {}),
}))
for (const channel of channels) {
if (bots.some((bot) => bot.id === channel.botId)) continue
bots.push({
id: channel.botId,
name: channel.name,
aliases: [normalizeName(channel.name)],
secretName: secretNameFor(channel.botId),
pendingSecret: true,
urlHost: '',
channelId: channel.channelId,
})
}
return { categoryId, channels, bots }
}