Movatterモバイル変換


[0]ホーム

URL:


LoginSignup
359

Go to list of users who liked

302

Share on X(Twitter)

Share on Facebook

Add to Hatena Bookmark

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

python知識ゼロからポケモンの名前でしりとりするslackbotを作ったノウハウのすべて

Last updated atPosted at 2018-06-30

こんなのです

pokeshiri1.gif

ソースはgithubで公開してますhttps://github.com/wagase/pokeshiri
よかったら「いいね」してください。

環境

OS:windows
言語:python

筆者について

趣味でプログラム書いてるにわか。
htmlとcssとjavascriptくらいはかける。
python知識ゼロ

Twitterフォローされると喜びます
https://twitter.com/wagase
よろしくお願いします

なぜpython?

話題だから

なぜslackbot?

開発経験ゼロだから気軽に作れるのがよかった

開発環境構築

知識ゼロだからまずpythonという言語の仕様を学ぶ

参考にしたサイト
https://www.pythonweb.jp/tutorial/
斜め読みしながらわからないところはググる

pythonをwindowsにインストール

https://www.python.org/
Python 3.6.5にしました
Download for windowsからpython-3.6.5.exeを取得して実行
Add Python to PATHチェックする
コンソールでHello pythonくらいはprintできた

Visual Studio Codeでpythonの設定をする

拡張機能「python」で検索して
Python
Python for VSCode
をインストール
pylintがないとかいわれるのでインストール
pip install pylint
これでいい感じに開発できるようになった

slackbotの作り方をググる

参考にしたもの
Pythonを使ったSlackBotの作成方法
PythonのslackbotライブラリでSlackボットを作る
↑この記事をみながら
pip3 install slackbotを実行
slackbotの登録
https://my.slack.com/services/new/bot
にいって書いてある項目を埋めるだけ
API トークンをコピーしておく

いざ実行

上記の参考サイトを丸パクリして
python run.py
をしてみる
Appのところにあるbotがアクティブ表示になった!
リプライすると「何言ってんだこいつ」って返ってくる!!
実行できた!

あとはゴリゴリプログラミングする

githubで公開中
https://github.com/wagase/pokeshiri
わからないところはググる

ポケモンのデータはこちらからお借りしました

全ポケモンのJSONデータ
https://github.com/kotofurumiya/pokemon_data/
ありがとうございます

ログ出力で参考にしたもの

Pythonでお手軽にかっこよくlogging

機能とか

ポケモンじゃないときは

image.png

ンで終わるポケモンはペナルティ

image.png
しりとりだから怒られる

しりとりになってないとペナルティ

image.png
slackのシステム的に1対Nのしりとりを想定してるため多少まちがっても
ゲームオーバーにはならない

一度登場したポケモンを言うとペナルティ

image.png

困ったときはヒントで答えてない一覧をだせる

image.png
もちろん一度言うとリストから消える

知らないポケモンがでたときに詳細表示機能で教えてもらえる

image.png
全ポケモンのJSONデータ作者さんに感謝

ランキング機能

image.png
登場回数順にランキングを表示。
ル攻めすると勝てるのでルチャブルは受け攻め強い

リセット機能

image.png
ただのリセット。ゲームリスタートの意味。
リセットするとペナルティと今まで言ったポケモンを忘れる。
リセットしてもランキングは消さない

ログ表示機能

image.png
今までの記録を教えてくれる

ソース

ソースはgithubで公開してます
https://github.com/wagase/pokeshiri

<追記 date=20180705>
この記事に下記にコピペしているソースは
特定のポケモンやある条件で発生するバグがあります
github上では見つけ次第修正していますがこの記事でも同じように修正するのは記事の趣旨とは異なるため
あえて初期のままにすることにしました
(初学者はこういう書き方をしてしまうとか反面教師にもなるかと思います)
追記>

