Come realizzare in PHP un bot per il controllo di accesso ad un gruppo Telegram

Ho raccolto in questo post una serie di appunti per la realizzazione di un bot di controllo di un gruppo Telegram, non è un vero e proprio tutorial, ma è sicuramente utile per ripercorrere il progetto.

Dicembre 9, 2022

Scenario

Un’organizzazzione, una società, ha necessità di automatizzare l’accesso ad un gruppo Telegram privato, limitando l’accesso ai soli utenti che hanno ad esempio pagato una quota d’iscrizione oppure che posseggono uno specifico NFT.

Si vuole quindi consentire l’accesso solo ad una ristretta cerchia di individui a cui possiamo applicare un meccanismo di controllo.

In questo scenario è possibile sfruttare le possibilità delle API per i bot di Telegram che consentono di interagire con gli utenti e con i gruppi Telegram tramite script e chiamate http effettuate con comandi cURL o, più semplicemente, in PHP tramite file_get_contents.

E’ necessario avere un server in grado di inviare le chiamate. Il server deve poter anche memorizzare le informazioni sugli utenti, per esempio utilizzando il database MySQL.

Indice

1. Creare il bot tramite Telegram utilizzando il BotFather

2. Configurare un Webhook verso il proprio server

3. Rispondere ad un messaggio dal webhook

4. Predisporre una tabella sul proprio database per tracciare gli utenti che entrano nel gruppo

5. Creare il gruppo e generare gli inviti per il gruppo 

6. Utilizzare il deeplink al bot per tracciare l’ingresso dell’utente

7. Alcune misure di sicurezza sull’accesso al Gruppo Telegram tramite bot

8. Predisporre un job che verifica periodicamente gli ingressi

1. Creare il bot tramite Telegram utilizzando il BotFather

Su Telegram potete cercare tra le Chat il BotFather, uno speciale bot di Telegram (avrà il simbolo degli account ufficiali) col quale potete creare un nuovo bot. Utilizzate il menu del bot per creare il vostro bot. Nella configurazione del bot dategli il permesso di utilizzare i gruppi. Sempre tramite il BotFather recuperate nella configurazione l’API token necessario per utilizzare il bot dal PHP.

2. Configurare un Webhook verso il proprio server

Chiamare la API di telegram con setWebhook per impostare il proprio webhook Il Webhook è l’url di un vostro file PHP (in questo esempio l’URL punta ad un file mybot.php ) che viene richiamato ogni volta che c’è un’interazione

col vostro bot.

Ad esempio se un utente invia un messaggio al vostro bot, il testo del messaggio viene inviato a anche al vostro web hook (con una serie di informazioni, tipo l’id della chat che permette di identificare l’utente).

Aggiungendo il bot ad un gruppo, come se fosse un utente, ogni messaggio scritto nel gruppo viene inviato anche al vostro webhook con l’informazione di chi è il mittente. In questo modo il vostro bot può rispondere ai messaggi che circolano sul gruppo.

Tutti i messaggi inviati da Telegram al nostro web hook sono chiamati update.

Quando si imposta il web hook con la chiamata alla API di Telegram, è opportuno inserire già la richiesta di voler gestire alcune informazioni sugli utenti:

$apiToken = "MY_TELEGRAM_API_TOKEN";
$webhookurl = "https://yoursite.com/mybot.php";
$response = file_get_contents(
   "https://api.telegram.org/bot". $apiToken."/setWebHook?url=".
   $webhookurl.
   "&allowed_updates=[".
   'chat_member',".
   'callback_query',".
   'message',".
   'chat_join_request']" ); 
print_r(json_decode($response));

chat_member, callback_query, message, chat_join_request, sono i nomi delle tipologie di update che vogliamo gestire col nostro web hook e, in questo caso, permettono di ricevere al web hook le informazioni sugli utenti delle chat, i messaggi e le richieste anche tramite i comandi del menu del bot (il menu di comandi del bot può essere creato sempre tramite il botFather).

