# Discord py 機器人初學筆記
###### tags: `Python` `Discord`
---
[TOC]
## 1. 建立基本機器人架構
### Step 1: 申請 Discord 帳號,並到 Discord Developer
- https://discord.com/developers
### Step 2: 建立一架新的機器人,並複製 Token
:::info
:bulb: **Token 很重要,需善加保存**

:::
### Step 3: 撰寫基本架構
- 需先於終端機打上 ==pip install discord==
```python=
import discord
from discord.ext import commands
intents = discord.Intents.all()
bot = commands.Bot(command_prefix='/',intents=intents)
@bot.event
async def on_ready():
print(">>{} is online!".format(bot.user))
bot.run("Token")
```
---
## 2. Message 用法
### Step 1: 建立架構
```python=
@bot.event
async def on_message():
await
```
### Step 2: 回復訊息功能
```python=
@bot.event
async def on_message(message):
if message.content == "你好":
await message.channel.send("嗨"+ str(message.author))
```
:::info
:bulb: 機器人會從訊息找到你的資料,並跟你打招呼~

:::
### Step 3: 避免機器人自己回覆自己
```python=
@bot.event
async def on_message(message):
if message.author == bot.user:
await
if message.content == "你好":
await message.channel.send("嗨"+ str(message.author))
```
:::warning
:warning: 如果沒有這樣設定的話,可能會導致無限輪迴
:::
## 3. Json 用法
### Step 1: 在同一個資料夾內,建立一個新的 json 檔
```jsonld=
{
"Token" : "打上自己的 Token"
}
```
:::info
:bulb: 每一筆資料之間都要用==逗點==隔開
:::
:::warning
:warning: 新增資料時要注意資料型態,json 的字串宣告需用雙引號
:::
### Step 2: 在 bot.py 裡引用並開啟 json
```python=
import json
with open("名稱.json","r", encoding = "utf8") as file:
jfile = json.loads(file) #此時 jfile 存取了 json 裡面的資料
```
:::info
:bulb: 上述用法須在 json 與 bot.py 在同一個資料夾才能使用
:::
:::warning
:warning: 注意 load 與 loads 的用法差別,兩個不一樣
:::
### Step 3: 利用 json 將 Token 隱藏起來
```python=
import discord
import json
from discord.ext import commands
with open("settings.json", "r", encoding = "utf8") as file:
jfile = json.load(file)
# 從 settings.json 取得資料
intents = discord.Intents.all()
bot = commands.Bot(command_prefix='/',intents=intents)
@bot.event
async def on_ready():
print(">>{} is online!".format(bot.user))
bot.run(jfile["Token"]) # 將 Token 隱藏起來
```
## 4. 成員加入 / 退出訊息
### Step 1: 建立基本架構
```python=
@bot.event
async def on_member_join(member): #成員加入
await
@bot.event
async def on_member_remove(member): #成員退出
await
```
### Step 2: 列印出成員名稱&編號
```python=
@bot.event
async def on_member_join(member): #成員加入
print("{} 加入".format(member))
@bot.event
async def on_member_remove(member): #成員退出
print("{} 退出".format(member))
```
:::info
:bulb: 這邊的 print 是在終端機上列印,不是在頻道裡列印
:::
### Step 3: 在頻道發出加入 / 退出訊息
```python=
@bot.event
async def on_member_join(member): #成員加入
channel = bot.get_channel("頻道ID")
await channel.send("{} 加入".format(member))
@bot.event
async def on_member_remove(member): #成員退出
channel = bot.get_channel("頻道ID")
await channel.send("{} 退出".format(member))
```
:::info
:bulb: 頻道 ID 也能用 json 存起來哦,但是要記得轉成 int
:::
## 5. 建立基本指令 (延遲查詢)
### Step 1: 建立基本架構
```python=
@bot.command()
async def ping(ctx): # ctx 代表上下文
await
```
:::info
:bulb: ping 在這邊代表指令名稱,呼叫的時候可以在頻道打 /ping
:::
### Step 2: 延遲功能
```python=
@bot.command()
async def ping(ctx): # ctx 代表上下文
await ctx.send("{}".format(bot.latency))
```
### Step 3: 優化
```python=
@bot.command()
async def ping(ctx): # ctx 代表上下文
await ctx.send("{} (ms)".format(round(bot.latency * 1000)))
```
#### 效果如下 :arrow_down: :clap:

