Skip to content

Commit ef18678

Browse files
committed
Added --ban-end and --ban-start flags to channel.ban mock event, allowing users to specify ban start and ban end fields (and inheritely, is_permanent)
1 parent 5dd5d07 commit ef18678

File tree

10 files changed

+306
-45
lines changed

10 files changed

+306
-45
lines changed

cmd/events.go

+6
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ var (
4545
clientId string
4646
version string
4747
websocketClient string
48+
banStart string
49+
banEnd string
4850
)
4951

5052
// websocketCmd-specific flags
@@ -158,6 +160,8 @@ func init() {
158160
triggerCmd.Flags().StringVar(&clientId, "client-id", "", "Manually set the Client ID used in revoke, grant, and bits transaction events.")
159161
triggerCmd.Flags().StringVarP(&version, "version", "v", "", "Chooses the EventSub version used for a specific event. Not required for most events.")
160162
triggerCmd.Flags().StringVar(&websocketClient, "session", "", "Defines a specific websocket client/session to forward an event to. Used only with \"websocket\" transport.")
163+
triggerCmd.Flags().StringVar(&banStart, "ban-start", "", "Sets the timestamp a ban started at.")
164+
triggerCmd.Flags().StringVar(&banEnd, "ban-end", "", "Sets the timestamp a ban is intended to end at. If not set, the ban event will appear as permanent. This flag can take a timestamp or relative time (600, 600s, 10d4h12m55s)")
161165

162166
// retrigger flags
163167
retriggerCmd.Flags().StringVarP(&forwardAddress, "forward-address", "F", "", "Forward address for mock event (webhook only).")
@@ -237,6 +241,8 @@ func triggerCmdRun(cmd *cobra.Command, args []string) error {
237241
ClientID: clientId,
238242
Version: version,
239243
WebSocketClient: websocketClient,
244+
BanStartTimestamp: banStart,
245+
BanEndTimestamp: banEnd,
240246
})
241247

