4chanapi.ts 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of 4chanapi.ts might be problematic. Click here for more details.
- package/LICENSE +9 -0
- package/README.md +516 -0
- package/package.json +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Omerg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
6
|
+
|
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
# 4capi
|
|
2
|
+
|
|
3
|
+
A typed TypeScript client for the [4chan API](https://github.com/4chan/4chan-API), designed for React Native.
|
|
4
|
+
|
|
5
|
+
- Full TypeScript types for every endpoint
|
|
6
|
+
- Built-in rate limiting (≤ 1 request/sec, per API rules)
|
|
7
|
+
- `If-Modified-Since` support — returns `null` on HTTP 304
|
|
8
|
+
- URL helpers for images, thumbnails, flags, and spoilers
|
|
9
|
+
- No Node.js dependencies — uses `fetch` natively available in React Native
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install 4capi
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { FourChanClient } from "4capi";
|
|
25
|
+
|
|
26
|
+
const client = new FourChanClient();
|
|
27
|
+
|
|
28
|
+
const thread = await client.getThread("g", 100000000);
|
|
29
|
+
if (thread) {
|
|
30
|
+
const op = thread.posts[0];
|
|
31
|
+
console.log(op.sub); // thread subject
|
|
32
|
+
console.log(op.com); // comment (HTML-escaped)
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## API Reference
|
|
39
|
+
|
|
40
|
+
### `new FourChanClient()`
|
|
41
|
+
|
|
42
|
+
Creates a client with a built-in rate limiter. All requests are serialised with at least 1 second between dispatches.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const client = new FourChanClient();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
### `getBoards()`
|
|
51
|
+
|
|
52
|
+
Fetches all boards and their settings.
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
const boards = await client.getBoards();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Response: `Board[]`**
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
[
|
|
62
|
+
{
|
|
63
|
+
"board": "g",
|
|
64
|
+
"title": "Technology",
|
|
65
|
+
"ws_board": 1,
|
|
66
|
+
"per_page": 15,
|
|
67
|
+
"pages": 10,
|
|
68
|
+
"max_filesize": 4096,
|
|
69
|
+
"max_webm_filesize": 3072,
|
|
70
|
+
"max_comment_chars": 2000,
|
|
71
|
+
"max_webm_duration": 120,
|
|
72
|
+
"bump_limit": 310,
|
|
73
|
+
"image_limit": 150,
|
|
74
|
+
"cooldowns": {
|
|
75
|
+
"threads": 600,
|
|
76
|
+
"replies": 60,
|
|
77
|
+
"images": 60
|
|
78
|
+
},
|
|
79
|
+
"meta_description": "...",
|
|
80
|
+
"is_archived": 1
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**`Board` fields**
|
|
86
|
+
|
|
87
|
+
| Field | Type | Description |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `board` | `string` | Board directory name (e.g. `"g"`, `"po"`) |
|
|
90
|
+
| `title` | `string` | Human-readable board title |
|
|
91
|
+
| `ws_board` | `0 \| 1` | 1 = worksafe |
|
|
92
|
+
| `per_page` | `number` | Threads per index page |
|
|
93
|
+
| `pages` | `number` | Total number of index pages |
|
|
94
|
+
| `max_filesize` | `number` | Max non-webm file size in KB |
|
|
95
|
+
| `max_webm_filesize` | `number` | Max webm file size in KB |
|
|
96
|
+
| `max_comment_chars` | `number` | Max characters in a comment |
|
|
97
|
+
| `max_webm_duration` | `number` | Max webm duration in seconds |
|
|
98
|
+
| `bump_limit` | `number` | Replies before thread stops bumping |
|
|
99
|
+
| `image_limit` | `number` | Max image replies per thread |
|
|
100
|
+
| `cooldowns` | `BoardCooldowns` | `{ threads, replies, images }` in seconds |
|
|
101
|
+
| `is_archived?` | `0 \| 1` | Archive enabled on this board |
|
|
102
|
+
| `country_flags?` | `0 \| 1` | Country flags enabled |
|
|
103
|
+
| `user_ids?` | `0 \| 1` | Poster IDs enabled |
|
|
104
|
+
| `board_flags?` | `Record<string, string>` | Map of flag code → name |
|
|
105
|
+
| `spoilers?` | `0 \| 1` | Spoiler images enabled |
|
|
106
|
+
| `custom_spoilers?` | `number` | Number of custom spoiler variants |
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
### `getThread(board, threadId, opts?)`
|
|
111
|
+
|
|
112
|
+
Fetches a full thread including all replies.
|
|
113
|
+
|
|
114
|
+
Returns `null` if the thread has not changed since `opts.ifModifiedSince`.
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const thread = await client.getThread("po", 570368);
|
|
118
|
+
|
|
119
|
+
// With If-Modified-Since (returns null on HTTP 304)
|
|
120
|
+
const lastFetch = new Date();
|
|
121
|
+
const updated = await client.getThread("po", 570368, {
|
|
122
|
+
ifModifiedSince: lastFetch,
|
|
123
|
+
});
|
|
124
|
+
if (updated === null) {
|
|
125
|
+
console.log("No new posts");
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Response: `Thread | null`**
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"posts": [
|
|
134
|
+
{
|
|
135
|
+
"no": 570368,
|
|
136
|
+
"resto": 0,
|
|
137
|
+
"sticky": 1,
|
|
138
|
+
"now": "01/01/24(Mon)00:00",
|
|
139
|
+
"time": 1704067200,
|
|
140
|
+
"name": "Anonymous",
|
|
141
|
+
"sub": "Welcome to /po/",
|
|
142
|
+
"com": "Paper & origami thread.",
|
|
143
|
+
"tim": 1704067200123,
|
|
144
|
+
"filename": "origami",
|
|
145
|
+
"ext": ".jpg",
|
|
146
|
+
"fsize": 204800,
|
|
147
|
+
"md5": "abc123def456ghi789jkl0==",
|
|
148
|
+
"w": 1200,
|
|
149
|
+
"h": 800,
|
|
150
|
+
"tn_w": 250,
|
|
151
|
+
"tn_h": 166,
|
|
152
|
+
"replies": 42,
|
|
153
|
+
"images": 18,
|
|
154
|
+
"unique_ips": 15,
|
|
155
|
+
"last_modified": 1704099600,
|
|
156
|
+
"semantic_url": "welcome-to-po"
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
"no": 570400,
|
|
160
|
+
"resto": 570368,
|
|
161
|
+
"now": "01/01/24(Mon)01:30",
|
|
162
|
+
"time": 1704072600,
|
|
163
|
+
"name": "Anonymous",
|
|
164
|
+
"com": "Nice thread, here's my latest crane.",
|
|
165
|
+
"tim": 1704072600456,
|
|
166
|
+
"filename": "crane",
|
|
167
|
+
"ext": ".png",
|
|
168
|
+
"fsize": 102400,
|
|
169
|
+
"md5": "xyz789abc012def345ghi6==",
|
|
170
|
+
"w": 800,
|
|
171
|
+
"h": 600,
|
|
172
|
+
"tn_w": 250,
|
|
173
|
+
"tn_h": 187
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### `getCatalog(board, opts?)`
|
|
182
|
+
|
|
183
|
+
Fetches all threads on a board grouped by page, including the most recent reply previews.
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
const catalog = await client.getCatalog("g");
|
|
187
|
+
|
|
188
|
+
// With cache check
|
|
189
|
+
const catalog = await client.getCatalog("g", { ifModifiedSince: lastFetch });
|
|
190
|
+
if (catalog === null) return; // not modified
|
|
191
|
+
|
|
192
|
+
for (const page of catalog) {
|
|
193
|
+
for (const thread of page.threads) {
|
|
194
|
+
console.log(`[${thread.no}] ${thread.sub ?? "(no subject)"} — ${thread.replies} replies`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Response: `CatalogPage[] | null`**
|
|
200
|
+
|
|
201
|
+
```json
|
|
202
|
+
[
|
|
203
|
+
{
|
|
204
|
+
"page": 1,
|
|
205
|
+
"threads": [
|
|
206
|
+
{
|
|
207
|
+
"no": 100000001,
|
|
208
|
+
"resto": 0,
|
|
209
|
+
"now": "03/29/26(Sun)12:00",
|
|
210
|
+
"time": 1743249600,
|
|
211
|
+
"name": "Anonymous",
|
|
212
|
+
"sub": "Programming thread",
|
|
213
|
+
"com": "Post your projects.",
|
|
214
|
+
"tim": 1743249600789,
|
|
215
|
+
"filename": "code",
|
|
216
|
+
"ext": ".png",
|
|
217
|
+
"w": 1920,
|
|
218
|
+
"h": 1080,
|
|
219
|
+
"tn_w": 250,
|
|
220
|
+
"tn_h": 140,
|
|
221
|
+
"replies": 87,
|
|
222
|
+
"images": 12,
|
|
223
|
+
"omitted_posts": 82,
|
|
224
|
+
"omitted_images": 10,
|
|
225
|
+
"last_modified": 1743260000,
|
|
226
|
+
"semantic_url": "programming-thread",
|
|
227
|
+
"last_replies": [
|
|
228
|
+
{
|
|
229
|
+
"no": 100000088,
|
|
230
|
+
"resto": 100000001,
|
|
231
|
+
"now": "03/29/26(Sun)14:55",
|
|
232
|
+
"time": 1743260100,
|
|
233
|
+
"name": "Anonymous",
|
|
234
|
+
"com": "Just finished my Rust project."
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### `getThreadList(board)`
|
|
246
|
+
|
|
247
|
+
Fetches a lightweight list of all threads and their last-modified timestamps. Useful for polling — much smaller than the full catalog.
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
const pages = await client.getThreadList("g");
|
|
251
|
+
|
|
252
|
+
for (const page of pages) {
|
|
253
|
+
for (const thread of page.threads) {
|
|
254
|
+
console.log(thread.no, thread.last_modified, thread.replies);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Response: `ThreadListPage[]`**
|
|
260
|
+
|
|
261
|
+
```json
|
|
262
|
+
[
|
|
263
|
+
{
|
|
264
|
+
"page": 1,
|
|
265
|
+
"threads": [
|
|
266
|
+
{ "no": 100000001, "last_modified": 1743260000, "replies": 87 },
|
|
267
|
+
{ "no": 100000002, "last_modified": 1743259000, "replies": 12 }
|
|
268
|
+
]
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
"page": 2,
|
|
272
|
+
"threads": [
|
|
273
|
+
{ "no": 99999900, "last_modified": 1743240000, "replies": 310 }
|
|
274
|
+
]
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
### `getIndex(board, page, opts?)`
|
|
282
|
+
|
|
283
|
+
Fetches a single index page (threads + preview replies). Pages are 1-based.
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
const indexPage = await client.getIndex("g", 1);
|
|
287
|
+
if (indexPage) {
|
|
288
|
+
for (const thread of indexPage) {
|
|
289
|
+
const op = thread.posts[0];
|
|
290
|
+
console.log(op.sub, op.replies);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Response: `IndexPage | null`** — an array of `{ posts: Post[] }` objects
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
### `getArchive(board)`
|
|
300
|
+
|
|
301
|
+
Fetches the list of archived thread IDs. Returns an empty array for boards without archives.
|
|
302
|
+
|
|
303
|
+
```ts
|
|
304
|
+
const archivedIds = await client.getArchive("g");
|
|
305
|
+
// [571958, 572866, 54195, ...]
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
**Response: `number[]`**
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
[571958, 572866, 54195, 12345, 67890]
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## URL Helpers
|
|
317
|
+
|
|
318
|
+
### `getImageUrl(board, tim, ext)`
|
|
319
|
+
|
|
320
|
+
Full-size image URL.
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import { getImageUrl } from "4capi";
|
|
324
|
+
|
|
325
|
+
const url = getImageUrl("g", post.tim!, post.ext!);
|
|
326
|
+
// "https://i.4cdn.org/g/1743249600789.png"
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### `getThumbnailUrl(board, tim)`
|
|
330
|
+
|
|
331
|
+
Thumbnail URL (always JPEG).
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
import { getThumbnailUrl } from "4capi";
|
|
335
|
+
|
|
336
|
+
const thumb = getThumbnailUrl("g", post.tim!);
|
|
337
|
+
// "https://i.4cdn.org/g/1743249600789s.jpg"
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### `getCountryFlagUrl(countryCode)`
|
|
341
|
+
|
|
342
|
+
Country flag GIF (boards with `country_flags` enabled).
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
import { getCountryFlagUrl } from "4capi";
|
|
346
|
+
|
|
347
|
+
const flag = getCountryFlagUrl(post.country!);
|
|
348
|
+
// "https://s.4cdn.org/image/country/us.gif"
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### `getBoardFlagUrl(board, flagCode)`
|
|
352
|
+
|
|
353
|
+
Board-specific flag GIF (boards with `board_flags` enabled).
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
import { getBoardFlagUrl } from "4capi";
|
|
357
|
+
|
|
358
|
+
const flag = getBoardFlagUrl("pol", post.board_flag!);
|
|
359
|
+
// "https://s.4cdn.org/image/flags/pol/EU.gif"
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### `getSpoilerUrl(board, customIndex?)`
|
|
363
|
+
|
|
364
|
+
Spoiler placeholder image. Pass a `customIndex` (1–10) for boards with custom spoilers.
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
import { getSpoilerUrl } from "4capi";
|
|
368
|
+
|
|
369
|
+
getSpoilerUrl("b"); // default spoiler
|
|
370
|
+
getSpoilerUrl("co", 3); // custom spoiler #3 for /co/
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### `icons`
|
|
374
|
+
|
|
375
|
+
Static icon URLs as constants.
|
|
376
|
+
|
|
377
|
+
```ts
|
|
378
|
+
import { icons } from "4capi";
|
|
379
|
+
|
|
380
|
+
icons.sticky // https://s.4cdn.org/image/sticky.gif
|
|
381
|
+
icons.closed // https://s.4cdn.org/image/closed.gif
|
|
382
|
+
icons.admin // https://s.4cdn.org/image/adminicon.gif
|
|
383
|
+
icons.mod // https://s.4cdn.org/image/modicon.gif
|
|
384
|
+
icons.developer // https://s.4cdn.org/image/developericon.gif
|
|
385
|
+
icons.manager // https://s.4cdn.org/image/managericon.gif
|
|
386
|
+
icons.founder // https://s.4cdn.org/image/foundericon.gif
|
|
387
|
+
icons.fileDeletedOp // https://s.4cdn.org/image/filedeleted.gif
|
|
388
|
+
icons.fileDeletedReply // https://s.4cdn.org/image/filedeleted-res.gif
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Error Handling
|
|
394
|
+
|
|
395
|
+
All methods throw `FourChanApiError` on non-200/304 responses (e.g. 404 for a thread that no longer exists).
|
|
396
|
+
|
|
397
|
+
```ts
|
|
398
|
+
import { FourChanClient, FourChanApiError } from "4capi";
|
|
399
|
+
|
|
400
|
+
const client = new FourChanClient();
|
|
401
|
+
|
|
402
|
+
try {
|
|
403
|
+
const thread = await client.getThread("g", 1);
|
|
404
|
+
} catch (err) {
|
|
405
|
+
if (err instanceof FourChanApiError) {
|
|
406
|
+
console.error(`HTTP ${err.status} — ${err.url}`);
|
|
407
|
+
if (err.status === 404) {
|
|
408
|
+
// thread was deleted
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
## Efficient Polling Pattern
|
|
417
|
+
|
|
418
|
+
Use `getThreadList` to detect changes cheaply, then only fetch threads that have actually updated.
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
const client = new FourChanClient();
|
|
422
|
+
|
|
423
|
+
let knownThreads = new Map<number, number>(); // threadId → last_modified
|
|
424
|
+
|
|
425
|
+
async function poll() {
|
|
426
|
+
const pages = await client.getThreadList("g");
|
|
427
|
+
|
|
428
|
+
for (const page of pages) {
|
|
429
|
+
for (const entry of page.threads) {
|
|
430
|
+
const prev = knownThreads.get(entry.no);
|
|
431
|
+
|
|
432
|
+
if (!prev || prev < entry.last_modified) {
|
|
433
|
+
const thread = await client.getThread("g", entry.no, {
|
|
434
|
+
ifModifiedSince: prev ? new Date(prev * 1000) : undefined,
|
|
435
|
+
});
|
|
436
|
+
if (thread) {
|
|
437
|
+
knownThreads.set(entry.no, entry.last_modified);
|
|
438
|
+
// handle updated thread...
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Poll every 30 seconds (the client enforces the per-request 1s rate limit)
|
|
446
|
+
setInterval(poll, 30_000);
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Post Field Reference
|
|
452
|
+
|
|
453
|
+
All fields on `Post` except `no`, `resto`, `now`, `time`, and `name` are optional — they only appear when applicable.
|
|
454
|
+
|
|
455
|
+
| Field | Type | Present when |
|
|
456
|
+
|---|---|---|
|
|
457
|
+
| `no` | `number` | Always |
|
|
458
|
+
| `resto` | `number` | Always — `0` for OP |
|
|
459
|
+
| `time` | `number` | Always — UNIX timestamp |
|
|
460
|
+
| `now` | `string` | Always — `MM/DD/YY(Day)HH:MM` |
|
|
461
|
+
| `name` | `string` | Always — defaults to `"Anonymous"` |
|
|
462
|
+
| `sub` | `string` | OP only, if subject was set |
|
|
463
|
+
| `com` | `string` | If a comment was included |
|
|
464
|
+
| `trip` | `string` | If poster used a tripcode |
|
|
465
|
+
| `id` | `string` | On boards with user IDs |
|
|
466
|
+
| `capcode` | `Capcode` | Staff posts only |
|
|
467
|
+
| `country` | `string` | On boards with country flags |
|
|
468
|
+
| `board_flag` | `string` | On boards with board flags |
|
|
469
|
+
| `since4pass` | `number` | If poster used 4chan pass option |
|
|
470
|
+
| `tim` | `number` | If post has an attachment |
|
|
471
|
+
| `filename` | `string` | If post has an attachment |
|
|
472
|
+
| `ext` | `string` | If post has an attachment |
|
|
473
|
+
| `fsize` | `number` | If post has an attachment |
|
|
474
|
+
| `md5` | `string` | If post has an attachment |
|
|
475
|
+
| `w` / `h` | `number` | If post has an attachment |
|
|
476
|
+
| `tn_w` / `tn_h` | `number` | If post has an attachment |
|
|
477
|
+
| `spoiler` | `1` | If file is spoilered |
|
|
478
|
+
| `custom_spoiler` | `number` | If board has custom spoilers |
|
|
479
|
+
| `filedeleted` | `1` | If file was deleted |
|
|
480
|
+
| `m_img` | `1` | If mobile-optimised image exists |
|
|
481
|
+
| `sticky` | `1` | OP — if thread is pinned |
|
|
482
|
+
| `closed` | `1` | OP — if thread is locked |
|
|
483
|
+
| `bumplimit` | `1` | OP — if bump limit reached |
|
|
484
|
+
| `imagelimit` | `1` | OP — if image limit reached |
|
|
485
|
+
| `replies` | `number` | OP — total reply count |
|
|
486
|
+
| `images` | `number` | OP — total image reply count |
|
|
487
|
+
| `omitted_posts` | `number` | OP — replies not shown in preview |
|
|
488
|
+
| `omitted_images` | `number` | OP — image replies not shown in preview |
|
|
489
|
+
| `last_modified` | `number` | OP — UNIX timestamp of last activity |
|
|
490
|
+
| `semantic_url` | `string` | OP — SEO slug |
|
|
491
|
+
| `unique_ips` | `number` | OP — unique poster count (live threads) |
|
|
492
|
+
| `last_replies` | `Post[]` | OP — preview of most recent replies |
|
|
493
|
+
| `archived` | `1` | OP — if thread is archived |
|
|
494
|
+
| `archived_on` | `number` | OP — UNIX timestamp of archival |
|
|
495
|
+
| `tag` | `string` | OP — `/f/` flash category |
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
## Building
|
|
500
|
+
|
|
501
|
+
```sh
|
|
502
|
+
npm run build # compiles to dist/
|
|
503
|
+
npm run typecheck # type-check only, no output
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## 4chan API Terms of Service
|
|
509
|
+
|
|
510
|
+
- Do not use "4chan" in your app name, product, or service name.
|
|
511
|
+
- Do not use the 4chan name, logo, or brand to promote your app.
|
|
512
|
+
- Credit the source as 4chan with a link.
|
|
513
|
+
- Do not claim your app is official.
|
|
514
|
+
- Do not clone 4chan or re-host/repackage the API JSON with ads.
|
|
515
|
+
|
|
516
|
+
Full terms: [https://github.com/4chan/4chan-API](https://github.com/4chan/4chan-API)
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "4chanapi.ts",
|
|
3
|
+
"author": "Honosal <honosal875@proton.me> (https://github.com/honosal)",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Typed TypeScript client for the 4chan API",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist"
|
|
10
|
+
],
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"typecheck": "tsc --noEmit",
|
|
14
|
+
"test": "echo \"No tests specified\" && exit 0"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"4chan",
|
|
18
|
+
"api",
|
|
19
|
+
"typescript",
|
|
20
|
+
"react-native"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "https://github.com/honosal/4chanapi.ts.git"
|
|
26
|
+
},
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/honosal/4chanapi.ts/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/honosal/4chanapi.ts",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "^5.4.5"
|
|
33
|
+
}
|
|
34
|
+
}
|