Add Moderation endpoints to API

This commit is contained in:
Evan Fiordeliso 2024-03-03 15:14:59 -05:00
parent 2b22790188
commit e941e414e2
23 changed files with 1386 additions and 0 deletions

View File

@ -20,6 +20,7 @@ import (
"go.fifitido.net/twitch/api/goals"
"go.fifitido.net/twitch/api/gueststar"
"go.fifitido.net/twitch/api/hypetrain"
"go.fifitido.net/twitch/api/moderation"
)
const HelixBaseUrl = "https://api.twitch.tv/helix"
@ -44,6 +45,7 @@ type API struct {
Goals *goals.Goals
GuestStar *gueststar.GuestStar
Hypetrain *hypetrain.Hypetrain
Moderation *moderation.Moderation
}
func New() *API {
@ -70,5 +72,6 @@ func New() *API {
Goals: goals.New(client, baseUrl),
GuestStar: gueststar.New(client, baseUrl),
Hypetrain: hypetrain.New(client, baseUrl),
Moderation: moderation.New(client, baseUrl),
}
}

View File

@ -0,0 +1,74 @@
package moderation
import (
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type AddBlockedTermParams struct {
// The ID of the broadcaster that owns the list of blocked terms.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that has permission to moderate the broadcasters chat room.
// This ID must match the user ID in the user access token.
ModeratorID string `url:"moderator_id"`
}
type AddBlockedTermRequest struct {
// The word or phrase to block from being used in the broadcasters chat room.
// The term must contain a minimum of 2 characters and may contain up to a maximum of 500 characters.
//
// Terms may include a wildcard character (*).
// The wildcard character must appear at the beginning or end of a word or set of characters.
// For example, *foo or foo*.
//
// If the blocked term already exists, the response contains the existing blocked term.
Text string `json:"text"`
}
type AddBlockedTermResponse struct {
// A list that contains the single blocked term that the broadcaster added.
Data []BlockedTerm `json:"data"`
}
// Adds a word or phrase to the broadcasters list of blocked terms.
// These are the terms that the broadcaster doesnt want used in their chat room.
//
// Requires a user access token that includes the moderator:manage:blocked_terms scope.
func (m *Moderation) AddBlockedTerm(ctx context.Context, params *AddBlockedTermParams, body *AddBlockedTermRequest) (*AddBlockedTermResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/blocked_terms", RawQuery: v.Encode()})
r, w := io.Pipe()
go func() {
if err := json.NewEncoder(w).Encode(body); err != nil {
w.CloseWithError(err)
} else {
w.Close()
}
}()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), r)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data AddBlockedTermResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,40 @@
package moderation
import (
"context"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type AddChannelModeratorParams struct {
// The ID of the broadcaster that owns the chat room. This ID must match the user ID in the access token.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the user to add as a moderator in the broadcasters chat room.
UserID string `url:"user_id"`
}
// Adds a moderator to the broadcasters chat room.
//
// Rate Limits: The broadcaster may add a maximum of 10 moderators within a 10-second window.
//
// Requires a user access token that includes the channel:manage:moderators scope.
func (m *Moderation) AddChannelModerator(ctx context.Context, params *AddChannelModeratorParams) error {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/moderators", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), nil)
if err != nil {
return err
}
res, err := m.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}

View File

@ -0,0 +1,40 @@
package moderation
import (
"context"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type AddChannelVIPParams struct {
// The ID of the user to give VIP status to.
UserID string `url:"user_id"`
// The ID of the broadcaster thats adding the user as a VIP. This ID must match the user ID in the access token.
BroadcasterID string `url:"broadcaster_id"`
}
// Adds the specified user as a VIP in the broadcasters channel.
//
// Rate Limits: The broadcaster may add a maximum of 10 VIPs within a 10-second window.
//
// Requires a user access token that includes the channel:manage:vips scope.
func (m *Moderation) AddChannelVIP(ctx context.Context, params *AddChannelVIPParams) error {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "channels/vips", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), nil)
if err != nil {
return err
}
res, err := m.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}

101
api/moderation/ban_user.go Normal file
View File

