Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content Classification Label updates #253

Merged
merged 5 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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