Kuzunoha-NEのブログ

プログラミングなどの勉強をしてます

Ngrokを使ってみようの巻

Ngrokを使ってみようの巻

こんばんは、葛の葉です。

さて、Ngrokというものがありまして、簡単なWebサーバーなら簡単に立ち上げることが出来るすぐれものがあるんです。

ngrok.com

アカウントを入るように求められるのでGoogleとかのアカウントで好きに入ってください。

そしたらダウンロードが出来るのでインストールします。スゴイ。ここまでが準備。


flaskを公開してみようぜ

いつものhelloworldのプログラム(12行)

import flask

app = flask.Flask(__name__)


@app.route('/')
def index():
    return "Hello, World!"


if __name__ == '__main__':
    app.run(debug=True, port=8080)

で起動するとlocalhost:8080で繋がるはずです。127.0.0.1:8080といったほうがいいかな?

f:id:Kuzunoha-NE:20180831192048j:plain

いつもの画面が出てきた。


ngrokを落とすとngrok.exeというものが出てきて、起動するとコマンドプロンプトが立ち上がる。

f:id:Kuzunoha-NE:20180831192937j:plain

このコマンドプロンプトの画面で以下のコマンドを打ちましょう。

ngrok http 8080

なお、8080はポート番号です。flaskアプリのport=8080と合致させましょう。そうしたら、下図の画面に移ります。

f:id:Kuzunoha-NE:20180831192957j:plain

なんかよくわからんけど、https://f1fba471.ngrok.ioに繋がるようだぜ。もちろん、http://f1fba471.ngrok.ioもOK。

f:id:Kuzunoha-NE:20180831192644j:plain

これでスマホでも見れるはず。

みられたねぇ!

コマンドプロンプト画面でctrl + c を押せばやめられます。

以上!!

python clova_sdkで開発しているんですよ -その1-

python clova_sdkで開発しているんですよ -その1-

久しく、お世話になっております。葛の葉です。

最近はclovaの開発を行っております。clovaの開発の記事はまだまだネット上には少ないので、今は公式のドキュメントを読み解きながらプログラミングをするという状況での開発を行っております。

前回の記事では、pythonSDKが来たことと、そのpipでのインストール方法、そして、SDKのドキュメントのリンクなどを記載しました。

kuzunoha-ne.hateblo.jp

現在、clovaの開発用SDKのある言語は以下のようになるようですね。

↓引用元

clova-developers.line.me

SDKがあると、開発が簡単になりますね。使える言語がある人はチャレンジされてみてはいかがでしょうか?


それと、今回は私が開発をしていて、気になった点などを記載したいと思います。

clovaのしゃべるセリフが淡々としている

完全に個人的な感想なのですけど、抑揚とブレス、といいますか。人間っぽくない喋り方といいますか。意図してセリフを区切ったりしないと、淡々としゃべり続けてしまうのがclovaなのかなと思いました。(ほかのスマートスピーカーがどうかは知らないですけど…)ですので、長いセリフとかになりますと全部聞き取れないです。勉強の一環として色んなスキルを導入して使ってみていますが、セリフとしては1秒以内に区切りを入れたほうがいいのかな、と思いました。途中でよくなに喋ってるのかよくわからなくなりました。

ビルトインインテントとカスタムインテントの優先順位ってどっちなんだ?

「はい」や「いいえ」という言葉に反応する「ビルトインインテント」がありまして、個人的には「はい」「いいえ」も自分で作ったインテントで反応させたかったのですが、使ってみたところ「ビルトインインテント」が優先されているように感じました。というか、カスタムインテントで呼び出されることはありませんでした。「はい」も「いいえ」もそれぞれ同じインテントで受け取って、プログラミング内で、返答を変えたかったのですが…ちょっと残念。

最初はよくわからなかったsession_attributesという存在

clovaにはsession_attributesというものが存在しています。最初、これを理解することがなかなかできませんでしたが、やってみれば見るほど、必要な存在なのだなと認識するようになりました。session_attributesとは、clovaがスキルの起動中において、データとしてユーザーが発音した内容を記憶しておける部分になります。例えばユーザーが答えた「マカロニピザ」や「ペパロニピザ」というピザの種類の情報と「2枚」「3枚」という枚数の情報を、session_attributesに保存しておくことができます。その保存した情報を使って、プログラムを使ってピザ屋に情報を送信する、ということが可能になります。

