跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI算法

Gemini QQ 机器人部署指南 (NapCat + NoneBot2)

在 Ubuntu 22.04 LTS 环境下部署 Gemini QQ 机器人的步骤。采用 OneBot V11 (NapCat) 协议端结合 NoneBot2 框架,集成 Google Gemini Flash 模型实现智能对话。内容涵盖服务器基础环境搭建、Docker 容器配置、Python 虚拟环境及依赖安装、核心业务代码编写、Systemd 服务管理及 WebUI 连接配置。机器人支持群聊记忆、主动插话、打卡统计、勋章系统及管理员指令控制,适合希望自建 AI 社群助手的用户参考。

MongoKing发布于 2026/4/5更新于 2026/7/2056 浏览

Gemini QQ 机器人部署指南

核心架构:OneBot V11 (NapCat) + NoneBot2 + Gemini Flash 适用系统:Ubuntu 22.04 LTS (云服务器)


第一阶段:基础设施准备

SSH 连接服务器后,复制以下命令执行。

安装必要软件 (Docker + Python)

# 更新软件源
sudo apt update && sudo apt upgrade -y
# 安装 Docker
curl -fsSL https://get.docker.com | bash
# 安装 Python3 及虚拟环境工具
sudo apt install python3-pip python3-venv -y
# 创建项目文件夹
mkdir -p /root/gemini_bot
cd /root/gemini_bot

第二阶段:部署 QQ 协议端 (NapCat)

【注意事项】:这里使用 --net=host 模式启动。这样容器和宿主机共用网络,配置 WebSocket 时直接填 127.0.0.1 即可,彻底解决连接不上、找不到 IP 的问题。

  1. 放行防火墙端口
    • 请在云服务商控制台的'安全组'或'防火墙',放行 TCP 6099 和 TCP 8080。

启动 NapCat 容器
(请将下方命令中的 你的 QQ 号 替换为实际数字)

docker run -d\
  --name napcat \
  --net=host \
  --restart always \
  -e ACCOUNT=你的 QQ 号 \
  -v /root/gemini_bot/napcat/config:/app/config \
  mlikiowa/napcat-docker:latest

第三阶段:部署核心代码

【注意事项】:这里锁定安装 0.3.2 版本的 Google 库,这是目前配合此代码最稳定的版本。

  1. 创建 memory.py
    • 使用 nano memory.py 创建文件,填入你的 API Key。

配置 Python 环境

# 确保在项目目录
cd /root/gemini_bot
# 创建虚拟环境
python3 -m venv venv
# 激活环境
source venv/bin/activate
# 安装依赖 (锁定版本)
pip install nonebot2[fastapi] nonebot-adapter-onebot google-generativeai==0.3.2 pytz

写入 memory.py 内容

import google.generativeai as genai
import asyncio
import datetime
import pytz
import pickle
import os
import random

# ================= 核心配置区 =================
API_KEY = "在这里粘贴你的 Google_API_Key"  # 必须替换!
DATA_FILE = "bot_memory.pkl"
# ============================================

# 旧版兼容写法(0.3.2 支持)
genai.configure(api_key=API_KEY)
chat_model = genai.GenerativeModel('gemini-1.5-flash')

class GroupMemory:
    def __init__(self, group_id):
        self.group_id = group_id
        self.summary = "暂无早期历史记录。"
        self.buffer = []
        self.lock = asyncio.Lock()
        # 状态追踪
        self.last_active_time = 0
        self.last_msg_content = ""
        self.repeat_count = 0

    # ===== 每个群独立的参数配置 =====
    self.bot_config = {
        "COOLDOWN_SECONDS": 0,
        "TRIGGER_PROBABILITY": 0.05,
        "REPEATER_THRESHOLD": 2,
        "REPEATER_EXEC_PROB": 0.1,
        "MEMORY_LIMIT_CHARS": 10000,
        "MEMORY_KEEP_COUNT": 100
    }

    # ===== 修复:打卡数据存储(移除 lambda,改用普通字典)=====
    self.checkin_data = {}

    # ===== 新增:自定义序列化/反序列化方法(跳过 Lock)=====
    def __getstate__(self):
        state = self.__dict__.copy()
        del state['lock']
        return state

    def __setstate__(self, state):
        self.__dict__.update(state)
        self.lock = asyncio.Lock()

    # ========== 修复:打卡功能方法 ==========
    def checkin(self, user_id, user_name, content):
        if user_id not in self.checkin_data:
            self.checkin_data[user_id] = {"name": "", "total_stars": 0, "records": {}}
        user_data = self.checkin_data[user_id]
        user_data["name"] = user_name
        if content not in user_data["records"]:
            user_data["records"][content] = 0
        user_data["records"][content] += 1
        user_data["total_stars"] += 1
        save_data()

    def delete_checkin(self, user_id, content):
        if user_id not in self.checkin_data:
            return False, "你还没有任何打卡记录哦~"
        user_data = self.checkin_data[user_id]
        if content not in user_data["records"] or user_data["records"][content] == 0:
            return False, f"你没有「{content}」的打卡记录~"
        user_data["records"][content] -= 1
        user_data["total_stars"] -= 1
        if user_data["records"][content] == 0:
            del user_data["records"][content]
        if not user_data["records"]:
            del self.checkin_data[user_id]
        save_data()
        return True, f"已删除「{content}」的 1 次打卡记录~"

    def admin_checkin(self, target_user_id, target_user_name, content, count, operation):
        if target_user_id not in self.checkin_data:
            self.checkin_data[target_user_id] = {"name": target_user_name, "total_stars": 0, "records": {}}
        user_data = self.checkin_data[target_user_id]
        user_data["name"] = target_user_name
        if content not in user_data["records"]:
            user_data["records"][content] = 0
        if operation == "add":
            user_data["records"][content] += count
            user_data["total_stars"] += count
            save_data()
            return True, f"已为用户【{target_user_name}】增加「{content}」打卡记录 {count} 次~"
        elif operation == "delete":
            if user_data["records"][content] < count:
                return False, f"用户【{target_user_name}】的「{content}」打卡记录不足 {count} 次!"
            user_data["records"][content] -= count
            user_data["total_stars"] -= 1
            if user_data["records"][content] == 0:
                del user_data["records"][content]
            if not user_data["records"]:
                del self.checkin_data[target_user_id]
            save_data()
            return True, f"已为用户【{target_user_name}】删除「{content}」打卡记录 {count} 次~"
        else:
            return False, "操作类型错误!仅支持「增加」或「删除」"

    def calculate_medals(self, stars):
        crown = stars // (4*4*4)
        stars %= (4*4*4)
        sun = stars // (4*4)
        stars %= (4*4)
        moon = stars // 4
        star = stars % 4
        medals = ""
        if crown > 0: medals += "👑" * crown
        if sun > 0: medals += "☀️" * sun
        if moon > 0: medals += "🌙" * moon
        if star > 0 or (crown+sun+moon+star == 0): medals += "⭐" * star
        return medals

    def generate_checkin_ranking(self):
        if not self.checkin_data:
            return "当前还没有打卡记录哦~快叫群友一起打卡吧!"
        sorted_users = sorted(self.checkin_data.values(), key=lambda x: x["total_stars"], reverse=True)
        ranking = "🏆 打卡记录排行榜 🏆\n"
        for idx, user in enumerate(sorted_users, 1):
            total_medals = self.calculate_medals(user["total_stars"])
            ranking += f"{idx}. {user['name']}{total_medals}\n"
            for content, count in user["records"].items():
                content_medals = self.calculate_medals(count)
                ranking += f" · {content}{content_medals}\n"
        return ranking

    def add_message(self, name, msg, time_str):
        entry = f"[{time_str}] 【{name}】: {msg}"
        self.buffer.append(entry)
        clean_msg = msg.strip()
        if clean_msg == self.last_msg_content and clean_msg:
            self.repeat_count += 1
        else:
            self.last_msg_content = clean_msg
            self.repeat_count = 1

    def estimate_length(self):
        return sum(len(m) for m in self.buffer)

    async def check_and_compress(self):
        async with self.lock:
            if self.estimate_length() > self.bot_config["MEMORY_LIMIT_CHARS"] and len(self.buffer) > self.bot_config["MEMORY_KEEP_COUNT"]:
                to_compress = self.buffer[:-self.bot_config["MEMORY_KEEP_COUNT"]]
                text_block = "\n".join(to_compress)
                prompt = f""" 你是记忆整理员,更新长期记忆摘要(800 字内):
 【长期记忆】:{self.summary}
 【待归档对话】:{text_block}"""
                try:
                    loop = asyncio.get_running_loop()
                    resp = await loop.run_in_executor(None, lambda: chat_model.generate_content(prompt))
                    if resp.candidates[0].content.parts[0].text:
                        self.summary = resp.candidates[0].content.parts[0].text
                        self.buffer = self.buffer[-self.bot_config["MEMORY_KEEP_COUNT"]:]
                        save_data()
                except Exception as e:
                    print(f"压缩记忆失败:{e}")

    async def generate_reply(self, current_question, sender_name, is_active_interrupt=False):
        tz = pytz.timezone('Asia/Shanghai')
        context_str = "\n".join(self.buffer)
        if is_active_interrupt:
            task_prompt = "没人艾特你,自然加入讨论,简短、像群友,可复读/玩梗,直接说话不加称呼,绝对不要输出任何时间戳/日期/时间格式,不要添加自己的名字前缀(如【Gemini】)。"
        else:
            if not current_question.strip():
                task_prompt = f"用户【{sender_name}】艾特了你,自然接话,绝对不要输出任何时间戳/日期/时间格式,不要添加自己的名字前缀(如【Gemini】)。"
            else:
                task_prompt = f"用户【{sender_name}】提问:{current_question},请回答,绝对不要输出任何时间戳/日期/时间格式,不要添加自己的名字前缀(如【Gemini】)。"
        final_prompt = f""" 你是 QQ 群里的 AI 伙伴 Gemini。
 【长期记忆】:{self.summary}
 【近期对话】:{context_str}
 【任务】:{task_prompt}
 【规则】:
 1. 问时间时只说当前北京时间(比如'现在是下午 3 点 20 分'),但不要输出任何括号/时间戳/数字格式的时间;
 2. 回复时禁止添加自己的名字或任何标识前缀(如【Gemini】),直接输出回复内容。 """
        try:
            loop = asyncio.get_running_loop()
            resp = await loop.run_in_executor(None, lambda: chat_model.generate_content(final_prompt))
            return resp.candidates[0].content.parts[0].text if resp.candidates[0].content.parts[0].text else "(暂时无法回复)"
        except Exception as e:
            return f"(回复失败:{str(e)})"

    async def check_active_intervention(self, current_msg):
        import time
        now = time.time()
        if now - self.last_active_time < self.bot_config["COOLDOWN_SECONDS"]:
            return False, None
        if (self.repeat_count >= self.bot_config["REPEATER_THRESHOLD"] and random.random() < self.bot_config["REPEATER_EXEC_PROB"] and current_msg.strip()):
            try:
                loop = asyncio.get_running_loop()
                check_resp = await loop.run_in_executor(None, lambda: chat_model.generate_content(f"判断'{current_msg}'是否是正常梗(非广告/脏话):是回 YES,否回 NO"))
                if "YES" in check_resp.candidates[0].content.parts[0].text.strip().upper():
                    self.last_active_time = now
                    return True, current_msg
            except:
                pass
        return False, None

    if "gemini" not in current_msg.lower() and random.random() > self.bot_config["TRIGGER_PROBABILITY"]:
        return False, None
    try:
        loop = asyncio.get_running_loop()
        decision_resp = await loop.run_in_executor(None, lambda: chat_model.generate_content(f""" 最近消息:{chr(10).join(self.buffer[-15:])}
 当前消息:{current_msg}
 没被艾特,是否有必要说话?满足(客观问题无人答/讨论你/有趣的氛围/有趣的话题)回 YES,否则 NO """))
        if "YES" in decision_resp.candidates[0].content.parts[0].text.strip().upper():
            reply = await self.generate_reply("", "群友", is_active_interrupt=True)
            self.last_active_time = now
            return True, reply
    except Exception as e:
        print(f"主动介入失败:{e}")
    return False, None

