Ubuntu TechHive
building-a-discord-bot-using-open-ai-and-python.md
使用 OpenAI 和 Python 构建 Discord 机器人
article.细节

使用 OpenAI 和 Python 构建 Discord 机器人

reading.进展 6 分钟阅读数

使用 OpenAI 和 Python 构建 Discord 机器人

使用 OpenAI 和 Python 构建 Discord 机器人

Discord

Discord 是一个多功能的通信平台,最初是为游戏玩家设计的,但此后已将其吸引力扩展到更广泛的在线社区和群体。它提供了一套丰富的功能,旨在通过文本、语音和视频通信来促进互动和建立社区。

Discord 的主要功能:

  1. *服务器和频道*:

    • *服务器*:Discord 服务器就像独立的社区中心,人们可以在这里聚集、交流和分享内容。每个服务器都可以细分为多个频道。
    • *频道*:这些是服务器内为特定主题或互动类型指定的区域。频道可以是基于文本的,也可以是基于语音的,服务器管理员可以创建多个频道来有效地组织讨论。
  2. *语音和视频通信*:

    • 用户可以加入语音频道,使用麦克风与他人交谈、共享屏幕,甚至进行视频聊天。此功能在游戏玩家中特别受欢迎,用于协调游戏内活动,但也用于虚拟会议、在线课程和休闲语音聊天。
  3. *文本消息*:

    • 文本频道允许用户通过键入的消息进行交流。这些频道支持富文本格式、直接消息、提及和嵌入式媒体(图像、视频和链接)。
  4. *集成和机器人*:

    • Discord 支持与其他服务(如 Spotify、Twitch 和许多游戏服务)的集成,通过允许流式传输音乐、共享游戏统计数据或直播游戏过程来增强用户体验。
    • 机器人是 Discord 的另一个强大功能,可以自动执行任务、管理服务器活动,甚至为用户添加有趣的互动元素。
  5. *角色和权限*:

    • Discord 允许在服务器内对用户角色和权限进行广泛的自定义。管理员可以分配具有特定权限的角色,以控制谁可以访问某些频道、修改服务器设置或管理其他用户。
  6. *安全和隐私*:

    • Discord 提供强大的隐私控制和安全功能,包括双重身份验证、审核工具,以及控制谁可以发送直接消息或添加你为好友的能力。
  7. *跨平台使用*:

    • Discord 可通过 Windows、macOS 和 Linux 的桌面应用程序、Android 和 iOS 的移动应用程序访问,也可以直接在 Web 浏览器中运行,使其具有广泛的可访问性。

OpenAI

OpenAI 是一家人工智能研究机构,旨在以造福全人类的方式促进和发展友好的人工智能。OpenAI 由埃隆·马斯克 (Elon Musk)、萨姆·奥特曼 (Sam Altman) 等人于 2015 年 12 月创立,最初是一家非营利实体,但后来成立了一个有利润上限的部门以吸引外部投资。

OpenAI 的关键方面包括:

  • 研究与开发: OpenAI 在人工智能和机器学习领域进行前沿研究,重点是开发先进的人工智能技术和能力。
  • 安全与伦理: OpenAI 使命的核心部分是确保人工智能技术的开发是安全且符合伦理的,并负责任地管理潜在风险。
  • GPT 模型: OpenAI 以其生成式预训练 Transformer (GPT) 模型而闻名,这些模型能够执行广泛的自然语言处理任务。
  • 协作与许可: OpenAI 与各种组织和公司合作,并通过 API 服务提供其人工智能模型的商业版本,例如 GPT-3。

Replit

Replit(前身为 Repl.it)是一个在线集成开发环境 (IDE),允许用户在线编写、运行和共享代码。它是一个为开发人员、教育工作者和学生设计的强大工具,旨在使编程更易于访问和协作。Replit 支持多种编程语言,包括 Python、JavaScript、Ruby、C++ 等。

