Bots de conversa de Telegram amb Google Apps Script

Bots de conversa Exemples Dades pràctiques   Recursos CITCEA
Google Apps Script Projectes Interacció   Inici

Generem un diagrama tipus pastís que es pot consultar per GET i per Telegram

En aquest exemple agafarem unes dades que tenim en un full de càlcul i generarem una imatge vectorial en forma de diagrama de tipus pastís amb els valors del full de càlcul. En aquest cas hem suposat que hi ha entre dos i dotze valors (hem definit dotze possibles colors) i que aquests són valors enters positius.

La següent imatge mostra el contingut del full de càlcul en un moment donat:

Full de càlcul

El resultat que obtindrem serà el gràfic següent:

Gràfic

En el nostre cas podrem obtenir el gràfic de dues maneres. Una d'elles serà a l'executar l'script des del navegador (funció doGet) i obtindrem una pàgina web que contindrà el gràfic. L'altra serà accedint des de Telegram (funció doPost) i, en cas de fer servir la comanda /grafic obtindrem una imatge del gràfic i un enllaç per descarregar-lo en format PDF.

El programa és el següent:

var token = "^^34328844:AAFIpk-e7j3UZtYQYQaTduf4hEhnDqIcNXI"; // API Token de Telegram 
var telegramUrl = "https://api.telegram.org/bot" + token;  // URL que comunica el bot de conversa amb Telegram 
var ssId = "^^ThsoSjkeMSfwEKy4mn_4QEYH96sxv3VURqE3WHCTswDA";  // Identificador del full de càlcul
var id_carpeta = "^^Hlod5Y4qEsLF9HrIsE6oYQa32f6NHeo";  // Carpeta on guardarem el fitxer
// Definim els colors que farem servir al gràfic
var negre = "#000000";
var blau = "#0000FF";
var transparent = "none";
var colors = ["#FF0000","#008800","#0000FF","#666600",
  "#FF00FF","#006666","#CC6600","#8000FF",
  "#888888","#884400","#00FFFF","#444444"]
