20201017のNode.jsに関する記事は11件です。

obniz占い。トランプさん再選するらしい。

obniz占いでアメリカ大統領選挙を占ってみた

 もうすぐアメリカ大統領選挙の投開票日。トランプ再選か、はたまた、バイデンさんか、obniz占いで、選挙結果を占ってみました。
 もし、的中したら今後個人的な悩みもobnizに占ってもらうと思う。
※妻に小遣いの値上げを交渉すべきかどうか悩んでいる。

トランプさんが再選するらしい

 そこは忠実に各州から選ばられる選挙人の数(538名)分を占い、総得票数が多い候補者の勝利とした。

結果は、、

トランプ氏 298票 対 バイデン氏 240票。

(ランダム関数にしては)トランプさん圧勝でした。

obnizとサーボモーターとLEDで作った

 簡単IoT工作ができるobnizデバイスを使い、サーボモーターを連結して、トランプさんとバイデンさんをサーボモータに張り付け、2人がくるくると回る状態を作る。
 ランダム関数で0,1を取得してきて、サーボモーターを制御するようにし、、、

いざ、obnizのボタンを中央に押すと。占い結果を返してくれる。
図2.jpg

ソース

test.js
const Obniz = require('obniz');
const obniz = new Obniz('xxxx-xxxx'); // Obniz_ID
obniz.onconnect = async function () {
    // サーボモータを利用
    const servo = obniz.wired('ServoMotor', { gnd: 0, vcc: 1, signal: 2 });
    // 角度を保持する変数
    let degrees = 90.0;
    // LEDを利用
    const rgbled = obniz.wired('WS2811', { gnd: 4, vcc: 5, din: 6 });
    // ディスプレイ表示(初期画面)
    obniz.display.clear();
    obniz.display.print('I will fortune the result of president selection. Press me!');
    // スイッチの反応を常時監視
    // 「スイッチ状態が変化した瞬間に1回だけ実行される」ことに注意しましょう
    obniz.switch.onchange = function (state) {
        // スイッチの状態で角度を決め、最後に動かします
        if (state === 'push') {
            // スイッチが押されている状態
            console.log('pushed');
            const rand = Math.floor(Math.random()*2);
            if (rand === 0){
            degrees = 0.0;
            //赤
            color = 255,0,0;
            } else if(rand === 1){
                degrees = 180.0;
                //青
                color = 0,0,255;
            }
        } else if(state ==='none'){
            // スイッチが押されていない状態
            console.log('released');
            degrees = 90.0;
            color = 0,0,0;
        }
        // ディスプレイに角度を表示
        obniz.display.clear();
        obniz.display.print(`You will be the presedent of United States of America`);
        // サーボを指定の角度まで動かします
        servo.angle(degrees);
        // 党のカラーを表示;
        rgbled.rgb(color);
    }
}

なぜか、青色が光らない・・

本当は党カラーをLEDで光らせたくバイデンさんが選ばられたら民主党の青色を光らせたかったが、共和党の赤色(lgb=255,0,0)は光るが、青色(lgb=0,0,255)が光らない・・。
IMG_9338.JPG

obnizくんは共和党支持なんだと思って、諦めた。。

test.js
            } else if(rand === 1){
                degrees = 180.0;
                //この部分を255,0,0にすると赤色に光るのに
                //なぜか0,0,255にすると青色に光らない・・・なぜ?
                color = 0,0,255;
            }

創作を楽しむのが上達の近道

 電子工作はじめてだったが楽しかった。創作の箱から電子部品を開けるのも興奮するし、サーボモーターがウィーンって動く音は30過ぎの自分もときめいた。お手軽だし、わかりやすいし、電子工作初心者には、obnizいいと思った。
 創作を楽しむのが上達の近道、みたいなので、楽しみながら、少しずつでも物理世界にアプローチできるようになれば、色んなアイデアが具現化できるかも、と思えてわくわくした。時間を見つけて、色々と試作してみたい。

おまけ

 派閥の数などで何となく首相が決まる日本と違い、国民が投票して、勝つためならやりたい放題な感じのアメリカ大統領選挙が、お祭りっぽくて好きです。
 電飾ばんばんに光らせて、お祭り感をもっと出せたらよかったな。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ElectronでVulkanを動かす技術は新しい形のゲームエンジンを作り出すか?

この話の続きみたいな記事です。

https://qiita.com/nanikore55554/items/5a620bfe4f7956ae8a85

Electron(より正確に言うとNode.js上)である程度現実的な形でVulkanを動かす事に成功しました。ですが、大半の人から見れば「で、それが何の役に立つの?」と言われると思います。

で、考えました。この技術の使いみちを。

これを使えば個人でもUnityのようなゲームエンジンを開発可能?

個人的に真っ先に思ったのが「これを使えばゲームエンジン(正確には3Dモデリングツールなどといった3D開発ソフト)を個人や小さな会社でも簡単に作れるようになるのではないのか?」ということです。

というのもElectronやNW.jsなどのNode.jsを用いたGUIツールキットはマルチプラットフォームが簡単にできるということの他にHTMLやCSSの技術を転用できるため、非常に作りやすい(≒人員を集めやすく、開発スピードが早い)という長所があります。(この辺は好き嫌いが分かれやすく、node.jsの方が使いづらくて嫌だという人も多いでしょうが)

