追記
2019/6/17 現在「雲さん」の機能は11個ほどに増えています。
2019/12/6 discord APIにメジャーアップデートが入りましたね。
本記事で紹介している内容はdiscord.py 0.16.12
当時のプログラムになります。
discord.py 1.2.4
版のソースコードに関してはGitHubにて公開していますので、そちらをご参照ください。
ソースコードはこちら:KUMOSAN - GitHub
はじめに
以前、Slack botで同様のものを作ったのですが、より多くの友人に遊んでもらいたいなということで、Discord版へ移植したbotのご紹介です。
Slack botではAWS Lambdaを使用して動作させていましたが、今回はVPSレンタルサーバ上にコードを載せます。
サーバ動作環境
- Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-50-generic x86_64)
- python 3.6.0
(/home/USERNAME/.pyenv/version)
- pip 19.0.3
- discord.py 0.16.12
- wikipedia 1.4.0
- requests 2.21.0
- urllib3 1.22
- beautifulsoup4 4.6.0
開発者アカウントの登録
Discord botを扱うためにはDiscord Developer Portalから、アカウントの登録が必要です。
作成したアカウントを導入したいDiscordサーバに登録し、アクセストークンを取得してください。
- 詳しくは以下等の他筆者様の記事をご参照下さい。
モジュールの導入
pipから以下のようにして、botで使いたいモジュールを導入します。
discord.py==0.16.12
wikipedia==1.4.0
requests==2.21.0
urllib==1.22
requests==2.0.1
beautifulsoup4==4.6.0
$ pip install -r requirements.txt
機能
今回実装したbotの機能は以下のとおりです。
- 静的な受け答え
- 指定都市の天気情報の取得
- wikipedia検索による指定ワードの情報取得
- Miller-Rabin高速素数判定法による巨大素数の判定(GitHub - PrimalityTest)
プログラム本体
では、早速コード本体を書いていきましょう。
- 「ここにトークンを書いてね」のところを上記(開発者のアカウント登録)で取得したアクセストークンに置き換えて下さい。
-
$ python kumosan.py
で動くので、実行後、導入したDiscordの適当なチャンネルで雲さん おはよ!
等声をかけてみて下さい。反応があったら成功です。
import discord
import wikipedia
import requests
import json
import random
client = discord.Client()
TOKEN = "ここにトークンを書いてね"
@client.event
async def on_ready():
print('Logged in as')
print(client.user.name)
print(client.user.id)
print('------')
@client.event
async def on_message(message):
# 「雲さん」で始まるか調べる
if message.content.startswith("雲さん"):
# 送り主がBotだった場合反応したくないので
if client.user != message.author:
try:
user_name = message.author.name
text = message.content
print(text)
print(type(text))
################# Don't touch. ################
kumo_san = '╭◜◝ ͡ ◜◝╮ \n( •ω• ) \n╰◟◞ ͜ ◟◞╯ < '
################# Don't touch. ################
msg = kumo_san + user_name + 'さん '
#msg = user_name + 'さん '
if text == ('雲さん'):
msg = 'はい!ご用でしょうか!'
elif text.find('おは') > -1:
msg += 'おはようございます!'
elif text.find('こんにちは') > -1 or text.find('こんちゃ') > -1 or text.find('やあ') > -1 or text.find('おっす') > -1:
msg += 'こんにちは!'
elif text.find('こんばん') > -1 or text.find('ばんわ') > -1:
msg += 'こんばんは!'
elif text.find('おつ') > -1 or text.find('疲') > -1 or text.find('お先') > -1 or text.find('おち') > -1 or text.find('落ち') > -1:
msg += 'おつかれさまです〜'
elif text.find('自己紹介して') > -1:
msg = 'はい!はじめまして。Takaseさんに作られたヒヨッ子botの雲と申します。趣味は素数を数えることです。皆さんのお役に立てすようにがんばりますので、どうぞよろしくお願いします!:cloud:'
elif text.find('help') > -1 or text.find('-h') > -1:
msg += 'https://discordapp.com/channels/407045885281828877/407050154315874315/558382007433035786'
elif text.find('って何') > -1:
msg += wikipediaSearch(text)
elif text.find('天気') > -1:
msg += getWeatherInformation(text)
elif text.find('は素数') > -1:
msg += primarity_test(text, 50)
else:
msg += 'その言葉は知らなかったから調べたよ。\n' + wikipediaSearch(text)
# メッセージが送られてきたチャンネルへメッセージを送ります
await client.send_message(message.channel, msg)
return msg
except Exception as e:
print(e)
raise e
# Get Weather Infomation
def getWeatherInformation(text):
weather_api_url = 'http://weather.livedoor.com/forecast/webservice/json/v1'
response_string = ''
city_id = ''
if text.find('長野') > -1:
city_id = '200010'
elif text.find('大阪') > -1:
city_id = '270000'
elif text.find('東京') > -1:
city_id = '130010'
elif text.find('北海道') > -1 or text.find('札幌') > -1:
city_id = '016010'
elif text.find('愛知') > -1 or text.find('名古屋') > -1:
city_id = '230010'
elif text.find('佐賀') > -1:
city_id = '410010'
else:
city_id = '130010'
response_string += '場所が聞き取れなかったので取り敢えず'
try:
params = {'city':city_id}
response = requests.get(weather_api_url,params=params)
response_dict = json.loads(response.text)
title = response_dict["title"]
description = response_dict["description"]["text"]
response_string += title + "です!\n\n"
forecasts_array = response_dict["forecasts"]
forcast_array = []
for forcast in forecasts_array:
telop = forcast["telop"]
telop_icon = ''
if telop.find('雪') > -1:
telop_icon = ':showman:'
elif telop.find('雷') > -1:
telop_icon = ':thunder_cloud_and_rain:'
elif telop.find('晴') > -1:
if telop.find('曇') > -1:
telop_icon = ':partly_sunny:'
elif telop.find('雨') > -1:
telop_icon = ':partly_sunny_rain:'
else:
telop_icon = ':sunny:'
elif telop.find('雨') > -1:
telop_icon = ':umbrella:'
elif telop.find('曇') > -1:
telop_icon = ':cloud:'
else:
telop_icon = ':fire:'
temperature = forcast["temperature"]
min_temp = temperature["min"]
max_temp = temperature["max"]
temp_text = ''
if min_temp is not None:
if len(min_temp) > 0:
temp_text += '\n最低気温は' + min_temp["celsius"] + "℃"
if max_temp is not None:
if len(max_temp) > 0:
temp_text += '\n最高気温は' + max_temp["celsius"] + "℃"
forcast_array.append(forcast["dateLabel"] + ' ' + telop + telop_icon + temp_text)
if len(forcast_array) > 0:
response_string += '\n\n'.join(forcast_array)
response_string += '\n\n' + description
except Exception as e:
response_string = '天気検索でエラーです>< :cold_sweat:\n' + e.message + '\n' + str(e)
return response_string
# Search from Wikipedia
def wikipediaSearch(text):
response_string = ''
wikipedia.set_lang('ja')
index_st = text.find(' ')
index_ed = text.find('って何')
search_text = text[index_st:index_ed]
search_response = wikipedia.search(search_text)
print(search_response)
if len(search_response) > 0:
try:
wiki_page = wikipedia.page(search_response[0])
except Exception as e:
try:
wiki_page = wikipedia.page(search_response[1])
except Exception as e:
response_string = 'お探しの言葉ではエラーを起こしました!:cold_sweat:\n' + e.message + '\n' + str(e)
response_string = '説明しよう!\n'
response_string += wiki_page.content[0:200] + '.....\n'
response_string += wiki_page.url
else:
response_string = '今はまだ見つけられないンゴ…でも頑張って見つけられるようになるゾ〜!'
return response_string
# Miller-Rabin Primality Test
def primarity_test(text, k):
response_string = ''
index_st = text.find(' ')
index_ed = text.find('は素数')
q = int(text[index_st:index_ed])
if q == 2:
response_string = str(q) + 'は素数です!:laughing:'
return response_string
if q < 2 or q & 1 == 0:
response_string = str(q) + 'は素数じゃないです!:rage:'
return response_string
d = (q - 1) >> 1
while d & 1 == 0:
d >>= 1
for i in range(k):
a = random.randint(1, q-1)
t = d
y = pow(a, t, q)
while t != q-1 and y != 1 and y != q-1:
y = pow(y, 2, q)
t <<= 1
if y != q-1 and t & 1 == 0:
response_string = str(q) + 'は素数です!:smile:'
response_string = str(q) + 'は素数です!:wink:'
return response_string
client.run(TOKEN)
- ソースコードはGitHubからもダウンロードいただけます。GitHub - KUMOSAN
使い方
- 雲さん おはよ!
- 雲さん 東京の天気教えてー
- 雲さん RWBYって何?
- 雲さん 6700417は素数?
かわいい
最後に
この記事を書いている現在では、雲さんの機能がいくつか増えているので、今回ご紹介した機能以外についてもまた記事にできたらなと思っています。
Discordに導入して稼働させていると、友人たちにいっぱい遊んでもらえてとても嬉しいですね。
参考
- slackのbotに天気を教えてもらう(Python on AWS Lambda + API Gateway) - ヤマムギ
- Discord Botアカウント初期設定ガイド for Developer - Qiita
- livedoorお天気Webサービス
お天気Webサービス(Livedoor Weather Web Service / LWWS)は、現在全国142カ所の今日・明日・あさっての天気予報・予想気温と都道府県の天気概況情報を提供しています。
引用:http://weather.livedoor.com/weather_hacks/webservice