Inoltre da Telegram, attraverso il BotFather dovrete disattivare il flag della privacy del vostro bot, altrimenti non é possibile ricevere i messaggi.

Predisponete nel web hook una funzione per tracciare su un file di log tutte le informazioni in ingresso, perché le chiamate avvengono lato server e non potrete vederne l’output nel browser.

function log_this($s) {
	$log_file_telegram = 'telegram_webhook_log.txt';
	file_put_contents($log_file_telegram, $s."\n" , FILE_APPEND | LOCK_EX);
}

E all’inizio del codice del vostro web hook registrate tutto quello che arriva:

$update = json_decode(file_get_contents("php://input"), TRUE);

log_this( "---- START AT ". date('Y-m-d H:i:s') ." -----");
log_this( print_r($_REQUEST, true) );
log_this( print_r($update, true) );
log_this( "---- ----");

Per testare il funzionamento aprite in Telegram una chat con il vostro bot.

All’inizio della chat dovrete premere il comando AVVIA (/start) su Telegram, questo ci tornerà utile in un secondo momento.

Ogni volta che inviate un messaggio al bot lo riceverete anche sul vostro web hook e lo ritroverete nel log.

---- 2022-12-02 00:13:11 ----
Array
(
)

Array
(
    [update_id] => 193342267
    [message] => Array
        (
            [message_id] => 12
            [from] => Array
                (
                    [id] => 1902767341
                    [is_bot] => 
                    [first_name] => Giulio
                    [last_name] => Pons
                    [language_code] => it
                )
            [chat] => Array
                (
                    [id] => 1902767341
                    [first_name] => Giulio
                    [last_name] => Pons
                    [type] => private
                )
            [date] => 1669936391
            [text] => /start
        )

)

3. Rispondere ad un messaggio dal webhook

Quando nel vostro file php mybot.php arriva un messaggio potrete quindi interagire:

$update = json_decode(file_get_contents("php://input"), TRUE);

if(isset($update["message"]["chat"]["id"])) {
    $chat_id = $update["message"]["chat"]["id"];
    $message = $update["message"]["text"];
    $data = [
      'chat_id' => $chat_id,
      'text' => $message == "/start" ? "Benvenuto!" : 'Ok!'
    ];
    $response = file_get_contents("https://api.telegram.org/bot$apiToken/sendMessage?" .http_build_query($data) );
    print_r(json_decode($response));
}

In questo esempio il bot risponde Benvenuto! al messaggio start e Ok a tutti gli altri messaggi (non un gran dialogo, certo).

4. Predisporre una tabella sul proprio database per tracciare gli utenti che entrano nel gruppo

Per gestire gli accessi al gruppo è necessario tenere traccia degli id degli utenti, quindi dobbiamo creare una tabella nel nostro database. Se il database è, ad esempio, quello di un sito dove ci sono già degli utenti avremo una tabella che chiamiamo gruppo_telegram con almeno questi campi: cod_user, cod_telegram_user.

Il primo codice identifica l’utente nel nostro database, mentre il secondo identifica il codice dell’utente su Telegram. Fate attenzione che su Telegram i codici numerici possono essere anche negativi!

5. Creare il gruppo e generare gli inviti per il gruppo

Create un gruppo su Telegram, aggiungete il vostro bot al gruppo e assegnategli i diritti di amministratore. Il gruppo deve essere privato così che non sia rintracciabile tramite la funzione Cerca di Telegram.

Il metodo più semplice per invitare gli utenti a partecipare al Gruppo è generare un invito.

Gli inviti di Telegram sono molto comodi e sono di fatto semplicemente dei link che conducono l’utente al nostro Gruppo, tipo questo https://t.me/+TxIFkgXuIapiATM0. Il link ti porta al gruppo dove l’utente accetta l’ingresso. Per i link di invito si può anche stabilire quante volte possono essere utilizzati e quanto tempo durano.

