diff --git a/.gitignore b/.gitignore index 05722bde..6ca677ff 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ twitch-cli *.html # Editor configs -.vsvode/ +.vscode/ .idea/ # junk files diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 1f29e122..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "cSpell.words": [ - "SPDX" - ] -} \ No newline at end of file diff --git a/docs/event.md b/docs/event.md index 07921cf6..7123382c 100644 --- a/docs/event.md +++ b/docs/event.md @@ -16,30 +16,42 @@ Used to either create or send mock events for use with local webhooks testing. **Args** -| Argument | Description | -|-----------------------|------------------------------------------------------------------------------------------------------------| -| `subscribe` | A standard subscription event. Triggers a basic tier 1 sub. | -| `unsubscribe` | A standard unsubscribe event. Triggers a basic tier 1 sub. | -| `gift` | A gifted subscription event. Triggers a basic tier 1 sub. | -| `cheer` | Only usable with the `eventsub` transport, shows Cheers from chat. | -| `transaction` | Bits in Extensions transactions events. | -| `add-reward` | Channel Points EventSub event for a Custom Reward being added. | -| `update-reward` | Channel Points EventSub event for a Custom Reward being updated. | -| `remove-reward` | Channel Points EventSub event for a Custom Reward being removed. | -| `add-redemption` | Channel Points EventSub event for a redemption being performed. | -| `update-redemption` | Channel Points EventSub event for a redemption being updated. | -| `raid` | Channel Raid event with a random viewer count. | -| `revoke` | User authorization revoke event. Uses local Client as set in `twitch configure` or generates one randomly. | -| `stream-change` | Stream Changed event. | -| `streamup` | Stream online event. | -| `streamdown` | Sstream offline event. | -| `add-moderator` | Channel moderator add event. | -| `remove-moderator` | Channel moderator removal event. | -| `ban` | Channel ban event. | -| `unban` | Channel unban event. | -| `hype-train-begin` | Channel hype train start event. | -| `hype-train-progress` | Channel hype train progress event. | -| `hype-train-end` | Channel hype train end event. | +| Argument | Description | +|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------| +| `add-moderator` | Channel moderator add event. | +| `add-redemption` | Channel Points EventSub event for a redemption being performed. | +| `add-reward` | Channel Points EventSub event for a Custom Reward being added. | +| `ban` | Channel ban event. | +| `channel-gift` | Channel gifting event; not to be confused with the `gift` event. This event is a description of the number of gifts given by a user. | +| `cheer` | Only usable with the `eventsub` transport | +| `drop` | Drops Entitlement event. | +| `gift` | A gifted subscription event. Triggers a basic tier 1 sub. | +| `grant` | Authorization grant event. | +| `hype-train-begin` | Channel hype train start event. | +| `hype-train-end` | Channel hype train end event. | +| `hype-train-progress` | Channel hype train progress event. | +| `poll-begin` | Channel poll begin event. | +| `poll-end` | Channel poll end event. | +| `poll-progress` | Channel poll progress event. | +| `prediction-begin` | Channel prediction begin event. | +| `prediction-end` | Channel prediction end event. | +| `prediction-lock` | Channel prediction lock event. | +| `prediction-progress` | Channel prediction progress event. | +| `raid` | Channel Raid event with a random viewer count. | +| `remove-moderator` | Channel moderator removal event. | +| `remove-reward` | Channel Points EventSub event for a Custom Reward being removed. | +| `revoke` | User authorization revoke event. Uses local Client as set in `twitch configure` or generates one randomly. | +| `stream-change` | Stream Changed event. | +| `streamdown` | Sstream offline event. | +| `streamup` | Stream online event. | +| `subscribe-message` | Subscription Message event. | +| `subscribe` | A standard subscription event. Triggers a basic tier 1 sub. | +| `transaction` | Bits in Extensions transactions events. | +| `unban` | Channel unban event. | +| `unsubscribe` | A standard unsubscribe event. Triggers a basic tier 1 sub. | +| `update-redemption` | Channel Points EventSub event for a redemption being updated. | +| `update-reward` | Channel Points EventSub event for a Custom Reward being updated. | + @@ -48,18 +60,18 @@ Used to either create or send mock events for use with local webhooks testing. | Flag | Shorthand | Description | Example | Required? (Y/N) | |---------------------|-----------|---------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|-----------------| | `--forward-address` | `-F` | Web server address for where to send mock events. | `-F https://localhost:8080` | N | -| `--transport` | `-T` | The method used to send events. Default is eventsub, but can send using websub. | `-T websub` | N | +| `--transport` | `-T` | The method used to send events. Default is `eventsub`, but can send using `websub`. | `-T websub` | N | | `--to-user` | `-t` | Denotes the receiver's TUID of the event, usually the broadcaster. | `-t 44635596` | N | | `--from-user` | `-f` | Denotes the sender's TUID of the event, for example the user that follows another user or the subscriber to a broadcaster. | `-f 44635596` | N | | `--gift-user` | `-g` | Used only for subcription-based events, denotes the gifting user ID | `-g 44635596` | N | | `--secret` | `-s` | Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC. | `-s testsecret` | N | -| `--count` | `-c` | Count of events to fire. This can be used to simulate an influx of subscriptions. | `-c 100` | N | +| `--count` | `-c` | Count of events to fire. This can be used to simulate an influx of events. | `-c 100` | N | | `--anonymous` | `-a` | If the event is anonymous. Only applies to `gift` and `cheer` events. | `-a` | N | | `--status` | `-S` | Status of the event object, currently applies to channel points redemptions. | `-S fulfilled` | N | | `--item-id` | `-i` | Manually set the ID of the event payload item (for example the reward ID in redemption events or game in stream events). | `-i 032e4a6c-4aef-11eb-a9f5-1f703d1f0b92` | N | | `--item-name` | `-n` | Manually set the name of the event payload item (for example the reward ID in redemption events or game name in stream events). | `-n "Science & Technology"` | N | | `--cost` | `-C` | Amount of bits or channel points redeemed/used in the event. | `-C 250` | N | -| `--description` | `-d` | Title the stream should be updated/started with. | `-d Awesome new title!` | N | +| `--description` | `-d` | Title the stream should be updated/started with. Additionally used as the category ID for Drops events. | `-d Awesome new title!` | N | **Examples** @@ -110,30 +122,42 @@ Allows you to test if your webserver responds to subscription requests properly. **Args** -| Argument | Description | -|-----------------------|------------------------------------------------------------------------------------------------------------| -| `subscribe` | A standard subscription event. Triggers a basic tier 1 sub. | -| `unsubscribe` | A standard unsubscribe event. Triggers a basic tier 1 sub. | -| `gift` | A gifted subscription event. Triggers a basic tier 1 sub. | -| `cheer` | Only usable with the `eventsub` transport, shows Cheers from chat. | -| `transaction` | Bits in Extensions transactions events. | -| `add-reward` | Channel Points EventSub event for a Custom Reward being added. | -| `update-reward` | Channel Points EventSub event for a Custom Reward being updated. | -| `remove-reward` | Channel Points EventSub event for a Custom Reward being removed. | -| `add-redemption` | Channel Points EventSub event for a redemption being performed. | -| `update-redemption` | Channel Points EventSub event for a redemption being updated. | -| `raid` | Channel Raid event with a random viewer count. | -| `revoke` | User authorization revoke event. Uses local Client as set in `twitch configure` or generates one randomly. | -| `stream-change` | Stream Changed event. | -| `streamup` | Stream online event. | -| `streamdown` | Sstream offline event. | -| `add-moderator` | Channel moderator add event. | -| `remove-moderator` | Channel moderator removal event. | -| `ban` | Channel ban event. | -| `unban` | Channel unban event. | -| `hype-train-begin` | Channel hype train start event. | -| `hype-train-progress` | Channel hype train progress event. | -| `hype-train-end` | Channel hype train end event. | +| Argument | Description | +|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------| +| `add-moderator` | Channel moderator add event. | +| `add-redemption` | Channel Points EventSub event for a redemption being performed. | +| `add-reward` | Channel Points EventSub event for a Custom Reward being added. | +| `ban` | Channel ban event. | +| `channel-gift` | Channel gifting event; not to be confused with the `gift` event. This event is a description of the number of gifts given by a user. | +| `cheer` | Only usable with the `eventsub` transport | +| `drop` | Drops Entitlement event. | +| `gift` | A gifted subscription event. Triggers a basic tier 1 sub. | +| `grant` | Authorization grant event. | +| `hype-train-begin` | Channel hype train start event. | +| `hype-train-end` | Channel hype train end event. | +| `hype-train-progress` | Channel hype train progress event. | +| `poll-begin` | Channel poll begin event. | +| `poll-end` | Channel poll end event. | +| `poll-progress` | Channel poll progress event. | +| `prediction-begin` | Channel prediction begin event. | +| `prediction-end` | Channel prediction end event. | +| `prediction-lock` | Channel prediction lock event. | +| `prediction-progress` | Channel prediction progress event. | +| `raid` | Channel Raid event with a random viewer count. | +| `remove-moderator` | Channel moderator removal event. | +| `remove-reward` | Channel Points EventSub event for a Custom Reward being removed. | +| `revoke` | User authorization revoke event. Uses local Client as set in `twitch configure` or generates one randomly. | +| `stream-change` | Stream Changed event. | +| `streamdown` | Sstream offline event. | +| `streamup` | Stream online event. | +| `subscribe-message` | Subscription Message event. | +| `subscribe` | A standard subscription event. Triggers a basic tier 1 sub. | +| `transaction` | Bits in Extensions transactions events. | +| `unban` | Channel unban event. | +| `unsubscribe` | A standard unsubscribe event. Triggers a basic tier 1 sub. | +| `update-redemption` | Channel Points EventSub event for a redemption being updated. | +| `update-reward` | Channel Points EventSub event for a Custom Reward being updated. | + **Flags** diff --git a/internal/events/types/authorization_revoke/authorization_revoke.go b/internal/events/types/authorization/authorization.go similarity index 91% rename from internal/events/types/authorization_revoke/authorization_revoke.go rename to internal/events/types/authorization/authorization.go index 09b32f39..8e5d7dbb 100644 --- a/internal/events/types/authorization_revoke/authorization_revoke.go +++ b/internal/events/types/authorization/authorization.go @@ -1,10 +1,9 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package authorization_revoke +package authorization import ( "encoding/json" - "errors" "time" "github.com/spf13/viper" @@ -18,11 +17,12 @@ var transportsSupported = map[string]bool{ models.TransportEventSub: true, } -var triggerSupported = []string{"revoke"} +var triggerSupported = []string{"revoke", "grant"} var triggerMapping = map[string]map[string]string{ models.TransportEventSub: { "revoke": "user.authorization.revoke", + "grant": "user.authorization.grant", }, } @@ -66,8 +66,6 @@ func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEven if err != nil { return events.MockEventResponse{}, err } - case models.TransportWebSub: - return events.MockEventResponse{}, errors.New("Websub is unsupported for authorization revoke events") default: return events.MockEventResponse{}, nil } diff --git a/internal/events/types/authorization_revoke/authorization_revoke_test.go b/internal/events/types/authorization/authorization_test.go similarity index 80% rename from internal/events/types/authorization_revoke/authorization_revoke_test.go rename to internal/events/types/authorization/authorization_test.go index a26a7f11..eeaa4627 100644 --- a/internal/events/types/authorization_revoke/authorization_revoke_test.go +++ b/internal/events/types/authorization/authorization_test.go @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -package authorization_revoke +package authorization import ( "encoding/json" @@ -29,7 +29,7 @@ func TestEventSub(t *testing.T) { r, err := Event{}.GenerateEvent(params) a.Nil(err) - var body models.AuthorizationRevokeEventSubResponse // replace with actual value + var body models.AuthorizationRevokeEventSubResponse err = json.Unmarshal(r.JSON, &body) a.Nil(err) @@ -37,21 +37,6 @@ func TestEventSub(t *testing.T) { a.Equal(body.Event.ClientID, body.Subscription.Condition.ClientID) a.Equal("1234", body.Event.ClientID) } -func TestWebSub(t *testing.T) { - a := test_setup.SetupTestEnv(t) - - params := *&events.MockEventParameters{ - FromUserID: fromUser, - ToUserID: toUser, - Transport: models.TransportWebSub, - Trigger: "revoke", - } - - _, err := Event{}.GenerateEvent(params) - a.NotNil(err) - - // write tests here for websub -} func TestFakeTransport(t *testing.T) { a := test_setup.SetupTestEnv(t) diff --git a/internal/events/types/drop/drop.go b/internal/events/types/drop/drop.go new file mode 100644 index 00000000..4b5aa184 --- /dev/null +++ b/internal/events/types/drop/drop.go @@ -0,0 +1,107 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package drop + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/internal/util" +) + +var transportsSupported = map[string]bool{ + models.TransportWebSub: false, + models.TransportEventSub: true, +} + +var triggerSupported = []string{"drop"} + +var triggerMapping = map[string]map[string]string{ + models.TransportEventSub: { + "drop": "drop.entitlement.grant", + }, +} + +type Event struct{} + +func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEventResponse, error) { + var event []byte + var err error + + if params.ItemID == "" { + params.ItemID = util.RandomGUID() + } + + if params.Description == "" { + params.Description = fmt.Sprintf("%v", util.RandomInt(1000)) + } + switch params.Transport { + case models.TransportEventSub: + body := &models.DropsEntitlementEventSubResponse{ + Subscription: models.EventsubSubscription{ + ID: params.ID, + Status: "enabled", + Type: triggerMapping[params.Transport][params.Trigger], + Version: "1", + Condition: models.EventsubCondition{ + OrganizationID: params.FromUserID, + }, + Transport: models.EventsubTransport{ + Method: "webhook", + Callback: "null", + }, + Cost: 0, + CreatedAt: util.GetTimestamp().Format(time.RFC3339Nano), + }, + Events: []models.DropsEntitlementEventSubEvent{ + { + ID: util.RandomGUID(), + Data: models.DropsEntitlementEventSubEventData{ + OrganizationID: params.FromUserID, + CategoryID: params.Description, + CategoryName: "", + CampaignID: util.RandomGUID(), + EntitlementID: util.RandomGUID(), + BenefitID: params.ItemID, + UserID: params.ToUserID, + UserName: params.ToUserName, + UserLogin: params.ToUserName, + CreatedAt: util.GetTimestamp().Format(time.RFC3339Nano), + }, + }, + }, + } + event, err = json.Marshal(body) + if err != nil { + return events.MockEventResponse{}, err + } + default: + return events.MockEventResponse{}, nil + } + + return events.MockEventResponse{ + ID: params.ID, + JSON: event, + FromUser: params.FromUserID, + ToUser: params.ToUserID, + }, nil +} + +func (e Event) ValidTransport(t string) bool { + return transportsSupported[t] +} + +func (e Event) ValidTrigger(t string) bool { + for _, ts := range triggerSupported { + if ts == t { + return true + } + } + return false +} +func (e Event) GetTopic(transport string, trigger string) string { + return triggerMapping[transport][trigger] +} diff --git a/internal/events/types/drop/drop_test.go b/internal/events/types/drop/drop_test.go new file mode 100644 index 00000000..f0bb768d --- /dev/null +++ b/internal/events/types/drop/drop_test.go @@ -0,0 +1,75 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package drop + +import ( + "encoding/json" + "testing" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/test_setup" +) + +var fromUser = "1234" +var toUser = "4567" + +func TestEventSub(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "drop", + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + + var body models.DropsEntitlementEventSubResponse // replace with actual value + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + + a.Len(body.Events, 1) +} + +func TestFakeTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: "fake_transport", + Trigger: "drop", + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + a.Empty(r) +} +func TestValidTrigger(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTrigger("drop") + a.Equal(true, r) + + r = Event{}.ValidTrigger("notdrop") + a.Equal(false, r) +} + +func TestValidTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTransport(models.TransportEventSub) + a.Equal(true, r) + + r = Event{}.ValidTransport("noteventsub") + a.Equal(false, r) +} +func TestGetTopic(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.GetTopic(models.TransportEventSub, "drop") + a.NotNil(r) +} diff --git a/internal/events/types/gift/channel_gift.go b/internal/events/types/gift/channel_gift.go new file mode 100644 index 00000000..b01d9ee7 --- /dev/null +++ b/internal/events/types/gift/channel_gift.go @@ -0,0 +1,108 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package gift + +import ( + "encoding/json" + "time" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/internal/util" +) + +var transportsSupported = map[string]bool{ + models.TransportWebSub: false, + models.TransportEventSub: true, +} + +var triggerSupported = []string{"channel-gift"} + +var triggerMapping = map[string]map[string]string{ + models.TransportEventSub: { + "channel-gift": "channel.subscription.gift", + }, +} + +type Event struct{} + +func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEventResponse, error) { + var event []byte + var err error + + if params.Cost <= 0 { + params.Cost = 5 + } + + total := int(util.RandomInt(200) + params.Cost) + + if params.IsAnonymous { + params.FromUserID = "274598607" + params.FromUserName = "ananonymousgifter" + } + + switch params.Transport { + case models.TransportEventSub: + body := &models.GiftEventSubResponse{ + Subscription: models.EventsubSubscription{ + ID: params.ID, + Status: "enabled", + Type: triggerMapping[params.Transport][params.Trigger], + Version: "1", + Condition: models.EventsubCondition{ + BroadcasterUserID: params.ToUserID, + }, + Transport: models.EventsubTransport{ + Method: "webhook", + Callback: "null", + }, + Cost: 0, + CreatedAt: util.GetTimestamp().Format(time.RFC3339Nano), + }, + Event: models.GiftEventSubEvent{ + UserID: params.FromUserID, + UserLogin: params.FromUserName, + UserName: params.FromUserName, + BroadcasterUserID: params.ToUserID, + BroadcasterUserLogin: params.ToUserName, + BroadcasterUserName: params.ToUserName, + Tier: "1000", + Total: int(params.Cost), + CumulativeTotal: &total, + IsAnonymous: params.IsAnonymous, + }, + } + if params.IsAnonymous { + body.Event.CumulativeTotal = nil + } + event, err = json.Marshal(body) + if err != nil { + return events.MockEventResponse{}, err + } + default: + return events.MockEventResponse{}, nil + } + + return events.MockEventResponse{ + ID: params.ID, + JSON: event, + FromUser: params.FromUserID, + ToUser: params.ToUserID, + }, nil +} + +func (e Event) ValidTransport(t string) bool { + return transportsSupported[t] +} + +func (e Event) ValidTrigger(t string) bool { + for _, ts := range triggerSupported { + if ts == t { + return true + } + } + return false +} +func (e Event) GetTopic(transport string, trigger string) string { + return triggerMapping[transport][trigger] +} diff --git a/internal/events/types/gift/channel_gift_test.go b/internal/events/types/gift/channel_gift_test.go new file mode 100644 index 00000000..98397bc2 --- /dev/null +++ b/internal/events/types/gift/channel_gift_test.go @@ -0,0 +1,79 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package gift + +import ( + "encoding/json" + "testing" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/test_setup" +) + +var fromUser = "1234" +var toUser = "4567" + +func TestEventSub(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "channel-gift", + Cost: 0, + IsAnonymous: true, + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + + var body models.GiftEventSubResponse // replace with actual value + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + + a.GreaterOrEqual(body.Event.Total, 1) + a.Nil(body.Event.CumulativeTotal) + a.NotEmpty(body.Event.UserID) +} + +func TestFakeTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: "fake_transport", + Trigger: "unsubscribe", + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + a.Empty(r) +} +func TestValidTrigger(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTrigger("channel-gift") + a.Equal(true, r) + + r = Event{}.ValidTrigger("notgift") + a.Equal(false, r) +} + +func TestValidTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTransport(models.TransportEventSub) + a.Equal(true, r) + + r = Event{}.ValidTransport("noteventsub") + a.Equal(false, r) +} +func TestGetTopic(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.GetTopic(models.TransportEventSub, "channel-gift") + a.NotNil(r) +} diff --git a/internal/events/types/poll/poll.go b/internal/events/types/poll/poll.go new file mode 100644 index 00000000..5fa6e84a --- /dev/null +++ b/internal/events/types/poll/poll.go @@ -0,0 +1,132 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package poll + +import ( + "encoding/json" + "fmt" + "time" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/internal/util" +) + +var transportsSupported = map[string]bool{ + models.TransportWebSub: false, + models.TransportEventSub: true, +} + +var triggerSupported = []string{"poll-begin", "poll-progress", "poll-end"} + +var triggerMapping = map[string]map[string]string{ + models.TransportEventSub: { + "poll-begin": "channel.poll.begin", + "poll-progress": "channel.poll.progress", + "poll-end": "channel.poll.end", + }, +} + +type Event struct{} + +func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEventResponse, error) { + var event []byte + var err error + + if params.Description == "" { + params.Description = "Pineapple on pizza?" + } + + switch params.Transport { + case models.TransportEventSub: + choices := []models.PollEventSubEventChoice{} + for i := 1; i < 5; i++ { + c := models.PollEventSubEventChoice{ + ID: util.RandomGUID(), + Title: fmt.Sprintf("Yes but choice %v", i), + } + if params.Trigger != "poll-begin" { + c.BitsVotes = intPointer(int(util.RandomInt(10))) + c.ChannelPointsVotes = intPointer(int(util.RandomInt(10))) + c.Votes = intPointer(*c.BitsVotes + *c.ChannelPointsVotes + int(util.RandomInt(10))) + } + choices = append(choices, c) + } + + body := &models.PollEventSubResponse{ + Subscription: models.EventsubSubscription{ + ID: params.ID, + Status: "enabled", + Type: triggerMapping[params.Transport][params.Trigger], + Version: "1", + Condition: models.EventsubCondition{ + BroadcasterUserID: params.ToUserID, + }, + Transport: models.EventsubTransport{ + Method: "webhook", + Callback: "null", + }, + Cost: 0, + CreatedAt: util.GetTimestamp().Format(time.RFC3339Nano), + }, + Event: models.PollEventSubEvent{ + ID: util.RandomGUID(), + BroadcasterUserID: params.ToUserID, + BroadcasterUserLogin: params.ToUserName, + BroadcasterUserName: params.ToUserName, + Title: params.Description, + Choices: choices, + BitsVoting: models.PollEventSubEventGoodVoting{ + IsEnabled: true, + AmountPerVote: 10, + }, + ChannelPointsVoting: models.PollEventSubEventGoodVoting{ + IsEnabled: true, + AmountPerVote: 500, + }, + StartedAt: util.GetTimestamp().Format(time.RFC3339Nano), + }, + } + + if params.Trigger == "poll-end" { + body.Event.EndedAt = util.GetTimestamp().Add(time.Minute * 15).Format(time.RFC3339Nano) + body.Event.Status = "completed" + } else { + body.Event.EndsAt = util.GetTimestamp().Add(time.Minute * 15).Format(time.RFC3339Nano) + } + + event, err = json.Marshal(body) + if err != nil { + return events.MockEventResponse{}, err + } + default: + return events.MockEventResponse{}, nil + } + + return events.MockEventResponse{ + ID: params.ID, + JSON: event, + FromUser: params.FromUserID, + ToUser: params.ToUserID, + }, nil +} + +func (e Event) ValidTransport(t string) bool { + return transportsSupported[t] +} + +func (e Event) ValidTrigger(t string) bool { + for _, ts := range triggerSupported { + if ts == t { + return true + } + } + return false +} +func (e Event) GetTopic(transport string, trigger string) string { + return triggerMapping[transport][trigger] +} + +func intPointer(i int) *int { + return &i +} diff --git a/internal/events/types/poll/poll_test.go b/internal/events/types/poll/poll_test.go new file mode 100644 index 00000000..c74601e0 --- /dev/null +++ b/internal/events/types/poll/poll_test.go @@ -0,0 +1,107 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package poll + +import ( + "encoding/json" + "testing" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/test_setup" +) + +var fromUser = "1234" +var toUser = "4567" + +func TestEventSub(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "poll-begin", + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + + var body models.PollEventSubResponse + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + a.NotEmpty(body.Event.EndsAt) + a.Empty(body.Event.EndedAt) + + params = *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "poll-progress", + } + + r, err = Event{}.GenerateEvent(params) + a.Nil(err) + + body = models.PollEventSubResponse{} + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + a.NotEmpty(body.Event.EndsAt) + a.Empty(body.Event.EndedAt) + + params = *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "poll-end", + } + + r, err = Event{}.GenerateEvent(params) + a.Nil(err) + + body = models.PollEventSubResponse{} + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + a.Empty(body.Event.EndsAt) + a.NotEmpty(body.Event.EndedAt) +} + +func TestFakeTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: "fake_transport", + Trigger: "poll-begin", + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + a.Empty(r) +} +func TestValidTrigger(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTrigger("poll-begin") + a.Equal(true, r) + + r = Event{}.ValidTrigger("notgift") + a.Equal(false, r) +} + +func TestValidTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTransport(models.TransportEventSub) + a.Equal(true, r) + + r = Event{}.ValidTransport("noteventsub") + a.Equal(false, r) +} +func TestGetTopic(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.GetTopic(models.TransportEventSub, "poll-begin") + a.NotNil(r) +} diff --git a/internal/events/types/prediction/prediction.go b/internal/events/types/prediction/prediction.go new file mode 100644 index 00000000..5c024c7f --- /dev/null +++ b/internal/events/types/prediction/prediction.go @@ -0,0 +1,153 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package prediction + +import ( + "encoding/json" + "time" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/internal/util" +) + +var transportsSupported = map[string]bool{ + models.TransportWebSub: false, + models.TransportEventSub: true, +} + +var triggerSupported = []string{"prediction-begin", "prediction-progress", "prediction-end"} + +var triggerMapping = map[string]map[string]string{ + models.TransportEventSub: { + "prediction-begin": "channel.prediction.begin", + "prediction-progress": "channel.prediction.progress", + "prediction-lock": "channel.prediction.lock", + "prediction-end": "channel.prediction.end", + }, +} + +type Event struct{} + +func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEventResponse, error) { + var event []byte + var err error + if params.Description == "" { + params.Description = "Will the developer finish this program?" + } + + switch params.Transport { + case models.TransportEventSub: + var outcomes []models.PredictionEventSubEventOutcomes + for i := 0; i < 2; i++ { + color := "blue" + title := "yes" + + if i == 1 { + color = "pink" + title = "no" + } + + o := models.PredictionEventSubEventOutcomes{ + ID: util.RandomGUID(), + Title: title, + Color: color, + } + + if params.Trigger != "prediction-begin" { + tp := []models.PredictionEventSubEventTopPredictors{} + + for j := 0; j < int(util.RandomInt(10))+1; j++ { + t := models.PredictionEventSubEventTopPredictors{ + UserID: util.RandomUserID(), + UserLogin: "testLogin", + UserName: "testLogin", + ChannelPointsUsed: int(util.RandomInt(10*1000)) + 100, + } + if params.Trigger == "prediction-lock" || params.Trigger == "prediction-end" { + if i == 0 { + t.ChannelPointsWon = intPointer(t.ChannelPointsUsed * 2) + } else { + t.ChannelPointsWon = intPointer(0) + } + } + tp = append(tp, t) + o.TopPredictors = &tp + } + } + + outcomes = append(outcomes, o) + } + + body := &models.PredictionEventSubResponse{ + Subscription: models.EventsubSubscription{ + ID: params.ID, + Status: "enabled", + Type: triggerMapping[params.Transport][params.Trigger], + Version: "1", + Condition: models.EventsubCondition{ + BroadcasterUserID: params.ToUserID, + }, + Transport: models.EventsubTransport{ + Method: "webhook", + Callback: "null", + }, + Cost: 0, + CreatedAt: util.GetTimestamp().Format(time.RFC3339Nano), + }, + Event: models.PredictionEventSubEvent{ + ID: util.RandomGUID(), + BroadcasterUserID: params.ToUserID, + BroadcasterUserLogin: params.ToUserName, + BroadcasterUserName: params.ToUserName, + Title: params.Description, + Outcomes: outcomes, + StartedAt: util.GetTimestamp().Format(time.RFC3339Nano), + }, + } + + if params.Trigger == "prediction-begin" || params.Trigger == "prediction-progress" { + body.Event.LocksAt = util.GetTimestamp().Add(time.Minute * 10).Format(time.RFC3339Nano) + } else if params.Trigger == "prediction-lock" { + body.Event.LockedAt = util.GetTimestamp().Add(time.Minute * 10).Format(time.RFC3339Nano) + } else if params.Trigger == "prediction-end" { + body.Event.WinningOutcomeID = outcomes[0].ID + body.Event.EndedAt = util.GetTimestamp().Add(time.Minute * 10).Format(time.RFC3339Nano) + body.Event.Status = "resolved" + } + + event, err = json.Marshal(body) + if err != nil { + return events.MockEventResponse{}, err + } + default: + return events.MockEventResponse{}, nil + } + + return events.MockEventResponse{ + ID: params.ID, + JSON: event, + FromUser: params.FromUserID, + ToUser: params.ToUserID, + }, nil +} + +func (e Event) ValidTransport(t string) bool { + return transportsSupported[t] +} + +func (e Event) ValidTrigger(t string) bool { + for _, ts := range triggerSupported { + if ts == t { + return true + } + } + return false +} +func (e Event) GetTopic(transport string, trigger string) string { + return triggerMapping[transport][trigger] +} + +func intPointer(i int) *int { + return &i +} diff --git a/internal/events/types/prediction/prediction_test.go b/internal/events/types/prediction/prediction_test.go new file mode 100644 index 00000000..a66c0897 --- /dev/null +++ b/internal/events/types/prediction/prediction_test.go @@ -0,0 +1,115 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package prediction + +import ( + "encoding/json" + "testing" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/test_setup" +) + +var fromUser = "1234" +var toUser = "4567" + +func TestEventSub(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "prediction-begin", + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + + var body models.PredictionEventSubResponse + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + + params = *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "prediction-progress", + } + + r, err = Event{}.GenerateEvent(params) + a.Nil(err) + + body = models.PredictionEventSubResponse{} + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + + params = *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "prediction-lock", + } + + r, err = Event{}.GenerateEvent(params) + a.Nil(err) + + body = models.PredictionEventSubResponse{} + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + + params = *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "prediction-end", + } + + r, err = Event{}.GenerateEvent(params) + a.Nil(err) + + body = models.PredictionEventSubResponse{} + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) +} + +func TestFakeTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: "fake_transport", + Trigger: "unsubscribe", + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + a.Empty(r) +} +func TestValidTrigger(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTrigger("prediction-begin") + a.Equal(true, r) + + r = Event{}.ValidTrigger("notgift") + a.Equal(false, r) +} + +func TestValidTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTransport(models.TransportEventSub) + a.Equal(true, r) + + r = Event{}.ValidTransport("noteventsub") + a.Equal(false, r) +} +func TestGetTopic(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.GetTopic(models.TransportEventSub, "prediction-begin") + a.NotNil(r) +} diff --git a/internal/events/types/subscribe/sub_event.go b/internal/events/types/subscribe/sub_event.go index 8f783f93..3fd79b6f 100644 --- a/internal/events/types/subscribe/sub_event.go +++ b/internal/events/types/subscribe/sub_event.go @@ -16,18 +16,20 @@ var transportsSupported = map[string]bool{ models.TransportEventSub: true, } -var triggerSupported = []string{"subscribe", "gift", "unsubscribe"} +var triggerSupported = []string{"subscribe", "gift", "unsubscribe", "subscribe-end"} var triggerMapping = map[string]map[string]string{ models.TransportWebSub: { - "subscribe": "subscriptions.subscribe", - "unsubscribe": "subscriptions.unsubscribe", - "gift": "subscriptions.subscribe", + "subscribe": "subscriptions.subscribe", + "unsubscribe": "subscriptions.unsubscribe", + "gift": "subscriptions.subscribe", + "subscribe-end": "", }, models.TransportEventSub: { - "subscribe": "channel.subscribe", - "unsubscribe": "channel.unsubscribe", - "gift": "channel.subscribe", + "subscribe": "channel.subscribe", + "unsubscribe": "channel.unsubscribe", + "gift": "channel.subscribe", + "subscribe-end": "channel.subscription.end", }, } diff --git a/internal/events/types/subscription_message/subscription_message.go b/internal/events/types/subscription_message/subscription_message.go new file mode 100644 index 00000000..6bcac31d --- /dev/null +++ b/internal/events/types/subscription_message/subscription_message.go @@ -0,0 +1,113 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package subscription_message + +import ( + "encoding/json" + "time" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/internal/util" +) + +var transportsSupported = map[string]bool{ + models.TransportWebSub: false, + models.TransportEventSub: true, +} + +var triggerSupported = []string{"subscribe-message"} + +var triggerMapping = map[string]map[string]string{ + models.TransportEventSub: { + "subscribe-message": "channel.subscription.message", + }, +} + +type Event struct{} + +func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEventResponse, error) { + var event []byte + var err error + + if params.Cost == 0 { + params.Cost = util.RandomInt(120) + 1 + } + + switch params.Transport { + case models.TransportEventSub: + body := &models.SubscribeMessageEventSubResponse{ + Subscription: models.EventsubSubscription{ + ID: params.ID, + Status: "enabled", + Type: triggerMapping[params.Transport][params.Trigger], + Version: "1", + Condition: models.EventsubCondition{ + BroadcasterUserID: params.ToUserID, + }, + Transport: models.EventsubTransport{ + Method: "webhook", + Callback: "null", + }, + Cost: 0, + CreatedAt: util.GetTimestamp().Format(time.RFC3339Nano), + }, + Event: models.SubscribeMessageEventSubEvent{ + UserID: params.FromUserID, + UserLogin: params.FromUserName, + UserName: params.FromUserName, + BroadcasterUserID: params.ToUserID, + BroadcasterUserLogin: params.ToUserName, + BroadcasterUserName: params.ToUserName, + Tier: "1000", + Message: models.SubscribeMessageEventSubMessage{ + Text: "Hello from the Twitch CLI! twitchdevLeek", + Emotes: []models.SubscribeMessageEventSubMessageEmote{ + { + Begin: 26, + End: 39, + ID: "304456816", + }, + }, + }, + CumulativeMonths: int(params.Cost) + int(util.RandomInt(10)), + DurationMonths: 1, + }, + } + + if !params.IsAnonymous { + streak := int(params.Cost) + body.Event.StreakMonths = &streak + } + event, err = json.Marshal(body) + if err != nil { + return events.MockEventResponse{}, err + } + + default: + return events.MockEventResponse{}, nil + } + + return events.MockEventResponse{ + ID: params.ID, + JSON: event, + FromUser: params.FromUserID, + ToUser: params.ToUserID, + }, nil +} + +func (e Event) ValidTransport(t string) bool { + return transportsSupported[t] +} + +func (e Event) ValidTrigger(t string) bool { + for _, ts := range triggerSupported { + if ts == t { + return true + } + } + return false +} +func (e Event) GetTopic(transport string, trigger string) string { + return triggerMapping[transport][trigger] +} diff --git a/internal/events/types/subscription_message/subscription_message_test.go b/internal/events/types/subscription_message/subscription_message_test.go new file mode 100644 index 00000000..7f30b2c4 --- /dev/null +++ b/internal/events/types/subscription_message/subscription_message_test.go @@ -0,0 +1,94 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package subscription_message + +import ( + "encoding/json" + "testing" + + "github.com/twitchdev/twitch-cli/internal/events" + "github.com/twitchdev/twitch-cli/internal/models" + "github.com/twitchdev/twitch-cli/test_setup" +) + +var fromUser = "1234" +var toUser = "4567" + +func TestEventSub(t *testing.T) { + a := test_setup.SetupTestEnv(t) + ten := 10 + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "subscribe-message", + Cost: int64(ten), + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + + var body models.SubscribeMessageEventSubResponse // replace with actual value + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + a.Equal(&ten, body.Event.StreakMonths) + a.GreaterOrEqual(body.Event.CumulativeMonths, 10) + + params = *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: models.TransportEventSub, + Trigger: "subscribe-message", + Cost: int64(ten), + IsAnonymous: true, + } + + r, err = Event{}.GenerateEvent(params) + a.Nil(err) + + err = json.Unmarshal(r.JSON, &body) + a.Nil(err) + a.Nil(body.Event.StreakMonths) + a.GreaterOrEqual(body.Event.CumulativeMonths, 10) +} + +func TestFakeTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + params := *&events.MockEventParameters{ + FromUserID: fromUser, + ToUserID: toUser, + Transport: "fake_transport", + Trigger: "subscribe-message", + } + + r, err := Event{}.GenerateEvent(params) + a.Nil(err) + a.Empty(r) +} +func TestValidTrigger(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTrigger("subscribe-message") + a.Equal(true, r) + + r = Event{}.ValidTrigger("notmessage") + a.Equal(false, r) +} + +func TestValidTransport(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.ValidTransport(models.TransportEventSub) + a.Equal(true, r) + + r = Event{}.ValidTransport("noteventsub") + a.Equal(false, r) +} +func TestGetTopic(t *testing.T) { + a := test_setup.SetupTestEnv(t) + + r := Event{}.GetTopic(models.TransportEventSub, "subscribe-message") + a.NotNil(r) +} diff --git a/internal/events/types/types.go b/internal/events/types/types.go index 49676ad4..ff61a213 100644 --- a/internal/events/types/types.go +++ b/internal/events/types/types.go @@ -6,38 +6,48 @@ import ( "errors" "github.com/twitchdev/twitch-cli/internal/events" - "github.com/twitchdev/twitch-cli/internal/events/types/authorization_revoke" + "github.com/twitchdev/twitch-cli/internal/events/types/authorization" + "github.com/twitchdev/twitch-cli/internal/events/types/ban" "github.com/twitchdev/twitch-cli/internal/events/types/channel_points_redemption" "github.com/twitchdev/twitch-cli/internal/events/types/channel_points_reward" "github.com/twitchdev/twitch-cli/internal/events/types/cheer" + "github.com/twitchdev/twitch-cli/internal/events/types/drop" "github.com/twitchdev/twitch-cli/internal/events/types/extension_transaction" "github.com/twitchdev/twitch-cli/internal/events/types/follow" + "github.com/twitchdev/twitch-cli/internal/events/types/gift" "github.com/twitchdev/twitch-cli/internal/events/types/hype_train" "github.com/twitchdev/twitch-cli/internal/events/types/moderator_change" + "github.com/twitchdev/twitch-cli/internal/events/types/poll" + "github.com/twitchdev/twitch-cli/internal/events/types/prediction" "github.com/twitchdev/twitch-cli/internal/events/types/raid" "github.com/twitchdev/twitch-cli/internal/events/types/stream_change" "github.com/twitchdev/twitch-cli/internal/events/types/streamdown" "github.com/twitchdev/twitch-cli/internal/events/types/streamup" "github.com/twitchdev/twitch-cli/internal/events/types/subscribe" - "github.com/twitchdev/twitch-cli/internal/events/types/ban" + "github.com/twitchdev/twitch-cli/internal/events/types/subscription_message" ) func All() []events.MockEvent { return []events.MockEvent{ - authorization_revoke.Event{}, + authorization.Event{}, + ban.Event{}, channel_points_redemption.Event{}, channel_points_reward.Event{}, cheer.Event{}, + drop.Event{}, extension_transaction.Event{}, follow.Event{}, + gift.Event{}, hype_train.Event{}, + moderator_change.Event{}, + poll.Event{}, + prediction.Event{}, raid.Event{}, - subscribe.Event{}, stream_change.Event{}, streamup.Event{}, streamdown.Event{}, - moderator_change.Event{}, - ban.Event{}, + subscribe.Event{}, + subscription_message.Event{}, } } diff --git a/internal/models/drops.go b/internal/models/drops.go index 79bd2a04..65d6d93a 100644 --- a/internal/models/drops.go +++ b/internal/models/drops.go @@ -2,17 +2,24 @@ // SPDX-License-Identifier: Apache-2.0 package models -type DropsEntitlementsData struct { - ID string `json:"id"` - BenefitID string `json:"benefit_id"` - Timestamp string `json:"timestamp"` - UserID string `json:"user_id"` - GameID string `json:"game_id"` +type DropsEntitlementEventSubResponse struct { + Subscription EventsubSubscription `json:"subscription"` + Events []DropsEntitlementEventSubEvent `json:"events"` } -type DropsEntitlementsResponse struct { - Pagination struct { - Cursor string `json:"cursor"` - } `json:"pagination"` - Data []DropsEntitlementsData `json:"data"` +type DropsEntitlementEventSubEvent struct { + ID string `json:"id"` + Data DropsEntitlementEventSubEventData `json:"data"` +} +type DropsEntitlementEventSubEventData struct { + EntitlementID string `json:"entitlement_id"` + BenefitID string `json:"benefit_id"` + CampaignID string `json:"campaign_id"` + OrganizationID string `json:"organization_id"` + CreatedAt string `json:"created_at"` + UserID string `json:"user_id"` + UserName string `json:"user_name"` + UserLogin string `json:"user_login"` + CategoryID string `json:"category_id"` + CategoryName string `json:"category_name"` } diff --git a/internal/models/eventsub.go b/internal/models/eventsub.go index 7c88d736..ad30fd60 100644 --- a/internal/models/eventsub.go +++ b/internal/models/eventsub.go @@ -24,6 +24,7 @@ type EventsubCondition struct { FromBroadcasterUserID string `json:"from_broadcaster_user_id,omitempty"` ClientID string `json:"client_id,omitempty"` ExtensionClientID string `json:"extension_client_id,omitempty"` + OrganizationID string `json:"organization_id,omitempty"` } type EventsubResponse struct { diff --git a/internal/models/poll.go b/internal/models/poll.go new file mode 100644 index 00000000..bef9b143 --- /dev/null +++ b/internal/models/poll.go @@ -0,0 +1,36 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package models + +type PollEventSubResponse struct { + Subscription EventsubSubscription `json:"subscription"` + Event PollEventSubEvent `json:"event"` +} + +type PollEventSubEvent struct { + ID string `json:"id"` + BroadcasterUserID string `json:"broadcaster_user_id"` + BroadcasterUserLogin string `json:"broadcaster_user_login"` + BroadcasterUserName string `json:"broadcaster_user_name"` + Title string `json:"title"` + Choices []PollEventSubEventChoice `json:"choices"` + BitsVoting PollEventSubEventGoodVoting `json:"bits_voting"` + ChannelPointsVoting PollEventSubEventGoodVoting `json:"channel_points_voting"` + Status string `json:"status,omitempty"` + StartedAt string `json:"started_at"` + EndsAt string `json:"ends_at,omitempty"` + EndedAt string `json:"ended_at,omitempty"` +} + +type PollEventSubEventChoice struct { + ID string `json:"id"` + Title string `json:"title"` + BitsVotes *int `json:"bits_votes,omitempty"` + ChannelPointsVotes *int `json:"channel_points_votes,omitempty"` + Votes *int `json:"votes,omitempty"` +} + +type PollEventSubEventGoodVoting struct { + IsEnabled bool `json:"is_enabled"` + AmountPerVote int `json:"amount_per_vote"` +} diff --git a/internal/models/prediction.go b/internal/models/prediction.go new file mode 100644 index 00000000..ca9c38e1 --- /dev/null +++ b/internal/models/prediction.go @@ -0,0 +1,40 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +package models + +type PredictionEventSubResponse struct { + Subscription EventsubSubscription `json:"subscription"` + Event PredictionEventSubEvent `json:"event"` +} + +type PredictionEventSubEvent struct { + ID string `json:"id"` + BroadcasterUserID string `json:"broadcaster_user_id"` + BroadcasterUserLogin string `json:"broadcaster_user_login"` + BroadcasterUserName string `json:"broadcaster_user_name"` + Title string `json:"title"` + WinningOutcomeID string `json:"winning_outcome_id,omitempty"` + Outcomes []PredictionEventSubEventOutcomes `json:"outcomes"` + StartedAt string `json:"started_at"` + LocksAt string `json:"locks_at,omitempty"` + LockedAt string `json:"locked_at,omitempty"` + EndedAt string `json:"ended_at,omitempty"` + Status string `json:"status,omitempty"` +} + +type PredictionEventSubEventOutcomes struct { + ID string `json:"id"` + Title string `json:"title"` + Color string `json:"color"` + Users *int `json:"users,omitempty"` + ChannelPoints *int `json:"channel_points,omitempty"` + TopPredictors *[]PredictionEventSubEventTopPredictors `json:"top_predictors,omitempty"` +} + +type PredictionEventSubEventTopPredictors struct { + UserID string `json:"user_id"` + UserLogin string `json:"user_login"` + UserName string `json:"user_name"` + ChannelPointsWon *int `json:"channel_points_won"` + ChannelPointsUsed int `json:"channel_points_used"` +} diff --git a/internal/models/subs.go b/internal/models/subs.go index b5d48a01..c2303314 100644 --- a/internal/models/subs.go +++ b/internal/models/subs.go @@ -41,3 +41,51 @@ type SubEventSubEvent struct { Tier string `json:"tier"` IsGift bool `json:"is_gift"` } + +type GiftEventSubResponse struct { + Subscription EventsubSubscription `json:"subscription"` + Event GiftEventSubEvent `json:"event"` +} + +type GiftEventSubEvent struct { + UserID string `json:"user_id"` + UserLogin string `json:"user_login"` + UserName string `json:"user_name"` + BroadcasterUserID string `json:"broadcaster_user_id"` + BroadcasterUserLogin string `json:"broadcaster_user_login"` + BroadcasterUserName string `json:"broadcaster_user_name"` + Tier string `json:"tier"` + Total int `json:"total"` + IsAnonymous bool `json:"is_anonymous"` + CumulativeTotal *int `json:"cumulative_total"` +} + +type SubscribeMessageEventSubResponse struct { + Subscription EventsubSubscription `json:"subscription"` + Event SubscribeMessageEventSubEvent `json:"event"` +} + +type SubscribeMessageEventSubEvent struct { + UserID string `json:"user_id"` + UserLogin string `json:"user_login"` + UserName string `json:"user_name"` + BroadcasterUserID string `json:"broadcaster_user_id"` + BroadcasterUserLogin string `json:"broadcaster_user_login"` + BroadcasterUserName string `json:"broadcaster_user_name"` + Tier string `json:"tier"` + Message SubscribeMessageEventSubMessage `json:"message"` + CumulativeMonths int `json:"cumulative_months"` + StreakMonths *int `json:"streak_months"` + DurationMonths int `json:"duration_months"` +} + +type SubscribeMessageEventSubMessage struct { + Text string `json:"text"` + Emotes []SubscribeMessageEventSubMessageEmote `json:"emotes"` +} + +type SubscribeMessageEventSubMessageEmote struct { + Begin int `json:"begin"` + End int `json:"end"` + ID string `json:"id"` +}