--- - name: Развернуть TSJ Guardian Telegram Bot на Proxmox hosts: proxmox become: true gather_facts: true vars: tsj_bot_user: "codex" tsj_bot_group: "admin" tsj_bot_root: "/home/codex/infra-admin/tsj-bot" tsj_bot_script_name: "tsj_guardian_bot.py" tsj_bot_script_dest: "{{ tsj_bot_root }}/{{ tsj_bot_script_name }}" tsj_bot_source_local_path: "{{ aw_repo_root }}/proxmox/tsj_guardian_bot.py" tsj_bot_service_name: "tsj-guardian-bot.service" tsj_bot_env_path: "{{ tsj_bot_root }}/.env" tsj_bot_state_dir: "/home/codex/infra-admin/.state" tsj_bot_logs_dir: "/home/codex/infra-admin/logs" tsj_bot_default_chat_id: "{{ telegram_default_chat_id | default(telegram_allowed_chat_ids.split(',')[0]) }}" pre_tasks: - name: Проверить наличие существующего .env бота на хосте ansible.builtin.stat: path: "{{ tsj_bot_env_path }}" register: tsj_bot_existing_env - name: Проверить обязательные переменные ansible.builtin.assert: that: - > ( telegram_bot_token is defined and (telegram_bot_token | string | length) > 20 and telegram_allowed_chat_ids is defined and (telegram_allowed_chat_ids | string | length) > 0 ) or (tsj_bot_existing_env.stat.exists | default(false)) fail_msg: "Задайте telegram_bot_token и telegram_allowed_chat_ids или оставьте на хосте существующий {{ tsj_bot_env_path }}." - name: Проверить наличие исходного файла бота на контроллере ansible.builtin.stat: path: "{{ tsj_bot_source_local_path }}" register: tsj_bot_source_stat delegate_to: localhost become: false - name: Остановить выполнение если файл бота не найден на контроллере ansible.builtin.assert: that: - tsj_bot_source_stat.stat.exists - tsj_bot_source_stat.stat.isreg fail_msg: "Файл бота не найден: {{ tsj_bot_source_local_path }}" tasks: - name: Установить зависимости Python для бота ansible.builtin.package: name: - python3 - python3-requests state: present - name: Создать каталоги бота ansible.builtin.file: path: "{{ item.path }}" state: directory owner: "{{ item.owner | default(tsj_bot_user) }}" group: "{{ item.group | default(tsj_bot_group) }}" mode: "{{ item.mode }}" loop: - { path: "{{ tsj_bot_root }}", mode: "0775" } - { path: "{{ tsj_bot_state_dir }}", mode: "0775" } - { path: "{{ tsj_bot_logs_dir }}", mode: "0775" } - name: Развернуть скрипт бота ansible.builtin.copy: src: "{{ tsj_bot_source_local_path }}" dest: "{{ tsj_bot_script_dest }}" owner: root group: "{{ tsj_bot_group }}" mode: "0750" notify: Restart tsj bot - name: Сгенерировать полный .env бота when: - telegram_bot_token is defined - (telegram_bot_token | string | length) > 20 - telegram_allowed_chat_ids is defined - (telegram_allowed_chat_ids | string | length) > 0 ansible.builtin.copy: dest: "{{ tsj_bot_env_path }}" owner: "{{ tsj_bot_user }}" group: "{{ tsj_bot_group }}" mode: "0640" content: | TELEGRAM_BOT_TOKEN={{ telegram_bot_token }} TELEGRAM_ALLOWED_CHAT_IDS={{ telegram_allowed_chat_ids }} TELEGRAM_DEFAULT_CHAT_ID={{ tsj_bot_default_chat_id }} HTTPS_PROXY={{ tsj_bot_https_proxy_url | default(tsj_bot_telegram_proxy_url | default('http://127.0.0.1:11090')) }} HTTP_PROXY={{ tsj_bot_http_proxy_url | default(tsj_bot_telegram_proxy_url | default('http://127.0.0.1:11090')) }} NO_PROXY={{ tsj_bot_no_proxy | default('localhost,127.0.0.1,10.10.10.0/24') }} NODE_13_HOST={{ tsj_bot_node_13_host | default('10.10.10.13') }} NODE_16_HOST={{ tsj_bot_node_16_host | default('10.10.10.16') }} NODE_13_URL={{ tsj_bot_node_13_url | default('http://10.10.10.13:5600/') }} NODE_16_URL={{ tsj_bot_node_16_url | default('http://10.10.10.16/') }} NODE_16_ENABLED={{ tsj_bot_node_16_enabled | default('false') }} CHECK_SCRIPT={{ tsj_bot_check_script | default('/home/codex/infra-admin/scripts/system_self_support.sh --check') }} HEAL_SCRIPT={{ tsj_bot_heal_script | default('/home/codex/infra-admin/scripts/system_self_support.sh --heal') }} FS_WARN_PCT={{ tsj_bot_fs_warn_pct | default(85) }} FS_CRIT_PCT={{ tsj_bot_fs_crit_pct | default(92) }} FS_TARGETS={{ tsj_bot_fs_targets | default('host,200,201,202,203') }} FS_EXCLUDE_TYPES={{ tsj_bot_fs_exclude_types | default('tmpfs,devtmpfs,proc,sysfs,cgroup,cgroup2,overlay,squashfs,nsfs,tracefs,debugfs,securityfs,configfs,fusectl,mqueue,hugetlbfs,ramfs') }} STATE_FILE={{ tsj_bot_state_file | default('/home/codex/infra-admin/.state/tsj_guardian_state.json') }} LOG_FILE={{ tsj_bot_log_file | default('/home/codex/infra-admin/logs/tsj_guardian_bot.log') }} HEARTBEAT_FILE={{ tsj_bot_heartbeat_file | default('/home/codex/infra-admin/.state/tsj_guardian_heartbeat') }} CHECK_INTERVAL_SEC={{ tsj_bot_check_interval_sec | default(60) }} OPERATOR_TIMEOUT_SEC={{ tsj_bot_operator_timeout_sec | default(900) }} RETRY_AUTORECOVERY_EVERY_SEC={{ tsj_bot_retry_autorecovery_every_sec | default(300) }} EXIT_ON_AUTORECOVERY_SUCCESS={{ tsj_bot_exit_on_autorecovery_success | default('true') }} ENABLE_AI_ESCALATION={{ tsj_bot_enable_ai_escalation | default('true') }} FS_IMMEDIATE_AI_ON_CRITICAL={{ tsj_bot_fs_immediate_ai_on_critical | default('true') }} AI_ESCALATION_MODE={{ tsj_bot_ai_escalation_mode | default('codex_exec') }} ENABLE_SERVER_FALLBACK={{ tsj_bot_enable_server_fallback | default('true') }} TELEGRAM_PROXY_URL={{ tsj_bot_telegram_proxy_url | default('http://127.0.0.1:11090') }} AI_CHAT_ENABLED={{ tsj_bot_ai_chat_enabled | default('true') }} AI_CHAT_TIMEOUT_SEC={{ tsj_bot_ai_chat_timeout_sec | default(1800) }} AI_CHAT_WORKDIR={{ tsj_bot_ai_chat_workdir | default('/home/codex/infra-admin') }} AI_CHAT_SANDBOX={{ tsj_bot_ai_chat_sandbox | default('workspace-write') }} CODEX_MODEL={{ tsj_bot_codex_model | default('gpt-5.3-codex') }} CODEX_FALLBACK_MODELS={{ tsj_bot_codex_fallback_models | default('gpt-5.4-mini') }} AI_EXEC_USER={{ tsj_bot_ai_exec_user | default('codex') }} TMUX_USER={{ tsj_bot_tmux_user | default('codex') }} TMUX_SESSION={{ tsj_bot_tmux_session | default('ai') }} TMUX_CREATE_IF_MISSING={{ tsj_bot_tmux_create_if_missing | default('false') }} TMUX_START_COMMAND={{ tsj_bot_tmux_start_command | default('codex') }} PFSENSE_CHANGE_CONTROL_ENABLED={{ tsj_bot_pfsense_change_control_enabled | default('true') }} PFSENSE_CHANGE_CONFIRM_TTL_SEC={{ tsj_bot_pfsense_change_confirm_ttl_sec | default(900) }} OPENVPN_CONFIG_ENABLED={{ tsj_bot_openvpn_config_enabled | default('true') }} OPENVPN_CONFIG_CONFIRM_TTL_SEC={{ tsj_bot_openvpn_config_confirm_ttl_sec | default(900) }} OPENVPN_EXPIRY_WARN_ENABLED={{ tsj_bot_openvpn_expiry_warn_enabled | default('false') }} OPENVPN_EXPIRY_WARN_DAYS={{ tsj_bot_openvpn_expiry_warn_days | default(30) }} OPENVPN_EXPIRY_WARN_TIMEOUT_SEC={{ tsj_bot_openvpn_expiry_warn_timeout_sec | default(120) }} OPENVPN_EXPIRY_WARN_INTERVAL_SEC={{ tsj_bot_openvpn_expiry_warn_interval_sec | default(21600) }} PFSENSE_MCP_BEARER={{ tsj_bot_pfsense_mcp_bearer | default(pfsense_mcp_bearer | default('')) }} SERVER_FALLBACK_COMMANDS={{ tsj_bot_server_fallback_commands | default('/home/codex/infra-admin/scripts/system_self_support.sh --heal') }} UPDATES_SCRIPT={{ tsj_bot_updates_script | default('/usr/bin/python3 /home/codex/infra-admin/scripts/proxmox_lxc_critical_updates.py') }} UPDATE_TARGETS={{ tsj_bot_update_targets | default('auto') }} AW_RUS_API_BASE={{ tsj_bot_aw_rus_api_base | default('http://10.10.10.13:5600/api/0') }} AW_RUS_WORKTIME_BASE={{ tsj_bot_aw_rus_worktime_base | default('http://10.10.10.13:5610') }} AW_RUS_WORKTIME_HEAL_CMD={{ tsj_bot_aw_rus_worktime_heal_cmd | default("sshpass -p '04091968' ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no igor@10.10.10.13 'sudo -S /usr/local/bin/aw-worktime-autoheal.sh && sudo -S systemctl start aw-worktime-ui-bridge.service'") }} AW_RUS_DLP_HEAL_CMD={{ tsj_bot_aw_rus_dlp_heal_cmd | default("sshpass -p '04091968' ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no igor@10.10.10.13 'sudo -S systemctl restart activitywatch-server.service && sudo -S systemctl start activitywatch-dlp-aggregator.service || true && sudo -S /usr/local/bin/aw-health-check && sudo -S /usr/local/bin/dlp-health-check'") }} AW_RUS_CASE_API_BASE={{ tsj_bot_aw_rus_case_api_base | default('http://10.10.10.13:5602') }} AW_RUS_HAYABUSA_ENABLED={{ tsj_bot_aw_rus_hayabusa_enabled | default('true') }} AW_RUS_HAYABUSA_SSH_CMD={{ tsj_bot_aw_rus_hayabusa_ssh_cmd | default("sshpass -p '04091968' ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no igor@10.10.10.13") }} AW_RUS_HOST={{ tsj_bot_aw_rus_host | default('SHARKON2025') }} AW_RUS_PRIMARY_USER={{ tsj_bot_aw_rus_primary_user | default('USER1') }} AW_RUS_STALE_SEC={{ tsj_bot_aw_rus_stale_sec | default(900) }} notify: Restart tsj bot - name: Обновить только AW-Rus/Hayabusa env ключи в существующем .env when: - not ( telegram_bot_token is defined and (telegram_bot_token | string | length) > 20 and telegram_allowed_chat_ids is defined and (telegram_allowed_chat_ids | string | length) > 0 ) - tsj_bot_existing_env.stat.exists | default(false) ansible.builtin.lineinfile: path: "{{ tsj_bot_env_path }}" regexp: "^{{ item.key }}=" line: "{{ item.key }}={{ item.value }}" create: false owner: "{{ tsj_bot_user }}" group: "{{ tsj_bot_group }}" mode: "0640" loop: - { key: "AW_RUS_API_BASE", value: "{{ tsj_bot_aw_rus_api_base | default('http://10.10.10.13:5600/api/0') }}" } - { key: "AW_RUS_WORKTIME_BASE", value: "{{ tsj_bot_aw_rus_worktime_base | default('http://10.10.10.13:5610') }}" } - { key: "AW_RUS_WORKTIME_HEAL_CMD", value: "{{ tsj_bot_aw_rus_worktime_heal_cmd | default(\"sshpass -p '04091968' ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no igor@10.10.10.13 'sudo -S /usr/local/bin/aw-worktime-autoheal.sh && sudo -S systemctl start aw-worktime-ui-bridge.service'\") }}" } - { key: "AW_RUS_DLP_HEAL_CMD", value: "{{ tsj_bot_aw_rus_dlp_heal_cmd | default(\"sshpass -p '04091968' ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no igor@10.10.10.13 'sudo -S systemctl restart activitywatch-server.service && sudo -S systemctl start activitywatch-dlp-aggregator.service || true && sudo -S /usr/local/bin/aw-health-check && sudo -S /usr/local/bin/dlp-health-check'\") }}" } - { key: "AW_RUS_CASE_API_BASE", value: "{{ tsj_bot_aw_rus_case_api_base | default('http://10.10.10.13:5602') }}" } - { key: "AW_RUS_HAYABUSA_ENABLED", value: "{{ tsj_bot_aw_rus_hayabusa_enabled | default('true') }}" } - { key: "AW_RUS_HAYABUSA_SSH_CMD", value: "{{ tsj_bot_aw_rus_hayabusa_ssh_cmd | default(\"sshpass -p '04091968' ssh -o PubkeyAuthentication=no -o StrictHostKeyChecking=no igor@10.10.10.13\") }}" } - { key: "AW_RUS_HOST", value: "{{ tsj_bot_aw_rus_host | default('SHARKON2025') }}" } - { key: "AW_RUS_PRIMARY_USER", value: "{{ tsj_bot_aw_rus_primary_user | default('USER1') }}" } - { key: "AW_RUS_STALE_SEC", value: "{{ tsj_bot_aw_rus_stale_sec | default(900) }}" } notify: Restart tsj bot - name: Установить systemd unit бота ansible.builtin.copy: dest: "/etc/systemd/system/{{ tsj_bot_service_name }}" owner: root group: root mode: "0644" content: | [Unit] Description=TSJ Guardian Telegram Bot After=network-online.target gost-tg.service anet-client.service pfsense-mcp-server.service Wants=network-online.target gost-tg.service anet-client.service pfsense-mcp-server.service [Service] Type=simple User=root WorkingDirectory=/home/codex/infra-admin EnvironmentFile={{ tsj_bot_env_path }} ExecStart=/usr/bin/python3 {{ tsj_bot_script_dest }} Restart=always RestartSec=5 KillMode=control-group [Install] WantedBy=multi-user.target notify: - Reload systemd - Restart tsj bot - name: Проверить синтаксис Python скрипта бота ansible.builtin.command: "python3 -m py_compile {{ tsj_bot_script_dest }}" changed_when: false - name: Включить и запустить сервис бота ansible.builtin.systemd: name: "{{ tsj_bot_service_name }}" enabled: true state: started - name: Проверить что сервис активен ansible.builtin.command: "systemctl is-active {{ tsj_bot_service_name }}" register: tsj_bot_active changed_when: false failed_when: tsj_bot_active.stdout.strip() != "active" handlers: - name: Reload systemd ansible.builtin.systemd: daemon_reload: true - name: Restart tsj bot ansible.builtin.systemd: name: "{{ tsj_bot_service_name }}" state: restarted