// 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) {}