Telegram Bot API getUpdates on long polling shorts on 50 seconds

I didn’t want to implement an open HTTPs server for webhooks, even if they look sexy, but I also wanted to load the Telegram servers as little as possible, so given that the long polling doesn’t really get any delay if you arbitrarily increase the timeout*, I setted up a 300 seconds timeout for a 5 minutes refresh of the request.

My getUpdates URL is built like so:

"https://api.telegram.org/bot".$telegrambot."/getUpdates?timeout=".$timeout."&offset=".$offset

where $timeout is set at 300.

What I found out from my PHP app logs is this:

Time | UpdateID | HTTP Response | Total time
20:56:09 455417146 200 50.210294
20:56:59 455417146 200 50.205697
20:57:49 455417146 200 50.18303
20:58:39 455417146 200 50.191815
20:59:30 455417146 200 50.180151
21:00:20 455417146 200 50.178455
21:01:10 455417146 200 50.204421
21:02:00 455417146 200 50.197673
21:02:50 455417146 200 50.193216
21:03:41 455417146 200 50.205001
21:04:31 455417146 200 50.190687
21:05:21 455417146 200 50.178421
21:06:11 455417146 200 50.198388
21:07:01 455417146 200 50.191959
21:07:51 455417146 200 50.190216
21:08:42 455417146 200 50.193751
21:09:32 455417146 200 50.192767
21:10:22 455417146 200 50.189805
21:11:12 455417146 200 50.205147
21:12:02 455417146 200 50.191382
21:12:53 455417146 200 50.191173
21:13:43 455417146 200 50.201623
21:14:33 455417146 200 50.190784
21:15:23 455417146 200 50.189714
21:16:13 455417146 200 50.191169
21:17:04 455417146 200 50.193477
21:17:54 455417146 200 50.199469
21:18:44 455417146 200 50.210404
21:19:34 455417146 200 50.183195
21:20:24 455417146 200 50.178331
21:21:15 455417146 200 50.203791
21:22:05 455417146 200 50.203149
21:22:55 455417146 200 50.190459
21:23:45 455417146 200 50.19248
21:24:35 455417146 200 50.193081
21:25:26 455417146 200 50.180961
21:26:16 455417146 200 50.197347

No matter what timeout value you set in the URL, the server will always return after 50 seconds.

Why oh why?

* you can check on Wikipedia for a long polling description, anyway here’s the hang of it: you load a URL with a set timeout in the query, the webserver accepts the request but doesn’t send back any data until either some updates are found, or the timeout expires. So, for example, if the timeout is 60 seconds, either there’s an update in the next minute, after which the server immediately responds with the update information, or at the 60 seconds mark the server will respond with “no updates”, at which point you can send out an HTTP request again to that URL, and the cycle starts anew. Since there is no real delay between the updates and the server response, it would be sensible to set a high timeout, even 10 minutes, so, while still getting updates as soon as possible, you will load the server very little.

Dump SQLite database to .sql with PHP alone

I have a webapp on my phone what uses SQLite, and I have no sqlite3 tool that I can call from CLI, so I needed a pure-PHP solution to dumping my database.

Nothing on the internet seems to be created for this purpose, or at least nothing that can be found in the first page of google.

So well, if this page won’t end up on the first page of google I’m just wasting my time.

Dammit.

So anyway:

<?php

$db = new SQLite3(dirname(__FILE__)."/your/db.sqlite");
$db->busyTimeout(5000);

$sql="";

$tables=$db->query("SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%';");

while ($table=$tables->fetchArray(SQLITE3_NUM)) {
	$sql.=$db->querySingle("SELECT sql FROM sqlite_master WHERE name = '{$table[0]}'").";\n\n";
	$rows=$db->query("SELECT * FROM {$table[0]}");
	$sql.="INSERT INTO {$table[0]} (";
	$columns=$db->query("PRAGMA table_info({$table[0]})");
	$fieldnames=array();
	while ($column=$columns->fetchArray(SQLITE3_ASSOC)) {
		$fieldnames[]=$column["name"];
	}
	$sql.=implode(",",$fieldnames).") VALUES";
	while ($row=$rows->fetchArray(SQLITE3_ASSOC)) {
		foreach ($row as $k=>$v) {
			$row[$k]="'".SQLite3::escapeString($v)."'";
		}
		$sql.="\n(".implode(",",$row)."),";
	}
	$sql=rtrim($sql,",").";\n\n";
}
file_put_contents("sqlitedump.sql",$sql);

Or just find, edit, comment the code on GitHub.

Migrating PHP code from MySQL to SQLite, the basics

