雑多開発ブログ(仮)

「黒い砂漠」ボス時刻を通知するBotを作ってみた「Discord」

昨年末、黒い砂漠に二年ぶりに復帰しました。現在はDiscord有のギルドに入っているのですが、身内の中でボスを通知してくれるbotがあればいいなーという話があがりました。調べた所わりと簡単にDiscordのbotが作れそうだったので作ってみました。本記事ではbotの使い方の解説をします。技術的な話はまた別記事にさせていただきます。 また、本botは音声通知の音源としてsoftalkの読み上げ音声を利用させていただいております。

Bot登録

discordapp.com

こちらからbotをサーバーに追加できます。このbotが行えるのはメッセージの送信、ボイスチャットへの参加及び発言のみです。

使い方

他のbotと同様に基本的にはコマンドを入力して使用します。全てのコマンドの頭にbが付きます。したがって、b ○○○のような形式で入力してください。

b next

現在の時刻から見て次に来るボスを通知します。

f:id:lilacxi:20190220214205p:plain

次のボスが二体の場合でも両方共に通知します。次のボスがなんだったか簡単に知りたい時に使ってください。

b join

コマンドを入力したユーザーが参加しているボイスチャンネルにbotを参加させます。

f:id:lilacxi:20190220214207p:plain

音声によるボス通知を設定したい場合はこのコマンドでbotを通話に参加させてください。

b kick

b joinで参加させたbotをボイスチャンネルから退場させます。

f:id:lilacxi:20190220214209p:plain

このコマンドではボス通知をオフにすることはできません。その場合は後述するコマンドを使用してください。

b set (通知時間) (再通知間隔)

メイン機能。数値Nを指定することで、ボスが登場するN分前に通知を行います。

f:id:lilacxi:20190220222723p:plain

上記の画像のようにb set 20 5と入力すると、ボスの20分前に最初の通知を行います。その後、ボスが登場するまで5分おきに通知を行います。
この再通知間隔を設定せずにb set 20と入力しても使うことができます。その場合、再通知は行いません。
通知時間は5の倍数分で設定することができます。(5,10,15,20...など)
通知を正確に行うために、コマンドを入力した時刻から一番近い5の倍数分からタイマーがセットされます。(16時43分にコマンドを入力した場合、16時45分からタイマーが開始します。)
タイマーが開始されるまでは、再セット及びオフにすることはできません。

またbotb joinによってボイスチャンネルに接続している場合、メッセージによる通知と同時に音声による通知を行います。もし、音声通知を希望する場合はb joinbotを通話に参加させてください。

b off

b setで設定したタイマーを解除します。

f:id:lilacxi:20190220222725p:plain

b setと同様にコマンドを入力した時刻から一番近い5の倍数分にタイマーが解除されます。

まとめ

取り合えず自分の欲しい機能だけを用意したのでどこかしら不便な所があるかもしれません。
不具合や要望がある場合は私のツイッターに連絡をお願いします。

twitter.com

Railsでsession_keyの名前を見つける方法

Rails5.1からはどうやらデフォルトでconfig/initializers/session_store.rbが作られなくなったようです。 session_store.rbを自分で作る際にsession_keyの名前がわかりませんでしたが、consoleで見ることができました。

環境

方法

stackoverflow.com

こちらにあるようにconsoleで

Rails.application.config.session_options[:key]
=> "_hoge_session"

と打てばsession_keyの名前がでてきます。session_store.rbには

Appname::Application.config.session_store :cookie_store, key: '_hoge_session', :expire_after => 1.hour

のように記述しますが、AppnameはRails newをした時の名前のようです。この名前はconfig/application.rbで確認できます。

require_relative 'boot'

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Hoge
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.
  end
end

moduleの横に書いてあるものがアプリケーションの名前です。

RailsのbootstrapでUncaught TypeError: Cannot read property 'fn' of undefined at setTransitionEndSupport エラーが発生する 他。

ローカルで動くHTML,CSS,JavascriptRailsで動かそうとしたらJSが大量にエラーを吐いていました。ChromeのF12でConsleを確認すると f:id:lilacxi:20181124151114p:plain いろいろとエラーが出てますね....。でてきたエラーは

  • Uncaught TypeError: Cannot read property 'fn' of undefined at setTransitionEndSupport - bootstrap.min.js
  • Uncaught ReferenceError: jQuery is not defined - 他js

これらのエラーは同じ原因によって発生しているようです。

原因 jQueryが読み込まれる順番が遅い

RailsではAssetspipelineによってjsやcssが一つのファイルとして纏められます。

railsguides.jp

javascriptをまとめる順番はassets/javascript/application.jsで明示することができますが、デフォルトでは以下のようになっています。

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .

最後の//= require_tree .でassets/javascript内のjsをファイル名の順番で読み込んでいるようです。

cccabinet.jpn.org

ここを見るとbootstrapはjQueryとPopper.jsを先に読み込む必要があります。しかし//= require_tree .は名前順に読み込むので、名前がそのままだと

  1. bootstrap.js
  2. jQuery.js
  3. Popper.js

という順番で読み込まれてしまいます。どうやらこれのせいで「Uncaught TypeError: Cannot read property 'fn' of undefined at setTransitionEndSupport 」エラーは発生しているようです。以下のリンクでも同様のエラーが発生していたようです。

github.com

他のJSの「Uncaught ReferenceError: jQuery is not defined」エラーもjQueryを前提としているのにjQueryより先に読み込まれていることによるエラーのようです。

これを解消するにはapplication.jsで読み込む順番を明示してあげる必要があります。

解消方法

application.jsのrequire部分に以下のように書き加えます。

前略
//
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require jquery-min
//= require popper.min
//= require_tree .

//= require_tree .の前に//= require jquery-min //= require popper.minと書くことによってjQuery,popper.jsを読み込んだ後に他のjsファイルを読み込んでくれます。このように他のjsの前提となっているファイルは//= require_tree .の前に書くようにすればいいですね。もしくは全てのjsを順番を明示して読み込むのもアリだと思います。

RailsでJavascriptのロード画面が終わらない

jQueryを使ってページを読み込んでいる間ロード画面を表示するようにしていたが、何故か延々とロード画面が表示されてページが表示されない。

よくよく調べてみるとページ内のリンクから飛んだ場合は必ずロード画面で止まるのに対して、アドレスに直接飛んだりページ更新をした場合は問題なく表示されました。原因がわからずapplication.jsをいじくり回していたら原因がわかりました。

対処方法

application.jsの//= require turbolinksを削除する。

原因

turbolinks  こいつが原因でした。

kray.jp

Rails4以降ではデフォルトでGemfileに記載されているため、よく見てなかった自分のアプリにも入っていました。またapp/assets/javascript/application.jsでもデフォルトで読み込む設定をしています。

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
//= require activestorage
//= require turbolinks
//= require_tree .

下から二行目の//= require turbolinksで読み込んでますね。

自分の環境ではロード画面の表示をjQueryのreadyイベントで行っていたのですが、上記のリンクによると

turbolinksでページをロードすると、jQueryのreadyイベントが発火しません。

なんてこった。どうりで内部リンクから飛ぶと正常に表示できないわけです。

今回のアプリケーションでは他人が作ったJavascriptcssを動かそうとしていた為、ロード画面のコードはいじらずapplication.jsの//= require turbolinksを消すことによって問題を解消しました。

~/.bash_profileを弄ったらterminalの色が消えた

windows subsystem for linux なるものが出ていることに(今更)気がついたのでDebianを導入してみました。 ところが、rubyを入れるときにbash_profileを作成するとなぜかターミナルの色が消えてしまいました。

どうしたものかと思えばさくっと解決策が見つかりました。

stackoverflow.com

echo "source ~/.bashrc" >> ~/.bash_profile

bash_profile にこのように記述をすると、起動時に毎回"source ~/.bashrc"を読んでくれるそうです。 どうやらbash_profileがあると、bashrcは読みに行かなくなるようなので、bash_profile内でbashrcを読むように命令する必要があるようです。

Raspberry piでrails sを実行するとエラーがでる時の対処法

この対処法は原因解決の理由が不明の部分がある為、あくまで自分用の覚え書きです。

以下の環境でrails s -b 0.0.0.0を実行するとエラーが出て正常に動作しませんでした。

環境

rubyrailsの導入方法はこちらを参考にさせていただきました。

qiita.com

いろいろ弄っていたら問題なくrails sできるようになったので、自分用に残しておきます。

結論

Bus Errorの解消法

rails sの時にBus Errorが発生する場合、config/boot.rbを編集し require bootsnap/setup を削除するかコメントアウトすれば解消できます。

Segmentation faultの解消法

rails new xxx を実行する際に末尾に--skip-bundleを加えて実行します。このオプションをつけるとbundle installが自動で実行されません。 処理が終わったあとアプリケーションディレクトリにcdしbundle installを行います。
このようにrails newの時にbundle installをskipすると、何故かrails sを実行した際にSegmentation faultエラーで詰まらなくなります。

概要

rails new xxxをした後にrails s -b 0.0.0.0を実行した際、下記のエラー文が出ました。

$ rails s -b 0.0.0.0
/home/hoge/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/bootsnap-1.3.1/lib/bootsnap/compile_cache/iseq.rb:18: [BUG] Bus Error at 0x1a632ab
ruby 2.4.0p0 (2016-12-24 revision 57164) [armv7l-linux-eabihf]

