87 lines
2.7 KiB
TypeScript
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;
|
|
}
|