85 lines
8.1 KiB
Python
85 lines
8.1 KiB
Python
from pathlib import Path
|
|
import os, shutil, difflib
|
|
from qt_assistant_parts import PICKER, BRIDGE, METHODS
|
|
|
|
ARTIFACT=Path(__file__).parent
|
|
PANEL='''function reviewAssistantPanel(a){
|
|
const r = a.reviewAssistant || {};
|
|
const recipient = r.recipient || {};
|
|
const busy = Boolean(r.busy);
|
|
const canChoose = r.ready && !busy;
|
|
const canToggle = canChoose && (recipient.id || r.enabled);
|
|
return `<section class="panel panel-pad" style="margin:16px 0"><div class="panel-title"><h2>医疗助理审核提醒</h2><span class="badge ${r.enabled ? "green" : ""}">${r.enabled ? "当前账号已开启" : "当前账号未开启"}</span></div>
|
|
<p>客户会话进入待审核时,由当前托管企业微信账号向指定助理的私人微信发送客户咨询、待发送回复和审核原因。</p>
|
|
<div class="setting-line"><span>托管企微账号(最近核验)</span><strong>${esc(r.accountName || "尚未核验")}</strong>${r.accountId ? `<small>${esc(r.accountId)}</small>` : ""}</div>
|
|
<div class="setting-line"><span>助理私人微信</span><div style="flex:1;text-align:right"><strong>${esc(recipient.name || "尚未选择")}</strong>${recipient.label ? `<div class="tiny">${esc(recipient.label)}</div>` : ""}</div></div>
|
|
<div class="toolbar" style="margin:12px 0;flex-wrap:wrap"><button class="btn small" data-action="review-assistant-refresh" ${!r.supported || busy ? "disabled" : ""}>${icon("refresh")}${busy ? "正在核验…" : "读取当前账号"}</button>
|
|
<button class="btn small blue" data-action="review-assistant-choose" ${canChoose ? "" : "disabled"}>${recipient.id ? "更换助理联系人" : "选择助理联系人"}</button>
|
|
<span style="margin-left:auto">开启审核提醒</span><button class="switch ${r.enabled ? "on" : ""}" role="switch" aria-label="开启医疗助理审核提醒" aria-checked="${Boolean(r.enabled)}" data-action="review-assistant-toggle" ${canToggle ? "" : "disabled"}></button></div>
|
|
${r.reason ? `<p class="tiny" style="color:var(--orange)">${esc(r.reason)}</p>` : ""}
|
|
${r.message ? `<p class="tiny" role="status">${esc(r.message)}</p>` : ""}
|
|
<p class="tiny">设置按企微账号分别保存。选择或更换联系人后默认关闭,确认收件人后再开启;同名联系人按列表中的联系编号区分。</p>
|
|
<p class="tiny">人工用同一企业微信账号直接回复客户后结束本次审核;后续自动回复仍逐条风控。此识别以账号发出的消息为准,不区分手机或电脑。在助理私人微信回复提醒,不会解除客户审核。</p>
|
|
<details style="margin-top:12px"><summary style="cursor:pointer">提醒内容示例(只读,不会发送)</summary><pre style="white-space:pre-wrap;word-break:break-word;margin:12px 0 0;font:inherit;font-size:12px">${esc(r.preview || "请先读取当前账号")}</pre></details></section>`;
|
|
}
|
|
|
|
'''
|
|
|
|
for root in (Path('C:/wechat_rpa'),Path('C:/kefu/wechat_rpa')):
|
|
for relative in ('wechat_gui_qt.py','assets/ui/console-app.js'):
|
|
p=root/relative;backup=root/'backups/review-assistant-20260917'/relative
|
|
backup.parent.mkdir(parents=True,exist_ok=True)
|
|
if not backup.exists():shutil.copyfile(p,backup)
|
|
shutil.copyfile(ARTIFACT/'review_assistant_contacts.py',root/'review_assistant_contacts.py')
|
|
p=root/'wechat_gui_qt.py';old=p.read_text(encoding='utf-8');s=old
|
|
assert 'class ReviewAssistantPicker' not in s
|
|
s=s.replace(' QListWidget,\n',' QListWidget,\n QListWidgetItem,\n',1)
|
|
s=s.replace('class MainWindow(QMainWindow):',PICKER+'class MainWindow(QMainWindow):',1)
|
|
s=s.replace(' wechatDatabaseLoaded = Signal(object)',' reviewAssistantUpdated = Signal(object)\n wechatDatabaseLoaded = Signal(object)',1)
|
|
# Python helpers use native protocol capability in either visual or protocol mode.
|
|
methods=METHODS.replace(' if self.runtime_settings.get("reply_transport", "visual") != "protocol":\n return False, "请切换到协议模式后配置医疗助理审核提醒"\n','')
|
|
s=s.replace(' def _console_automation_state(self, wecom) -> dict:',methods+' def _console_automation_state(self, wecom) -> dict:',1)
|
|
pos=s.index(' def _console_automation_state(');end=s.index(' def _console_system_state(',pos)
|
|
section=s[pos:end].replace(' return {\n',' return {\n "reviewAssistant": self._review_assistant_console_state(),\n',1)
|
|
s=s[:pos]+section+s[end:]
|
|
s=s.replace(' @Slot(str, result=str)\n def saveAutomationSettings',BRIDGE+' @Slot(str, result=str)\n def saveAutomationSettings',1)
|
|
s=s.replace(' self.wechatDatabaseLoaded.connect(self._wechat_database_loaded)', ' self.reviewAssistantUpdated.connect(self._review_assistant_updated)\n self.wechatDatabaseLoaded.connect(self._wechat_database_loaded)',1)
|
|
s=s.replace(' "notify_manual": True,',' "notify_manual": True,\n "review_assistant_accounts": {},',1)
|
|
if root==Path('C:/kefu/wechat_rpa'):
|
|
start=s.index(' def save_runtime_settings(self, settings: dict | None = None)')
|
|
end=s.index(' def _broadcast_save_result(',start)
|
|
section=s[start:end]
|
|
section=section.replace(' -> None:', ' -> bool:',1)
|
|
section=section.replace(' self.runtime_settings = merged',' previous_settings = getattr(self, "runtime_settings", {})\n self.runtime_settings = merged',1)
|
|
section=section.replace(' self.append_log(f"运行参数保存失败:{exc}", "err")',' self.append_log(f"运行参数保存失败:{exc}", "err")\n self.runtime_settings = previous_settings\n return False',1)
|
|
section=section.replace(' self._broadcast_save_result(True, "设置已保存并生效")',' self._broadcast_save_result(True, "设置已保存并生效")\n return True',1)
|
|
s=s[:start]+section+s[end:]
|
|
compile(s,str(p),'exec');t=p.with_suffix('.assistant.tmp');t.write_text(s,encoding='utf-8');os.replace(t,p)
|
|
label='production' if root==Path('C:/wechat_rpa') else 'source'
|
|
(ARTIFACT/f'{label}-qt.diff').write_text(''.join(difflib.unified_diff(old.splitlines(True),s.splitlines(True),fromfile='before',tofile=str(p))),encoding='utf-8')
|
|
p=root/'assets/ui/console-app.js';old=p.read_text(encoding='utf-8');s=old
|
|
assert 'function reviewAssistantPanel' not in s
|
|
s=s.replace('automationView = function(){',PANEL+'automationView = function(){',1)
|
|
start=s.index('automationView = function(){');end=s.index('\nlogsView = function',start)
|
|
section=s[start:end]
|
|
section=section.replace('</div><div class="automation-top">','</div>${reviewAssistantPanel(a)}<div class="automation-top">',1)
|
|
if root==Path('C:/wechat_rpa'):
|
|
section=section.replace(' <section class="panel panel-pad"><div class="panel-title"><h2>${ready?', ' ${reviewAssistantPanel(a)}<section class="panel panel-pad"><div class="panel-title"><h2>${ready?',1)
|
|
section=section.replace('发送结果不明确时暂停监听并保留记录,防止重复发送。','发送结果不明确时保留该会话记录待核对,其他会话继续监听,防止重复发送。')
|
|
assert '${reviewAssistantPanel(a)}' in section
|
|
s=s[:start]+section+s[end:]
|
|
actions=''' "review-assistant-refresh": () => callBridge("refreshReviewAssistant"),
|
|
"review-assistant-choose": () => callBridge("chooseReviewAssistant"),
|
|
"review-assistant-toggle": () => {
|
|
const r = (STATE.automation || {}).reviewAssistant || {};
|
|
if (!r.ready || r.busy) return;
|
|
callBridgeWithFeedback("setReviewAssistantEnabled", [JSON.stringify({expected_account: r.accountId, enabled: !r.enabled})], {
|
|
pending: "正在核对当前账号与助理联系人…",
|
|
});
|
|
},
|
|
'''
|
|
s=s.replace(' "save-automation": () => callBridgeWithFeedback(',actions+' "save-automation": () => callBridgeWithFeedback(',1)
|
|
t=p.with_suffix('.assistant.tmp');t.write_text(s,encoding='utf-8');os.replace(t,p)
|
|
(ARTIFACT/f'{label}-js.diff').write_text(''.join(difflib.unified_diff(old.splitlines(True),s.splitlines(True),fromfile='before',tofile=str(p))),encoding='utf-8')
|
|
print(label, 'UI installed')
|