@ -0,0 +1,101 @@
package moderation
import (
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"time"
"github.com/google/go-querystring/query"
)
type BanUserParams struct {
// The ID of the broadcaster whose chat room the user is being banned from.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that has permission to moderate the broadcasters chat room.
// This ID must match the user ID in the user access token.
ModeratorID string `url:"moderator_id"`
}
type BanUserRequest struct {
// The ID of the user to ban or put in a timeout.
UserID string `json:"user_id"`
// To ban a user indefinitely, dont include this field.
//
// To put a user in a timeout, include this field and specify the timeout period, in seconds.
// The minimum timeout is 1 second and the maximum is 1,209,600 seconds (2 weeks).
Duration *int `json:"duration"`
// The reason the youre banning the user or putting them in a timeout.
// The text is user defined and is limited to a maximum of 500 characters.
Reason *string `json:"reason"`
}
type BanUserResponse struct {
// Identifies the user and type of ban.
Data []BanUserResponseData `json:"data"`
}
type BanUserResponseData struct {
// The broadcaster whose chat room the user was banned from chatting in.
BroadcasterID string `json:"broadcaster_id"`
// The moderator that banned or put the user in the timeout.
ModeratorID string `json:"moderator_id"`
// The user that was banned or put in a timeout.
UserID string `json:"user_id"`
// The UTC date and time (in RFC3339 format) that the ban or timeout was placed.
CreatedAt time.Time `json:"created_at"`
// The UTC date and time (in RFC3339 format) that the timeout will end. Is null if the user was banned instead of being put in a timeout.
EndTime *time.Time `json:"end_time"`
}
// Bans a user from participating in the specified broadcasters chat room or puts them in a timeout.
//
// For information about banning or putting users in a timeout, see Ban a User and Timeout a User.
//
// If the user is currently in a timeout, you can call this endpoint to change the duration of the timeout or ban them altogether.
// If the user is currently banned, you cannot call this method to put them in a timeout instead.
//
// To remove a ban or end a timeout, see Unban user.
//
// Requires a user access token that includes the moderator:manage:banned_users scope.
func (m *Moderation) BanUser(ctx context.Context, params *BanUserParams, body *BanUserRequest) (*BanUserResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/bans", RawQuery: v.Encode()})
r, w := io.Pipe()
go func() {
if err := json.NewEncoder(w).Encode(body); err != nil {
w.CloseWithError(err)
} else {
w.Close()
}
}()
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), r)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data BanUserResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,73 @@
package moderation
import (
"context"
"encoding/json"
"net/http"
"net/url"
)
type CheckAutoModStatusRequest struct {
Data []CheckAutoModStatusRequestData `json:"data"`
}
type CheckAutoModStatusRequestData struct {
// A caller-defined ID used to correlate this message with the same message in the response.
MsgID string `json:"msg_id"`
// The message to check.
MsgText string `json:"msg_text"`
}
type CheckAutoModStatusResponse struct {
// The list of messages and whether Twitch would approve them for chat.
Data []CheckAutoModStatusResponseData `json:"data"`
}
type CheckAutoModStatusResponseData struct {
// The caller-defined ID passed in the request.
MsgID string `json:"msg_id"`
// A Boolean value that indicates whether Twitch would approve the message for chat or hold it for moderator review or block it from chat.
// Is true if Twitch would approve the message; otherwise, false if Twitch would hold the message for moderator review or block it from chat.
IsPermitted bool `json:"is_permitted"`
}
// Checks whether AutoMod would flag the specified message for review.
//
// AutoMod is a moderation tool that holds inappropriate or harassing chat messages for moderators to review. Moderators approve or deny the messages that AutoMod flags; only approved messages are released to chat. AutoMod detects misspellings and evasive language automatically. For information about AutoMod, see How to Use AutoMod.
//
// Rate Limits: Rates are limited per channel based on the account type rather than per access token.
//
// Account type | Limit per minute | Limit per hour
// ------------ | ---------------- | --------------
// Free | 1 | 10
// Normal | 5 | 50
// Affiliated | 10 | 100
// Partnered | 30 | 300
//
// The above limits are in addition to the standard Twitch API rate limits.
// The rate limit headers in the response represent the Twitch rate limits and not the above limits.
//
// Requires a user access token that includes the moderation:read scope.
func (c *Moderation) CheckAutoModStatus(ctx context.Context, broadcasterID string, params *CheckAutoModStatusRequest) (*CheckAutoModStatusResponse, error) {
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "modetation/enforcements/status", RawQuery: url.Values{"broadcaster_id": {broadcasterID}}.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), nil)
if err != nil {
return nil, err
}
res, err := c.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data CheckAutoModStatusResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,50 @@
package moderation
import (
"context"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type DeleteChatMessagesParams struct {
// The ID of the broadcaster that owns the chat room to remove messages from.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that has permission to moderate the broadcasters chat room.
// This ID must match the user ID in the user access token.
ModeratorID string `url:"moderator_id"`
// The ID of the message to remove. The id tag in the PRIVMSG tag contains the messages ID. Restrictions:
//
// - The message must have been created within the last 6 hours.
//
// - The message must not belong to the broadcaster.
//
// - The message must not belong to another moderator.
//
// If not specified, the request removes all messages in the broadcasters chat room.
MessageID *string `url:"message_id,omitempty"`
}
// Removes a single chat message or all chat messages from the broadcasters chat room.
//
// Requires a user access token that includes the moderator:manage:chat_messages scope.
func (m *Moderation) DeleteChatMessages(ctx context.Context, params *DeleteChatMessagesParams) error {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/chat", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint.String(), nil)
if err != nil {
return err
}
res, err := m.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}

View File

@ -0,0 +1,51 @@
package moderation
import (
"context"
"encoding/json"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type GetAutoModSettingsParams struct {
// The ID of the broadcaster whose AutoMod settings you want to get.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that has permission to moderate the broadcasters chat room.
// This ID must match the user ID in the user access token.
ModeratorID string `url:"moderator_id"`
}
type GetAutoModSettingsResponse struct {
// The list of AutoMod settings. The list contains a single object that contains all the AutoMod settings.
Data []AutoModSettings `json:"data"`
}
// Gets the broadcasters AutoMod settings.
// The settings are used to automatically block inappropriate or harassing messages from appearing in the broadcasters chat room.
//
// Requires a user access token that includes the moderator:read:automod_settings scope.
func (m *Moderation) GetAutoModSettings(ctx context.Context, params *GetAutoModSettingsParams) (*GetAutoModSettingsResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/automod/settings", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetAutoModSettingsResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,104 @@
package moderation
import (
"context"
"encoding/json"
"net/http"
"net/url"
"time"
"github.com/google/go-querystring/query"
"go.fifitido.net/twitch/api/types"
)
type GetBannedUsersParams struct {
// The ID of the broadcaster whose list of banned users you want to get. This ID must match the user ID in the access token.
BroadcasterID string `url:"broadcaster_id"`
// A list of user IDs used to filter the results.
// To specify more than one ID, include this parameter for each user you want to get.
// For example, user_id=1234&user_id=5678.
// You may specify a maximum of 100 IDs.
//
// The returned list includes only those users that were banned or put in a timeout.
// The list is returned in the same order that you specified the IDs.
UserID []string `url:"user_id"`
// The maximum number of items to return per page in the response.
// The minimum page size is 1 item per page and the maximum is 100 items per page.
// The default is 20.
First *int `url:"first"`
// The cursor used to get the next page of results. The Pagination object in the response contains the cursors value.
// Read More: https://dev.twitch.tv/docs/api/guide#pagination
After *types.Cursor `url:"after"`
// The cursor used to get the previous page of results. The Pagination object in the response contains the cursors value.
// Read More: https://dev.twitch.tv/docs/api/guide#pagination
Before *types.Cursor `url:"before"`
}
type GetBannedUsersResponse struct {
// The list of banned users. The list contains a single object that contains all the banned users.
Data []GetBannedUsersResponseData `json:"data"`
// Contains information about the pagination in the response.
// The object is empty if there are no more pages of results.
// Read More: https://dev.twitch.tv/docs/api/guide#pagination
Pagination types.Pagination `json:"pagination"`
}
type GetBannedUsersResponseData struct {
// The ID of the banned user.
UserID string `json:"user_id"`
// The banned users login name.
UserLogin string `json:"user_login"`
// The banned users display name.
UserName string `json:"user_name"`
// The UTC date and time (in RFC3339 format) of when the timeout expires, or an empty string if the user is permanently banned.
ExpiresAt time.Time `json:"expires_at"`
// The UTC date and time (in RFC3339 format) of when the user was banned.
CreatedAt time.Time `json:"created_at"`
// The reason the user was banned or put in a timeout if the moderator provided one.
Reason string `json:"reason"`
// The ID of the moderator that banned the user or put them in a timeout.
ModeratorID string `json:"moderator_id"`
// The moderators login name.
ModeratorLogin string `json:"moderator_login"`
// The moderators display name.
ModeratorName string `json:"moderator_name"`
}
// Gets all users that the broadcaster banned or put in a timeout.
//
// Requires a user access token that includes the moderation:read or moderator:manage:banned_users scope.
func (m *Moderation) GetBannedUsers(ctx context.Context, params *GetBannedUsersParams) (*GetBannedUsersResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/banned", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetBannedUsersResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,65 @@
package moderation
import (
"context"
"encoding/json"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
"go.fifitido.net/twitch/api/types"
)
type GetBlockedTermsParams struct {
// The ID of the broadcaster whose blocked terms you're getting.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that has permission to moderate the broadcaster's chat room.
// This ID must match the user ID in the user access token.
ModeratorID string `url:"moderator_id"`
// The maximum number of items to return per page in the response.
// The minimum page size is 1 item per page and the maximum is 100 items per page.
// The default is 20.
First *int `url:"first"`
// The cursor used to get the next page of results. The Pagination object in the response contains the cursors value.
After *types.Cursor `url:"after"`
}
type GetBlockedTermsResponse struct {
// The list of blocked terms. The list is in descending order of when they were created (see the created_at timestamp).
Data []BlockedTerm `json:"data"`
// Contains information about the pagination in the response.
// The object is empty if there are no more pages of results.
// Read More: https://dev.twitch.tv/docs/api/guide#pagination
Pagination types.Pagination `json:"pagination"`
}
// Gets the broadcasters list of non-private, blocked words or phrases.
// These are the terms that the broadcaster or moderator added manually or that were denied by AutoMod.
//
// Requires a user access token that includes the moderator:read:blocked_terms or moderator:manage:blocked_terms scope.
func (m *Moderation) GetBlockedTerms(ctx context.Context, params *GetBlockedTermsParams) (*GetBlockedTermsResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/blocked_terms", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetBlockedTermsResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,72 @@
package moderation
import (
"context"
"encoding/json"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
"go.fifitido.net/twitch/api/types"
)
type GetModeratedChannelsParams struct {
// A users ID. Returns the list of channels that this user has moderator privileges in. This ID must match the user ID in the user OAuth token
UserID string `url:"user_id"`
// The cursor used to get the next page of results. The Pagination object in the response contains the cursors value.
After *types.Cursor `url:"after"`
// The maximum number of items to return per page in the response.
// The minimum page size is 1 item per page and the maximum is 100 items per page.
// The default is 20.
First *int `url:"first"`
}
type GetModeratedChannelsResponse struct {
// The list of channels that the user has moderator privileges in.
Data []GetModeratedChannelsResponseData `json:"data"`
// Contains information about the pagination in the response.
// The object is empty if there are no more pages of results.
// Read More: https://dev.twitch.tv/docs/api/guide#pagination
Pagination types.Pagination `json:"pagination"`
}
type GetModeratedChannelsResponseData struct {
// An ID that uniquely identifies the channel this user can moderate.
BroadcasterID string `json:"broadcaster_id"`
// The channels login name.
BroadcasterLogin string `json:"broadcaster_login"`
// The channels display name.
BroadcasterName string `json:"broadcaster_name"`
}
// Gets a list of channels that the specified user has moderator privileges in.
//
// Query parameter user_id must match the user ID in the User-Access token
// Requires OAuth Scope: user:read:moderated_channels
func (m *Moderation) GetModeratedChannels(ctx context.Context, params *GetModeratedChannelsParams) (*GetModeratedChannelsResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/channels", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetModeratedChannelsResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,82 @@
package moderation
import (
"context"
"encoding/json"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
"go.fifitido.net/twitch/api/types"
)
type GetModeratorsParams struct {
// The ID of the broadcaster whose list of moderators you want to get. This ID must match the user ID in the access token.
BroadcasterID string `url:"broadcaster_id"`
// A list of user IDs used to filter the results.
// To specify more than one ID, include this parameter for each moderator you want to get.
// For example, user_id=1234&user_id=5678.
// You may specify a maximum of 100 IDs.
//
// The returned list includes only the users from the list who are moderators in the broadcasters channel.
// The list is returned in the same order as you specified the IDs.
UserID []string `url:"user_id"`
// The maximum number of items to return per page in the response.
// The minimum page size is 1 item per page and the maximum is 100 items per page.
// The default is 20.
First *int `url:"first"`
// The cursor used to get the next page of results. The Pagination object in the response contains the cursors value.
// Read More: https://dev.twitch.tv/docs/api/guide#pagination
After *types.Cursor `url:"after"`
}
type GetModeratorsResponse struct {
// The list of moderators. The list contains a single object that contains all the moderators.
Data []GetModeratorsResponseData `json:"data"`
// Contains information about the pagination in the response.
// The object is empty if there are no more pages of results.
// Read More: https://dev.twitch.tv/docs/api/guide#pagination
Pagination types.Pagination `json:"pagination"`
}
type GetModeratorsResponseData struct {
// The ID of the user that has permission to moderate the broadcasters channel.
UserID string `json:"user_id"`
// The users login name.
UserLogin string `json:"user_login"`
// The users display name.
UserName string `json:"user_name"`
}
// Gets all users allowed to moderate the broadcasters chat room.
//
// Requires a user access token that includes the moderation:read scope.
// If your app also adds and removes moderators, you can use the channel:manage:moderators scope instead.
func (m *Moderation) GetModerators(ctx context.Context, params *GetModeratorsParams) (*GetModeratorsResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/moderators", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetModeratorsResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,52 @@
package moderation
import (
"context"
"encoding/json"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type GetShieldModeStatusParams struct {
// The ID of the broadcaster whose Shield Mode activation status you want to get.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that is one of the broadcasters moderators. This ID must match the user ID in the access token.
ModeratorID string `url:"moderator_id"`
}
type GetShieldModeStatusResponse struct {
// A list that contains a single object with the broadcasters Shield Mode status.
Data []ShieldModeStatus `json:"data"`
}
// Gets the broadcasters Shield Mode activation status.
//
// To receive notification when the broadcaster activates and deactivates Shield Mode,
// subscribe to the channel.shield_mode.begin and channel.shield_mode.end subscription types.
//
// Requires a user access token that includes the moderator:read:shield_mode or moderator:manage:shield_mode scope.
func (m *Moderation) GetShieldModeStatus(ctx context.Context, params *GetShieldModeStatusParams) (*GetShieldModeStatusResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/shield_mode", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data GetShieldModeStatusResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,79 @@
package moderation
import (
"context"
"encoding/json"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
"go.fifitido.net/twitch/api/types"
)
type GetVIPsParams struct {
// Filters the list for specific VIPs.
// To specify more than one user, include the user_id parameter for each user to get. For example, &user_id=1234&user_id=5678.
// The maximum number of IDs that you may specify is 100.
// Ignores the ID of those users in the list that arent VIPs.
UserIDs []string `url:"user_id"`
// The ID of the broadcaster whose list of VIPs you want to get. This ID must match the user ID in the access token.
BroadcasterID string `url:"broadcaster_id"`
// The maximum number of items to return per page in the response.
// The minimum page size is 1 item per page and the maximum is 100 items per page.
// The default is 20.
First *int `url:"first"`
// The cursor used to get the next page of results.
// The Pagination object in the response contains the cursors value.
// Read More: https://dev.twitch.tv/docs/api/guide#pagination
After *types.Cursor `url:"after"`
}
type GetVIPsResponse struct {
// The list of VIPs. The list contains a single object that contains all the VIPs.
Data []GetVIPsResponseData `json:"data"`
// Contains information about the pagination in the response.
// The object is empty if there are no more pages of results.
// Read More: https://dev.twitch.tv/docs/api/guide#pagination
Pagination types.Pagination `json:"pagination"`
}
type GetVIPsResponseData struct {
// An ID that uniquely identifies the VIP user.
UserID string `json:"user_id"`
// The users display name.
UserName string `json:"user_name"`
// The users login name.
UserLogin string `json:"user_login"`
}
// Gets a list of the broadcasters VIPs.
//
// Requires a user access token that includes the channel:read:vips scope.
// If your app also adds and removes VIP status, you can use the channel:manage:vips scope instead.
func (m *Moderation) GetVIPs(ctx context.Context, params GetVIPsParams) (*GetVIPsResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/vips", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
var data GetVIPsResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,49 @@
package moderation
import (
"context"
"net/http"
"net/url"
)
type AutoModAction string
const (
AutoModActionAllow AutoModAction = "ALLOW"
AutoModActionDeny AutoModAction = "DENY"
)
type ManageHeldAutoModMessagesRequest struct {
// The moderator who is approving or denying the held message. This ID must match the user ID in the access token.
UserID string `url:"user_id"`
// The ID of the message to allow or deny.
MsgID string `url:"msg_id"`
// The action to take for the message.
Action AutoModAction `url:"action"`
}
// Allow or deny the message that AutoMod flagged for review.
// For information about AutoMod, see How to Use AutoMod: https://help.twitch.tv/s/article/how-to-use-automod
//
// To get messages that AutoMod is holding for review, subscribe to the automod-queue.<moderator_id>.<channel_id> topic using PubSub.
// PubSub sends a notification to your app when AutoMod holds a message for review.
//
// Requires a user access token that includes the moderator:manage:automod scope.
func (m *Moderation) ManageHeldAutoModMessages(ctx context.Context, body *ManageHeldAutoModMessagesRequest) error {
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/automod/message"})
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), nil)
if err != nil {
return err
}
res, err := m.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}

85
api/moderation/models.go Normal file
View File

@ -0,0 +1,85 @@
package moderation
import "time"
type AutoModSettings struct {
// The ID of the broadcaster whose AutoMod settings you want to get.
BroadcasterID string `json:"broadcaster_id"`
// The ID of the broadcaster or a user that has permission to moderate the broadcasters chat room.
// This ID must match the user ID in the user access token.
ModeratorID string `json:"moderator_id"`
// The default AutoMod level for the broadcaster. This field is null if the broadcaster has set one or more of the individual settings.
OverallLevel int `json:"overall_level"`
// The Automod level for discrimination against disability.
Disability int `json:"disability"`
// The Automod level for hostility involving aggression.
Aggression int `json:"aggression"`
// The Automod level for discrimination based on sexuality, sex, or gender.
SexualitySexOrGender int `json:"sexuality_sex_or_gender"`
// The Automod level for discrimination against women.
Misogyny int `json:"misogyny"`
// The Automod level for hostility involving name calling or insults.
Bullying int `json:"bullying"`
// The Automod level for profanity.
Swearing int `json:"swearing"`
// The Automod level for racial discrimination.
RaceEthnicityOrReligion int `json:"race_ethnicity_or_religion"`
// The Automod level for sexual content.
SexBasedTerms int `json:"sex_based_terms"`
}
type BlockedTerm struct {
// The broadcaster that owns the list of blocked terms.
BroadcasterID string `json:"broadcaster_id"`
// The moderator that blocked the word or phrase from being used in the broadcasters chat room.
ModeratorID string `json:"moderator_id"`
// An ID that identifies this blocked term.
ID string `json:"id"`
// The blocked word or phrase.
Text string `json:"text"`
// The UTC date and time (in RFC3339 format) that the term was blocked.
CreatedAt time.Time `json:"created_at"`
// The UTC date and time (in RFC3339 format) that the term was updated.
//
// When the term is added, this timestamp is the same as created_at.
// The timestamp changes as AutoMod continues to deny the term.
UpdatedAt time.Time `json:"updated_at"`
// The UTC date and time (in RFC3339 format) that the blocked term is set to expire.
// After the block expires, users may use the term in the broadcasters chat room.
//
// This field is null if the term was added manually or was permanently blocked by AutoMod.
ExpiresAt *time.Time `json:"expires_at"`
}
type ShieldModeStatus struct {
// A Boolean value that determines whether Shield Mode is active. Is true if Shield Mode is active; otherwise, false.
IsActive bool `json:"is_active"`
// An ID that identifies the moderator that last activated Shield Mode.
ModeratorID string `json:"moderator_id"`
// The moderators login name.
ModeratorLogin string `json:"moderator_login"`
// The moderators display name.
ModeratorName string `json:"moderator_name"`
// The UTC timestamp (in RFC3339 format) of when Shield Mode was last activated.
LastActivatedAt time.Time `json:"last_activated_at"`
}

View File

@ -0,0 +1,18 @@
package moderation
import (
"net/http"
"net/url"
)
type Moderation struct {
client *http.Client
baseUrl *url.URL
}
func New(client *http.Client, baseUrl *url.URL) *Moderation {
return &Moderation{
client: client,
baseUrl: baseUrl,
}
}

View File

@ -0,0 +1,42 @@
package moderation
import (
"context"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type RemoveBlockedTermParams struct {
// The ID of the broadcaster that owns the list of blocked terms.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that has permission to moderate the broadcasters chat room.
// This ID must match the user ID in the user access token.
ModeratorID string `url:"moderator_id"`
// The ID of the blocked term to remove from the broadcasters list of blocked terms.
ID string `url:"id"`
}
// Removes the word or phrase from the broadcasters list of blocked terms.
//
// Requires a user access token that includes the moderator:manage:blocked_terms scope.
func (m *Moderation) RemoveBlockedTerm(ctx context.Context, params *RemoveBlockedTermParams) error {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/blocks", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint.String(), nil)
if err != nil {
return err
}
res, err := m.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}

View File

@ -0,0 +1,40 @@
package moderation
import (
"context"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type RemoveChannelModeratorParams struct {
// The ID of the broadcaster that owns the chat room. This ID must match the user ID in the access token.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the user to remove as a moderator from the broadcasters chat room.
UserID string `url:"user_id"`
}
// Removes a moderator from the broadcasters chat room.
//
// Rate Limits: The broadcaster may remove a maximum of 10 moderators within a 10-second window.
//
// Requires a user access token that includes the channel:manage:moderators scope.
func (m *Moderation) RemoveChannelModerator(ctx context.Context, params *RemoveChannelModeratorParams) error {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/moderators", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint.String(), nil)
if err != nil {
return err
}
res, err := m.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}

View File

@ -0,0 +1,43 @@
package moderation
import (
"context"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type RemoveChannelVIPParams struct {
// The ID of the user to remove VIP status from.
UserID string `url:"user_id"`
// The ID of the broadcaster who owns the channel where the user has VIP status.
BroadcasterID string `url:"broadcaster_id"`
}
// Removes the specified user as a VIP in the broadcasters channel.
//
// If the broadcaster is removing the users VIP status, the ID in the broadcaster_id query parameter must match the user ID in the access token;
// otherwise, if the user is removing their VIP status themselves, the ID in the user_id query parameter must match the user ID in the access token.
//
// Rate Limits: The broadcaster may remove a maximum of 10 VIPs within a 10-second window.
//
// Requires a user access token that includes the channel:manage:vips scope.
func (m *Moderation) RemoveChannelVIP(ctx context.Context, params *RemoveChannelVIPParams) error {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "channels/vips", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint.String(), nil)
if err != nil {
return err
}
res, err := m.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}

View File

@ -0,0 +1,44 @@
package moderation
import (
"context"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type UnbanUserParams struct {
// The ID of the broadcaster whose chat room the user is banned from chatting in.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that has permission to moderate the broadcasters chat room.
// This ID must match the user ID in the user access token.
ModeratorID string `url:"moderator_id"`
// The ID of the user to remove the ban or timeout from.
UserID string `url:"user_id"`
}
// Removes the ban or timeout that was placed on the specified user.
//
// # To ban a user, see Ban user
//
// Requires a user access token that includes the moderator:manage:banned_users scope.
func (m *Moderation) UnbanUser(ctx context.Context, params *UnbanUserParams) error {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/bans", RawQuery: v.Encode()})
req, err := http.NewRequestWithContext(ctx, http.MethodDelete, endpoint.String(), nil)
if err != nil {
return err
}
res, err := m.client.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
return nil
}

View File

@ -0,0 +1,110 @@
package moderation
import (
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type UpdateAutoModSettingsParams struct {
// The ID of the broadcaster whose AutoMod settings you want to update.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that has permission to moderate the broadcasters chat room.
// This ID must match the user ID in the user access token.
ModeratorID string `url:"moderator_id"`
}
// Because PUT is an overwrite operation, you must include all the fields that you want set after the operation completes.
// Typically, youll send a GET request, update the fields you want to change, and pass that object in the PUT request.
//
// You may set either overall_level or the individual settings like aggression, but not both.
//
// Setting overall_level applies default values to the individual settings.
// However, setting overall_level to 4 does not necessarily mean that it applies 4 to all the individual settings.
// Instead, it applies a set of recommended defaults to the rest of the settings.
// For example, if you set overall_level to 2, Twitch provides some filtering on discrimination and sexual content,
// but more filtering on hostility (see the first example response).
//
// If overall_level is currently set and you update swearing to 3, overall_level will be set to null and all settings other than swearing will be set to 0.
// The same is true if individual settings are set and you update overall_level to 3 — all the individual settings are updated to reflect the default level.
//
// Note that if you set all the individual settings to values that match what overall_level would have set them to,
// Twitch changes AutoMod to use the default AutoMod level instead of using the individual settings.
//
// Valid values for all levels are from 0 (no filtering) through 4 (most aggressive filtering).
// These levels affect how aggressively AutoMod holds back messages for moderators to review before they appear in chat or are denied (not shown).
type UpdateAutoModSettingsRequest struct {
// The Automod level for hostility involving aggression.
Aggression int `json:"aggression"`
// The Automod level for hostility involving name calling or insults.
Bullying int `json:"bullying"`
// The Automod level for discrimination against disability.
Disability int `json:"disability"`
// The Automod level for discrimination against women.
Misogyny int `json:"misogyny"`
// The default AutoMod level for the broadcaster.
OverallLevel int `json:"overall_level"`
// The Automod level for discrimination based on sexuality, sex, or gender.
RaceEthnicityOrReligion int `json:"race_ethnicity_or_religion"`
// The Automod level for sexual content.
SexBasedTerms int `json:"sex_based_terms"`
// The Automod level for discrimination against sexuality, sex, or gender.
SexualitySexOrGender int `json:"sexuality_sex_or_gender"`
// The Automod level for profanity.
Swearing int `json:"swearing"`
}
type UpdateAutoModSettingsResponse struct {
// The list of AutoMod settings. The list contains a single object that contains all the AutoMod settings.
Data []AutoModSettings `json:"data"`
}
// Updates the broadcasters AutoMod settings.
// The settings are used to automatically block inappropriate or harassing messages from appearing in the broadcasters chat room.
//
// Requires a user access token that includes the moderator:manage:automod_settings scope.
func (m *Moderation) UpdateAutoModSettings(ctx context.Context, params *UpdateAutoModSettingsParams, body *UpdateAutoModSettingsRequest) (*UpdateAutoModSettingsResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/automod/settings", RawQuery: v.Encode()})
r, w := io.Pipe()
go func() {
if err := json.NewEncoder(w).Encode(body); err != nil {
w.CloseWithError(err)
} else {
w.Close()
}
}()
req, err := http.NewRequestWithContext(ctx, http.MethodPut, endpoint.String(), r)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data UpdateAutoModSettingsResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}

View File

@ -0,0 +1,69 @@
package moderation
import (
"context"
"encoding/json"
"io"
"net/http"
"net/url"
"github.com/google/go-querystring/query"
)
type UpdateShieldModeStatusParams struct {
// The ID of the broadcaster whose Shield Mode you want to activate or deactivate.
BroadcasterID string `url:"broadcaster_id"`
// The ID of the broadcaster or a user that is one of the broadcasters moderators. This ID must match the user ID in the access token.
ModeratorID string `url:"moderator_id"`
}
type UpdateShieldModeStatusRequest struct {
// A Boolean value that determines whether to activate Shield Mode. Set to true to activate Shield Mode; otherwise, false to deactivate Shield Mode.
IsActive bool `json:"is_active"`
}
type UpdateShieldModeStatusResponse struct {
// A list that contains a single object with the broadcasters updated Shield Mode status.
Data []ShieldModeStatus `json:"data"`
}
// Activates or deactivates the broadcasters Shield Mode.
//
// Twitchs Shield Mode feature is like a panic button that broadcasters can push to protect themselves from chat abuse coming from one or more accounts.
// When activated, Shield Mode applies the overrides that the broadcaster configured in the Twitch UX.
// If the broadcaster hasnt configured Shield Mode, it applies default overrides.
//
// Requires a user access token that includes the moderator:manage:shield_mode scope.
func (m *Moderation) UpdateShieldModeStatus(ctx context.Context, params *UpdateShieldModeStatusParams) (*UpdateShieldModeStatusResponse, error) {
v, _ := query.Values(params)
endpoint := m.baseUrl.ResolveReference(&url.URL{Path: "moderation/shield_mode", RawQuery: v.Encode()})
r, w := io.Pipe()
go func() {
if err := json.NewEncoder(w).Encode(UpdateShieldModeStatusRequest{IsActive: true}); err != nil {
w.CloseWithError(err)
} else {
w.Close()
}
}()
req, err := http.NewRequestWithContext(ctx, http.MethodPatch, endpoint.String(), r)
if err != nil {
return nil, err
}
res, err := m.client.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
var data UpdateShieldModeStatusResponse
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
return nil, err
}
return &data, nil
}