From 15f20f329120339e8c82672d860965af052bbe2a Mon Sep 17 00:00:00 2001 From: Akshay Date: Thu, 29 Aug 2024 22:14:18 +0100 Subject: init --- src/geddit.js | 392 +++++++++++++++++++++++++++++++++++++++++++++++++ src/index.js | 17 +++ src/routes/index.js | 35 +++++ src/views/comment.pug | 18 +++ src/views/comments.pug | 25 ++++ src/views/index.pug | 19 +++ src/views/post.pug | 21 +++ src/views/utils.pug | 3 + 8 files changed, 530 insertions(+) create mode 100644 src/geddit.js create mode 100644 src/index.js create mode 100644 src/routes/index.js create mode 100644 src/views/comment.pug create mode 100644 src/views/comments.pug create mode 100644 src/views/index.pug create mode 100644 src/views/post.pug create mode 100644 src/views/utils.pug (limited to 'src') diff --git a/src/geddit.js b/src/geddit.js new file mode 100644 index 0000000..825c3a9 --- /dev/null +++ b/src/geddit.js @@ -0,0 +1,392 @@ +class Geddit { + constructor() { + this.host = "https://www.reddit.com"; + this.parameters = { + limit: 25, + include_over_18: true, + } + this.search_params = { + limit: 25, + include_over_18: true, + type: "sr,link,user", + } + } + + async getSubmissions(sort = null, subreddit = null, options = {}) { + let params = { + limit: 25, + include_over_18: true, + } + + sort = sort ? sort : "hot"; + subreddit = subreddit ? "/r/" + subreddit : ""; + + return await fetch(this.host + subreddit + `/${sort}.json?` + new URLSearchParams(Object.assign(params, options))) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + posts: data.children + })) + .catch(err => null); + + } + + async getDomainHot(domain, options = this.parameters) { + return await fetch(this.host + "/domain/" + domain + "/hot.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + posts: data.children + })) + .catch(err => null); + } + + async getDomainBest(domain, options = this.parameters) { + return await fetch(this.host + "/domain/" + domain + "/best.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + posts: data.children + })) + .catch(err => null); + } + + async getDomainTop(domain, options = this.parameters) { + return await fetch(this.host + "/domain/" + domain + "/top.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + posts: data.children + })) + .catch(err => null); + } + + async getDomainNew(domain, options = this.parameters) { + return await fetch(this.host + "/domain/" + domain + "/new.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + posts: data.children + })) + .catch(err => null); + } + + async getDomainRising(domain, options = this.parameters) { + return await fetch(this.host + "/domain/" + domain + "/rising.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + posts: data.children + })) + .catch(err => null); + } + + async getDomainControversial(domain, options = this.parameters) { + return await fetch(this.host + "/domain/" + domain + "/controversial.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + posts: data.children + })) + .catch(err => null); + } + + async getSubreddit(subreddit) { + return await fetch(this.host + "/r/" + subreddit + "/about.json") + .then(res => res.json()) + .then(json => json.data) + .catch(err => null); + } + + async getSubredditRules(subreddit) { + return await fetch(this.host + "/r/" + subreddit + "/about/rules.json") + .then(res => res.json()) + .then(json => json.data) + .catch(err => null); + } + + async getSubredditModerators(subreddit) { + return await fetch(this.host + "/r/" + subreddit + "/about/moderators.json") + .then(res => res.json()) + .then(json => json.data) + .then(data = ({ + users: data.children + })) + .catch(err => null); + } + + async getSubredditWikiPages(subreddit) { + return await fetch(this.host + "/r/" + subreddit + "/wiki/pages.json") + .then(res => res.json()) + .then(json => json.data) + .catch(err => null); + } + + async getSubredditWikiPage(subreddit, page) { + return await fetch(this.host + "/r/" + subreddit + "/wiki/" + page + ".json") + .then(res => res.json()) + .then(json => json.data) + .catch(err => null); + } + + async getSubredditWikiPageRevisions(subreddit, page) { + return await fetch(this.host + "/r/" + subreddit + "/wiki/revisions" + page + ".json") + .then(res => res.json()) + .then(json => json.data.children) + .catch(err => null); + } + + async getPopularSubreddits(options = this.parameters) { + return await fetch(this.host + "/subreddits/popular.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + subreddits: data.children + })) + .catch(err => null); + } + + async getNewSubreddits(options = this.parameters) { + return await fetch(this.host + "/subreddits/new.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + subreddits: data.children + })) + .catch(err => null); + } + + async getPremiumSubreddits(options = this.parameters) { + return await fetch(this.host + "/subreddits/premium.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + subreddits: data.children + })) + .catch(err => null); + } + + async getDefaultSubreddits(options = this.parameters) { + return await fetch(this.host + "/subreddits/default.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + subreddits: data.children + })) + .catch(err => null); + } + + async getPopularUsers(options = this.parameters) { + return await fetch(this.host + "/users/popular.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + users: data.children + })) + .catch(err => null); + } + + async getNewUsers(options = this.parameters) { + return await fetch(this.host + "/users/new.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + users: data.children + })) + .catch(err => null); + } + + async searchSubmissions(query, options = {}) { + options.q = query; + options.type = "link"; + + let params = { + limit: 25, + include_over_18: true + } + + console.log(this.host + "/search.json?" + new URLSearchParams(Object.assign(params, options))); + + return await fetch(this.host + "/search.json?" + new URLSearchParams(Object.assign(params, options))) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + items: data.children + })) + .catch(err => null); + } + + async searchSubreddits(query, options = {}) { + options.q = query; + + let params = { + limit: 25, + include_over_18: true + } + + return await fetch(this.host + "/subreddits/search.json?" + new URLSearchParams(Object.assign(params, options))) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + items: data.children + })) + .catch(err => null); + } + + async searchUsers(query, options = {}) { + options.q = query; + + let params = { + limit: 25, + include_over_18: true + } + + return await fetch(this.host + "/users/search.json?" + new URLSearchParams(Object.assign(params, options))) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + items: data.children + })) + .catch(err => null); + } + + async searchAll(query, subreddit = null, options = {}) { + options.q = query; + subreddit = subreddit ? "/r/" + subreddit : ""; + + let params = { + limit: 25, + include_over_18: true, + type: "sr,link,user", + } + + return await fetch(this.host + subreddit + "/search.json?" + new URLSearchParams(Object.assign(params, options))) + .then(res => res.json()) + .then(json => Array.isArray(json) ? ({ + after: json[1].data.after, + items: json[0].data.children.concat(json[1].data.children) + }) : ({ + after: json.data.after, + items: json.data.children + })) + .catch(err => null); + } + + async getSubmission(id) { + return await fetch(this.host + "/by_id/" + id + ".json") + .then(res => res.json()) + .then(json => json.data.children[0].data) + .catch(err => null); + } + + async getSubmissionComments(id, options = this.parameters) { + return await fetch(this.host + "/comments/" + id + ".json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => ({ + submission: json[0].data.children[0], + comments: json[1].data.children + })) + .catch(err => null); + } + + async getSubredditComments(subreddit, options = this.parameters) { + return await fetch(this.host + "/r/" + subreddit + "/comments.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data.children) + .catch(err => null); + } + + async getUser(username) { + return await fetch(this.host + "/user/" + username + "/about.json") + .then(res => res.json()) + .then(json => json.data) + .catch(err => null); + } + + async getUserOverview(username, options = this.parameters) { + return await fetch(this.host + "/user/" + username + "/overview.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + items: data.children + })) + .catch(err => null); + } + + async getUserComments(username, options = this.parameters) { + return await fetch(this.host + "/user/" + username + "/comments.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + items: data.children + })) + .catch(err => null); + } + + async getUserSubmissions(username, options = this.parameters) { + return await fetch(this.host + "/user/" + username + "/submitted.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data) + .then(data => ({ + after: data.after, + items: data.children + })) + .catch(err => null); + } + + async getLiveThread(id) { + return await fetch(this.host + "/live/" + id + "/about.json") + .then(res => res.json()) + .then(json => json.data) + .catch(err => null); + } + + async getLiveThreadUpdates(id, options = this.parameters) { + return await fetch(this.host + "/live/" + id + ".json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data.children) + .catch(err => null); + } + + + async getLiveThreadContributors(id, options = this.parameters) { + return await fetch(this.host + "/live/" + id + "/contributors.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data.children) + .catch(err => null); + } + + async getLiveThreadDiscussions(id, options = this.parameters) { + return await fetch(this.host + "/live/" + id + "/discussions.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data.children) + .catch(err => null); + } + + async getLiveThreadsNow(options = this.parameters) { + return await fetch(this.host + "/live/happening_now.json?" + new URLSearchParams(options)) + .then(res => res.json()) + .then(json => json.data.children) + .catch(err => null); + } +} + +export { Geddit } diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..bfe31f4 --- /dev/null +++ b/src/index.js @@ -0,0 +1,17 @@ +const express = require('express'); +const path = require('path'); +const routes = require('./routes/index'); +const geddit = require('./geddit.js'); + +const app = express(); + +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'pug'); + +app.use(express.static('public')); +app.use('/', routes); + +const server = app.listen(3000, () => { + console.log(`started on ${server.address().port}`); +}); + diff --git a/src/routes/index.js b/src/routes/index.js new file mode 100644 index 0000000..6cf5403 --- /dev/null +++ b/src/routes/index.js @@ -0,0 +1,35 @@ +const express = require('express'); +const router = express.Router(); +const geddit = require('../geddit.js'); +const G = new geddit.Geddit(); +const fs = require('fs/promises'); + + +// GET / +router.get('/', async (req, res) => { + res.redirect("/r/all") +}); + +// GET /r/:id +router.get('/r/:subreddit', async (req, res) => { + var subreddit = req.params.subreddit; + + var postsReq = G.getSubmissions(`r/${subreddit}`); + var aboutReq = G.getSubreddit(`${subreddit}`); + + var [posts, about] = await Promise.all([postsReq, aboutReq]); + res.render('index', { subreddit, posts, about }); +}); + +// GET /comments/:id +router.get('/comments/:id', async (req, res) => { + var id = req.params.id; + + response = await G.getSubmissionComments(id); + var post = response.submission.data; + var comments = response.comments; + + res.render('comments', { post, comments }); +}); + +module.exports = router; diff --git a/src/views/comment.pug b/src/views/comment.pug new file mode 100644 index 0000000..24e1a9b --- /dev/null +++ b/src/views/comment.pug @@ -0,0 +1,18 @@ +include utils +mixin comment(com, isfirst) + - var data = com.data + - var kind = com.kind + if kind == "more" + div.more #{data.count} more comments + else + div(class=`comment ${isfirst?'first':''}`) + div.comment-body !{data.body} + div.info-container + div.info-item by u/#{data.author} + div.info-item ↑ #{fmtnum(data.ups)} + div.replies + if data.replies + if data.replies.data + if data.replies.data.children + each reply in data.replies.data.children + +comment(reply,false) diff --git a/src/views/comments.pug b/src/views/comments.pug new file mode 100644 index 0000000..f7964a3 --- /dev/null +++ b/src/views/comments.pug @@ -0,0 +1,25 @@ +doctype html +html + head + meta(charset='UTF-8') + title reddit + link(rel='stylesheet', href='/styles.css') + body + main#content + div.header + a(href=`/r/${post.subreddit}`) + h4 ← r/#{post.subreddit} + h2 #{post.title} + if post.post_hint == 'image' + img(src=post.url).post-media + else if post.post_hint == 'hosted:video' + video(src=post.url).post-media + p.self-text !{post.selftext} + hr + + div.comments-container + each child in comments + include comment + +comment(child, true) + + script(src='https://unpkg.com/htmx.org@1.9.10') diff --git a/src/views/index.pug b/src/views/index.pug new file mode 100644 index 0000000..d017570 --- /dev/null +++ b/src/views/index.pug @@ -0,0 +1,19 @@ +doctype html +html + head + meta(charset='UTF-8') + title reddit + link(rel='stylesheet', href='/styles.css') + body + main#content + div.header + a(href=`/r/#{subreddit}`) + h1 r/#{subreddit} + if about + p #{about.public_description} + + each child in posts.posts + include post + +post(child.data) + + script(src='https://unpkg.com/htmx.org@1.9.10') diff --git a/src/views/post.pug b/src/views/post.pug new file mode 100644 index 0000000..77ef3f5 --- /dev/null +++ b/src/views/post.pug @@ -0,0 +1,21 @@ +include utils +mixin post(p) + article.post + div.post-container + div.post-text + div.title-container !{p.title} + div.info-container + div.info-item by u/#{p.author} + div.info-item ↑ #{fmtnum(p.ups)} + div.info-item #{p.domain} + div.info-item + a(href=`/r/${p.subreddit}`) r/#{p.subreddit} + div.info-item + a(href=`/comments/${p.id}`) #{fmtnum (p.num_comments)} #{fmttxt(p.num_comments, 'comment')} + div.media-preview + if p.post_hint == "image" || p.post_hint == "link" + if p.thumbnail && p.thumbnail != "self" || p.thumbnail != "default" + a(href=p.url) + img(src=p.thumbnail width='100px') + else if p.post_hint == "hosted:video" + video(src=p.secure_media.reddit_video.scrubber_media_url width='100px') diff --git a/src/views/utils.pug b/src/views/utils.pug new file mode 100644 index 0000000..f3f61bb --- /dev/null +++ b/src/views/utils.pug @@ -0,0 +1,3 @@ +- var fmtnum = (n)=>n>=1000?(n/1000).toFixed(1)+'k':n; +- var fmttxt = (n,t)=>`${t}${n==1?'':'s'}` + -- cgit v1.2.3