1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

IPFactory(現役生)Advent Calendar 2024

Day 11

Discord Bot をAPIやAIにつなげてみた

Posted at

初めに

初めて投稿させて頂く、hibiと申します。
Discordをよく使っているので、Botを作ってみたらおもしろそうということで実行に移してみました。
ですが技術は全くないので自分ができる範囲で作成したのでご了承ください。

ツールや言語、フレームワーク

・言語
 python

・ツール
 Ubuntu環境をVSCodeで操作
 Discord
 Discord Developer Portal
 openweathermaps API
 OpenAI API

・フレームワーク(最後に使用)
 LangChain

(LangChainとはなんぞやというのは下の方の5.LangChainで話しています。)

最初にしてたこと

当初は題名を「Discordbotを簡単に作ってみた」にしようとしていました。
でもこれだけじゃまじでしょぼくないか?という内容になり、
APIやAIと繋げる内容に変更しています。
題名のAPIとかを見たいよって人は「4. API導入」から見て下さい。

やったこと

・botに「自己紹介」と打つと予め書いておいた文の出力
・「天気 場所」を入力するとAPIから取得した情報の出力
・botとAPIを繋げて会話
・最後に大規模言語モデルを使用して、簡単に一括で機能構築、実装(会話、天気の情報、計算できる機能)

1. 下準備

まず最初にDiscordアカウントを作成します。
その後、この画面の左列の+マークを押します。
スクリーンショット 2024-12-16 141252.png


次にサーバーの作成と出てくるので、オリジナルの作成を押します。
スクリーンショット 2024-12-16 142436.png


その後、サーバーについて聞かれますがこれはどっちでもいいと思います。
自分は今回、自分と友達のためを選びました。
スクリーンショット 2024-12-16 142448.png


次にサーバーのカスタマイズで画像とサーバー名を決めます。
今回は画像:なし サーバー名:test bot としておきます。
スクリーンショット 2024-12-16 143453.png
下準備はこれでおしまいです。

2. Discord Developer Portal

検索サイトでDiscord Developer Portalと調べます。
このサイトは簡単に言うとdiscordの開発用ポータルみたいな所です。
開いたら、右上のNew Applicationを押します。
スクリーンショット 2024-12-16 152705.png


次に、botの名前を決めます。名前は適当で大丈夫です。
今回はbot1とします。
discordの利用規約にチェックして作成します。
スクリーンショット 2024-12-16 153523.png


次に左欄のBotからReset Token を押します。
スクリーンショット 2024-12-16 154358.png


この画面が出てくると思うので先に進んで、パスワードを入力します。
スクリーンショット 2024-12-16 154441.png


Tokenの所に長い文字が出てくるのでこれをメモ帳などに
コピーしておきます。
※このTokenを他人に絶対見せない!(悪用される可能性があるから)
見せてしまった場合、もう一度Reset Tokenを押して更新します。
TokenはプログラムとDiscordを繋げる物になります。
スクリーンショット 2024-12-16 155138.png


次に下にスクロールするとPrivileged Gateway Intentsというものがあるのでそのチェックを全部画像の様に変更し、右下のSave Changesを押します。
スクリーンショット 2024-12-16 155839.png


次に右欄からOAuth2に移動しOAuth2 URL Generatorから
botとその下のAdministratorにチェックマークを入れます。
スクリーンショット 2024-12-16 160455.png
スクリーンショット 2024-12-16 160515.png


チェックマークを押すと一番下にURLができてるので、
それをコピーして、先ほど下準備で用意したサーバーに貼り付けます。
スクリーンショット 2024-12-16 160959.png
スクリーンショット 2024-12-16 161506.png


貼り付けて開くと、この様な画面が出るので先ほど設定していたBotをサーバーに招待します。
スクリーンショット 2024-12-16 161347.png


この画面が出れば成功です。
スクリーンショット 2024-12-16 162014.png

3. Visual Studio Code

