Files

437 lines
12 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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()