This is also for self-reference.

Data types

MySQL

There’s a shitload of data types, really, just go look at the official documentation and bask in the glory of MySQL redundancy (j/k).

SQLite

Very few, reassuring and simple data types. Basically text, numbers, and “whatever” (blob). Again, go look at the official documentation.

Database connection

MySQL

$GLOBALS["dbcon"]=@mysqli_connect($dbhost, $dbuser, $dbpass);
if (mysqli_error($GLOBALS["dbcon"])) die("errore connessione db");
@mysqli_select_db($GLOBALS["dbcon"],$dbname);
if (mysqli_error($GLOBALS["dbcon"])) die("errore connessione db");
@mysqli_set_charset($GLOBALS["dbcon"],'utf8');
if (mysqli_error($GLOBALS["dbcon"])) die("errore connessione db");

SQLite

$db = new SQLite3(dirname(__FILE__)."/DB/db.sqlite");
$db->busyTimeout(5000);
// WAL mode has better control over concurrency.
// Source: https://www.sqlite.org/wal.html
$db->exec('PRAGMA journal_mode = wal;');
$db->exec('PRAGMA synchronous=NORMAL;');

(last couple of rows are only useful if you plan to have some -little- write-concurrency, otherwise don’t use them)

Very important thing to know: if you are writing code for a local-running application, SQLite connections will not “time out” as there’s no server to wait for your input, just a file on the disk (or memory, even!)

Queries

MySQL

$results=mysqli_query($GLOBALS["dbcon"],$query);

SQLite

$db->exec($query);

when you don’t expect results, so for INSERT, UPDATE or DELETE

$results=$db->query($query);

when you expect multiple results, or several fields in a row

$value=$db->querySingle($query);

when you want returned a single-value result, for example when the query is something like SELECT timestamp FROM records WHERE id=$number LIMIT 1 (for this, with MySQL, you should parse the results with mysqli_fetch_array or similar, and then select the first value with [0])

Fetch results

MySQL

$row=mysqli_fetch_array($results);

when you want both associative and indexed arrays,

$row=mysqli_fetch_assoc($results);

when you only need associative arrays, and

$row=mysqli_fetch_row($results);

if you want only indexed arrays.

SQLite

In parallel with above:

$row=$results->fetchArray();

$row=$results->fetchArray(SQLITE3_ASSOC);

$row=$results->fetchArray(SQLITE3_NUM);

Speed considerations

If you don’t need associative arrays, you should always go for indexed arrays, since both in MySQL and SQLite they are fetched significantly faster; also, even if by very little, fetching only associative arrays is still faster then having both associative and indexed fetched together (and you’re not going to need those both anyway).

Escaping

MySQL

mysqli_real_escape_string($GLOBALS["dbcon"],$string);

SQLite

SQLite3::escapeString($string);

this function is not binary safe though at the time of writing (hasn’t been for a while from what I understand…)

Database functions

Just converting the PHP functions won’t be sufficient for most.

Think about time functions for examples, or DEFAULT values, or NULLing a NOT NULL timestamp column to have it automatically assigned to CURRENT_TIMESTAMP, these things are not present in SQLite.

MySQL

DEFAULT CURRENT_TIMESTAMP

NOW(), YEAR(), TIMEDIFF(), and another shitload of functions.

SQLite

DEFAULT (Datetime('now','localtime'))

Several variations on the strftime() functions, of which the Datetime() above is an example.

Again, go look at the official documentation, as what you see above is my own translation for my own purposes, and you will find both in official sources and in the vast world of StackOverflow a plethora of readings and interpretations.

Backup linux & raspbian important files on dropbox, automatically

I just create a repository on GitHub:

https://github.com/ephestione/bazidrop

It’s about a little tool I wrote, that I needed to have small, fresh backups made regularly of my raspberry pi’s operating system, that I could safely upload to the cloud (hence, encrypted).

I already use https://github.com/lzkelley/bkup_rpimage to make regular backups of my systems, but that creates 4GB .img files (even if sparse) that I cannot easily move around, and include all the contents of the sytem, even those files that can be restored with a new installation.

Instead, I needed to systematically backup my home folder, crontabs, apache files, mysql databases (maybe you have something else in mind), zip everything, and put it on dropbox.

This is the reason I wrote bazidrop.sh, which I think someone else could find useful.

Apache on Raspbian using older version of PHP

A few weeks ago I used this tutorial to install PHP 7.3 on my Raspbian.

Fast forward to today, I was bashing my head because after 2 hours of messing around, I could have exec() work from PHP CLI but not from the browser.

