--- ###### tags: `Sprout` --- # Discord Bot ---- ### What is ChatBot ? * 在通訊平台上,能夠與使用者聊天的機器人 * e.g. Messenger, Line, Discord, Twitch ---- ### What is API ? * Application Programming Interface (應用程式介面) * 讓你的App與其他App溝通的橋樑 ![](https://i.imgur.com/IycWFnp.png =450x300) ---- ### 前情提要 * 由於課程時間有限,我會盡量cover重要的東西 * 課程中會盡量搭配 [API reference](https://discordpy.readthedocs.io/en/stable/api.html),教你如何找資料 * 講義多數內容參考 [Proladon 的 Youtube 頻道](https://www.youtube.com/c/Proladon) --- ### Discord Bot Setup ---- ### 在進入正題之前.. * 建議下載[桌面版 discord app](https://discord.com/download) * 創一個自己的 server ---- ##### 1. https://discord.com/developers/applications ##### Click 'New Application' ![](https://i.imgur.com/7FQlG3c.png) ##### 2. 取名 -> Create ![](https://i.imgur.com/DqdHTFU.png =350x300) ---- #### 3. Bot -> 把 Priviledge Gateway Intents 全部打開 ![](https://i.imgur.com/7PGUON6.png) ---- ##### 5. OAuth2 -> URL Generator -> Scope ![](https://hackmd.io/_uploads/H1pF6DoWR.png) ---- ##### 6. Bot Permissions ![image](https://hackmd.io/_uploads/HkICpDs-0.png) ---- ##### 7. Copy link to browser ![](https://i.imgur.com/u1WQnYR.png) ##### 8. Done ! ![](https://i.imgur.com/8C2EvZn.png =300x400) --- ### Hello, world ---- ##### 取得 token ![](https://i.imgur.com/teoVQVN.png) ##### 下載 discord 模組 ``` pip install discord ``` ##### 下載 dotenv 模組 (把重要的資料存在環境變數) ``` pip install python-dotenv ``` ---- #### 下載 [課程專案](https://github.com/EnzoHuang0807/DcBot_Tutorial/tree/main) #### 複製 .env.defaults 並命名成 .env,再把 token 複製到 .env ![](https://i.imgur.com/afLb2XP.png =500x50) ---- #### For Music Bot ``` pip install pyNaCl ``` * 如果你的作業系統是 Windows : * 把 [FFmpeg 和 yt-dlp 執行檔們](https://drive.google.com/drive/folders/1saBzH-jajJuDgn2_d_Ts_Yf-RtNj6aZa?usp=sharing)下載下來放進 DcBot_Tutorial * 如果你的作業系統不是 Windows * 到 [FFmpeg 的下載網站](https://ffmpeg.org/download.html) 選擇自己的作業系統然後從下載的 packge 中找到 `ffmpeg` 和 `ffprobe` 兩個執行檔 * 到 [yt-dlp 的 GitHub](https://github.com/yt-dlp/yt-dlp) 滑到 Release Files 下載對應的執行檔 ![](https://i.imgur.com/hwkt6AR.png) 你的 Directory 應該會長這樣 ![](https://i.imgur.com/ElRT0dO.png =200x) ---- ##### 執行 `bot.py` ##### 會看到 "Bot is online" ![](https://i.imgur.com/HTDqT7D.png) ##### 在你的 Server 輸入 $Hello ![](https://i.imgur.com/a5eOvXj.png) ##### 成功了! --- ### 專案解說時間 ---- ### 專案管理 * cmds : 儲存指令的地方 * `main.py` : 主要指令 * `event.py` : 間隔 / 定時指令 * `music.py` : 音樂機器人 * `event.py` : event 相關指令 * `core.py` : 讀資料庫、使用Cog管理指令 * `bot.py` : 執行 bot (Ctrl + C 中止) * `.env` : 資料庫 (區隔開發環境與生產環境) ---- ### Cog 是甚麼 ? [Reference](https://www.youtube.com/watch?v=KnO2-0l3BaM) * 將每個擁有 discord 指令的檔案視為模組塊 * 可以自由載入與卸載 (使用bot command) * 改完code之後直接reload該檔案,不用 重新執行bot * 好管理,大作業合作方便 * [詳細實作](https://www.youtube.com/watch?v=4JptXXkqiKU) ---- ![](https://i.imgur.com/1BTccbJ.png =600x600) --- ### Decorator ---- 等等會用到的 code [在這裡](https://github.com/EnzoHuang0807/Sprout/blob/main/DiscordBot.ipynb) ---- ##### 在 python 裡面,所有的東西都是物件,函式也不例外 ##### 嘗試把一個function當作另一個function的參數 ```python= def animal_func(func): def wrap(): print(f"Animal : {func.__name__}") func() return wrap def dog(): print("Woof Woof") def cat(): print("Meow Meow") animal_func(dog)() animal_func(cat)() ``` 此時,`animal_func(func)` "裝飾" 了 `dog()` 和 `cat()`,`animal_func(func)` 就是一個 Decorator ---- #### Syntax Candy "@" : 讓語法簡化的語法 ```python= def animal_func(func): def wrap(): print(f"Animal : {func.__name__}") func() return wrap @animal_func def dog(): print("Woof Woof") @animal_func def cat(): print("Meow Meow") dog() cat() ``` ---- #### Decorator 的意義是甚麼? 如果是有很多個很類似的函式,或者是函式內容很複雜時,就可以使用 Decorator 將 code 簡化 [參考資料](https://medium.com/citycoddee/python%E9%80%B2%E9%9A%8E%E6%8A%80%E5%B7%A7-3-%E7%A5%9E%E5%A5%87%E5%8F%88%E7%BE%8E%E5%A5%BD%E7%9A%84-decorator-%E5%97%B7%E5%97%9A-6559edc87bc0) --- ### Asyncio ---- #### 同步 (Sync) 與異步 (Async) 當你的函式大部分時間都在 "等待",就可以使用異步函式讓你的等待時間拿來做其他事情 [參考資料](https://jimmy-huang.medium.com/python-asyncio-%E5%8D%94%E7%A8%8B-%E4%BA%8C-e717018bb984) ---- #### Sync : 一般的函式 -> 從頭做到尾 ```python= import time def count(): print("One") time.sleep(1) print("Two") def task(): for _ in range(3): count() task() # executed in 3.03 seconds. ``` ---- #### Async : 在標記 await 的地方做其他的事情 ```python= import asyncio import time async def count(): print("One") await asyncio.sleep(1) print("Two") async def task(): await asyncio.gather(count(), count(), count()) asyncio.run(task()) # executed in 1.01 seconds. ``` ---- #### 關鍵字們 * async:用來宣告 function 能夠有異步的功能 * await:用來標記這個 function 切換暫停和繼續的點 * coroutine : 這種 function 的名稱 (中文 : 協程) --- ### 第一個 command 解說 ---- #### Decorator ```python= @commands.command() async def Hello(self, ctx): await ctx.send("Hello, world") ``` 提醒 : 如果要在 `bot.py` 直接寫指令的話,Decorator 要改成 @bot.command 哦 ---- ![](https://i.imgur.com/Vm45win.png =600x450) ---- ctx 是甚麼 ? 它為甚麼有 'send' 這個 method ? ```python= @commands.command() async def Hello(self, ctx): await ctx.send("Hello, world") ``` ---- #### ctx : 前後文參數 discord 內建的class,會記錄呼叫 bot command 的使用者的名稱、所在的channel等等 ![](https://i.imgur.com/fTTJrSs.png =600x450) ---- 除了普通的 string 以外,也可以傳 embed (?) 跟 file ![](https://i.imgur.com/4jzauAl.png =600x600) ---- #### 練習 : 寫一個指令,呼叫該指令bot會傳一張梗圖 (提示 : discord 會自動把看起來像 url 的 string 視為網址) --- ### Embed ---- #### Embed 是甚麼 ? 把文字,網址,圖片等等收集起來一起輸出 ![](https://i.imgur.com/zN9awxe.png =600x450) ---- #### 練習 : 做一個會輸出Embed的指令吧 [好用的網站](https://cog-creators.github.io/discord-embed-sandbox/) 可以把會用到的url寫在`data.json`方便管理 --- ### Event ---- 原本就有的 function, 自己定義細項 ![](https://i.imgur.com/sJ9IAy2.png =600x400) 以 on_member_join 為例 ---- 複製 channel id 到 `.env` 的 "general channel" ![](https://i.imgur.com/TdRZyu4.png =300x) ---- ```python= @commands.Cog.listener() async def on_member_join(self, member): g_channel = self.bot.get_channel(int(os.getenv("general_channel"))) await g_channel.send(f"歡迎{member}加入!") ``` 因為沒有context參數,所以要指定訊息發送的channel ---- #### 手把手查詢 API Reference ![](https://i.imgur.com/8CZUJgL.png =550x250) ![](https://i.imgur.com/dsyknAo.png =550x250) ---- #### 其他 Event * on_message 可以偵測訊息內容 * on_command_error 可以處理例外 (指令輸入錯誤之類) --- ### 間隔 / 定時指令 ---- #### Import 新模組 ``` python= from discord.ext import tasks ``` #### 重新定義__init__ ``` python= def __init__(self): super().__init__() self.peanuts.start() ``` ---- #### 每20秒輸出一次 "Peanuts !" ```python= @tasks.loop(seconds=20.0) async def peanuts(self): await self.bot.wait_until_ready() self.channel = self.bot.get_channel(int(os.getenv("general_channel"))) await self.channel.send("Peanuts !") ``` ![](https://i.imgur.com/zxWI2FJ.png =600x350) ---- * 有了間隔時間執行指令的功能,就可以做出定時執行指令的功能 * i.e., 每隔一段時間就檢查時間到了沒 * 善用datetime模組 * [datetime documentation](https://docs.python.org/3/library/datetime.html) ---- #### 練習 用datetime模組印出24小時制的現在時間 (e.g. 1300, 1400) ##### 關鍵字 * timezone, timedelta * datetime.datetime.now().strftime() --- ### Music Bot ?! ---- #### Music Bot 運作流程 1. 用 `yt-dlp` 搭配 `FFmpeg` 去 youtube下載音檔 (mp3) 2. 把你的 Bot 連到 Voice Channel 3. 呼叫 `discord.FFmpegPCMAudio` 播歌 ---- #### 今天會教的 Music Bot 指令 1. 下載音檔 2. 將 Bot 連到 Voice Channel 播歌 (沒有播放清單) 3. 暫停 / 繼續 4. 卡歌 / 讓 Bot 離開 Voice Channel ---- * 可以用這些基本指令,延伸出 queue, skip 等酷東西 * 使用完 Music Bot 記得呼叫 `$leave` 離開 Voice Channel,不然會因為閒置太久出錯 (重新啟動Bot) * [yt-dlp](https://www.mankier.com/1/yt-dlp) 不是 python 套件,所以我們會用神秘的方法讓 Music Bot 動起來 ---- #### 關於 yt-dlp * yt-dlp 不是 python 套件,所以不能像之前用過的東西一樣直接 import * yt-dlp 是我們放在資料夾中的執行檔 * 可以嘗試在小黑窗 (terminal) 輸入 ```bash= ./yt-dlp.exe <youtube url> ``` * ##### 為了讓我們可以在 python 裡面使用 terminal 的指令,我們呼叫 os.system() ```python= os.system(f"yt-dlp.exe --extract-audio --audio-format mp3 --audio-quality 0 {url}") ``` ---- #### 程式解說 ##### 一起看看專案的 `music.py` ##### 有問題也可以翻 API Reference ---- #### VoiceChannel.connect() 連接 voice channel ![](https://i.imgur.com/JERkGJG.png =560x480) ---- #### 重要的 class : VoiceClient ![](https://i.imgur.com/p3HvMKy.png =560x300) --- ### Wrap ---- #### 今天學到的指令 * 傳文字,圖片,Embed * 間隔 / 定時 指令 * Event * 簡易 Music Bot ---- #### 沒教到的酷東西 * 複雜 Music Bot * Cog 實作 * 訊息複誦,清理訊息 * 子命令 * Reaction 拿身分組 (on_role_reaction_add) * Button ---- ### That's It ! ### 謝謝大家