うどん県出身グーナーの呟き

神奈川県在住のグーナーです。プログラムをやっているので勉強していることや作った便利ツールを公開したいと思います。

London渡航記その2(2018/12/22) 前半

London渡航記その2(2018/12/22)

概要

これはしがない社会人2年目の男が単身ロンドンに旅立ち、生サッカー観戦に興奮し、スタジアム観戦に興奮し、 物価に絶望した、渡航記の2日目になります。

初めての朝ロンドン

到着翌日、いきなりメインイベントがやってきました。そう、エミレーツでの初観戦です! あまりに興奮しすぎてランチタイムキックオフなのにまさかの午前7時に起床、8時にはホテルを出てしまった私はとりあえず初めての朝のロンドンを散歩することにしました。 f:id:ryo_udon:20190414144439j:plain うん。いい雰囲気。さすがは歴史の街。夜もいいけど朝もいいよね。 と思いながら何も考えず歩いて、 f:id:ryo_udon:20190414144552j:plain 電車乗ると f:id:ryo_udon:20190414144605j:plainf:id:ryo_udon:20190414144620j:plain

自然と足が向いてました、Emirates Stadiumに実は一番近いと噂のHolloway Road駅(ちなみに試合当日は下車専用になるようです)

途中に、往年の名選手や現在所属選手のサイン入りユニフォームがずらりと並ぶギャラリーを眺め、道草を食いつつ、(まだ時間前なのに開けてくれてありがとうございました!) f:id:ryo_udon:20190414144807j:plainf:id:ryo_udon:20190414144823j:plain

ついにたどり着いたのが、

我らがホーム!!

とりあえず思ったのは、

デカイ(小並感)

しばらく呆然と眺めて堪能した後、まだショップがopenしていなかったのでとりあえずスタジアム周辺を散策をすることにしました。

そうしたらあるわあるわ、そこかしこに撮影スポットが。 f:id:ryo_udon:20190414161751j:plain まずはベルカンプ大先生とパシャリ。 f:id:ryo_udon:20190414162527j:plain f:id:ryo_udon:20190414182022j:plain

キングをパシャリ(意外と大きいのね) f:id:ryo_udon:20190414182134j:plainf:id:ryo_udon:20190414182151j:plain

スタジアムの外壁にはこのような往年の名選手のプロフィールが描かれており、意外な経歴などをしれてかなり勉強になります。 f:id:ryo_udon:20190414163013j:plainf:id:ryo_udon:20190414185230j:plainf:id:ryo_udon:20190414185246j:plain

ひとまずぐるりと一周した私は、(一周するのに1時間かかりました。スタジアムの周りをランニングしている人に三回は抜かれました。)

スタジアム西側の近辺の街を散策することに。

まずは有名なARSENALのモニュメントをパシャリ。 f:id:ryo_udon:20190414185538j:plain

このあたりの赤レンガの多い街並みも中々ロンドンという雰囲気があっていい感じ。 f:id:ryo_udon:20190414185555j:plain

朝のひんやりとした空気の中、ぶらぶらと歩いているとふと路上に転がるあるものが目に入りました。 f:id:ryo_udon:20190414185839j:plain ん?

f:id:ryo_udon:20190414185912j:plain

んん?

こ、これは、これはあれですよ。

うちのファンタジスタとか親方とかフェラーリ大好きなエースや頭がイかれてると噂のCBがシーズン前に吸っていたという笑気ガスとかいうやつですよ。

本当にあったんだなー

閑静な住宅街で変なものにテンションが上がる25歳。

そんなよくわからないテンションのまま、トレイラのネーム入りサードユニフォームやマフラー、トレーニングウェアなどを大量購入し、

さあ! 念願のエミレーツ・スタジアムだ!!

Raspberry PI 起動時に自動的にPythonを実行させる方法

概要

先日投稿したTeamMakerですが、現在私はRaspberry Piにて運用しています。 ryo-udon.hatenadiary.jp

github.com

ただ最近、OSの問題か、電源の問題か、Raspberry Piが何もしていないのに落ちていることがあります。 なので、今回はその対策として、Raspberry Pi起動時にPythonを自動的に実行する方法と Pythonの処理が通信の途絶などで誤って終了した際に自動的に再起動してくれるスクリプトを勉強したので紹介したいと思います。

