Commit eb8975d504be5d02d52d7693c6afaba01da7982d

Authored by Andrii Shvaika
1 parent 031f579b

SMS Email Limits

... ... @@ -34,6 +34,7 @@ import org.thingsboard.rule.engine.api.TbRelationTypes;
34 34 import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
35 35 import org.thingsboard.server.actors.ActorSystemContext;
36 36 import org.thingsboard.server.actors.TbActorRef;
  37 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
37 38 import org.thingsboard.server.common.data.Customer;
38 39 import org.thingsboard.server.common.data.DataConstants;
39 40 import org.thingsboard.server.common.data.Device;
... ...
... ... @@ -21,7 +21,6 @@ import freemarker.template.Template;
21 21 import lombok.extern.slf4j.Slf4j;
22 22 import org.apache.commons.lang3.StringUtils;
23 23 import org.jetbrains.annotations.NotNull;
24   -import org.springframework.beans.factory.annotation.Autowired;
25 24 import org.springframework.context.MessageSource;
26 25 import org.springframework.core.NestedRuntimeException;
27 26 import org.springframework.mail.javamail.JavaMailSenderImpl;
... ... @@ -40,6 +39,8 @@ import org.thingsboard.server.common.data.id.EntityId;
40 39 import org.thingsboard.server.common.data.id.TenantId;
41 40 import org.thingsboard.server.dao.exception.IncorrectParameterException;
42 41 import org.thingsboard.server.dao.settings.AdminSettingsService;
  42 +import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
  43 +import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
