Files

96 lines
2.7 KiB
Go
Raw Permalink Normal View History

// Hello plugin — Phase C Step 9 proof-of-life.
//
// Build with TinyGo:
//
// tinygo build -o hello.wasm -target wasi ./main.go
//
// The module exports the five functions the OwnCord plugin ABI requires:
//
// allocate(size) → ptr — host writes input into module memory
// deallocate(ptr, size) — host signals it's done with a buffer
// list_commands() → (ptr, len) — JSON array of command names
// command_dispatch(p, l) → (ptr, len) — handle a slash command, return JSON reply
// on_event(ptr, len) — receive a broadcast event (no-op here)
//
// The file is guarded with the "tinygo" build constraint so the standard Go
// toolchain (go build / go vet) ignores it. Compile with:
//
// tinygo build -o hello.wasm -target wasi ./main.go
//go:build tinygo
package main
import (
"encoding/json"
"unsafe"
)
func main() {}
// allocations keeps live references so the GC does not reclaim buffers the
// host is still holding a pointer to.
var allocations [][]byte
//export allocate
func allocate(size uint32) uint32 {
buf := make([]byte, size)
allocations = append(allocations, buf)
return uint32(uintptr(unsafe.Pointer(&buf[0])))
}
//export deallocate
func deallocate(ptr uint32, size uint32) {
for i, b := range allocations {
if uint32(len(b)) == size && uint32(uintptr(unsafe.Pointer(&b[0]))) == ptr {
allocations = append(allocations[:i], allocations[i+1:]...)
return
}
}
}
// listCommandsJSON is the static payload returned by list_commands.
var listCommandsJSON = []byte(`["hello"]`)
//export list_commands
func listCommands() (uint32, uint32) {
return uint32(uintptr(unsafe.Pointer(&listCommandsJSON[0]))), uint32(len(listCommandsJSON))
}
type dispatchInput struct {
UserID int64 `json:"user_id"`
ChannelID int64 `json:"channel_id"`
Command string `json:"command"`
Args []string `json:"args"`
}
type dispatchOutput struct {
Reply string `json:"reply"`
}
// resultBuf holds the most recent command_dispatch result. Safe because the
// host is single-threaded within a plugin invocation.
var resultBuf []byte
//export command_dispatch
func commandDispatch(ptr uint32, length uint32) (uint32, uint32) {
raw := unsafe.Slice((*byte)(unsafe.Pointer(uintptr(ptr))), length)
var in dispatchInput
if err := json.Unmarshal(raw, &in); err != nil {
resultBuf = []byte(`{"reply":"hello: malformed payload"}`)
} else {
reply := "Hello from the hello plugin!"
if len(in.Args) > 0 {
reply = "Hello, " + in.Args[0] + "!"
}
out, _ := json.Marshal(dispatchOutput{Reply: reply})
resultBuf = out
}
return uint32(uintptr(unsafe.Pointer(&resultBuf[0]))), uint32(len(resultBuf))
}
//export on_event
func onEvent(_ uint32, _ uint32) {}