import { z } from "zod"; import type { JsonObject } from "../core/json.js"; export const X_ACTIVITY_SOURCE_EVENT_TYPE = "stream.thought.source.x.activity"; export const X_ACTIVITY_SOURCE_SCHEMA_VERSION = 1; export const X_WEBHOOK_REVISION = "x-activity-v2-webhook-v2"; export const X_PUBLIC_POST_EVENT_TYPES = ["post.create", "post.delete"] as const; export const X_PRIVATE_ACTIVITY_EVENT_TYPES = ["like.create"] as const; export const X_ACTIVITY_EVENT_TYPES = [...X_PUBLIC_POST_EVENT_TYPES, ...X_PRIVATE_ACTIVITY_EVENT_TYPES] as const; export const X_ACTIVITY_DIRECTIONS = ["inbound", "outbound"] as const; export const X_SOURCE_LANES = ["personal-public", "personal-private", "public-watchlist"] as const; export const xPublicPostEventTypeSchema = z.enum(X_PUBLIC_POST_EVENT_TYPES); export const xActivityEventTypeSchema = z.enum(X_ACTIVITY_EVENT_TYPES); export const xActivityDirectionSchema = z.enum(X_ACTIVITY_DIRECTIONS); export const xSourceLaneSchema = z.enum(X_SOURCE_LANES); export const xNumericIdSchema = z.string().regex(/^[0-9]{1,19}$/); export const xUsernameSchema = z.string().regex(/^[A-Za-z0-9_]{1,50}$/); const xReferenceSchema = z.object({ type: z.enum(["replied_to", "quoted", "retweeted"]), postId: xNumericIdSchema, }).strict(); const xPositionedTagSchema = z.object({ start: z.number().int().nonnegative(), end: z.number().int().positive(), tag: z.string().min(1).max(200), }).strict(); const xMentionSchema = z.object({ start: z.number().int().nonnegative(), end: z.number().int().positive(), username: z.string().min(1).max(100), userId: xNumericIdSchema.optional(), }).strict(); const xUrlEntitySchema = z.object({ start: z.number().int().nonnegative(), end: z.number().int().positive(), url: z.string().min(1).max(2_048), expandedUrl: z.string().min(1).max(8_192).optional(), displayUrl: z.string().min(1).max(2_048).optional(), unwoundUrl: z.string().min(1).max(8_192).optional(), }).strict(); const xEntitiesSchema = z.object({ hashtags: z.array(xPositionedTagSchema).max(100).optional(), cashtags: z.array(xPositionedTagSchema).max(100).optional(), mentions: z.array(xMentionSchema).max(100).optional(), urls: z.array(xUrlEntitySchema).max(100).optional(), }).strict(); const xAttachmentsSchema = z.object({ mediaKeys: z.array(z.string().min(1).max(200)).max(50).optional(), pollIds: z.array(z.string().min(1).max(200)).max(10).optional(), }).strict(); const xTransportSchema = z.object({ revision: z.literal(X_WEBHOOK_REVISION), signatureVerified: z.literal(true), kind: z.literal("x-v2-webhook"), }).strict(); const xActivityEnvelopeFields = { eventUuid: z.string().min(1).max(200), matchedUserId: xNumericIdSchema, subscriptionTag: z.string().min(1).max(200), lane: xSourceLaneSchema, transport: xTransportSchema, }; const xPostCreatePayloadSchema = z.object({ ...xActivityEnvelopeFields, eventType: z.literal("post.create"), post: z.object({ postId: xNumericIdSchema, authorId: xNumericIdSchema, text: z.string().max(100_000), createdAt: z.iso.datetime(), conversationId: xNumericIdSchema.optional(), inReplyToUserId: xNumericIdSchema.optional(), editHistoryPostIds: z.array(xNumericIdSchema).min(1).max(100).optional(), referencedPosts: z.array(xReferenceSchema).max(100).optional(), language: z.string().min(1).max(32).optional(), replySettings: z.string().min(1).max(100).optional(), possiblySensitive: z.boolean().optional(), entities: xEntitiesSchema.optional(), attachments: xAttachmentsSchema.optional(), }).strict(), }).strict(); const xPostDeletePayloadSchema = z.object({ ...xActivityEnvelopeFields, eventType: z.literal("post.delete"), post: z.object({ postId: xNumericIdSchema, authorId: xNumericIdSchema, }).strict(), }).strict(); const xLikeCreatePayloadSchema = z.object({ ...xActivityEnvelopeFields, eventType: z.literal("like.create"), direction: z.literal("outbound"), like: z.object({ likeId: z.string().min(1).max(200), actorId: xNumericIdSchema, postId: xNumericIdSchema, postAuthorId: xNumericIdSchema, likedPostCreatedAt: z.iso.datetime().optional(), eventTimestampMs: z.string().regex(/^[0-9]{1,16}$/).optional(), eventAt: z.iso.datetime().optional(), }).strict(), }).strict(); export const xActivityPayloadSchema = z.discriminatedUnion("eventType", [ xPostCreatePayloadSchema, xPostDeletePayloadSchema, xLikeCreatePayloadSchema, ]) as unknown as z.ZodType; export type XActivityEventType = z.infer; export type XActivityDirection = z.infer; export type XSourceLane = z.infer; export type XActivityPayload = z.infer | z.infer | z.infer;