selfhostable, read-only reddit client
at main 504 lines 13 kB view raw
1class 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 this.headers = { 14 "User-Agent": 15 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36", 16 }; 17 } 18 19 async getSubmissions(sort = "hot", subreddit = null, options = {}) { 20 const params = { 21 limit: 20, 22 include_over_18: true, 23 }; 24 25 const subredditStr = subreddit ? `/r/${subreddit}` : ""; 26 27 return await fetch( 28 `${ 29 this.host + subredditStr 30 }/${sort}.json?${new URLSearchParams(Object.assign(params, options))}`, 31 { headers: this.headers }, 32 ) 33 .then((res) => res.json()) 34 .then((json) => json.data) 35 .then((data) => ({ 36 after: data.after, 37 posts: data.children, 38 })) 39 .catch((err) => null); 40 } 41 42 async getDomainHot(domain, options = this.parameters) { 43 return await fetch( 44 `${this.host}/domain/${domain}/hot.json?${new URLSearchParams(options)}`, 45 { headers: this.headers }, 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 } 55 56 async getDomainBest(domain, options = this.parameters) { 57 return await fetch( 58 `${this.host}/domain/${domain}/best.json?${new URLSearchParams(options)}`, 59 { headers: this.headers }, 60 ) 61 .then((res) => res.json()) 62 .then((json) => json.data) 63 .then((data) => ({ 64 after: data.after, 65 posts: data.children, 66 })) 67 .catch((err) => null); 68 } 69 70 async getDomainTop(domain, options = this.parameters) { 71 return await fetch( 72 `${this.host}/domain/${domain}/top.json?${new URLSearchParams(options)}`, 73 { headers: this.headers }, 74 ) 75 .then((res) => res.json()) 76 .then((json) => json.data) 77 .then((data) => ({ 78 after: data.after, 79 posts: data.children, 80 })) 81 .catch((_) => null); 82 } 83 84 async getDomainNew(domain, options = this.parameters) { 85 return await fetch( 86 `${this.host}/domain/${domain}/new.json?${new URLSearchParams(options)}`, 87 { headers: this.headers }, 88 ) 89 .then((res) => res.json()) 90 .then((json) => json.data) 91 .then((data) => ({ 92 after: data.after, 93 posts: data.children, 94 })) 95 .catch((err) => null); 96 } 97 98 async getDomainRising(domain, options = this.parameters) { 99 return await fetch( 100 `${this.host}/domain/${domain}/rising.json?${new URLSearchParams(options)}`, 101 { headers: this.headers }, 102 ) 103 .then((res) => res.json()) 104 .then((json) => json.data) 105 .then((data) => ({ 106 after: data.after, 107 posts: data.children, 108 })) 109 .catch((err) => null); 110 } 111 112 async getDomainControversial(domain, options = this.parameters) { 113 return await fetch( 114 `${this.host}/domain/${domain}/controversial.json?${new URLSearchParams(options)}`, 115 { headers: this.headers }, 116 ) 117 .then((res) => res.json()) 118 .then((json) => json.data) 119 .then((data) => ({ 120 after: data.after, 121 posts: data.children, 122 })) 123 .catch((err) => null); 124 } 125 126 async getSubreddit(subreddit) { 127 return await fetch(`${this.host}/r/${subreddit}/about.json`, { 128 headers: this.headers, 129 }) 130 .then((res) => res.json()) 131 .then((json) => json.data) 132 .catch((err) => null); 133 } 134 135 async getSubredditRules(subreddit) { 136 return await fetch(`${this.host}/r/${subreddit}/about/rules.json`, { 137 headers: this.headers, 138 }) 139 .then((res) => res.json()) 140 .then((json) => json.data) 141 .catch((err) => null); 142 } 143 144 async getSubredditModerators(subreddit) { 145 return await fetch(`${this.host}/r/${subreddit}/about/moderators.json`, { 146 headers: this.headers, 147 }) 148 .then((res) => res.json()) 149 .then((json) => json.data) 150 .then((data) => ({ 151 users: data.children, 152 })) 153 .catch((err) => null); 154 } 155 156 async getSubredditWikiPages(subreddit) { 157 return await fetch(`${this.host}/r/${subreddit}/wiki/pages.json`, { 158 headers: this.headers, 159 }) 160 .then((res) => res.json()) 161 .then((json) => json.data) 162 .catch((err) => null); 163 } 164 165 async getSubredditWikiPage(subreddit, page) { 166 return await fetch(`${this.host}/r/${subreddit}/wiki/${page}.json`, { 167 headers: this.headers, 168 }) 169 .then((res) => res.json()) 170 .then((json) => json.data) 171 .catch((err) => null); 172 } 173 174 async getSubredditWikiPageRevisions(subreddit, page) { 175 return await fetch( 176 `${this.host}/r/${subreddit}/wiki/revisions${page}.json`, 177 { headers: this.headers }, 178 ) 179 .then((res) => res.json()) 180 .then((json) => json.data.children) 181 .catch((err) => null); 182 } 183 184 async getPopularSubreddits(options = this.parameters) { 185 return await fetch( 186 `${this.host}/subreddits/popular.json?${new URLSearchParams(options)}`, 187 { headers: this.headers }, 188 ) 189 .then((res) => res.json()) 190 .then((json) => json.data) 191 .then((data) => ({ 192 after: data.after, 193 subreddits: data.children, 194 })) 195 .catch((err) => null); 196 } 197 198 async getNewSubreddits(options = this.parameters) { 199 return await fetch( 200 `${this.host}/subreddits/new.json?${new URLSearchParams(options)}`, 201 { headers: this.headers }, 202 ) 203 .then((res) => res.json()) 204 .then((json) => json.data) 205 .then((data) => ({ 206 after: data.after, 207 subreddits: data.children, 208 })) 209 .catch((err) => null); 210 } 211 212 async getPremiumSubreddits(options = this.parameters) { 213 return await fetch( 214 `${this.host}/subreddits/premium.json?${new URLSearchParams(options)}`, 215 { headers: this.headers }, 216 ) 217 .then((res) => res.json()) 218 .then((json) => json.data) 219 .then((data) => ({ 220 after: data.after, 221 subreddits: data.children, 222 })) 223 .catch((err) => null); 224 } 225 226 async getDefaultSubreddits(options = this.parameters) { 227 return await fetch( 228 `${this.host}/subreddits/default.json?${new URLSearchParams(options)}`, 229 { headers: this.headers }, 230 ) 231 .then((res) => res.json()) 232 .then((json) => json.data) 233 .then((data) => ({ 234 after: data.after, 235 subreddits: data.children, 236 })) 237 .catch((err) => null); 238 } 239 240 async getPopularUsers(options = this.parameters) { 241 return await fetch( 242 `${this.host}/users/popular.json?${new URLSearchParams(options)}`, 243 { headers: this.headers }, 244 ) 245 .then((res) => res.json()) 246 .then((json) => json.data) 247 .then((data) => ({ 248 after: data.after, 249 users: data.children, 250 })) 251 .catch((err) => null); 252 } 253 254 async getNewUsers(options = this.parameters) { 255 return await fetch( 256 `${this.host}/users/new.json?${new URLSearchParams(options)}`, 257 { headers: this.headers }, 258 ) 259 .then((res) => res.json()) 260 .then((json) => json.data) 261 .then((data) => ({ 262 after: data.after, 263 users: data.children, 264 })) 265 .catch((err) => null); 266 } 267 268 async searchSubmissions(query, options = {}) { 269 options.q = query; 270 options.type = "link"; 271 272 return await fetch( 273 `${this.host}/search.json?${new URLSearchParams(options)}`, 274 { headers: this.headers }, 275 ) 276 .then((res) => res.json()) 277 .then((json) => json.data) 278 .then((data) => ({ 279 after: data.after, 280 items: data.children, 281 })) 282 .catch((err) => null); 283 } 284 285 async searchSubreddits(query, options = {}) { 286 options.q = query; 287 288 const params = { 289 limit: 25, 290 include_over_18: false, 291 }; 292 293 return await fetch( 294 `${this.host}/subreddits/search.json?${new URLSearchParams(Object.assign(params, options))}`, 295 { headers: this.headers }, 296 ) 297 .then((res) => res.json()) 298 .then((json) => json.data) 299 .then((data) => ({ 300 after: data.after, 301 items: data.children, 302 })) 303 .catch((err) => null); 304 } 305 306 async searchUsers(query, options = {}) { 307 options.q = query; 308 309 const params = { 310 limit: 25, 311 include_over_18: true, 312 }; 313 314 return await fetch( 315 `${this.host}/users/search.json?${new URLSearchParams(Object.assign(params, options))}`, 316 { headers: this.headers }, 317 ) 318 .then((res) => res.json()) 319 .then((json) => json.data) 320 .then((data) => ({ 321 after: data.after, 322 items: data.children, 323 })) 324 .catch((err) => null); 325 } 326 327 async searchAll(query, subreddit = null, options = {}) { 328 options.q = query; 329 const subredditStr = subreddit ? `/r/${subreddit}` : ""; 330 331 const params = { 332 limit: 25, 333 include_over_18: true, 334 type: "sr,link,user", 335 }; 336 337 return await fetch( 338 `${ 339 this.host + subredditStr 340 }/search.json?${new URLSearchParams(Object.assign(params, options))}`, 341 { headers: this.headers }, 342 ) 343 .then((res) => res.json()) 344 .then((json) => 345 Array.isArray(json) 346 ? { 347 after: json[1].data.after, 348 items: json[0].data.children.concat(json[1].data.children), 349 } 350 : { 351 after: json.data.after, 352 items: json.data.children, 353 }, 354 ) 355 .catch((err) => null); 356 } 357 358 async getSubmission(id) { 359 return await fetch(`${this.host}/by_id/${id}.json`, { 360 headers: this.headers, 361 }) 362 .then((res) => res.json()) 363 .then((json) => json.data.children[0].data) 364 .catch((err) => null); 365 } 366 367 async getSubmissionComments(id, options = this.parameters) { 368 return await fetch( 369 `${this.host}/comments/${id}.json?${new URLSearchParams(options)}`, 370 { headers: this.headers }, 371 ) 372 .then((res) => res.json()) 373 .then((json) => ({ 374 submission: json[0].data.children[0], 375 comments: json[1].data.children, 376 })) 377 .catch((err) => null); 378 } 379 380 async getSingleCommentThread(parent_id, child_id, options = this.parameters) { 381 return await fetch( 382 `${this.host}/comments/${parent_id}/comment/${child_id}.json?${new URLSearchParams(options)}`, 383 { headers: this.headers }, 384 ) 385 .then((res) => res.json()) 386 .then((json) => ({ 387 submission: json[0].data.children[0], 388 comments: json[1].data.children, 389 })) 390 .catch((err) => null); 391 } 392 393 async getSubredditComments(subreddit, options = this.parameters) { 394 return await fetch( 395 `${this.host}/r/${subreddit}/comments.json?${new URLSearchParams(options)}`, 396 { headers: this.headers }, 397 ) 398 .then((res) => res.json()) 399 .then((json) => json.data.children) 400 .catch((err) => null); 401 } 402 403 async getUser(username) { 404 return await fetch(`${this.host}/user/${username}/about.json`, { 405 headers: this.headers, 406 }) 407 .then((res) => res.json()) 408 .then((json) => json.data) 409 .catch((err) => null); 410 } 411 412 async getUserOverview(username, options = this.parameters) { 413 return await fetch( 414 `${this.host}/user/${username}/overview.json?${new URLSearchParams(options)}`, 415 { headers: this.headers }, 416 ) 417 .then((res) => res.json()) 418 .then((json) => json.data) 419 .then((data) => ({ 420 after: data.after, 421 items: data.children, 422 })) 423 .catch((err) => null); 424 } 425 426 async getUserComments(username, options = this.parameters) { 427 return await fetch( 428 `${this.host}/user/${username}/comments.json?${new URLSearchParams(options)}`, 429 { headers: this.headers }, 430 ) 431 .then((res) => res.json()) 432 .then((json) => json.data) 433 .then((data) => ({ 434 after: data.after, 435 items: data.children, 436 })) 437 .catch((err) => null); 438 } 439 440 async getUserSubmissions(username, options = this.parameters) { 441 return await fetch( 442 `${this.host}/user/${username}/submitted.json?${new URLSearchParams(options)}`, 443 { headers: this.headers }, 444 ) 445 .then((res) => res.json()) 446 .then((json) => json.data) 447 .then((data) => ({ 448 after: data.after, 449 items: data.children, 450 })) 451 .catch((err) => null); 452 } 453 454 async getLiveThread(id) { 455 return await fetch(`${this.host}/live/${id}/about.json`, { 456 headers: this.headers, 457 }) 458 .then((res) => res.json()) 459 .then((json) => json.data) 460 .catch((err) => null); 461 } 462 463 async getLiveThreadUpdates(id, options = this.parameters) { 464 return await fetch( 465 `${this.host}/live/${id}.json?${new URLSearchParams(options)}`, 466 { headers: this.headers }, 467 ) 468 .then((res) => res.json()) 469 .then((json) => json.data.children) 470 .catch((err) => null); 471 } 472 473 async getLiveThreadContributors(id, options = this.parameters) { 474 return await fetch( 475 `${this.host}/live/${id}/contributors.json?${new URLSearchParams(options)}`, 476 { headers: this.headers }, 477 ) 478 .then((res) => res.json()) 479 .then((json) => json.data.children) 480 .catch((err) => null); 481 } 482 483 async getLiveThreadDiscussions(id, options = this.parameters) { 484 return await fetch( 485 `${this.host}/live/${id}/discussions.json?${new URLSearchParams(options)}`, 486 { headers: this.headers }, 487 ) 488 .then((res) => res.json()) 489 .then((json) => json.data.children) 490 .catch((err) => null); 491 } 492 493 async getLiveThreadsNow(options = this.parameters) { 494 return await fetch( 495 `${this.host}/live/happening_now.json?${new URLSearchParams(options)}`, 496 { headers: this.headers }, 497 ) 498 .then((res) => res.json()) 499 .then((json) => json.data.children) 500 .catch((err) => null); 501 } 502} 503 504export { Geddit };