diff --git a/bin/dm.py b/bin/dm.py new file mode 100755 index 0000000..c7eb93a --- /dev/null +++ b/bin/dm.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 +""" +DM: Headless Direct Message API (muse.ai) + +Distinct from Chat (front-door, externally hosted). +DM operates via headless browser automation on muse.ai. +Chat operates via signed HTTPS API to chat.muse-dev.online. + +This separation increases headless reliability by isolating +browser automation from network API calls. + +Usage: + dm.py send --agent 646 --target "message" + dm.py read --agent 646 --target [n] + dm.py thread --from 646 --to muse --target "message" +""" +import argparse +import subprocess +import time +import sys + +API = "/home/super/Projects/NetVM/bin/muse-chat-api.py" +NETVM_EXEC = "/home/super/Projects/NetVM/bin/netvm-exec.sh" +VALID_AGENTS = ["muse", "pip", "646"] + +def run(cmd, timeout=60): + result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=timeout) + return result.stdout.strip() + +def dm_send(agent, target, message): + """Send a DM via headless to agent's target (main or side chat).""" + if agent not in VALID_AGENTS: + print(f"ERROR: Unknown agent {agent}", file=sys.stderr) + sys.exit(1) + + # Navigate to target + if target == "main": + run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat main") + else: + run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat use {target}") + + time.sleep(2) + + # Send + safe = message.replace('"', '\\"').replace('$', '\\$').replace('`', '\\`')[:1000] + result = run(f'{NETVM_EXEC} {agent} -- python3 {API} --account {agent} send "{safe}"') + + # Back to main + run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat main") + + print(f"DM sent to {agent}/{target}") + return result + +def dm_read(agent, target, n=5): + """Read DMs via headless from agent's target.""" + if agent not in VALID_AGENTS: + print(f"ERROR: Unknown agent {agent}", file=sys.stderr) + sys.exit(1) + + if target == "main": + run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat main") + else: + run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat use {target}") + + time.sleep(2) + msgs = run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} messages {n}") + run(f"{NETVM_EXEC} {agent} -- python3 {API} --account {agent} sidechat main") + + print(msgs) + return msgs + +def dm_thread(from_agent, to_agent, target, message): + """Thread a message from one agent into another's chat (via operator).""" + # Read from source (optional, for context) + # Send to target with attribution + attributed = f"[{from_agent}] {message}" + return dm_send(to_agent, target, attributed) + +def main(): + p = argparse.ArgumentParser(description="DM: Headless Direct Messages (muse.ai)") + sub = p.add_subparsers(dest='cmd', required=True) + + # dm send + ps = sub.add_parser('send', help='Send a DM via headless') + ps.add_argument('--agent', required=True, choices=VALID_AGENTS) + ps.add_argument('--target', required=True, help='main or chat_id') + ps.add_argument('message', help='Message to send') + ps.set_defaults(func=lambda a: dm_send(a.agent, a.target, a.message)) + + # dm read + pr = sub.add_parser('read', help='Read DMs via headless') + pr.add_argument('--agent', required=True, choices=VALID_AGENTS) + pr.add_argument('--target', required=True, help='main or chat_id') + pr.add_argument('--n', type=int, default=5, help='Number of messages') + pr.set_defaults(func=lambda a: dm_read(a.agent, a.target, a.n)) + + # dm thread + pt = sub.add_parser('thread', help='Thread from one agent to another') + pt.add_argument('--from', dest='from_agent', required=True, choices=VALID_AGENTS) + pt.add_argument('--to', dest='to_agent', required=True, choices=VALID_AGENTS) + pt.add_argument('--target', required=True, help='main or chat_id on target') + pt.add_argument('message', help='Message to thread') + pt.set_defaults(func=lambda a: dm_thread(a.from_agent, a.to_agent, a.target, a.message)) + + args = p.parse_args() + args.func(args) + +if __name__ == '__main__': + main()