Files
clawdbot/extensions/tlon/src/urbit/send.ts
Hunter Miller 8a2720db4c fix(tlon): Fix Zod v4 record() and @urbit/aura v3 API changes (#1631)
* fix(tlon): Fix Zod v4 record() and @urbit/aura v3 API changes

- Fix Zod v4.3.6 bug: single-arg z.record() fails with toJSONSchema()
  - Use two-arg form: z.record(z.string(), schema)
  - Fixes 'Cannot read properties of undefined (reading _zod)' error

- Fix @urbit/aura v3.0.0 API migration:
  - unixToDa() → da.fromUnix()
  - formatUd() → scot('ud', ...)
  - Fixes '(0 , _aura.unixToDa) is not a function' error

These were blocking Tlon plugin loading and outbound messaging.

* fix: add tlon schema/aura tests (#1631) (thanks @arthyn)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-01-24 21:09:18 +00:00

115 lines
2.4 KiB
TypeScript

import { scot, da } from "@urbit/aura";
export type TlonPokeApi = {
poke: (params: { app: string; mark: string; json: unknown }) => Promise<unknown>;
};
type SendTextParams = {
api: TlonPokeApi;
fromShip: string;
toShip: string;
text: string;
};
export async function sendDm({ api, fromShip, toShip, text }: SendTextParams) {
const story = [{ inline: [text] }];
const sentAt = Date.now();
const idUd = scot('ud', da.fromUnix(sentAt));
const id = `${fromShip}/${idUd}`;
const delta = {
add: {
memo: {
content: story,
author: fromShip,
sent: sentAt,
},
kind: null,
time: null,
},
};
const action = {
ship: toShip,
diff: { id, delta },
};
await api.poke({
app: "chat",
mark: "chat-dm-action",
json: action,
});
return { channel: "tlon", messageId: id };
}
type SendGroupParams = {
api: TlonPokeApi;
fromShip: string;
hostShip: string;
channelName: string;
text: string;
replyToId?: string | null;
};
export async function sendGroupMessage({
api,
fromShip,
hostShip,
channelName,
text,
replyToId,
}: SendGroupParams) {
const story = [{ inline: [text] }];
const sentAt = Date.now();
const action = {
channel: {
nest: `chat/${hostShip}/${channelName}`,
action: replyToId
? {
reply: {
id: replyToId,
delta: {
add: {
memo: {
content: story,
author: fromShip,
sent: sentAt,
},
},
},
},
}
: {
post: {
add: {
content: story,
author: fromShip,
sent: sentAt,
kind: "/chat",
blob: null,
meta: null,
},
},
},
},
};
await api.poke({
app: "channels",
mark: "channel-action-1",
json: action,
});
return { channel: "tlon", messageId: `${fromShip}/${sentAt}` };
}
export function buildMediaText(text: string | undefined, mediaUrl: string | undefined): string {
const cleanText = text?.trim() ?? "";
const cleanUrl = mediaUrl?.trim() ?? "";
if (cleanText && cleanUrl) return `${cleanText}\n${cleanUrl}`;
if (cleanUrl) return cleanUrl;
return cleanText;
}