Unlit Sphere

Let’s discover a new world with Unity

サークルプライバシーポリシー

サークルプライバシーポリシー

nmxi studio「エヌエムエックスアイ スタジオ」(以下「当サークル」といいます)は、以下のとおり個人情報保護方針を定め、個人情報保護の仕組みを構築し、全従業員に個人情報保護の重要性の認識と取組みを徹底させることにより、個人情報の保護を推進致します。

個人情報の管理
当サークルは、お客さまの個人情報を正確かつ最新の状態に保ち、個人情報への不正アクセス・紛失・破損・改ざん・漏洩などを防止するため、セキュリティシステムの維持・管理体制の整備・社員教育の徹底等の必要な措置を講じ、安全対策を実施し個人情報の厳重な管理を行ないます。
個人情報の利用目的
提供するサービスの中にはお客様のご同意の上で個人情報をお預かりするものがありますが、それらの情報は、当サークルからのご連絡や業務のご案内やご質問に対する回答として、電子メールや資料のご送付に利用いたします。
個人情報の第三者への開示・提供の禁止
当サークルは、お客さまよりお預かりした個人情報を適切に管理し、次のいずれかに該当する場合を除き、個人情報を第三者に開示いたしません。

 

  • お客さまの同意がある場合
  • お客さまが希望されるサービスを行なうために当サークルが業務を委託する業者に対して開示する場合
  • 法令に基づき開示することが必要である場合
個人情報の安全対策
当サークルは、個人情報の正確性及び安全性確保のために、セキュリティに万全の対策を講じています。
ご本人の照会
お客さまがご本人の個人情報の照会・修正・削除などをご希望される場合には、ご本人であることを確認の上、対応させていただきます。
法令、規範の遵守と見直し
当サークルは、保有する個人情報に関して適用される日本の法令、その他規範を遵守するとともに、本ポリシーの内容を適宜見直し、その改善に努めます。
お問い合せ
当サークルの個人情報の取扱に関するお問い合せは下記までご連絡ください。

nmxi studio
Mail: nmxi.service[a]gmail.com

【Unity】Node.jsのサーバにWebSocketを使ってデータを送信

Pref.

UnityからNode.jsで動かしてるサーバにオブジェクトの座標を送ってみたかったんです. こちらの @oishihiroaki さんの記事を参考にwebsocket-sharpをUnityに入れてやってみたのでまとめます.ありがとうございます(๑•̀ㅂ•́)و✧

以下に今回作ったサンプルプロジェクトを置いておきました.

https://github.com/nmxi/UnityWebSocketTest

Env.

Windows10 64bit Unity 20181.5f1 Node v8.11.3

Method

1.Unityに2つのライブラリをインポート

こちらの記事を参考にまずwebsocket-sharpをUnityに入れます.

上記の記事の中ではMonoDeveropを利用してビルドし,dllファイルを生成しているのですが,Unity2018からエディタがVisual Studioになったので(?)今回はそちらの方法を簡単に書き残しておきます.簡単ですがw

まずGitHubからCloneかZipで保存しまして,websocket-sharp.slnVisual Studioで開きます.

アセット 1@.png

Visual Studioが開かれると思いますので,以下の画像のようにソリューションエクスプローラーの中からwebsocket-sharpを右クリックし,ビルドを選択します.このときソリューションエクスプローラーの中に存在しているExample*は必要ないので私は消しました.

アセット 2@.png

ビルドをするとwebsocket-sharp-master/websocket-sharp/bin/Debugの中に websocket-sharp.dllが生成されます.

image.png

このdllをUnityのプロジェクト側のAssets/Pluginsに入れてください. これで,erbsocket-sharpのUnityへのインポートは完了です.

今回UniRXもインポートしました. UniRxは非同期処理においてはちゃめちゃ便利なライブラリです. アセットストアからインポートインポートして下さい.

https://assetstore.unity.com/packages/tools/integration/unirx-reactive-extensions-for-unity-17276

image.png

2つともインポートが完了したら,UnityのAssets/Pluginsの中身は以下のようになっていると思います.

image.png

2.UnityでWebSocketクライアントを作る

  • Unity上のオブジェクトの3次元座標情報をNode.jsで動かしているサーバに送信
  • オブジェクトの座標に変化があったときだけサーバに情報を送信

これらができるクライアントを作ります.

まずUnityの空間上に

  • Box
  • Button 2つ

を配置しました.こんな感じ.

image.png

スクリプトをひとつだけ書きました.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using WebSocketSharp;
using UniRx;

