Add conduit abstraction
This commit is contained in:
parent
9279a1ac3c
commit
39f46618ab
|
@ -3,11 +3,16 @@ package conduit
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CreateConduitsRequest struct {
|
||||||
|
// The number of shards to create for this conduit.
|
||||||
|
ShardCount int `json:"shard_count"`
|
||||||
|
}
|
||||||
|
|
||||||
type CreateConduitsResponse struct {
|
type CreateConduitsResponse struct {
|
||||||
Data []ConduitData `json:"data"`
|
Data []ConduitData `json:"data"`
|
||||||
}
|
}
|
||||||
|
@ -15,10 +20,20 @@ type CreateConduitsResponse struct {
|
||||||
// Creates a new conduit.
|
// Creates a new conduit.
|
||||||
//
|
//
|
||||||
// Requires an app access token.
|
// Requires an app access token.
|
||||||
func (c *Conduit) CreateConduits(ctx context.Context, shareCount int) (*CreateConduitsResponse, error) {
|
func (c *Conduit) CreateConduits(ctx context.Context, body *CreateConduitsRequest) (*CreateConduitsResponse, error) {
|
||||||
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "eventsub/conduits", RawQuery: "share_count=" + fmt.Sprint(shareCount)})
|
endpoint := c.baseUrl.ResolveReference(&url.URL{Path: "eventsub/conduits"})
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), nil)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package conduit
|
package conduit
|
||||||
|
|
||||||
import "time"
|
import "go.fifitido.net/twitch/api/eventsub"
|
||||||
|
|
||||||
type ConduitData struct {
|
type ConduitData struct {
|
||||||
// Conduit ID.
|
// Conduit ID.
|
||||||
|
@ -18,7 +18,7 @@ type Shard struct {
|
||||||
Status Status `json:"status"`
|
Status Status `json:"status"`
|
||||||
|
|
||||||
// The transport details used to send the notifications.
|
// The transport details used to send the notifications.
|
||||||
Transport *Transport `json:"transport"`
|
Transport *eventsub.Transport `json:"transport"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Status string
|
type Status string
|
||||||
|
@ -58,19 +58,3 @@ const (
|
||||||
// websocket_network_error — The Twitch WebSocket server experienced a network error writing the message to the client.
|
// websocket_network_error — The Twitch WebSocket server experienced a network error writing the message to the client.
|
||||||
WebsocketnetworkError Status = "websocket_network_error"
|
WebsocketnetworkError Status = "websocket_network_error"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Transport struct {
|
|
||||||
// The transport method. Possible values are:
|
|
||||||
//
|
|
||||||
// webhook, websocket
|
|
||||||
Method string `json:"method"`
|
|
||||||
|
|
||||||
// The callback URL where the notifications are sent. Included only if method is set to webhook.
|
|
||||||
Callback *string `json:"callback,omitempty"`
|
|
||||||
|
|
||||||
// The UTC date and time that the WebSocket connection was established. Included only if method is set to websocket.
|
|
||||||
ConnectedAt *time.Time `json:"connected_at,omitempty"`
|
|
||||||
|
|
||||||
// The UTC date and time that the WebSocket connection was lost. Included only if method is set to websocket.
|
|
||||||
DisconnectedAt *time.Time `json:"disconnected_at,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"go.fifitido.net/twitch/api/eventsub"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UpdateConduitShardsRequest struct {
|
type UpdateConduitShardsRequest struct {
|
||||||
|
@ -21,7 +23,9 @@ type ShardUpdate struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
|
|
||||||
// The transport details that you want Twitch to use when sending you notifications.
|
// The transport details that you want Twitch to use when sending you notifications.
|
||||||
Transport *Transport `json:"transport"`
|
//
|
||||||
|
// Does not support conduit transport type.
|
||||||
|
Transport *eventsub.Transport `json:"transport"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateConduitShardsResponse struct {
|
type UpdateConduitShardsResponse struct {
|
||||||
|
|
|
@ -1 +1,129 @@
|
||||||
package conduit
|
package conduit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"go.fifitido.net/twitch/api"
|
||||||
|
"go.fifitido.net/twitch/api/conduit"
|
||||||
|
eventsubapi "go.fifitido.net/twitch/api/eventsub"
|
||||||
|
"go.fifitido.net/twitch/eventsub"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Conduit struct {
|
||||||
|
api *api.API
|
||||||
|
|
||||||
|
id string
|
||||||
|
transports []eventsub.Transport
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(transports ...eventsub.Transport) *Conduit {
|
||||||
|
return &Conduit{
|
||||||
|
transports: transports,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conduit) AddTransport(transport eventsub.Transport) {
|
||||||
|
c.transports = append(c.transports, transport)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApiTransport implements eventsub.Transport
|
||||||
|
func (c *Conduit) ApiTransport() *eventsubapi.Transport {
|
||||||
|
return &eventsubapi.Transport{
|
||||||
|
Method: "conduit",
|
||||||
|
ConduitID: &c.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run implements eventsub.Transport.
|
||||||
|
func (c *Conduit) Run() error {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for _, transport := range c.transports {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(transport eventsub.Transport) {
|
||||||
|
defer wg.Done()
|
||||||
|
if err := transport.Run(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}(transport)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start implements eventsub.Transport.
|
||||||
|
func (c *Conduit) Start() {
|
||||||
|
go func() {
|
||||||
|
if err := c.Run(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close implements eventsub.Transport.
|
||||||
|
func (c *Conduit) Close() error {
|
||||||
|
for _, transport := range c.transports {
|
||||||
|
if err := transport.Close(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conduit) Register(ctx context.Context) error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
createRes, err := c.api.Conduit.CreateConduits(context.Background(), &conduit.CreateConduitsRequest{
|
||||||
|
ShardCount: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.id = createRes.Data[0].ID
|
||||||
|
updateReq := &conduit.UpdateConduitShardsRequest{
|
||||||
|
ConduitID: c.id,
|
||||||
|
Shards: make([]conduit.ShardUpdate, len(c.transports)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, transport := range c.transports {
|
||||||
|
updateReq.Shards[i] = conduit.ShardUpdate{
|
||||||
|
ID: fmt.Sprint(i),
|
||||||
|
Transport: transport.ApiTransport(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateRes, err := c.api.Conduit.UpdateConduitShards(ctx, updateReq)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(updateRes.Errors) > 0 {
|
||||||
|
return fmt.Errorf("failed to update shards: %v", updateRes.Errors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conduit) Unregister(ctx context.Context) error {
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
|
||||||
|
if c.id == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.api.Conduit.DeleteConduit(ctx, c.id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.id = ""
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue