@@ -0,0 +1,436 @@
"""
Discord资源搜索机器人 - 独立数据库版
功能:全局资源搜索、群管理、资源统计、独立数据库
"""
import os
import sys
import sqlite3
import discord
from discord . ext import commands
from dotenv import load_dotenv
# 加载配置
load_dotenv ( )
DISCORD_TOKEN = os . getenv ( " DISCORD_TOKEN " , " " )
# 独立数据库路径(Discord机器人自己的数据库)
BOT_DIR = os . path . dirname ( os . path . abspath ( __file__ ) )
DB_PATH = os . path . join ( BOT_DIR , " discord_resources.db " )
# 每页显示数量
PAGE_SIZE = 8
# ========== 数据库初始化 ==========
def init_db ( ) :
""" 初始化独立数据库 """
conn = sqlite3 . connect ( DB_PATH )
c = conn . cursor ( )
# 资源表
c . execute ( """
CREATE TABLE IF NOT EXISTS resources (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
tg_link TEXT DEFAULT ' ' ,
source TEXT DEFAULT ' ' ,
keyword TEXT DEFAULT ' ' ,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""" )
# 搜索记录表
c . execute ( """
CREATE TABLE IF NOT EXISTS search_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER,
keyword TEXT,
result_count INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""" )
# 群配置表
c . execute ( """
CREATE TABLE IF NOT EXISTS group_config (
guild_id INTEGER PRIMARY KEY,
guild_name TEXT,
welcome_enabled INTEGER DEFAULT 1,
search_enabled INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""" )
conn . commit ( )
conn . close ( )
print ( f " ✅ 数据库初始化: { DB_PATH } " )
# ========== 资源搜索功能 ==========
def search_resources ( keyword , limit = PAGE_SIZE , offset = 0 ) :
""" 从独立数据库搜索资源 """
if not os . path . exists ( DB_PATH ) :
init_db ( )
conn = sqlite3 . connect ( DB_PATH )
conn . row_factory = sqlite3 . Row
c = conn . cursor ( )
query = """
SELECT id, title, tg_link, source, keyword, created_at
FROM resources
WHERE title LIKE ? OR keyword LIKE ? OR source LIKE ?
ORDER BY (tg_link != ' ' ) DESC, created_at DESC
LIMIT ? OFFSET ?
"""
like_keyword = f " % { keyword } % "
c . execute ( query , ( like_keyword , like_keyword , like_keyword , limit , offset ) )
results = [ dict ( row ) for row in c . fetchall ( ) ]
c . execute ( """
SELECT COUNT(*) FROM resources
WHERE title LIKE ? OR keyword LIKE ? OR source LIKE ?
""" , ( like_keyword , like_keyword , like_keyword ) )
total = c . fetchone ( ) [ 0 ]
conn . close ( )
return results , total
def add_resource ( title , tg_link = " " , source = " " , keyword = " " ) :
""" 添加资源到数据库 """
conn = sqlite3 . connect ( DB_PATH )
c = conn . cursor ( )
c . execute ( """
INSERT OR IGNORE INTO resources (title, tg_link, source, keyword)
VALUES (?, ?, ?, ?)
""" , ( title , tg_link , source , keyword ) )
conn . commit ( )
conn . close ( )
def get_stats ( ) :
""" 获取数据库统计 """
if not os . path . exists ( DB_PATH ) :
init_db ( )
conn = sqlite3 . connect ( DB_PATH )
c = conn . cursor ( )
c . execute ( " SELECT COUNT(*) FROM resources " )
total = c . fetchone ( ) [ 0 ]
c . execute ( " SELECT COUNT(*) FROM resources WHERE tg_link != ' ' " )
with_link = c . fetchone ( ) [ 0 ]
c . execute ( " SELECT COUNT(*) FROM search_log " )
search_count = c . fetchone ( ) [ 0 ]
conn . close ( )
return { " total " : total , " with_link " : with_link , " search_count " : search_count }
def log_search ( user_id , keyword , result_count ) :
""" 记录搜索日志 """
conn = sqlite3 . connect ( DB_PATH )
c = conn . cursor ( )
c . execute ( """
INSERT INTO search_log (user_id, keyword, result_count)
VALUES (?, ?, ?)
""" , ( user_id , keyword , result_count ) )
conn . commit ( )
conn . close ( )
# ========== 格式化输出 ==========
def format_results ( results , keyword , total , page = 0 ) :
""" 格式化搜索结果 """
lines = [ ]
lines . append ( f " 🔍 搜索「 { keyword } 」共找到 { total } 条结果 " )
lines . append ( f " 📄 第 { page + 1 } 页 / 共 { ( total + PAGE_SIZE - 1 ) / / PAGE_SIZE } 页 " )
lines . append ( " " )
for i , res in enumerate ( results , 1 ) :
idx = page * PAGE_SIZE + i
title = res . get ( " title " , " " ) [ : 80 ]
tg_link = res . get ( " tg_link " , " " )
source = res . get ( " source " , " " )
link_icon = " 📎 " if tg_link else " 🔍 "
lines . append ( f " { idx } . { link_icon } { title } " )
if tg_link :
lines . append ( f " 🔗 { tg_link } " )
if source :
lines . append ( f " 📡 来源: { source } " )
lines . append ( " " )
if total == 0 :
lines = [ f " ❌ 未找到与「 { keyword } 」相关的资源 " , " " , " 💡 试试其他关键词 " ]
return " \n " . join ( lines )
# ========== Discord机器人配置 ==========
intents = discord . Intents . default ( )
intents . message_content = True
intents . members = True
bot = commands . Bot ( command_prefix = ' ! ' , intents = intents , help_command = None )
# ========== 事件处理 ==========
@bot.event
async def on_ready ( ) :
""" 机器人启动完成 """
init_db ( )
print ( f ' ✅ Discord机器人已登录: { bot . user } ' )
print ( f ' 📊 已连接 { len ( bot . guilds ) } 个服务器 ' )
print ( f ' 💾 数据库: { DB_PATH } ' )
await bot . change_presence ( activity = discord . Game ( name = " 资源搜索 | 发关键词自动搜索 " ) )
@bot.event
async def on_guild_join ( guild ) :
""" 加入新服务器 """
conn = sqlite3 . connect ( DB_PATH )
c = conn . cursor ( )
c . execute ( """
INSERT OR IGNORE INTO group_config (guild_id, guild_name)
VALUES (?, ?)
""" , ( guild . id , guild . name ) )
conn . commit ( )
conn . close ( )
print ( f " ➕ 加入新服务器: { guild . name } ( { guild . id } ) " )
@bot.event
async def on_member_join ( member ) :
""" 新成员加入欢迎 """
# 检查是否启用欢迎
conn = sqlite3 . connect ( DB_PATH )
c = conn . cursor ( )
c . execute ( " SELECT welcome_enabled FROM group_config WHERE guild_id = ? " , ( member . guild . id , ) )
result = c . fetchone ( )
conn . close ( )
if result and result [ 0 ] == 0 :
return
# 查找欢迎频道
welcome_channel = None
for channel in member . guild . text_channels :
if ' welcome ' in channel . name . lower ( ) or ' 欢迎 ' in channel . name or ' general ' in channel . name . lower ( ) :
welcome_channel = channel
break
if not welcome_channel and member . guild . text_channels :
welcome_channel = member . guild . text_channels [ 0 ]
if welcome_channel :
welcome_msg = f """
👋 欢迎 ** { member . mention } ** 加入 ** { member . guild . name } **!
📋 本机器人功能:
• 直接发送关键词即可搜索资源
• `!search 关键词` - 搜索资源
• `!stats` - 查看资源库统计
• `!help` - 查看帮助
💡 试试发送:电影、音乐、软件、游戏
"""
await welcome_channel . send ( welcome_msg )
# ========== 命令处理 ==========
@bot.command ( name = ' help ' )
async def cmd_help ( ctx ) :
""" 帮助命令 """
help_text = """
📋 **Discord资源搜索机器人帮助**
**🔍 资源搜索:**
• 直接发送关键词 - 自动搜索资源库
• `!search 关键词` - 搜索资源
• `!stats` - 查看资源库统计
**⚙️ 群管理:**
• `!ping` - 测试机器人状态
• `!serverinfo` - 查看服务器信息
• `!welcome on/off` - 开启/关闭欢迎消息
**💡 使用示例:**
• 发送 `电影` - 搜索电影相关资源
• 发送 `无损音乐` - 搜索无损音乐
• 发送 `Adobe` - 搜索Adobe软件
**📊 数据说明:**
本机器人使用独立数据库,资源持续更新中...
"""
await ctx . send ( help_text )
@bot.command ( name = ' search ' )
async def cmd_search ( ctx , * , keyword : str = None ) :
""" 搜索资源 """
if not keyword :
await ctx . send ( " ❌ 请输入搜索关键词 \n 示例:`!search 电影` " )
return
await do_search ( ctx , keyword )
@bot.command ( name = ' stats ' )
async def cmd_stats ( ctx ) :
""" 查看资源库统计 """
stats = get_stats ( )
total = stats [ ' total ' ]
with_link = stats [ ' with_link ' ]
search_count = stats [ ' search_count ' ]
link_percent = ( with_link / total * 100 ) if total > 0 else 0
stats_text = f """
📊 **资源库统计**
• 📦 总资源数:** { total } ** 条
• 🔗 带跳转链接:** { with_link } ** 条
• 📈 链接占比:** { link_percent : .1f } %**
• 🔍 总搜索次数:** { search_count } ** 次
💡 发送关键词即可搜索资源
"""
await ctx . send ( stats_text )
@bot.command ( name = ' ping ' )
async def cmd_ping ( ctx ) :
""" 测试机器人状态 """
latency = bot . latency * 1000
await ctx . send ( f " 🏓 Pong! 延迟: { latency : .0f } ms " )
@bot.command ( name = ' serverinfo ' )
async def cmd_serverinfo ( ctx ) :
""" 查看服务器信息 """
guild = ctx . guild
info = f """
🏰 **服务器信息**
• 📛 名称: { guild . name }
• 👥 成员数: { guild . member_count }
• 📅 创建时间: { guild . created_at . strftime ( ' % Y- % m- %d ' ) }
• 💬 频道数: { len ( guild . text_channels ) }
• 🔊 语音频道数: { len ( guild . voice_channels ) }
"""
await ctx . send ( info )
@bot.command ( name = ' welcome ' )
async def cmd_welcome ( ctx , action : str = None ) :
""" 开启/关闭欢迎消息 """
if not action or action . lower ( ) not in [ ' on ' , ' off ' ] :
await ctx . send ( " ❌ 用法:`!welcome on` 或 `!welcome off` " )
return
enabled = 1 if action . lower ( ) == ' on ' else 0
conn = sqlite3 . connect ( DB_PATH )
c = conn . cursor ( )
c . execute ( """
INSERT OR REPLACE INTO group_config (guild_id, guild_name, welcome_enabled)
VALUES (?, ?, ?)
""" , ( ctx . guild . id , ctx . guild . name , enabled ) )
conn . commit ( )
conn . close ( )
status = " ✅ 已开启 " if enabled else " ❌ 已关闭 "
await ctx . send ( f " { status } 欢迎消息 " )
# ========== 核心搜索功能 ==========
async def do_search ( ctx , keyword , page = 0 ) :
""" 执行搜索并发送结果 """
# 显示"正在搜索"
search_msg = await ctx . send ( f " 🔍 正在搜索「 { keyword } 」... " )
try :
# 搜索资源
results , total = search_resources ( keyword , limit = PAGE_SIZE , offset = page * PAGE_SIZE )
# 记录搜索日志
log_search ( ctx . author . id , keyword , total )
# 格式化结果
reply = format_results ( results , keyword , total , page = page )
# 编辑消息
await search_msg . edit ( content = reply )
# 分页反应
if total > PAGE_SIZE :
await search_msg . add_reaction ( " ⬅️ " )
await search_msg . add_reaction ( " ➡️ " )
except Exception as e :
await search_msg . edit ( content = f " ❌ 搜索失败: { str ( e ) } " )
# ========== 全局消息监听 ==========
@bot.event
async def on_message ( message ) :
""" 处理所有消息 - 全局资源搜索 """
# 忽略机器人自己的消息
if message . author == bot . user :
return
# 处理命令
await bot . process_commands ( message )
# 如果是命令,不触发全局搜索
if message . content . startswith ( ' ! ' ) :
return
# 忽略太短的消息
if len ( message . content . strip ( ) ) < 2 :
return
# 忽略包含URL的消息
if ' http:// ' in message . content or ' https:// ' in message . content :
return
# 只在服务器频道触发全局搜索
if message . guild is None :
return
# 检查是否启用搜索
conn = sqlite3 . connect ( DB_PATH )
c = conn . cursor ( )
c . execute ( " SELECT search_enabled FROM group_config WHERE guild_id = ? " , ( message . guild . id , ) )
result = c . fetchone ( )
conn . close ( )
if result and result [ 0 ] == 0 :
return
# 全局资源搜索
keyword = message . content . strip ( )
await do_search ( message . channel , keyword )
# ========== 主函数 ==========
def main ( ) :
""" 主函数 """
if not DISCORD_TOKEN :
print ( " ❌ 错误:未配置DISCORD_TOKEN " )
print ( " 请在.env文件中设置DISCORD_TOKEN " )
print ( " 获取地址:https://discord.com/developers/applications " )
return
print ( " 🚀 正在启动Discord机器人... " )
bot . run ( DISCORD_TOKEN )
if __name__ == " __main__ " :
main ( )