https://d226lax1qjow5r.cloudfront.net/blog/blogposts/whamageddon-streaming-last-christmas-into-a-phone-call-dr/Streaming-Last-Christmas-into-a-Phone-Call.png

Streamer Last Christmas en un appel téléphonique avec Python

Publié le May 10, 2021

Temps de lecture : 6 minutes

Noël est une période coûteuse, mais je peux vous aider à économiser une fortune sur les cadeaux en m'assurant que personne ne vous parle le jour de Noël en les envoyant tous à Whamhalla !

Chaque année, mes amis et moi participons à la compétition Whamageddonles règles sont simples :

  1. L'objectif est de passer le plus de temps possible sans entendre le classique de Noël de WHAM, "Last Christmas".

  2. Le jeu commence le 1er décembre et se termine le 24 décembre à minuit.

  3. Seule la version originale s'applique

  4. Vous êtes exclu dès que vous reconnaissez la chanson.

Quiconque a déjà mis le pied dehors ou allumé la radio pendant la période de Noël peut attester qu'il est difficile d'éviter "Last Christmas". À partir d'une seconde après minuit le 1er décembre, c'est comme si tous les magasins la répétaient. La plupart des gens jouent le jeu de manière défensive, en évitant les endroits où la chanson est la plus susceptible d'être diffusée. Mais nous ne sommes pas comme les autres, nous allons passer à l'attaque.

Les principes de la guerre

Santa driving a tankSanta driving a tank

Saisir, conserver et exploiter l'initiative. L'action offensive est le moyen le plus efficace et le plus décisif pour atteindre un objectif commun clairement défini. Les opérations offensives sont les moyens par lesquels une force militaire saisit et conserve l'initiative tout en maintenant sa liberté d'action et en obtenant des résultats décisifs. Cela est fondamentalement vrai à tous les niveaux de la guerre.

Nous pouvons gagner le Whamageddon soit en évitant la chanson jusqu'au 25 décembre, soit en faisant en sorte que tous nos amis l'entendent avant nous, de sorte que nous soyons la dernière personne à rester debout. Nous devons trouver un moyen de leur faire entendre la chanson sans nous y exposer en même temps. Pas d'incident de tir ami, s'il vous plaît !

Le vecteur d'attaque évident est de leur envoyer un lien Youtube de la chanson, mais le Rickrolling a rendu tout le monde trop méfiant pour cliquer sur des liens Youtube inconnus. Nous avons besoin d'un canal qu'ils ne soupçonneraient pas.

Frapper l'ennemi à un moment, en un lieu ou d'une manière à laquelle il n'est pas préparé. La surprise peut modifier de manière décisive l'équilibre de la puissance de combat. En recherchant la surprise, les forces peuvent obtenir des succès bien disproportionnés par rapport à l'effort déployé. La surprise peut porter sur le rythme, la taille de la force, la direction ou l'emplacement de l'effort principal et le moment choisi. La tromperie peut augmenter la probabilité d'obtenir une surprise.

Streaming audio lors d'un appel téléphonique

Autant que Dernier Noël imprègne tout à cette époque de l'année, je ne pense pas avoir déjà répondu au téléphone et l'avoir entendu. Personne ne soupçonnerait Wham ! d'appeler.

Nous allons utiliser deux API pour y parvenir, l'API Voice de Nexmo Nexmo pour passer l'appel sortant et diffuser le mp3 à nos amis, et l'API Spotify Spotify pour fournir un court extrait de Last Christmas. Les deux API ont des wrappers Python pour les rendre plus faciles à utiliser, et nous allons envelopper le tout dans un CLI sympa pour que nous puissions éliminer nos amis avec une seule commande. Lorsque nous aurons terminé, cela ressemblera à ceci :

Screencast of our Voice Application CLScreencast of our Voice Application CL

Pour commencer