public class PositionSync : MonoBehaviour {

    [SerializeField] private string _serverAddress;
    [SerializeField] private int _port;

    [SerializeField] private Transform _syncObjTransform;   //Share transform

    [SerializeField] private SyncPhase _nowPhase;

    private WebSocket ws;

    public enum SyncPhase {
        Idling,
        Syncing
    }

    private void Awake() {
        _nowPhase = SyncPhase.Idling;

        var cTransformValue = gameObject.ObserveEveryValueChanged(_ => _syncObjTransform.position);
        cTransformValue.Subscribe(x => OnChangedTargetTransformValue(x));
    }

    /// <summary>
    /// Get Down Start Sync Button
    /// </summary>
    public void OnSyncStartButtonDown() {
        var ca = "ws://" + _serverAddress + ":" + _port.ToString();
        Debug.Log("Connect to " + ca);
        ws = new WebSocket(ca);

        //Add Events
        //On catch message event
        ws.OnMessage += (object sender, MessageEventArgs e) => {
            print(e.Data);
        };

        //On error event
        ws.OnError += (sender, e) => {
            Debug.Log("WebSocket Error Message: " + e.Message);
            _nowPhase = SyncPhase.Idling;
        };

        //On WebSocket close event
        ws.OnClose += (sender, e) => {
            Debug.Log("Disconnected Server");
        };

        ws.Connect();

        _nowPhase = SyncPhase.Syncing;
    }

    /// <summary>
    /// Get Down Stop Sync Button
    /// </summary>
    public void OnSyncStopButtonDown() {
        ws.Close(); //Disconnect
    }

    public void OnChangedTargetTransformValue(Vector3 pos) {
        if (_nowPhase == SyncPhase.Syncing) {
            Debug.Log(pos);
            ws.Send(pos.ToString());
        }
    }
}

本当に数分で書いたのではちゃめちゃですが... 以上のスクリプトでは オブジェクトのTransform.positionに変化があったとき & Enumで定義したSyncPhase _nowPhaseSyncingのとき

サーバ側にVector3をstring型にして送信しています. 余談ですが「この値が変化したとき」といったときの処理にUniRxを使っています.

このスクリプトをEmptyObjectなどをMasterとかに名前を変えて空のオブジェクトを作成して,コンポーネントとして追加します. コンポーネントを追加したら,サーバアドレスとポート,座標情報をサーバに送りたいオブジェクトのTransformを変数SyncObjTransformにセットします.サーバアドレスとポートは特に問題がなければ"localhost"と8080を入れておきます.

image.png

スタートボタンとストップボタンのOnClickには

  • PositionSync.OnSyncStartButtonDowns
  • PositionSync.OnSyncStopButtonDown

をセットしておきます.以下はStartButtonの例

image.png

3.Node.jsでWebSocketサーバを作る

Node.jsを予めインストールしておいて下さい. 今回私の環境ではv8.11.3を利用しました.

適当なディレクトリに以下のserver.jsを作ります.

var WebSocketServer = require('ws').Server
var wss = new WebSocketServer({
    port: 8080
});

wss.on('connection', function(ws) {
    ws.on('message', function(message) {
        console.log('received: %s', message);
    });
    ws.send('This is server');
});

これでポート番号8080でサーバが立ち,クライアントが接続したときに"This is server"とメッセージを送るようになります.

4.動作確認

まずサーバ側を以下コマンドで起動させます.

$ node server.js

次にUnity側をPlayにし,Sync Startボタンを押して 箱を移動させると...

ecWc.gif

できました. 上の様子ではマウスでドラッグするとボックスが動いていますが,以下の記事を参考させて頂きました.

Ref.

http://neareal.com/1230/ https://qiita.com/oishihiroaki/items/bb2977c72052f5dd5bd9 http://hwks.hatenadiary.jp/entry/2014/07/20/022229 http://blog.katty.in/2206

【Unity】TwitterRestAPIを使ってツイートの検索を行う

Pref.

UnityからTwitterのREST-APIを自家実装でたたけるように,TwitterOAuth認証について勉強しました∩(゚∀゚∩)

今回この記事では,TwitterOauth認証とは?というところから,Oauth認証の方法,UnityからOauth認証を行いAPIを叩いてみるところまでやってます.

この記事を読んで応用すれば自分でTwitterクライアントをUnityで作れます(白目) (ライブラリ使えばかんたんなのに...って言うのは置いておいて.勉強のためw)

GitHubにサンプルプロジェクトを置いておいたので,よかったら見ていってね https://github.com/nmxi/UnityTwitterRestAPI

Env.

Unity2018.1.5f1 Twitter REST-API 2018/06/28現在 (今年の8月を過ぎてもUser Streams APIは使っていないので,この方法で行けるはず...)

Contents

1.OAuth認証とは

APIを使う上でTwitterの場合はセキュリティ関係のあれこれのためにOAuth認証をしなければなりません. そのOAuthとは...

OAuth (オー オース) は、権限の認可(authorization)を行うためのオープンスタンダードである。 2016年現在の最新の標準は、2012年にRFCとして発行されたOAuth 2.0である(RFC6749、RFC6750) ※Wikipedia引用

つまり権限許可のための認証の仕組みのことですね.この文章には現在2.0が標準と書かれていますが,TwitterはOAuth1.0を利用しています. 1.0から2.0の変更点は自分もよく分かっていませんが,セキュリティ向上が行われたというわけではなく仕組みが変わったくらいらしいですので,Twitterの認証の仕方が古いというわけではないみたいです.

2.TwitterでのOAuth認証の方法

どのようなことをやりたいか定義しないと文章が書きづらいので,今回

としたいと思います.

Twitter APIを使って過去にTweetされたデータに検索をかけるわけなのですが,まず以下の図を見てください.

Untitled Diagram (3).png

簡単ですね.ClientからTwitterのサーバにRequestをGETで送り,Twitterから結果が返される. ただそれだけです.このClientとは今回に置き換えるとUnityで作るものです. そして1章の説明であったOAuthの仕組みはこのRequestデータの中に含みます.

RequestデータをGETで送るのですがそのRequest HeaderにOAuth認証に使う署名(signature)を入れたりして 認証させます.上の図よりも詳しい図が以下の図です.

Untitled Diagram (4).png