## 6. 傳送圖片功能 (利用 url)
### Step 1: 建立基本架構
```python=
@bot.command()
async def pic(ctx):
await ctx.send("圖片網址")
```
### Step 2: 隨機傳送圖片
```python=
import random
l1 = {"圖片網址1","圖片網址2"}
@bot.command()
async def pic(ctx):
r_pic = random.choice(l1)
await ctx.send(r_pic)
```
### Step 3: 配合 json
#### Json 如下 :arrow_down:
```jsonld=
{
"picture" : ["圖片網址1","圖片網址2"]
}
```
#### python 如下 :arrow_down:
```python=
import random
import json
with open("settings.json","r",encoding= "utf8") as file:
jfile = json.load(file)
@bot.command()
async def pic(ctx):
r_pic = random.choice(jfile["picture"])
await ctx.send(r_pic)
```
#### 效果如下 :arrow_down: :clap:

## 7. 指令 Cog 實作
### Step 1: 建立一個資料夾 core 與建立 classes.py
#### Classes 如下 :arrow_down:
```python=
import discord
from discord.ext import commands
class Cog_Extension(commands.Cog):
def __init__(self,bot):
self.bot = bot
```
### Step 2: 建立一個資料夾 cmds 與建立 main.py 與 react.py
#### main 如下 :arrow_down:
```python=
import discord
from discord.ext import commands
from core.classes import Cog_Extension
#從 core 裡面的 classes 引入 Cog_Extension
class Main(Cog_Extension):
@commands.command() #這邊要用 commands
async def ping(self,ctx): # ctx 代表上下文
await ctx.send("{} (ms)".format(round(self.bot.latency * 1000)))
#由於我們已將self.bot 定義成 bot,所以這邊要用 self.bot
async def setup(bot): # 這邊的 bot 是 bot.py 裡面定義的 bot
await bot.add_cog(Main(bot)) # 呼叫功能 add_cog 並傳入 bot
```
#### react 如下 :arrow_down:
```python=
import discord
import random
from discord.ext import commands
from core.classes import Cog_Extension
#從 core 裡面的 classes 引入 Cog_Extension
with open("data.json","r",encoding="utf8") as file:
jfile = json.load(file)
class React(Cog_Extension):
@commands.command()
async def pic(self,ctx):
r_pic = random.choice(jfile["picture"])
await ctx.send(r_pic)
async def setup(bot): # 這邊的 bot 是 bot.py 裡面定義的 bot
await bot.add_cog(React(bot)) # 呼叫功能 add_cog 並傳入 bot
```
### Step 3: 修改 bot.py
#### bot 如下 :arrow_down:
```python=
import discord
import json
import random
from discord.ext import commands
import os
import asyncio
with open("data.json","r",encoding="utf8") as file:
jfile = json.load(file)
intents = discord.Intents.all()
bot = commands.Bot(command_prefix="/",intents=intents)
@bot.event
async def on_ready():
print("> Bot is online")
@bot.event
async def on_member_join(member):
channel = bot.get_channel(int(jfile["join_channel"]))
await channel.send("{} join!".format(member))
@bot.event
async def on_memeber_remove(member):
channel = bot.get_channel(int(jfile["leave_channel"]))
await channel.send("{} leave!".format(member))
@bot.command() #讀取 Extension
async def load(ctx,extension):
await bot.load_extension("cmds.{}".format(extension))
await ctx.send("Loaded {} done.".format(extension))
@bot.command() #卸載 Extension
async def unload(ctx,extension):
await bot.unload_extension("cmds.{}".format(extension))
await ctx.send("Un-loaded {} done.".format(extension))
@bot.command() #重新讀取 Extension
async def reload(ctx,extension):
await bot.reload_extension("cmds.{}".format(extension))
await ctx.send("Reloaded {} done.".format(extension))
async def main():
for filename in os.listdir("./cmds"):
if filename.endswith(".py"):
await bot.load_extension("cmds.{}".format(filename[:-3]))
await bot.start(jfile["token"])
#由於 bot.run 只能用在程式的結尾,而 bot.start 能在協程後面
if __name__=="__main__": #避免呼叫的時候執行
asyncio.run(main())
```
#### 效果如下 :arrow_down: :clap:
##### 1.分類表

##### 2.將 Main 卸載

##### 3.將 Main 讀取

##### 4.將 Main 重新讀取

