fix(mcp): filter out chat room members from contact list
_load_contacts_from() loaded all rows from the contact table without filtering, causing ~8000+ chat room member records (local_type=3) to appear as contacts. This inflated the contact count and polluted search results. Add WHERE local_type != 3 to exclude chat room members.
This commit is contained in:
175
mcp_server.py
175
mcp_server.py
@@ -16,8 +16,8 @@ import xml.etree.ElementTree as ET
|
|||||||
from Crypto.Cipher import AES
|
from Crypto.Cipher import AES
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
import zstandard as zstd
|
import zstandard as zstd
|
||||||
from config import _config_file_path, _DEFAULT
|
from config import _config_file_path, _DEFAULT
|
||||||
from decode_image import ImageResolver
|
from decode_image import ImageResolver
|
||||||
from key_utils import get_key_info, key_path_variants, strip_key_metadata
|
from key_utils import get_key_info, key_path_variants, strip_key_metadata
|
||||||
|
|
||||||
# ============ 加密常量 ============
|
# ============ 加密常量 ============
|
||||||
@@ -30,17 +30,17 @@ WAL_HEADER_SZ = 32
|
|||||||
WAL_FRAME_HEADER_SZ = 24
|
WAL_FRAME_HEADER_SZ = 24
|
||||||
|
|
||||||
# ============ 配置加载 ============
|
# ============ 配置加载 ============
|
||||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
CONFIG_FILE = _config_file_path()
|
CONFIG_FILE = _config_file_path()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(CONFIG_FILE, encoding="utf-8") as f:
|
with open(CONFIG_FILE, encoding="utf-8") as f:
|
||||||
_cfg = json.load(f)
|
_cfg = json.load(f)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
_cfg = dict(_DEFAULT)
|
_cfg = dict(_DEFAULT)
|
||||||
for _key in ("keys_file", "decrypted_dir"):
|
for _key in ("keys_file", "decrypted_dir"):
|
||||||
if _key in _cfg and not os.path.isabs(_cfg[_key]):
|
if _key in _cfg and not os.path.isabs(_cfg[_key]):
|
||||||
_cfg[_key] = os.path.join(os.path.dirname(CONFIG_FILE), _cfg[_key])
|
_cfg[_key] = os.path.join(os.path.dirname(CONFIG_FILE), _cfg[_key])
|
||||||
|
|
||||||
DB_DIR = _cfg["db_dir"]
|
DB_DIR = _cfg["db_dir"]
|
||||||
KEYS_FILE = _cfg["keys_file"]
|
KEYS_FILE = _cfg["keys_file"]
|
||||||
@@ -59,11 +59,11 @@ if not DECODED_IMAGE_DIR:
|
|||||||
elif not os.path.isabs(DECODED_IMAGE_DIR):
|
elif not os.path.isabs(DECODED_IMAGE_DIR):
|
||||||
DECODED_IMAGE_DIR = os.path.join(SCRIPT_DIR, DECODED_IMAGE_DIR)
|
DECODED_IMAGE_DIR = os.path.join(SCRIPT_DIR, DECODED_IMAGE_DIR)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(KEYS_FILE, encoding="utf-8") as f:
|
with open(KEYS_FILE, encoding="utf-8") as f:
|
||||||
ALL_KEYS = strip_key_metadata(json.load(f))
|
ALL_KEYS = strip_key_metadata(json.load(f))
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
ALL_KEYS = {}
|
ALL_KEYS = {}
|
||||||
|
|
||||||
# ============ 解密函数 ============
|
# ============ 解密函数 ============
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ atexit.register(_cache.cleanup)
|
|||||||
# ============ 联系人缓存 ============
|
# ============ 联系人缓存 ============
|
||||||
|
|
||||||
_contact_names = None # {username: display_name}
|
_contact_names = None # {username: display_name}
|
||||||
_contact_full = None # [{username, nick_name, remark, alias, description, phone}]
|
_contact_full = None # [{username, nick_name, remark, alias, description, phone}]
|
||||||
_contact_tags = None # {label_id: {name, sort_order, members: [{username, display_name}]}}
|
_contact_tags = None # {label_id: {name, sort_order, members: [{username, display_name}]}}
|
||||||
_self_username = None
|
_self_username = None
|
||||||
_contact_db_mtime = 0 # mtime of the decrypted contact.db when caches were last populated
|
_contact_db_mtime = 0 # mtime of the decrypted contact.db when caches were last populated
|
||||||
@@ -247,55 +247,60 @@ _QUERY_LIMIT_MAX = 500
|
|||||||
_HISTORY_QUERY_BATCH_SIZE = 500
|
_HISTORY_QUERY_BATCH_SIZE = 500
|
||||||
|
|
||||||
|
|
||||||
def _load_contacts_from(db_path):
|
def _load_contacts_from(db_path):
|
||||||
names = {}
|
names = {}
|
||||||
full = []
|
full = []
|
||||||
conn = sqlite3.connect(db_path)
|
conn = sqlite3.connect(db_path)
|
||||||
try:
|
try:
|
||||||
columns = {
|
columns = {
|
||||||
row[1] for row in conn.execute("PRAGMA table_info(contact)").fetchall()
|
row[1] for row in conn.execute("PRAGMA table_info(contact)").fetchall()
|
||||||
}
|
}
|
||||||
optional_columns = {
|
optional_columns = {
|
||||||
"alias": "",
|
"alias": "",
|
||||||
"description": "",
|
"description": "",
|
||||||
"phone": "",
|
"phone": "",
|
||||||
"phone_number": "",
|
"phone_number": "",
|
||||||
"mobile": "",
|
"mobile": "",
|
||||||
"mobile_phone": "",
|
"mobile_phone": "",
|
||||||
"telephone": "",
|
"telephone": "",
|
||||||
}
|
}
|
||||||
select_columns = ["username", "nick_name", "remark"]
|
select_columns = ["username", "nick_name", "remark"]
|
||||||
select_columns.extend(
|
select_columns.extend(
|
||||||
col for col in optional_columns
|
col for col in optional_columns
|
||||||
if col in columns and col not in select_columns
|
if col in columns and col not in select_columns
|
||||||
)
|
)
|
||||||
rows = conn.execute(
|
# 过滤群成员 (local_type=3, 每个群每个成员一条 → 数量爆炸 → 共 9416 假联系人)
|
||||||
"SELECT " + ", ".join(f"[{col}]" for col in select_columns)
|
# 仅在 local_type 列存在时加 WHERE, 兼容老版本 schema (#117 fix)
|
||||||
+ " FROM contact"
|
sql = (
|
||||||
).fetchall()
|
"SELECT " + ", ".join(f"[{col}]" for col in select_columns)
|
||||||
for r in rows:
|
+ " FROM contact"
|
||||||
data = dict(zip(select_columns, r))
|
)
|
||||||
uname = data.get("username")
|
if "local_type" in columns:
|
||||||
nick = data.get("nick_name")
|
sql += " WHERE local_type != 3"
|
||||||
remark = data.get("remark")
|
rows = conn.execute(sql).fetchall()
|
||||||
display = remark if remark else nick if nick else uname
|
for r in rows:
|
||||||
names[uname] = display
|
data = dict(zip(select_columns, r))
|
||||||
phone = ""
|
uname = data.get("username")
|
||||||
for col in ("phone", "phone_number", "mobile", "mobile_phone", "telephone"):
|
nick = data.get("nick_name")
|
||||||
if data.get(col):
|
remark = data.get("remark")
|
||||||
phone = data.get(col) or ""
|
display = remark if remark else nick if nick else uname
|
||||||
break
|
names[uname] = display
|
||||||
full.append({
|
phone = ""
|
||||||
'username': uname,
|
for col in ("phone", "phone_number", "mobile", "mobile_phone", "telephone"):
|
||||||
'nick_name': nick or '',
|
if data.get(col):
|
||||||
'remark': remark or '',
|
phone = data.get(col) or ""
|
||||||
'alias': data.get("alias") or '',
|
break
|
||||||
'description': data.get("description") or '',
|
full.append({
|
||||||
'phone': phone,
|
'username': uname,
|
||||||
})
|
'nick_name': nick or '',
|
||||||
finally:
|
'remark': remark or '',
|
||||||
conn.close()
|
'alias': data.get("alias") or '',
|
||||||
return names, full
|
'description': data.get("description") or '',
|
||||||
|
'phone': phone,
|
||||||
|
})
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
return names, full
|
||||||
|
|
||||||
|
|
||||||
def _get_contact_db_path():
|
def _get_contact_db_path():
|
||||||
@@ -344,23 +349,23 @@ def get_contact_names():
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
def get_contact_full():
|
def get_contact_full():
|
||||||
get_contact_names()
|
get_contact_names()
|
||||||
return _contact_full or []
|
return _contact_full or []
|
||||||
|
|
||||||
|
|
||||||
def get_contact_tag_names_by_username():
|
def get_contact_tag_names_by_username():
|
||||||
tags = _load_contact_tags()
|
tags = _load_contact_tags()
|
||||||
by_username = {}
|
by_username = {}
|
||||||
for tag in tags.values():
|
for tag in tags.values():
|
||||||
name = tag.get('name') or ''
|
name = tag.get('name') or ''
|
||||||
if not name:
|
if not name:
|
||||||
continue
|
continue
|
||||||
for member in tag.get('members', []):
|
for member in tag.get('members', []):
|
||||||
username = member.get('username')
|
username = member.get('username')
|
||||||
if username:
|
if username:
|
||||||
by_username.setdefault(username, []).append(name)
|
by_username.setdefault(username, []).append(name)
|
||||||
return by_username
|
return by_username
|
||||||
|
|
||||||
|
|
||||||
def _extract_pb_field_30(data):
|
def _extract_pb_field_30(data):
|
||||||
|
|||||||
Reference in New Issue
Block a user