Systemd

今回はLinuxの起動処理やシステムを管理するSystemdという機能を利用します。 こちらを参考にさせていただきました。

そもそもSystemdとは何か。DevelopersIOさんで調べたところ、以下のようにありました。

Systemdは、サービスを1つのシェルスクリプトではなくUnitという単位で管理し、設定ファイルとして持ちます。 このため処理を細分化でき個別に実効することが可能です。また処理ごとの依存関係を明確にすることが出来ます。 さらに処理が細分化されているということは、並列での実行も可能になります。例えばAの処理の後にBとCを並列実行する、 というようなきめ細やかな設定が可能になります。

つまりは処理ごとに .serviceというファイルを /lib/systemd/system/ 以下に作成してあげれば起動時や デバイスを差し込んだ際にプログラムを設定することができます。

serviceファイルの作成

まずは /lib/systemd/system/の中に.serviceファイルを作成します。 ここでserviceファイルの名前は任意なので、この記事の中では TeamMaker.serviceと記述する TeamMakerの部分を任意の名前にしてください。

sudo vim /lib/systemd/system/TeamMaker.service
[Unit]
Description = TeamMaker

[Service]
ExecStart=/usr/local/bin/python3 /home/pi/workspace/Discord_Python/discord_python/src/main.py
Restart=always
Type=simple

[Install]
WantedBy=multi-user.target

[Unit]の Descriptionではその処理の詳細を書きます。 今回は指定していませんが、この[Unit]の中ではafterbeforeなどを

after=test.service

のように記述することで、他の処理の後に実行という設定ができます。 この他にも同じ使い方でRequireWantsなどを使うことで、依存関係を表現することができます。

[Service]の中では主に実行コマンドを記述します。 ExecStartに実行コマンドを記入します。 今回はpython3でTeamMaker.pyを実行します。 なので実行するpythonを指定し、実行するpythonプログラムのフルパスを指定しています。 Restartはプロセスの再起動です。 一応、エラーが発報されても再実行するスクリプトを後で紹介しますが、念の為常に再実行するように設定しておきます。

[Install]ではシンボリックリンクの設定をしています。 Wantedbyは.watsディレクトリにシンボリックリンクを貼る

詳しくはこちらのメモ書きなどを見てください。

Subprocess

今回はこちらの質問への回答を参考にしました。

import os,sys
import time
import subprocess

COMMAND  = "python3"
SRC_NAME = "TeamMaker.py"

dir = os.path.abspath(__file__)
proc = subprocess.Popen([COMMAND, dir[:-len("main.py")] + SRC_NAME])
while True:
    ecode = proc.poll()
    if ecode is None:
        time.sleep(2)
    elif ecode == 0:
        print('NORMAL END')
        print('NEXT' + '-'*10)
        proc = subprocess.Popen([ COMMAND, SRC_NAME])
    else:
        print('ERROR')
        print('RESTARTING' + '='*10)
        proc = subprocess.Popen([ COMMAND, SRC_NAME])

subprocessモジュールはターミナル上で実行するコマンドを実行することができます。また、proc.poll()の返り値をみることでエラーも確認できますので、今回はこれを利用します

このスクリプトの内容は凄くシンプルです。

proc = subprocess.Popen([COMMAND, dir[:-len("main.py")] + SRC_NAME])

この部分で何度も再起動させるpythonプログラムと実行方法を指定します。 今回は実行方法には"python3"を、実行対象には絶対パスでTeamMaker.pyを指定しています。 その後、

ecode = proc.poll()

にてエラーコードを取得します。 ecode == Noneが実行中 ecode == 0というのは正常終了、 それ意外がエラーになります。

これによって処理が突然落ちてしまっても再度起動されるようになります。

終わりに

これでTeamMakerの運用環境はほぼほぼ作り終えたので、今後はサッカー分析ツールを作成していきたいと思います。

やりたいことリスト

python

TeamMakerに新機能追加

*トーナメント表作成
*トーナメントの進捗更新
リーグ戦の表を作成
リーグ戦の得点、勝ち点計算
リーグ戦、トーナメントの混合
使用するチームをランダムで設定
*用途ごとにブランチ分け

*リモート起動する機能

matplotlib + animation + mouse control

qiita.com yura2.hateblo.jp -> さらに+ オブジェクトの移動速度を変更