GETでリクエストを送る際にURLの後ろにクエリストリングを記述し同時にAuthorization HeaderにOAuthのための認証のあれこれを 貼り付けてリクエストを送ると,TwitterからJSONで結果が返ってきます. この図のJSONはコード215,認証失敗ですね(笑 正しければ検索の結果がJSONで返ってきます. もし上の図のGETの中身のParameterとはなんぞや?Headerとはなんぞや?という方は, 以下の説明に進む前に,この辺りの記事を読まれてから進んだほうが良いかもしれません.

記事を執筆して下さった方ありがとうございます. また,このツールを使い,学習するとめちゃくちゃHeaderとはなんぞや?という質問に効くと思います. Advanced REST client

3.実際のリクエスト作成手順

ではもっとクローズアップしていきます.ここから面倒です.

まず「APIキー」や「アクセストークン」といった用語がこれから出てきますので簡単に説明します.

それらはTwitterAPIを利用するための鍵です. Twitterの開発者用ページでどんなアプリを作るかなどの登録を行い,「APIキー」や「アクセストークン」といったものが発行されるので,そちらを先に行っておきましょう.

開発者ページの右上の方から新規アプリを作成していき,最終的に以下のような画面たどり着きます.

f762d8heiuqw.png

この画面にある4つの文字列をこの後使うので,メモっておいて下さい.

Consumer APi Keys ・API Key(APIキー) ・API Secret Key(APIシークレット)

Access token & access token secret ・Access token(アクセストークン) ・Access token secret(アクセストークンシークレット)


2章の最初でも定義しましたが,この章ではもっと詳しく今回やりたいことを定義します.

  • リクエストメソッドはGETを使う
  • Twitter APIの中でもTweet検索をするためのSearch APIを使う
  • 検索ワードは"#Unity"
  • 検索範囲は全て(言語制限なし,地域制限なし)
  • 取得するツイートは最も新しいTweetから上位20Tweet

OAuth1.0で認証する際のリクエストは以下のようなファーマットでなければなりません.

OAuth_requestParam_Example.png

ちなみに普段ブラウザでURLバーに書いてるのはRequestURLの部分のみ.Request Headerの部分は,何かツールを使ったりしない限り書き換えたりすることはできないです.(このため上にリンクを載せたツールを使ったり,Curlコマンドを使ったりしてテストしなければならない!)

Request URLの中身から説明していきます. まず前半のhttps://api.twitter.com/1.1/search/tweets.jsonというのはTwitter APIを使って「検索」を行うときにアクセスするURLです.今回は「検索」をしたいのでこのURLにアクセスしますがやりたいことによってこのURLは変わります.

続いてRequest URLの中の「?」よりも後の部分は「クエリストリング」と呼びますが.クエリストリングには今回の検索で使うパラメータを指定します. Count=20というのは今回の「検索」の場合最新検索結果から上位20件を表示という意味で,q=〇〇というのは検索ワードが〇〇という意味になります.(後述しますがこの検索ワード〇〇はURLエンコードされている必要があります.)

例として「検索」の場合以下のようなものなどがパラメータに指定できます.

パラメータ 説明
q q=Unity 検索ワード(このパラメータは必須)
lang lang=ja 検索する対象の地域の言語コード
result_type result_type=popular 取得するTweetの種類
count count=10 検索結果の上位何件分のTweetを取得するか
include_entities include_entities=false Tweetオブジェクト内にentitiesを含むか否か

今回のやりたいことの定義に従うとリクエストURLは以下のようになります.

https://api.twitter.com/1.1/search/tweets.json?count=20&q=%23Unity

っとここで「#」が「%23」になっとるぞーいって思った方,これはリクエストURLに含んではいけない「#」という文字を,含んでも良い文字に変換しています.URLエンコードといいますが,下で説明しています.


では次にRequest Headerの中身を説明していきます. こちらは結構初心者からすると複雑です...\(^o^)/

まず後に出てくる単語で初心者だと「なんぞや?」となりやすいものを説明しておきます.

1.UnixTime(ユニックスタイム) 1970/1/1 00:00:00から経過している秒数のことです.例として2018-07-30 13:47:33のUnixTimeは 1532958453といった感じになります.ぱっと見,数字の羅列です.

2.Nonce(ナンス) number used onceの略で一度しか使われない数字(文字列)という意味があります. 唯一無二な文字列なわけですが,この記事ではUnixTimeの数字の後ろに"N"をつけたものを利用します.

3.URLエンコード URLには含んでは行けない文字が規定されています.その文字をエンコードすることにより,URLに使っても良い文字列にする作業のことをURLエンコードといいます.

URLエンコード URLエンコード
認証 %E8%AA%8D%E8%A8%BC

こんな感じに%〇〇%〇〇みたいに変換されます.

4.ハッシュ ハッシュとは、簡単にいってしまうと「暗号化技術」のことです.ここでは深く説明しませんが,様々なハッシュ化アルゴリズムが存在していて,代表的なものにmd5やshaなどのものが存在します.


Request Headerの中では予めTwitterのサーバ側で「Request Headerの中にはAuthorization ヘッダーが存在して,その中にはoauth_nonceoauth_signature_methodと....の情報を入れてね!」といった感じにとても細かく決められています.

クエリストリングもそうですが,この中身が1つでも間違うとBad Autentication data.みたいな返答が返ってきてしまうわけです...

Request Headerの中身の話に戻ります.

TwitterのOauth1.0で認証する際のRequest Headerには以下の情報が必ず含まれなければなりません.

Request Headerに必要なヘッダー Authorization

Authorizationヘッダーに必要な情報

Key名 入れる情報(value)
Oauth oauth_consumer_key APIキー
oauth_nonce Nonce.UnixTimeにNをつけた文字列
oauth_signature URLエンコード済みの認証用文字列(これを作るのが一番面倒)
oauth_signature_method 文字列"SMAC-SHA1"(細かくここでは説明しません)
oauth_timestamp UnixTime
oauth_token アクセストーク
oauth_version 文字列"1.0"(認証バージョンの設定,ここでは1.0を選択)

これらのデータをそれぞれ揃えていくことがRequest Headerを組み立てる作業になるわけですが, 上の表の3番目のoauth_signature以外は殆どが文字列を入れていくだけなので,深く考えることはありません.

oauth_signatureは以下の複雑な手順を踏んで作成した認証用の文字列を入れなければなりません. ここからはそのoauth_signatureに入れる文字列の作り方について説明します.


認証用の文字列を作っていくわけなのですが,3つの材料が必要です.

784ewfew.png

順番に作り方を書いていきます.

1.URLエンコード済みの「リクエストメソッド」を作成 リクエストメソッドは3章のはじめに記述したように"GET"を使うので,文字列"GET"をURLエンコードしたものがURLエンコード済みの「リクエストメソッド」になります.

2.URLエンコード済みの「リクエストURL」を作成 これも3章のはじめで説明した部分にありますが,リクエストURL,つまり今回ならば「検索」を行うので"https://api.twitter.com/1.1/search/tweets.json" にアクセスを行います. よって文字列"https://api.twitter.com/1.1/search/tweets.json" をURLエンコードしたものがURLエンコード済みの「リクエストURL」になります.

3.URLエンコード済みの「パラメータ文字列」を作成 ここが認証文字列を作るための最終関門です.

まず以下の情報を入手します.

Key名 入れる情報(value)
oauth_nonce Nonce.UnixTimeにNをつけた文字列
oauth_signature_method 文字列"SMAC-SHA1"(細かくここでは説明しません)
oauth_timestamp UnixTime
oauth_consumer_key アクセストーク
oauth_version 文字列"1.0"(認証バージョンの設定,ここでは1.0を選択)
q クエリ文字列(クエリストリングに記述したものと一緒)

これらの情報をKeyの名前順に昇順ソートを行い Key=Value&Key=Value&Key=...に組み直します.

こんな感じの文字列になります.

oauth_consumer_key=oooooo&oauth_nonce=1532958453N&oauth_....

その組みなおした文字列をURLエンコード

以下のような%を含んだ文字列がURLエンコード済みの「パラメータ文字列」になります.

oauth_consumer_key%3Doooooo%26oauth_nonce%3D1532958453N%26oauth_....

3つの材料が揃いました.

これらを リクエストメソッド, リクエストURL, パラメータ文字列 の順に&でつなぎます.

3f8huiwenjk.png

つないだらこんな感じになります.

GET&https%3A%2F%2Fapi.twitter.com%2F1.1%2Fsearch%2Ftweets.json&oauth_consumer_key%3Doooooo%26oauth_nonce%3D1532958453N%26oauth_....

そして最後にHMAC-SHA1方式のハッシュ値に変換します(ハッシュ化). ハッシュ値に変換するときには「ハッシュ化するデータ」を「salt(塩)と呼ばれるデータ」を使ってハッシュ化します.

今回の場合は ハッシュ化するデータには3つの材料をつなげた文字列を使い, salt(塩)と呼ばれるデータにはAPIシークレットとアクセストークンシークレットを"&"で繋いだ文字列を使います.

ハッシュ化します.イメージはこんな感じ.

7482yhiuenjc.png

ハッシュ値が出てきたらそれをBase64エンコードします. これで認証用文字列が完成しました.ホッ (´・ω・`)

4f2ejnk.png


ではもうひと頑張りして,続きをやっていきましょう.

上で記述した表を,もう一度以下に記述します.

Authorizationヘッダーに必要な情報

Key名 入れる情報(value)
Oauth oauth_consumer_key APIキー
oauth_nonce Nonce.UnixTimeにNをつけた文字列
oauth_signature URLエンコード済みの認証用文字列(完成した!)
oauth_signature_method 文字列"SMAC-SHA1"(細かくここでは説明しません)
oauth_timestamp UnixTime
oauth_token アクセストーク
oauth_version 文字列"1.0"(認証バージョンの設定,ここでは1.0を選択)

全てのKeyのValueに何を入れればよいかが分かりました. ではAuthorizationヘッダーを組み立てていきましょう.

Authorizationヘッダーに必要な情報の表の順に Key=Value,Key=Value,Key=...のように文字列を繋げていきます. 今までは"&"を使って繋げていましたが,ここでは",(カンマ)"を使います.

そして完成した文字列をRequest HeaderのAuthorizationヘッダーに格納します.

これでTwitterAPIをつかって文字列検索を行うためのリクエストデータが完成しました. 4章ではこのやり方を使って,Unityからリクエストを送って結果を受け取れるようにします.

4.サンプルプログラム for Unity

このスクリプトの「TwitterSearch」関数をボタンなどから呼ぶと,Twitterに「#Unity」というハッシュタグ付きツイートの検索を行い,検索結果の上位20件をJsonで返されるので,Debug.Logでコンソール上に表示します.

「TwitterSearch」関数を呼ぶ前にAPIキーなどの鍵とトークンを入れることを忘れないようにしてくださいね ヾ(。>﹏<。)ノ゙

入力した後にエラーコード「401」が返ってきたら,APIキーなどに空白文字が入っていないかを見てみて下さい. 直る場合があります.

using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Security.Cryptography;
using System.Net;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;

/// <summary>
/// Twitterから特定の文字列を含むツイートを検索し,結果をJsonで取得する
/// </summary>
public class TwitterCommentSearcher : MonoBehaviour {

    [SerializeField] private string _apiKey;
    [SerializeField] private string _apiSecret;
    [SerializeField] private string _accessToken;
    [SerializeField] private string _accessTokenSecret;

    [SerializeField] private int _count;    //一度に取得するtweeet数
    [SerializeField] private string _searchWord;    //検索文字列

    private string _requestURL = "https://api.twitter.com/1.1/search/tweets.json";
    private string _requestMethod = "GET";

    private Dictionary<string, string> _dic;

    private string _oauthSignature; //baase64でエンコードされたsignature

    private WWW www;

    private long _lastFetchTweetID; //最後に取得したTweetのID

    void Awake() {
        //検索ワードに#Unityを代入
        _searchWord = "#Unity";

        //Encode searchWord
        _searchWord = LargeCharUrlEncode(_searchWord);
    }

    public void TwitterSearch() {
        StartCoroutine(Fetch());
    }

    private IEnumerator Fetch() {
        //Fetch UnixTime
        var baseDt = new DateTimeOffset(new DateTime(1970, 1, 1, 0, 0, 0), TimeSpan.Zero);
        long unixTime = (DateTimeOffset.Now - baseDt).Ticks / 10000000;

        //Encode Signature_key
        string signatureKey = _apiSecret + "&" + _accessTokenSecret;

        //for Nonce
        string nonce = unixTime.ToString() + "N";

        //必要なパラメータを列挙
        var _dicTmp = new SortedDictionary<string, string>() {
        {"oauth_nonce", nonce},
        {"oauth_signature_method", "HMAC-SHA1" },
        {"oauth_timestamp", unixTime.ToString()},
        {"oauth_consumer_key", _apiKey},
        {"oauth_token", _accessToken},
        {"oauth_version", "1.0"},
        {"count", _count.ToString()},
        {"q" , _searchWord}    //クエリ
        };

        //ソートを行い_dicに詰めいていく
        _dic = new Dictionary<string, string>();
        foreach (KeyValuePair<string, string> pair in _dicTmp) {
            _dic.Add(pair.Key, pair.Value);
        }

        //_dicをキー=値&キー=値&の順に組み立てる
        string paramStr = "";
        foreach (KeyValuePair<string, string> pair in _dic) {
            paramStr += "&" + pair.Key + "=" + pair.Value;
        }
        paramStr = paramStr.Remove(0, 1);

        //Debug.Log(paramStr);

        //paramStrをURLエンコード
        string encodedRequestParams = LargeCharUrlEncode(paramStr);
        //Debug.Log(encodedRequestParams);

        //リクエストメソッドをエンコード
        string encodedRequestMethod = LargeCharUrlEncode(_requestMethod);

        //リクエストURLをエンコード
        string encodedRequestURL = LargeCharUrlEncode(_requestURL);

        string signatureData = encodedRequestMethod + "&" + encodedRequestURL + "&" + encodedRequestParams;

        //Debug.Log(signatureData);

        //HMAC-SHA1方式のハッシュ値に変換
        _oauthSignature = HashingHMACSHA1(signatureKey, signatureData);

        ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(OnRemoteCertificateValidationCallback);

        string url = "https://api.twitter.com/1.1/search/tweets.json?count=" + _count.ToString() + "&q=" + _searchWord;
        //Debug.Log(url);
        HttpWebRequest hwr = (HttpWebRequest)WebRequest.Create(url);

        string authorizationHeaderParams = "Oauth oauth_consumer_key=" + _dic["oauth_consumer_key"] + "," +
                                            "oauth_nonce=" + _dic["oauth_nonce"] + "," +
                                            "oauth_signature=" + WWW.EscapeURL(_oauthSignature) + "," +
                                            "oauth_signature_method=HMAC-SHA1," +
                                            "oauth_timestamp=" + _dic["oauth_timestamp"] + "," +
                                            "oauth_token=" + _dic["oauth_token"] + "," +
                                            "oauth_version=1.0";

        //Debug.Log(authorizationHeaderParams);

        hwr.Headers.Add("Authorization", authorizationHeaderParams);

        hwr.Method = "GET";

        HttpWebResponse res = (HttpWebResponse)hwr.GetResponse();   //fetch

        Stream receiveStream = res.GetResponseStream();

        StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);

        Encoding ascii = Encoding.ASCII;
        string responseJsonStr = readStream.ReadToEnd();

        Debug.Log(responseJsonStr);

        res.Close();
        readStream.Close();

        yield break;
    }

    public string HashingHMACSHA1(string key, string dataToSign) {
        Byte[] secretBytes = UTF8Encoding.UTF8.GetBytes(key);
        HMACSHA1 hmac = new HMACSHA1(secretBytes);

        Byte[] dataBytes = UTF8Encoding.UTF8.GetBytes(dataToSign);
        Byte[] calcHash = hmac.ComputeHash(dataBytes);
        String calcHashString = Convert.ToBase64String(calcHash);
        return calcHashString;
    }

    //信頼できないSSL証明書を「問題なし」にする
    private bool OnRemoteCertificateValidationCallback(
      System.Object sender,
      X509Certificate certificate,
      X509Chain chain,
      SslPolicyErrors sslPolicyErrors) {
        return true;
    }

    /// <summary>
    /// URLエンコードを大文字で返す
    /// </summary>
    /// <param name="inputText"></param>
    /// <returns></returns>
    private string LargeCharUrlEncode(string inputText) {
        var escapedStr = WWW.EscapeURL(inputText);

        string sdTmp = "";
        for (int i = 0; i < escapedStr.Length; i++) {
            var c = escapedStr[i];
            if (c == '%') {
                sdTmp += c;
                c = escapedStr[i + 1];
                c = c.ToString().ToUpper()[0];
                sdTmp += c;
                c = escapedStr[i + 2];
                c = c.ToString().ToUpper()[0];
                sdTmp += c;
                i = i + 2;
            } else {
                sdTmp += c;
            }
        }

        return sdTmp;
    }
}

Reference

FirebaseでPWAを動かすノート

Pref.

FirebaseでPWAを実装しようとしたメモ的なもの. PWAは前提としてssl(https)を使った通信でなければならない.よって個人でサーバを借りてnginxとかでやるのは面倒. そこで出できたのがgoogleの提供する,Firebase. Firebaseを使えば勝手にhttpsで通信をしてくれるので,なんと手っ取り早い.

この記事ではFirebase上でPWAを動かすあれこれを書いていきたいと思います.

Env.

macOS Firebase

Method

Firebase-tools

Firebase-toolsとはFirebaseで公開するコンテンツの編集とかを行うためのツール. これを入れると色々とゴネゴネできるみたい.

入れ方は以下のURL先を参照して.node.js(npm)を入れる必要がある. https://qiita.com/taketakekaho/items/dd08cf01b4fe86b2e218

Firebase-toolsのコマンド

npmをつかってまずはfirebase-toolsのインストール

$ npm install -g firebase-tools

Firebase-tools上でFirebaseにログインする.このコマンドを叩くとブラウザが立ち上がり,ログインできる. またログアウトも以下のコマンド

$ firebase login
$ firebase logout

Firebase-toolsの初期化コマンド? これを実行するとホームディレクトリ(?)にfirebaseの関連ファイルが生成される. 実行後,予めFirebaseでプロジェクトを作成しているとどのプロジェクトをローカルで編集するかを選択できる.

$ firebase init

ローカルでサーバを立ち上げて実際に確認できる. コマンドを叩いたあとlocalhost:5000にブラウザでアクセス.

$ firebase serve

実際にローカルで編集したものをFirebaseにデプロイする.

$ firebase deploy

以下のコマンドを叩くことでコンソール上から直接ブラウザを立ち上げ,Firebaseで実際に公開されているサイトを確認できる.

$ firebase open hosting:site

以下のコマンドを叩くことでデプロイと実際にデプロイされたサイトを表示を一回のコマンドで実行可能.

$ firebase deploy && firebase open hosting:site

自分のプロジェクトにエイリアスをつける

$ firebase use --add

操作するプロジェクトを変更する

$ firebase use [alias]

Firebaseの認証

Firebaseで認証を行うにはindex.htmlの\と\の間にFirebaseのアプリケーション登録時に出てくるjsのコードを貼り付ける.

以下のようなjsのコード

<script src="https://www.gstatic.com/firebasejs/5.5.2/firebase.js"></script>
<script>
  // Initialize Firebase
  var config = {
    apiKey: "AIzaSyA-pTWNkIM8unupQ5DtYXXo7xTSEQA0BYQ",
    authDomain: "myfirstfirebase-6f247.firebaseapp.com",
    databaseURL: "https://myfirstfirebase-6f247.firebaseio.com",
    projectId: "myfirstfirebase-6f247",
    storageBucket: "myfirstfirebase-6f247.appspot.com",
    messagingSenderId: "294712912982"
  };
  firebase.initializeApp(config);
</script>

以下のようなコードを入力することで,DBの書き換えの共有が可能

  <body>
    <div id="message">
      <h1>Firebaseてすと</h1>
      <p>文字を入れると更新されます</p>
      <ul>
        <li id="chatText"></li>
        <li><input type="text" name="" id="my_text"></li>
        <li><a onclick="changeData()">更新</a></li>
      </ul>
    </div>


    <script>
      var db = firebase.database();
      var myChatAll = db.ref("/my/chat/all");

      function changeData(){
        var text = document.getElementById("my_text").value;
        myChatAll.set({title:"example", text:text});
      }

      myChatAll.on("value", function(snapshot) { 
        document.getElementById("chatText").innerText = snapshot.val().text;
      });
    </script>
  </body>
var db = firebase.database();

でFirebase上のDBに接続

var myChatAll = db.ref("/my/chat/all");

でDBの/my/chat/allという場所の情報に接続 changeDataというメソッドが呼ばれたら,テキストインプットフィールドの中にある文字列をDBに送信している.

以下のコードでDBに登録している.

myChatAll.set({title:"example", text:text});

またDBが更新された場合は.onでイベントを登録できる. function名はsnapshotでID=chatTextに取得した文字列を代入している.

document.getElementById("chatText").innerText = snapshot.val().text;

onceを使うことで,一回だけ取得もできるみたい

database.ref('/cat_message/message').once('value')

独自ドメイン設定

独自ドメインを以下の画面から設定可能.しかしDNSにTXTレコードを設定してから,5分くらい時間たってから次に進まないと,所有確認が失敗した. f:id:nagatsukimxi:20181222102212p:plain

また,2時間くらいたたないと独自ドメインhttpsが有効化されない.

通常のサイトをPWAに対応させる

基本的に以下の2つを追加してあげれば,対応するみたい?

・service-workers.js プッシュ通知やインストール時の挙動を指定するときに利用

・manifest.json アイコンやスプラッシュスクリーンの情報などを記述

index.htmlに上の2つのファイルを指定してあげる\の下あたり

    <!-- マニフェストの指定 -->
    <link rel="manifest" href="manifest.json">
    <script>
    // service workerが有効なら、service-worker.js を登録します
      if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('./service-worker.js').then(function() { console.log('Service Worker Registered'); });
     }
    </script>

googleのアカウントを使って認証をする

いつも何かしらのサービスで目にする,googleのアカウント認証を実装する.

これPWAでは使えないかも

以下のソースはFirebaseのGoogleアカウントOAuth認証を試したのでメモを参照した. ありがとうございます.

    <script>
    var AuthUI = {
      init: function(){
        AuthUI.provider = new firebase.auth.GoogleAuthProvider();
        AuthUI.elAuthBtn = document.querySelector('.authBtn');
        AuthUI.elUserId = document.querySelector('.userId');
        AuthUI.draw();
        AuthUI.elAuthBtn.addEventListener('click', function(){
          AuthUI.doAuth();
        });
        firebase.auth().getRedirectResult().then(function(result) {
          AuthUI.elAuthBtn.disabled = false;
          if (result.credential) {
            // This gives you a Google Access Token. You can use it to access the Google API.
            var token = result.credential.accessToken;
          }
          if(result.user){
            AuthUI.data.authed = true;
            AuthUI.data.userId = result.user.email;
            AuthUI.draw();
          }
        }).catch(function(error) {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          // The email of the user's account used.
          var email = error.email;
          // The firebase.auth.AuthCredential type that was used.
          var credential = error.credential;
          // ...
        });
      },
      data: {
        authed: false,
        userId: '',
        info: ''
      },
      draw: function(){
        AuthUI.elAuthBtn.textContent = AuthUI.data.authed ? 'LOGOUT' : 'LOGIN';
        AuthUI.elUserId.textContent = AuthUI.data.userId;
      },
      doAuth: function(){
        if(AuthUI.data.authed){
          firebase.auth().signOut().then(function() {
            AuthUI.data.authed = false;
            AuthUI.data.userId = '';
            AuthUI.draw();
          }, function(error) {
            // An error happened.
          });
        }
        else {
          firebase.auth().signInWithRedirect(AuthUI.provider);
        }
      }
    };
    AuthUI.init();
    </script>

PWAのサンプルたち

先人様ありがとうございます.

vue.jsでPWAを動作させるサンプル. https://github.com/BosNaufal/vue-simple-pwa

Ref.

https://qiita.com/kohashi/items/43ea22f61ade45972881 https://qiita.com/radiocat/items/034904a094d07c389a4f https://qiita.com/tamalign/items/18d443787cf99bec1138