~長文省略~

どうやらBus Errorとやらで正常に動作していないようです。
rails Raspberry pi Bus Error で検索した結果、以下のサイトがヒットしました。

romkey.com

どうやらbootsnapがRuby VMと相性が悪いそうです。この問題は少なくともRuby 2.4.1から2.5.1までに見られているそうです。
つまる所、bootsnapが有効だとBus Errorが出るようなので、config/boot.rbを編集しbootsnapを無効にしてあげれば解消できました。
ところがBus Errorを解消した後、rails sをすると下記のエラー文が出ました。

$ rails s -b 0.0.0.0
=> Booting Puma
=> Rails 5.2.1 application starting in development
=> Run `rails server -h` for more startup options
/home/hoge/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rb-inotify-0.9.10/lib/rb-inotify/notifier.rb:54: [BUG] Segmentation fault at 0xe92d000e
ruby 2.4.0p0 (2016-12-24 revision 57164) [armv7l-linux-eabihf]

~長文省略~

なにやらrb-inotifyというgemでセグメンテーション違反が発生しているようです。セグメンテーション違反とはWikipediaによると

セグメンテーション違反(英語:segmentation fault)はソフトウェアの実行時に起きる特定のエラー条件である。segfault(セグフォールト)と略される場合がある。 セグメンテーション違反はアクセスが許可されていないメモリ上の位置、もしくは許可されていない方法(例えばリードオンリーの位置へ書き込みをしようとする、もしくはオペレーティングシステムの部分を上書きしようとする)でメモリ上の位置にアクセスしようとするときに起こる。
セグメンテーション違反 - Wikipedia

なるほど?正直な所、自分にはどうすれば解決するかわかりませんでした。しかし結論で述べたようにrails newを実行する際にbundle installをskipし、後ほどbundle installすることによってセグメンテーション違反を解消することはできました。なぜ解消できたか不明な為、あくまで自分用の一時的な対処法です。

Raspberry piのファイルをSambaで共有してWindows10から編集する

Apacheで表示するhtmlをメインPCから編集したかったのでやってみました。

自分用備忘録を含むためApacheをインストールする所から書くので、メインPCに共有したいフォルダがある場合は2.以降を参照してください。

今回使用したバージョン

1. Apache2のインストールと設定

取り敢えずapt-getをupdate、upgradeしてからapache2をインストールします。

$ sudo apt-get update

$ sudo apt-get upgrade

$ sudo apt-get install apahce2

apache2でのデフォルトディレクトリは /var/wwwです。この中にhtmlファイルを置くと、ブラウザから表示することができます。

apache2のディレクトリを変更する場合は、指定したいディレクトリを記述した設定ファイルを登録する必要があります。 私はこちらのサイトを参考にさせていただきました。 g2-girichan.hatenablog.com

confファイルを弄り私は /home/public/www をディレクトリに設定しました。

今回はSambaで/home/publicを共有することによって、メインPCから/home/public/www内のhtmlを直接編集することができるようにします。

2. Sambaのインストールと設定

ラズパイにSambaをインストールすることで指定したフォルダを共有することができるようになります。なので取り敢えずSambaをインストールします。

$ sudo apt-get install samba

Sambaをインストールした後にやることは3つあります。こちらを参考に進めさせていただきました。

www.virment.com


Sambaにユーザーを登録する

Sambaには登録されたユーザーか、あるいはゲストとして接続する必要があります。今回はRaspberry piに専用のユーザーを用意してSambaに接続します。
まず新しくユーザーを作成します。このユーザーはSambaへの接続にしか使わないので、今回はhomeにディレクトリは作っていません。

$ sudo useradd hoge

このユーザーをpdbeditコマンドを使いSambaユーザーとして登録します。

$ pdbedit -a hoge

このコマンドを実行するとパスワードを入力するように求められます。このパスワードはSambaに接続する時にメインPCから入力するものです。

共有するフォルダのパーミッションを変更する

Linuxのフォルダやファイルにはパーミッションというものがあります。ファイルを閲覧したり編集する為の権限のことです。

メインPCから共有フォルダ内のファイルなどを編集する際には、Sambaに接続するユーザーと共有フォルダの権限が一致する必要があります。

自分のためにパーミッションがどういうものか覚え書きをするので興味のない方は ここ をクリックしてください。



mkdirで作ったディレクトリのパーミッションは親ディレクトリのパーミッションが操作ユーザーと異なる場合rootになるようです。例としてに /home/public/wwwのパーミッションを見てみましょう。パーミッションは ls -lで見ることができます。

:/home/public $ ls -l

drwxr-xr-x 2 root root ~省略~ www

