fix(bridge): isolate DOM observation scope and strip UI noise (TypeScript declarations, metrics) from Discord pending approval embeds
This commit is contained in:
48
bot.py
48
bot.py
@@ -724,15 +724,55 @@ class GravityBot(commands.Bot):
|
|||||||
desc_parts = []
|
desc_parts = []
|
||||||
if header:
|
if header:
|
||||||
desc_parts.append(header)
|
desc_parts.append(header)
|
||||||
desc_parts.append(f"**명령:** `{request.command[:200]}`")
|
|
||||||
|
# Clean command text (remove "Running2" artifacts → "Running 2")
|
||||||
|
cmd_text = request.command[:200]
|
||||||
|
import re
|
||||||
|
cmd_text = re.sub(r'Running(\d)', r'Running \1', cmd_text)
|
||||||
|
desc_parts.append(f"**명령:** `{cmd_text}`")
|
||||||
|
|
||||||
if buttons:
|
if buttons:
|
||||||
btn_names = [b.get("text", "?") for b in buttons]
|
btn_names = [b.get("text", "?") for b in buttons]
|
||||||
desc_parts.append(f"**선택지:** {' / '.join(btn_names)}")
|
desc_parts.append(f"**선택지:** {' / '.join(btn_names)}")
|
||||||
if request.description:
|
|
||||||
desc_parts.append(request.description[:500])
|
# Clean description: strip noise headers and garbage
|
||||||
|
desc_raw = request.description or ""
|
||||||
|
# Remove old-style headers
|
||||||
|
desc_raw = re.sub(r'\[AI 본문 요약\]\s*', '', desc_raw)
|
||||||
|
desc_raw = re.sub(r'\[결행 명령\]\s*', '', desc_raw)
|
||||||
|
# Remove lines that are clearly noise
|
||||||
|
desc_lines = desc_raw.split('\n')
|
||||||
|
clean_desc_lines = []
|
||||||
|
for dline in desc_lines:
|
||||||
|
dline_stripped = dline.strip()
|
||||||
|
if not dline_stripped:
|
||||||
|
continue
|
||||||
|
# Skip UI artifacts
|
||||||
|
if dline_stripped in ('chevron_right', 'chevron_left', 'close', 'check',
|
||||||
|
'content_copy', 'expand_more', 'expand_less',
|
||||||
|
'Show more', 'Show less', 'Copy', 'Edit', 'Copied!'):
|
||||||
|
continue
|
||||||
|
# Skip "Thought for Xs"
|
||||||
|
if re.match(r'^Thought for \d+', dline_stripped):
|
||||||
|
continue
|
||||||
|
# Skip TypeScript declarations and file paths
|
||||||
|
if re.match(r'^(declare|import|export)\s+(class|function|interface|type|enum|const)', dline_stripped):
|
||||||
|
continue
|
||||||
|
if re.search(r'\.ts:\d+:', dline_stripped):
|
||||||
|
continue
|
||||||
|
if re.search(r'extension.*src.*sdk', dline_stripped, re.IGNORECASE):
|
||||||
|
continue
|
||||||
|
clean_desc_lines.append(dline_stripped)
|
||||||
|
|
||||||
|
clean_desc = '\n'.join(clean_desc_lines).strip()
|
||||||
|
if clean_desc and len(clean_desc) > 3:
|
||||||
|
# Truncate and wrap in code block for readability
|
||||||
|
if len(clean_desc) > 300:
|
||||||
|
clean_desc = clean_desc[:300] + '…'
|
||||||
|
desc_parts.append(f"```\n{clean_desc}\n```")
|
||||||
|
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
title="⚠️ 승인 요청",
|
title=f"⚠️ 승인 요청 — {request.step_type or 'action'}",
|
||||||
description="\n".join(desc_parts),
|
description="\n".join(desc_parts),
|
||||||
color=discord.Color.orange(),
|
color=discord.Color.orange(),
|
||||||
timestamp=datetime.now(timezone.utc),
|
timestamp=datetime.now(timezone.utc),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export function generateApprovalObserverScript(_port: number): string {
|
export function generateApprovalObserverScript(_port: number): string {
|
||||||
return `
|
return `
|
||||||
// ── Gravity Bridge v5: Context-First DOM Extraction ──
|
// ── Gravity Bridge v6: Clean Context Extraction ──
|
||||||
(function(){
|
(function(){
|
||||||
'use strict';
|
'use strict';
|
||||||
var BASE='',_obs=false,_sent={},_ready=false;
|
var BASE='',_obs=false,_sent={},_ready=false;
|
||||||
@@ -9,7 +9,7 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
var CLEANUP_MS=300000;
|
var CLEANUP_MS=300000;
|
||||||
|
|
||||||
function log(m){console.log('[GB Observer] '+m);}
|
function log(m){console.log('[GB Observer] '+m);}
|
||||||
log('v5 Script loaded — Context-First Tailored Extraction');
|
log('v6 Script loaded — Clean Context Extraction');
|
||||||
|
|
||||||
// React-Compatible Synthetic Clicker
|
// React-Compatible Synthetic Clicker
|
||||||
function dispatchReactClick(el){
|
function dispatchReactClick(el){
|
||||||
@@ -25,16 +25,58 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Noise filter: lines that are UI artifacts, not real content ──
|
||||||
|
var NOISE_PATTERNS = [
|
||||||
|
/^chevron_right$/i,
|
||||||
|
/^chevron_left$/i,
|
||||||
|
/^arrow_/i,
|
||||||
|
/^Thought for \\\\d+/i,
|
||||||
|
/^expand_/i,
|
||||||
|
/^close$/i,
|
||||||
|
/^more_/i,
|
||||||
|
/^content_copy$/i,
|
||||||
|
/^check$/i,
|
||||||
|
/^\\\\d+ lines?$/i,
|
||||||
|
/^Show more$/i,
|
||||||
|
/^Show less$/i,
|
||||||
|
/^Copy$/i,
|
||||||
|
/^Edit$/i,
|
||||||
|
/^Copied!$/i,
|
||||||
|
/^\\\\s*$/,
|
||||||
|
/^declare\\\\s+(class|function|interface|type|enum|const|var|let)\\\\s/, // TypeScript declarations
|
||||||
|
/^(import|export|from)\\\\s/, // JS imports
|
||||||
|
/^\\\\s*[{}()\\\\[\\\\];]\\\\s*$/, // lone brackets
|
||||||
|
/\\\\.ts:\\\\d+:/, // file:line references
|
||||||
|
/extension.*src.*sdk/i, // SDK file paths
|
||||||
|
];
|
||||||
|
function isNoiseLine(line) {
|
||||||
|
if (!line || line.trim().length < 2) return true;
|
||||||
|
var trimmed = line.trim();
|
||||||
|
for (var i = 0; i < NOISE_PATTERNS.length; i++) {
|
||||||
|
if (NOISE_PATTERNS[i].test(trimmed)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function cleanLines(text) {
|
||||||
|
if (!text) return '';
|
||||||
|
var lines = text.split('\\\\n');
|
||||||
|
var clean = [];
|
||||||
|
for (var i = 0; i < lines.length; i++) {
|
||||||
|
if (!isNoiseLine(lines[i])) clean.push(lines[i].trim());
|
||||||
|
}
|
||||||
|
return clean.join('\\\\n').trim();
|
||||||
|
}
|
||||||
|
|
||||||
function cleanButtonText(btn) {
|
function cleanButtonText(btn) {
|
||||||
if (!btn) return '';
|
if (!btn) return '';
|
||||||
var clone = btn.cloneNode(true);
|
var clone = btn.cloneNode(true);
|
||||||
var icons = clone.querySelectorAll('.google-symbols, .codicon');
|
var icons = clone.querySelectorAll('.google-symbols, .codicon, [class*="icon"], svg');
|
||||||
for(var i=0; i<icons.length; i++) {
|
for(var i=0; i<icons.length; i++) {
|
||||||
if(icons[i].parentNode) icons[i].parentNode.removeChild(icons[i]);
|
if(icons[i].parentNode) icons[i].parentNode.removeChild(icons[i]);
|
||||||
}
|
}
|
||||||
var tr = clone.querySelector('.truncate');
|
var tr = clone.querySelector('.truncate');
|
||||||
var txt = (tr ? tr.textContent : clone.textContent) || '';
|
var txt = (tr ? tr.textContent : clone.textContent) || '';
|
||||||
return txt.trim().replace(/^[\s\u200B-\u200D\uFEFF\u00A0]+/, '').replace(/(Alt|Ctrl|Shift|Meta)\\\\+.*/i,'').trim();
|
return txt.trim().replace(/^[\\\\s\\\\u200B-\\\\u200D\\\\uFEFF\\\\u00A0]+/, '').replace(/(Alt|Ctrl|Shift|Meta)\\\\+.*/i,'').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
function btnId(b,type){
|
function btnId(b,type){
|
||||||
@@ -48,95 +90,81 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
return type+'|'+txt+'|'+idx;
|
return type+'|'+txt+'|'+idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractCommandContext(b){
|
// ── Context extraction: TIGHT scope — only button's immediate context ──
|
||||||
var container = b.closest('.p-1') || b.parentElement.parentElement;
|
// v6 FIX: Never climb more than 4 parents. Never grab editor/sidebar content.
|
||||||
if (!container) return "";
|
|
||||||
var titleSpans = container.querySelectorAll('span[title^="command("]');
|
|
||||||
if (titleSpans && titleSpans.length > 0) {
|
|
||||||
var t = titleSpans[0].getAttribute('title');
|
|
||||||
if (t && t.length > 5) return t.substring(0, 800);
|
|
||||||
}
|
|
||||||
var preEls = container.querySelectorAll('pre');
|
|
||||||
if (preEls && preEls.length > 0) {
|
|
||||||
var t2 = (preEls[preEls.length-1].textContent || '').trim();
|
|
||||||
if (t2.length > 2) return t2.substring(0, 800);
|
|
||||||
}
|
|
||||||
var fallback = (container.textContent || '').replace(cleanButtonText(b), '').trim();
|
|
||||||
return fallback.substring(0, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractChatContextFromNode(botTurn) {
|
function extractCommandContext(b) {
|
||||||
if (!botTurn) return '';
|
// Strategy 1: aria-label or title on button itself
|
||||||
|
var ariaLabel = b.getAttribute('aria-label') || b.getAttribute('title') || '';
|
||||||
var res = '';
|
if (ariaLabel && ariaLabel.length > 5 && ariaLabel.length < 500) {
|
||||||
// Use innerText if available on the markdown container (preserves spacing perfectly)
|
return ariaLabel;
|
||||||
var md = botTurn.querySelector('.markdown-body') || botTurn.querySelector('.prose');
|
|
||||||
if (md && md.innerText && md.innerText.trim().length > 10) {
|
|
||||||
res = md.innerText.trim();
|
|
||||||
return res.substring(0, 3500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolContainer = botTurn.querySelector('.bg-agent-convo-background') || botTurn.querySelector('.bg-ide-background-color');
|
// Strategy 2: Look for command text in button's DIRECT parent chain (max 3 levels)
|
||||||
var textParts = [];
|
var el = b.parentElement;
|
||||||
function walk(node) {
|
for (var depth = 0; depth < 3 && el; depth++) {
|
||||||
if (toolContainer && node === toolContainer) return;
|
// Check for code/pre elements (command text)
|
||||||
if (node.id === 'antigravity.agentSidePanelInputBox') return;
|
var pres = el.querySelectorAll('pre, code');
|
||||||
if (node.nodeType === 1) {
|
for (var pi = 0; pi < pres.length; pi++) {
|
||||||
var tag = node.tagName.toUpperCase();
|
var preText = (pres[pi].textContent || '').trim();
|
||||||
if (tag==='BUTTON' || tag==='SVG' || tag==='STYLE' || tag==='SCRIPT') return;
|
if (preText.length > 2 && preText.length < 500 && !isNoiseLine(preText)) {
|
||||||
// Skip tool action blocks aggressively if they masquerade as normal divs
|
return preText.substring(0, 400);
|
||||||
if (node !== botTurn && node.classList && (node.classList.contains('bg-ide-background-color') || node.classList.contains('bg-agent-convo-background'))) return;
|
}
|
||||||
}
|
}
|
||||||
if (node.nodeType === 3) {
|
// Check for span with title attribute containing command info
|
||||||
var val = node.nodeValue;
|
var titleSpans = el.querySelectorAll('span[title]');
|
||||||
if (val && val.trim()) textParts.push(val.trim());
|
for (var ti = 0; ti < titleSpans.length; ti++) {
|
||||||
} else {
|
var spanTitle = titleSpans[ti].getAttribute('title') || '';
|
||||||
if (node.childNodes && node.childNodes.length > 0) {
|
if (spanTitle.length > 5 && spanTitle.length < 500) {
|
||||||
for(var i=0; i<node.childNodes.length; i++) {
|
return spanTitle.substring(0, 400);
|
||||||
walk(node.childNodes[i]);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (node.nodeType === 1) {
|
|
||||||
var tg = node.tagName.toUpperCase();
|
|
||||||
if (tg==='P' || tg==='DIV' || tg==='BR' || tg==='LI' || tg==='PRE') textParts.push('\\n');
|
|
||||||
}
|
}
|
||||||
|
el = el.parentElement;
|
||||||
}
|
}
|
||||||
walk(botTurn);
|
|
||||||
res = textParts.join(' ').replace(/ \\n /g, '\\n').replace(/\\n+/g, '\\n').trim();
|
|
||||||
return res.substring(0, 3500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function extractChatContext(b) {
|
// Strategy 3: Immediate parent's text only (NOT full page)
|
||||||
try {
|
var immediateParent = b.parentElement;
|
||||||
var botTurn = b.closest('.text-ide-message-block-bot-color') || b.closest('.bg-agent-convo-background');
|
if (immediateParent) {
|
||||||
if (!botTurn) {
|
var parentText = '';
|
||||||
var container = b.closest('.p-1') || b.parentElement;
|
var children = immediateParent.childNodes;
|
||||||
botTurn = container ? container.parentElement : null;
|
for (var ci = 0; ci < children.length; ci++) {
|
||||||
|
var child = children[ci];
|
||||||
|
if (child.nodeType === 3 && child.nodeValue && child.nodeValue.trim()) {
|
||||||
|
parentText += child.nodeValue.trim() + ' ';
|
||||||
|
} else if (child.nodeType === 1 && child.tagName !== 'BUTTON' && child.tagName !== 'SVG') {
|
||||||
|
var childText = child.textContent || '';
|
||||||
|
if (childText.trim().length > 2 && childText.trim().length < 200) {
|
||||||
|
parentText += childText.trim() + ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parentText = parentText.trim();
|
||||||
|
if (parentText.length > 3 && parentText.length < 300) {
|
||||||
|
return cleanLines(parentText).substring(0, 300);
|
||||||
}
|
}
|
||||||
return extractChatContextFromNode(botTurn);
|
|
||||||
} catch(e) {
|
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractContext(b) {
|
function extractContext(b) {
|
||||||
var cmd = extractCommandContext(b);
|
var cmd = cleanButtonText(b);
|
||||||
var chat = extractChatContext(b);
|
var detail = extractCommandContext(b);
|
||||||
if (!chat && !cmd) return "";
|
if (!detail) return cmd;
|
||||||
var combined = "";
|
// Deduplicate: if detail contains button text, just show detail
|
||||||
if (chat && chat.length > 5) combined += "[AI 본문 요약]\\n" + chat + "\\n\\n";
|
if (detail.includes(cmd)) return cleanLines(detail);
|
||||||
if (cmd && cmd.length > 2) combined += "[결행 명령]\\n" + cmd;
|
return cmd + ': ' + cleanLines(detail);
|
||||||
return combined.trim();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var ACTION_WORDS = ['Allow', 'Run', 'Approve', 'Accept', '결행', '수락', '반영', '허용', '승인'];
|
var ACTION_WORDS = ['Allow', 'Run', 'Approve', 'Accept', 'Always allow', '결행', '수락', '반영', '허용', '승인'];
|
||||||
var REJECT_WORDS = ['Reject', 'Cancel', 'Deny', 'Stop', 'Decline', 'Dismiss', '거절', '거부', '취소', '차단', '정지', '무시'];
|
var REJECT_WORDS = ['Reject', 'Cancel', 'Deny', 'Stop', 'Decline', 'Dismiss', '거절', '거부', '취소', '차단', '정지', '무시'];
|
||||||
|
|
||||||
function isActionBtn(txt) {
|
function isActionBtn(txt) {
|
||||||
for(var i=0; i<ACTION_WORDS.length; i++) {
|
for(var i=0; i<ACTION_WORDS.length; i++) {
|
||||||
if(txt.indexOf(ACTION_WORDS[i]) !== -1) return true;
|
if(txt.indexOf(ACTION_WORDS[i]) !== -1) return true;
|
||||||
}
|
}
|
||||||
|
// "Running N command(s)" pattern
|
||||||
|
if (/Running\\\\d*\\\\s*command/i.test(txt)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
function isRejectBtn(txt) {
|
function isRejectBtn(txt) {
|
||||||
@@ -174,7 +202,7 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
var items = document.querySelectorAll('[aria-label^="Gravity Bridge Control"], [title^="Gravity Bridge Control"]');
|
var items = document.querySelectorAll('[aria-label^="Gravity Bridge Control"], [title^="Gravity Bridge Control"]');
|
||||||
if (items.length > 0) {
|
if (items.length > 0) {
|
||||||
var text = items[0].getAttribute('aria-label') || items[0].getAttribute('title') || '';
|
var text = items[0].getAttribute('aria-label') || items[0].getAttribute('title') || '';
|
||||||
var m = text.match(/port:(\\d+)/);
|
var m = text.match(/port:(\\\\d+)/);
|
||||||
if (m && m[1]) {
|
if (m && m[1]) {
|
||||||
clearInterval(timer);
|
clearInterval(timer);
|
||||||
tryPingAsync(parseInt(m[1], 10)).then(function(ok){ cb(ok ? parseInt(m[1],10) : HARDCODED_PORT); });
|
tryPingAsync(parseInt(m[1], 10)).then(function(ok){ cb(ok ? parseInt(m[1],10) : HARDCODED_PORT); });
|
||||||
@@ -194,13 +222,33 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
startObserver();
|
startObserver();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Chat body scanning (for Discord relay of AI responses) ──
|
||||||
var _lastText = "";
|
var _lastText = "";
|
||||||
var _lastTextTime = 0;
|
var _lastTextTime = 0;
|
||||||
var _lastTextSent = false;
|
var _lastTextSent = false;
|
||||||
|
|
||||||
|
function extractCleanChatText(container) {
|
||||||
|
if (!container) return '';
|
||||||
|
// Try markdown body first
|
||||||
|
var md = container.querySelector('.markdown-body') || container.querySelector('.prose');
|
||||||
|
var rawText = '';
|
||||||
|
if (md && md.innerText && md.innerText.trim().length > 10) {
|
||||||
|
rawText = md.innerText.trim();
|
||||||
|
} else {
|
||||||
|
rawText = (container.innerText || container.textContent || '').trim();
|
||||||
|
}
|
||||||
|
// Clean the text
|
||||||
|
return cleanLines(rawText).substring(0, 3500);
|
||||||
|
}
|
||||||
|
|
||||||
function scanChatBodies() {
|
function scanChatBodies() {
|
||||||
if(!_ready)return;
|
if(!_ready)return;
|
||||||
var botTurns = document.querySelectorAll('.text-ide-message-block-bot-color');
|
// Find bot response containers — try multiple selectors for compatibility
|
||||||
|
var botTurns = document.querySelectorAll(
|
||||||
|
'.text-ide-message-block-bot-color, ' +
|
||||||
|
'[data-testid*="bot"], [data-testid*="assistant"], ' +
|
||||||
|
'[class*="agent-convo"], [class*="bot-message"]'
|
||||||
|
);
|
||||||
if (botTurns.length === 0) return;
|
if (botTurns.length === 0) return;
|
||||||
|
|
||||||
var lastTurn = botTurns[botTurns.length - 1];
|
var lastTurn = botTurns[botTurns.length - 1];
|
||||||
@@ -217,7 +265,7 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
if (Date.now() - _lastTextTime > 3000) {
|
if (Date.now() - _lastTextTime > 3000) {
|
||||||
_lastTextSent = true;
|
_lastTextSent = true;
|
||||||
lastTurn.dataset.agChatScraped = "pending";
|
lastTurn.dataset.agChatScraped = "pending";
|
||||||
var finalTxt = extractChatContextFromNode(lastTurn);
|
var finalTxt = extractCleanChatText(lastTurn);
|
||||||
if (finalTxt && finalTxt.length > 5 && finalTxt !== "Review Changes") {
|
if (finalTxt && finalTxt.length > 5 && finalTxt !== "Review Changes") {
|
||||||
fetch(BASE+'/chat', {
|
fetch(BASE+'/chat', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -250,13 +298,13 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
if(txt.length <= 1) continue;
|
if(txt.length <= 1) continue;
|
||||||
|
|
||||||
if(!isActionBtn(txt)) continue;
|
if(!isActionBtn(txt)) continue;
|
||||||
// Skip inline code lens buttons unless they actually match the pattern properly
|
// Skip inline code lens buttons
|
||||||
if (b.closest('.codelens-decoration') && !txt.includes('Accept') && !txt.includes('수락') && !txt.includes('반영')) {
|
if (b.closest('.codelens-decoration') && !txt.includes('Accept') && !txt.includes('수락') && !txt.includes('반영')) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchedType = txt.includes('Accept') ? 'diff_review' : (txt.includes('Run') ? 'command' : 'permission');
|
var matchedType = txt.includes('Accept') ? 'diff_review' : (txt.includes('Run') || /Running\\\\d/.test(txt) ? 'command' : 'permission');
|
||||||
var container=b.closest('.p-1') || b.parentElement.parentElement;
|
var container=b.parentElement;
|
||||||
var groupKey=matchedType+'|'+btnId(b,matchedType);
|
var groupKey=matchedType+'|'+btnId(b,matchedType);
|
||||||
if(_sent[groupKey])continue;
|
if(_sent[groupKey])continue;
|
||||||
|
|
||||||
@@ -275,27 +323,21 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var desc=extractContext(b);
|
var desc=extractContext(b);
|
||||||
var is_dom_dummy = false;
|
|
||||||
if (!desc || desc.trim().length <= 2) {
|
|
||||||
desc = "MISSING_DESCRIPTION_FROM_DOM_FALLBACK_TO_STEP_PROBE";
|
|
||||||
is_dom_dummy = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rid=now.toString()+'_'+Math.random().toString(36).substring(2,6);
|
var rid=now.toString()+'_'+Math.random().toString(36).substring(2,6);
|
||||||
|
|
||||||
_sent[groupKey]={rid:rid,ts:now};
|
_sent[groupKey]={rid:rid,ts:now};
|
||||||
for(var mk=0;mk<bidList.length;mk++)_sent[bidList[mk]]={rid:rid,ts:now};
|
for(var mk=0;mk<bidList.length;mk++)_sent[bidList[mk]]={rid:rid,ts:now};
|
||||||
|
|
||||||
log('DETECTED GROUP '+matchedType+': '+buttonsArr.map(function(x){return x.text;}).join(', '));
|
log('DETECTED '+matchedType+': '+txt+' ['+desc.substring(0,80)+']');
|
||||||
|
|
||||||
(function(rid2,btnRefs2,bidList2,groupKey2,txt2,desc2,type2,buttonsArr2,isDummy2){
|
(function(rid2,btnRefs2,bidList2,groupKey2,txt2,desc2,type2,buttonsArr2){
|
||||||
var payload={
|
var payload={
|
||||||
request_id:rid2,
|
request_id:rid2,
|
||||||
command:txt2,
|
command:txt2,
|
||||||
description:desc2,
|
description:desc2,
|
||||||
step_type:type2,
|
step_type:type2,
|
||||||
buttons:buttonsArr2,
|
buttons:buttonsArr2
|
||||||
is_dom_dummy: isDummy2
|
|
||||||
};
|
};
|
||||||
fetch(BASE+'/pending',{
|
fetch(BASE+'/pending',{
|
||||||
method:'POST',
|
method:'POST',
|
||||||
@@ -312,7 +354,7 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
delete _sent[groupKey2];
|
delete _sent[groupKey2];
|
||||||
for(var di=0;di<bidList2.length;di++)delete _sent[bidList2[di]];
|
for(var di=0;di<bidList2.length;di++)delete _sent[bidList2[di]];
|
||||||
});
|
});
|
||||||
})(rid,btnRefs,bidList,groupKey,txt,desc,matchedType,buttonsArr,is_dom_dummy);
|
})(rid,btnRefs,bidList,groupKey,txt,desc,matchedType,buttonsArr);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -353,7 +395,8 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clickRejectButton(approveBtn){
|
function clickRejectButton(approveBtn){
|
||||||
var container=approveBtn.closest('.p-1') || approveBtn.parentElement.parentElement;
|
var container=approveBtn.parentElement;
|
||||||
|
if(!container) container = approveBtn.parentElement && approveBtn.parentElement.parentElement;
|
||||||
if(!container)return;
|
if(!container)return;
|
||||||
var siblings=container.querySelectorAll('button');
|
var siblings=container.querySelectorAll('button');
|
||||||
for(var i=0;i<siblings.length;i++){
|
for(var i=0;i<siblings.length;i++){
|
||||||
@@ -405,7 +448,7 @@ export function generateApprovalObserverScript(_port: number): string {
|
|||||||
}).observe(document.body,{childList:true,subtree:true});
|
}).observe(document.body,{childList:true,subtree:true});
|
||||||
setInterval(scheduleScan,3000);
|
setInterval(scheduleScan,3000);
|
||||||
|
|
||||||
// ── TRIGGER-CLICK POLLING (Fallback for missed pushes) ──
|
// ── TRIGGER-CLICK POLLING ──
|
||||||
(function pollTriggerClick(){
|
(function pollTriggerClick(){
|
||||||
if(_ready&&BASE){
|
if(_ready&&BASE){
|
||||||
fetch(BASE+'/trigger-click?t='+Date.now()).then(function(r){return r.json();}).then(function(d){
|
fetch(BASE+'/trigger-click?t='+Date.now()).then(function(r){return r.json();}).then(function(d){
|
||||||
|
|||||||
Reference in New Issue
Block a user