Por que eu devo ler este artigo:Nesse artigo aprenderemos sobre os aplicativos do Google, os famosos Google Apps, bem como os scripts da linguagem que o mesmo disponibiliza, baseada em JavaScript, para implementar as nossas regras e aplicações online. O leitor verá como criar uma função de forma personalizada, consumir os serviços mais essenciais da suíte do Google, bem como praticar via exemplos práticos a forma como tudo funciona integradamente. Isso tudo será útil na integração de suas aplicações web de forma mais rápida e simples com os produtos Google, sem necessitar sequer uma IDE ou ferramentas desktop para tal.

É muito difícil imaginar uma única pessoa no mundo que não tenha utilizado pelo menos um dos produtos ou serviços do Google em sua vida. Ele é conhecido pelo seu famoso motor de busca, os vídeos do portal YouTube e também por suas inúmeras aplicações web.

O Google Apps é um conjunto de aplicações, nomeadamente, Gmail, Calendário, Drive, Docs e Formulários, as quais você pode personalizar e automatizar usando scripts comuns da linguagem JavaScript com classes bem definidas pelo próprio Google baseadas no Google App Script (GAS), uma linguagem que herda do JavaScript, porém com customizações específicas para as aplicações Google como um todo. Quase todas as aplicações do Google oferecem um ou mais serviços. Os serviços e APIs GAS fornecem fácil acesso para automatizar tarefas em todos os produtos Google, inclusive em serviços de terceiros. Você pode usar essas classes de serviços em seu código GAS para personalizar ou automatizar suas aplicações do Google Apps.

Google App Script

Antes de começar, precisamos responder rapidamente a algumas perguntas que você pode ter:

  • Onde é que todos esses Apps são executados? Em meu computador?
    o Res: Não, todos esses apps são executados em servidores baseados na nuvem do próprio Google.
  • Como posso obter acesso a esses apps?
    o Você pode interagir com esses aplicativos através de navegadores web. Nenhum hardware especial ou instalações de software são necessárias, exceto um moderno navegador em seu desktop.

O Google implementa o GAS baseado no JavaScript 1.6 com algumas porções do 1.7 e 1.8. Você também pode escrever seu código no Google Docs, planilhas e formulários que usam o GAS e ainda pode automatizar tarefas de maneira semelhante ao que o Visual Basic faz no Microsoft Office. Entretanto o GAS é executado no servidor do Google e os resultados são processados no browser. O editor de script integrado permite editar e depurar seus scripts do navegador, e relembrando, não é preciso instalar nada. Podemos também ativar as funções de depuração e testes para rodar tanto com base em suas interações quanto com base em um acionador de resposta a um evento ou intervalos de tempo. Esses eventos incluem onOpen, onEdit, onInstall e muitos outros. O GAS também é usado para criar add-ons em documentos, planilhas e formulários, além de ajudá-lo com todos os aspectos de automatização de tarefas.

Sua única limitação é que o GAS pode não funcionar continuamente por mais de seis minutos (a princípio). Todas as suas funções devem concluir a execução e retornar resultados dentro desse prazo.

Serviços do Google Apps

Os serviços do Google Apps permitem que o GAS interaja com praticamente todos os Apps do Google. Como dito anteriormente, quase todos os Apps fornecem um ou mais serviços. Você pode usar essas classes de serviço em seu código GAS para personalizar e automatizar os Apps. Os serviços são agrupados como básicos e avançados: os básicos podem ser usados diretamente, porém para os avançados é necessário pagamento para ativação.

Para entender melhor tais conceitos e iniciar nossas atividades, vejamos os passos para criar uma Planilha Google, na prática:

  1. Execute o seu navegador favorito e digite a URL do Google Drive disponível na seção Links na barra de endereços. (Para usar o Google Drive é necessário possuir uma conta Google. Se não a tiver, você deverá criar uma.).
  2. No painel esquerdo, clique no botão Novo e Planilhas Google.
  3. Após criar uma nova planilha, se o leitor desejar que um outro usuário tenha acesso às configurações que fará, basta clicar com o botão esquerdo sobre a opção compartilhar na parte superior e um novo pop-up será aberto. Depois disso, digite o endereço de e-mail, ou endereços, com o qual irá compartilhar o documento. Por fim clique em Enviar.

Os exemplos desenvolvidos neste artigo são uma adaptação do livro “Learning Google Apps Script”, do Ramalingam Ganapathy (vide seção Links para URL oficial).

Scripts de projetos

Os scripts são organizados como projetos, que podem ser de dois tipos: independentes ou delimitados a um tipo de arquivo (arquivos nativos do Google Drive, tais como planilhas, documentos e formulários). Scripts independentes são criados em um arquivo separado. Você pode ver esses arquivos listados entre outros arquivos no Google Drive. Scripts delimitados estão embutidos dentro de arquivos gtype (tipo “Google”) individuais e foram criados usando as respectivas aplicações. Como você pode ver, os arquivos de script independente podem ser abertos diretamente a partir do Drive, porém os scripts delimitados só podem ser abertos dentro das respectivas aplicações. No entanto, o script delimitado terá mais privilégios, por exemplo você pode ter acesso ao documento ativo dentro de scripts delimitados, mas não dentro de scripts independentes.

Para criar um arquivo de script siga estes passos:

  1. É preciso conectar o aplicativo ao Google Drive. Ao clicar em NOVO, selecione a opção Mais e depois Conectar mais apps. Marque para exibir apenas Apps do Google e selecione o GAS.
  2. Sigas os passos conforme descrito na criação de planilhas no Drive.
  3. Navegue até NOVO > Mais > Script do Google Apps ao invés de Planilhas Google, como ilustra a Figura 1.
