Skip to content

Commit

Permalink
Implement storing scheduled posts on Reddit's wiki
Browse files Browse the repository at this point in the history
- Add the `ScheduleWikiStorage` class
  • Loading branch information
vaclav-2012 committed Jan 22, 2021
1 parent 8c45ee1 commit b9c083c
Show file tree
Hide file tree
Showing 11 changed files with 435 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ We follow [Semantic Versions](https://semver.org/).
- Add `Schedule`, `ScheduleStorage` and `ScheduleFileStorage` classes for storing scheduled posts
- Change the post scheduling to the UTC time
- Implement the post scheduling based on the data provided by the `Schedule` class
- Add the `ScheduleWikiStorage` class for storing scheduled posts on Reddit's wiki


## Version 0.1.8
Expand Down
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Provide a command-line utility for hosting an awesome Slow Start Rewatch.

- Schedule a submission of multiple Reddit posts
- Templates-based posts with dynamically updated link references to other posts
- Store the post schedule in the local file
- Store the post schedule in a local file or Reddit's wiki
- Reddit authorization via OAuth2 using a local HTTP server with cute GIFs
- Storing the refresh token locally to keep the authorization active
- Submitting text posts with thumbnails
Expand All @@ -38,12 +38,27 @@ pip install slow-start-rewatch

## Usage

Launch the program from the command line:
When started for the first time the location of the schedule must be set.

1. Using the schedule stored in Reddit's wiki:

```bash
slow-start-rewatch -w /r/subreddit/wiki/wiki-path
```

2. Using the schedule stored in the local YAML file:

```bash
slow-start-rewatch -f /path/to/the/schedule.yml
```

After the location of the schedule is stored in the local config, the program can be launched without any parameters:

```bash
slow-start-rewatch
```


## License

[MIT](https://github.com/slow-start-fans/slow-start-rewatch/blob/master/LICENSE)
Expand Down
7 changes: 6 additions & 1 deletion slow_start_rewatch/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,23 @@

@click.command()
@click.option("--debug", is_flag=True)
@click.option("-w", "--schedule_wiki_url")
@click.option("-f", "--schedule_file")
@click.version_option(version=version(), prog_name=distribution_name)
def main(
debug: bool,
schedule_wiki_url: Optional[str],
schedule_file: Optional[str],
) -> None:
"""Main entry point for CLI."""
if debug:
logging.getLogger().setLevel(logging.DEBUG)

try:
App(schedule_file=schedule_file).run()
App(
schedule_wiki_url=schedule_wiki_url,
schedule_file=schedule_file,
).run()
except SlowStartRewatchException as exception:
click.echo(click.style(str(exception), fg="red"), err=True)

Expand Down
7 changes: 6 additions & 1 deletion slow_start_rewatch/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@ class App(object):

def __init__(
self,
schedule_wiki_url: Optional[str] = None,
schedule_file: Optional[str] = None,
) -> None:
"""Initialize App."""
config = Config()
config.load()

if schedule_file:
if schedule_wiki_url:
config["schedule_wiki_url"] = schedule_wiki_url
config["schedule_file"] = None
elif schedule_file:
config["schedule_file"] = schedule_file
config["schedule_wiki_url"] = None

self.reddit_cutifier = RedditCutifier(config)
self.timer = Timer(config)
Expand Down
1 change: 1 addition & 0 deletions slow_start_rewatch/config_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def __init__(self, local_config_file: str) -> None:

self.locally_stored_items = [
"refresh_token",
"schedule_wiki_url",
"schedule_file",
]

Expand Down
150 changes: 150 additions & 0 deletions slow_start_rewatch/schedule/schedule_wiki_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# -*- coding: utf-8 -*-

import re
import textwrap

from praw import Reddit
from prawcore.exceptions import Forbidden, NotFound, PrawcoreException
from structlog import get_logger

from slow_start_rewatch.config import Config
from slow_start_rewatch.exceptions import (
InvalidWikiLink,
MissingPost,
MissingSchedule,
RedditError,
)
from slow_start_rewatch.schedule.schedule_storage import ScheduleStorage

log = get_logger()


class ScheduleWikiStorage(ScheduleStorage):
"""Stores data about scheduled posts in Reddit wiki."""

def __init__(
self,
config: Config,
reddit: Reddit,
) -> None:
"""Initialize ScheduleWikiStorage."""
super().__init__()

self.reddit = reddit

schedule_wiki_url: str = config["schedule_wiki_url"]

if not schedule_wiki_url:
raise RuntimeError(
"The config must contain 'schedule_wiki_url' item.",
)

match = re.search(
r"\/r\/(?P<subreddit>[^\/]+)\/wiki\/(?P<path>.+)",
schedule_wiki_url,
)

if not match:
raise InvalidWikiLink(
"The link to the schedule wiki page is invalid: {0}".format(
schedule_wiki_url,
),
)

self.wiki_path = match.group("path")
self.wiki_subreddit = match.group("subreddit")

self.wiki = self.reddit.subreddit(self.wiki_subreddit).wiki

def load_schedule_data(self) -> str:
"""Load schedule data from the wiki."""
log.info(
"schedule_wiki_read",
subreddit=self.wiki_subreddit,
wiki_path=self.wiki_path,
)

try:
schedule_data = self.wiki[self.wiki_path].content_md
except NotFound as error:
log.exception("schedule_wiki_missing")
raise MissingSchedule(
"The schedule wiki page not found: /r/{0}/wiki/{1}".format(
self.wiki_subreddit,
self.wiki_path,
),
) from error
except Forbidden as error:
log.exception("schedule_wiki_access_denied")
raise MissingSchedule(
"Missing permissions to access the schedule wiki page: " +
"/r/{0}/wiki/{1}".format(
self.wiki_subreddit,
self.wiki_path,
),
) from error

return schedule_data

def load_post_body(self, body_template_source: str) -> str:
"""Load a post body from the wiki."""
wiki_path = "{0}/{1}".format(
self.wiki_path,
body_template_source,
)
log.info(
"post_wiki_read",
subreddit=self.wiki_subreddit,
wiki_path=wiki_path,
)

try:
post_body = self.wiki[wiki_path].content_md
except NotFound as error:
log.exception("post_wiki_missing")
raise MissingPost(
"The post wiki page not found: /r/{0}/wiki/{1}".format(
self.wiki_subreddit,
wiki_path,
),
) from error
except Forbidden as error:
log.exception("post_wiki_access_denied")
raise MissingPost(
"Missing permissions to access the post wiki page: " +
"/r/{0}/wiki/{1}".format(
self.wiki_subreddit,
wiki_path,
),
) from error

return post_body

def save_schedule_data(self, schedule_data: str) -> None:
"""
Save the schedule data to the wiki.
The content is indented by 4 spaces for better formatting on Reddit.
"""
log.info(
"schedule_wiki_update",
subreddit=self.wiki_subreddit,
wiki_path=self.wiki_path,
)
schedule_data = textwrap.indent(text=schedule_data, prefix=" ")

try:
self.wiki[self.wiki_path].edit(
content=schedule_data,
reason="Rewatch Update",
)
except (PrawcoreException, KeyError) as error:
log.exception("schedule_wiki_update_failed")
raise RedditError(
"Failed to update the schedule wiki page: " +
"/r/{0}/wiki/{1} Error: {2}".format(
self.wiki_subreddit,
self.wiki_path,
str(error),
),
) from error
9 changes: 7 additions & 2 deletions slow_start_rewatch/schedule/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
ScheduleFileStorage,
)
from slow_start_rewatch.schedule.schedule_storage import ScheduleStorage
from slow_start_rewatch.schedule.schedule_wiki_storage import (
ScheduleWikiStorage,
)

log = get_logger()

Expand All @@ -33,12 +36,14 @@ def __init__(
self.post_helper = PostHelper(config, reddit)

self.schedule_storage: ScheduleStorage
if config["schedule_file"]:
if config["schedule_wiki_url"]:
self.schedule_storage = ScheduleWikiStorage(config, reddit)
elif config["schedule_file"]:
self.schedule_storage = ScheduleFileStorage(config)
else:
raise MissingSchedule(
"Schedule storage not defined.",
hint="The Schedule must be stored in a file.",
hint="The Schedule must be stored in a file or Reddit's wiki.",
)

def load(self) -> None:
Expand Down
4 changes: 4 additions & 0 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ def test_init(
config = mock_config.return_value

App()
assert "schedule_wiki_url" not in config
assert "schedule_file" not in config

App(schedule_wiki_url="/r/anime/wiki/slow-start-rewatch")
assert config["schedule_wiki_url"] == "/r/anime/wiki/slow-start-rewatch"

App(schedule_file="schedule.yml")
assert config["schedule_file"] == "schedule.yml"

Expand Down
1 change: 1 addition & 0 deletions tests/test_config_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,6 @@ def config_data():
"""Return mock Config data."""
return {
"refresh_token": "moe_moe_kyun",
"schedule_wiki_url": None,
"schedule_file": "slow_start_rewatch/schedule.yml",
}
Loading

0 comments on commit b9c083c

Please sign in to comment.