Initial upload for secondary development
This commit is contained in:
144
chatlog_fastAPI/routers/search.py
Normal file
144
chatlog_fastAPI/routers/search.py
Normal file
@@ -0,0 +1,144 @@
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from urllib.parse import quote
|
||||
from services.chatlog_client import MessageIndexNotReady, chatlog_client
|
||||
from services.message_formatter import extract_quote
|
||||
|
||||
router = APIRouter(prefix="/api/search", tags=["search"])
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def search(
|
||||
talker: str = Query(..., description="群/联系人 ID"),
|
||||
time: str = Query("", description="时间范围,如 2024-01-01,2024-01-31"),
|
||||
sender: str = Query("", description="发送者 ID,可选"),
|
||||
keyword: str = Query("", description="关键词,可选"),
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=500),
|
||||
):
|
||||
"""透传 chatlog /api/v1/chatlog,返回 {"total": N, "items": [...]}"""
|
||||
offset = (page - 1) * page_size
|
||||
try:
|
||||
data = await chatlog_client.get_messages(
|
||||
talker,
|
||||
time=time,
|
||||
sender=sender,
|
||||
keyword=keyword,
|
||||
limit=page_size,
|
||||
offset=offset,
|
||||
)
|
||||
except MessageIndexNotReady as e:
|
||||
raise HTTPException(status_code=503, detail=str(e)) from e
|
||||
for item in data.get("items", []) or []:
|
||||
contents = item.get("contents") or item.get("Contents") or {}
|
||||
if not isinstance(contents, dict):
|
||||
contents = {}
|
||||
try:
|
||||
is_file = int(item.get("type") or item.get("Type") or 0) == 49 and int(
|
||||
item.get("subType") or item.get("sub_type") or item.get("SubType") or 0
|
||||
) == 6
|
||||
except Exception:
|
||||
is_file = False
|
||||
file_md5 = str(contents.get("md5") or "") if is_file else ""
|
||||
item["is_file"] = is_file
|
||||
item["file_name"] = (
|
||||
contents.get("title") or contents.get("fileName") or contents.get("filename") or ""
|
||||
) if is_file else ""
|
||||
item["file_md5"] = file_md5
|
||||
item["quote"] = item.get("quote") or extract_quote(item)
|
||||
file_name = item["file_name"]
|
||||
item["file_url"] = f"/api/files/{quote(file_md5, safe='')}?filename={quote(file_name or file_md5, safe='')}" if file_md5 else ""
|
||||
return data
|
||||
|
||||
|
||||
@router.get("/chatrooms")
|
||||
async def chatrooms(
|
||||
keyword: str = Query("", description="关键词搜索"),
|
||||
limit: int = Query(100, ge=1, le=500),
|
||||
offset: int = Query(0, ge=0),
|
||||
):
|
||||
"""获取所有可用的微信群聊列表"""
|
||||
fetch_limit = min(2000, offset + limit)
|
||||
rooms_data = await chatlog_client.get_chatrooms(keyword=keyword, limit=fetch_limit, offset=0)
|
||||
if isinstance(rooms_data, list):
|
||||
room_items = rooms_data
|
||||
total = len(room_items)
|
||||
else:
|
||||
room_items = rooms_data.get("items") or rooms_data.get("data") or []
|
||||
total = rooms_data.get("total", len(room_items))
|
||||
|
||||
merged = []
|
||||
seen = set()
|
||||
|
||||
def get_room_id(item: dict) -> str:
|
||||
return str(item.get("name") or item.get("Name") or item.get("userName") or item.get("UserName") or "")
|
||||
|
||||
def add_room(item: dict):
|
||||
room_id = get_room_id(item)
|
||||
if not room_id or not room_id.endswith("@chatroom") or room_id in seen:
|
||||
return
|
||||
seen.add(room_id)
|
||||
merged.append(item)
|
||||
|
||||
for item in room_items:
|
||||
if isinstance(item, dict):
|
||||
add_room(item)
|
||||
|
||||
# Freshly imported phone records may exist in sessions/messages before
|
||||
# chatroom metadata is populated. Merge @chatroom sessions as fallback.
|
||||
try:
|
||||
session_items = await chatlog_client.get_sessions(keyword="", limit=2000)
|
||||
except Exception:
|
||||
session_items = []
|
||||
|
||||
lowered_keyword = (keyword or "").lower()
|
||||
for session in session_items:
|
||||
if not isinstance(session, dict):
|
||||
continue
|
||||
user_name = str(session.get("userName") or session.get("UserName") or "")
|
||||
if not user_name.endswith("@chatroom"):
|
||||
continue
|
||||
nick_name = session.get("nickName") or session.get("NickName") or ""
|
||||
remark = session.get("remark") or session.get("Remark") or ""
|
||||
if lowered_keyword:
|
||||
haystack = f"{user_name} {nick_name} {remark}".lower()
|
||||
if lowered_keyword not in haystack:
|
||||
continue
|
||||
add_room({
|
||||
"name": user_name,
|
||||
"nickName": nick_name,
|
||||
"remark": remark,
|
||||
"source": "session",
|
||||
})
|
||||
|
||||
return {"total": max(total, len(merged)), "items": merged[offset:offset + limit]}
|
||||
|
||||
@router.get("/avatar")
|
||||
async def avatar(wxid: str = Query(...)):
|
||||
url = await chatlog_client.get_avatar_url(wxid)
|
||||
return {"url": url}
|
||||
|
||||
|
||||
@router.get("/members")
|
||||
async def members(
|
||||
talker: str = Query(..., description="群 ID"),
|
||||
time: str = Query("", description="统计时间范围,可选"),
|
||||
):
|
||||
"""
|
||||
获取群成员列表(按发言量降序)
|
||||
返回 {"members": [...], "total": N}
|
||||
每个成员:userName, displayName, msgCount, lastSpeakTime
|
||||
"""
|
||||
return await chatlog_client.get_chatroom_members(talker, time=time)
|
||||
|
||||
|
||||
@router.get("/sessions")
|
||||
async def sessions(
|
||||
keyword: str = Query("", description="关键词搜索"),
|
||||
limit: int = Query(500, ge=1, le=2000),
|
||||
):
|
||||
"""
|
||||
获取所有会话列表,含最新一条消息预览和时间(来自微信原生 Session 表)。
|
||||
返回:[{ userName, nickName, remark, content, nTime, nOrder }]
|
||||
"""
|
||||
items = await chatlog_client.get_sessions(keyword=keyword, limit=limit)
|
||||
return items
|
||||
Reference in New Issue
Block a user