You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用discord.py创建Discord频道聊天归档文件?

Discord.py频道聊天归档可行实现方案

核心思路

因为discord-components与chat_exporter存在已知冲突,本方案完全不依赖第三方导出库,手动实现HTML生成+文件发送全逻辑,无额外依赖,不会触发兼容问题。

问题1:生成文件无法发送的修复

常见触发原因是文件写入时被进程锁定,或发送时参数错误,可按照以下方案处理:

  • 保证文件写入完成、流关闭后再调用发送接口,不要在文件写入的上下文管理器内执行发送操作
  • 优先使用BytesIO实现内存生成文件,避免本地磁盘写入的权限、路径、文件锁定问题
  • 正确示例代码:
import discord
from io import BytesIO

# 生成HTML内容后直接转为内存文件发送
async def send_archive(html_content, ctx):
    file_io = BytesIO(html_content.encode("utf-8"))
    file_io.seek(0) # 重置文件指针到开头,否则读取内容为空
    await ctx.send(file=discord.File(file_io, filename="chat_archive.html"))

如果需要留存本地文件,写法如下:

html_content = "你的HTML内容"
file_path = "chat_archive.html"
# 写入文件
with open(file_path, "w", encoding="utf-8") as f:
    f.write(html_content)
# 退出with块后文件已关闭,再执行发送
await ctx.send(file=discord.File(file_path))

问题2:HTML样式不达标的解决方案

直接使用内嵌CSS的单文件HTML模版,所有样式内置,无需加载外部资源,打开即可呈现票务机器人常用的深色主题归档效果,参考模版如下:

# 整体HTML归档模版,所有样式内置
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>聊天归档</title>
    <style>
        * {margin: 0;padding: 0;box-sizing: border-box;font-family: system-ui, -apple-system, sans-serif;}
        body {background: #36393f;color: #dcddde;padding: 24px;max-width: 1200px;margin: 0 auto;}
        .archive-head {padding-bottom: 16px;border-bottom: 1px solid #42464d;margin-bottom: 24px;}
        .archive-head h2 {color: #fff;margin-bottom: 8px;}
        .archive-head p {color: #a3a6aa;font-size: 0.9rem;}
        .msg-item {display: flex;gap: 16px;padding: 12px 8px;border-radius: 4px;margin-bottom: 8px;}
        .msg-item:hover {background: #32353b;}
        .avatar {width: 40px;height: 40px;border-radius: 50%;flex-shrink: 0;}
        .msg-body {flex: 1;min-width: 0;}
        .msg-head {display: flex;align-items: center;gap: 10px;margin-bottom: 4px;}
        .username {font-weight: 600;color: #fff;}
        .time {font-size: 0.75rem;color: #a3a6aa;}
        .msg-content {line-height: 1.6;word-wrap: break-word;}
        .msg-img {max-width: 400px;max-height: 300px;border-radius: 4px;margin-top: 8px;}
        .msg-attachment {margin-top: 8px;color: #00aff4;}
    </style>
</head>
<body>
    <div class="archive-head">
        <h2>频道:{channel_name}</h2>
        <p>归档生成时间:{gen_time}</p>
        <p>消息总数:{msg_count} 条</p>
    </div>
    <div class="msg-list">{msg_html}</div>
</body>
</html>
"""

# 单条消息渲染模版
MSG_TEMPLATE = """
<div class="msg-item">
    <img class="avatar" src="{avatar}" alt="{author}">
    <div class="msg-body">
        <div class="msg-head">
            <span class="username">{author}</span>
            <span class="time">{send_time}</span>
        </div>
        <div class="msg-content">{content}</div>
        {attachments}
    </div>
</div>
"""

归档内容生成逻辑

from datetime import datetime

async def gen_archive_html(channel):
    # 拉取历史消息,按时间正序排列
    messages = await channel.history(oldest_first=True).flatten()
    msg_html_list = []
    for msg in messages:
        # 处理附件
        attach_html = ""
        for att in msg.attachments:
            if att.content_type and att.content_type.startswith("image/"):
                attach_html += f'<img class="msg-img" src="{att.url}" alt="附件">'
            else:
                attach_html += f'<p class="msg-attachment">附件:{att.filename}</p>'
        # 渲染单条消息,默认头像可替换为base64编码实现无外链
        default_avatar = "data:image/png;base64,此处替换为你自己的默认头像base64编码"
        msg_html = MSG_TEMPLATE.format(
            avatar=msg.author.avatar.url if msg.author.avatar else default_avatar,
            author=msg.author.display_name,
            send_time=msg.created_at.strftime("%Y-%m-%d %H:%M:%S"),
            content=msg.content.replace("\n", "<br>"),
            attachments=attach_html
        )
        msg_html_list.append(msg_html)
    # 渲染完整HTML
    full_html = HTML_TEMPLATE.format(
        channel_name=channel.name,
        gen_time=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
        msg_count=len(messages),
        msg_html="".join(msg_html_list)
    )
    return full_html

完整调用示例

# 绑定到discord.py命令
@bot.command()
async def export_archive(ctx):
    html = await gen_archive_html(ctx.channel)
    await send_archive(html, ctx)
    await ctx.send("聊天归档已生成并发送")

如需完全无外部资源,可将所有资源链接转换为base64编码内嵌到HTML中,无需依赖任何外部链接。

内容的提问来源于stack exchange,提问作者Mdog504

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 05:57:03