Pesquisa personalizada

2008/11/19

E-mails com Pedidos do Mercado Livre + GMail * Greasemonkey = Produtividade

Já faz algum tempinho que acumulei centenas de vendas no Mercado Livre. Em tempos de gloria, chegava a fazer mais de uma dezena de vendas ao dia e isso trouxe a problemática de atender aos clientes de forma ágil e adequada. E a verdade, é que o simples processo (sem falar nos outros) de enviar o e-mail com os dados do pedido ao cliente, me aborrecia pelo tempo perdido.

O Paulinho, um amigo, me disse que o bom programador, era antes de tudo, um grande preguiçoso. Eis-me: A preguiça personificada! Raramente repito duas tarefas da mesma forma, sempre dou um jeitinho de melhorar (nos meus critérios) o processo, seja o que for... Obviamente, as vezes, decaio na inprodutividade, no perfeccionismo imperfeito e na tolice aguda! Mas mesmo assim, invento e reinvento! Até, no ápice do sucesso, faço funcionar!

A questão é que, contrariando qualquer instinto (por mais forte que fosse), resisti a tentação de fazer um script, ou até mesmo, simplesmente criar uma regra/filtro que me ajudasse na simples tarefa de responder com os dados da compra de meus clientes. Visto que me despersuadia:
- "Não seja tolo, Marcio. Vai perder ainda mais tempo com isso."
Sinceramente, nem me reconheço com essa atitude, mas é verdade: resisti!

A questão, é que depois de fazer a mesma coisa por mais de 800 vezes (real!! foi mais que isso mesmo!), você pára (logo agora que tentava escrever direito, me tiram o tal do acento... <0) e, após xingar toda seus antepassados - se excomungar -, diz:
Devia ter automatizado isso antes...

EMail com Pedido do Mercado Livre

Eis, logo eu que atualmente me vejo trabalhando nuns trocentos projetos ao mesmo tempo e tentando ser dono de meu próprio narzi, mas ao contrário, cada vez mais preso... me vejo tomado por uma completa indignação por fazer a mesma tarefa por quase 1000 vezes e digo a mim mesmo:
- basta! Vou automatizar isto!

Chega o momento de ver o que seria melhor. Com certeza o melhor é criar um sistema, baseado em uma base de dados, que concentre todas as informações de compra, inclusive passadas e que fosse integrada com a "maravilha" do ML... até fiz vários progressos nesse sentido, mas parei no ponto onde precisava alimentar a base com os dados que retirava de 5 (sim, cinco!) diferentes páginas do ML e mantê-los de uma forma racional e principalmente utilizável!

Optei, então, por me concentrar somente no simples problema de responder ao cliente com os dados de sua compra, como: onde pagar, o quanto pagar, onde fazer etc. Isso poderia simplesmente ser feito com um filtro no GMail, por exemplo e até soluções mais elaboradas via VBA, por exemplo, com o Outlook. Mas queria uma coisa que NÃO funcionasse totalmente por si só, que ainda precisasse de minha leitura prévia (e rápida). Por isso, já que atualmente ando trabalhando tanto com AJAX e naturalmente Javascript (como se nunca o tivesse feito antes [8 anos atrás já usava DHTML e AJAX, sem nem saber que o nome desse tipo de coisa era isso]) que fosse simples e perfeitamente integrado à minha rotina.

Após estudar as opções, resolvi botar o meu velho entusiamo no Greasemonkey para render algo e fui atrás de alguma alguma API do GMAIL que funcionasse bem com ele. Nessas horas, é ótimo descobrir que você é um tolo nada original, vi que já haviam criado tal API - algo simples, mas que resolve o mínimo. Daí então, foi só botar a mão em XPath, RegEx, DTHML/DOM e, claro, não poderia faltar, o Firebug.

Firebug

E é esse, meu pequeno legado, e sei que não evitará que ainda assim me chamem de pária ou até mesmo lammer, mas eventualmente, inspirará (nem que seja o desprezo) alguém. Não sei quantos programadores vendem no ML, mas deve ter mais alguém; creio.

À Programação

O Greasemonkey tem uma variável com um objeto mágico chamado de nome window que encapsula a window "real". Se quiser acessar o objeto window real, na verdade, terá que usar unsafeWindow. Isso vale para qualquer variável global, pois ela só estará acessível em unsafeWindow. Isso é causa de muita dor de cabeça para os iniciantes no Greasemonkey. Por exemplo, a instância do console do Firebug só existe em unsafeWindow.