pythonsdkではこんな感じ

import cek
extensionId = '************skill.first'
clova = cek.Clova(application_id=extensionId,default_language='ja', debug_mode=True)
response_builder = cek.ResponseBuilder(default_language='ja')
~~~~~~~~
flaskだとかwebアプリ部分
~~~~~~~~

# PizzaInntent(ピザの種類が呼ばれたときに呼び出されるインテント)
@clova.handle.intent('PizzaIntent')
def pizza_handler(clova_request):
message = '何枚欲しいの?'
end_session = False
response = response_builder.simple_speech_text(message=message, end_session=end_session)

response.session_attributes['pizza'] = clova_request.slot_value('pizzaName')

PizzaInntentが呼び出され、そのうち、pizzaName、ピザの種類を表す単語を取得します。 「マカロニピザを頼んで」という言葉のうち「マカロニピザ」という単語を取得します。

これをsession_attributesとして、{"pizza":"マカロニピザ"}というものに保存されます。

{
    "response": {
        "card": {},
        "directives": [],
        "outputSpeech": {
            "type": "SimpleSpeech",
            "values": {
                "lang": "ja",
                "type": "PlainText",
                "value": "何枚欲しいの?"
            }
        },
        "shouldEndSession": false
    },
    "sessionAttributes": {
        "pizza": "マカロニピザ",
     },
    "version": "1.0"
}

逆にsession_attributesから値を取り出す場合は

pizza = clova_request.session_attributes['pizza']

のような形になります。

このように、ユーザーがしゃべった内容を受け取ることで、プログラミング側であれこれ出来るようになるのです。


私、プログラミング歴短いんですけど、Clovaの開発を通して色々なことを学べていっていけているように感じます。とりあえず、今日は気になったところをサクッと書いてみました。来週もclovaについて書いていきます。よかった次も見ていってください。

ClovaSDKのPythonVerがあるのだ

ClovaSDKのPythonVerがあるのだ

こんばんは、葛の葉です。

先日、駅すぱあとのヴァル研究所さんで行われたハンズオンに出席しました。

linedev.connpass.com

Lineさんが作っているスマートスピーカーの「Clova」の「スキル」の開発についての勉強会だったのですが、これが中々に面白い体験でした。正直、スマートスピーカーを触ったのが今回はじめてだったのですが、「スキル」を開発出来てとても面白かったです。

そんな「Clova」の開発用のPythonで使用できるSDKが出たということで、そのドキュメントのリンクを張っておきます。

こちら↓

Clova Extension Kit SDK for Python — clova-cek-sdk 1.0.0 documentation

なお、PythonのClovaSDKはpipでインストールすることが出来ます。

pip install clova-cek-sdk

モジュールとして使うときはimport cekで使います。

import cek

ちょっとSDKとは直接関係ないお話

スキルって?

スマートスピーカーを触ったことがなかった私には「スキル」という言葉がよくわからなかったです。いわゆる、スマートフォンにおける「アプリ」のようなものでして、機能の一単位のことになるようです。それで、今回のClovaSDKはそのスキルを作成できるものとなっています。

clova-developers.line.me

Clova Friendsってどんな感じ?

f:id:Kuzunoha-NE:20180816194217j:plain

Clova Friendsの大きさは500mlの缶ジュースより少し高さがあるくらいで、太さは500mlのペットボトルぐらいな気がしますClova Friends miniは持っていないのですが、Clova Friendsの半分ぐらいじゃないでしょうか?

clova.line.me

なんと、内臓バッテリー付きです。Bluetoothスマホと接続して音楽などを流したりすることもできますので、Clova Friendsを屋外に持ち出して、外で音楽を流すっていうこともできますね。一つ思いついた遊びとして、スマホで演奏するアプリを使って、Clova Friendsで流すなんてやれたら面白いかもなぁなんて思いました。

スキル、としては、ネットワークに繋がっていることが前提なものが多いように見受けられますので、外部に持ち出す部分とは少し相性悪いのかも?とは正直に思いました。


