mirror of
https://github.com/diamondburned/arikawa.git
synced 2025-07-28 08:21:34 +00:00
Compare commits
No commits in common. "4992f4ab200a0d8d5d8770da179be9cec2e51a97" and "273fcf1418825376b1def78b13a1f9bfb765559a" have entirely different histories.
4992f4ab20
...
273fcf1418
|
@ -209,6 +209,6 @@ func (c *Client) DeleteReactions(
|
||||||
func (c *Client) DeleteAllReactions(channelID discord.ChannelID, messageID discord.MessageID) error {
|
func (c *Client) DeleteAllReactions(channelID discord.ChannelID, messageID discord.MessageID) error {
|
||||||
return c.FastRequest(
|
return c.FastRequest(
|
||||||
"DELETE",
|
"DELETE",
|
||||||
EndpointChannels+channelID.String()+"/messages/"+messageID.String()+"/reactions",
|
EndpointChannels+channelID.String()+"/messages/"+messageID.String()+"/reactions/",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,7 +174,11 @@ func (c *Client) SendMessageComplex(
|
||||||
return msg, c.RequestJSON(&msg, "POST", URL, httputil.WithJSONBody(data))
|
return msg, c.RequestJSON(&msg, "POST", URL, httputil.WithJSONBody(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := c.MeanwhileMultipart(data.WriteMultipart, "POST", URL)
|
writer := func(mw *multipart.Writer) error {
|
||||||
|
return data.WriteMultipart(mw)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := c.MeanwhileMultipart(writer, "POST", URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -382,7 +382,7 @@ func (ctx *Context) Start() func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ErrorLogger(errors.Wrap(err, "failed to send message"))
|
ctx.ErrorLogger(err)
|
||||||
|
|
||||||
// TODO: there ought to be a better way lol
|
// TODO: there ought to be a better way lol
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
package session
|
package session
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -37,22 +36,14 @@ type Session struct {
|
||||||
// Command handler with inherited methods.
|
// Command handler with inherited methods.
|
||||||
*handler.Handler
|
*handler.Handler
|
||||||
|
|
||||||
// internal state to not be copied around.
|
// MFA only fields
|
||||||
*sessionState
|
MFA bool
|
||||||
}
|
Ticket string
|
||||||
|
|
||||||
// sessionState contains fields crucial for controlling the state of session. It
|
|
||||||
// should not be copied around.
|
|
||||||
type sessionState struct {
|
|
||||||
hstop chan struct{}
|
hstop chan struct{}
|
||||||
wstop sync.Once
|
wstop sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
func (state *sessionState) Reset() {
|
|
||||||
state.hstop = make(chan struct{})
|
|
||||||
state.wstop = sync.Once{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWithIntents(token string, intents ...gateway.Intents) (*Session, error) {
|
func NewWithIntents(token string, intents ...gateway.Intents) (*Session, error) {
|
||||||
g, err := gateway.NewGatewayWithIntents(token, intents...)
|
g, err := gateway.NewGatewayWithIntents(token, intents...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -108,15 +99,15 @@ func NewWithGateway(gw *gateway.Gateway) *Session {
|
||||||
return &Session{
|
return &Session{
|
||||||
Gateway: gw,
|
Gateway: gw,
|
||||||
// Nab off gateway's token
|
// Nab off gateway's token
|
||||||
Client: api.NewClient(gw.Identifier.Token),
|
Client: api.NewClient(gw.Identifier.Token),
|
||||||
Handler: handler.New(),
|
Handler: handler.New(),
|
||||||
sessionState: &sessionState{},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Open() error {
|
func (s *Session) Open() error {
|
||||||
// Start the handler beforehand so no events are missed.
|
// Start the handler beforehand so no events are missed.
|
||||||
s.sessionState.Reset()
|
s.hstop = make(chan struct{})
|
||||||
|
s.wstop = sync.Once{}
|
||||||
go s.startHandler()
|
go s.startHandler()
|
||||||
|
|
||||||
// Set the AfterClose's handler.
|
// Set the AfterClose's handler.
|
||||||
|
@ -133,18 +124,6 @@ func (s *Session) Open() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithContext returns a shallow copy of Session with the context replaced in
|
|
||||||
// the API client. All methods called on the returned Session will use this
|
|
||||||
// given context.
|
|
||||||
//
|
|
||||||
// This method is thread-safe only after Open and before Close are called. Open
|
|
||||||
// and Close should not be called on the returned Session.
|
|
||||||
func (s *Session) WithContext(ctx context.Context) *Session {
|
|
||||||
cpy := *s
|
|
||||||
cpy.Client = s.Client.WithContext(ctx)
|
|
||||||
return &cpy
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Session) startHandler() {
|
func (s *Session) startHandler() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -158,7 +137,7 @@ func (s *Session) startHandler() {
|
||||||
|
|
||||||
func (s *Session) Close() error {
|
func (s *Session) Close() error {
|
||||||
// Stop the event handler
|
// Stop the event handler
|
||||||
s.wstop.Do(func() { close(s.hstop) })
|
s.wstop.Do(func() { s.hstop <- struct{}{} })
|
||||||
// Close the websocket
|
// Close the websocket
|
||||||
return s.Gateway.Close()
|
return s.Gateway.Close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,8 @@ type State struct {
|
||||||
|
|
||||||
// *: State doesn't actually keep track of pinned messages.
|
// *: State doesn't actually keep track of pinned messages.
|
||||||
|
|
||||||
readyMu sync.Mutex
|
// Ready is not updated by the state.
|
||||||
ready gateway.ReadyEvent
|
Ready gateway.ReadyEvent
|
||||||
|
|
||||||
// StateLog logs all errors that come from the state cache. This includes
|
// StateLog logs all errors that come from the state cache. This includes
|
||||||
// not found errors. Defaults to a no-op, as state errors aren't that
|
// not found errors. Defaults to a no-op, as state errors aren't that
|
||||||
|
@ -142,25 +142,11 @@ func NewFromSession(s *session.Session, store Store) (*State, error) {
|
||||||
// method is thread-safe.
|
// method is thread-safe.
|
||||||
func (s *State) WithContext(ctx context.Context) *State {
|
func (s *State) WithContext(ctx context.Context) *State {
|
||||||
copied := *s
|
copied := *s
|
||||||
copied.Session = s.Session.WithContext(ctx)
|
copied.Client = copied.Client.WithContext(ctx)
|
||||||
|
|
||||||
return &copied
|
return &copied
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ready takes in a callback to access the Ready event in a thread-safe manner.
|
|
||||||
// As it acquires a mutex for thread-safety, the callback shouldn't do anything
|
|
||||||
// blocking to prevent stalling the state updates. It should also not reference
|
|
||||||
// or copy the Ready instance, as that instance will not be thread-safe.
|
|
||||||
//
|
|
||||||
// Note that the Ready that passed in will never be nil; if Ready events are not
|
|
||||||
// received yet, then the pointer will point to State's zero-value Ready
|
|
||||||
// instance.
|
|
||||||
func (s *State) Ready(fn func(*gateway.ReadyEvent)) {
|
|
||||||
s.readyMu.Lock()
|
|
||||||
fn(&s.ready)
|
|
||||||
s.readyMu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
//// Helper methods
|
//// Helper methods
|
||||||
|
|
||||||
func (s *State) AuthorDisplayName(message *gateway.MessageCreateEvent) string {
|
func (s *State) AuthorDisplayName(message *gateway.MessageCreateEvent) string {
|
||||||
|
|
|
@ -50,11 +50,6 @@ func (s *State) hookSession() {
|
||||||
func (s *State) onEvent(iface interface{}) {
|
func (s *State) onEvent(iface interface{}) {
|
||||||
switch ev := iface.(type) {
|
switch ev := iface.(type) {
|
||||||
case *gateway.ReadyEvent:
|
case *gateway.ReadyEvent:
|
||||||
// Acquire the ready mutex for the rest of these update calls, as they
|
|
||||||
// will be accessing ready's fields.
|
|
||||||
s.readyMu.Lock()
|
|
||||||
s.ready = *ev
|
|
||||||
|
|
||||||
// Reset the store before proceeding.
|
// Reset the store before proceeding.
|
||||||
if resetter, ok := s.Store.(StoreResetter); ok {
|
if resetter, ok := s.Store.(StoreResetter); ok {
|
||||||
if err := resetter.Reset(); err != nil {
|
if err := resetter.Reset(); err != nil {
|
||||||
|
@ -62,6 +57,9 @@ func (s *State) onEvent(iface interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set Ready to the state
|
||||||
|
s.Ready = *ev
|
||||||
|
|
||||||
// Handle presences
|
// Handle presences
|
||||||
for _, p := range ev.Presences {
|
for _, p := range ev.Presences {
|
||||||
if err := s.Store.PresenceSet(0, p); err != nil {
|
if err := s.Store.PresenceSet(0, p); err != nil {
|
||||||
|
@ -86,9 +84,6 @@ func (s *State) onEvent(iface interface{}) {
|
||||||
s.stateErr(err, "failed to set self in state")
|
s.stateErr(err, "failed to set self in state")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release the ready mutex only after we're done with everything.
|
|
||||||
s.readyMu.Unlock()
|
|
||||||
|
|
||||||
case *gateway.GuildCreateEvent:
|
case *gateway.GuildCreateEvent:
|
||||||
s.batchLog(storeGuildCreate(s.Store, ev))
|
s.batchLog(storeGuildCreate(s.Store, ev))
|
||||||
|
|
||||||
|
@ -273,23 +268,17 @@ func (s *State) onEvent(iface interface{}) {
|
||||||
case *gateway.SessionsReplaceEvent:
|
case *gateway.SessionsReplaceEvent:
|
||||||
|
|
||||||
case *gateway.UserGuildSettingsUpdateEvent:
|
case *gateway.UserGuildSettingsUpdateEvent:
|
||||||
s.readyMu.Lock()
|
for i, ugs := range s.Ready.UserGuildSettings {
|
||||||
for i, ugs := range s.ready.UserGuildSettings {
|
|
||||||
if ugs.GuildID == ev.GuildID {
|
if ugs.GuildID == ev.GuildID {
|
||||||
s.ready.UserGuildSettings[i] = ev.UserGuildSettings
|
s.Ready.UserGuildSettings[i] = ev.UserGuildSettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.readyMu.Unlock()
|
|
||||||
|
|
||||||
case *gateway.UserSettingsUpdateEvent:
|
case *gateway.UserSettingsUpdateEvent:
|
||||||
s.readyMu.Lock()
|
s.Ready.Settings = &ev.UserSettings
|
||||||
s.ready.Settings = &ev.UserSettings
|
|
||||||
s.readyMu.Unlock()
|
|
||||||
|
|
||||||
case *gateway.UserNoteUpdateEvent:
|
case *gateway.UserNoteUpdateEvent:
|
||||||
s.readyMu.Lock()
|
s.Ready.Notes[ev.ID] = ev.Note
|
||||||
s.ready.Notes[ev.ID] = ev.Note
|
|
||||||
s.readyMu.Unlock()
|
|
||||||
|
|
||||||
case *gateway.UserUpdateEvent:
|
case *gateway.UserUpdateEvent:
|
||||||
if err := s.Store.MyselfSet(ev.User); err != nil {
|
if err := s.Store.MyselfSet(ev.User); err != nil {
|
||||||
|
|
|
@ -89,10 +89,24 @@ func (c *Client) MeanwhileMultipart(
|
||||||
writer func(*multipart.Writer) error,
|
writer func(*multipart.Writer) error,
|
||||||
method, url string, opts ...RequestOption) (httpdriver.Response, error) {
|
method, url string, opts ...RequestOption) (httpdriver.Response, error) {
|
||||||
|
|
||||||
|
// We want to cancel the request if our bodyWriter fails.
|
||||||
|
ctx, cancel := context.WithCancel(c.context)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
r, w := io.Pipe()
|
r, w := io.Pipe()
|
||||||
body := multipart.NewWriter(w)
|
body := multipart.NewWriter(w)
|
||||||
|
|
||||||
go func() { w.CloseWithError(writer(body)) }()
|
var bgErr error
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := writer(body); err != nil {
|
||||||
|
bgErr = err
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the writer so the body gets flushed to the HTTP reader.
|
||||||
|
w.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
// Prepend the multipart writer and the correct Content-Type header options.
|
// Prepend the multipart writer and the correct Content-Type header options.
|
||||||
opts = PrependOptions(
|
opts = PrependOptions(
|
||||||
|
@ -102,7 +116,11 @@ func (c *Client) MeanwhileMultipart(
|
||||||
)
|
)
|
||||||
|
|
||||||
// Request with the current client and our own context:
|
// Request with the current client and our own context:
|
||||||
return c.Request(method, url, opts...)
|
resp, err := c.WithContext(ctx).Request(method, url, opts...)
|
||||||
|
if err != nil && bgErr != nil {
|
||||||
|
return nil, bgErr
|
||||||
|
}
|
||||||
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) FastRequest(method, url string, opts ...RequestOption) error {
|
func (c *Client) FastRequest(method, url string, opts ...RequestOption) error {
|
||||||
|
@ -158,7 +176,7 @@ func (c *Client) Request(method, url string, opts ...RequestOption) (httpdriver.
|
||||||
fn(q, nil)
|
fn(q, nil)
|
||||||
}
|
}
|
||||||
// Exit after cleaning everything up.
|
// Exit after cleaning everything up.
|
||||||
return nil, errors.Wrap(err, "failed to apply http request options")
|
return nil, errors.Wrap(err, "failed to apply options")
|
||||||
}
|
}
|
||||||
|
|
||||||
r, doErr = c.Client.Do(q)
|
r, doErr = c.Client.Do(q)
|
||||||
|
|
|
@ -78,14 +78,25 @@ func WithBody(body io.ReadCloser) RequestOption {
|
||||||
// WithJSONBody inserts a JSON body into the request. This ignores JSON errors.
|
// WithJSONBody inserts a JSON body into the request. This ignores JSON errors.
|
||||||
func WithJSONBody(v interface{}) RequestOption {
|
func WithJSONBody(v interface{}) RequestOption {
|
||||||
if v == nil {
|
if v == nil {
|
||||||
return func(httpdriver.Request) error { return nil }
|
return func(httpdriver.Request) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var rp, wp = io.Pipe()
|
var rp, wp = io.Pipe()
|
||||||
|
var err error
|
||||||
|
|
||||||
go func() { wp.CloseWithError(json.EncodeStream(wp, v)) }()
|
go func() {
|
||||||
|
err = json.EncodeStream(wp, v)
|
||||||
|
wp.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
return func(r httpdriver.Request) error {
|
return func(r httpdriver.Request) error {
|
||||||
|
// TODO: maybe do something to this?
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
r.AddHeader(http.Header{
|
r.AddHeader(http.Header{
|
||||||
"Content-Type": {"application/json"},
|
"Content-Type": {"application/json"},
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue