]> git.gir.st - subscriptionfeed.git/blob - app/reddit/__init__.py
reddit: validate some parameters and use HTTPExceptions
[subscriptionfeed.git] / app / reddit / __init__.py
1 import re
2 import sqlite3
3 from flask_login import current_user, login_required
4 from flask import Blueprint, render_template, request, redirect, flash, url_for
5 from werkzeug.exceptions import BadRequest, BadGateway
6
7 from ..common.common import *
8 from .lib import *
9
10 frontend = Blueprint('reddit', __name__,
11 template_folder='templates',
12 static_folder='static',
13 static_url_path='/static/rd')
14
15 @frontend.route('/feed/subreddits')
16 @frontend.route('/r/<subreddit>/')
17 def reddit(subreddit=None):
18 token = getattr(current_user, 'token', 'guest')
19 after = request.args.get('after')
20
21 sortorder = request.args.get('s', "hot")
22 timerange = request.args.get('t', None)
23
24 if subreddit and not re.fullmatch(r"[-+_0-9A-Za-z]{2,21}", subreddit):
25 raise BadRequest("invalid subreddit")
26 if sortorder not in ("hot", "new", "rising", "controversial", "top"):
27 raise BadRequest("invalid sort order")
28 if timerange not in (None, "hour", "day", "week", "month", "year", "all"):
29 raise BadRequest("invalid top time range")
30
31 all_subreddits = get_subreddits(token)
32 subreddits = [subreddit] if subreddit else all_subreddits
33
34 if subreddits:
35 try:
36 data = fetch_reddit(subreddits,
37 sorted_by=sortorder, time=timerange, limit=36, after=after)
38 videos = parse_reddit_videos(data)
39 after = data['data']['after']
40 except RedditException as e:
41 raise BadGateway(f"error retrieving reddit data: {e}")
42
43 # set pin/hide stati of retrieved videos
44 video_ids = [v['video_id'] for v in videos]
45 pinned, hidden = fetch_video_flags(token, video_ids)
46 videos = sorted([
47 {**v, 'pinned': v['video_id'] in pinned}
48 for v in videos
49 if v['video_id'] not in hidden
50 ], key=lambda v:v['pinned'], reverse=True)
51 else: # not subscribed to anything
52 videos = []
53
54 title = f"/r/{subreddit}" if subreddit else "my subreddits"
55 return render_template('reddit.html.j2', title=title, rows=videos,
56 subreddits=all_subreddits, after=after)
57
58 @frontend.route('/manage/subreddits')
59 # disabled for guest user: @login_required
60 def subscription_manager():
61 token = getattr(current_user, 'token', 'guest')
62 subreddits = get_subreddits(token)
63 return render_template('subreddit_manager.html.j2', subreddits=subreddits)
64
65 @frontend.route('/manage/subreddits', methods=['POST'])
66 @login_required
67 def manage_subscriptions():
68 token = current_user.token
69 if 'subscribe' in request.form:
70 subreddit = request.form.get("subscribe")
71 match = re.search(r"(?:(?:https?://)?(?:old.|www.|\w\w.)?reddit.com)?(?:/?r/)?([-+_0-9A-Za-z]{2,21})", subreddit)
72 if match:
73 subreddit = match.group(1)
74 else:
75 flash("invalid subreddit", "error")
76 return redirect(request.url, code=303)
77 with sqlite3.connect(cf['global']['database']) as conn:
78 c = conn.cursor()
79 c.execute("""
80 INSERT OR IGNORE INTO subreddits (user, subreddit)
81 VALUES (?, ?)
82 """, (token, subreddit))
83
84 elif 'unsubscribe' in request.form:
85 subreddit = request.form.get("unsubscribe")
86 with sqlite3.connect(cf['global']['database']) as conn:
87 c = conn.cursor()
88 c.execute("""
89 DELETE FROM subreddits
90 WHERE user = ? AND subreddit = ?
91 """, (token, subreddit))
92 # TODO: sql-error-handling, report success
93
94 else:
95 flash("unsupported action", "error")
96
97 return redirect(request.url, code=303)
98
99 def get_subreddits(token):
100 with sqlite3.connect(cf['global']['database']) as conn:
101 c = conn.cursor()
102 c.execute("""
103 SELECT subreddit
104 FROM subreddits
105 WHERE user = ?
106 ORDER BY subreddit COLLATE NOCASE ASC
107 """, (token,))
108 subreddits = [sr for (sr,) in c.fetchall()]
109 return subreddits
110
111 @frontend.app_template_filter('trim3')
112 def trim3(n):
113 if type(n) != int:
114 return n # not a number
115 elif round(n, 1) >= 10_000:
116 return "%.0fk" % (n/1000)
117 elif n >= 1_000:
118 return "%.1fk" % (n/1000)
119 else:
120 return "%d" % n
121
122 @frontend.before_app_request
123 def inject_reddit_button():
124 if not 'header_items' in g:
125 g.header_items = []
126 g.header_items.append({
127 'name': 'reddit',
128 'url': url_for('reddit.reddit'),
129 'parent': frontend.name,
130 'priority': 5
131 })
Imprint / Impressum