Skip to content

Commit

Permalink
Add very basic Slack support
Browse files Browse the repository at this point in the history
  • Loading branch information
GamePad64 committed Nov 17, 2024
1 parent e84574b commit 1aa20b9
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 20 deletions.
1 change: 1 addition & 0 deletions .idea/notifico.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ members = [
"transports/notifico-telegram",
"transports/notifico-whatsapp",
"transports/notifico-smpp",
"transports/notifico-slack",
"notifico-template",
"notifico-template/migration",
"notifico-worker",
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Notifico

[Documentation](https://notifico.tech)

Notifico is a self-hosted, open-source notification server that delivers real-time notifications
to various clients via email, SMS, messengers and other means of communication.

Expand Down
2 changes: 2 additions & 0 deletions notifico-worker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ notifico-telegram = { path = "../transports/notifico-telegram" }
notifico-smtp = { path = "../transports/notifico-smtp" }
notifico-whatsapp = { path = "../transports/notifico-whatsapp" }
notifico-smpp = { path = "../transports/notifico-smpp" }
notifico-slack = { path = "../transports/notifico-slack" }

notifico-template = { path = "../notifico-template" }
notifico-subscription = { path = "../notifico-subscription" }
notifico-dbpipeline = { path = "../notifico-dbpipeline" }
Expand Down
2 changes: 2 additions & 0 deletions notifico-worker/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use notifico_core::config::credentials::MemoryCredentialStorage;
use notifico_core::engine::Engine;
use notifico_core::pipeline::runner::PipelineRunner;
use notifico_dbpipeline::DbPipelineStorage;
use notifico_slack::SlackPlugin;
use notifico_smpp::SmppPlugin;
use notifico_smtp::EmailPlugin;
use notifico_subscription::SubscriptionManager;
Expand Down Expand Up @@ -98,6 +99,7 @@ async fn main() {
engine.add_plugin(Arc::new(EmailPlugin::new(credentials.clone())));
engine.add_plugin(Arc::new(WaBusinessPlugin::new(credentials.clone())));
engine.add_plugin(Arc::new(SmppPlugin::new(credentials.clone())));
engine.add_plugin(Arc::new(SlackPlugin::new(credentials.clone())));

let subman = Arc::new(SubscriptionManager::new(
db_connection,
Expand Down
20 changes: 0 additions & 20 deletions pipelines.yml

This file was deleted.

11 changes: 11 additions & 0 deletions transports/notifico-slack/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "notifico-slack"
version = "0.1.0"
edition = "2021"

[dependencies]
notifico-core = { path = "../../notifico-core" }
reqwest = "0.12.9"
serde = "1.0.215"
async-trait = "0.1.83"
serde_json = "1.0.133"
109 changes: 109 additions & 0 deletions transports/notifico-slack/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
mod step;

use crate::step::{Step, STEPS};
use async_trait::async_trait;
use notifico_core::credentials::{CredentialStorage, TypedCredential};
use notifico_core::engine::{EnginePlugin, PipelineContext, StepOutput};
use notifico_core::error::EngineError;
use notifico_core::recipient::TypedContact;
use notifico_core::step::SerializedStep;
use notifico_core::templater::RenderedTemplate;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::borrow::Cow;
use std::sync::Arc;

#[derive(Debug, Serialize, Deserialize)]
pub struct SlackCredentials {
token: String,
}

impl TypedCredential for SlackCredentials {
const CREDENTIAL_TYPE: &'static str = "slack";
}

pub struct SlackPlugin {
client: reqwest::Client,
credentials: Arc<dyn CredentialStorage>,
}

impl SlackPlugin {
pub fn new(credentials: Arc<dyn CredentialStorage>) -> Self {
SlackPlugin {
client: reqwest::Client::new(),
credentials,
}
}
}

#[async_trait]
impl EnginePlugin for SlackPlugin {
async fn execute_step(
&self,
context: &mut PipelineContext,
step: &SerializedStep,
) -> Result<StepOutput, EngineError> {
let step: Step = step.clone().convert_step()?;

match step {
Step::Send { credential } => {
let Some(recipient) = context.recipient.clone() else {
return Err(EngineError::RecipientNotSet);
};

let credential: SlackCredentials = self
.credentials
.get_typed_credential(context.project_id, &credential)
.await?;

let contact: SlackContact = recipient.get_primary_contact()?;

for message in context.messages.iter().cloned() {
let content: SlackMessage = message.try_into()?;

let payload = json!({
"channel": contact.channel_id,
"text": content.text,
});

self.client
.post("https://slack.com/api/chat.postMessage")
.header("Authorization", format!("Bearer {}", credential.token))
.json(&payload)
.send()
.await
.unwrap();
}
Ok(StepOutput::Continue)
}
}
}

fn steps(&self) -> Vec<Cow<'static, str>> {
STEPS.iter().map(|&s| s.into()).collect()
}
}

#[derive(Serialize, Deserialize, Clone)]
struct SlackContact {
channel_id: String,
}

impl TypedContact for SlackContact {
const CONTACT_TYPE: &'static str = "slack";
}

#[derive(Serialize, Deserialize, Clone)]
pub struct SlackMessage {
pub text: String,
}

impl TryFrom<RenderedTemplate> for SlackMessage {
type Error = EngineError;

fn try_from(value: RenderedTemplate) -> Result<Self, Self::Error> {
Ok(Self {
text: value.get("text")?.to_string(),
})
}
}
10 changes: 10 additions & 0 deletions transports/notifico-slack/src/step.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[serde(tag = "step")]
pub enum Step {
#[serde(rename = "slack.send")]
Send { credential: String },
}

pub(crate) const STEPS: &[&str] = &["slack.send"];

0 comments on commit 1aa20b9

Please sign in to comment.