87 lines
2.7 KiB
TypeScript

import type { RuntimeEnv } from "../../api.js";
import type { Foreigns } from "../urbit/foreigns.js";
import { formatChangesDate } from "./utils.js";
export async function fetchGroupChanges(
api: { scry: (path: string) => Promise<unknown> },
runtime: RuntimeEnv,
daysAgo = 5,
) {
try {
const changeDate = formatChangesDate(daysAgo);
runtime.log?.(`[tlon] Fetching group changes since ${daysAgo} days ago (${changeDate})...`);
const changes = await api.scry(`/groups-ui/v5/changes/${changeDate}.json`);
if (changes) {
runtime.log?.("[tlon] Successfully fetched changes data");
return changes;
}
return null;
} catch (error: any) {
runtime.log?.(
`[tlon] Failed to fetch changes (falling back to full init): ${error?.message ?? String(error)}`,
);
return null;
}
}
export interface InitData {
channels: string[];
foreigns: Foreigns | null;
}
/**
* Fetch groups-ui init data, returning channels and foreigns.
* This is a single scry that provides both channel discovery and pending invites.
*/
export async function fetchInitData(
api: { scry: (path: string) => Promise<unknown> },
runtime: RuntimeEnv,
): Promise<InitData> {
try {
runtime.log?.("[tlon] Fetching groups-ui init data...");
const initData = (await api.scry("/groups-ui/v6/init.json")) as any;
const channels: string[] = [];
if (initData?.groups) {
for (const groupData of Object.values(initData.groups as Record<string, any>)) {
if (groupData && typeof groupData === "object" && groupData.channels) {
for (const channelNest of Object.keys(groupData.channels)) {
if (channelNest.startsWith("chat/")) {
channels.push(channelNest);
}
}
}
}
}
if (channels.length > 0) {
runtime.log?.(`[tlon] Auto-discovered ${channels.length} chat channel(s)`);
} else {
runtime.log?.("[tlon] No chat channels found via auto-discovery");
}
const foreigns = (initData?.foreigns as Foreigns) || null;
if (foreigns) {
const pendingCount = Object.values(foreigns).filter((f) =>
f.invites?.some((i) => i.valid),
).length;
if (pendingCount > 0) {
runtime.log?.(`[tlon] Found ${pendingCount} pending group invite(s)`);
}
}
return { channels, foreigns };
} catch (error: any) {
runtime.log?.(`[tlon] Init data fetch failed: ${error?.message ?? String(error)}`);
return { channels: [], foreigns: null };
}
}
export async function fetchAllChannels(
api: { scry: (path: string) => Promise<unknown> },
runtime: RuntimeEnv,
): Promise<string[]> {
const { channels } = await fetchInitData(api, runtime);
return channels;
}