Vous aurez besoin d'un peu d'expérience avec Python pour faire fonctionner cette application, et au moins la version 3.6 car nous utilisons des chaînes f (parce qu'elles sont géniales) ainsi que quelques autres choses :

  1. Le code code source de GitHub

  2. Un Account Spotify et votre propre application Spotify. Notez votre identifiant client et votre secret car vous devrez les ajouter à votre compte Spotify. .env plus tard

  3. Ngrok; si vous n'êtes pas sûr de savoir à quoi cela sert, vous devriez lire d'abord notre tutoriel ngrok

Vonage API Account

To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.

Configuration

Il y a un fichier d'exemple .env.example dans le référentiel, renommez-le en .env et commencez à remplir les valeurs. Pour les valeurs NEXMO_APPLICATION_ID et NEXMO_PRIVATE_KEY vous devez créer une nouvelle Applications Nexmovous pouvez le faire via le tableau de bord, n'oubliez pas de sauvegarder l'application générée dans un endroit sûr et définissez la valeur de l'application dans le tableau de bord. private.key généré dans un endroit sûr et de définir la valeur de NEXMO_PRIVATE_KEY à l'emplacement de votre clé.

Le script a également plusieurs dépendances ; je recommande de les de les installer via pipenvmais vous pouvez utiliser un autre gestionnaire de dépendances Python si vous préférez.

pipenv install

Une fois que vous avez installé vos dépendances, assurez-vous d'avoir activé votre environnement virtuel (ce serait pipenv shell si vous utilisez pipenv) et que votre shell contient les variables environnementales du fichier .env dans votre shell. Enfin, nous devons être en mesure d'exposer notre script à l'internet public afin que les webhooks Nexmo puissent l'atteindre. Pour cela nous allons utiliser ngrok, ne vous souciez pas de configurer votre tunnel, assurez-vous simplement que ngrok est lancé.

ngrok http 80

Vous êtes maintenant prêt à exécuter le script.

python pvpwham.py NUMBER

Il existe quelques options supplémentaires que vous pouvez spécifier. python pvpwham.py --help pour plus d'informations.

Examinons plus en détail certaines des parties les plus intéressantes du code.

Validation du Numbers

Il est essentiel de s'assurer que nous disposons d'un numéro de téléphone valide à cibler, car rien d'autre ne fonctionne sans lui. Nous passons donc un peu de temps à valider le numéro et à le convertir dans le bon format. Cependant, les gens écrivent les numéros de téléphone de manière très bizarre et merveilleuse. Si nous ne pouvons pas le valider, nous les invitons à s'en tenir à E.164, et nous leur fournissons même un lien vers plus d'informations avant qu'ils ne soient invités à réessayer.

while e164_number == False:
    insight_response = nexmo_client.get_basic_number_insight(number=number)
    if insight_response["status"] == 3:
        insight_response = nexmo_client.get_basic_number_insight(
            number=number, country=country
        )

    if insight_response["status"] != 0:
        click.clear()
        click.secho(intro, bg="magenta", fg="green")
        click.secho(
            f"{number} does not appear to be a valid telephone number",
            bg="magenta",
            fg="white",
        )
        click.secho(
            "It might work if you enter it in the E.164 format",
            bg="magenta",
            fg="white",
        )

        if click.confirm(wtf_e164_message):
            click.launch(
                "https://developer.nexmo.com/concepts/guides/glossary#e-164-format"
            )

        if click.confirm(try_number_again_message):
            number = click.prompt("Ok, give it to me in E.164 this time")
        else:
            raise click.BadArgumentUsage(
                click.style(
                    f"{number} does not appear to be a valid number. Try entering it in the E.164 format",
                    bg="red",
                    fg="white",
                    bold=True,
                )
            )
    else:
        e164_number = insight_response["international_format_number"]

Trouver un MP3 de la chanson

Nous n'avons pas besoin de la chanson entière ; quelques secondes devraient suffire à envoyer quelqu'un à Whamhalla. L'aperçu de 30 secondes de Spotify est idéal. Le script utilise par défaut "Last Christmas", mais vous pouvez le configurer en utilisant l'option --track option.

spotify_client_credentials_manager = SpotifyClientCredentials(
    client_id=os.environ["SPOTIFY_CLIENT_ID"],
    client_secret=os.environ["SPOTIFY_CLIENT_SECRET"],
)
spotify_client = spotipy.Spotify(
    client_credentials_manager=spotify_client_credentials_manager
)
tracks = spotify_client.search(track, limit=1, type="track")

if len(tracks["tracks"]["items"]) == 0:
    raise click.BadOptionUsage(
        track,
        click.style(f"Can't find track: {track}", bg="red", fg="white", bold=True),
    )

track = tracks["tracks"]["items"][0]

Notre serveur

Nous avons plusieurs webhooks Nexmo différents à gérer.

URL de réponse - elle est appelée lorsque quelqu'un répond à l'appel et contient une liste d'actions à exécuter par Nexmo. Dans notre cas, nous allons dire à Nexmo d'enregistrer l'appel, d'utiliser la synthèse vocale pour lire un court avertissement à l'utilisateur (cette fonction est désactivée par défaut mais peut être activée à l'aide de l'option --delay )et enfin de diffuser notre aperçu Spotify dans l'appel.

@cherrypy.tools.json_out()
def index(self, **params):
    ncco_file = [
        {
            "action": "record",
            "eventUrl": [f"{self.ngrok_tunnel['public_url']}/recording"],
        }
    ]

    if delay == "short":
        ncco_file.append({"action": "talk", "text": "whamageddon"})
    elif delay == "long":
        ncco_file.append(
            {
                "action": "talk",
                "text": "hang up your phone or prepare to enter Whamhalla",
            }
        )

    ncco_file.append(
        {"action": "stream", "streamUrl": [f"{self.preview_url}?t=mp3"]}
    )

    return ncco_file

URL d'enregistrement - Une fois que l'utilisateur a raccroché le téléphone et que l'appel est terminé, Nexmo envoie à ce webhook les détails de l'enregistrement afin que nous puissions télécharger le mp3 et écouter le désarroi de notre ami lorsqu'il réalise ce qui s'est passé.

NB : Nous enregistrons tout, y compris l'audio que nous avons diffusé dans l'appel. Vous ne voulez pas non plus vous envoyer accidentellement à Whamhalla. Vous pouvez modifier le script pour utiliser enregistrements fractionnés pour éviter cela.

@cherrypy.expose
def fetch_recording():
    data = cherrypy.request.json
    click.secho("## Fetching Call Recording", bg="green", fg="black", bold=True)
    recording_response = nexmo_client.get_recording(data["recording_url"])

    recordingfile = f"/tmp/{data['recording_uuid']}.mp3"
    os.makedirs(os.path.dirname(recordingfile), exist_ok=True)

    with open(recordingfile, "wb") as f:
        f.write(recording_response)

    click.secho("## Call Recording Saved", bg="green", fg="black", bold=True)
    if click.confirm(
        click.style(
            "## Listen to your friend's anguish now?", bg="magenta", fg="white"
        )
    ):
        click.launch(recordingfile)

URL des événements - Cette URL est purement informative. Le script met à jour le terminal avec le dernier statut qu'il reçoit via le webhook des événements.

@cherrypy.expose
@cherrypy.tools.json_in()
def events(self):
    data = cherrypy.request.json
    click.secho(
        f"## Status: {data['status']}", bg="blue", fg="white", bold=True
    )
    return "OK"

Entretien ménager

Lorsque l'appel est terminé, nous disposons d'un on_end_request qui fait un peu de rangement. Nous fermons notre serveur Cherrypy et tuons notre tunnel Ngrok.

def quit_cherry():
    cherrypy.engine.exit()
    click.secho("## Exiting NCCO Server", bg="blue", fg="white", bold=True)
    requests.delete("http://localhost:4040/api/tunnels/pvpwham")
    click.secho("## Closing tunnel", bg="blue", fg="white", bold=True)

Quelques autres commandes amusantes

Vous pouvez trouver des choses assez étranges sur Spotify si vous êtes assez créatif.

python pvpwham.py NUMBER --track='Rick Astley Never gonna give you up'

python pvpwham.py NUMBER --track='Sound Effects Animals Chimps, Apes'

python pvpwham.py NUMBER --track='Halloween Sound effects machine ghostly whispers'

Quelle est la prochaine étape ?

Diriger chaque opération militaire vers un objectif clairement défini, décisif et réalisable. Le but militaire ultime de la guerre est la destruction de la capacité et de la volonté de combattre de l'ennemi.

Même si vous ne parvenez pas à éliminer tous vos amis, il faut espérer que les survivants seront tellement angoissés chaque fois que le téléphone sonnera qu'ils se rendront. Ils iront s'asseoir dans leur centre commercial pour attendre l'inévitable.

Puis, après avoir écrasé l'opposition, jetez un coup d'œil à certaines utilisations moins militantes de l'API Voice :

Partager:

https://a.storyblok.com/f/270183/150x150/a3d03a85fd/placeholder.svg
Aaron BassettAnciens de Vonage

Aaron était défenseur des développeurs chez Nexmo. Ingénieur logiciel chevronné et artiste numérique en herbe, Aaron est souvent amené à créer des choses avec du code ou de l'électronique, parfois les deux. On peut généralement savoir qu'il travaille sur quelque chose de nouveau grâce à l'odeur de composants brûlés qui flotte dans l'air.