discord.py

by: 呂家睿

自介

  • 建中資訊38th學術長+副社
  • 原神好玩,貓戰也好玩,釣魚好玩?
  • 我很菜,都被電爛
  • 有程式(或數學?)相關的問題可以問我
  • 頭像是應急食品

其他咚咚

  • C++
  • python
  • 競程是什麼可以吃嗎?
  • 數學好難
  • 喜歡做遊戲開發,歡迎問我、找我、煩我
  • 上面這點很重要,再讀一次

discord.py

What is

BOTS

幫助管理

提供娛樂

結合其他平台

discord.py

TOKEN

Setup

理論可行,無法實踐

安裝vscode

2. 打開下載的檔案,勾選同意然後一直按繼續,最後按安裝

這裝的是windows版,如果回家想裝符合自己裝置的版本點這裡

開file

  1. 開一個folder

2. 用code editor 或 IDE (我們會用vscode)開啟那個folder

3. 加一個file

PyPI

Python Package Index

big code

你想/會寫的

PyPI

你不會/懶得寫的

Pip installs package

是一個指令,可以幫助你處理有關套件的東西。下面是一些常用的指令

pip install package_name 	#從PyPI幫你把你要的套件下載

pip uninstall package_name	#解安裝

pip show package_name		#顯示這個套件的一些資訊

pip list					#顯示你有的所有套件
pip install discord.py

現在在你的terminal打上這個

OK所以我們要先領取一個機器人然後再寫他的程式

1.取名字好難

點new application然後給你的機器人取名

2.點選左邊bot的欄位

把三個priveledge gateway intent 打開

接著要把它丟進你想要的伺服器

3. 點選左邊OAuth2

  • 在OAuth2 URL Generator的地方把bot打勾
  • 把bot permission的 administrator也打勾
  • 最下面會有一個URL,複製你的URL在新分頁把它打開

4. 選擇你想要丟的伺服器

  • 我個人建議你開一個自己的然後丟上去

現在如果你打開你的伺服器,  然後看到機器人在裡面你就成功了!!!!

終於可以寫扣了

Event 

  • 是bot的登入憑證(像是密碼)
  • 有他就可以控制你的bot
  • 不要洩漏出去!!

Token

點這個他會幫你生Token

簡單的東西?

import discord
from discord.ext import commands

TOKEN = "Type Your Token"
intents = discord.Intents.all()
bot = commands.Bot(command_prefix = "?", intents = intents)

bot.run(TOKEN)

先import一些需要的東西

這個是指令前綴,比如說!?*>&%$之類的,可以自己選自己想要的

先import一些需要的東西

記得加這行你的機器人才會運作

  • 把重要資料存在另外一個檔案
  • 可以保密重要資訊(ex: TOKEN)

Token & .env

import discord
from discord.ext import commands

from dotenv import load_dotenv
import os

load_dotenv()
TOKEN = os.getenv("DISCORD_TOKEN")

intents = discord.Intents.all()
bot = commands.Bot(command_prefix = "?", intents = intents)

bot.run(TOKEN)
DISCORD_TOKEN = "Your TOKEN"

1. 開一個.env

2. install python-dotenv

pip install python-dotenv

簡單的東西?

如果你Run這個程式的話,應該會看到妳的bot變成online了

bot該起床了

@bot.event
async def on_ready():
    print(f"阿~~ {bot.user.name} 起床了!")

在你的機器人on_ready(啟動)的時候會叫這個函式

裝飾器

(這個我們有空再講)

機器人的名字

函式名字要打一樣的

async def & await

宣告一個特殊函式,裡面可以進行await功能

  • 加在一行程式前面
  • 告訴電腦這行程式很慢,讓他去先執行別的事情
  • 必須要是awaitable物件才能加await

講白了就是讓電腦不要白等,讓程式更快

async def & await
import time

def weird_func(num):
    print(num)
    time.sleep(2)


start_time = time.time()

for i in range(5):
    weird_func(i)
print(f"Total time = {time.time() - start_time}")

飯粒

沒用async

用async

import time
import asyncio

async def weird_func(num):
    print(num)
    await asyncio.sleep(2)

async def main():
    await asyncio.gather(*(weird_func(i) for i in range(5)))