Abrindo o GAS
Figura 1. Abrindo o GAS.
  1. Um novo projeto ainda sem título será aberto em uma nova aba ou janela do navegador. O novo projeto inclui um ficheiro de código, Código.gs, com uma função em branco, myFunction(), como mostrado na Figura 2.
Criando um novo projeto GAS
Figura 2. Criando um novo projeto GAS.
  1. Para salvar ou mudar o nome do novo projeto, pressione Ctrl + S no teclado ou clique na opção Save (ícone do disquete) no editor. Se você está salvando o projeto pela primeira vez, em seguida será apresentado um pedido para introduzir um novo nome ao projeto. Digite o nome do projeto e clique no botão Ok. O novo arquivo de script será guardado na pasta atual.

Criando novo projeto em planilhas

Crie uma nova planilha ou abra a existente. Você verá uma série de itens de menu na parte superior da janela. Clique em Ferramentas > Editor de Scripts. Isso irá abrir uma nova janela com um novo projeto sem título. Salve o projeto como descrito na seção anterior.

Embora seja possível criar vários projetos delimitados, um projeto por arquivo é suficiente e pode ajudá-lo a evitar problemas com função e nomes de variáveis duplicadas. Agora abra a planilha que você criou anteriormente e faça as seguintes alterações: Nas colunas A e B, digite alguns nomes e sobrenomes. Na célula C2, inclua a fórmula =CONCATENAR(A2; " "; B2). Agora você poderá ver o primeiro nome e sobrenome das células A2 e B2 respectivamente concatenados com um espaço entre eles. CONCATENAR em planilhas Google é uma função que está embutida na fórmula. Você também pode criar a sua própria, chamando uma fórmula personalizada. Por exemplo, abra o editor de script, copie e cole o seguinte código.


function myFunction(s1,s2) {
  return s1 + " " + s2;
  }

Salve o script, volte para a planilha e na célula C2 digite =myFunction(A2;B2). Isso funciona exatamente da mesma maneira que a fórmula embutida. Você pode estender a sua fórmula para outras células abaixo da C2. Vimos que essa é uma fórmula simples, mas que também podem ser criadas fórmulas complexas de acordo com suas exigências. Sua fórmula personalizada deve retornar um valor único ou uma matriz bidimensional. Na Figura 3, podemos ver como a função personalizada irá funcionar.

Implementando uma função personalizada
Figura 3. Implementando uma função personalizada.

Criando elementos básicos

Vimos até aqui sobre o GAS e como criar um projeto de script. Agora, vamos aprender como criar um botão clicável, um menu personalizado, uma caixa de mensagem, uma sidebar e caixas de diálogo, bem como uma forma de depurar os scripts para facilitar nos testes.

Abra o editor de script na recém-criada planilha do Google, selecione qualquer uma de suas células e clique em Inserir > Desenho no menu superior. A janela de edição do desenho será aberta, clique no ícone da caixa de texto e em qualquer lugar dentro da área da tela, e então digite “Clique em mim”, salve e saia do editor. A mensagem “Clique em mim” será exibida na célula selecionada. Você também pode arrastar essa imagem para qualquer parte da planilha, exceto para a barra de menu. Se você clicar na imagem uma seta estilo drop-down será exibida no canto superior direito. Selecione a função Transferir Script e uma janela de atribuição ao script será aberta (Figura 4). Você poderá atribuir qualquer nome, mas lembre-se que esse nome será utilizado para criar uma função nos passos seguintes, neste artigo usaremos o nome devmediaGAS.

Criando um novo script GAS
Figura 4. Criando um novo script GAS.

Agora abra o editor de script na mesma planilha. Ao abrir o editor, uma função padrão, myFunction, já estará lá, apague todas as informações e entre com o código a seguir:


function devmediaGAS() {
    Browser.msgBox("DevmediaGAS", "Hello World", Browser.Buttons.OK)
  }

Nesse código, o msgBox é o método da classe do navegador com três parâmetros, cujos nomes são autoexplicativos. O parâmetro title denota o título da caixa de mensagem, o prompt, a mensagem ao usuário e buttons, a categoria ou conjuntos de botões que você gostaria de incluir na sua caixa de mensagem.

Feito isso, será ativado na planilha o seu botão “Clique em mim”. Acesse a janela da planilha e clique no seu botão; será aberta uma tela de autorização, então clique no botão continuar e permita o acesso, logo após, como podemos visualizar na Figura 5, a sua caixa de mensagem será exibida. Sempre que clicar sobre o botão, essa caixa de mensagem será aberta.

Caixa de mensagem do botão
Figura 5. Caixa de mensagem do botão.

Exibindo um toast quando um botão é clicado

O toast aparece como um pop-up no canto inferior direito da planilha ativa, com um título e a mensagem em si. Para criar um script toast é preciso criar uma função nova, ou editar/substituir a função devmediaGAS que criamos, conforme a Listagem 1.

Listagem 1. Modificando a função devmediaGAS para um script toast.

function devmediaGAS() {
  SpreadsheetApp.getActiveSpreadsheet().toast("Hello World", "DevmediaGAS");
  }

Agora, se você clicar no seu botão “Clique em mim”, em seguida uma caixa de diálogo toast aparecerá no canto inferior da tela e irá desparecer dentro de 5 segundos. Você também pode incluir um terceiro argumento, ou seja, segundos de tempo de espera. Caso queira mostrar o mesmo sem tempo é só adicionar um número negativo, por exemplo: ("Hello World", "DevmediaGAS", -1).

