This is an automated email from the ASF dual-hosted git repository.

gongchao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new 9fd58ced4a [feature] add twilio sms client support (#3159)
9fd58ced4a is described below

commit 9fd58ced4a0371eb0ab73f248f857e5add83c175
Author: Sarthak Arora <[email protected]>
AuthorDate: Sat Mar 22 11:42:32 2025 +0530

    [feature] add twilio sms client support (#3159)
    
    Co-authored-by: yunfan24 <[email protected]>
    Co-authored-by: tomsun28 <[email protected]>
---
 .../apache/hertzbeat/alert/config/SmsConfig.java   |   6 +
 .../{SmsConfig.java => TwilioSmsProperties.java}   |  44 ++----
 .../hertzbeat/alert/service/SmsClientFactory.java  |   7 +-
 .../alert/service/impl/TwilioSmsClientImpl.java    | 171 +++++++++++++++++++++
 .../alert/service/TwilioSmsClientImplTest.java     |  64 ++++++++
 .../hertzbeat/common/constants/SmsConstants.java   |   3 +
 .../src/main/resources/application.yml             |   4 +
 home/docs/help/alert_sms.md                        |  46 ++++++
 script/application.yml                             |   4 +
 .../hertzbeat-mysql-iotdb/conf/application.yml     |   4 +
 .../hertzbeat-mysql-tdengine/conf/application.yml  |   4 +
 .../conf/application.yml                           |   4 +
 .../conf/application.yml                           |   4 +
 web-app/src/app/pojo/SmsNoticeSender.ts            |   2 +
 .../{enums/sms-type.enum.ts => TwilioSmsConfig.ts} |  15 +-
 web-app/src/app/pojo/enums/sms-type.enum.ts        |   3 +-
 .../message-server/message-server.component.html   |  48 ++++++
 .../message-server/message-server.component.ts     |   8 +-
 web-app/src/assets/i18n/en-US.json                 |   4 +
 web-app/src/assets/i18n/ja-JP.json                 |   4 +
 web-app/src/assets/i18n/zh-CN.json                 |   4 +
 web-app/src/assets/i18n/zh-TW.json                 |   3 +
 22 files changed, 408 insertions(+), 48 deletions(-)

diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/SmsConfig.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/SmsConfig.java
index ded6348674..3ceb6deb78 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/SmsConfig.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/SmsConfig.java
@@ -64,6 +64,12 @@ public class SmsConfig {
      * Aws configuration
      */
     private AwsSmsProperties aws;
+
+    /**
+     * Twilio SMS configuration
+     */
+    private TwilioSmsProperties twilio;
+
     /**
      * Smslocal SMS configuration
      */
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/SmsConfig.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/TwilioSmsProperties.java
similarity index 56%
copy from 
hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/SmsConfig.java
copy to 
hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/TwilioSmsProperties.java
index ded6348674..26c74bfd9b 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/SmsConfig.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/config/TwilioSmsProperties.java
@@ -21,51 +21,29 @@ import jakarta.validation.constraints.NotBlank;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
 
 /**
- * SMS configuration
+ * Twilio SMS configuration properties
  */
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
-@Component
-@ConfigurationProperties(prefix = "alerter.sms")
-public class SmsConfig {
-
-    /**
-     * whether to enable SMS, default is false
-     */
-    private boolean enable = false;
-
-    /**
-     * sms service provider
-     */
-    @NotBlank(message = "Type cannot be empty")
-    private String type;
-
-    /**
-     * Tencent cloud SMS configuration
-     */
-    private TencentSmsProperties tencent;
-
+public class TwilioSmsProperties {
     /**
-     * Aliyun SMS configuration
+     * Twilio Account SID
      */
-    private AlibabaSmsProperties alibaba;
+    @NotBlank(message = "Account SID cannot be empty")
+    private String accountSid;
 
     /**
-     * UniSMS configuration
+     * Twilio Auth Token
      */
-    private UniSmsProperties unisms;
+    @NotBlank(message = "Auth Token cannot be empty")
+    private String authToken;
 
     /**
-     * Aws configuration
-     */
-    private AwsSmsProperties aws;
-    /**
-     * Smslocal SMS configuration
+     * Twilio Issued Phone Number
      */
-    private SmslocalSmsProperties smslocal;
+    @NotBlank(message = "Twilio Phone Number cannot be empty")
+    private String twilioPhoneNumber;
 }
\ No newline at end of file
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/SmsClientFactory.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/SmsClientFactory.java
index 1e42299ea6..50aebbf9dc 100644
--- 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/SmsClientFactory.java
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/SmsClientFactory.java
@@ -23,6 +23,7 @@ import org.apache.hertzbeat.alert.config.SmsConfig;
 import org.apache.hertzbeat.alert.service.impl.SmsLocalSmsClientImpl;
 import org.apache.hertzbeat.alert.service.impl.AwsSmsClientImpl;
 import org.apache.hertzbeat.alert.service.impl.TencentSmsClientImpl;
+import org.apache.hertzbeat.alert.service.impl.TwilioSmsClientImpl;
 import org.apache.hertzbeat.alert.service.impl.UniSmsClientImpl;
 import org.apache.hertzbeat.alert.service.impl.AlibabaSmsClientImpl;
 import org.apache.hertzbeat.base.dao.GeneralConfigDao;
@@ -35,6 +36,7 @@ import org.springframework.stereotype.Component;
 import static org.apache.hertzbeat.common.constants.SmsConstants.ALIBABA;
 import static org.apache.hertzbeat.common.constants.SmsConstants.AWS;
 import static org.apache.hertzbeat.common.constants.SmsConstants.TENCENT;
+import static org.apache.hertzbeat.common.constants.SmsConstants.TWILIO;
 import static org.apache.hertzbeat.common.constants.SmsConstants.UNISMS;
 import static org.apache.hertzbeat.common.constants.SmsConstants.SMSLOCAL;
 
@@ -141,9 +143,12 @@ public class SmsClientFactory {
             case AWS:
                 currentSmsClient = new AwsSmsClientImpl(smsConfig.getAws());
                 break;
+            case TWILIO:
+                currentSmsClient = new 
TwilioSmsClientImpl(smsConfig.getTwilio());
+                break;
             default:
                 log.warn("[SmsClientFactory] Unsupported SMS provider type: 
{}", smsConfig.getType());
                 break;
         }
     }
-} 
\ No newline at end of file
+}
\ No newline at end of file
diff --git 
a/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/TwilioSmsClientImpl.java
 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/TwilioSmsClientImpl.java
new file mode 100644
index 0000000000..5a6ba033f6
--- /dev/null
+++ 
b/hertzbeat-alerter/src/main/java/org/apache/hertzbeat/alert/service/impl/TwilioSmsClientImpl.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.alert.service.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.hertzbeat.alert.config.TwilioSmsProperties;
+import org.apache.hertzbeat.alert.service.SmsClient;
+import org.apache.hertzbeat.common.entity.alerter.GroupAlert;
+import org.apache.hertzbeat.common.entity.alerter.NoticeReceiver;
+import org.apache.hertzbeat.common.entity.alerter.NoticeTemplate;
+import org.apache.hertzbeat.common.support.exception.SendMessageException;
+import org.apache.hertzbeat.common.util.JsonUtil;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+
+import static org.apache.hertzbeat.common.constants.SmsConstants.TWILIO;
+
+/**
+ * Twilio SMS Client Implementation<br>
+ * API doc: <a href=
+ * 
"https://www.twilio.com/docs/sms/api";>https://www.twilio.com/docs/sms/api</a>
+ */
+@Slf4j
+public class TwilioSmsClientImpl implements SmsClient {
+
+    private static final String API_URL_FORMAT = 
"https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json";;
+    private static final String MESSAGE_TEMPLATE = "Instance: %s, Priority: 
%s, Content: %s";
+
+    private final String accountSid;
+    private final String authToken;
+    private final String twilioPhoneNumber;
+
+    public TwilioSmsClientImpl(TwilioSmsProperties config) {
+        if (config != null) {
+            this.accountSid = config.getAccountSid();
+            this.authToken = config.getAuthToken();
+            this.twilioPhoneNumber = config.getTwilioPhoneNumber();
+        } else {
+            this.accountSid = "";
+            this.authToken = "";
+            this.twilioPhoneNumber = "";
+        }
+    }
+
+    @Override
+    public void sendMessage(NoticeReceiver receiver, NoticeTemplate 
noticeTemplate, GroupAlert alert) {
+        String instance = null;
+        String priority = null;
+        String content = null;
+        if (alert.getCommonLabels() != null) {
+            instance = alert.getCommonLabels().get("instance") == null ? 
alert.getGroupKey()
+                    : alert.getCommonLabels().get("instance");
+            priority = alert.getCommonLabels().get("priority") == null ? 
"unknown"
+                    : alert.getCommonLabels().get("priority");
+            content = alert.getCommonAnnotations().get("summary");
+            content = content == null ? 
alert.getCommonAnnotations().get("description") : content;
+            if (content == null) {
+                content = 
alert.getCommonAnnotations().values().stream().findFirst().orElse(null);
+            }
+        }
+        this.send(receiver.getPhone(), createMessage(instance, priority, 
content));
+    }
+
+    private String createMessage(String instance, String priority, String 
content) {
+        return String.format(MESSAGE_TEMPLATE, instance, priority, content);
+    }
+
+    private void send(String phoneNumber, String message) {
+        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
+            String endpoint = String.format(API_URL_FORMAT, accountSid);
+            URI requestUri = new URI(endpoint);
+
+            HttpPost httpPost = createHttpPost(requestUri, phoneNumber, 
message);
+            log.info("Sending Twilio SMS request to {}", requestUri);
+            executeRequest(httpClient, httpPost, phoneNumber);
+        } catch (Exception e) {
+            log.warn("Failed to send SMS: {}", e.getMessage());
+            throw new SendMessageException(e.getMessage());
+        }
+    }
+
+    private HttpPost createHttpPost(URI requestUri, String toNumber, String 
message) {
+        HttpPost httpPost = new HttpPost(requestUri);
+
+        String auth = accountSid + ":" + authToken;
+        String encodedAuth = 
Base64.getEncoder().encodeToString(auth.getBytes());
+        httpPost.setHeader("Authorization", "Basic " + encodedAuth);
+
+        List<BasicNameValuePair> parameters = new ArrayList<>();
+        parameters.add(new BasicNameValuePair("To", toNumber));
+        parameters.add(new BasicNameValuePair("From", twilioPhoneNumber));
+        parameters.add(new BasicNameValuePair("Body", message));
+
+        try {
+            httpPost.setEntity(new UrlEncodedFormEntity(parameters));
+            return httpPost;
+        } catch (Exception e) {
+            log.error("Failed to create HTTP request: {}", e.getMessage());
+            throw new SendMessageException(e.getMessage());
+        }
+    }
+
+    private void executeRequest(CloseableHttpClient httpClient, HttpPost 
httpPost, String phoneNumber)
+            throws Exception {
+        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
+            int statusCode = response.getStatusLine().getStatusCode();
+            String responseBody = EntityUtils.toString(response.getEntity());
+            log.info("SMS response status: {}, body: {}", statusCode, 
responseBody);
+
+            if (statusCode < 200 || statusCode >= 300) {
+
+                if (responseBody.contains("21608")) {
+                    throw new SendMessageException(
+                            "The Twilio trial account can only send SMS to 
verified phone numbers");
+                } else {
+                    throw new SendMessageException(
+                            "HTTP request failed with status code: " + 
statusCode + ", response: " + responseBody);
+                }
+            }
+
+            JsonNode jsonResponse = JsonUtil.fromJson(responseBody);
+            if (jsonResponse == null) {
+                throw new SendMessageException(statusCode + ":" + 
responseBody);
+            }
+
+            JsonNode sidNode = jsonResponse.get("sid");
+            if (sidNode == null) {
+                throw new SendMessageException(statusCode + ":" + 
responseBody);
+            }
+
+            String sid = sidNode.asText();
+            log.info("Successfully sent SMS to phone: {}, sid: {}", 
phoneNumber, sid);
+        }
+    }
+
+    @Override
+    public String getType() {
+        return TWILIO;
+    }
+
+    @Override
+    public boolean checkConfig() {
+        return !(accountSid.isBlank() || authToken.isBlank() || 
twilioPhoneNumber.isBlank());
+    }
+}
\ No newline at end of file
diff --git 
a/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/TwilioSmsClientImplTest.java
 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/TwilioSmsClientImplTest.java
new file mode 100644
index 0000000000..2c32417ea8
--- /dev/null
+++ 
b/hertzbeat-alerter/src/test/java/org/apache/hertzbeat/alert/service/TwilioSmsClientImplTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.alert.service;
+
+import org.apache.hertzbeat.alert.config.TwilioSmsProperties;
+import org.apache.hertzbeat.alert.service.impl.TwilioSmsClientImpl;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+/**
+ * Test case for {@link TwilioSmsClientImpl}
+ */
+class TwilioSmsClientImplTest {
+
+    @Test
+    void getType() {
+        TwilioSmsProperties twilioSmsProperties = new TwilioSmsProperties();
+        twilioSmsProperties.setAccountSid("accountSid");
+        twilioSmsProperties.setAuthToken("authToken");
+        twilioSmsProperties.setTwilioPhoneNumber("twilioPhoneNumber");
+        TwilioSmsClientImpl twilioSmsClient = new 
TwilioSmsClientImpl(twilioSmsProperties);
+
+        assertEquals("twilio", twilioSmsClient.getType());
+    }
+
+    @ParameterizedTest
+    @CsvSource({
+            "accountSid, authToken, twilioPhoneNumber, true",
+            "accountSid, authToken, '', false",
+            "accountSid, '', twilioPhoneNumber, false",
+            "accountSid, '', '', false",
+            "'', authToken, twilioPhoneNumber, false",
+            "'', authToken, '', false",
+            "'', '', twilioPhoneNumber, false",
+            "'', '', '', false",
+    })
+    void checkConfig(String accountSid, String authToken, String 
twilioPhoneNumber, boolean expected) {
+        TwilioSmsProperties twilioSmsProperties = new TwilioSmsProperties();
+        twilioSmsProperties.setAccountSid(accountSid);
+        twilioSmsProperties.setAuthToken(authToken);
+        twilioSmsProperties.setTwilioPhoneNumber(twilioPhoneNumber);
+        TwilioSmsClientImpl twilioSmsClient = new 
TwilioSmsClientImpl(twilioSmsProperties);
+
+        assertEquals(expected, twilioSmsClient.checkConfig());
+    }
+}
\ No newline at end of file
diff --git 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/SmsConstants.java
 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/SmsConstants.java
index 16e0234a86..7929035562 100644
--- 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/SmsConstants.java
+++ 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/SmsConstants.java
@@ -35,4 +35,7 @@ public interface SmsConstants {
 
     // Aws cloud SMS
     String AWS = "aws";
+
+    // Twilio SMS
+    String TWILIO = "twilio";
 }
diff --git a/hertzbeat-manager/src/main/resources/application.yml 
b/hertzbeat-manager/src/main/resources/application.yml
index 10341c84ab..5839bf6dda 100644
--- a/hertzbeat-manager/src/main/resources/application.yml
+++ b/hertzbeat-manager/src/main/resources/application.yml
@@ -231,6 +231,10 @@ alerter:
       access-key-id: YOUR_ACCESS_KEY_ID
       access-key-secret: YOUR_ACCESS_KEY_SECRET
       region: AWS_REGION_FOR_END_USER_MESSAGING
+    twilio:
+      account-sid: YOUR_ACCOUNT_SID
+      auth-token: YOUR_AUTH_TOKEN
+      twilio-phone-number: YOUR_TWILIO_PHONE_NUMBER
 scheduler:
   server:
     enabled: true
diff --git a/home/docs/help/alert_sms.md b/home/docs/help/alert_sms.md
index 63fc55400a..317cd9e322 100644
--- a/home/docs/help/alert_sms.md
+++ b/home/docs/help/alert_sms.md
@@ -225,6 +225,52 @@ alerter:
 
    Now you can configure this information in your hertzbeat application.
 
+### Twilio SMS Configuration
+
+To activate and use the Twilio SMS service, refer to the official Twilio 
documentation: [SMS Getting Started 
Guide](https://www.twilio.com/docs/sms/quickstart)
+
+You can configure the Twilio SMS service either through the graphical 
interface or in the `application.yml` file.
+To use `application.yml`, add/fill in the following Twilio SMS configuration 
(replace parameters with your own SMS server configuration):
+
+```yaml
+alerter:
+      sms:
+            enable: true # Whether to enable
+            type: twilio # SMS provider type, supports "twilio"
+            twilio: # Twilio SMS configuration
+                  account-sid: # Your Twilio Account SID
+                  auth-token: # Your Twilio Auth Token
+                  twilio-phone-number: # Your Twilio Phone Number
+```
+
+1. Create a Twilio account
+
+      - If you don't have a Twilio account, sign up at [Twilio 
Console](https://www.twilio.com/console)
+      - You'll get a free trial account that allows you to test the SMS service
+
+2. Obtain Twilio credentials
+
+      - After signing in, locate your Account SID and Auth Token on the 
dashboard
+      - These credentials will be used to authenticate API requests
+
+3. Get a Twilio Phone Number
+
+      - In the Twilio Console, navigate to "Phone Numbers" > "Manage" > 
"Active Numbers"
+      - Click "Buy a Number" or use the trial number provided by Twilio
+      - Make sure the number has SMS capabilities enabled
+      - This number will be used as the sender for your SMS alerts
+      - A phone number is required for the Twilio SMS service to work
+
+4. Testing Your Configuration on Twilio
+
+      - Twilio provides a sandbox environment for testing
+      - If you are in the trial period, you will only be able to send SMS to 
verified phone numbers
+      - To verify a phone number, add it to your verified phone numbers list 
in the Twilio Console
+
+      > The message follows the format: "Instance: {}, Priority: {}, Content: 
{}"
+
+      This information can be configured in the Hertzbeat application.
+
 ## Operation steps
 
 1. **【Alarm notification】->【Add new recipient】 ->【Select SMS notification 
method】**
diff --git a/script/application.yml b/script/application.yml
index e79f661b46..b52a1b4d22 100644
--- a/script/application.yml
+++ b/script/application.yml
@@ -230,6 +230,10 @@ alerter:
       access-key-id: YOUR_ACCESS_KEY_ID
       access-key-secret: YOUR_ACCESS_KEY_SECRET
       region: AWS_REGION_FOR_END_USER_MESSAGING
+    twilio:
+      account-sid: YOUR_ACCOUNT_SID
+      auth-token: YOUR_AUTH_TOKEN 
+      twilio-phone-number: YOUR_TWILIO_PHONE_NUMBER
 scheduler:
   server:
     enabled: true
diff --git a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
index 4e46efaf4f..e0f4787cf6 100644
--- a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
+++ b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
@@ -197,6 +197,10 @@ alerter:
       access-key-id: YOUR_ACCESS_KEY_ID
       access-key-secret: YOUR_ACCESS_KEY_SECRET
       region: AWS_REGION_FOR_END_USER_MESSAGING
+    twilio:
+      account-sid: YOUR_ACCOUNT_SID
+      auth-token: YOUR_AUTH_TOKEN 
+      twilio-phone-number: YOUR_TWILIO_PHONE_NUMBER
 scheduler:
   server:
     enabled: true
diff --git 
a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
index 7fa65f3159..5552637a1b 100644
--- a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
+++ b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
@@ -193,6 +193,10 @@ alerter:
       access-key-id: YOUR_ACCESS_KEY_ID
       access-key-secret: YOUR_ACCESS_KEY_SECRET
       region: AWS_REGION_FOR_END_USER_MESSAGING
+    twilio:
+      account-sid: YOUR_ACCOUNT_SID
+      auth-token: YOUR_AUTH_TOKEN 
+      twilio-phone-number: YOUR_TWILIO_PHONE_NUMBER
 scheduler:
   server:
     enabled: true
diff --git 
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
index 249af2cdc0..bb09bf75d1 100644
--- 
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
+++ 
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
@@ -191,6 +191,10 @@ alerter:
       access-key-id: YOUR_ACCESS_KEY_ID
       access-key-secret: YOUR_ACCESS_KEY_SECRET
       region: AWS_REGION_FOR_END_USER_MESSAGING
+    twilio:
+      account-sid: YOUR_ACCOUNT_SID
+      auth-token: YOUR_AUTH_TOKEN 
+      twilio-phone-number: YOUR_TWILIO_PHONE_NUMBER
 scheduler:
   server:
     enabled: true
diff --git 
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
 
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
index 9388e078b7..647ad882d8 100644
--- 
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
+++ 
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
@@ -190,6 +190,10 @@ alerter:
       access-key-id: YOUR_ACCESS_KEY_ID
       access-key-secret: YOUR_ACCESS_KEY_SECRET
       region: AWS_REGION_FOR_END_USER_MESSAGING
+    twilio:
+      account-sid: YOUR_ACCOUNT_SID
+      auth-token: YOUR_AUTH_TOKEN 
+      twilio-phone-number: YOUR_TWILIO_PHONE_NUMBER
 scheduler:
   server:
     enabled: true
diff --git a/web-app/src/app/pojo/SmsNoticeSender.ts 
b/web-app/src/app/pojo/SmsNoticeSender.ts
index d3d4f2c049..759502b2aa 100644
--- a/web-app/src/app/pojo/SmsNoticeSender.ts
+++ b/web-app/src/app/pojo/SmsNoticeSender.ts
@@ -21,6 +21,7 @@ import { AlibabaSmsConfig } from './AlibabaSmsConfig';
 import { AwsSmsConfig } from './AwsSmsConfig';
 import { SmslocalSmsConfig } from './SmslocalSmsConfig';
 import { TencentSmsConfig } from './TencentSmsConfig';
+import { TwilioSmsConfig } from './TwilioSmsConfig';
 import { UniSmsConfig } from './UniSmsConfig';
 import { SmsType } from './enums/sms-type.enum';
 
@@ -32,6 +33,7 @@ export class SmsNoticeSender {
   unisms: UniSmsConfig = new UniSmsConfig();
   smslocal: SmslocalSmsConfig = new SmslocalSmsConfig();
   aws: AwsSmsConfig = new AwsSmsConfig();
+  twilio: TwilioSmsConfig = new TwilioSmsConfig();
   enable: boolean = false;
   creator!: string;
   modifier!: string;
diff --git a/web-app/src/app/pojo/enums/sms-type.enum.ts 
b/web-app/src/app/pojo/TwilioSmsConfig.ts
similarity index 80%
copy from web-app/src/app/pojo/enums/sms-type.enum.ts
copy to web-app/src/app/pojo/TwilioSmsConfig.ts
index f62f40a0e1..4735885aa5 100644
--- a/web-app/src/app/pojo/enums/sms-type.enum.ts
+++ b/web-app/src/app/pojo/TwilioSmsConfig.ts
@@ -17,15 +17,8 @@
  * under the License.
  */
 
-export enum SmsType {
-  TENCENT = 'tencent',
-  ALIBABA = 'alibaba',
-  UNISMS = 'unisms',
-  SMSLOCAL = 'smslocal',
-  AWS = 'aws'
-}
-
-export enum UniSmsAuthMode {
-  HMAC = 'hmac',
-  SIMPLE = 'simple'
+export class TwilioSmsConfig {
+  accountSid!: string;
+  authToken!: string;
+  twilioPhoneNumber!: string;
 }
diff --git a/web-app/src/app/pojo/enums/sms-type.enum.ts 
b/web-app/src/app/pojo/enums/sms-type.enum.ts
index f62f40a0e1..95dba15349 100644
--- a/web-app/src/app/pojo/enums/sms-type.enum.ts
+++ b/web-app/src/app/pojo/enums/sms-type.enum.ts
@@ -22,7 +22,8 @@ export enum SmsType {
   ALIBABA = 'alibaba',
   UNISMS = 'unisms',
   SMSLOCAL = 'smslocal',
-  AWS = 'aws'
+  AWS = 'aws',
+  TWILIO = 'twilio'
 }
 
 export enum UniSmsAuthMode {
diff --git 
a/web-app/src/app/routes/setting/settings/message-server/message-server.component.html
 
b/web-app/src/app/routes/setting/settings/message-server/message-server.component.html
index ad2db43370..a804f51547 100644
--- 
a/web-app/src/app/routes/setting/settings/message-server/message-server.component.html
+++ 
b/web-app/src/app/routes/setting/settings/message-server/message-server.component.html
@@ -72,6 +72,11 @@
               <br />
               {{ 'alert.notice.sender.sms.unisms.authMode' | i18n }}: {{ 
smsNoticeSender.unisms.authMode }}
             </ng-container>
+            <ng-container *ngSwitchCase="SmsType.TWILIO">
+              {{ 'alert.notice.sender.sms.twilio.accountSid' | i18n }}: {{ 
smsNoticeSender.twilio.accountSid }}
+              <br />
+              {{ 'alert.notice.sender.sms.twilio.twilioPhoneNumber' | i18n }}: 
{{ smsNoticeSender.twilio.twilioPhoneNumber }}
+            </ng-container>
           </ng-container>
           <br />
           {{ 'common.enable' | i18n }}: {{ smsNoticeSender.enable ? 
('common.yes' | i18n) : ('common.no' | i18n) }}
@@ -160,6 +165,7 @@
             <nz-option [nzValue]="SmsType.UNISMS" nzLabel="{{ 
'alert.notice.sender.sms.type.unisms' | i18n }}"></nz-option>
             <nz-option [nzValue]="SmsType.SMSLOCAL" nzLabel="{{ 
'alert.notice.sender.sms.type.smslocal' | i18n }}"></nz-option>
             <nz-option [nzValue]="SmsType.AWS" nzLabel="{{ 
'alert.notice.sender.sms.type.aws' | i18n }}"></nz-option>
+            <nz-option [nzValue]="SmsType.TWILIO" nzLabel="{{ 
'alert.notice.sender.sms.type.twilio' | i18n }}"></nz-option>
           </nz-select>
         </nz-form-control>
       </nz-form-item>