これがなにを表しているかはこちらにとても丁寧に記載されていました。

qiita.com

こちらを参考にls -lによるwwwの結果を読み取ると

  • ディレクトリの所有者は読み取り書き込み実行の全てができる。
  • 所有グループとその他のユーザーは読み取りと実行しか行うことができない。
  • 所有者はroot、所有グループもrootである。

ということがわかります。つまりrootでないとディレクトリ内のファイルやフォルダを編集することができないということです。

したがって今回の場合では共有したいディレクトリのパーミッションをSambaユーザーにものにする必要があります。

ちなみにwwwディレクトリのパーミッションがrootに設定されたのはAというアカウントでrootパーミッションであるpublicディレクトリ下にmkdirしたからです。逆に/home/A/ など親ディレクトリのパーミッションと操作ユーザーが一致するディレクトリにmkdirすると、作られたディレクトリのパーミッションは操作ユーザーのものになるようです。



では共有するディレクトリをhogeで編集できるようにしましょう。パーミッションの所有者をhogeにしても良いですが、この先Sambaユーザーが増えるかもしれないことを考えてSambaユーザー全てが所属するグループ「groupS」を作ることにします。その後共有ディレクトリの所有グループをgroupSに設定します。

まず新しくグループを作り、そのグループにhogeを追加します。

$ groupadd groupS
$ gpasswd -a hoge groupS

今回共有したいディレクトリは /home/publicです。chgrpコマンドを用いてpublicの所有グループをgroupSに設定します。

$ chgrp groupS /home/public/

所有グループに書き込みを行う権限がないので権限を与えます。

$ chmod -R 775 /home/public/

/homeでls -lを行うとpublicに対してこのように表示されます。

drwxrwxr-x 2 www-data groupS ~省略~ public

このように出たら成功です。Sanmbaユーザーでログインし共有ディレクトリに対して書き込みを行うことができるようになっています。ちなみに775にしている理由はその他のユーザーでも実行できるようにしておかないとApacheが読み込めなくなるからです。

Sambaの設定をする

Sambaはインストールすると設定ファイルを /etc/samba/smb.conf として生成します。

共有するフォルダなどの設定はこのファイルを弄ることで設定することができます。

$ sudo vi /etc/samba/smb.conf

でsmb.confを開き、ファイルの末尾に以下の文章を追記します。

[public]
comment = apache directory
path = /home/public #"指定したいディレクトリ"
writable = yes
hosts allow = 192.168.xxx.

[public]は共有した時に表示する名称です。

commentは共有先一覧を詳細表示で見た時にコメントに表示される文章です。

pathは共有したいディレクトリです。私の場合はApacheディレクトリの一つ上のディレクトリを指定しています。

writableは共有するフォルダでの書き込みを許可します。これを記述しないとファイルが開けても書き込むことができません。read only = no と記述しても同様の効果を得ることができます。

host allowは接続できるIPアドレスを指定しています。ローカルネットワークのIPアドレスを指定することでLAN内からしか接続することができないように設定することができます。

これらの設定項目はこちらを参考にさせていただきました。

www.uetyi.com

設定ファイルに記述して保存した後にはsambaのサービスを以下のコマンドで再起動する必要があります。

$ sudo service smbd restart
$ sudo service nmbd restart

ここまで設定したらRaspberry pi側で行う操作は以上です。

3. Windows10での設定

さて、ここからは共有ディレクトリに接続するためにメインPCであるWindows10の設定を行っていきます。

ネットワークを開いてもRaspberry Piを見つけることができないと思います。これはWindows10があるアップデート以降SMB1をデフォルトでインストールしなくなったからです。詳しいことは長くなるし本筋から外れるため記さないが、とにかくSMB1を有効にしないとネットワークにSambaが表示されなくなっているようです。表示する方法があるならば是非教えてください。

ネットワークで見つからないので直接ネットワークドライブとして割り振ることにします。

PCを右クリックして「ネットワークドライブの割り当て」を選択します。

f:id:lilacxi:20180814084940p:plain

割り当て画面で\\"Raspberry piIPアドレス"\public をフォルダーとして選択します。

f:id:lilacxi:20180814085123p:plain

完了を押しPCに出てきたpublicを表示しようとすると「ネットワーク資格情報の入力」というポップアップが表示されます。

f:id:lilacxi:20180814091039p:plain

ユーザー名に"hoge" パスワードにSambaにユーザー登録した時のパスワードを入力します。資格情報を記憶するにチェックを入れOKを押せば無事接続完了です。

4. おわりに

共有するフォルダのパーミッションあたりが詰まりやすいのではないかと個人的には思います。自分用メモとしての役割が大きいですが、参考になれば幸いです。