selfhostable, read-only reddit client

add biome

+2
.gitignore
··· 177 177 reference 178 178 bun.lockb 179 179 result 180 + 181 + .direnv
+12
biome.json
··· 1 + { 2 + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", 3 + "organizeImports": { 4 + "enabled": true 5 + }, 6 + "linter": { 7 + "enabled": true, 8 + "rules": { 9 + "recommended": true 10 + } 11 + } 12 + }
+4 -4
build.js
··· 1 1 import { execSync } from "bun"; 2 2 3 3 try { 4 - // Precompile Pug templates in the `src` directory to the `dist` directory 5 - execSync("pug src -o dist"); 6 - console.log("Pug templates compiled successfully."); 4 + // Precompile Pug templates in the `src` directory to the `dist` directory 5 + execSync("pug src -o dist"); 6 + console.log("Pug templates compiled successfully."); 7 7 } catch (error) { 8 - console.error("Failed to compile Pug templates:", error); 8 + console.error("Failed to compile Pug templates:", error); 9 9 }
+1 -1
flake.nix
··· 83 83 pkgs.mkShell { 84 84 nativeBuildInputs = [ 85 85 pkgs.bun 86 + pkgs.biome 86 87 ]; 87 - RUST_BACKTRACE = 1; 88 88 }); 89 89 90 90 packages = forAllSystems (system: {
+17 -17
package.json
··· 1 1 { 2 - "name": "scarlet", 3 - "module": "index.ts", 4 - "type": "module", 5 - "devDependencies": { 6 - "@types/bun": "latest", 7 - "pug-cli": "^1.0.0-alpha6" 8 - }, 9 - "peerDependencies": { 10 - "typescript": "^5.0.0" 11 - }, 12 - "dependencies": { 13 - "express": "^4.19.2", 14 - "he": "^1.2.0", 15 - "pug": "^3.0.3", 16 - "timeago.js": "^4.0.2" 17 - } 18 - } 2 + "name": "scarlet", 3 + "module": "index.ts", 4 + "type": "module", 5 + "devDependencies": { 6 + "@types/bun": "latest", 7 + "pug-cli": "^1.0.0-alpha6" 8 + }, 9 + "peerDependencies": { 10 + "typescript": "^5.0.0" 11 + }, 12 + "dependencies": { 13 + "express": "^4.19.2", 14 + "he": "^1.2.0", 15 + "pug": "^3.0.3", 16 + "timeago.js": "^4.0.2" 17 + } 18 + }
+4
readme.txt
··· 13 13 - [x] fix spacing between comments 14 14 - [x] collapse even singular comments 15 15 - [ ] highlights for op, sticky etc. 16 + - [ ] support 'more comments' 17 + - [ ] avoid js to toggle details in views/index.pug 18 + - [ ] set home to sum of subs 19 + - [ ] details tag on safari
+462 -342
src/geddit.js
··· 1 1 class Geddit { 2 - constructor() { 3 - this.host = "https://www.reddit.com"; 4 - this.parameters = { 5 - limit: 25, 6 - include_over_18: true, 7 - } 8 - this.search_params = { 9 - limit: 25, 10 - include_over_18: true, 11 - type: "sr,link,user", 12 - } 13 - } 2 + constructor() { 3 + this.host = "https://www.reddit.com"; 4 + this.parameters = { 5 + limit: 25, 6 + include_over_18: true, 7 + }; 8 + this.search_params = { 9 + limit: 25, 10 + include_over_18: true, 11 + type: "sr,link,user", 12 + }; 13 + } 14 14 15 - async getSubmissions(sort = null, subreddit = null, options = {}) { 16 - let params = { 17 - limit: 20, 18 - include_over_18: true, 19 - } 15 + async getSubmissions(sort = null, subreddit = null, options = {}) { 16 + const params = { 17 + limit: 20, 18 + include_over_18: true, 19 + }; 20 20 21 - sort = sort ? sort : "hot"; 22 - subreddit = subreddit ? "/r/" + subreddit : ""; 21 + sort = sort ? sort : "hot"; 22 + subreddit = subreddit ? "/r/" + subreddit : ""; 23 23 24 - return await fetch(this.host + subreddit + `/${sort}.json?` + new URLSearchParams(Object.assign(params, options))) 25 - .then(res => res.json()) 26 - .then(json => json.data) 27 - .then(data => ({ 28 - after: data.after, 29 - posts: data.children 30 - })) 31 - .catch(err => null); 24 + return await fetch( 25 + this.host + 26 + subreddit + 27 + `/${sort}.json?` + 28 + new URLSearchParams(Object.assign(params, options)), 29 + ) 30 + .then((res) => res.json()) 31 + .then((json) => json.data) 32 + .then((data) => ({ 33 + after: data.after, 34 + posts: data.children, 35 + })) 36 + .catch((err) => null); 37 + } 32 38 33 - } 39 + async getDomainHot(domain, options = this.parameters) { 40 + return await fetch( 41 + this.host + 42 + "/domain/" + 43 + domain + 44 + "/hot.json?" + 45 + new URLSearchParams(options), 46 + ) 47 + .then((res) => res.json()) 48 + .then((json) => json.data) 49 + .then((data) => ({ 50 + after: data.after, 51 + posts: data.children, 52 + })) 53 + .catch((err) => null); 54 + } 34 55 35 - async getDomainHot(domain, options = this.parameters) { 36 - return await fetch(this.host + "/domain/" + domain + "/hot.json?" + new URLSearchParams(options)) 37 - .then(res => res.json()) 38 - .then(json => json.data) 39 - .then(data => ({ 40 - after: data.after, 41 - posts: data.children 42 - })) 43 - .catch(err => null); 44 - } 56 + async getDomainBest(domain, options = this.parameters) { 57 + return await fetch( 58 + this.host + 59 + "/domain/" + 60 + domain + 61 + "/best.json?" + 62 + new URLSearchParams(options), 63 + ) 64 + .then((res) => res.json()) 65 + .then((json) => json.data) 66 + .then((data) => ({ 67 + after: data.after, 68 + posts: data.children, 69 + })) 70 + .catch((err) => null); 71 + } 45 72 46 - async getDomainBest(domain, options = this.parameters) { 47 - return await fetch(this.host + "/domain/" + domain + "/best.json?" + new URLSearchParams(options)) 48 - .then(res => res.json()) 49 - .then(json => json.data) 50 - .then(data => ({ 51 - after: data.after, 52 - posts: data.children 53 - })) 54 - .catch(err => null); 55 - } 56 - 57 - async getDomainTop(domain, options = this.parameters) { 58 - return await fetch(this.host + "/domain/" + domain + "/top.json?" + new URLSearchParams(options)) 59 - .then(res => res.json()) 60 - .then(json => json.data) 61 - .then(data => ({ 62 - after: data.after, 63 - posts: data.children 64 - })) 65 - .catch(err => null); 66 - } 67 - 68 - async getDomainNew(domain, options = this.parameters) { 69 - return await fetch(this.host + "/domain/" + domain + "/new.json?" + new URLSearchParams(options)) 70 - .then(res => res.json()) 71 - .then(json => json.data) 72 - .then(data => ({ 73 - after: data.after, 74 - posts: data.children 75 - })) 76 - .catch(err => null); 77 - } 73 + async getDomainTop(domain, options = this.parameters) { 74 + return await fetch( 75 + this.host + 76 + "/domain/" + 77 + domain + 78 + "/top.json?" + 79 + new URLSearchParams(options), 80 + ) 81 + .then((res) => res.json()) 82 + .then((json) => json.data) 83 + .then((data) => ({ 84 + after: data.after, 85 + posts: data.children, 86 + })) 87 + .catch((err) => null); 88 + } 78 89 79 - async getDomainRising(domain, options = this.parameters) { 80 - return await fetch(this.host + "/domain/" + domain + "/rising.json?" + new URLSearchParams(options)) 81 - .then(res => res.json()) 82 - .then(json => json.data) 83 - .then(data => ({ 84 - after: data.after, 85 - posts: data.children 86 - })) 87 - .catch(err => null); 88 - } 90 + async getDomainNew(domain, options = this.parameters) { 91 + return await fetch( 92 + this.host + 93 + "/domain/" + 94 + domain + 95 + "/new.json?" + 96 + new URLSearchParams(options), 97 + ) 98 + .then((res) => res.json()) 99 + .then((json) => json.data) 100 + .then((data) => ({ 101 + after: data.after, 102 + posts: data.children, 103 + })) 104 + .catch((err) => null); 105 + } 89 106 90 - async getDomainControversial(domain, options = this.parameters) { 91 - return await fetch(this.host + "/domain/" + domain + "/controversial.json?" + new URLSearchParams(options)) 92 - .then(res => res.json()) 93 - .then(json => json.data) 94 - .then(data => ({ 95 - after: data.after, 96 - posts: data.children 97 - })) 98 - .catch(err => null); 99 - } 107 + async getDomainRising(domain, options = this.parameters) { 108 + return await fetch( 109 + this.host + 110 + "/domain/" + 111 + domain + 112 + "/rising.json?" + 113 + new URLSearchParams(options), 114 + ) 115 + .then((res) => res.json()) 116 + .then((json) => json.data) 117 + .then((data) => ({ 118 + after: data.after, 119 + posts: data.children, 120 + })) 121 + .catch((err) => null); 122 + } 100 123 101 - async getSubreddit(subreddit) { 102 - return await fetch(this.host + "/r/" + subreddit + "/about.json") 103 - .then(res => res.json()) 104 - .then(json => json.data) 105 - .catch(err => null); 106 - } 124 + async getDomainControversial(domain, options = this.parameters) { 125 + return await fetch( 126 + this.host + 127 + "/domain/" + 128 + domain + 129 + "/controversial.json?" + 130 + new URLSearchParams(options), 131 + ) 132 + .then((res) => res.json()) 133 + .then((json) => json.data) 134 + .then((data) => ({ 135 + after: data.after, 136 + posts: data.children, 137 + })) 138 + .catch((err) => null); 139 + } 107 140 108 - async getSubredditRules(subreddit) { 109 - return await fetch(this.host + "/r/" + subreddit + "/about/rules.json") 110 - .then(res => res.json()) 111 - .then(json => json.data) 112 - .catch(err => null); 113 - } 141 + async getSubreddit(subreddit) { 142 + return await fetch(this.host + "/r/" + subreddit + "/about.json") 143 + .then((res) => res.json()) 144 + .then((json) => json.data) 145 + .catch((err) => null); 146 + } 114 147 115 - async getSubredditModerators(subreddit) { 116 - return await fetch(this.host + "/r/" + subreddit + "/about/moderators.json") 117 - .then(res => res.json()) 118 - .then(json => json.data) 119 - .then(data = ({ 120 - users: data.children 121 - })) 122 - .catch(err => null); 123 - } 148 + async getSubredditRules(subreddit) { 149 + return await fetch(this.host + "/r/" + subreddit + "/about/rules.json") 150 + .then((res) => res.json()) 151 + .then((json) => json.data) 152 + .catch((err) => null); 153 + } 124 154 125 - async getSubredditWikiPages(subreddit) { 126 - return await fetch(this.host + "/r/" + subreddit + "/wiki/pages.json") 127 - .then(res => res.json()) 128 - .then(json => json.data) 129 - .catch(err => null); 130 - } 155 + async getSubredditModerators(subreddit) { 156 + return await fetch(this.host + "/r/" + subreddit + "/about/moderators.json") 157 + .then((res) => res.json()) 158 + .then((json) => json.data) 159 + .then( 160 + (data = { 161 + users: data.children, 162 + }), 163 + ) 164 + .catch((err) => null); 165 + } 131 166 132 - async getSubredditWikiPage(subreddit, page) { 133 - return await fetch(this.host + "/r/" + subreddit + "/wiki/" + page + ".json") 134 - .then(res => res.json()) 135 - .then(json => json.data) 136 - .catch(err => null); 137 - } 167 + async getSubredditWikiPages(subreddit) { 168 + return await fetch(this.host + "/r/" + subreddit + "/wiki/pages.json") 169 + .then((res) => res.json()) 170 + .then((json) => json.data) 171 + .catch((err) => null); 172 + } 138 173 139 - async getSubredditWikiPageRevisions(subreddit, page) { 140 - return await fetch(this.host + "/r/" + subreddit + "/wiki/revisions" + page + ".json") 141 - .then(res => res.json()) 142 - .then(json => json.data.children) 143 - .catch(err => null); 144 - } 174 + async getSubredditWikiPage(subreddit, page) { 175 + return await fetch( 176 + this.host + "/r/" + subreddit + "/wiki/" + page + ".json", 177 + ) 178 + .then((res) => res.json()) 179 + .then((json) => json.data) 180 + .catch((err) => null); 181 + } 145 182 146 - async getPopularSubreddits(options = this.parameters) { 147 - return await fetch(this.host + "/subreddits/popular.json?" + new URLSearchParams(options)) 148 - .then(res => res.json()) 149 - .then(json => json.data) 150 - .then(data => ({ 151 - after: data.after, 152 - subreddits: data.children 153 - })) 154 - .catch(err => null); 155 - } 183 + async getSubredditWikiPageRevisions(subreddit, page) { 184 + return await fetch( 185 + this.host + "/r/" + subreddit + "/wiki/revisions" + page + ".json", 186 + ) 187 + .then((res) => res.json()) 188 + .then((json) => json.data.children) 189 + .catch((err) => null); 190 + } 156 191 157 - async getNewSubreddits(options = this.parameters) { 158 - return await fetch(this.host + "/subreddits/new.json?" + new URLSearchParams(options)) 159 - .then(res => res.json()) 160 - .then(json => json.data) 161 - .then(data => ({ 162 - after: data.after, 163 - subreddits: data.children 164 - })) 165 - .catch(err => null); 166 - } 192 + async getPopularSubreddits(options = this.parameters) { 193 + return await fetch( 194 + this.host + "/subreddits/popular.json?" + new URLSearchParams(options), 195 + ) 196 + .then((res) => res.json()) 197 + .then((json) => json.data) 198 + .then((data) => ({ 199 + after: data.after, 200 + subreddits: data.children, 201 + })) 202 + .catch((err) => null); 203 + } 167 204 168 - async getPremiumSubreddits(options = this.parameters) { 169 - return await fetch(this.host + "/subreddits/premium.json?" + new URLSearchParams(options)) 170 - .then(res => res.json()) 171 - .then(json => json.data) 172 - .then(data => ({ 173 - after: data.after, 174 - subreddits: data.children 175 - })) 176 - .catch(err => null); 177 - } 205 + async getNewSubreddits(options = this.parameters) { 206 + return await fetch( 207 + this.host + "/subreddits/new.json?" + new URLSearchParams(options), 208 + ) 209 + .then((res) => res.json()) 210 + .then((json) => json.data) 211 + .then((data) => ({ 212 + after: data.after, 213 + subreddits: data.children, 214 + })) 215 + .catch((err) => null); 216 + } 178 217 179 - async getDefaultSubreddits(options = this.parameters) { 180 - return await fetch(this.host + "/subreddits/default.json?" + new URLSearchParams(options)) 181 - .then(res => res.json()) 182 - .then(json => json.data) 183 - .then(data => ({ 184 - after: data.after, 185 - subreddits: data.children 186 - })) 187 - .catch(err => null); 188 - } 218 + async getPremiumSubreddits(options = this.parameters) { 219 + return await fetch( 220 + this.host + "/subreddits/premium.json?" + new URLSearchParams(options), 221 + ) 222 + .then((res) => res.json()) 223 + .then((json) => json.data) 224 + .then((data) => ({ 225 + after: data.after, 226 + subreddits: data.children, 227 + })) 228 + .catch((err) => null); 229 + } 189 230 190 - async getPopularUsers(options = this.parameters) { 191 - return await fetch(this.host + "/users/popular.json?" + new URLSearchParams(options)) 192 - .then(res => res.json()) 193 - .then(json => json.data) 194 - .then(data => ({ 195 - after: data.after, 196 - users: data.children 197 - })) 198 - .catch(err => null); 199 - } 231 + async getDefaultSubreddits(options = this.parameters) { 232 + return await fetch( 233 + this.host + "/subreddits/default.json?" + new URLSearchParams(options), 234 + ) 235 + .then((res) => res.json()) 236 + .then((json) => json.data) 237 + .then((data) => ({ 238 + after: data.after, 239 + subreddits: data.children, 240 + })) 241 + .catch((err) => null); 242 + } 200 243 201 - async getNewUsers(options = this.parameters) { 202 - return await fetch(this.host + "/users/new.json?" + new URLSearchParams(options)) 203 - .then(res => res.json()) 204 - .then(json => json.data) 205 - .then(data => ({ 206 - after: data.after, 207 - users: data.children 208 - })) 209 - .catch(err => null); 210 - } 244 + async getPopularUsers(options = this.parameters) { 245 + return await fetch( 246 + this.host + "/users/popular.json?" + new URLSearchParams(options), 247 + ) 248 + .then((res) => res.json()) 249 + .then((json) => json.data) 250 + .then((data) => ({ 251 + after: data.after, 252 + users: data.children, 253 + })) 254 + .catch((err) => null); 255 + } 211 256 212 - async searchSubmissions(query, options = {}) { 213 - options.q = query; 214 - options.type = "link"; 257 + async getNewUsers(options = this.parameters) { 258 + return await fetch( 259 + this.host + "/users/new.json?" + new URLSearchParams(options), 260 + ) 261 + .then((res) => res.json()) 262 + .then((json) => json.data) 263 + .then((data) => ({ 264 + after: data.after, 265 + users: data.children, 266 + })) 267 + .catch((err) => null); 268 + } 215 269 216 - let params = { 217 - limit: 25, 218 - include_over_18: true 219 - } 270 + async searchSubmissions(query, options = {}) { 271 + options.q = query; 272 + options.type = "link"; 220 273 221 - return await fetch(this.host + "/search.json?" + new URLSearchParams(Object.assign(params, options))) 222 - .then(res => res.json()) 223 - .then(json => json.data) 224 - .then(data => ({ 225 - after: data.after, 226 - items: data.children 227 - })) 228 - .catch(err => null); 229 - } 274 + const params = { 275 + limit: 25, 276 + include_over_18: true, 277 + }; 230 278 231 - async searchSubreddits(query, options = {}) { 232 - options.q = query; 279 + return await fetch( 280 + this.host + 281 + "/search.json?" + 282 + new URLSearchParams(Object.assign(params, options)), 283 + ) 284 + .then((res) => res.json()) 285 + .then((json) => json.data) 286 + .then((data) => ({ 287 + after: data.after, 288 + items: data.children, 289 + })) 290 + .catch((err) => null); 291 + } 233 292 234 - let params = { 235 - limit: 25, 236 - include_over_18: true 237 - } 293 + async searchSubreddits(query, options = {}) { 294 + options.q = query; 238 295 239 - return await fetch(this.host + "/subreddits/search.json?" + new URLSearchParams(Object.assign(params, options))) 240 - .then(res => res.json()) 241 - .then(json => json.data) 242 - .then(data => ({ 243 - after: data.after, 244 - items: data.children 245 - })) 246 - .catch(err => null); 247 - } 296 + const params = { 297 + limit: 25, 298 + include_over_18: true, 299 + }; 248 300 249 - async searchUsers(query, options = {}) { 250 - options.q = query; 301 + return await fetch( 302 + this.host + 303 + "/subreddits/search.json?" + 304 + new URLSearchParams(Object.assign(params, options)), 305 + ) 306 + .then((res) => res.json()) 307 + .then((json) => json.data) 308 + .then((data) => ({ 309 + after: data.after, 310 + items: data.children, 311 + })) 312 + .catch((err) => null); 313 + } 251 314 252 - let params = { 253 - limit: 25, 254 - include_over_18: true 255 - } 315 + async searchUsers(query, options = {}) { 316 + options.q = query; 256 317 257 - return await fetch(this.host + "/users/search.json?" + new URLSearchParams(Object.assign(params, options))) 258 - .then(res => res.json()) 259 - .then(json => json.data) 260 - .then(data => ({ 261 - after: data.after, 262 - items: data.children 263 - })) 264 - .catch(err => null); 265 - } 318 + const params = { 319 + limit: 25, 320 + include_over_18: true, 321 + }; 266 322 267 - async searchAll(query, subreddit = null, options = {}) { 268 - options.q = query; 269 - subreddit = subreddit ? "/r/" + subreddit : ""; 323 + return await fetch( 324 + this.host + 325 + "/users/search.json?" + 326 + new URLSearchParams(Object.assign(params, options)), 327 + ) 328 + .then((res) => res.json()) 329 + .then((json) => json.data) 330 + .then((data) => ({ 331 + after: data.after, 332 + items: data.children, 333 + })) 334 + .catch((err) => null); 335 + } 270 336 271 - let params = { 272 - limit: 25, 273 - include_over_18: true, 274 - type: "sr,link,user", 275 - } 337 + async searchAll(query, subreddit = null, options = {}) { 338 + options.q = query; 339 + subreddit = subreddit ? "/r/" + subreddit : ""; 276 340 277 - return await fetch(this.host + subreddit + "/search.json?" + new URLSearchParams(Object.assign(params, options))) 278 - .then(res => res.json()) 279 - .then(json => Array.isArray(json) ? ({ 280 - after: json[1].data.after, 281 - items: json[0].data.children.concat(json[1].data.children) 282 - }) : ({ 283 - after: json.data.after, 284 - items: json.data.children 285 - })) 286 - .catch(err => null); 287 - } 341 + const params = { 342 + limit: 25, 343 + include_over_18: true, 344 + type: "sr,link,user", 345 + }; 288 346 289 - async getSubmission(id) { 290 - return await fetch(this.host + "/by_id/" + id + ".json") 291 - .then(res => res.json()) 292 - .then(json => json.data.children[0].data) 293 - .catch(err => null); 294 - } 347 + return await fetch( 348 + this.host + 349 + subreddit + 350 + "/search.json?" + 351 + new URLSearchParams(Object.assign(params, options)), 352 + ) 353 + .then((res) => res.json()) 354 + .then((json) => 355 + Array.isArray(json) 356 + ? { 357 + after: json[1].data.after, 358 + items: json[0].data.children.concat(json[1].data.children), 359 + } 360 + : { 361 + after: json.data.after, 362 + items: json.data.children, 363 + }, 364 + ) 365 + .catch((err) => null); 366 + } 295 367 296 - async getSubmissionComments(id, options = this.parameters) { 297 - return await fetch(this.host + "/comments/" + id + ".json?" + new URLSearchParams(options)) 298 - .then(res => res.json()) 299 - .then(json => ({ 300 - submission: json[0].data.children[0], 301 - comments: json[1].data.children 302 - })) 303 - .catch(err => null); 304 - } 368 + async getSubmission(id) { 369 + return await fetch(this.host + "/by_id/" + id + ".json") 370 + .then((res) => res.json()) 371 + .then((json) => json.data.children[0].data) 372 + .catch((err) => null); 373 + } 305 374 306 - async getSubredditComments(subreddit, options = this.parameters) { 307 - return await fetch(this.host + "/r/" + subreddit + "/comments.json?" + new URLSearchParams(options)) 308 - .then(res => res.json()) 309 - .then(json => json.data.children) 310 - .catch(err => null); 311 - } 375 + async getSubmissionComments(id, options = this.parameters) { 376 + return await fetch( 377 + this.host + "/comments/" + id + ".json?" + new URLSearchParams(options), 378 + ) 379 + .then((res) => res.json()) 380 + .then((json) => ({ 381 + submission: json[0].data.children[0], 382 + comments: json[1].data.children, 383 + })) 384 + .catch((err) => null); 385 + } 312 386 313 - async getUser(username) { 314 - return await fetch(this.host + "/user/" + username + "/about.json") 315 - .then(res => res.json()) 316 - .then(json => json.data) 317 - .catch(err => null); 318 - } 387 + async getSubredditComments(subreddit, options = this.parameters) { 388 + return await fetch( 389 + this.host + 390 + "/r/" + 391 + subreddit + 392 + "/comments.json?" + 393 + new URLSearchParams(options), 394 + ) 395 + .then((res) => res.json()) 396 + .then((json) => json.data.children) 397 + .catch((err) => null); 398 + } 319 399 320 - async getUserOverview(username, options = this.parameters) { 321 - return await fetch(this.host + "/user/" + username + "/overview.json?" + new URLSearchParams(options)) 322 - .then(res => res.json()) 323 - .then(json => json.data) 324 - .then(data => ({ 325 - after: data.after, 326 - items: data.children 327 - })) 328 - .catch(err => null); 329 - } 400 + async getUser(username) { 401 + return await fetch(this.host + "/user/" + username + "/about.json") 402 + .then((res) => res.json()) 403 + .then((json) => json.data) 404 + .catch((err) => null); 405 + } 330 406 331 - async getUserComments(username, options = this.parameters) { 332 - return await fetch(this.host + "/user/" + username + "/comments.json?" + new URLSearchParams(options)) 333 - .then(res => res.json()) 334 - .then(json => json.data) 335 - .then(data => ({ 336 - after: data.after, 337 - items: data.children 338 - })) 339 - .catch(err => null); 340 - } 407 + async getUserOverview(username, options = this.parameters) { 408 + return await fetch( 409 + this.host + 410 + "/user/" + 411 + username + 412 + "/overview.json?" + 413 + new URLSearchParams(options), 414 + ) 415 + .then((res) => res.json()) 416 + .then((json) => json.data) 417 + .then((data) => ({ 418 + after: data.after, 419 + items: data.children, 420 + })) 421 + .catch((err) => null); 422 + } 341 423 342 - async getUserSubmissions(username, options = this.parameters) { 343 - return await fetch(this.host + "/user/" + username + "/submitted.json?" + new URLSearchParams(options)) 344 - .then(res => res.json()) 345 - .then(json => json.data) 346 - .then(data => ({ 347 - after: data.after, 348 - items: data.children 349 - })) 350 - .catch(err => null); 351 - } 424 + async getUserComments(username, options = this.parameters) { 425 + return await fetch( 426 + this.host + 427 + "/user/" + 428 + username + 429 + "/comments.json?" + 430 + new URLSearchParams(options), 431 + ) 432 + .then((res) => res.json()) 433 + .then((json) => json.data) 434 + .then((data) => ({ 435 + after: data.after, 436 + items: data.children, 437 + })) 438 + .catch((err) => null); 439 + } 352 440 353 - async getLiveThread(id) { 354 - return await fetch(this.host + "/live/" + id + "/about.json") 355 - .then(res => res.json()) 356 - .then(json => json.data) 357 - .catch(err => null); 358 - } 441 + async getUserSubmissions(username, options = this.parameters) { 442 + return await fetch( 443 + this.host + 444 + "/user/" + 445 + username + 446 + "/submitted.json?" + 447 + new URLSearchParams(options), 448 + ) 449 + .then((res) => res.json()) 450 + .then((json) => json.data) 451 + .then((data) => ({ 452 + after: data.after, 453 + items: data.children, 454 + })) 455 + .catch((err) => null); 456 + } 359 457 360 - async getLiveThreadUpdates(id, options = this.parameters) { 361 - return await fetch(this.host + "/live/" + id + ".json?" + new URLSearchParams(options)) 362 - .then(res => res.json()) 363 - .then(json => json.data.children) 364 - .catch(err => null); 365 - } 458 + async getLiveThread(id) { 459 + return await fetch(this.host + "/live/" + id + "/about.json") 460 + .then((res) => res.json()) 461 + .then((json) => json.data) 462 + .catch((err) => null); 463 + } 366 464 465 + async getLiveThreadUpdates(id, options = this.parameters) { 466 + return await fetch( 467 + this.host + "/live/" + id + ".json?" + new URLSearchParams(options), 468 + ) 469 + .then((res) => res.json()) 470 + .then((json) => json.data.children) 471 + .catch((err) => null); 472 + } 367 473 368 - async getLiveThreadContributors(id, options = this.parameters) { 369 - return await fetch(this.host + "/live/" + id + "/contributors.json?" + new URLSearchParams(options)) 370 - .then(res => res.json()) 371 - .then(json => json.data.children) 372 - .catch(err => null); 373 - } 474 + async getLiveThreadContributors(id, options = this.parameters) { 475 + return await fetch( 476 + this.host + 477 + "/live/" + 478 + id + 479 + "/contributors.json?" + 480 + new URLSearchParams(options), 481 + ) 482 + .then((res) => res.json()) 483 + .then((json) => json.data.children) 484 + .catch((err) => null); 485 + } 374 486 375 - async getLiveThreadDiscussions(id, options = this.parameters) { 376 - return await fetch(this.host + "/live/" + id + "/discussions.json?" + new URLSearchParams(options)) 377 - .then(res => res.json()) 378 - .then(json => json.data.children) 379 - .catch(err => null); 380 - } 487 + async getLiveThreadDiscussions(id, options = this.parameters) { 488 + return await fetch( 489 + this.host + 490 + "/live/" + 491 + id + 492 + "/discussions.json?" + 493 + new URLSearchParams(options), 494 + ) 495 + .then((res) => res.json()) 496 + .then((json) => json.data.children) 497 + .catch((err) => null); 498 + } 381 499 382 - async getLiveThreadsNow(options = this.parameters) { 383 - return await fetch(this.host + "/live/happening_now.json?" + new URLSearchParams(options)) 384 - .then(res => res.json()) 385 - .then(json => json.data.children) 386 - .catch(err => null); 387 - } 500 + async getLiveThreadsNow(options = this.parameters) { 501 + return await fetch( 502 + this.host + "/live/happening_now.json?" + new URLSearchParams(options), 503 + ) 504 + .then((res) => res.json()) 505 + .then((json) => json.data.children) 506 + .catch((err) => null); 507 + } 388 508 } 389 509 390 - export { Geddit } 510 + export { Geddit };
+10 -11
src/index.js
··· 1 - const express = require('express'); 2 - const path = require('path'); 3 - const routes = require('./routes/index'); 4 - const geddit = require('./geddit.js'); 1 + const express = require("express"); 2 + const path = require("path"); 3 + const routes = require("./routes/index"); 4 + const geddit = require("./geddit.js"); 5 5 6 6 const app = express(); 7 7 8 - app.set('views', path.join(__dirname, 'views')); 9 - app.set('view engine', 'pug'); 8 + app.set("views", path.join(__dirname, "views")); 9 + app.set("view engine", "pug"); 10 10 11 - app.use(express.static(path.join(__dirname, 'public'))); 12 - app.use('/', routes); 11 + app.use(express.static(path.join(__dirname, "public"))); 12 + app.use("/", routes); 13 13 14 14 const port = process.env.READIT_PORT; 15 - const server = app.listen(port?port:3000, () => { 16 - console.log(`started on ${server.address().port}`); 15 + const server = app.listen(port ? port : 3000, () => { 16 + console.log(`started on ${server.address().port}`); 17 17 }); 18 -
+2 -2
src/mixins/comment.pug
··· 19 19 20 20 mixin comment(com, isfirst) 21 21 - var data = com.data 22 - - var kind = com.kind 22 + - console.log(com) 23 23 - var hasReplyData = hasReplies(data) 24 24 25 - if kind == "more" 25 + if com.kind == "more" 26 26 div(class=`more ${isfirst ? 'first' : ''}`) 27 27 | #{data.count} more #{fmttxt(data.count, 'comment')} 28 28 else
+49 -47
src/routes/index.js
··· 1 - const express = require('express'); 2 - const he = require('he'); 1 + const express = require("express"); 2 + const he = require("he"); 3 3 const router = express.Router(); 4 - const geddit = require('../geddit.js'); 4 + const geddit = require("../geddit.js"); 5 5 const G = new geddit.Geddit(); 6 6 7 - 8 7 // GET / 9 - router.get('/', async (req, res) => { 10 - res.redirect("/r/all") 8 + router.get("/", async (req, res) => { 9 + res.redirect("/r/all"); 11 10 }); 12 11 13 12 // GET /r/:id 14 - router.get('/r/:subreddit', async (req, res) => { 15 - var subreddit = req.params.subreddit; 16 - var query = req.query? req.query : {}; 17 - if (!query.sort) { 18 - query.sort = 'hot'; 19 - } 13 + router.get("/r/:subreddit", async (req, res) => { 14 + var subreddit = req.params.subreddit; 15 + var query = req.query ? req.query : {}; 16 + if (!query.sort) { 17 + query.sort = "hot"; 18 + } 20 19 21 - var postsReq = G.getSubmissions(query.sort, `${subreddit}`, query); 22 - var aboutReq = G.getSubreddit(`${subreddit}`); 20 + var postsReq = G.getSubmissions(query.sort, `${subreddit}`, query); 21 + var aboutReq = G.getSubreddit(`${subreddit}`); 23 22 24 - var [posts, about] = await Promise.all([postsReq, aboutReq]); 23 + var [posts, about] = await Promise.all([postsReq, aboutReq]); 25 24 26 - res.render('index', { subreddit, posts, about, query }); 25 + res.render("index", { subreddit, posts, about, query }); 27 26 }); 28 27 29 28 // GET /comments/:id 30 - router.get('/comments/:id', async (req, res) => { 31 - var id = req.params.id; 29 + router.get("/comments/:id", async (req, res) => { 30 + var id = req.params.id; 32 31 33 - response = await G.getSubmissionComments(id); 32 + var params = { 33 + limit: 50, 34 + }; 35 + response = await G.getSubmissionComments(id, params); 34 36 35 - res.render('comments', unescape_submission(response)); 37 + res.render("comments", unescape_submission(response)); 36 38 }); 37 39 38 40 // GET /subs 39 - router.get('/subs', async (req, res) => { 40 - res.render('subs'); 41 + router.get("/subs", async (req, res) => { 42 + res.render("subs"); 41 43 }); 42 44 43 45 // GET /media 44 - router.get('/media/*', async (req, res) => { 45 - var url = req.params[0]; 46 - console.log(`making request to ${url}`); 47 - return await fetch(url, { 48 - headers: { 49 - Accept: "*/*", 50 - } 51 - }); 46 + router.get("/media/*", async (req, res) => { 47 + var url = req.params[0]; 48 + console.log(`making request to ${url}`); 49 + return await fetch(url, { 50 + headers: { 51 + Accept: "*/*", 52 + }, 53 + }); 52 54 }); 53 55 54 56 module.exports = router; 55 57 56 58 function unescape_submission(response) { 57 - var post = response.submission.data; 58 - var comments = response.comments; 59 + var post = response.submission.data; 60 + var comments = response.comments; 59 61 60 - if (post.selftext_html) { 61 - post.selftext_html = he.decode(post.selftext_html); 62 - } 63 - comments.forEach(unescape_comment); 62 + if (post.selftext_html) { 63 + post.selftext_html = he.decode(post.selftext_html); 64 + } 65 + comments.forEach(unescape_comment); 64 66 65 - return { post, comments }; 67 + return { post, comments }; 66 68 } 67 69 68 70 function unescape_comment(comment) { 69 - if (comment.data.body_html) { 70 - comment.data.body_html = he.decode(comment.data.body_html); 71 - } 72 - if (comment.data.replies) { 73 - if(comment.data.replies.data) { 74 - if(comment.data.replies.data.children) { 75 - comment.data.replies.data.children.forEach(unescape_comment); 76 - } 77 - } 78 - } 71 + if (comment.data.body_html) { 72 + comment.data.body_html = he.decode(comment.data.body_html); 73 + } 74 + if (comment.data.replies) { 75 + if (comment.data.replies.data) { 76 + if (comment.data.replies.data.children) { 77 + comment.data.replies.data.children.forEach(unescape_comment); 78 + } 79 + } 80 + } 79 81 }