php -v from CLI correctly reported 7.3, so what gives?

After two hours I also decided to try phpinfo() from the browser, and guess what, it was still using 7.0!

So, after:

sudo a2dismod php7.0
sudo service apache2 restart

phpinfo() correctly reported 7.3 and I had my exec() working as intended.

Protect empty mountpoint from write access when drive is not mounted in Raspbian and Linux

I knew I had read something to this effect in the past since one of my raspi‘s had this thing where you couldn’t write to a mountpoint unless the drive/USB disk was actually mounted.

Since I spent some time in finding it again, here I add it to my blog as personal reference.

From this post:

Always set the attributes of mountpoint directories to immutable using chattr.

This is accomplished with chattr +i /mountpoint (with the mount unmounted).

This would error-out on new write activity and also protects the mount point in other situations.

VIPBEN thermal paste 2x30gr silicone compound, SUCKS

I had bought the two huge 30gr white and grey syringes of this “VIPBEN thermal paste greasy consistency silicone compound” on dealextreme ages ago, and only soldom used them to place coolers on my several raspberry pi’s, nothing more serious.

Recently my AMD FX 9590 started overheating even with an Enermax liquid cooler I installed (ok, it was more than 4 years ago…) with the bundled micro-sized thermal paste syringe.

I suspected that said thermal paste went bad, so I removed the heatsink block, and the paste was indeed hardened… and the micro-syringe that was in the package was actually all spent.

I had just read this tomshardware review where even toothpaste and denture glue were used (!!!) and the difference wasn’t so abismal with all the other proper products, so I told myself that I could as well even use the VIPBEN thermal paste on the dam FX9590…. right? RIGHT?

Well I used the pea size technique, first with white paste, then with grey one (yeah, I thoroughly cleaned all the surfaces with alcohol first!) and the overheating protection shut down the PC three times faster than what happened with the dried enermax paste.

Go figure, now I’m waiting for a syringe of Arctic MX-2 to arrive in the mail before I can use the PC again…

lighttpd cannot install because of libssl1.1, how to fix

My situation happened on a Raspberry with Raspbian Stretch installed, I had added PHP7.0 repository from sury.org quite a while back and when by chance today I went to open PiHole admin page from my laptop it simply wouldn’t load.

Google what’s up, I found out lighttpd was maybe the culprit, look around and noticed it wasn’t installed, and couldn’t be installed with the error:

lighttpd : Depends: libssl1.1 (>= 1.1.0) but it is not going to be installed

I then discovered that said libssl1.1 library off sury.org, coming together with the PHP distro, broke lighttpd.

I removed the repository source from /etc/apt/sources.list.d/php.list (your filename may vary) and issued a sudo apt-get update followed by a dist-upgrade but that didn’t solve anything.

I couldn’t find for the life of me a way to simply exchange the libssl1.1 package (which was kept from the sury.org repository even after removing its source) to the original repository version, and

sudo apt-get remove libsll1.1

wanted me to remove also ALL the packages that depended on this library. And they were a LOT.

Like hell I wanted to do that!

Or not?

Well I tried

sudo apt-get install --reinstall libssl1.1

hoping it would get rid of the messy libssl1.1 exchanging it with the official repository one, but that also gave an error:

Reinstallation of libssl1.1 is not possible, it cannot be downloaded.

Sooooo I was getting out of options and very annoyed.

I thought, why not letting the frigging apt remove all those packages? I can always install them later on… I hope…

EDIT: benosco down in the comments offered an easier solution, you might want to check it before going the hardcore way like I did.

So I copied the list of the packages that were to be removed off putty window and into notepad++, crossed my fingers and let apt remove everything.

After that was done during an extremely long and suffered delay, I had to do

sudo apt-get install <paste big-ass list of packages here>

In my case, it didn’t work out that simply at the first try, I got some CRC mismatch somewhere so they were not installed all together.

But I issued the command with less packages listed, beginning with apache2 php7.0 mysql-server transmission-cli samba python somethingIcannotreallyrecallnow, and then reissued the first command with all the list again to fill in the blanks.

It installed everything piece by piece, and lighttpd with it as well.

Since apt-get actually removes those packages without purging them (unless you ask it to purge libssl1.1 which isn’t a good idea) their settings are kept and reinstalling the packages will restore the system to the previous state.

At least it did for me, since the system came back to normal after a reboot.

Cura bug sets nozzle temperature to 0 instead of leaving control to printer

Straight to the chase: if you set the nozzle temperature to zero degrees in Cura, and the printer instead of immediately starting the print at the temperature YOU set on it, it sets the nozzle temperature to 0, then there is a workaround which is not straightforward at all.

