-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1465 from miahemu/notification
Add Webhook
- Loading branch information
Showing
9 changed files
with
402 additions
and
0 deletions.
There are no files selected for viewing
40 changes: 40 additions & 0 deletions
40
...main-api/src/main/java/ai/chat2db/server/domain/api/param/message/MessageCreateParam.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
|
||
|
||
} |
13 changes: 13 additions & 0 deletions
13
...b-server-domain-api/src/main/java/ai/chat2db/server/domain/api/service/WebhookSender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
...-core/src/main/java/ai/chat2db/server/domain/core/enums/ExternalNotificationTypeEnum.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
42 changes: 42 additions & 0 deletions
42
...main-core/src/main/java/ai/chat2db/server/domain/core/notification/BaseWebhookSender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
|
||
} |
70 changes: 70 additions & 0 deletions
70
...-core/src/main/java/ai/chat2db/server/domain/core/notification/DingTalkWebhookSender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 + "×tamp=" + 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"); | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
...main-core/src/main/java/ai/chat2db/server/domain/core/notification/LarkWebhookSender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
...ain-core/src/main/java/ai/chat2db/server/domain/core/notification/WeComWebhookSender.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
|
||
} | ||
} |
Oops, something went wrong.