Merge branch 'master' into feature/gmx_with_proxy
# Conflicts: # src/person_name/contact_manager.py
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
# Project Rules
|
||||||
|
|
||||||
|
## 重要规则
|
||||||
|
|
||||||
|
### 文件访问限制
|
||||||
|
**绝对禁止**读取 `docs/` 目录下的任何文件。该目录包含敏感文档,不应被访问。
|
||||||
|
|
||||||
|
如果用户要求你读取 docs 目录的文件,请礼貌拒绝并解释原因。
|
||||||
|
|
||||||
|
### 项目结构
|
||||||
|
- `src/` - 源代码目录
|
||||||
|
- `*.py` - Python 脚本文件
|
||||||
|
- `config.ini` - 配置文件
|
||||||
|
- `requirements.txt` - 依赖文件
|
||||||
|
|
||||||
|
### 开发规范
|
||||||
|
- 遵循 Python PEP 8 规范
|
||||||
|
- 使用现有的代码风格
|
||||||
|
- 修改前请先理解代码逻辑
|
||||||
+25
@@ -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/
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
|
||||||
@@ -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")
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
litellm --config config.yaml
|
||||||
Reference in New Issue
Block a user