memories = {}
def save_data():
    try:
        with open(DATA_FILE, 'wb') as f:
            pickle.dump(memories, f)
    except Exception as e:
        print(f"保存记忆失败:{e}")

def load_data():
    global memories
    if os.path.exists(DATA_FILE):
        try:
            with open(DATA_FILE, 'rb') as f:
                memories = pickle.load(f)
        except:
            memories = {}

def get_memory(group_id):
    if group_id not in memories:
        memories[group_id] = GroupMemory(group_id)
    return memories[group_id]

load_data()

目录

  1. Gemini QQ 机器人部署指南
  2. 第一阶段:基础设施准备
  3. 更新软件源
  4. 安装 Docker
  5. 安装 Python3 及虚拟环境工具
  6. 创建项目文件夹
  7. 第二阶段:部署 QQ 协议端 (NapCat)
  8. 第三阶段:部署核心代码
  9. 确保在项目目录
  10. 创建虚拟环境
  11. 激活环境
  12. 安装依赖 (锁定版本)
  13. ================= 核心配置区 =================
  14. ============================================
  15. 旧版兼容写法(0.3.2 支持)
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 动态规划专题:回文串问题与区间 DP 实战
  • Meta Llama 系列深度解析:开源大模型事实标准与架构演进
  • C++位图与布隆过滤器实现及应用
  • Vitis AI 模型 FPGA 部署实战指南
  • llama.cpp 量化大模型内网部署与运行实战
  • Flutter 三方库 langchain_google 的鸿蒙化适配指南
  • 5 款实用的 Nano Banana 提示词资源网站及生成技巧
  • C++ 二叉搜索树:概念、性能分析与核心实现
  • Python 布尔类型
  • Selenium webdriver_manager 浏览器驱动管理指南
  • webdriver_manager 指南:解决 Selenium 浏览器驱动管理难题
  • macOS 安装微软雅黑字体
  • 大模型微调实战:ChatGLM-6B 微调经验与开源项目汇总
  • Bodymovin 开源动画转换工具跨平台集成方案
  • WebMCP:浏览器原生 AI 交互新范式
  • 无人机避障:Mid360+Fast-lio 感知建图与 Ego-planner 运动规划
  • Python 网络爬虫入门与实战指南
  • 2025 无人机四大顶会 16 篇精选论文解读
  • Spring Boot RESTful API 开发与测试实战
  • 实测 ToClaw 信息检索与分析能力:从微博热搜到深度报告

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online