Você deve estar se perguntando se pode executar a função devmediaGAS sem a ajuda do botão. A resposta é sim. No editor de script há um menu com a função Run. Se você clicar em Run > devmediaGAS, em seguida a função devmediaGAS será executada e a caixa de mensagem será aberta, ou seja, ela não depende da interação com botão.

Criando um menu personalizado

Criar um botão para cada função pode não ser viável. Embora você não possa alterar ou adicionar itens para o menu padrão (exceto o menu Complementos), como arquivo, editar, exibir, e assim por diante, você pode adicionar menus personalizados e itens de menu. Para essa tarefa, vamos criar um novo documento através do Google Documentos, ou você pode abrir um documento existente.

Abra o editor de script e digite as funções como na Listagem 2.

Listagem 2. Criando um menu personalizado.

function createMenu() {
  DocumentApp.getUi()
  .createMenu("PACKT")
  .addItem("DevmediaGAS" ,"devmediaGAS")
  .addToUi();
  }
   
  function devmediaGAS() {
  var ui = DocumentApp.getUi();
  ui.alert("DevmediaGAS", "Hello World", ui.ButtonSet.OK);
  }

Na primeira função estamos usando a classe DocumentApp, invocando o método getUi e consecutivamente os métodos createMenu, addItem e addToUi via encadeamento de funções. A segunda função deve ser familiar para você, pois seu método alert é bem similar ao msgBox usado anteriormente, mas dessa vez com a classe DocumentApp e métodos associados. Agora, execute a função createMenu e mude para a janela do documento (você deverá salvar o projeto para utilizar o documento). Você verá um novo item no menu chamado PACKT ao lado da opção Ajuda.

Você pode ver no menu personalizado PACKT um item DevmediaGAS, que está associado com a função. O item do menu DevmediaGAS funciona da mesma forma que o botão que criamos antes. A desvantagem desse método de inserção do menu personalizado é que para obtê-lo e mostrá-lo é necessário executar a função createMenu toda vez, dentro do editor de script. Considere como o usuário seria capaz de utilizar essa função devmediaGAS se ele não conhece a técnica. Pense que o utilizador pode não ser um programador como você. Para permitir que os usuários executem as funções GAS selecionadas, você deve criar um menu personalizado e torná-lo visível assim que o documento for aberto, ou seja, através da função onOpen. A função onOpen é uma função especial, pois sempre que um usuário abre um documento, o intérprete GAS executa essa função em primeiro lugar. Outros nomes de funções semelhantes são: onEdit, onInstall, doGet e doPost. As duas primeiras são funções relacionadas a eventos de planilha e as duas seguintes são referentes a serviços de scripts GET e POST como funções de callback.

Criando uma sidebar

A sidebar é uma caixa de diálogo estática e está incluída no lado direito da janela do editor de documentos. Na Listagem 3, temos o código para criar uma sidebar.

Listagem 3. Criando uma sidebar.

function onOpen() {
  var htmlOutput = HtmlService
  .createHtmlOutput('<button onclick="alert(\'Hello World\');">Clique em mim</button>')
  .setTitle('Minha Sidebar');
  DocumentApp.getUi().showSidebar(htmlOutput);
  }

No código anterior, usamos o HtmlService (classe de serviços utilitários para HTML) e invocamos o seu método createHtmlOutput (função que imprime um valor em HTML usando o respectivo service), consecutivamente invocando o método setTitle. Para testar esse código, execute a função onOpen ou recarregue o documento. A sidebar será aberta no lado direito da janela do documento. O layout da sidebar tem tamanho fixo, o que significa que não é possível mudá-la ou redimensioná-la.

Criando um novo item no Menu Complementos

Na Listagem 3, incluímos a linha de código HTML como um argumento de string ao método createHtmlOutput. Alternativamente, você pode colocar esse trecho HTML em um arquivo separado. Para criar um novo arquivo HTML, no editor de script vá em Arquivo > New > Html file, e em seguida, na caixa Create File, digite o nome para o novo arquivo. Nesse exemplo será utilizado Index. Insira as tags button entre as tags do body como mostrado na Listagem 4. Depois, insira o código da Listagem 5 no arquivo Código.gs.

Listagem 4. Editando o arquivo html.

<!DOCTYPE html>
  <html>
  <head>
  <base target="_top">
  </head>
  <body>
  <button onclick="alert('Hello World');">Clique em mim</button>
  </body>
  </html>
Listagem 5. Criando um novo item no menu Complementos para exibir uma sidebar.

function createMenu() {
      DocumentApp.getUi()
          .createMenu("PACKT")
          .addItem("DevmediaGAS", "devmediaGAS")
          .addToUi();
  }
   
  function devmediaGAS() {
      var ui = DocumentApp.getUi();
      ui.alert("DevmediaGAS", "Hello World", ui.ButtonSet.OK);
  }
   
  function onOpen() {
      DocumentApp.getUi()
          .createAddonMenu()
          .addItem("Exibir Sidebar", "exibirSidebar")
          .addToUi();
  }
   
  function exibirSidebar() {
      DocumentApp.getUi()
          .showSidebar(HtmlService.createHtmlOutputFromFile('Index')
              .setTitle('devmediaGAS'));
  }

Para testar o código, execute a função onOpen ou recarregue o documento. No menu, um novo item foi adicionado ao menu Complementos, chamado DevmediaGAS. Clicando nesse item e selecionando o exibirSidebar, sua sidebar será exibida.

Criando uma caixa de diálogo