Python細々としたところ

  • python実行時のoptionをhelpで表示できるようにする

GUI

tkinter, pyqt5など

人認識

映像から二次元の図へ変換

マウスコントロール

www.lisz-works.com

Google Colaboratory

colab.research.google.com

C++

VS

並列処理

クラス図

今時、クラス図を描いてコーディングというのも時代遅れ感があるが、 システム全体の説明を簡単に図示できるようになりたい。

ロンドン渡航記まとめ(その1) 2018/12/20 ~ 2018/12/21

London渡航記その1(2018/12/20 ~ 2018/12/21)

概要

これはしがない社会人2年目の男が単身ロンドンに旅立ち、生サッカー観戦に興奮し、スタジアム観戦に興奮し、 物価に絶望した、渡航記になります。

なぜロンドンへ

そもそもなぜロンドンへ行ったのかというと、理由は唯一つ。

エミレーツスタジアムアーセナルが見たい!!

社会人になった二年前に最初のリフレッシュ休暇でロンドンに行くと固く誓った私は待ちました。待ち続けました。誰にも文句を言われずにロンドンにはばたける日を。

そしてついに昨年末、やっとタイミングがあいロンドンに渡ったわけであります。

事前準備

事前準備と作りはしましたが、特に何もしていません。

旅程は12/20 ~ 12/28までの6泊9日で、 ただ、航空券とホテルとスタジアムのチケットを取っただけです(一応、空いた日用のツアーを何個か申し込みましたが)。

ばらばらにとってもいいのですが、私は海外旅行は大体こちらでいつも航空券とホテルを予約しています。

自分でやってもいいのですが、面倒くさがりかつかなり細かく調整してくださる旅工房さんを愛用しています。

チケットについてはもともとRed Memberになっていたので、ただポチポチするだけでした。 正直、僕には猿さん以上にわかりやす書く自信がないので詳しくはこちらをご覧ください。

ただ、今後はHISさんが毎試合スコアブックを付けるほどのガチグーナー笹木香利さんと一緒にエミレーツ観戦する笹旅なるものが今年も計画中らしいので、そちらを利用したほうがいいかも。なにせ、HISさんはArsenal FCと提携していますからね。おそらくかなりいい席で見ることができます。

後、忘れてはいけないのがコンセント。 イギリスは日本とは形状が違うので、変換器が必要になります。(こんな感じです) という訳で、私はとりあえずあまり大きくなく、色々な国でも使えるものをと思い、 以下の変換器を購入しました。

もっと安いものもありましたが、USBポートをかなり使うので、2つでは心もとないと思い、4つついている、この商品にしました。

さて、これでほぼ準備は完了。あとは当日を待つのみ。

出発当日

早めに仕事を上がり、羽田空港に到着。あまりにも興奮しすぎて着いてのは出発の4時間前。 ポチポチ持参した携帯ゲーム機(Vitaだよ!生産終了したレアものだよ!)で時間を潰しつつ、

さあ出発!

経由地ドーハに到着

飛行機に12時間ぐらい縦にも横にも揺られた後についたのが経由地のドーハ(今回カタール航空を使用) ドーハはこんな感じでした。

ドン!

f:id:ryo_udon:20190407152110j:plain
ドーハ空港内1
写真の上にある通路はモノレールでした。

ドン!

f:id:ryo_udon:20190407152126j:plain
空港内ホール
よくわからないモニュメントが多い空港でした。

驚いたのは、空港の中に鷹を肩に乗っけた王族みたいな人がいたこと。

写真をとってもいいのかな?などと迷ったけど、「無礼者! 首をはねろ!」になりたくなかったので そこは自重し、おとなしくハンバーガを食べて搭乗口で待つことにしました。

だけど、いつまで経っても案内されない。 あれ? なんで? と直ぐ側にいたおじさん(訛り的にフランス?)と話をしているとこんな放送が聞こえてきました。

" 只今、ガトウィック空港でドローンが飛んでいるため飛行機が飛べません "

僕とおじさん、ポカーン。 すぐ横にいた女の子二人を連れた家族もポカーン。 向かいでサンドイッチを食べていたおじさんも咥えたまま、え?

そう、日本でどのぐらい話題になったかわからないですが、これですよ、これ。

