feat: extensions module + 19 new commands (100 → 119)

Mirror of new packs from the rust fork. Self-contained `extensions.py` with
new do_* handlers, registered via 'set_speak' / 'set_clipboard_fn' callbacks
from main.py.

Wiring (main.py)
  - `import extensions` at top.
  - `extensions.set_speak(_speak)` + `extensions.set_clipboard_fn(_set_clipboard)`
    right after _set_clipboard is defined.
  - Added 15 new action-type dispatches in run_action.

extensions.py (new, ~360 lines)
  Action types:
    lock_workstation  → rundll32 user32.dll,LockWorkStation
    screenshot {mode} → clipboard via System.Windows.Forms.Clipboard.SetImage
                        OR file → ~/Pictures/Screenshots/jarvis-<ts>.png
    disk_free / disk_list → PowerShell Get-PSDrive
    coin_flip / uuid_gen / password_gen (length 6..64, copies to clipboard,
                        speaks length only — never echoes the password)
    ssl_check {domain} → PowerShell TCP+SslStream→X509Certificate2 expiry
    wifi_ssid → netsh wlan show interfaces parser (ru + en field names)
    public_ip → urllib request to api.ipify.org
    unit_convert {mode: length|weight|temperature|speed}
                  Russian-grammar pluralisation (фут/фута/футов).
    date_math {mode: days_until|day_of_week}
                  Recognises Новый год, Рождество, 8 марта, 9 мая + "до N <месяц>".
    echo_repeat / self_ping — debug commands
    interesting_fact → Groq LLM (requires GROQ_TOKEN)

commands.yaml — 19 new entries:
  lock_workstation, screenshot_clipboard, screenshot_file, disk_free, disk_list,
  coin_flip, password_gen, uuid_gen, ssl_check, wifi_ssid, public_ip,
  convert_length, convert_weight, convert_temperature, convert_speed,
  days_until, day_of_week, echo_repeat, self_ping, interesting_fact.

Tests: python syntax check + yaml.safe_load both pass. Total commands: 119.

NOT YET mirrored from rust (would require deeper rework):
  memory_pack / profile_switch / scheduler / macros / vision / codebase_qa
  / github_pr / llm_switch / llm_context / media_keys / mood_log / pomodoro
  / habit_nudge / daily_briefing / quick_search / diagnostics / sleep_timer
  / wikipedia (the rewritten one) / intro / mailto / generators (UUID, coin,
  password ARE included — others can follow).

The reason these weren't mirrored: they need new state files
(memory.json, profiles.json, schedule.json, macros.json) and background
threads (scheduler tick). Doable as a follow-up but out of scope here.
This commit is contained in:
Bossiara13 2026-05-15 23:51:13 +03:00
parent ef8da9cba8
commit f82427c7b6
3 changed files with 715 additions and 0 deletions

38
main.py
View file

@ -17,6 +17,8 @@ import simpleaudio as sa
import vosk
import webrtcvad
import yaml
import extensions # new-command handlers ported from rust fork
from comtypes import CLSCTX_ALL
from fuzzywuzzy import fuzz
from pvrecorder import PvRecorder
@ -243,6 +245,11 @@ def _set_clipboard(text: str):
print(f"clipboard set failed: {e}")
# Wire extensions module to our voice + clipboard helpers
extensions.set_speak(_speak)
extensions.set_clipboard_fn(_set_clipboard)
_CODEGEN_TRIGGERS = (
'сгенерируй скрипт', 'напиши скрипт', 'сделай скрипт',
'сгенерируй код', 'набросай код', 'напиши код',
@ -1537,6 +1544,37 @@ def run_action(action):
do_my_ip(action, _current_voice or '')
elif t == 'summarize_selection':
do_summarize_selection(action, _current_voice or '')
# ── Extension handlers (ported from rust fork) ──
elif t == 'lock_workstation':
extensions.do_lock_workstation(action, _current_voice or '')
elif t == 'screenshot':
extensions.do_screenshot(action, _current_voice or '')
elif t == 'disk_free':
extensions.do_disk_free(action, _current_voice or '')
elif t == 'disk_list':
extensions.do_disk_list(action, _current_voice or '')
elif t == 'coin_flip':
extensions.do_coin(action, _current_voice or '')
elif t == 'password_gen':
extensions.do_password_gen(action, _current_voice or '')
elif t == 'uuid_gen':
extensions.do_uuid(action, _current_voice or '')
elif t == 'ssl_check':
extensions.do_ssl_check(action, _current_voice or '')
elif t == 'wifi_ssid':
extensions.do_wifi_ssid(action, _current_voice or '')
elif t == 'public_ip':
extensions.do_public_ip(action, _current_voice or '')
elif t == 'unit_convert':
extensions.do_unit_convert(action, _current_voice or '')
elif t == 'date_math':
extensions.do_date_math(action, _current_voice or '')
elif t == 'echo_repeat':
extensions.do_echo_repeat(action, _current_voice or '')
elif t == 'self_ping':
extensions.do_self_ping(action, _current_voice or '')
elif t == 'interesting_fact':
extensions.do_interesting_fact(action, _current_voice or '')
_current_voice: str = ''