Merge branch 'master' into feature/gmx_with_proxy

# Conflicts:
#	src/person_name/contact_manager.py
This commit is contained in:
2026-04-15 22:54:44 +02:00
6 changed files with 262 additions and 8 deletions
+19
View File
@@ -0,0 +1,19 @@
# Project Rules
## 重要规则
### 文件访问限制
**绝对禁止**读取 `docs/` 目录下的任何文件。该目录包含敏感文档,不应被访问。
如果用户要求你读取 docs 目录的文件,请礼貌拒绝并解释原因。
### 项目结构
- `src/` - 源代码目录
- `*.py` - Python 脚本文件
- `config.ini` - 配置文件
- `requirements.txt` - 依赖文件
### 开发规范
- 遵循 Python PEP 8 规范
- 使用现有的代码风格
- 修改前请先理解代码逻辑
+25
View File
@@ -0,0 +1,25 @@
litellm_settings:
drop_params: true # This strips 'thinking', etc. before sending to Copilot
model_list:
# - model_name: github_copilot/gpt-4
# litellm_params:
# model: github_copilot/gpt-4
# - model_name: github_copilot/claude-sonnet-4.6
# litellm_params:
# model: github_copilot/claude-sonnet-4.6
# - model_name: github_copilot/gpt-5.1-codex
# model_info:
# mode: responses
# litellm_params:
# model: github_copilot/gpt-5.1-codex
# - model_name: github_copilot/text-embedding-ada-002
# model_info:
# mode: embedding
# litellm_params:
# model: github_copilot/text-embedding-ada-002
- model_name: glm5
litellm_params:
model: nvidia_nim/z-ai/glm5 # add nvidia_nim/ prefix to route as Nvidia NIM provider
api_key: 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
api_base: "" # [OPTIONAL] - default is https://integrate.api.nvidia.com/v1/
+61
View File
@@ -0,0 +1,61 @@
{
"$schema": "https://opencode.ai/config.json",
"watcher": {
"ignore": [
"venv/**",
".git/**",
".idea/**",
"__pycache__/**",
"pojo/__pycache__/**",
"dist/**",
"build/**",
"out/**",
"lib/**",
"*.log",
"appointment_*.log",
"utils/*.log",
".DS_Store",
".~contact.xlsx",
"docs/**"
]
},
"permission": {
"read": "allow",
"edit": "ask",
"bash": "ask"
},
"instructions": [
"AGENTS.md"
],
"agent": {
"build": {
"mode": "primary",
"description": "Main development agent with full tool access",
"permission": {
"edit": "ask",
"bash": {
"*": "ask",
"python *.py": "allow",
"pip install *": "ask",
"git status": "allow",
"git log*": "allow",
"git diff": "allow"
}
}
},
"plan": {
"mode": "primary",
"description": "Planning agent for analysis without making changes",
"permission": {
"edit": "deny",
"bash": {
"*": "deny",
"git status": "allow",
"git log*": "allow",
"git diff": "allow",
"grep *": "allow"
}
}
}
}
}
+79
View File
@@ -0,0 +1,79 @@
"""
脚本:从 DESTINATION_EMAIL_LIST 集合中批量删除指定邮箱地址
用法:
直接修改下方 EMAIL_LIST_TO_REMOVE 列表,然后运行脚本。
或在代码中调用 remove_emails_from_destination(email_list) 函数。
"""
import logging
from typing import List
from src.db.mongo_manager import MONGO_STORE_MANAGER
from src.pojo.mail.mail_pojo import MailAddress
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)
def remove_emails_from_destination(email_list: List[str]) -> None:
"""
从 DESTINATION_EMAIL_LIST 集合中删除给定的邮箱地址列表。
Args:
email_list (List[str]): 需要删除的邮箱地址字符串列表
"""
if not email_list:
logger.warning("传入的邮箱列表为空,无需删除。")
return
success_count = 0
fail_count = 0
for email in email_list:
email = email.strip()
if not email:
continue
try:
# remove_email_from_destination_email_list 需要一个 MailAddress 对象
# password 字段对删除操作无影响,传空字符串即可
mail_address = MailAddress(mail=email, password="")
MONGO_STORE_MANAGER.remove_email_from_destination_email_list(mail_address)
logger.info(f"已删除邮箱: {email}")
success_count += 1
except Exception as e:
logger.error(f"删除邮箱 {email} 时出错: {e}")
fail_count += 1
logger.info(f"删除完成 — 成功: {success_count},失败: {fail_count},共处理: {success_count + fail_count}")
# ──────────────────────────────────────────────
# 直接运行时,修改下方列表即可批量删除
# ──────────────────────────────────────────────
EMAIL_LIST_TO_REMOVE: List[str] = [
"susannekaar@gmx.net",
"dianataya@gmx.net",
"sophiezhoz@gmx.net",
"claudiavimu@gmx.net",
"leoniekeyk@gmx.net",
"katjamoem@gmx.net",
"annechoa@gmx.net",
"manuelacoep@gmx.net",
"kathrinbeet@gmx.net",
"katjapoyu@gmx.net",
"klausciluwe@gmx.net",
"petraneak@gmx.net",
"leahpona@gmx.net",
"jenniferhoko@gmx.net",
"phillippkemikv@gmx.net",
"sandrasika@gmx.net",
"leoniekala@gmx.net",
"sabinekiav@gmx.net",
"marinabaes@gmx.net",
"ulrikegevo@gmx.net",
"claudiadare@gmx.net"
]
if __name__ == "__main__":
remove_emails_from_destination(EMAIL_LIST_TO_REMOVE)
+77 -8
View File
@@ -16,7 +16,7 @@ DEFAULT_SERIAL_TO_IGNORE = ["47e7e36b", "bitbrowser"]
def upload_contacts_list(): def upload_contacts_list():
_contacts_to_book = read_contacts(str(Path.home()) + "/Desktop/contact_list_2025-05-20.xlsx") _contacts_to_book = read_contacts(str(Path.home()) + "/Desktop/contact_list_2026-04-11_FIXED.xlsx")
return _contacts_to_book return _contacts_to_book
@@ -119,7 +119,7 @@ def write_new_contacts_to_excel(valid_contacts: list, file_name=str(datetime.dat
def generate_valid_contact_list_for_day(segment_number=1): def generate_valid_contact_list_for_day(segment_number=1):
_collection_name = "2026-03-28" _collection_name = "2026-04-11"
_valid_contact_list = MONGO_STORE_MANAGER.get_all_successful_items_for_one_day(_collection_name) _valid_contact_list = MONGO_STORE_MANAGER.get_all_successful_items_for_one_day(_collection_name)
_all_contacts = MONGO_STORE_MANAGER.get_all_contacts_to_book() _all_contacts = MONGO_STORE_MANAGER.get_all_contacts_to_book()
_contact_to_save = [] _contact_to_save = []
@@ -131,9 +131,11 @@ def generate_valid_contact_list_for_day(segment_number=1):
if _true_contact.mail == _contact.mail: if _true_contact.mail == _contact.mail:
_contact.last_name = _true_contact.last_name _contact.last_name = _true_contact.last_name
_contact.phone = _true_contact.phone _contact.phone = _true_contact.phone
_contact.passport = str(_true_contact.resident_card_number)[:9] _contact.passport = str(_true_contact.passport)[:9]
_contact.first_name = _true_contact.first_name _contact.first_name = _true_contact.first_name
_contact.resident_card_number = str(_true_contact.resident_card_number)[:9] _contact.resident_card_number = str(_true_contact.passport)[:9]
if _contact.mail == "angielovato14903@yahoo.com":
print("no resident card number for " + _contact.mail)
print("{}:{}".format(_true_contact.mail, _true_contact.source_from)) print("{}:{}".format(_true_contact.mail, _true_contact.source_from))
if isinstance(_true_contact.source_from, str) and _true_contact.source_from is not None and len(_true_contact.source_from) > 0: if isinstance(_true_contact.source_from, str) and _true_contact.source_from is not None and len(_true_contact.source_from) > 0:
print(_true_contact.source_from) print(_true_contact.source_from)
@@ -285,14 +287,80 @@ def write_resident_card_number_to_contact_list(file_to_read, file_name="contact_
write_list_with_segment_number(file_name, _contacts_to_book, 1) write_list_with_segment_number(file_name, _contacts_to_book, 1)
def check_resident_card_number(file_path):
"""读取 contact_list Excel 文件,检查 resident_card_number 是否为 9 位纯数字字符串。
若不是则输出该联系人信息,并调用 generate_single_titre_sejour_number() 生成新值进行修复。
最终将所有联系人(含修复结果)写入原文件名+_FIXED 的新文件,保持原有列格式。"""
_contact_list = read_contacts(file_path)
_has_invalid = False
for _contact in _contact_list:
rcn = str(_contact.resident_card_number) if _contact.resident_card_number is not None else ""
if not (len(rcn) == 9 and rcn.isdigit()):
print(_contact)
_contact.resident_card_number = generate_single_titre_sejour_number()
_has_invalid = True
if not _has_invalid:
print("[OK] Tous les resident_card_number sont valides (9 chiffres). Aucun fichier créé.")
return
# Construire le chemin du fichier de sortie : même dossier, nom + _FIXED + extension
p = Path(file_path)
output_file = str(p.parent / (p.stem + "_FIXED" + p.suffix))
# Écriture dans le même format que write_new_contacts_to_excel
row = 0
col = 0
workbook = xlsxwriter.Workbook(output_file, {'nan_inf_to_errors': True})
header_data = ['name', 'phone', 'passport', 'email', 'store', 'serial', 'ip_country', 'ua',
'resident_card_number', 'source_from']
worksheet = workbook.add_worksheet()
header_format = workbook.add_format({'bold': True})
for col_num, data in enumerate(header_data):
worksheet.write(row, col_num, data, header_format)
row = 1
def safe_write_val(row_num, col_num, value):
try:
if isinstance(value, str):
if value.lower() in ['nan', 'inf', '-inf']:
worksheet.write(row_num, col_num, "")
else:
worksheet.write(row_num, col_num, value)
elif isinstance(value, (int, float)):
if math.isnan(value) or math.isinf(value):
worksheet.write(row_num, col_num, "")
else:
worksheet.write(row_num, col_num, value)
else:
worksheet.write(row_num, col_num, value if value is not None else "")
except (TypeError, ValueError):
worksheet.write(row_num, col_num, "")
for info in _contact_list:
worksheet.write(row, col, "{} {}".format(info.last_name, info.first_name))
worksheet.write(row, col + 1, info.phone)
worksheet.write(row, col + 2, info.passport)
worksheet.write(row, col + 3, info.mail)
worksheet.write(row, col + 4, info.store)
worksheet.write(row, col + 5, info.serial)
worksheet.write(row, col + 6, info.ip_country)
safe_write_val(row, col + 7, info.ua)
worksheet.write(row, col + 8, info.resident_card_number)
worksheet.write(row, col + 9, info.source_from)
row += 1
workbook.close()
print("Fichier corrigé écrit dans : " + output_file)
if __name__ == '__main__': if __name__ == '__main__':
# write_resident_card_number_to_contact_list(file_to_read=str(Path.home()) + "/Desktop/contact_list_all_13.xlsx", # write_resident_card_number_to_contact_list(file_to_read=str(Path.home()) + "/Desktop/contact_list_all_13.xlsx",
# file_name="contact_list_all_13") # file_name="contact_list_all_13")
# contacts_to_book = upload_contacts_list() contacts_to_book = upload_contacts_list()
# MONGO_STORE_MANAGER.upload_contact_list(contacts_to_book) MONGO_STORE_MANAGER.upload_contact_list(contacts_to_book)
# print("start at {}".format(datetime.datetime.now())) # print("start at {}".format(datetime.datetime.now()))
generate_valid_contact_list_for_day(segment_number=2) # generate_valid_contact_list_for_day(segment_number=2)
# generate_contact_from_mail_list("/Users/lpan/Downloads/邮箱及密码_23_03_25_yahoo.xlsx") # generate_contact_from_mail_list("/Users/panlei/Downloads/100_yahoo_11_04.xlsx")
# print("end at {}".format(datetime.datetime.now())) # print("end at {}".format(datetime.datetime.now()))
# update_contact_list_not_received_mail() # update_contact_list_not_received_mail()
# get_old_validated_contact_list() # get_old_validated_contact_list()
@@ -301,4 +369,5 @@ if __name__ == '__main__':
# merge_contact_list_files( # merge_contact_list_files(
# "/Users/lpan/Desktop/contact_list_2024-11-06.xlsx" # "/Users/lpan/Desktop/contact_list_2024-11-06.xlsx"
# ]) # ])
# check_resident_card_number(str(Path.home()) + "/Desktop/contact_list_2026-04-11_FIXED.xlsx")
# fix_phone_number_format(str(Path.home()) + "/Desktop/gmx_ch_100_2024-06-13.xlsx") # fix_phone_number_format(str(Path.home()) + "/Desktop/gmx_ch_100_2024-06-13.xlsx")
+1
View File
@@ -0,0 +1 @@
litellm --config config.yaml