Files

134 lines
3.8 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.
"""
工具函数模块:语言检测、HTML清洗、内容提取、时间解析、AI API调用、文本换行
"""
import re
import requests
from datetime import datetime, timedelta, timezone
from email.utils import parsedate_to_datetime
def is_chinese(text):
"""判断文本是否为中文(汉字占比 > 20%"""
if not text:
return True
chinese_chars = sum(1 for c in text if '\u4e00' <= c <= '\u9fff')
return chinese_chars > len(text) * 0.2
def clean_html(html_text):
"""清理HTML标签,合并空白字符"""
if not html_text:
return ""
text = re.sub(r'<[^>]+>', ' ', html_text)
text = re.sub(r'\s+', ' ', text)
return text.strip()
def extract_content(entry):
"""从RSS条目提取完整正文内容"""
content = entry.get('summary', '') or entry.get('description', '')
if not content:
content_list = entry.get('content', [{}])
if content_list and isinstance(content_list, list):
content = content_list[0].get('value', '')
return clean_html(content)
def parse_pub_time(entry):
"""解析发布时间,统一转换为北京时间(UTC+8"""
beijing_tz = timezone(timedelta(hours=8))
time_fields = ['published_parsed', 'updated_parsed', 'created_parsed']
for field in time_fields:
if entry.get(field):
try:
utc_time = datetime(*entry[field][:6], tzinfo=timezone.utc)
return utc_time.astimezone(beijing_tz).replace(tzinfo=None)
except Exception:
continue
for field in ['published', 'updated', 'created']:
if entry.get(field):
try:
dt = parsedate_to_datetime(entry[field])
if dt.tzinfo is not None:
return dt.astimezone(beijing_tz).replace(tzinfo=None)
return dt
except Exception:
continue
return datetime.now()
def call_ai(messages, api_key, base_url, model, temperature=0.3, max_tokens=800, purpose=None):
"""调用 OpenAI 兼容的 Chat Completion API"""
if not api_key:
raise ValueError('OPENAI_API_KEY 未配置')
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {api_key}'
}
data = {
'model': model,
'messages': messages,
'temperature': temperature,
'max_tokens': max_tokens,
}
try:
response = requests.post(
f'{base_url}/chat/completions',
headers=headers,
json=data,
timeout=120
)
response.raise_for_status()
result = response.json()['choices'][0]['message']['content']
_record_api_success(model, purpose, max_tokens)
return result
except Exception:
_record_api_failure(model, purpose, max_tokens)
raise
def wrap_text(text, font, max_width, draw):
"""将长文本按像素宽度自动换行,返回行列表"""
lines = []
current_line = ""
for char in text:
test_line = current_line + char
bbox = draw.textbbox((0, 0), test_line, font=font)
line_width = bbox[2] - bbox[0]
if line_width <= max_width:
current_line = test_line
else:
if current_line:
lines.append(current_line)
current_line = char
if current_line:
lines.append(current_line)
return lines
def _record_api_success(model, purpose, max_tokens):
try:
from .monitor import get_monitor
get_monitor().record_api_call(model or "unknown", purpose or "unknown", max_tokens, True)
except Exception:
pass
def _record_api_failure(model, purpose, max_tokens):
try:
from .monitor import get_monitor
get_monitor().record_api_call(model or "unknown", purpose or "unknown", max_tokens, False)
except Exception:
pass