start_time = time.time()
asyncio.run(main())
print(f"Total time {time.time() - start_time} seconds")
@bot.event
async def on_member_join(member: discord.Member):
    await member.send(f"很高興認識你{member.name}!\n我是機器人{bot.user.name}!\n祝你抽卡不歪!!")

@bot.event
async def on_message(message: discord.Message):
    if message.author == bot.user:
        return
    if "金" in message.content:
        await message.reply("一發出金!!")
        
    await bot.process_commands(message)

有成員加入時會呼叫

有人發訊息時會呼叫

.send會私訊

會回覆這則訊息

這行很重要一定要加

full code

import discord
from discord.ext import commands


TOKEN = "Your token"
intents = discord.Intents.all()
bot = commands.Bot(command_prefix = "?", intents = intents)


@bot.event
async def on_ready():
    print(f"阿~~ {bot.user.name} 起床了!")

@bot.event
async def on_member_join(member: discord.Member):
    await member.send(f"很高興認識你{member.name}!\n我是機器人{bot.user.name}!\n祝你抽卡不歪!!")

@bot.event
async def on_message(message: discord.Message):
    if message.author == bot.user:
        return
    if "金" in message.content:
        await message.reply("一發出金!!")

    await bot.process_commands(message)

    
bot.run(TOKEN)

其他還有很多的event

這邊給幾個建議的可以看看

member類

message類

polls類

reaction類

Prefix Command

!?*>&%$

hello world

@bot.command()
async def hello(ctx: commands.Context):
    await ctx.send(f"{ctx.author.mention}很高興認識你")

從event變成了command呢

這個是會@發訊息的人

大概長這樣子

如果不行的話可能是你的on_message函式沒加這行

await bot.process_commands(message)
from datetime import datetime

@bot.command()
async def time(ctx: commands.Context):
    cur_time = datetime.now()
    cur_time_string = cur_time.strftime("%Y/%m/%d %H:%M:%S")
    await ctx.reply(f"讓我掐指一算,現在大概是{cur_time_string}左右")

記得import才能用喔

一些有趣的飯粒

指令也可以結合python的其他library

這個就是簡單的求時間指令

大概長這樣

import random

rng = random.SystemRandom()

@bot.command()
async def wish(ctx: commands.Context):
    await ctx.reply(f"Hmmm所以你想要抽卡是吧?那就助你出金囉\n**請自行腦補抽卡介面**")
    wish_rng = rng.randint(1, 1000)
    if wish_rng <= 970:
        await ctx.reply(f"恩好吧你抽到了垃圾呢")
    elif wish_rng <= 997:
        await ctx.reply(f"歐優不錯喔出紫了")
    else:
        await ctx.reply(f"OMG出金了!!!!woooooooooooooooooooo")

非常簡易的賭博抽卡模擬器

random也是非常實用的library大家成發時也可以多多使用!

更多飯粒

大概這樣子

更多指令參數

有時候你的指令可能需要帶一些參數才能執行,要怎麼做呢?

@bot.command()
async def add(ctx: commands.Context, num1: int, num2: int):
    await ctx.reply(f"加起來會是{num1+num2}!")

要幾個寫幾個大膽寫就對了

判斷吃的參數是根據空格,如果有加""的話會當作一整個參數

老樣子還是放個圖

更多更多指令參數

那如果我不想要一些我想要把所有的都吃進來怎麼辦?

方法一

@bot.command()
async def echo(ctx: commands.Context, *args):
    await ctx.reply(f"You've just said {args}.\n And the first word is {args[0]}")

參數的吃法跟上一個是一樣的,直接用args的話會是一個tuple

方法二

@bot.command()
async def echo2(ctx: commands.Context, *, message):
    await ctx.reply(f"You've just said {message}.\n And the first word is {message[0]}")

參數是一個字元一個字元吃,直接用message會是string的形式

權限和error

有些指令可能不想給所有人使用,這時候就可以設定role權限

@bot.command()
@commands.has_role("mod")
async def wish(ctx: commands.Context):
    await ctx.reply(f"Hmmm所以你想要抽卡是吧?那就助你出金囉\n**請自行腦補抽卡介面**")
    wish_rng = rng.randint(1, 1000)
    if wish_rng <= 970:
        await ctx.reply(f"恩好吧你抽到了垃圾呢")
    elif wish_rng <= 997:
        await ctx.reply(f"歐優不錯喔出紫了")
    else:
        await ctx.reply(f"OMG出金了!!!!woooooooooooooooooooo")