Replit 的主要功能:

  1. *即时 IDE 设置*:

    • *零安装*:Replit 完全在浏览器中运行,无需在本地机器上安装软件或管理依赖项。
    • *支持多种语言*:Replit 支持 50 多种编程语言,允许你在项目和语言之间切换,而无需任何额外的设置。
  2. *协作编码*:

    • *实时协作*:与 Google Docs 类似,Replit 允许多个用户在同一环境中同时编写代码。此功能对于结对编程、教学或协作解决问题特别有用。
    • *多人模式*:此模式支持与其他用户进行实时协作,包括一个聊天功能,可以直接在 IDE 内讨论代码。
  3. *托管和部署*:

    • *即时部署*:Replit 提供托管服务,这意味着你可以直接从平台开发和部署 Web 应用程序。它为每个项目生成一个公开的实时 URL。
    • *常驻 Repls*:对于付费用户,Replit 提供了即使在关闭浏览器后也能保持应用程序持续运行的能力,这对于机器人、Web 应用程序和其他持续性服务非常有用。
  4. *版本控制*:

    • *内置 Git 支持*:Replit 集成了 Git,允许用户从任何外部 Git 仓库推送和拉取代码。这种集成通过 GitHub 等平台促进了版本控制和代码共享。
  5. *教育工具*:

    • *课堂管理*:教育工作者可以直接在 Replit 内管理编程作业、提供反馈并监控学生的进度。由于其易用性和极简的设置,它在教育环境中被广泛使用。
    • *模板和示例*:Replit 提供了一系列模板和示例项目,可以帮助用户快速上手新的语言或框架。
  6. *Nix 环境*:

    • *可自定义的环境*:高级用户可以使用 Nix(一种强大的包管理工具)自定义他们的编码环境。此功能允许开发人员定义包含特定系统依赖项的精确项目环境。
  7. *集成数据库支持*:

    • *内置数据库*:Replit 包含一个用于简单键值存储的内置数据库功能,这对于需要持久化数据而无需设置外部数据库的应用程序非常有用。

Replit 的最佳用途:

  • *快速原型设计*:无需担心环境设置即可快速构建应用程序原型。
  • *学习与教育*:由于其可访问性和易用性,非常适合课堂和自学。
  • *技术面试*:通过共享会话轻松进行编程面试和评估。
  • *黑客马拉松*:在黑客马拉松期间快速启动项目,而不会浪费时间在设置上。

演示

旁注:

Async & await

在 Python 中,`async` 和 `await` 是用于定义和处理异步代码的关键字,这些代码允许你在不阻塞程序执行的情况下执行长时间运行的操作。这对于 I/O 密集型和网络密集型任务特别有用,例如发出网络请求、读取或写入文件等,程序大部分时间都在等待外部事件。

以下是这两个关键字的详细说明以及它们在 Python 中的工作方式:

  • `async` 关键字
  1. *定义异步函数*:`async` 关键字用于将函数声明为“异步函数”。这意味着该函数是非阻塞的,并返回一个 `awaitable` 对象,具体来说是一个 `asyncio.Future` 对象。异步函数使用 `async def` 语法定义:

    async def fetch_data():
        # 函数体

    通过使用 `async def` 声明函数,你是在告诉 Python 该函数可以执行异步操作,并且可以被暂停和恢复。

  • `await` 关键字
  1. *暂停和恢复执行*:`await` 关键字在异步函数内部使用,用于暂停函数的执行,直到 `awaitable` 对象(例如其他异步函数返回的对象)被解析。这允许函数在等待结果可用的同时,让其他任务在期间运行。

    async def fetch_data():
        data = await some_async_function()
        return data

    在此示例中,`some_async_function()` 是另一个异步函数,`await` 会暂停 `fetch_data()`,直到 `some_async_function()` 完成其任务并返回一些数据。

  • `async` 和 `await` 如何协同工作
  • *并发*:`async` 和 `await` 共同提供了一种在 Python 中编写并发代码的方法。通过使用这些关键字,你可以通过允许 Python 在等待 I/O 操作完成时处理其他任务来保持应用程序的响应能力。这不是并行执行,而是并发执行(通过协作式多任务处理)。
  • *事件循环*:`async` 和 `await` 背后的核心概念是事件循环,它由 Python 中的 `asyncio` 库提供。事件循环管理和分配不同任务的执行。它跟踪所有正在运行的任务,并在被等待的操作完成后立即恢复被 `await` 暂停的函数。

示例用法

这是一个使用 `asyncio` 异步执行任务的简单示例:

import asyncio

async def main():
  print("Hello")
  await asyncio.sleep(1)  # 异步睡眠,不会阻塞事件循环
  print("world")

# 运行异步主函数
asyncio.run(main())

实际应用

  • *Web 开发*:在 Web 服务器和 Web 应用程序中,`async` 和 `await` 允许同时处理多个 Web 请求。这使得处理高负载和执行多个网络请求变得高效。
  • *数据收集*:它们在需要通过互联网从多个来源(如 API 或数据库)收集数据的场景中非常有用,而无需等待每个任务按顺序完成。

第 0 步:你好,机器人!

  • 第 1 步:安装 discord.py

首先,你需要安装 discord.py 库。 你可以使用 Rye 作为包管理器。

  • 第 2 步:设置你的 Discord 机器人

前往 Discord 开发者门户 (https://discord.com/developers/applications)。 创建一个新应用程序并为其命名。 转到“Bot”选项卡并点击“Add Bot”。 复制生成的令牌。运行机器人时需要此令牌。

  • 第 3 步:编写 Python 脚本

创建一个使用机器人令牌登录并响应消息的 Python 脚本。以下是一个响应简单命令的机器人基础示例:

环境变量

import discord
import os
from dotenv import load_dotenv

# 从 .env 文件加载环境变量
load_dotenv()

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

client = discord.Client(intents=intents)

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

@client.event
async def on_message(message):
    # 不要让机器人响应自己
    if message.author == client.user:
        return

    elif message.content.startswith('!hello'):
        await message.channel.send('Hello!')

    elif message.content.startswith('hi'):
        await message.channel.send('welcome!')

    elif message.content.startswith('what is the best meetup up there?'):
        await message.channel.send('The Ubuntu Tech Hive :)')

    elif message.content.startswith('!bye'):
        await message.channel.send('Goodbye!')

    else:
        await message.channel.send("I did not understand your request!!")


client.run(os.environ["DISCORD_TOKEN"])import math  # 导入 math 库

使用了什么:

  • dotenv api
  • Discord 令牌
  • Discord api 机器人
  • Python3

局限性

  • 机器人无法主动参与。
  • 仅根据条件响应特定提示。
  • 不太实用。

第 1 步:让我们让机器人更有用

所需工具: OpenAi api dotenv api Discord 令牌 Python3

import discord
import my_openai
import os
from dotenv import load_dotenv

# 从 .env 文件加载环境变量
load_dotenv()

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

client = discord.Client(intents=intents)

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

@client.event
async def on_message(message):
    # 不要让机器人响应自己
    if message.author == client.user:
        return

    if message.content.startswith('$hello'):
        await message.channel.send('Hello, how can I help?')

# 检查消息是否以问题关键字开头
    if message.content.startswith('!ask'):
        # 移除命令部分并去除前导/尾随空格
        user_question = message.content[len('!ask'):].strip()

        if user_question:

            try:
                response = my_openai.generate_answer(message.content)
                await message.channel.send("Hang Tight! Looking for the response...")
                await message.channel.send(response)

            except Exception as e:
                await message.channel.send(f"Error processing your request: {str(e)}")
        else:
            await message.channel.send("Please provide a question after !ask")


    if message.content.startswith('$question'):
        response = my_openai.generate_answer(message.content)
        await message.channel.send("looking for response")
        await message.channel.send(response)

# 将 'your_token_here' 替换为你机器人的实际令牌
client.run(os.environ["DISCORD_TOKEN"])
from openai import OpenAI
import os

# 设置你的 OpenAI API 凭据
client = OpenAI(
    # 这是默认值,可以省略
    api_key=os.environ["OPENAI_TOKEN"],
)


MODEL = "gpt-3.5-turbo"
# 定义生成答案的函数
def generate_answer(question):
    response = client.chat.completions.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": "You are a helpful assistant in a discord server answering question and being helpful."},
            {"role": "user", "content": question},
        ],
        temperature=0,
    )

    return response.choices[0].message.content

局限性

  • 机器人仍然是本地的。
  • 对我们的 Ubuntu 或其他人没有用处。
  • 没有状态保存。