my_mention.py
# -*- coding: utf-8 -*-fromslackbot.botimportrespond_to# @botname: で反応するデコーダfromslackbot.botimportlisten_to# チャネル内発言で反応するデコーダfromslackbot.botimportdefault_reply# 該当する応答がない場合に反応するデコーダfromlibsimportmy_functions# 自作関数の読み込みfromlibsimportlog# 何回呼ばれたかカウントしたいmaincount=0resetcount=0hintcount=0detailcount=0rankingcount=0nomalcount=0errorcount=0notpokecount=0@respond_to(r'.+')defmention_func(message):globalmaincountglobalresetcountglobalhintcountglobaldetailcountglobalrankingcountglobalnomalcountglobalerrorcountglobalnotpokecountmaincount=maincount+1req=message.body['text']log.logger.info("["+str(maincount)+"] :総実行回数【"+str(req)+"】:受け取ったメッセージ")ifreq=="リセット"orreq=="reset":resetcount=resetcount+1my_functions.reset()message.send("リセットしました")elifreq=="log"orreq=="ログ"orreq=="記録":message.send("["+str(maincount)+"] :総実行回数")message.send("["+str(resetcount)+"] :総リセット回数")message.send("["+str(hintcount)+"] :総ヒント回数")message.send("["+str(detailcount)+"] :総詳細表示回数")message.send("["+str(rankingcount)+"] :総ランキング表示回数")message.send("["+str(notpokecount)+"] :総ポケモンじゃなくね?回数")message.send("["+str(nomalcount)+"] :総しりとり成立回数")message.send("["+str(errorcount)+"] :総しりとり不成立回数")elifreq=="ランキング"orreq=="ranking":rankingcount=rankingcount+1message.send(my_functions.remarkRanking())elifreq[:4]=="ヒント|"orreq[:4]=="ヒント|"orreq[:4]=="hint":hintcount=hintcount+1log.logger.info("["+str(hintcount)+"] :ヒント回数")hint=my_functions.hint(req[4:5])message.send(str(hint))elifreq[:3]=="詳細|"orreq[:3]=="詳細|":detailcount=detailcount+1log.logger.info("["+str(detailcount)+"] :詳細表示回数")ifmy_functions.checkExistenceAllPoke(req[3:len(req)]):message.send(my_functions.getpokedetail(req[3:len(req)]))else:message.send("よくわかりませんでした"+req[3:len(req)])else:ifmy_functions.checkExistencePoke(req):my_functions.memoryRemark(req)IsShiritoriOK=True# すでに言ったことがあるかどうかifmy_functions.checkExistencereq(req):IsShiritoriOK=Falsemessage.send(my_functions.countreqstock(req))# しりとりになってるかどうかifnotmy_functions.checkTruelastword(req):IsShiritoriOK=Falsemessage.send(my_functions.forgivelastword(req))ifIsShiritoriOK:nomalcount=nomalcount+1log.logger.info("["+str(nomalcount)+"] :しりとり成立回数")else:errorcount=errorcount+1log.logger.info("["+str(errorcount)+"] :しりとり不成立回数")my_functions.reqstockappend(req)ret=my_functions.shiritori(req)log.logger.info(""+str(ret)+"】:返答")else:notpokecount=notpokecount+1ret="ポケモンじゃなくね?"log.logger.info("["+str(notpokecount)+"] :ポケモンじゃなくね?回数")message.send(ret)
my_functions.py
# -*- coding: utf-8 -*-importjsonimportrandomimportcollectionsdefmid(text,s,e):returntext[s-1:s+e-1]defleft(text,e):returntext[:e]defright(text,s):returntext[-s:]# pokemon_data.jsonを読み取ってポケモンの名前だけにするdefgetpokenamelist():dic={}forkeyinPOKEDATA:ifnotkey["no"]indic.keys():dic[key["no"]]=key["name"]returndic# 辞書{'ア':['アーボ','アーボック'....],'イ':['イシツブテ','イワーク'....].....} の形にするが最後にンがつくものは除外defmakekanalistNotnn():kanalist={}foriinrange(1,len(KATAKANA)+1):kanas=[]j=1forkeyinPOKENAMELIST:ifleft(POKENAMELIST[key],1)==mid(KATAKANA,i,1)andright(POKENAMELIST[key],1)!="":kanas.append(POKENAMELIST[key])j=j+1kanalist[mid(KATAKANA,i,1)]=kanasreturnkanalist# 辞書{'ア':['アーボ','アーボック'....],'イ':['イシツブテ','イワーク'....].....} の形にするが「ン」で終わるやつを取得defmakekanalistGetnn():kanalist={}foriinrange(1,len(KATAKANA)+1):kanas=[]j=1forkeyinPOKENAMELIST:ifleft(POKENAMELIST[key],1)==mid(KATAKANA,i,1)andright(POKENAMELIST[key],1)=="":kanas.append(POKENAMELIST[key])j=j+1kanalist[mid(KATAKANA,i,1)]=kanasreturnkanalist# makekanalistNotnnのリストから指定した文字で始まるポケモンを適当に選ぶdefpokechoice(kana):val=""iflen(stock[kana])==0:iflen(nstock[kana])!=0:val=random.choice(nstock[kana])memoryRemark(val)delnstock(kana,val)val=val+"・・・もう【"+kana+"】から始まるポケモンは答えられないよ。負けました。リセットしてね"ifpenalty!=0:val=val+" ペナルティ合計は("+str(penalty)+"回)でした"else:val=random.choice(stock[kana])memorylastword(val)reqstockappend(val)delstock(kana,val)rest=len(stock[kana])memoryRemark(val)val=val+"・・・【"+kana+"】のこり【"+str(rest)+""+"次のことばは【"+getshiri(val)+"】です"returnval# そのポケモンがしりとりで存在するかどうかdefcheckExistencePoke(req):ifreqinPOKENAMELIST.values():returnTrueelse:returnFalse# そのポケモンがそもそも存在するかどうかdefcheckExistenceAllPoke(req):forkeyinPOKEDATA:ifreq==key["name"]:returnTruereturnFalse# しりとりメソッドdefshiritori(req):atama=left(req,1)shiri=getshiri(req)ifshiri=="":globalpenaltypenalty=penalty+1return"「ン」で終わるやつはだめだよ ペナルティ("+str(penalty)+"回)"else:ifreqinstock[atama]:delstock(atama,req)returnpokechoice(shiri)# 末尾の文字を調整するdefgetshiri(req):shiri=right(req,1)# ミミッキュ対策ifshiriin"ァィゥェォッャュョヮヵヶ":shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")shiri=shiri.replace("","")# 長音対策ifshiri=="":shiri=mid(req,len(req)-1,1)returnshiri# 一度いったやつはストックから消すdefdelstock(kana,val):stock[kana].remove(val)# 一度いったやつはストックから消すdefdelnstock(kana,val):nstock[kana].remove(val)# 一度言われたやつを覚えるdefreqstockappend(req):reqstock.append(req)# 一度言われたことがあるかどうかしらべるdefcheckExistencereq(req):ifreqinreqstock:returnTrueelse:returnFalse# 何回言われてるか調べて返すdefcountreqstock(req):globalpenaltypenalty=penalty+1returnreq+"は【"+str(reqstock.count(req)+1)+"】回目だよ。できれば違うやつ言ってね ペナルティ("+str(penalty)+"回)"#  リセットdefreset():globalstockglobalnstockgloballastWordglobalreqstockglobalpenaltypenalty=0lastWord=""stock=makekanalistNotnn()nstock=makekanalistGetnn()reqstock.clear()# ヒントdefhint(req):globalpenaltyifreqinstock:penalty=penalty+1returnstock[req]else:return"カタカナ一文字でお願いします"# 詳細機能defgetpokedetail(req):ret=""forkeyinPOKEDATA:ifkey["name"]==req:ret=ret+str(key)+"\n"returnret# 最後の文字を覚えるdefmemorylastword(req):globallastWordlastWord=getshiri(req)# しりとりになってるか調べるdefcheckTruelastword(req):iflastWord!=left(req,1)andnotlastWord=="":returnFalseelse:returnTrue# しりとりになってないメッセージdefforgivelastword(req):globalpenaltypenalty=penalty+1returnreq+"はしりとりになってないよ。できれば【"+lastWord+"】から始まるやつ言ってほしかったな ペナルティ("+str(penalty)+"回)"# 呼ばれたものを記憶defmemoryRemark(req):remarkstock.append(req)# ランキングカウントdefremarkRanking():ret=""i=0c=collections.Counter(remarkstock)foriteminc.most_common():i=i+1ifi>5:breakret=ret+str(item[0])+" "+str(item[1])+""+"\n"returnret# 定数群KATAKANA="アイウエオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモヤユヨラリルレロワヲンヴ"POKEDATA=json.load(open("data/pokemon_data.json","r",encoding="utf-8"))POKENAMELIST=getpokenamelist()# 変数群stock=makekanalistNotnn()nstock=makekanalistGetnn()remarkstock=[]reqstock=[]lastWord=""penalty=0
log.py
importlogginglogger=logging.getLogger(__name__)_detail_formatting='[%(asctime)s] %(module)s.%(funcName)s %(levelname)s -> %(message)s'logging.basicConfig(level=logging.DEBUG,format=_detail_formatting,# 出力のformatも変えられるfilename="./pokeshiri.log",# logファイルのありか)

その他

ニドラン♀とかニドラン♂が入力できない問題

<追記 date=20180705>
slackの仕様なのか♀が「:女性のマーク:」っていう絵文字になるんですけど(困惑)
image.png
だれか解決方法知ってたら教えてください(小声)
追記>

<追記 date=20190819>
↑これslack公式に問い合わせたところ♀が勝手に:女性のマーク:になるのは仕様で
設定等で勝手に変わらないようには現状できないとのことでした。
要望は出しておきました。
追記>

おまけ 

Amazon linux (EC2 t2.micro)での実行方法 無料

pythonとpipとslackbotをインストール

sudo git clone https://github.com/yyuu/pyenv.git /usr/bin/.pyenvcd /usr/bin/.pyenvpyenv install 3.6.5pyenv global 3.6.5sudo apt-get install python3-pipsudo yum install -y python36u-pippip3 install slackbot

上記はpythonにわかが2018/07頃に実行したhistoryです。
正確な情報は自分で調べることをおすすめします。

pokeshiriのモジュールを適当なところにおいて

nohup python -u run.py >out.log

でサーバーで実行し続けてくれます。
止めたいときは

ps -C pythonkill 番号

でいけます

Herokuで実行する方法 無料

Herokuのアカウントを作る
https://signup.heroku.com/login

HerokuCLIをインストールしてパスを通す
https://devcenter.heroku.com/articles/getting-started-with-python#set-up
パスはC:\Program Files\heroku\bin
ここでいいはず

herokuコマンドでherokuのgitに上げる

heroku loginheroku create {名前}heroku git:clone -a {名前}cd {名前}

作業フォルダ(名前つけたやつ)にpokeshiriのモジュールをおいて

git add .git commit -am "init"git push heroku master

これでpythonが動く環境ができて勝手にデプロイしてくれる
なお外部依存のモジュールは
requirements.txtに書く必要があるのでrootにコミットしておく

requirements.txt
slackbot==0.5.3

実行するには

heroku run nohup python -u run.py >out.log

止めるには

heroku psheroku kill {実行名}

でいけました

あとがき

python知識ゼロからポケモンの名前でしりとりするslackbotを作ったノウハウのすべてでした
pythonは本当に学習コストが少ないと思いました
JSONの読み込みとかリストの並び替えとか辞書の扱い方とかググればすぐにサンプルコードがでてきます。
またググればでてくるポケモンデータJSONのすごさにちょっと感動
ポケモンデータJSON作者様にこのうえない謝辞をおくります。

以上
ありがとうございました。

よかったら「いいね」してください。

Twitterフォローされると喜びます
https://twitter.com/wagase
よろしくお願いします

359

Go to list of users who liked

302
6

Go to list of comments

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
359

Go to list of users who liked

302

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?


[8]ページ先頭

©2009-2025 Movatter.jp