mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 03:41:16 +00:00
[eventsub] Add support for suspicious user events
- User status is updated - User sent a message while having sus-user status Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
acf96c31ad
commit
7737d939f4
6 changed files with 180 additions and 43 deletions
|
@ -273,6 +273,29 @@ Fields:
|
||||||
- `plan` - The sub-plan they are using (`1000` = T1, `2000` = T2, `3000` = T3, `Prime`)
|
- `plan` - The sub-plan they are using (`1000` = T1, `2000` = T2, `3000` = T3, `Prime`)
|
||||||
- `username` - The login-name of the user who gifted the subscription
|
- `username` - The login-name of the user who gifted the subscription
|
||||||
|
|
||||||
|
## `sus_user_message`
|
||||||
|
|
||||||
|
A suspicious (monitored / restricted) user sent a message in the given channel
|
||||||
|
|
||||||
|
- `ban_evasion` _string_ - Status of the ban-evasion detection: `unknown`, `possible`, `likely`
|
||||||
|
- `channel` _string_ - The channel in which the event occurred
|
||||||
|
- `message` _string_ - The message the user sent in plain text
|
||||||
|
- `shared_ban_channels` _[]string_ - IDs of channels with shared ban-info in which the user is also banned
|
||||||
|
- `status` _string_ - Restriction status: `active_monitoring`, `restricted`
|
||||||
|
- `user_id` _string_ - ID of the user sending the message
|
||||||
|
- `user_type` _[]string_ - How the user ended being on the naughty-list: `manually_added`, `ban_evader_detector`, or `shared_channel_ban`
|
||||||
|
- `username` _string_ - The login-name of the user sending the message
|
||||||
|
|
||||||
|
## `sus_user_update`
|
||||||
|
|
||||||
|
The status of suspicious user was changed by a moderator
|
||||||
|
|
||||||
|
- `channel` _string_ - The channel in which the event occurred
|
||||||
|
- `moderator` _string_ - The login-name of the moderator changing the status
|
||||||
|
- `status` _string_ - Restriction status: `no_treatment`, `active_monitoring`, `restricted`
|
||||||
|
- `user_id` _string_ - ID of the suspicious user
|
||||||
|
- `username` _string_ - Login-name of the suspicious user
|
||||||
|
|
||||||
## `timeout`
|
## `timeout`
|
||||||
|
|
||||||
Moderator action caused a user to be timed out from chat.
|
Moderator action caused a user to be timed out from chat.
|
||||||
|
|
|
@ -45,6 +45,8 @@ var (
|
||||||
eventTypeSubgift = ptrStr("subgift")
|
eventTypeSubgift = ptrStr("subgift")
|
||||||
eventTypeSubmysterygift = ptrStr("submysterygift")
|
eventTypeSubmysterygift = ptrStr("submysterygift")
|
||||||
eventTypeSub = ptrStr("sub")
|
eventTypeSub = ptrStr("sub")
|
||||||
|
eventTypeSusUserMessage = ptrStr("sus_user_message")
|
||||||
|
eventTypeSusUserUpdate = ptrStr("sus_user_update")
|
||||||
eventTypeTimeout = ptrStr("timeout")
|
eventTypeTimeout = ptrStr("timeout")
|
||||||
eventTypeWatchStreak = ptrStr("watch_streak")
|
eventTypeWatchStreak = ptrStr("watch_streak")
|
||||||
eventTypeWhisper = ptrStr("whisper")
|
eventTypeWhisper = ptrStr("whisper")
|
||||||
|
@ -83,6 +85,8 @@ var (
|
||||||
eventTypeSub,
|
eventTypeSub,
|
||||||
eventTypeSubgift,
|
eventTypeSubgift,
|
||||||
eventTypeSubmysterygift,
|
eventTypeSubmysterygift,
|
||||||
|
eventTypeSusUserMessage,
|
||||||
|
eventTypeSusUserUpdate,
|
||||||
eventTypeTimeout,
|
eventTypeTimeout,
|
||||||
eventTypeWatchStreak,
|
eventTypeWatchStreak,
|
||||||
eventTypeWhisper,
|
eventTypeWhisper,
|
||||||
|
|
|
@ -29,6 +29,8 @@ const (
|
||||||
EventSubEventTypeChannelPollBegin = "channel.poll.begin"
|
EventSubEventTypeChannelPollBegin = "channel.poll.begin"
|
||||||
EventSubEventTypeChannelPollEnd = "channel.poll.end"
|
EventSubEventTypeChannelPollEnd = "channel.poll.end"
|
||||||
EventSubEventTypeChannelPollProgress = "channel.poll.progress"
|
EventSubEventTypeChannelPollProgress = "channel.poll.progress"
|
||||||
|
EventSubEventTypeChannelSuspiciousUserMessage = "channel.suspicious_user.message"
|
||||||
|
EventSubEventTypeChannelSuspiciousUserUpdate = "channel.suspicious_user.update"
|
||||||
EventSubEventTypeStreamOffline = "stream.offline"
|
EventSubEventTypeStreamOffline = "stream.offline"
|
||||||
EventSubEventTypeStreamOnline = "stream.online"
|
EventSubEventTypeStreamOnline = "stream.online"
|
||||||
EventSubEventTypeUserAuthorizationRevoke = "user.authorization.revoke"
|
EventSubEventTypeUserAuthorizationRevoke = "user.authorization.revoke"
|
||||||
|
@ -235,6 +237,53 @@ type (
|
||||||
StartedAt time.Time `json:"started_at"`
|
StartedAt time.Time `json:"started_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EventSubEventSuspiciousUserMessage contains the payload for a
|
||||||
|
// channel.suspicious_user.message
|
||||||
|
EventSubEventSuspiciousUserMessage struct {
|
||||||
|
BroadcasterUserID string `json:"broadcaster_user_id"`
|
||||||
|
BroadcasterUserName string `json:"broadcaster_user_name"`
|
||||||
|
BroadcasterUserLogin string `json:"broadcaster_user_login"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
UserLogin string `json:"user_login"`
|
||||||
|
LowTrustStatus string `json:"low_trust_status"` // Can be the following: "none", "active_monitoring", or "restricted"
|
||||||
|
SharedBanChannelIDs []string `json:"shared_ban_channel_ids"`
|
||||||
|
Types []string `json:"types"` // can be "manual", "ban_evader_detector", or "shared_channel_ban"
|
||||||
|
BanEvasionEvaluation string `json:"ban_evasion_evaluation"` // can be "unknown", "possible", or "likely"
|
||||||
|
Message struct {
|
||||||
|
MessageID string `json:"message_id"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Fragments []struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Text string `json:"text"`
|
||||||
|
Cheermote struct {
|
||||||
|
Prefix string `json:"prefix"`
|
||||||
|
Bits int `json:"bits"`
|
||||||
|
Tier int `json:"tier"`
|
||||||
|
} `json:"Cheermote"`
|
||||||
|
Emote struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
EmoteSetID string `json:"emote_set_id"`
|
||||||
|
} `json:"emote"`
|
||||||
|
} `json:"fragments"`
|
||||||
|
} `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EventSubEventSuspiciousUserUpdated contains the payload for a
|
||||||
|
// channel.suspicious_user.update
|
||||||
|
EventSubEventSuspiciousUserUpdated struct {
|
||||||
|
BroadcasterUserID string `json:"broadcaster_user_id"`
|
||||||
|
BroadcasterUserName string `json:"broadcaster_user_name"`
|
||||||
|
BroadcasterUserLogin string `json:"broadcaster_user_login"`
|
||||||
|
ModeratorUserID string `json:"moderator_user_id"`
|
||||||
|
ModeratorUserName string `json:"moderator_user_name"`
|
||||||
|
ModeratorUserLogin string `json:"moderator_user_login"`
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
UserName string `json:"user_name"`
|
||||||
|
UserLogin string `json:"user_login"`
|
||||||
|
LowTrustStatus string `json:"low_trust_status"` // Can be the following: "none", "active_monitoring", or "restricted"
|
||||||
|
}
|
||||||
|
|
||||||
// EventSubEventUserAuthorizationRevoke contains the payload for an
|
// EventSubEventUserAuthorizationRevoke contains the payload for an
|
||||||
// authorization revoke event
|
// authorization revoke event
|
||||||
EventSubEventUserAuthorizationRevoke struct {
|
EventSubEventUserAuthorizationRevoke struct {
|
||||||
|
|
|
@ -3,35 +3,36 @@ package twitch
|
||||||
// Collection of known API scopes
|
// Collection of known API scopes
|
||||||
const (
|
const (
|
||||||
// API Scopes
|
// API Scopes
|
||||||
ScopeChannelBot = "channel:bot"
|
ScopeChannelBot = "channel:bot"
|
||||||
ScopeChannelEditCommercial = "channel:edit:commercial"
|
ScopeChannelEditCommercial = "channel:edit:commercial"
|
||||||
ScopeChannelManageAds = "channel:manage:ads"
|
ScopeChannelManageAds = "channel:manage:ads"
|
||||||
ScopeChannelManageBroadcast = "channel:manage:broadcast"
|
ScopeChannelManageBroadcast = "channel:manage:broadcast"
|
||||||
ScopeChannelManageModerators = "channel:manage:moderators"
|
ScopeChannelManageModerators = "channel:manage:moderators"
|
||||||
ScopeChannelManagePolls = "channel:manage:polls"
|
ScopeChannelManagePolls = "channel:manage:polls"
|
||||||
ScopeChannelManagePredictions = "channel:manage:predictions"
|
ScopeChannelManagePredictions = "channel:manage:predictions"
|
||||||
ScopeChannelManageRaids = "channel:manage:raids"
|
ScopeChannelManageRaids = "channel:manage:raids"
|
||||||
ScopeChannelManageRedemptions = "channel:manage:redemptions"
|
ScopeChannelManageRedemptions = "channel:manage:redemptions"
|
||||||
ScopeChannelManageVIPS = "channel:manage:vips"
|
ScopeChannelManageVIPS = "channel:manage:vips"
|
||||||
ScopeChannelManageWhispers = "user:manage:whispers"
|
ScopeChannelManageWhispers = "user:manage:whispers"
|
||||||
ScopeChannelReadAds = "channel:read:ads"
|
ScopeChannelReadAds = "channel:read:ads"
|
||||||
ScopeChannelReadHypetrain = "channel:read:hype_train"
|
ScopeChannelReadHypetrain = "channel:read:hype_train"
|
||||||
ScopeChannelReadPolls = "channel:read:polls"
|
ScopeChannelReadPolls = "channel:read:polls"
|
||||||
ScopeChannelReadRedemptions = "channel:read:redemptions"
|
ScopeChannelReadRedemptions = "channel:read:redemptions"
|
||||||
ScopeChannelReadSubscriptions = "channel:read:subscriptions"
|
ScopeChannelReadSubscriptions = "channel:read:subscriptions"
|
||||||
ScopeClipsEdit = "clips:edit"
|
ScopeClipsEdit = "clips:edit"
|
||||||
ScopeModeratorManageAnnoucements = "moderator:manage:announcements"
|
ScopeModeratorManageAnnoucements = "moderator:manage:announcements"
|
||||||
ScopeModeratorManageBannedUsers = "moderator:manage:banned_users"
|
ScopeModeratorManageBannedUsers = "moderator:manage:banned_users"
|
||||||
ScopeModeratorManageChatMessages = "moderator:manage:chat_messages"
|
ScopeModeratorManageChatMessages = "moderator:manage:chat_messages"
|
||||||
ScopeModeratorManageChatSettings = "moderator:manage:chat_settings"
|
ScopeModeratorManageChatSettings = "moderator:manage:chat_settings"
|
||||||
ScopeModeratorManageShieldMode = "moderator:manage:shield_mode"
|
ScopeModeratorManageShieldMode = "moderator:manage:shield_mode"
|
||||||
ScopeModeratorManageShoutouts = "moderator:manage:shoutouts"
|
ScopeModeratorManageShoutouts = "moderator:manage:shoutouts"
|
||||||
ScopeModeratorReadFollowers = "moderator:read:followers"
|
ScopeModeratorReadFollowers = "moderator:read:followers"
|
||||||
ScopeModeratorReadShoutouts = "moderator:read:shoutouts"
|
ScopeModeratorReadShoutouts = "moderator:read:shoutouts"
|
||||||
ScopeUserBot = "user:bot"
|
ScopeModeratorReadSuspiciousUsers = "moderator:read:suspicious_users"
|
||||||
ScopeUserManageChatColor = "user:manage:chat_color"
|
ScopeUserBot = "user:bot"
|
||||||
ScopeUserManageWhispers = "user:manage:whispers"
|
ScopeUserManageChatColor = "user:manage:chat_color"
|
||||||
ScopeUserReadChat = "user:read:chat"
|
ScopeUserManageWhispers = "user:manage:whispers"
|
||||||
|
ScopeUserReadChat = "user:read:chat"
|
||||||
|
|
||||||
// Deprecated v5 scope but used in chat
|
// Deprecated v5 scope but used in chat
|
||||||
ScopeV5ChannelEditor = "channel_editor"
|
ScopeV5ChannelEditor = "channel_editor"
|
||||||
|
|
29
scopes.go
29
scopes.go
|
@ -4,20 +4,21 @@ import "github.com/Luzifer/twitch-bot/v3/pkg/twitch"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
channelExtendedScopes = map[string]string{
|
channelExtendedScopes = map[string]string{
|
||||||
twitch.ScopeChannelEditCommercial: "run commercial",
|
twitch.ScopeChannelEditCommercial: "run commercial",
|
||||||
twitch.ScopeChannelManageBroadcast: "modify category / title",
|
twitch.ScopeChannelManageBroadcast: "modify category / title",
|
||||||
twitch.ScopeChannelManagePolls: "manage polls",
|
twitch.ScopeChannelManagePolls: "manage polls",
|
||||||
twitch.ScopeChannelManagePredictions: "manage predictions",
|
twitch.ScopeChannelManagePredictions: "manage predictions",
|
||||||
twitch.ScopeChannelManageRaids: "start raids",
|
twitch.ScopeChannelManageRaids: "start raids",
|
||||||
twitch.ScopeChannelManageVIPS: "manage VIPs",
|
twitch.ScopeChannelManageVIPS: "manage VIPs",
|
||||||
twitch.ScopeChannelReadAds: "see when an ad-break starts",
|
twitch.ScopeChannelReadAds: "see when an ad-break starts",
|
||||||
twitch.ScopeChannelReadHypetrain: "see Hype-Train events",
|
twitch.ScopeChannelReadHypetrain: "see Hype-Train events",
|
||||||
twitch.ScopeChannelReadRedemptions: "see channel-point redemptions",
|
twitch.ScopeChannelReadRedemptions: "see channel-point redemptions",
|
||||||
twitch.ScopeChannelReadSubscriptions: "see subscribed users / sub count / points",
|
twitch.ScopeChannelReadSubscriptions: "see subscribed users / sub count / points",
|
||||||
twitch.ScopeClipsEdit: "create clips on behalf of this user",
|
twitch.ScopeClipsEdit: "create clips on behalf of this user",
|
||||||
twitch.ScopeModeratorReadFollowers: "see who follows this channel",
|
twitch.ScopeModeratorReadFollowers: "see who follows this channel",
|
||||||
twitch.ScopeModeratorReadShoutouts: "see shoutouts created / received",
|
twitch.ScopeModeratorReadShoutouts: "see shoutouts created / received",
|
||||||
twitch.ScopeUserManageWhispers: "send whispers on behalf of this user",
|
twitch.ScopeModeratorReadSuspiciousUsers: "see users marked suspicious / restricted",
|
||||||
|
twitch.ScopeUserManageWhispers: "send whispers on behalf of this user",
|
||||||
}
|
}
|
||||||
|
|
||||||
botDefaultScopes = []string{
|
botDefaultScopes = []string{
|
||||||
|
|
|
@ -232,6 +232,22 @@ func (t *twitchWatcher) getTopicRegistrations(userID string) []topicRegistration
|
||||||
Hook: t.handleEventSubStreamOnOff(true),
|
Hook: t.handleEventSubStreamOnOff(true),
|
||||||
Optional: true,
|
Optional: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Topic: twitch.EventSubEventTypeChannelSuspiciousUserMessage,
|
||||||
|
Version: twitch.EventSubTopicVersionBeta,
|
||||||
|
Condition: twitch.EventSubCondition{BroadcasterUserID: userID, ModeratorUserID: userID},
|
||||||
|
RequiredScopes: []string{twitch.ScopeModeratorReadSuspiciousUsers},
|
||||||
|
Hook: t.handleEventSubSusUserMessage,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Topic: twitch.EventSubEventTypeChannelSuspiciousUserUpdate,
|
||||||
|
Version: twitch.EventSubTopicVersionBeta,
|
||||||
|
Condition: twitch.EventSubCondition{BroadcasterUserID: userID, ModeratorUserID: userID},
|
||||||
|
RequiredScopes: []string{twitch.ScopeModeratorReadSuspiciousUsers},
|
||||||
|
Hook: t.handleEventSubSusUserUpdate,
|
||||||
|
Optional: true,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -438,6 +454,49 @@ func (t *twitchWatcher) handleEventSubStreamOnOff(isOnline bool) func(json.RawMe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (*twitchWatcher) handleEventSubSusUserMessage(m json.RawMessage) (err error) {
|
||||||
|
var payload twitch.EventSubEventSuspiciousUserMessage
|
||||||
|
if err := json.Unmarshal(m, &payload); err != nil {
|
||||||
|
return errors.Wrap(err, "unmarshalling event")
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := fieldcollection.FieldCollectionFromData(map[string]any{
|
||||||
|
"ban_evasion": payload.BanEvasionEvaluation,
|
||||||
|
"channel": "#" + payload.BroadcasterUserLogin,
|
||||||
|
"message": payload.Message.Text,
|
||||||
|
"shared_ban_channels": payload.SharedBanChannelIDs,
|
||||||
|
"status": payload.LowTrustStatus,
|
||||||
|
"user_id": payload.UserID,
|
||||||
|
"user_type": payload.Types,
|
||||||
|
"username": payload.UserLogin,
|
||||||
|
})
|
||||||
|
|
||||||
|
log.WithFields(log.Fields(fields.Data())).Info("restricted user message")
|
||||||
|
go handleMessage(ircHdl.Client(), nil, eventTypeSusUserMessage, fields)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*twitchWatcher) handleEventSubSusUserUpdate(m json.RawMessage) (err error) {
|
||||||
|
var payload twitch.EventSubEventSuspiciousUserUpdated
|
||||||
|
if err := json.Unmarshal(m, &payload); err != nil {
|
||||||
|
return errors.Wrap(err, "unmarshalling event")
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := fieldcollection.FieldCollectionFromData(map[string]any{
|
||||||
|
"channel": "#" + payload.BroadcasterUserLogin,
|
||||||
|
"moderator": payload.ModeratorUserLogin,
|
||||||
|
"status": payload.LowTrustStatus,
|
||||||
|
"user_id": payload.UserID,
|
||||||
|
"username": payload.UserLogin,
|
||||||
|
})
|
||||||
|
|
||||||
|
log.WithFields(log.Fields(fields.Data())).Info("user restriction updated")
|
||||||
|
go handleMessage(ircHdl.Client(), nil, eventTypeSusUserUpdate, fields)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (t *twitchWatcher) updateChannelFromAPI(channel string) error {
|
func (t *twitchWatcher) updateChannelFromAPI(channel string) error {
|
||||||
t.lock.Lock()
|
t.lock.Lock()
|
||||||
defer t.lock.Unlock()
|
defer t.lock.Unlock()
|
||||||
|
|
Loading…
Reference in a new issue