Skip to content

Commit

Permalink
get rewards redemption subscriptions working
Browse files Browse the repository at this point in the history
  • Loading branch information
dmmulroy committed Apr 16, 2024
1 parent e829117 commit 0f361be
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 63 deletions.
2 changes: 1 addition & 1 deletion manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

packages = [
{ name = "birl", version = "1.6.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "ranger"], otp_app = "birl", source = "hex", outer_checksum = "976CFF85D34D50F7775896615A71745FBE0C325E50399787088F941B539A0497" },
{ name = "dot_env", version = "0.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "simplifile"], otp_app = "dot_env", source = "hex", outer_checksum = "18F51CAFE99F6E3F2B10CF5DBACE63505DB6DC1FA69AFA0105756ACC8201C3A2" },
{ name = "dot_env", version = "0.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib", "simplifile"], otp_app = "dot_env", source = "hex", outer_checksum = "AF5C972D6129F67AF3BB00134AB2808D37111A8D61686CFA86F3ADF652548982" },
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
{ name = "gleam_bitwise", version = "1.3.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_bitwise", source = "hex", outer_checksum = "B36E1D3188D7F594C7FD4F43D0D2CE17561DE896202017548578B16FE1FE9EFC" },
{ name = "gleam_crypto", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "ADD058DEDE8F0341F1ADE3AAC492A224F15700829D9A3A3F9ADF370F875C51B7" },
Expand Down
3 changes: 0 additions & 3 deletions src/glitch/api/eventsub.gleam
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import gleam/dynamic.{type Decoder}
import gleam/io
import gleam/json
import gleam/result
import glitch/api/api_request
Expand Down Expand Up @@ -67,8 +66,6 @@ pub fn create_eventsub_subscription(
|> api_request.set_body(send_message_request_to_json(request))
|> api_request.set_path("eventsub/subscriptions")

io.debug(api_req)

use response <- result.try(client.post(client, api_req))

api_response.get_eventsub_data(
Expand Down
65 changes: 30 additions & 35 deletions src/glitch/eventsub/eventsub.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import glitch/api/eventsub.{CreateEventSubSubscriptionRequest}
import glitch/eventsub/websocket_message.{type WebSocketMessage}
import glitch/eventsub/websocket_server
import glitch/types/condition.{Condition}
import glitch/types/subscription.{ChannelChatMessage}
import glitch/types/subscription
import glitch/types/transport.{Transport, WebSocket}

pub opaque type EventSub {
Expand Down Expand Up @@ -57,52 +57,47 @@ fn loop(selector, mailbox, handle) {
fn handle(state: EventSub, message: WebSocketMessage) {
case message {
websocket_message.Close -> {
io.println("Closed")
io.debug(message)
}
websocket_message.NotificationMessage(..) -> {
io.println("NotificationMessage")
io.debug(message)
}
websocket_message.SessionKeepaliveMessage(..) -> {
io.println("SessionKeepaliveMessage")
io.debug(message)
}
websocket_message.UnhandledMessage(_) -> {
io.println("UnhandledMessage")
io.debug(message)
}
websocket_message.WelcomeMessage(_, payload) -> {
// let resp =
// eventsub.create_eventsub_subscription(
// state.client,
// CreateEventSubSubscriptionRequest(
// ChannelChatMessage,
// "1",
// Condition(
// Some("209286766"),
// None,
// None,
// None,
// None,
// Some(client.client_id(state.client)),
// None,
// Some("209286766"),
// ),
// Transport(
// WebSocket,
// None,
// None,
// Some(payload.session.id),
// None,
// None,
// None,
// ),
// ),
// )
// let _ = io.debug(resp)
let assert Ok(_) =
eventsub.create_eventsub_subscription(
state.client,
CreateEventSubSubscriptionRequest(
subscription.ChannelChatMessage,
"1",
Condition(
Some("209286766"),
None,
None,
None,
None,
Some(client.client_id(state.client)),
None,
Some("209286766"),
),
Transport(
WebSocket,
None,
None,
Some(payload.session.id),
None,
None,
None,
),
),
)

let resp =
let assert Ok(_) =
eventsub.create_eventsub_subscription(
state.client,
CreateEventSubSubscriptionRequest(
Expand All @@ -129,7 +124,7 @@ fn handle(state: EventSub, message: WebSocketMessage) {
),
),
)
let _ = io.debug(resp)

message
}
}
Expand Down
156 changes: 136 additions & 20 deletions src/glitch/eventsub/websocket_message.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ fn welcome_message_decoder() -> Decoder(WebSocketMessage) {
)
}

// TODO: Figure out how to handle "strict", i.e. not allowing additional filds
fn session_keepalive_message_decoder() -> Decoder(WebSocketMessage) {
dynamic.decode1(
SessionKeepaliveMessage,
Expand Down Expand Up @@ -214,45 +215,170 @@ fn notification_message_payload_decoder() -> Decoder(NotificationMessagePayload)

pub type Event {
ChannelChatMessageEvent(
badges: List(Badge),
broadcaster_user_id: String,
broadcaster_user_login: String,
broadcaster_user_name: String,
chatter_user_id: String,
chatter_user_login: String,
chatter_user_name: String,
message_id: String,
message: ChannelChatMessage,
color: String,
badges: List(Badge),
message: ChannelChatMessage,
message_id: String,
message_type: ChannelChatMessageType,
cheer: Option(Cheer),
reply: Option(ChannelChatMessageReply),
channel_points_custom_reward_id: Option(String),
reply: Option(ChannelChatMessageReply),
)
ChannelPointsCustomRewardRedemptionAddEvent(
id: String,
broadcaster_user_id: String,
broadcaster_user_login: String,
broadcaster_user_name: String,
user_id: String,
user_login: String,
user_name: String,
user_input: String,
status: RewardStatus,
reward: Reward,
redeemed_at: String,
)
}

pub type Reward {
Reward(id: String, title: String, cost: Int, prompt: String)
}

pub fn reward_decoder() -> Decoder(Reward) {
dynamic.decode4(
Reward,
dynamic.field("id", dynamic.string),
dynamic.field("title", dynamic.string),
dynamic.field("cost", dynamic.int),
dynamic.field("prompt", dynamic.string),
)
}

pub type RewardStatus {
Canceled
Fulfilled
Unfulfilled
Unknown
}

pub fn rewards_status_to_string(reward_status: RewardStatus) -> String {
case reward_status {
Canceled -> "canceled"
Fulfilled -> "fulfilled"
Unfulfilled -> "unfulfilled"
Unknown -> "unknown"
}
}

pub fn reward_status_from_string(string: String) -> Result(RewardStatus, Nil) {
case string {
"canceled" -> Ok(Canceled)
"fulfilled" -> Ok(Fulfilled)
"unfulfilled" -> Ok(Unfulfilled)
"unknown" -> Ok(Unknown)
_ -> Error(Nil)
}
}

pub fn reward_status_decoder() -> Decoder(RewardStatus) {
fn(data: Dynamic) {
use string <- result.try(dynamic.string(data))

string
|> reward_status_from_string
|> result.replace_error([
dynamic.DecodeError(
expected: "RewardsStatus",
found: "String(" <> string <> ")",
path: [],
),
])
}
}

pub type Image {
Image(url_1x: Uri, url_2x: Uri, url_3x: Uri)
}

pub fn image_decoder() -> Decoder(Image) {
dynamic.decode3(
Image,
dynamic.field("url_1x", dynamic_ext.uri),
dynamic.field("url_2x", dynamic_ext.uri),
dynamic.field("url_3x", dynamic_ext.uri),
)
}

pub type MaxPerStream {
MaxPerStream(is_enabled: Bool, value: Int)
}

pub fn max_per_stream_decoder() -> Decoder(MaxPerStream) {
dynamic.decode2(
MaxPerStream,
dynamic.field("is_enabled", dynamic.bool),
dynamic.field("value", dynamic.int),
)
}

pub type Cooldown {
Cooldown(is_enabled: Bool, seconds: Int)
}

pub fn cooldown_decoder() -> Decoder(Cooldown) {
dynamic.decode2(
Cooldown,
dynamic.field("is_enabled", dynamic.bool),
dynamic.field("second", dynamic.int),
)
RewardEvent(id: String, title: String, cost: Int, prompt: String)
}

fn event_decoder() -> Decoder(Event) {
dynamic.any([channel_chat_messsage_event_decoder(), reward_event_decoder()])
dynamic.any([
channel_chat_messsage_event_decoder(),
channel_points_custom_reward_redemption_add_event_decoder(),
])
}

fn channel_chat_messsage_event_decoder() -> Decoder(Event) {
dynamic_ext.decode14(
ChannelChatMessageEvent,
dynamic.field("badges", dynamic.list(of: badge_decoder())),
dynamic.field("broadcaster_user_id", dynamic.string),
dynamic.field("broadcaster_user_login", dynamic.string),
dynamic.field("broadcaster_user_name", dynamic.string),
dynamic.field("chatter_user_id", dynamic.string),
dynamic.field("chatter_user_login", dynamic.string),
dynamic.field("chatter_user_name", dynamic.string),
dynamic.field("message_id", dynamic.string),
dynamic.field("message", channel_chat_message_decoder()),
dynamic.field("color", dynamic.string),
dynamic.field("badges", dynamic.list(of: badge_decoder())),
dynamic.field("message", channel_chat_message_decoder()),
dynamic.field("message_id", dynamic.string),
dynamic.field("message_type", channel_chat_messsage_type_decoder()),
dynamic.optional_field("cheer", cheer_decoder()),
dynamic.optional_field("reply", channel_chat_message_reply_decoder()),
dynamic.optional_field("channel_points_custom_reward_id", dynamic.string),
dynamic.optional_field("reply", channel_chat_message_reply_decoder()),
)
}

fn channel_points_custom_reward_redemption_add_event_decoder() -> Decoder(Event) {
dynamic_ext.decode11(
ChannelPointsCustomRewardRedemptionAddEvent,
dynamic.field("id", dynamic.string),
dynamic.field("broadcaster_user_id", dynamic.string),
dynamic.field("broadcaster_user_login", dynamic.string),
dynamic.field("broadcaster_user_name", dynamic.string),
dynamic.field("user_id", dynamic.string),
dynamic.field("user_login", dynamic.string),
dynamic.field("user_name", dynamic.string),
dynamic.field("user_input", dynamic.string),
dynamic.field("status", reward_status_decoder()),
dynamic.field("reward", reward_decoder()),
dynamic.field("redeemed_at", dynamic.string),
)
}

Expand Down Expand Up @@ -500,13 +626,3 @@ pub fn mention_decoder() -> Decoder(Mention) {
dynamic.field("user_login", dynamic.string),
)
}

fn reward_event_decoder() -> Decoder(Event) {
dynamic.decode4(
RewardEvent,
dynamic.field("id", dynamic.string),
dynamic.field("title", dynamic.string),
dynamic.field("cost", dynamic.int),
dynamic.field("prompt", dynamic.string),
)
}
5 changes: 1 addition & 4 deletions src/glitch/eventsub/websocket_server.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,15 @@ fn handle_start(state: WebSockerServer) {
loop: fn(message, state, _conn) {
case message {
stratus.Text(message) -> {
io.debug(message)
let decoded_message =
websocket_message.from_json(message)
|> function.tap(io.debug)
|> result.unwrap(UnhandledMessage(message))

process.send(state.mailbox, decoded_message)
actor.continue(state)
}
message -> {
_ -> {
io.println("Received unexpected message:")
io.debug(message)
actor.continue(state)
}
}
Expand Down
Loading

0 comments on commit 0f361be

Please sign in to comment.