若干、最近スマートスピーカーに焦点が当たりつつあると思ってます。

皆さんもぜひ、色々なスマートスピーカーを触ってみてください。

Pythonで特定文字以降の文字を削除する(正規表現を使わない)

Pythonで特定文字以降の文字を削除する(正規表現を使わない)

イテレータで作ったVerがありますので、よかったら以下もどうぞ

kuzunoha-ne.hateblo.jp

こんばんは、葛の葉です。

さて、APIなどで、文字列を取得することがあると思うのですが、余分な部分は無くして綺麗な形で変数や配列に入れたいと思うことが多々あると思います。

たとえば町田(東京)(東京)部分

たとえばシェフの気まぐれサラダ(工場生産)(工場生産)の部分

たとえばからあげ:20180810:20180810の部分

正規表現で削除することも出来るのですが、私はこんなやりかたをしてみました。

moji = 'シェフの気まぐれサラダ(工場生産)'

moji = moji.split('(')[0]

print(moji)

>>>シェフの気まぐれサラダ

>>>シェフの気まぐれサラダ 出来ました!!!

要するにsplit('(')を使ってを境に文字を区切って配列にしています。

moji.split('(')['シェフの気まぐれサラダ', '工場生産)']という配列になりますね。

その[0]番目の要素を取り出すのでシェフの気まぐれサラダの部分だけ取り出すことが出来るわけですねぇ。

ただ、配列に分解して取り出しているだけなので、複雑な文等には適していないと思います。


切りたい文字が入っている要素、入っていない要素の入子になっている配列での取り出し方

また、例えば配列の中に括弧()が入っている、入っていないものが入子になっている配列もあるかと思います。

dinners = ['チキングリル', 'シェフの気まぐれサラダ(工場生産)', 'ドラゴンのしっぽビール(発泡酒)', 'スコーン']

こんな場合は以下のようにしてみましょうか

dinners = ['チキングリル', 'シェフの気まぐれサラダ(工場生産)', 'ドラゴンのしっぽビール(発泡酒)', 'スコーン']
cut_dinners = []  # 入れ替える別配列を用意しておく

for dinner in dinners:
    if '(' in dinner:
        cut_dinners.append(dinner.split('(')[0])
    else:
        cut_dinners.append(dinner)

print(cut_dinners)
>>> ['チキングリル', 'シェフの気まぐれサラダ', 'ドラゴンのしっぽビール', 'スコーン']

if '(' in dinner:という方法をとると良いですね。 文字列内にがあるか…?を判定し、

Trueならcut_dinners.append(dinner.split('(')[0])

Flaseならcut_dinners.append(dinner)

とします。


API等を使うと色んな値が入った文字列と出会うようになったなぁと思ってます。

その文字列を加工していくことも、すごく重要ですよね。

API使って開発されている皆様のお役に立てれば幸いです。

DockerでflaskとuWSGIを別コンテナのNginxと連携してみた

DockerでflaskとuWSGIを別コンテナのNginxと連携してみた

こんばんは、葛の葉です。

さて、よくNginxuWSGIを使ってWebサーバーの構築をしている記事は見かけますが、

同じコンテナ内でNginxuWSGIをインストールしているケースが多く、

NginxuWSGIと別コンテナで構築しているケースがあまり無いのかな、と思いました。

DockerComposeを使った場合になるのですが、簡単に書いていきたいと思います。

なお、もっといいケースなんてたくさんあると思うので、むしろ教えて頂ければと思います。


まず、docker-compose.ymlが必要で、更にIPアドレスを固定化するか、コンテナネームを固定化したほうがよいです。

それは記事にしましたので、下に載せます。

コンテナネームの固定化について

kuzunoha-ne.hateblo.jp

コンテナのIPアドレスの固定化について

kuzunoha-ne.hateblo.jp


flaskのあるコンテナでの設定

uWSGIが必要なため、flaskのあるコンテナでpipでインストールします。

pip install uwsgi

また、設定ファイルが必要なので、uwsgi.iniというデータを作り、以下のように記載します。

[uwsgi]
http = :3031 #ここはuwsgiのポート番号
chdir = /var/www/ #下記、flaskで実行したいプログラムのあるディレクトリ
wsgi-file = page.py #flaskのプログラム
callable = app #flaskを実行するためにここは不変更