使用了什么:

  • dotenv api
  • Discord 令牌
  • Discord api 机器人
  • Python3
  • openai api 密钥

第 2 步:让我们部署,引入 Replit

import discord
import my_openai
import os
from dotenv import load_dotenv

# 从 .env 文件加载环境变量
load_dotenv()

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

client = discord.Client(intents=intents)

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

@client.event
async def on_message(message):
    # 不要让机器人响应自己
    if message.author == client.user:
        return

    if message.content.startswith('$hello'):
        await message.channel.send('Hello, how can I help?')

# 检查消息是否以问题关键字开头
    if message.content.startswith('!ask'):
        # 移除命令部分并去除前导/尾随空格
        user_question = message.content[len('!ask'):].strip()

        if user_question:

            try:
                response = my_openai.generate_answer(message.content)
                await message.channel.send("Hang Tight! Looking for the response...")
                await message.channel.send(response)

            except Exception as e:
                await message.channel.send(f"Error processing your request: {str(e)}")
        else:
            await message.channel.send("Please provide a question after !ask")


    if message.content.startswith('$question'):
        response = my_openai.generate_answer(message.content)
        await message.channel.send("looking for response")
        await message.channel.send(response)

# 将 'your_token_here' 替换为你机器人的实际令牌
client.run(os.environ["DISCORD_TOKEN"])
from replit import db

# URL 及其对应的自定义键
urls = {
    "deep-dive-into-rag-with-langchain": "https://dev.ubuntuhive.tech/en-us/articles/deep-dive-into-rag-with-langchain/",
    "exploring-new-chatgpt-features": "https://dev.ubuntuhive.tech/en-us/articles/exploring-new-chatgpt-features-/",
    "build-small-apps-with-llms": "https://dev.ubuntuhive.tech/en-us/articles/build-small-apps-with-llms/",
    "docker-orchestra-swarm-k8s-kubernetes-for-beginners": "https://dev.ubuntuhive.tech/en-us/articles/docker-orchestra-swarm-k8s-kubernetes-for-beginners/",
    "devops-from-scratch-automated-basics": "https://dev.ubuntuhive.tech/en-us/articles/devops-from-scratch-automated-basics/",
    "from-networks-to-http-and-apis": "https://dev.ubuntuhive.tech/en-us/articles/from-networks-to-http-and-apis/",
    "data-and-cloud-infrastructure-as-code": "https://dev.ubuntuhive.tech/en-us/articles/data-and-cloud-infrastructure-as-code/"
}

# 存储在数据库的 'articles' 键下
db["articles"] = urls
from openai import OpenAI
import os



# 设置你的 OpenAI API 凭据
# my_api_key = ''
client = OpenAI(
    # 这是默认值,可以省略
    api_key=os.environ["OPENAI_TOKEN"],
)


MODEL = "gpt-3.5-turbo"
# 定义生成答案的函数
def generate_answer(question):
# 带有系统消息的示例
    response = client.chat.completions.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": "You are a helpful assistant in a discord server answering question and being helpful."},
            {"role": "user", "content": question},
        ],
        temperature=0,
    )

    return response.choices[0].message.content

使用了什么:

  • dotenv api
  • Discord 令牌
  • Discord api 机器人
  • Python3
  • openai api 密钥
  • replit

第 3 步:欢迎新成员

import discord
import my_openai as opai
import os
from discord.ext import commands
from replit import db
from dotenv import load_dotenv

# 从 .env 文件加载环境变量
load_dotenv()

intents = discord.Intents.default()
intents.message_content = True
# 初始化 Discord 机器人
bot = commands.Bot(command_prefix='!',intents=intents)

@bot.event
async def on_ready():
    print(f'Logged in as {bot.user}!')

