https://a.storyblok.com/f/270183/956768/b4766b482f/streaming-last-christmas-into-a-phone-call.png

ラスト・クリスマスをPythonでストリーミング配信

最終更新日 May 10, 2021

所要時間:1 分

クリスマスは何かとお金のかかる時期だが、私はクリスマス当日に誰もあなたと口をきかないように、プレゼントをすべてWhamhallaに送ることで、プレゼント代を節約するお手伝いをすることができる!

毎年、友人と私は ワンマゲドンルールは簡単だ:

  1. 目的は、WHAMのクリスマス・クラシック、"Last Christmas "をできるだけ長く聴かないこと。

  2. 試合開始は12月1日、試合終了は12月24日午前0時。

  3. オリジナル版のみ適用

  4. 曲がわかった時点でアウト

クリスマスに外に出たり、ラジオをつけたりしたことのある人なら、『ラスト・クリスマス』を避けて通ることがいかに難しいかを証明できるだろう。12月1日の真夜中1秒過ぎから、まるでどの店でもこの曲を繰り返し流しているかのようだ。ほとんどの人は、この曲を流している可能性の高い場所を避けて、防衛的にゲームをする。しかし、私たちは普通の人ではない。

戦争の原則

Santa driving a tankSanta driving a tank

主導権を握り、保持し、利用する。攻撃行動は、明確に定義された共通の目的を達成するための最も効果的かつ決定的な方法である。攻撃的作戦とは、軍事力が行動の自由を維持しながら主導権を握り、保持し、決定的な成果を達成する手段である。これは基本的に、戦争のあらゆるレベルにわたって当てはまる。

ワンマゲドンに勝つには、12月25日までこの曲を聴かないようにするか、友達全員に先に聴かせ、最後の一人になるしかない。仲間を騙して曲を聴かせ、同時に自分も曲にさらされないようにする方法を見つけなければならない。くれぐれもフレンドリー・ファイアは起こさないように!

明白な攻撃ベクトルは、彼らに曲のYoutubeリンクを送ることだが、Rickrollingによって、誰もが未知のYoutubeリンクをクリックすることを警戒しすぎている。彼らが疑わないチャンネルが必要だ。

敵が準備していない時間や場所、方法で攻撃すること。奇襲は戦闘力の均衡を決定的に変えることができる。奇襲を仕掛けることで、部隊は費やした努力に見合わない成功を収めることができる。奇襲は、テンポ、部隊の規模、主力の方向または場所、およびタイミングにおいて行うことができる。欺瞞は奇襲を達成する確率を助けることができる。

音声をストリーミングして通話

と同じくらい ラスト・クリスマスがこの時期すべてを支配しているように、私は電話に出てそれを聞いたことがないと思う。誰もワム!からの電話だとは思わないだろう。

そのために2つのAPIを使う、 Nexmo Voice APIを使って発信し、mp3を友達に聞かせる。 Spotify APIを使ってLast Christmasの短いサンプルを提供する。どちらのAPIもPythonラッパーで簡単に扱えるようになっている。完成するとこんな感じになる:

Screencast of our Voice Application CLScreencast of our Voice Application CL

はじめに

