converted to config.py

This commit is contained in:
2026-02-23 23:31:15 +00:00
parent 639ab4dd16
commit e5d673a8dd
6 changed files with 131 additions and 61 deletions

View File

@@ -9,7 +9,6 @@ import traceback
import datetime as dt
from pathlib import Path
from logger import Logger
from jsonwrapper import AutoSaveDict
from typing import (
Optional,
Callable,
@@ -24,53 +23,38 @@ from bot import (
Row,
)
# configuration is now a Python module instead of a JSON file
from config import config as cfg, TEMPLATE
def config_app(path: Path) -> AutoSaveDict:
config = {
'client_id': '',
'client_secret': '',
'user_agent': '',
'username': '',
'password': '',
'sub_name': '',
'max_days': '',
'max_posts': '',
'sleep_minutes': '',
}
configuration: List[List[str]] = []
# the old JSON-based interactive configuration helper has been removed. the
# values are now stored in ``config/config.py``. ``cfg`` above is a simple
# dict containing the settings; callers should treat numeric entries as
# integers.
if not os.path.exists(path):
for key, _ in config.items():
config_name = ' '.join(key.split('_')).title()
user_inp = input(f"{config_name}: ")
configuration.append([key, user_inp])
for config_name, value in configuration:
config[config_name] = value
config_handler = AutoSaveDict(
path,
**config
)
return config_handler
config_dir = Path(utils.BASE_DIR, 'config')
config_dir.mkdir(parents=True, exist_ok=True)
config_file = Path(config_dir, 'config.json')
handler = config_app(config_file)
handler.init()
config_path = Path(config_dir, 'config.py')
# ensure a configuration file exists - if not, write the template and exit so
# the user can edit it.
if not config_path.exists():
config_path.write_text(TEMPLATE)
print(f"Created new configuration template at {config_path!r}.\n"
"Please populate the values and restart the bot.")
sys.exit(0)
posts = Posts('deleted_posts', config_dir)
logger = Logger(1)
untracked_flairs = (utils.Flair.SOLVED, utils.Flair.ABANDONED)
posts.init()
reddit = praw.Reddit(
client_id=handler['client_id'],
client_secret=handler['client_secret'],
user_agent=handler['user_agent'],
username=handler['username'],
password=handler['password'],
client_id=cfg['client_id'],
client_secret=cfg['client_secret'],
user_agent=cfg['user_agent'],
username=cfg['username'],
password=cfg['password'],
)
@@ -123,7 +107,7 @@ def notify_if_error(func: Callable[..., int]) -> Callable[..., int]:
msg = f"Error with '{bot_name}':\n\n{full_error}\n\nPlease report to author ({author})"
send_modmail(
reddit,
handler['sub_name'],
cfg['sub_name'],
f'An error has occured with {utils.BOT_NAME} msg',
msg
)
@@ -167,13 +151,13 @@ def main() -> int:
posts_to_delete: Set[Row] = set()
ignore_methods = ['Removed by mod',]
if utils.parse_cmd_line_args(sys.argv, logger, config_file, posts):
if utils.parse_cmd_line_args(sys.argv, logger, config_path, posts):
return 0
saved_submission_ids = {row.post_id for row in posts.fetch_all()}
max_posts = handler['max_posts']
max_posts = cfg.get('max_posts')
limit = int(max_posts) if max_posts else None
sub_name = handler['sub_name']
sub_name = cfg['sub_name']
for submission in reddit.subreddit(sub_name).new(limit=limit):
try:
@@ -185,7 +169,7 @@ def main() -> int:
for stored_post in posts.fetch_all():
try:
submission = reddit.submission(id=stored_post.post_id)
max_days = int(handler['max_days'])
max_days = int(cfg['max_days'])
created = utils.string_to_dt(stored_post.record_created).date()
flair = utils.get_flair(submission.link_flair_text)
@@ -199,7 +183,7 @@ def main() -> int:
if method not in ignore_methods:
send_modmail(
reddit,
handler['sub_name'],
cfg['sub_name'],
"User's account has been deleted",
utils.modmail_removal_notification(stored_post, 'Account has been deleted')
)
@@ -213,7 +197,7 @@ def main() -> int:
msg = utils.modmail_removal_notification(stored_post, method)
send_modmail(
reddit,
handler['sub_name'],
cfg['sub_name'],
'A post has been deleted',
msg
)
@@ -237,7 +221,7 @@ def main() -> int:
logger.info(f"Total posts deleted: {len(posts_to_delete)}")
# wait before the next cycle
sleep_minutes = int(handler['sleep_minutes']) if handler['sleep_minutes'] else 5
sleep_minutes = int(cfg.get('sleep_minutes', 5))
time.sleep(sleep_minutes * 60)
# end of while True

View File

@@ -55,27 +55,37 @@ Ban Template;
def parse_cmd_line_args(args: List[str], logger: Logger, config_file: Path, posts: Posts) -> bool:
"""Parse a very small set of operations from ``sys.argv``.
``config_file`` now refers to the Python configuration module path
(typically ``.../config/config.py``). ``reset_config`` will overwrite the
file with the default template, which is imported from the configuration
module itself so that it does not need to be duplicated here.
"""
help_msg = """Command line help prompt
Command: help
Args: []
Decription: Prints the help prompt
Description: Prints the help prompt
Command: reset_config
Args: []
Decription: Reset the bot credentials
Description: Overwrite the Python configuration file with default values
Command: reset_db
Args: []
Decription: Reset the database
Description: Reset the database
"""
if len(args) > 1:
if args[1] == 'help':
logger.info(help_msg)
elif args[1] == 'reset_config':
# write the template text back to the configuration file. import
# TEMPLATE lazily in case the module has not yet been created.
try:
os.remove(config_file)
except FileNotFoundError:
logger.error("No configuration file found")
from config import TEMPLATE
config_file.write_text(TEMPLATE)
except Exception:
logger.error("Unable to reset configuration file")
elif args[1] == 'reset_db':
try:
os.remove(posts.path)

View File

@@ -1,11 +1,19 @@
import unittest
import datetime as dt
from pathlib import Path
from .actions import (
Flair,
get_flair,
string_to_dt,
submission_is_older,
parse_cmd_line_args,
)
from logger import Logger
class DummyPosts:
def __init__(self, path):
self.path = path
class TestActions(unittest.TestCase):
@@ -46,3 +54,27 @@ class TestActions(unittest.TestCase):
post_made = today - dt.timedelta(days=(max_days + 1))
result = submission_is_older(post_made.date(), max_days)
self.assertTrue(result)
def test_parse_cmd_line_args_reset_config_and_db(self) -> None:
tmp = Path(__file__).parent / "tmp_test"
tmp.mkdir(exist_ok=True)
cfg_file = tmp / "config.py"
# ensure file exists with junk content
cfg_file.write_text("not important")
db_file = tmp / "db.sqlite"
db_file.write_text("x")
posts = DummyPosts(db_file)
logger = Logger(1)
# reset_config should rewrite the config file
result = parse_cmd_line_args(["prog", "reset_config"], logger, cfg_file, posts)
self.assertTrue(result)
self.assertTrue(cfg_file.exists())
content = cfg_file.read_text()
self.assertIn("client_id", content)
# reset_db should remove the database file
db_file.write_text("x")
result = parse_cmd_line_args(["prog", "reset_db"], logger, cfg_file, posts)
self.assertTrue(result)
self.assertFalse(db_file.exists())