そして、この技術は同時にある二つの問題も解決します。それはGUIツールキットの先行きがElectronを除きどれもこれも不安定であること、もう一つは3D APIとGUIツールキットの相性が致命的に悪いという事です。

GUIツールキットの問題点の具体例

各種GUIツールキットの問題点を書いてみました。
これは私個人の感想も多分に含まれていますので客観的ではないかもしれませんが・・・。

Qt

  1. C++のC++のクロスプラットフォームのGUI
  2. かなりメジャー
  3. 使いやすい
  4. Vulkanにも対応可能(ただ情報が少ない)
  5. Autodesk社のMayaなど3Dツールでの採用実績あり
  6. 無料だとLGPLかGPLを強制させられる
  7. 有料版の値段が高すぎる(年に50万ぐらいかかる模様)
  8. バグの多さなどプロジェクトの先行きが不安視されている・・のか? (そういう話を聞いた事があるけど実際どうなのかよく分からない。)

C++ Builder

  1. C++のクロスプラットフォームのGUI
  2. C#の感覚で使えて使いやすい(そもそもC#の元祖の一つらしい)
  3. Vulkanが使えない(理論上は可能なようですが、現実的には相当きつい)
  4. マイナー(昔はよく使われていたようですが)

wxWidgets

  1. 昔からあるC++のクロスプラットフォームのGUIツールキット
  2. 専用ライセンスだがソースコードの公開及びリバースエンジニアリングを認める必要性がない
  3. Vulkanが動く(Githubに参考となるソースコード有り)
  4. サンプルになるコードがあまりない
  5. 使いづらい
  6. マイナー(知名度とラッパーの多さの割にあまり使われていない模様)

ImGUI

  1. OpenGL上で動くGUI。VulkanやDirectX上にも対応可能
  2. 使いづらい
  3. 日本語を打つとき問題有り
  4. そもそもデバッグなどの埋め込み用であり本格的なGUIソフトには不向き
  5. ImGUIと似たようなライブラリはいくつかあるが、どれもImGUIと同じ欠点を抱えている。

Xamarin

  1. MSが作ったC#で動くクロスプラットフォームのGUIツールキット
  2. Vulkanを使えない (理論上は不可能じゃないようですが)
  3. オワコン化扱いしているひとが結構いる

React Native

  1. Facebookが作ったC#で動くクロスプラットフォームのGUIツールキット
  2. Googleのサジェストでオワコン化扱いされている

Flutter

  1. 最近流行りのGUIツールキット。基本的にはスマホ用
  2. 使いやすい
  3. 最近はPC用の開発が進んでいる。
  4. 3D APIに対応していない(そのうち対応してもおかしくなさそうですが)
  5. まだ最近できたばかりで突然オワコン化する危険がある。(多分大丈夫でしょうが)

Electron

  1. 「ブラウザを内包して動かす」という力技で動くGUIツールキット
  2. HTMLとCSSで動くので作りやすい。
  3. Visual Studio CodeやSlackなどの採用実績有り
  4. 理由は不明だが、マイナーだったりオワコン扱いされていない。
  5. ブラウザなので重い、メモリをやたら使う
  6. 3D APIがWebGLしか使えない(よってゲームエンジンを作る時に大幅に不利になる)
  7. ただし、最後二つの問題点は・・・

要するに

長々と書いていきましたが、要するにどのGUIツールキットも致命的な問題点を抱えていて、オワコン化したりマイナーになるスピードが非常に早い。おまけに3D APIと致命的に相性が悪い

よって、個人や小さな会社がUnityのようなゲームエンジンやBlenderのような3Dモデリングツールを作るのは難易度があまりにも高すぎます。
Unity社やUEのEpic Gamesみたいな大企業ならまだしも、小さな会社が3D APIと相性が良いGUIツールキットを作るなんて無理でしょう(そもそもGUIツールキットを作ること自体難易度があまりにも高い)。しかも、GUIツールキットをを自作しても一気にオワコン化するリスクを抱えます。このリスクは大企業でも非常に高いです。

しかし、Electronはこの欠点を抱えていません。

もしElectronでゲームエンジンを作れたら

上で考察したようにGUIツールキットでオワコンに中々ならないと考えられるのはElectronのようなウェブブラウザベースのGUIツールキットぐらいです。それどころかあのMSが非常にメモリを使う、重いという欠点があるにも関わらずわざわざ自社製のGUIツールキットを使わずにElectronでVisual Studio Codeを作っているぐらいです。それだけElectronの技術は有用であり、他のGUIツールキットを使う事がElecronと比べて差がつけられている事が伺いしれます。

つまり、「Electronで3D APIが動けば、ゲームエンジンを作る上でかなり有利になる」というわけです。しかし、今までのElectronはWebGLしか動かず、ゲームエンジンを作る上ではかなり不利でした。ですが、最初に書いたように「ElectronでVulkanを動かすことに成功した」ため、この欠点が消えました。

メモリをやたら使う欠点をどう捉えるか?

しかし、Electronにはもう一つ問題があります。それは「メモリをやたら使う」事です。また、上の「ElectronでVulkanを動かす技術」は割とCPUに負担がかかります。

ですが、この問題はもはや些細な事だと思います。理由はPCの性能が上がっており、Electrnの重さはあまり気にならない状況まで来ているからです。でなきゃ、MSはわざわざElectronでVSCodeを作ったりしないでしょう。