結局さらに二時間待って、なんとか出発できることに。 (しばらくドローンを見るたびに破壊衝動を抑えるのが大変でした)

とりあえず、いろいろとありましたが、これでなんとかロンドンに向かうことができました。

待ちに待ったロンドン到着

本当なら正午に着くはずが、ガトウィックに到着したのは午後5時半。

とはいってもこれを見ればテンションは上がる上がる。

これで興奮しすぎたんでしょうね。 こんな出来事がありました。

皆さん、お気をつけを!

ロンドン市内へ

ガトウィック空港からロンドン市内まではGatwick Expressを利用しました。 僕はしらなかったのですが、オンラインで予約をすると10%安くなるようです。時間は40分、定価料金は20ポンド程度。 電車の中はwi-fiが完備されているので、もしSIMなどを買う方は、空港ではなく、市内で買ってもいいと思います。

そして着いたのがVictoria駅。

f:id:ryo_udon:20190407153317j:plainf:id:ryo_udon:20190407153332j:plain
Victoria Station

歴史を感じます。

ホテルへ

今回滞在したホテルはこちら。かなり日本人の方が利用されているようでした。

最寄りがRussell Square駅でヒースロー空港からは乗り換えなしで行ける+King's Cross駅まで一駅とかなり交通の便がよく、 そして、なにより、Emirates Stadiumまでたったの4駅なのです!!

グーナーはもう、ここに泊まるしかないですね!

そして一日目はホテル下のパブでフィッシュアンドチップスとビールでFinish!!

一日目まとめ

というわけで、中々波乱の1日目でした。f:id:ryo_udon:20190407155337j:plain

ちなみにTubeという愛称で親しまれているロンドンの地下鉄ですが、都営大江戸線並みかそれよりもさらに深いです。 なので、絶対に階段を登ろうと考えないでください。

その2に続く

【Python】Discord用チーム分けBotをPythonで作ったよ【discord.py】

github.com

TeamMaker

Member List Maker Bot for Discord App

2019/12/05

ご指摘がありましたので追記します。

現在、Discord.pyはver 1.0 ~ になっていますが、現状このTeamMakerボットはver 0.7より以前のもののみ対応しています。

理由は下のリンクにありますが、変数名やクラス構成が大きく変わったことが原因となります。

現在、Discord.pyのversion確認をしつつ、1.0以降も対応できるように変更中です。

変更が完了次第、こちらの記事も追記したいと思います。

discordpy.readthedocs.io

2019/12/27 追記 version 1.2.5でも問題なく稼働することを確認しました。 ご指摘いただいた方、ありがとうございました。

2020/10/20 編集 ソースコードの整理を実施した関係で実行する際に呼び出すコードが

TeamMaker.py -> BotBase.py

に変更しました。 TeamMaker.pyはチーム分け機能のみを持つクラスになっています。

トーナメント表を自動で作る機能も追加しました。 ryo-udon.hatenadiary.jp


TeamMakerとは / What is TeamMaker

皆さん、ゲームは好きですか?

僕は大好きです。 夜な夜な友人と集まり、サッカーゲームを楽しんでいます。 そんな中、ある日あまりにも人数が集まり過ぎて、紅白戦をすることになりました。

さて、チーム分けをしよう!

...

どうやって?

結局、その場では適当なスマホアプリをインストールしましたが、何度もそれをやるのは面倒!

という個人的な理由から生まれたのがこのTeam Makerです。

TeamMakerとはボイスチャットアプリとして有名な Discordで動かせるチーム分けBotになります。

使い方は簡単。ただ指定のボイチャチャンネルに参加する人が集まった状態で一言、 "!start" とコメントするだけ。 これだけで自動的にチーム分けをしてくれます。

その他にもいろいろと機能があり、今後も追加していく予定です。 まずは使用方法から見てみてください。

TeamMaker使用方法 / How to use TeamMaker

現在搭載されている機能は2つのチームにランダムで分けてくれることです。 それ以外にも機能はありますが、現在開発中なので、随時別記事に追加していきます。

!start

すでにこのbotをサーバーに入れられている方は友達と一緒にVOICE CHANNELSにあるGeneralに参加して から以下の画像のように !startと打ってください。 するとGeneralに参加したメンバを勝手に2つのチームに分けてくれます。 f:id:ryo_udon:20190407113427p:plain

