'commit'
This commit is contained in:
@@ -1,333 +0,0 @@
|
||||
# coding=utf-8
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import numpy as np
|
||||
|
||||
import cv2
|
||||
|
||||
# 添加项目根目录到 sys.path
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if project_root not in sys.path:
|
||||
sys.path.append(project_root)
|
||||
|
||||
from WeiXin import WxUtil
|
||||
from WeiXin.WxUtil import perform_input_action
|
||||
from Util.LlmUtil import get_llm_response
|
||||
from Util import Win32Patch
|
||||
|
||||
# 配置日志
|
||||
log_dir = WxUtil.LOG_DIR
|
||||
if not os.path.exists(log_dir):
|
||||
os.makedirs(log_dir)
|
||||
|
||||
log_file_path = os.path.join(log_dir, "T2_ChatMonitor.log")
|
||||
|
||||
# 设置 logger
|
||||
logger = logging.getLogger("T2_ChatMonitor")
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
if logger.hasHandlers():
|
||||
logger.handlers.clear()
|
||||
|
||||
file_handler = logging.FileHandler(log_file_path, encoding='utf-8', mode='w')
|
||||
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
stream_handler = logging.StreamHandler()
|
||||
stream_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
|
||||
logger.addHandler(stream_handler)
|
||||
|
||||
logger.propagate = False
|
||||
logger.info(f"🚀 日志文件路径: {os.path.abspath(log_file_path)}")
|
||||
|
||||
# 同时将 WxUtil 的日志也输出到同一个文件
|
||||
wx_logger = logging.getLogger("WxUtil")
|
||||
wx_logger.propagate = False # 防止日志向上传递导致重复 (因为 WxUtil 中调用了 basicConfig)
|
||||
if not any(isinstance(h, logging.FileHandler) and os.path.abspath(h.baseFilename) == os.path.abspath(log_file_path) for h in wx_logger.handlers):
|
||||
wx_logger.addHandler(file_handler)
|
||||
wx_logger.addHandler(stream_handler) # 确保 WxUtil 也输出到控制台
|
||||
|
||||
class ChatMonitorBot:
|
||||
"""
|
||||
大张老师自动巡课系统 (CV版)
|
||||
"""
|
||||
def __init__(self):
|
||||
self.device = None
|
||||
self.screenshot_path = os.path.join(WxUtil.OUTPUT_DIR, "T2_ChatMonitor_live_shot.jpg")
|
||||
self.debug_view_path = os.path.join(WxUtil.OUTPUT_DIR, "T2_ChatMonitor_debug_view.jpg")
|
||||
self.dialogue_log = []
|
||||
self.input_pos = None
|
||||
self.last_screen_hash = None
|
||||
self.last_processed_msg_hash = None
|
||||
# [User Requested] 移除持久化存储,只在内存中记录,重启即忘
|
||||
self.processed_hashes = set()
|
||||
self.check_interval = 3 # 检查频率 (秒)
|
||||
|
||||
self.persona = (
|
||||
"你是一名1999年毕业、拥有27年一线教学经验的小学高级女教师,名叫‘大张老师’。你目前在‘长春市少惠林作文素养培养中心’工作。"
|
||||
"你不仅是一位作文教学专家,更是一位心思细腻、能与家长共情的教育智者。"
|
||||
"你的回复风格应该是:温柔、知性、亲切,就像一位邻家大姐姐在聊天。"
|
||||
"【严格约束】:\n"
|
||||
"1. 绝对禁止发散!绝对禁止幻觉!\n"
|
||||
"2. 知道什么就说什么,不要乱讲话,不要自己编造内容!\n"
|
||||
"3. 仅针对家长明确表达的内容进行回复。\n"
|
||||
"4. 严禁使用列表格式。严禁使用‘首先、其次’等逻辑词。\n"
|
||||
"5. 回复必须简练,字数严格控制在 50 字以内!\n"
|
||||
"6. 对方问什么就答什么。例如问‘学校叫什么’,就只回答‘少惠林’,不要回复地址和电话!\n"
|
||||
"如果涉及到校区信息,必须且只能使用以下真实数据:\n"
|
||||
"- 单位/学校名称:长春市少惠林作文素养培养中心(简称:少惠林)\n"
|
||||
"- 地址:南环城路与临河街交汇,TOUCH12街3楼325号\n"
|
||||
"- 联系人:小张老师(电话:18686619970)\n"
|
||||
"- 每学期开学招收小学三年级至六年级,初中七年级的学生入学,其它年段不招生。\n"
|
||||
)
|
||||
|
||||
def _record_processed_hash(self, msg_hash):
|
||||
"""记录已处理的消息哈希 (仅内存)"""
|
||||
self.processed_hashes.add(msg_hash)
|
||||
# 仅保留最近 100 条记录,防止无限增长
|
||||
if len(self.processed_hashes) > 100:
|
||||
# 简单丢弃旧的(转列表切片再转回集合)
|
||||
temp = list(self.processed_hashes)[-100:]
|
||||
self.processed_hashes = set(temp)
|
||||
|
||||
async def get_reply(self, last_message_text, context_text=""):
|
||||
prompt = (
|
||||
f"【教师人设】:{self.persona}\n\n"
|
||||
f"【上下文对话内容】:\n{context_text}\n\n"
|
||||
f"【最后一条待回复消息】:\n{last_message_text}\n\n"
|
||||
"【任务要求】:\n"
|
||||
"请作为大张老师回复家长。**必须且只能针对最后一条消息进行回复!**\n"
|
||||
"参考上下文对话内容,确保回复逻辑连贯。\n"
|
||||
"严禁发散,严禁编造家长没说过的情况。如果不清楚家长的意图,就温柔询问。\n"
|
||||
"字数严格控制在 50 字以内。直接输出回复正文。"
|
||||
)
|
||||
|
||||
full_response = ""
|
||||
async for chunk in get_llm_response(prompt, stream=False):
|
||||
full_response += chunk
|
||||
return full_response.strip().strip('"').strip('“').strip('”')
|
||||
|
||||
def step_1_prepare_env(self):
|
||||
"""步骤1: 环境准备"""
|
||||
logger.info("--- [Step 1] 环境准备 ---")
|
||||
WxUtil.setup_script_environment()
|
||||
return True
|
||||
|
||||
def step_2_connect_device(self):
|
||||
"""步骤2: 连接设备"""
|
||||
logger.info("--- [Step 2] 连接设备 ---")
|
||||
self.device = WxUtil.connect_device()
|
||||
if not self.device:
|
||||
logger.error("❌ 设备连接失败,请检查手机是否连接且开启了调试模式")
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_image_hash(self, file_path):
|
||||
"""计算图片的 MD5 哈希值 (忽略顶部 100 像素的状态栏)"""
|
||||
if not os.path.exists(file_path):
|
||||
return None
|
||||
try:
|
||||
# 使用 OpenCV 读取图片
|
||||
img = cv2.imread(file_path)
|
||||
if img is None:
|
||||
# 如果读取失败,回退到文件哈希
|
||||
with open(file_path, "rb") as f:
|
||||
return hashlib.md5(f.read()).hexdigest()
|
||||
|
||||
# 裁剪掉顶部 150 像素 (状态栏/时间)
|
||||
h, w = img.shape[:2]
|
||||
if h > 150:
|
||||
cropped_img = img[150:h, 0:w]
|
||||
else:
|
||||
cropped_img = img
|
||||
|
||||
# 计算裁剪后数据的哈希
|
||||
return hashlib.md5(cropped_img.tobytes()).hexdigest()
|
||||
except Exception as e:
|
||||
logger.error(f"计算哈希出错: {e}, 回退到文件哈希")
|
||||
with open(file_path, "rb") as f:
|
||||
return hashlib.md5(f.read()).hexdigest()
|
||||
|
||||
def get_stable_message_hash(self, msg):
|
||||
"""
|
||||
计算消息的稳定哈希值(忽略坐标等易变字段)
|
||||
仅包含: sender, content, time_display, type
|
||||
"""
|
||||
if not msg:
|
||||
return ""
|
||||
|
||||
stable_data = {
|
||||
"sender": msg.get("sender", ""),
|
||||
"content": msg.get("content") or "", # 确保 None 转为空字符串
|
||||
"time_display": msg.get("time_display", ""),
|
||||
"type": msg.get("type", "")
|
||||
}
|
||||
|
||||
# 序列化并计算哈希
|
||||
msg_str = json.dumps(stable_data, sort_keys=True, ensure_ascii=False)
|
||||
return hashlib.md5(msg_str.encode('utf-8')).hexdigest()
|
||||
|
||||
async def run(self):
|
||||
"""
|
||||
主运行循环
|
||||
"""
|
||||
logger.info("🚀 正在启动 T2_ChatMonitor (Auto-Reply)...")
|
||||
|
||||
# 定义 JSON 序列化辅助函数
|
||||
def numpy_serializer(obj):
|
||||
if isinstance(obj, np.integer):
|
||||
return int(obj)
|
||||
if isinstance(obj, np.floating):
|
||||
return float(obj)
|
||||
if isinstance(obj, np.ndarray):
|
||||
return obj.tolist()
|
||||
raise TypeError(f"Type {type(obj)} not serializable")
|
||||
|
||||
# 1. 环境准备
|
||||
if not self.step_1_prepare_env(): return
|
||||
if not self.step_2_connect_device(): return
|
||||
|
||||
# [User Requested] 移除首屏概念,直接进入监控循环
|
||||
# 以前说过什么都不管了,只关注最后一条
|
||||
logger.info("🚀 启动完成,直接进入实时监控阶段...")
|
||||
|
||||
# 3. 进入循环阶段
|
||||
while True:
|
||||
try:
|
||||
# A. 截图并计算哈希
|
||||
self.device.screenshot(self.screenshot_path)
|
||||
current_screen_hash = self.get_image_hash(self.screenshot_path)
|
||||
|
||||
# B. 如果屏幕无变化,则跳过识别
|
||||
if current_screen_hash == self.last_screen_hash:
|
||||
await asyncio.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
self.last_screen_hash = current_screen_hash
|
||||
logger.info("📸 屏幕发生变化,正在分析...")
|
||||
|
||||
# C. 分析最新图片
|
||||
dialogue_log, input_pos = await WxUtil.analyze_chat_image(
|
||||
self.screenshot_path,
|
||||
self.debug_view_path,
|
||||
device=self.device,
|
||||
process_strategy="UNREAD" # 监控阶段:只处理带红点的新语音
|
||||
)
|
||||
|
||||
if not dialogue_log:
|
||||
logger.info("😴 未识别到有效消息")
|
||||
await asyncio.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
logger.info(f"📊 当前识别到 {len(dialogue_log)} 条消息,最后一条: {dialogue_log[-1]}")
|
||||
|
||||
# 更新当前对话日志(可用于上下文参考)
|
||||
self.dialogue_log = dialogue_log
|
||||
self.input_pos = input_pos
|
||||
|
||||
# D. 只关注最后一条消息
|
||||
last_msg = dialogue_log[-1]
|
||||
# 计算稳定哈希(忽略坐标变化)
|
||||
current_msg_hash = self.get_stable_message_hash(last_msg)
|
||||
|
||||
# E. 判断是否需要回复 (对方发送且非重复消息)
|
||||
sender = last_msg.get('sender', '')
|
||||
|
||||
# Check if hash is already processed (in-memory only)
|
||||
is_processed = current_msg_hash in self.processed_hashes
|
||||
|
||||
# Log only if it changed from last *in-memory* check to avoid spam
|
||||
if is_processed and current_msg_hash != self.last_processed_msg_hash:
|
||||
# logger.info(f"🚫 [监控] 消息哈希已存在于历史记录中,跳过回复 (Hash: {current_msg_hash})")
|
||||
self.last_processed_msg_hash = current_msg_hash
|
||||
|
||||
if not is_processed and current_msg_hash != self.last_processed_msg_hash:
|
||||
if sender != "我":
|
||||
event_shot = WxUtil.get_next_debug_path("event_new_msg")
|
||||
self.device.screenshot(event_shot)
|
||||
logger.info(f"💡 [监控] 发现新消息: {last_msg},保存现场截图: {event_shot}")
|
||||
|
||||
# 获取上下文文本 (格式化为 Sender: Content)
|
||||
context_text = "\n".join([f"{m.get('time_display', '') + ' ' if m.get('time_display') else ''}{m.get('sender')}: {m.get('content')}" for m in dialogue_log[:-1]])
|
||||
last_content = last_msg.get('content') or ""
|
||||
|
||||
# 兜底逻辑:如果最后一条是语音且内容为空(可能因无红点未被 UNREAD 策略处理),尝试强制转换
|
||||
if last_msg.get('type') == 'voice' and not last_content.strip():
|
||||
logger.info("⚠️ [监控] 最后一条语音消息未获取到内容(可能已读无红点),尝试强制转换...")
|
||||
# 强制使用 LAST 策略重试
|
||||
dialogue_log_retry, _ = await WxUtil.analyze_chat_image(
|
||||
self.screenshot_path,
|
||||
self.debug_view_path,
|
||||
device=self.device,
|
||||
process_strategy="LAST"
|
||||
)
|
||||
if dialogue_log_retry:
|
||||
# 更新引用
|
||||
self.dialogue_log = dialogue_log_retry
|
||||
dialogue_log = dialogue_log_retry
|
||||
last_msg = dialogue_log[-1]
|
||||
last_content = last_msg.get('content') or ""
|
||||
logger.info(f"🔄 [重试] 强制转换后内容: {last_content}")
|
||||
|
||||
# 重新构建哈希
|
||||
current_msg_hash = self.get_stable_message_hash(last_msg)
|
||||
# 再次检查是否已处理 (因为内容变了,哈希变了)
|
||||
if current_msg_hash in self.processed_hashes:
|
||||
logger.info(f"🚫 [重试] 转换后发现该消息已处理,跳过。")
|
||||
self.last_processed_msg_hash = current_msg_hash
|
||||
# 跳过本次循环的剩余部分
|
||||
continue
|
||||
|
||||
# 生成回复
|
||||
reply = await self.get_reply(last_content, context_text)
|
||||
|
||||
if reply:
|
||||
logger.info(f"🤖 [监控] LLM 建议回复: {reply}")
|
||||
if self.input_pos:
|
||||
logger.info(f"⚡ [监控] 执行自动回复...")
|
||||
# input_pos 是 ((x,y), box) 格式,取第一个元素坐标点
|
||||
target_pos = self.input_pos[0] if isinstance(self.input_pos, (list, tuple)) and len(self.input_pos) == 2 and isinstance(self.input_pos[0], (list, tuple)) else self.input_pos
|
||||
# 简单兼容处理:如果 input_pos[0] 是 tuple/list 且 input_pos[1] 是 None/box,则取 input_pos[0]
|
||||
if isinstance(self.input_pos, (list, tuple)) and len(self.input_pos) == 2 and isinstance(self.input_pos[0], (list, tuple)):
|
||||
target_pos = self.input_pos[0]
|
||||
|
||||
perform_input_action(self.device, target_pos, reply)
|
||||
|
||||
# 发送后截图留存
|
||||
reply_sent_shot = WxUtil.get_next_debug_path("event_reply_sent")
|
||||
self.device.screenshot(reply_sent_shot)
|
||||
logger.info(f"✅ [监控] 回复已发送,保存发送后截图: {reply_sent_shot}")
|
||||
|
||||
self._record_processed_hash(current_msg_hash)
|
||||
self.last_processed_msg_hash = current_msg_hash
|
||||
else:
|
||||
logger.error("❌ 未找到输入框位置,无法发送回复")
|
||||
else:
|
||||
logger.info("⚪ [监控] LLM 认为无需回复")
|
||||
self._record_processed_hash(current_msg_hash)
|
||||
self.last_processed_msg_hash = current_msg_hash
|
||||
else:
|
||||
# 是我发的消息,更新哈希,不再处理
|
||||
self.last_processed_msg_hash = current_msg_hash
|
||||
|
||||
await asyncio.sleep(self.check_interval)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in monitoring loop: {e}", exc_info=True)
|
||||
await asyncio.sleep(self.check_interval)
|
||||
|
||||
async def run_main():
|
||||
"""
|
||||
运行自动巡课机器人
|
||||
"""
|
||||
bot = ChatMonitorBot()
|
||||
await bot.run()
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 应用 Win32 补丁
|
||||
Win32Patch.patch()
|
||||
asyncio.run(run_main())
|
||||
119
WeiXin/WxUtil.py
119
WeiXin/WxUtil.py
@@ -40,9 +40,16 @@ def parse_wechat_time(time_str):
|
||||
支持: "10:03", "昨天 10:03", "星期三 10:03", "2025年1月1日 10:03"
|
||||
"""
|
||||
try:
|
||||
clean_str = time_str.strip()
|
||||
|
||||
# 0. 预处理:过滤纯数字(防止电话号码被误识别为时间)
|
||||
# 微信时间戳通常包含中文或冒号,单纯的数字串(如 "18686619970")不是有效时间
|
||||
if re.match(r'^\d+$', clean_str):
|
||||
logger.warning(f"忽略疑似电话号码/纯数字的时间字符串: '{clean_str}'")
|
||||
return ""
|
||||
|
||||
now = datetime.now()
|
||||
today = now.date()
|
||||
clean_str = time_str.strip()
|
||||
|
||||
# 1. HH:mm (当天)
|
||||
# 注意:有时候 OCR 会把冒号识别成其他字符,这里假设是标准的 HH:mm
|
||||
@@ -130,10 +137,11 @@ def parse_wechat_time(time_str):
|
||||
dt = datetime.combine(today, datetime.min.time().replace(hour=h, minute=m))
|
||||
return dt.strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
return clean_str # 解析失败,返回原串
|
||||
# 解析失败,返回空字符串,避免将无关文本(如电话号码)误认为时间注入到上下文中
|
||||
return ""
|
||||
except Exception as e:
|
||||
logger.warning(f"时间解析失败 '{time_str}': {e}")
|
||||
return time_str
|
||||
return ""
|
||||
|
||||
def get_next_debug_path(desc="step"):
|
||||
"""获取下一个顺序命名的调试图片路径 (debug_N_desc.jpg)"""
|
||||
@@ -306,6 +314,11 @@ def _detect_bubble_color(img, bbox):
|
||||
if g > 150:
|
||||
return "white"
|
||||
|
||||
# 特殊补丁:如果 B, G, R 都很接近且在 130 左右,可能是微信的背景灰 (通常用于时间戳或系统消息)
|
||||
if 110 < r < 160 and 110 < g < 160 and 110 < b < 160:
|
||||
if abs(r - g) < 15 and abs(g - b) < 15:
|
||||
return "system_gray"
|
||||
|
||||
return "unknown"
|
||||
|
||||
def _scan_chat_messages(image_path):
|
||||
@@ -359,7 +372,7 @@ def _scan_chat_messages(image_path):
|
||||
# 微信菜单关键字(用于排除干扰)
|
||||
MENU_KEYWORDS = ["听筒播放", "收藏", "背景播放", "删除", "多选", "取消转文字", "转文字", "引用", "提醒"]
|
||||
# 忽略的系统消息内容
|
||||
IGNORE_CONTENT = ["撤回了一条消息", "打招呼的消息", "拍了拍", "你撤回了一条消息", "引用", "Clear Text", "Switch IME", "Done"]
|
||||
IGNORE_CONTENT = ["撤回了一条消息", "打招呼的消息", "拍了拍", "你撤回了一条消息", "引用", "Clear Text", "Switch IME", "Done", "按住说话", "发送"]
|
||||
|
||||
# 5. 整合所有消息
|
||||
messages = []
|
||||
@@ -367,9 +380,9 @@ def _scan_chat_messages(image_path):
|
||||
|
||||
# 绘制过滤区域边界 (可视化)
|
||||
cv2.line(debug_img, (0, 150), (w, 150), (255, 0, 255), 2) # 顶部线
|
||||
cv2.line(debug_img, (0, h - 100), (w, h - 100), (255, 0, 255), 2) # 底部线
|
||||
cv2.line(debug_img, (0, h - 60), (w, h - 60), (255, 0, 255), 2) # 底部线 (放宽到底部 60px)
|
||||
cv2.putText(debug_img, "TOP_FILTER", (10, 140), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 1)
|
||||
cv2.putText(debug_img, "BOTTOM_FILTER", (10, h - 110), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 1)
|
||||
cv2.putText(debug_img, "BOTTOM_FILTER", (10, h - 70), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 1)
|
||||
|
||||
claimed_ocr_indices = set()
|
||||
|
||||
@@ -379,7 +392,7 @@ def _scan_chat_messages(image_path):
|
||||
cv2.circle(debug_img, (ax, ay), 10, (255, 255, 0), -1)
|
||||
|
||||
# 过滤掉顶部和底部的非聊天区域
|
||||
if ay < 150 or ay > h - 100:
|
||||
if ay < 150 or ay > h - 60:
|
||||
logger.info(f"忽略区域外语音图标: ({ax}, {ay})")
|
||||
cv2.rectangle(debug_img, (ax-35, ay-35), (ax+35, ay+35), (128, 128, 128), 1)
|
||||
cv2.putText(debug_img, "FILTERED", (ax - 40, ay - 45), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (128, 128, 128), 1)
|
||||
@@ -416,25 +429,32 @@ def _scan_chat_messages(image_path):
|
||||
|
||||
if voice_is_left:
|
||||
# 语音在左 (对方): 文本必须也是左对齐
|
||||
# - min_x 必须靠左 (< 300)
|
||||
# - min_x 必须靠左 (< 450) [Fix] 放宽阈值,防止长文本或缩进文本被过滤
|
||||
# - max_x 不能太靠右 (> w - 150),否则可能是"我"的消息
|
||||
if min_x > 300 or max_x > w - 150:
|
||||
if min_x > 450 or max_x > w - 150:
|
||||
logger.debug(f"忽略文本 '{text[:10]}' (Left Voice): min_x={min_x}, max_x={max_x} 不满足左对齐条件")
|
||||
continue
|
||||
else:
|
||||
# 语音在右 (我): 文本必须也是右对齐
|
||||
# - max_x 必须靠右 (> w - 300)
|
||||
# - min_x 不能太靠左 (< 100)
|
||||
if max_x < w - 300 or min_x < 100:
|
||||
logger.debug(f"忽略文本 '{text[:10]}' (Right Voice): min_x={min_x}, max_x={max_x} 不满足右对齐条件")
|
||||
continue
|
||||
|
||||
if -50 < c_y - ay < 800 and abs(c_x - ax) < 500:
|
||||
# [Fix] 放宽 X 轴判定范围 (500 -> 600) 以适应更宽的文本
|
||||
if -50 < c_y - ay < 800 and abs(c_x - ax) < 600:
|
||||
# 检查中间是否有其他语音图标
|
||||
has_intermediate_audio = False
|
||||
for other_ax, other_ay in audio_matches:
|
||||
# 只有当中间的语音图标在 [150, h-60] 的有效聊天区域内时,才视为阻断
|
||||
if ay + 20 < other_ay < c_y - 10:
|
||||
if 150 <= other_ay <= h - 60:
|
||||
has_intermediate_audio = True
|
||||
logger.info(f"语音({ax},{ay}) 被中间语音图标({other_ax},{other_ay}) 阻断,无法关联文本 '{text[:10]}...'")
|
||||
logger.info(f"语音({ax},{ay}) 被中间有效区域内的语音图标({other_ax},{other_ay}) 阻断,无法关联文本 '{text[:10]}...'")
|
||||
break
|
||||
else:
|
||||
logger.info(f"语音({ax},{ay}) 忽略非聊天区域(Y={other_ay})的语音图标阻断")
|
||||
|
||||
if has_intermediate_audio:
|
||||
continue
|
||||
@@ -532,7 +552,7 @@ def _scan_chat_messages(image_path):
|
||||
c_x = int((bbox[0][0] + bbox[2][0]) / 2)
|
||||
c_y = int((bbox[0][1] + bbox[2][1]) / 2)
|
||||
|
||||
if c_y < 150 or c_y > h - 100:
|
||||
if c_y < 150 or c_y > h - 60:
|
||||
continue
|
||||
|
||||
# 判定发送者 (增强版: 几何 + 颜色)
|
||||
@@ -544,6 +564,9 @@ def _scan_chat_messages(image_path):
|
||||
sender = "我"
|
||||
elif sender_color == "white":
|
||||
sender = "对方"
|
||||
elif sender_color == "system_gray":
|
||||
# 灰底文字通常是时间戳或系统消息,由系统发送,几何上居中
|
||||
sender = "system"
|
||||
|
||||
# 2. 几何特征强制修正 (Double Check)
|
||||
# 假设头像+边距约占 15% 宽度
|
||||
@@ -561,9 +584,12 @@ def _scan_chat_messages(image_path):
|
||||
# 规则 B: 如果这一行极其靠左 (小于 35% 宽度),且不靠右,那肯定是"对方"
|
||||
# 扩大判定范围,防止因为 OCR 稍微缩进导致判定失效
|
||||
# 注意:如果颜色明确为"我"(绿色),则跳过此规则,因为"我"的长消息也可能靠左
|
||||
elif min_x < w * 0.35 and max_x < w * 0.85:
|
||||
elif min_x < w * 0.35 and max_x < w * 0.75: # 修正:max_x 阈值从 0.85 降低到 0.75
|
||||
if sender == "我":
|
||||
logger.info(f"Geometry says '对方' (min_x={min_x} < {w*0.35}) but Color is '我' (Green). Trusting Color.")
|
||||
elif sender == "system":
|
||||
# 即使颜色是系统灰,但如果位置极其靠左,也可能是对方的某种特殊气泡
|
||||
pass
|
||||
else:
|
||||
sender = "对方"
|
||||
|
||||
@@ -584,6 +610,9 @@ def _scan_chat_messages(image_path):
|
||||
# [Fix] 如果颜色明确是绿色,说明是"我"的左对齐文本(长文换行),不应被几何规则强制改为"对方"
|
||||
if sender == "我" and sender_color == "green":
|
||||
logger.info(f"Geometry says '对方' (center={c_x} < {w*0.45}) but Color is 'green'. Keeping '我'.")
|
||||
elif sender == "system":
|
||||
# 系统消息允许居中或偏左
|
||||
pass
|
||||
else:
|
||||
if sender == "我":
|
||||
logger.warning(f"Sender detected as '我' by color but center is left ({c_x} < {w*0.45}). Correcting to '对方'.")
|
||||
@@ -592,6 +621,9 @@ def _scan_chat_messages(image_path):
|
||||
elif c_x > w * 0.55:
|
||||
if sender == "对方":
|
||||
logger.warning(f"Sender detected as '对方' by color but center is right ({c_x} > {w*0.55}). Correcting to '我'.")
|
||||
elif sender == "system":
|
||||
pass
|
||||
else:
|
||||
sender = "我"
|
||||
else:
|
||||
logger.info(f"Message in middle zone ({w*0.45} < {c_x} < {w*0.55}), trusting color detection: {sender}")
|
||||
@@ -665,10 +697,12 @@ def _scan_chat_messages(image_path):
|
||||
|
||||
return final_messages_with_time, debug_img, chat_title
|
||||
|
||||
async def analyze_chat_image(image_path, output_path, device=None, target_name="对方", process_strategy="ALL"):
|
||||
async def analyze_chat_image(image_path, output_path, device=None, target_name="对方", process_strategy="ALL", restore_processed_voice=True):
|
||||
"""
|
||||
全面采用 CV + OCR 识别微信聊天截图中的最后一条消息
|
||||
:param process_strategy: 语音处理策略 (ALL/UNREAD/LAST)
|
||||
:param restore_processed_voice: 是否在转文字后还原(隐藏文字)。默认为 True。
|
||||
设为 False 可防止最后一条消息在无内容时陷入"转文字->还原->空内容"的死循环。
|
||||
注意:此函数现在包含一个循环,如果发现需要转文字的语音,会逐个处理并重新截图。
|
||||
"""
|
||||
try:
|
||||
@@ -799,21 +833,39 @@ async def analyze_chat_image(image_path, output_path, device=None, target_name="
|
||||
# 1. 截图 (但不立即 OCR,而是丢给异步任务)
|
||||
peek_shot = get_next_debug_path("step_peek_content")
|
||||
d.screenshot(peek_shot)
|
||||
logger.info("已截图,启动异步OCR任务以提取内容...")
|
||||
logger.info(f"已截图 {peek_shot},启动异步OCR任务以提取内容...")
|
||||
|
||||
async def _async_ocr_task(img_path, target_y):
|
||||
"""内部异步任务:在线程池中运行 OCR"""
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
# 在默认执行器(线程池)中运行耗时的 _scan_chat_messages
|
||||
logger.info(f"🚀 [Async OCR] 开始分析截图 {os.path.basename(img_path)} (目标 Y={target_y})")
|
||||
msgs, _, _ = await loop.run_in_executor(None, _scan_chat_messages, img_path)
|
||||
|
||||
found = None
|
||||
# 收集所有可能是该语音消息转换出的文本
|
||||
all_found_texts = []
|
||||
for pm in msgs:
|
||||
if pm['type'] == 'voice' and pm.get('is_converted'):
|
||||
if abs(pm['y'] - target_y) < 50:
|
||||
found = pm.get('content')
|
||||
break
|
||||
# 容差稍微放大,因为转文字展开后 Y 坐标会变
|
||||
if abs(pm['y'] - target_y) < 150: # 进一步放宽容差
|
||||
content = pm.get('content', '').strip()
|
||||
if content:
|
||||
all_found_texts.append((pm['y'], content))
|
||||
|
||||
if all_found_texts:
|
||||
# 按 Y 轴排序,确保多行文本顺序正确
|
||||
all_found_texts.sort(key=lambda x: x[0])
|
||||
found = " ".join([t[1] for t in all_found_texts])
|
||||
logger.info(f"✨ [Async OCR] 在 Y={target_y} 附近找到转换文字: {found}")
|
||||
|
||||
if not found:
|
||||
logger.warning(f"⚠️ [Async OCR] 未能在 Y={target_y} 附近找到已转换文字")
|
||||
return target_y, found
|
||||
except Exception as e:
|
||||
logger.error(f"❌ [Async OCR] 任务执行失败: {e}")
|
||||
return target_y, None
|
||||
|
||||
# 创建并保存任务
|
||||
task = asyncio.create_task(_async_ocr_task(peek_shot, vy))
|
||||
@@ -822,9 +874,10 @@ async def analyze_chat_image(image_path, output_path, device=None, target_name="
|
||||
analyze_chat_image._ocr_tasks = []
|
||||
analyze_chat_image._ocr_tasks.append(task)
|
||||
|
||||
# 2. 立即还原状态 (取消转文字)
|
||||
# 2. 还原状态 (取消转文字)
|
||||
# 注意:由于 OCR 还没出结果,我们无法精确定位展开后的文字位置
|
||||
# 但通常点击原语音气泡位置 (vx, vy) 也能触发菜单
|
||||
if restore_processed_voice:
|
||||
logger.info("准备还原状态 (取消转文字)...")
|
||||
|
||||
d.long_click(vx, vy, 1.0) # 盲点原坐标
|
||||
@@ -852,6 +905,11 @@ async def analyze_chat_image(image_path, output_path, device=None, target_name="
|
||||
logger.warning("❌ 未找到'隐藏文字'按钮,无法还原状态!(后续可能导致重复处理)")
|
||||
|
||||
# 3. 准备下一次循环
|
||||
if len(target_voices) == 1:
|
||||
logger.info("✅ 当前屏幕所有目标语音已处理完毕,无需再次全屏扫描。")
|
||||
final_messages = messages # 使用本轮初始扫描的消息列表
|
||||
break
|
||||
|
||||
# 重新截图,因为界面可能微调,或者只是恢复了
|
||||
next_screenshot = get_next_debug_path("step_restored")
|
||||
d.screenshot(next_screenshot)
|
||||
@@ -860,6 +918,17 @@ async def analyze_chat_image(image_path, output_path, device=None, target_name="
|
||||
current_output_path = get_next_debug_path("flag_restored")
|
||||
|
||||
continue
|
||||
else:
|
||||
logger.info("⏩ [配置] 跳过还原状态步骤 (保持文字展开)。")
|
||||
# 即使不还原,我们也不建议继续处理下一条,因为界面已经大幅变动(展开了文字)。
|
||||
# 除非我们重新截图并重新定位。
|
||||
# 但在这里,如果 restore_processed_voice=False,通常意味着我们只关心最后一条(LAST策略),或者我们接受界面变动。
|
||||
|
||||
# 为了安全起见,如果不还原,我们最好终止循环(假设只处理这一条,或者下一轮主循环再处理其他的)
|
||||
# 否则后续的 target_voices 坐标全都不准了。
|
||||
logger.info("🛑 因不还原状态,终止本轮多语音处理循环,等待下一次主监控循环。")
|
||||
final_messages = messages # 这里的 messages 其实是展开前的,但没关系,我们的内容通过 captured_voice_contents 注入
|
||||
break
|
||||
|
||||
else:
|
||||
logger.warning("❌ 未找到'转文字'按钮,可能是已转换或误判")
|
||||
@@ -887,12 +956,13 @@ async def analyze_chat_image(image_path, output_path, device=None, target_name="
|
||||
if captured_voice_contents:
|
||||
logger.info(f"正在注入 {len(captured_voice_contents)} 条已还原的语音内容...")
|
||||
for m in final_messages:
|
||||
if m['type'] == 'voice' and not m.get('content'):
|
||||
if m['type'] == 'voice' and (not m.get('content') or m.get('content').strip() == ""):
|
||||
for py, content in captured_voice_contents.items():
|
||||
if abs(m['y'] - py) < 30: # 匹配原始 Y 坐标
|
||||
# 注入时的容差也要放大,因为 final_messages 的 Y 可能和点击时的 vy 略有差异
|
||||
if abs(m['y'] - py) < 100:
|
||||
m['content'] = content
|
||||
m['is_converted'] = True # 标记为逻辑上已转换
|
||||
logger.info(f" -> 注入内容: {content[:10]}...")
|
||||
logger.info(f" -> 注入内容到 Y={m['y']} (原 py={py}): {content[:20]}...")
|
||||
break
|
||||
|
||||
# 构造返回值
|
||||
@@ -931,15 +1001,16 @@ async def analyze_chat_image(image_path, output_path, device=None, target_name="
|
||||
# 尝试注入异步获取的语音内容
|
||||
if msg['type'] == 'voice':
|
||||
# 模糊匹配 Y 坐标 (增大容差到 100,应对界面滚动)
|
||||
# 优先检查 content 是否为空或为 placeholder
|
||||
if not msg.get('content') or msg.get('content').strip() == "":
|
||||
for y_key, content in captured_voice_contents.items():
|
||||
if abs(msg['y'] - y_key) < 100:
|
||||
msg['is_converted'] = True
|
||||
msg['content'] = content
|
||||
logger.info(f"注入语音内容到最终消息列表: {content}")
|
||||
logger.info(f"✅ [注入] 成功将异步语音内容 '{content}' 注入到 Y={msg['y']} 的消息中")
|
||||
break
|
||||
|
||||
# 无论是否有内容,都加入 dialogue_log
|
||||
# 如果是语音且没内容,T2 会有兜底逻辑去处理
|
||||
if msg['type'] == 'text':
|
||||
if msg.get('content'): # 文本消息没内容通常是识别错误,可以丢弃
|
||||
dialogue_log.append(msg)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,20 +1,7 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "D:\\dsWork\\aiData\\WordAddIn\\",
|
||||
"Documents": [
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form"
|
||||
}
|
||||
],
|
||||
"Documents": [],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
@@ -22,46 +9,11 @@
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": 1,
|
||||
"SelectedChildIndex": -1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 0,
|
||||
"Title": "AiRibbon.cs",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\AiRibbon.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs",
|
||||
"RelativeToolTip": "WordAddIn\\AiRibbon.cs",
|
||||
"ViewState": "AgIAAPcAAAAAAAAAAAA1wAsBAABFAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:05:10.056Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\ReferenceGeneratorForm.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"RelativeToolTip": "WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:05:02.111Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 2,
|
||||
"Title": "OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\OutlineSettingsForm.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"RelativeToolTip": "WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:04:59.465Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,20 +1,7 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"WorkspaceRootPath": "D:\\dsWork\\aiData\\WordAddIn\\",
|
||||
"Documents": [
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|d:\\dswork\\aidata\\wordaddin\\wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\airibbon.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\referencegeneratorform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form"
|
||||
},
|
||||
{
|
||||
"AbsoluteMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|D:\\dsWork\\aiData\\WordAddIn\\wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form",
|
||||
"RelativeMoniker": "D:0:0:{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}|WordAddIn\\WordAddIn.csproj|solutionrelative:wordaddin\\outlinesettingsform.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}|Form"
|
||||
}
|
||||
],
|
||||
"Documents": [],
|
||||
"DocumentGroupContainers": [
|
||||
{
|
||||
"Orientation": 0,
|
||||
@@ -22,46 +9,11 @@
|
||||
"DocumentGroups": [
|
||||
{
|
||||
"DockedWidth": 200,
|
||||
"SelectedChildIndex": 1,
|
||||
"SelectedChildIndex": -1,
|
||||
"Children": [
|
||||
{
|
||||
"$type": "Bookmark",
|
||||
"Name": "ST:0:0:{3ae79031-e1bc-11d0-8f78-00a0c9110057}"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 0,
|
||||
"Title": "AiRibbon.cs",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\AiRibbon.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\AiRibbon.cs",
|
||||
"RelativeToolTip": "WordAddIn\\AiRibbon.cs",
|
||||
"ViewState": "AgIAAPcAAAAAAAAAAAA1wAsBAABFAAAAAAAAAA==",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:05:10.056Z",
|
||||
"EditorCaption": ""
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 1,
|
||||
"Title": "ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\ReferenceGeneratorForm.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"RelativeToolTip": "WordAddIn\\ReferenceGeneratorForm.cs [\u8BBE\u8BA1]",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:05:02.111Z"
|
||||
},
|
||||
{
|
||||
"$type": "Document",
|
||||
"DocumentIndex": 2,
|
||||
"Title": "OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"DocumentMoniker": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs",
|
||||
"RelativeDocumentMoniker": "WordAddIn\\OutlineSettingsForm.cs",
|
||||
"ToolTip": "D:\\dsWork\\aiData\\WordAddIn\\WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"RelativeToolTip": "WordAddIn\\OutlineSettingsForm.cs [\u8BBE\u8BA1]",
|
||||
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
|
||||
"WhenOpened": "2026-01-24T10:04:59.465Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.14.36915.13 d17.14
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WordAddIn", "WordAddIn\WordAddIn.csproj", "{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpsInstaller", "WpsInstaller\WpsInstaller.csproj", "{A1B2C3D4-E5F6-4789-0011-223344556677}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -15,6 +17,10 @@ Global
|
||||
{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EC0A80FA-FCBD-4239-945A-627B20CCCBAA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A1B2C3D4-E5F6-4789-0011-223344556677}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A1B2C3D4-E5F6-4789-0011-223344556677}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A1B2C3D4-E5F6-4789-0011-223344556677}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A1B2C3D4-E5F6-4789-0011-223344556677}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace WordAddIn
|
||||
else if (ext == ".docx" || ext == ".doc")
|
||||
{
|
||||
// 使用 Word Interop 打开并读取文档内容
|
||||
dynamic wordApp = Globals.ThisAddIn.Application;
|
||||
dynamic wordApp = AddInContext.CurrentApplication;
|
||||
dynamic doc = wordApp.Documents.Open(FileName: file, Visible: false, ReadOnly: true, AddToRecentFiles: false);
|
||||
content = doc.Content.Text;
|
||||
doc.Close(SaveChanges: false);
|
||||
@@ -174,7 +174,7 @@ namespace WordAddIn
|
||||
LoadingForm loading = null;
|
||||
try
|
||||
{
|
||||
var doc = Globals.ThisAddIn.Application.ActiveDocument;
|
||||
var doc = AddInContext.CurrentApplication.ActiveDocument;
|
||||
string fullText = doc.Content.Text;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fullText) || fullText.Length < 10)
|
||||
@@ -230,7 +230,7 @@ namespace WordAddIn
|
||||
LoadingForm loading = null;
|
||||
try
|
||||
{
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
var selection = AddInContext.CurrentApplication.Selection;
|
||||
string originalText = null;
|
||||
|
||||
// 1. 优先使用选区文本
|
||||
@@ -313,7 +313,7 @@ namespace WordAddIn
|
||||
LoadingForm loading = null;
|
||||
try
|
||||
{
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
var selection = AddInContext.CurrentApplication.Selection;
|
||||
|
||||
// 1. 判断是否有选中文本
|
||||
bool hasSelection = selection.Type == Word.WdSelectionType.wdSelectionNormal ||
|
||||
@@ -403,7 +403,7 @@ namespace WordAddIn
|
||||
LoadingForm loading = null;
|
||||
try
|
||||
{
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
var selection = AddInContext.CurrentApplication.Selection;
|
||||
|
||||
// 1. 判断是否有选中文本或剪贴板内容
|
||||
bool hasSelection = selection.Type == Word.WdSelectionType.wdSelectionNormal ||
|
||||
@@ -489,7 +489,7 @@ namespace WordAddIn
|
||||
LoadingForm loading = null;
|
||||
try
|
||||
{
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
var selection = AddInContext.CurrentApplication.Selection;
|
||||
string originalText = selection.Text; // 可以为空,作为生成的上下文参考
|
||||
|
||||
string referenceContent = null;
|
||||
@@ -521,7 +521,7 @@ namespace WordAddIn
|
||||
// 尝试用 Word Interop 打开读取 (针对 .doc, .docx)
|
||||
try
|
||||
{
|
||||
dynamic wordApp = Globals.ThisAddIn.Application;
|
||||
dynamic wordApp = AddInContext.CurrentApplication;
|
||||
dynamic doc = wordApp.Documents.Open(FileName: path, Visible: false, ReadOnly: true, AddToRecentFiles: false);
|
||||
referenceContent = doc.Content.Text;
|
||||
doc.Close(SaveChanges: false);
|
||||
@@ -621,7 +621,7 @@ namespace WordAddIn
|
||||
LoadingForm loading = null;
|
||||
try
|
||||
{
|
||||
var doc = Globals.ThisAddIn.Application.ActiveDocument;
|
||||
var doc = AddInContext.CurrentApplication.ActiveDocument;
|
||||
string fullText = doc.Content.Text;
|
||||
|
||||
// 1. 基础校验:文档内容太少则不处理
|
||||
@@ -732,7 +732,7 @@ namespace WordAddIn
|
||||
LoadingForm loading = null;
|
||||
try
|
||||
{
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
var selection = AddInContext.CurrentApplication.Selection;
|
||||
string prompt = selection.Text;
|
||||
|
||||
// 1. 尝试获取选区文本
|
||||
@@ -869,8 +869,8 @@ namespace WordAddIn
|
||||
? new OutlineStyleConfig()
|
||||
: JsonConvert.DeserializeObject<OutlineStyleConfig>(styleJson);
|
||||
|
||||
var doc = Globals.ThisAddIn.Application.ActiveDocument;
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
var doc = AddInContext.CurrentApplication.ActiveDocument;
|
||||
var selection = AddInContext.CurrentApplication.Selection;
|
||||
Word.Range insertRange = selection.Range;
|
||||
insertRange.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
|
||||
|
||||
@@ -968,7 +968,7 @@ namespace WordAddIn
|
||||
client.DownloadFile(imageUrl, tempPath);
|
||||
}
|
||||
|
||||
var selection = Globals.ThisAddIn.Application.Selection;
|
||||
var selection = AddInContext.CurrentApplication.Selection;
|
||||
var inlineShape = selection.InlineShapes.AddPicture(FileName: tempPath, LinkToFile: false, SaveWithDocument: true);
|
||||
|
||||
// 强制将图片所在的段落设为“正文”样式
|
||||
|
||||
@@ -15,7 +15,8 @@ namespace WordAddIn
|
||||
|
||||
private void ThisAddIn_Startup(object sender, System.EventArgs e)
|
||||
{
|
||||
// 在此添加初始化代码
|
||||
// 初始化全局上下文,供 AiRibbon 等使用
|
||||
AddInContext.CurrentApplication = this.Application;
|
||||
}
|
||||
|
||||
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
|
||||
|
||||
@@ -31,10 +31,10 @@
|
||||
<ResolveComReferenceSilent>true</ResolveComReferenceSilent>
|
||||
<IsWebBootstrapper>False</IsWebBootstrapper>
|
||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||
<PublishUrl>publish\</PublishUrl>
|
||||
<PublishUrl>D:\办公\</PublishUrl>
|
||||
<InstallUrl />
|
||||
<TargetCulture>zh-chs</TargetCulture>
|
||||
<ApplicationVersion>1.0.0.1</ApplicationVersion>
|
||||
<ApplicationVersion>1.0.0.5</ApplicationVersion>
|
||||
<AutoIncrementApplicationRevision>true</AutoIncrementApplicationRevision>
|
||||
<UpdateEnabled>true</UpdateEnabled>
|
||||
<UpdateInterval>7</UpdateInterval>
|
||||
@@ -197,6 +197,15 @@
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>true</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="Extensibility">
|
||||
<Guid>{AC0714F2-3D04-11D1-C2E4-00600893B433}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
</Otherwise>
|
||||
</Choose>
|
||||
@@ -204,6 +213,9 @@
|
||||
<Reference Include="stdole, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<Private>False</Private>
|
||||
</Reference>
|
||||
<Reference Include="Extensibility, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<!--
|
||||
This section defines the user source files that are part of the project.
|
||||
@@ -276,6 +288,12 @@
|
||||
<None Include="ThisAddIn.Designer.xml">
|
||||
<DependentUpon>ThisAddIn.cs</DependentUpon>
|
||||
</None>
|
||||
<Compile Include="AddInContext.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="WpsAddIn.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="ThisAddIn.Designer.cs">
|
||||
<DependentUpon>ThisAddIn.Designer.xml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -305,7 +323,7 @@
|
||||
<FlavorProperties GUID="{BAA0C2D2-18E2-41B9-852F-F413020CAA33}">
|
||||
<ProjectProperties HostName="Word" HostPackage="{29A7B9D7-A7F1-4328-8EF0-6B2D1A56B2C1}" OfficeVersion="15.0" VstxVersion="4.0" ApplicationType="Word" Language="cs" TemplatesPath="" DebugInfoExeName="#Software\Microsoft\Office\16.0\Word\InstallRoot\Path#WINWORD.EXE" DebugInfoCommandLine="/x" AddItemTemplatesGuid="{51063C3A-E220-4D12-8922-BDA915ACD783}" />
|
||||
<Host Name="Word" GeneratedCodeNamespace="WordAddIn" PublishedHash="69C324AB27932AA2FBF2B7EA72250886FF164DE6" IconIndex="0">
|
||||
<HostItem Name="ThisAddIn" Code="ThisAddIn.cs" CanonicalName="AddIn" CanActivate="false" IconIndex="1" Blueprint="ThisAddIn.Designer.xml" GeneratedCode="ThisAddIn.Designer.cs" PublishedHash="8166DE8D98B62268329D9D6125CF7DE54A2E1945" />
|
||||
<HostItem Name="ThisAddIn" Code="ThisAddIn.cs" CanonicalName="AddIn" PublishedHash="8166DE8D98B62268329D9D6125CF7DE54A2E1945" CanActivate="false" IconIndex="1" Blueprint="ThisAddIn.Designer.xml" GeneratedCode="ThisAddIn.Designer.cs" />
|
||||
</Host>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PublishUrlHistory />
|
||||
<PublishUrlHistory>D:\办公\</PublishUrlHistory>
|
||||
<InstallUrlHistory />
|
||||
<SupportUrlHistory />
|
||||
</PropertyGroup>
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
|
||||
<asmv1:assemblyIdentity name="WordAddIn.dll" version="1.0.0.1" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" type="win32" />
|
||||
<asmv1:assemblyIdentity name="WordAddIn.dll" version="1.0.0.5" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" type="win32" />
|
||||
<description xmlns="urn:schemas-microsoft-com:asm.v1">WordAddIn</description>
|
||||
<application />
|
||||
<entryPoint>
|
||||
@@ -166,14 +166,14 @@
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="WordAddIn.dll" size="102912">
|
||||
<dependentAssembly dependencyType="install" allowDelayedBinding="true" codebase="WordAddIn.dll" size="98816">
|
||||
<assemblyIdentity name="WordAddIn" version="1.0.0.0" language="neutral" processorArchitecture="msil" />
|
||||
<hash>
|
||||
<dsig:Transforms>
|
||||
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
|
||||
</dsig:Transforms>
|
||||
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
|
||||
<dsig:DigestValue>opK7SiQ9dqUcG7Gosdxa7kFJixJFR1aqCz+EqKq90Rw=</dsig:DigestValue>
|
||||
<dsig:DigestValue>7zXanmFKW8rfBCQZX7ppBtABOc2pdXJG1SvwKHdlaHM=</dsig:DigestValue>
|
||||
</hash>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
@@ -200,4 +200,4 @@
|
||||
</vstov4:customizations>
|
||||
</vstav3:application>
|
||||
</vstav3:addIn>
|
||||
<publisherIdentity name="CN=DESKTOP-Q6H7B6L\Administrator" issuerKeyHash="42606c52715d5764c2f9dab5bee6dcd76cc7a67d" /><Signature Id="StrongNameSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>qit2kD2zs+ShAnH0i1668jnenJLrkB7hu+wiVm45iqg=</DigestValue></Reference></SignedInfo><SignatureValue>lAH5ZUPogQCgWwdpfhRH4tv+uGhG6G7VkPX9Ya++V0axm6kr4GDGcTsS+QkX+ctsY9DZsFef8FooFSJrV2IX2tosaFZt7f4TIs2JgOBRVgLxSYs+QacG/aRhMzsl3g5e8QPCF0j+/drHrtK+DFAambYV2UcbU0Wu/p/KPbXBCEM=</SignatureValue><KeyInfo Id="StrongNameKeyInfo"><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><msrel:RelData xmlns:msrel="http://schemas.microsoft.com/windows/rel/2005/reldata"><r:license xmlns:r="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:as="http://schemas.microsoft.com/windows/pki/2005/Authenticode"><r:grant><as:ManifestInformation Hash="a88a396e5622ecbbe11e90eb929cde39f2ba5e8bf47102a1e4b3b33d90762baa" Description="" Url=""><as:assemblyIdentity name="WordAddIn.dll" version="1.0.0.1" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" type="win32" /></as:ManifestInformation><as:SignedBy /><as:AuthenticodePublisher><as:X509SubjectName>CN=DESKTOP-Q6H7B6L\Administrator</as:X509SubjectName></as:AuthenticodePublisher></r:grant><r:issuer><Signature Id="AuthenticodeSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>cQ+XFSDPlMtb8zYjSzOcudEMYwxmXeQ3CaeHduxOKFA=</DigestValue></Reference></SignedInfo><SignatureValue>juCXR/mToOVD5qLmdm5mhw6eMV82cFLlHY7r6DbZQF7ZJifsMTls4/HMYErjnDskobZDavRqL95lmRGFA2vMs/a6RrD+35/eT1DoFa+6RYmPsKIT4X3v/K9Yejaac9QvMiPpYLzvaPb6VdBaH2i4ZI2vVb9UZFmQtLivy/zrfGs=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==</X509Certificate></X509Data></KeyInfo></Signature></r:issuer></r:license></msrel:RelData></KeyInfo></Signature></asmv1:assembly>
|
||||
<publisherIdentity name="CN=DESKTOP-Q6H7B6L\Administrator" issuerKeyHash="42606c52715d5764c2f9dab5bee6dcd76cc7a67d" /><Signature Id="StrongNameSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>MX0OcTmNbrJm6U9dkilxZisvL1NcqKQwS/O5HzEfK8c=</DigestValue></Reference></SignedInfo><SignatureValue>iUXnLUEc4RK0kHUR+ntcwfk1oyorIMye08lCaa2O2s2oYz9qED7J+sTgz0LaUHKt68VdKfwPPituBAIW2WxQCIqzdn9HT8230+f2Kl8oKk47JRzKW4gDgqOTf+KGF5XvAoXj68RrWTv/uwa8zJ6hiv3YqCHdPClmVi2wkX+y6LI=</SignatureValue><KeyInfo Id="StrongNameKeyInfo"><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><msrel:RelData xmlns:msrel="http://schemas.microsoft.com/windows/rel/2005/reldata"><r:license xmlns:r="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:as="http://schemas.microsoft.com/windows/pki/2005/Authenticode"><r:grant><as:ManifestInformation Hash="c72b1f311fb9f34b30a4a85c532f2f2b667129925d4fe966b26e8d39710e7d31" Description="" Url=""><as:assemblyIdentity name="WordAddIn.dll" version="1.0.0.5" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" type="win32" /></as:ManifestInformation><as:SignedBy /><as:AuthenticodePublisher><as:X509SubjectName>CN=DESKTOP-Q6H7B6L\Administrator</as:X509SubjectName></as:AuthenticodePublisher></r:grant><r:issuer><Signature Id="AuthenticodeSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>gxVlMLILST0l5MXxWhgiyjXV0uin5VywdE0lnEL0sdM=</DigestValue></Reference></SignedInfo><SignatureValue>oOM3Allutv+cuMtJeNLSE+jNzj7daIE1wnvZArg0M+3Y5zcOcNmlbdI5DT/WC8pw+il/Hy2wx/LvA5c5KnHrzeEPDGUWgX4P+Vr+K2EI0fU3qerHIpEHUY16LDwq39IkVAhnzUbxDxhiYMCKiDyTtpzhJS92Yew0fkJwMUgkozw=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==</X509Certificate></X509Data></KeyInfo></Signature></r:issuer></r:license></msrel:RelData></KeyInfo></Signature></asmv1:assembly>
|
||||
Binary file not shown.
@@ -1,21 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<asmv1:assembly xsi:schemaLocation="urn:schemas-microsoft-com:asm.v1 assembly.adaptive.xsd" manifestVersion="1.0" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns="urn:schemas-microsoft-com:asm.v2" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xrml="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:co.v1="urn:schemas-microsoft-com:clickonce.v1" xmlns:co.v2="urn:schemas-microsoft-com:clickonce.v2">
|
||||
<assemblyIdentity name="WordAddIn.vsto" version="1.0.0.1" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
|
||||
<assemblyIdentity name="WordAddIn.vsto" version="1.0.0.5" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" />
|
||||
<description asmv2:publisher="WordAddIn" asmv2:product="WordAddIn" xmlns="urn:schemas-microsoft-com:asm.v1" />
|
||||
<deployment install="false" />
|
||||
<compatibleFrameworks xmlns="urn:schemas-microsoft-com:clickonce.v2">
|
||||
<framework targetVersion="4.7.2" profile="Full" supportedRuntime="4.0.30319" />
|
||||
</compatibleFrameworks>
|
||||
<dependency>
|
||||
<dependentAssembly dependencyType="install" codebase="WordAddIn.dll.manifest" size="14627">
|
||||
<assemblyIdentity name="WordAddIn.dll" version="1.0.0.1" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" type="win32" />
|
||||
<dependentAssembly dependencyType="install" codebase="WordAddIn.dll.manifest" size="14626">
|
||||
<assemblyIdentity name="WordAddIn.dll" version="1.0.0.5" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" type="win32" />
|
||||
<hash>
|
||||
<dsig:Transforms>
|
||||
<dsig:Transform Algorithm="urn:schemas-microsoft-com:HashTransforms.Identity" />
|
||||
</dsig:Transforms>
|
||||
<dsig:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" />
|
||||
<dsig:DigestValue>I1vKeiF4MhMscqYaDoQyOhwm/n7ejlXLDPofhX5hyfw=</dsig:DigestValue>
|
||||
<dsig:DigestValue>JFxcDqTXA+7AjzUW97YoJ63KNke/KOZhQOjlegJDpCk=</dsig:DigestValue>
|
||||
</hash>
|
||||
</dependentAssembly>
|
||||
</dependency>
|
||||
<publisherIdentity name="CN=DESKTOP-Q6H7B6L\Administrator" issuerKeyHash="42606c52715d5764c2f9dab5bee6dcd76cc7a67d" /><Signature Id="StrongNameSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>CPGOApeLUsS3acxFelGymvNVWFUcCBuAjIl9aDADJ+Y=</DigestValue></Reference></SignedInfo><SignatureValue>UXQ+gP7XRrId28tB/3aBIK28hJTR+HvorvhHTQ8XW/hjmvvgaPhinIPSVjH982bbZx3W+KNqKHf00JQjhctW+KC+WGNgELwjeSw1ZBjhWl7RaMO6l9HQi0Tvjt7G2idttnbbZifsKf+mv1ceF5QroYURI0bp32rTH3QavazE/oc=</SignatureValue><KeyInfo Id="StrongNameKeyInfo"><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><msrel:RelData xmlns:msrel="http://schemas.microsoft.com/windows/rel/2005/reldata"><r:license xmlns:r="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:as="http://schemas.microsoft.com/windows/pki/2005/Authenticode"><r:grant><as:ManifestInformation Hash="e6270330687d898c801b081c555855f39ab2517a45cc69b7c4528b97028ef108" Description="" Url=""><as:assemblyIdentity name="WordAddIn.vsto" version="1.0.0.1" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" /></as:ManifestInformation><as:SignedBy /><as:AuthenticodePublisher><as:X509SubjectName>CN=DESKTOP-Q6H7B6L\Administrator</as:X509SubjectName></as:AuthenticodePublisher></r:grant><r:issuer><Signature Id="AuthenticodeSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>uv+SjBSA0U/yAogolAQpL/jvpJgCboKXI7ngcMLDZ6g=</DigestValue></Reference></SignedInfo><SignatureValue>SRkSyDSS8w4TR/CohjvR0SbmQ+N8KVSZC96LfgKyO6YYQ550v5/7lBIJeSlE+AaDooR88jBCkiuy0guBgOk2OiO0aLeRA6wtWtxGmOEX70SiRXJiysEUiMV8Es6MZ82IN/ZIoesbmlzeQP6y+BVg3Bry++gSpW9VkBJgCg0eSAg=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==</X509Certificate></X509Data></KeyInfo></Signature></r:issuer></r:license></msrel:RelData></KeyInfo></Signature></asmv1:assembly>
|
||||
<publisherIdentity name="CN=DESKTOP-Q6H7B6L\Administrator" issuerKeyHash="42606c52715d5764c2f9dab5bee6dcd76cc7a67d" /><Signature Id="StrongNameSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>VkiZ+y4H/hcb1yQg8BG6Ua0d5N/j6D4AcVs6C7CQWrw=</DigestValue></Reference></SignedInfo><SignatureValue>UgENk7DSSlRUuKqa1QdDtbD6Q72PVZmvBGBw3aoqNNvyYG/g0plqQTKO3zHd3cdI6gl7QVZekqmTcIepQcIZJdKe0nUy3uXt2YgAmikvTJDH90YunPZ6VCSgbkkdsxeCXEotjiJVXZ6PoZ3x3DeGAP1vHyhrkPLkWCfZ5eCwP8Y=</SignatureValue><KeyInfo Id="StrongNameKeyInfo"><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><msrel:RelData xmlns:msrel="http://schemas.microsoft.com/windows/rel/2005/reldata"><r:license xmlns:r="urn:mpeg:mpeg21:2003:01-REL-R-NS" xmlns:as="http://schemas.microsoft.com/windows/pki/2005/Authenticode"><r:grant><as:ManifestInformation Hash="bc5a90b00b3a5b71003ee8e3dfe41dad51ba11f02024d71b17fe072efb994856" Description="" Url=""><as:assemblyIdentity name="WordAddIn.vsto" version="1.0.0.5" publicKeyToken="06a92c46db516926" language="neutral" processorArchitecture="msil" xmlns="urn:schemas-microsoft-com:asm.v1" /></as:ManifestInformation><as:SignedBy /><as:AuthenticodePublisher><as:X509SubjectName>CN=DESKTOP-Q6H7B6L\Administrator</as:X509SubjectName></as:AuthenticodePublisher></r:grant><r:issuer><Signature Id="AuthenticodeSignature" xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha256" /><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha256" /><DigestValue>SCNblBMUJn632sbfyxAd48c6urWHFRRJX6rRL4Jv7GQ=</DigestValue></Reference></SignedInfo><SignatureValue>ZP/rFAV9IwnUQGufXeBuRfnAL+jmpfcDongw9mnP2it/XcF+KBree43W2KG5r56z3zQLdx3LNReBBOKT8k6Nwrds3YFuK8MmHLaWU0KQdu29jIF5S9tfgHL2SEJcYY69Q1Bt7WW2btHSRx9CfBt+Y7cgP+7lSUW0A0LtO9mZn8I=</SignatureValue><KeyInfo><KeyValue><RSAKeyValue><Modulus>23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaE=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></KeyValue><X509Data><X509Certificate>MIICDTCCAXagAwIBAgIQJpSlsxfGbpRNgyjD5Q/yDjANBgkqhkiG9w0BAQsFADBFMUMwQQYDVQQDHjoARABFAFMASwBUAE8AUAAtAFEANgBIADcAQgA2AEwAXABBAGQAbQBpAG4AaQBzAHQAcgBhAHQAbwByMB4XDTI2MDEyNDAwMzYxNVoXDTI3MDEyNDA2MzYxNVowRTFDMEEGA1UEAx46AEQARQBTAEsAVABPAFAALQBRADYASAA3AEIANgBMAFwAQQBkAG0AaQBuAGkAcwB0AHIAYQB0AG8AcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA23ghUI75yy8GcLhPT5yA+v2ssRfSW+chNqMI+pq5OAr7RXQJOt/+bHdoT9NoS9GQkvblc7ekoUNShIcN601ZZOPgAxkXaQS+La14Wp8GOwnM1qHNJwgla4VAIYjmCj3CUMXLsxspOFVZKRnB6/idYku4Gzhc7NjRBAwe8qWBXaECAwEAATANBgkqhkiG9w0BAQsFAAOBgQC3GjWMhEFFrn9PGYrQVGNAaibG1sR/eRX3ZWNRjYLhiqbsdeHvm4CX/W0EWd9yZlbaej87XyeUbX+mC3q3yMFVPCRn/W65WYyBPbWTWmSfU9VLKKULAH6WqjS7CqEU3cQSuKcU6nYG4AJu2/FkQJaA82tr3pvcQD28HmOlDeNh0Q==</X509Certificate></X509Data></KeyInfo></Signature></r:issuer></r:license></msrel:RelData></KeyInfo></Signature></asmv1:assembly>
|
||||
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
||||
8be011f4fbaa7c6c228af0678a568daed0783ec95bd0f02e2c98071ea79ec1db
|
||||
5cf5c0a43cd482b679e7e2b7f6131b108dbaf0753d9a650271203dc323e3d3cd
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user