---
# System prepended metadata

title: Chapter10. 把 AI 帶到 Discord
tags: [ChatGPT 開發手冊]

---

# Chapter10. 把 AI 帶到 Discord

:+1: 完整程式碼在 https://github.com/iamalex33329/chatgpt-develop-guide-zhtw

## 其他章節

[Chapter1. OpenAI API 入門](https://hackmd.io/@U3f2IzHERbymAst2-lDdjA/S1cNMYi6T)

[Chapter2. 使用 Python 呼叫 API](https://hackmd.io/@U3f2IzHERbymAst2-lDdjA/HyZBg5ia6)

[Chapter3. API 參數解析與錯誤處理](https://hackmd.io/@U3f2IzHERbymAst2-lDdjA/BJWNtsh6p)

[Chapter4. 打造自己的 ChatGPT](https://hackmd.io/@112356044/Hk81U96Tp)

[Chapter5. 突破時空限制 - 整合搜尋功能](https://hackmd.io/@112356044/HkbVM-ApT)

[Chapter6. 讓 AI 幫 AI - 自動串接流程](https://hackmd.io/@112356044/r1Ke-GR6T)

[Chapter7. 網頁版聊天程式與文字生圖 Image API](https://hackmd.io/@112356044/Hyf-AvgAT)

[Chapter8. 設計 LINE AI 聊天機器人](https://hackmd.io/@112356044/r1d6HsgAa)

[Chapter9. 自媒體業者必看！使用 AI 自動生成高品質字幕](https://hackmd.io/@112356044/rJ2T37V0T)

[Chapter11. AI 客製化投資理財應用實戰](https://hackmd.io/@112356044/HkUE0rER6)

[Chapter12. 用 LangChain 實作新書宣傳自動小編](https://hackmd.io/@112356044/SybvbdN0p)

## 目錄結構

[TOC]

## 建立第一個 Discord 機器人

### 前置作業

1. 註冊 Discord 帳號：進入[註冊頁面](https://discord.com/register)，填寫個人資訊並完成驗證。

2. 建立伺服器：進入到[首頁](https://discord.com/channels/@me)，點擊左側的**加號按鈕**來建立自己的伺服器。

### 建立 Discord 開發者應用程式

要建立開發者應用程式，才能開發 Discord 機器人

1. 輸入以下網址並登入 Discord 帳號：:link: https://discord.com/developers/applications

2. 建立新的開發者應用：

![Screenshot 2024-03-17 at 6.26.50 PM](https://hackmd.io/_uploads/B1VdHS4Aa.png)

3. 開始機器人權限（下滑並勾選這三個選項）：

![Screenshot 2024-03-17 at 6.28.13 PM](https://hackmd.io/_uploads/ByDaSSE0T.png)

> 記得點選**儲存變更**按鈕

### 取得 TOKEN

點選左側菜單「BOT」，接著按下「Reset Token」按鈕，輸入完密碼會得到一串 TOKEN

![Screenshot 2024-03-17 at 6.31.58 PM](https://hackmd.io/_uploads/S1YjIr4R6.png)

### 將 Discord 機器人加入伺服器

加入機器人前需要建立 OAuth2 URL 的授權連結，來確定機器人進入伺服器可取得的權限。

點選左側菜單「OAuth2」，接著勾選以下選項，勾選完會得到一串 URL 連結

![Screenshot 2024-03-17 at 6.35.49 PM](https://hackmd.io/_uploads/B1l5PrN0T.png)

![Screenshot 2024-03-17 at 6.35.59 PM](https://hackmd.io/_uploads/ByF9wBECa.png)

![Screenshot 2024-03-17 at 6.36.21 PM](https://hackmd.io/_uploads/Bkk2PrVCa.png)

將網址複製下來，並開啟

![Screenshot 2024-03-17 at 6.36.54 PM](https://hackmd.io/_uploads/ry-ADrNCT.png)

回到伺服器會發現 BOT 已經加入到伺服器中了

![Screenshot 2024-03-17 at 6.37.55 PM](https://hackmd.io/_uploads/rkTbdrE0T.png)

## 建立回聲機器人

首先，先下載 `discord` 套件

``` python=
!pip install discord

import discord
import apikey


token = apikey.DISCORD_BOT_TOKEN

intents = discord.Intents.default()     # 取得預設的 intent
intents.message_content = True          # 啟用訊息內容
client = discord.Client(intents=intents)


@client.event
async def on_ready():
    print(f'{client.user} is login!')


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

    await message.channel.send(message.content)


client.run(token)
```
![Screenshot 2024-03-17 at 6.51.11 PM](https://hackmd.io/_uploads/rk2XsSNRa.png)

- 在 Discord 中，接收訊息、指令或是成員更新等等，稱之為「事件」，設定 intent 可以決定機器人該對哪些「事件」進行回覆。

- 使用 decorator 設定「當某事件發生時，程式該如何回應」

- 當接收到的訊息是機器人本人時**跳出函式**，避免陷入無窮迴圈

## 讓 Discord 機器人只處理指名給自己的訊息

若使用者越來越多時，每個人都呼叫機器人會讓版面變得很混亂，因此這邊多加一個設定：當 `@機器人` 時，才會進行回覆

``` python=
import discord
import apikey
import re

token = apikey.DISCORD_BOT_TOKEN

intents = discord.Intents.default()     # 取得預設的 intent
intents.message_content = True          # 啟用訊息內容

client = discord.Client(intents=intents)

# 標記使用者時在訊息中的格式為 <@username>
pattern_mention = re.compile(r'\s{0,1}<@\d+>\s')


@client.event
async def on_ready():
    print(f'{client.user} is login!')


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

    # 只處理 @機器人 的訊息
    if client.user in message.mentions:
        msg = re.sub(pattern_mention, '', message.content)          # 移除訊息中指名的資訊
        await message.channel.send(f'{message.author.mention} {msg}')   # 回覆時指名原發言者


client.run(token)
```

![Screenshot 2024-03-17 at 6.52.23 PM](https://hackmd.io/_uploads/SJW_oS40p.png)

## 加入 AI 的 Discord 機器人

``` python=
from googlesearch import search

import discord
import openai
import apikey
import re


openai.api_key = apikey.OPENAI_API_KEY

hist = []
backtrace = 2


def get_reply(messages):
    try:
        response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo", messages=messages
        )
        reply = response["choices"][0]["message"]["content"]
    except openai.OpenAIError as err:
        reply = f"An {err.error.type} error occurred\n{err.error.message}"
    return reply


def chat(sys_msg, user_msg, use_web):
    web_res = []
    if use_web:
        content = "The following are established facts:\n"
        for res in search(user_msg, advanced=True, num_results=5):
            content += f"Title: {res.title}\nSummary: {res.description}\n\n"
        content += "Please answer the following questions based on the above facts:\n"
        web_res = [{"role": "user", "content": content}]

    web_res.append({"role": "user", "content": user_msg})
    while len(hist) >= 2 * backtrace:
        hist.pop(0)
    reply = get_reply(hist + web_res + [{"role": "system", "content": sys_msg}])
    hist.append({"role": "user", "content": user_msg})
    while len(hist) >= 2 * backtrace:
        hist.pop(0)
    hist.append({"role": "assistant", "content": reply})
    if use_web:
        reply = f"The following reply is based on internet information:\n\n{reply}"
    return reply


prompt_for_check_web = """
{}
'''
{}
'''
If needed, please clearly reply with "Y"; if not, clearly reply with "N", do not add any extra words.
"""


def check_tool(prompt, msg):
    reply = get_reply(
        [{"role": "user", "content": prompt_for_check_web.format(prompt, msg)}]
    )
    print(reply)
    return reply == "Y"


token = apikey.DISCORD_BOT_TOKEN

intents = discord.Intents.default()
intents.message_content = True

client = discord.Client(intents=intents)
pattern_mention = re.compile(r"\s{0,1}<@\d+>\s")


@client.event
async def on_ready():
    print(f"{client.user} is logged in")


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

    if not client.user in message.mentions:
        return

    msg = re.sub(pattern_mention, "", message.content)
    draw_pict = check_tool(
        "Confirm whether drawing a picture is required for the following:", msg
    )
    if draw_pict:
        response = openai.Image.create(prompt=msg, n=1, size="1024x1024")
        image_url = response["data"][0]["url"]
        await message.channel.send(image_url)
        return

    use_web = check_tool(
        "If I want to know the following, confirm if internet search is needed:", msg
    )
    reply_msg = chat("Assistant", msg, use_web)
    await message.channel.send(f"{message.author.mention} {reply_msg}")


client.run(token)
```

![Screenshot 2024-03-17 at 6.59.19 PM](https://hackmd.io/_uploads/By4MpS4Ap.png)

1. 使用者發送訊息
2. AI 判斷訊息是否需要繪圖工具來完成
    - if Y
        - 呼叫繪圖工具並跳出函式
    - else N
        - AI 判斷訊息是否需要網路搜尋來完成
            - if Y 
                - 搜尋網路資訊來回答
            - if N 
                - AI 自行回答