そして、コマンドでuwsgi --ini uwsgi.ini(先程のuwsgi.ini)として、実行すればよいです。

これをdocker-composeでのcommandで設定すれば、コンテナが起動するときに指定したコマンドが実行されるはずです。

        command: uwsgi --ini uwsgi.ini

とすればよいです。

uwsgi.iniをホストマシンから持ってくるにはvolumesで共有すればよいので、

例えばdocker-compose.ymlのあるディレクトリにapp/configディレクトリを作り、

そのapp/config以下に先程のuwsgi.iniを保存しておき、docker-compose.ymlで以下のように記載して共有します。

また、docker-compose up時にuwsgi.iniを起動するようにしておきます。

        volumes:
            - ./flask_app/config/:/var/www/config/
        command: uwsgi --ini /var/www/config/uwsgi.ini

とこのようにしておきます。


Nginxのあるコンテナでの設定

Nginxは/etc/nginx/nginx.confの設定を変えてあげればうまくいきます。

また、そのnginx.confをホストOSで作ってあげて、渡してあげればよい、ということになります。

ホストOSにnginx.confを持ってくる方法はとりあえず記載しないとして (一度、Nginxコンテナ単体で起動して、共有のフォルダにコピーしてホストOSに持ってくる等)

設定ファイルのlocation部分を以下のようにの設定します。

    location / {
        client_max_body_size 1m;
        client_body_buffer_size 8k;

        proxy_pass http://[flaskのあるコンテナネームorコンテナのIPアドレス]:3031; 
        proxy_redirect default;
}

これを、例えばdocker-compose.ymlのあるディレクトリにnginx/configディレクトリを作り、

そのnginx/config以下に先程のnginx.confを保存しておき、docker-compose.ymlで以下のように記載して共有します。

        volumes:
            - ./nginx/config/nginx.conf:/etc/nginx/nginx.conf

直接、Nginxのnginx.confファイルに共有してあげます。

専用のconfをつくって、そこに読みに行くような設定もありますが、ちょっとこちらでは記載しません。(Nginxの設定方法の話になるので)


docker-compose upする

この状態でdocker-compose upすると、nginxで開放したポート番号に接続すれば、flaskのアプリが起動するはずです。

DockerComposeでContainerのIPアドレスの固定化をするかー

こんばんは、葛の葉です。

久しぶりにDockerのことを書こうと思いました。

今回はDocker-ComposeでcontainerのIPアドレスを固定化を行いました。

        networks:
            aplication_net:
                ipv4_address: 172.19.x.x

DockerComposeで各コンテナの設定部分に上記の部分を記載すればよいです。

aplication_netはDockerNetworkの名称になります。

ipv4_address: 172.19.x.xは使用するIPアドレスです。


また、コンテナ間にDockerNetworkを構成する記述を行います。

networks:
    aplication_net:
        driver: bridge
        ipam:
            driver: default 
            config: 
            - subnet: 172.19.0.0/24

aplication_net:部分はDockerNetwork名を記載します。

subnet: 172.19.0.0/24は使用したいネットワークアドレスとそのサブネットマスクをプレフィックス長で記載します。

なお、cofig:下ではgateway:というプロパティ、デフォルトゲートウェイの設定もあるそうなのですか、v3では出来ないようです。

デフォルトゲートウェイがどういうIPアドレスであるかが不明なため、今回は172.19.0.1を想定しています。


docker-compose.ymlは以下の通りになります。

services:
    mysql:
        image: ./mysql
~~~~~~~~~~~中略~~~~~~~~~~~~~~~~~~
        networks:
            aplication_net:
                ipv4_address: 172.19.0.2

    app:
        build: ./app
~~~~~~~~~~~中略~~~~~~~~~~~~~~~~~~
        networks:
            aplication_net:
                ipv4_address: 172.19.0.3

    nginx:
        image: nginx
~~~~~~~~~~~中略~~~~~~~~~~~~~~~~~~
        networks:
            aplication_net:
                ipv4_address: 172.19.0.4

networks:
    aplication_net: 
        driver: bridge
        ipam:
            driver: default 
            config: 
            - subnet: 172.19.0.0/24

