Skip to content

Commit

Permalink
Merge pull request #1465 from miahemu/notification
Browse files Browse the repository at this point in the history
Add Webhook
  • Loading branch information
miahemu authored Jul 22, 2024
2 parents 298060b + cd68edc commit 2173b05
Show file tree
Hide file tree
Showing 9 changed files with 402 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package ai.chat2db.server.domain.api.param.message;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

/**
* @author Juechen
* @version : MessageCreateParam.java
*/
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class MessageCreateParam {

/**
* 平台类型
* @see ai.chat2db.server.domain.core.enums.ExternalNotificationTypeEnum
*/
private String platformType;

/**
* 服务URL
*/
private String serviceUrl;

/**
* 密钥
*/
private String secretKey;

/**
* 消息模版
*/
private String textTemplate;


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ai.chat2db.server.domain.api.service;

import ai.chat2db.server.domain.api.param.message.MessageCreateParam;

/**
* @author Juechen
* @version : WebhookSender.java
*/
public interface WebhookSender {

void sendMessage(MessageCreateParam param);

}
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,15 @@
<artifactId>chat2db-sqlserver</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.1</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package ai.chat2db.server.domain.core.enums;

import ai.chat2db.server.domain.api.service.WebhookSender;
import ai.chat2db.server.domain.core.notification.DingTalkWebhookSender;
import ai.chat2db.server.domain.core.notification.LarkWebhookSender;
import ai.chat2db.server.domain.core.notification.WeComWebhookSender;
import ai.chat2db.server.tools.base.enums.BaseEnum;
import lombok.Getter;

/**
* @author Juechen
* @version : ExternalNotificationTypeEnum.java
*/
@Getter
public enum ExternalNotificationTypeEnum implements BaseEnum<String> {

/**
* 企业微信
*/
WECOM("WeCom", WeComWebhookSender.class),

/**
* 钉钉
*/
DINGTALK("DingTalk", DingTalkWebhookSender.class),

/**
* 飞书
*/
LARK("Lark", LarkWebhookSender.class),

;

final String description;

final Class<? extends WebhookSender> webhookSender;


@Override
public String getCode() {
return this.name();
}

public static WebhookSender getWebhookSender(String platformType) {
String lowerCasePlatformType = platformType.toLowerCase();
switch (lowerCasePlatformType) {
case "wecom":
return new WeComWebhookSender();
case "dingtalk":
return new DingTalkWebhookSender();
case "lark":
return new LarkWebhookSender();
default:
return null;
}
}

/**
* Get enum by name
*
* @param name
* @return
*/
public static ExternalNotificationTypeEnum getByName(String name) {
for (ExternalNotificationTypeEnum dbTypeEnum : ExternalNotificationTypeEnum.values()) {
if (dbTypeEnum.name().equalsIgnoreCase(name)) {
return dbTypeEnum;
}
}
return null;
}

ExternalNotificationTypeEnum(String description, Class<? extends WebhookSender> webhookSender) {
this.description = description;
this.webhookSender = webhookSender;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ai.chat2db.server.domain.core.notification;

import ai.chat2db.server.domain.api.param.message.MessageCreateParam;
import ai.chat2db.server.domain.api.service.WebhookSender;
import ai.chat2db.server.domain.core.enums.ExternalNotificationTypeEnum;
import org.springframework.stereotype.Service;

@Service
public class BaseWebhookSender implements WebhookSender {

/**
* Sends a message through the specified webhook platform.
*
* @param param The parameter object containing message details and platform type.
* @throws IllegalArgumentException if the provided param is null or invalid.
* @throws RuntimeException if an error occurs while attempting to send the message.
*/
public void sendMessage(MessageCreateParam param) throws IllegalArgumentException {
// Validate the input parameter to ensure it's not null and meets the necessary criteria.
if (param == null || param.getPlatformType() == null) {
throw new IllegalArgumentException("MessageCreateParam or its platform type cannot be null.");
}

try {
// Attempt to retrieve the appropriate WebhookSender based on the platform type.
ExternalNotificationTypeEnum extern = ExternalNotificationTypeEnum.getByName(param.getPlatformType());
WebhookSender sender = extern.getWebhookSender(param.getPlatformType());

// Guard clause for null sender. Ideally, getWebhookSender should prevent this, but it's good to be cautious.
if (sender == null) {
throw new RuntimeException("Failed to retrieve WebhookSender for platform type: " + param.getPlatformType());
}

// Send the message. Any exceptions thrown by sendMessage should be caught and handled here.
sender.sendMessage(param);
} catch (Exception e) {
// Wrap and re-throw any runtime exceptions as a checked exception specific to webhook sending.
throw new RuntimeException("An error occurred while sending the message.", e);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package ai.chat2db.server.domain.core.notification;

import ai.chat2db.server.domain.api.param.message.MessageCreateParam;
import okhttp3.*;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;

/**
* @author Juechen
* @version : DingTalkWebhookSender.java
*/
@Service
public class DingTalkWebhookSender extends BaseWebhookSender {

private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";

@Override
public void sendMessage(MessageCreateParam param) {
try {
OkHttpClient client = new OkHttpClient();
String secret = param.getSecretKey();
Long timestamp = System.currentTimeMillis();

String sign = generateSign(secret, timestamp);

String webhookUrl = param.getServiceUrl() + "&sign=" + sign + "&timestamp=" + timestamp;


String payload = "{\"msgtype\": \"text\",\"text\": {\"content\": \"" + param.getTextTemplate() + "\"}}";
RequestBody requestBody = RequestBody.create(payload, MediaType.parse("application/json; charset=utf-8"));

Request request = new Request.Builder()
.url(webhookUrl)
.post(requestBody)
.header("Content-Type", "application/json")
.build();


Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new RuntimeException("Failed to send message: " + response.code());
}
System.out.println(response.body().string());
} catch (IOException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}

}

private static String generateSign(String secret, Long timestamp) throws NoSuchAlgorithmException, UnsupportedEncodingException, InvalidKeyException {
String stringToSign = timestamp + "\n" + secret;
Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256"));
byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8"));
return URLEncoder.encode(new String(Base64.encodeBase64(signData)), "UTF-8");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package ai.chat2db.server.domain.core.notification;

import ai.chat2db.server.domain.api.param.message.MessageCreateParam;
import okhttp3.*;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import org.apache.commons.codec.binary.Base64;
import org.springframework.stereotype.Service;

/**
* @author Juechen
* @version : LarkWebhookSender.java
*/
@Service
public class LarkWebhookSender extends BaseWebhookSender {

private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";

@Override
public void sendMessage(MessageCreateParam param) {
try {
OkHttpClient client = new OkHttpClient();
String webhookUrl = param.getServiceUrl();
String secret = param.getSecretKey();
int timestamp = (int) (System.currentTimeMillis() / 1000);

String signature = GenSign(secret, timestamp);

String payload = "{\"timestamp\": \"" + timestamp
+ "\",\"sign\": \"" + signature
+ "\",\"msg_type\":\"text\",\"content\":{\"text\":\""+ param.getTextTemplate() +"\"}}";
RequestBody body = RequestBody.create(payload, MediaType.parse("application/json; charset=utf-8"));


Request request = new Request.Builder()
.url(webhookUrl)
.post(body)
.addHeader("Content-Type", "application/json")
.build();

Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new RuntimeException("Failed to send message: " + response.code());
}
System.out.println(response.body().string());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}

}

private static String GenSign(String secret, int timestamp) throws NoSuchAlgorithmException, InvalidKeyException {
String stringToSign = timestamp + "\n" + secret;
Mac mac = Mac.getInstance(HMAC_SHA256_ALGORITHM);
mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), HMAC_SHA256_ALGORITHM));
byte[] signData = mac.doFinal(new byte[]{});
return new String(Base64.encodeBase64(signData));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ai.chat2db.server.domain.core.notification;

import ai.chat2db.server.domain.api.param.message.MessageCreateParam;
import okhttp3.*;
import org.springframework.stereotype.Service;

import java.io.IOException;

/**
* @author Juechen
* @version : WeComWebhookSender.java
*/
@Service
public class WeComWebhookSender extends BaseWebhookSender {

@Override
public void sendMessage(MessageCreateParam param) {
try {
OkHttpClient client = new OkHttpClient();
String webhookUrl = param.getServiceUrl();
String text = param.getTextTemplate();

String payload = "{\"msgtype\": \"text\",\"text\": {\"content\": \"" + text + "\"}}";

RequestBody requestBody = RequestBody.create(payload, MediaType.parse("application/json; charset=utf-8"));

Request request = new Request.Builder()
.url(webhookUrl)
.post(requestBody)
.addHeader("Content-Type", "application/json")
.build();

Response response = client.newCall(request).execute();
if (!response.isSuccessful()) {
throw new RuntimeException("Failed to send message: " + response.code());
}
System.out.println(response.body().string());
} catch (IOException e) {
throw new RuntimeException(e);
}

}
}
Loading

0 comments on commit 2173b05

Please sign in to comment.