Current section

Files

Jump to
teac lib teac event_sub.ex
Raw

lib/teac/event_sub.ex

defmodule Teac.EventSub do
@moduledoc """
Top-level dispatcher for EventSub event payloads.
`parse/2` converts a raw subscription map and event map (as delivered by
`Teac.WssClient` or a webhook) into a typed struct. Handlers no longer need
to know which sub-module owns each subscription type.
## Usage in a handler
defmodule MyApp.TwitchHandler do
@behaviour Teac.WssClient.Handler
@impl true
def handle_event(subscription, event, state) do
parsed = Teac.EventSub.parse(subscription, event)
send(state.pid, {:twitch_event, parsed})
{:ok, state}
end
@impl true
def handle_revocation(%{"type" => type}, state) do
send(state.pid, {:twitch_subscription_revoked, type})
state
end
end
## Unknown types
Any subscription type not yet handled returns
`%Teac.EventSub.Unknown{type: type, data: event_map}` rather than raising.
This makes handlers forward-compatible with new Twitch event types.
"""
alias Teac.EventSub
defmodule Unknown do
@moduledoc "Fallback struct for unrecognised EventSub subscription types."
@type t :: %__MODULE__{type: String.t(), data: map()}
defstruct [:type, :data]
end
@doc """
Parses a raw EventSub notification into a typed struct.
Accepts either the full subscription map (with a `"type"` key) or a bare
type string as the first argument.
Returns `%Teac.EventSub.Unknown{}` for unrecognised types.
"""
@spec parse(map() | String.t(), map()) :: struct()
def parse(%{"type" => type}, event), do: parse(type, event)
# ── Channel ──────────────────────────────────────────────────────────────
def parse("channel.update", event), do: EventSub.ChannelUpdate.from_event(event)
def parse("channel.follow", event), do: EventSub.Follow.from_event(event)
def parse("channel.ad_break.begin", event), do: EventSub.AdBreak.from_event(event)
def parse("channel.raid", event), do: EventSub.Raid.from_event(event)
def parse("channel.cheer", event), do: EventSub.Cheer.from_event(event)
def parse("channel.bits.use", event), do: EventSub.Bits.from_event(event)
def parse("channel.ban", event), do: EventSub.Ban.from_event("channel.ban", event)
def parse("channel.unban", event), do: EventSub.Ban.from_event("channel.unban", event)
# Chat
def parse("channel.chat.message", event), do: EventSub.ChatMessage.from_event(event)
def parse("channel.chat.notification", event), do: EventSub.ChatNotification.from_event(event)
def parse("channel.chat.clear", event), do: EventSub.ChatEvent.from_event("channel.chat.clear", event)
def parse("channel.chat.clear_user_messages", event), do: EventSub.ChatEvent.from_event("channel.chat.clear_user_messages", event)
def parse("channel.chat.message_delete", event), do: EventSub.ChatEvent.from_event("channel.chat.message_delete", event)
def parse("channel.chat_settings.update", event), do: EventSub.ChatEvent.from_event("channel.chat_settings.update", event)
def parse("channel.chat.user_message_hold", event), do: EventSub.ChatEvent.from_event("channel.chat.user_message_hold", event)
def parse("channel.chat.user_message_update", event), do: EventSub.ChatEvent.from_event("channel.chat.user_message_update", event)
# Subscriptions
def parse("channel.subscribe", event), do: EventSub.Subscription.from_event("channel.subscribe", event)
def parse("channel.subscription.end", event), do: EventSub.Subscription.from_event("channel.subscription.end", event)
def parse("channel.subscription.gift", event), do: EventSub.Subscription.from_event("channel.subscription.gift", event)
def parse("channel.subscription.message", event), do: EventSub.Subscription.from_event("channel.subscription.message", event)
# Channel Points
def parse("channel.channel_points_custom_reward.add", event), do: EventSub.ChannelPoints.from_event("channel.channel_points_custom_reward.add", event)
def parse("channel.channel_points_custom_reward.update", event), do: EventSub.ChannelPoints.from_event("channel.channel_points_custom_reward.update", event)
def parse("channel.channel_points_custom_reward.remove", event), do: EventSub.ChannelPoints.from_event("channel.channel_points_custom_reward.remove", event)
def parse("channel.channel_points_custom_reward_redemption.add", event), do: EventSub.ChannelPoints.from_event("channel.channel_points_custom_reward_redemption.add", event)
def parse("channel.channel_points_custom_reward_redemption.update", event), do: EventSub.ChannelPoints.from_event("channel.channel_points_custom_reward_redemption.update", event)
def parse("channel.channel_points_automatic_reward_redemption.add", event), do: EventSub.ChannelPoints.from_event("channel.channel_points_automatic_reward_redemption.add", event)
# Polls
def parse("channel.poll.begin", event), do: EventSub.Poll.from_event("channel.poll.begin", event)
def parse("channel.poll.progress", event), do: EventSub.Poll.from_event("channel.poll.progress", event)
def parse("channel.poll.end", event), do: EventSub.Poll.from_event("channel.poll.end", event)
# Predictions
def parse("channel.prediction.begin", event), do: EventSub.Prediction.from_event("channel.prediction.begin", event)
def parse("channel.prediction.progress", event), do: EventSub.Prediction.from_event("channel.prediction.progress", event)
def parse("channel.prediction.lock", event), do: EventSub.Prediction.from_event("channel.prediction.lock", event)
def parse("channel.prediction.end", event), do: EventSub.Prediction.from_event("channel.prediction.end", event)
# Goals
def parse("channel.goal.begin", event), do: EventSub.Goal.from_event("channel.goal.begin", event)
def parse("channel.goal.progress", event), do: EventSub.Goal.from_event("channel.goal.progress", event)
def parse("channel.goal.end", event), do: EventSub.Goal.from_event("channel.goal.end", event)
# Hype Train
def parse("channel.hype_train.begin", event), do: EventSub.HypeTrain.from_event("channel.hype_train.begin", event)
def parse("channel.hype_train.progress", event), do: EventSub.HypeTrain.from_event("channel.hype_train.progress", event)
def parse("channel.hype_train.end", event), do: EventSub.HypeTrain.from_event("channel.hype_train.end", event)
# Charity
def parse("channel.charity_campaign.donate", event), do: EventSub.Charity.from_event("channel.charity_campaign.donate", event)
def parse("channel.charity_campaign.start", event), do: EventSub.Charity.from_event("channel.charity_campaign.start", event)
def parse("channel.charity_campaign.progress", event), do: EventSub.Charity.from_event("channel.charity_campaign.progress", event)
def parse("channel.charity_campaign.stop", event), do: EventSub.Charity.from_event("channel.charity_campaign.stop", event)
# Moderation
def parse("channel.moderate", event), do: EventSub.Moderation.from_event("channel.moderate", event)
def parse("channel.moderator.add", event), do: EventSub.Moderation.from_event("channel.moderator.add", event)
def parse("channel.moderator.remove", event), do: EventSub.Moderation.from_event("channel.moderator.remove", event)
def parse("channel.vip.add", event), do: EventSub.Moderation.from_event("channel.vip.add", event)
def parse("channel.vip.remove", event), do: EventSub.Moderation.from_event("channel.vip.remove", event)
def parse("channel.warning.send", event), do: EventSub.Moderation.from_event("channel.warning.send", event)
def parse("channel.warning.acknowledge", event), do: EventSub.Moderation.from_event("channel.warning.acknowledge", event)
def parse("channel.unban_request.create", event), do: EventSub.Moderation.from_event("channel.unban_request.create", event)
def parse("channel.unban_request.resolve", event), do: EventSub.Moderation.from_event("channel.unban_request.resolve", event)
def parse("channel.shield_mode.begin", event), do: EventSub.Moderation.from_event("channel.shield_mode.begin", event)
def parse("channel.shield_mode.end", event), do: EventSub.Moderation.from_event("channel.shield_mode.end", event)
def parse("channel.suspicious_user.message", event), do: EventSub.Moderation.from_event("channel.suspicious_user.message", event)
def parse("channel.suspicious_user.update", event), do: EventSub.Moderation.from_event("channel.suspicious_user.update", event)
def parse("channel.shared_chat.begin", event), do: EventSub.Moderation.from_event("channel.shared_chat.begin", event)
def parse("channel.shared_chat.update", event), do: EventSub.Moderation.from_event("channel.shared_chat.update", event)
def parse("channel.shared_chat.end", event), do: EventSub.Moderation.from_event("channel.shared_chat.end", event)
# Shoutouts
def parse("channel.shoutout.create", event), do: EventSub.Shoutout.from_event("channel.shoutout.create", event)
def parse("channel.shoutout.receive", event), do: EventSub.Shoutout.from_event("channel.shoutout.receive", event)
# Guest Star
def parse("channel.guest_star_session.begin", event), do: EventSub.GuestStar.from_event("channel.guest_star_session.begin", event)
def parse("channel.guest_star_session.end", event), do: EventSub.GuestStar.from_event("channel.guest_star_session.end", event)
def parse("channel.guest_star_guest.update", event), do: EventSub.GuestStar.from_event("channel.guest_star_guest.update", event)
def parse("channel.guest_star_settings.update", event), do: EventSub.GuestStar.from_event("channel.guest_star_settings.update", event)
# ── Stream ───────────────────────────────────────────────────────────────
def parse("stream.online", event), do: EventSub.StreamEvent.from_event("stream.online", event)
def parse("stream.offline", event), do: EventSub.StreamEvent.from_event("stream.offline", event)
# ── User ─────────────────────────────────────────────────────────────────
def parse("user.update", event), do: EventSub.UserEvent.from_event("user.update", event)
def parse("user.authorization.grant", event), do: EventSub.UserEvent.from_event("user.authorization.grant", event)
def parse("user.authorization.revoke", event), do: EventSub.UserEvent.from_event("user.authorization.revoke", event)
def parse("user.whisper.message", event), do: EventSub.UserEvent.from_event("user.whisper.message", event)
# ── Automod ──────────────────────────────────────────────────────────────
def parse("automod.message.hold", event), do: EventSub.Automod.from_event("automod.message.hold", event)
def parse("automod.message.update", event), do: EventSub.Automod.from_event("automod.message.update", event)
def parse("automod.settings.update", event), do: EventSub.Automod.from_event("automod.settings.update", event)
def parse("automod.terms.update", event), do: EventSub.Automod.from_event("automod.terms.update", event)
# ── Drop ─────────────────────────────────────────────────────────────────
def parse("drop.entitlement.grant", event), do: EventSub.Drop.from_event("drop.entitlement.grant", event)
# ── Extension ────────────────────────────────────────────────────────────
def parse("extension.bits_transaction.create", event), do: EventSub.Extension.from_event("extension.bits_transaction.create", event)
# ── Conduit ──────────────────────────────────────────────────────────────
def parse("conduit.shard.disabled", event), do: EventSub.ConduitEvent.from_event("conduit.shard.disabled", event)
# ── Fallback ─────────────────────────────────────────────────────────────
def parse(type, event), do: %Unknown{type: type, data: event}
end