---
# System prepended metadata

title: Interacció de programes a través de la web.
tags: [' programació', asix]

---

---
title: Interacció de programes a través de la web.
tags: ASIX, programació
---

<div style="width: 30%; margin-left: auto;">
    
![](https://hackmd.io/_uploads/HJiR4eGJT.png)    
    
</div>


<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Llicència de Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />Aquesta obra està subjecta a una llicència de <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Reconeixement-CompartirIgual 4.0 Internacional de Creative Commons</a>

![imagen](https://i.pinimg.com/originals/a3/a2/de/a3a2de1c8627d7ce468635608aa25ae3.jpg "Copiada de: https://i.pinimg.com/originals/a3/a2/de/a3a2de1c8627d7ce468635608aa25ae3.jpg")


# Interacció de programes a través de la web.
Aquesta activitat neix d'una consulta d'un company vostre que necessita poder controlar un programa a distància --ell ja sap qui és--. Jo en realitat no sé de manera precisa quina és l'aplicació que vol realitzar, però em va comentar que estava buscant una manera de controlar una aplicació a partir de missatges enviats desde una altra màquina, o un mòbil o qualsevol altre dispositiu que no es trobi a la mateixa xarxa local; aquests missatges poden ser emails, missatges de Discord, de Whatsapp o similars.

## Premises de partida.
Com us acabo de comentar, no conec les necessitats concretes de l'aplicació, així que sentaré unes premises per a començar a dissenyar la nostra solució:

1. El nostre programa no s'executa en un servidor amb IP pública. I possiblement la nostra app correrà en un portàtil que pot canviar de xarxa sovint.
2. No volem modificar les regles NAT del nostre router (No volem obrir ports específics que apuntinn a la nostra màquina).
3. No volem gastar ni un sol euro en llogar servidors externs.
4. Ens és igual quin servei de missatgeria utilitzar.
5. Els sistema de comunicació ha de ser bidireccional, és a dir, hem de poder enviar ordres a la nostra aplicació i hem de poder rebre resultats.
6. No sabem si la nostra aplicació necessita realitzar accions sobre una màquina en particular.

## Disseny de la aplicació.
### 1. Tipus d'aplicació.
Aquesta és una decissió molt important que vindrà determinada per les especificacions i les necessitats particulars del nostre __client__.
Habitualment, quan pensem en una aplicació que ha de respondre a través d'internet pensem de manera natural en crear una API ja sigui de tipus __RESTfull__ o __GraphQL__ (les API __SOAP__ actualment han quedat una mica obsoletes). Però aquest tipus d'aplicacions necessiten una IP pública o un nom de domini al que poder fer referència i les premises de partida #1 i #2 no ho fan possible. Enlloc d'implementar una API, farem una aplicació que consulti de manera regular un servei de missatgeria. He desestimat whatsapp perquè obliga a tenir oberta una sessió del nostre whatsapp a la màquina on volem executar el programa i no em sembla pràctic ni segur, Discord i Telegram són bones opcions, totes dues ofereixen mecanismes per a identificarnos de manera segura sense haver de tenir oberta una sessió a la màquina on està corrent la nostra app, però el mètode de missatgeria triat per la seva senzillesa d'implementació ha estat https://ntfy.sh. 
Llavors la nostra aplicació haurà de suscriure's al tòpic que volguem utilitzar per a comunicar-nos, llegir els missatges que li arrivin i actuar en consequència.
Com es pot veure al seguent snippet, suscriure's és molt fàcil.

```python=
def main():
    resp = requests.get(f"https://ntfy.sh/{TOPIC}/raw", stream=True)
    for line in resp.iter_lines():
        if line:
            procesa_event(line)
```

El bucle for que hi veiem a la línia 3 forma un bucle sense fi que executa la funció procesa_event cada cop que es rep un missatge al tòpic suscrit.

Una possible implementació de la funció _procesa_event(missatge)_, podria ésser la seguent:

```python=
def procesa_event(missatge):
    arguments = missatge.decode('utf-8').split(" ")
    if arguments[0] =="/saluda":
        saluda(arguments[1])
    elif arguments[0] == "/A10":
        surt()
    elif arguments[0] == "/help":
        ajuda()
```
També pot ser útil implementar una funció que ens permeti enviar missatges de tornada a __ntfy.sh__ mitjançant peticions POST al servidor web.
 
```python=
def envia_missatge(missatge):
    requests.post("https://ntfy.sh/" + TOPIC,
    data = missatge.encode('utf-8'),
    headers = {
        "Title": TITOL.encode('utf-8'),
        "Tags": "exploding_head",
        "clear": "false"
    })
```

### 2. Codi font de la nostra app.
Així quedaria el nostre script complert:

```python=
#!/usr/bin/python3


"""
Programa que es suscriu a un tòpic de ntfy.sh i processa els events que rep.
"""

import requests

TOPIC = "ITICBCN_ASIX_1853"
TITOL = "Missatge automàtic enviat amb Python"

def saluda(nom):
    print(f">> Hola, {nom}!")
    envia_missatge(f"He saludat a {nom} correctament!")


def envia_missatge(missatge):
    requests.post("https://ntfy.sh/" + TOPIC,
    data = missatge.encode('utf-8'),
    headers = {
        "Title": TITOL.encode('utf-8'),
        "Tags": "exploding_head",
        "clear": "false"
    })


def surt():
    envia_missatge("Sortint de l'aplicació...")
    print(f">> Sayonara, baby!")
    exit(0)


def ajuda():
    missatge = f">> Comandes disponibles:\n"
    missatge += f">> /saluda <nom>\n"
    missatge += f">> /A10\n"
    missatge += f">> /help\n"
    envia_missatge(missatge)
    print(">> Han demanat ajuda.")


def procesa_event(missatge):
    arguments = missatge.decode('utf-8').split(" ")
    if arguments[0] =="/saluda":
        saluda(arguments[1])
    elif arguments[0] == "/A10":
        surt()
    elif arguments[0] == "/help":
        ajuda()


def main():
    resp = requests.get(f"https://ntfy.sh/{TOPIC}/raw", stream=True)
    for line in resp.iter_lines():
        if line:
            procesa_event(line)

if __name__ == "__main__":
    main()
```