ここら辺から結構省略してしまうので、やりたい人は自分で調べて下さい。(丸投げ)
先にフォルダをデスクトップに作成します。今回はdiscordtest1にします。
次にUbuntuをVisual Studio Code上で開きます。

中央上部の検索欄にdiscordtest1までのディレクトリを開きます。
スクリーンショット 2024-12-17 143629.png


このdiscodetest1の中にファイルを作ります。今回はbot123.pyにしておきます。
スクリーンショット 2024-12-17 144501.png


そしてvscodeの下側にターミナルがあるので開きます。
スクリーンショット 2024-12-17 142358.png


lsコマンドを使用してデスクトップまで行き、先ほど作ったdiscordtest1フォルダまで行きます。
スクリーンショット 2024-12-17 141622.png


その後py -3 -m pip install -U discord.pyと入力します。
Pythonの環境でdiscord.pyライブラリをインストールし、使用する為にダウンロードします。

次にbot123.py上でプログラムをこのように書きます。
TOKEN=の所に先ほどメモしておいた物を貼り付けます。
helloと打ったらsee youが返ってくるようにします。
自己紹介と打ったらその内容が返ってくるようにします。

bot123.py

import discord

TOKEN = "MTMxODEwNTAwMfg1MDcxMTYwMg.GzXto4.-1lBvLX2CAoFh38IFQSg0foBc2wiwLMn047fd1"

# Discordクライアントのインスタンスを作成
client = discord.Client(intents=discord.Intents.all())

# 自己紹介メッセージ
self_introduction = """
名前 hibi
学科 情報科
趣味 ゲーム、アニメ
一言 discord bot テストテステステスト
"""

@client.event
async def on_ready():
    print(f'We have logged in as {client.user}')

@client.event
async def on_message(message):
    # Bot自身のメッセージは無視
    if message.author == client.user:
        return

    # 'hello'と入力された場合の応答
    if message.content == 'hello':
        await message.channel.send('see you!')

    # '自己紹介'と入力された場合の応答
    if message.content == '自己紹介':
        await message.channel.send(self_introduction)

# Botを実行
client.run(TOKEN)

次に表示の隣の‥マークからデバッグ無しで実行をします。
スクリーンショット 2024-12-17 152721.png


この画面が出てきたら正常に作動しています。
スクリーンショット 2024-12-17 152842.png


そして最後に、先ほど作ったdiscordのサーバーでbotにチャットしてみます。
botから返答が来たら成功です。
スクリーンショット 2024-12-17 153112.png
スクリーンショット 2024-12-17 153131.png

4. API導入(openweathermaps,chatgpt)

ここからAPIを導入してBotにその情報を回答してもらいたいと思います。
まず「天気 場所」を打つとbotがその情報を持ってきてもらうようにします。
webでopenweathermapsを開いてAPIキーを発行します。
発行出来たら、bot123.pyに記述していきます。

bot123.py
import discord
import requests

TOKEN = "MTMxODEwNTAwMfg1MDcxMTYwMg.GzXto4.-1lBvLX2CAoFh38IFQSg0foBc2wiwLMn047fd1"
WEATHER_API_KEY = "s"  # OpenWeatherMapのAPIキー
WEATHER_API_URL = "https://api.openweathermap.org/data/2.5/weather"

client = discord.Client(intents=discord.Intents.all())

@client.event
async def on_ready():
    print(f'We have logged in as {client.user}')