@@ -373,6 +379,48 @@
         </nz-form-item>
       </ng-container>
 
+      <!-- Twilio SMS -->
+      <ng-container *ngIf="smsType === SmsType.TWILIO">
+        <nz-form-item>
+          <nz-form-label [nzSpan]="7" nzFor="accountSid" nzRequired="true">
+            {{ 'alert.notice.sender.sms.twilio.accountSid' | i18n }}
+          </nz-form-label>
+          <nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | 
i18n">
+            <input
+              [(ngModel)]="smsNoticeSender.twilio.accountSid"
+              nz-input
+              required
+              name="accountSid"
+              type="password"
+              id="twilioAccountSid"
+            />
+          </nz-form-control>
+        </nz-form-item>
+        <nz-form-item>
+          <nz-form-label [nzSpan]="7" nzFor="authToken" nzRequired="true">
+            {{ 'alert.notice.sender.sms.twilio.authToken' | i18n }}
+          </nz-form-label>
+          <nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | 
i18n">
+            <input [(ngModel)]="smsNoticeSender.twilio.authToken" nz-input 
required name="authToken" type="password" id="twilioAuthToken" />
+          </nz-form-control>
+        </nz-form-item>
+        <nz-form-item>
+          <nz-form-label [nzSpan]="7" nzFor="twilioPhoneNumber" 
nzRequired="true">
+            {{ 'alert.notice.sender.sms.twilio.twilioPhoneNumber' | i18n }}
+          </nz-form-label>
+          <nz-form-control [nzSpan]="12" [nzErrorTip]="'validation.required' | 
i18n">
+            <input
+              [(ngModel)]="smsNoticeSender.twilio.twilioPhoneNumber"
+              nz-input
+              required
+              name="twilioPhoneNumber"
+              type="text"
+              id="twilioPhoneNumber"
+            />
+          </nz-form-control>
+        </nz-form-item>
+      </ng-container>
+
       <nz-form-item>
         <nz-form-label nzSpan="7" nzFor="smsEnable" nzRequired="true">{{ 
'common.enable' | i18n }}</nz-form-label>
         <nz-form-control nzSpan="12">
diff --git 
a/web-app/src/app/routes/setting/settings/message-server/message-server.component.ts
 
b/web-app/src/app/routes/setting/settings/message-server/message-server.component.ts
index 3989d14f21..c20b89cf68 100644
--- 
a/web-app/src/app/routes/setting/settings/message-server/message-server.component.ts
+++ 
b/web-app/src/app/routes/setting/settings/message-server/message-server.component.ts
@@ -33,6 +33,7 @@ import { SmsType, UniSmsAuthMode } from 
'src/app/pojo/enums/sms-type.enum';
 import { AwsSmsConfig } from '../../../../pojo/AwsSmsConfig';
 import { EmailNoticeSender } from '../../../../pojo/EmailNoticeSender';
 import { SmslocalSmsConfig } from '../../../../pojo/SmslocalSmsConfig';
+import { TwilioSmsConfig } from '../../../../pojo/TwilioSmsConfig';
 import { GeneralConfigService } from 
'../../../../service/general-config.service';
 
 @Component({
@@ -146,6 +147,7 @@ export class MessageServerComponent implements OnInit {
             this.smsNoticeSender.unisms = { ...new UniSmsConfig(), 
...message.data.unisms };
             this.smsNoticeSender.smslocal = { ...new SmslocalSmsConfig(), 
...message.data.smslocal };
             this.smsNoticeSender.aws = { ...new AwsSmsConfig(), 
...message.data.aws };
+            this.smsNoticeSender.twilio = { ...new TwilioSmsConfig(), 
...message.data.twilio };
             this.smsType = message.data.type || 'tencent';
           } else {
             this.smsNoticeSender = new SmsNoticeSender();
@@ -171,7 +173,8 @@ export class MessageServerComponent implements OnInit {
       alibaba: { ...this.smsNoticeSender.alibaba },
       unisms: { ...this.smsNoticeSender.unisms },
       smslocal: { ...this.smsNoticeSender.smslocal },
-      aws: { ...this.smsNoticeSender.aws }
+      aws: { ...this.smsNoticeSender.aws },
+      twilio: { ...this.smsNoticeSender.twilio }
     };
     this.isSmsServerModalVisible = true;
   }
@@ -184,7 +187,8 @@ export class MessageServerComponent implements OnInit {
       alibaba: { ...this.tempSmsNoticeSender.alibaba },
       unisms: { ...this.tempSmsNoticeSender.unisms },
       smslocal: { ...this.tempSmsNoticeSender.smslocal },
-      aws: { ...this.tempSmsNoticeSender.aws }
+      aws: { ...this.tempSmsNoticeSender.aws },
+      twilio: { ...this.tempSmsNoticeSender.twilio }
     };
     this.isSmsServerModalVisible = false;
   }
diff --git a/web-app/src/assets/i18n/en-US.json 
b/web-app/src/assets/i18n/en-US.json
index 8553bdfe31..f6b128fa39 100644
--- a/web-app/src/assets/i18n/en-US.json
+++ b/web-app/src/assets/i18n/en-US.json
@@ -158,12 +158,16 @@
   "alert.notice.sender.sms.aws.accessKeyId": "Aws SMS AccessKeyId",
   "alert.notice.sender.sms.aws.accessKeySecret": "Aws SMS AccessKeySecret",
   "alert.notice.sender.sms.aws.region": "Aws Region",
+  "alert.notice.sender.sms.twilio.accountSid": "Twilio Account SID",
+  "alert.notice.sender.sms.twilio.authToken": "Twilio Auth Token",
+  "alert.notice.sender.sms.twilio.twilioPhoneNumber": "Twilio Phone Number",
   "alert.notice.sender.sms.type": "Sms Type",
   "alert.notice.sender.sms.type.alibaba": "Alibaba Sms",
   "alert.notice.sender.sms.type.tencent": "Tencent Sms",
   "alert.notice.sender.sms.type.unisms": "UniSMS",
   "alert.notice.sender.sms.type.smslocal": "Smslocal Sms",
   "alert.notice.sender.sms.type.aws": "Aws Sms",
+  "alert.notice.sender.sms.type.twilio": "Twilio Sms",
   "alert.notice.template": "Notice Template",
   "alert.notice.template.content": "Template Content",
   "alert.notice.template.delete": "Delete Template",
diff --git a/web-app/src/assets/i18n/ja-JP.json 
b/web-app/src/assets/i18n/ja-JP.json
index 3a849708fe..4f5df70163 100644
--- a/web-app/src/assets/i18n/ja-JP.json
+++ b/web-app/src/assets/i18n/ja-JP.json
@@ -158,12 +158,16 @@
   "alert.notice.sender.sms.aws.accessKeyId": "Aws SMS AccessKeyId",
   "alert.notice.sender.sms.aws.accessKeySecret": "Aws SMS AccessKeySecret",
   "alert.notice.sender.sms.aws.region": "Aws Region",
+  "alert.notice.sender.sms.twilio.accountSid": "Twilio Account SID",
+  "alert.notice.sender.sms.twilio.authToken": "Twilio Auth Token",
+  "alert.notice.sender.sms.twilio.twilioPhoneNumber": "Twilio Phone Number",
   "alert.notice.sender.sms.type": "SMSタイプ",
   "alert.notice.sender.sms.type.alibaba": "Alibaba Sms",
   "alert.notice.sender.sms.type.tencent": "Tencent Sms",
   "alert.notice.sender.sms.type.unisms": "UniSMS",
   "alert.notice.sender.sms.type.smslocal": "Smslocal Sms",
   "alert.notice.sender.sms.type.aws": "Aws Sms",
+  "alert.notice.sender.sms.type.twilio": "Twilio Sms",
   "alert.notice.template": "通知テンプレート",
   "alert.notice.template.content": "テンプレート内容",
   "alert.notice.template.delete": "テンプレートを削除",
diff --git a/web-app/src/assets/i18n/zh-CN.json 
b/web-app/src/assets/i18n/zh-CN.json
index 4b24ed798d..bb382f071d 100644
--- a/web-app/src/assets/i18n/zh-CN.json
+++ b/web-app/src/assets/i18n/zh-CN.json
@@ -158,12 +158,16 @@
   "alert.notice.sender.sms.aws.accessKeyId": "Aws SMS AccessKeyId",
   "alert.notice.sender.sms.aws.accessKeySecret": "Aws SMS AccessKeySecret",
   "alert.notice.sender.sms.aws.region": "Aws Region",
+  "alert.notice.sender.sms.twilio.accountSid": "Twilio Account SID",
+  "alert.notice.sender.sms.twilio.authToken": "Twilio Auth Token",
+  "alert.notice.sender.sms.twilio.twilioPhoneNumber": "Twilio Phone Number",
   "alert.notice.sender.sms.type": "短信类型",
   "alert.notice.sender.sms.type.alibaba": "阿里短信",
   "alert.notice.sender.sms.type.tencent": "腾讯短信",
   "alert.notice.sender.sms.type.unisms": "合一短信(UniSMS)",
   "alert.notice.sender.sms.type.smslocal": "当地短信(Smslocal)",
   "alert.notice.sender.sms.type.aws": "Aws Sms",
+  "alert.notice.sender.sms.type.twilio": "Twilio Sms",
   "alert.notice.template": "通知模板",
   "alert.notice.template.content": "模板内容",
   "alert.notice.template.delete": "删除通知模板",
diff --git a/web-app/src/assets/i18n/zh-TW.json 
b/web-app/src/assets/i18n/zh-TW.json
index 0dd6edc967..433274d281 100644
--- a/web-app/src/assets/i18n/zh-TW.json
+++ b/web-app/src/assets/i18n/zh-TW.json
@@ -158,6 +158,9 @@
   "alert.notice.sender.sms.aws.accessKeyId": "Aws SMS AccessKeyId",
   "alert.notice.sender.sms.aws.accessKeySecret": "Aws SMS AccessKeySecret",
   "alert.notice.sender.sms.aws.region": "Aws Region",
+  "alert.notice.sender.sms.twilio.accountSid": "Twilio Account SID",
+  "alert.notice.sender.sms.twilio.authToken": "Twilio Auth Token",
+  "alert.notice.sender.sms.twilio.twilioPhoneNumber": "Twilio Phone Number",
   "alert.notice.sender.sms.type": "騰訊類型",
   "alert.notice.sender.sms.type.alibaba": "阿裏短訊",
   "alert.notice.sender.sms.type.tencent": "騰訊短訊",


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to