Bots de conversa de Telegram amb Google Apps Script

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

Empleator

Aquest bot de Telegram permet crear un currículum i simular una entrevista de feina. Per a l'entrada del currículum, el bot va preguntant les diferents informacions que després formaran part del document PDF generat. En el mode d'entrevista, l'usuari ha de respoondre un test de diverses preguntes i es calcula una puntuació a partir de les respostes.

La informació entrada pels usuaris es guarda en un full de càlcul. Hi ha un document de Google que fa de plantilla; sobre una còpia d'aquest és on es posarà la informació introduïda per l'usuari per així generar el currículum.

El programa de l'script és el següent:

var token = "^^33169381:AAHcCfJ6yr9wlwRBSge347FEZo-97mLFt8k";
var telegramUrl = "https://api.telegram.org/bot" + token;
var spreadsheetId = "^^2LSz3xGeK7Ft7fA8otvN_ZXX1Bl9ItarKBX3y7cV9E";
var respostes = new Array(14);
var hoja = SpreadsheetApp.openById(spreadsheetId).getSheetByName('Hoja1'); // Abre la hoja de cálculo
var rango = hoja.getRange(1, 3, 1, hoja.getLastColumn()-2); // Obtiene el rango de la primera fila
var pf = rango.getValues()[0]; // Obtiene los valores de la primera fila como un array
var cv = [null,null,'Tu nombre', 'Quan vas neixer', 'País de naixement','Tu calle, 123','Ciutat',
         '000 000 000','no_reply@example.com','Com ets','Que idiomas hablas','Titols que poseeixes',
         'Experiencia laboral','Noms projectes','Logros hechos','Vull'];
var titols = { "Quan vas neixer": "Edat:", "País de naixement": "Nacionalitat:",
             "Experiencia laboral": "EXPERIENCIA", "Titols que poseeixes":"FORMACIÓ",
             "Noms projectes": "PROJECTES","Vull":"MÉS SOBRE MI","Com ets":"APTITUDS",
             "Logros hechos":"ASSOLIMENTS", "Que idiomas hablas":"IDIOMES"};
var docId = '^^-5mFe3LLiam0OUZUtkfi_feorN1w_c05KqAyEHWvMY';
var contador= 0;
var preguntas = new Array(12);
var tt = "Què faries per incentivar la creativitat en el teu equip durant la fase de disseny d'un projecte?\n";
tt = tt + "A) Proporcionar temps addicional per desenvolupar idees fora del convencional.\n";
tt = tt + "B) Evitar idees fora de la indústria per enfocar-se només en les solucions provades.\n";
tt = tt + "C) Fixar objectius molt específics per evitar distraccions.\n";
tt = tt + "D) Evitar l'ús d'eines digitals per centrar-se únicament en el tradicional.";
preguntas[0] = tt;
tt = "Com abordaries la integració d'un nou membre a l'equip enmig d'un projecte ja avançat?\n";
tt = tt + "A) Reassignar el nou membre a un equip diferent.\n";
tt = tt + "B) Involucrar-lo en una part petita del projecte perquè conegui l'equip.\n";
tt = tt + "C) Donar-li un rol important immediatament perquè aprengui amb la pràctica.\n";
tt = tt + "D) Deixar que s'integri pel seu compte sense guiar-lo.";
preguntas[1] = tt;
tt = "Un membre de l'equip té una proposta que no ha estat provada abans a l'empresa. Com actuaries?\n";
tt = tt + "A) Descarta la proposta ja que és un risc.\n";
tt = tt + "B) Analitzar la seva viabilitat i si és prometedora, provar-la en una escala petita.\n";
tt = tt + "C) Esperar que altres equips avaluïn la proposta primer.\n";
tt = tt + "D) Realitzar una anàlisi detallada per trobar errors en la proposta.";
preguntas[2] = tt;
tt = "Com gestionaries la comunicació entre el teu equip durant un projecte complex?\n";
tt = tt + "A) Utilitzar una plataforma en línia perquè tots els membres comparteixin ";
tt = tt + "actualitzacions i progressos.\n";
tt = tt + "B) Organitzar reunions diàries en persona perquè tots estiguin al corrent.\n";
tt = tt + "C) Deixar que cada membre triï la forma de comunicació que prefereixi.\n";
tt = tt + "D) Demanar a cada membre que informi directament al líder del projecte sense comunicar-se ";
tt = tt + "amb els altres.";
preguntas[3] = tt;
tt = "Com animaries el teu equip perquè prengui la iniciativa en els seus rols individuals?\n";
tt = tt + "A) Establir objectius clars però permetre flexibilitat en el seu enfocament.\n";
tt = tt + "B) Insistir que tots segueixin un protocol rígid per evitar errors.\n";
tt = tt + "C) Recompensar només els que compleixin objectius específics.\n";
tt = tt + "D) Evitar reconèixer públicament les iniciatives individuals per no generar competència.";
preguntas[4] = tt;
tt = "Com respondries si un projecte crític té un retard considerable?\n";
tt = tt + "A) Buscar una solució ràpida amb una empresa externa.\n";
tt = tt + "B) Motivar el teu equip perquè proposin solucions col·lectives.\n",
tt = tt + "C) Ignorar el retard i centrar-se en la següent etapa.\n";
tt = tt + "D) Delegar el problema a un altre departament.";
preguntas[5] = tt;
tt = "Quina estratègia utilitzaries per assegurar-te que els rols dins de l'equip estan equilibrats ";
tt = tt + "i són manejables?\n";
tt = tt + "A) Revisar periòdicament la càrrega de treball amb l'equip.\n";
tt = tt + "B) Distribuir més treball entre els membres més productius.\n";
tt = tt + "C) No realitzar cap ajust perquè cada membre gestioni la seva pròpia càrrega.\n";
tt = tt + "D) Reassignar tasques segons el rang jeràrquic de cada membre.";
preguntas[6] = tt;
tt = "El teu equip troba un problema tècnic que pot requerir un redisseny. Com procediries?\n";
tt = tt + "A) Informar immediatament a la direcció sense considerar solucions alternatives.\n";
tt = tt + "B) Avaluar ràpidament el problema per proposar solucions creatives.\n";
tt = tt + "C) Aturar tot el projecte fins que es resolgui el problema.\n";
tt = tt + "D) Reassignar el problema a un equip de suport extern.";
preguntas[7] = tt;
tt = "Com afrontaries un conflicte entre dos membres clau de l'equip?\n";
tt = tt + "A) Demanar-los que resolguin el problema sense intervenció.\n";
tt = tt + "B) Involucrar-te directament i mediar per trobar un compromís.\n";
tt = tt + "C) Reassignar un dels membres a un altre equip per evitar més conflictes.\n";
tt = tt + "D) Ignorar el conflicte fins que sigui un problema greu.";
preguntas[8] = tt;
tt = "Com fomentaries la innovació a llarg termini en un entorn competitiu?\n";
tt = tt + "A) Establir un programa de desenvolupament professional continu per al teu equip.\n";
tt = tt + "B) Sol·licitar idees innovadores però sense donar-los seguiment regular.\n";
tt = tt + "C) Centrar-se únicament en la innovació en els productes existents.\n";
tt = tt + "D) Evitar prendre riscos per mantenir l'estabilitat actual.";
preguntas[9] = tt;
tt = "Com fomentaries la creativitat en un projecte on les restriccions pressupostàries són una limitació?\n";
tt = tt + "A) Limitar la cerca de solucions al que s'ha provat prèviament.\n";
tt = tt + "B) Permetre que l'equip proposi idees de baix cost i les revisi col·lectivament.\n";
tt = tt + "C) Delegar la responsabilitat creativa únicament a un líder.\n";
tt = tt + "D) Centrar-se només en opcions que compleixin amb un pressupost específic.";
preguntas[10] = tt;
tt = "Quina estratègia implementaries per impulsar la presa d'iniciativa entre els membres del teu equip ";
tt = tt + "durant l'execució d'un projecte?\n";
tt = tt + "A) Oferir als membres un espai per suggerir millores en les seves àrees.\n";
tt = tt + "B) Oferir incentius només a aquells que es destaquin en el compliment d'objectius específics.\n";
tt = tt + "C) Supervisar estrictament les activitats per garantir l'adherència als objectius.\n";
tt = tt + "D) Desincentivar la iniciativa per evitar possibles errors.";
preguntas[11] = tt;
var respostes = [["A","B","C","D"],["A","B","C","D"],["A","B","C","D"],["A","B","C","D"],
                 ["A","B","C","D"],["A","B","C","D"],["A","B","C","D"],["A","B","C","D"],
                 ["A","B","C","D"],["A","B","C","D"],["A","B","C","D"],["A","B","C","D"]];
var puntuacio = [[5,0,1,0],[0,5,2,0],[0,5,2,1],[4,5,2,0],[5,0,2,0],[2,5,0,1],
                 [5,2,0,1],[1,5,0,2],[0,5,1,0],[5,2,1,0],[0,5,1,2],[5,2,0,0]];
function doPost(e) {
  var data = JSON.parse(e.postData.contents);
  if (data.message) {
    var text = data.message.text;
    var id = data.message.chat.id;
    var id_usuari = data.message.from.id;
    var id_missatge = data.message.message_id;
    var lang = data.message.from.language_code;
    var nom_usr = data.message.from.first_name;
    var location = data.message.location;
    var spreadsheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName("Hoja1");
    if (text.indexOf("/") == 0) {
      if(text == '/cv'){  // Iniciar curriculum
        iniParam(id);
      }
      if(text == '/start'){
        var resposta = "Comandes disponibles:\n";
        resposta = resposta + "/cv    Amb aquesta comanda es crea el curriculum.\n";
		resposta = resposta + "/sim    Amb aquesta comanda s'inicia una simulació d'entrevista ";
		resposta = resposta + "de treball per ser enginyer mecànic.";
        sendText(id,resposta);
      }
      if (text == '/sim') {
        var inde = 0;
        var puntuacion = 0;
        PropertiesService.getScriptProperties().setProperty('inde', inde);
        PropertiesService.getScriptProperties().setProperty('puntuacion', puntuacion);
        pregunt(id, inde);
      }
    } else {
      param(id, text);
    }
  } else if (data.callback_query) {
    var id = data.callback_query.message.chat.id;
    var callbackData = data.callback_query.data;
    var id_usuari = data.callback_query.from.id;
    handleCallbackQuery(id, callbackData, id_usuari);
  }
}
function subs(id_usr){
  var sh = SpreadsheetApp.openById(spreadsheetId).getSheetByName("Hoja1");  // Obrim el full de càlcul
  var dades = sh.getDataRange().getValues();  // Agafem el contingut
  var dar_fil = sh.getLastRow();  // Índex de la darrera filera
  var dar_col= sh.getLastColumn(); //Índex darrera columna
  var template = DriveApp.getFileById(docId); //Agafem la plantilla de currículum
  // Mirem si l'usuari consta a la llista
  for(var i = 0; i < dar_fil; i++){  // Mirem totes les dades
    var row = dades[i];  // Agafa una filera
    var num_usuari = row[0];  // Agafa el número d'usuari de la filera
    if(num_usuari == id_usr){  // Mira si és l'usuari actual
      // Crear una copia amb un nom únic (row[2] és el nom de l'usuari)
      var copiedFile = template.makeCopy('Currículum_' + row[2].replace('^',''));
      var doc = DocumentApp.openById(copiedFile.getId()); //Obrim la copia
      var body = doc.getBody(); //Entrem al cos del document
      for(var e=2; e < dar_col-1; e++){ //Recorrem cada columna
        row[e] = row[e].replace("^",""); 
        if (row[e] == "."){
          if (cv[e] in titols){
            body.replaceText(titols[cv[e]],'').replaceText(cv[e], '');
          } else {
            body.replaceText(cv[e],'');
          }
        } else {
          // Reemplaza 'texto' por la respuesta del usuario en todo el documento
          body.replaceText(cv[e], row[e]);
        }
      }
    }
  }
  doc.saveAndClose();
  var tt = "Moltes felicitats! Ja has completat el formulari, a continuació t'enviem ";
  tt = tt + "el curriculum en format pdf. Aquesta acció pot tardar uns segons.";
  sendText(id_usr,tt);
  // Fem un bucle per esperar que estigui llest el pdf
  var repetir = true;
  var cnt = 0;  // Comptador de dècimes de segon
  while(repetir){  // Mentre no estigui
    // Mirem si ja s'ha creat el pdf
    var pdf = DriveApp.getFileById(copiedFile.getId()).getAs('application/pdf');
    Utilities.sleep(10000);  // Esperem una dècima de segon
    cnt++;
    if((cnt > 10000) || (pdf)){  // Si ja està o han passat deu segons
      repetir = false;  // Sortim del bucle
      sendDocument(id_usr, pdf);
    }
  }
}
function param(id_usr, comand) {  
  // Processa els paràmetres
  var userProperties = PropertiesService.getUserProperties();
  var contador = parseInt(userProperties.getProperty('contador')) || 0;
  var sh = SpreadsheetApp.openById(spreadsheetId).getSheetByName("Hoja1");  
  // Obrim el full de càlcul
  var dades = sh.getDataRange().getValues();  
  // Agafem el contingut
  var dar_fil = sh.getLastRow();  
  // Índex de la darrera filera
  // Mirem si l'usuari consta a la llista
  var fila = 0;
  for (var i = 0; i < dar_fil; i++) {  
    // Mirem totes les dades
    var row = dades[i];  
    // Agafa una filera
    var num_usuari = row[0];  
    // Agafa el número d'usuari de la filera
    if (num_usuari == id_usr) {  
      // Mira si és l'usuari actual
      fila = i;  
      // No sumem 1 perquè al vector es compta a partir de 0
    }
  }
  var row = dades[fila];  
  // Agafem les dades de l'usuari
  var row0 = dades[0].slice(2, 16); 
  // Agafem les preguntes
  var row1 = dades[1].slice(2, 16); 
  // Agafem estat d'edició
  var rowi = row.slice(2, 16);
  // A la segona casella tenim l'estat de l'enviament de paràmetres
  var estat = row[1];
  if (estat < 14) {  
    // Si encara no hem rebut tots els paràmetres
    var rang = sh.getRange(fila + 1, 2);  
    // Selecciona la casella de l'estat
    rang.setValue(estat + 1);  
    // Passem a l'estat següent
    var rang = sh.getRange(fila + 1, estat + 3);  
    // Selecciona la casella següent
    // Posem ^ al davant perquè ho interpreti com a text
    rang.setValue('^' + comand);  
    // Guarda el valor a la casella
  }
  if (estat == 14) {  
    // Si ja tenim tots els paràmetres, comencem el procès d'edició
    if (comand.toLowerCase() == "si") {
      editar(id_usr, rowi);
      contador += 2;
      userProperties.setProperty('contador', contador);
      return;
    }
    if (comand.toLowerCase() == "no") {
      subs(id_usr);
    }
    if (comand== "cancelar"){
      subs(id_usr);
    }
    var editarNumero = parseInt(comand);
    if (!isNaN(editarNumero) && editarNumero >= 1 && editarNumero <= rowi.length) {
      var respostaEditar = rowi[editarNumero - 1];
      var indexRes = editarNumero - 1;
      sendText(id_usr, "Siusplau, escriu la nova resposta per '" + row0[editarNumero - 1] + "'." );
      contador += 1;
      userProperties.setProperty('contador', contador);
      userProperties.setProperty('indexRes', indexRes);
      return;
    };
    if (contador > 2) {
      var savedIndexRes = userProperties.getProperty('indexRes');
      var prova = parseInt(savedIndexRes);
      actualitzarResposta(id_usr, comand, fila, prova + 2);
      contador += 1
      userProperties.setProperty('contador', contador);
    }
    if (contador > 3) {
      subs(id_usr);
    }
  } else {
    sendText(id_usr, pf[estat + 1]);
  }
}
function editar(id_usr){
  // Agregar una opción para cancelar
  var missatge = "\nEscriu el número de la resposta que vols editar o 'cancelar' per sortir.";
  sendText(id_usr, missatge);
}
function actualitzarResposta(id_usr,novaResposta ,fila,columna) {
  // Actualizar la respuesta en el historial
  var sh = SpreadsheetApp.openById(spreadsheetId).getSheetByName("Hoja1");
  var rang = sh.getRange(fila + 1, columna + 1);  // +1 because getRange is 1-based
  rang.setValue('^' + novaResposta);
  // Actualizar el registro de respuestas del usuario
  //dades[fila] = historialRespostes;
  // Confirmar la actualización al usuario
  sendText(id_usr, "La resposta ha sigut actualitzada correctament.");
}
function iniParam(id_usr){  // Es prepara per rebre paràmetres
  var sh = SpreadsheetApp.openById(spreadsheetId).getSheetByName("Hoja1");  // Agafa el full de càlcul
  var dades = sh.getDataRange().getValues();  // Agafa els valors
  var dar_fil = sh.getLastRow();  // Índex de la darrera filera
  // Mirem si l'usuari ja consta a la llista
  var fila = 0;
  for(var i = 0; i < dar_fil; i++){  // Recorrem les dades
    var row = dades[i];  // Llegim una filera
    var num_usuari = row[0];   // Identificador de l'usuari
    if(num_usuari == id_usr){  // Si és el que correspon
      fila = i + 1;  // Cal sumar 1 perquè comença a comptar des d'1
    }
  }
  // Si l'usuari és nou el donarem d'alta
  // Si no és nou, esborrarem les dades perquè entenem que vol tornar a començar
  var camps = new Array(16);  // Contindrà els valors per guardar a la taula
  camps[0] = id_usr;  // Identificador de l'usuari
  camps[1] = 0;  // Encara no tenim cap paràmetre
  for(var i = 2; i < 16; i++){
    camps[i] = "";  // No hi ha paràmetres
  }
  var valu = new Array(1);  // Matriu de dades a escriure
  valu[0] = camps;  // Converteix el vector en una matriu d'una filera i dues columnes
  if(fila == 0){  // Si l'usuari és nou
    sh.appendRow(camps);   // Afegeix una filera amb les dades 
  } else {  // Si no és nou, sobrescrivim
    // La funció getRange ens permet seleccionar el grup de caselles sobre les que anem a escriure
    // El primer paràmetre és la filera on comença el grup, en el nostre cas la que hem trobat abans
    // El segon paràmetre és la columna on comença el grup, en el nostre exemple la primera (comencen en 1)
    // El tercer paràmetre és el nombre de fileres que tindrà el grup, per a nosaltres una
    // El quart paràmetre és el nombre de columnes que ocupa el grup, en el nostre cas set
    var rang = sh.getRange(fila, 1, 1, 16);
    rang.setValues(valu);  // Guarda els valors a les caselles, substituïnt els anteriors
  }
  //Reinciem el comptador
  var userProperties = PropertiesService.getUserProperties();
  userProperties.setProperty('contador', 0);
  var resposta = "Iniciem la creació del curriculum.\nPer passar a la següent ";
  resposta = resposta + "pregunta i deixar la actual en blanc escriu un punt";
  sendText(id_usr,resposta);
  sendText(id_usr,pf[0]);
}
function fiParam(id_usr,fila){  // Acaba el procés de recepció de paràmetres
  var sh = SpreadsheetApp.openById(spreadsheetId).getSheetByName("Hoja1");  // Obre el full de càlcul
  // Tornem l'usuari a l'estat inicial
  var camps = new Array(16);  // Contindrà els valors per guardar a la taula
  camps[0] = id_usr;  // Identificador de l'usuari
  camps[1] = 0;  // Encara no tenim cap paràmetre
  for(var i = 2; i < 16; i++){
    camps[i] = "";
  }
  var valu = new Array(1);  // Matriu de dades a escriure
  valu[0] = camps;  // Converteix el vector en una matriu d'una filera i dues columnes
  var rang = sh.getRange(fila+1, 1, 1, 16);
  rang.setValues(valu);  // Guarda els valors a les caselles, substituïnt els anteriors
}
function sendText(chatId,text_env,keyBoard){  // Funció que prepara per enviar 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 sendDocument(chatId, documentBlob) {
  var data = {
    method: "post",
    payload: {
      method: "sendDocument",
      chat_id: String(chatId),
      document: documentBlob
    }
  };
  UrlFetchApp.fetch(telegramUrl + '/', data);
}
function pregunt(id, inde) {
  var tecles = {
    "inline_keyboard": [
      [{"text": respostes[inde][0], "callback_data": "sol1-" + inde },
       {"text": respostes[inde][1], "callback_data": "sol2-" + inde },
       {"text": respostes[inde][2], "callback_data": "sol3-" + inde },
       {"text": respostes[inde][3], "callback_data": "sol4-" + inde }]
    ]
  };
  var titol = preguntas[inde];
  sendText(id, titol, tecles);
}
function handleCallbackQuery(id, callbackData, id_usuari) {
  var inde = parseInt(PropertiesService.getScriptProperties().getProperty('inde'));
  var puntuacion = parseInt(PropertiesService.getScriptProperties().getProperty('puntuacion'));
  var respuestaIndex = parseInt(callbackData.split("-")[0].replace('sol', ''));
  puntuacion += puntuacio[inde][respuestaIndex - 1];
  inde += 1;
  if (inde < preguntas.length) {
    PropertiesService.getScriptProperties().setProperty('inde', inde);
    PropertiesService.getScriptProperties().setProperty('puntuacion', puntuacion);
    pregunt(id, inde);
  } else {
    var tt = "Aquest test es una simulació d'una entrevista de trabal per un enginyer ";
    tt = tt + "mecànic. Per aquest tipus d'entrevistes es valoren principalment aquestes ";
    tt = tt + "4 aptituts: creativitat, treball en equip, iniciativa i gestió de projectes. ";
    tt = tt + "Per aquesta raó en aquest test es valoren principalmente aquestes qualitats.\n\n ";
    tt = tt + "Has obtingut una puntuació total de " + redondearAPeriodico(puntuacion/6,2) + " sobre 10."
    sendText(id, tt);
  }
}
function redondearAPeriodico(num, decimales) {
  var factor = Math.pow(10, decimales);
  return Math.round(num * factor) / factor;
}

 

 

 

 

 

 

 

 

 

 

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