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

Stream Letzte Weihnachten in ein Telefonat mit Python

Zuletzt aktualisiert am May 10, 2021

Lesedauer: 6 Minuten

Weihnachten ist eine teure Zeit, aber ich kann Ihnen helfen, ein Vermögen an Geschenken zu sparen, indem ich sicherstelle, dass niemand am Weihnachtstag mit Ihnen spricht, indem ich sie alle nach Whamhalla schicke!

Jedes Jahr nehmen meine Freunde und ich an dem Wettbewerb WhamageddonDie Regeln sind einfach:

  1. Das Ziel ist es, so lange wie möglich den Weihnachtsklassiker "Last Christmas" von WHAM nicht zu hören.

  2. Das Spiel beginnt am 1. Dezember und endet am 24. Dezember um Mitternacht.

  3. Es gilt nur die Originalfassung

  4. Du bist raus, sobald du das Lied erkennst

Jeder, der in der Weihnachtszeit schon einmal einen Fuß vor die Tür gesetzt oder das Radio eingeschaltet hat, kann bestätigen, wie schwierig es ist, "Last Christmas" zu vermeiden. Ab einer Sekunde nach Mitternacht am 1. Dezember ist es so, als ob es in jedem Geschäft auf Wiederholung läuft. Die meisten Menschen verhalten sich defensiv und meiden die Orte, an denen der Song am ehesten zu hören ist. Wir sind aber nicht die meisten Menschen, wir gehen in die Offensive.

Die Grundsätze des Krieges

Santa driving a tankSanta driving a tank

Die Initiative ergreifen, beibehalten und ausnutzen. Offensives Handeln ist der wirksamste und entscheidendste Weg, um ein klar definiertes gemeinsames Ziel zu erreichen. Offensivoperationen sind das Mittel, mit dem eine Streitkraft die Initiative ergreift und behält, während sie gleichzeitig ihre Handlungsfreiheit behält und entscheidende Ergebnisse erzielt. Dies gilt im Grunde für alle Ebenen des Krieges.

Wir können Whamageddon gewinnen, indem wir entweder den Song bis zum 25. Dezember meiden oder dafür sorgen, dass alle unsere Freunde ihn vor uns hören, so dass wir als letzte übrig bleiben. Wir müssen einen Weg finden, sie dazu zu bringen, den Song zu hören, ohne uns dabei selbst zu gefährden. Bitte kein friendly fire!

Der offensichtliche Angriffsvektor ist, ihnen einen Youtube-Link des Liedes zu schicken, aber Rickrolling hat dazu geführt, dass jeder zu vorsichtig ist, auf unbekannte Youtube-Links zu klicken. Wir brauchen einen Kanal, den sie nicht verdächtigen würden.

Den Feind zu einem Zeitpunkt, an einem Ort oder auf eine Weise angreifen, auf die er nicht vorbereitet ist. Überraschungsangriffe können das Gleichgewicht der Kampfkraft entscheidend verschieben. Durch das Streben nach Überraschung können Streitkräfte Erfolge erzielen, die in keinem Verhältnis zu den aufgewendeten Mitteln stehen. Überraschungen können sich auf das Tempo, die Größe der Truppe, die Richtung oder den Ort des Hauptangriffs und den Zeitpunkt beziehen. Täuschung kann die Wahrscheinlichkeit eines Überraschungserfolgs erhöhen.

Streaming von Audio in ein Telefongespräch

So viel wie Letzte Weihnachten in dieser Zeit des Jahres alles durchdringt, glaube ich nicht, dass ich jemals ans Telefon gegangen bin und es gehört habe. Niemand würde je vermuten, dass Wham! anruft.

Wir werden zwei APIs verwenden, um dies zu erreichen, die Nexmo Voice API um den ausgehenden Anruf zu tätigen und die mp3-Datei unseren Freunden vorzuspielen, und die Spotify-API um eine kurze Hörprobe von Last Christmas abzuspielen. Beide APIs haben Python-Wrapper, um die Arbeit mit ihnen zu erleichtern, und wir werden alles in eine nette CLI verpacken, damit wir unsere Freunde mit einem einzigen Befehl ausschalten können. Wenn wir fertig sind, wird es genau so aussehen:

Screencast of our Voice Application CLScreencast of our Voice Application CL

Erste Schritte

Sie benötigen ein wenig Erfahrung mit Python, um dies zum Laufen zu bringen, und mindestens die Version 3.6, da wir f-strings verwenden verwenden (weil sie fantastisch sind) sowie ein paar andere Dinge:

  1. Der Quellcode von GitHub

  2. Ein Spotify Account und Ihre eigene Spotify-Anwendung. Notieren Sie sich Ihre Kundennummer und Ihr Geheimnis, da Sie diese später zu Ihrem Konto hinzufügen müssen. .env später

  3. NgrokWenn Sie sich nicht sicher sind, wofür das ist, sollten Sie zuerst unser Ngrok-Tutorial lesen

Vonage API-Konto

Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.

Konfiguration

Es gibt eine Beispiel .env.example Datei im Repository, benennen Sie diese um in .env und beginnen Sie mit dem Eintragen der Werte. Für die NEXMO_APPLICATION_ID und NEXMO_PRIVATE_KEY müssen Sie eine neue Nexmo-Anwendung erstellenerstellen, dies können Sie über das Dashboard tun, denken Sie daran, die erzeugte private.key an einem sicheren Ort zu speichern und den Wert von NEXMO_PRIVATE_KEY auf den Speicherort Ihres Schlüssels.

