~nickwinston123/armagetronad/arma_chatbot_config

« back to all changes in this revision

Viewing changes to arma_terminal/arma_terminal.py

  • Committer: hackermans
  • Date: 2025-05-28 18:34:25 UTC
  • Revision ID: nickwinston123@gmail.com-20250528183425-z5cssgt5eeqyqox3

consolidated arma chatbot config programs

- arma_terminal: live console viewer with input passthrough
- game_manager: manages bans, IP rotation, and keeping the game open
- game_updater: syncs updated game files from shared folder
- ollama_chat: ai chatbot 

- launcher: launches all not already opened programs with positioning and window checks

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import curses
 
2
import threading
 
3
import time
 
4
import os
 
5
import textwrap
 
6
import configparser
 
7
import logging
 
8
import sys
 
9
 
 
10
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
 
11
 
 
12
config = configparser.ConfigParser()
 
13
ini_path = os.path.join(os.path.dirname(__file__), 'arma_terminal_real.ini')
 
14
try:
 
15
    config.read(ini_path)
 
16
except Exception as e:
 
17
    logging.error(f"Error reading configuration: {e}")
 
18
    sys.exit(1)
 
19
 
 
20
try:
 
21
    CONSOLE_LOG       = config.get('Paths',    'console_log')
 
22
    COMMANDS_FILE     = config.get('Paths',    'commands_file')
 
23
    COMMAND_PREFIX    = config.get('Settings', 'command_prefix')
 
24
    MAX_LOG_LINES     = config.getint('Settings', 'max_log_lines', fallback=1000)
 
25
except Exception as e:
 
26
    logging.error(f"Error retrieving config values: {e}")
 
27
    sys.exit(1)
 
28
 
 
29
def tail_log(lines, lock, stop_event):
 
30
    with open(CONSOLE_LOG, 'r', encoding='utf-8', errors='ignore') as f:
 
31
        f.seek(0, os.SEEK_END)
 
32
        while not stop_event.is_set():
 
33
            chunk = f.readline()
 
34
            if not chunk:
 
35
                time.sleep(0.1)
 
36
                continue
 
37
            text = chunk.strip()
 
38
            if text:
 
39
                with lock:
 
40
                    lines.append(text)
 
41
            with lock:
 
42
                if len(lines) > MAX_LOG_LINES:
 
43
                    lines[:] = lines[-MAX_LOG_LINES:]
 
44
 
 
45
def draw_scrollbar(win, top_line, total_lines, height):
 
46
    if total_lines <= height:
 
47
        return
 
48
    scroll_height = height - 2
 
49
    bar_height = max(1, int(scroll_height * (height / total_lines)))
 
50
    top_pos = int(scroll_height * (top_line / total_lines))
 
51
    for i in range(scroll_height):
 
52
        char = '█' if top_pos <= i < top_pos + bar_height else '│'
 
53
        try:
 
54
            win.addch(i + 1, win.getmaxyx()[1] - 2, char)
 
55
        except curses.error:
 
56
            pass
 
57
 
 
58
def draw_screen(stdscr, lines, lock):
 
59
    curses.curs_set(1)
 
60
    stdscr.nodelay(True)
 
61
 
 
62
    height, width = stdscr.getmaxyx()
 
63
    log_h = height - 3
 
64
    log_win = curses.newwin(log_h, width, 0, 0)
 
65
    input_win = curses.newwin(3, width, log_h, 0)
 
66
 
 
67
    input_str = ""
 
68
    cursor_x = 0
 
69
    scroll_offset = 0
 
70
    history = []
 
71
    history_index = 0
 
72
 
 
73
    dragging = False
 
74
    drag_start_y = None
 
75
    drag_start_offset = None
 
76
 
 
77
    curses.mousemask(curses.ALL_MOUSE_EVENTS | curses.REPORT_MOUSE_POSITION)
 
78
 
 
79
    while True:
 
80
        with lock:
 
81
            raw_lines = lines[-MAX_LOG_LINES:]
 
82
        wrapped = []
 
83
        for line in raw_lines:
 
84
            wrapped.extend(textwrap.wrap(line, width - 3) or [''])
 
85
 
 
86
        visible_lines = log_h - 2
 
87
        max_offset = max(len(wrapped) - visible_lines, 0)
 
88
        scroll_offset = min(scroll_offset, max_offset)
 
89
 
 
90
        start_idx = max(0, len(wrapped) - visible_lines - scroll_offset)
 
91
        display = wrapped[start_idx:start_idx + visible_lines]
 
92
 
 
93
        log_win.erase()
 
94
        log_win.box()
 
95
        for idx, disp in enumerate(display):
 
96
            log_win.addnstr(idx + 1, 1, disp, width - 3)
 
97
        draw_scrollbar(log_win, start_idx, len(wrapped), log_h)
 
98
        log_win.refresh()
 
99
 
 
100
        input_win.erase()
 
101
        input_win.box()
 
102
        prompt = "> " + input_str
 
103
        input_win.addnstr(1, 1, prompt, width - 2)
 
104
        input_win.move(1, 2 + cursor_x)
 
105
        input_win.refresh()
 
106
 
 
107
        try:
 
108
            ch = stdscr.get_wch()
 
