old-twitter-image-search/src/api.ts

211 lines
5.6 KiB
TypeScript

import { GM_getValue, GM_xmlhttpRequest } from 'vite-plugin-monkey/dist/client';
export const DANBOORU_API = "https://danbooru.donmai.us"
const SAUCENAO_KEY = "debugging only"
export type booru_post = {
id: number,
timestamp: string,
source_domain: string,
thumbnail: string,
img_width: number,
img_height: number
}
export async function tweet_search(id:string,username:string) : Promise<Array<booru_post>> {
const posts_api = `${DANBOORU_API}/posts.json`
const tweet_url = `https://twitter.com/${username}/status/${id}`;
const posts_url = new URL(posts_api);
posts_url.searchParams.append('tags',`status:any source:${tweet_url}`);
posts_url.searchParams.append('limit',`5`);
const booru_request = new Promise<string>((res,rej) => {
GM_xmlhttpRequest({
url: posts_url.toString(),
method:'GET',
responseType: 'json',
anonymous:true,
onload: (response) => res(response.responseText),
onerror: (error) => rej(error)
})
})
const response = await booru_request
const posts:any[] = JSON.parse(response)
//console.debug({grape:posts})
const result = posts.map<booru_post>( post => {
const preview = post.media_asset.variants?.find( (v:any) => v.type=="preview" || v.type=="180x180")
const source_domain = new URL(post.source).hostname
return {
id: post.id,
timestamp: post.created_at,
source_domain: source_domain,
thumbnail: preview?.url,
img_width: post.image_width,
img_height: post.image_height
}
})
return result
}
export async function iqdb_search(media_url:string) : Promise<Array<booru_post>> {
const iqdb_api = `${DANBOORU_API}/iqdb_queries.json`
const iqdb_url = new URL(iqdb_api);
iqdb_url.searchParams.append('url',media_url);
iqdb_url.searchParams.append('similarity','80');
iqdb_url.searchParams.append('limit','5');
const booru_request = new Promise<string>((res,rej) => {
GM_xmlhttpRequest({
url: iqdb_url.toString(),
method:'GET',
responseType: 'json',
anonymous:true,
onload: (response) => res(response.responseText),
onerror: (error) => rej(error)
})
})
const response = await booru_request
const iqdb_res = JSON.parse(response) as any[]
const result = iqdb_res.map<booru_post>( i => {
const post = i.post;
const preview = post.media_asset.variants?.find( (v:any) => v.type=="preview" || v.type=="180x180")
const source_domain = new URL(post.source).hostname
return {
id: post.id,
timestamp: post.created_at,
source_domain: source_domain,
thumbnail: preview?.url,
img_width: post.image_width,
img_height: post.image_height
}
})
return result
}
type sauce_response = {
header: any,
results: sauce_item[]
}
type sauce_item = {
header: {
similarity : string,
thumbnail : string
},
data: {
danbooru_id: number,
source: string
}
}
export async function saucenao_search(media_url:string) {
const saucenao_key = GM_getValue('DunkOatmeal_SNKey','');
if ( !saucenao_key ) {
alert("SauceNAO API required. \nGo to Profile Menu > Settings to add it.")
return []
}
const danbo_bm = 0b1000000000;
//const pixiv_bm = 0b0000100000;
const bitmask = danbo_bm //| pixiv_bm;
const min_similarity = 80
const sauce_api = `http://saucenao.com/search.php`;
const sauce_url = new URL(sauce_api);
sauce_url.searchParams.append('numres','5');
sauce_url.searchParams.append('output_type','2');
sauce_url.searchParams.append('dbmask',String(bitmask));
sauce_url.searchParams.append('api_key',saucenao_key);
sauce_url.searchParams.append('url',media_url);
const booru_request = new Promise<string>((res,rej) => {
GM_xmlhttpRequest({
url: sauce_url.toString(),
method:'GET',
responseType: 'json',
anonymous:true,
onload: (response) => res(response.responseText),
onerror: (error) => rej(error)
})
})
const response = await booru_request
const sauce_res:sauce_response = JSON.parse(response)
console.debug({sauce:sauce_res})
const result = sauce_res.results
.filter(x => Number(x.header.similarity) >= min_similarity )
.map<booru_post>( s => {
const danbo = s.data;
const preview = s.header.thumbnail;
const source_domain = new URL(danbo.source).hostname
return {
id: danbo.danbooru_id,
timestamp: "",
source_domain: source_domain,
thumbnail: preview,
img_width: -1,
img_height: -1
}
})
return result;
}
export type danbo_artist = {
id: number,
name: string,
is_banned: bool,
urls: Array<danbo_artist_url>
}
type danbo_artist_url = {
url:string,
is_active:bool
}
export async function danbo_artist_search(tweet_username:string) : Promise<danbo_artist> {
const artist_api = `${DANBOORU_API}/artists.json`
const tweet_url = `https://twitter.com/${tweet_username}`;
const artists_url = new URL(artist_api);
artists_url.searchParams.append("search[url_matches]",tweet_url);
artists_url.searchParams.append("only","id,name,urls,is_banned");
const booru_request = new Promise<string>((res,rej) => {
GM_xmlhttpRequest({
url: artists_url.toString(),
method:'GET',
responseType: 'json',
anonymous:true,
onload: (response) => res(response.responseText),
onerror: (error) => rej(error)
})
})
const response = await booru_request
const artists : danbo_artist[] = JSON.parse(response);
let artist = artists.shift();
if ( artist ) artist.urls = artist.urls.filter(x =>
x.is_active &&
!( x.url.includes("twitter.com/intent/") || x.url.includes("pixiv.net/stacc/")));
return artist
}