Bots de conversa de Telegram amb Google Apps Script

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

Representem gràficament una funció

En aquest exemple obtindrem la representació d'una funció, que pot ser contínua o discontínua. Per a cada funció caldrà definir el rang de valors (en ambdós eixos) que volem representar. En el programa que es mostra a continuació hem marcat en color les línies que corresponen a la definició de la funció i els rangs de visualització, la resta del programa serà igual per a qualsevol funció.

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
var negre = "#000000";
var blau = "#0000FF";
var gris = "#888888";
var transparent = "null";
function doGet(e){
  var resp = grafic();
  var nomFitxer = "grafic";
  var fitxerNou = DriveApp.getFolderById(IdCarpeta).createFile(nomFitxer + ".svg",resp);
  var idFitxer = fitxerNou.getId();
  var urlDescFitxer = fitxerNou.getDownloadUrl();
  var urlFitxer = "https://drive.google.com/uc?export=view&id=" + idFitxer;
  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>Representació de la funció</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.";
    sendText(id,resposta);
    var resp = grafic();
    var nomFitxer = "grafic";
    var fitxerNou = DriveApp.getFolderById(IdCarpeta).createFile(nomFitxer + ".svg",resp);
    var idFitxer = fitxerNou.getId();
    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();
    var b64 = blob.getContentType() + ';base64,'+ Utilities.base64Encode(blob.getBytes());
    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);
    var pdf = DriveApp.createFile(html.getAs("application/pdf").setName(nomFitxer + ".pdf"));
    var urlDescPDF = pdf.getDownloadUrl();  // Enllaç per descarregar el fitxer
    var pdfId = pdf.getId();
    var repetir = true;
    var cnt = 0;
    while(repetir){
      var thumb = DriveApp.getFileById(pdfId).getThumbnail();
      Utilities.sleep(100);
      cnt++;
      if((cnt > 100) || (thumb)){
        repetir = false;
      }
    }
    var descrip = "Pots descarregar el PDF aquí: " + urlDescPDF;
    sendBlobFile(id,thumb,descrip);
    realitzat = true;
  }
  if (!realitzat){
    var resposta = "Comanda desconeguda";
    sendText(id,resposta);
  }
}
// Definim el rang de valors dels eixos i la distància entre marques
var minX = -2 * Math.PI;
var maxX = 2 * Math.PI;
var incX = Math.PI / 4;
var minY = -1.5;
var maxY = 1.5;
var incY = 0.25;
// Funció que volem representar
function funcio(x){
  try{  // Fem un try per prevenir valors anòmals, com infinit
    var y = Math.cos(x) - 0.2 * Math.cos(3 * x);
  } catch(err) {
    var y = "";  // Si hi ha un error, torna una cadena buida
  }
  return y;
}
function grafic(){
  // Creem la capçalera
  var grafic =  cap(640, 480, "Capa 1")+ '\n';
  // Fem els eixos
  var zeroX = 50;
  var zeroY = 435;
  var fiX = 620;
  var fiY = 25;
  grafic = grafic + linia(zeroX-5, zeroY, fiX, zeroY, 3, negre, "eixX") + '\n';
  grafic = grafic + linia(zeroX, fiY, zeroX, 440, 3, negre, "eixY") + '\n';
  // Posem les divisions de l'eix Y
  var divY = (maxY - minY) / incY;
  for (i = 0; i <= divY; i++){
    var ym = zeroY - ((zeroY - fiY) / divY) * i;
    var valor = incY * i + minY;
    valor = Math.round(valor * 100) / 100;  // Posem valors amb dos decimals
    var val = valor.toString().replace(".",",");  // Canviant punts per comes
    var noml = 'my' + i;
    var nomt = 'tmy' + i;
    if(valor == 0){  // Marquem la posició del zero
      grafic = grafic + linia(zeroX-5, ym, fiX, ym, 2, gris, noml + "e0") + '\n';
      var y0 = ym;
    }
    grafic = grafic + linia(zeroX-5, ym, zeroX + 1, ym, 2, negre, noml) + '\n';
    grafic = grafic + text(zeroX-10, ym+5, val, 16, negre, "d", 0, 0, nomt) + '\n'; 
  } 
  // Posem les divisions de l'eix X
  var divX = (maxX - minX) / incX;
  for (i = 0; i <= divX; i++){
    var xm = zeroX - ((zeroX - fiX) / divX) * i;
    var valor = incX * i + minX;
    valor = Math.round(valor * 100) / 100;  // Posem valors amb dos decimals
    var val = valor.toString().replace(".",",");  // Canviant punts per comes
    var noml = 'mx' + i;
    var nomt = 'tmx' + i;
    if(valor == 0){  // Marquem la posició del zero
      grafic = grafic + linia(xm, zeroY+5, xm, fiY, 2, gris, noml + "e0") + '\n';
    }
    grafic = grafic + linia(xm, zeroY+5, xm, zeroY, 2, negre, noml) + '\n';
    grafic = grafic + text(xm, zeroY + 25, val, 16, negre, "c", 0, 0, nomt) + '\n'; 
  }
  // Dibuixem la línia del gràfic
  incX = incX / 20;  // Ara farem 20 punts per a cada divisió de l'escala
  divX = (maxX - minX) / incX;
  var numpunts = (maxX - minX) / incX;
  var yp = "";  // El primer segment correspondrà al segon punt
  for (i = 0; i <= numpunts; i++){
    var valX = incX * i + minX;
    var xm = zeroX - ((zeroX - fiX) / divX) * i;
    var valY = funcio(valX);  // Calculem la Y aplicant la funció sobre la X
    // Si el punt és anòmal, no dibuixem
    if(valY !== ""){  // Posem !== perquè "" == 0 però no "" === 0
      var ym = y0 - ((zeroY - fiY) /  (maxY - minY)) * valY;
      if((valY <= maxY) && (valY >= minY)){  // Només dibuixem si queda dins
        if(yp !== ""){  // Si el punt anterior no val, tampoc dibuixem
          var nomlg = 'L' + i;
          grafic = grafic + linia(xp, yp, xm, ym, 4, blau, nomlg) + '\n';
        }
      } else {
        var ym = "";  // Si quedava fora, el punt no val com a anterior
      }
    } else {
      var ym = "";  // El punt no era vàlid
    }
    var xp = xm;  // El punt actual passa a anterior per a la propera iteració
    var yp = ym;
  } 
  // Posem el final del fitxer svg
  grafic = grafic + peu(); 
  return grafic;
}
function linia(xi, yi, xf, yf, gruix, color, ident){
  var resp = '<line id="' + ident + '" x1="' + xi + '" y1="' + yi + '" x2="' + xf + '" y2="' + yf;
  resp = resp + '" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="';
  resp = resp + gruix + '" stroke="' + color + '" fill="none"/>';
  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">' + '\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);
}

El gràfic obtingut és el següent:

Gràfica

Per veure com quedaria una funció discontínua, podem canviar les línies marcades en color per les següents:

var minX = -5;
var maxX = 7;
var incX = 1;
var minY = -3;
var maxY = 1.5;
var incY = 0.25;
// Funció que volem representar
function funcio(x){
  try{  // Fem un try per prevenir valors anòmals, com infinit
    var y = (1 / (x - 2)) - 1;
  } catch(err) {
    var y = "";  // Si hi ha un error, torna una cadena buida
  }
  return y;
}

El gràfic obtingut és el següent:

Gràfica

 

 

 

 

 

 

 

 

 

 

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