All files / src/services/eventsub EventSubRouter.ts

100% Statements 94/94
100% Branches 11/11
100% Functions 6/6
100% Lines 94/94

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 954x 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,
    })
  }
}