これを実行するにはPythonの経験が少し必要で、少なくともバージョン3.6が必要です。 (を使っているからだ。)を使います:

  1. ソースコード ソースコード(GitHubより

  2. Spotifyアカウントと自分の Spotifyアプリケーション.クライアントIDとシークレットをメモしておいてください。 .env後で

  3. ングロックこのチュートリアルが何のためにあるのかわからない場合は ngrokチュートリアルを読んでください。

構成

リポジトリに .env.exampleファイルがあるので、これを .envにリネームして、値を埋めてください。には NEXMO_APPLICATION_IDNEXMO_PRIVATE_KEYについては 新しいNexmoアプリケーションを作成する必要があります。ダッシュボードから行うことができます。 private.keyを安全な場所に保存し NEXMO_PRIVATE_KEYの値をキーの場所に設定することを忘れないでください。

また、このスクリプトにはいくつかの依存関係がある。 を使ってインストールすることをお勧めします。でインストールすることをお勧めしますが、お望みであれば他のPython依存性管理を使うこともできます。

pipenv install

依存関係をインストールしたら、仮想環境をアクティブにしてください。 (これは pipenv shellpipenvを使用している場合)ファイルにある環境変数がシェルに含まれていることを確認してください。 .envファイルにある環境変数がシェルに含まれていることを確認してください。最後に、NexmoのWebhookがスクリプトにアクセスできるように、スクリプトをインターネットに公開する必要があります。トンネルの設定は気にせず、ngrokが起動していることを確認してください。

ngrok http 80

これでスクリプトを実行する準備ができた。

python pvpwham.py NUMBER

指定できる追加オプションがいくつかあります。 python pvpwham.py --helpを実行してください。

コードの興味深い部分をもう少し詳しく見てみよう。

番号の検証

ターゲットとなる有効な電話番号を確保することは非常に重要です。そのため、電話番号の検証や正しいフォーマットへの変換に少し時間をかけています。しかし、電話番号はさまざまな奇妙で素晴らしい方法で表記されるため、有効性を確認できない場合は、E.164に固執するよう促し、再試行を求められる前に詳細情報へのリンクを提供することもあります。

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

曲のMP3を探す

曲全体は必要ない。誰かをワムハラに送るには数秒で十分だ。Spotifyの30秒間のプレビューが理想的だ。スクリプトのデフォルトは "Last Christmas "だが、オプションで設定できる。 --trackオプションで設定できる。

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]

サーバー

Nexmoのウェブフックにはいくつか種類があります。

応答URL - 誰かが電話に出るたびに呼び出され、Nexmoが実行するアクションのリストが含まれています。今回のケースでは、通話を録音し、音声合成を使ってユーザーに短い警告を読み上げるようNexmoに指示します。 (デフォルトでは無効になっていますが --delayオプションで有効にできます)。そして最後に、Spotifyのプレビューを通話にストリーミングします。

@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 - ユーザーが電話を切り、通話が完了すると、Nexmoはこのウェブフックに録音の詳細を入力します。

注:通話に流した音声を含め、すべてを録音しています。誤って自分自身をWhamhallaに送らないようにしてください。スクリプトを修正して 分割録音スクリプトを修正してください。

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

Events URL - これは純粋に情報提供のためのものです。スクリプトはevents webhook経由で受け取った最新のステータスでターミナルを更新します。

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

ハウスキーピング

呼び出しが完了するたびに on_end_requestフックがあります。Cherrypyサーバーをシャットダウンし、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)

その他の楽しいコマンド

Spotifyでは、工夫次第でかなり変わったものを見つけることができる。

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'

次はどうする?

すべての軍事作戦は、明確に定義された、決定的で達成可能な目的に向かって行う。戦争の究極の軍事目的は、敵の戦闘能力と戦意を破壊することである。

たとえ友人全員を消すことができなくても、うまくいけば生き残った者は電話が鳴るたびに不安に駆られ、降参することになるだろう。地元のショッピングセンターに入って、避けられない事態をじっと待つのだ。

反対派を打ち負かした後は、Voice APIのあまり過激でない使い方を見てみよう:

シェア:

https://a.storyblok.com/f/270183/150x150/a3d03a85fd/placeholder.svg
Aaron Bassettヴォネージの卒業生

アーロンはNexmoの開発者支持者だった。ベテランのソフトウェア・エンジニアであり、デジタル・アーティスト志望でもあるアーロンは、コードや電子機器、時にはその両方を使って何かを作っているところをよく見かける。彼が何か新しいことに取り組んでいるときは、空気中の部品が燃える匂いでわかるのが通例だ。