かのRubyの作者であるまつもとゆきひろ氏が「コンピュータ資源を「人間のために使っていい時代」が来た」という趣旨の発言をしましたが、まさにゲームエンジン制作もそういう時代に来ていると思います。

無論、UEのような最新のグラフィック技術をフルに活用したゲームエンジンを作るのなら話は別ですが、それとはまた別の方向、最新の性能とグラフィック技術をフル活用しない代わりに作りやすさや拡張のしやすさを重視したゲームエンジンを作る場合、Electronは非常に有用でしょう。

個人でゲームエンジンを作る際の大きな助けとなるVulkan

もう一つ、ゲームエンジン制作で有利なのはVulkanが使えるということです。

知っている方も多いと思いますが、VulkanはOpenGLに代わるクロスプラットフォームの3D APIです。つまり、これでプログラムを書けば多くのハードで3Dのプログラムがどこでも動く代物です。実際にWindowsとAndroid、更にはあのニンテンドースイッチでも動きます。

しかも本来Vulkanに対応していないはずのMacとiOSでも動きます。というのもMoltenVKと言いまして、metalとVulkanを互換するライブラリがあるからです。
(普通にiOSとmac専用APIであるMetalを使ったほうが動作が早いプログラムを作れるでしょうが。)

更にいえばXbox Series Xでも少なくとも理論上は動きます。MoltenVKと同じでrostkatzeというDirectXとも互換するためのライブラリがあるのです。
(ただ、コンソールゲーム機はオープンソースのソフトウェアの使用制限が大変きついため実際は規約で不可能の可能性も非常に高いですが・・・)。

極端な事を言えばVulkanはOpenGLでは達成できなかった3D APIのデファクトスタンダードになる可能性を秘めており、Vulkanのソースさえ書けば3Dソフトのクロスプラットフォームは簡単に実現可能というわけです。
(最もそのVulkanのソースを書くこと自体が大変難易度が高いわけですが)

最後に感じたこと

  1. C++ Builderを作っているエンバカデロ・テクノロジーズはVulkanを使えるようにするべきでは?
  2. Firefoxを作っているMozilaはFirefox版Electronを作るべきでは?
  3. 結局書いてきたけどエンジンを自作するならUnityとUEを使ったほうが早いと言われても仕方ないと思う
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

温度センサーとLEDで室温をぱっと見で分かるものを作ってみた

目的

温度センサーとLEDを使ってみたかったので、2つを同時に使って色で室温がぱっとわかるものを作ってみました。

実現方法

使用したもの

・obniz Board 1Y
・高精度IC温度センサー LM60BIZ
・マイコン内蔵RGB 8mmLED PL9823-F8

ロジック

個人的には25℃をより下だと涼しい、25℃より上だと暖かいだと感じているので
 温度センサーで取得した温度が25℃より大きい場合、オレンジ
 それ以外の場合、水色
にしました。
obnizへは
 温度センサー 0、1、2 へ接続
 LED 9、10、11 へ接続
しています。

実際に動かしてみた

実際に動かした動画をTwitterにあげてます!
※温度センサーに指を近づけて、温度をわざとあげました。

コード

const Obniz = require('obniz');
const obniz = new Obniz('ObnizのID');  // Obniz_IDに自分のIDを入れてください

obniz.onconnect = async function () {
    // 温度センサの利用
    const tempsens = obniz.wired('LM60', { gnd: 0, output: 1, vcc: 2 });
    // RGB LEDを利用
    const rgbled = obniz.wired('WS2811', { gnd: 9, vcc: 10, din: 11 });

    // setIntervalで間隔を作る
    setInterval(async function () {
        // 同期で取得
        const temp = await tempsens.getWait();

        if (temp>25) {
            // オレンジ
            rgbled.rgb(255, 165, 0);
        } else {
            // 水色
            rgbled.rgb(0, 191, 250);
        }

        // ターミナル表示
        console.log('obniz temp:', temp);
        // 温度をコンソールに表示
        console.log(temp);
        // obnizディスプレイ表示
        obniz.display.clear();
        obniz.display.print(temp + 'C');
    }, 10000); // 10000ミリ秒 = 10秒
}

考察

最初は温度センサーとLEDを組み合わせるのはハードル結構高いのかなと思ったのですが、obnizを使うと簡単にできることを体験することができました!(2時間くらいで出来ました!)
・今は25℃固定ですが、徐々にLEDの色を変えていく
・LINEなどのツールを使って、温度確認したときに色がでるようにする
などいろいろ発展させていければと思っています!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ArchiverがZIPに含めるファイルをフィルタする

NodeモジュールArchiverは、ディレクトリをZIP圧縮するとき、ZIPに含めるファイルをフィルタすることができます。
(できないと思い込んでて、普通にできた。。)

ZIP圧縮するディレクトリ

以下のディレクトリをZIP圧縮するとします。

ZIP圧縮するディレクトリ
somedir
│  file1.txt
│
├─dir1
│      file2.jpg
│      file3.txt
│
└─dir2
        file4.jpg
        file5.txt

プログラム

プログラムは以下になります。
ディレクトリをZIP圧縮するためにdirectory関数を使いますが、第3引数にフィルタする関数を渡せます。
フィルタする関数の引数には、ZIP圧縮に含めるフォルダ・ファイルのデータが渡されます。
このデータをみて、含める場合データを返し、含めない場合falseを返します。
サンプルでは拡張子.txtのファイルを含めないようにしています。

サンプル
const fs = require('fs')
const path = require('path')
const archiver = require('archiver')

const output = fs.createWriteStream(__dirname + '/somedir.zip')
const archive = archiver('zip', {
  zlib: { level: 9 }
})

output.on('close', function() {
  console.log('finish')
})

archive.pipe(output)

archive.directory('somedir', 'somedir', filter)

archive.finalize()

function filter(entry) {
  if (path.extname(entry.name) === '.txt') {
    // zipに含めない場合falseを返す
    return false
  } else {
    // zipに含める
    return entry
  }
}

ZIP圧縮されたディレクトリの中身

ZIP圧縮されたディレクトリを解凍し、中身を見てみると、以下になります。
拡張子.txtのファイルはZIPに含まれていません。

解凍したディレクトリ
somedir
├─dir1
│      file2.jpg
│
└─dir2
        file4.jpg

非同期でフィルタできない

directory関数に渡すフィルタ関数ですが、Promiseを返せません。
async/awaitが使えず、非同期でフィルタができません。
(これはちょい残念かも)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gitのリポジトリ移行を Selenium(Node.js) +シェルスクリプトで全自動化した話

先日、業務で『今までGitのリポジトリはGitLabで管理していたが、今後はGitHubを使用する (色々と便利な機能が多いため。GitHub Actionsとか)。そのためのリポジトリ移行の取りまとめ役』 というタスクを任されました。

会社には1,000近いリポジトリがあり、これを全て手作業で移行しようとすると膨大な時間がかかります(ストレスめっちゃかかりそう…)。
その移行作業を Selenium(Node.js) +シェルスクリプトで全自動化する事に成功しました。


前提:下記の変数の箇所は、あなたの環境に合わせて逐一書き換えてください

init.sh
${あなたのsshパスワード}
${移行元 Gitアカウント}
${移行先 Gitアカウント}
create_repo.js
${あなたのGitHubアカウント名}
${あなたのGitHubパスワード}
ほか、使用する変数名(@init.sh):
①$new_repository_name: 新規に作るリポジトリ名
②$current_repo_name: 現在使用しているリポジトリ名
③$current_repo_path_with_namespace: 現在使用しているリポジトリのパス

(※)GitLab上では名前空間によってリポジトリが区切られているため、②と③を分ける必要があります。
しかし、もし『GitHubからGitHubへの以降』であれば、②③が無くとも①のみで充当できるかと思います。


まず、1件ずつ、手動で移行する際には、下記の通りにやれば移行出来ます(GitHub上でのリポジトリは既に作られてるものとする)
GitHubでも、GitLabでも同じです。

打つコマンド.
git clone --mirror git@${移行元 Gitアカウント}:$current_repo_path_with_namespace.git
cd  $current_repo_path_with_namespace
git remote rm origin
git remote add origin git@${移行先 Gitアカウント}/$new_repo_path.git
cd ..
rm -rf ".git"

全自動化すると下記の通りになります。init.shを見て頂ければ全容は掴めると思います。

ディレクトリ全容.
      +- node_modules # Seleniumを使うためにnpm installしたもの
      +- init.sh # メインの処理を①-⑦の順序で実行
      |
      +- create_repo.js  # ①でSeleniumでリポジトリを自動で作成
      |
      +- repo_list.csv  # 移行するリポジトリの一覧
      |
      +- cd.sh  # ③で、cdコマンドでディレクトリを移動したい。実際に移動してしまうと、init.sh自体を読み込めなく
なるためセッション上で移動するためにファイルを別出し
init.sh
while read row; do  #csvをループする
  new_repository_name=`echo ${row} | cut -d , -f 1`
  current_repo_name=`echo ${row} | cut -d , -f 2`
  current_repo_path_with_namespace==`echo ${row} | cut -d , -f 3`

  # ① create_repo.jsを起動。Seleniumを使ってリポジトリを作成
  node create_repo.js ${new_repository_name}

  # ② sshの対話処理を使って以降元のリポジトリをgit clone
  expect -c "
    set timeout 10
    spawn git clone --mirror git@${移行元 Gitアカウント}:$current_repo_path_with_namespace.git
    expect \"Enter passphrase for key '/Users/glengoyne/.ssh/id_rsa': \"
    send \"${あなたのsshパスワード}\n\"
    interact
  " 0<&9-

  # ③ 以降してきたリポジトリのディレクトリに移る
  source cd.sh $current_repo_name

  # ④ 移行元のリポジトリとの紐付きを解除 
  git remote rm origin

  # ⑤ 移行先のリポジトリと紐付ける
  git remote add origin git@${移行先 Gitアカウント}/$new_repo_path.git

  # ⑥  sshの対話処理を使って以降先のリポジトリへgit pull
  expect -c "
    set timeout 10
    spawn git push -u origin --all
    expect \"Enter passphrase for key '/Users/glengoyne/.ssh/github/id_github_rsa': \"
    send \"${あなたのsshパスワード}\n\"
    interact
  " 0<&9-

  # ⑦ ②でcloneしてきたリポジトリのディレクトリを削除
  rm -rf ".git"

done  9<&0- 0<repo_list.csv
create_repo.js
const {Builder, By, until} = require('selenium-webdriver');

async function CreateRepo(repo_name) {
  const driver = new Builder().forBrowser('chrome').build();
  try {
    await driver.get('https://github.com/new');
    await driver.findElement(By.id('login_field')).sendKeys(`${あなたのGitHubアカウント名}`) 
    await driver.findElement(By.id('password')).sendKeys(`${あなたのGitHubパスワード}`)
    await driver.findElement(By.name('commit')).click();
    await driver.wait(until.titleIs('Create a New Repository'), 10000);
    console.log("ログイン完了")

    await driver.findElement(By.className('js-owner-container')).click()
    await driver.findElement(By.css(".select-menu-list > label:nth-child(2)")).click();

    await driver.findElement(By.id('repository_name')).sendKeys(repo_name)
    await (await driver.findElement(By.id('repository_visibility_private'))).click();
    await driver.sleep(1000);
    await driver.findElement(By.className('first-in-line')).click();
    await driver.wait(until.urlContains(`${移行先のGitアカウント}`), 15000); // 『Create an Repository』ボタンを押してサーバー側がちゃんと処理してくれるまで、15秒待つ
    console.log("リポジトリが完成!", repo_name)
  } finally {
    await driver.quit();
    console.log("finished!!!")
  }
};


const repo_name = process.argv.slice(2)[0]
console.log(" repo_name", repo_name)
CreateRepo(repo_name)
repo_list.csv
new_repository_name1, current_repo_name1, current_repo_path_with_namespace1
new_repository_name2, current_repo_name2, current_repo_path_with_namespace2
new_repository_name3, current_repo_name3, current_repo_path_with_namespace3
new_repository_name4, current_repo_name4, current_repo_path_with_namespace4
...その下にもずーっと続く
cd.sh
cd ${current_repo_name}

リポジトリの移行方法は↑ここまで。今回の件でシェルスクリプトを書くにあたり学びとなった事を下記に記載します。

①shファイルでcsvを読み込み、行(${row})毎に処理を実行する:
csv読み込み.sh
while read row; do 
  new_repository_name=`echo ${row} | cut -d , -f 1`
  current_repo_name=`echo ${row} | cut -d , -f 2`
  current_repo_path_with_namespace==`echo ${row} | cut -d , -f 3`
done  9<&0- 0<repo_list.csv
②シェルスクリプト内でディレクトリを移動したい:

-実際にcdコマンドでディレクトリを移動してしまうと、init.shの後続スクリプトを読み込めなくなるので、cd.shのようにファイルを別出しする事で『セッション上のディレクトリ移動』が出来る。こうする事でinit.shの後続スクリプトを継続実行できる。

③対話処理(パスワード入力など)の自動化;

expect構文を使えば実行可能。下記のスクリプトでは、set timeoutで、10秒間サーバーからの応答を待つ。

expect.sh
  expect -c "
    set timeout 10
    spawn git clone --mirror git@${移行元 Gitアカウント}:$current_repo_path_with_namespace.git
    expect \"Enter passphrase for key '/Users/glengoyne/.ssh/id_rsa': \"
    send \"${あなたのsshパスワード}\n\"
    interact
  "
④ファイルディスクリプタを使用する事で、while文の中でexpectを併用する。

シェル上では、ディスクリプタ"<0"を標準入力として使っている。
while文の処理を"<0"で使っていると、expect処理が失敗してしまうため、whileの処理を0-9以外のどこかのディスクリプタに移すのが↓この部分。

done  9<&0- 0<repo_list.csv

expectの処理を"0から9のどこかのディスクリプタ"でやらせるのが、下記の部分

expect.sh
  expect -c "
  " 0<&9-

自分も、詳しく人に説明できるほど理解出来ていないので、何か間違いがあればコメントなど頂ければと思います。


普段フロントエンドエンジニアとして仕事している事もあり、シェルスクリプトなど今までまともに書いたことが無く、色々と学びになる事がありました。
シェルスクリプトで書ける事が増えてくると、本当に応用が効きそうです。
いつか、腰を据えて学んでみたいなと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

振り向かずに「俺に何か用かい?さっきから尾けてきてるね」とシティーハンターっぽく言えるようになるプロダクト

目的

 昨今ソーシャルディスタンスと言われ、人と人との距離があくようになりましたね。
 そんな中で後ろから近づいてくる人がいたら、怖いですよね。
 早く気づいて、先手を打ってシティーハンターっぽく話しかける準備をするためのツールを作りました。
 (セリフはお好みです。)
 そう、まずは気づくことが大事なのです!

 シティーハンターって何?と思った方はこちら

出来上がったもの(CHになれたか)

 こうしてみると?


 
 こうなります↓ あまりメッセージがないのでキャプチャ
 ushiro.PNG

 ★参考 取得した距離とまるめた後の距離
 image.png

 5秒単位で後ろに超音波を飛ばして、その跳ね返りから距離を測定するセンサーを使いました!
 ★参考URL
  超音波センサー

環境

 Node.js v14.9.0
 Visual Studio Code v1.49.3
 @line/bot-sdk

使い方

 1.情報を受けるプログラムを起動しておく
 2.リュックからひょっこりセンサーを出しておく
   (obnizは電源にはつないカバンの中へ。センサーはボタンかな?と思われそう。)
 3.後ろから人が尾けてくる
 4.最後は決め台詞でどうぞ!

処理の流れ

 センサーで検知!
 →obnizで拾う
 →サーバのプログラムで受ける
 →LINEサーバ
 →LINEのトーク画面にプッシュ!

今回できなかったけど、やってみたいこと

 1.セリフは自分→シティーハンターの音声を再生
 2.コードをさしっぱなし→電池式
 3.センサーで受けたことに対して、動作も何か入れたい

ソース

alert_distance
// ########################################
//          obniz処理部分
//  Obniz_ID:自分のobniz ID(XXXX-XXXX)
// ########################################
const fs = require('fs');
const Obniz = require('obniz');

// 外だしファイルからobniz_idを取得
const obniz_setting = JSON.parse(fs.readFileSync('./credential/obniz.json', 'utf8'));

const obniz = new Obniz(obniz_setting.id);

// obnizと接続確立したとき
obniz.onconnect = async () => {
    obniz.display.clear();
    obniz.display.print('obniz Ready');
}
obniz.onconnect = async function () {
    // 超音波測距センサを利用する
    const hcsr04 = obniz.wired('HC-SR04', { gnd: 0, echo: 1, trigger: 2, vcc: 3 });

    // ディスプレイ
    obniz.display.clear(); // クリア
    obniz.display.print('Stay away!');
    //obniz.display.print('Hello obniz!'); // Hello obniz! と表示

    // setIntervalで一定間隔で処理
    setInterval(async function () {
        // 距離を取得
        let distance = await hcsr04.measureWait();
        console.log(distance);
        // mに変更 小数点以下の桁が多かったので、第1位で切り捨て
        let distance_m = Math.floor(distance / 100) / 10;
        // 距離(m)をターミナルに表示
        console.log(distance_m + ' m');
        // obnizディスプレイに表示
        // 一度消してから距離+mmの単位を表示
        obniz.display.clear();
        obniz.display.print(distance_m + ' m');
        const SOCIAL_DISTANCE = 2.0;
        // 距離が一般的なソーシャルディスタンス未満かどうかの判定
        if (distance_m < SOCIAL_DISTANCE) { 
            // obnizディスプレイに近接していることを表示
            obniz.display.clear();
            obniz.display.print('Too close!!');
            // LINEにメッセージをプッシュする関数を呼び出し
            await pushAlert(distance_m);
        }

    }, 5000); // 5000ミリ秒 = 5秒おきに実行
}


// ########################################
//          LINEBot イベント処理部分
//  channelSecret:LINE Developers → チャネル基本設定 → チャネルシークレット
//  channelAccessToken:LINE Developers → Messaging API設定 → チャネルアクセストークン(長期)
// ########################################
// Messaging APIで利用するクレデンシャル(秘匿情報)です。
const credential = JSON.parse(fs.readFileSync('./credential/credential.json', 'utf8'));
const user = JSON.parse(fs.readFileSync('./credential/line_user.json', 'utf8'));

const config = {
    channelSecret: credential.channelSecret,
    channelAccessToken: credential.channelAccessToken
};
const userId = user.source.userId;

const line = require('@line/bot-sdk');
const client = new line.Client(config);
// ソーシャルディスタンス内に人が近づいたらメッセージプッシュ!Messaging APIイベントを渡されて処理するところ
function pushAlert(distance_m){
    try{
        client.pushMessage(userId, {
                type: 'text',
                text: '後ろに、、、いるよ、、、距離 '+ distance_m +'m',
        });
    }catch(err){
        console.log(err.name +  ': ' + err.message);
        process.exit(-1);
    }
    // resolveを返す
    return Promise.resolve(null);
};
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

npm run startを実行するまで~初めてのNode.js動作確認~

背景

実務経験ないまま、共同開発を経験させていただく機会があり
npm run startでブラウザが立ち上がるまで動作確認する必要があった。
内容自体は単純ではありますが、覚えも含めて投稿させていただきます!
また、この動作確認の一連の流れのまとめがなかったため、投稿してみようと思った次第です。

前提

  • 2020/10時点での状況
  • Windows使用者
  • 当時の知識:「npm」「Node.js」というワードを始めて聞いた...
  • Git/GitHubを全く触ったことがなかった...

作業手順優先で詳細は本投稿では省いています。
詳細はリンク先で記載がありますので、申し訳ないですがそちらをご参照くださいm(__)m

手順

1. GitHubにアカウント登録・GitHub内でリポジトリを作成

作業箇所:ブラウザ
https://qiita.com/kutarou197/items/8acea22ae36dbcf45c9f

2. Node.jsのインストール

作業箇所:ブラウザ
https://qiita.com/sefoo0104/items/0653c935ea4a4db9dc2b

npmとはNode Package Managerの略です。
ということでNode.jsのインストールが必須です。
Node.jsのインストールと同時にnpmもインストールされます。
※ちなみに自分は「Node.jsのインストールが必要」ということに気づかず、Node.jsをインストールせずにcmd(=コマンドプロンプト)にコード打込んでエラー出して悶絶、というのを3時間程続けていました(笑)

※尚、cmdはwindowsがデフォルトで持っているツールで、スタートメニュー横の虫眼鏡ボタンで「コマンドプロンプト」で調べると写真のように出てきます。
image.png

3. nodistでバージョン管理する

- nodist のインストール

作業箇所:ブラウザ
https://qiita.com/satoyan419/items/56e0b5f35912b9374305

- バージョン管理

作業箇所:cmd(=コマンドプロンプト)
https://qiita.com/satoyan419/items/56e0b5f35912b9374305
↑「nodistのインストール」と同じリンクです。

共同開発する際、指定されたNode.jsのバージョンに変更
Node.jsのバージョンによってnpmも対応するバージョンが異なるので、npmのバージョン管理もお忘れなく!

尚、リンク先のnpxのインストールは今回は必要ありませんが、困ることはないので一緒にインストールしておいてもいいかもしれません。

4. GitHubで作成したリポジトリをクローンする

作業箇所:cmd

git clone リポジトリのURL(https~)

5. ディレクトリを変更し、Gitのremote urlを変更

作業箇所:cmd

cd リポジトリ名
でディレクトリ移動し
Remote url set-url origin リポジトリのURL(https~)
でリモートURLを変更
https://qiita.com/minoringo/items/917e325892733e0d606e

6. リモートリポジトリにプッシュする

作業箇所:cmd
https://qiita.com/yukibe/items/9ef9d54f2e7d53cfb51c
git push origin master -f
上記コマンドを入力して、リンクにある「リモートリポジトリに反映」の「push」のような画面が出たらOKです

7. 動作確認

作業箇所:全てcmd(=コマンドプロンプト)

- npm packageインストール

既製のパッケージをリモートリポジトリにインストール
npm install

- ファイルやプロジェクトの実行

npm run start
でブラウザが立ち上がったら完了!!
(この時点では真っ白のブラウザかと思います。)

※npmとは?:既に作られたNode.jsのパッケージ
https://qiita.com/righteous/items/e5448cb2e7e11ab7d477


ほんとに作業内容しか記述していないので、各種作業により端末内で何が起こっているのかは各リンクや関連記事をご参照ください!

最後まで読んでいただき、ありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

heroku update available from 7.42.6 to 7.46.0

herokuコマンドを実行したときにこれが出た。

»   Warning: heroku update available from 7.42.6 to 7.46.0.

これを実行すればOK

heroku update

※↑自分の場合、結構時間がかかった。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

@vue/cliのインストールからVueプロジェクト作成まで

はじめに

今回は@vue/cliのインストールをしてから実際にVueプロジェクトを実行するまでを書いていきます!
Node.jsがインストールされている必要があるので、
まだインストールできていない人はNode.jsのインストール方法(Windows)を見てね!

目次

  1. @vue/cliのインストール
  2. vue create コマンドでVueプロジェクトの作成
  3. 実際にブラウザで確認してみよう!

1. @vue/cliのインストール

  • @vue/cliはVueプロジェクトの雛形を作るのを便利にしてくれるコマンドラインインタフェースです! 以下のコマンドを実行するとインストールできます!
$ npm install -g @vue/cli
  • @vue/cliが正しくインストールされているかを確認する場合は「vue --version」を実行し、 バージョン情報が表示されればOK!
$ vue --version
@vue/cli 4.5.7

2. vue create コマンドでVueプロジェクトの作成

  • 「vue create project-name」を実行すると作成されます(project-nameは自分の好きな名前で作れます)
$ vue create sample
  • 以下のように表示されたらエンターでOK!

1.png

  • Vueプロジェクトの雛形が以下のように生成されます

2.png

3. 実際にブラウザで確認してみよう!

  • 作成されたプロジェクトフォルダに移動し「npm run serve」を実行!
$ cd sample
$ npm run serve

3.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JS/GCS】ファイルアップロードオプション

内容

GCSに画像やら動画やらをアップロードする実装についてメモしておきます。

fs.createReadStream

下記のドキュメントで実装されています。
Cloud Storage の使用  |  Node.js ドキュメントに対応した App Engine フレキシブル環境

上記で実装している blob.createWriteStream メソッドの中身を見てみると以下のようなサンプル実装もあり、参考になります。

import * as fs from 'fs'

fs.createReadStream('panda.jpg')
  .pipe(file.createWriteStream({
    metadata: {
      contentType: 'image/jpeg',
      metadata: {
        custom: 'metadata'
      }
    }
  }))
  .on('error', function(err) {})
  .on('finish', function() {
    // The file upload is complete.
  });

ファイル作成

また createWriteStream を使って、バイナリからファイルを作成することも可能です。

const stream = fs.createWriteStream(filename); // 'image.png' とか 'video.mp4' とか
stream.write(binary) // バイナリデータをファイルに書き出す

bucket.upload

下記の場合だとサーバー上に画像が吐き出される形になります。

const bucket = this.gcs.bucket('staging');

bucket.upload(filename, {
  // GCSのフォルダ構成を指定
  // filenameは、'image.png' とか 'video.mp4' とか
  destination: `test/${filename}`,
  gzip: true,
  metadata: {
    cacheControl: 'no-cache',
  },
}).catch((err)=>{
  console.error(`Failed to Upload`)
});

Google Cloud Storageにファイルを保存する - iyuichiの私的開発ログ

file.save

バイナリをそのままアップロードする方法です。
save メソッドの引数がバイナリでも受け取ることができるので(any型)、ファイルをサーバー上に作成せずともGCSにアップロードすることが可能です。

const bucket = this.gcs.bucket('staging'); // バケットの指定
const blob = bucket.file(`test/${filename}`); // ここでフォルダ構成 + ファイル名を指定できる

return blob.save(binary) // バイナリデータを指定
  .then(() => {
    return file.getSignedUrl({ // 必須項目は、action と expires
      action: 'read',
      expires: '03-09-2500',
    })
  })
  .then((urls: any[]) => {
    const url = urls[0];
    return url
  })
  // 特に expires とか設定しないのであれば、`https://storage.googleapis.com/staging/test/${filename}` とかで返すことも可能
  .catch((err: any) => {
    console.log(`Unable to upload encoded file ${err}`)
  })

参考にした記事
uploading buffer or string · Issue #2334 · googleapis/google-cloud-node · GitHub
File - Documentation

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Discord.jsでWebhookを利用したグローバルチャットの作り方

はじめに

この記事では、webhookを利用したグローバルチャットのサンプルコードを書きます。
投稿するのは、初めてなので何か間違っていたら、教えてください。

必要なもの

  • fs
  • discord.js v12.3.0
  • Botアカウント

(Botアカウントの設定や、Discord.js、fsのインストールなどはすでに済ませている前提で書きます。)

コード

const Discord = require("discord.js")
const client = new Discord.Client()
const fs = require("fs")
const prefix = "?"

client.on("ready", () => {
  console.log(client.user.tag + "でログイン中")
});
client.on("message", message => {
  if (message.author.bot) {
    return;
  }
  if (message.channel.type == "dm") {
    return;
  }
  if (message.content == prefix+"globalchatjoin") {
    if (!message.channel.permissionsFor(message.guild.me).has("MANAGE_WEBHOOKS")) {
      message.channel.send("Webhookを作成する権限がありません。")
      return;
    }
    message.channel.createWebhook('グローバルチャット用webhook').then(webhook => {
      var webhookinfo = {
        "id": webhook.id,
        "token": webhook.token,
        "channel": message.channel.id
      }
      var savedata = JSON.stringify(webhookinfo);
      try {
        fs.mkdirSync(`globalchatfiles/${message.guild.id}/`, { recursive: true });
        fs.writeFileSync(`globalchatfiles/${message.guild.id}/webhook.json`, savedata);
        //成功すれば、Webhookが保存されます。
      }
      catch (error) {
        message.channel.send("参加できませんでした。")
        return;
      }
      var sentchannelid = webhook.channel
      const webhooks = new Discord.WebhookClient(webhook.id, webhook.token)
      webhooks.send("グローバルチャットに参加しました。")
      //ほかのサーバーに参加通知を送る
      //サーバーごとにファイルを読み込んで、webhookで送信する。
      client.guilds.cache.forEach(guild => {
        try {
          var webhookjoined = JSON.parse(fs.readFileSync(`globalchatfiles/${guild.id}/webhook.json`))
        } catch (err) {
          return;
          //参加していなければ、そのサーバーはパスする。
        }
        var channelid = webhookjoined.channel
        try {
          client.channels.cache.get(channelid).id
        }
        catch (error) {
          return;
          //チャンネルが削除されていたら、動作をキャンセルするコード。
        }
        var webhookid = webhookjoined.id
        var webhooktoken = webhookjoined.token
        if (message.channel.id == sentchannelid) return;
        if (message.guild.id == guild.id) return;
        try {
          new Discord.WebhookClient(webhookid, webhooktoken).send(message.guild.name + "が、グローバルチャットに参加しました。", { username: "グローバルチャットマネージャー", disableMentions: "all"})
        } catch (error) {

        }
      })
      //webhookは、チャンネルごとに10個までしか作れないので、作成できなかった場合には、参加成功メッセージが来ない仕組み。
    }).catch(console.error);
  } 
});
client.on("message", message => {
  if (message.author.bot) {
    return;
  }
  if (message.channel.type == "dm") {
    return;
  }
  try {
    const guild_webhook = JSON.parse(fs.readFileSync(`globalchatfiles/${message.guild.id}/webhook.json`))
    var sentchannelid = guild_webhook.channel
  } catch (error) {
    return;
    //読み取れなかった場合、ほとんどの場合は参加していないのでリターンする。
  }
  if (message.channel.id == sentchannelid) {
    //サーバーごとにファイルを読み込んで、webhookで送信する。
    client.guilds.cache.forEach(guild => {
      try {
        var webhook = JSON.parse(fs.readFileSync(`globalchatfiles/${guild.id}/webhook.json`))
      } catch (err) {
        return;
        //参加していなければ、そのサーバーはパスする。
      }
      var channelid = webhook.channel
      try {
        client.channels.cache.get(channelid).id
      }
      catch (error) {
        return;
        //チャンネルが削除されていたら、動作をキャンセルするコード。
      }
      var webhookid = webhook.id
      var webhooktoken = webhook.token
      const serverwebhook = new Discord.WebhookClient(webhookid, webhooktoken)
      if (message.channel.id == channelid) return;
      if (message.guild.id == guild.id) return;
      try {
        serverwebhook.send(message.content, { username: message.author.tag, avatarURL: `https://cdn.discordapp.com/avatars/${message.author.id}/${message.author.avatar}.png`, disableMentions: "all"})
      } catch (error) {
      }
    })
  }
});

client.login("トークンをここに記述")

これ単体でも、Botは動くと思うので、お試しください。

説明

権限不足時

if (!message.channel.permissionsFor(message.guild.me).has("MANAGE_WEBHOOKS")) {
  message.channel.send("Webhookを作成する権限がありません。")
  return;
}

このコードは、webhookを作る権限がBotに与えられていないときにキャンセルするためのコード

disableMentions

webhookで送信するときのコードに書かれている disableMentions: "all" は、@everyoneや、ユーザーに対するメンションを無効化するためのコード
(ユーザーに対するメンションを許可するには、everyoneに設定する。)

参考サイト

公式ドキュメント

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む