diff --git a/io.go b/io.go new file mode 100644 index 0000000..106ae10 --- /dev/null +++ b/io.go @@ -0,0 +1,91 @@ +package rpcfetch + +import ( + "encoding/binary" + "encoding/json" + "net" + "os" + "strconv" +) + +type Client struct { + id string + + dialed bool + active bool + net.Conn +} + +// Raw wraps around the Raw method to provide generic json parsing +func Raw[T any](d *Client, opcode uint32, payload any) (uint32, T, error) { + var p T + + opcodeResp, payloadResp, err := d.Raw(opcode, payload) + if err != nil { + return opcodeResp, p, err + } + + return opcodeResp, p, json.Unmarshal(payloadResp, &p) +} + +// Raw writes a raw payload to Discord and returns the response opcode and payload +func (d *Client) Raw(opcode uint32, payload any) (uint32, []byte, error) { + if err := binary.Write(d.Conn, binary.LittleEndian, opcode); err != nil { + return 0, nil, err + } + + if p, err := json.Marshal(payload); err != nil { + return 0, nil, err + } else { + if err = binary.Write(d.Conn, binary.LittleEndian, uint32(len(p))); err != nil { + return 0, nil, err + } + + if _, err = d.Conn.Write(p); err != nil { + return 0, nil, err + } + } + + var ( + opcodeResp uint32 + lengthResp uint32 + ) + + if err := binary.Read(d.Conn, binary.LittleEndian, &opcodeResp); err != nil { + return 0, nil, err + } + + if err := binary.Read(d.Conn, binary.LittleEndian, &lengthResp); err != nil { + return 0, nil, err + } + + payloadResp := make([]byte, lengthResp) + _, err := d.Read(payloadResp) + return opcodeResp, payloadResp, err +} + +// New sets up and returns the reference to a new Client +func New(id string) *Client { + d := &Client{ + id: id, + } + + return d +} + +func sockPath() string { + snap := "/run/user/" + strconv.Itoa(os.Getuid()) + "/snap.discord" + + if _, err := os.Stat(snap); err == nil { + return snap + } + + for _, env := range []string{"XDG_RUNTIME_DIR", "TMPDIR", "TMP", "TEMP"} { + if val, ok := os.LookupEnv(env); ok { + return val + } + } + + // fallback + return "/tmp" +} diff --git a/io_unix.go b/io_unix.go new file mode 100644 index 0000000..c83ba1d --- /dev/null +++ b/io_unix.go @@ -0,0 +1,20 @@ +package rpcfetch + +import ( + "net" + "time" +) + +func (d *Client) dial() error { + if d.dialed { + panic("attempted to dial on open client") + } + + if conn, err := net.DialTimeout("unix", sockPath()+"/discord-ipc-0", 5*time.Second); err != nil { + return err + } else { + d.Conn = conn + d.dialed = true + } + return nil +}