Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions plugins/examples/cln-plugin-startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use cln_plugin::options::{
DefaultStringArrayConfigOption, IntegerArrayConfigOption, IntegerConfigOption,
StringArrayConfigOption,
};
use cln_plugin::{messages, Builder, Error, Plugin};
use cln_plugin::{messages, Builder, Error, HookBuilder, Plugin};

const TEST_NOTIF_TAG: &str = "test_custom_notification";

Expand Down Expand Up @@ -79,7 +79,12 @@ async fn main() -> Result<(), anyhow::Error> {
.rpcmethod("test-log-levels", "send on all log levels", test_log_levels)
.subscribe("connect", connect_handler)
.subscribe("test_custom_notification", test_receive_custom_notification)
.hook("peer_connected", peer_connected_handler)
.hook_from_builder(
HookBuilder::new("peer_connected", peer_connected_handler)
.after(Vec::new())
.before(Vec::new())
.filters(Vec::new()),
)
.notification(messages::NotificationTopic::new(TEST_NOTIF_TAG))
.start(state)
.await?
Expand Down
10 changes: 8 additions & 2 deletions plugins/lsps-plugin/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ use cln_lsps::jsonrpc::client::JsonRpcClient;
use cln_lsps::lsps0::primitives::Msat;
use cln_lsps::lsps0::{
self,
transport::{Bolt8Transport, CustomMessageHookManager, WithCustomMessageHookManager},
transport::{
Bolt8Transport, CustomMessageHookManager, WithCustomMessageHookManager, LSPS0_MESSAGE_TYPE,
},
};
use cln_lsps::lsps2::cln::tlv::encode_tu64;
use cln_lsps::lsps2::cln::{
Expand All @@ -19,6 +21,7 @@ use cln_lsps::lsps2::model::{
use cln_lsps::util;
use cln_lsps::LSP_FEATURE_BIT;
use cln_plugin::options;
use cln_plugin::{HookBuilder, HookFilter};
use cln_rpc::model::requests::{
DatastoreMode, DatastoreRequest, DeldatastoreRequest, DelinvoiceRequest, DelinvoiceStatus,
ListdatastoreRequest, ListinvoicesRequest, ListpeersRequest,
Expand Down Expand Up @@ -55,7 +58,10 @@ async fn main() -> Result<(), anyhow::Error> {
let state = State { hook_manager };

if let Some(plugin) = cln_plugin::Builder::new(tokio::io::stdin(), tokio::io::stdout())
.hook("custommsg", CustomMessageHookManager::on_custommsg::<State>)
.hook_from_builder(
HookBuilder::new("custommsg", CustomMessageHookManager::on_custommsg::<State>)
.filters(vec![HookFilter::Int(i64::from(LSPS0_MESSAGE_TYPE))]),
)
.option(OPTION_ENABLED)
.rpcmethod(
"lsps-listprotocols",
Expand Down
5 changes: 4 additions & 1 deletion plugins/lsps-plugin/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use cln_lsps::lsps2::model::{Lsps2BuyRequest, Lsps2GetInfoRequest};
use cln_lsps::util::wrap_payload_with_peer_id;
use cln_lsps::{lsps0, lsps2};
use cln_plugin::Plugin;
use cln_plugin::{HookBuilder, HookFilter};
use cln_rpc::notifications::CustomMsgNotification;
use cln_rpc::primitives::PublicKey;
use log::debug;
Expand Down Expand Up @@ -42,7 +43,9 @@ async fn main() -> Result<(), anyhow::Error> {
// cln_plugin::FeatureBitsKind::Init,
// util::feature_bit_to_hex(LSP_FEATURE_BIT),
// )
.hook("custommsg", on_custommsg)
.hook_from_builder(HookBuilder::new("custommsg", on_custommsg).filters(vec![
HookFilter::Int(i64::from(lsps0::transport::LSPS0_MESSAGE_TYPE)),
]))
.hook("htlc_accepted", on_htlc_accepted)
.configure()
.await?
Expand Down
95 changes: 94 additions & 1 deletion plugins/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::codec::{JsonCodec, JsonRpcCodec};
pub use anyhow::anyhow;
use anyhow::{Context, Result};
use futures::sink::SinkExt;
use serde::Serialize;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
extern crate log;
use log::trace;
Expand Down Expand Up @@ -204,12 +205,21 @@ where
self.hooks.insert(
hookname.to_string(),
Hook {
name: hookname.to_string(),
callback: Box::new(move |p, r| Box::pin(callback(p, r))),
before: Vec::new(),
after: Vec::new(),
filters: None,
},
);
self
}

pub fn hook_from_builder(mut self, hook: HookBuilder<S>) -> Builder<S, I, O> {
self.hooks.insert(hook.name.clone(), hook.build());
self
}

/// Register a custom RPC method for the RPC passthrough from the
/// main daemon
pub fn rpcmethod<C, F>(mut self, name: &str, description: &str, callback: C) -> Builder<S, I, O>
Expand Down Expand Up @@ -411,10 +421,21 @@ where
.chain(self.wildcard_subscription.iter().map(|_| String::from("*")))
.collect();

let hooks: Vec<messages::Hook> = self
.hooks
.values()
.map(|v| messages::Hook {
name: v.name.clone(),
before: v.before.clone(),
after: v.after.clone(),
filters: v.filters.clone(),
})
.collect();

messages::GetManifestResponse {
options: self.options.values().cloned().collect(),
subscriptions,
hooks: self.hooks.keys().map(|s| s.clone()).collect(),
hooks,
rpcmethods,
notifications: self.notifications.clone(),
featurebits: self.featurebits.clone(),
Expand Down Expand Up @@ -458,6 +479,56 @@ where
}
}

impl<S> HookBuilder<S>
where
S: Send + Clone,
{
pub fn new<C, F>(name: &str, callback: C) -> Self
where
C: Send + Sync + 'static,
C: Fn(Plugin<S>, Request) -> F + 'static,
F: Future<Output = Response> + Send + 'static,
{
Self {
name: name.to_string(),
callback: Box::new(move |p, r| Box::pin(callback(p, r))),
before: Vec::new(),
after: Vec::new(),
filters: None,
}
}

pub fn before(mut self, before: Vec<String>) -> Self {
self.before = before;
self
}

pub fn after(mut self, after: Vec<String>) -> Self {
self.after = after;
self
}

pub fn filters(mut self, filters: Vec<HookFilter>) -> Self {
// Empty Vec would filter everything, must be None instead to not get serialized
if filters.is_empty() {
self.filters = None;
} else {
self.filters = Some(filters);
}
self
}

fn build(self) -> Hook<S> {
Hook {
callback: self.callback,
name: self.name,
before: self.before,
after: self.after,
filters: self.filters,
}
}
}

impl<S> RpcMethodBuilder<S>
where
S: Send + Clone,
Expand Down Expand Up @@ -542,7 +613,29 @@ struct Hook<S>
where
S: Clone + Send,
{
name: String,
callback: AsyncCallback<S>,
before: Vec<String>,
after: Vec<String>,
filters: Option<Vec<HookFilter>>,
}

pub struct HookBuilder<S>
where
S: Clone + Send,
{
name: String,
callback: AsyncCallback<S>,
before: Vec<String>,
after: Vec<String>,
filters: Option<Vec<HookFilter>>,
}

#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
pub enum HookFilter {
Str(String),
Int(i64),
}

impl<S> Plugin<S>
Expand Down
11 changes: 10 additions & 1 deletion plugins/src/messages.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::options::UntypedConfigOption;
use crate::HookFilter;
use serde::de::{self, Deserializer};
use serde::{Deserialize, Serialize};
use serde_json::Value;
Expand Down Expand Up @@ -150,6 +151,14 @@ pub(crate) struct RpcMethod {
pub(crate) usage: String,
}

#[derive(Serialize, Default, Debug)]
pub(crate) struct Hook {
pub(crate) name: String,
pub(crate) before: Vec<String>,
pub(crate) after: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) filters: Option<Vec<HookFilter>>,
}
#[derive(Serialize, Default, Debug, Clone)]
pub struct NotificationTopic {
pub method: String,
Expand All @@ -175,7 +184,7 @@ pub(crate) struct GetManifestResponse {
pub(crate) rpcmethods: Vec<RpcMethod>,
pub(crate) subscriptions: Vec<String>,
pub(crate) notifications: Vec<NotificationTopic>,
pub(crate) hooks: Vec<String>,
pub(crate) hooks: Vec<Hook>,
pub(crate) dynamic: bool,
pub(crate) featurebits: FeatureBits,
pub(crate) nonnumericids: bool,
Expand Down
Loading