function doGet(e){
  var resp = grafic();  // Crea el gràfic
  var nomFitxer = "grafic";  // Nom del fitxer
  // Creem el fitxer
  var fitxerNou = DriveApp.getFolderById(IdCarpeta).createFile(nomFitxer + ".svg",resp);
  var idFitxer = fitxerNou.getId();  // Identificador del fitxer
  var urlDescFitxer = fitxerNou.getDownloadUrl();  // Adreça per descarregar el fitxer
  // Adreça per veure el fitxer
  var urlFitxer = "https://drive.google.com/uc?export=view&id=" + idFitxer;
  // Codi HTML de la pàgina web
  var pagina = "<!DOCTYPE HTML>" + "\n";
  pagina = pagina + "<html>" + "\n";
  pagina = pagina + "<head>" + "\n";
  pagina = pagina + "<meta charset='UTF-8'>" + "\n";
  pagina = pagina + "</head>" + "\n";
  pagina = pagina + "<body>" + "\n";
  pagina = pagina + "<h1>Dades de l'any passat</h1>" + "\n";
  pagina = pagina + resp + "\n";
  pagina = pagina + "</body>" + "\n";
  pagina = pagina + "</html>" + "\n";
  return HtmlService.createHtmlOutput(pagina);
}
function doPost(e){
  var data = JSON.parse(e.postData.contents);  // Llegeix les dades rebudes per JSON i les guarda
  var text = data.message.text;  // Comanda enviada
  var id = data.message.chat.id;  // Identificador de la finestra d'on prové el missatge 
  var id_usuari = data.message.from.id; // Identificador de l'usuari
  var id_missatge = data.message.message_id; // Identificador del missatge
  var lang = data.message.from.language_code ;  // Idioma de l'usuari 
  var nom_usr = data.message.from.first_name ;  // Nom de l'usuari
  var location = data.message.location;  // Localització de l'usuari (si es sap) 
  var realitzat = false;
  if(text == '/grafic'){
    var resposta = "Estem preparant el gràfic. Espera un moment.";  // Text que s'envia
    sendText(id,resposta);  // Mentre es prepara el gràfic, ja que triga un temps
    var resp = grafic();  // Crea el gràfic
    var nomFitxer = "grafic";  // Nom del fitxer
    // Crea el fitxer
    var fitxerNou = DriveApp.getFolderById(IdCarpeta).createFile(nomFitxer + ".svg",resp);
    var idFitxer = fitxerNou.getId();  // Identificador del fitxer
    var urlDescFitxer = fitxerNou.getDownloadUrl();  // Enllaç per descarregar el fitxer
    // Enllaç per veure el fitxer al navegador
    var urlFitxer = "https://drive.google.com/uc?export=view&id=" + idFitxer;
    var blob = UrlFetchApp.fetch(urlFitxer).getBlob();  // Crea el gràfic com a dades binàries
    var b64 = blob.getContentType() + ';base64,'+ Utilities.base64Encode(blob.getBytes());
    // Crea una pàgina web senzilla que conté el gràfic
    var htmlEst = '<style type="text/css" media="print">@page {size: landscape;} </style>';
    var htmlInst = htmlEst + '<img src="data:' + b64 + '" width=900 alt="Histograma">';
    var html = HtmlService.createHtmlOutput(htmlInst);
    // Crea el PDF a partir de la pàgina web
    var pdf = DriveApp.createFile(html.getAs("application/pdf").setName(nomFitxer + ".pdf"));
    var urlDescPDF = pdf.getDownloadUrl();  // Enllaç per descarregar el fitxer
    var pdfId = pdf.getId();  // Identificador del fitxer PDF
    var repetir = true;
    // Hem d'esperar que estigui llesta la miniatura
    var cnt = 0;  // Comptador de dècimes de segon
    while(repetir){  // Mentre no estigui
      var thumb = DriveApp.getFileById(pdfId).getThumbnail();  // Mirem si ja s'ha creat la miniatura
      Utilities.sleep(100);  // Esperem una dècima de segon
      cnt++;
      if((cnt > 100) || (thumb)){  // Si ja està o han passat deu segons
        repetir = false;  // Sortim del bucle
      }
    }
    // El títol conté l'enllaç
    var descrip = "Pots descarregar el PDF aquí: " + urlDescPDF;
    sendBlobFile(id,thumb,descrip);  // Envia la imatge
    realitzat = true;
  }
  if (!realitzat){
    var resposta = "Comanda desconeguda";
    sendText(id,resposta);
  }
}
function grafic(){
  var resp = "";
  var sh = SpreadsheetApp.openById(ssId).getSheetByName("circ");  // Agafem el full
  var dades = sh.getDataRange().getValues();  // Agafa els valors
  // Vectors per guardar les dades
  var etiquetes = new Array();
  var valors = new Array();
  var suma = 0;
  for (var i in dades){  // Mira totes les dades
    if(i > 0){  // Ignorem la filera dels títols
      var row = dades[i];  // Agafa una filera
      etiquetes.push(row[0]);  // Guardem les etiquetes
      valors.push(row[1]);  // Guardem els valors
      suma = suma + row[1];  // Suma els valors, per tenir el total
    }
  }
  var grafic =  cap(640, 480, "Capa 1")+ '\n';  // Inici del gràfic
  var ang = 0;  // Angle inicial
  for (var i = 0; i < valors.length; i++){  // Per a tots els valors
    // Crea el sector
    var ident = "arc" + i;
    var angle = ang + 360 * valors[i] / suma;  // Angle final
    grafic = grafic + sector(210, 240, 150, ang, angle, colors[i], 2, transparent, ident) + '\n';
    ang = angle;  // Nou angle inicial
    // Posa la llegenda
    var ytxt = 460 - (valors.length - i) * 25;
    var ident = "circ" + i;
    grafic = grafic + cercle(450, ytxt, 10, 0, colors[i], transparent, ident) + '\n';
    var ident = "txt" + i;
    var perc = 100 * valors[i] / suma;
    var lleg = etiquetes[i] + "   " + valors[i] + " (" + perc.toFixed(2) + " %)";
    lleg = lleg.replace(".",",");
    grafic = grafic + text(470, ytxt+6, lleg, 16, negre, "e", 0, 0, ident) + '\n'; 
  }
  // Posem el final del fitxer svg
  grafic = grafic + peu(); 
  return grafic;
}
function sector(xc, yc, r, ai, af, color, gruix, vora, ident){
  var xi = xc + (r * Math.cos(ai * Math.PI / 180.0));
  var yi = yc + (r * Math.sin(ai * Math.PI / 180.0));
  var xf = xc + (r * Math.cos(af * Math.PI / 180.0));
  var yf = yc + (r * Math.sin(af * Math.PI / 180.0));
  var laf = 0;
  var sf = 1;
  if ((af - ai) > 180){
    laf = 1;
  }
  var resp = '<path id="' + ident + '" d=" M' + xi + ',' + yi + ' A' +  r;
  resp = resp + ',' + r + ' ' + 0 + ' ' + laf + ' ' + sf + ' ' + xf + ',';
  resp = resp + yf + ' L' + xc +',' + yc + ' Z';
  resp = resp + '" fill="' + color + '" stroke="' + vora;
  resp = resp + '" stroke-width="' + gruix + '" />';
  return resp;
}
function cercle(xc, yc, r, gruix, color, vora, ident){
  var resp = '<circle id="' + ident + '" cx="' + xc + '" cy="' + yc +  '" r="' + r;
  resp = resp + '" stroke-width="' + gruix + '" stroke="' + vora + '" fill="' + color + '"/>';
  return resp;
}
function text(x, y, txt, mida, color, alin, negreta, cursiva, ident){
  if (alin == "e"){  // Esquerra
    var aln = "start";
  }
  if (alin == "c"){  // Centre
    var aln = "middle";
  }
  if (alin == "d"){  // Dreta
    var aln = "end";
  }
  var resp = '<text id="' + ident + '" xml:space="preserve" text-anchor="' + aln;
  resp = resp + '" font-family="serif" font-size="' + mida + '" x="' + x + '" y="' + y +'"'; 
  if (negreta == 1){
    resp = resp + ' font-weight="bold"';
  }
  if (cursiva == 1){
    resp = resp + ' font-style="italic"';
  }
  resp = resp + ' stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="0" stroke="';
  resp = resp + color + '" fill="' + color + '">' + txt + '</text>';
  return resp;
}
function cap(ample, alt, titol){
  var resp = '<svg width="' + ample + '" height="' + alt;
  resp = resp + '" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"';
  resp = resp + ' xmlns:xlink="http://www.w3.org/1999/xlink">' + '\n';
  resp = resp + '<g>' + '\n' + '<title>' + titol + '</title>';
  return resp;
}
function peu(){
  var resp = '</g>' + '\n' + '</svg>';
  return resp;
}
function sendText(chatId,text_env,keyBoard){  // Funció que envia un text o un teclat a Telegram 
  keyBoard = keyBoard || 0;
  if(keyBoard.inline_keyboard || keyBoard.keyboard){
    var data = {
      method: "post",
      payload: {
        method: "sendMessage",
        chat_id: String(chatId),
        text: text_env,
        parse_mode: "HTML",
        reply_markup: JSON.stringify(keyBoard)
      }
    }
  } else {
    var data = {
      method: "post",
      payload: {
        method: "sendMessage",
        chat_id: String(chatId),
        text: text_env,
        parse_mode: "HTML"
      }
    }
  }
  UrlFetchApp.fetch( telegramUrl + '/', data);
}
function sendBlobFile(chatId,blob_data,caption){
  var payload = {
    method: "sendPhoto",
    chat_id: String(chatId),
    photo: blob_data,
    caption: caption,
    parse_mode: "HTML"
  };
  var options = {
    method: "POST",
    payload: payload,
    muteHttpExceptions: true
  };
  UrlFetchApp.fetch( telegramUrl + '/', options);
}

 

 

 

 

 

 

 

 

 

 

Llicència de Creative Commons
Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.