Para criar uma caixa de diálogo que impede o usuário de atualizar qualquer coisa no documento, devemos atualizar o código no arquivo Código.gs como exibido na Listagem 6.

Listagem 6. Criando uma caixa de diálogo modal.

function devmediaGAS() {
  var ui = DocumentApp.getUi();
  ui.alert("DevmediaGAS", "Hello World!", ui.ButtonSet.OK);
  }
  function onOpen(){
  DocumentApp.getUi()
  .createAddonMenu()
  .addItem("Exibir Dialogo", "exibirDialogo")
  .addToUi();
  }
  function exibirDialogo() {
  var html = HtmlService
  .createHtmlOutputFromFile('Index');
  DocumentApp.getUi()
  .showModalDialog(html, 'DevmediaGAS');
  }

O método showModalDialog adiciona uma caixa de diálogo, impedindo que seja feita alguma alteração no documento. Após executar o código, vá até o menu Complementos e selecione a opção Exibir Dialogo, e então será aberto um pop-up com sua caixa de diálogo.

Agora vamos criar uma caixa de diálogo modeless e ver a diferença da modal. Para isso é preciso alterar a função exibirDialogo() com o código da Listagem 7.

Listagem 7. Criando uma caixa de diálogo modeless.

function exibirDialogo() {
  var html = HtmlService.createHtmlOutputFromFile('Index');
  DocumentApp.getUi()
  .showModelessDialog(html, 'DevmediaGAS');
  }

Note que o método showModalDialog foi alterado para o showModelessDialog. A diferença da caixa de diálogo modeless é que ela não o impede de fazer outra coisa. Assim, para a edição do documento, basta arrastá-la pela página e continuar fazendo as suas implementações.

Depurando scripts

Registrar os valores das variáveis em alguns pontos é essencial para os testes e depuração do código. A classe Logger é uma ferramenta útil para fazer isso e tem alguns métodos que são essenciais para a depuração. Conforme mostra a Listagem 8, vamos atualizar a função exibirDialogo() e executá-la como de costume a partir do menu Complementos. Depois faça qualquer ação, como digitar o seu nome e clicar em Sim ou Não, ou fechar a caixa de diálogo, etc.

Listagem 8. Criando uma função com a classe logger.

function onOpen(){
  DocumentApp.getUi()
  .createAddonMenu()
  .addItem("Exibir Dialogo", "exibirDialogo")
  .addToUi();
  }
  function exibirDialogo() {
  var ui = DocumentApp.getUi();
  var response = ui.prompt(
  'DevmediaGAS', 'Você vai entrar com o seu nome abaixo', ui.ButtonSet.YES_NO
  );
  if (response.getSelectedButton() == ui.Button.YES) {
  Logger.log('Seu nome é %s.', response.getResponseText());
  } else if (response.getSelectedButton() == ui.Button.NO) {
  Logger.log('Você clicou\'NO\' button');
  } else {
  Logger.log('Você fechou o diálogo.');
  }
  }

Agora dentro do editor de script, pressione Ctrl + Enter (Windows) ou Command + Enter (Mac), e então você poderá ver o texto registrado com um timestamp, como mostrado na Figura 6. Você também pode acessar o log a partir do menu Visualizar, e depois selecionar a opção Log. Note que o log registrado será diferente de acordo com a interação do usuário.

Visualizando um log
Figura 6. Visualizando um log.

Para um estudo mais detalhado da classe Logger, vamos criar a função de depuração como mostrado na Listagem 9, e ao executá-la veremos o log de forma diferente, de acordo com a Figura 7.

Listagem 9. Criando uma função de depuração.

function debug(){
  var square = 0;
  for(var i = 0; i < 10; i++){
  square = i * i;
  Logger.log(square);
  }
  }
Exibição de logs formatados
Figura 7. Exibição de logs formatados.

Além de registrar, você pode usar o recurso de depuração do editor. No editor, você pode definir breakpoints (pontos de parada no código) em uma ou mais linhas. Para fazer isso, clique uma vez sobre o número da linha na qual você deseja definir um breakpoint. Um ponto vermelho vai ser adicionado apenas no lado da esquerdo do número da linha, como mostrado na Figura 8.

Inserindo um breakpoint na ferramenta
Figura 8. Inserindo um breakpoint na ferramenta.

Selecione a função debug no seletor de funções caso a mesma já não esteja selecionada. Clique no botão Debug (o do ícone de um inseto) à esquerda do seletor de funções. A função será executada até o ponto onde você colocou o seu breakpoint e, em seguida, fará uma pausa. A janela de edição é dividida horizontalmente e mostra o objeto e seus valores na parte inferior da janela. Clique em “Continue debugging” para ver os valores a cada ciclo do loop. Você também pode utilizar outros recursos, como step into (entra na função), step over (passa para a próxima função) e step out (sai da função). Para sair da sessão de depuração clique em Stop debugging e lembre-se de remover os breakpoints quando não mais precisar deles para evitar futuras paradas.

Criando scripts para o Gmail

