Module: init¶
There is a really primitive program that will interactively ask you some questions
- Path to credentials file
- Hashtag to track
- Mastodon server URL
- Bot's username
- Timezone [UTC]
- Number of top posts to show
- Days to look back for posts
- Tag users in posts?
- Maximum toots to fetch
- Dry run mode?
- Hours margin for analysis
It will spit out a basic ini file that you can then modify.
Code Reference¶
Interactive initialization for mastoscore configuration using prompt_toolkit
confirm_abandon()
¶
Confirm if the user wants to abandon changes
Source code in mastoscore/init.py
def confirm_abandon() -> bool:
"""Confirm if the user wants to abandon changes"""
try:
response = get_input(
PromptSession(), "Are you sure you want to abandon? (yes/no)", "no"
)
return response.lower() in ["y", "yes"]
except (KeyboardInterrupt, EOFError):
return False
display_config(config)
¶
Display the current configuration in a scrollable window
Source code in mastoscore/init.py
def display_config(config: ConfigParser) -> None:
"""Display the current configuration in a scrollable window"""
from io import StringIO
# Convert config to string
output = StringIO()
config.write(output)
content = output.getvalue()
# Create text area with content
text_area = TextArea(
text=content, read_only=True, scrollbar=True, wrap_lines=True, focusable=True
)
# Create key bindings
kb = KeyBindings()
@kb.add("q")
def _(event):
event.app.exit()
# Create window with content
window = HSplit(
[
text_area,
Window(
height=1,
content=FormattedTextControl(
"Use up/down/pageup/pagedown to scroll, q to continue"
),
),
]
)
# Create and run application
app = Application(
layout=Layout(window), key_bindings=kb, full_screen=True, mouse_support=True
)
app.run()
display_section_values(title, values)
¶
Display the current section's values in yellow
Source code in mastoscore/init.py
def display_section_values(title, values):
"""Display the current section's values in yellow"""
print(f"\n=== {title} ===")
for key, value in values.items():
if not key.startswith(("event_", "episode_")):
print(f" {key}: \033[33m{value}\033[0m")
edit_section(config, section, session)
¶
Re-prompt for all values in a section
Source code in mastoscore/init.py
def edit_section(config: ConfigParser, section: str, session: PromptSession) -> bool:
"""Re-prompt for all values in a section"""
if section not in config:
print(f"\nError: Section {section} not found")
return False
print(f"\n=== Editing {section} section ===")
try:
if section == "mastoscore":
config[section]["cred_file"] = get_input(
session,
"Path to credentials file (e.g., .env/mybot.env)",
config[section].get("cred_file"),
)
config[section]["hashtag"] = get_input(
session,
"Hashtag to track (without the # symbol)",
config[section].get("hashtag"),
)
config[section]["api_base_url"] = get_input(
session, "Mastodon server URL", config[section].get("api_base_url")
)
config[section]["botusername"] = get_input(
session,
"Bot's username (without @)",
config[section].get("botusername"),
)
config[section]["timezone"] = get_input(
session, "Timezone", config[section].get("timezone", "UTC")
)
config[section]["top_n"] = get_input(
session,
"Number of top posts to show",
config[section].get("top_n", "3"),
)
config[section]["lookback"] = get_input(
session,
"Days to look back for posts",
config[section].get("lookback", "2"),
)
config[section]["tag_users"] = get_input(
session,
"Tag users in posts? (true/false)",
config[section].get("tag_users", "false"),
)
elif section == "fetch":
config[section]["max"] = get_input(
session, "Maximum toots to fetch", config[section].get("max", "3000")
)
config[section]["dry_run"] = get_input(
session,
"Enable dry run mode for fetching? (true/false)",
config[section].get("dry_run", "false"),
)
elif section == "analyse":
config[section]["hours_margin"] = get_input(
session,
"Hours margin for analysis",
config[section].get("hours_margin", "1"),
)
display_section_values(f"{section} Section Updated", config[section])
return True
except (KeyboardInterrupt, EOFError):
print(f"\nSection {section} edit cancelled.")
return False
get_input(session, prompt, default=None, required=True)
¶
Get input with light blue prompt and yellow response
Source code in mastoscore/init.py
def get_input(session, prompt, default=None, required=True) -> str:
"""Get input with light blue prompt and yellow response"""
while True:
try:
if default:
result = session.prompt(
HTML(
f"<ansicyan>{prompt}</ansicyan> [<ansiyellow>{default}</ansiyellow>]: "
)
)
if not result and default:
return str(default)
else:
result = session.prompt(HTML(f"<ansicyan>{prompt}</ansicyan>: "))
if result or not required:
return str(result) if result else ""
print("This field is required. Please provide a value.")
except (KeyboardInterrupt, EOFError):
print("\nInput cancelled.")
raise
init()
¶
Interactive configuration setup for mastoscore using prompt_toolkit
Source code in mastoscore/init.py
def init():
"""
Interactive configuration setup for mastoscore using prompt_toolkit
"""
# Set up signal handlers
def signal_handler(signum, frame):
print("\nReceived interrupt signal Exiting...")
exit(1)
signal(SIGINT, signal_handler)
signal(SIGQUIT, signal_handler)
logger = getLogger(__name__)
basicConfig(
format="%(asctime)s %(levelname)-8s %(message)s",
level=logging.ERROR,
datefmt="%H:%M:%S",
)
logger.setLevel(INFO)
# Define prompt style
style = Style.from_dict(
{
"ansicyan": "#00B7EB bold", # Light blue
"ansiyellow": "#FFFF00", # Yellow
}
)
# Create prompt session
session = PromptSession(style=style)
print("\nWelcome to Mastoscore Configuration\n")
print("This wizard will help you create a configuration file for your event.\n")
config = ConfigParser(interpolation=ExtendedInterpolation())
# [mastoscore] section
config["mastoscore"] = {}
ms = config["mastoscore"]
print("\n=== Basic Configuration (mastoscore section) ===")
try:
# Core values
ms["cred_file"] = get_input(
session, "Path to credentials file (e.g., .env/mybot.env)"
)
ms["hashtag"] = get_input(session, "Hashtag to track (without the # symbol)")
ms["api_base_url"] = get_input(session, "Mastodon server URL")
ms["botusername"] = get_input(session, "Bot's username (without @)")
ms["timezone"] = get_input(session, "Timezone", "UTC")
ms["top_n"] = get_input(session, "Number of top posts to show", "3")
ms["lookback"] = get_input(session, "Days to look back for posts", "2")
ms["tag_users"] = get_input(
session, "Tag users in posts? (true/false)", "false"
)
# Standard paths
ms["journaldir"] = "data"
ms["journalfile"] = "${mastoscore:hashtag}"
# [fetch] section
print("\n=== Fetch Configuration ===")
config["fetch"] = {}
fetch = config["fetch"]
fetch["max"] = get_input(session, "Maximum toots to fetch", "3000")
fetch["dry_run"] = get_input(
session, "Enable dry run mode for fetching? (true/false)", "false"
)
# [analyse] section
print("\n=== Analysis Configuration ===")
config["analyse"] = {}
analyse = config["analyse"]
analyse["hours_margin"] = get_input(session, "Hours margin for analysis", "1")
# Add debug level to sections
config["fetch"]["debug"] = "20"
config["analyse"]["debug"] = "20"
analyse["hours_margin"] = get_input(session, "Hours margin for analysis", "1")
except (KeyboardInterrupt, EOFError):
print("\nConfiguration cancelled.")
return False
except Exception as e:
logger.error(f"Error during configuration: {e}")
return False
# Clear screen and show final configuration
clear()
print("\nConfiguration Summary:\n")
display_config(config)
# Ask user what to do
while True:
action = get_input(session, "Choose action (save/change/abandon)", "save")
if action.lower() == "save":
filename = f"ini/{ms['hashtag']}.ini"
if save_config(config, filename):
print(f"\nConfiguration saved to {filename}")
exit(0)
else:
print(
"\nFailed to save configuration. Please try save again or abandon to exit."
)
continue
elif action.lower() == "change":
section = select_section()
if section:
if edit_section(config, section, session):
# Show updated configuration
clear()
print("\nConfiguration Summary:\n")
display_config(config)
continue
elif action.lower() == "abandon":
if confirm_abandon():
print("\nConfiguration abandoned.")
exit(1)
else:
print("\nInvalid choice. Please choose 'save', 'change', or 'abandon'")
save_config(config, filename)
¶
Save configuration to file with error handling
Source code in mastoscore/init.py
def save_config(config: ConfigParser, filename: str) -> bool:
"""Save configuration to file with error handling"""
import os
try:
# Create directory if it doesn't exist
os.makedirs(os.path.dirname(filename), exist_ok=True)
# Write configuration file
with open(filename, "w") as f:
config.write(f)
return True
except PermissionError:
print(f"\nError: Permission denied writing to {filename}")
return False
except FileNotFoundError:
print(f"\nError: Directory path not found for {filename}")
return False
except Exception as e:
print(f"\nError writing configuration file: {e}")
return False
select_section()
¶
Display a menu to select a section to edit
Source code in mastoscore/init.py
def select_section() -> str:
"""Display a menu to select a section to edit"""
sections = [
("mastoscore", "1. mastoscore - Basic Configuration"),
("fetch", "2. fetch - Fetch Configuration"),
("analyse", "3. analyse - Analysis Configuration"),
]
radio_list = RadioList(sections)
# Create application
app = Application(
layout=Layout(
HSplit(
[
Window(
content=FormattedTextControl(
"Select a section to edit (use arrow keys or number keys):"
)
),
radio_list,
]
)
),
full_screen=True,
)
result = app.run()
return result if result else ""