diff --git a/bin/muse-chat-api.py b/bin/muse-chat-api.py index ef9e0e5..5596f66 100755 --- a/bin/muse-chat-api.py +++ b/bin/muse-chat-api.py @@ -159,11 +159,11 @@ def cmd_send(ws, message): }})()""", True) print(result) -def cmd_messages(ws, n=5): +def cmd_messages(ws, n=5, width=200): # Check approvals first (non-blocking) check_approvals(ws) result = ev(ws, f"""(() => {{ - const ps = [...document.querySelectorAll('p')].slice(-{n*2}).map(p=>p.innerText.slice(0,200)); + const ps = [...document.querySelectorAll('p')].slice(-{n*2}).map(p=>p.innerText.slice(0,{width})); return ps.join('\\n---\\n'); }})()""") print(result) @@ -181,15 +181,30 @@ def cmd_wait(ws, timeout=30): cmd_messages(ws, 2) def cmd_sidechat_use(ws, chat_id): - """Navigate to a side chat by ID.""" - ws.send(json.dumps({ - "id": 1, "method": "Page.navigate", - "params": {"url": f"https://muse.ai/thread/{chat_id}"} - })) - json.loads(ws.recv()) - time.sleep(5) - url = ev(ws, "window.location.href") - print(f"Navigated to: {url}") + """Open sidebar, click the box with the chat name.""" + import base64 + b64 = base64.b64encode(chat_id.encode()).decode() + js = ( + "(async()=>{" + # Open sidebar first + "const ob=[...document.querySelectorAll('button')].find(" + "b=>b.textContent.includes('Open chat and side chats'));" + "if(ob)ob.click();" + "await new Promise(r=>setTimeout(r,2000));" + # Find and click the chat + "const name=atob('" + b64 + "');" + "const els=[...document.querySelectorAll('*')];" + "const cands=els.filter(e=>e.textContent&&e.textContent.includes(name));" + "cands.sort((a,b)=>a.textContent.length-b.textContent.length);" + "const el=cands[0];" + "if(!el)return 'NOTFOUND';" + "el.click();" + "await new Promise(r=>setTimeout(r,4000));" + "return window.location.href;" + "})()" + ) + result = ev(ws, js, True) + print(f"Navigated to: {result}") def cmd_sidechat_main(ws): """Navigate back to main chat.""" @@ -218,6 +233,35 @@ def cmd_sidechat_list(ws): })()""") print(chats[:500]) +def cmd_sidechat_create(ws): + """Create a new side chat via the 'New side chat' button. Returns the new thread URL.""" + # Ensure sidebar is open (same selector as cmd_sidechat_list: textContent) + ev(ws, """(() => { + const btn = [...document.querySelectorAll("button")].find(b => + b.textContent.includes("Open chat and side chats") + ); + if (btn) btn.click(); + })()""") + import time as _time + _time.sleep(2) + result = ev(ws, """(() => { + const btn = [...document.querySelectorAll('button')].find(b => + (b.getAttribute('aria-label')||'') === 'New side chat'); + if (!btn) return 'NOT_FOUND'; + btn.click(); + return 'CLICKED'; + })()""") + if result == 'NOT_FOUND': + print("ERROR: 'New side chat' button not found (sidebar may be closed)", file=sys.stderr) + sys.exit(1) + import time as _time + _time.sleep(5) + url = ev(ws, "window.location.href") + print(f"Created: {url}") + return url + + + def cdp_call(ws, method, params=None): """Raw CDP method call for non-Runtime domains (DOM, Page, ...). @@ -333,6 +377,10 @@ def main(): help='Agent name (matches node, profile, ACCOUNTS.md)') p.add_argument('command', choices=['send', 'messages', 'wait', 'approvals', 'sidechat', 'upload']) p.add_argument('arg', nargs='*', default=[]) + p.add_argument('--dry-run', action='store_true', + help='upload: stage attachment without sending') + p.add_argument('--message', default=None, + help='upload: caption text sent with the file') args = p.parse_args() node, cdp_url = ACCOUNTS[args.account] @@ -347,7 +395,8 @@ def main(): cmd_send(ws, " ".join(args.arg)) elif args.command == 'messages': n = int(args.arg[0]) if args.arg else 5 - cmd_messages(ws, n) + w = int(args.arg[1]) if len(args.arg) > 1 else 200 + cmd_messages(ws, n, w) elif args.command == 'wait': t = int(args.arg[0]) if args.arg else 30 cmd_wait(ws, t) @@ -355,10 +404,12 @@ def main(): cmd_approvals(ws) elif args.command == 'sidechat': if not args.arg: - print("ERROR: sidechat requires subcommand (use|main|list)", file=sys.stderr) + print("ERROR: sidechat requires subcommand (use|main|list|create)", file=sys.stderr) sys.exit(1) sub = args.arg[0] - if sub == "use": + if sub == "create": + cmd_sidechat_create(ws) + elif sub == "use": if len(args.arg) < 2: print("ERROR: sidechat use requires chat_id", file=sys.stderr) sys.exit(1) @@ -374,15 +425,8 @@ def main(): if not args.arg: print("ERROR: upload requires a filepath", file=sys.stderr) sys.exit(1) - fpath = args.arg[0] - rest = args.arg[1:] - dry = '--dry-run' in rest - msg = None - if '--message' in rest: - i = rest.index('--message') - if i + 1 < len(rest): - msg = rest[i + 1] - cmd_upload(ws, fpath, message=msg, dry_run=dry) + cmd_upload(ws, args.arg[0], message=args.message, + dry_run=args.dry_run) finally: ws.close()