Você também pode criar contatos do Gmail por script, usando o método de criação de contato da classe ContactsApp. Por exemplo, considere o nome Finn Adventure e o ID do e-mail <finn@example.com>, então o código ContactsApp.createContact("Finn", "Adventure". ("finn@example.com") irá criar o contato esperado. Para saber dos demais métodos disponíveis na classe ContactsApp, no editor de código digite ContactsApp e . (um ponto) ao lado dele. Então você visualizará todos os métodos disponíveis com os detalhes dos parâmetros de referência do código. Você poderá encontrar métodos depreciados, que, conforme nas outras linguagens, não são aconselhados para uso.

Uma Planilha Google possui uma ou mais abas na mesma. As planilhas são indexadas da esquerda para a direita a partir de 0. Por exemplo, a planilha a esquerda é referida pelo índice 0, a próxima, 1, e assim por diante. Em GAS, podemos nos referir a essa planilha pelo seu índice ou pelo seu nome. Por exemplo:

  • O método getSheets() retorna uma matriz de objetos Sheets. A partir da matriz, podemos nos referir a uma planilha individual por seu índice.
  • A função getSheetbyName("Contatos") retorna um objeto Sheet com o nome Contatos.

Em Planilhas do Google, o rótulo da coluna começa a partir da letra A e é contado em um ponto de vista programático, a partir da esquerda para direita começando com o número 1. Por exemplo a coluna A é 1, B é 2, e assim por diante. As linhas são identificadas pelos respectivos números de etiqueta. Em GAS, nós podemos referenciar uma célula ou um intervalo de células via notação A1 ou por número de linhas e colunas separadas.

  • O método getRange('D1:F10') retorna um objeto Range referenciando as células D1 a F10.
  • Já o método getRange(1,4,10,3) retorna um objeto Range referenciando o mesmo intervalo D1:F10.
  • O offset é um método de referência indireta para se referir a uma célula a partir de uma base de referência da célula. Um offset de referência é determinado pela quantidade de linhas e colunas que foram criadas a partir da célula base. Por exemplo, se a célula base é D1, o método offset(10,3) retorna o intervalo D1:F10.

Muitas vezes é necessário ler e/ou escrever dados de/para uma sheet. Normalmente utiliza-se o método getValue para ler o valor de uma célula e o método getValues para retornar um array de valores bidimensionais. Para escrever um valor único ou vários valores de uma vez em um array bidimensional de valores, use os métodos setValue e setValues, respectivamente.

Agora, vamos criar uma aplicação para procurar contatos existentes. Essa aplicação deve ser capaz de pesquisar e listar os seus contatos do Gmail em planilhas. Vamos então criar uma nova planilha, nomeá-la como Contatos e configurá-la de acordo com a Figura 9. Na Listagem 10, iremos criar a função searchContacts para buscar os contatos.

Planilha exemplo
Figura 9. Planilha exemplo.
Listagem 10. Criando a função searchContacts.


function searchContacts() {
      var SheetContacts = SpreadsheetApp.getActiveSpreadsheet()
          .getSheetByName("Contatos");
      // Leia a entrada da célula A3
      var searchCriteria = SheetContacts.getRange("A3").getValue();
      // Primeiros 10 contatos.
      // [Você pode alterar esse limite, mas é aconselhável manter uma quantidade menor.]
      var numOfContacts = 10;
      // Limpar dados existentes
      SheetContacts.getRange(7, 1, numOfContacts, 4).clear();
  }

Podemos ver claramente o método do objeto Range para limpar tudo, incluindo o formato e a fórmula de uma variedade de células. Você pode usar o método clear do objeto Sheet para limpar toda a planilha. Alternativamente, você pode usar o método clearContents para limpar apenas o conteúdo. Vamos completá-lo com o código da Listagem 11.

Listagem 11. Criando uma função para buscar contatos.


    // Retorna um array de contatos se for o caso
      // os nomes dos contatos combinam com o texto de pesquisa
      var contacts = ContactsApp.getContactsByName(searchCriteria);
      // Limitar o número de contatos
      if (contacts.length > numOfContacts) contacts.length = numOfContacts;
      var cell = SheetContacts.getRange("A7");
      for (var i in contacts) {
          var name = contacts[i].getFullName();
          var email = contacts[i].getEmails()[0];
          if (email) email = email.getAddress();
          else email = "";
          // Para simplificar ao obter o primeiro número de telefone
          var phone = contacts[i].getPhones()[0];
          if (phone) phone = phone.getPhoneNumber();
          else phone = "";
          // Para simplificar ao obter o primeiro endereço
          var address = contacts[i].getAddresses()[0];
          if (address) address = address.getAddress();
          else address = "";
          // cell.offset(rowOffset, columnOffset)
          cell.offset(i, 0).setValue(name);
          cell.offset(i, 1).setValue(email);
          cell.offset(i, 2).setValue(phone);
          cell.offset(i, 3).setValue(address);
      }
  };

Não copie e cole o código, edite-o. Ao fazer isso, você vai estar ciente da disponibilidade do método signatures (nomes de métodos e parâmetros) de classes, como SpreadsheetApp, ContactApp e Contact com a ajuda da referência do código no editor de script. Depois de ter editado o código e salvado sem erros, vá para a janela da planilha. Se você digitar um termo de busca na célula A3 (search box) e clicar em Buscar, os 10 primeiros contatos serão listados.

E se desejarmos atualizar os contatos listados pela função searchContacts? Por exemplo, podemos querer atualizar o número de telefone ou endereço de um contato. Para atualizar os campos de contato, vamos criar outra função, chamada updateContacts. Antes de criá-la, na planilha de contatos, adicione um novo botão próximo ao botão Buscar, com o nome Atualizar, e nomeie como função updateContacts. Atualize os valores do campo que você gostaria de atualizar e crie a função da Listagem 12.

Listagem 12. Criando uma função para atualizar contatos.

function updateContacts() {
      var SheetContacts = SpreadsheetApp.getActiveSpreadsheet()
          .getSheetByName("Contatos");
      var cell = SheetContacts.getRange("A7");
      var numOfContacts = 10;
      for (var i = 0; i < numOfContacts; i++) {
          var email = cell.offset(0, 1).getValue();
          // Pular se o campo de e-mail for nulo
          if (!email) continue;
          var contact = ContactsApp.getContact(email);
          // Pular se o contato for nulo ou indefinido
          if (!contact) continue;
          var name = cell.offset(i, 0).getValue();
          // Ignorar se o campo for nulo
          if (!name) continue;
          contact.setFullName(name);
          var phone = cell.offset(i, 2).getValue().toString();
          // os números de telefone retornam como um array
          var contPhone = contact.getPhones(ContactsApp.Field.MAIN_PHONE)[0];
          // Atualiza o número de telefone principal se existir outra maneira de adicionar
          if (phone) {
              if (contPhone) {
                  contPhone.setPhoneNumber(phone);
              } else {
                  contact.addPhone(ContactsApp.Field.MAIN_PHONE, phone);
              }
          }
          var address = cell.offset(i, 3).getValue().toString();
          // Retorna o endereço como um array
          var contAddress = contact
              .getAddresses(ContactsApp.Field.HOME_ADDRESS)[0];
          // Atualiza o endereço residencial se existir outra maneira de adicionar
          if (address) {
              if (contAddress) {
                  contAddress.setAddress(address);
              } else {
                  contact.addAddress(ContactsApp.Field.HOME_ADDRESS, address);
              }
          }
      }
  };

A função updateContacts recupera os contatos pelo ID de e-mail, e, para cada contato, também recupera valores de campo e atualiza/adiciona aos valores da planilha. Essa função pode atualizar/adicionar o nome completo, telefone e campos de endereço, mas não o ID do e-mail.

Agora vamos criar a função parseEmail, que é capaz de verificar os 10 últimos tópicos da caixa de entrada, extrair a partir do campo e corpo do texto as mensagens não lidas e colocar os dados extraídos na sheet à esquerda da planilha (caso tenha mais de uma sheet). Na Listagem 13, temos um código para criar a função parseEmail.

Listagem 13. Criando uma função para buscar e-mail.

/**
  * Obtém o conteúdo das mensagens mais recentes na caixa de entrada do Gmail
  * e coloca os dados extraídos na guia esquerda das sheets
  */
  function parseEmail() {
      var emailSheet = SpreadsheetApp.getActiveSpreadsheet()
          .getSheets()[0];
      // limpa toda a sheet
      emailSheet.clear();
      // Verifica no máximo 10 tópicos
      var thread = GmailApp.getInboxThreads(0, 10);
      var row = 1;
      for (var thrd in thread) {
          var messages = thread[thrd].getMessages();
          for (var msg in messages) {
              var message = messages[msg];
              if (message && message.isUnread())
                  emailSheet.getRange(row, 1).setValue(message.getFrom());
              emailSheet.getRange(row++, 2).setValue(message.getPlainBody());
          }
      }
  };

A função saveEmailAttachmentsToDrive pode baixar anexos do Gmail para o Drive. Nessa função o PropertiesService é utilizado para evitar um download repetido do mesmo anexo. A função createFolder_ é usada para criar pastas, caso não exista uma pasta com o mesmo nome no Drive (Se algum nome é anexado com _, então ele não será listado no menu Run).

Você não pode executar essas funções diretamente, mas elas podem ser chamadas a partir das outras funções. Essas são chamadas de funções privadas. Você pode criar a função createFolder_ no mesmo arquivo de script, juntamente com a função saveEmailAttachmentsToDrive, ou em um arquivo de script separado, como Library.gs. Vejamos a Listagem 14.

Listagem 14. Criando função para salvar email.

/**
   * Verifica últimos 100 tópicos da caixa de entrada,
   * e salva os anexos na pasta 'Anexos do Gmail '
   */
  function saveEmailAttachmentsToDrive() {
      // Cria a pasta Anexos do Gmail se ela não existe.
      createFolder_( 'Anexos do Gmail ');
      // Obtém os últimos 100 tópicos da caixa de entrada
      var threads = GmailApp.getInboxThreads(0, 100);
      var messages = GmailApp.getMessagesForThreads(threads);
      var folderID = PropertiesService.getUserProperties()
          .getProperty("FOLDER");
      var file, folder = DriveApp.getFolderById(folderID);
      for (var i = 0; i < messages.length; i++) {
          for (var j = 0; j < messages[i].length; j++) {
              if (!messages[i][j].isUnread()) {
                  var msgId = messages[i][j].getId();
                  // atribuir se MSG_ID for indefinido
                  var oldMsgId = PropertiesService.getUserProperties()
                      .getProperty('MSG_ID') || '';
                  if (msgId > oldMsgId) {
                      var attachments = messages[i][j].getAttachments();
                      for (var k = 0; k < attachments.length; k++) {
                          PropertiesService.getUserProperties()
                              .setProperty('MSG_ID', messages[i][j].getId());
                          try {
                              file = folder.createFile(attachments[k]);
                              Utilities.sleep(1000); // Wait before next iteration.
                          } catch (e) {
                              Logger.log(e);
                          }
                      }
                  } else return;
              }
          }
      }
  };

A função saveEmailAttachmentsToDrive chama a função createFolder_ com o nome da pasta como um argumento. Além disso, ela também itera sobre a lista de mensagens para verificar quais estão lidas e, assim, considerar apenas essas como válidas. Para cada mensagem é necessário validar o seu identificador (MSG_ID) se a mesma o tiver, caso contrário devemos gerar um via PropertiesService. Então, a função considera o tratamento dos anexos via método getAttachments() do objeto message, criando cada um dos arquivos na pasta do Drive via função createFile() do objeto folder outrora recuperado do DriverApp.