!teams (2020/07/17  追記 )

最初から追加していた機能なのにこちらのブログで紹介するのを忘れていたので追記します。

TeamMakerにはチーム数を指定して、チーム分けをする機能があります。

コマンドは簡単

!teams (分けたいチームの数)

でチーム分けができます。

!men (2020/07/17 追記)

こちらもまたまた最初から追加しているのに紹介するのを忘れておりました。

チームの数の他に、一チームあたりの人数でチーム分けをする機能があります。

!men (人数)

でチーム分けができます

TeamMaker追加方法 / How to invite TeamMaker

家庭用PC(Windows, Macにてインストールする場合)

基本的にはいろいろなブログで掲載されている情報の通り、DeveloperサイトでBotを作成したあと、自分のサーバに追加してください。

その後、Botのtokenを取得し、TeamSetting.jsonというファイルに追記してください。 このGithubにはsampleとしてSampleSetting.jsonというファイルがSettingディレクトリに 入っていますので、その名前を変更してしてください。

TeamSetting.json

{
    "F1ileName":"TeamSetting.json",
    "Token":"---your token----",
    "ServerName":"---your server---",

    "MainChannel":"General",
    "Channel1":"General",
    "Channel2":"Channel2",

    "Group1":"General",
    "Group2":"Group2"
}

基本的に編集が必要なのはこのファイルだけです。 頭から説明すると、

{
    "FileName":"TeamSetting.json",      // この設定ファイルの名前
    "Token":"---your token---",         // ここにDiscord.comにて作成したBotへのアクセスtokenを追加してください。
    "ServerName":"---your server---",   // ここにこのbotを参加させるserverの名前を入れてください。TeamMakerはこのサーバーのみを見に行きます

    "MainChannel":"General"            // チーム分けをする際に最初に全員に入っていてもらうvoice channnelです。
}

ここで記載しなかったものについては現在、整備中のものですので気にしないでください。 (今、releaseとdevelopにブランチを分けています.もう少々お待ち下さい)

ここまでくれば後は簡単です。お使いのPCからコマンドで

sudo pip install discord.py

でdiscord.pyをインストールしたあと、

python BotBase.py

を実行すればTeamMaker Botを起動することができます。 このTeamMaker.py BotBase.pyはどのディレクトリからでも実行ができます。

もし、これで実行できない場合は

  • tokenが間違っていないか

  • AnaCondaなどを使わず、通常のPythonを利用する

などを試してみてください。 二個目についてはどうやらDiscord.pyがソケット通信を利用していることが原因で、 Discord.comに接続できないことがあるようです。

ちなみに私はMacにAnacondaをインストールして無事に動きましたが、

あとで説明するRasbianでminicondaを入れた際にはいろいろとネットワークの

設定を頑張りましたが、うまく接続させることができませんでした。

常時稼働環境の作成 / How to run this bot with raspberry pi

現在、私はこのbotを自分のPCからではなく、Raspberry pi 2B+にて常時稼働させています。

PCからでも問題はないのですが、PCそのものをネットワークのつながらないところに持っていくことがあったり、

また、そもそも自分のPCに常時動いてほしくないというワガママから、家の中に眠っていたRaspberry Pi を引っ張りだすことにしました。

ただ、以外と落とし穴が多く存在していたので、念の為情報を残したいと思います。

まず、目指すべき環境は、

**"Raspberry Pi + Python 3.6 ~ + Discord.py"**

になります。

本来であればminicondaなどを入れて環境を作りたいと思ったのですが、 そのせいで逆に環境構築に時間がかかるというなんとも悲しい経験をしたので、ここでは一番シンプルな方法を紹介します。

まず、RasbianのイメージをmicroSDに焼きます。 ここでRasbianをチョイスした理由は、Raspberry Piユーザとしては身近だったことと、 どうやらDiscord.pyを簡単に動かせるようだという前情報を入手したからです。

実際、Lubuntu, Xubuntuなどで試してうまくDiscord.comに接続できなかったため、現在はRaspbianで運用しています。 (実際は全く違う理由であることに気づいたのですが、それは後ほど説明します)

microSDへのイメージの焼付方法ですが、以下のページを参考にしました。

