atUserIds = config.resolveAtUserIds(envVars);
if (StringUtils.isNotBlank(user.getOpenId())) {
- atOpenIds.add(user.getOpenId());
+ atUserIds.add(user.getOpenId());
}
- String title = envVars.expand(config.getTitle());
- MessageModel messageModel = buildJobModel.messageModelBuilder(title)
- .atAll(config.isAtAll()).atOpenIds(atOpenIds).text(text).build();
-
- Logger.log(listener, "当前机器人信息: %s", config.getRobotName());
- SendResult sendResult = service.send(config.getRobotId(), messageModel);
- Logger.log(listener, "发送的消息详情: %s", sendResult.getRequestBody());
- if (!sendResult.isOk()) {
- Logger.error(listener, sendResult.getMsg());
+ if (StringUtils.isNotBlank(user.getMobile())) {
+ atUserIds.add(user.getMobile());
}
+
+ buildJobModel.setTitle(envVars.expand(StringUtils.defaultIfBlank(config.getTitle(), DEFAULT_TITLE)));
+ buildJobModel.setContent(envVars.expand(config.getContent()).replaceAll("\\\\n", LF));
+ String text = config.isRaw() ? envVars.expand(config.getMessage()) : buildJobModel.toMarkdown(robotType);
+
+ MessageModel messageModel = buildJobModel.messageModelBuilder()
+ .atAll(config.isAtAll()).atUserIds(atUserIds).text(text).build();
+
+ service.send(listener, config.getRobotId(), messageModel);
});
}
/**
- * 获取任务的环境变量
+ * Retrieves the environment variables for a given job run.
*
- * @param run 当前运行的任务实例
- * @param listener 任务输出流监听器
- * @return 环境变量键值对
+ * @param run The current job run instance.
+ * @param listener The listener for job output streams.
+ * @return A map of environment variable names to their values.
*/
private EnvVars getEnvVars(Run, ?> run, TaskListener listener) {
EnvVars envVars = new EnvVars();
@@ -162,7 +157,6 @@ private EnvVars getEnvVars(Run, ?> run, TaskListener listener) {
envVars = run.getEnvironment(listener);
envVars.overrideAll(PipelineEnvContext.get());
} catch (IOException | InterruptedException e) {
- log.error(e);
Logger.log(listener, "获取 Job 任务的环境变量时发生异常");
Logger.log(listener, ExceptionUtils.getStackTrace(e));
}
@@ -170,12 +164,13 @@ private EnvVars getEnvVars(Run, ?> run, TaskListener listener) {
}
/**
- * 从给定的 Run 对象和 BuildJobModel 对象中获取信息并更新环境变量。
+ * Updates environment variables with information from the given Run object and BuildJobModel.
+ * This includes executor details, project information, and job status.
*
- * @param run 表示当前构建的 Run 对象。
- * @param listener 用于记录日志和报告错误的 TaskListener 对象。
- * @param buildJobModel 包含要在环境变量中设置的构建与任务信息的 BuildJobModel 对象。
- * @return 更新后的 EnvVars 对象。
+ * @param run The current build run object.
+ * @param listener The task listener for logging and error reporting.
+ * @param buildJobModel The model containing build and job information to set in the environment variables.
+ * @return The updated environment variables object.
*/
private EnvVars fetchUpdateEnvVariables(Run, ?> run, TaskListener listener, BuildJobModel buildJobModel) {
EnvVars envVars = getEnvVars(run, listener);
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/LarkStepListener.java b/src/main/java/io/jenkins/plugins/lark/notice/LarkStepListener.java
new file mode 100644
index 0000000..ac9fd73
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/LarkStepListener.java
@@ -0,0 +1,41 @@
+package io.jenkins.plugins.lark.notice;
+
+import hudson.EnvVars;
+import hudson.Extension;
+import io.jenkins.plugins.lark.notice.context.PipelineEnvContext;
+import lombok.extern.slf4j.Slf4j;
+import org.jenkinsci.plugins.workflow.flow.StepListener;
+import org.jenkinsci.plugins.workflow.steps.Step;
+import org.jenkinsci.plugins.workflow.steps.StepContext;
+import org.springframework.lang.NonNull;
+
+/**
+ * A listener for Jenkins Pipeline steps that merges Pipeline environment variables with the current environment.
+ * This class implements StepListener to handle notifications before a new Pipeline step starts.
+ *
+ * It retrieves the environment variables from the Pipeline context and merges them with the current environment using PipelineEnvContext.
+ * Any exceptions that occur during this process are logged using SLF4J.
+ *
+ * @author xm.z
+ */
+@Slf4j
+@Extension
+public class LarkStepListener implements StepListener {
+
+ /**
+ * This method is called before a new Pipeline step starts, and it merges Pipeline environment variables with the current environment.
+ *
+ * @param step The new Step instance.
+ * @param context Step context information.
+ */
+ @Override
+ public void notifyOfNewStep(@NonNull Step step, @NonNull StepContext context) {
+ try {
+ EnvVars envVars = context.get(EnvVars.class);
+ PipelineEnvContext.merge(envVars);
+ } catch (Exception e) {
+ log.error("[lark] An exception occurred while retrieving environment variables from the pipeline", e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/config/LarkGlobalConfig.java b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkGlobalConfig.java
new file mode 100644
index 0000000..e98c5b8
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkGlobalConfig.java
@@ -0,0 +1,169 @@
+package io.jenkins.plugins.lark.notice.config;
+
+import hudson.Extension;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+import io.jenkins.cli.shaded.org.apache.commons.lang.StringUtils;
+import io.jenkins.plugins.lark.notice.config.LarkRobotConfig.LarkRobotConfigDescriptor;
+import io.jenkins.plugins.lark.notice.enums.NoticeOccasionEnum;
+import jenkins.model.Jenkins;
+import lombok.Getter;
+import lombok.ToString;
+import net.sf.json.JSONArray;
+import net.sf.json.JSONObject;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.DataBoundSetter;
+import org.kohsuke.stapler.StaplerRequest;
+
+import java.net.ProxySelector;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Global configuration for the Lark notification plugin. This class stores settings that are global to Jenkins,
+ * such as proxy configurations, verbosity level, occasions for notifications, and configurations for individual Lark robots.
+ *
+ * @author xm.z
+ */
+@Getter
+@ToString
+@Extension
+@SuppressWarnings("unused")
+public class LarkGlobalConfig extends Descriptor implements Describable {
+
+ private LarkProxyConfig proxyConfig;
+ private boolean verbose;
+ private Set noticeOccasions = Arrays.stream(NoticeOccasionEnum.values()).map(Enum::name).collect(Collectors.toSet());
+ private ArrayList robotConfigs = new ArrayList<>();
+
+ /**
+ * Data-bound constructor for setting up global Lark notification configurations.
+ *
+ * @param proxyConfig Configuration for proxy if it is needed.
+ * @param verbose Whether to enable verbose logging for the plugin.
+ * @param noticeOccasions Occasions on which notifications should be sent.
+ * @param robotConfigs Configurations for individual Lark robots.
+ */
+ @DataBoundConstructor
+ public LarkGlobalConfig(LarkProxyConfig proxyConfig, boolean verbose,
+ Set noticeOccasions, ArrayList robotConfigs) {
+ this.proxyConfig = proxyConfig;
+ this.verbose = verbose;
+ this.noticeOccasions = noticeOccasions;
+ this.robotConfigs = robotConfigs;
+ }
+
+ /**
+ * Default constructor that loads saved configurations or initializes the class with default values.
+ */
+ public LarkGlobalConfig() {
+ super(LarkGlobalConfig.class);
+ load(); // Load saved configuration from disk
+ }
+
+ /**
+ * Gets the singleton instance of the LarkGlobalConfig.
+ *
+ * @return The singleton instance of LarkGlobalConfig.
+ */
+ public static LarkGlobalConfig getInstance() {
+ return Jenkins.get().getDescriptorByType(LarkGlobalConfig.class);
+ }
+
+ /**
+ * Retrieves a specific Lark robot configuration by its ID.
+ *
+ * @param robotId The ID of the robot whose configuration is to be retrieved.
+ * @return An Optional containing the LarkRobotConfig if found, otherwise an empty Optional.
+ */
+ public static Optional getRobot(String robotId) {
+ return getInstance().robotConfigs.stream()
+ .filter(item -> robotId.equals(item.getId()))
+ .findAny();
+ }
+
+ /**
+ * Obtains a ProxySelector based on the current proxy configuration.
+ *
+ * @return A ProxySelector if proxy is configured, otherwise null.
+ */
+ public ProxySelector obtainProxySelector() {
+ return proxyConfig == null ? null : proxyConfig.obtainProxySelector();
+ }
+
+ @DataBoundSetter
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ @DataBoundSetter
+ public void setNoticeOccasions(Set noticeOccasions) {
+ this.noticeOccasions = noticeOccasions;
+ }
+
+ @DataBoundSetter
+ public void setProxyConfig(LarkProxyConfig proxyConfig) {
+ this.proxyConfig = proxyConfig;
+ }
+
+ @DataBoundSetter
+ public void setRobotConfigs(ArrayList robotConfigs) {
+ this.robotConfigs = robotConfigs;
+ }
+
+ /**
+ * Customizes the behavior when the global configuration form is submitted.
+ * Filters out robot configs without a webhook URL.
+ *
+ * @param req The StaplerRequest containing the form submission.
+ * @param json The JSONObject representing the submitted form data.
+ * @return true if successful, throwing FormException otherwise.
+ * @throws FormException If there's an error processing the form submission.
+ */
+ @Override
+ public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
+ Object robotConfigObj = json.get("robotConfigs");
+ if (robotConfigObj == null) {
+ json.put("robotConfigs", new JSONArray());
+ } else {
+ JSONArray robotConfigs = JSONArray.fromObject(robotConfigObj);
+ robotConfigs.removeIf(item -> {
+ JSONObject jsonObject = JSONObject.fromObject(item);
+ String webhook = jsonObject.getString("webhook");
+ return StringUtils.isEmpty(webhook);
+ });
+ }
+ req.bindJSON(this, json);
+ // Additional form processing can be done here
+ save(); // Save the configuration to disk
+ return super.configure(req, json);
+ }
+
+ /**
+ * Returns all possible notice occasions as defined in NoticeOccasionEnum.
+ *
+ * @return An array of NoticeOccasionEnum values.
+ */
+ public NoticeOccasionEnum[] getAllNoticeOccasions() {
+ return NoticeOccasionEnum.values();
+ }
+
+ @Override
+ public Descriptor getDescriptor() {
+ return this;
+ }
+
+ /**
+ * Additional getters for UI binding
+ */
+ public LarkProxyConfig getLarkProxyConfig() {
+ return Jenkins.get().getDescriptorByType(LarkProxyConfig.class);
+ }
+
+ public LarkRobotConfigDescriptor getLarkRobotConfigDescriptor() {
+ return Jenkins.get().getDescriptorByType(LarkRobotConfigDescriptor.class);
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/config/LarkNotifierConfig.java b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkNotifierConfig.java
new file mode 100644
index 0000000..d357a4b
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkNotifierConfig.java
@@ -0,0 +1,211 @@
+package io.jenkins.plugins.lark.notice.config;
+
+import hudson.EnvVars;
+import hudson.Extension;
+import hudson.model.AbstractDescribableImpl;
+import hudson.model.Descriptor;
+import io.jenkins.plugins.lark.notice.enums.NoticeOccasionEnum;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import org.apache.commons.lang.StringUtils;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static io.jenkins.plugins.lark.notice.sdk.constant.Constants.COMMA;
+import static io.jenkins.plugins.lark.notice.sdk.constant.Constants.LF;
+
+/**
+ * Configuration class for Lark notifier. This class holds the settings for a Lark notification,
+ * such as message format, robot details, and occasions for sending notifications.
+ *
+ * @author xm.z
+ */
+@Getter
+@Setter
+@ToString
+@NoArgsConstructor
+public class LarkNotifierConfig extends AbstractDescribableImpl {
+
+ /**
+ * Whether to use the raw message format.
+ */
+ private boolean raw;
+
+ /**
+ * Whether the configuration is disabled.
+ */
+ private boolean disabled;
+
+ /**
+ * Whether the configuration is selected.
+ */
+ private boolean checked;
+
+ /**
+ * Robot ID.
+ */
+ private String robotId;
+
+ /**
+ * Robot name.
+ */
+ private String robotName;
+
+ /**
+ * Whether to @mention all users.
+ */
+ private boolean atAll;
+
+ /**
+ * OpenID of the user(s) to @mention.
+ */
+ private String atUserId;
+
+ /**
+ * Message title.
+ */
+ private String title;
+
+ /**
+ * Message content.
+ */
+ private String content;
+
+ /**
+ * Message template.
+ */
+ private String message;
+
+ /**
+ * Set of occasions for which notifications should be sent.
+ */
+ private Set noticeOccasions;
+
+ /**
+ * DataBoundConstructor for binding form data to LarkNotifierConfig instance.
+ *
+ * @param raw Whether to use raw message format
+ * @param disabled Whether the configuration is disabled
+ * @param checked Whether the configuration is selected
+ * @param robotId Robot ID
+ * @param robotName Robot name
+ * @param atAll Whether to @mention all users
+ * @param atUserId OpenID of the user(s) to @mention
+ * @param title Message title
+ * @param content Message content
+ * @param message Message template
+ * @param noticeOccasions Set of occasions for notifications
+ */
+ @DataBoundConstructor
+ public LarkNotifierConfig(boolean raw, boolean disabled, boolean checked, String robotId, String robotName,
+ boolean atAll, String atUserId, String title, String content, String message, Set noticeOccasions) {
+ this.raw = raw;
+ this.disabled = disabled;
+ this.checked = checked;
+ this.robotId = robotId;
+ this.robotName = robotName;
+ this.atAll = atAll;
+ this.atUserId = atUserId;
+ this.title = title;
+ this.content = content;
+ this.message = message;
+ this.noticeOccasions = noticeOccasions;
+ }
+
+ /**
+ * Constructs a LarkNotifierConfig instance from a given robotConfig.
+ * Uses default noticeOccasion values.
+ *
+ * @param robotConfig Lark robot configuration
+ */
+ public LarkNotifierConfig(LarkRobotConfig robotConfig) {
+ this(false, false, false, robotConfig.getId(), robotConfig.getName(), false,
+ null, null, null, null, getDefaultNoticeOccasions());
+ }
+
+ /**
+ * Retrieves the default set of notice occasions.
+ *
+ * @return Default set of notice occasions
+ */
+ private static Set getDefaultNoticeOccasions() {
+ return LarkGlobalConfig.getInstance().getNoticeOccasions();
+ }
+
+ /**
+ * Gets the notice occasions.
+ * Returns the default notice occasions if the current notifier's noticeOccasions field is null.
+ *
+ * @return Set of notice occasions
+ */
+ public Set getNoticeOccasions() {
+ return noticeOccasions == null ? getDefaultNoticeOccasions() : noticeOccasions;
+ }
+
+ /**
+ * Resolves atUserIds based on environment variables.
+ * Returns an empty Set if atUserId property is empty.
+ *
+ * @param envVars Environment variables
+ * @return Set of resolved atUserIds
+ */
+ public Set resolveAtUserIds(EnvVars envVars) {
+ if (StringUtils.isEmpty(atUserId)) {
+ return new HashSet<>(16);
+ }
+ String realOpenId = envVars.expand(atUserId);
+ return Arrays.stream(StringUtils.split(realOpenId.replace(LF, COMMA), COMMA))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Returns the value of the content property.
+ * Returns an empty string if content is null.
+ *
+ * @return Value of the content property
+ */
+ public String getContent() {
+ return content == null ? "" : content;
+ }
+
+ /**
+ * Copies properties from another notifierConfig instance to this one.
+ *
+ * @param notifierConfig Notification configuration to copy from
+ */
+ public void copy(LarkNotifierConfig notifierConfig) {
+ this.setRaw(notifierConfig.isRaw());
+ this.setDisabled(notifierConfig.isDisabled());
+ this.setChecked(notifierConfig.isChecked());
+ this.setAtAll(notifierConfig.isAtAll());
+ this.setAtUserId(notifierConfig.getAtUserId());
+ this.setTitle(notifierConfig.getTitle());
+ this.setContent(notifierConfig.getContent());
+ this.setMessage(notifierConfig.getMessage());
+ this.setNoticeOccasions(notifierConfig.getNoticeOccasions());
+ }
+
+ /**
+ * Descriptor for LarkNotifierConfig.
+ * Used for displaying properties of LarkNotifierConfig in the Jenkins UI.
+ */
+ @Extension
+ public static class LarkNotifierConfigDescriptor extends Descriptor {
+
+ /**
+ * Retrieves a list of selectable notice occasions.
+ *
+ * @return Array of selectable notice occasions
+ */
+ public NoticeOccasionEnum[] getNoticeOccasionTypes() {
+ return NoticeOccasionEnum.values();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/config/LarkProxyConfig.java b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkProxyConfig.java
new file mode 100644
index 0000000..6559c41
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkProxyConfig.java
@@ -0,0 +1,120 @@
+package io.jenkins.plugins.lark.notice.config;
+
+import hudson.Extension;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.apache.commons.lang.StringUtils;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+import java.io.IOException;
+import java.net.*;
+import java.net.Proxy.Type;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Represents the configuration for Lark notification proxy, used to configure the proxy server information for Lark notifications.
+ *
+ * The LarkProxyConfig class extends Descriptor, indicating that this configuration item can be configured in the Jenkins system configuration center.
+ *
+ * LarkProxyConfig includes the following properties:
+ *
+ * - type: The proxy type, supporting SOCKS and HTTP.
+ * - host: The hostname or IP address of the proxy server.
+ * - port: The port number of the proxy server.
+ *
+ *
+ * LarkProxyConfig contains an obtainProxySelector method for obtaining an instance of the proxy selector for the proxy server.
+ *
+ * @author xm.z
+ */
+@Getter
+@Setter
+@ToString
+@Extension
+public class LarkProxyConfig extends Descriptor implements Describable {
+
+ /**
+ * Proxy type, supporting SOCKS and HTTP.
+ */
+ private Type type;
+
+ /**
+ * Hostname or IP address of the proxy server.
+ */
+ private String host;
+
+ /**
+ * Port number of the proxy server.
+ */
+ private Integer port;
+
+ /**
+ * Creates a new instance of LarkProxyConfig.
+ */
+ public LarkProxyConfig() {
+ super(LarkProxyConfig.class);
+ }
+
+ /**
+ * Constructs a new instance of LarkProxyConfig with input parameters.
+ *
+ * @param type Proxy type.
+ * @param host Hostname or IP address of the proxy server.
+ * @param port Port number of the proxy server.
+ */
+ @DataBoundConstructor
+ public LarkProxyConfig(Type type, String host, int port) {
+ this();
+ this.type = type;
+ this.host = host;
+ this.port = port;
+ }
+
+ /**
+ * Sets the hostname or IP address of the proxy server, removing any leading or trailing whitespace.
+ *
+ * @param host Hostname or IP address of the proxy server.
+ */
+ public void setHost(String host) {
+ if (host != null) {
+ this.host = host.trim();
+ }
+ }
+
+ /**
+ * Returns the descriptor for LarkProxyConfig.
+ *
+ * @return The descriptor for LarkProxyConfig.
+ */
+ @Override
+ public Descriptor getDescriptor() {
+ return this;
+ }
+
+ /**
+ * Obtains an instance of the proxy selector for the proxy server, returning NO_PROXY if no proxy is needed.
+ *
+ * @return An instance of the proxy selector for the proxy server.
+ */
+ public ProxySelector obtainProxySelector() {
+ return new ProxySelector() {
+ @Override
+ public List select(URI uri) {
+ if (type == Type.DIRECT || StringUtils.isEmpty(host) || port == null) {
+ return Collections.singletonList(Proxy.NO_PROXY);
+ }
+ InetSocketAddress inetSocketAddress = new InetSocketAddress(host, port);
+ return Collections.singletonList(new Proxy(type, inetSocketAddress));
+ }
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ /* ignore */
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/config/LarkRobotConfig.java b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkRobotConfig.java
new file mode 100644
index 0000000..327e5ed
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkRobotConfig.java
@@ -0,0 +1,251 @@
+package io.jenkins.plugins.lark.notice.config;
+
+import hudson.Extension;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+import hudson.model.User;
+import hudson.util.FormValidation;
+import hudson.util.FormValidation.Kind;
+import hudson.util.Secret;
+import io.jenkins.plugins.lark.notice.Messages;
+import io.jenkins.plugins.lark.notice.enums.BuildStatusEnum;
+import io.jenkins.plugins.lark.notice.enums.MsgTypeEnum;
+import io.jenkins.plugins.lark.notice.enums.RobotType;
+import io.jenkins.plugins.lark.notice.enums.SecurityPolicyEnum;
+import io.jenkins.plugins.lark.notice.model.BuildJobModel;
+import io.jenkins.plugins.lark.notice.model.MessageModel;
+import io.jenkins.plugins.lark.notice.model.RobotConfigModel;
+import io.jenkins.plugins.lark.notice.sdk.MessageSender;
+import io.jenkins.plugins.lark.notice.sdk.model.SendResult;
+import io.jenkins.plugins.lark.notice.tools.JsonUtils;
+import jenkins.model.Jenkins;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import org.apache.commons.lang.StringUtils;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+
+import java.net.ProxySelector;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static io.jenkins.plugins.lark.notice.sdk.constant.Constants.DEFAULT_TITLE;
+import static io.jenkins.plugins.lark.notice.sdk.constant.Constants.NOTICE_ICON;
+
+/**
+ * Configuration class for Lark robot, including robot ID, name, Webhook key, and a list of security policy configurations
+ *
+ * @author xm.z
+ */
+@Getter
+@Setter
+@ToString
+@NoArgsConstructor
+@SuppressWarnings("unused")
+public class LarkRobotConfig implements Describable {
+
+ /**
+ * Robot ID.
+ */
+ private String id;
+
+ /**
+ * Robot name.
+ */
+ private String name;
+
+ /**
+ * Webhook key, implemented using Jenkins' Secret.
+ */
+ private Secret webhook;
+
+ /**
+ * List of security policy configurations, includes a set of Key-Value pairs.
+ */
+ private List securityPolicyConfigs;
+
+ /**
+ * Constructor for initializing robot configuration object
+ *
+ * @param id Robot ID
+ * @param name Robot name
+ * @param webhook Webhook key
+ * @param securityPolicyConfigs List of security policy configurations
+ */
+ @DataBoundConstructor
+ public LarkRobotConfig(String id, String name, String webhook, List securityPolicyConfigs) {
+ this.id = StringUtils.defaultIfBlank(id, UUID.randomUUID().toString());
+ this.name = name;
+ this.webhook = Secret.fromString(webhook);
+ this.securityPolicyConfigs = securityPolicyConfigs;
+ }
+
+ /**
+ * Gets robot ID, generates a UUID if empty
+ *
+ * @return Robot ID
+ */
+ public String getId() {
+ if (StringUtils.isBlank(id)) {
+ setId(UUID.randomUUID().toString());
+ }
+ return id;
+ }
+
+ /**
+ * Gets the plaintext value of the Webhook key
+ *
+ * @return Webhook key plaintext value
+ */
+ public String getWebhook() {
+ if (webhook == null) {
+ return null;
+ }
+ return webhook.getPlainText();
+ }
+
+ public RobotType obtainRobotType() {
+ String webhook = getWebhook();
+ if (StringUtils.isBlank(webhook)) {
+ return null;
+ }
+ return RobotType.fromUrl(webhook);
+ }
+
+ /**
+ * Gets the list of security policy configurations, includes a set of Key-Value pairs
+ *
+ * @return List of security policy configurations
+ */
+ public List getSecurityPolicyConfigs() {
+ return Arrays.stream(SecurityPolicyEnum.values()).map(enumItem -> {
+ LarkSecurityPolicyConfig policyConfig = LarkSecurityPolicyConfig.of(enumItem);
+ if (securityPolicyConfigs != null) {
+ Optional config = securityPolicyConfigs.stream()
+ .filter(configItem -> enumItem.name().equals(configItem.getType())).findAny();
+ config.ifPresent(t -> policyConfig.setValue(t.getValue()));
+ }
+ return policyConfig;
+ }).collect(Collectors.toList());
+ }
+
+ /**
+ * Gets the descriptor for this class, used to display the robot configuration page in Jenkins
+ *
+ * @return Robot configuration page descriptor
+ */
+ @Override
+ public Descriptor getDescriptor() {
+ return Jenkins.get().getDescriptorByType(LarkRobotConfigDescriptor.class);
+ }
+
+ /**
+ * Descriptor for robot configuration page, used to display the robot configuration page in Jenkins
+ */
+ @Extension
+ public static class LarkRobotConfigDescriptor extends Descriptor {
+
+ /**
+ * Gets the descriptor for the security policy configuration page
+ *
+ * @return Security policy configuration page descriptor
+ */
+ public LarkSecurityPolicyConfig.LarkSecurityPolicyConfigDescriptor getLarkSecurityPolicyConfigDescriptor() {
+ return Jenkins.get().getDescriptorByType(LarkSecurityPolicyConfig.LarkSecurityPolicyConfigDescriptor.class);
+ }
+
+
+ /**
+ * Gets the default list of security policy configurations
+ *
+ * @return Default list of security policy configurations
+ */
+ public ArrayList getDefaultSecurityPolicyConfigs() {
+ return Arrays.stream(SecurityPolicyEnum.values())
+ .map(LarkSecurityPolicyConfig::of)
+ .collect(Collectors.toCollection(ArrayList::new));
+ }
+
+ /**
+ * Validates whether the robot name is empty
+ *
+ * @param value Robot name
+ * @return Validation result, returns FormValidation.ok() if validation passes, otherwise returns an error message
+ */
+ public FormValidation doCheckName(@QueryParameter String value) {
+ return StringUtils.isNotBlank(value) ? FormValidation.ok() :
+ FormValidation.error(Messages.form_validation_name());
+ }
+
+ /**
+ * Validates whether the Webhook key is empty
+ *
+ * @param value Webhook key
+ * @return Validation result, returns FormValidation.ok() if validation passes, otherwise returns an error message
+ */
+ public FormValidation doCheckWebhook(@QueryParameter String value) {
+ return StringUtils.isBlank(value) || Objects.isNull(RobotType.fromUrl(value)) ?
+ FormValidation.error(Messages.form_validation_webhook()) : FormValidation.ok();
+ }
+
+ /**
+ * Tests whether the robot configuration works properly.
+ *
+ * @param id The robot ID.
+ * @param name The robot's name.
+ * @param webhook The Webhook key.
+ * @param proxy The proxy settings.
+ * @param keyword The keyword.
+ * @param secret The encryption key.
+ * @return Returns the test result. If the test passes, it returns FormValidation.respond(Kind.OK); otherwise, it returns an error message.
+ */
+ public FormValidation doTest(@QueryParameter("id") String id, @QueryParameter("name") String name,
+ @QueryParameter("webhook") String webhook, @QueryParameter("proxy") String proxy,
+ @QueryParameter("keyword") String keyword, @QueryParameter("secret") String secret) {
+
+ List securityPolicyConfigs = Stream.of(keyword, secret)
+ .map(json -> JsonUtils.readValue(json, LarkSecurityPolicyConfig.class))
+ .filter(Objects::nonNull).filter(config -> StringUtils.isNotBlank(config.getValue()))
+ .collect(Collectors.toList());
+
+ LarkRobotConfig robotConfig = new LarkRobotConfig(id, name, webhook, securityPolicyConfigs);
+
+ ProxySelector proxySelector = Optional.ofNullable(JsonUtils.readValue(proxy, LarkProxyConfig.class))
+ .orElseGet(LarkProxyConfig::new).obtainProxySelector();
+
+ RobotType robotType = robotConfig.obtainRobotType();
+ if (Objects.isNull(robotType)) {
+ return FormValidation.error(Messages.form_validation_webhook());
+ }
+
+ MessageSender sender = robotType.obtainInstance(RobotConfigModel.of(robotConfig, proxySelector));
+ SendResult sendResult = sender.sendCard(buildTestMessage(robotType));
+
+ return !sendResult.isOk() ? FormValidation.error(sendResult.getMsg()) :
+ FormValidation.respond(Kind.OK, "Test Successful");
+ }
+
+ /**
+ * Builds a message model for testing the robot configuration.
+ *
+ * @return The test message model.
+ */
+ private MessageModel buildTestMessage(RobotType robotType) {
+ String rootUrl = Jenkins.get().getRootUrl();
+ User user = Optional.ofNullable(User.current()).orElse(User.getUnknown());
+
+ BuildJobModel buildJobModel = BuildJobModel.builder().projectName("Lark Notice Plugin").title(DEFAULT_TITLE)
+ .projectUrl(rootUrl).jobName("System Configuration").jobUrl(rootUrl + "/configure")
+ .statusType(BuildStatusEnum.SUCCESS).duration("-")
+ .executorName(user.getDisplayName()).build();
+
+ return MessageModel.builder().type(MsgTypeEnum.CARD)
+ .title(NOTICE_ICON + " Test Successful")
+ .text(buildJobModel.toMarkdown(robotType))
+ .atAll(false).build();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/config/LarkSecurityPolicyConfig.java b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkSecurityPolicyConfig.java
new file mode 100644
index 0000000..eda7402
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/config/LarkSecurityPolicyConfig.java
@@ -0,0 +1,107 @@
+package io.jenkins.plugins.lark.notice.config;
+
+import hudson.Extension;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+import hudson.util.Secret;
+import io.jenkins.plugins.lark.notice.enums.SecurityPolicyEnum;
+import jenkins.model.Jenkins;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.ToString;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+/**
+ * Represents a configuration for Lark security policies. This class is used to define and manage
+ * different types of security policies that can be applied within a Lark application or service.
+ * It encapsulates the policy type, its value, and a description to provide context.
+ *
+ * @author xm.z
+ */
+@Getter
+@Setter
+@ToString
+@NoArgsConstructor
+public class LarkSecurityPolicyConfig implements Describable {
+
+ /**
+ * The type of the security policy
+ */
+ private String type;
+
+ /**
+ * The value of the security policy, stored securely
+ */
+ private Secret value;
+
+ /**
+ * A description of the security policy
+ */
+ private String desc;
+
+ /**
+ * Data-bound constructor for setting up a new LarkSecurityPolicyConfig instance.
+ *
+ * @param type The type of the security policy.
+ * @param value The value of the security policy.
+ * @param desc A description of the security policy.
+ */
+ @DataBoundConstructor
+ public LarkSecurityPolicyConfig(String type, String value, String desc) {
+ this.type = type;
+ this.desc = desc;
+ this.value = Secret.fromString(value);
+ }
+
+ /**
+ * Factory method to create a new LarkSecurityPolicyConfig instance from an enum representing
+ * a security policy.
+ *
+ * @param securityPolicyEnum The enum representing the security policy.
+ * @return A new instance of LarkSecurityPolicyConfig with the type and description set from the enum.
+ */
+ public static LarkSecurityPolicyConfig of(SecurityPolicyEnum securityPolicyEnum) {
+ return new LarkSecurityPolicyConfig(
+ securityPolicyEnum.name(), "", securityPolicyEnum.getDesc());
+ }
+
+ /**
+ * Retrieves the plain text value of the security policy.
+ *
+ * @return The decrypted value of the security policy if set; otherwise, null.
+ */
+ public String getValue() {
+ if (value == null) {
+ return null;
+ }
+ return value.getPlainText();
+ }
+
+ /**
+ * Sets the value of the security policy. The value is securely stored.
+ *
+ * @param value The value to be set for the security policy.
+ */
+ public void setValue(String value) {
+ this.value = Secret.fromString(value);
+ }
+
+ /**
+ * Provides the descriptor for this class which is used by Jenkins for UI binding and instantiation.
+ *
+ * @return The descriptor instance for this class.
+ */
+ @Override
+ public Descriptor getDescriptor() {
+ return Jenkins.get().getDescriptorByType(LarkSecurityPolicyConfigDescriptor.class);
+ }
+
+ /**
+ * Descriptor for {@link LarkSecurityPolicyConfig}. This class is necessary for Jenkins to
+ * properly handle our {@link LarkSecurityPolicyConfig} instances within its UI and data management system.
+ */
+ @Extension
+ public static class LarkSecurityPolicyConfigDescriptor extends Descriptor {
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/config/link/LarkManagementLink.java b/src/main/java/io/jenkins/plugins/lark/notice/config/link/LarkManagementLink.java
new file mode 100644
index 0000000..3bf8064
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/config/link/LarkManagementLink.java
@@ -0,0 +1,98 @@
+package io.jenkins.plugins.lark.notice.config.link;
+
+import hudson.Extension;
+import hudson.model.Descriptor;
+import hudson.model.Descriptor.FormException;
+import hudson.model.ManagementLink;
+import hudson.util.FormApply;
+import io.jenkins.plugins.lark.notice.Messages;
+import io.jenkins.plugins.lark.notice.config.LarkGlobalConfig;
+import jenkins.model.Jenkins;
+import org.kohsuke.stapler.StaplerRequest;
+import org.kohsuke.stapler.StaplerResponse;
+import org.kohsuke.stapler.verb.POST;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+/**
+ * LarkManagementLink provides a management link in Jenkins' system configuration page
+ * for configuring the Lark notification plugin. This class extends ManagementLink,
+ * allowing it to define additional management options specific to the Lark plugin.
+ *
+ * @author xm.z
+ */
+@Extension(ordinal = Double.MAX_VALUE)
+public class LarkManagementLink extends ManagementLink {
+
+ /**
+ * Returns the path to the icon file displayed next to the link on the management page.
+ *
+ * @return A string representing the relative path to the icon file.
+ */
+ @Override
+ public String getIconFileName() {
+ return "/plugin/lark-notice/images/logo.png";
+ }
+
+ /**
+ * Returns the display name of the management link as defined in resource bundle.
+ *
+ * @return A string representing the display name of the link.
+ */
+ @Override
+ public String getDisplayName() {
+ return Messages.plugin_display_name();
+ }
+
+ /**
+ * Returns the URL name of the management link. This URL is appended to "/manage"
+ * to access this management link.
+ *
+ * @return A string representing the URL name.
+ */
+ @Override
+ public String getUrlName() {
+ return "lark";
+ }
+
+ /**
+ * Provides a detailed description for the management link, typically used as tooltip text.
+ *
+ * @return A string representing the description of the management link.
+ */
+ @Override
+ public String getDescription() {
+ return Messages.plugin_management_description();
+ }
+
+ /**
+ * Processes the configuration submission for the Lark plugin. If the user has administrative
+ * permissions, this method updates the plugin's global configuration based on the submitted form data.
+ *
+ * @param req The request object containing the form submission.
+ * @param res The response object used to redirect the user after successful submission.
+ * @throws ServletException If an error occurs during the servlet operation.
+ * @throws FormException If there is an error processing the submitted form.
+ * @throws IOException If an I/O error occurs.
+ */
+ @POST
+ public void doConfigure(StaplerRequest req, StaplerResponse res)
+ throws ServletException, FormException, IOException {
+ if (Jenkins.get().hasPermission(Jenkins.ADMINISTER)) {
+ getLarkGlobalConfigDescriptor().configure(req, req.getSubmittedForm());
+ FormApply.success(req.getContextPath() + "/manage").generateResponse(req, res, null);
+ }
+ }
+
+ /**
+ * Retrieves the descriptor instance for the LarkGlobalConfig class, which contains
+ * the configuration logic and data for the Lark plugin.
+ *
+ * @return The descriptor instance for LarkGlobalConfig.
+ */
+ public Descriptor getLarkGlobalConfigDescriptor() {
+ return Jenkins.get().getDescriptorByType(LarkGlobalConfig.class);
+ }
+
+}
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/config/property/LarkJobProperty.java b/src/main/java/io/jenkins/plugins/lark/notice/config/property/LarkJobProperty.java
new file mode 100644
index 0000000..cc44e58
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/config/property/LarkJobProperty.java
@@ -0,0 +1,110 @@
+package io.jenkins.plugins.lark.notice.config.property;
+
+import hudson.Extension;
+import hudson.model.Job;
+import hudson.model.JobProperty;
+import hudson.model.JobPropertyDescriptor;
+import io.jenkins.plugins.lark.notice.config.LarkGlobalConfig;
+import io.jenkins.plugins.lark.notice.config.LarkNotifierConfig;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+import org.apache.commons.collections.CollectionUtils;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * LarkJobProperty is a job property that stores configurations for sending notifications via Lark
+ * for specific Jenkins jobs. This allows each job to have its own set of Lark notifier configurations.
+ *
+ * @author xm.z
+ */
+@ToString
+@NoArgsConstructor
+public class LarkJobProperty extends JobProperty> {
+
+ private List notifierConfigs;
+
+ /**
+ * Data-bound constructor for setting up Lark notifier configurations.
+ *
+ * @param notifierConfigs A list of LarkNotifierConfig objects representing the configurations for Lark notifications.
+ */
+ @DataBoundConstructor
+ public LarkJobProperty(List notifierConfigs) {
+ this.notifierConfigs = notifierConfigs;
+ }
+
+ /**
+ * Retrieves the list of Lark notifier configurations, merging the global configurations with any job-specific overrides.
+ *
+ * @return A list of LarkNotifierConfig objects.
+ */
+ public List getNotifierConfigs() {
+ return LarkGlobalConfig.getInstance().getRobotConfigs()
+ .stream()
+ .map(robotConfig -> {
+ LarkNotifierConfig newNotifierConfig = new LarkNotifierConfig(robotConfig);
+ if (CollectionUtils.isNotEmpty(notifierConfigs)) {
+ notifierConfigs.stream()
+ .filter(notifierConfig -> robotConfig.getId().equals(notifierConfig.getRobotId()))
+ .findFirst()
+ .ifPresent(newNotifierConfig::copy);
+ }
+ return newNotifierConfig;
+ })
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Retrieves the list of Lark notifier configurations that are marked as checked/enabled by the user.
+ *
+ * @return A filtered list of LarkNotifierConfig objects that are enabled.
+ */
+ public List getCheckedNotifierConfigs() {
+ return this.getNotifierConfigs().stream()
+ .filter(LarkNotifierConfig::isChecked).collect(Collectors.toList());
+ }
+
+ /**
+ * Retrieves the list of available (checked and not disabled) Lark notifier configurations.
+ *
+ * @return A filtered list of LarkNotifierConfig objects that are available for use.
+ */
+ public List getAvailableNotifierConfigs() {
+ return this.getNotifierConfigs().stream()
+ .filter(config -> config.isChecked() && !config.isDisabled())
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Descriptor for {@link LarkJobProperty}. This descriptor is used by Jenkins to manage the job property,
+ * including presenting configuration options in the UI and handling data binding.
+ */
+ @Extension
+ public static class LarkJobPropertyDescriptor extends JobPropertyDescriptor {
+
+ /**
+ * Determines whether this job property is applicable to the given job type.
+ *
+ * @param jobType The class of the job to check.
+ * @return true if the job property is applicable, false otherwise.
+ */
+ @Override
+ public boolean isApplicable(Class extends Job> jobType) {
+ // By default, applicable to all job types. Override if specific job types should be excluded.
+ return super.isApplicable(jobType);
+ }
+
+ /**
+ * Provides a default list of Lark notifier configurations, based on the global settings.
+ *
+ * @return A list of default LarkNotifierConfig objects.
+ */
+ public List getDefaultNotifierConfigs() {
+ return LarkGlobalConfig.getInstance().getRobotConfigs().stream().map(LarkNotifierConfig::new)
+ .collect(Collectors.toList());
+ }
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/config/property/LarkUserProperty.java b/src/main/java/io/jenkins/plugins/lark/notice/config/property/LarkUserProperty.java
new file mode 100644
index 0000000..498745a
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/config/property/LarkUserProperty.java
@@ -0,0 +1,87 @@
+package io.jenkins.plugins.lark.notice.config.property;
+
+import edu.umd.cs.findbugs.annotations.NonNull;
+import edu.umd.cs.findbugs.annotations.Nullable;
+import hudson.Extension;
+import hudson.model.User;
+import hudson.model.UserProperty;
+import hudson.model.UserPropertyDescriptor;
+import io.jenkins.plugins.lark.notice.Messages;
+import lombok.Getter;
+import net.sf.json.JSONObject;
+import org.kohsuke.stapler.StaplerRequest;
+
+/**
+ * Represents a user property for storing Lark-specific information about a Jenkins user.
+ * This includes the user's mobile number and OpenID which can be used for notifications or identification in Lark.
+ *
+ * @author xm.z
+ */
+@Getter
+public class LarkUserProperty extends UserProperty {
+
+ /**
+ * The mobile number associated with the user's Lark account.
+ */
+ private final String mobile;
+
+ /**
+ * The OpenID associated with the user's Lark account.
+ */
+ private final String openId;
+
+ /**
+ * Constructs a new LarkUserProperty with the specified mobile number and OpenID.
+ *
+ * @param mobile The mobile number associated with the user's Lark account.
+ * @param openId The OpenID associated with the user's Lark account.
+ */
+ public LarkUserProperty(String mobile, String openId) {
+ this.mobile = mobile;
+ this.openId = openId;
+ }
+
+ /**
+ * Descriptor for {@link LarkUserProperty}. This descriptor is used by Jenkins to manage the user property,
+ * including presenting configuration options in the UI and handling data binding for the property.
+ */
+ @Extension(ordinal = 1)
+ public static final class LarkUserPropertyDescriptor extends UserPropertyDescriptor {
+
+ /**
+ * Returns the display name for this user property, which is shown in the Jenkins UI.
+ *
+ * @return A string containing the display name of this user property.
+ */
+ @NonNull
+ @Override
+ public String getDisplayName() {
+ return Messages.user_property_title();
+ }
+
+ /**
+ * Creates a new instance of {@link LarkUserProperty} with default values.
+ * This is used when a new user is created.
+ *
+ * @param user The user for whom the property is being created.
+ * @return A new instance of LarkUserProperty with null for both mobile and openId.
+ */
+ @Override
+ public UserProperty newInstance(User user) {
+ return new LarkUserProperty(null, null);
+ }
+
+ /**
+ * Creates a new instance of {@link LarkUserProperty} using data submitted from the configuration form.
+ *
+ * @param req The request from which to read the submission.
+ * @param formData The JSON object containing the form data.
+ * @return A new instance of LarkUserProperty initialized with form data.
+ */
+ @Override
+ public UserProperty newInstance(@Nullable StaplerRequest req, @NonNull JSONObject formData) {
+ return new LarkUserProperty(formData.optString("mobile"), formData.optString("openId"));
+ }
+ }
+}
+
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/context/PipelineEnvContext.java b/src/main/java/io/jenkins/plugins/lark/notice/context/PipelineEnvContext.java
new file mode 100644
index 0000000..02032c7
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/context/PipelineEnvContext.java
@@ -0,0 +1,57 @@
+package io.jenkins.plugins.lark.notice.context;
+
+import hudson.EnvVars;
+
+/**
+ * A utility class for managing environment variables within a pipeline's execution context.
+ * It leverages a {@link ThreadLocal} store to maintain environment variables specific to the current thread,
+ * allowing for thread-safe modifications and access to the environment variables during a pipeline's execution.
+ *
+ * @author xm.z
+ */
+public class PipelineEnvContext {
+
+ /**
+ * Thread-local storage for environment variables. This ensures that each thread has its own
+ * copy of environment variables, preventing conflicts in concurrent environments.
+ */
+ private static final ThreadLocal STORE = new ThreadLocal<>();
+
+ /**
+ * Merges the given environment variables with the current thread's environment variables.
+ * If the current thread does not have any environment variables set, it initializes them with the given value.
+ * If environment variables are already present, it overrides them with the values from the given environment variables.
+ *
+ * @param value The environment variables to merge into the current thread's environment variables.
+ */
+ public static void merge(EnvVars value) {
+ if (value == null) {
+ return;
+ }
+ EnvVars current = STORE.get();
+ if (current == null) {
+ STORE.set(value);
+ } else {
+ current.overrideAll(value);
+ }
+ }
+
+ /**
+ * Retrieves the current thread's environment variables. If no environment variables are set for the current thread,
+ * it returns an empty {@link EnvVars} instance.
+ *
+ * @return The current thread's environment variables, never {@code null}.
+ */
+ public static EnvVars get() {
+ EnvVars current = STORE.get();
+ return current == null ? new EnvVars() : current;
+ }
+
+ /**
+ * Resets the environment variables for the current thread. This effectively clears any environment variables
+ * that were previously set or merged into the thread's environment variable store.
+ */
+ public static void reset() {
+ STORE.remove();
+ }
+}
diff --git a/src/main/java/io/jenkins/plugins/feishu/notification/enums/BuildStatusEnum.java b/src/main/java/io/jenkins/plugins/lark/notice/enums/BuildStatusEnum.java
similarity index 54%
rename from src/main/java/io/jenkins/plugins/feishu/notification/enums/BuildStatusEnum.java
rename to src/main/java/io/jenkins/plugins/lark/notice/enums/BuildStatusEnum.java
index a0be12e..959fa71 100644
--- a/src/main/java/io/jenkins/plugins/feishu/notification/enums/BuildStatusEnum.java
+++ b/src/main/java/io/jenkins/plugins/lark/notice/enums/BuildStatusEnum.java
@@ -1,6 +1,6 @@
-package io.jenkins.plugins.feishu.notification.enums;
+package io.jenkins.plugins.lark.notice.enums;
-import io.jenkins.plugins.feishu.notification.Messages;
+import io.jenkins.plugins.lark.notice.Messages;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -8,7 +8,8 @@
import java.util.Map;
/**
- * 构建状态枚举类
+ * Enum representing the various build statuses that can occur during the lifecycle of a build process.
+ * Each status is associated with a specific label, color, and template for consistent representation across different contexts.
*
* @author xm.z
*/
@@ -17,42 +18,43 @@
public enum BuildStatusEnum {
/**
- * 开始
+ * Indicates that the build has started.
*/
START(Messages.build_status_start(), "blue", "blue"),
/**
- * 失败
+ * Indicates that the build has failed.
*/
FAILURE(Messages.build_status_failure(), "red", "red"),
/**
- * 成功
+ * Indicates that the build was successful.
*/
SUCCESS(Messages.build_status_success(), "green", "green"),
/**
- * 取消
+ * Indicates that the build was aborted.
*/
ABORTED(Messages.build_status_aborted(), "neutral", "grey"),
/**
- * 不稳定
+ * Indicates that the build is unstable.
*/
UNSTABLE(Messages.build_status_unstable(), "yellow", "yellow"),
/**
- * 未构建
+ * Indicates that the build has not been built.
*/
NOT_BUILT(Messages.build_status_not_built(), "turquoise", "turquoise"),
/**
- * 未知
+ * Indicates an unknown build status.
*/
UNKNOWN(Messages.build_status_unknown(), "purple", "purple");
/**
- * 将NoticeOccasionEnum值和BuildStatusEnum值进行映射,方便获取对应的构建状态。
+ * A static map to quickly find a {@link BuildStatusEnum} instance based on a {@link NoticeOccasionEnum}.
+ * This facilitates the mapping between occasions when a notice might be sent and the corresponding build statuses.
*/
static final Map BUILD_STATUS_ENUM_MAP = new HashMap<>(
Map.of(
@@ -65,8 +67,19 @@ public enum BuildStatusEnum {
)
);
+ /**
+ * The human-readable label for the status.
+ */
private final String label;
+
+ /**
+ * The color associated with this status, used for UI representation.
+ */
private final String color;
+
+ /**
+ * The template identifier associated with this status, if applicable.
+ */
private final String template;
-}
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/enums/MsgTypeEnum.java b/src/main/java/io/jenkins/plugins/lark/notice/enums/MsgTypeEnum.java
new file mode 100644
index 0000000..eebbdf0
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/enums/MsgTypeEnum.java
@@ -0,0 +1,91 @@
+package io.jenkins.plugins.lark.notice.enums;
+
+import io.jenkins.plugins.lark.notice.model.MessageModel;
+import io.jenkins.plugins.lark.notice.sdk.MessageSender;
+import io.jenkins.plugins.lark.notice.sdk.model.SendResult;
+
+/**
+ * Defines the types of messages that can be sent using a MessageSender. Each enum constant represents a different
+ * message type (e.g., text, image) and implements an abstract method {@code send} to send the message
+ * through the specified MessageSender with a given MessageModel.
+ *
+ * @author xm.z
+ */
+public enum MsgTypeEnum {
+
+ /**
+ * Represents a text message type. Implements the {@code send} method to send text messages.
+ */
+ TEXT {
+ @Override
+ public SendResult send(MessageSender sender, MessageModel msg) {
+ return sender.sendText(msg);
+ }
+ },
+
+ /**
+ * Represents an image message type. Implements the {@code send} method to send image messages.
+ */
+ IMAGE {
+ @Override
+ public SendResult send(MessageSender sender, MessageModel msg) {
+ return sender.sendImage(msg);
+ }
+ },
+
+ //====================================================Lark==========================================================
+
+ /**
+ * Represents a shared chat message type. Implements the {@code send} method to send shared chat messages.
+ */
+ SHARE_CHAT {
+ @Override
+ public SendResult send(MessageSender sender, MessageModel msg) {
+ return sender.sendShareChat(msg);
+ }
+ },
+
+ /**
+ * Represents a post message type. Implements the {@code send} method to send post messages.
+ */
+ POST {
+ @Override
+ public SendResult send(MessageSender sender, MessageModel msg) {
+ return sender.sendPost(msg);
+ }
+ },
+
+ LINK {
+ @Override
+ public SendResult send(MessageSender sender, MessageModel msg) {
+ return sender.sendLink(msg);
+ }
+ },
+
+ MARKDOWN {
+ @Override
+ public SendResult send(MessageSender sender, MessageModel msg) {
+ return sender.sendMarkdown(msg);
+ }
+ },
+
+ /**
+ * Represents an interactive message type. Implements the {@code send} method to send interactive messages.
+ */
+ CARD {
+ @Override
+ public SendResult send(MessageSender sender, MessageModel msg) {
+ return sender.sendCard(msg);
+ }
+ };
+
+ /**
+ * Abstract method to be implemented by each enum constant. It defines how a message of a specific type
+ * should be sent using a MessageSender with the provided MessageModel.
+ *
+ * @param sender The MessageSender instance used to send the message.
+ * @param msg The message model containing the details of the message to be sent.
+ * @return A SendResult indicating the result of the send operation.
+ */
+ public abstract SendResult send(MessageSender sender, MessageModel msg);
+}
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/enums/NoticeOccasionEnum.java b/src/main/java/io/jenkins/plugins/lark/notice/enums/NoticeOccasionEnum.java
new file mode 100644
index 0000000..03ab63e
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/enums/NoticeOccasionEnum.java
@@ -0,0 +1,89 @@
+package io.jenkins.plugins.lark.notice.enums;
+
+import hudson.model.Result;
+import io.jenkins.plugins.lark.notice.Messages;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Enum representing the various occasions on which a notification might be sent during the build process.
+ * Each enum constant represents a specific occasion (e.g., when a build starts, fails, or succeeds) and is associated with a descriptive message.
+ *
+ * @author xm.z
+ */
+@Getter
+@AllArgsConstructor
+public enum NoticeOccasionEnum {
+
+ /**
+ * Indicates that the build has started.
+ */
+ START(Messages.notice_start()),
+
+ /**
+ * Indicates that the build was aborted.
+ */
+ ABORTED(Messages.notice_aborted()),
+
+ /**
+ * Indicates that the build has failed.
+ */
+ FAILURE(Messages.notice_failure()),
+
+ /**
+ * Indicates that the build was successful.
+ */
+ SUCCESS(Messages.notice_success()),
+
+ /**
+ * Indicates that the build is unstable.
+ */
+ UNSTABLE(Messages.notice_unstable()),
+
+ /**
+ * Indicates that the build has not been built.
+ */
+ NOT_BUILT(Messages.notice_not_built());
+
+ /**
+ * A static map to quickly find a {@link NoticeOccasionEnum} instance based on a {@link Result}.
+ * This facilitates the mapping between build results and the corresponding notification occasions.
+ */
+ static final Map RESULT_TO_ENUM_MAP = new HashMap<>(
+ Map.of(
+ Result.SUCCESS, SUCCESS,
+ Result.FAILURE, FAILURE,
+ Result.ABORTED, ABORTED,
+ Result.UNSTABLE, UNSTABLE,
+ Result.NOT_BUILT, NOT_BUILT
+ )
+ );
+
+ /**
+ * The description of the notice occasion.
+ */
+ private final String desc;
+
+ /**
+ * Retrieves the corresponding {@link NoticeOccasionEnum} for a given {@link Result}.
+ *
+ * @param result The build result used to determine the notice occasion.
+ * @return The corresponding {@link NoticeOccasionEnum}, or null if no match is found.
+ */
+ public static NoticeOccasionEnum getNoticeOccasion(Result result) {
+ return RESULT_TO_ENUM_MAP.get(result);
+ }
+
+ /**
+ * Determines the build status associated with this notice occasion.
+ *
+ * @return The corresponding {@link BuildStatusEnum}, defaults to {@link BuildStatusEnum#UNKNOWN} if no direct mapping exists.
+ */
+ public BuildStatusEnum buildStatus() {
+ return BuildStatusEnum.BUILD_STATUS_ENUM_MAP.getOrDefault(this, BuildStatusEnum.UNKNOWN);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/enums/RobotType.java b/src/main/java/io/jenkins/plugins/lark/notice/enums/RobotType.java
new file mode 100644
index 0000000..21b5659
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/enums/RobotType.java
@@ -0,0 +1,63 @@
+package io.jenkins.plugins.lark.notice.enums;
+
+import io.jenkins.plugins.lark.notice.model.RobotConfigModel;
+import io.jenkins.plugins.lark.notice.sdk.MessageSender;
+import io.jenkins.plugins.lark.notice.sdk.impl.DingMessageSender;
+import io.jenkins.plugins.lark.notice.sdk.impl.LarkMessageSender;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.SneakyThrows;
+
+import java.net.URL;
+
+/**
+ * RobotType
+ *
+ * @author xm.z
+ */
+@Getter
+@AllArgsConstructor
+public enum RobotType {
+
+ // Lark
+ LARK("Lark", "open.larksuite.com", "text_tag") {
+ @Override
+ public MessageSender obtainInstance(RobotConfigModel robotConfig) {
+ return new LarkMessageSender(robotConfig);
+ }
+ },
+
+ FS("飞书", "open.feishu.cn", "text_tag") {
+ @Override
+ public MessageSender obtainInstance(RobotConfigModel robotConfig) {
+ return new LarkMessageSender(robotConfig);
+ }
+ },
+
+ DING_TAlK("钉钉", "api.dingtalk.com", "font") {
+ @Override
+ public MessageSender obtainInstance(RobotConfigModel robotConfig) {
+ return new DingMessageSender(robotConfig);
+ }
+ };
+
+ private final String name;
+
+ private final String host;
+
+ private final String statusTagName;
+
+ @SneakyThrows
+ public static RobotType fromUrl(String url) {
+ String host = new URL(url).getHost();
+ for (RobotType type : RobotType.values()) {
+ if (host.contains(type.getHost())) {
+ return type;
+ }
+ }
+ return null;
+ }
+
+ public abstract MessageSender obtainInstance(RobotConfigModel robotConfig);
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/enums/SecurityPolicyEnum.java b/src/main/java/io/jenkins/plugins/lark/notice/enums/SecurityPolicyEnum.java
new file mode 100644
index 0000000..4403393
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/enums/SecurityPolicyEnum.java
@@ -0,0 +1,36 @@
+package io.jenkins.plugins.lark.notice.enums;
+
+import io.jenkins.plugins.lark.notice.Messages;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.ToString;
+
+/**
+ * Enum representing the types of security policies that can be applied within a system.
+ * This enum is used to distinguish between different security mechanisms, such as key-based or secret-based authentication or encryption.
+ *
+ * @author xm.z
+ */
+@Getter
+@ToString
+@AllArgsConstructor
+public enum SecurityPolicyEnum {
+
+ /**
+ * Represents a security policy based on keys. This can be used for mechanisms where
+ * public/private keys or similar key-based strategies are employed for authentication or encryption.
+ */
+ KEY(Messages.security_policy_type_key()),
+
+ /**
+ * Represents a security policy based on secrets. This can be employed in contexts where
+ * secret tokens, passwords, or similar confidential information is used for secure access or data protection.
+ */
+ SECRET(Messages.security_policy_type_secret());
+
+ /**
+ * A descriptive text explaining the security policy type.
+ */
+ private final String desc;
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/model/BuildJobModel.java b/src/main/java/io/jenkins/plugins/lark/notice/model/BuildJobModel.java
new file mode 100644
index 0000000..e434848
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/model/BuildJobModel.java
@@ -0,0 +1,126 @@
+package io.jenkins.plugins.lark.notice.model;
+
+import io.jenkins.plugins.lark.notice.enums.BuildStatusEnum;
+import io.jenkins.plugins.lark.notice.enums.RobotType;
+import io.jenkins.plugins.lark.notice.tools.Utils;
+import lombok.Builder;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static io.jenkins.plugins.lark.notice.enums.MsgTypeEnum.CARD;
+import static io.jenkins.plugins.lark.notice.sdk.constant.Constants.LF;
+
+/**
+ * Represents a model for a build job, encapsulating details about a specific build process.
+ * This model includes information such as the project and job names, URLs, build status,
+ * duration, executor details, and additional content related to the build.
+ *
+ * @author xm.z
+ */
+@Data
+@Builder
+public class BuildJobModel {
+
+ /**
+ * The title of the project associated with the build job.
+ */
+ private String title;
+
+ /**
+ * The name of the project associated with the build job.
+ */
+ private String projectName;
+
+ /**
+ * The URL pointing to the project's homepage or repository.
+ */
+ private String projectUrl;
+
+ /**
+ * The name of the build job. This often serves as an identifier within the CI/CD system.
+ */
+ private String jobName;
+
+ /**
+ * The URL pointing to the detailed page of the build job within the CI/CD system.
+ */
+ private String jobUrl;
+
+ /**
+ * The status of the build, represented as an enum value. It indicates the outcome of the build process
+ * (e.g., success, failure).
+ */
+ private BuildStatusEnum statusType;
+
+ /**
+ * The duration of the build process. This is typically a human-readable string representing the time taken
+ * to complete the build.
+ */
+ private String duration;
+
+ /**
+ * The name of the person or system that executed the build. This can be useful for tracking responsibility
+ * and ownership of the build process.
+ */
+ private String executorName;
+
+ /**
+ * The mobile number of the executor. This could be used for sending notifications or alerts related to the build.
+ */
+ private String executorMobile;
+
+ /**
+ * The OpenID of the executor. In systems integrated with WeChat Work or similar platforms, this can be used
+ * to identify users uniquely.
+ */
+ private String executorOpenId;
+
+ /**
+ * Additional content or notes related to the build job. This could include logs, error messages, or custom
+ * messages intended for reporting or notification purposes.
+ */
+ private String content;
+
+ /**
+ * Converts the build job details into a Markdown formatted string.
+ * This is useful for generating readable and formatted messages for notifications or reports.
+ *
+ * @return A string in Markdown format containing the build job details.
+ */
+ public String toMarkdown(RobotType robotType) {
+ boolean hasDingTask = RobotType.DING_TAlK.equals(robotType);
+ String tagName = robotType.getStatusTagName();
+ List lines = new ArrayList<>();
+ // 如果是钉钉任务,先添加特定格式的标题和分隔线
+ if (hasDingTask) {
+ lines.add(String.format("## <%s color='%s'>%s%s>", tagName, statusType.getColor(), title, tagName));
+ lines.add("---");
+ }
+ // 添加通用信息
+ Collections.addAll(lines,
+ String.format("\uD83D\uDCCB **任务名称**: [%s](%s)", projectName, projectUrl),
+ String.format("\uD83D\uDD22 **任务编号**: [%s](%s)", jobName, jobUrl),
+ String.format("\uD83C\uDF1F **构建状态**: <%s color='%s'>%s%s>",
+ tagName, statusType.getColor(), statusType.getLabel(), tagName),
+ String.format("\uD83D\uDD50 **构建用时**: %s", duration),
+ String.format("\uD83D\uDC64 **执 行 者** : %s", executorName),
+ content == null ? "" : content
+ );
+ return String.join(hasDingTask ? " " + LF : LF, lines);
+ }
+
+ /**
+ * Prepares a {@link MessageModel.MessageModelBuilder} instance with pre-populated fields based on the build job details.
+ * This builder can then be used to further customize and create a {@link MessageModel} instance for messaging purposes.
+ *
+ * @return A {@link MessageModel.MessageModelBuilder} instance with pre-populated fields.
+ */
+ public MessageModel.MessageModelBuilder messageModelBuilder() {
+ return MessageModel.builder().type(CARD).statusType(statusType)
+ .buttons(Utils.createDefaultButtons(jobUrl)).title(title);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/model/ButtonModel.java b/src/main/java/io/jenkins/plugins/lark/notice/model/ButtonModel.java
new file mode 100644
index 0000000..526e2a5
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/model/ButtonModel.java
@@ -0,0 +1,60 @@
+package io.jenkins.plugins.lark.notice.model;
+
+import hudson.Extension;
+import hudson.model.AbstractDescribableImpl;
+import hudson.model.Descriptor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+/**
+ * Model for storing button-related information. This class represents a button element that can be used in various UI contexts,
+ * containing properties such as the button's title, the URL to be requested upon clicking, and the button's visual style type.
+ * It extends {@link AbstractDescribableImpl} to allow for easy integration with Jenkins' configuration system.
+ *
+ * @author xm.z
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class ButtonModel extends AbstractDescribableImpl {
+
+ /**
+ * The title of the button. This is typically displayed as the button's text.
+ */
+ private String title;
+
+ /**
+ * The URL to be requested when the button is clicked. This could link to an internal or external resource.
+ */
+ private String url;
+
+ /**
+ * The type of the button, which determines its visual style. Valid types include "primary", "danger", and "default".
+ */
+ private String type;
+
+ /**
+ * Constructor for creating a new instance of a ButtonModel with specified title, URL, and type.
+ *
+ * @param title The title (text) of the button.
+ * @param url The URL to be requested upon clicking the button.
+ * @param type The visual style type of the button ("primary", "danger", "default").
+ */
+ @DataBoundConstructor
+ public ButtonModel(String title, String url, String type) {
+ this.title = title;
+ this.url = url;
+ this.type = type;
+ }
+
+ /**
+ * Descriptor class for {@link ButtonModel}. This class is used to describe the properties of the ButtonModel
+ * that are configurable in Jenkins UI. By extending {@link Descriptor}, it integrates with Jenkins' system
+ * for managing plugin configurations, allowing users to specify values for title, url, and type through the Jenkins interface.
+ */
+ @Extension
+ public static class DescriptorImpl extends Descriptor {
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/model/ImgModel.java b/src/main/java/io/jenkins/plugins/lark/notice/model/ImgModel.java
new file mode 100644
index 0000000..7aa57c0
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/model/ImgModel.java
@@ -0,0 +1,85 @@
+package io.jenkins.plugins.lark.notice.model;
+
+import hudson.Extension;
+import hudson.model.AbstractDescribableImpl;
+import hudson.model.Descriptor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+/**
+ * Model for storing image-related information. This class encapsulates properties related to an image,
+ * such as a unique identifier (imgKey), display mode, whether it should be displayed in a compact width,
+ * custom maximum display width, and alternative text content for hover actions. It extends {@link AbstractDescribableImpl}
+ * to facilitate integration with Jenkins' configuration system, allowing images to be dynamically configured within Jenkins plugins or jobs.
+ *
+ * @author xm.z
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+public class ImgModel extends AbstractDescribableImpl {
+
+ /**
+ * The unique key associated with the image. This key is typically used to fetch or reference the image.
+ */
+ private String imgKey;
+
+ /**
+ * The display mode of the image. Supported modes include:
+ *
+ * - crop_center: Center-crop mode, which limits the height for long images and displays them after center cropping.
+ * - fit_horizontal: Tile mode, which stretches the image to fill the width of the card, displaying the uploaded image in its entirety.
+ * - custom_width: Custom width mode, allowing for a specific maximum display width.
+ * - compact_width: Compact width mode, displaying the image in a more condensed format.
+ *
+ */
+ private String mode;
+
+ /**
+ * Indicates whether the image should be displayed in a compact format.
+ * By default, this is set to false. If true, the image will be displayed with a maximum width of 278px in a compact format.
+ */
+ private boolean compactWidth;
+
+ /**
+ * Custom maximum display width for the image.
+ * By default, the image spans the full width of the card. This value allows specifying a maximum display width between 278px and 580px.
+ */
+ private Integer customWidth;
+
+ /**
+ * Alternative content text that appears when hovering over the image.
+ */
+ private String altContent;
+
+ /**
+ * Constructor for creating a new instance of an ImgModel with specified image key, display mode, compact width flag,
+ * custom maximum width, and alternative hover content.
+ *
+ * @param imgKey The unique key for the image.
+ * @param mode The display mode of the image.
+ * @param compactWidth Flag indicating if the image should be displayed in a compact format.
+ * @param customWidth The custom maximum width for the image display.
+ * @param altContent The alternative content text for hover actions.
+ */
+ @DataBoundConstructor
+ public ImgModel(String imgKey, String mode, boolean compactWidth, Integer customWidth, String altContent) {
+ this.imgKey = imgKey;
+ this.mode = mode;
+ this.compactWidth = compactWidth;
+ this.customWidth = customWidth;
+ this.altContent = altContent;
+ }
+
+ /**
+ * Descriptor class for {@link ImgModel}. This class is used to describe the properties of the ImgModel
+ * that are configurable in Jenkins UI. By extending {@link Descriptor}, it integrates with Jenkins' system
+ * for managing plugin configurations, allowing users to specify values for imgKey, mode, compactWidth, customWidth,
+ * and altContent through the Jenkins interface.
+ */
+ @Extension
+ public static class DescriptorImpl extends Descriptor {
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/io/jenkins/plugins/lark/notice/model/MessageModel.java b/src/main/java/io/jenkins/plugins/lark/notice/model/MessageModel.java
new file mode 100644
index 0000000..0c6e265
--- /dev/null
+++ b/src/main/java/io/jenkins/plugins/lark/notice/model/MessageModel.java
@@ -0,0 +1,139 @@
+package io.jenkins.plugins.lark.notice.model;
+
+import io.jenkins.plugins.lark.notice.enums.BuildStatusEnum;
+import io.jenkins.plugins.lark.notice.enums.MsgTypeEnum;
+import io.jenkins.plugins.lark.notice.sdk.model.lark.support.At;
+import io.jenkins.plugins.lark.notice.sdk.model.lark.support.Button;
+import io.jenkins.plugins.lark.notice.sdk.model.lark.support.ImgElement;
+import io.jenkins.plugins.lark.notice.tools.Utils;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.apache.commons.lang.StringUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Model for storing message-related information. This class encapsulates properties related to a message,
+ * such as the message type, build status, recipient identifiers, title, text content, images, and buttons.
+ * It is designed to facilitate the creation and customization of messages sent from Jenkins build notifications
+ * to various platforms.
+ *
+ * @author xm.z
+ */
+@Getter
+@Setter
+@ToString
+@Builder
+public class MessageModel {
+
+ /**
+ * The type of the message, determining the format or channel through which the message is sent.
+ */
+ private MsgTypeEnum type;
+
+ /**
+ * The build status, indicating the outcome of the build process (e.g., success, failure).
+ */
+ private BuildStatusEnum statusType;
+
+ /**
+ * A set of UserIds to be mentioned in the message. These are typically user identifiers on platforms like WeChat.
+ */
+ private Set atUserIds;
+
+ /**
+ * Flag indicating whether the message should mention all users within the message scope.
+ */
+ private boolean atAll;
+
+ /**
+ * The title of the message, displayed prominently in the message. This can be customized or left blank to use the default title.
+ */
+ private String title;
+
+ /**
+ * The main text content of the message.
+ */
+ private String text;
+
+ /**
+ * An image element to be displayed at the top of the message body.
+ */
+ private ImgElement topImg;
+
+ /**
+ * An image element to be displayed at the bottom of the message body.
+ */
+ private ImgElement bottomImg;
+
+ /**
+ * A list of buttons that can be included in the message, providing interactive elements for the recipient.
+ */
+ private List