This repository was archived by the owner on Nov 24, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpost_qr.py
More file actions
182 lines (151 loc) · 8 KB
/
Copy pathpost_qr.py
File metadata and controls
182 lines (151 loc) · 8 KB
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
180
181
182
#!/usr/bin/python3
import praw
import OAuth2Util
import time
import requests
import json
import os
import humanize
import struct
from bs4 import BeautifulSoup
# sweet mother of imports
def get_cia_info(url):
"""
Takes a CIA url, and collects the titleID as well as the SMDH title/creator text in english.
It returns titleid, shortdesc, longdesc, publisher
"""
req = requests.get(url, headers={'Range': 'bytes=11292-11299'})
if req.headers['content-length'] != '8': # abort if we get a bad length
return None
titleid = "0x%0.16X" % struct.unpack('!Q', req.content)[0]
rawsize = req.headers['Content-Range'].split('/')[1]
req = requests.get(url,
headers={'Range': 'bytes=' + str(int(rawsize) - 12984) + "-" + str(int(rawsize) - 12984 + 511)})
if req.headers['content-length'] != '512': # abort if we get a bad length
return None
shortdesc = req.text[0:128].translate({ord(c): None for c in '\x00'}) # strip
longdesc = req.text[128:384].translate({ord(c): None for c in '\x00'}) # the
publisher = req.text[384:512].translate({ord(c): None for c in '\x00'}) # nulls!
req.close()
return (titleid, shortdesc, longdesc, publisher)
def determine_api_url(original_url):
"""
Convert our original reddit URL into a corresponding github API url for the release.
"""
upr_tokens = original_url.split('github.com/', 1)[1].split('/') # author, repo, "releases", "tags" tag
if len(upr_tokens) >= 2 and upr_tokens[0] and upr_tokens[1]: # make sure we have a user and a project at least
if len(upr_tokens) >= 5 and upr_tokens[4] is not '':
return "https://api.github.com/repos/" + upr_tokens[0] + "/" + upr_tokens[1] + "/releases/tags/" + \
upr_tokens[4]
url = "https://api.github.com/repos/" + upr_tokens[0] + "/" + upr_tokens[1] + "/tags"
try:
tag_name = json.loads(requests.get(url).text)[0]["name"] # one liners lol
return "https://api.github.com/repos/" + upr_tokens[0] + "/" + upr_tokens[1] + "/releases/tags/" + tag_name
except IndexError: # If one of the posts does not have any releases, it will not terminate the program
return None # This fixes the posts scanned prior not saving
return None
def make_qr(github_api_url, headers, auth):
"""
Takes a github api URL to get the direct download url and size, and uses google api to make a qr.
It returns a list of tuples containing the link to the qr, file name,
and the formatted file size, as well as the titleID, short description, long description,
and publisher fields from the SMDH data.
It also returns the body and name of the release.
"""
retlist = [] # define a blank list to return
req = requests.get(github_api_url, headers=headers, auth=auth)
data = json.loads(req.text)
if 'assets' in data:
for item in data['assets']:
if (item['name'][-3::]) == "cia": # if the download links have cia, make qr, else return None
url = item["browser_download_url"] # search for keys containing url and size
ciainfo = get_cia_info(url)
if ciainfo:
file_size = humanize.naturalsize(item['size'])
qr_url = ('https://chart.googleapis.com/chart?chs=300x300&cht=qr&chl=' + url + '&choe=UTF-8.png')
if ciainfo[0][0:6] == "0x0004": # only add to the list if this is a 3DS cia
body = data['body'].split('\r')
body = ' '.join(body)
retlist.append((qr_url, item['name'], file_size, ciainfo[0], ciainfo[1], ciainfo[2], ciainfo[3],
data['tag_name'], body, url))
req.close()
return retlist
def main():
r = praw.Reddit(r'3DS Homebrew QR Poster for /r/3DSHacks v1.0'
'By /u/Im_Soul')
o = OAuth2Util.OAuth2Util(r) # create reddit oauth
# o.refresh()
# files are opened from pwd
# cd to script's own directory to allow execution from elsewhere
os.chdir(os.path.dirname(os.path.realpath(__file__)))
gc = open('github_credentials.txt')
auth = [i for i in gc]
ghauth = (auth[0].rstrip('\n').rstrip('\r'), auth[1].rstrip('\n').rstrip('\r'))
gc.close()
headers = {
'User-Agent': '3DS-QR-Poster',
}
if os.path.isfile("posts_scanned.txt"): # check for posts_scanned.txt
with open("posts_scanned.txt", "r") as f: # if so, import the ids stored to the file
posts_scanned = list(filter(None, f.read().split("\n")))
else:
posts_scanned = [] # if not, make empty list to store ids
if os.path.isfile("run_log.txt"): # check for run_log.txt
with open("run_log.txt", "r") as l: # if so, import the ids stored to the file
run_log = list(filter(None, l.read().split("\n")))
else:
run_log = [] # otherwise, make an empty list
subreddit = r.get_subreddit('3dshacks') # subreddit to scan
for submission in subreddit.get_new(limit=5): # get 5 posts
if submission.id not in posts_scanned: # check if we already checked the id
if 'github.com' in submission.url.lower(): # check if url is github
comment = '' # blank out our comments
api_url = determine_api_url(submission.url)
elif 'gbatemp.net' in submission.url:
comment = ''
html_data = requests.get(submission.url)
soup = BeautifulSoup(html_data.text, "html.parser")
links = [link.get('href', '') for link in soup.find_all('a') \
if ("github" in link.get('href', '')) and ("release" in link.get('href', ''))]
if len(links) > 0:
api_url = determine_api_url(links[0])
else:
api_url = None
else:
api_url = None
if api_url is not None:
qrlist = make_qr(api_url, headers, ghauth)
if qrlist: # if 'make_qr()' was a success
item = 0
for qrentry in qrlist:
comment += \
'QR Code for [' + qrentry[1] + ' (' + qrentry[2] + ')](' + qrentry[0] + ') \n' + \
'\n' + \
'* Title ID: ' + qrentry[3] + ' \n' + \
'* Short Description: ' + qrentry[4] + ' \n' + \
'* Long Description: ' + qrentry[5] + ' \n' + \
'* Publisher: ' + qrentry[6] + ' \n\n' + \
'***** \n\n'
item += 1
if item == len(qrlist):
comment += \
'**Description for ' + qrentry[7] + ':** \n\n' + \
qrentry[8] + ' \n\n' + \
'***** \n'
if comment is not '': # check if we have anything to post
comment += '*[3DS QR Bot](https://github.com/nwk6661/3DS-QR-Poster)*'
submission.add_comment(comment)
print(comment)
log = "Replied to " + submission.id + " on " + time.asctime(time.localtime(time.time()))
requests.put("https://api.titledb.com/v1/submission",
data=json.dumps({"url": qrentry[9]}), headers=headers) # Add to titledb
run_log.append(log) # log post id and time a post was replied to
posts_scanned.append(submission.id) # add id to list
with open("posts_scanned.txt", "w") as f: # write from posts_scanned list to the file
for post_id in posts_scanned:
f.write(post_id + "\n")
with open("run_log.txt", "w") as l: # write from the run_log to the file
for log_entry in run_log:
l.write(log_entry + "\n")
if __name__ == '__main__':
main()