Enviar mensagens SMS com Dart e Programmable SMS da Twilio

March 13, 2020
Escrito por
Alex Baban
Contribuidor
As opiniões expressas pelos colaboradores da Twilio são de sua autoria

Enviar mensagens SMS com Dart e Programmable SMS da Twilio

De acordo com o relatório anual Octoverse do GitHub, o Dart foi a linguagem de programação que teve o crescimento mais rápido em 2019.

Dart é uma linguagem otimizada para o cliente que proporciona aplicativos rápidos em qualquer plataforma. Desenvolvido pelo Google, o Dart é usado para a criação de aplicativos móveis, de desktop, de back-end e da Web. O código Dart é fácil de escrever e não requer muito trabalho com gerenciamento de memória e sistemas de criação.

Desde a versão 2.6, anunciada em novembro de 2019, o Dart vem com o dart2native, um compilador que produz executáveis autônomos e específicos de plataforma. Graças a isso, os desenvolvedores podem criar ferramentas para a linha de comando no macOS, Windows ou Linux. Os executáveis totalmente independentes podem ser executados instantaneamente em máquinas que não precisam ter o SDK do Dart instalado.

A API do Programmable SMS da Twilio facilita o envio e o recebimento de mensagens SMS. Você pode enviar mensagens em nível global usando os números de telefone da Twilio, que lidam com as complexidades da operadora para você. Ao integrar a API em um pacote, você e seus colegas desenvolvedores de Dart podem chegar ao trabalho importante ainda mais rápido.

O que você aprenderá

Criar pacotes Dart é fácil e divertido e este post mostrará como. Você aprenderá a criar um pacote de bibliotecas Dart e usá-lo em um pacote diferente. Você aprenderá a chamar a API do Programmable SMS da Twilio pelo código Dart. Você aprenderá a converter seu pacote Dart em um aplicativo de linha de comando executável que usa argumentos.

O que você criará

Seu próprio pacote Dart chamado my_twilio. Em seguida, você usará o my_twilio em um aplicativo de linha de comando para enviar mensagens de texto usando a API do Twilio.

Pré-requisitos

Convenções de layout do pacote

Um pacote de bibliotecas é um pacote do qual outros pacotes podem depender. Quando você cria um pacote, há algumas convenções de estilo a serem seguidas. As mais relevantes para este projeto são:

  • nomeie bibliotecas, pacotes, diretórios e arquivos de origem usando caixa baixa_com_sublinhados
  • nomeie funções, variáveis e parâmetros usando minúsculasconcatenadas
  • classes e tipos de nome usando MaiúsculasConcatenadas
  • coloque a importação de dart: na parte superior antes de outras importações
  • coloque as importações de package: antes das importações relativas

As convenções de layout do pacote relevantes para este projeto são:

  • cada pacote tem um arquivo pubspec.yaml no diretório raiz do pacote
  • as bibliotecas que são públicas para outros pacotes devem ficar na pasta lib
  • o código de implementação é colocado em lib/src e é considerado privado
  • se você incluir um exemplo de como usar sua biblioteca, ela deverá ficar no diretório example na raiz do pacote

A estrutura do pacote my_twilio

my_twilio
 ├── example
 │    └── send_sms.dart
 ├── lib
 │    ├── src
 │    │    ├── constants.dart
 │    │    ├── messages.dart
 │    │    └── utils.dart
 │    └── my_twilio.dart
 └── pubspec.yaml

Configuração da estrutura do pacote

No local de sua escolha, crie uma nova pasta chamada my_twilio.

Na pasta my_twilio, crie um arquivo de texto chamado pubspec.yaml e duas pastas chamadas lib e example.

Na pasta example, crie um arquivo de texto chamado send_sms.dart.

Dentro de lib, crie um arquivo de texto chamado my_twilio.dart e uma pasta chamada src.

Dentro da pasta src, crie três arquivos de texto chamados messages.dartconstants.dart e utils.dart.

Criar o código-fonte

Insira o seguinte código no arquivo pubspec.yaml:

# pubspec.yaml 

name: my_twilio

version: 0.1.0

environment:
  sdk: '>=2.6.0 <3.0.0'

dependencies:
  http: ^0.12.0+4

O arquivo pubspec.yaml contém alguns metadados sobre o pacote:

  • cada pacote precisa de um nome
  • cada pacote tem uma versão
  • um pacote talvez funcione somente com certas versões do SDK do Dart e você pode especificar essas versões usando uma restrição de SDK
  • a seção de dependências lista cada pacote necessário para o funcionamento do seu pacote

Insira o seguinte código Dart no arquivo my_twilio.dart no diretório lib:

// my_twilio.dart

import './src/messages.dart' show Messages;

class MyTwilio {
  final String _accountSid;
  final String _authToken;

  const MyTwilio(this._accountSid, this._authToken);

  Messages get messages => Messages(_accountSid, _authToken);
}

O código no arquivo my_twilio.dart é a biblioteca exposta pelo pacote my_twilio, que pode ser importada em outros pacotes ou programas Dart. Ela começa com uma importação da classe Messages definida em ./src/messages.dart. Ela define uma classe MyTwilio com dois campos, _accountSid e _authToken, um constructor (construtor) MyTwilio() e um getter, messages, que retorna uma classe Messages. Como mostrado abaixo, a classe Messages define um método chamado create() que faz a tarefa de chamar a API da Twilio para enviar a mensagem de texto.

Insira o seguinte código no arquivo constants.dart no diretório lib:

// constants.dart

const String TWILIO_SMS_API_BASE_URL = 'https://api.twilio.com/2010-04-01';

Para manter o código limpo e organizado, as constantes ficam em constants.dart. Por enquanto, um tipo de constante String é definido para conter o valor do URL base da API de SMS da Twilio. Se necessário, você pode adicionar mais constantes aqui.

Insira o seguinte código no arquivo utils.dart no diretório lib:

// utils.dart

import 'dart:convert' show base64, utf8;

// returns base64 encoded Twilio credentials
// used in authorization headers of http requests
String toAuthCredentials(String accountSid, String authToken) =>
    base64.encode(utf8.encode(accountSid + ':' + authToken));

O código em utils.dart define uma função chamada toAuthCredentials() que retorna suas credenciais da Twilio como uma string codificada base64 pronta para ser usada no cabeçalho de autorização de uma solicitação HTTP enviada para a API da Twilio. A codificação é feita com a ajuda da biblioteca dart:convert, que faz parte das principais bibliotecas do Dart.

Insira o seguinte código Dart no arquivo messages.dart na pasta lib:

// messages.dart

import 'dart:convert' show json;

import 'package:http/http.dart' as http;

import './constants.dart';
import './utils.dart';

class Messages {
  final String _accountSid;
  final String _authToken;

  const Messages(this._accountSid, this._authToken);

  Future<Map> create(data) async {
    var client = http.Client();

    var url =
        '${TWILIO_SMS_API_BASE_URL}/Accounts/${_accountSid}/Messages.json';

    try {
      var response = await client.post(url, headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Authorization': 'Basic ' + toAuthCredentials(_accountSid, _authToken)
      }, body: {
        'From': data['from'],
        'To': data['to'],
        'Body': data['body']
      });

      return (json.decode(response.body));
    } catch (e) {
      return ({'Runtime Error': e});
    } finally {
      client.close();
    }
  }
}

O código messages.dart importa o código de constants.dartutils.dart e do pacote de bibliotecas externas http de pub.dev (o principal repositório público para pacotes Dart). http contém um conjunto de funções e classes de alto nível que facilitam o consumo de recursos HTTP.

O código em messages.dart define uma classe Messages que contém dois campos, _accountSid e _authToken, um constructor (construtor) Messages() e um método create(). Quando uma instância de Messages é criada, os valores de suas credenciais da Twilio são armazenados em _accountSid e _authToken. Quando o método create() é chamado, uma solicitação POST HTTP autenticada é enviada à API da Twilio com a mensagem de texto.

Com a ajuda do pacote http, uma instância http.Client() é criada e client.post() é chamado. Esta é uma solicitação HTTP assíncrona e as palavras-chave async/await (assíncrono/espera) são usadas para instruir o programa que ele precisa esperar pela resposta da Twilio.

Instalar dependências

A ferramenta pub é o gerenciador de pacotes da linguagem de programação Dart. O comando pub get obtém todas as dependências listadas no arquivo pubspec.yaml no diretório de trabalho atual, bem como suas dependências transitivas. Uma dependência transitiva é usada indiretamente pelo pacote porque uma de suas dependências requer isso.

my_twilio depende do pacote http hospedado em pub.dev. Você informa o pub sobre isso na seção de dependências em pubspec.yaml:

...
dependencies:
  http: ^0.12.0+4
...

Reticências ("...") indica uma seção de código elaborada para brevidade ou ênfase.

Abra uma janela do console no diretório my_twilio e execute a seguinte instrução de linha de comando:

pub get

O utilitário pub baixará e salvará o pacote http e suas dependências.

Testar o pacote com um exemplo

Copie o seguinte código no arquivo send_sms.dart na pasta de exemplo:

// send_sms.dart

import 'dart:io' show Platform;

import 'package:my_twilio/my_twilio.dart';

Future<void> main() async {
  // See http://twil.io/secure for important security information
  var _accountSid = Platform.environment['TWILIO_ACCOUNT_SID'];
  var _authToken = Platform.environment['TWILIO_AUTH_TOKEN'];

  // Your Account SID and Auth Token from www.twilio.com/console
  // You can skip this block if you store your credentials in environment variables
  _accountSid ??= 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
  _authToken ??= 'your_auth_token';

  // Create an authenticated client instance for Twilio API
  var client = new MyTwilio(_accountSid, _authToken);

  // Send a text message
  // Returns a Map object (key/value pairs)
  Map message = await client.messages.create({
    'body': 'Hello from Dart!',
    'from': '+12345678901', // a valid Twilio number
    'to': '+12345678902' // your phone number
  });

  // access individual properties using the square bracket notation
  // for example print(message['sid']);
  print(message);
}

O código send_sms.dart importa a biblioteca my_twilio, cria uma instância da classe MyTwilio e chama client.messages.create() para enviar uma mensagem de texto. O argumento create() é uma coleção de pares de chave/valor para "body" (corpo), o texto da mensagem; "from" (de), um número válido da Twilio; e "to" (para), o número de telefone de destino da mensagem.

Primeiro, as credenciais para acesso à API da Twilio são retiradas de variáveis de ambiente com uma tentativa como esta:

var _accountSid = Platform.environment['TWILIO_ACCOUNT_SID'];

Se as variáveis de ambiente não forem definidas, _accountSid armazenará um valor null.

Com _accountSid ??= 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; _accountSid é testado para null e, se for null, o valor do lado direito do operador ??= será atribuído a ele. Se você definiu variáveis de ambiente para suas credenciais da Twilio, esta linha não terá nenhum efeito.

Substitua _accountSid e _authToken por seus valores secretos do usuário da Twilio ou escolha a melhor opção e defina as variáveis de ambiente TWILIO_ACCOUNT_SID e TWILIO_AUTH_TOKEN.

Substitua os números de telefone from e to por seu número de telefone da Twilio e o número de telefone celular que você usou para se registrar em sua conta da Twilio.

Também é possível substituir o valor body por sua mensagem.

Para executar o app de exemplo pela linha de comando, execute o comando dart, como se segue, na máquina virtual Dart.

Abra uma janela do console na pasta my_twilio e execute a seguinte instrução de linha de comando:

dart ./example/send_sms.dart

Se o programa for executado com êxito, você verá uma saída de linha de comando semelhante a esta:

{sid: SMf4a886e1db45417687035967922ca19c, date_created: Mon, 09 Mar 2020 20:19:50 +0000, date_updated: Mon, 09 Mar 2020 20:19:50 +0000, date_sent: null, account_sid: AC40000000000000000000000000000006, to: +16175551212, from: +12125551212, messaging_service_sid: null, body: Hello again from Dart., status: queued, num_segments: 1, num_media: 0, direction: outbound-api, api_version: 2010-04-01, price: null, price_unit: USD, error_code: null, error_message: null, uri: /2010-04-01/Accounts/AC40000000000000000000000000000006/Messages/SMf40000000045417687035967922ca19c.json, subresource_uris: {media: /2010-04-01/Accounts/AC40000000000000000000000000000006/Messages/SMf40000000045417687035967922ca19c/Media.json}}

Logo em seguida, você deverá receber uma mensagem SMS em seu dispositivo móvel. Ela virá do seu número de telefone da Twilio e a mensagem será o valor fornecido para body acima.

Compilar um executável autônomo com dart2native

A ferramenta dart2native é um compilador que você pode usar para compilar um programa Dart no código da máquina. Você pode criar uma versão executável do pacote my_twilio em algumas etapas.

Abra uma janela do console na pasta my_twilio.

No Windows, execute a seguinte instrução de linha de comando:

dart2native ./example/send_sms.dart -o ./example/send_sms.exe

No macOS, execute:

dart2native ./example/send_sms.dart -o ./example/send_sms

O compilador criará send_sms.exe no Windows ou send_sms no macOS) na pasta de exemplo.

Você pode executar esse executável em qualquer computador que tenha o mesmo sistema operacional em que foi criado. No momento, não há suporte para a compilação cruzada.  Além disso, o executável contém tudo o que precisa ser executado. (O SDK do Dart não é necessário.)

Usar o pacote my_twilio em outro projeto Dart

Na mesma pasta onde você criou a pasta my_twilio, crie uma pasta chamada my_dart_project.

Na pasta my_dart_project, crie um arquivo de texto chamado pubspec.yaml e duas pastas chamadas bin e lib.

Na pasta bin, crie um arquivo de texto chamado main.dart. Este será o ponto de entrada do seu app.

Copie o seguinte código no arquivo pubspec.yaml:

# pubspec.yaml

name: my_dart_project

version: 0.1.0

environment:
  sdk: '>=2.6.0 <3.0.0'
  
dependencies:
  my_twilio:
    path: ../my_twilio

Você pode adicionar um pacote local como uma dependência ao projeto, basta especificar o caminho relativo para o pacote na seção de dependências pubspec.yaml. Isso informa ao utilitário pub como localizar o pacote.

Abra uma janela do console na pasta my_dart_project e execute a seguinte instrução de linha de comando:

pub get

Quando o utilitário pub terminar de ser executado, você poderá importar my_twilio para seu código Dart.

Insira o seguinte código Dart no arquivo main-dart:

// main.dart

import 'dart:io' show Platform;

import 'package:my_twilio/my_twilio.dart';

Future<void> main() async {
  // See http://twil.io/secure for important security information
  var _accountSid = Platform.environment['TWILIO_ACCOUNT_SID'];
  var _authToken = Platform.environment['TWILIO_AUTH_TOKEN'];

  // Your Account SID and Auth Token from www.twilio.com/console
  // You can skip this block if you store your credentials in environment variables
  _accountSid ??= 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
  _authToken ??= 'your_auth_token';

  // Create an authenticated client instance for Twilio API
  var client = new MyTwilio(_accountSid, _authToken);

  // Send a text message
  // Returns a Map object (key/value pairs)
  Map message = await client.messages.create({
    'body': 'Hello from Dart.',
    'from': '+12345678901', // a valid Twilio number
    'to': '+12345678902' // your phone number
  });

  // access individual properties using the square bracket notation
  // for example print(message['sid']);
  print(message);
}

Substitua _accountSid e _authToken por seus valores secretos do usuário da Twilio ou escolha a melhor opção e defina as variáveis de ambiente TWILIO_ACCOUNT_SID e TWILIO_AUTH_TOKEN.

Substitua os números de telefone from e to por seu número de telefone da Twilio e o número de telefone celular que você usou para se registrar em sua conta da Twilio.

Também é possível substituir o valor body por sua mensagem.

Testar o executável my_dart_project

Abra uma janela do console na pasta my_dart_project e execute a seguinte instrução de linha de comando:

dart ./bin/main.dart

Você verá uma saída de console semelhante à produzida pelo pacote my_twilio e deverá receber uma mensagem SMS no seu celular.

Criar um executável autônomo que usa argumentos

Você modificará main.dart para criar um executável de linha de comando sms.exe, no Windows, ou sms, no macOS, e inseri-lo nos argumentos --from--to e --body para que ele possa ser executado como o seguinte exemplo:

sms.exe --from +12345678901 --to +12345678902 --body 'Hello from Dart!'

Comece importando o pacote hospedado args, que tem uma biblioteca, arg_parser.dart. Você usará esse pacote para analisar a lista de argumentos e retornar os resultados.

Adicione o pacote args como uma dependência no arquivo pubspec.yaml modificando a seção dependencies para que ele se pareça com o bloco de códigos a seguir. Observe que o recuo mostrado é obrigatório.

...
dependencies:
  my_twilio:
    path: ../my_twilio
  args: ^1.5.0

Em uma janela de console no diretório my_dart_project, execute pub get para obter o pacote args.

Na parte superior do arquivo main-dart, importe args, modifique a instrução import dart:io para expor a função exit e definir uma variável global chamada argResults com um tipo de ArgResults, como mostrado no bloco de códigos abaixo.

Você usará a função exit() para encerrar o programa se nenhum argumento válido for passado.

Você usará a variável argResults para armazenar um conjunto de pares de chave/valor contendo os argumentos do programa.

import 'dart:io' show Platform, exit;

import 'package:args/args.dart';
import 'package:my_twilio/my_twilio.dart';

ArgResults argResults;
...

Modifique o método main() para aceitar argumentos de linha de comando, que serão passados como uma lista de strings.

Na parte superior da função main(), adicione uma chamada a uma função parseCommandLineArguments(), como mostrado abaixo: 

