101 lines
4.6 KiB
Python
101 lines
4.6 KiB
Python
import webview, threading, subprocess, sys, time, os, ctypes, atexit, socket, random
|
||
|
||
WINDOW_WIDTH, WINDOW_HEIGHT, RIGHT_PADDING, TOP_PADDING = 600, 900, 0, 300
|
||
|
||
def find_free_port(lo=8501, hi=8599):
|
||
ports = list(range(lo, hi+1)); random.shuffle(ports)
|
||
for p in ports:
|
||
try: s = socket.socket(); s.bind(('127.0.0.1', p)); s.close(); return p
|
||
except OSError: continue
|
||
raise RuntimeError(f'No free port in {lo}-{hi}')
|
||
|
||
def get_screen_width():
|
||
try: return ctypes.windll.user32.GetSystemMetrics(0)
|
||
except: return 1920
|
||
|
||
def start_streamlit(port):
|
||
global proc
|
||
cmd = [sys.executable, "-m", "streamlit", "run", "stapp.py", "--server.port", str(port), "--server.headless", "true", "--theme.base", "dark"] # 暗黑模式
|
||
proc = subprocess.Popen(cmd)
|
||
atexit.register(proc.kill)
|
||
|
||
def inject(text):
|
||
window.evaluate_js(f"""
|
||
const textarea = document.querySelector('textarea[data-testid="stChatInputTextArea"]');
|
||
if (textarea) {{
|
||
// 1. 用原生 setter 设置值(绕过 React)
|
||
const nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
|
||
nativeTextAreaValueSetter.call(textarea, {repr(text)});
|
||
// 2. 触发 React 的 input 事件
|
||
textarea.dispatchEvent(new Event('input', {{ bubbles: true }}));
|
||
// 3. 触发 change 事件(有些组件需要)
|
||
textarea.dispatchEvent(new Event('change', {{ bubbles: true }}));
|
||
// 4. 延迟提交
|
||
setTimeout(() => {{
|
||
const btn = document.querySelector('[data-testid="stChatInputSubmitButton"]');
|
||
if (btn) {{btn.click();console.log('Submitted:', {repr(text)});}}
|
||
}}, 200);
|
||
}}""")
|
||
|
||
def get_last_reply_time():
|
||
last = window.evaluate_js("""
|
||
const el = document.getElementById('last-reply-time');
|
||
el ? parseInt(el.textContent) : 0;
|
||
""") or 0
|
||
return last or int(time.time())
|
||
|
||
def idle_monitor():
|
||
last_trigger_time = 0
|
||
while True:
|
||
time.sleep(5)
|
||
try:
|
||
now = time.time()
|
||
if now - last_trigger_time < 120: continue
|
||
last_reply = get_last_reply_time()
|
||
if now - last_reply > 1800:
|
||
print('[Idle Monitor] Detected idle state, injecting task...')
|
||
inject("[AUTO]🤖 用户已经离开超过30分钟,作为自主智能体,请阅读自动化sop,执行自动任务。")
|
||
last_trigger_time = now
|
||
except Exception as e:
|
||
print(f'[Idle Monitor] Error: {e}')
|
||
|
||
if __name__ == '__main__':
|
||
import argparse
|
||
parser = argparse.ArgumentParser()
|
||
parser.add_argument('port', nargs='?', default='0');
|
||
parser.add_argument('--tg', action='store_true', help='启动 Telegram Bot');
|
||
parser.add_argument('--no-sched', action='store_true', help='不启动计划任务调度器')
|
||
parser.add_argument('--llm_no', type=int, default=0, help='LLM编号')
|
||
args = parser.parse_args()
|
||
port = str(find_free_port()) if args.port == '0' else args.port
|
||
print(f'[Launch] Using port {port}')
|
||
threading.Thread(target=start_streamlit, args=(port,), daemon=True).start()
|
||
|
||
if args.tg:
|
||
tgproc = subprocess.Popen([sys.executable, "tgapp.py"], creationflags=subprocess.CREATE_NO_WINDOW if os.name=='nt' else 0)
|
||
atexit.register(tgproc.kill)
|
||
print('[Launch] Telegram Bot started')
|
||
else: print('[Launch] Telegram Bot not enabled (use --tg to start)')
|
||
|
||
if not args.no_sched:
|
||
try:
|
||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM); sock.bind(('127.0.0.1', 45762)); sock.listen(1)
|
||
scheduler_proc = subprocess.Popen([sys.executable, "agentmain.py", "--scheduled", "--llm_no", str(args.llm_no)], creationflags=subprocess.CREATE_NO_WINDOW if os.name=='nt' else 0);
|
||
atexit.register(lambda: (scheduler_proc.kill(), sock.close()))
|
||
print('[Launch] Task Scheduler started')
|
||
except OSError:
|
||
print('[Launch] Task Scheduler already running (port occupied)')
|
||
else: print('[Launch] Task Scheduler disabled (--no-sched)')
|
||
|
||
monitor_thread = threading.Thread(target=idle_monitor, daemon=True)
|
||
monitor_thread.start()
|
||
if os.name == 'nt':
|
||
screen_width = get_screen_width()
|
||
x_pos = screen_width - WINDOW_WIDTH - RIGHT_PADDING
|
||
else: x_pos = 100
|
||
time.sleep(2)
|
||
window = webview.create_window(
|
||
title='GenericAgent', url=f'http://localhost:{port}',
|
||
width=WINDOW_WIDTH, height=WINDOW_HEIGHT, x=x_pos, y=TOP_PADDING,
|
||
resizable=True, text_select=True)
|
||
webview.start() |