I link di invito possono essere generati dal bot con una chiamata alla api con il comando createChatInviteLink, inoltre conviene salvarli nella tabella gruppo_telegram (aggiungiamo quindi un campo invito_al_gruppo) per tenere traccia dei link inviati agli utenti.

L’idea di base, quindi, dovrebbe essere generare un link per ogni utente (tramite API) salvarlo nella nostra tabella e quando l’utente entra recuperare tramite il nostro web hook l’informazione di che link ha cliccato per cercarlo sulla tabelle e tracciare l’ingresso dell’utente nel gruppo.

Tuttavia non é possibile in alcun modo a recuperare il codice dell’invito utilizzato per entrare, quindi non c’è un modo certo di collegare l’utente appena entrato con un record dei miei utenti.

Non so se questa limitazione della API bot di Telegram sia voluta, o se sia un bug, o se sia possibile e abbia sbagliato io, per fortuna però è possibile aggirare il problema, utilizzando la funzionalità dei deep link del comando /start del bot.

6. Utilizzare il deeplink al bot per tracciare l’ingresso dell’utente

Per tracciare un utente che entra nel Gruppo utilizziamo il comando /start. E’ possibile, infatti, generare dei link alla chat del nostro bot: https://t.me/giuliopons_bot e, inoltre, è possibile aggiungere un parametro start: https://t.me/giuliopons_bot?start=123 che viene inoltrato al nostro webhook.

In questo modo possiamo creare dei link parametrizzati al bot, uno per ogni utente che vogliamo fare entrare nel gruppo. Salviamo anche questi link nella tabella gruppo_telegram, aggiungendo un campo link_chat_bot. Quando l’utente clicca sul link arriva nella chat col bot (non ancora nel gruppo). Quando clicca su AVVIA, al nostro web hook arriva il testo del messaggio col comando /start e il parametro 123 che abbiamo aggiunto specificatamente per quell’utente.

Array
(
    [update_id] => 1945834392
    [message] => Array
        (
            [message_id] => 15
            [from] => Array
                (
                    [id] => 1138917315
                    [is_bot] => 
                    [first_name] => Ciro
                    [last_name] => Scotto
                    [username] => ciruz
                    [language_code] => it
                )
            [chat] => Array
                (
                    [id] => 1138917315
                    [first_name] => Ciro
                    [last_name] => Scotto
                    [username] => ciruz
                    [type] => private
                )
            [date] => 1669948753
            [text] => /start 123
        )

)

Il bot, a questo punto può dal webhook rispondere alla chat, generando un link invite_link per il Gruppo e fornendolo come risposta alla chat. Nello stesso momento il web hook può salvare sulla tabella gruppo_telegram che ha creato un link per quell’utente Telegram.

Quando l’utente clicca sul link di invito al Gruppo che ha ricevuto come risposta, al webhook riceveremo una update relativa all’ingresso di questo nuovo membro nel Gruppo e potremo, grazie al suo id telegram, sapere se è l’utente che ha cliccato sul link di invito generato.

In pratica il bot, tramite il deeplink, traccia l’utente sul nostro db, associa cioè l’id utente del nostro db all’id utente di Telegram e genera un link di invito che può essere usato solo una volta.

Quando l’utente clicca su quell’invito, al web hook torna l’informazione dell’ingresso nel gruppo e posso tracciare sulla mia tabella che l’utente è entrato.

7. Alcune misure di sicurezza sull’accesso al Gruppo Telegram tramite bot

Abbiamo visto che l’accesso al gruppo, quindi, avviene con un invito generato dal bot e mandato all’utente nella chat che l’utente ha col bot.

Se l’utente non usa quel link, ma lo invia ad un altro utente Telegram, può accadere che un utente diverso riuscirà ad accedere al gruppo (perché il link generato dal bot è valido per un accesso e non è vincolato all’utente a cui l’abbiamo mandato).

Per evitare che questo accada all’interno del web hook mybot.php quando un nuovo utente entra posso verificare nell’update che riceve il web hook se tra i new_chat_member compare l’id che corrisponde ad un utente che ha ricevuto un invito, se non è tra questi posso automaticamente bannarlo.

