mirror of
https://git.stupid.fish/teidesu/scripts.git
synced 2025-07-28 02:32:11 +10:00
chore: update public repo
This commit is contained in:
parent
c118bcbfc3
commit
f02ccb6029
12 changed files with 510 additions and 2 deletions
|
@ -11,8 +11,11 @@
|
||||||
"@fuman/net": "^0.0.9",
|
"@fuman/net": "^0.0.9",
|
||||||
"@fuman/node": "^0.0.4",
|
"@fuman/node": "^0.0.4",
|
||||||
"@mtcute/node": "^0.19.1",
|
"@mtcute/node": "^0.19.1",
|
||||||
|
"@types/better-sqlite3": "^7.6.12",
|
||||||
"@types/plist": "^3.0.5",
|
"@types/plist": "^3.0.5",
|
||||||
"@types/spinnies": "^0.5.3",
|
"@types/spinnies": "^0.5.3",
|
||||||
|
"better-sqlite3": "^11.8.1",
|
||||||
|
"canvas": "^3.1.0",
|
||||||
"cheerio": "^1.0.0",
|
"cheerio": "^1.0.0",
|
||||||
"es-main": "^1.3.0",
|
"es-main": "^1.3.0",
|
||||||
"filesize": "^10.1.6",
|
"filesize": "^10.1.6",
|
||||||
|
@ -38,5 +41,11 @@
|
||||||
"htmlparser2": "^10.0.0",
|
"htmlparser2": "^10.0.0",
|
||||||
"zod": "3.23.8",
|
"zod": "3.23.8",
|
||||||
"zx": "8.2.2"
|
"zx": "8.2.2"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"onlyBuiltDependencies": [
|
||||||
|
"better-sqlite3",
|
||||||
|
"canvas"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
pnpm-lock.yaml
generated
38
pnpm-lock.yaml
generated
|
@ -23,12 +23,21 @@ importers:
|
||||||
'@mtcute/node':
|
'@mtcute/node':
|
||||||
specifier: ^0.19.1
|
specifier: ^0.19.1
|
||||||
version: 0.19.1
|
version: 0.19.1
|
||||||
|
'@types/better-sqlite3':
|
||||||
|
specifier: ^7.6.12
|
||||||
|
version: 7.6.12
|
||||||
'@types/plist':
|
'@types/plist':
|
||||||
specifier: ^3.0.5
|
specifier: ^3.0.5
|
||||||
version: 3.0.5
|
version: 3.0.5
|
||||||
'@types/spinnies':
|
'@types/spinnies':
|
||||||
specifier: ^0.5.3
|
specifier: ^0.5.3
|
||||||
version: 0.5.3
|
version: 0.5.3
|
||||||
|
better-sqlite3:
|
||||||
|
specifier: ^11.8.1
|
||||||
|
version: 11.8.1
|
||||||
|
canvas:
|
||||||
|
specifier: ^3.1.0
|
||||||
|
version: 3.1.0
|
||||||
cheerio:
|
cheerio:
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
@ -507,6 +516,9 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
eslint: '>=8.40.0'
|
eslint: '>=8.40.0'
|
||||||
|
|
||||||
|
'@types/better-sqlite3@7.6.12':
|
||||||
|
resolution: {integrity: sha512-fnQmj8lELIj7BSrZQAdBMHEHX8OZLYIHXqAKT1O7tDfLxaINzf00PMjw22r3N/xXh0w/sGHlO6SVaCQ2mj78lg==}
|
||||||
|
|
||||||
'@types/debug@4.1.12':
|
'@types/debug@4.1.12':
|
||||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||||
|
|
||||||
|
@ -688,6 +700,9 @@ packages:
|
||||||
better-sqlite3@11.3.0:
|
better-sqlite3@11.3.0:
|
||||||
resolution: {integrity: sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==}
|
resolution: {integrity: sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==}
|
||||||
|
|
||||||
|
better-sqlite3@11.8.1:
|
||||||
|
resolution: {integrity: sha512-9BxNaBkblMjhJW8sMRZxnxVTRgbRmssZW0Oxc1MPBTfiR+WW21e2Mk4qu8CzrcZb1LwPCnFsfDEzq+SNcBU8eg==}
|
||||||
|
|
||||||
bindings@1.5.0:
|
bindings@1.5.0:
|
||||||
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==}
|
||||||
|
|
||||||
|
@ -726,6 +741,10 @@ packages:
|
||||||
caniuse-lite@1.0.30001684:
|
caniuse-lite@1.0.30001684:
|
||||||
resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==}
|
resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==}
|
||||||
|
|
||||||
|
canvas@3.1.0:
|
||||||
|
resolution: {integrity: sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg==}
|
||||||
|
engines: {node: ^18.12.0 || >= 20.9.0}
|
||||||
|
|
||||||
ccount@2.0.1:
|
ccount@2.0.1:
|
||||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||||
|
|
||||||
|
@ -1579,6 +1598,9 @@ packages:
|
||||||
resolution: {integrity: sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==}
|
resolution: {integrity: sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
node-addon-api@7.1.1:
|
||||||
|
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
|
||||||
|
|
||||||
node-releases@2.0.18:
|
node-releases@2.0.18:
|
||||||
resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
|
resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==}
|
||||||
|
|
||||||
|
@ -2462,6 +2484,10 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
|
'@types/better-sqlite3@7.6.12':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.10.0
|
||||||
|
|
||||||
'@types/debug@4.1.12':
|
'@types/debug@4.1.12':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/ms': 0.7.34
|
'@types/ms': 0.7.34
|
||||||
|
@ -2669,6 +2695,11 @@ snapshots:
|
||||||
bindings: 1.5.0
|
bindings: 1.5.0
|
||||||
prebuild-install: 7.1.2
|
prebuild-install: 7.1.2
|
||||||
|
|
||||||
|
better-sqlite3@11.8.1:
|
||||||
|
dependencies:
|
||||||
|
bindings: 1.5.0
|
||||||
|
prebuild-install: 7.1.2
|
||||||
|
|
||||||
bindings@1.5.0:
|
bindings@1.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
file-uri-to-path: 1.0.0
|
file-uri-to-path: 1.0.0
|
||||||
|
@ -2712,6 +2743,11 @@ snapshots:
|
||||||
|
|
||||||
caniuse-lite@1.0.30001684: {}
|
caniuse-lite@1.0.30001684: {}
|
||||||
|
|
||||||
|
canvas@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
node-addon-api: 7.1.1
|
||||||
|
prebuild-install: 7.1.2
|
||||||
|
|
||||||
ccount@2.0.1: {}
|
ccount@2.0.1: {}
|
||||||
|
|
||||||
chalk@2.4.2:
|
chalk@2.4.2:
|
||||||
|
@ -3808,6 +3844,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
semver: 7.6.3
|
semver: 7.6.3
|
||||||
|
|
||||||
|
node-addon-api@7.1.1: {}
|
||||||
|
|
||||||
node-releases@2.0.18: {}
|
node-releases@2.0.18: {}
|
||||||
|
|
||||||
normalize-package-data@2.5.0:
|
normalize-package-data@2.5.0:
|
||||||
|
|
1
scripts/misc/shikimori/.gitignore
vendored
Normal file
1
scripts/misc/shikimori/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/_very-secret-ratelimit-bypass.ts
|
53
scripts/misc/shikimori/animes.ts
Normal file
53
scripts/misc/shikimori/animes.ts
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
import { asyncPool } from '@fuman/utils'
|
||||||
|
import Database from 'better-sqlite3'
|
||||||
|
import { counterIter, ffetchShiki } from './utils.ts'
|
||||||
|
|
||||||
|
const isManga = process.argv[2] === 'manga'
|
||||||
|
const isRanobe = process.argv[2] === 'ranobe'
|
||||||
|
|
||||||
|
const collection = isManga ? 'mangas' : isRanobe ? 'ranobe' : 'animes'
|
||||||
|
|
||||||
|
const db = new Database('assets/shikimori.db')
|
||||||
|
db.exec(`
|
||||||
|
create table if not exists ${collection} (
|
||||||
|
id integer primary key,
|
||||||
|
data text not null
|
||||||
|
);
|
||||||
|
create table if not exists ${collection}_related (
|
||||||
|
id integer primary key,
|
||||||
|
data text not null
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
|
||||||
|
const insertQuery = db.prepare(`insert into ${collection} (id, data) values (?, ?) on conflict (id) do update set data = excluded.data`)
|
||||||
|
const insertRelatedQuery = db.prepare(`insert into ${collection}_related (id, data) values (?, ?) on conflict (id) do update set data = excluded.data`)
|
||||||
|
|
||||||
|
const maxId = await ffetchShiki(`/api/${collection}?order=id_desc`).json<any>().then(res => res[0].id)
|
||||||
|
console.log('max id: %d', maxId)
|
||||||
|
|
||||||
|
const counter = counterIter(1, maxId)
|
||||||
|
|
||||||
|
await asyncPool(counter.iter, async (id) => {
|
||||||
|
if (id % 1000 === 0) {
|
||||||
|
console.log('currently at %d', id)
|
||||||
|
}
|
||||||
|
// const data = await ffetchShiki(`/api/${collection}/${id}`, {
|
||||||
|
// validateResponse: false,
|
||||||
|
// }).json<any>()
|
||||||
|
|
||||||
|
// if (data.code === 404) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// insertQuery.run(id, JSON.stringify(data))
|
||||||
|
|
||||||
|
const data = await ffetchShiki(`/api/${collection}/${id}/related`, {
|
||||||
|
validateResponse: false,
|
||||||
|
}).json<any>()
|
||||||
|
|
||||||
|
if (data.code === 404) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
insertRelatedQuery.run(id, JSON.stringify(data))
|
||||||
|
}, { limit: 64 })
|
30
scripts/misc/shikimori/bans.ts
Normal file
30
scripts/misc/shikimori/bans.ts
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import { asyncPool } from '@fuman/utils'
|
||||||
|
import Database from 'better-sqlite3'
|
||||||
|
import { counterIter, ffetchShiki } from './utils.ts'
|
||||||
|
|
||||||
|
const db = new Database('assets/shikimori.db')
|
||||||
|
db.exec(`
|
||||||
|
create table if not exists bans (
|
||||||
|
id integer primary key,
|
||||||
|
data text not null
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
|
||||||
|
const insertQuery = db.prepare('insert into bans (id, data) values (?, ?) on conflict (id) do update set data = excluded.data')
|
||||||
|
|
||||||
|
const counter = counterIter(1)
|
||||||
|
|
||||||
|
await asyncPool(counter.iter, async (page) => {
|
||||||
|
if (page % 100 === 0) {
|
||||||
|
console.log('currently at page %d', page)
|
||||||
|
}
|
||||||
|
const data = await ffetchShiki(`/api/bans?page=${page}`).json<any>()
|
||||||
|
if (!data.length) {
|
||||||
|
counter.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const ban of data) {
|
||||||
|
insertQuery.run(ban.id, JSON.stringify(ban))
|
||||||
|
}
|
||||||
|
}, { limit: 64 })
|
59
scripts/misc/shikimori/characters.ts
Normal file
59
scripts/misc/shikimori/characters.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { asyncPool } from '@fuman/utils'
|
||||||
|
import Database from 'better-sqlite3'
|
||||||
|
import { counterIter, ffetchShiki } from './utils.ts'
|
||||||
|
|
||||||
|
const db = new Database('assets/shikimori.db')
|
||||||
|
db.pragma('journal_mode = WAL')
|
||||||
|
db.exec(`
|
||||||
|
create table if not exists characters (
|
||||||
|
id integer primary key,
|
||||||
|
data text not null
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
|
||||||
|
const insertQuery = db.prepare('insert into characters (id, data) values (?, ?) on conflict (id) do update set data = excluded.data')
|
||||||
|
|
||||||
|
// find maxId with binary search
|
||||||
|
let maxIdPage = 20000
|
||||||
|
let maxIdPageStart = 1
|
||||||
|
let maxId = 0
|
||||||
|
while (true) {
|
||||||
|
const midPage = Math.floor((maxIdPageStart + maxIdPage) / 2)
|
||||||
|
console.log('trying page %d', midPage)
|
||||||
|
const res = await ffetchShiki.post('/api/graphql', {
|
||||||
|
json: {
|
||||||
|
query: `{characters(page: ${midPage}, limit: 50) { id }}`,
|
||||||
|
},
|
||||||
|
}).json<any>()
|
||||||
|
const items = res.data.characters
|
||||||
|
if (!items.length) {
|
||||||
|
maxIdPage = midPage - 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxIdPageStart === midPage) {
|
||||||
|
maxId = Math.max(...items.map(item => item.id))
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
maxIdPageStart = midPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('max id: %d', maxId)
|
||||||
|
|
||||||
|
const counter = counterIter(1, maxId)
|
||||||
|
|
||||||
|
await asyncPool(counter.iter, async (id) => {
|
||||||
|
if (id % 1000 === 0) {
|
||||||
|
console.log('currently at %d', id)
|
||||||
|
}
|
||||||
|
const data = await ffetchShiki(`/api/characters/${id}`, {
|
||||||
|
validateResponse: false,
|
||||||
|
}).json<any>()
|
||||||
|
|
||||||
|
if (data.code === 404) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
insertQuery.run(id, JSON.stringify(data))
|
||||||
|
}, { limit: 64 })
|
49
scripts/misc/shikimori/clubs.ts
Normal file
49
scripts/misc/shikimori/clubs.ts
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import { asyncPool } from '@fuman/utils'
|
||||||
|
import Database from 'better-sqlite3'
|
||||||
|
import { counterIter, ffetchShiki } from './utils.ts'
|
||||||
|
|
||||||
|
const db = new Database('assets/shikimori.db')
|
||||||
|
db.pragma('journal_mode = WAL')
|
||||||
|
db.exec(`
|
||||||
|
create table if not exists clubs (
|
||||||
|
id integer primary key,
|
||||||
|
data text not null
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
|
||||||
|
const insertQuery = db.prepare('insert into clubs (id, data) values (?, ?) on conflict (id) do update set data = excluded.data')
|
||||||
|
|
||||||
|
// collect clubs ids
|
||||||
|
|
||||||
|
const ids: Set<number> = new Set()
|
||||||
|
|
||||||
|
const pageCounter = counterIter(1)
|
||||||
|
|
||||||
|
await asyncPool(pageCounter.iter, async (page) => {
|
||||||
|
const data = await ffetchShiki('/api/clubs', {
|
||||||
|
query: { page, limit: 50 },
|
||||||
|
validateResponse: false,
|
||||||
|
}).json<any>()
|
||||||
|
if (!data.length) {
|
||||||
|
pageCounter.end()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const club of data) {
|
||||||
|
ids.add(club.id)
|
||||||
|
}
|
||||||
|
}, { limit: 16 })
|
||||||
|
|
||||||
|
console.log('collected %d clubs', ids.size)
|
||||||
|
|
||||||
|
await asyncPool(ids, async (id, idx) => {
|
||||||
|
if (idx % 100 === 0) {
|
||||||
|
console.log('currently at %d', idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clubData = await ffetchShiki(`/api/clubs/${id}`).json<any>()
|
||||||
|
if (clubData.code === 404) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
insertQuery.run(id, JSON.stringify(clubData))
|
||||||
|
}, { limit: 64 })
|
37
scripts/misc/shikimori/comments.ts
Normal file
37
scripts/misc/shikimori/comments.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import { asyncPool } from '@fuman/utils'
|
||||||
|
import Database from 'better-sqlite3'
|
||||||
|
import { counterIter, ffetchShiki } from './utils.ts'
|
||||||
|
|
||||||
|
const db = new Database('assets/shikimori.db')
|
||||||
|
db.pragma('journal_mode = WAL')
|
||||||
|
db.exec(`
|
||||||
|
create table if not exists comments (
|
||||||
|
id integer primary key,
|
||||||
|
data text not null
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
|
||||||
|
const insertQuery = db.prepare('insert into comments (id, data) values (?, ?) on conflict (id) do update set data = excluded.data')
|
||||||
|
|
||||||
|
const counter = counterIter(11312000)
|
||||||
|
let consequent404 = 0
|
||||||
|
await asyncPool(counter.iter, async (id) => {
|
||||||
|
if (id % 1000 === 0) {
|
||||||
|
console.log('currently at %d', id)
|
||||||
|
}
|
||||||
|
const data = await ffetchShiki(`/api/comments/${id}`, {
|
||||||
|
validateResponse: false,
|
||||||
|
}).json<any>()
|
||||||
|
|
||||||
|
if (data.code === 404) {
|
||||||
|
consequent404++
|
||||||
|
if (consequent404 > 10_000) {
|
||||||
|
counter.end()
|
||||||
|
console.log('10k consequent 404-s, stopping')
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
consequent404 = 0
|
||||||
|
insertQuery.run(id, JSON.stringify(data))
|
||||||
|
}, { limit: 64 })
|
59
scripts/misc/shikimori/people.ts
Normal file
59
scripts/misc/shikimori/people.ts
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import { asyncPool } from '@fuman/utils'
|
||||||
|
import Database from 'better-sqlite3'
|
||||||
|
import { counterIter, ffetchShiki } from './utils.ts'
|
||||||
|
|
||||||
|
const db = new Database('assets/shikimori.db')
|
||||||
|
db.pragma('journal_mode = WAL')
|
||||||
|
db.exec(`
|
||||||
|
create table if not exists people (
|
||||||
|
id integer primary key,
|
||||||
|
data text not null
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
|
||||||
|
const insertQuery = db.prepare('insert into people (id, data) values (?, ?) on conflict (id) do update set data = excluded.data')
|
||||||
|
|
||||||
|
// find maxId with binary search
|
||||||
|
let maxIdPage = 20000
|
||||||
|
let maxIdPageStart = 1
|
||||||
|
let maxId = 0
|
||||||
|
while (true) {
|
||||||
|
const midPage = Math.floor((maxIdPageStart + maxIdPage) / 2)
|
||||||
|
console.log('trying page %d', midPage)
|
||||||
|
const res = await ffetchShiki.post('/api/graphql', {
|
||||||
|
json: {
|
||||||
|
query: `{people(page: ${midPage}, limit: 50) { id }}`,
|
||||||
|
},
|
||||||
|
}).json<any>()
|
||||||
|
const items = res.data.people
|
||||||
|
if (!items.length) {
|
||||||
|
maxIdPage = midPage - 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxIdPageStart === midPage) {
|
||||||
|
maxId = Math.max(...items.map(item => item.id))
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
maxIdPageStart = midPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('max id: %d', maxId)
|
||||||
|
|
||||||
|
const counter = counterIter(1, maxId)
|
||||||
|
|
||||||
|
await asyncPool(counter.iter, async (id) => {
|
||||||
|
if (id % 1000 === 0) {
|
||||||
|
console.log('currently at %d', id)
|
||||||
|
}
|
||||||
|
const data = await ffetchShiki(`/api/people/${id}`, {
|
||||||
|
validateResponse: false,
|
||||||
|
}).json<any>()
|
||||||
|
|
||||||
|
if (data.code === 404) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
insertQuery.run(id, JSON.stringify(data))
|
||||||
|
}, { limit: 64 })
|
129
scripts/misc/shikimori/users.ts
Normal file
129
scripts/misc/shikimori/users.ts
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
import { asyncPool } from '@fuman/utils'
|
||||||
|
import Database from 'better-sqlite3'
|
||||||
|
import { counterIter, ffetchShiki } from './utils.ts'
|
||||||
|
|
||||||
|
const db = new Database('assets/shikimori.db')
|
||||||
|
db.pragma('journal_mode = WAL')
|
||||||
|
db.exec(`
|
||||||
|
create table if not exists users (
|
||||||
|
id integer primary key,
|
||||||
|
data text not null
|
||||||
|
);
|
||||||
|
`)
|
||||||
|
|
||||||
|
const insertQuery = db.prepare('insert into users (id, data) values (?, ?) on conflict (id) do update set data = excluded.data')
|
||||||
|
|
||||||
|
async function fetchUserFriends(userId: number) {
|
||||||
|
const list: any[] = []
|
||||||
|
for (let page = 1; ; page++) {
|
||||||
|
const data = await ffetchShiki(`/api/users/${userId}/friends`, {
|
||||||
|
query: { page, limit: 100 },
|
||||||
|
validateResponse: false,
|
||||||
|
}).json<any>()
|
||||||
|
if (!data.length) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(...data)
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchUserRates(userId: number, kind: 'anime' | 'manga') {
|
||||||
|
const list: any[] = []
|
||||||
|
|
||||||
|
for (let page = 1; ; page++) {
|
||||||
|
const data = await ffetchShiki(`/api/users/${userId}/${kind}_rates`, {
|
||||||
|
query: { page, limit: 1000 },
|
||||||
|
validateResponse: false,
|
||||||
|
}).json<any>()
|
||||||
|
if (data === null || !data.length) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of data) {
|
||||||
|
// clean up unnecessary data before inserting
|
||||||
|
delete item.user
|
||||||
|
if (item[kind]) {
|
||||||
|
item[`${kind}_id`] = item[kind].id
|
||||||
|
delete item[kind]
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchUserHistory(userId: number) {
|
||||||
|
const list: any[] = []
|
||||||
|
for (let page = 0; ; page++) {
|
||||||
|
const data = await ffetchShiki(`/api/users/${userId}/history`, {
|
||||||
|
query: { page, limit: 100 },
|
||||||
|
validateResponse: false,
|
||||||
|
}).json<any>()
|
||||||
|
if (!data.length) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const item of data) {
|
||||||
|
if (item.target) {
|
||||||
|
item.target_type = item.target.url.startsWith('/animes/') ? 'anime' : 'manga'
|
||||||
|
item.target_id = item.target.id
|
||||||
|
delete item.target
|
||||||
|
}
|
||||||
|
list.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
const counter = counterIter(467800)
|
||||||
|
let consequent404 = 0
|
||||||
|
await asyncPool(counter.iter, async (id) => {
|
||||||
|
if (id % 100 === 0) {
|
||||||
|
console.log('currently at %d', id)
|
||||||
|
}
|
||||||
|
const data = await ffetchShiki(`/api/users/${id}`, {
|
||||||
|
validateResponse: false,
|
||||||
|
}).json<any>()
|
||||||
|
|
||||||
|
if (data.code === 404) {
|
||||||
|
consequent404++
|
||||||
|
if (consequent404 > 1_000) {
|
||||||
|
counter.end()
|
||||||
|
console.log('1k consequent 404-s, stopping')
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
consequent404 = 0
|
||||||
|
|
||||||
|
// fetch extra data
|
||||||
|
const [
|
||||||
|
favsData,
|
||||||
|
friends,
|
||||||
|
animeRates,
|
||||||
|
mangaRates,
|
||||||
|
history,
|
||||||
|
] = await Promise.all([
|
||||||
|
ffetchShiki(`/api/users/${id}/favourites`).json<any>(),
|
||||||
|
fetchUserFriends(id),
|
||||||
|
fetchUserRates(id, 'anime'),
|
||||||
|
fetchUserRates(id, 'manga'),
|
||||||
|
fetchUserHistory(id),
|
||||||
|
])
|
||||||
|
|
||||||
|
data._extra = {
|
||||||
|
favs: favsData,
|
||||||
|
friends,
|
||||||
|
animeRates,
|
||||||
|
mangaRates,
|
||||||
|
history,
|
||||||
|
}
|
||||||
|
|
||||||
|
insertQuery.run(id, JSON.stringify(data))
|
||||||
|
}, { limit: 32 })
|
39
scripts/misc/shikimori/utils.ts
Normal file
39
scripts/misc/shikimori/utils.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { ffetch as ffetchBase } from '../../../utils/fetch.ts'
|
||||||
|
import { rateLimitBypass } from './_very-secret-ratelimit-bypass.ts'
|
||||||
|
|
||||||
|
export const ffetchShiki = ffetchBase.extend({
|
||||||
|
baseUrl: 'https://shikimori.one',
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
|
||||||
|
'Accept-Language': 'en-US,en;q=0.9',
|
||||||
|
'Accept-Encoding': 'gzip, deflate, br',
|
||||||
|
},
|
||||||
|
retry: {},
|
||||||
|
...(rateLimitBypass as any),
|
||||||
|
})
|
||||||
|
|
||||||
|
export function counterIter(start = 0, end = Infinity) {
|
||||||
|
let i = start
|
||||||
|
let ended = false
|
||||||
|
|
||||||
|
const iter: IterableIterator<number> = {
|
||||||
|
[Symbol.iterator]: () => iter,
|
||||||
|
next() {
|
||||||
|
if (ended) {
|
||||||
|
return { value: undefined, done: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > end) {
|
||||||
|
return { value: undefined, done: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { value: i++, done: false }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
iter,
|
||||||
|
end: () => {
|
||||||
|
ended = true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises'
|
||||||
import { join } from 'node:path'
|
import { join } from 'node:path'
|
||||||
import plist from 'plist'
|
import plist from 'plist'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { $ } from 'zx'
|
import { $, sleep } from 'zx'
|
||||||
import { ffetch } from '../../utils/fetch.ts'
|
import { ffetch } from '../../utils/fetch.ts'
|
||||||
|
|
||||||
const latestVerInfo = await ffetch('https://api.github.com/repos/forkgram/tdesktop/releases/latest').parsedJson(
|
const latestVerInfo = await ffetch('https://api.github.com/repos/forkgram/tdesktop/releases/latest').parsedJson(
|
||||||
|
@ -40,12 +40,17 @@ if (!arm64Asset) {
|
||||||
console.log('installing new version...')
|
console.log('installing new version...')
|
||||||
await $`curl -L ${arm64Asset.browser_download_url} -o /tmp/forkgram.zip`
|
await $`curl -L ${arm64Asset.browser_download_url} -o /tmp/forkgram.zip`
|
||||||
await $`unzip -o /tmp/forkgram.zip -d /tmp/forkgram`
|
await $`unzip -o /tmp/forkgram.zip -d /tmp/forkgram`
|
||||||
await $`kill -9 $(pgrep -f /Applications/Forkgram.app/Contents/MacOS/Telegram)`
|
const pid = await $`/usr/bin/pgrep -f /Applications/Forkgram.app/Contents/MacOS/Telegram`.text().catch(() => null)
|
||||||
|
if (pid) {
|
||||||
|
await $`kill -9 ${pid.trim()}`
|
||||||
|
}
|
||||||
await $`rm -rf ${INSTALL_PATH}`
|
await $`rm -rf ${INSTALL_PATH}`
|
||||||
await $`mv /tmp/forkgram/Telegram.app ${INSTALL_PATH}`
|
await $`mv /tmp/forkgram/Telegram.app ${INSTALL_PATH}`
|
||||||
await $`rm -rf /tmp/forkgram`
|
await $`rm -rf /tmp/forkgram`
|
||||||
await $`xattr -cr ${INSTALL_PATH}`
|
await $`xattr -cr ${INSTALL_PATH}`
|
||||||
|
|
||||||
|
await sleep(1000)
|
||||||
|
|
||||||
await $`open ${INSTALL_PATH}`
|
await $`open ${INSTALL_PATH}`
|
||||||
|
|
||||||
console.log('✅ done')
|
console.log('✅ done')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue