いーじすの戯言

戯言を書く予定です。しょうもなさ全開

Discord.jsのBotでみまもり君を作る&絵文字を送る

動機とか

新型コロナウイルスのおかげて友人とdiscordで通話しながら作業する機会が増えたので、誰かが通話し始めたら通知が行くと嬉しいな。みたいな感じで作りはじめた。

と言ってもそんなに真面目な目的ではなく、まあdicordのapiとか触ってみたいと思っていたし〜みたいなかるーいノリ
pythonかjsが主流(C#とかもあるらしい)ぽいので、とりあえずjsで書いてみた。
あと、そのサーバでカスタム絵文字で遊ぶ文化があるので、絵文字も送ろうと思ったがちょっと一手間必要で調べてもイマイチぴんと来る資料が見つからなかったので、合わせてまとめて書いた

内輪向けのコードがほぼそのまま乗っているので、一部の人にしか通じないネタがあるが理解できなくてもbotは作れるのでご安心を。気づいたら適当にツイートでもしてくだされ。

前提環境

ssh クライアント

  • MacBook Pro 13-inch Mojave
  • Visual Studio Code
  • サーバー

  • Raspberry Pi 3 Model B+
  • Rasbian 10.3
  • 環境構築

    サーバの設定

    npmとか色々入れる

    sudo apt-get update
    sudo apt-get upgrade
    sudo apt-get install -y nodejs npm
    sudo npm cache clean
    sudo npm install n -g
    sudo n stable
    sudo ln -sf /usr/local/bin/node /usr/bin/node

    serverの適当なところにフォルダを用意し、Erisを入れる

    mkdir discordBot
    cd discordBot
    mkdir watchingBot
    sudo npm install eris
    discordの設定

    サーバーの作成を先にしておいてください ここにアクセス
    左上のNew Applicationを押してBotを作成
    NAMEやアイコンを適当に決め、OAuthのタブに移動
    BOTにチェックを入れ、下のAdministratorにチェック
    Adminにしなくてもいい場合もあるが、後から直すのも面倒なのでこのままで
    この状態でhttps://discordapp.com/api/oauth2/authorize?client_id=〜の形で表示されているurlをコピーし、開くとサーバに追加する画面が表示される
    作成した任意のサーバを選択し、認証
    Botのタブに移動し、TOKENをコピーのボタンを押してTOKENを取得
    あとで使うのでタブを開けておくか、適当に貼っておくかする

    このTOKENは絶対に公開してはいけません

    大荒れしたこともあるらしい。なむなむ。
    githubとかにこのTOKENが上がると自動でdiscordがTOKENを更新して事故(事件)を防ぐらしいけど、気をつけようね。

    本編

    やっていきます
    Visual Studio Code(以後vscode)で新規ファイル index.jsを作成
    vscodesshをしてコーディングをする方法があります。ググってみて

    動作テスト

    まずはEris公式にあるサンプルコードをコピペ

    var bot = new Eris("BOT_TOKEN");
    bot.on("ready", () => {
        console.log("Ready!");
    });
    bot.on("messageCreate", (msg) => {
        if(msg.content === "!ping") {
            bot.createMessage(msg.channel.id, "Pong!");
        }
    });
    bot.connect();
    

    !pingというメッセージを送信するとPong!と返すだけのプログラム
    保存したらindex.jsがあるディレクトリで

    npm index.js

    Botが動きます
    起動前はbotはオフライン表示になっていますが、起動するとちゃんとオンラインになってるはず
    そして!pingと送るとPongと返ることも確認できます

    そしたらカスタマイズして行く

    abal.moe ここの公式サイトを見れば大体のことはできる

    みまもり君のコーディング

    ここでは監視君(?)に必要そうな動作として

  • voice channelに人が来たら指定のtext channelで通知
  • voice channelから人が抜けたら指定のtext channelで通知
  • を実装する

    
    const TOKEN = "??????";
    const Eris = require("eris"); 
     
    //Eriisを作る
    var bot = new Eris(TOKEN); 
    
    var guildId = '000000000000000"';
    var channelId = "000000000000000";
    
    var emojiArray={};
     
    bot.on("ready", () => { 
      console.log("ready"); 
      bot.createMessage(channelId, "進捗みまもり隊が起動しました。進捗どうですか?");
      emojiLoad();
    });
    
     
    bot.on("voiceChannelJoin", (member, newChannel) => { 
      //入室処理
      let ch = newChannel.guild.defaultChannel; 
      
      var reply =  member.username + "さんが チャンネル[" + newChannel.name + "] に入室しました" + emojiArray['sintyoku_dodesuka'];
      bot.createMessage(channelId, reply); 
    }); 
    
    bot.on("voiceChannelSwitch", (member, newChannel, oldChannel) => { 
      //入室処理
      let ch = newChannel.guild.defaultChannel; 
      
      var reply =  member.username + "さんが チャンネル[" + newChannel.name + "] に移動しました";
      bot.createMessage(channelId, reply); 
    }); 
     
    bot.on("voiceChannelLeave", (member, oldChannel) => { 
      // 退室処理 
      let ch = oldChannel.guild.defaultChannel; 
      console.log("%s さんが チャンネル %s を退室しました。:oyasumi:", member.username, oldChannel.name); 
      var reply =  member.username + "さんが チャンネル[" + oldChannel.name + "] を退室しました。"+emojiArray['oyasumi']
      bot.createMessage(channelId,reply);
    }); 
    
    bot.on("messageCreate", (msg) => {
      //Botの投稿を無視する
      //自分自身だけでなく、他のBotも無視する
      if(msg.author.bot)return
      console.log(emojiArray['dame']);
    
      if(msg.content === emojiArray['iidesuyo']) {
          //iidesuyoの絵文字を受け取るとgood_poemの絵文字を返す
          bot.createMessage(msg.channel.id,emojiArray['good_poem']);
          console.log("ok");
      } else if(msg.content === emojiArray['dame']) { 
          //dameの絵文字を受け取るとdamekaの絵文字を返す
          bot.createMessage(msg.channel.id,emojiArray['dameka']);
          console.log("not ok");
      }
    });
     
    //Discordに接続
    bot.connect(); 
    
    function emojiLoad(){
      //serverを特定
     
      var guild = bot.guilds.find(guild => guild.id == guildId);
      console.log(guild.emojis);
      makeEmoji(guild,'sintyoku_dodesuka');
      makeEmoji(guild,'oyasumi');
      makeEmoji(guild,'iidesuyo');
      makeEmoji(guild,'dame');
      makeEmoji(guild,'dameka');
    }
    
    //絵文字の文字列
    function makeEmoji(guild,_name){
      emojiArray[_name]= "<:"+_name+":"+guild.emojis.find(emoji => emoji.name == _name).id +">";
    }
    
    コードの解説
    var guildId = '000000000000000"';
    var channelId = "000000000000000";
    

    もちろん000000〜はダミー
    channelIdはテキストチャンネルのIdで、discordアプリ内でチャンネルを右クリックするとIDをコピーというのがある
    ない場合はユーザー設定->テーマ->開発者モードをオンにすると見れる
    guildIdはサーバ名を右クリックで見れる。guildとはこの場合Discordの各サーバのこと

    bot.on("ready", () => { 
      console.log("ready"); 
      bot.createMessage(channelId, "進捗みまもり隊が起動しました。進捗どうですか?");
      emojiLoad();
    });
    

    botが実行された時に一度だけ実行される
    bot.createMessage(channelId, "進捗みまもり隊が起動しました。進捗どうですか?"); のように、createMessagenにチャンネルIDと発言内容を渡してやるだけで送ることができる

    bot.on("voiceChannelJoin", (member, newChannel) => { 
      //入室処理
      let ch = newChannel.guild.defaultChannel; 
      
      var reply =  member.username + "さんが チャンネル[" + newChannel.name + "] に入室しました" + emojiArray['sintyoku_dodesuka'];
      bot.createMessage(channelId, reply); 
    }); 
    

    voiceChannelJoinnは誰かがvoiceチャンネルに入った時に実行されるイベントになる
    voiceChannelLeaveはいなくなった時、Switchは移動した時に実行
    member.usernameのから名前も取れるのでメンションも簡単につけられる

    bot.on("messageCreate", (msg) => {
      //Botの投稿を無視する
      //自分自身だけでなく、他のBotも無視する
      if(msg.author.bot)return
      console.log(emojiArray['dame']);
    
      if(msg.content === emojiArray['iidesuyo']) {
          //iidesuyoの絵文字を受け取るとgood_poemの絵文字を返す
          bot.createMessage(msg.channel.id,emojiArray['good_poem']);
          console.log("ok");
      } else if(msg.content === emojiArray['dame']) { 
          //dameの絵文字を受け取るとdamekaの絵文字を返す
          bot.createMessage(msg.channel.id,emojiArray['dameka']);
          console.log("not ok");
      }
    });
    

    messageCreateは誰かがmessageを送った時に実行される
    bot自身の送信を拾わないようにしている(無限ループは起こる可能性がある)
    あとはそのまま。簡単だね

    //Discordに接続
    bot.connect(); 

    繋ごう。

    function emojiLoad(){
      //serverを特定
     
      var guild = bot.guilds.find(guild => guild.id == guildId);
      console.log(guild.emojis);
      makeEmoji(guild,'sintyoku_dodesuka');
      makeEmoji(guild,'oyasumi');
      makeEmoji(guild,'iidesuyo');
      makeEmoji(guild,'dame');
      makeEmoji(guild,'dameka');
    }
    

    bot.guilds.findでサーバ名を確かめる
    makeEmojiでemojiArrayに絵文字の文字列を追加
    botで絵文字を送るときは :thinking_face: のように送ってもただの文字列になってしまうので、

    //絵文字の文字列
    function makeEmoji(guild,_name){
      emojiArray[_name]= "<:"+_name+":"+guild.emojis.find(emoji => emoji.name == _name).id +">";
    }
    

    これで <:thinking_face:123456789>みたいにしてやる
    これでサーバに追加したカスタム絵文字が使える。
    nitro課金をしていないのでgifでもできるかは不明。できそうだけど

    終わり

    こんな感じでdiscordとjavascriptで簡単にbotが作れる
    今回はサーバマシンを別に用意したが、macとかでも常駐させればいいし、glitch みたいなサービスを使ってもいい



    …個人的にGASに拡張してみたい
    それでは。

    ご挨拶と夜の浅草寺

    技術ブログだけじゃなくて写真もここに上げていきたいと思います

    機材・環境

    カメラ
    レンズ
    • Sony Eマウント 標準パワーズームレンズ 3.5-5.6/16-50
    • Sony Eマウント キット望遠レンズ 4.5-6.3/55-210
    編集

    今回の写真

    f:id:u1218:20191209001948j:plain
    Sony α6300
    28mm
    f:4.5/1.3sec
    isoは多分100
    Lightroomで自動簡易レタッチ

    「月」「銀杏」「五重塔」という神がかった組み合わせですが
    まあ、月が小さすぎますね

    しかも満月でも半月でも三日月でもない、中途半端な形なんですよね…

    まあアングルも正直納得はいっていない
    銀杏は良い感じでかぶさってるのでまあ良いんですが
    月と五重塔の位置関係をどうすれば良いのかわからないです

    …これ月いらなかったのでは()


    銀杏の葉の影が露骨だなって思うんですけど、元絵もなんか露骨に
    下から明かりを当たられてるので仕方ないんですよね
    まあ範囲指定してちゃんとやれば良いんですけど。


    2枚目
    f:id:u1218:20191209004029j:plain
    Sony α6300
    22mm
    f:5.6/1.6sec

    Photoshopでトリミング&微加工

    スカイツリーと門の組み合わせ
    この二つは良いんだけど、なにせ背景が難しいので
    スカイツリーが赤色装飾なのを良いことに青の彩度をガタ落ちさせてみました

    いや、とても微妙
    トリミングも下に変なものが写り込んでたからだし、
    明らかに撮影が悪い

    赤が色あせた感じなのと、スカイツリーのディテールが
    割と綺麗だったので少しいじってみました

    これ、撮影時の構図は悪くないのにトリミングして良くない感じに
    なったしまった感がある

    まあお寺だから仕方ないんだけどなぁ、という感想

    ロケーション

    浅草寺には何回か来たことはあったのですが、カメラを持ち込んだのは初
    しかも夜が初めてとは…w

    日曜日ということもあってか、外国人観光客と酔っ払いと同業が
    そこそこ出没していたので意外と閑散とした絵は取れないです
    でもそれを踏まえてもライトアップは綺麗ですね
    個人的には本堂とか雷門よりも
    五重塔の光の感じが好きです
    他のもとっても良いんですけどね

    仲見世通りのシャッターがおしゃれなので
    お店閉まってるからなぁ、と思わずに覗きに行く価値ありですね

    ついでに夜ご飯は近くのもんじゃ屋さんで
    下町の良い雰囲気のお店がたくさんあるので是非是非

    それでは。

    UnityでVRで頑張って作る(2)

    やってやりましょう!
    (一行さん可愛い)

    前記事
    aegis.hatenablog.jp


    次の目的として、動画の再生をできるようにする
    とりあえずplainオブジェクトを作ってそこに流したい
    友人からもらったmp4のデータを取り込んで、VideoPlayerで再生する方法を試す
    f:id:u1218:20191017172829p:plain
    照明は当てていないのでunLitのシェーダを設定するのを忘れずに

    f:id:u1218:20191017173510p:plain
    ちゃんと再生されています(わかりにくいけど)

    そしてジャイロでみる方向にスクリーンを表示してみる

    f:id:u1218:20191017174724p:plain
    まずjyroのスクリプトをカメラにアタッチし、HMDの向いている方向をシーンのカメラに合わせる
    これでだいぶVRっぽくなった

    写真のvirtualFieldはそんなに意味はないのでスルー

    virtual CameraをDiveカメラと同じ位置に配置
    virtual Cameraにもgyroのスクリプトをアタッチ
    これがスクリーンの親になっている

    そしてVideoPlayerがアタッチされているPlaneであるmovieFrameを子オブジェクトに設定
    movieFlameを視線の先に適度に離して設置
    f:id:u1218:20191017175323p:plain

    こうすることで、virtualCameraはカメラと同じ挙動を示すようになった
    視線がどこを向いているのかわかりにくかったので、Canvasを二つ作って左右のcameraのいちに設置、
    真ん中に四角いカーソルを設置しました
    f:id:u1218:20191017175706p:plain
    画像は使い回しなので雑です。とりあえずわかればいいんだよ。

    これで「カメラの向いている方法に映像が追従」するようなプログラムになった。


    とりあえずここまで

    UnityでVRで頑張って作る(1)

    やっていく
    前記事
    aegis.hatenablog.jp


    それにしても頭の悪そうなタイトルである

    環境

    MacBook Pro(13, 2018)
    Unity 2019.1.8.f1

    デバッグ環境
    iPhone 5S
    iOS12.2

    ビルド環境
    iPhone SE
    iOS13.1.2

    とりあえず、普通に3Dでプロジェクトを作成し、
    UnityにはVRの機能が標準であるらしいので調べてみると、

    unity3d.com

    公式のチュートリアルもあったので読んでみた

    Unity は特定の VRバイスのためにビルトインサポートを導入しました。この手引きは Oculus 社製の VRバイス ― 特に、Oculus Rift Development Kit 2 (DK2) と コンシューマ版 Gear VR (Samsung Galaxy S6、S6 Edge、S6 Edge+、Note 5 ハンドセット用のモバイルヘッドセット) ― に焦点を当てています。

    このドキュメントはすべての VR HMD に関連した内容を提供します。詳細に関しては使用するVR HMDのドキュメントをご覧ください。

    うるせぇ!!!HMD持ってないんじゃ!!!!

    ...はい、別の方法を検証しましょう
    そこで上がるのがスマホのジャイロセンサをトラッキングに使って、
    映像もそのまま画面に出力するというもの

    arvr.google.com

    このGoogle CardBoardを使う
    どうやらGoogleさんは公式でSDKを出してくれているようですが、今回はDive Unity Pluginというものを使う
    こちらの記事を参考にしました

    freesworder.net

    この方の記事と同様にDive_Cameraをヒエラルキーに入れて、
    f:id:u1218:20191015233116p:plain

    メインカメラを消して、ジャイロのスクリプトを書く

    public class jailo : MonoBehaviour
    {
        Quaternion currentGyro;
    
        void Start()
        {
            Input.gyro.enabled = true;
        }
    
        void Update()
        {
            var rotRH = Input.gyro.attitude;
            var rot = new Quaternion(-rotRH.x, -rotRH.z, -rotRH.y, rotRH.w) * Quaternion.Euler(90f, 0f, 0f);
    
            transform.localRotation = rot;
        }
    
        void OnGUI()
        {
            var g = Input.gyro.attitude;
            GUILayout.Label(g.x.ToString());
            GUILayout.Label(g.y.ToString());
    
            GUILayout.Label(g.z.ToString());
            GUILayout.Label(g.w.ToString());
    
        }
    }
    

    gyroの綴りが違うのはご愛嬌(は?)

    とりあえずカメラではなく、適当なキューブにアタッチして試します
    f:id:u1218:20191015233227p:plain

    動いたのでカメラにアタッチ

    位置関係とかを取るために、droidくんを配置
    f:id:u1218:20191015233227p:plain

    スクリーンショット
    f:id:u1218:20191015235316p:plain

    ちゃんと右目カメラと左目カメラが重なってますね

    実を言うと別に二眼である意味はないのですが、持っているダンボーHMDで、端末サイズと端子の位置的に
    二眼のcardboardがいいなって言う話です。

    これでヘッドトラッキングができました。

    では

    次の記事
    aegis.hatenablog.jp

    Unity Remote5とiOS13間のInput.Gyro取得不具合について.2

    Unity Remote 5 ネタです
    詳細は前の記事
    aegis.hatenablog.jp
    にあるので、そちらを参照ください

    さて、前回触れ忘れていたことについて。

    Unity Remote 5 はUnity 5向けです!!


    ......過去の自分をぶん殴りたい
    見てみると確かにUnity Remote5は三年前のver2.0から更新されていないし、
    それ以降のバージョンがサポートされていないのも当然のこと。


    ...愚かだ.........



    Unityのバージョンを5に下げてやってもいいんですが、これまた前述の記事の通り、
    iOS12でなら正常に動くんです。
    なのでiOS12の端末を使えばいいんですが、当方のiPhoneSEは13になってしまっている

    正規の手段ではダウングレードできないっぽいので、家の中に眠っているiPhoneを探してみた結果


    ...ありましたよ、iOS12.2のiPhone5sが。


    え?古すぎ?そんなんでできるのかだって?
    ...わからん


    でも幸いなことにSEと5sは画面サイズが一緒なので、Unityエディター上でデバッグするなら5s,
    ビルドしてちゃんとテストするならSEでやればいいこと

    13の不具合を矯正するコードを書いてもよかったんですが、ハードの変更で済んでしまうならとりあえずはいいかなと
    13のアプデでちゃんと取得できるようになったりしたらいいな()


    では。

    Unity Remote5とiOS13間のInput.Gyro取得不具合について.1

    検証環境

    PCサイド

    • MacBookPro 13inch 2018
    • macOS Mojave 10.14.6
    • Unity 2019.1.8f1
    • Xcode 11.1

    iPhoneサイド

    概要

    Unity2019.3とiPhoneのUnity Remote5を用いて、iPhoneSEとMacを有線で接続し、
    Input.Gyroの値をUnityのPlayで取得すると、値が特定の角度で非連続的になってしまう問題を発見しました

    オブジェクトの角度が飛んでいる?のと
    重力方向を軸とした時の回転が正常に取得できていません


    上記環境でビルドしたプログラムをiPhoneにインストールして実行した結果、ジャイロの値は正常に取得できたので、Unity Remote5上でデバッグした時に発生する問題です
    以下はビルドして実行した動画


    ツイートでも書いているのですが、iOS12.3の端末を接続してテストすると、正常にジャイロの値が取得できるので、OSのバージョンによって挙動が異なるようです

    取得するためのc#はいたってシンプルです

    //getGyro.cs
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class getGyro : MonoBehaviour
    {
        Quaternion currentGyro;
    
        void Start()
        {
            Input.gyro.enabled = true;
        }
    
        void Update()
        {
            var rotRH = Input.gyro.attitude;
            var rot = new Quaternion(-rotRH.x, -rotRH.z, -rotRH.y, rotRH.w) * Quaternion.Euler(90f, 0f, 0f);
    
            transform.localRotation = rot;
        }
    
        void OnGUI()
        {
            var g = Input.gyro.attitude;
            GUILayout.Label(g.x.ToString());
            GUILayout.Label(g.y.ToString());
    
            GUILayout.Label(g.z.ToString());
            GUILayout.Label(g.w.ToString());
    
        }
    }
    

    iOS13端末上のiPhoneでRemoteを使うならばその飛びを補完するコードが必要になりそうです
    しかし重力軸回転のブレは単純に加速度センサの値を取ってきているようにも見えるので、時間があったら検証します

    では。

    UnityでVRで頑張って作る(0)

    疲れたけど一応書いておきます

    過去に、インタラクティブでない全天球映像について研究していたことがあって、
    その編集は普通AviUtlやiMovieなどの「普通の映像編集ソフト」を使っていて感じたのが、


    ...これめちゃくちゃ編集大変じゃんしんどい


    なので全天球映像編集ツールをUnityで作りたい作れる作ろうと言っていたのですが
    なんか気が向かなかった(←は?)のと
    HMDを持っていないのとで
    開発に取り掛かっていなかったんですよね

    ってことをTwitterで呟いたら、TLの有識者から色々アドバイスをもらったので
    モチベが爆上がりなので
    あと当時患ってた胃炎も治って心身共に調子がいいので

    「全天球映像を直感的に編集できる作業空間をクリエイターに提供したい」の意思の元
    完全独学ですがVR映像編集ソフトのプロトタイプを作ろうと思います

    ... 続かなかったらごめんなさい。
    ゆっくりやります

    この記事を書いている時点でpart1分くらいの作業はできたんですが、疲れたので
    明日以降の私にお願いしてゆっくりすることにします

    あと温泉ランドに行ったこともちゃんと記事にしようかな、また行きたいし

    では。

    次の記事
    aegis.hatenablog.jp