109
        except curses.error:
 
110
            time.sleep(0.05)
 
111
            continue
 
112
 
 
113
        if ch == curses.KEY_RESIZE:
 
114
            height, width = stdscr.getmaxyx()
 
115
            log_h = height - 3
 
116
            stdscr.erase(); stdscr.refresh()
 
117
            log_win.resize(log_h, width); log_win.mvwin(0, 0)
 
118
            input_win.resize(3, width); input_win.mvwin(log_h, 0)
 
119
            continue
 
120
 
 
121
        if ch == curses.KEY_UP:
 
122
            if history:
 
123
                history_index = max(history_index - 1, 0)
 
124
                input_str = history[history_index]
 
125
                cursor_x = len(input_str)
 
126
            continue
 
127
        if ch == curses.KEY_DOWN:
 
128
            if history:
 
129
                history_index = min(history_index + 1, len(history))
 
130
                input_str = history[history_index] if history_index < len(history) else ""
 
131
                cursor_x = len(input_str)
 
132
            continue
 
133
 
 
134
        if ch == curses.KEY_LEFT:
 
135
            cursor_x = max(0, cursor_x - 1)
 
136
            continue
 
137
        if ch == curses.KEY_RIGHT:
 
138
            cursor_x = min(len(input_str), cursor_x + 1)
 
139
            continue
 
140
 
 
141
        if ch == curses.KEY_PPAGE:
 
142
            scroll_offset = min(scroll_offset + 3, max_offset)
 
143
            continue
 
144
        if ch == curses.KEY_NPAGE:
 
145
            scroll_offset = max(scroll_offset - 3, 0)
 
146
            continue
 
147
 
 
148
        if ch == curses.KEY_END:
 
149
            scroll_offset = 0
 
150
            continue
 
151
 
 
152
        if ch == curses.KEY_MOUSE:
 
153
            try:
 
154
                _, mx, my, _, bstate = curses.getmouse()
 
155
                if bstate & curses.BUTTON1_PRESSED:
 
156
                    if 1 <= my < log_h - 1 and mx == width - 2:
 
157
                        dragging = True
 
158
                        drag_start_y = my
 
159
                        drag_start_offset = scroll_offset
 
160
                elif bstate & curses.BUTTON1_RELEASED:
 
161
                    dragging = False
 
162
                elif dragging and drag_start_y is not None:
 
163
                    dy = my - drag_start_y
 
164
                    scroll_offset = min(max(drag_start_offset - dy, 0), max_offset)
 
165
                elif bstate & curses.BUTTON4_PRESSED:
 
166
                    scroll_offset = min(scroll_offset + 1, max_offset)
 
167
                elif bstate & curses.BUTTON5_PRESSED:
 
168
                    scroll_offset = max(scroll_offset - 1, 0)
 
169
            except Exception:
 
170
                pass
 
171
            continue
 
172
 
 
173
        if isinstance(ch, str) and ch.isprintable():
 
174
            input_str = input_str[:cursor_x] + ch + input_str[cursor_x:]
 
175
            cursor_x += 1
 
176
        elif ch in (curses.KEY_BACKSPACE, '\b', '\x7f'):
 
177
            if cursor_x > 0:
 
178
                input_str = input_str[:cursor_x - 1] + input_str[cursor_x:]
 
179
                cursor_x -= 1
 
180
        elif ch == curses.KEY_DC:
 
181
            if cursor_x < len(input_str):
 
182
                input_str = input_str[:cursor_x] + input_str[cursor_x + 1:]
 
183
        elif ch == '\n':
 
184
            cmd = input_str.strip()
 
185
            input_str = ""
 
186
            cursor_x = 0
 
187
            if cmd:
 
188
                history.append(cmd)
 
189
            history_index = len(history)
 
190
            if cmd.lower() in ("exit", "quit"):
 
191
                return
 
192
            full_cmd = f"{COMMAND_PREFIX} {cmd}" if COMMAND_PREFIX else cmd
 
193
            try:
 
194
                with open(COMMANDS_FILE, 'a', encoding='utf-8') as f:
 
195
                    f.write(full_cmd + "\n")
 
196
                    f.flush()
 
197
                    os.fsync(f.fileno())
 
198
            except Exception as e:
 
199
                with lock:
 
200
                    lines.append(f"→ ERROR writing command: {e}")
 
201
            else:
 
202
                with lock:
 
203
                    lines.append(f"→ SENT: {full_cmd}")
 
204
        elif ch in (curses.KEY_EXIT, '\x1b'):
 
205
            return
 
206
 
 
207
def main(stdscr):
 
208
    lines = []
 
209
    lock = threading.Lock()
 
210
    stop_event = threading.Event()
 
211
 
 
212
    t = threading.Thread(target=tail_log, args=(lines, lock, stop_event), daemon=True)
 
213
    t.start()
 
214
 
 
215
    try:
 
216
        draw_screen(stdscr, lines, lock)
 
217
    finally:
 
218
        stop_event.set()
 
219
        t.join(0.1)
 
220
 
 
221
if __name__ == "__main__":
 
222
    import curses
 
223
    curses.wrapper(main)
 
 
b'\\ No newline at end of file'