Initial implementation
This commit is contained in:
parent
1a2a0196e5
commit
ba287dd2e8
204
main.go
204
main.go
|
|
@ -2,39 +2,146 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/diamondburned/arikawa/v3/api"
|
||||||
|
"github.com/diamondburned/arikawa/v3/discord"
|
||||||
"github.com/diamondburned/arikawa/v3/gateway"
|
"github.com/diamondburned/arikawa/v3/gateway"
|
||||||
"github.com/diamondburned/arikawa/v3/session"
|
"github.com/diamondburned/arikawa/v3/session"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
writeWait = 10 * time.Second
|
||||||
|
pingPeriod = 50 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
var upgrader = websocket.Upgrader{}
|
var upgrader = websocket.Upgrader{}
|
||||||
|
|
||||||
|
type mtag string
|
||||||
|
|
||||||
|
const (
|
||||||
|
mtagOnDiscordChat mtag = "on_discord_chat"
|
||||||
|
mtagOnMinecraftChat mtag = "on_minecraft_chat"
|
||||||
|
mtagNothing mtag = "nothing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type message struct {
|
||||||
|
Tag mtag
|
||||||
|
OnDiscordChat *onDiscordChatMessage
|
||||||
|
OnMinecraftChat *onMinecraftChatMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
type onDiscordChatMessage struct {
|
||||||
|
SenderNickname string
|
||||||
|
SenderUsername string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
type onMinecraftChatMessage struct {
|
||||||
|
Sender string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
type skyBase struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
luaOutboxes []chan message
|
||||||
|
goInbox chan message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sb *skyBase) luaDispatch(m message) {
|
||||||
|
sb.mu.RLock()
|
||||||
|
defer sb.mu.RUnlock()
|
||||||
|
for _, ch := range sb.luaOutboxes {
|
||||||
|
ch <- m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var base *skyBase
|
||||||
|
|
||||||
func handleIndex(w http.ResponseWriter, r *http.Request) {
|
func handleIndex(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Write([]byte("hello from skybase"))
|
w.Write([]byte("hello from skybase"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleChat(w http.ResponseWriter, r *http.Request) {
|
func handleChat(w http.ResponseWriter, r *http.Request) {
|
||||||
c, err := upgrader.Upgrade(w, r, nil)
|
ws, err := upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer c.Close()
|
|
||||||
|
|
||||||
|
outbox := make(chan message, 10)
|
||||||
|
base.mu.Lock()
|
||||||
|
base.luaOutboxes = append(base.luaOutboxes, outbox)
|
||||||
|
base.mu.Unlock()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
ws.Close()
|
||||||
|
base.mu.Lock()
|
||||||
|
for i, ch := range base.luaOutboxes {
|
||||||
|
if ch != outbox {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
base.luaOutboxes[i] = base.luaOutboxes[len(base.luaOutboxes)-1]
|
||||||
|
base.luaOutboxes = base.luaOutboxes[:len(base.luaOutboxes)-1]
|
||||||
|
close(ch)
|
||||||
|
}
|
||||||
|
base.mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
pingTicker := time.NewTicker(pingPeriod)
|
||||||
|
defer pingTicker.Stop()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer cancel()
|
||||||
for {
|
for {
|
||||||
mt, message, err := c.ReadMessage()
|
_, data, err := ws.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = c.WriteMessage(mt, message)
|
var msg message
|
||||||
|
if err = json.Unmarshal(data, &msg); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
base.goInbox <- msg
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-pingTicker.C:
|
||||||
|
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
if err = ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case msg := <-outbox:
|
||||||
|
data, err := json.Marshal(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("sending", string(data))
|
||||||
|
|
||||||
|
ws.SetWriteDeadline(time.Now().Add(writeWait))
|
||||||
|
if err = ws.WriteMessage(websocket.TextMessage, data); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -47,25 +154,95 @@ func main() {
|
||||||
if token == "" {
|
if token == "" {
|
||||||
log.Fatal("missing bot token")
|
log.Fatal("missing bot token")
|
||||||
}
|
}
|
||||||
|
channelID, err := strconv.ParseUint(os.Getenv("CHANNEL_ID"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("missing channel ID")
|
||||||
|
}
|
||||||
httpListen := os.Getenv("HTTP_LISTEN")
|
httpListen := os.Getenv("HTTP_LISTEN")
|
||||||
if httpListen == "" {
|
if httpListen == "" {
|
||||||
log.Fatal("missing http listen address")
|
log.Fatal("missing http listen address")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// init base
|
||||||
|
base = &skyBase{
|
||||||
|
luaOutboxes: []chan message{},
|
||||||
|
goInbox: make(chan message, 10),
|
||||||
|
}
|
||||||
|
|
||||||
// init session
|
// init session
|
||||||
s := session.New("Bot " + token)
|
id := gateway.DefaultIdentifier("Bot " + token)
|
||||||
|
id.Presence = &gateway.UpdatePresenceCommand{
|
||||||
|
Status: discord.DoNotDisturbStatus,
|
||||||
|
Activities: []discord.Activity{{
|
||||||
|
Type: discord.CustomActivity,
|
||||||
|
Name: "whatever",
|
||||||
|
State: "PQJDfhkjldalsljkfhasd",
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
s := session.NewWithIdentifier(id)
|
||||||
s.AddHandler(func(c *gateway.ReadyEvent) {
|
s.AddHandler(func(c *gateway.ReadyEvent) {
|
||||||
u, err := s.Me()
|
u, err := s.Me()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
log.Println("started as", u.Username)
|
log.Println("started as", u.Username)
|
||||||
|
|
||||||
|
channelID := discord.ChannelID(channelID)
|
||||||
|
|
||||||
|
for msg := range base.goInbox {
|
||||||
|
switch msg.Tag {
|
||||||
|
case mtagOnMinecraftChat:
|
||||||
|
p := msg.OnMinecraftChat
|
||||||
|
if p == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.Content == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered := strings.ReplaceAll(p.Content, "`", "\\`")
|
||||||
|
filtered = strings.ReplaceAll(filtered, "|", "\\|")
|
||||||
|
filtered = strings.ReplaceAll(filtered, "*", "\\*")
|
||||||
|
filtered = strings.ReplaceAll(filtered, "_", "\\_")
|
||||||
|
filtered = strings.ReplaceAll(filtered, "#", "\\#")
|
||||||
|
filtered = strings.ReplaceAll(filtered, "[", "\\[")
|
||||||
|
filtered = strings.ReplaceAll(filtered, "-", "\\-")
|
||||||
|
filtered = strings.ReplaceAll(filtered, ".", "\\.")
|
||||||
|
filtered = strings.ReplaceAll(filtered, ">", "\\>")
|
||||||
|
log.Printf("<%s> %s\n", p.Sender, p.Content)
|
||||||
|
|
||||||
|
r, _ := utf8.DecodeRuneInString(p.Content)
|
||||||
|
isAlphanumeric := unicode.IsLetter(r) || unicode.IsNumber(r)
|
||||||
|
if !isAlphanumeric {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
s.SendMessageComplex(channelID, api.SendMessageData{
|
||||||
|
Content: fmt.Sprintf("<%s> %s", p.Sender, filtered),
|
||||||
|
AllowedMentions: &api.AllowedMentions{},
|
||||||
|
Flags: discord.SuppressEmbeds | discord.SuppressNotifications,
|
||||||
|
})
|
||||||
|
case mtagNothing:
|
||||||
|
default:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
s.AddHandler(func(c *gateway.MessageCreateEvent) {
|
s.AddHandler(func(c *gateway.MessageCreateEvent) {
|
||||||
if c.Author.Bot {
|
if c.Author.Bot {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Println(c.Author.Username, "sent", c.Content)
|
|
||||||
|
log.Printf("[@%s] %s\n", c.Author.DisplayName, c.Content)
|
||||||
|
base.luaDispatch(message{
|
||||||
|
Tag: mtagOnDiscordChat,
|
||||||
|
OnDiscordChat: &onDiscordChatMessage{
|
||||||
|
SenderUsername: c.Author.Username,
|
||||||
|
SenderNickname: c.Author.DisplayName,
|
||||||
|
Content: c.Content,
|
||||||
|
},
|
||||||
|
})
|
||||||
})
|
})
|
||||||
s.AddIntents(gateway.IntentGuildMessages)
|
s.AddIntents(gateway.IntentGuildMessages)
|
||||||
|
|
||||||
|
|
@ -74,17 +251,16 @@ func main() {
|
||||||
http.HandleFunc("/chat", handleChat)
|
http.HandleFunc("/chat", handleChat)
|
||||||
|
|
||||||
// begin
|
// begin
|
||||||
var wg sync.WaitGroup
|
go func() {
|
||||||
wg.Go(func() {
|
|
||||||
if err := s.Connect(context.Background()); err != nil {
|
if err := s.Connect(context.Background()); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
})
|
}()
|
||||||
wg.Go(func() {
|
go func() {
|
||||||
http.ListenAndServe(httpListen, nil)
|
http.ListenAndServe(httpListen, nil)
|
||||||
})
|
}()
|
||||||
|
|
||||||
// wait
|
// wait
|
||||||
wg.Wait()
|
select {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue