Desklet de Cinnamon para movimientos de empresa del Banco Sabadell

Como ya conté en la entrada API del Banco Sabadell quería ponerme en el escritorio un desklet que me mostrara los últimos movimientos de la cuenta de la empresa, pero se conoce que el Banco Sabadell piensa que no tiene por qué permitirme acceder a mis propios datos.

Así que al no poder usar la API hay que hacerlo a lo bruto, simulando un acceso web normal. Antes esto era un poco más latoso, pero desde que existe puppeteer está chupado. Adjunto el código del script que obtiene los datos y los dos ficheros del desklet.

El script de Node para obtener los datos:

const puppeteer = require("puppeteer");
const fs = require("fs");
const path = require('path');

(async () =>
{
	const browser = await puppeteer.launch({/*headless: false, devtools: true, slowMo:500*/});
	const page = await browser.newPage();

	// Log in	
	await page.goto("https://www.bancsabadell.com/txempbs/default.bs");
	await page.goto("https://www.bancsabadell.com/txempbs/default.bs");
	await page.waitFor("input[name='userNIF']");
	await page.$eval("input[name='userNIF']", el => el.value = 'DNI_DE_ACCESO');
	await page.$eval("input[name='pinNIF']", el => el.value = 'PIN_DE_ACCESO');
	await page.$eval("button[name='s1']", f => f.click());

	await page.waitFor("a.enlaceIzquierda2Nv");

	// Save cookies
	const cookiesObject = await page.cookies();
	
	for (let cookie of cookiesObject)
            await page.setCookie(cookie);

  await page.goto("https://www.bancsabadell.com/txempbs/CUExtractOperationsQueryNew.init.bs");
  await page.waitFor("div.cuenta-item");
	await page.$eval("div.cuenta-item", f => f.click());

	await page.waitFor("#sm_modules_container2");

	const data = await page.evaluate(() => {
    const tds = Array.from(document.querySelectorAll('table.sorted tr'))
    return tds.map(td => td.innerText);
  });

  const filename = path.join(__dirname, 'output.csv');
  fs.writeFileSync(filename, data.join("\n"));

	await page.waitFor(1000);
	await browser.close();
})();

El fichero desklet.js:


const Desklet = imports.ui.desklet;
const St = imports.gi.St;
const Cinnamon = imports.gi.Cinnamon;
const GLib = imports.gi.GLib;

function HelloDesklet(metadata, desklet_id) {
    this._init(metadata, desklet_id);
}

HelloDesklet.prototype = {
    __proto__: Desklet.Desklet.prototype,

    _init: function(metadata, desklet_id) {
        Desklet.Desklet.prototype._init.call(this, metadata, desklet_id);
        GLib.spawn_command_line_sync("node /home/koas/bin/bankScraper/sabadell.js");
        this.setupUI();
    },

    setupUI: function() {
        // main container for the desklet
        this.window = new St.Bin();
        this.text = new St.Label();
        this.text.set_text(Cinnamon.get_file_contents_utf8_sync("/home/koas/bin/bankScraper/output.csv"));
        
        this.window.add_actor(this.text);
        this.setContent(this.window);
    }

}

function main(metadata, desklet_id) {
    return new HelloDesklet(metadata, desklet_id);
}

El fichero metadata.json:


{
    "dangerous": false, 
    "description": "Muestra los \u00faltimos movimientos de una empresa del Banco Sabadell", 
    "prevent-decorations": false, 
    "uuid": "bancoSabadell@koas.dev", 
    "name": "\u00daltimos movimientos Banco Sabadell Empresas"
}

Ejecutar Vue.js para desarrollo desde un dominio distinto a localhost

Para cada proyecto que comienzo me creo un host virtual en mi servidor web local que apunte a la carpeta de desarrollo con un dominio tipo nombreProyecto_dev. Luego añado una entrada a /etc/hosts apuntando ese dominio inventado a 127.0.0.1. Esto permite detectar en el código cuándo estamos en un servidor de desarrollo y cuándo estamos en producción.

Pero el entorno de desarrollo de Vue.js sirve nuestra página desde localhost:8080, y esto es un problema. Supongamos que el código PHP del proyecto se sirve desde el dominio proyecto_dev: cuando hagamos una llamada desde JS (en localhost) a PHP (en proyecto_dev) las cookies no se enviarán, ya que se trata de dominios distintos.

Para solucionar esto editamos (o creamos, si no existe) el fichero vue.config.js en el directorio raíz de nuestra aplicación y añadimos estas líneas:

module.exports = {
    configureWebpack: {
        devServer: {
            host: 'proyecto_dev',
            port: '8080'
        }
    }
}

Una utilidad imprescindible para línea de comandos: screen

