mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 11:51:17 +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 {
|
||||||
|
|
|
@ -28,6 +28,7 @@ const (
|
||||||
ScopeModeratorManageShoutouts = "moderator:manage:shoutouts"
|
ScopeModeratorManageShoutouts = "moderator:manage:shoutouts"
|
||||||
ScopeModeratorReadFollowers = "moderator:read:followers"
|
ScopeModeratorReadFollowers = "moderator:read:followers"
|
||||||
ScopeModeratorReadShoutouts = "moderator:read:shoutouts"
|
ScopeModeratorReadShoutouts = "moderator:read:shoutouts"
|
||||||
|
ScopeModeratorReadSuspiciousUsers = "moderator:read:suspicious_users"
|
||||||
ScopeUserBot = "user:bot"
|
ScopeUserBot = "user:bot"
|
||||||
ScopeUserManageChatColor = "user:manage:chat_color"
|
ScopeUserManageChatColor = "user:manage:chat_color"
|
||||||
ScopeUserManageWhispers = "user:manage:whispers"
|
ScopeUserManageWhispers = "user:manage:whispers"
|
||||||
|
|
|
@ -17,6 +17,7 @@ var (
|
||||||
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.ScopeModeratorReadSuspiciousUsers: "see users marked suspicious / restricted",
|
||||||
twitch.ScopeUserManageWhispers: "send whispers on behalf of this user",
|
twitch.ScopeUserManageWhispers: "send whispers on behalf of this user",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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