@wish.error
async def wish_error(ctx: commands.Context, error):
    if isinstance(error, commands.MissingRole):
        await ctx.send("You don't have the permission to use that command")

這行限制command的使用

出error的時候要做的事情

error大概長這樣

Slash Command

/////////////////////////////////

slash指令

  • 打指令的時候會出現提示
  • 可以加描述
  • 可以加上option讓使用更直觀

slash指令

@bot.event
async def on_ready():
    print(f"阿~~ {bot.user} 起床了!")
    slash_commands = await bot.tree.sync()
    for i in slash_commands:
            print(f"已註冊: {i}")
        

@bot.tree.command(name = "hello", description = "Says hello to you")
async def hello(interaction: discord.Interaction):
    await interaction.response.send_message(f"你好啊{interactions.user.mention}")

slash command前面要加這行裝飾器

name的地方填指令名字

description就填你想給使用者看的描述

如果你的指令註冊成功把它印出來

options

@bot.tree.command(name = "hello", description = "Says hello to you")
async def hello(interaction: discord.Interaction, message: str):
    await interaction.response.send_message(f"你好啊{interactions.user.mention}\n{message}")

跟前面prefix command一樣使用更多參數時,你可以加上option操作

程式上也跟prefix command差不多

會幫你生出神奇的小框框

小缺點:

1. 參數不能太長(小於100字元)

2. 不能像prefix command *args吃全部內容

option promax

import typing

@bot.tree.command(name = "hello", description = "Says hello to you")
async def hello(interaction: discord.Interaction, message: typing.Optional[str] = "祝你一發出金"):
    await interaction.response.send_message(f"你好啊{interactions.user.mention}\n{message}")

剛剛的參數,但是不強制要求要填

也能做到選擇題的效果

@bot.tree.command(name = "hello", description = "Says hello to you")
async def hello(interaction: discord.Interaction, message: typing.Literal["你", "好", "阿"]):
    await interaction.response.send_message(f"你好啊{interactions.user.mention}\n{message}")

auto complete

簡單講就是可以讓你的option變得像google搜尋那樣聰明

infor38 = ["陳柏安", "張雲淞", "江彥成", "呂家睿", "李家睿", 
           "葉倚誠", "邱楷鈞", "葉彥辰", "彭星寶", "林禹喆"]

@bot.tree.command(name = "do_you_know", description = "可以問我知不知道這個人")
async def do_you_know(interaction: discord.Interaction, name: str):
    await interaction.response.send_message(f"那必須的{name}那麼電!")

@do_you_know.autocomplete("name")
async def name_autocomplete(interaction: discord.Interaction, current: str):
    matches = []
    for name in infor38:
        if current in name:
            matches.append(discord.app_commands.Choice(name = name, value = name))
    return matches[:25]

一個帶option的指令

google本狗在此

找推薦選項的邏輯

Guild 欄位

全球通用

只能在一個伺服器

1.點使用者設定

2.進階 -> 開發者模式打開

3.右鍵伺服器點複製伺服器ID

GUILD_ID =  discord.Object(id = your id number)


@bot.event
async def on_ready():
    print(f"阿~~ {bot.user} 起床了!")
    guild_only = await bot.tree.sync(guild = GUILD_ID)
    slash_commands = await bot.tree.sync()
    for i in slash_commands:
        print(f"已註冊: {i}")
    for i in guild_only:
        print(f"已註冊: {i}")


@bot.tree.command(name = "hello", description = "Says hello to you", guild = GUILD_ID)
async def hello(interaction: discord.Interaction):
    await interaction.response.send_message(f"你好啊{interactions.user.mention}")

記得sync不然指令會找不到

Guild 欄位

其他伺服器

有許可的伺服器

@bot.tree.command(name = "hello", description = "Says hello to you", guild = GUILD_ID)
async def hello(interaction: discord.Interaction):
  	if not interaction.user.guild_permissions.send_messages:
        return
    
    if interaction.user.guild_permissions.administrator:
        await interaction.response.send_message(f"{interaction.user.mention}管理員大人好!\n")
    else:
        await interaction.response.send_message(f"你好啊{interaction.user.mention}\n")

