Another shot at this.
[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(__file__)
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(
69                     f'Filter {filtered} struck down "{title}"'
70                 )
71                 continue
72             if msg.ups < self.min_votes:
73                 logger.debug(
74                     f'"{title}" doesn\'t have enough upvotes to be interesting'
75                 )
76                 continue
77
78             self.deduper.add(title)
79             content = f"{msg.ups}"
80             if (
81                     msg.thumbnail != "self"
82                     and msg.thumbnail != "default"
83                     and msg.thumbnail != ""
84             ):
85                 content = f'<IMG SRC="{msg.thumbnail}">'
86             self.messages.add(
87 f"""
88 <TABLE STYLE="font-size:{self.font_size}pt;">
89   <TR>
90     <!-- The number of upvotes or item image: -->
91     <TD STYLE="font-weight:900; padding:8px;">
92       <FONT COLOR="maroon" SIZE=40>{content}</FONT>
93     </TD>
94
95     <!-- The content and author: -->
96     <TD>
97       <B>{title}</B><BR><FONT COLOR=#bbbbbb>({msg.author})</FONT>
98     </TD>
99   </TR>
100 </TABLE>"""
101             )
102
103     def scrape_reddit(self) -> bool:
104         self.deduper.clear()
105         self.messages.clear()
106         for subreddit in self.subreddit_list:
107             try:
108                 msg = self.praw.subreddit(subreddit).hot()
109                 self.append_message(msg)
110             except:
111                 pass
112             try:
113                 msg = self.praw.subreddit(subreddit).new()
114                 self.append_message(msg)
115             except:
116                 pass
117             try:
118                 msg = self.praw.subreddit(subreddit).rising()
119                 self.append_message(msg)
120             except:
121                 pass
122             try:
123                 msg = self.praw.subreddit(subreddit).controversial("week")
124                 self.append_message(msg)
125             except:
126                 pass
127             try:
128                 msg = self.praw.subreddit(subreddit).top("day")
129                 self.append_message(msg)
130             except:
131                 pass
132             logger.debug(f"There are now {self.messages.size()} messages")
133         return True
134
135     def shuffle_messages(self) -> bool:
136         layout = page_builder.page_builder()
137         layout.set_layout(page_builder.page_builder.LAYOUT_FOUR_ITEMS)
138         x = ""
139         for subreddit in self.subreddit_list:
140             x += f"{subreddit} "
141         if len(x) > 30:
142             if "SeaWA" in x:
143                 x = "[local interests]"
144             else:
145                 x = "Unknown, fixme"
146         layout.set_title("Reddit /r/%s" % x.strip())
147         subset = self.messages.subset(4)
148         if subset is None:
149             logger.debug("Not enough messages to pick from.")
150             return False
151         for msg in subset:
152             layout.add_item(msg)
153         with file_writer.file_writer("%s_4_10800.html" % self.subreddit_list[0]) as f:
154             layout.render_html(f)
155         return True
156
157
158 class til_reddit_renderer(reddit_renderer):
159     def __init__(self, name_to_timeout_dict: Dict[str, int]):
160         super().__init__(
161             name_to_timeout_dict, ["todayilearned"], min_votes=100, font_size=20
162         )
163
164
165 class quotes_reddit_renderer(reddit_renderer):
166     def __init__(self, name_to_timeout_dict: Dict[str, int]):
167         super().__init__(
168             name_to_timeout_dict, ["quotes"], min_votes=100, font_size=20
169         )
170
171
172 class showerthoughts_reddit_renderer(reddit_renderer):
173     @staticmethod
174     def dont_tell_me_about_gift_cards(msg: str) -> bool:
175         return "gift card" in msg
176
177     def __init__(self, name_to_timeout_dict: Dict[str, int]):
178         super().__init__(
179             name_to_timeout_dict,
180             ["showerthoughts"],
181             min_votes=150,
182             additional_filters=[
183                 showerthoughts_reddit_renderer.dont_tell_me_about_gift_cards
184             ],
185         )
186
187
188 class seattle_reddit_renderer(reddit_renderer):
189     def __init__(self, name_to_timeout_dict: Dict[str, int]):
190         super().__init__(
191             name_to_timeout_dict,
192             ["seattle", "seattleWA", "SeaWA", "bellevue", "kirkland", "CoronavirusWA"],
193             min_votes=50,
194         )
195
196
197 class lifeprotips_reddit_renderer(reddit_renderer):
198     def __init__(self, name_to_timeout_dict: Dict[str, int]):
199         super().__init__(
200             name_to_timeout_dict, ["lifeprotips"], min_votes=50
201         )
202
203
204 #x = reddit_renderer({"Test", 1234}, ["seattle","bellevue"], min_votes=50, font_size=24)
205 #x.periodic_render("Scrape")
206 #x.periodic_render("Shuffle")