docker-compose upでコンテナを起動しましょう。

docker exec -it コンテナ名 /bin/bashでコンテナ内に入り、hostname -iIPアドレスを確認してみましょう。

root@xxxxxxxxxxxx:/# hostname -i
172.19.0.3 

IPアドレスが表示されました!

GSSとGASとDiscordで遊んじゃおう

GSSとGASとDiscordで遊んじゃおう

こんにちは、葛の葉です。

GoogleSpreadSheetってあるじゃあないですか。

このGoogleSpreadSheetにscriptを実装できるようになっていて

それがGoogleAppsScriptっていうやつになります。

developers.google.com

さらに、そのGoogleAppsScriptを使ってDiscordと連携できるようになります。

今回はGoogleSpreadSheetのセルの中身をGASで取得してDiscordに発信するbot

作りたいと思います。


必要な材料

  • Googleアカウント
  • Discordアカウント
  • Discordのノリにそれなりについていける心

好きなシートを作るんだ

好きなシートを作りましょう。

A1セルに好きな言葉も入れておきましょう。

ちなみに私葛の葉って名前だけど、これ元は雌の狐の名前なので

よーく考えたら男なのに女性の名前使ってましたね。

まぁ…いいか…

f:id:Kuzunoha-NE:20180720194400p:plain

スクリプトエディタ起動

右上に[ツール]タブがあって、そのメニューにスクリプトエディタっていうのがありますので

クリックすると別画面が開いて、それがスクリプトエディタが表示されます。

f:id:Kuzunoha-NE:20180720194810p:plain

こんな画面がスクリプトエディタです。

f:id:Kuzunoha-NE:20180720195159p:plain


DiscordのWebhookを起動

botとして出力させたいDiscordのチャンネルで歯車のボタンを押します。

f:id:Kuzunoha-NE:20180720195519p:plain

そこから画面左側の[Webhooks][Webhooks作成]をクリックします。

出てきた画面には好きなbotの名前やアイコンを設定して保存しましょう。

f:id:Kuzunoha-NE:20180720200655p:plain

そうしたら画面右側に作ったbotが表示されていると思うので[編集]を押します。

さっきbotを作った画面に似たようなものが出てきますが、その下にスクロールすると

URL[画面の赤い四角部分]が表示されていますので、これをコピーします。

f:id:Kuzunoha-NE:20180720201250p:plain

コピーしたURLをブラウザーで開くと何やらいろいろ書かれたものが出てきますが、

"token: "の右側の色んな文字数字をコピーしておいてください。


スクリプトエディタでコードを書く

function submit(){
  discord(get_value())
};

function get_value() {
  var sheet = get_sheet('自分の作ったシートのURL',0);
  var range = sheet.getRange(1,1); //ここの(1,1)は座標です。第一引数が行、第二引数が列です。
  var value = range.getValue();
  return value;
};

function get_sheet(gss_url,sheet_num) {
  var ss = SpreadsheetApp.openByUrl(gss_url);
  var sheet = ss.getSheets()[sheet_num];
  return sheet;
};

function discord(message) {
    const url        = 'さっきコピーしたwebhooksのURL';
    const token      = 'さっきのURLの中のtokenの文字列';
    const channel    = '#general';
    const text       = message;
    const username   = 'bot';
    const parse      = 'full';
    const method     = 'post';

    const payload = {
        'token'      : token,
        'channel'    : channel,
        "content"    : text,
        'username'   : username,
        'parse'      : parse,
    };

    const params = {
        'method' : method,
        'payload' : payload,
        'muteHttpExceptions': true

    };

   response = UrlFetchApp.fetch(url, params);
}

関数の実行

関数を選択を押すとプルダウンがでてきますのでsubmitを押してください。

実行ボタンを押すとスクリプトエディタの上部になんかメッセージが出てきます。

権限がどうのって聞いてきますが、ポチポチ押せばOKです。

f:id:Kuzunoha-NE:20180720204152p:plain

そしたら、葛の葉ってメッセージが#generalに表示されるはず!


A1の値を変えてもいいですし、sheet.getRange(1,1)(1,1)の値を変えてみるのも面白いですよ!