Oppure posso anche decidere di registrare su una nuova tabella gli id Telegram e i dati di nome, cognome e username dell’utente che è entrato con un invito “passato” da un altro utente, oppure posso fare entrambe le cose, segnarmi l’utente e bannarlo.

Nel sistema descritto fino a questo punto bisogna notare anche che il codice passato tramite deep link all’AVVIO della conversazione col bot (al punto 6) era semplicemente un id numerico passato in chiaro (123). Ovviamente questa pratica non è sicura e conviene generare una stringa più complessa che contiene al suo interno un messaggio di verifica crittografato per evitare che un utente malevolo possa in qualche modo generare indirizzi di accesso alla chat col bot che contengano un parametro allo start che possa in qualche modo ottenere un nuovo invito corretto.

Supponendo che l’id del database locale sia 123, invece di inviare semplicemente 123 si può costruire una stringa così:

$id = 123; // id utente del mio database
$cod = $id . "-". md5("CHIAVE-SEGRETA-CODIFICA" . $id); 

Il codice $cod così ottenuto sarà qualcosa di questo tipo: 123-e47057d970badb3a00886d6d0d22bc6e.

Quando nel web hook ricevete il parametro dovete verificare che non sia stato manomesso e quindi dovete splittare la stringa con lo “-“, e rigenerare la stringa criptata. Se è identica a quella arrivata nello start, allora il codice 123 è legittimo.

$KEY_PER_ENTRARE = "CHIAVE-SEGRETA-CODIFICA";
if(preg_match("#/start #",$update["message"]["text"])){
	$temp = str_replace("/start ","",$update["message"]["text"]);
	$ar = explode("-",$temp);
	if(isset($ar[1]) && $ar[1] == md5($ar[0] . "-". $KEY_PER_ENTRARE) ) {
		// la richiesta è ok
		// e $ar[0] contiene un id utente legittimo
	}
}

8. Predisporre un job che verifica periodicamente gli ingressi

Gli utenti presenti nel Gruppo sono anche utenti del mio sito, ad esempio sono utenti che hanno acquistato un servizio di assistenza per un certo periodo di tempo, oppure hanno comprato un NFT, e in generale hanno un titolo per stare dentro al gruppo, può succedere che questo titolo scada o che le condizioni finiscano. Un caso estremo potrebbe essere che l’utente si cancelli da solo dal sistema.

Pertanto è necessario creare un job che giri periodicamente, ad esempio ogni notte, e verifichi le condizioni degli utenti salvati nella tabella gruppo_telegram.

Tutti quegli utenti che sono presenti nella tabella ma che non hanno più titolo di appartenere al gruppo, vanno bannati utilizzando il comando banChatMember della api di Telegram bot.


Author

PHP expert. Wordpress plugin and theme developer. Father, Maker, Arduino and ESP8266 enthusiast.

Recommended

PHP bot to get wikipedia definitions

Wikipedia, the collaborative and multilingual encyclopedia project, has a lot of usefull terms defined in its database, you can find…

Agosto 29, 2010

Hacking del sistema di irrigazione Claber

Modificare il sistema di irrigazione della Claber per utilizzarlo con Arduino e una app realizzata ad hoc

Novembre 1, 2019

Orari trenord, corri solo quando ce n’è bisogno

Hai presente quando corri in stazione per prendere il treno al volo e, quando arrivi, ti accorgi che il treno ha 12 minuti di ritardo?

Marzo 30, 2017

Customize your site icon for new WordPress 4.4 embeds

With the new WordPress 4.4 embed feature the posts of your blog becomes embeddable in other WordPress sites, like you do…

Dicembre 11, 2015

Twitter counter no longer works

Since 20 of November 2015 the twitter button has changed. This is the new Twitter sharing button, as you can…

Novembre 23, 2015

Get instagram data without official api in PHP

Instagram has an official API to interact with its database of images and users. If you have enough time to…

Dicembre 3, 2013