DiscordのBOTを自作 投票BOT途中経過(2)
前回の続きでdiscord.pyを使って投票機能を持ったBotの制作です。
投票BOTの完成が目前!と思いきや、困ったことに。
サーバー再起動で初期化される。
当然ですよね。なのでデータを保存するように変更することに。
それと、再起動後にPythonも立ち上がる必要があるのでその設定もします。
pickleでデータを保持する
保持したい投票内容や投票数はpickleを使用して保存しました。
変数やリストごとにファイルを生成する必要があるようで、ファイル数が増えるのが難点ですがプロセスが投票の途中で終了してしまった時など、復帰する際には必ず必要になりそうです。
データの保存
botでの使用例としてまず保存は、
import pickle val = 0 pickle.dump(val,open('val.pkl','wb'))
valに0を格納し、val.pklと言うファイルをwb=バイナリで開きvalの値を保存します。
ファイル名の拡張子は何でも良さそうですがpyやshなどは避けたほうが良さそうです。
データの読み込み
開く場合は以下の様に
import pickle val = pickle.load(open('val.pkl','rb'))
val.pklをrb=バイナリで開き、その値をvalへ格納します。
ただし、ファイルがないとエラーが起きますので予めファイルを作成します。
import os import pickle if not os.path.isfile("./val.pkl"): val = 0 pickle.dump(val,open('val.pkl','wb')) else: val = pickle.load(open('val.pkl','rb'))
3行目でファイルの検索をして作成するか判断します。
これでファイルがない初回時のエラー回避とファイル作成が行えます。
XserverではCronでデーモン化
常駐化出来た~ヒャッハー!!なんて喜んでいたらサーバー再起動でプロセスが落ちるので再度問題に。これを何とか解決します。
前の記事でも書いたようにXserverではルート権限が使用できません。そのため再起動時の立ち上げを行うデーモンの設定が出来ない問題があります。
ルート権限が使えないのはなんとも残念ですが安全性から言うと必要なのかな。
これの解決策としてXserverで用意されているプロセスをデーモン化するCronを利用します。特に難しい設定などはなく必要な項目は時間とコマンドのみです。
時間は時分の他に月、日、曜日など細かく設定できます。
コマンドについてはLinux上で実行するものと同じです。
ただし、これで一応プロセスを起動できますが、Cronによるデーモン化の欠点として事前に決めた時間にしか起動できない所です。
Cronの設定
pythonのパスは3.6を使用したいのでXserverと異なります。
コマンドは以前の記事で使用した常駐用のコマンドにCronで複数のプロセスが起動しないよう多重起動防止用にpidofコマンドを追加し、起動していなければnohupで常駐させます。後は、pythonとbot.pyのパスは省略せずに記載すれば実行できます。
/usr/sbin/pidof -x /home/user/.pyenv/versions/anaconda3-5.1.0/bin/python3.6 /home/user/discord/bot.py >/dev/null || nohup /home/user/.pyenv/versions/anaconda3-5.1.0/bin/python3.6 /home/user/discord/bot.py < /dev/null &
これを必要になりそうな時間(再起動の時間が分かればベスト)の6時、18時の12時間ごとに設定しておけば大丈夫そうです。
現在の所、投票までしっかりと出来ていますが、時間指定での終了の方法が全く分かっていません・・・。時間取得は出来るのですが。スケジューラが使えそうかな?と思いましたがどうもうまく動作しませんでした。手動で停止させるしかなさそうです。
ソースコード
現状のソースコードは以下の通りです。
import os import pickle import re import datetime import time import asyncio import sched #discord.pyのインポート import discord client = discord.Client() #BOTログイン処理 @client.event async def on_ready(): print('Logged in as') print(client.user.name) print(client.user.id) print('------') ## pickle 作成 if not os.path.isfile("./_VOTEtime.pkl"): pickle.dump(0,open('_VOTEtime.pkl','wb')) else: VOTEtime = pickle.load(open('_VOTEtime.pkl','rb')) if not os.path.isfile("./_VOTEid.pkl"): VOTEid = 0 pickle.dump(VOTEid,open('_VOTEid.pkl','wb')) else: VOTEid = pickle.load(open('_VOTEid.pkl','rb')) if not os.path.isfile("./_VOTElist1_1.pkl"): pickle.dump([],open('_VOTElist1_1.pkl','wb')) else: VOTElist1_1 = pickle.load(open('_VOTElist1_1.pkl','rb')) if not os.path.isfile("./_VOTElist1_2.pkl"): pickle.dump([],open('_VOTElist1_2.pkl','wb')) else: VOTElist1_2 = pickle.load(open('_VOTElist1_2.pkl','rb')) if not os.path.isfile("./_VOTElist1_3.pkl"): pickle.dump([],open('_VOTElist1_3.pkl','wb')) else: VOTElist1_3 = pickle.load(open('_VOTElist1_3.pkl','rb')) if not os.path.isfile("./_VOTElist1_4.pkl"): pickle.dump([],open('_VOTElist1_4.pkl','wb')) else: VOTElist1_4 = pickle.load(open('_VOTElist1_4.pkl','rb')) if not os.path.isfile("./_VOTElist1_5.pkl"): pickle.dump([],open('_VOTElist1_5.pkl','wb')) else: VOTElist1_5 = pickle.load(open('_VOTElist1_5.pkl','rb')) if not os.path.isfile("./_voteqlist.pkl"): pickle.dump([],open('_voteqlist.pkl','wb')) else: voteqlist = pickle.load(open('_voteqlist.pkl','rb')) if not os.path.isfile("./_voteqmsg.pkl"): pickle.dump(0,open('_voteqmsg.pkl','wb')) else: voteqmsg = pickle.load(open('_voteqmsg.pkl','rb')) if not os.path.isfile("./_ROOMid.pkl"): ROOMid = 0 pickle.dump(ROOMid,open('_ROOMid.pkl','wb')) else: ROOMid = pickle.load(open('_ROOMid.pkl','rb')) # BOT動作プログラム @client.event # 投票処理 async def on_message(message): # グローバル変数 # 設定時間 global VOTEtime # 投票メッセージid global VOTEid # 投票リスト1 global VOTElist1_1 # 投票リスト2 global VOTElist1_2 # 投票リスト3 global VOTElist1_3 # 投票リスト4 global VOTElist1_4 # 投票リスト5 global VOTElist1_5 # 質問内容 global voteqlist # メッセージ global voteqmsg # 部屋ID global ROOMid # 送り主がBotだった場合反応したくないので if client.user != message.author: #-------------------------------------------------------------------------------------------------------------- # 質問終了 if message.content.startswith("-voteend"): # 投票しているか if not VOTEid: # エラーメッセージを送ります await client.send_message(message.channel, message.author.name + "さん、投票が開催されていません!") else: await client.edit_message(VOTEid, voteqmsg + message.author.name + "さんが投票を終了しました。") ## 初期化 VOTEtime = 0 pickle.dump(VOTEtime,open('_VOTEtime.pkl','wb')) VOTEid = 0 pickle.dump(VOTEid,open('_VOTEid.pkl','wb')) VOTElist1_1 = [] pickle.dump(VOTElist1_1,open('_VOTElist1_1.pkl','wb')) VOTElist1_2 = [] pickle.dump(VOTElist1_2,open('_VOTElist1_2.pkl','wb')) VOTElist1_3 = [] pickle.dump(VOTElist1_3,open('_VOTElist1_3.pkl','wb')) VOTElist1_4 = [] pickle.dump(VOTElist1_4,open('_VOTElist1_4.pkl','wb')) VOTElist1_5 = [] pickle.dump(VOTElist1_5,open('_VOTElist1_5.pkl','wb')) voteqlist = [] pickle.dump(voteqlist,open('_voteqlist.pkl','wb')) #-------------------------------------------------------------------------------------------------------------- # 質問内容の設定 elif message.content.startswith("-voteq "): # 投票を行っているか? if not VOTEid: # チャンネルIDを設定しているか? if not ROOMid: # エラーメッセージ await client.send_message(message.channel, message.author.name + "さん、エラーです!\n投票部屋IDが定義されていません!\nコマンド:-votecid {チャンネルID}で設定できます。 ") else: # メッセージを格納 voteq = message.content # 入力メッセージのリスト化 voteqlist = voteq.split() # 回答数2つ if len(voteqlist) &amp;amp;amp;amp;amp;amp;gt;= 5: voteqmsg = "質問:" + voteqlist[1] + "\n1:" + voteqlist[2] + "\n2:" + voteqlist[3] # 回答数3つ if len(voteqlist) &amp;amp;amp;amp;amp;amp;gt;= 6: voteqmsg = voteqmsg + "\n3:" + voteqlist[4] # 回答数4つ if len(voteqlist) &amp;amp;amp;amp;amp;amp;gt;= 7: voteqmsg = voteqmsg + "\n4:" + voteqlist[5] # 回答数5つ if len(voteqlist) &amp;amp;amp;amp;amp;amp;gt;= 8: voteqmsg = voteqmsg + "\n5:" + voteqlist[6] if len(voteqlist) &amp;amp;amp;amp;amp;amp;gt;= 9: await client.send_message(message.channel, message.author.name + "さん、エラーです!\n質問の設定を確認してください!") return else: await client.send_message(message.channel, message.author.name + "さん、エラーです!\n質問の設定を確認してください!") return pickle.dump(voteqmsg,open('_voteqmsg.pkl','wb')) pickle.dump(voteqlist,open('_voteqlist.pkl','wb')) ## リスト初期化 VOTElist1_1 = [] pickle.dump(VOTElist1_1,open('_VOTElist1_1.pkl','wb')) VOTElist1_2 = [] pickle.dump(VOTElist1_2,open('_VOTElist1_2.pkl','wb')) VOTElist1_3 = [] pickle.dump(VOTElist1_3,open('_VOTElist1_3.pkl','wb')) VOTElist1_4 = [] pickle.dump(VOTElist1_4,open('_VOTElist1_4.pkl','wb')) VOTElist1_5 = [] pickle.dump(VOTElist1_5,open('_VOTElist1_5.pkl','wb')) # 日時設定 VOTEtime = datetime.datetime.now() pickle.dump(VOTEtime,open('_VOTEtime.pkl','wb')) # VOTEtime = datetime.timedelta(hours=int(qtime)) + datetime.datetime.now() # 投票内容をVOTEチャンネルへメッセージを送ります VOTEid = await client.send_message(discord.Object(id=ROOMid), voteqmsg + "\n開始日時:" + VOTEtime.strftime("%Y/%m/%d %H:%M:%S")) pickle.dump(VOTEid,open('_VOTEid.pkl','wb')) else: # エラーメッセージ await client.send_message(message.channel, message.author.name + "さん、エラーです!\n既に投票が開催されています!") #-------------------------------------------------------------------------------------------------------------- # ヘルプ表示 elif message.content.startswith("-voteh"): # ヘルプメッセージ hm = "簡易投票BOT \n投票開催:-voteq {質問内容} {時間[1~30]min} {回答A1} {回答A2}...{回答A5まで}\n例:-voteq how 1 A B C D E\n\n投票方法:-votea {投票番号[1...5]}\n例:-votea 1\n\n投票終了:-voteend" # ヘルプメッセージを送ります await client.send_message(message.channel, hm) #-------------------------------------------------------------------------------------------------------------- # 投票の処理をします。 # 投票1の処理 elif message.content.startswith("-votea 1"): if not VOTEid: eva = message.author.name + "さん、投票が行われていないようです!" await client.send_message(message.channel, eva) else: while message.author.name in VOTElist1_1: VOTElist1_1.remove(message.author.name) while message.author.name in VOTElist1_2: VOTElist1_2.remove(message.author.name) while message.author.name in VOTElist1_3: VOTElist1_3.remove(message.author.name) while message.author.name in VOTElist1_4: VOTElist1_4.remove(message.author.name) while message.author.name in VOTElist1_5: VOTElist1_5.remove(message.author.name) VOTElist1_1.append(message.author.name) pickle.dump(VOTElist1_1,open('_VOTElist1_1.pkl','wb')) pickle.dump(VOTElist1_2,open('_VOTElist1_2.pkl','wb')) pickle.dump(VOTElist1_3,open('_VOTElist1_3.pkl','wb')) pickle.dump(VOTElist1_4,open('_VOTElist1_4.pkl','wb')) pickle.dump(VOTElist1_5,open('_VOTElist1_5.pkl','wb')) editer() # 投票内容をVOTEチャンネルへメッセージを送ります await client.edit_message(VOTEid, voteqmsg + VOTEtime.strftime("%Y/%m/%d %H:%M:%S")) # 投票メッセージを削除します。 await client.delete_message(message) #-------------------------------------------------------------------------------------------------------------- # 投票2の処理 elif message.content.startswith("-votea 2"): if not VOTEid: eva = message.author.name + "さん、投票が行われていないようです!" await client.send_message(message.channel, eva) else: while message.author.name in VOTElist1_1: VOTElist1_1.remove(message.author.name) while message.author.name in VOTElist1_2: VOTElist1_2.remove(message.author.name) while message.author.name in VOTElist1_3: VOTElist1_3.remove(message.author.name) while message.author.name in VOTElist1_4: VOTElist1_4.remove(message.author.name) while message.author.name in VOTElist1_5: VOTElist1_5.remove(message.author.name) VOTElist1_2.append(message.author.name) pickle.dump(VOTElist1_1,open('_VOTElist1_1.pkl','wb')) pickle.dump(VOTElist1_2,open('_VOTElist1_2.pkl','wb')) pickle.dump(VOTElist1_3,open('_VOTElist1_3.pkl','wb')) pickle.dump(VOTElist1_4,open('_VOTElist1_4.pkl','wb')) pickle.dump(VOTElist1_5,open('_VOTElist1_5.pkl','wb')) editer() # 投票内容をVOTEチャンネルへメッセージを送ります await client.edit_message(VOTEid, voteqmsg + VOTEtime.strftime("%Y/%m/%d %H:%M:%S")) # 投票メッセージを削除します。 await client.delete_message(message) #-------------------------------------------------------------------------------------------------------------- # 投票3の処理 elif message.content.startswith("-votea 3"): if len(voteqlist) &amp;amp;amp;amp;amp;amp;lt;= 5: eva = message.author.name + "さん、投票番号が間違っているようです!" await client.send_message(message.channel, eva) elif not VOTEid: eva = message.author.name + "さん、投票が行われていないようです!" await client.send_message(message.channel, eva) else: while message.author.name in VOTElist1_1: VOTElist1_1.remove(message.author.name) while message.author.name in VOTElist1_2: VOTElist1_2.remove(message.author.name) while message.author.name in VOTElist1_3: VOTElist1_3.remove(message.author.name) while message.author.name in VOTElist1_4: VOTElist1_4.remove(message.author.name) while message.author.name in VOTElist1_5: VOTElist1_5.remove(message.author.name) VOTElist1_3.append(message.author.name) pickle.dump(VOTElist1_1,open('_VOTElist1_1.pkl','wb')) pickle.dump(VOTElist1_2,open('_VOTElist1_2.pkl','wb')) pickle.dump(VOTElist1_3,open('_VOTElist1_3.pkl','wb')) pickle.dump(VOTElist1_4,open('_VOTElist1_4.pkl','wb')) pickle.dump(VOTElist1_5,open('_VOTElist1_5.pkl','wb')) editer() # 投票内容をVOTEチャンネルへメッセージを送ります await client.edit_message(VOTEid, voteqmsg + VOTEtime.strftime("%Y/%m/%d %H:%M:%S")) # 投票メッセージを削除します。 await client.delete_message(message) #-------------------------------------------------------------------------------------------------------------- # 投票4の処理 elif message.content.startswith("-votea 4"): if len(voteqlist) &amp;amp;amp;amp;amp;amp;lt;= 6: eva = message.author.name + "さん、投票番号が間違っているようです!" await client.send_message(message.channel, eva) elif not VOTEid: eva = message.author.name + "さん、投票が行われていないようです!" await client.send_message(message.channel, eva) else: while message.author.name in VOTElist1_1: VOTElist1_1.remove(message.author.name) while message.author.name in VOTElist1_2: VOTElist1_2.remove(message.author.name) while message.author.name in VOTElist1_3: VOTElist1_3.remove(message.author.name) while message.author.name in VOTElist1_4: VOTElist1_4.remove(message.author.name) while message.author.name in VOTElist1_5: VOTElist1_5.remove(message.author.name) VOTElist1_4.append(message.author.name) pickle.dump(VOTElist1_1,open('_VOTElist1_1.pkl','wb')) pickle.dump(VOTElist1_2,open('_VOTElist1_2.pkl','wb')) pickle.dump(VOTElist1_3,open('_VOTElist1_3.pkl','wb')) pickle.dump(VOTElist1_4,open('_VOTElist1_4.pkl','wb')) pickle.dump(VOTElist1_5,open('_VOTElist1_5.pkl','wb')) editer() # 投票内容をVOTEチャンネルへメッセージを送ります await client.edit_message(VOTEid, voteqmsg + VOTEtime.strftime("%Y/%m/%d %H:%M:%S")) # 投票メッセージを削除します。 await client.delete_message(message) #-------------------------------------------------------------------------------------------------------------- # 投票5の処理 elif message.content.startswith("-votea 5"): if len(voteqlist) &amp;amp;amp;amp;amp;amp;lt;= 7: eva = message.author.name + "さん、投票番号が間違っているようです!" await client.send_message(message.channel, eva) elif not VOTEid: eva = message.author.name + "さん、投票が行われていないようです!" await client.send_message(message.channel, eva) else: while message.author.name in VOTElist1_1: VOTElist1_1.remove(message.author.name) while message.author.name in VOTElist1_2: VOTElist1_2.remove(message.author.name) while message.author.name in VOTElist1_3: VOTElist1_3.remove(message.author.name) while message.author.name in VOTElist1_4: VOTElist1_4.remove(message.author.name) while message.author.name in VOTElist1_5: VOTElist1_5.remove(message.author.name) VOTElist1_5.append(message.author.name) pickle.dump(VOTElist1_1,open('_VOTElist1_1.pkl','wb')) pickle.dump(VOTElist1_2,open('_VOTElist1_2.pkl','wb')) pickle.dump(VOTElist1_3,open('_VOTElist1_3.pkl','wb')) pickle.dump(VOTElist1_4,open('_VOTElist1_4.pkl','wb')) pickle.dump(VOTElist1_5,open('_VOTElist1_5.pkl','wb')) editer() # 投票内容をVOTEチャンネルへメッセージを送ります await client.edit_message(VOTEid, voteqmsg + VOTEtime.strftime("%Y/%m/%d %H:%M:%S")) # 投票メッセージを削除します。 await client.delete_message(message) #-------------------------------------------------------------------------------------------------------------- # 投票番号が入っていない時のエラー elif message.content.startswith("-votea"): eva = message.author.name + "さん、投票番号を入力してください!!" await client.send_message(message.channel, eva) #-------------------------------------------------------------------------------------------------------------- # 投票部屋設定 elif message.content.startswith("-votecid"): # メッセージを格納 votecid = message.content # 入力メッセージのリスト化 votecidl = votecid.split() ROOMid = votecidl[1] pickle.dump(ROOMid,open('_ROOMid.pkl','wb')) await client.send_message(message.channel, message.author.name + "さん、投票部屋IDを" + ROOMid + "に設定しました!") #-------------------------------------------------------------------------------------------------------------- # 投票の記述エラー elif message.content.startswith("-vote" or "-voteq"): # エラーメッセージ em = message.author.name + "さん、エラーです!\nスペース抜けや2重スペース入っていませんか?\nもしくは-votehでヘルプを参照してください!" # エラーメッセージを送ります await client.send_message(message.channel, em) def editer(): # 質問内容 global voteqlist # メッセージ global voteqmsg # 投票率計算 VP1_1 = len(VOTElist1_1) / ( len(VOTElist1_1) + len(VOTElist1_2) + len(VOTElist1_3) + len(VOTElist1_4) + len(VOTElist1_5) ) * 100 VP1_2 = len(VOTElist1_2) / ( len(VOTElist1_1) + len(VOTElist1_2) + len(VOTElist1_3) + len(VOTElist1_4) + len(VOTElist1_5) ) * 100 VP1_3 = len(VOTElist1_3) / ( len(VOTElist1_1) + len(VOTElist1_2) + len(VOTElist1_3) + len(VOTElist1_4) + len(VOTElist1_5) ) * 100 VP1_4 = len(VOTElist1_4) / ( len(VOTElist1_1) + len(VOTElist1_2) + len(VOTElist1_3) + len(VOTElist1_4) + len(VOTElist1_5) ) * 100 VP1_5 = len(VOTElist1_5) / ( len(VOTElist1_1) + len(VOTElist1_2) + len(VOTElist1_3) + len(VOTElist1_4) + len(VOTElist1_5) ) * 100 # str VPs1_1 = str(VP1_1) VPs1_2 = str(VP1_2) VPs1_3 = str(VP1_3) VPs1_4 = str(VP1_4) VPs1_5 = str(VP1_5) # 回答数2つ if len(voteqlist) == 5: voteqmsg = "質問:" + voteqlist[1] + "\n1:" + voteqlist[2] + " :" + VPs1_1 + "%\n2:" + voteqlist[3] + " :" + VPs1_2 + "%\n" + "投票数:" + str(len(VOTElist1_1) + len(VOTElist1_2) + len(VOTElist1_3) + len(VOTElist1_4) + len(VOTElist1_5)) + "\n開始日時:" # 回答数3つ elif len(voteqlist) == 6: voteqmsg = "質問:" + voteqlist[1] + "\n1:" + voteqlist[2] + " :" + VPs1_1 + "%\n2:" + voteqlist[3] + " :" + VPs1_2 + "%\n3:" + voteqlist[4] + " :" + VPs1_3 + "%\n" + "投票数:" + str(len(VOTElist1_1) + len(VOTElist1_2) + len(VOTElist1_3) + len(VOTElist1_4) + len(VOTElist1_5)) + "\n開始日時:" # 回答数4つ elif len(voteqlist) == 7: voteqmsg = "質問:" + voteqlist[1] + "\n1:" + voteqlist[2] + " :" + VPs1_1 + "%\n2:" + voteqlist[3] + " :" + VPs1_2 + "%\n3:" + voteqlist[4] + " :" + VPs1_3 + "%\n4:" + voteqlist[5] + " :" + VPs1_4 + "%\n" + "投票数:" + str(len(VOTElist1_1) + len(VOTElist1_2) + len(VOTElist1_3) + len(VOTElist1_4) + len(VOTElist1_5)) + "\n開始日時:" # 回答数5つ elif len(voteqlist) == 8: voteqmsg = "質問:" + voteqlist[1] + "\n1:" + voteqlist[2] + " :" + VPs1_1 + "%\n2:" + voteqlist[3] + " :" + VPs1_2 + "%\n3:" + voteqlist[4] + " :" + VPs1_3 + "%\n4:" + voteqlist[5] + " :" + VPs1_4 + "%\n5:" + voteqlist[6] + " :" + VPs1_5 + "%\n" + "投票数:" + str(len(VOTElist1_1) + len(VOTElist1_2) + len(VOTElist1_3) + len(VOTElist1_4) + len(VOTElist1_5)) + "\n開始日時:" #本番用 #client.run("xxxx") #テスト用 client.run("zzzz")