Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 1x 10x 10x 10x 1x 15x 15x 15x 15x 15x 15x 15x 15x 12x 12x 12x 12x 12x 12x 12x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 1x | import {
eventDefinitions,
type EventKey,
type EventPayload,
} from "@workspace/shared/twitchEventSub"
import type { Logger } from "@workspace/logger"
import type { AnnouncementService } from "../announcements/AnnouncementService"
import type { ConvexService } from "../ConvexService"
export type HandlerContext = {
announcements: Pick<AnnouncementService, "send">
convex: Pick<ConvexService, "streamOnline">
messageId: string
template?: string
}
import { chatMessage } from "../../handlers/eventsub/chatMessage"
import { streamOnline } from "../../handlers/eventsub/streamOnline"
import { follow } from "../../handlers/eventsub/follow"
import { subscribe } from "../../handlers/eventsub/subscribe"
import { resubscribe } from "../../handlers/eventsub/resubscribe"
import { subscriptionGift } from "../../handlers/eventsub/subscriptionGift"
import { cheer } from "../../handlers/eventsub/cheer"
import { raid } from "../../handlers/eventsub/raid"
import { hypeTrainBegin } from "../../handlers/eventsub/hypeTrainBegin"
import { hypeTrainEnd } from "../../handlers/eventsub/hypeTrainEnd"
import { charityDonation } from "../../handlers/eventsub/charityDonation"
function route<K extends EventKey>(
key: K,
handler: (event: EventPayload<K>, context: HandlerContext) => Promise<void>
) {
return {
key,
type: eventDefinitions[key].type,
version: eventDefinitions[key].version,
parse: (value: unknown) => {
// K correlates the parser and handler. Zod validates external data first.
const event = eventDefinitions[key].parse(value) as EventPayload<K>
const broadcasterId =
"broadcaster_user_id" in event
? event.broadcaster_user_id
: event.to_broadcaster_user_id
return {
broadcasterId,
dispatch: (context: HandlerContext) => handler(event, context),
}
},
}
}
export const routes = [
route("chatMessage", chatMessage),
route("streamOnline", streamOnline),
route("follow", follow),
route("subscribe", subscribe),
route("resubscribe", resubscribe),
route("subscriptionGift", subscriptionGift),
route("cheer", cheer),
route("raid", raid),
route("hypeTrainBegin", hypeTrainBegin),
route("hypeTrainEnd", hypeTrainEnd),
route("charityDonation", charityDonation),
]
export class EventSubRouter {
constructor(
private readonly services: Pick<HandlerContext, "announcements" | "convex">,
private readonly logger: Logger
) {}
async dispatch(
parsed: ReturnType<(typeof routes)[number]["parse"]>,
key: EventKey,
messageId: string,
template?: string,
beforeSend?: () => Promise<boolean>
): Promise<void> {
const announcements: HandlerContext["announcements"] = {
send: (broadcaster, definition, values, custom) =>
this.services.announcements.send(
broadcaster,
definition,
values,
custom,
beforeSend
),
}
await parsed.dispatch({
...this.services,
announcements,
messageId,
template,
})
this.logger.info("Event dispatched", {
event: key,
})
}
}
|