Das Skript hat auch mehrere Abhängigkeiten; ich würde empfehlen sie über pipenv zu installierenzu installieren, aber Sie können auch eine andere Python-Abhängigkeitsverwaltung verwenden, wenn Sie dies bevorzugen.

pipenv install

Nachdem Sie Ihre Abhängigkeiten installiert haben, stellen Sie sicher, dass Sie Ihre virtuelle Umgebung aktiviert haben (dies wäre pipenv shell wenn Sie pipenv verwenden) und dass Ihre Shell die Umgebungsvariablen aus der .env Datei enthält. Schließlich müssen wir unser Skript dem öffentlichen Internet zugänglich machen, damit die Nexmo Webhooks es erreichen können. Dafür werden wir ngrok verwenden. Machen Sie sich keine Gedanken über die Konfiguration Ihres Tunnels, stellen Sie nur sicher, dass ngrok läuft.

ngrok http 80

Jetzt können Sie das Skript ausführen.

python pvpwham.py NUMBER

Es gibt einige zusätzliche Optionen, die Sie angeben können, führen Sie python pvpwham.py --help für weitere Informationen.

Schauen wir uns einige der interessanteren Teile des Codes genauer an.

Validierung der Nummer

Wir müssen sicherstellen, dass wir eine gültige Telefonnummer haben, ohne die nichts anderes funktioniert. Daher verbringen wir ein wenig Zeit damit, die Nummer zu validieren und in das richtige Format zu konvertieren. Allerdings schreiben die Leute Telefonnummern auf eine ganze Reihe von seltsamen und wunderbaren Arten. Wenn wir sie nicht validieren können, fordern wir sie auf, sich an die E.164 zu halten, und bieten sogar einen Link zu weiteren Informationen an, bevor sie gebeten werden, es erneut zu versuchen.

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"]

Eine MP3 des Liedes finden

Wir brauchen nicht den ganzen Song; ein paar Sekunden sollten ausreichen, um jemanden nach Whamhalla zu schicken. Die 30-Sekunden-Vorschau von Spotify ist ideal. Das Skript ist standardmäßig auf "Last Christmas" eingestellt, aber Sie können dies mit der 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]

Unser Server

Wir haben mehrere verschiedene Nexmo-Webhooks, mit denen wir umgehen müssen.

Antwort-URL - wird immer dann aufgerufen, wenn jemand den Anruf entgegennimmt und enthält eine Liste von Aktionen, die Nexmo ausführen soll. In unserem Fall werden wir Nexmo anweisen, den Anruf aufzuzeichnen, Text-to-Speech zu verwenden, um dem Benutzer eine kurze Warnung vorzulesen (dies ist standardmäßig deaktiviert, kann aber mit der Option --delay Option aktiviert werden)und schließlich unsere Spotify-Vorschau in den Anruf zu streamen

@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

Aufnahme-URL - Sobald der Benutzer aufgelegt hat und der Anruf beendet ist, übermittelt Nexmo die Aufnahmedaten an diesen Webhook, so dass wir die mp3-Datei herunterladen und die Bestürzung unseres Freundes anhören können, als er realisiert, was passiert ist.

NB: Wir zeichnen alles auf, auch den Ton, den wir in den Anruf gestreamt haben. Sie wollen sich ja nicht versehentlich auch noch nach Whamhalla schicken. Sie könnten das Skript so ändern, dass es geteilte Aufnahmen um dies zu verhindern.

@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)

Ereignis-URL - Diese Adresse dient nur zu Informationszwecken. Das Skript aktualisiert das Terminal mit dem neuesten Status, den es über den Event-Webhook erhält.

@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"

Hauswirtschaft

Wenn der Aufruf abgeschlossen ist, haben wir einen on_end_request Hook, der einige Aufräumarbeiten erledigt. Wir fahren unseren Cherrypy-Server herunter und beenden unseren Ngrok-Tunnel.

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)

Einige andere lustige Befehle

Wenn man kreativ genug ist, kann man auf Spotify ziemlich seltsame Dinge finden.

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'

Wie geht es weiter?

Richten Sie jede militärische Operation auf ein klar definiertes, entscheidendes und erreichbares Ziel aus. Das oberste militärische Ziel des Krieges ist die Zerstörung der Kampffähigkeit und des Kampfeswillens des Gegners.

Selbst wenn es Ihnen nicht gelingt, alle Ihre Freunde zu eliminieren, werden die Überlebenden hoffentlich jedes Mal, wenn das Telefon klingelt, so verängstigt sein, dass sie aufgeben. Sie gehen in ihr örtliches Einkaufszentrum, um dort auf das Unvermeidliche zu warten.

Nachdem Sie die Opposition zerschlagen haben, sehen Sie sich einige der weniger militanten Einsatzmöglichkeiten der Voice API an:

Teilen Sie:

https://a.storyblok.com/f/270183/150x150/a3d03a85fd/placeholder.svg
Aaron BassettVonage Ehemalige

Aaron war ein Entwickler-Befürworter bei Nexmo. Aaron ist ein erfahrener Software-Ingenieur und Möchtegern-Digitalkünstler, der häufig Dinge mit Code oder Elektronik entwickelt, manchmal auch beides. Wenn er an etwas Neuem arbeitet, erkennt man das in der Regel am Geruch von brennenden Bauteilen in der Luft.