43 44
44 45 import javax.annotation.PostConstruct;
45 46 import javax.mail.MessagingException;
... ... @@ -58,18 +59,24 @@ public class DefaultMailService implements MailService {
58 59 public static final String UTF_8 = "UTF-8";
59 60 public static final int _10K = 10000;
60 61 public static final int _1M = 1000000;
61   - @Autowired
62   - private MessageSource messages;
63 62
64   - @Autowired
65   - private Configuration freemarkerConfig;
  63 + private final MessageSource messages;
  64 + private final Configuration freemarkerConfig;
  65 + private final AdminSettingsService adminSettingsService;
  66 + private final TbApiUsageStateService apiUsageStateService;
  67 + private final TbApiUsageClient apiUsageClient;
66 68
67 69 private JavaMailSenderImpl mailSender;
68 70
69 71 private String mailFrom;
70 72
71   - @Autowired
72   - private AdminSettingsService adminSettingsService;
  73 + public DefaultMailService(MessageSource messages, Configuration freemarkerConfig, AdminSettingsService adminSettingsService, TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) {
  74 + this.messages = messages;
  75 + this.freemarkerConfig = freemarkerConfig;
  76 + this.adminSettingsService = adminSettingsService;
  77 + this.apiUsageStateService = apiUsageStateService;
  78 + this.apiUsageClient = apiUsageClient;
  79 + }
73 80
74 81 @PostConstruct
75 82 private void init() {
... ... @@ -148,8 +155,11 @@ public class DefaultMailService implements MailService {
148 155 }
149 156
150 157 @Override
151   - public void sendEmail(String email, String subject, String message) throws ThingsboardException {
152   - sendMail(mailSender, mailFrom, email, subject, message);
  158 + public void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException {
  159 + if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) {
  160 + sendMail(mailSender, mailFrom, email, subject, message);
  161 + apiUsageClient.report(tenantId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1);
  162 + }
153 163 }
154 164
155 165 @Override
... ... @@ -223,20 +233,23 @@ public class DefaultMailService implements MailService {
223 233 }
224 234
225 235 @Override
226   - public void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException {
227   - MimeMessage mailMsg = mailSender.createMimeMessage();
228   - MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8");
229   - helper.setFrom(StringUtils.isBlank(from) ? mailFrom : from);
230   - helper.setTo(to.split("\\s*,\\s*"));
231   - if (!StringUtils.isBlank(cc)) {
232   - helper.setCc(cc.split("\\s*,\\s*"));
233   - }
234   - if (!StringUtils.isBlank(bcc)) {
235   - helper.setBcc(bcc.split("\\s*,\\s*"));
  236 + public void send(TenantId tenantId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException {
  237 + if (apiUsageStateService.getApiUsageState(tenantId).isEmailSendEnabled()) {
  238 + MimeMessage mailMsg = mailSender.createMimeMessage();
  239 + MimeMessageHelper helper = new MimeMessageHelper(mailMsg, "UTF-8");
  240 + helper.setFrom(StringUtils.isBlank(from) ? mailFrom : from);
  241 + helper.setTo(to.split("\\s*,\\s*"));
  242 + if (!StringUtils.isBlank(cc)) {
  243 + helper.setCc(cc.split("\\s*,\\s*"));
  244 + }
  245 + if (!StringUtils.isBlank(bcc)) {
  246 + helper.setBcc(bcc.split("\\s*,\\s*"));
  247 + }
  248 + helper.setSubject(subject);
  249 + helper.setText(body);
  250 + mailSender.send(helper.getMimeMessage());
  251 + apiUsageClient.report(tenantId, ApiUsageRecordKey.EMAIL_EXEC_COUNT, 1);
236 252 }
237   - helper.setSubject(subject);
238   - helper.setText(body);
239   - mailSender.send(helper.getMimeMessage());
240 253 }
241 254
242 255 @Override
... ...
... ... @@ -17,7 +17,6 @@ package org.thingsboard.server.service.sms;
17 17
18 18 import com.fasterxml.jackson.databind.JsonNode;
19 19 import lombok.extern.slf4j.Slf4j;
20   -import org.springframework.beans.factory.annotation.Autowired;
21 20 import org.springframework.core.NestedRuntimeException;
22 21 import org.springframework.stereotype.Service;
23 22 import org.thingsboard.rule.engine.api.SmsService;
... ... @@ -26,12 +25,15 @@ import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
26 25 import org.thingsboard.rule.engine.api.sms.config.SmsProviderConfiguration;
27 26 import org.thingsboard.rule.engine.api.sms.config.TestSmsRequest;
28 27 import org.thingsboard.server.common.data.AdminSettings;
  28 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
29 29 import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
30 30 import org.thingsboard.server.common.data.exception.ThingsboardException;
31 31 import org.thingsboard.server.common.data.id.EntityId;
32 32 import org.thingsboard.server.common.data.id.TenantId;
33 33 import org.thingsboard.server.dao.settings.AdminSettingsService;
34 34 import org.thingsboard.server.dao.util.mapping.JacksonUtil;
  35 +import org.thingsboard.server.queue.usagestats.TbApiUsageClient;
  36 +import org.thingsboard.server.service.apiusage.TbApiUsageStateService;
35 37
36 38 import javax.annotation.PostConstruct;
37 39 import javax.annotation.PreDestroy;
... ... @@ -40,14 +42,20 @@ import javax.annotation.PreDestroy;
40 42 @Slf4j
41 43 public class DefaultSmsService implements SmsService {
42 44
43   - @Autowired
44   - private SmsSenderFactory smsSenderFactory;
45   -
46   - @Autowired
47   - private AdminSettingsService adminSettingsService;
  45 + private final SmsSenderFactory smsSenderFactory;
  46 + private final AdminSettingsService adminSettingsService;
  47 + private final TbApiUsageStateService apiUsageStateService;
  48 + private final TbApiUsageClient apiUsageClient;
48 49
49 50 private SmsSender smsSender;
50 51
  52 + public DefaultSmsService(SmsSenderFactory smsSenderFactory, AdminSettingsService adminSettingsService, TbApiUsageStateService apiUsageStateService, TbApiUsageClient apiUsageClient) {
  53 + this.smsSenderFactory = smsSenderFactory;
  54 + this.adminSettingsService = adminSettingsService;
  55 + this.apiUsageStateService = apiUsageStateService;
  56 + this.apiUsageClient = apiUsageClient;
  57 + }
  58 +
51 59 @PostConstruct
52 60 private void init() {
53 61 updateSmsConfiguration();
... ... @@ -78,18 +86,26 @@ public class DefaultSmsService implements SmsService {
78 86 }
79 87 }
80 88
81   - @Override
82   - public void sendSms(String numberTo, String message) throws ThingsboardException {
  89 + private int sendSms(String numberTo, String message) throws ThingsboardException {
83 90 if (this.smsSender == null) {
84 91 throw new ThingsboardException("Unable to send SMS: no SMS provider configured!", ThingsboardErrorCode.GENERAL);
85 92 }
86   - this.sendSms(this.smsSender, numberTo, message);
  93 + return this.sendSms(this.smsSender, numberTo, message);
87 94 }
88 95
89 96 @Override
90   - public void sendSms(String[] numbersTo, String message) throws ThingsboardException {
91   - for (String numberTo : numbersTo) {
92   - this.sendSms(numberTo, message);
  97 + public void sendSms(TenantId tenantId, String[] numbersTo, String message) throws ThingsboardException {
  98 + if (apiUsageStateService.getApiUsageState(tenantId).isSmsSendEnabled()) {
  99 + int smsCount = 0;
  100 + try {
  101 + for (String numberTo : numbersTo) {
  102 + smsCount += this.sendSms(numberTo, message);
  103 + }
  104 + } finally {
  105 + if (smsCount > 0) {
  106 + apiUsageClient.report(tenantId, ApiUsageRecordKey.SMS_EXEC_COUNT, smsCount);
  107 + }
  108 + }
93 109 }
94 110 }
95 111
... ...
... ... @@ -21,7 +21,9 @@ public enum ApiFeature {
21 21 TRANSPORT("transportApiState", "Device API"),
22 22 DB("dbApiState", "Telemetry persistence"),
23 23 RE("ruleEngineApiState", "Rule Engine execution"),
24   - JS("jsExecutionApiState", "JavaScript functions execution");
  24 + JS("jsExecutionApiState", "JavaScript functions execution"),
  25 + EMAIL("emailApiState", "Email messages"),
  26 + SMS("smsApiState", "SMS messages");
25 27
26 28 @Getter
27 29 private final String apiStateKey;
... ...
... ... @@ -23,11 +23,15 @@ public enum ApiUsageRecordKey {
23 23 TRANSPORT_DP_COUNT(ApiFeature.TRANSPORT, "transportDataPointsCount", "transportDataPointsLimit"),
24 24 STORAGE_DP_COUNT(ApiFeature.DB, "storageDataPointsCount", "storageDataPointsLimit"),
25 25 RE_EXEC_COUNT(ApiFeature.RE, "ruleEngineExecutionCount", "ruleEngineExecutionLimit"),
26   - JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit");
  26 + JS_EXEC_COUNT(ApiFeature.JS, "jsExecutionCount", "jsExecutionLimit"),
  27 + EMAIL_EXEC_COUNT(ApiFeature.EMAIL, "emailCount", "emailLimit"),
  28 + SMS_EXEC_COUNT(ApiFeature.SMS, "smsCount", "smsLimit");
27 29 private static final ApiUsageRecordKey[] JS_RECORD_KEYS = {JS_EXEC_COUNT};
28 30 private static final ApiUsageRecordKey[] RE_RECORD_KEYS = {RE_EXEC_COUNT};
29 31 private static final ApiUsageRecordKey[] DB_RECORD_KEYS = {STORAGE_DP_COUNT};
30 32 private static final ApiUsageRecordKey[] TRANSPORT_RECORD_KEYS = {TRANSPORT_MSG_COUNT, TRANSPORT_DP_COUNT};
  33 + private static final ApiUsageRecordKey[] EMAIL_RECORD_KEYS = {EMAIL_EXEC_COUNT};
  34 + private static final ApiUsageRecordKey[] SMS_RECORD_KEYS = {SMS_EXEC_COUNT};
31 35
32 36 @Getter
33 37 private final ApiFeature apiFeature;
... ... @@ -52,6 +56,10 @@ public enum ApiUsageRecordKey {
52 56 return RE_RECORD_KEYS;
53 57 case JS:
54 58 return JS_RECORD_KEYS;
  59 + case EMAIL:
  60 + return EMAIL_RECORD_KEYS;
  61 + case SMS:
  62 + return SMS_RECORD_KEYS;
55 63 default:
56 64 return new ApiUsageRecordKey[]{};
57 65 }
... ...
... ... @@ -47,6 +47,12 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
47 47 @Getter
48 48 @Setter
49 49 private ApiUsageStateValue jsExecState;
  50 + @Getter
  51 + @Setter
  52 + private ApiUsageStateValue emailExecState;
  53 + @Getter
  54 + @Setter
  55 + private ApiUsageStateValue smsExecState;
50 56
51 57 public ApiUsageState() {
52 58 super();
... ... @@ -64,6 +70,8 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
64 70 this.dbStorageState = ur.getDbStorageState();
65 71 this.reExecState = ur.getReExecState();
66 72 this.jsExecState = ur.getJsExecState();
  73 + this.emailExecState = ur.getEmailExecState();
  74 + this.smsExecState = ur.getSmsExecState();
67 75 }
68 76
69 77 public boolean isTransportEnabled() {
... ... @@ -81,4 +89,12 @@ public class ApiUsageState extends BaseData<ApiUsageStateId> implements HasTenan
81 89 public boolean isJsExecEnabled() {
82 90 return !ApiUsageStateValue.DISABLED.equals(jsExecState);
83 91 }
  92 +
  93 + public boolean isEmailSendEnabled(){
  94 + return !ApiUsageStateValue.DISABLED.equals(emailExecState);
  95 + }
  96 +
  97 + public boolean isSmsSendEnabled(){
  98 + return !ApiUsageStateValue.DISABLED.equals(smsExecState);
  99 + }
84 100 }
... ...
... ... @@ -42,6 +42,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
42 42 private long maxJSExecutions;
43 43 private long maxDPStorageDays;
44 44 private int maxRuleNodeExecutionsPerMessage;
  45 + private long maxEmails;
  46 + private long maxSms;
45 47
46 48 private double warnThreshold;
47 49
... ... @@ -58,6 +60,10 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura
58 60 return maxREExecutions;
59 61 case STORAGE_DP_COUNT:
60 62 return maxDPStorageDays;
  63 + case EMAIL_EXEC_COUNT:
  64 + return maxEmails;
  65 + case SMS_EXEC_COUNT:
  66 + return maxSms;
61 67 }
62 68 return 0L;
63 69 }
... ...
... ... @@ -450,6 +450,8 @@ public class ModelConstants {
450 450 public static final String API_USAGE_STATE_DB_STORAGE_COLUMN = "db_storage";
451 451 public static final String API_USAGE_STATE_RE_EXEC_COLUMN = "re_exec";
452 452 public static final String API_USAGE_STATE_JS_EXEC_COLUMN = "js_exec";
  453 + public static final String API_USAGE_STATE_EMAIL_EXEC_COLUMN = "email_exec";
  454 + public static final String API_USAGE_STATE_SMS_EXEC_COLUMN = "sms_exec";
453 455
454 456 /**
455 457 * Cassandra attributes and timeseries constants.
... ...
... ... @@ -63,6 +63,12 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
63 63 @Enumerated(EnumType.STRING)
64 64 @Column(name = ModelConstants.API_USAGE_STATE_JS_EXEC_COLUMN)
65 65 private ApiUsageStateValue jsExecState = ApiUsageStateValue.ENABLED;
  66 + @Enumerated(EnumType.STRING)
  67 + @Column(name = ModelConstants.API_USAGE_STATE_EMAIL_EXEC_COLUMN)
  68 + private ApiUsageStateValue emailExecState = ApiUsageStateValue.ENABLED;
  69 + @Enumerated(EnumType.STRING)
  70 + @Column(name = ModelConstants.API_USAGE_STATE_SMS_EXEC_COLUMN)
  71 + private ApiUsageStateValue smsExecState = ApiUsageStateValue.ENABLED;
66 72
67 73 public ApiUsageStateEntity() {
68 74 }
... ... @@ -83,6 +89,8 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
83 89 this.dbStorageState = ur.getDbStorageState();
84 90 this.reExecState = ur.getReExecState();
85 91 this.jsExecState = ur.getJsExecState();
  92 + this.emailExecState = ur.getEmailExecState();
  93 + this.smsExecState = ur.getSmsExecState();
86 94 }
87 95
88 96 @Override
... ... @@ -99,6 +107,8 @@ public class ApiUsageStateEntity extends BaseSqlEntity<ApiUsageState> implements
99 107 ur.setDbStorageState(dbStorageState);
100 108 ur.setReExecState(reExecState);
101 109 ur.setJsExecState(jsExecState);
  110 + ur.setEmailExecState(emailExecState);
  111 + ur.setSmsExecState(smsExecState);
102 112 return ur;
103 113 }
104 114
... ...
... ... @@ -78,6 +78,8 @@ public class ApiUsageStateServiceImpl extends AbstractEntityService implements A
78 78 apiUsageState.setReExecState(ApiUsageStateValue.ENABLED);
79 79 apiUsageState.setJsExecState(ApiUsageStateValue.ENABLED);
80 80 apiUsageState.setDbStorageState(ApiUsageStateValue.ENABLED);
  81 + apiUsageState.setSmsExecState(ApiUsageStateValue.ENABLED);
  82 + apiUsageState.setEmailExecState(ApiUsageStateValue.ENABLED);
81 83 apiUsageStateValidator.validate(apiUsageState, ApiUsageState::getTenantId);
82 84
83 85 ApiUsageState saved = apiUsageStateDao.save(apiUsageState.getTenantId(), apiUsageState);
... ...
... ... @@ -416,5 +416,7 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
416 416 db_storage varchar(32),
417 417 re_exec varchar(32),
418 418 js_exec varchar(32),
  419 + email_exec varchar(32),
  420 + sms_exec varchar(32),
419 421 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
420 422 );
... ...
... ... @@ -442,6 +442,8 @@ CREATE TABLE IF NOT EXISTS api_usage_state (
442 442 db_storage varchar(32),
443 443 re_exec varchar(32),
444 444 js_exec varchar(32),
  445 + email_exec varchar(32),
  446 + sms_exec varchar(32),
445 447 CONSTRAINT api_usage_state_unq_key UNIQUE (tenant_id, entity_id)
446 448 );
447 449
... ...
... ... @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.ApiFeature;
20 20 import org.thingsboard.server.common.data.ApiUsageStateMailMessage;
21 21 import org.thingsboard.server.common.data.ApiUsageStateValue;
22 22 import org.thingsboard.server.common.data.exception.ThingsboardException;
  23 +import org.thingsboard.server.common.data.id.TenantId;
23 24
24 25 import javax.mail.MessagingException;
25 26
... ... @@ -27,7 +28,7 @@ public interface MailService {
27 28
28 29 void updateMailConfiguration();
29 30
30   - void sendEmail(String email, String subject, String message) throws ThingsboardException;
  31 + void sendEmail(TenantId tenantId, String email, String subject, String message) throws ThingsboardException;
31 32
32 33 void sendTestMail(JsonNode config, String email) throws ThingsboardException;
33 34
... ... @@ -39,7 +40,7 @@ public interface MailService {
39 40
40 41 void sendPasswordWasResetEmail(String loginLink, String email) throws ThingsboardException;
41 42
42   - void send(String from, String to, String cc, String bcc, String subject, String body) throws MessagingException;
  43 + void send(TenantId tenantId, String from, String to, String cc, String bcc, String subject, String body) throws MessagingException;
43 44
44 45 void sendAccountLockoutEmail( String lockoutEmail, String email, Integer maxFailedLoginAttempts) throws ThingsboardException;
45 46
... ...
... ... @@ -17,14 +17,13 @@ package org.thingsboard.rule.engine.api;
17 17
18 18 import org.thingsboard.rule.engine.api.sms.config.TestSmsRequest;
19 19 import org.thingsboard.server.common.data.exception.ThingsboardException;
  20 +import org.thingsboard.server.common.data.id.TenantId;
20 21
21 22 public interface SmsService {
22 23
23 24 void updateSmsConfiguration();
24 25
25   - void sendSms(String numberTo, String message) throws ThingsboardException;
26   -
27   - void sendSms(String[] numbersTo, String message) throws ThingsboardException;;
  26 + void sendSms(TenantId tenantId, String[] numbersTo, String message) throws ThingsboardException;;
28 27
29 28 void sendTestSms(TestSmsRequest testSmsRequest) throws ThingsboardException;
30 29
... ...
... ... @@ -19,6 +19,7 @@ import io.netty.channel.EventLoopGroup;
19 19 import org.springframework.data.redis.core.RedisTemplate;
20 20 import org.thingsboard.common.util.ListeningExecutor;
21 21 import org.thingsboard.rule.engine.api.sms.SmsSenderFactory;
  22 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
22 23 import org.thingsboard.server.common.data.Customer;
23 24 import org.thingsboard.server.common.data.Device;
24 25 import org.thingsboard.server.common.data.DeviceProfile;
... ...
... ... @@ -26,6 +26,7 @@ import org.thingsboard.rule.engine.api.TbNode;
26 26 import org.thingsboard.rule.engine.api.TbNodeConfiguration;
27 27 import org.thingsboard.rule.engine.api.TbNodeException;
28 28 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  29 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
29 30 import org.thingsboard.server.common.data.plugin.ComponentType;
30 31 import org.thingsboard.server.common.msg.TbMsg;
31 32
... ... @@ -87,7 +88,7 @@ public class TbSendEmailNode implements TbNode {
87 88
88 89 private void sendEmail(TbContext ctx, EmailPojo email) throws Exception {
89 90 if (this.config.isUseSystemSmtpSettings()) {
90   - ctx.getMailService().send(email.getFrom(), email.getTo(), email.getCc(),
  91 + ctx.getMailService().send(ctx.getTenantId(), email.getFrom(), email.getTo(), email.getCc(),
91 92 email.getBcc(), email.getSubject(), email.getBody());
92 93 } else {
93 94 MimeMessage mailMsg = mailSender.createMimeMessage();
... ...
... ... @@ -23,6 +23,7 @@ import org.thingsboard.rule.engine.api.TbNodeConfiguration;
23 23 import org.thingsboard.rule.engine.api.TbNodeException;
24 24 import org.thingsboard.rule.engine.api.sms.SmsSender;
25 25 import org.thingsboard.rule.engine.api.util.TbNodeUtils;
  26 +import org.thingsboard.server.common.data.ApiUsageRecordKey;
26 27 import org.thingsboard.server.common.data.plugin.ComponentType;
27 28 import org.thingsboard.server.common.msg.TbMsg;
28 29
... ... @@ -75,7 +76,7 @@ public class TbSendSmsNode implements TbNode {
75 76 String message = TbNodeUtils.processPattern(this.config.getSmsMessageTemplate(), msg.getMetaData());
76 77 String[] numbersToList = numbersTo.split(",");
77 78 if (this.config.isUseSystemSmsSettings()) {
78   - ctx.getSmsService().sendSms(numbersToList, message);
  79 + ctx.getSmsService().sendSms(ctx.getTenantId(), numbersToList, message);
79 80 } else {
80 81 for (String numberTo : numbersToList) {
81 82 this.smsSender.sendSms(numberTo, message);
... ...