diff --git a/pom.xml b/pom.xml
index 5f69324..d2a9ba5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.n1netails
n1netails-teams-webhook-client
- 0.2.0
+ 0.3.0
jar
n1netails-teams-webhook-client
@@ -55,6 +55,20 @@
gson
2.13.2
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.11.4
+ test
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.11.4
+ test
+
diff --git a/readme.md b/readme.md
index c8565cb..a0672ec 100644
--- a/readme.md
+++ b/readme.md
@@ -47,13 +47,13 @@ Install the teams webhook client by adding the following dependency:
com.n1netails
n1netails-teams-webhook-client
- 0.2.0
+ 0.3.0
```
Gradle (Groovy)
```groovy
-implementation 'com.n1netails:n1netails-teams-webhook-client:0.2.0'
+implementation 'com.n1netails:n1netails-teams-webhook-client:0.3.0'
```
## Usage
@@ -94,12 +94,11 @@ The message card is a more flexible and customizable way to send messages.
```java
import com.n1netails.n1netails.teams.api.TeamsWebhookClient;
import com.n1netails.n1netails.teams.internal.TeamsWebhookClientImpl;
-import com.n1netails.n1netails.teams.model.Fact;
-import com.n1netails.n1netails.teams.model.MessageCard;
-import com.n1netails.n1netails.teams.model.Section;
+import com.n1netails.n1netails.teams.model.*;
import com.n1netails.n1netails.teams.service.WebhookService;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
public class Example {
@@ -120,10 +119,15 @@ public class Example {
facts.add(new Fact("Fact 1", "Fact 1 Value"));
facts.add(new Fact("Fact 2", "Fact 2 Value"));
section.setFacts(facts);
+ section.setImageUrl("https://raw.githubusercontent.com/n1netails/n1netails/refs/heads/main/n1netails_icon_transparent.png");
sections.add(section);
messageCard.setSections(sections);
+ List actions = new ArrayList<>();
+ actions.add(new Action("View Website", "https://github.com/n1netails/n1netails-teams-webhook-client"));
+ messageCard.setActions(actions);
+
client.sendMessage("YOUR_WEBHOOK_URL", messageCard);
} catch (Exception e) {
e.printStackTrace();
@@ -137,6 +141,77 @@ public class Example {
+### Image Message
+
+```java
+import com.n1netails.n1netails.teams.api.TeamsWebhookClient;
+import com.n1netails.n1netails.teams.internal.TeamsWebhookClientImpl;
+import com.n1netails.n1netails.teams.model.WebhookMessage;
+import com.n1netails.n1netails.teams.service.WebhookService;
+
+public class Example {
+ public static void main(String[] args) {
+ try {
+ WebhookService webhookService = new WebhookService();
+ TeamsWebhookClient client = new TeamsWebhookClientImpl(webhookService);
+
+ WebhookMessage message = new WebhookMessage();
+ message.setContent("Hello, from n1netails-teams-webhook-client!");
+ message.setImageUrl("https://raw.githubusercontent.com/n1netails/n1netails/refs/heads/main/n1netails_icon_transparent.png");
+
+ List actions = new ArrayList<>();
+ actions.add(new Action("Visit Website", "https://github.com/n1netails/n1netails-teams-webhook-client"));
+ message.setActions(actions);
+
+ client.sendMessage("YOUR_WEBHOOK_URL", message);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+```
+
+#### Example image message output
+
+

+
+
+### GIF Message
+
+```java
+import com.n1netails.n1netails.teams.api.TeamsWebhookClient;
+import com.n1netails.n1netails.teams.internal.TeamsWebhookClientImpl;
+import com.n1netails.n1netails.teams.model.WebhookMessage;
+import com.n1netails.n1netails.teams.service.WebhookService;
+
+public class Example {
+ public static void main(String[] args) {
+ try {
+ WebhookService webhookService = new WebhookService();
+ TeamsWebhookClient client = new TeamsWebhookClientImpl(webhookService);
+
+ WebhookMessage message = new WebhookMessage();
+ message.setContent("Hello, from n1netails-teams-webhook-client!");
+ // Gifs are also supported
+ message.setImageUrl("https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExYmEwajlsZjBqb2d2Zmk1bmJoYTEyN2Q0czRmOWtmYjd4YWVzMHoxaCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/xsE65jaPsUKUo/giphy.gif");
+
+ List actions = new ArrayList<>();
+ actions.add(new Action("Visit Website", "https://github.com/n1netails/n1netails-teams-webhook-client"));
+ message.setActions(actions);
+
+ client.sendMessage("YOUR_WEBHOOK_URL", message);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
+```
+
+#### Example gif message output
+
+

+
+
# Develop
## Build
diff --git a/src/main/java/com/n1netails/n1netails/teams/model/Action.java b/src/main/java/com/n1netails/n1netails/teams/model/Action.java
new file mode 100644
index 0000000..3883d73
--- /dev/null
+++ b/src/main/java/com/n1netails/n1netails/teams/model/Action.java
@@ -0,0 +1,29 @@
+package com.n1netails.n1netails.teams.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Action for MessageCard
+ */
+@Getter
+@Setter
+public class Action {
+ private String name;
+ private String target;
+
+ /**
+ * Action Constructor
+ */
+ public Action() {}
+
+ /**
+ * Action Constructor with parameters
+ * @param name Name of the action
+ * @param target Target URL of the action
+ */
+ public Action(String name, String target) {
+ this.name = name;
+ this.target = target;
+ }
+}
diff --git a/src/main/java/com/n1netails/n1netails/teams/model/MessageCard.java b/src/main/java/com/n1netails/n1netails/teams/model/MessageCard.java
index cbc1860..f183db8 100644
--- a/src/main/java/com/n1netails/n1netails/teams/model/MessageCard.java
+++ b/src/main/java/com/n1netails/n1netails/teams/model/MessageCard.java
@@ -18,6 +18,7 @@ public class MessageCard {
private String title;
private String summary;
private List sections;
+ private List actions;
/**
* Message Card Constructor
diff --git a/src/main/java/com/n1netails/n1netails/teams/model/Section.java b/src/main/java/com/n1netails/n1netails/teams/model/Section.java
index a6a1b86..fb01de9 100644
--- a/src/main/java/com/n1netails/n1netails/teams/model/Section.java
+++ b/src/main/java/com/n1netails/n1netails/teams/model/Section.java
@@ -14,6 +14,7 @@ public class Section {
private String title;
private List facts;
+ private String imageUrl;
/**
* Section Constructor
diff --git a/src/main/java/com/n1netails/n1netails/teams/model/WebhookMessage.java b/src/main/java/com/n1netails/n1netails/teams/model/WebhookMessage.java
index 29a88db..90d8020 100644
--- a/src/main/java/com/n1netails/n1netails/teams/model/WebhookMessage.java
+++ b/src/main/java/com/n1netails/n1netails/teams/model/WebhookMessage.java
@@ -3,6 +3,8 @@
import lombok.Getter;
import lombok.Setter;
+import java.util.List;
+
/**
* Teams Webhook Message
* @author shahid foy
@@ -12,6 +14,8 @@
public class WebhookMessage {
private String content;
+ private String imageUrl;
+ private List actions;
/**
* Webhook Message Constructor
diff --git a/src/main/java/com/n1netails/n1netails/teams/model/WebhookPayload.java b/src/main/java/com/n1netails/n1netails/teams/model/WebhookPayload.java
index 9cc715a..63b059e 100644
--- a/src/main/java/com/n1netails/n1netails/teams/model/WebhookPayload.java
+++ b/src/main/java/com/n1netails/n1netails/teams/model/WebhookPayload.java
@@ -45,6 +45,7 @@ public static class Content {
private String type;
private String version;
private List body;
+ private List actions;
/**
* Content Constructor
@@ -80,10 +81,43 @@ public static class BodyItem {
private String size;
private boolean wrap;
private boolean separator;
+ private String url;
+ private List sources;
/**
* Body Item Constructor
*/
public BodyItem() {}
}
+
+ /**
+ * Media Source
+ */
+ @Setter
+ @Getter
+ public static class MediaSource {
+ private String mimeType;
+ private String url;
+
+ /**
+ * Media Source Constructor
+ */
+ public MediaSource() {}
+ }
+
+ /**
+ * Action Item
+ */
+ @Setter
+ @Getter
+ public static class ActionItem {
+ private String type;
+ private String title;
+ private String url;
+
+ /**
+ * Action Item Constructor
+ */
+ public ActionItem() {}
+ }
}
diff --git a/src/main/java/com/n1netails/n1netails/teams/service/WebhookService.java b/src/main/java/com/n1netails/n1netails/teams/service/WebhookService.java
index e4fc156..f829226 100644
--- a/src/main/java/com/n1netails/n1netails/teams/service/WebhookService.java
+++ b/src/main/java/com/n1netails/n1netails/teams/service/WebhookService.java
@@ -18,14 +18,56 @@
*/
public class WebhookService {
+ /**
+ * Media type for Microsoft Adaptive Cards.
+ */
public static final String APPLICATION_VND_MICROSOFT_CARD_ADAPTIVE = "application/vnd.microsoft.card.adaptive";
+
+ /**
+ * HTTP Adaptive card IO Schemas Adaptive card JSON
+ */
public static final String HTTP_ADAPTIVECARDS_IO_SCHEMAS_ADAPTIVE_CARD_JSON = "http://adaptivecards.io/schemas/adaptive-card.json";
+
+ /**
+ * Adaptive Card schema URL.
+ */
public static final String ADAPTIVE_CARD = "AdaptiveCard";
+
+ /**
+ * Content version
+ */
public static final String VERSION = "1.4";
+
+ /**
+ * Text block
+ */
public static final String TEXT_BLOCK = "TextBlock";
+
+ /**
+ * Bolder
+ */
public static final String BOLDER = "Bolder";
+
+ /**
+ * Medium
+ */
public static final String MEDIUM = "Medium";
+ /**
+ * Image
+ */
+ public static final String IMAGE = "Image";
+
+ /**
+ * Media
+ */
+ public static final String MEDIA = "Media";
+
+ /**
+ * Action Open URL.
+ */
+ public static final String ACTION_OPEN_URL = "Action.OpenUrl";
+
private final Gson gson = new Gson();
/**
@@ -80,7 +122,12 @@ private void send(String webhookUrl, Object message) throws TeamsWebhookExceptio
}
}
- private static WebhookPayload getWebhookPayload(WebhookMessage message) {
+ /**
+ * Generates a webhook payload from a webhook message.
+ * @param message webhook message
+ * @return the webhook payload
+ */
+ public static WebhookPayload getWebhookPayload(WebhookMessage message) {
WebhookPayload payload = new WebhookPayload();
WebhookPayload.Attachment attachment = new WebhookPayload.Attachment();
attachment.setContentType(APPLICATION_VND_MICROSOFT_CARD_ADAPTIVE);
@@ -99,17 +146,46 @@ private static WebhookPayload.Content getContent(WebhookMessage message) {
content.set$schema(HTTP_ADAPTIVECARDS_IO_SCHEMAS_ADAPTIVE_CARD_JSON);
content.setType(ADAPTIVE_CARD);
content.setVersion(VERSION);
- WebhookPayload.BodyItem bodyItem = new WebhookPayload.BodyItem();
- bodyItem.setType(TEXT_BLOCK);
- bodyItem.setText(message.getContent());
- bodyItem.setWeight(BOLDER);
- bodyItem.setSize(MEDIUM);
List bodyItems = new ArrayList<>();
- bodyItems.add(bodyItem);
+
+ if (message.getContent() != null) {
+ WebhookPayload.BodyItem bodyItem = new WebhookPayload.BodyItem();
+ bodyItem.setType(TEXT_BLOCK);
+ bodyItem.setText(message.getContent());
+ bodyItem.setWeight(BOLDER);
+ bodyItem.setSize(MEDIUM);
+ bodyItems.add(bodyItem);
+ }
+
+ if (message.getImageUrl() != null) {
+ WebhookPayload.BodyItem imageItem = new WebhookPayload.BodyItem();
+ imageItem.setType(IMAGE);
+ imageItem.setUrl(message.getImageUrl());
+ bodyItems.add(imageItem);
+ }
+
+ if (message.getActions() != null) {
+ List actions = new ArrayList<>();
+ for (Action action : message.getActions()) {
+ WebhookPayload.ActionItem actionItem = new WebhookPayload.ActionItem();
+ actionItem.setType(ACTION_OPEN_URL);
+ actionItem.setTitle(action.getName());
+ actionItem.setUrl(action.getTarget());
+ actions.add(actionItem);
+ }
+ content.setActions(actions);
+ }
+
content.setBody(bodyItems);
return content;
}
+ /**
+ * Generates a webhook payload from a message card.
+ *
+ * @param card the message card
+ * @return the webhook payload
+ */
public static WebhookPayload getWebhookPayload(MessageCard card) {
WebhookPayload payload = new WebhookPayload();
@@ -175,7 +251,26 @@ private static WebhookPayload.Content getContent(MessageCard card) {
body.add(factBlock);
}
}
+
+ if (section.getImageUrl() != null) {
+ WebhookPayload.BodyItem imageItem = new WebhookPayload.BodyItem();
+ imageItem.setType(IMAGE);
+ imageItem.setUrl(section.getImageUrl());
+ body.add(imageItem);
+ }
+ }
+ }
+
+ if (card.getActions() != null) {
+ List actions = new ArrayList<>();
+ for (Action action : card.getActions()) {
+ WebhookPayload.ActionItem actionItem = new WebhookPayload.ActionItem();
+ actionItem.setType(ACTION_OPEN_URL);
+ actionItem.setTitle(action.getName());
+ actionItem.setUrl(action.getTarget());
+ actions.add(actionItem);
}
+ content.setActions(actions);
}
content.setBody(body);
diff --git a/src/test/java/com/n1netails/n1netails/teams/service/WebhookServiceTest.java b/src/test/java/com/n1netails/n1netails/teams/service/WebhookServiceTest.java
new file mode 100644
index 0000000..5d19474
--- /dev/null
+++ b/src/test/java/com/n1netails/n1netails/teams/service/WebhookServiceTest.java
@@ -0,0 +1,80 @@
+package com.n1netails.n1netails.teams.service;
+
+import com.n1netails.n1netails.teams.model.*;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+public class WebhookServiceTest {
+
+ @Test
+ public void testGetWebhookPayloadFromWebhookMessage() {
+ WebhookMessage message = new WebhookMessage();
+ message.setContent("Test Content");
+ message.setImageUrl("http://example.com/image.png");
+ message.setActions(Collections.singletonList(new Action("Action", "http://example.com")));
+
+ WebhookPayload payload = WebhookService.getWebhookPayload(message);
+
+ assertNotNull(payload);
+ assertEquals(1, payload.getAttachments().size());
+ WebhookPayload.Attachment attachment = payload.getAttachments().get(0);
+ assertEquals(WebhookService.APPLICATION_VND_MICROSOFT_CARD_ADAPTIVE, attachment.getContentType());
+
+ WebhookPayload.Content content = attachment.getContent();
+ assertEquals(WebhookService.HTTP_ADAPTIVECARDS_IO_SCHEMAS_ADAPTIVE_CARD_JSON, content.get$schema());
+ assertEquals(WebhookService.ADAPTIVE_CARD, content.getType());
+ assertEquals(WebhookService.VERSION, content.getVersion());
+
+ assertEquals(2, content.getBody().size());
+
+ WebhookPayload.BodyItem textItem = content.getBody().get(0);
+ assertEquals(WebhookService.TEXT_BLOCK, textItem.getType());
+ assertEquals("Test Content", textItem.getText());
+
+ WebhookPayload.BodyItem imageItem = content.getBody().get(1);
+ assertEquals(WebhookService.IMAGE, imageItem.getType());
+ assertEquals("http://example.com/image.png", imageItem.getUrl());
+
+ assertNotNull(content.getActions());
+ assertEquals(1, content.getActions().size());
+ assertEquals("Action", content.getActions().get(0).getTitle());
+ }
+
+ @Test
+ public void testGetWebhookPayloadFromMessageCard() {
+ MessageCard card = new MessageCard();
+ card.setTitle("Test Title");
+ card.setSummary("Test Summary");
+
+ Section section = new Section();
+ section.setTitle("Section Title");
+ section.setImageUrl("http://example.com/section-image.png");
+// section.setVideoUrl("http://example.com/section-video.mp4");
+ card.setSections(Collections.singletonList(section));
+
+ Action action = new Action("View More", "http://example.com");
+ card.setActions(Collections.singletonList(action));
+
+ WebhookPayload payload = WebhookService.getWebhookPayload(card);
+
+ assertNotNull(payload);
+ WebhookPayload.Content content = payload.getAttachments().get(0).getContent();
+
+ assertEquals(4, content.getBody().size());
+ assertEquals("Test Title", content.getBody().get(0).getText());
+ assertEquals("Test Summary", content.getBody().get(1).getText());
+ assertEquals("Section Title", content.getBody().get(2).getText());
+
+ assertEquals(WebhookService.IMAGE, content.getBody().get(3).getType());
+ assertEquals("http://example.com/section-image.png", content.getBody().get(3).getUrl());
+
+ assertNotNull(content.getActions());
+ assertEquals(1, content.getActions().size());
+ assertEquals(WebhookService.ACTION_OPEN_URL, content.getActions().get(0).getType());
+ assertEquals("View More", content.getActions().get(0).getTitle());
+ assertEquals("http://example.com", content.getActions().get(0).getUrl());
+ }
+}
diff --git a/teams-message-gif.png b/teams-message-gif.png
new file mode 100644
index 0000000..677deff
Binary files /dev/null and b/teams-message-gif.png differ
diff --git a/teams-message-image.png b/teams-message-image.png
new file mode 100644
index 0000000..517631a
Binary files /dev/null and b/teams-message-image.png differ