Cuando trabajamos con línea de comandos en servidores remotos hay veces que debemos ejecutar alguna tarea que requiere tiempo. En estos casos, si perdemos la conexión con el servidor no podremos saber de una forma sencilla cuándo se ha terminado de ejecutar esa tarea, ya que aunque podamos reconectarnos la sesión de terminal se habrá perdido.

Para evitar esto podemos usar screen, que nos permite tener varias “sesiones” abiertas. Si perdemos la conexión al servidor mientras estamos dentro de una de las sesiones al volver a conectar podremos recuperar la sesión en la que estábamos sin problema. Esa es su ventaja número uno.

Su segunda ventaja es que permite dividir la pantalla en dos de forma horizontal y vertical. Esto es algo que pueden hacer los programas gráficos de terminal, podemos tener tantas pestañas abiertas en un servidor como queramos, pero no está de más conocerlo porque hay casos en los que nos resultará más cómodo tener la información de dos sesiones a la vista al mismo tiempo. Aquí hay un listado con los comandos más habituales de screen.

Bonus tip: para simplificar un poco el uso de screen existe una utilidad llamada screenie que no es más que un pequeño interfaz de texto para crear nuevas sesiones o conectarse a las existentes.

API del Banco Sabadell

Estoy empezando a hacer una aplicación que usa la API del Banco Sabadell, y hay dos cosas a tener en cuenta:

  • Después de registrarte en el portal de desarrolladores y crear tu aplicación hay que ir a una oficina a que te la validen para que no se quede en estado pendiente de validación.
  • El número de teléfono que aparece de contacto es un 902, el equivalente nacional gratuito es 963 085 000

Según vaya descubriendo más cosas os iré contando.

Actualización 15-03-2019: tenía pendiente actualizar esta entrada y hasta ahora no había tenido tiempo.

TL;DR; muy mal el Banco Sabadell.

Fui a la oficina a que me activaran la aplicación y allí no tenían ni la más remota idea de qué era la API. Después de estar la gestora (que eso sí, tuvo una actitud impecable) un rato al teléfono no puedo activarme nada, pero le dio mi correo a alguien del departamento técnico.

Al día siguiente me enviaron un correo haciendo un montón de preguntas sobre la aplicación. Respondí a todas y pasaron varios días sin recibir respuesta, y finalmente me escribieron diciendo que no cumplía los requisitos para activarse. La aplicación era algo muy sencillo, simplemente quería obtener los últimos movimientos y mostrarlos en mi escritorio con un desklet de Cinnamon, pero parece ser que al Banco Sabadell no le parece normal que una empresa quiera acceder al listado de sus propios movimientos. Es de risa.

Así que muy mal, Banco de Sabadell. No sé para qué tenéis una API si cuando una empresa luego la quiere usar para acceder a sus datos no se lo permitís.

Al final lo he conseguido usando puppeteer.

¿Cómo hacer que mi teléfono Android aparezca en adb para hacer debug remoto en Linux Mint?

Lo primero es conectar el teléfono por USB al ordenador, si nos pregunta qué modo de conexión queremos usar seleccionaremos el de imágenes.

A continuación ejecutamos lsusb, que nos devolverá varias líneas con información sobre nuestros dispositivos USB, el que nos interesa es el del teléfono, en mi caso (Huawei P Smart):

Bus 001 Device 075: ID 12d1:107e Huawei Technologies Co., Ltd. 

Con esta información crearemos el fichero /etc/udev/rules.d/51-android.rules con este contenido:

SUBSYSTEM=="usb", ATTR{idVendor}=="12d1", ATTR{idProduct}=="107e", MODE="0666" GROUP="plugdev", SYMLINK+="android%n"

Hecho esto reiniciamos el servicio udev:

/etc/init.d/udev restart

Editamos el fichero ~/.android/adb_usb.ini y añadimos una línea con el identificador del fabricante (12d1 en mi caso) en formato hexadecimal:

0x12d1

Y arrancamos el servidor de adb:

adb kill-server && adb start-server

Y ¡listo! Nos vamos a las opciones del desarrollador del teléfono, activamos la opción Depuración por USB y ya podremos inspeccionar desde el ordenador las ventanas de Chrome.

El operador unario + en JavaScript

Me enteré el otro día leyendo un artículo de que el operador unario + en JavaScript sirve para intentar convertir al tipo Number al operando que le siga. Si no lo consigue el resultado será NaN.

Es decir, que en vez de usar

let cadena = "5";
let numero = parseInt(cadena, 10);

podemos usar

let cadena = "5";
let numero = +cadena;

Es mucho más conciso, si bien esto es algo que quizá no todo el mundo conoce y puede llevar a confusión, y ya sabemos que…