If you love gjson, hates Go interfaces and want less boilerplate you'll prefer this over https://github.com/niftynei/glightning.
A c-lightning plugin is expected to work as a daemon that listens to JSON-RPC commands on stdin and returns responses on stdout. It's kept alive by lightningd
for as long as that is alive.
Here you don't have to think about that, just instantiate a p = Plugin{}
object, then call p.Run()
. Done.
To instantiate your Plugin
struct you'll need some values:
Name
: The plugin name, used on logging.Dynamic
: If you want lightningd'splugin start/stop
commands to work on this.Options
: A list of options the plugin will accept fromlightningd initialization. These could be passed either as command-line arguments to
lightningdor written at the
.lightning/config` file.RPCMethods
: A list of RPC methods with names and descriptions and a "usage" string that describes the accepted and required parameters.Subscriptions
: A list of subscriptionHooks
: A list of hook names and handler functions.OnInit
: A function to run after the plugin has initialized. It will have access to the plugin struct (as a parameter) and you can do odd stuff here, like start a webserver or just do one-off things.
From inside the functions and handlers you'll have access to a plugin.Plugin
struct with Args
, the set of options passed to the plugins from lightningd initialization; Log
and Logf
, basic logging functions that will print a line to the lightningd logs prefixed with your plugin name; and Client
, a lightningd-gjson-rpc client you can use to call methods on lightningd.
With the above, the and the Plugins docs you'll be able to make any plugin you want.
package main
import (
"errors"
"time"
"github.com/fiatjaf/lightningd-gjson-rpc/plugin"
)
func main() {
p := plugin.Plugin{
Name: "useless",
Version: "v1.0",
Options: []plugin.Option{
{
"payment-adjective",
"string",
"default-value",
"How do you want your payments to be called.",
},
},
RPCMethods: []plugin.RPCMethod{
{
"donothing",
"arg1 [arg2]",
"does nothing with {arg1} and optional {arg2}.",
"",
func (p *plugin.Plugin, params plugin.Params) (interface{}, int, error) {
arg1 := params.Get("arg1").String()
arg2 := params.Get("arg2").String()
if arg2 != "" && arg2 != arg1 {
return nil, -1, errors.New("arg2 is not equal to arg1!")
}
return map[string]interface{}{
"arg1": arg1,
"arg2": arg2,
}, 0, nil
},
},
},
Subscriptions: []plugin.Subscription{
{
"invoice_payment",
func(p *plugin.Plugin, params plugin.Params) {
label := params.Get("invoice_payment.label").String()
adjective := p.Args.Get("payment-adjective").String()
p.Logf("%s payment received with label %s", adjective, label)
},
},
},
Hooks: []plugin.Hook{
{
"htlc_accepted",
func(p *plugin.Plugin, params plugin.Params) (resp interface{}) {
// hold the invoice just because you want lightning to fail
time.Sleep(30 * time.Minute)
return map[string]interface{}{"result": "continue"}
},
},
},
OnInit: func(p *plugin.Plugin) {
p.Log("already listening for hooks, subscriptions and RPC methods")
},
}
p.Run()
}
We have colored logs!