權限和error again

也可以用role名稱來判斷

role = discord.utils.get(interaction.guild.roles, name = "mod")
if role not in interaction.user.roles:

Fancy Stuff

Embed Message

@bot.tree.command(name = "embed", description = "example embed message", guild = GUILD_ID)
async def embed_create(interaction: discord.Interaction):
    embed = discord.Embed(title = "My title", 
                          url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
                          description = "Click the title!", color = discord.Color.blurple())
    embed.set_thumbnail(url = "https://preview.redd.it/mq2hik89nqt01.png?auto=webp&s=d5544eea984961c596c4e68452300183a28add61")
    embed.add_field(name = "欄位1", value = "這是一個欄位沒什麼神奇的")
    embed.add_field(name = "欄位2", value = "這是另一個欄位,也沒什麼神奇的")
    embed.set_footer(text = "確定不點一下標題嗎?")
    embed.set_author(name = interaction.user.name)

    await interaction.response.send_message(embed = embed)

Buttons

class View(discord.ui.View):
    @discord.ui.button(label = "button 1", style = discord.ButtonStyle.green, emoji = "⚡")
    async def button_call(self, button, interaction):
        await button.response.send_message("You are so dian!")

    @discord.ui.button(label = "button 2", style = discord.ButtonStyle.blurple, emoji = "💀")
    async def button_2_call(self, button, interaction):
        await button.response.send_message(":skull:")
    
    @discord.ui.button(label = "button 3", style = discord.ButtonStyle.red, emoji = "🤯")
    async def button_3_call(self, button, interaction):
        await button.response.send_message("CPU overload")


@bot.tree.command(name = "button", description = "example button", guild = GUILD_ID)
async def buttons(interaction: discord.Interaction):
    await interaction.response.send_message(view = View())

AIBOT

with Gemini

瑣碎的作業

3. 一些基本的code

2. pip install google

pip install -q -U google-genai
from google import genai

GEMINI_API_KEY = "Your api key"

my_ai = genai.Client(api_key = GEMINI_API_KEY)
ai_model = "gemini-2.5-flash"

用API key建立你的模型

注重安全性或想要做成github public project記得也把API KEY放在.env裡面

拿來用

def ask_ai(question: str):
    answer =  my_ai.models.generate_content(model =  ai_model, contents = question)
    return answer
 
@bot.command()
async def chat(ctx: commands.Context, *, question: str):
    answer =  ask_ai(question).text
    await ctx.reply(answer)

生成gemini回復

bot command

恩真的好爛的笑話

gemini生成設定

GENERATION_CONFIG = {
    "system_instruction": "You are a weird guy, like a quirky but funny one.",
    "temperature": 0.8,     
    "top_p": 0.8,             
    "top_k": 4,            
    "max_output_tokens": 1000,
    "safety_settings" : [
    {"category": "HARM_CATEGORY_HARASSMENT",       "threshold": "BLOCK_NONE"},
    {"category": "HARM_CATEGORY_HATE_SPEECH",      "threshold": "BLOCK_NONE"},
    {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT","threshold": "BLOCK_ONLY_HIGH"},
    {"category": "HARM_CATEGORY_DANGEROUS_CONTENT","threshold": "BLOCK_ONLY_HIGH"},
    ]
}

0~1, 越大越有創意(抽象?

最多能生成的字元

HARM_CATEGORY_HARASSMENT 騷擾

HARM_CATEGORY_HATE_SPEECH 仇恨言論

HARM_CATEGORY_SEXUALLY_EXPLICIT 煽情露骨

HARM_CATEGORY_DANGEROUS_CONTENT 危險內容

HARM_CATEGORY_CIVIC_INTEGRITY 公民誠信

篩選等級:

BLOCK_NONE

BLOCK_ONLY_HIGH

BLOCK_MEDIUM_AND_ABOVE

BLOCK_LOW_AND_ABOVE

HARM_BLOCK_THREHOLD_UNSPECIFIED

不額外篩選

全部擋掉

上完啦!

是不是感覺解脫了

總之大家成發加油啦!!!有問題歡迎問我

discord bot

By ck11300111呂家睿

discord bot

  • 258