242248
if err != nil {

docs/event.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,10 @@ This command can take either the Event or Alias listed as an argument. It is pre
7777
| Flag | Shorthand | Description | Example | Required? (Y/N) |
7878
|---------------------------|-----------|---------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------|-----------------|
7979
| `--anonymous` | `-a` | Denotes if the event is anonymous. Only applies to Gift and Sub events. | `-a` | N |
80+
| `--ban-end` | | Sets the timestamp a ban is intended to end at. If not set, the ban event will appear as permanent. | `--ban-end 10d20h12m35s` | N |
81+
| `--ban-start` | | Sets the timestamp a ban started at. | `--ban-start 2017-04-13T14:34:23` | N |
8082
| `--charity-current-value` | | For charity events, manually set the charity dollar value. | `--charity-current-value 11000` | N |
81-
| `--charity-target-value` | | For charity events, manually set the charity dollar value. | `--charity-current-value 23400` | N |
83+
| `--charity-target-value` | | Only used for "charity-*" events. Manually set the target dollar value for charity events. (default 1500000) | `--charity-target-value 23400` | N |
8284
| `--client-id` | | Manually set the Client ID used for revoke, grant, and bits transactions. | `--client-id 4ofh8m0706jqpholgk00u3xvb4spct` | N |
8385
| `--cost` | `-C` | Amount of subscriptions, bits, or channel points redeemed/used in the event. | `-C 250` | N |
8486
| `--count` | `-c` | Count of events to fire. This can be used to simulate an influx of events. | `-c 100` | N |

internal/events/event.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ type MockEventParameters struct {
2020
ItemID string
2121
ItemName string
2222
Cost int64
23-
IsPermanent bool
2423
Description string
2524
GameID string
2625
Tier string
2726
Timestamp string
2827
CharityCurrentValue int
2928
CharityTargetValue int
3029
ClientID string
30+
BanStartTimestamp string
31+
BanEndTimestamp string
3132
}
3233

3334
type MockEventResponse struct {

internal/events/trigger/trigger_event.go

+4
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ type TriggerParameters struct {
4848
ClientID string
4949
Version string
5050
WebSocketClient string
51+
BanStartTimestamp string
52+
BanEndTimestamp string
5153
}
5254

5355
type TriggerResponse struct {
@@ -135,6 +137,8 @@ https://dev.twitch.tv/docs/eventsub/handling-webhook-events#processing-an-event`
135137
CharityTargetValue: p.CharityTargetValue,
136138
ClientID: p.ClientID,
137139
GiftUser: p.GiftUser,
140+
BanStartTimestamp: p.BanStartTimestamp,
141+
BanEndTimestamp: p.BanEndTimestamp,
138142
}
139143

140144
e, err := types.GetByTriggerAndTransportAndVersion(p.Event, p.Transport, p.Version)

internal/events/types/ban/ban.go

+57-18
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ package ban
44

55
import (
66
"encoding/json"
7+
"regexp"
8+
"strconv"
79
"strings"
810
"time"
911

@@ -17,16 +19,14 @@ var transportsSupported = map[string]bool{
1719
models.TransportWebSocket: true,
1820
}
1921

20-
var triggerSupported = []string{"ban", "unban"}
22+
var triggerSupported = []string{"ban"}
2123

2224
var triggerMapping = map[string]map[string]string{
2325
models.TransportWebhook: {
24-
"ban": "channel.ban",
25-
"unban": "channel.unban",
26+
"ban": "channel.ban",
2627
},
2728
models.TransportWebSocket: {
28-
"ban": "channel.ban",
29-
"unban": "channel.unban",
29+
"ban": "channel.ban",
3030
},
3131
}
3232

@@ -50,25 +50,64 @@ func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEven
5050
ModeratorUserName: "CLIModerator",
5151
}
5252

53-
if params.Trigger == "ban" {
54-
reason := "This is a test event"
55-
bannedAt := params.Timestamp
56-
var endsAt *string = nil
53+
reason := "This is a test event"
5754

58-
if !params.IsPermanent {
59-
// Timeout uses channel.ban as well, which is why IsPermanent exists
60-
// Set ends_at to be 3 minutes in the future
55+
// This event supports --timestamp historically, but is overridden by the newer --ban-start
56+
bannedAt := params.Timestamp
57+
if params.BanStartTimestamp != "" {
58+
bannedAt = params.BanStartTimestamp
59+
}
60+
61+
var endsAt *string = nil
62+
var isPermanent bool
63+
64+
if params.BanEndTimestamp == "" {
65+
// Default to perma ban
66+
isPermanent = true
67+
} else {
68+
r1 := regexp.MustCompile("^[0-9]+$")
69+
r2 := regexp.MustCompile("^(?:(?P<Days>[0-9]+)[dD])?(?:(?P<Hours>[0-9]+)[hH])?(?:(?P<Minutes>[0-9]+)[mM])?(?:(?P<Seconds>[0-9]+)[sS])?$")
70+
71+
if r1.MatchString(params.BanEndTimestamp) {
72+
// Similar format to /timeout <user> <seconds>
73+
// twitch event trigger channel.ban --ban-end=600
74+
seconds, _ := strconv.Atoi(r1.FindAllString(params.BanEndTimestamp, -1)[0])
6175
tNow, _ := time.Parse(time.RFC3339Nano, params.Timestamp)
62-
tLater := tNow.Add(time.Minute * 10).Format(time.RFC3339Nano)
76+
tLater := tNow.Add(time.Duration(seconds) * time.Second).Format(time.RFC3339Nano)
6377
endsAt = &tLater
64-
}
78+
isPermanent = false
79+
80+
} else if r2.MatchString(params.BanEndTimestamp) {
81+
// Relative time specified by shorthands. e.g. 90d10h30m45s
82+
// Can include or exclude any of those, but they have to be in the same order as above
83+
values := r2.FindStringSubmatch(params.BanEndTimestamp)
84+
days, _ := strconv.Atoi(values[r2.SubexpIndex("Days")])
85+
hours, _ := strconv.Atoi(values[r2.SubexpIndex("Hours")])
86+
minutes, _ := strconv.Atoi(values[r2.SubexpIndex("Minutes")])
87+
seconds, _ := strconv.Atoi(values[r2.SubexpIndex("Seconds")])
6588

66-
ban.Reason = &reason
67-
ban.BannedAt = &bannedAt
68-
ban.EndsAt = endsAt
69-
ban.IsPermanent = &params.IsPermanent
89+
tNow, _ := time.Parse(time.RFC3339Nano, params.Timestamp)
90+
tLater := tNow.Add(time.Duration(days*24) * time.Hour).
91+
Add(time.Duration(hours) * time.Hour).
92+
Add(time.Duration(minutes) * time.Minute).
93+
Add(time.Duration(seconds) * time.Second).
94+
Format(time.RFC3339Nano)
95+
endsAt = &tLater
96+
isPermanent = false
97+
98+
} else {
99+
// Timeout with user provided timestamp
100+
endsAt = &params.BanEndTimestamp
101+
isPermanent = false
102+
103+
}
70104
}
71105

106+
ban.Reason = reason
107+
ban.BannedAt = bannedAt
108+
ban.EndsAt = endsAt
109+
ban.IsPermanent = isPermanent
110+
72111
body := *&models.EventsubResponse{
73112
Subscription: models.EventsubSubscription{
74113
ID: params.ID,

internal/events/types/ban/ban_test.go

-21
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,6 @@ func TestEventSubBan(t *testing.T) {
3434

3535
a.Equal(toUser, body.Event.BroadcasterUserID, "Expected to user %v, got %v", toUser, body.Event.BroadcasterUserID)
3636
a.Equal(fromUser, body.Event.UserID, "Expected from user %v, got %v", r.ToUser, body.Event.UserID)
37-
38-
// test for unban
39-
params = events.MockEventParameters{
40-
FromUserID: fromUser,
41-
ToUserID: toUser,
42-
Transport: models.TransportWebhook,
43-
Trigger: "unban",
44-
SubscriptionStatus: "enabled",
45-
}
46-
47-
r, err = Event{}.GenerateEvent(params)
48-
a.Nil(err)
49-
50-
err = json.Unmarshal(r.JSON, &body)
51-
a.Nil(err)
52-
53-
a.Equal(toUser, body.Event.BroadcasterUserID, "Expected to user %v, got %v", toUser, body.Event.BroadcasterUserID)
54-
a.Equal(fromUser, body.Event.UserID, "Expected from user %v, got %v", fromUser, body.Event.UserID)
5537
}
5638

5739
func TestFakeTransport(t *testing.T) {
@@ -75,9 +57,6 @@ func TestValidTrigger(t *testing.T) {
7557
r := Event{}.ValidTrigger("ban")
7658
a.Equal(true, r)
7759

78-
r = Event{}.ValidTrigger("unban")
79-
a.Equal(true, r)
80-
8160
r = Event{}.ValidTrigger("notban")
8261
a.Equal(false, r)
8362
}

internal/events/types/types.go

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
"github.com/twitchdev/twitch-cli/internal/events/types/streamup"
3636
"github.com/twitchdev/twitch-cli/internal/events/types/subscribe"
3737
"github.com/twitchdev/twitch-cli/internal/events/types/subscription_message"
38+
"github.com/twitchdev/twitch-cli/internal/events/types/unban"
3839
user_update "github.com/twitchdev/twitch-cli/internal/events/types/user"
3940
"github.com/twitchdev/twitch-cli/internal/models"
4041
)
@@ -67,6 +68,7 @@ func AllEvents() []events.MockEvent {
6768
streamdown.Event{},
6869
subscribe.Event{},
6970
subscription_message.Event{},
71+
unban.Event{},
7072
user_update.Event{},
7173
}
7274
}

internal/events/types/unban/unban.go

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package unban
4+
5+
import (
6+
"encoding/json"
7+
"strings"
8+
9+
"github.com/twitchdev/twitch-cli/internal/events"
10+
"github.com/twitchdev/twitch-cli/internal/models"
11+
"github.com/twitchdev/twitch-cli/internal/util"
12+
)
13+
14+
var transportsSupported = map[string]bool{
15+
models.TransportWebhook: true,
16+
models.TransportWebSocket: true,
17+
}
18+
19+
var triggerSupported = []string{"unban"}
20+
21+
var triggerMapping = map[string]map[string]string{
22+
models.TransportWebhook: {
23+
"unban": "channel.unban",
24+
},
25+
models.TransportWebSocket: {
26+
"unban": "channel.unban",
27+
},
28+
}
29+
30+
type Event struct{}
31+
32+
func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEventResponse, error) {
33+
var event []byte
34+
var err error
35+
36+
switch params.Transport {
37+
case models.TransportWebhook, models.TransportWebSocket:
38+
ban := models.UnbanEventSubEvent{
39+
UserID: params.FromUserID,
40+
UserLogin: params.FromUserName,
41+
UserName: params.FromUserName,
42+
BroadcasterUserID: params.ToUserID,
43+
BroadcasterUserLogin: params.ToUserName,
44+
BroadcasterUserName: params.ToUserName,
45+
ModeratorUserId: util.RandomUserID(),
46+
ModeratorUserLogin: "CLIModerator",
47+
ModeratorUserName: "CLIModerator",
48+
}
49+
50+
body := *&models.EventsubResponse{
51+
Subscription: models.EventsubSubscription{
52+
ID: params.ID,
53+
Status: params.SubscriptionStatus,
54+
Type: triggerMapping[params.Transport][params.Trigger],
55+
Version: e.SubscriptionVersion(),
56+
Condition: models.EventsubCondition{
57+
BroadcasterUserID: params.ToUserID,
58+
},
59+
Transport: models.EventsubTransport{
60+
Method: "webhook",
61+
Callback: "null",
62+
},
63+
Cost: 0,
64+
CreatedAt: params.Timestamp,
65+
},
66+
Event: ban,
67+
}
68+
69+
event, err = json.Marshal(body)
70+
if err != nil {
71+
return events.MockEventResponse{}, err
72+
}
73+
74+
// Delete event info if Subscription.Status is not set to "enabled"
75+
if !strings.EqualFold(params.SubscriptionStatus, "enabled") {
76+
var i interface{}
77+
if err := json.Unmarshal([]byte(event), &i); err != nil {
78+
return events.MockEventResponse{}, err
79+
}
80+
if m, ok := i.(map[string]interface{}); ok {
81+
delete(m, "event") // Matches JSON key defined in body variable above
82+
}
83+
84+
event, err = json.Marshal(i)
85+
if err != nil {
86+
return events.MockEventResponse{}, err
87+
}
88+
}
89+
default:
90+
return events.MockEventResponse{}, nil
91+
}
92+
93+
return events.MockEventResponse{
94+
ID: params.ID,
95+
JSON: event,
96+
FromUser: params.FromUserID,
97+
ToUser: params.ToUserID,
98+
}, nil
99+
}
100+
101+
func (e Event) ValidTransport(t string) bool {
102+
return transportsSupported[t]
103+
}
104+
105+
func (e Event) ValidTrigger(t string) bool {
106+
for _, ts := range triggerSupported {
107+
if ts == t {
108+
return true
109+
}
110+
}
111+
return false
112+
}
113+
114+
func (e Event) GetTopic(transport string, trigger string) string {
115+
return triggerMapping[transport][trigger]
116+
}
117+
func (e Event) GetAllTopicsByTransport(transport string) []string {
118+
allTopics := []string{}
119+
for _, topic := range triggerMapping[transport] {
120+
allTopics = append(allTopics, topic)
121+
}
122+
return allTopics
123+
}
124+
func (e Event) GetEventSubAlias(t string) string {
125+
// check for aliases
126+
for trigger, topic := range triggerMapping[models.TransportWebhook] {
127+
if topic == t {
128+
return trigger
129+
}
130+
}
131+
return ""
132+
}
133+
134+
func (e Event) SubscriptionVersion() string {
135+
return "1"
136+
}

0 commit comments

Comments
 (0)