EMail já preenchido com os dados do pedido

O GMail te disponibiliza o objeto unsafeWindow.gmonkey e é por meio dele que você interage com o DOM do GMail e particularmente, alguns de seus membros. Antes de avançar no uso de unsafeWindow.gmonkey, você vai precisar informar ao GMail que deseja (sim o suporte ao Greasemonkey, graças a um funcionário da Google, é da própria Google) carregar o módulo do GMonkey. Para mais informações, leia a:
API reference for version 1.0 of the experimental Gmail Greasemonkey API.

Após se obter acesso à DOM (DHTML) do GMail, o resto é trivial e atualmente, totalmente dependente da estrutura atual do GMail, ou seja, em futuras modificações do GMail, corre-se o risco do código que funcionava antes parar de funcionar.

Para flexibilizar e facilitar o acesso à DHTML do GMail, preferi o uso conjunto de XPath e RegEx pelo poder inerente dessas linguagens de consultas.

O funcionamento é basicamente clicar em "Reply" no e-mail que foi recebido do Mercado Livre (com os dados da compra do cliente) e ir até o Greasemonkey e acionar o comando "Preencher Dados de Compra". Com isso, de forma automática e quase mágica, todos os dados, de todos os campos, serão preenchidos, e, inclui, seguintes recursos:

  • Preenchimento do Nome/Email do destinatário (o cliente);
  • Suporte a uma template de resposta HTML onde todos os campos são preenchidos de forma automática. Os dados são obtidos do email original que foi enviado pelo ML;
  • Cálculo automático do total da compra e incluindo o frete (padrão) e quantidade adquirida.
   1:// ==UserScript==
   2:// @name           GMail - Pedidos do ML
   3:// @namespace      net.marciowb.gmail.ml.pedidos
   4:// @description    Me ajuda a preencher os pedidos do ML - by Marcio Wesley Borges<marciowb@gmail.com>
   5:// @include        http://mail.google.com/mail/*
   6:// ==/UserScript==
   7:
   8:const GMONKEYVER = "1.0";
   9:var gmail = null;
  10:var console = null;
  11:
  12:function evaluateXPath(xPath, aNode) {
  13:        if (!aNode) {
  14:                aNode=document;
  15:        }
  16:        var res = aNode.ownerDocument.evaluate(xPath, aNode, null, XPathResult.ANY_TYPE, null);
  17:        switch (res.resultType) {
  18:                case XPathResult.STRING_TYPE:
  19:                        return res.stringValue;
  20:                case XPathResult.NUMBER_TYPE:
  21:                        return res.numberValue;
  22:                case XPathResult.BOOLEAN_TYPE:
  23:                        return res.booleanValue;
  24:                default:;
  25:        }
  26:        var foundNodes = new Array();
  27:        var item = res.iterateNext(); 
  28:        while (item) {
  29:                foundNodes.push(item);
  30:                item = res.iterateNext();
  31:        }
  32:        return foundNodes;
  33:}
  34:
  35:/**
  36:* Obtém o elemento BODY do e-mail que está sendo composto (criado).
  37:*/
  38:function getComposingMailBody() {
  39:        if (!gmail)
  40:                return null;
  41:        var el = gmail.getActiveViewElement();  
  42:        if (!el)
  43:                return null;
  44:        var aa=evaluateXPath("//iframe[@id!='remote_iframe_0']", el);
  45:        if (!aa || aa.length==0)
  46:                return null;
  47:        var doc = aa[0].contentDocument;
  48:        if (!doc)
  49:                return null;
  50:        var body = doc.body;
  51:        return body;
  52:}
  53:
  54:/** 
  55:* Esta função é para ser chamada por dadosCompra().<br>
  56:* @param _gmail É fornecido automaticamente pela função gmonkey.load que chama esta.
  57:*/
  58:function _dadosCompra(_gmail) {
  59:        console = unsafeWindow.console;
  60:        gmail = _gmail;
  61:        
  62:        var bodyElem = getComposingMailBody();
  63:        if (bodyElem==null) {
  64:                window.alert("Antes, é necessário que  um reply no e-mail com os dados da compra do Mercado Livre");
  65:                return;
  66:        }
  67:        
  68:        
  69:        //Tenta obter o "quoted" mail que está sendo respondido
  70:        var aQuoted=evaluateXPath("//div[@class='gmail_quote']", bodyElem);
  71:        if (!aQuoted || aQuoted.length==0) {
  72:                window.alert("Não se deve apagar o quoted mail ainda.");
  73:                return;
  74:        }
  75:        var quotedMailBody = aQuoted[0].innerHTML;
  76:                
  77:        //Obtém o código do anúncio e nome do produto
  78:        var rgx = /;id=(\d*)[^>]*>([^<]*)/
  79:        var m = rgx.exec(quotedMailBody);
  80:        if (!m || m.length<3) {
  81:                window.alert("Isto  funciona para os e-mails de compra do Mercado Livre.");
  82:                return;
  83:        }
  84:        var codAnuncio = m[1];
  85:        var nomeAnuncio = m[2];
  86:        
  87:        //Obtém o nome do comprador
  88:        rgx = /Nome:\s*<b\b[^>]*>(.*)<\/b>/
  89:        m = rgx.exec(quotedMailBody);
  90:        var nomeComprador = m[1];
  91:        
  92:        //Obtém o código, apelido e pontuação do comprador
  93:        rgx = /Apelido:.*id=(\d*)".*[^>]>(.*) \((\d*)\)/
  94:        m = rgx.exec(quotedMailBody);
  95:        var codComprador = m[1];
  96:        var apelidoComprador = m[2];
  97:        var pontuacaoComprador = m[3];
  98:        
  99:        //Obtém a quantidade de produtos comprados
 100:        rgx = /Quantidade:\s*(\d*)/
 101:        m = rgx.exec(quotedMailBody);
 102:        var quantidade = m[1];
 103:        
 104:        //Obtém o preço pelo qual o cliente comprou o produto
 105:        rgx = /Preço:\s*([^<]*)/
 106:        m = rgx.exec(quotedMailBody);
 107:        var descPreco = m[1].replace("R$","").replace("unit.","");
 108:        
 109:        //Obtém o e-mail do comprador
 110:        rgx = /E-mail:[^>]*>([^<]*)/i
 111:        m = rgx.exec(quotedMailBody);
 112:        var emailComprador = m[1];
 113:        
 114:        //Obtém o telefone do comprador
 115:        rgx = /Telefone:\s*([^<]*)/
 116:        m = rgx.exec(quotedMailBody);
 117:        var telefoneComprador = m[1];
 118:        
 119:        //Obtém a cidade do comprador
 120:        rgx = /Cidade:\s*([^<]*)/
 121:        m = rgx.exec(quotedMailBody);
 122:        var cidadeComprador = m[1];
 123:        
 124:        //Obtém o estado do comprador
 125:        rgx = /Estado:\s*([^<]*)/
 126:        m = rgx.exec(quotedMailBody);
 127:        var estadoComprador = m[1];
 128:        
 129:        //Obtém o país do comprador
 130:        rgx = /País:\s*([^<]*)/
 131:        m = rgx.exec(quotedMailBody);
 132:        var paisComprador = m[1];
 133:
 134:        //////////////////////////////////////  
 135:        var VLR_MIN = 4; //Valor mínimo da compra. Isto é realizado por segurança nossa e o objetivo é evitar que um erro qualquer nos cause prejuízo.
 136:        var TIP_FRETE = "Frete por SEDEX";
 137:        var VLR_FRETE = "23.00";
 138:        
 139:        var vlrTotal = (Number(descPreco) * Number(quantidade)) + Number(VLR_FRETE);
 140:        if (vlrTotal<VLR_MIN) {
 141:                window.alert("Erro: o valor total da compra está sendo computado como: " + vlrTotal);
 142:                return;
 143:        }
 144:        
 145:        var TEMPLATE = '<div>Oi,<br> <br> &nbsp;&nbsp; Td blz?<br><br></div><span style="color: rgb(0, 102, 0);">&nbsp;&nbsp; É o seguinte, informa aí:<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">Nome: @NOME_COMPRADOR</span><br style="font-family: courier new,monospace;"> <span style="font-family: courier new,monospace;">Endereço:</span></span><br style="font-family: courier new,monospace;"><span style="color: rgb(0, 102, 0);"><span style="font-family: courier new,monospace;">Bairro: </span><br style="font-family: courier new,monospace;"> <span style="font-family: courier new,monospace;">Cidade/Estado: @CIDADE_COMPRADOR-@ESTADO_COMPRADOR </span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">CEP: </span><br style="font-family: courier new,monospace;"> <span style="font-family: courier new,monospace;">APELIDO: @APELIDO_COMPRADOR </span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">CPF: </span><br style="font-family: courier new,monospace;"> <span style="font-family: courier new,monospace;">TEL: @TELEFONE_COMPRADOR </span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">E-MAIL: @EMAIL_COMPRADOR </span><br style="font-family: courier new,monospace;"> <span style="font-family: courier new,monospace;">FORMA DE PAGAMENTO: </span><br><br>E se tu tivé uma companhia, diz, também:<br><span style="font-family: courier new,monospace;">Nome da Empresa: </span><br style="font-family: courier new,monospace;"> <span style="font-family: courier new,monospace;">CNPJ: </span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">Inscrição Estadual: </span><br></span><br><b style="color: rgb(0, 102, 0);">Importante: </b><span style="color: rgb(0, 102, 0);"><br> Nessa, você  levando:<pre> <br><table border="1" cellspacing="0" cols="3" style="font-family: courier new,monospace;font-size:9pt" rules="groups" width="100%" frame="void"><tbody> <tr> <td width="35" align="right"><b>Qnt.</b></td> <td  align="right" width="94"><b>Valor</b></td> <td align="left"><b>Descrição</b></td> </tr> <tr> <td width="35" align="right">@QNT_PRODUTO</td> <td align="right" >@VALOR_PRODUTO</td> <td>@NOME_PRODUTO</td> </tr> <tr> <td width="35" align="right">@QNT_FRETE</td> <td align="right" >@VALOR_FRETE</td> <td>@TIPO_FRETE</td> </tr> <tr> <td bgcolor="#e6e6e6" align="right" height="16"><b>@QNT_TOTAL</b></td> <td bgcolor="#e6e6e6" align="right" ><b>@VALOR_TOTAL</b></td> <td bgcolor="#e6e6e6"><font size="2"><b>Total</b></font></td> </tr> </tbody> </table><font face="monospace"><br></font></pre> &nbsp;&nbsp; É isso, aí.<br>Valeu brother,<br> Ful. de Talz';
 146:        var emailBody = TEMPLATE
 147:                .replace("@NOME_PRODUTO", (nomeAnuncio+" #"+codAnuncio))
 148:                .replace("@VALOR_PRODUTO", descPreco)
 149:                .replace("@QNT_PRODUTO", quantidade)
 150:                .replace("@QNT_FRETE", "1")
 151:                .replace("@VALOR_FRETE", VLR_FRETE)
 152:                .replace("@TIPO_FRETE", TIP_FRETE)
 153:                .replace("@QNT_TOTAL", (Number(quantidade)+1))
 154:                .replace("@VALOR_TOTAL", vlrTotal)
 155:                .replace("@EMAIL_COMPRADOR", emailComprador)
 156:                .replace("@TELEFONE_COMPRADOR", telefoneComprador)
 157:                .replace("@APELIDO_COMPRADOR", apelidoComprador)
 158:                .replace("@CIDADE_COMPRADOR", cidadeComprador)
 159:                .replace("@ESTADO_COMPRADOR", estadoComprador)
 160:                .replace("@NOME_COMPRADOR", nomeComprador);
 161:                
 162:        bodyElem.innerHTML = emailBody;
 163:        
 164:        //Tenta obter o campo de listagens dos e-mails dos destinatários
 165:        var el = gmail.getActiveViewElement();
 166:        var aTos=evaluateXPath('//textarea[@name="to"]', el);
 167:        if (!aTos || aTos.length<1) {
 168:                window.alert("Erro: não foi possível determinar onde está o campo relativo aos emails do destinatário. Isso pode ocorrer no caso do GMail mudar sua estrutura DHTML.");
 169:                return;
 170:        }
 171:        var to = aTos[0];
 172:        to.value = nomeComprador + "<" + emailComprador + ">";
 173:        
 174:        console.log("Viva Java! Viva a Liberdade! Reviva a SUN!");
 175:}
 176:
 177:function dadosCompra() {
 178:        unsafeWindow.gmonkey.load(GMONKEYVER, _dadosCompra);
 179:}
 180:
 181:GM_registerMenuCommand('Preencher Dados de Compra', dadosCompra); 
 182:

Caso alguém queira o script acima para o Greasemonkey, pode pegá-lo aqui:
Preenchimento automático de e-mail com os dados da compra do cliente do Mercado Livre.

Referências

2008/11/13

Gotan Project - La Viguela

aquí me tumbo a cantar
al compás de la vigüela
que al hombre que lo desvela
una pena extraordinaria
como el ave solitaria
con el cantar se consuela

vengan santos milagrosos
vengan todos en mi ayuda
que la lengua se me añuda
y se me turba la vista
pido a mi dios que me asista
y en esta ocasión me ayuda

aquí me tumbo a cantar
al compás de la vigüela
que al hombre que lo desvela
una pena extraordinaria
como el ave solitaria
con el cantar se consuela

2008/10/03

NoSerialMouse: Ao Plugar Dispositivo Serial, Windows fica "maluco" e reconhece como Mouse

Algumas vezes quando se pluga algum dispositivo serial - e isso vale para dispositivos seriais que funcionam via USB, ou seja, aqueles que criam uma porta COM virtual no computador -, o Windows aparentemente fica maluco, um monte de coisas começam a acontecer na tela e se tem a impressão de que alguém ou alguma coisa tomou o controle do computador. Isso pode acontecer com qualquer dispositivo serial, apesar de frequentemente acontecer mais com certos tipos, como: qualquer GPS NMEA 0183, seja plugado na USB ou na porta serial.