@bot.command(help="Ask the user what kind of summary they are looking for.")
async def choose_article(ctx):
    # 从 Replit 数据库检索文章
    articles = db["articles"]
    # 创建带有选项的消息
    options = "\n".join([f"Send {idx+1} for {key.replace('-', ' ').title()}" for idx, key in enumerate(articles.keys())])
    prompt = "Please choose from the below list:\n" + options
    await ctx.send(prompt)

    # 用于验证用户选择的检查函数
    def check(m):
        return m.author == ctx.author and m.content.isdigit() and 1 <= int(m.content) <= len(articles)

    try:
        # 等待用户响应
        msg = await bot.wait_for('message', check=check, timeout=60.0)  # 60 秒回复时间
        choice = int(msg.content) - 1
        selected_key = list(articles.keys())[choice]
        selected_article = articles[selected_key]
        # 你可以在此处添加额外功能来总结文章
        await ctx.send(f"You selected: {selected_key.replace('-', ' ').title()}\nURL: {selected_article}")
        await ctx.send("Please wait while I get you the summary...")
        await ctx.send(opai.generate_summary(selected_article))

    except Exception as e:
        await ctx.send('No valid input received or timeout reached. Please try again.')

@bot.command(help="Sends a welcome message with useful resources.")
async def new_member(ctx):
    articles = db["articles"]
    most_recent_article_key = list(articles.keys())[-1]  # 获取最新文章键
    most_recent_article_url = articles[most_recent_article_key]  # 获取 URL

    # 欢迎消息和资源
    welcome_message = (
        f"Welcome to the server, {ctx.author.mention}! 🎉 Here are some resources you might find helpful:\n"
        f"1. Check out our latest article: {most_recent_article_url}\n"
        f"2. Visit our Ubuntu Hive website for more insights: https://dev.ubuntuhive.tech/en-us/\n"
        f"3. Learn more about tech with this comprehensive tutorial: [Insert Your Tutorial Link Here]\n"
        f"4. Other misc resources and info: [Insert More Links or Info Here]\n"
    )

    await ctx.send(welcome_message)

# 运行机器人
token = os.getenv("DISCORD_TOKEN")
if token:
  bot.run(token)
else:
  print("Error: Discord token not found in environment variables. Please ensure DISCORD_TOKEN is set.")
from replit import db

# URL 及其对应的自定义键
urls = {
    "deep-dive-into-rag-with-langchain": "https://dev.ubuntuhive.tech/en-us/articles/deep-dive-into-rag-with-langchain/",
    "exploring-new-chatgpt-features": "https://dev.ubuntuhive.tech/en-us/articles/exploring-new-chatgpt-features-/",
    "build-small-apps-with-llms": "https://dev.ubuntuhive.tech/en-us/articles/build-small-apps-with-llms/",
    "docker-orchestra-swarm-k8s-kubernetes-for-beginners": "https://dev.ubuntuhive.tech/en-us/articles/docker-orchestra-swarm-k8s-kubernetes-for-beginners/",
    "devops-from-scratch-automated-basics": "https://dev.ubuntuhive.tech/en-us/articles/devops-from-scratch-automated-basics/",
    "from-networks-to-http-and-apis": "https://dev.ubuntuhive.tech/en-us/articles/from-networks-to-http-and-apis/",
    "data-and-cloud-infrastructure-as-code": "https://dev.ubuntuhive.tech/en-us/articles/data-and-cloud-infrastructure-as-code/"
}

# 存储在数据库的 'articles' 键下
db["articles"] = urls
from openai import OpenAI
import os

# 设置你的 OpenAI API 凭据
# my_api_key = ''
client = OpenAI(
    # 这是默认值,可以省略
    api_key=os.environ["OPENAI_TOKEN"],
)


MODEL = "gpt-3.5-turbo"
# 定义生成答案的函数
def generate_answer(question):
# 带有系统消息的示例
    response = client.chat.completions.create(
        model=MODEL,
        messages=[
            {"role": "system", "content": "You are a helpful assistant in a discord server answering question and being helpful."},
            {"role": "user", "content": question},
        ],
        temperature=0,
    )

    return response.choices[0].message.content

def generate_summary(articles):
  response = client.chat.completions.create(
    model=MODEL,
    messages=[
        {"role": "system", "content": "You are a helpful assistant in a discord server. You are great as providing summaries of text when given an article link. when prompted, provide a max of 200 words summary of the provided link"},
        {"role": "user", "content": articles},
    ],
    temperature=0,
  )

  return response.choices[0].message.content

重要链接