-
Notifications
You must be signed in to change notification settings - Fork 1
/
_reddit.py
181 lines (166 loc) · 6.37 KB
/
_reddit.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# -*- coding: utf-8 -*-
import config
from _util import dbg, mediamatch, gfycat, vreddit
from _pastebin import pastebin
from praw import Reddit
from prawcore import OAuthException, NotFound, BadRequest, Forbidden, Redirect
from random import choice
from re import match
from requests import get
from urllib.error import HTTPError
from json import loads, dumps
from datetime import datetime
from os.path import exists
from Crypto.Cipher import AES
import base64
class RedditException(Exception):
pass
class RedditWrapper:
def __init__(self):
self.client_id = config.reddit_id
self.client_secret = config.reddit_secret
self.refresh_token = config.reddit_token
self.gfycat_re = "^https://gfycat\.com/[A-Za-z]+$"
self.vreddit_re = "^https://v.redd.it/[0-9A-za-z]+$"
self.pb = pastebin()
self.cachefile = "re.c"
self.cachekey = None
self.reddit = self.mkreddit()
self.cache = {}
self.cipher = AES.new(config.reddit_secret[:16], AES.MODE_ECB)
self.loadcache()
def clearcache(self):
dbg("Clearing cache")
now = datetime.now()
for sub in list(self.cache):
for key in list(self.cache[sub]):
delta = (now - self.cache[sub][key]["time"]).days
if delta >= 3: del self.cache[sub][key]
def updatecache(self):
dbg("Updating reddit cache file")
json_raw = {}
for sub in self.cache:
json_raw[sub] = {}
for key in self.cache[sub]:
json_raw[sub][key] = { "list": self.cache[sub][key]["list"],
"time": self.cache[sub][key]["time"].timestamp() }
json_str = dumps(json_raw)
json_str = json_str + " " * (16 - (len(json_str) % 16))
json_enc = base64.b64encode(self.cipher.encrypt(json_str.encode("utf-8"))).decode("utf-8")
dbg("Encrypted, len is %d" % len(json_enc))
if self.cachekey:
try:
self.pb.deletepaste(self.cachekey)
except HTTPError:
self.cachekey = self.pb.getkey(self.cachefile)
self.pb.deletepaste(self.cachekey)
self.cachekey = self.pb.paste(self.cachefile, json_enc)
def loadcache(self):
dbg("Loading reddit cache file")
self.cachekey = self.pb.getkey(self.cachefile)
if not self.cachekey:
dbg("No cache paste found")
return
json_enc = self.pb.getpaste(self.cachekey)
json_raw = loads(self.cipher.decrypt(base64.b64decode(json_enc.encode("utf-8"))).decode("utf-8"))
for sub in json_raw:
self.cache[sub] = {}
for key in json_raw[sub]:
self.cache[sub][key] = { "list": json_raw[sub][key]["list"],
"time": datetime.fromtimestamp(json_raw[sub][key]["time"]) }
def mkreddit(self):
dbg("mkreddit function entrypoint")
return Reddit(client_id=self.client_id,
client_secret=self.client_secret,
refresh_token=self.refresh_token,
user_agent="styrilbot alpha")
def requestlist(self, sub, keywords):
try:
if keywords:
dbg("Forming search-by-keywords list in sub %s with keywords %s" % (sub, keywords))
ret = [u.url for u in list(self.reddit.subreddit(sub).search(keywords, sort="top", limit=500, params={'include_over_18': 'on'}))]
if not len(ret):
dbg("Search-by-keywords list is null-length")
raise RedditException("Nothing found in subreddit '%s' with keywords '%s'" % (sub, " ".join(keywords.split("+"))))
else:
dbg("Forming list without keywords in sub %s" % sub)
ret = [u.url for u in list(self.reddit.subreddit(sub).top(limit=500))]
if not len(ret):
raise RedditException("Looks like subreddit '%s' is empty" % sub)
except NotFound:
dbg("Got NotFound exception")
raise RedditException("Subreddit '%s' not found" % sub)
except Forbidden:
dbg("Got Forbidden exception")
raise RedditException("Subreddit '%s' is private" % sub)
except Redirect:
dbg("Got Redirect exception")
raise RedditException("Looks like subreddit '%s' is empty" % sub)
except BadRequest:
dbg("Got BadRequest exception")
raise RedditException("Something went wrong, try again")
return ret
def getlist(self, sub, keywords):
dbg("getlist function entrypoint")
if sub in self.cache and keywords in self.cache[sub]:
delta = (datetime.now() - self.cache[sub][keywords]["time"]).days
if delta >= 3:
dbg("Cache is old, updating")
ret = self.requestlist(sub, keywords)
self.cache[sub][keywords]["list"] = ret
self.cache[sub][keywords]["time"] = datetime.now()
self.clearcache()
self.updatecache()
else:
dbg("Using cached list")
ret = self.cache[sub][keywords]["list"]
else:
ret = self.requestlist(sub, keywords)
if not sub in self.cache: self.cache[sub] = {}
if not keywords in self.cache[sub]: self.cache[sub][keywords] = {}
self.cache[sub][keywords]["list"] = ret
self.cache[sub][keywords]["time"] = datetime.now()
self.updatecache()
return ret
def getmedia(self, sub, subs):
media = None
retries = 0
while not media:
retries += 1
if retries > 30:
dbg("Retries count exceeded")
raise RedditException("No media of required types found in subreddit '%s' in 30 retries" % sub)
dbg("Trying to find media in results list, retry #%d of 30" % retries)
url = choice(subs)
media = mediamatch(url)
if not media:
if match(self.gfycat_re, url):
dbg("Found gfycat url, retrieving gif url")
media = gfycat(url)
elif match(self.vreddit_re, url):
dbg("Found v.redd.it link, retrieving mp4 url")
media = vreddit(url)
return media
def process(self, command):
dbg("Processing command")
args = command.split()
if len(args) == 1:
dbg("No subreddit name given")
raise RedditException("No subreddit name given")
sub = args[1]
dbg("Sub is %s" % sub)
if len(args) > 2:
keywords = "+".join(args[2:])
dbg("Keywords are %s" % keywords)
else:
keywords = ''
dbg("No keywords given")
try:
dbg("Trying to get list with results")
subs = self.getlist(sub, keywords)
except OAuthException:
dbg("Got OAuthException, creating new Reddit instance")
self.reddit = self.mkreddit()
dbg("Rerying to get list with results")
subs = self.getlist(sub, keywords, message)
return self.getmedia(sub, subs)