As per this forum post:

  • Inside Cura, open Help menu and click on Show configuration folder
  • close Cura
  • open subfolder “definition_changes” inside the configuration folder explorer window that just popped up
  • open your printer’s *_settings.inst.cfg in notepad
  • identify [values] section and add
    machine_nozzle_temp_enabled = False
  • start Cura after having saved the modified file

Thank you Ultimaker for making this very intuitive for your users.

But seriously, thank you for the nice free software.

Lettura della fattura elettronica XML con PHP

Visualizzare e formattare in HTML una fattura elettronica con del codice PHP concettualmente è semplice, ma scrivere un apposito programma che fa un parsing del codice XML, e quindi disegnare un layout HTML in cui inserire i dati ottenuti, è uno spreco di tempo, se lo si fa solo per “guardare” la fattura senza doverla ulteriormente processare in modo automatizzato.

Per questo ringraziamo AssoSoftware che fornisce liberamente il suo foglio di stile XSL (in realtà ne esiste anche uno ufficiale rilasciato da Sogei, ma… vabè non devo aggiungere niente su Sogei e quindi se volete provatelo, e mi saprete dire nei commenti quale vi piace di più), il quale applicato all’XML originale seguendo questa guida restituisce una gradevole formattazione tabellare completa di annotazioni.

D’accordo lo ammetto questo articolo dai risultati di ricerca sembra essere fuorviante, perché io stesso cercando una routine che leggesse l’XML per restituire delle variabili in PHP, ho trovato il mio articolo pensando “vuoi vedere che l’ho già scritta io questa routine e me ne sono dimenticato?” ed invece no… quindi non essendoci altre risorse realmente utilizzabili per il PHP, credo proprio che ora mi ci metterò io a scrivere la routine…

Ho rimediato e l’ho appena scritta.

Questo è uno snippet del mio reale codice, per chi bazzica di PHP è comprensibile. E’ incompleto siccome mi serviva solo a recepire i dati che vanno inseriti nel registro acquisti, ma la logica dietro è abbastanza chiara per poterlo estendere a vostro piacimento.

$xml = simplexml_load_string($source);

$supplier = $xml->FatturaElettronicaHeader->CedentePrestatore->DatiAnagrafici->Anagrafica->Denominazione;
if (!$supplier) {
	$supplier = $xml->FatturaElettronicaHeader->CedentePrestatore->DatiAnagrafici->Anagrafica->Nome . " " . $xml->FatturaElettronicaHeader->CedentePrestatore->DatiAnagrafici->Anagrafica->Cognome;
}
$supplierpiva = $xml->FatturaElettronicaHeader->CedentePrestatore->DatiAnagrafici->IdFiscaleIVA->IdCodice;
$suppliervia = $xml->FatturaElettronicaHeader->CedentePrestatore->Sede->Indirizzo;
$suppliercivico = $xml->FatturaElettronicaHeader->CedentePrestatore->Sede->NumeroCivico;
$suppliercap = $xml->FatturaElettronicaHeader->CedentePrestatore->Sede->CAP;
$suppliercomune = $xml->FatturaElettronicaHeader->CedentePrestatore->Sede->Comune;
$supplierprovincia = $xml->FatturaElettronicaHeader->CedentePrestatore->Sede->Provincia;

$invoicedate = $xml->FatturaElettronicaBody->DatiGenerali->DatiGeneraliDocumento->Data;
$invoiceno = $xml->FatturaElettronicaBody->DatiGenerali->DatiGeneraliDocumento->Numero;
$invoicewhat = $xml->FatturaElettronicaBody->DatiGenerali->DatiGeneraliDocumento->Causale[0];
if (!$invoicewhat) $invoicewhat=$xml->FatturaElettronicaBody->DatiBeniServizi->DettaglioLinee[0]->Descrizione;


foreach ($xml->FatturaElettronicaBody->DatiBeniServizi->DatiRiepilogo as $riepilogo) {
	switch ($riepilogo->AliquotaIVA) {
		case "22.00":
			$imponibileord = $riepilogo->ImponibileImporto;
			$impostaord = $riepilogo->Imposta;
			break;
		case "10.00":
			$imponibilerid = $riepilogo->ImponibileImporto;
			$impostarid = $riepilogo->Imposta;
			break;
		case "4.00":
			$imponibilemin = $riepilogo->ImponibileImporto;
			$impostamin = $riepilogo->Imposta;
			break;
		case "0.00":
			$esente = $riepilogo->ImponibileImporto;
			break;
	}
}