Future<void> main(List<String> args) async {
  // ensure the required command-line arguments are present
  // then parse and save them in the argResults variable
  parseCommandLineArguments(args);
...

Defina a função parseCommandLineArguments() adicionando o seguinte código Dart ao arquivo main.dart acima da função main():

void parseCommandLineArguments(arguments) {
  final parser = new ArgParser()
    ..addOption('from',
        abbr: 'f', help: 'A valid Twilio number (in E.164 format).')
    ..addOption('to',
        abbr: 't', help: 'Your (destination) phone number (in E.164 format).')
    ..addOption('body',
        abbr: 'b',
        help: 'The text of the message. (surrounded with quotation marks)');

  try {
    argResults = parser.parse(arguments);

    // if required arguments are not present throw exception
    // usage will be printed
    if (argResults.options.isEmpty ||
        argResults['from'] == null ||
        argResults['to'] == null ||
        argResults['body'] == null) {
      throw ArgParserException;
    }
  } catch (e) {

    print('Sends a text message via Twilio\'s API\n' +
        'usage: sms --from|-f <phone number> --to|-t <phone number> --body|-b <message text>');
    print(parser.usage);
    print(
        'example: sms -f +12345678901 -t +12345678902 -b "Hey, how\'s it going?"');
    exit(1);
  }
}

A função começa com a criação de um objeto parser, uma instância da classe ArgParser exposta pelo pacote args. Usando as chamadas do método addOption(), as opções de linha de comando esperadas são adicionadas ao analisador. Em seguida, os argumentos do programa são analisados com uma chamada para parser.parse(arguments) e salvos em argResults. Se o programa for chamado com argumentos inválidos ou sem argumentos, ArgParserException será lançado e as instruções de uso serão impressas com a ajuda de parser.usage, que contém o texto de ajuda.

O último item a ser alterado na função main() é substituir os valores codificados dos parâmetros bodyfrom e to no método client.messages.create() pelos valores obtidos da coleção argResults.

Substitua a função client.messages.create() existente pelo seguinte código:

  Map message = await client.messages.create({
    'body': argResults['body'], // the text of the message
    'from': argResults['from'], // a valid Twilio number
    'to': argResults['to'] // your phone number
  });

Se precisar verificar se as alterações de código estão corretas, você poderá verificá-las em relação ao arquivo main.dart no repositório associado dart-twilio-sms no GitHub.

Testar o pacote my_dart_project

No diretório my_dart_project, abra uma janela do console e execute a seguinte instrução de linha de comando:

dart ./bin/main.dart --from +12345678901 --to +12345678902 --body "Hello again from Dart."

Você deve ver a saída do console semelhante à saída mostrada acima para o pacote my_twilio. Você também deve receber uma mensagem SMS no dispositivo móvel especificado pelo argumento --to.

Criar um executável autônomo pelo pacote my_dart_project

No Windows, abra uma janela do console na pasta my_dart_project e execute a seguinte instrução de linha de comando:

dart2native ./bin/main.dart -o ./bin/sms.exe

No macOS, execute o seguinte comando:

dart2native ./bin/main.dart -o ./bin/sms

Como testar o executável my_dart_project com argumentos

No Windows, para testar o executável compilado, abra uma janela do console na pasta bin e execute o programa com os argumentos de linha de comando, substituindo os valores mostrados abaixo pelo número de telefone da Twilio e pelo número de telefone celular registrado:

sms.exe --from +12345678901 --to +12345678902 --body "Hello again from Dart."

No macOS, execute:

./sms --from +12345678901 --to +12345678902 --body "Hello again from Dart."

Você verá uma saída de console semelhante a esta e deverá receber uma mensagem SMS em seu dispositivo móvel.

{sid: SMf4a886e1db45417687035967922ca19c, date_created: Mon, 09 Mar 2020 20:19:50 +0000, date_updated: Mon, 09 Mar 2020 20:19:50 +0000, date_sent: null, account_sid: AC40000000000000000000000000000006, to: +16175551212, from: +12125551212, messaging_service_sid: null, body: Hello again from Dart., status: queued, num_segments: 1, num_media: 0, direction: outbound-api, api_version: 2010-04-01, price: null, price_unit: USD, error_code: null, error_message: null, uri: /2010-04-01/Accounts/AC40000000000000000000000000000006/Messages/SMf40000000045417687035967922ca19c.json, subresource_uris: {media: /2010-04-01/Accounts/AC40000000000000000000000000000006/Messages/SMf40000000045417687035967922ca19c/Media.json}}

Resumo

Ao criar um pacote simples de bibliotecas Dart e usá-lo para criar um programa de linha de comando autônomo para enviar mensagens de texto, você aprendeu a se comunicar com a API de mensagens da Twilio usando o código Dart. Você pode adicionar mais funcionalidade a my_twilio para interagir com outras APIs da Twilio, como o Programmable Voice.

Recursos adicionais

Documentos sobre o Dart: Criar pacotes

Documentos sobre o Dart: Escrever aplicativos de linha de comando

pub.dev http (biblioteca para fazer solicitações HTTP)

pub.dev args (biblioteca para analisar argumentos de linha de comando)

Saiba mais lendo o código-fonte de outros projetos Dart no GitHub firebase-dartdart-sass.

Este artigo foi traduzido do original "Sending SMS Messages with Dart and Twilio Programmable SMS". Enquanto melhoramos nossos processos de tradução, adoraríamos receber seus comentários em help@twilio.com - contribuições valiosas podem render brindes da Twilio.