Discord.jsのBotでみまもり君を作る&絵文字を送る
動機とか
新型コロナウイルスのおかげて友人とdiscordで通話しながら作業する機会が増えたので、誰かが通話し始めたら通知が行くと嬉しいな。みたいな感じで作りはじめた。
と言ってもそんなに真面目な目的ではなく、まあdicordのapiとか触ってみたいと思っていたし〜みたいなかるーいノリ
pythonかjsが主流(C#とかもあるらしい)ぽいので、とりあえずjsで書いてみた。
あと、そのサーバでカスタム絵文字で遊ぶ文化があるので、絵文字も送ろうと思ったがちょっと一手間必要で調べてもイマイチぴんと来る資料が見つからなかったので、合わせてまとめて書いた
内輪向けのコードがほぼそのまま乗っているので、一部の人にしか通じないネタがあるが理解できなくてもbotは作れるのでご安心を。気づいたら適当にツイートでもしてくだされ。
前提環境
ssh クライアント
サーバー
環境構築
サーバの設定
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を作成
vscodeでsshをしてコーディングをする方法があります。ググってみて
動作テスト
まずは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
ここの公式サイトを見れば大体のことはできる
みまもり君のコーディング
ここでは監視君(?)に必要そうな動作として
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に拡張してみたい
それでは。
ご挨拶と夜の浅草寺
技術ブログだけじゃなくて写真もここに上げていきたいと思います
機材・環境
編集
- Adobe Lightroom
- Adobe Photoshop
- Fotor Photo Editor
今回の写真
Sony α6300
28mm
f:4.5/1.3sec
isoは多分100
Lightroomで自動簡易レタッチ
「月」「銀杏」「五重塔」という神がかった組み合わせですが
まあ、月が小さすぎますね
しかも満月でも半月でも三日月でもない、中途半端な形なんですよね…
まあアングルも正直納得はいっていない
銀杏は良い感じでかぶさってるのでまあ良いんですが
月と五重塔の位置関係をどうすれば良いのかわからないです
…これ月いらなかったのでは()
銀杏の葉の影が露骨だなって思うんですけど、元絵もなんか露骨に
下から明かりを当たられてるので仕方ないんですよね
まあ範囲指定してちゃんとやれば良いんですけど。
2枚目
Sony α6300
22mm
f:5.6/1.6sec
Photoshopでトリミング&微加工
スカイツリーと門の組み合わせ
この二つは良いんだけど、なにせ背景が難しいので
スカイツリーが赤色装飾なのを良いことに青の彩度をガタ落ちさせてみました
いや、とても微妙
トリミングも下に変なものが写り込んでたからだし、
明らかに撮影が悪い
赤が色あせた感じなのと、スカイツリーのディテールが
割と綺麗だったので少しいじってみました
これ、撮影時の構図は悪くないのにトリミングして良くない感じに
なったしまった感がある
まあお寺だから仕方ないんだけどなぁ、という感想
UnityでVRで頑張って作る(2)
やってやりましょう!
(一行さん可愛い)
次の目的として、動画の再生をできるようにする
とりあえずplainオブジェクトを作ってそこに流したい
友人からもらったmp4のデータを取り込んで、VideoPlayerで再生する方法を試す
照明は当てていないのでunLitのシェーダを設定するのを忘れずに
ちゃんと再生されています(わかりにくいけど)
そしてジャイロでみる方向にスクリーンを表示してみる
まずjyroのスクリプトをカメラにアタッチし、HMDの向いている方向をシーンのカメラに合わせる
これでだいぶVRっぽくなった
写真のvirtualFieldはそんなに意味はないのでスルー
virtual CameraをDiveカメラと同じ位置に配置
virtual Cameraにもgyroのスクリプトをアタッチ
これがスクリーンの親になっている
そしてVideoPlayerがアタッチされているPlaneであるmovieFrameを子オブジェクトに設定
movieFlameを視線の先に適度に離して設置
こうすることで、virtualCameraはカメラと同じ挙動を示すようになった
視線がどこを向いているのかわかりにくかったので、Canvasを二つ作って左右のcameraのいちに設置、
真ん中に四角いカーソルを設置しました
画像は使い回しなので雑です。とりあえずわかればいいんだよ。
これで「カメラの向いている方法に映像が追従」するようなプログラムになった。
とりあえずここまで
UnityでVRで頑張って作る(1)
やっていく
前記事
aegis.hatenablog.jp
それにしても頭の悪そうなタイトルである
とりあえず、普通に3Dでプロジェクトを作成し、
UnityにはVRの機能が標準であるらしいので調べてみると、
公式のチュートリアルもあったので読んでみた
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持ってないんじゃ!!!!
...はい、別の方法を検証しましょう
そこで上がるのがスマホのジャイロセンサをトラッキングに使って、
映像もそのまま画面に出力するというもの
このGoogle CardBoardを使う
どうやらGoogleさんは公式でSDKを出してくれているようですが、今回はDive Unity Pluginというものを使う
こちらの記事を参考にしました
この方の記事と同様にDive_Cameraをヒエラルキーに入れて、
メインカメラを消して、ジャイロのスクリプトを書く
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の綴りが違うのはご愛嬌(は?)
とりあえずカメラではなく、適当なキューブにアタッチして試します
動いたのでカメラにアタッチ
位置関係とかを取るために、droidくんを配置
ちゃんと右目カメラと左目カメラが重なってますね
実を言うと別に二眼である意味はないのですが、持っているダンボール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
検証環境
iPhoneサイド
概要
Unity2019.3とiPhoneのUnity Remote5を用いて、iPhoneSEとMacを有線で接続し、
Input.Gyroの値をUnityのPlayで取得すると、値が特定の角度で非連続的になってしまう問題を発見しました
動画8秒ほどから見られるのですが、iPhoneを横持ちした際に、仰角を取るときと俯角を取るときで、z軸中心に180度回転してしまっています
— すじーい (@aegis_fms) 2019年10月14日
詳しい検証はまだしていませんが、iOS12のiPhone6Sでは正常の挙動を示したので、iOSのバージョンを原因と踏んでます。 pic.twitter.com/TK7Z1wEDDx
オブジェクトの角度が飛んでいる?のと
重力方向を軸とした時の回転が正常に取得できていません
上記環境でビルドしたプログラムをiPhoneにインストールして実行した結果、ジャイロの値は正常に取得できたので、Unity Remote5上でデバッグした時に発生する問題です
以下はビルドして実行した動画
— すじーい (@aegis_fms) 2019年10月14日
ツイートでも書いているのですが、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