1
0
Fork 0
mirror of https://github.com/diamondburned/arikawa.git synced 2025-03-22 18:09:21 +00:00

Added simple example

This commit is contained in:
diamondburned 2020-01-17 14:29:13 -08:00
parent 842daaa957
commit 2dc983d243
8 changed files with 134 additions and 68 deletions

View file

@ -1,7 +1,7 @@
# arikawa
[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue?style=flat-square )](https://godoc.org/github.com/diamondburned/arikawa)
[![ Examples](https://img.shields.io/badge/Example-__example%2F-blueviolet?style=flat-square)]()
[![ Examples](https://img.shields.io/badge/Example-__example%2F-blueviolet?style=flat-square)](https://github.com/diamondburned/arikawa/tree/master/_example)
[![ Discord nixhub](https://img.shields.io/badge/Discord-nixhub-7289da?style=flat-square )](https://discord.gg/kF9mYBV )
[![ Hime Arikawa](https://img.shields.io/badge/Hime-Arikawa-ea75a2?style=flat-square )](https://hime-goto.fandom.com/wiki/Hime_Arikawa )

45
_example/simple/main.go Normal file
View file

@ -0,0 +1,45 @@
package main
import (
"log"
"os"
"github.com/diamondburned/arikawa/gateway"
"github.com/diamondburned/arikawa/session"
)
// To run, do `BOT_TOKEN="TOKEN HERE" go run .`
func main() {
var token = os.Getenv("BOT_TOKEN")
if token == "" {
log.Fatalln("No $BOT_TOKEN given.")
}
s, err := session.New("Bot " + token)
if err != nil {
log.Fatalln("Session failed:", err)
}
s.AddHandler(func(c *gateway.MessageCreateEvent) {
log.Println(c.Author.Username, "sent", c.Content)
})
if err := s.Open(); err != nil {
log.Fatalln("Failed to connect:", err)
}
defer s.Close()
u, err := s.Me()
if err != nil {
log.Fatalln("Failed to get myself:", err)
}
log.Println("Started as", u.Username)
// Wait is optional.
if err := s.Wait(); err != nil {
log.Fatalln("Fatal error:", err)
}
}

View file

@ -1,32 +1,47 @@
package discord
import (
"bytes"
"strconv"
"strings"
"time"
)
const DiscordEpoch = 1420070400000 * int64(time.Millisecond)
type Snowflake uint64
type Snowflake int64
func NewSnowflake(t time.Time) Snowflake {
return Snowflake(TimeToDiscordEpoch(t) << 22)
}
const Me = Snowflake(-1)
func (s *Snowflake) UnmarshalJSON(v []byte) error {
v = bytes.Trim(v, `"`)
u, err := strconv.ParseUint(string(v), 10, 64)
id := strings.Trim(string(v), `"`)
if id == "null" {
return nil
}
i, err := strconv.ParseInt(id, 10, 64)
if err != nil {
return err
}
*s = Snowflake(u)
*s = Snowflake(i)
return nil
}
func (s *Snowflake) MarshalJSON() ([]byte, error) {
return []byte(`"` + strconv.FormatUint(uint64(*s), 10) + `"`), nil
var id string
switch i := int64(*s); i {
case -1: // @me
id = "@me"
default:
id = strconv.FormatInt(i, 10)
}
return []byte(`"` + id + `"`), nil
}
func (s Snowflake) String() string {

View file

@ -2,6 +2,7 @@ package discord
import (
"encoding/json"
"strings"
"time"
)
@ -15,7 +16,12 @@ var (
)
func (t *Timestamp) UnmarshalJSON(v []byte) error {
r, err := time.Parse(TimestampFormat, string(v))
str := strings.Trim(string(v), `"`)
if str == "null" {
return nil
}
r, err := time.Parse(TimestampFormat, str)
if err != nil {
return err
}

View file

@ -40,6 +40,9 @@ var (
WSRetries = uint(5)
// WSError is the default error handler
WSError = func(err error) {}
// WSExtraReadTimeout is the duration to be added to Hello, as a read
// timeout for the websocket.
WSExtraReadTimeout = time.Second
)
var (
@ -138,7 +141,7 @@ func NewGatewayWithDriver(token string, driver json.Driver) (*Gateway, error) {
g.WS = ws
// Try and dial it
return g, g.connect()
return g, nil
}
// Close closes the underlying Websocket connection.
@ -158,7 +161,53 @@ func (g *Gateway) Reconnect() error {
// Close, but we don't care about the error (I think)
g.Close()
// Actually a reconnect at this point.
return g.connect()
return g.Open()
}
func (g *Gateway) Open() error {
// Reconnect timeout
ctx, cancel := context.WithTimeout(context.Background(), g.WSTimeout)
defer cancel()
var Lerr error
for i := uint(0); i < g.WSRetries; i++ {
// Check if context is expired
if err := ctx.Err(); err != nil {
// Don't bother if it's expired
return err
}
// Reconnect to the Gateway
if err := g.WS.Redial(ctx); err != nil {
// Save the error, retry again
Lerr = errors.Wrap(err, "Failed to reconnect")
continue
}
// Try to resume the connection
if err := g.Start(); err != nil {
// If the connection is rate limited (documented behavior):
// https://discordapp.com/developers/docs/topics/gateway#rate-limiting
if err == ErrInvalidSession {
continue // retry
}
// Else, fatal
return errors.Wrap(err, "Failed to start gateway")
}
// Started successfully, return
return nil
}
// Check if any earlier errors are fatal
if Lerr != nil {
return Lerr
}
// We tried.
return ErrWSMaxTries
}
// Start authenticates with the websocket, or resume from a dead Websocket
@ -254,49 +303,3 @@ func (g *Gateway) Send(code OPCode, v interface{}) error {
return g.WS.Send(ctx, b)
}
func (g *Gateway) connect() error {
// Reconnect timeout
ctx, cancel := context.WithTimeout(context.Background(), g.WSTimeout)
defer cancel()
var Lerr error
for i := uint(0); i < g.WSRetries; i++ {
// Check if context is expired
if err := ctx.Err(); err != nil {
// Don't bother if it's expired
return err
}
// Reconnect to the Gateway
if err := g.WS.Redial(ctx); err != nil {
// Save the error, retry again
Lerr = errors.Wrap(err, "Failed to reconnect")
continue
}
// Try to resume the connection
if err := g.Start(); err != nil {
// If the connection is rate limited (documented behavior):
// https://discordapp.com/developers/docs/topics/gateway#rate-limiting
if err == ErrInvalidSession {
continue // retry
}
// Else, fatal
return errors.Wrap(err, "Failed to start gateway")
}
// Started successfully, return
return nil
}
// Check if any earlier errors are fatal
if Lerr != nil {
return Lerr
}
// We tried.
return ErrWSMaxTries
}

View file

@ -5,7 +5,6 @@ import (
"context"
"io/ioutil"
"net/http"
"time"
"github.com/diamondburned/arikawa/internal/json"
"github.com/pkg/errors"
@ -43,8 +42,6 @@ type Conn struct {
*websocket.Conn
json.Driver
ReadTimeout time.Duration // DefaultTimeout
events chan Event
}
@ -52,9 +49,8 @@ var _ Connection = (*Conn)(nil)
func NewConn(driver json.Driver) *Conn {
return &Conn{
Driver: driver,
ReadTimeout: DefaultTimeout,
events: make(chan Event, WSBuffer),
Driver: driver,
events: make(chan Event, WSBuffer),
}
}
@ -81,11 +77,7 @@ func (c *Conn) Listen() <-chan Event {
func (c *Conn) readLoop(ch chan Event) {
for {
ctx, cancel := context.WithTimeout(
context.Background(), c.ReadTimeout)
defer cancel()
b, err := c.readAll(ctx)
b, err := c.readAll(context.Background())
if err != nil {
// Check if the error is a fatal one
if code := websocket.CloseStatus(err); code > -1 {

View file

@ -69,7 +69,7 @@ func NewWithGateway(gw *gateway.Gateway) *Session {
}
func (s *Session) Open() error {
if err := s.gateway.Start(); err != nil {
if err := s.gateway.Open(); err != nil {
return errors.Wrap(err, "Failed to start gateway")
}
@ -91,6 +91,10 @@ func (s *Session) startHandler(stop <-chan struct{}) {
}
}
func (s *Session) Wait() error {
return s.gateway.Wait()
}
func (s *Session) Close() error {
// Stop the event handler
if s.hstop != nil {

View file

@ -56,6 +56,7 @@ func (s *State) hookSession() error {
}
switch ev := iface.(type) {
case *gateway.ReadyEvent:
case *gateway.MessageCreateEvent:
_ = ev
panic("IMPLEMENT ME")