Profanity filter, please.
[kiosk.git] / reddit_renderer.py
1 #!/usr/bin/env python3
2
3 import logging
4 from typing import Callable, Dict, Iterable, List, Set
5
6 import praw  # type: ignore
7
8 from scottutilz import profanity_filter
9
10 import file_writer
11 import grab_bag
12 import page_builder
13 import renderer
14 import kiosk_secrets as secrets
15
16
17 logger = logging.getLogger(__name__)
18
19
20 class reddit_renderer(renderer.abstaining_renderer):
21     """A renderer to pull text content from reddit."""
22
23     def __init__(
24         self,
25         name_to_timeout_dict: Dict[str, int],
26         subreddit_list: List[str],
27         *,
28         min_votes: int = 20,
29         font_size: int = 24,
30         additional_filters: Iterable[Callable[[str], bool]] = [],
31     ):
32         super().__init__(name_to_timeout_dict)
33         self.subreddit_list = subreddit_list
34         self.praw = praw.Reddit(
35             client_id=secrets.reddit_client_id,
36             client_secret=secrets.reddit_client_secret,
37             user_agent=secrets.reddit_user_agent,
38         )
39         self.min_votes = min_votes
40         self.font_size = font_size
41         self.messages = grab_bag.grab_bag()
42         self.filters: List[Callable[..., bool]] = [
43             profanity_filter.ProfanityFilter().contains_bad_word
44         ]
45         self.filters.extend(additional_filters)
46         self.deduper: Set[str] = set()
47
48     def periodic_render(self, key: str) -> bool:
49         logger.debug('called for "%s"' % key)
50         if key == "Scrape":
51             return self.scrape_reddit()
52         elif key == "Shuffle":
53             return self.shuffle_messages()
54         else:
55             raise Exception("Unexpected operation")
56
57     def append_message(self, messages: List[str]) -> None:
58         for msg in messages:
59             title = str(msg.title)
60             if title in self.deduper:
61                 continue
62             filtered = ""
63             for filt in self.filters:
64                 if filt(title) is True:
65                     filtered = filt.__name__
66                     break
67             if filtered != "":
68                 logger.info(f'Filter {filtered} struck down "{title}"')
69                 continue
70             if msg.ups < self.min_votes:
71                 logger.debug(
72                     f'"{title}" doesn\'t have enough upvotes to be interesting'
73                 )
74                 continue
75
76             self.deduper.add(title)
77             content = f"{msg.ups}"
78             if (
79                 msg.thumbnail != "self"
80                 and msg.thumbnail != "default"
81                 and msg.thumbnail != ""
82             ):
83                 content = f'<IMG SRC="{msg.thumbnail}">'
84             self.messages.add(
85                 f"""
86 <TABLE STYLE="font-size:{self.font_size}pt;">
87   <TR>
88     <!-- The number of upvotes or item image: -->
89     <TD STYLE="font-weight:900; padding:8px;">
90       <FONT COLOR="maroon" SIZE=40>{content}</FONT>
91     </TD>
92
93     <!-- The content and author: -->
94     <TD>
95       <B>{title}</B><BR><FONT COLOR=#bbbbbb>({msg.author})</FONT>
96     </TD>
97   </TR>
98 </TABLE>"""
99             )
100
101     def scrape_reddit(self) -> bool:
102         self.deduper.clear()
103         self.messages.clear()
104         for subreddit in self.subreddit_list:
105             try:
106                 msg = self.praw.subreddit(subreddit).hot()
107                 self.append_message(msg)
108             except:
109                 pass
110             try:
111                 msg = self.praw.subreddit(subreddit).new()
112                 self.append_message(msg)
113             except:
114                 pass
115             try:
116                 msg = self.praw.subreddit(subreddit).rising()
117                 self.append_message(msg)
118             except:
119                 pass
120             try:
121                 msg = self.praw.subreddit(subreddit).controversial("week")
122                 self.append_message(msg)
123             except:
124                 pass
125             try:
126                 msg = self.praw.subreddit(subreddit).top("day")
127                 self.append_message(msg)
128             except:
129                 pass
130             logger.debug(f"There are now {self.messages.size()} messages")
131         return True
132
133     def shuffle_messages(self) -> bool:
134         layout = page_builder.page_builder()
135         layout.set_layout(page_builder.page_builder.LAYOUT_FOUR_ITEMS)
136         x = ""
137         for subreddit in self.subreddit_list:
138             x += f"{subreddit} "
139         if len(x) > 30:
140             if "SeaWA" in x:
141                 x = "[local interests]"
142             else:
143                 x = "Unknown, fixme"
144         layout.set_title("Reddit /r/%s" % x.strip())
145         subset = self.messages.subset(4)
146         if subset is None:
147             logger.debug("Not enough messages to pick from.")
148             return False
149         for msg in subset:
150             layout.add_item(msg)
151         with file_writer.file_writer("%s_4_10800.html" % self.subreddit_list[0]) as f:
152             layout.render_html(f)
153         return True
154
155
156 class til_reddit_renderer(reddit_renderer):
157     def __init__(self, name_to_timeout_dict: Dict[str, int]):
158         super().__init__(
159             name_to_timeout_dict, ["todayilearned"], min_votes=100, font_size=20
160         )
161
162
163 class quotes_reddit_renderer(reddit_renderer):
164     def __init__(self, name_to_timeout_dict: Dict[str, int]):
165         super().__init__(name_to_timeout_dict, ["quotes"], min_votes=100, font_size=20)
166
167
168 class showerthoughts_reddit_renderer(reddit_renderer):
169     @staticmethod
170     def dont_tell_me_about_gift_cards(msg: str) -> bool:
171         return "gift card" in msg
172
173     def __init__(self, name_to_timeout_dict: Dict[str, int]):
174         super().__init__(
175             name_to_timeout_dict,
176             ["showerthoughts"],
177             min_votes=150,
178             additional_filters=[
179                 showerthoughts_reddit_renderer.dont_tell_me_about_gift_cards
180             ],
181         )
182
183
184 class seattle_reddit_renderer(reddit_renderer):
185     def __init__(self, name_to_timeout_dict: Dict[str, int]):
186         super().__init__(
187             name_to_timeout_dict,
188             ["seattle", "seattleWA", "SeaWA", "bellevue", "kirkland", "CoronavirusWA"],
189             min_votes=50,
190         )
191
192
193 class lifeprotips_reddit_renderer(reddit_renderer):
194     def __init__(self, name_to_timeout_dict: Dict[str, int]):
195         super().__init__(name_to_timeout_dict, ["lifeprotips"], min_votes=50)
196
197
198 # x = reddit_renderer({"Test", 1234}, ["seattle","bellevue"], min_votes=50, font_size=24)
199 # x.periodic_render("Scrape")
200 # x.periodic_render("Shuffle")