## 8. 事件 Cog 實作
### Step 1: 在 cmds 內建立 event.py
#### event 如下 :arrow_down:
```python=
import discord
import json
from discord.ext import commands
from core.classes import Cog_Extension
with open("data.json","r",encoding="utf8") as file:
jfile = json.load(file)
class Event(Cog_Extension):
@commands.Cog.listener()
async def on_member_join(self,member):
channel = self.bot.get_channel(int(jfile["join_channel"]))
await channel.send("{} join!".format(member))
@commands.Cog.listener()
async def on_memeber_remove(self,member):
channel = self.bot.get_channel(int(jfile["leave_channel"]))
await channel.send("{} leave!".format(member))
@commands.Cog.listener()
async def on_message(self,message):
if message.content.startswith("hi"):
await message.channel.send("hello")
async def setup(bot):
await bot.add_cog(Event(bot))
```
:::info
:bulb: 在 Cog 裡面,需用 @commands.Cog.listener()
:::
### Step 2: 優化 on_message 功能
```python=
@commands.Cog.listener()
async def on_message(self,message):
if message.content.startswith("hi"):
await message.channel.send("hello {}".format(message.author))
```
#### 效果如下 :arrow_down: :clap:
##### 回復功能

## 9. 所有檔案
#### bot.py
```python=
import discord
import json
import random
from discord.ext import commands
import os
import asyncio
with open("data.json","r",encoding="utf8") as file:
jfile = json.load(file)
intents = discord.Intents.all()
bot = commands.Bot(command_prefix="/",intents=intents)
@bot.event
async def on_ready():
print("> Bot is online")
@bot.command()
async def load(ctx,extension):
await bot.load_extension("cmds.{}".format(extension))
await ctx.send("Loaded {} done.".format(extension))
@bot.command()
async def unload(ctx,extension):
await bot.unload_extension("cmds.{}".format(extension))
await ctx.send("Un-loaded {} done.".format(extension))
@bot.command()
async def reload(ctx,extension):
await bot.reload_extension("cmds.{}".format(extension))
await ctx.send("Reloaded {} done.".format(extension))
async def main():
for filename in os.listdir("./cmds"):
if filename.endswith(".py"):
await bot.load_extension("cmds.{}".format(filename[:-3]))
await bot.start(jfile["token"])
if __name__=="__main__":
asyncio.run(main())
```
#### event.py
```python=
import discord
import json
from discord.ext import commands
from core.classes import Cog_Extension
with open("data.json","r",encoding="utf8") as file:
jfile = json.load(file)
class Event(Cog_Extension):
@commands.Cog.listener()
async def on_member_join(self,member):
channel = self.bot.get_channel(int(jfile["join_channel"]))
await channel.send("{} join!".format(member))
@commands.Cog.listener()
async def on_memeber_remove(self,member):
channel = self.bot.get_channel(int(jfile["leave_channel"]))
await channel.send("{} leave!".format(member))
@commands.Cog.listener()
async def on_message(self,message):
if message.content.startswith("hi") and message.author != self.bot.user:
await message.channel.send("hello {}".format(message.author))
async def setup(bot):
await bot.add_cog(Event(bot))
```
#### main.py
```python=
import discord
import json
from discord.ext import commands
from core.classes import Cog_Extension
class Main(Cog_Extension): #繼承 Cog 類別
@commands.command()
async def ping(self,ctx): # ctx 代表上下文
await ctx.send("{} (ms)".format(round(self.bot.latency * 1000)))
async def setup(bot):
await bot.add_cog(Main(bot))
```
#### react.py
```python=
import discord
import json
from discord.ext import commands
from core.classes import Cog_Extension
import random
with open("data.json","r",encoding="utf8") as file:
jfile = json.load(file)
class React(Cog_Extension): #繼承 Cog 類別
@commands.command()
async def pic(self,ctx):
r_pic = random.choice(jfile["picture"])
await ctx.send(r_pic)
async def setup(bot):
await bot.add_cog(React(bot))
```
#### classes.py
```python=
import discord
import json
from discord.ext import commands
class Cog_Extension(commands.Cog):
def __init__(self,bot):
self.bot = bot
```
#### data.json
```python=
{
"token": "MTA4NzA2OTQ4ODMwMzg0NTM3Ng.GKa7We.l3bC_c0W5RiUktYVo5l19KDyoNc4EjoXfjZRhs",
"join_channel": "1086638861213499433",
"leave_channel": "1086638911280922624",
"picture": [
"https://th.bing.com/th/id/R.ecc5b5e754b520dc955cff152f934cae?rik=q%2bWWYRBHNo3ROw&pid=ImgRaw&r=0",
"https://th.bing.com/th/id/R.6e9b001ffd29f4013ad85232a900e746?rik=l3eaBT97IM6YNA&pid=ImgRaw&r=0",
"https://th.bing.com/th/id/OIP.0M0PB0odBhmSNPZsksgWFAAAAA?w=204&h=204&c=7&r=0&o=5&dpr=1.3&pid=1.7"
]
}
```
[ToC]
* 官方文件: