This commit is contained in:
HuangHai
2026-01-26 09:15:45 +08:00
parent 6b6a32c902
commit d9bb1d1f83
17 changed files with 70 additions and 358 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -4,6 +4,7 @@ import time
import logging
import sys
import os
import WxUtil
# 添加项目根目录到 sys.path
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -29,11 +30,8 @@ def open_chat(target_name="糖豆爸爸"):
logger.info(f"开始执行 T1: 打开微信并进入 '{target_name}' 的对话框...")
# 连接设备
try:
d = u2.connect()
logger.info(f"设备连接成功: {d.info.get('serial')}")
except Exception as e:
logger.error(f"设备连接失败: {e}")
d = WxUtil.connect_device()
if not d:
return
# 1. 启动微信

View File

@@ -1,14 +1,15 @@
# coding=utf-8
import uiautomator2 as u2
import time
import logging
import sys
import os
import asyncio
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 analyze_chat_image
# 配置日志
@@ -26,14 +27,11 @@ logging.basicConfig(
)
logger = logging.getLogger("T2_GetHistory")
def get_history(target_name="对方"):
async def get_history(target_name="对方"):
logger.info("开始执行 T2: 获取当前屏幕对话历史...")
try:
d = u2.connect()
logger.info(f"设备连接成功: {d.info.get('serial')}")
except Exception as e:
logger.error(f"设备连接失败: {e}")
d = WxUtil.connect_device()
if not d:
return
# 截图
@@ -53,8 +51,12 @@ def get_history(target_name="对方"):
analyzed_path = os.path.join(screenshot_dir, analyzed_filename)
# 调用 WxUtil 中的分析函数
dialogue_log = analyze_chat_image(save_path, analyzed_path, target_name=target_name)
dialogue_log, input_box = await analyze_chat_image(save_path, analyzed_path, device=d, target_name=target_name)
if dialogue_log == "VOICE_CONVERTING":
logger.info("检测到语音正在转文字T2 任务暂停。")
return
logger.info("✅ T2 执行完成。历史对话如下:")
if dialogue_log:
for log in dialogue_log:
@@ -66,4 +68,4 @@ def get_history(target_name="对方"):
logger.error(f"❌ T2 执行失败: {e}")
if __name__ == "__main__":
get_history()
asyncio.run(get_history())

View File

@@ -1,5 +1,4 @@
# coding=utf-8
import uiautomator2 as u2
import time
import logging
import sys
@@ -11,6 +10,7 @@ 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 find_input_box_center
# 配置日志
@@ -31,11 +31,8 @@ logger = logging.getLogger("T3_MarkInputBox")
def mark_input_box():
logger.info("开始执行 T3: 标识输入框位置...")
try:
d = u2.connect()
logger.info(f"设备连接成功: {d.info.get('serial')}")
except Exception as e:
logger.error(f"设备连接失败: {e}")
d = WxUtil.connect_device()
if not d:
return
screenshot_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Screenshots")

View File

@@ -1,5 +1,4 @@
# coding=utf-8
import uiautomator2 as u2
import time
import logging
import sys
@@ -11,6 +10,7 @@ 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 find_input_box_center, perform_input_action, analyze_chat_image, clean_screenshots_dir, is_in_chat_interface
from Util.LlmUtil import get_llm_response
@@ -37,8 +37,9 @@ async def generate_and_input():
try:
# 1. 连接设备
d = u2.connect()
logger.info(f"设备连接成功: {d.info.get('serial')}")
d = WxUtil.connect_device()
if not d:
return
# 检查界面状态
if not is_in_chat_interface(d):
@@ -54,14 +55,14 @@ async def generate_and_input():
d.screenshot(tmp_shot)
analyzed_shot = os.path.join(screenshot_dir, "t4_temp_history_analyzed.jpg")
dialogue_log = analyze_chat_image(tmp_shot, analyzed_shot)
dialogue_log, input_box = await analyze_chat_image(tmp_shot, analyzed_shot, device=d)
# 语音转文字处理
if dialogue_log == "VOICE_CONVERTING":
logger.info("检测到语音正在转文字,等待 3 秒后重新截图分析...")
await asyncio.sleep(3)
d.screenshot(tmp_shot)
dialogue_log = analyze_chat_image(tmp_shot, analyzed_shot)
dialogue_log, input_box = await analyze_chat_image(tmp_shot, analyzed_shot, device=d)
history_text = ""
if dialogue_log:

View File

@@ -6,13 +6,13 @@ import sys
import time
import cv2
import uiautomator2 as u2
# 添加项目根目录到 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 get_vlm_analysis
from Util.EasyOcrKit import EasyOcrKit
@@ -24,11 +24,8 @@ async def main():
logger.info("🚀 T6 VLM 语音坐标调试工具启动...")
# 连接设备
try:
d = u2.connect()
logger.info(f"设备已连接: {d.info.get('serial')}")
except Exception as e:
logger.error(f"设备连接失败: {e}")
d = WxUtil.connect_device()
if not d:
return
# 截图目录

View File

@@ -4,21 +4,24 @@ import sys
import time
import cv2
import uiautomator2 as u2
# 添加项目根目录到 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 find_all_template_matches
def run_cv_debug():
# 1. 拍照 (获取当前设备屏幕)
print("📸 正在连接设备并截取屏幕...")
d = WxUtil.connect_device()
if not d:
return
try:
d = u2.connect()
screenshot_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Screenshots")
if not os.path.exists(screenshot_dir):
os.makedirs(screenshot_dir)

View File

@@ -6,8 +6,6 @@ import sys
import time
from datetime import datetime
import uiautomator2 as u2
# 添加项目根目录到 sys.path
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if project_root not in sys.path:
@@ -15,6 +13,7 @@ if project_root not in sys.path:
from Util import Win32Patch
from WeiXin import WxUtil
from WeiXin.WxUtil import perform_input_action, clean_screenshots_dir, find_template_match, find_all_template_matches
from Util.LlmUtil import get_llm_response
from Util.EasyOcrKit import EasyOcrKit
@@ -54,7 +53,9 @@ CHECK_INTERVAL = 5 # 检查频率 (秒)
class ChatBot:
def __init__(self):
self.d = u2.connect()
self.d = WxUtil.connect_device()
if not self.d:
raise Exception("无法连接到设备,任务终止")
self.last_message_text = ""
self.last_processed_msg = None # 记录上一条已处理/回复过的对方消息内容
self.screenshot_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Screenshots")

View File

@@ -25,6 +25,23 @@ ocr_kit = EasyOcrKit()
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger("WxUtil")
def connect_device():
"""
连接设备并返回设备对象,同时打印详细的设备信息
"""
try:
d = u2.connect()
# 获取可靠的序列号
device_serial = d.serial if hasattr(d, 'serial') else "未知"
logger.info(f"设备连接成功: {device_serial}")
# 获取并打印详细设备信息
device_info = d.device_info
logger.info(f"详细设备信息: 品牌={device_info.get('brand')}, 型号={device_info.get('model')}, SDK={device_info.get('sdk')}")
return d
except Exception as e:
logger.error(f"设备连接失败: {e}")
return None
async def get_vlm_json(image_path, prompt):
"""
@@ -249,7 +266,7 @@ async def analyze_chat_image(image_path, output_path, device=None, target_name="
unconverted_voices = [unconverted_voices[-1]]
# 使用传入的 device 或创建新连接
d = device if device else u2.connect()
d = device if device else connect_device()
for voice in unconverted_voices:
vx, vy = voice['coordinates']

View File

@@ -472,105 +472,6 @@ namespace WordAddIn
}
}
/// <summary>
/// 【智能扩写】按钮点击事件处理。
/// 功能描述:
/// 1. 获取用户选中的文本(或剪贴板内容)。
/// 2. 弹出选项窗体,允许用户输入自定义扩写提示词。
/// 3. 调用 AI 扩写接口,传入原文、用户提示和全文上下文。
/// 4. 将 AI 生成的扩写内容替换原有选区,或插入到光标位置。
/// </summary>
/// <param name="control">触发事件的 Ribbon 控件</param>
public async void OnExpandClick(Office.IRibbonControl control)
{
if (!EnsureLoggedIn()) return;
LoadingForm loading = null;
try
{
var selection = Globals.ThisAddIn.Application.Selection;
// 1. 判断是否有选中文本
// wdSelectionNormal 表示有选区,否则检查 Text 属性
bool hasSelection = selection.Type == Word.WdSelectionType.wdSelectionNormal ||
(!string.IsNullOrEmpty(selection.Text) && selection.Text.Length > 1);
if (!hasSelection)
{
// 2. 尝试从剪贴板读取
IDataObject data = Clipboard.GetDataObject();
if (data != null && data.GetDataPresent(DataFormats.Text))
{
string clipboardText = (string)data.GetData(DataFormats.Text);
if (string.IsNullOrWhiteSpace(clipboardText))
{
MessageBox.Show("剪贴板中内容为空,请先选择一段需要扩写的文字,或者复制文字到剪贴板。");
return;
}
}
else
{
MessageBox.Show("请先选择一段需要扩写的文字,或者复制文字到剪贴板。");
return;
}
}
string originalText;
if (hasSelection)
{
originalText = selection.Text.Trim();
}
else
{
// 使用剪贴板内容
IDataObject data = Clipboard.GetDataObject();
originalText = (string)data.GetData(DataFormats.Text);
}
if (string.IsNullOrWhiteSpace(originalText))
{
MessageBox.Show("获取文字失败,请重试。");
return;
}
// 3. 弹出选项设置窗体,让用户输入自定义扩写提示词
string customPrompt = null;
using (var optionsForm = new ExpandOptionsForm())
{
if (optionsForm.ShowDialog() != DialogResult.OK)
{
return; // 用户取消
}
customPrompt = optionsForm.SelectedPrompt;
}
loading = new LoadingForm("AI 正在扩写中,请稍候...");
loading.Show();
loading.Refresh();
// 4. 在后台线程运行 AI 请求,防止阻塞 UI
// 传入 GetCombinedContext() 以利用文档背景和参考资料
string expandedText = await Task.Run(() => _aiService.ExpandText(originalText, customPrompt, GetCombinedContext()));
if (!string.IsNullOrEmpty(expandedText))
{
// 5. 智能插入:尝试保持格式
SmartInsert(selection, expandedText);
}
}
catch (Exception ex)
{
MessageBox.Show("扩写失败: " + ex.Message);
}
finally
{
if (loading != null)
{
loading.CloseSafe();
loading.Dispose();
}
}
}
/// <summary>
/// 【素材写作】按钮点击事件处理。
@@ -674,8 +575,7 @@ namespace WordAddIn
/// <summary>
/// 智能插入辅助方法。
/// 使用 TypeText 方法插入文本,以保持当前光标处的格式
/// 如果 TypeText 失败,则回退到直接赋值 Text 属性。
/// 插入文本并强制应用“正文”样式,确保格式统一
/// </summary>
/// <param name="selection">Word 选区对象</param>
/// <param name="text">要插入的文本</param>
@@ -683,8 +583,19 @@ namespace WordAddIn
{
try
{
// 使用 TypeText 插入,让 Word 自动应用当前光标位置的格式
selection.TypeText(text);
// 获取当前 Range
Word.Range range = selection.Range;
// 插入文本
range.Text = text;
// 强制应用“正文”样式 (wdStyleNormal)
// 这样可以避免插入的内容受到当前光标位置样式(如标题样式)的影响
range.set_Style(Word.WdBuiltinStyle.wdStyleNormal);
// 将光标移动到插入内容之后,方便用户继续操作
range.Collapse(Word.WdCollapseDirection.wdCollapseEnd);
range.Select();
}
catch (Exception ex)
{
@@ -1060,6 +971,9 @@ namespace WordAddIn
var selection = Globals.ThisAddIn.Application.Selection;
var inlineShape = selection.InlineShapes.AddPicture(FileName: tempPath, LinkToFile: false, SaveWithDocument: true);
// 强制将图片所在的段落设为“正文”样式
inlineShape.Range.Paragraphs[1].set_Style(Word.WdBuiltinStyle.wdStyleNormal);
// 简单的图片调整
inlineShape.LockAspectRatio = Microsoft.Office.Core.MsoTriState.msoTrue;
if (inlineShape.Width > 400)

View File

@@ -12,7 +12,6 @@
<button id="btnReferenceGenerate" label="素材写作" size="large" onAction="OnReferenceGenerateClick" imageMso="ReviewCompareTwoVersions" />
<button id="btnPolish" label="润色加工" size="large" onAction="OnPolishClick" imageMso="Spelling" />
<button id="btnElevate" label="深度升华" size="large" onAction="OnElevateClick" imageMso="SmartArtInsert" />
<button id="btnExpand" label="智能扩写" size="large" onAction="OnExpandClick" imageMso="ReviewNewComment" />
<button id="btnFormat" label="格式美化" imageMso="FontColorCycle" size="large" onAction="OnFormatClick" />
<button id="btnImage" label="生成配图" size="large" onAction="OnImageClick" imageMso="PictureInsertFromFile" />
</group>

View File

@@ -41,38 +41,6 @@ namespace WordAddIn
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {ApiKey}");
}
/// <summary>
/// 智能扩写:将简短的文本扩充为细节丰富、逻辑通顺的段落。
/// </summary>
/// <param name="originalText">需要扩写的原文</param>
/// <param name="customInstruction">用户自定义的扩写要求(如“更幽默一点”、“强调数据支持”等),可选</param>
/// <param name="documentContext">文档全文上下文(摘要、风格等),用于保持文风一致,可选</param>
/// <returns>扩写后的纯文本内容</returns>
public async Task<string> ExpandText(string originalText, string customInstruction = null, string documentContext = null)
{
// 构造 User 指令
string instruction = string.IsNullOrWhiteSpace(customInstruction)
? "请将以下内容扩写成一段正式、通顺的段落,保持原意但增加细节"
: customInstruction;
// 构造 System Prompt
// 强制要求不使用 Markdown方便直接插入 Word
string systemPrompt = "你是一个专业的文档编辑助手。请直接输出扩写后的纯文本内容不要使用任何Markdown标记如#、**、- 等)。直接使用自然段落。";
// 注入文档上下文(如果有)
if (!string.IsNullOrWhiteSpace(documentContext))
{
systemPrompt += $"\n\n【全文背景与排版规则】你已通读全文以下是文档的整体上下文信息和排版规则请在扩写时保持与全文主题和风格的一致性并参考排版规则生成对应的层级结构\n{documentContext}";
}
var messages = new List<Message>
{
new Message { role = "system", content = systemPrompt },
new Message { role = "user", content = $"{instruction}\n\n{originalText}" }
};
return await GetChatCompletion(messages);
}
/// <summary>
/// 润色加工:优化文本的语言表达,提升流畅度和专业性。

View File

@@ -1,182 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WordAddIn
{
/// <summary>
/// 智能扩写设置窗体。
/// 功能描述:
/// 1. 提供多种预设的扩写模板(如"商务正式"、"细腻情感"等)。
/// 2. 允许用户修改预设模板或输入完全自定义的扩写要求。
/// 3. 返回用户的最终选择 (SelectedPrompt) 给调用方。
/// </summary>
public class ExpandOptionsForm : Form
{
/// <summary>
/// 用户最终确认的扩写要求。
/// </summary>
public string SelectedPrompt { get; private set; }
private ComboBox cmbTemplates;
private TextBox txtCustomPrompt;
private Button btnOk;
private Button btnCancel;
private Label lblTemplate;
private Label lblCustom;
private readonly Dictionary<string, string> _templates = new Dictionary<string, string>
{
{ "默认扩写", "请将以下内容扩写成一段正式、通顺的段落,保持原意但增加细节。" },
{ "细腻情感", "请对选中文本进行扩写,重点从心理活动、情绪变化和感官体验三个维度进行细腻描写。" },
{ "商务正式", "请将选中文本改写为商务职场风格,使用专业术语,语气正式且客观。" },
{ "生动故事", "请将选中文本扩写为一个生动的小故事,增加对话和场景描写,使其引人入胜。" },
{ "学术严谨", "请将选中文本扩写为学术风格,逻辑严密,用词精准,适合用于论文或报告。" },
{ "诗意唯美", "请用优美、诗意的语言扩写选中文本,多用修辞手法,营造唯美的意境。" }
};
public ExpandOptionsForm()
{
InitializeComponent();
InitializeTemplates();
}
/// <summary>
/// 初始化窗体组件。
/// </summary>
private void InitializeComponent()
{
this.lblTemplate = new System.Windows.Forms.Label();
this.lblCustom = new System.Windows.Forms.Label();
this.cmbTemplates = new System.Windows.Forms.ComboBox();
this.txtCustomPrompt = new System.Windows.Forms.TextBox();
this.btnOk = new System.Windows.Forms.Button();
this.btnCancel = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// lblTemplate
//
this.lblTemplate.AutoSize = true;
this.lblTemplate.Location = new System.Drawing.Point(20, 20);
this.lblTemplate.Name = "lblTemplate";
this.lblTemplate.Size = new System.Drawing.Size(83, 12);
this.lblTemplate.TabIndex = 0;
this.lblTemplate.Text = "选择预设模板:";
//
// lblCustom
//
this.lblCustom.AutoSize = true;
this.lblCustom.Location = new System.Drawing.Point(20, 80);
this.lblCustom.Name = "lblCustom";
this.lblCustom.Size = new System.Drawing.Size(107, 12);
this.lblCustom.TabIndex = 2;
this.lblCustom.Text = "扩写要求 (可修改):";
//
// cmbTemplates
//
this.cmbTemplates.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cmbTemplates.FormattingEnabled = true;
this.cmbTemplates.Location = new System.Drawing.Point(20, 45);
this.cmbTemplates.Name = "cmbTemplates";
this.cmbTemplates.Size = new System.Drawing.Size(440, 20);
this.cmbTemplates.TabIndex = 1;
this.cmbTemplates.SelectedIndexChanged += new System.EventHandler(this.CmbTemplates_SelectedIndexChanged);
//
// txtCustomPrompt
//
this.txtCustomPrompt.Location = new System.Drawing.Point(20, 105);
this.txtCustomPrompt.Multiline = true;
this.txtCustomPrompt.Name = "txtCustomPrompt";
this.txtCustomPrompt.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtCustomPrompt.Size = new System.Drawing.Size(440, 120);
this.txtCustomPrompt.TabIndex = 3;
//
// btnOk
//
this.btnOk.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnOk.Location = new System.Drawing.Point(260, 250);
this.btnOk.Name = "btnOk";
this.btnOk.Size = new System.Drawing.Size(90, 35);
this.btnOk.TabIndex = 4;
this.btnOk.Text = "开始扩写";
this.btnOk.UseVisualStyleBackColor = true;
this.btnOk.Click += new System.EventHandler(this.BtnOk_Click);
//
// btnCancel
//
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(370, 250);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(90, 35);
this.btnCancel.TabIndex = 5;
this.btnCancel.Text = "取消";
this.btnCancel.UseVisualStyleBackColor = true;
//
// ExpandOptionsForm
//
this.AcceptButton = this.btnOk;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(500, 311);
this.Controls.Add(this.btnCancel);
this.Controls.Add(this.btnOk);
this.Controls.Add(this.txtCustomPrompt);
this.Controls.Add(this.lblCustom);
this.Controls.Add(this.cmbTemplates);
this.Controls.Add(this.lblTemplate);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ExpandOptionsForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "智能扩写设置";
this.ResumeLayout(false);
this.PerformLayout();
}
/// <summary>
/// 初始化模板下拉列表。
/// </summary>
private void InitializeTemplates()
{
foreach (var key in _templates.Keys)
{
cmbTemplates.Items.Add(key);
}
if (cmbTemplates.Items.Count > 0)
{
cmbTemplates.SelectedIndex = 0;
}
}
/// <summary>
/// 模板选择改变事件。
/// 当用户选择不同模板时,自动填充对应的 Prompt 到文本框中。
/// </summary>
private void CmbTemplates_SelectedIndexChanged(object sender, EventArgs e)
{
if (cmbTemplates.SelectedItem != null && _templates.ContainsKey(cmbTemplates.SelectedItem.ToString()))
{
txtCustomPrompt.Text = _templates[cmbTemplates.SelectedItem.ToString()];
}
}
/// <summary>
/// 确定按钮点击事件。
/// 验证输入有效性,并保存结果。
/// </summary>
private void BtnOk_Click(object sender, EventArgs e)
{
SelectedPrompt = txtCustomPrompt.Text.Trim();
if (string.IsNullOrEmpty(SelectedPrompt))
{
MessageBox.Show("扩写要求不能为空。", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
this.DialogResult = DialogResult.None; // Prevent closing
return;
}
}
}
}

View File

@@ -245,9 +245,6 @@
<Compile Include="Models.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="ExpandOptionsForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ImageOptionsForm.cs">
<SubType>Form</SubType>
</Compile>