Minecraftの入退室をWebhookで通知する
概要
ゲームサーバーのログをポーリングして入退室等々の通知を行います。 追加インストール不要(OS同梱のもの)で設定します。
環境
- Ubuntu 14.x or later
- ディストリビューションによっては
curl
が同梱されていない場合があるようなのでsudo apt install -y curl
でインストールしましょう。
- ディストリビューションによっては
設定手順
sudo su -
等々で root
に昇格した上で、下記コマンドをコピー&ペーストで実行します。
cat << 'EOF' > /opt/minecraft/notify_log.sh #!/bin/bash HOST_NAME=$(hostname) CHANNEL=#minecraft-notice USERNAME=Minecraft ICON_EMOJI=:minecraft: # NOTE: DiscordはWebhook URL末尾に /slack とつけるとSlack互換のpayloadを扱えます # See: https://discordapp.com/developers/docs/resources/webhook#execute-slackcompatible-webhook WEBHOOK_URL=https://hooks.slack.com/services/****/****/******** post_webhook() { while read i do echo $i | grep -iq -e "joined the game" -e "left the game" if [ $? = "0" ];then echo $i curl -X POST --data-urlencode "payload=\ {\ \"channel\": \"$CHANNEL\", \ \"username\": \"$USERNAME\", \ \"icon_emoji\": \"$ICON_EMOJI\", \ \"text\": \"[HOST_NAME: $HOST_NAME]$i\" \ }" \ $WEBHOOK_URL fi done } tail -n 0 -F $1 | post_webhook EOF chmod +x /opt/minecraft/notify_log.sh chown ubuntu /opt/minecraft/notify_log.sh
cat << EOF > /etc/systemd/system/minecraft_notifier.service # /etc/systemd/system/minecraft_notifier.service [Unit] Description=Server daemon for Minecraft notifier [Service] Type=forking User=ubuntu KillMode=none Restart=on-failure ExecStart=/usr/bin/screen -dmS minecraft_notifier /bin/bash -c "/opt/minecraft/notify_log.sh /opt/minecraft/logs/latest.log" ExecStop=/usr/bin/screen -S minecraft_notifier -X quit [Install] WantedBy=multi-user.target EOF systemctl enable minecraft_notifier systemctl start minecraft_notifier
ポイント
HOST_NAME
HOST_NAME=$(hostname)
の部分は、愛称やドメイン・アドレス等々を直書きしても良いと思います。特に複数サーバーの運用をしている方などは。
HOST_NAME=example.com
といった感じに。
WEBHOOK_URL
スクリプト中にもコメントを挿しましたが、なんと Discord は Slack の Incoming Webhook に互換があります!
Discord に通知したい場合は Webhook URL の末尾に /slack
を付加すると Slack 互換になります。(イケてますよね、さすが Discord)
なお、本スクリプトでは payload
で送っているので channel
icon_emoji
等は指定しても Discord では効きません。
echo $i | grep -iq -e "joined the game" -e "left the game"
ここで joined / left のログを検知して通知処理に飛ばしています。 サーバーの警告等も合わせて通知したい場合は、ここで追加で grep すると良いと思います。
操作方法
状態確認
sudo systemctl status minecraft_notifier
停止
sudo systemctl stop minecraft_notifier
起動/再起動
sudo systemctl start minecraft_notifier
sudo systemctl restart minecraft_notifier
自動起動設定(OS起動時に自動で起動させる/させない)
sudo systemctl enable minecraft_notifier
sudo systemctl disable minecraft_notifier
ご一緒にサーバーはいかがでしょうか←
「ご一緒にポテトは」的な()
さくらインターネット からも Minecraftサーバ の スタートアップスクリプト が出ましたね。 vps-news.sakura.ad.jp
私は早速移行し、ここで個人のサーバーを運用しています。
さくらから提供されているスクリプトで鯖立てしつつ、↑で紹介したような通知設定を行っています。快適です。
同様に screen + systemd でスッキリ収まっていて良い感じです。
おまけ
Minecraft とタイトルに入れましたが、ポーリングするログファイルと grep パターン、通知テキストを少し修正すれば、他のゲームやツール等々でも普通に使えますよね。 直近でいうと Factorio でも使えそうですね。はい。
以上、良いゲームライフを :wave:
よくある Press back again to exit なToastを再現してみる
現在携わっている業務アプリでは原則 onBackPressed をフックしてダイアログでの終了確認を行うようにしています。 が、終了確認ダイアログのNegativeButtonを毎度毎度押したくないので、よくある(?)あの「終了するにはもう一度押してください」というToastを再現してみました。
コード
package com.example.finishingconfirmsoftly; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.Toast; public class MainActivity extends AppCompatActivity { static final long SHORT_DURATION_TIMEOUT = 4000; static final long LONG_DURATION_TIMEOUT = 7000; boolean doubleBackToExitPressedOnce = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public void onBackPressed() { if (doubleBackToExitPressedOnce) { super.onBackPressed(); return; } this.doubleBackToExitPressedOnce = true; Toast doubleBackToExit = Toast.makeText(this, "Press back again to exit", Toast.LENGTH_SHORT); doubleBackToExit.show(); new Handler().postDelayed(new Runnable() { @Override public void run() { doubleBackToExitPressedOnce = false; } }, (doubleBackToExit.getDuration() == Toast.LENGTH_SHORT ? SHORT_DURATION_TIMEOUT : LONG_DURATION_TIMEOUT) / 2); } }
まだ汎用化してないので絶妙に汚いですが :bow:
見た目
その他
参考:
android.widget.Toast
のソースコード
Notify Gradle's build completion to Slack.
Gradleのビルド終了をSlackに通知する
今関わってるプロジェクトのビルドが諸事情により酷く長く、Instant Runが使えないこともしばしばあり、ビルド終了の通知を受けたくなったのでやってみたメモ。
How to
Slack通知を専門に行うtaskを追加する。
[project dir]/gradle/build_notifier.gradle
task pushToSlack << { Properties localProperties = new Properties() localProperties.load(project.rootProject.file('local.properties').newDataInputStream()) (["${project.rootDir}/gradle/notify_build_finish.sh", localProperties.getProperty('slackToken')].execute().text) } tasks.whenTaskAdded { task -> if (task.name ==~ /assemble.*/) { task.doLast { project.tasks.getByName("pushToSlack").execute() } } }
NOTE: task名が assemble
で始まるタスクの終了を通知します
通知を行うシェルスクリプトを追加する。
[project dir]/gradle/notify_build_finish.sh
#!/bin/bash curl -X POST --data-urlencode 'payload={"channel": "#チャンネル名 または @ユーザ名", "username": "Gradle", "text": "Build has just finished.", "icon_emoji": ":gradle:"}' https://hooks.slack.com/services/$1
NOTE: chmod +x notify_build_finish.sh
で実行権限の付与を忘れないこと。
SlackのIncomingWebhookのTokenを設定。
[project dir]/local.properties
slackToken=*********/*********/***********************
追加したtaskを読み込む。
[project dir]/app/build.gradle
... apply from: file('../gradle/build_notifier.gradle') ... android {...} ...
めも
おまけ
単純にMac内で通知が見れればいい場合は、 .bashrc
に
alias notify='osascript -e '"'"'display notification "めっせーじ" with title "たいとる" subtitle "さぶたいとる" sound name "Purr"'"'"''
と入れておいて、
$ ./gradlew check ; notify
なんてすると終了時に通知を受け取れますね。
gradlew自体にaliasを張って、
alias gradle='echo $@ ; osascript -e '"'"'display notification "めっせーじ" with title "たいとる" subtitle "さぶたいとる" sound name "Purr"'"'"''
こんな感じにしたかったんだけどうまくいきませんでした。:thinking_face:
以上。
現存するFindBugsErrorを全て除外設定してみる
いろいろな理由から、
「今あるFindBugsのエラーを一旦無視したい!」「だけどFindBugs自体は有効化させたままでいたい…!」
という時があるかもしれません。
(私は既存プロジェクトにFindBugsを途中から導入しようとしてこうなりました…。既に数百件のエラーがあります…。)
(「とりあえず今あるエラーは一旦無視して、これから発生する分は随時対応しようね」ということにしました。)
除外ルールを自動生成してしまおう!
FindBugsのレポートを生成
パースしやすいように、一時的にXMLにします。
task findbugs(type: FindBugs) { ... ignoreFailures = false effort = "max" reportLevel = "low" excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs_filter.xml") classes = files("$project.buildDir/intermediates/javac/") source 'src/main' include '**/*.java' reports { // xml.enabled = false // html.enabled = !xml.isEnabled() // html { // destination(new File("${project.rootDir}/build/reports/findbugs_report.html")) // } // 一時的に形式をXMLに変更 xml.enabled = true html.enabled = !xml.isEnabled() html { destination(new File("${project.rootDir}/build/reports/findbugs_report.xml")) } } classpath = files() ... }
Findbugsを実行
$ ./gradlew build # FindBugsは成果物を静的解析するので、まだであればbuildする $ ./gradlew findbugs
除外ルール自動生成
$ cat fb-filter-generator.rb #! /usr/bin/ruby require 'active_support' require 'active_support/core_ext' require 'open-uri' def create_match(classname:, abbrev:) <<~EOS <Match> <Class name="#{classname}"/> <Bug code="#{abbrev}"/> </Match> EOS end hash = Hash.from_xml open('./build/reports/findbugs_report.xml').read filter_xml = "" hash["BugCollection"]["BugInstance"].each do |bi| if bi["Class"].is_a?(Array) bi["Class"].each do |bc| filter_xml << create_match(classname: bc["classname"], abbrev: bi["abbrev"]) end else filter_xml << create_match(classname: bi["Class"]["classname"], abbrev: bi["abbrev"]) end end File.open('./findbugs_filter.xml','w') do |file| file.puts <<~EOS <?xml version="1.0" encoding="UTF-8"?> <FindBugsFilter> #{filter_xml} </FindBugsFilter> EOS end
↑のスクリプトをプロジェクト直下で実行します。
$ ruby ./fb-filter-generator.rb
実行したディレクトリに findbugs_filter.xml
という除外ルールのファイルができるはずです。
私はFindBugsの除外ファイルを
excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs_filter.xml")
と設定していますので、
$ mv ./findbugs_filter.xml ./config/findbugs/findbugs_filter.xml
と実行して設置しました。
これで、いま存在するFindBugsのエラーが全て除外できます!!!
Android Java から Fluentd にログを送信してみる
環境
- Docker
- fluent/fluentd
- Android Studio
- Java
- Gradle
手順
FluentdのDockerコンテナを準備
$ docker pull fluent/fluentd
fluent.conf を作成
$ mkdir fluent && cd fluent $ mkdir etc log $ cat > "./etc/fluent.conf" <<EOF # Receive events from 24224/tcp # This is used by log forwarding and the fluent-cat command <source> @type forward port 24224 </source> # Match events tagged with "app.*" <match app.*> @type file path /fluentd/log/app.*.log symlink_path /fluentd/log/app.log append true time_slice_format %Y%m%d time_slice_wait 1m time_format %Y%m%dT%H%M%S%z </match> EOF
configは docs.fluentd.org をベースに雑に書きました。
$ docker run -it --rm \ --name docker-fluent-logger \ -p 24224:24224 -p 24224:24224/udp \ -v $(pwd)/log:/fluentd/log \ -v $(pwd)/etc:/fluentd/etc \ -e FLUENTD_CONF=fluent.conf \ fluent/fluentd:latest
Android側
$ grep fluent app/build.gradle // https://mvnrepository.com/artifact/org.fluentd/fluent-logger api group: 'org.fluentd', name: 'fluent-logger', version: '0.3.2'
$ grep uses-permission app/src/main/AndroidManifest.xml <uses-permission android:name="android.permission.INTERNET"/>
package com.example.fluentloggertest; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; import org.fluentd.logger.FluentLogger; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { new Thread() { @Override public void run() { FluentLogger LOG = FluentLogger.getLogger("app", "fluentdのDockerコンテナが動いているマシンIP", 24224); LOG.log("action", "MainActivity", "onClickFab"); // こんな感じ? } }.start(); } }); } ...
プロジェクトテンプレから、デフォルトでFabがあるものを使っています。
logを確認
↑のアプリでFabをクリックすると、、
$ tail -f ./log/app/* ==> ./log/app.foo-bar.log <== 2018-09-20T01:34:26+00:00 app.action {"MainActivity":"onClickFab"} 2018-09-20T01:34:28+00:00 app.action {"MainActivity":"onClickFab"}
こんな感じでログが保存されます! (あー、この app.log のシンボリックリンクはコンテナ内のpathですね…)
超雑ですが、以上です。m(_ _)m
Androidデバイスでディスプレイ解像度変更
前回↓のような記事を書いた通り(?)、私はディスプレイを広く使いたくてしょうがない人間です。 sashimi4.hatenablog.com
そしてAndroidデバイスに於いても解像度を変更する手段を発見したので記します。
Android開発環境(adb環境)をセットアップ
省きます。 For geek
とつけたのはそういう意味です。
なお、 adbさえあればいいミニマルな環境構築方法は他の方もよく紹介されていますので、ググっていただければ簡単に見つかるかと思います。
adbコマンド実行
規定(≒現在)の解像度を確認。
$ adb shell wm size Physical size: 720x1280
好みの解像度を設定。 & 任意解像度でオーバーライド出来ているか確認。
$ adb shell wm size 1080x1920 ~ $ adb shell wm size Physical size: 720x1280 Override size: 1080x1920
この端末は4Kは無理でした 😇
$ adb shell wm size 2160x3840 ~ $ adb shell wm size Physical size: 720x1280 Override size: 1440x2560
こんな感じで弄れます。
使ってみたメモ
- 本変更は端末を再起動しても持続されるようです。
- IME設定でキーの高さを最大にすると操作性が上がります。
- 画面の隅の方をタッチすると、タッチ補正により意図しない座標がタッチされることがありました。おそらくdpiの変更により補正条件に悪影響が出ているのでしょうかね…
とりあえず以上です。
コマンドラインからMacOSの画面解像度を変更する
DisplayMenuとかQuickResといったGUIアプリも有るし持っているんだけど、コマンドラインからいじりたくなったのでメモ。
Installation
$ brew install homebrew/cask/cscreen ==> brew cask install homebrew/cask/cscreen ==> Satisfying dependencies ==> Downloading http://www.pyehouse.com/wp-content/uploads/2012/09/cscreenIntel.dmg ######################################################################## 100.0% ==> Verifying checksum for Cask cscreen ==> Installing Cask cscreen ==> Linking Binary 'cscreen' to '/usr/local/bin/cscreen'. 🍺 cscreen was successfully installed!
Usage
Show display list
$ cscreen -l DisplayID Index Depth Width Height Refresh 4280a40 1 32 2560 1600 0 use -h to display all usage options
Show available settings for display
$ cscreen -s1 -v DisplayID Index Depth Width Height Refresh 4280a40 1 32 2560 1600 0 4280a40 2 32 1280 800 0 4280a40 3 32 2048 1280 0 4280a40 4 32 1650 1050 0 4280a40 5 32 1440 900 0 4280a40 6 32 1152 720 0 4280a40 7 32 840 524 0 4280a40 8 32 1024 768 0 4280a40 9 32 800 600 0 4280a40 10 32 640 480 0 use -h to display all usage options
※ ディスプレイが1つしか無いので -s1
は省略可。
MacBook Pro内蔵のRetinaディスプレイなんですが、リフレッシュレートが見えませんね... :thinking_face:
Set display as you like!
$ cscreen -s1 -x 2560 -y 1600
私はメインでMacBook Pro (Retina, 13-inch, Early 2015)を使っているので、上記のコマンドでDotByDot表示できます。
その他は -d
オプションでDepthや -r
オプションでRefresh rateを設定できるようです。
詳しくは -h
オプションでヘルプを見ると良いと思います。
比率が合うわけない 1920x1080 とか、その他中途半端な値等を指定すると、 cscreen -s1 -v
で表示された中の近似値が選ばれるみたい?
ちなみにドット数を超えた解像度は設定できない模様。他のGUIアプリだと設定できるんですけどね…。
以上です。