O problema acontece porque ao se plugar o dispositivo, o Windows verifica que ele é serial e durante um pequeno período de tempo ele amostrará os dados que estão sendo transmitidos pelo dispositivo, por coincidência, no momento da amostragem o Windows poderá detectar alguma sequência de dados que caracterize um Mouse, nesse caso, graças a magia do plug-and-play, ele irá automaticamente adicionar um Mouse de acordo com o dispositivo que ele (o Windows) pensa se tratar à lista de dispositivos do computador. Após isso, ocorre o comportamente aparentemente aleatório e inesperado na tela.

Para resolver esse problema, é bastante simples: basta desativar o Mouse que foi criado no Gerenciador de Dispositivos. No caso do meu GPS, o WIndows o reconheceu como sendo o "Microsoft Serial Ballpoint" - então, foi só desativá-lo para resolver o problema.

No vídeo abaixo, mostro como reconhecer esse falso Mouse e como desabilitá-lo:

Apesar do procedimento acima ser simples, nem sempre é fácil executá-lo pois o ponteiro do mouse fica pulando na tela e clicando em tudo que é lugar, então, até que se possa executar todos os passos e desativar o "falso mouse", muito trabalho será necessário. Pensando nisso, resolvi pesquisar melhores soluções para o problema, já que muita gente passa e passará por isso. Veja o que encontrei:

NoSerialMouse

NoSerialMouse

Pensando no bem coletivo e aproveitando um pequeno programinha que fiz anteriormente, acabei por automatizar a solução descrita pelo artigo 283063 da Microsoft. A idéia foi criar algo bem simples, mas que fosse eficaz. Você deixa ele rodando antes de plugar o dispositivo e quando o dispositivo é plugado, ele automaticamente desativa o mecanismo que faria o Windows reconhecê-lo como um Mouse. O programa funciona particularmente bem com dispositivos USB e foi testado no Windows XP, Vista 64 e 2000. O NoSerialMouse pode listar todas as portas COM do computador ou somente as COM que estejam efetivamente disponíveis.

O NoSerialMouse é gratuito. No entanto, pode ser que alguém se interesse pelos fontes, já que o mecanismo de reconhecimento das portas seriais é particularmente sofisticado e reconhece com excelente precisão os mais diversos tipos de portas seriais - onde, inclusive, outros mecanismos de enumeração do hardware, falham (isso vale para Win2k, XP, Vista32/64). Os fontes do programa estão em Delphi 6 (com código e comentários em inglês) e posso, eventualmente, responder alguma questão por e-mail. Para adquirir os fontes, acesse:

Referências:

Marcadores: ,