Skip to content

Commit

Permalink
Merge pull request #253 from twitchdev/ccl-updates
Browse files Browse the repository at this point in the history
Content Classification Label updates
  • Loading branch information
Xemdo authored Jul 13, 2023
2 parents 5a867de + 1f9dbe2 commit 90b03e8
Show file tree
Hide file tree
Showing 13 changed files with 548 additions and 50 deletions.
12 changes: 9 additions & 3 deletions internal/database/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/jmoiron/sqlx"
)

const currentVersion = 4
const currentVersion = 5

type migrateMap struct {
SQL string
Expand All @@ -37,12 +37,18 @@ var migrateSQL = map[int]migrateMap{
4: {
SQL: `
ALTER TABLE categories ADD COLUMN igdb_id text not null default 0; UPDATE categories SET igdb_id = abs(random() % 100000); ALTER TABLE clips ADD COLUMN vod_offset int default 0; UPDATE clips SET vod_offset = abs(random() % 3000); ALTER TABLE drops_entitlements ADD COLUMN last_updated text default '2023-01-01T04:17:53.325Z';
CREATE TABLE chat_settings( broadcaster_id text not null primary key, slow_mode boolean not null default 0, slow_mode_wait_time int not null default 10, follower_mode boolean not null default 0, follower_mode_duration int not null default 60, subscriber_mode boolean not null default 0, emote_mode boolean not null default 0, unique_chat_mode boolean not null default 0, non_moderator_chat_delay boolean not null default 0, non_moderator_chat_delay_duration int not null default 10, shieldmode_is_active boolean not null default 0, shieldmode_moderator_id text not null default '', shieldmode_moderator_login text not null default '', shieldmode_moderator_name text not null default '', shieldmode_last_activated text not null default '' );
CREATE TABLE chat_settings (broadcaster_id text not null primary key, slow_mode boolean not null default 0, slow_mode_wait_time int not null default 10, follower_mode boolean not null default 0, follower_mode_duration int not null default 60, subscriber_mode boolean not null default 0, emote_mode boolean not null default 0, unique_chat_mode boolean not null default 0, non_moderator_chat_delay boolean not null default 0, non_moderator_chat_delay_duration int not null default 10, shieldmode_is_active boolean not null default 0, shieldmode_moderator_id text not null default '', shieldmode_moderator_login text not null default '', shieldmode_moderator_name text not null default '', shieldmode_last_activated text not null default '' );
INSERT INTO chat_settings (broadcaster_id) SELECT id FROM users;
ALTER TABLE users ADD COLUMN chat_color text not null default '#9146FF';
CREATE TABLE vips ( broadcaster_id text not null, user_id text not null, created_at text not null default '', primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) );`,
Message: `Updating database to include API changes since last version. See Twitch CLI changelog for more info.`,
},
5: {
SQL: `
ALTER TABLE users ADD COLUMN branded_content boolean not null default false;
ALTER TABLE users ADD COLUMN content_labels text not null default '';`,
Message: `Updating database to include Content Classification Label field.`,
},
}

func checkAndUpdate(db sqlx.DB) error {
Expand Down Expand Up @@ -81,7 +87,7 @@ func initDatabase(db sqlx.DB) error {
createSQL := `
create table events( id text not null primary key, event text not null, json text not null, from_user text not null, to_user text not null, transport text not null, timestamp text not null);
create table categories( id text not null primary key, category_name text not null, igdb_id text not null );
create table users( id text not null primary key, user_login text not null, display_name text not null, email text not null, user_type text, broadcaster_type text, user_description text, created_at text not null, category_id text, modified_at text, stream_language text not null default 'en', title text not null default '', delay int not null default 0, chat_color text not null default '#9146FF', foreign key (category_id) references categories(id) );
create table users( id text not null primary key, user_login text not null, display_name text not null, email text not null, user_type text, broadcaster_type text, user_description text, created_at text not null, category_id text, modified_at text, stream_language text not null default 'en', title text not null default '', delay int not null default 0, chat_color text not null default '#9146FF', branded_content boolean not null default false, content_labels text not null default '', foreign key (category_id) references categories(id) );
create table follows ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) );
create table blocks ( broadcaster_id text not null, user_id text not null, created_at text not null, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) );
create table bans ( broadcaster_id text not null, user_id text not null, created_at text not null, expires_at text, primary key (broadcaster_id, user_id), foreign key (broadcaster_id) references users(id), foreign key (user_id) references users(id) );
Expand Down
40 changes: 22 additions & 18 deletions internal/database/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,28 @@ import (
)

type User struct {
ID string `db:"id" json:"id" dbs:"u1.id"`
UserLogin string `db:"user_login" json:"login"`
DisplayName string `db:"display_name" json:"display_name"`
Email string `db:"email" json:"email,omitempty"`
UserType string `db:"user_type" json:"type"`
BroadcasterType string `db:"broadcaster_type" json:"broadcaster_type"`
UserDescription string `db:"user_description" json:"description"`
CreatedAt string `db:"created_at" json:"created_at"`
ModifiedAt string `db:"modified_at" json:"-"`
ProfileImageURL string `dbi:"false" json:"profile_image_url" `
OfflineImageURL string `dbi:"false" json:"offline_image_url" `
ViewCount int `dbi:"false" json:"view_count"`
CategoryID sql.NullString `db:"category_id" json:"game_id" dbi:"force"`
CategoryName sql.NullString `db:"category_name" json:"game_name" dbi:"false"`
Title string `db:"title" json:"title"`
Language string `db:"stream_language" json:"stream_language"`
Delay int `db:"delay" json:"delay" dbi:"force"`
ChatColor string `db:"chat_color" json:"-"`
ID string `db:"id" json:"id" dbs:"u1.id"`
UserLogin string `db:"user_login" json:"login"`
DisplayName string `db:"display_name" json:"display_name"`
Email string `db:"email" json:"email,omitempty"`
UserType string `db:"user_type" json:"type"`
BroadcasterType string `db:"broadcaster_type" json:"broadcaster_type"`
UserDescription string `db:"user_description" json:"description"`
CreatedAt string `db:"created_at" json:"created_at"`
ModifiedAt string `db:"modified_at" json:"-"`
ProfileImageURL string `dbi:"false" json:"profile_image_url" `
OfflineImageURL string `dbi:"false" json:"offline_image_url" `
ViewCount int `dbi:"false" json:"view_count"`
CategoryID sql.NullString `db:"category_id" json:"game_id" dbi:"force"`
CategoryName sql.NullString `db:"category_name" json:"game_name" dbi:"false"`
Title string `db:"title" json:"title"`
Language string `db:"stream_language" json:"stream_language"`
Delay int `db:"delay" json:"delay" dbi:"force"`
ChatColor string `db:"chat_color" json:"-"`
IsBrandedContent bool `db:"branded_content" json:"is_branded_content"`

// UnparsedCCLs is a comma seperated array (e.g. "Gambling,ViolentGraphic,ProfanityVulgarity")
UnparsedCCLs string `db:"content_labels" json:"-"`
}

type Follow struct {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package stream_change
package channel_update_v1

import (
"encoding/json"
Expand Down Expand Up @@ -71,7 +71,7 @@ func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEven
StreamLanguage: "en",
StreamCategoryID: params.GameID,
StreamCategoryName: params.ItemName,
IsMature: false,
IsMature: falsePtr(),
},
}
event, err = json.Marshal(body)
Expand Down Expand Up @@ -142,3 +142,8 @@ func (e Event) GetEventSubAlias(t string) string {
func (e Event) SubscriptionVersion() string {
return "1"
}

func falsePtr() *bool {
f := false
return &f
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package stream_change
package channel_update_v1

import (
"encoding/json"
Expand Down
147 changes: 147 additions & 0 deletions internal/events/types/channel_update_v2/channel_update.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package channel_update_v2

import (
"encoding/json"
"strings"

"github.com/twitchdev/twitch-cli/internal/events"
"github.com/twitchdev/twitch-cli/internal/models"
)

var transportsSupported = map[string]bool{
models.TransportWebhook: true,
models.TransportWebSocket: true,
}

var triggerSupported = []string{"stream-change"}

var triggerMapping = map[string]map[string]string{
models.TransportWebhook: {
"stream-change": "channel.update",
},
models.TransportWebSocket: {
"stream-change": "channel.update",
},
}

type Event struct{}

func (e Event) GenerateEvent(params events.MockEventParameters) (events.MockEventResponse, error) {
var event []byte
var err error

if params.Description == "" {
params.Description = "Example title from the CLI!"
}
if params.ItemID == "" && params.GameID == "" {
params.GameID = "509658"
} else if params.ItemID != "" && params.GameID == "" {
params.GameID = params.ItemID
}
if params.ItemName == "" {
params.ItemName = "Just Chatting"
}

switch params.Transport {
case models.TransportWebhook, models.TransportWebSocket:
body := &models.EventsubResponse{
// make the eventsub response (if supported)
Subscription: models.EventsubSubscription{
ID: params.ID,
Status: params.SubscriptionStatus,
Type: triggerMapping[params.Transport][params.Trigger],
Version: e.SubscriptionVersion(),
Condition: models.EventsubCondition{
BroadcasterUserID: params.ToUserID,
},
Transport: models.EventsubTransport{
Method: "webhook",
Callback: "null",
},
Cost: 0,
CreatedAt: params.Timestamp,
},
Event: models.ChannelUpdateEventSubEvent{
BroadcasterUserID: params.ToUserID,
BroadcasterUserLogin: params.ToUserName,
BroadcasterUserName: params.ToUserName,
StreamTitle: params.Description,
StreamLanguage: "en",
StreamCategoryID: params.GameID,
StreamCategoryName: params.ItemName,
ContentClassificationLabels: []string{
"MatureGame",
"ViolentGraphic",
},
},
}
event, err = json.Marshal(body)
if err != nil {
return events.MockEventResponse{}, err
}

// Delete event info if Subscription.Status is not set to "enabled"
if !strings.EqualFold(params.SubscriptionStatus, "enabled") {
var i interface{}
if err := json.Unmarshal([]byte(event), &i); err != nil {
return events.MockEventResponse{}, err
}
if m, ok := i.(map[string]interface{}); ok {
delete(m, "event") // Matches JSON key defined in body variable above
}

event, err = json.Marshal(i)
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 (e Event) GetAllTopicsByTransport(transport string) []string {
allTopics := []string{}
for _, topic := range triggerMapping[transport] {
allTopics = append(allTopics, topic)
}
return allTopics
}
func (e Event) GetEventSubAlias(t string) string {
// check for aliases
for trigger, topic := range triggerMapping[models.TransportWebhook] {
if topic == t {
return trigger
}
}
return ""
}

func (e Event) SubscriptionVersion() string {
return "beta"
}
98 changes: 98 additions & 0 deletions internal/events/types/channel_update_v2/channel_update_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package channel_update_v2

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.TransportWebhook,
Trigger: "stream-change",
SubscriptionStatus: "enabled",
}

r, err := Event{}.GenerateEvent(params)
a.Nil(err)

var body models.ChannelUpdateEventSubResponse
err = json.Unmarshal(r.JSON, &body)
a.Nil(err, "Error unmarshalling JSON")

// write actual tests here (making sure you set appropriate values and the like) for eventsub
a.Equal(toUser, body.Event.BroadcasterUserID, "Expected Stream Channel %v, got %v", toUser, body.Event.BroadcasterUserID)

// test for changing a title
params = events.MockEventParameters{
FromUserID: fromUser,
ToUserID: toUser,
Transport: models.TransportWebhook,
Trigger: "stream_change",
SubscriptionStatus: "enabled",
GameID: "1234",
}

r, err = Event{}.GenerateEvent(params)
a.Nil(err)

err = json.Unmarshal(r.JSON, &body)
a.Nil(err)

a.Equal(toUser, body.Event.BroadcasterUserID, "Expected Stream Channel %v, got %v", toUser, body.Event.BroadcasterUserID)
a.Equal("Example title from the CLI!", body.Event.StreamTitle, "Expected new stream title, got %v", body.Event.StreamTitle)
a.Equal("1234", body.Event.StreamCategoryID)
}

func TestFakeTransport(t *testing.T) {
a := test_setup.SetupTestEnv(t)

params := events.MockEventParameters{
FromUserID: fromUser,
ToUserID: toUser,
Transport: "fake_transport",
Trigger: "stream-change",
SubscriptionStatus: "enabled",
}

r, err := Event{}.GenerateEvent(params)
a.Nil(err)
a.Empty(r)
}
func TestValidTrigger(t *testing.T) {
a := test_setup.SetupTestEnv(t)

r := Event{}.ValidTrigger("stream-change")
a.Equal(true, r)

r = Event{}.ValidTrigger("not_trigger_keyword")
a.Equal(false, r)
}

func TestValidTransport(t *testing.T) {
a := test_setup.SetupTestEnv(t)

r := Event{}.ValidTransport(models.TransportWebhook)
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.TransportWebhook, "stream-change")
a.NotNil(r)
}
6 changes: 4 additions & 2 deletions internal/events/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"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/channel_update_v1"
"github.com/twitchdev/twitch-cli/internal/events/types/channel_update_v2"
"github.com/twitchdev/twitch-cli/internal/events/types/charity"
"github.com/twitchdev/twitch-cli/internal/events/types/cheer"
"github.com/twitchdev/twitch-cli/internal/events/types/drop"
Expand All @@ -29,7 +31,6 @@ import (
"github.com/twitchdev/twitch-cli/internal/events/types/raid"
"github.com/twitchdev/twitch-cli/internal/events/types/shield_mode"
"github.com/twitchdev/twitch-cli/internal/events/types/shoutout"
"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"
Expand Down Expand Up @@ -60,7 +61,8 @@ func AllEvents() []events.MockEvent {
raid.Event{},
shield_mode.Event{},
shoutout.Event{},
stream_change.Event{},
channel_update_v1.Event{},
channel_update_v2.Event{},
streamup.Event{},
streamdown.Event{},
subscribe.Event{},
Expand Down
Loading

0 comments on commit 90b03e8

Please sign in to comment.