about summary refs log tree commit diff
path: root/pkg/discord/discord.go
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-04-04 00:05:12 +0200
committerMel <einebeere@gmail.com>2022-04-04 00:05:12 +0200
commit4cbe353d824d5064ee6f72f4693ee3c759b50e0a (patch)
tree9e0d47d6098e87b913a44348cf3d9811d992f190 /pkg/discord/discord.go
parentdfeef2aad6fb25eadc13c12e1caea7160bfe6c3e (diff)
downloadjinx-4cbe353d824d5064ee6f72f4693ee3c759b50e0a.tar.zst
jinx-4cbe353d824d5064ee6f72f4693ee3c759b50e0a.zip
Successful Identify
Diffstat (limited to 'pkg/discord/discord.go')
-rw-r--r--pkg/discord/discord.go106
1 files changed, 95 insertions, 11 deletions
diff --git a/pkg/discord/discord.go b/pkg/discord/discord.go
index 13de020..1fefe6c 100644
--- a/pkg/discord/discord.go
+++ b/pkg/discord/discord.go
@@ -2,10 +2,14 @@ package discord
 
 import (
 	"bytes"
+	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
+	"log"
+	"math/rand"
 	"net/http"
+	"time"
 
 	"github.com/gorilla/websocket"
 )
@@ -15,41 +19,59 @@ const USER_AGENT = "DiscordBot (https://jinx.rnrd.eu/, v0.0.0) Jinx"
 
 type Discord struct {
 	Token string
+	Conn  *websocket.Conn
 }
 
 func NewClient(token string) *Discord {
 	return &Discord{
 		Token: token,
+		Conn:  nil,
 	}
 }
 
-func (d *Discord) Connect() error {
-	gatewayURL, err := d.gateway()
+func (d *Discord) Connect(ctx context.Context) error {
+	gatewayURL, err := d.getGateway()
 	if err != nil {
 		return err
 	}
 
-	fmt.Printf("Gateway: %s\n", gatewayURL)
+	fmt.Printf("gateway: %s\n", gatewayURL)
 
 	connectHeader := http.Header{}
-	conn, _, err := websocket.DefaultDialer.Dial(gatewayURL, connectHeader)
+	d.Conn, _, err = websocket.DefaultDialer.Dial(gatewayURL, connectHeader)
 	if err != nil {
 		return err
 	}
 
-	defer conn.Close()
-
-	messageType, message, err := conn.ReadMessage()
-	if err != nil {
+	var helloMsg GatewayPayload[GatewayHelloMsg]
+	if err = d.Conn.ReadJSON(&helloMsg); err != nil {
 		return err
 	}
 
-	fmt.Printf("Type: %d, Message: %s\n", messageType, message)
+	fmt.Printf("connection response Payload: %+v\n", helloMsg)
+
+	if helloMsg.Op != GATEWAY_OP_HELLO {
+		return fmt.Errorf("expected opcode %d, got %d", GATEWAY_OP_HELLO, helloMsg.Op)
+	}
+
+	go d.startHeartbeat(ctx, helloMsg.Data.HeartbeatInterval)
+
+	if err = d.identify(); err != nil {
+		return err
+	}
 
 	return nil
 }
 
-func (d *Discord) gateway() (string, error) {
+func (d *Discord) Disconnect() error {
+	if d.Conn == nil {
+		return errors.New("not connected")
+	}
+
+	return d.Conn.Close()
+}
+
+func (d *Discord) getGateway() (string, error) {
 	url := DISCORD_URl + "gateway"
 
 	req, err := http.NewRequest("GET", url, nil)
@@ -77,7 +99,7 @@ func (d *Discord) gateway() (string, error) {
 	switch resp.StatusCode {
 	case 200:
 	default:
-		return "", errors.New("Gateway response status code: " + resp.Status)
+		return "", errors.New("gateway response status code: " + resp.Status)
 	}
 
 	body := struct {
@@ -92,3 +114,65 @@ func (d *Discord) gateway() (string, error) {
 	url = body.URL + "?v=9&encoding=json"
 	return url, nil
 }
+
+func (d *Discord) startHeartbeat(ctx context.Context, interval uint64) {
+	// REF: heartbeat_interval * jitter
+	jitter := rand.Intn(int(interval))
+	time.Sleep(time.Duration(jitter) * time.Millisecond)
+
+	ticker := time.NewTicker(time.Duration(interval) * time.Millisecond)
+	defer ticker.Stop()
+
+	for {
+		select {
+		case <-ctx.Done():
+			return
+		case <-ticker.C:
+			fmt.Println("sending heartbeat.")
+
+			msg := GatewayPayload[any]{
+				Op: GATEWAY_OP_HEARTBEAT,
+			}
+
+			if err := d.Conn.WriteJSON(msg); err != nil {
+				log.Fatalf("error sending heartbeat: %s\n", err)
+			}
+		}
+	}
+}
+
+func (d *Discord) identify() error {
+	msg := GatewayPayload[GatewayIdentifyMsg]{
+		Op: GATEWAY_OP_IDENTIFY,
+		Data: GatewayIdentifyMsg{
+			Token: d.Token,
+			Properties: GatewayIdentifyProperties{
+				OS:      "linux",
+				Browser: "jinx",
+				Device:  "jinx",
+			},
+		},
+		Sequence: 0,
+	}
+
+	if err := d.Conn.WriteJSON(msg); err != nil {
+		return err
+	}
+
+	var res GatewayPayload[GatewayReadyMsg]
+	if err := d.Conn.ReadJSON(&res); err != nil {
+		return err
+	}
+
+	fmt.Printf("identify response payload: %+v\n", res)
+
+	if res.Op != GATEWAY_OP_DISPATCH {
+		return fmt.Errorf("expected opcode %d, got %d", GATEWAY_OP_DISPATCH, res.Op)
+	}
+
+	if res.EventName != "READY" {
+		return fmt.Errorf("expected event name %s, got %s", "READY", res.EventName)
+	}
+
+	return nil
+}