diff --git a/commands.yaml b/commands.yaml index 32bc7fe..5144ede 100644 --- a/commands.yaml +++ b/commands.yaml @@ -681,6 +681,65 @@ help: - что можешь action: {type: help_commands} +launch_game: + phrases: + - запусти игру + - включи игру + - поиграем + - хочу поиграть + - запусти доту + - включи кс + - включи фортнайт + action: {type: launch_game} + +list_games: + phrases: + - какие у меня игры + - список игр + - покажи игры + action: {type: list_games} + +mouse_left_click: + phrases: + - клик + - кликни + - нажми мышкой + action: {type: mouse, op: left} + +mouse_right_click: + phrases: + - правый клик + - клик правой + - правой кнопкой + action: {type: mouse, op: right} + +mouse_double_click: + phrases: + - двойной клик + - дабл клик + - двойное нажатие + action: {type: mouse, op: double} + +mouse_scroll_down: + phrases: + - промотай вниз + - прокрути вниз + action: {type: mouse, op: scroll_down} + +mouse_scroll_up: + phrases: + - промотай вверх + - прокрути вверх + action: {type: mouse, op: scroll_up} + +random_choice: + phrases: + - выбери из + - выбери + - решай за меня + - выбрать + action: {type: random_choice} + thanks: phrases: - спасибо diff --git a/main.py b/main.py index 0779cc6..b0eb515 100644 --- a/main.py +++ b/main.py @@ -1074,6 +1074,125 @@ def do_help_commands(action, voice: str): _speak(f"Загружено {len(names)} команд. Например: {preview}. Полный список в буфере.") +_GAME_TRIGGERS = ('запусти игру', 'включи игру', 'хочу поиграть', 'поиграем', + 'запусти', 'включи', 'launch game', 'play game', 'launch', + 'запусти гру', 'увімкни гру') + + +def _load_games_config(): + user_profile = os.environ.get('USERPROFILE', os.path.expanduser('~')) + cfg_path = os.path.join(user_profile, 'Documents', 'jarvis-games.json') + if not os.path.isfile(cfg_path): + sample = [ + {"name": "dota", "aliases": ["доту", "доту 2", "dota 2"], "steam_appid": 570}, + {"name": "cs2", "aliases": ["кс", "контру", "контр страйк"], "steam_appid": 730}, + {"name": "elden ring", "aliases": ["элден", "элден ринг"], "steam_appid": 1245620}, + {"name": "witcher 3", "aliases": ["ведьмак", "ведьмака 3"], "steam_appid": 292030}, + {"name": "factorio", "aliases": ["факторио"], "steam_appid": 427520}, + ] + os.makedirs(os.path.dirname(cfg_path), exist_ok=True) + with open(cfg_path, 'w', encoding='utf-8') as f: + json.dump(sample, f, ensure_ascii=False, indent=2) + return cfg_path, sample, True + try: + with open(cfg_path, 'r', encoding='utf-8') as f: + return cfg_path, json.load(f), False + except (OSError, json.JSONDecodeError) as e: + print(f"games config read failed: {e}") + return cfg_path, [], False + + +def do_launch_game(action, voice: str): + cfg_path, entries, created = _load_games_config() + if created: + _speak("Создал шаблон, добавь свои игры и повтори.") + subprocess.Popen(['cmd', '/c', 'start', '', cfg_path]) + return + query = _strip_trigger(voice, _GAME_TRIGGERS).strip().lower() + if not query: + _speak("Какую игру?") + return + + def name_match(e, q): + if e['name'].lower() == q: return True + if q in e['name'].lower(): return True + for a in e.get('aliases', []): + if a.lower() == q or a.lower() in q: return True + return False + + match = next((e for e in entries if name_match(e, query)), None) + if not match: + _speak(f"Не нашёл {query}.") + return + + appid = match.get('steam_appid') + path = match.get('path') + epic_uri = match.get('epic_uri') + if appid: + webbrowser.open(f"steam://rungameid/{appid}") + elif epic_uri: + webbrowser.open(epic_uri) + elif path and os.path.isfile(path): + subprocess.Popen(['cmd', '/c', 'start', '', path]) + else: + _speak(f"Не настроен запуск для {match['name']}.") + return + _speak(f"Запускаю {match['name']}.") + + +def do_list_games(action, voice: str): + cfg_path, entries, created = _load_games_config() + if created or not entries: + _speak("Список пуст.") + return + names = [e['name'] for e in entries] + _speak(f"В библиотеке {len(names)}: {', '.join(names)}.") + + +_MOUSE_PS = r""" +Add-Type -Name MouseM -Namespace Win32 -MemberDefinition @' +[DllImport("user32.dll")] +public static extern void mouse_event(uint flags, int dx, int dy, int data, System.UIntPtr extra); +'@ +$LD=0x0002; $LU=0x0004; $RD=0x0008; $RU=0x0010; $MD=0x0020; $MU=0x0040; $WHEEL=0x0800 +function Click([uint32]$d,[uint32]$u){ [Win32.MouseM]::mouse_event($d,0,0,0,[UIntPtr]::Zero); Start-Sleep -Milliseconds 30; [Win32.MouseM]::mouse_event($u,0,0,0,[UIntPtr]::Zero) } +switch ($args[0]) { + 'left' { Click $LD $LU } + 'right' { Click $RD $RU } + 'middle' { Click $MD $MU } + 'double' { Click $LD $LU; Start-Sleep -Milliseconds 50; Click $LD $LU } + 'scroll_up' { [Win32.MouseM]::mouse_event($WHEEL,0,0, 360,[UIntPtr]::Zero) } + 'scroll_down' { [Win32.MouseM]::mouse_event($WHEEL,0,0,-360,[UIntPtr]::Zero) } +} +""".strip() + + +def do_mouse(action, voice: str): + op = action.get('op', 'left') + subprocess.run( + ['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', _MOUSE_PS, op], + capture_output=True, timeout=5, + ) + + +_CHOICE_TRIGGERS = ('выбери из', 'выбери', 'решай за меня', 'выбрать', + 'pick from', 'choose from', 'decide between', + 'обери з', 'вибери') + + +def do_random_choice(action, voice: str): + rest = _strip_trigger(voice, _CHOICE_TRIGGERS).strip().lower() + options = None + for sep in (' или ', ' or ', ', ', ' либо ', ' чи '): + if sep in rest: + options = [t.strip() for t in rest.split(sep) if t.strip()] + break + if not options or len(options) < 2: + _speak("Скажи: выбери из X или Y или Z.") + return + _speak(f"Я выбираю {random.choice(options)}.") + + def do_stopwatch(action, voice: str): op = action.get('op', 'check') state = _stopwatch_state_path() @@ -1238,6 +1357,14 @@ def run_action(action): do_power(action, _current_voice or '') elif t == 'help_commands': do_help_commands(action, _current_voice or '') + elif t == 'launch_game': + do_launch_game(action, _current_voice or '') + elif t == 'list_games': + do_list_games(action, _current_voice or '') + elif t == 'mouse': + do_mouse(action, _current_voice or '') + elif t == 'random_choice': + do_random_choice(action, _current_voice or '') _current_voice: str = ''