@client.event
async def on_message(message):
    if message.author == client.user:
        return

    # '天気'と入力された場合の応答
    if message.content.startswith('天気'):
        try:
            # ユーザーが場所を入力しているか確認
            location = message.content.split(' ')[1].strip() if len(message.content.split(' ')) > 1 else 'Tokyo'

            # APIリクエストを送信
            params = {
                'q': location,
                'appid': WEATHER_API_KEY,
                'units': 'metric',  # 摂氏を使用
                'lang': 'ja'        # 日本語で取得
            }

            # リクエストを送信
            response = requests.get(WEATHER_API_URL, params=params)
            data = response.json()

            # レスポンスを表示して問題の原因を調査
            print(data)

            # ステータスコードが200の場合に天気情報を取得
            if response.status_code == 200:
                weather = data['weather'][0]['description']
                temp = data['main']['temp']
                feels_like = data['main']['feels_like']
                await message.channel.send(
                    f"場所: {location}\n天気: {weather}\n気温: {temp}℃\n体感温度: {feels_like}℃"
                )
            # ステータスコードが200以外の場合のエラーハンドリング
            else:
                if data.get('message') == 'city not found':
                    await message.channel.send("指定された場所が見つかりませんでした。場所を正しく入力してください。")
                else:
                    await message.channel.send(f"エラーが発生しました: {data.get('message', '原因不明のエラー')}")

        except Exception as e:
            await message.channel.send("エラーが発生しました: " + str(e))

client.run(TOKEN)


記述出来たら実行で確認し、botに天気 場所 を指定すると
このように出してくれました。
スクリーンショット 2024-12-18 125719.png


次にbotと会話してみたいと思います。
具体的にはchatgptをbotに繋げて会話するだけです。

まずOpenAIのホームページから先ほどと同じようにAPIキーを取得します。
ここで注意なのがOpenAIのAPIは有料になっていて
お金を払わないとAPIのキーを発行した所で使えません。
なので、最低額の5$を課金し使えるようにします。
(数か月ちょっとまで使えるらしい)

次にdiscordtest1に新しくファイルを2つ追加します。
今回は「gen_response.py」と「test.py」を作成します。
合計3つのファイルの中身を記述していきたいと思います。

bot123.py

import discord
   from gen_response import gen_response
  
   TOKEN = "MTMxODEwNTAwMfg1MDcxMTYwMg.GzXto4.-1lBvLX2CAoFh38IFQSg0foBc2wiwLMn047fd1"
  
   # Discordクライアントのインスタンスを作成
   client = discord.Client(intents=discord.Intents.all())
  
   @client.event
   async def on_ready():
      print(f'We have logged in as {client.user}')
 
  @client.event
  async def on_message(message):
      # Bot自身のメッセージは無視
      if message.author == client.user:
          return
 
      # メッセージ内容に応じて応答
      response = gen_response(message.content, opt="あなたの属性は以下のとおりです\n")
      await message.channel.send(response)
 
  # Botを実行
  client.run(TOKEN)
gen_response.py
 import os
   import sys
   from openai import OpenAI
  
   client = OpenAI()
  
   def gen_response(message : str, opt : str = "特になし") -> str:
       response = client.chat.completions.create(
               model = "gpt-4o-mini",
              messages=[
                  {"role": "system", "content": f"日本語での応答を心がけてください.オプションは以下の通りです。\n{opt}    "},
                 {"role": "user", "content": message}
              ],
              stream=False
              )
      return response.choices[0].message.content
test.py
 from gen_response import gen_response
  
   question = input()
   response = gen_response(question)
  4 print(response)

実行したら、botに話しかけてみます。
返答が返ってきたら成功です。
スクリーンショット 2024-12-18 145130.png

5. LangChain

友達にchatgptとか他色々入ってる機能を一括していれたツールを入れて
みればと言われてLangChainを紹介されたので、入れて使ってみました。

そもそもLangChainて何?

面白そうだったので、とりあえず調べてみました。
「大規模言語モデル(LLM: Large Language Model)を活用したアプリケーションを簡単に構築・統合するためのフレームワーク」
これを使えばさっきひとつずつ取得していたOpenAIやopenweathermapsのAPIなどを簡単に一括でできるようになります。
とりあえず会話と天気、計算を出力できるような機能を取り入れてみます。
必要なのは以下の5個です。
Langchain: ChatGPTを使った会話機能の中心部分
openai: OpenAI APIの利用
requests: 天気情報を取得
sympy: 数式を処理するライブラリ
python-dotenv:環境変数を簡単かつ直接記述せず管理する為

まず最初に、インストールをしていきます。

pip install langchain openai requests python-dotenv sympy

そして、bot123.pyに内容を記述します。

bot123.py
import os
import discord
from discord.ext import commands
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage
import requests
from sympy import sympify, N
from dotenv import load_dotenv

# 環境変数の読み込み
load_dotenv()
DISCORD_TOKEN = os.getenv("DISCORD_TOKEN")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
WEATHER_API_KEY = os.getenv("WEATHER_API_KEY")

# LangChainのセットアップ
chat_model = ChatOpenAI(openai_api_key=OPENAI_API_KEY, temperature=0)

# 天気情報取得関数
def get_weather(city):
    url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={WEATHER_API_KEY}&units=metric&lang=ja"
    response = requests.get(url)
    if response.status_code == 200:
        data = response.json()
        description = data["weather"][0]["description"]
        temp = data["main"]["temp"]
        return f"{city}の天気は「{description}」、現在の気温は{temp}°Cです。"
    else:
        return f"{city}の天気情報を取得できませんでした。"

# 計算機能
def calculate_expression(expression):
    try:
        # 数式を評価
        result = N(sympify(expression))
        return f"計算結果: {result}"
    except Exception as e:
        return f"計算に失敗しました: {str(e)}"

# Discord Botのセットアップ
intents = discord.Intents.default()
intents.messages = True  # メッセージ内容にアクセス
bot = commands.Bot(command_prefix="!", intents=intents)

# システムメッセージ(AIの性格設定)
system_message = SystemMessage(content="あなたは親切なアシスタントです。ユーザーの質問に答えてください。")

# メッセージ応答
@bot.event
async def on_message(message):
    if message.author.bot:
        return

    user_input = message.content

    # 天気関連のリクエスト
    if "天気" in user_input:
        city = user_input.split("天気")[-1].strip()
        weather_info = get_weather(city)
        await message.channel.send(weather_info)

    # 計算関連のリクエスト
    elif "計算" in user_input:
        expression = user_input.split("計算")[-1].strip()
        calculation_result = calculate_expression(expression)
        await message.channel.send(calculation_result)

    # LangChainによるChatGPT応答
    else:
        messages = [system_message, HumanMessage(content=user_input)]
        response = chat_model.invoke(messages)
        await message.channel.send(response.content)  # contentを使ってメッセージを送信

    # コマンド処理を通す
    await bot.process_commands(message)

# Botの起動
if __name__ == "__main__":
    bot.run(DISCORD_TOKEN)

次に先ほどと同じようにOpenAI,openweather,discordbotからAPIを取得し、.envファイルを作成して自分のAPIキーを記述します。(python-dotenvを入れたのはこの為)

.env
DISCORD_TOKEN=token #自分のトークン
OPENAI_API_KEY=key #自分のキー
WEATHER_API_KEY=key #自分のキー

では実際に話しかけてみます。
会話から天気の情報、計算の質問をしていきます。
スクリーンショット 2024-12-19 172104.png

こんな感じに質問した内容に対して回答して頂けました。
プログラムも作りやすいし、一括で機能を取得できるので、
とても使いやすかったです。
こっちでやる方が簡単なので時間の短縮にもなりました。
友達に感謝!!!!!

6. 最後に

初めて投稿した記事でしたが、見返すと色々問題が
ありすぎて目も当てられません(泣)
色々省略してしまった所が多くて申し訳ないです。
最後のLangChainのやつは機能を組み合わせられる
のでもっと自然な形にしたかったなぁと感じています。

一番成長を感じた所

これは何と言っても基本的なlinuxコマンドを覚えられたことです。
linuxコマンド自体ほぼ知りませんでしたが、友達が教えてくれたり(これが一番大きい)作業している内に段々と身についたのでよかったです。
意外と大変でしたが、終わった達成感があったので作ってよかったと感じています。

今後について

こんな簡単な物しか作れないですが、これからもうちょっと凝った内容の記事を書けたらいいなと思っています。
他にも技術系の記事をこれから出して行けたらいいなと思います。
ありがとうございました。

1
0
0

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?