diff --git a/pyproject.toml b/pyproject.toml index acbec1f..c16536e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "markovbot-gui" -version = "0.1.0" +version = "0.1.1" description = "Markov Chain Bot GUI" readme = "README.md" authors = [ diff --git a/src/markovbot_gui/config_window.py b/src/markovbot_gui/config_window.py index fcfbe26..8e51961 100644 --- a/src/markovbot_gui/config_window.py +++ b/src/markovbot_gui/config_window.py @@ -1,5 +1,3 @@ -import json -import logging from pathlib import Path from kivy.clock import Clock @@ -10,6 +8,9 @@ from kivy.uix.label import Label from kivy.uix.popup import Popup from kivy.uix.textinput import TextInput +from src.markovbot_gui.libs.settings import Settings +from src.markovbot_gui.libs.timer import logger + class ConfigWindow(BoxLayout): def __init__(self, config_path: Path, **kwargs): @@ -20,14 +21,13 @@ class ConfigWindow(BoxLayout): self.padding = dp(20) # Load existing configuration - self.default_config = { + default_config = { "Host": "irc.chat.twitch.tv", "Port": 6667, "Channel": "#", "Nickname": "", "Authentication": "oauth:", "DeniedUsers": ["StreamElements", "Nightbot", "Moobot", "Marbiebot"], - "AllowedUsers": [], "Cooldown": 20, "KeyLength": 2, "MaxSentenceWordAmount": 25, @@ -38,19 +38,13 @@ class ConfigWindow(BoxLayout): "EnableGenerateCommand": True, "SentenceSeparator": " - ", "AllowGenerateParams": True, - "GenerateCommands": ["!generate", "!g"], } - try: - if config_path.exists(): - with config_path.open("r") as f: - saved_config = json.load(f) - # Update self.default_config with saved values - self.default_config.update(saved_config) - except json.JSONDecodeError: - logging.exception(f"Failed to parse config file at {config_path}") - except Exception: - logging.exception("Error loading config file") + if config_path.exists(): + self.s = Settings.read(config_path) + else: + self.s = Settings(**default_config) # type: ignore[arg-type] + self.s.write(config_path) # Create widgets # Channel input @@ -63,7 +57,7 @@ class ConfigWindow(BoxLayout): self.channel_input = TextInput( multiline=False, size_hint_x=0.7, - text=self.default_config["Channel"], + text=self.s.channel, ) channel_layout.add_widget(channel_label) channel_layout.add_widget(self.channel_input) @@ -78,7 +72,7 @@ class ConfigWindow(BoxLayout): self.nickname_input = TextInput( multiline=False, size_hint_x=0.7, - text=self.default_config["Nickname"], + text=self.s.nickname, ) nickname_layout.add_widget(nickname_label) nickname_layout.add_widget(self.nickname_input) @@ -94,11 +88,25 @@ class ConfigWindow(BoxLayout): multiline=False, size_hint_x=0.7, password=True, - text=self.default_config["Authentication"], + text=self.s.authentication, ) auth_layout.add_widget(auth_label) auth_layout.add_widget(self.auth_input) + automatic_generation_label = Label(text="Automatic generation (seconds): ") + self.automatic_generation_input = TextInput( + multiline=False, + size_hint_x=0.7, + text=str(self.s.automatic_generation_timer), + ) + automatic_generation_layout = BoxLayout( + orientation="horizontal", + size_hint_y=None, + height=dp(40), + ) + automatic_generation_layout.add_widget(automatic_generation_label) + automatic_generation_layout.add_widget(self.automatic_generation_input) + # Save button save_button = Button( text="Save", @@ -112,21 +120,23 @@ class ConfigWindow(BoxLayout): self.add_widget(channel_layout) self.add_widget(nickname_layout) self.add_widget(auth_layout) + self.add_widget(automatic_generation_layout) self.add_widget(save_button) def save_config(self, instance): - # Get values from inputs - self.default_config["Channel"] = self.channel_input.text.strip() - self.default_config["Nickname"] = self.nickname_input.text.strip() - self.default_config["Authentication"] = self.auth_input.text.strip() - try: - # Create directory if it doesn't exist - self.config_path.parent.mkdir(parents=True, exist_ok=True) - - # Save configuration - with self.config_path.open("w") as f: - json.dump(self.default_config, f, indent=4) + self.s.channel = self.channel_input.text.strip() + self.s.nickname = self.nickname_input.text.strip() + self.s.authentication = self.auth_input.text.strip() + self.s.automatic_generation_timer = int( + self.automatic_generation_input.text + ) + if 0 < self.s.automatic_generation_timer < 29: # noqa: PLR2004 + raise ValueError( + "Value for 'Automatic generation' must be at least 30 seconds, " # noqa: EM101 + "or a negative number for no automatic generations." + ) + self.s.write(self.config_path) # Show success message success_popup = Popup( @@ -140,7 +150,7 @@ class ConfigWindow(BoxLayout): Clock.schedule_once(success_popup.dismiss, 1) except Exception as e: # noqa: BLE001 - # Show error message if saving fails + self.show_error_message(f"Failed to save configuration:\n{e!s}") error_popup = Popup( title="Error", content=Label(text=f"Failed to save configuration:\n{e!s}"), @@ -148,3 +158,4 @@ class ConfigWindow(BoxLayout): size=(dp(400), dp(150)), ) error_popup.open() + logger.exception("Failed to save configuration") diff --git a/src/markovbot_gui/libs/markov_chain_bot.py b/src/markovbot_gui/libs/markov_chain_bot.py index e953a50..ebc44cb 100644 --- a/src/markovbot_gui/libs/markov_chain_bot.py +++ b/src/markovbot_gui/libs/markov_chain_bot.py @@ -32,7 +32,8 @@ class MarkovChain: if self.s.help_message_timer > 0: if self.s.help_message_timer < 300: # noqa: PLR2004 raise ValueError( - 'Value for "HelpMessageTimer" in must be at least 300 seconds, or a negative number for no help messages.', # noqa: EM101 + 'Value for "HelpMessageTimer" in must be at least 300 seconds, ' # noqa: EM101 + "or a negative number for no help messages.", ) t = LoopingTimer(self.s.help_message_timer, self._command_help) t.start() @@ -44,6 +45,9 @@ class MarkovChain: 'Value for "Automatic_generation_message" must be at least 30 seconds, or a negative number for no ' # noqa: EM101 "automatic generations.", ) + logger.info( + f"Automatic generation enabled, will send messages every {self.s.automatic_generation_timer} seconds" + ) t = LoopingTimer( self.s.automatic_generation_timer, self._command_automatic_generation, diff --git a/src/markovbot_gui/main.py b/src/markovbot_gui/main.py index 1c0fdde..d7ffdee 100644 --- a/src/markovbot_gui/main.py +++ b/src/markovbot_gui/main.py @@ -16,14 +16,15 @@ class BotApp(App): self.config_path = ( platformdirs.user_config_path("markovbot_gui") / "settings.json" ) + self.data_path = platformdirs.user_data_path("markovbot_gui") def run_bot(self, instance): bot_runner = BotRunner(settings_path=self.config_path) popup = Popup( - title="Bot Running", + title=f"Bot runner, database available at {self.data_path}", content=bot_runner, size_hint=(None, None), - size=(dp(600), dp(400)), + size=(dp(600), dp(600)), auto_dismiss=False, ) popup.open() @@ -31,10 +32,10 @@ class BotApp(App): def run_config(self, instance): config_window = ConfigWindow(config_path=self.config_path) popup = Popup( - title=f"Bot Configuration, available at {self.config_path}", + title=f"Bot configuration, available at {self.config_path}", content=config_window, size_hint=(None, None), - size=(dp(400), dp(400)), + size=(dp(600), dp(400)), auto_dismiss=False, ) diff --git a/uv.lock b/uv.lock index b278897..f29669e 100644 --- a/uv.lock +++ b/uv.lock @@ -246,7 +246,7 @@ wheels = [ [[package]] name = "markovbot-gui" -version = "0.1.0" +version = "0.1.1" source = { virtual = "." } dependencies = [ { name = "kivy", extra = ["base"] }, @@ -260,7 +260,7 @@ dependencies = [ { name = "twitchwebsocket" }, ] -[package.dependency-groups] +[package.dev-dependencies] dev = [ { name = "mypy" }, { name = "pyright" }, @@ -280,7 +280,7 @@ requires-dist = [ { name = "twitchwebsocket", specifier = ">=1.2.1" }, ] -[package.metadata.dependency-groups] +[package.metadata.requires-dev] dev = [ { name = "mypy", specifier = ">=1.13.0" }, { name = "pyright", specifier = ">=1.1.387" },