1- import asyncio
2- import functools
31import random
42import re
5- import urllib .parse
63from datetime import datetime
74
85import requests
6+ from yarl import URL
97
108from cloudbot import hook
119from cloudbot .util import timeformat , formatting
1210
13- reddit_re = re .compile (r'.*(((www\.)?reddit\.com/r|redd\.it)[^ ]+)' , re .I )
11+ reddit_re = re .compile (
12+ r"""
13+ https? # Scheme
14+ ://
1415
15- base_url = "http://reddit.com/r/{}/.json"
16- short_url = "http://redd.it/{}"
16+ # Domain
17+ (?:
18+ redd\.it|
19+ (?:www\.)?reddit\.com/r
20+ )
21+
22+ (?:/(?:[A-Za-z0-9!$&-.:;=@_~\u00A0-\u10FFFD]|%[A-F0-9]{2})*)* # Path
23+
24+ (?:\?(?:[A-Za-z0-9!$&-;=@_~\u00A0-\u10FFFD]|%[A-F0-9]{2})*)? # Query
25+ """ ,
26+ re .IGNORECASE | re .VERBOSE
27+ )
28+
29+ base_url = "https://reddit.com/r/{}"
30+ short_url = "https://redd.it/{}"
31+
32+
33+ def api_request (url , bot ):
34+ """
35+ :type url: yarl.URL
36+ :type bot: cloudbot.bot.CloudBot
37+ """
38+ url = url .with_query ("" ).with_scheme ("https" ) / ".json"
39+ r = requests .get (str (url ), headers = {'User-Agent' : bot .user_agent })
40+ r .raise_for_status ()
41+ return r .json ()
1742
1843
1944def format_output (item , show_url = False ):
@@ -40,72 +65,57 @@ def format_output(item, show_url=False):
4065 " - \x02 {author}\x02 , {timesince} ago{warning}" .format (** item )
4166
4267
43- @hook .regex (reddit_re )
68+ @hook .regex (reddit_re , singlethread = True )
4469def reddit_url (match , bot ):
45- url = match .group (1 )
46- if "redd.it" in url :
47- url = "http://" + url
48- response = requests .get (url )
49- url = response .url + "/.json"
50- if not urllib .parse .urlparse (url ).scheme :
51- url = "http://" + url + "/.json"
52-
53- # the reddit API gets grumpy if we don't include headers
54- headers = {'User-Agent' : bot .user_agent }
55- r = requests .get (url , headers = headers )
56- r .raise_for_status ()
57- if r .status_code != 200 :
58- return
59- data = r .json ()
60- assert isinstance (data , list ), "Reddit API returned data in an unknown format"
70+ url = match .group ()
71+ url = URL (url ).with_scheme ("https" )
72+
73+ if url .host .endswith ("redd.it" ):
74+ response = requests .get (url , headers = {'User-Agent' : bot .user_agent })
75+ response .raise_for_status ()
76+ url = URL (response .url ).with_scheme ("https" )
77+
78+ data = api_request (url , bot )
6179 item = data [0 ]["data" ]["children" ][0 ]["data" ]
6280
6381 return format_output (item )
6482
6583
66- @asyncio .coroutine
67- @hook .command (autohelp = False )
68- def reddit (text , bot , loop , reply ):
69- """<subreddit> [n] - gets a random post from <subreddit>, or gets the [n]th post in the subreddit"""
84+ @hook .command (autohelp = False , singlethread = True )
85+ def reddit (text , bot , reply ):
86+ """[subreddit] [n] - gets a random post from <subreddit>, or gets the [n]th post in the subreddit"""
7087 id_num = None
71- headers = {'User-Agent' : bot .user_agent }
7288
7389 if text :
7490 # clean and split the input
7591 parts = text .lower ().strip ().split ()
92+ url = base_url .format (parts .pop (0 ).strip ())
7693
7794 # find the requested post number (if any)
78- if len (parts ) > 1 :
79- url = base_url .format (parts [0 ].strip ())
95+ if parts :
8096 try :
81- id_num = int (parts [1 ]) - 1
97+ id_num = int (parts [0 ]) - 1
8298 except ValueError :
8399 return "Invalid post number."
84- else :
85- url = base_url .format (parts [0 ].strip ())
86100 else :
87- url = "http ://reddit.com/.json "
101+ url = "https ://reddit.com"
88102
89103 try :
90- # Again, identify with Reddit using an User Agent, otherwise get a 429
91- inquiry = yield from loop .run_in_executor (None , functools .partial (requests .get , url , headers = headers ))
92- inquiry .raise_for_status ()
93- if inquiry .status_code != 200 :
94- return "r/{} either does not exist or is private." .format (text )
95- data = inquiry .json ()
104+ data = api_request (URL (url ), bot )
96105 except Exception as e :
97106 reply ("Error: " + str (e ))
98107 raise
108+
99109 data = data ["data" ]["children" ]
100110
101111 # get the requested/random post
102112 if id_num is not None :
103113 try :
104- item = data [id_num ][ "data" ]
114+ item = data [id_num ]
105115 except IndexError :
106116 length = len (data )
107117 return "Invalid post number. Number must be between 1 and {}." .format (length )
108118 else :
109- item = random .choice (data )[ "data" ]
119+ item = random .choice (data )
110120
111- return format_output (item , show_url = True )
121+ return format_output (item [ "data" ] , show_url = True )
0 commit comments