基本的にそんなに難しいことはないのでこの流れに沿って操作するのみです。 もしバックアップが取りたいという方がいれば、自分のRaspberry Piの設定を.imgに書き出すという方法があります。リンク

イメージが書き込めたらそれをRaspberry Piに差し込んで、起動します。 Raspbianの設定についてはユーザ名やパスワードを設定するだけなので、ここでは省略します。

次にPythonの実行環境を整えていきます。 RaspbianにはデフォルトでPython 3.5がインストールされていますが、Discord.pyの推奨は3.6なためアップグレードを行います。 アップグレード方法は以下のページを参考にして、3.6.5を入れました。

ここまでくれば後少しです。 Ctrl + Alt + t でターミナルを開いていただき、

sudo pip3 install discord.py

と打ち込んでください。これで環境構築は終了です!

後は、Git cloneなり、USBメモリで適当なディレクトリにこのTeamMakerを入れていただき、

     python3 BotBase.py

と実行していただければこれで常時稼働でこのBotを動かすことができます。

TeamMaker Botの構成

TeamMakerの構成は基本的に以下の通りです。

├── Setting
│   └── TeamSetting.json  <-- 各サーバ名とDiscord.comで作成したBotのtokenを入力 <br>
├── config
└── src
    ├── InitSetting.py     <-- TeamSetting.jsonを読み込むクラス<br>
    ├── ServerControl.py    <-- サーバ内のメンバのリストなどを取得するクラス<br>
    └── TeamMaker.py    <-- チーム分けを行うクラス + Main() <br>

InitSetting.pyはTokenやサーバ名を登録するクラス ServerControl.pyは登録したサーバ名をもとにメンバのリストとそのID列を取ってきます。 ここではメンバリストがログイン順になっており、Discord APIの中でもそれを基準にIDをふっているようなので、ランダムにチーム分けができるようにID列を取得する際にrandom()を使用してランダムな配列にしています。

ServerControl.py

    def GetChannelUsers(self, channelName):
        members = []
        for server in self.client.servers:
            if server.name == self.serverName:
                for channel in server.channels:
                    if channel.name == channelName:
                        for member in channel.voice_members:
                            members.append(member)
        return members

    def GetListIDs(self, members):
        memberListSize = len(members)
        listIDs = list(range(memberListSize))
        random.shuffle(listIDs)
        return listIDs

最後にTeamMaker.pyが今回の肝になるソースコードになります。 チーム分けの部分はシンプルなのでそれは後で確認いただくとして、基本的なDiscordのBotがどのように動くのかを説明します。

TeamMaker.py


client = discord.Client() # 接続に使用するオブジェクト
initInfo    = INIT_SETTING() #token、サーバ名、Channel名を取得


# ①起動時に通知してくれる処理
@client.event
async def on_ready():
    print('ログインしました')

# ②何かメッセージが送信された際に実行される処理
@client.event
async def on_message(message):
    sendMessage = ""
    sc      = SERVER_CONTROL(client, initInfo.serverName, BOT_NAME)
    members, listIDs = sc.GetMemberAndListIDs(initInfo.mainChannel)
    
    # ここでメッセージを区別
    if message.content.startswith('/neko'):
        reply = 'にゃーん'
        await client.send_message(message.channel, reply) 
    
    # !がついているとコマンドとして扱う
    elif message.content.startswith("!"):
        tm      = TEAM_MAKER(members, listIDs)
        sendMessage = tm.Main(message)
        await client.send_message(message.channel, sendMessage)
   
# Discord.comに接続する
client.run(initInfo.token)

特に難しいことはありません。ただ、tokenをもとにclientがrun()を実行し、Discord.comに接続します。 接続完了後、①の処理に入り、BotがOnlineであることを知らせてくれます。 基本Botはこの状態で待機を続け、何かメッセージが送られた際に②の処理に入り、コマンドの判定をした後に チーム分けをするかを判断し、

 await client.send_message(message.channel, sendMessage)

にてチーム分けの結果が格納された文字列 を以下のように送信しています。

f:id:ryo_udon:20190407123814p:plain
実行結果

TeamMakerの説明は以上です。

/*コードブロックに言語名を表示*/ pre.code:before { content: attr(data-lang); display: inline-block; background: white; color: #666; padding: 3px; position: absolute; margin-left: -10px; margin-top: -30px; } pre.code { padding-top: 30px !important; }