A função createFolder_, por sua vez, procura pelo diretório fornecido, criando-o se não existir e retornando seu ID único no final (Listagem 15).

Listagem 15. Criando pasta no Drive.

function createFolder_(name) {
      var folder, folderID, found = false;
   
      /*
       * Retorna a coleção de todas a pastas do usuário como um iterator
       * isso significa que ele não retorna os nomes das pastas ao mesmo tempo
       * mas você deve levá-los um por um
       */
      var folders = DriveApp.getFolders();
   
      while (folders.hasNext()) {
          folder = folders.next();
          if (folder.getName() == name) {
              folderID = folder.getId();
              found = true;
              break;
          }
      };
   
      if (!found) {
          folder = DriveApp.createFolder(name);
          folderID = folder.getId();
      };
   
      PropertiesService.getUserProperties().setProperty("FOLDER", folderID);
      return folderID;
  }

O método getFolders é um método de iteração. Um iterador não retorna todos os dados de uma só vez, mas apenas os dados atuais. Para obter dados sucessivamente, você deve chamar o método seguinte repetidamente até que o hasNext se torne falso.

Temos também a função sendEmail, que é capaz de enviar e-mails com mensagens pré-fixadas. Lembre-se de substituir o ID do e-mail e da mensagem de texto. Esse serviço é usado principalmente para enviar e-mails com métodos limitados (somente sendEmail e getRemainingDailyQuota), e ele não pode acessar a sua conta do Gmail. Você pode usar a classe mailApp para mais métodos.


function sendEmail() {
      var to = "[[id do e-mail recebedor]]", message = "[[Messagem a ser enviada]]\n";
      MailApp.sendEmail(to, "Digite aqui o assunto do e-mail", message);
  }

Criando gatilhos

Os gatilhos são estruturas que permitem disparar funções pré-criadas nos nossos arquivos de script a partir de uma determinada configuração, isto é, de tempos em tempos ou a partir de um evento especificado (abertura da planilha, envio de formulário, etc.). Para criar um gatilho, no editor de scripts, clique em Recursos e selecione “Current project’s triggers” do projeto atual, em seguida, uma caixa de diálogo com os gatilhos do projeto atual será exibida. Clique em “No triggers set up. Click here to add one now” e selecione as opções nos dropdowns como ilustra a Figura 10, clicando em Save.

Criando um novo gatilho
Figura 10. Criando um novo gatilho.

Sob o título Run, selecione a função sendEmail, que é a que iremos usar para criar o gatilho. Então, selecione From spreadsheet (de planilha) e On form submit (no envio do formulário) sob o título Events. Em outras palavras, sempre que um usuário do formulário enviar dados para a planilha, o gatilho irá executar a função sendEmail automaticamente. O leitor também tem a opção de configurar um intervalo de tempo para execução se preferir.

Você também pode criar ou excluir gatilhos programaticamente como mostra a Listagem 16.

Listagem 16. Excluir/criar gatilhos via script.

/**
   * Excluir todos os gatilhos.
   *
   */
  function deleteTriggers() {
      var triggers = ScriptApp.getProjectTriggers();
      triggers.forEach(function(trigger) {
          try {
              ScriptApp.deleteTrigger(trigger);
          } catch (e) {
              throw e.message;
          };
          Utilities.sleep(1000);
      });
  };
   
  function createTrigger() {
      var ss = SpreadsheetApp.getActiveSpreadsheet();
      // Create new trigger
      ScriptApp.newTrigger("sendEmail")
          .forSpreadsheet(ss).onFormSubmit().create();
  };

Na função deleteTriggers, o método de serviço Utilities.sleep é usado para pausar o script temporariamente para os milissegundos especificados. Caso contrário, você pode enfrentar demasiados serviços de invocação e obter erros de execução.

Encaminhamento de e-mails

A função forwardEmails é capaz de transmitir mensagens do correio eletrônico, caso uma determinada palavra-chave seja encontrada no texto do e-mail ou se inserirmos um ID de e-mail pré-fixado. Vejamos na Listagem 17 (autocomentada) como criar essa função.

Listagem 17. Criando uma função para encaminhamento de e-mails.


/**
   * 1. Todos os controles não lidam com os tópicos e mensagens da caixa de entrada 
   *
   * 2. Se uma determinada palavra-chave for encontrada, em seguida é encaminhada a outro 
   * destinatário
   *
   * 3. Marca as mensagens como lidas
   *
   */
  function forwardEmails() {
      var recipient = "[[id do e-mail para o encaminhamento]]";
   
      /*
       * Usar palavras-chave separadas por '|'.
       * Por exemplo: "compra | fatura"
       */
      var words = "DevMedia | Fabrício";
      var regExp = new RegExp(words, 'g');
      var len = GmailApp.getInboxUnreadCount();
      for (var i = 0; i < len; i++) {
          // pega a thread 'i'th na caixa de entrada
          var thread = GmailApp.getInboxThreads(i, 1)[0];
          // obtém todas as mensagens com a thread 'i'th
          if (thread) {
              var messages = thread.getMessages();
              var msgLen = messages.length;
   
              var isAllMarkedRead = true;
              // itera cada mensagem 
              // CUIDADO: limite de iterações do loop para o teste inicial 
              for (var j = 0; j < msgLen; j++) {
                  var message = messages[j];
                  if (message && message.isUnread()) {
                      var bodyText = message.getBody();
                      var test = regExp.exec(bodyText);
                      if (test) {
                          message.forward(recipient);
                          isAllMarkedRead = false;
                          message.markRead();
                          break;
                      }
                  }
              };
              if (isAllMarkedRead) len++;
              Utilities.sleep(1000);
          }
      }
  };

Na Listagem 18, vamos incorporar uma imagem na mensagem de e-mail. Você também pode usar o código HTML ao invés de texto simples. Para fazer isso, é necessário o upload de uma imagem qualquer para o Google Drive. Feito isso, você deve usar o ID da imagem que acabou de salvar no Drive no método DriveApp.getFileById.

Listagem 18. Criando função para envio de imagem.


function sendEmail() {
      var sheet = SpreadsheetApp.getActiveSpreadsheet()
          .getSheetByName("Nome da sheet do projeto");
      var lastRow = sheet.getLastRow();
      var lastCol = sheet.getLastColumn();
      var data = sheet.getRange(lastRow, 1, 1, lastCol).getValues()[0];
      var image = DriveApp.getFileById("[[id do arquivo da imagem no Drive]]").getBlob();
  var to = "[[id do e-mail que irá receber a mensagem ]]";
  var message = '<img src="cid:logo" />';
  message += "<p>Name: " + data[1] + "</p>";
  message += "<p>Phone: " + data[2] + "</p>";
  message += "<p>Question: " + data[3] + "</p>";
  MailApp.sendEmail(
      to,
      "Exemplo de incorporação de imagens",
      "", {
          inlineImages: {
              logo: image
          },
          htmlBody: message
      }
  );
  }

Enviar um e-mail personalizado a centenas de destinatários de uma só vez pode ser uma tarefa demorada. Compor um projeto e digitar o assunto, o ID de e-mail de cada destinatário para cada mensagem pode ser tedioso. Usando este e-mail com a aplicação merge, você pode enviar o mesmo tipo de informação a todos os destinatários, porém de forma personalizada.

O primeiro passo é criar um projeto no seu Gmail, para isso é necessário acessar o Gmail, selecionar a opção Escrever e depois digitar as informações exibidas na Figura 11 nos campos de preenchimento do novo e-mail. O projeto é utilizado como um molde e você pode usar qualquer caractere especial para substituir o texto. No projeto, o código mostrado na imagem utiliza a esquerda (<<) e direita (>>), substituindo o primeiro nome pela coluna Primeiro Nome em uma sheet EmailList. Você pode incluir qualquer outro marcador ou campo de acordo com sua exigência. Configure o projeto, mas não o envie nesse momento.

Criando um projeto para envio de e-mail personalizado
Figura 11. Criando um projeto para envio de e-mail personalizado.

Depois crie uma sheet com o nome EmailList Em uma nova planilha ou alguma existente, crie os cabeçalhos como na Figura 12 e no editor de script crie as funções de acordo com a Listagem 19. Substitua o nome do remetente com valores reais. Defina a quantidade máxima (esse código usa 50), considerando a sua cota de e-mails enviados diariamente.

Cabeçalhos da sheet EmailList
Figura 12. Cabeçalhos da sheet EmailList.
Listagem 19. Criando função para envio de e-mails.


// Retorna o seu projeto 
  function getDraftBody(draftName) {
      var drafts = GmailApp.getDraftMessages();
      for (var i in drafts)
          if (drafts[i].getSubject() == draftName)
              return drafts[i].getPlainBody();
  }
   
  function sendEmails() {
      // números das colunas da sheet EmailList, com base 0
      const PRIMEIRO_NOME_COL = 0;
      const ID_DO_EMAIL_COL = 1;
      const ASS_COL = 2;
      const DATA_COL = 3;
      var maxEmails = 50;
      var draftName = "Scripts do Gmail"; // Nome do assunto do projeto
      var draftBody = getDraftBody(draftName);
      var quotaLeft = MailApp.getRemainingDailyQuota();
      var ss = SpreadsheetApp.getActive();
      var sheet = ss.getSheetByName("EmailList");
      // Obtém todos os dados da sheet como um array 2-dimensional 
      var data = sheet.getDataRange().getValues();
      var header = data.shift();
      for (var i = 0, count = 0; count < maxEmails && count < quotaLeft &&
          i < data.length; ++i) {
          var firstName = data[i][PRIMEIRO_NOME_COL];
          var recipient = data[i][ID_DO_EMAIL_COL];
          var subject = data[i][ASS_COL];
          var htmlBody = draftBody.replace("<<PrimeiroNome>>", firstName);
          if (recipient) {
              GmailApp.sendEmail(
                  recipient,
                  subject,
                  "", {
                      name: "[[ Nome do remetente ]]",
                      htmlBody: htmlBody
                  }
              );
              data[i][DATA_COL] = new Date();
              ++count;
          }
      };
      // insere cabeçalho no topo do array
      data.unshift(header);
      // Armazena valores do array na sheet
      sheet.getRange(1, 1, data.length, header.length)
  .setValues(data);
  }

Preencha os dados da sheet EmailList. Para enviar os e-mails, execute a função SendEmails. O campo <<First Name>> no seu projeto será substituído conforme os dados da coluna “Primeiro nome” da sheet EmailList. A data e a hora serão marcadas de acordo com o momento do envio.

O leitor pode ainda testar o restante da sua implementação criando e reinventando novas formas de consumir tais serviços ou adaptando os mesmos à realidade da sua aplicação. Um bom exercício para fixar tudo isso é criar uma espécie de newsletter simples, com alguns endereços de e-mail de teste, apenas para ter noção de como tais scripts se comportam em um cenário mais próximo da realidade.