Created
August 10, 2019 04:26
-
-
Save Sasquire/7b5745d2caece26772e05fc14e98d67f to your computer and use it in GitHub Desktop.
a quick little program that will add a box and two fields so you can easily merge the tags, sources, description, and flag one as inferior to the other.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name e621 post merger | |
// @namespace http://tampermonkey.net/ | |
// @version 0.1 | |
// @description try to take over the world! | |
// @author You | |
// @match https://e621.net/post/show/* | |
// @grant GM.setValue | |
// @grant GM.getValue | |
// ==/UserScript== | |
const e621_api = {}; | |
e621_api.user_agent = 'Set this to a more meaningful useragent'; | |
e621_api.credentials = async () => { | |
// eslint-disable-next-line no-undef | |
const username = await GM.getValue('username'); | |
// eslint-disable-next-line no-undef | |
const api_key = await GM.getValue('api_key'); | |
if(!username || !api_key){ | |
throw new Error('Username or API key is not set'); | |
} | |
return { | |
username: username, | |
api_key: api_key | |
}; | |
}; | |
e621_api.download = async (url, options) => { | |
options = options || {}; | |
options['User-Agent'] = options['User-Agent'] || e621_api.user_agent; | |
options.method = options.method || 'GET'; | |
// eslint-disable-next-line no-undef | |
return fetch(new Request(url), options) | |
.then(async res => { | |
if(res.status != 200){ | |
const text = await res.text(); | |
throw new Error(`Status: ${res.status}\n${text}`); | |
} else { | |
return res.text(); // This is a promise | |
} | |
}) | |
.then(e => { | |
// Try to parse the json, If you can't just return the text; | |
try { | |
return JSON.parse(e); | |
} catch(error) { | |
return e; | |
} | |
}); | |
}; | |
/* | |
Good results look like this | |
{ | |
post_id: 1947854, | |
location: "https://e621.net/post/show/1947854", | |
success: true | |
} | |
Official Documentation | |
post[tags] A space delimited list of tags. | |
post[file] The file data encoded as a multipart form. | |
post[rating] The rating for the post. Can be: safe, questionable, or explicit. | |
post[upload_url] If this is a URL, e621 will download the file. | |
post[source] This will be used as the post's 'Source' text. Separate multiple URLs with %0A (url-encoded newline) to define multiple sources. Limit of five URLs | |
post[description] The description for the post. | |
post[is_rating_locked] Set to true to prevent others from changing the rating. | |
post[is_note_locked] Set to true to prevent others from adding notes. | |
post[parent_id] The ID of the parent post. | |
Input looks like | |
{ | |
tags: <A space delimited list of tags>, | |
rating: <Can be: safe, questionable, or explicit>, | |
url: <URL pointing to the image>, | |
source: <A new line delimited list of sources>, | |
description: <description for the post>, | |
parent: <ID of the parent post> | |
} | |
*/ | |
e621_api.post_create = async (obj) => { | |
const url = 'https://e621.net/post/create.json'; | |
const { username, api_key } = await e621_api.credentials(); | |
// eslint-disable-next-line no-undef | |
const form = new FormData(); | |
form.set('login', username); | |
form.set('password_hash', api_key); | |
form.set('post[tags]', obj.tags); | |
form.set('post[rating]', obj.rating); | |
form.set('post[upload_url]', obj.url); | |
form.set('post[source]', obj.source); | |
form.set('post[description]', obj.description); | |
const new_parent = Number(obj.parent); | |
if(Number.isNaN(new_parent) == false && Number > 0){ | |
form.set('post[parent_id]', new_parent); | |
} | |
return e621_api.download(url, { | |
method: 'POST', | |
'User-Agent': e621_api.user_agent, | |
body: form | |
}); | |
}; | |
/* | |
Official documentation | |
id The ID number of the post to update. | |
post[tags] A space delimited list of tags. | |
post[old_tags] A space delimited list of tags. Should include the same tags submitted to post[tags] minus any intended changes. Does nothing without post[tags]. | |
post[rating] The rating for the post. Can be: safe, questionable, or explicit. | |
post[source] This will be used as the post's 'Source' text. Separate multiple URLs with %0A (url-encoded newline) to define multiple sources. Limit of five URLs | |
post[description] This will be used as the post's 'Description' text. | |
post[is_rating_locked] Set to true to prevent others from changing the rating. | |
post[is_note_locked] Set to true to prevent others from adding notes. | |
post[parent_id] The ID of the parent post. | |
reason The reason for the submitted changes. Inline DText allowed. | |
If an option is not specified, it will not be changed | |
Input looks like | |
{ | |
// This is required | |
post_id: <ID of the post to edit>, | |
// If you want one of these, both must be specified | |
tags: <A space delimited list of tags>, | |
old_tags: <A space delimited list of old tags> | |
rating: <Can be: safe, questionable, or explicit>, | |
source: <A new line delimited list of sources>, | |
description: <description for the post>, | |
parent_id: <ID of the parent post>, | |
reason: <dtext reason for editing the post> | |
} | |
*/ | |
e621_api.post_update = async (obj) => { | |
const url = 'https://e621.net/post/update.json'; | |
const { username, api_key } = await e621_api.credentials(); | |
if(!obj.post_id){ | |
throw new Error('post_id is not defined'); | |
} | |
// eslint-disable-next-line no-undef | |
const form = new FormData(); | |
form.set('login', username); | |
form.set('password_hash', api_key); | |
form.set('id', obj.post_id); | |
if(obj.tags && obj.old_tags){ | |
form.set('post[tags]', obj.tags); | |
form.set('post[old_tags]', obj.old_tags); | |
} | |
if(obj.rating){ | |
form.set('post[rating]', obj.rating); | |
} | |
if(obj.source){ | |
form.set('post[source]', obj.source); | |
} | |
if(obj.description){ | |
form.set('post[description]', obj.description); | |
} | |
if(obj.parent_id){ | |
form.set('post[parent_id]', obj.parent_id); | |
} | |
if(obj.reason){ | |
form.set('reason', obj.reason); | |
} | |
return e621_api.download(url, { | |
method: 'POST', | |
'User-Agent': e621_api.user_agent, | |
body: form | |
}); | |
}; | |
e621_api.$set_parent = async (post, parent) => { | |
return e621_api.post_update({ | |
post_id: post, | |
parent_id: parent | |
}); | |
}; | |
/* A | |
async function set_note(post_id, x, y, width, height, text, is_active){ | |
message(`Setting note of post ${post_id}`); | |
const url = new URL('https://e621.net/note/update.json'); | |
url.searchParams.set('note[post_id]', post_id); | |
url.searchParams.set('note[x]', x); | |
url.searchParams.set('note[y]', y); | |
url.searchParams.set('note[width]', width); | |
url.searchParams.set('note[height]', height); | |
url.searchParams.set('note[is_active]', is_active ? 1 : 0); | |
url.searchParams.set('note[body]', text); | |
return download_url(url.href, 'POST'); | |
} | |
*/ | |
e621_api.post_show = async (id) => { | |
if(!id){ | |
throw new Error('post_id is not defined'); | |
} | |
// eslint-disable-next-line no-undef | |
const url = new URL('https://e621.net/post/show.json'); | |
url.searchParams.set('id', id); | |
const data = await e621_api.download(url.href); | |
// Very nice QOL, worthy of this sin. | |
data.sources = data.sources || []; | |
return data; | |
}; | |
e621_api.$post_edit = async (id, edit) => { | |
const post_data = await e621_api.post_show(id); | |
const changed_data = await edit(post_data); | |
await e621_api.post_update(changed_data); | |
}; | |
/* | |
Good results look like this | |
Official Documentation | |
<id> The ID number of the post to flag for deletion. | |
<inferior_parent> The ID number of the post which is superior to the post | |
being flagged.For duplicates, this should be the ID of the post which is | |
older. Use only when flag_option is set to inferior. | |
<flag_option> Indicates the reason the post should be deleted. Valid values are: | |
uploader Uploader requests deletion | |
inferior Repost/inferior version of existing post | |
1 Artist is on avoid-posting list | |
2 Post is paysite material | |
3 Uncredited trace | |
4 Real-life pornography | |
5 File corrupted | |
6 Image previously deleted | |
[8:20 PM] Kira: The flagging params MUST be in the POST body section. | |
*/ | |
e621_api.post_flag = async (id, option, inferior_parent) => { | |
if(!id && !option){ | |
throw new Error('post_id or flag_option is not defined'); | |
} | |
if(option == 'inferior' && inferior_parent === undefined){ | |
// eslint-disable-next-line max-len | |
throw new Error('if flag_option "inferior", inferior_parent must be supplied'); | |
} | |
const url = 'https://e621.net/post/flag.json'; | |
const { username, api_key } = await e621_api.credentials(); | |
// eslint-disable-next-line no-undef | |
const form = new FormData(); | |
form.set('login', username); | |
form.set('password_hash', api_key); | |
form.set('id', id); | |
form.set('flag_option', option); | |
if(option == 'inferior'){ | |
form.set('inferior_parent', inferior_parent); | |
} | |
return e621_api.download(url, { | |
method: 'POST', | |
'User-Agent': e621_api.user_agent, | |
body: form | |
}); | |
}; | |
/* | |
Official documentation | |
<id> The ID number of the post to delete. | |
<reason> The reason you are deleting the post. | |
<mode> Set to 1 if you are attempting to permanently destroy this post | |
(will only work if called on an already deleted post). | |
*/ | |
e621_api.post_destroy = async (id, reason, permanent) => { | |
if(!id && !reason){ | |
throw new Error('post_id or del_reason is not defined'); | |
} | |
const url = 'https://e621.net/post/flag.json'; | |
const { username, api_key } = await e621_api.credentials(); | |
// eslint-disable-next-line no-undef | |
const form = new FormData(); | |
form.set('login', username); | |
form.set('password_hash', api_key); | |
form.set('id', id); | |
form.set('reason', reason); | |
if(permanent){ | |
form.set('mode', '1'); | |
} | |
return e621_api.download(url, { | |
method: 'POST', | |
'User-Agent': e621_api.user_agent, | |
body: form | |
}); | |
}; | |
e621_api.$inferior_flag = async (bad_id, better_id) => { | |
return e621_api.post_flag(bad_id, 'inferior', better_id); | |
}; | |
e621_api.$inferior_delete = async (bad_id, better_id) => { | |
const reason = `Inferior version/duplicate of post #${better_id}`; | |
return e621_api.post_destroy(bad_id, reason); | |
}; | |
e621_api.comment_create = async (post_id, text) => { | |
const url = 'https://e621.net/comment/create.json'; | |
const { username, api_key } = await e621_api.credentials(); | |
// eslint-disable-next-line no-undef | |
const form = new FormData(); | |
form.set('login', username); | |
form.set('password_hash', api_key); | |
form.set('comment[post_id]', post_id); | |
form.set('comment[body]', text); | |
return e621_api.download(url, { | |
method: 'POST', | |
'User-Agent': e621_api.user_agent, | |
body: form | |
}); | |
}; | |
// This function should not be called, it is ""private"" | |
// eslint-disable-next-line no-underscore-dangle | |
e621_api._note_update_create = async (obj) => { | |
// All of these must be defined for a note to be created | |
['post_id', 'x', 'y', 'width', 'height', 'text'].forEach(e => { | |
if(obj[e] == undefined){ | |
throw new Error(`${e} is not defined`); | |
} | |
}); | |
const url = 'https://e621.net/note/update.json'; | |
const { username, api_key } = await e621_api.credentials(); | |
// eslint-disable-next-line no-undef | |
const form = new FormData(); | |
form.set('login', username); | |
form.set('password_hash', api_key); | |
if(obj.note_id){ | |
form.set('id', obj.note_id); | |
} | |
form.set('note[post_id] ', obj.post_id); | |
form.set('note[x] ', obj.x); | |
form.set('note[y]', obj.y); | |
form.set('note[width] ', obj.width); | |
form.set('note[height]', obj.height); | |
form.set('note[body]', obj.text); | |
if(obj.visible !== undefined){ | |
const is_visible = obj.visible ? 1 : 0; | |
form.set('note[is_active]', is_visible); | |
} | |
return e621_api.download(url, { | |
method: 'POST', | |
'User-Agent': e621_api.user_agent, | |
body: form | |
}); | |
}; | |
e621_api.note_create = async (obj) => { | |
if(obj.note_id){ | |
throw new Error('When creating notes, a note_id is invalid'); | |
} | |
// eslint-disable-next-line no-underscore-dangle | |
return e621_api._note_update_create(obj); | |
}; | |
e621_api.note_update = async (obj) => { | |
if(!obj.note_id){ | |
throw new Error('note_id is not defined'); | |
} | |
// eslint-disable-next-line no-underscore-dangle | |
return e621_api._note_update_create(obj); | |
}; | |
e621_api.note_list = async (post_id) => { | |
// eslint-disable-next-line no-undef | |
const url = new URL('https://e621.net/note/index.json'); | |
url.searchParams.set('post_id', post_id); | |
return e621_api.download(url.href, { | |
method: 'POST', | |
'User-Agent': e621_api.user_agent | |
}); | |
}; | |
GM.setValue('username', 'lmaolikeiwouldcommitthis'); | |
GM.setValue('api_key', 'lmaolikeiwouldcommitthis'); | |
function merge_listener(){ | |
const id1 = document.getElementById('post_1').value; | |
const id2 = document.getElementById('post_2').value; | |
merge_posts(id1, id2); | |
} | |
document.getElementById('stats').innerHTML += ` | |
<input id="post_1" placeholder="lower"></input> | |
<input id="post_2" placeholder="bigger"></input> | |
<button id="clicklytrhth">Merge</button>` | |
document.getElementById('clicklytrhth').addEventListener('dblclick', merge_listener); | |
async function merge_posts(id1, id2){ | |
console.log('getting1') | |
const d1 = await e621_api.post_show(id1); | |
console.log(d1) | |
console.log('getting2') | |
const d2 = await e621_api.post_show(id2); | |
console.log(d2); | |
console.log('got') | |
const combined_tags = `${d1.tags} ${d2.tags}`; | |
const combined_sources = d1.sources.concat(d2.sources).join('\n'); | |
const combined_description = d1.description || d2.description ? `${d1.description}\n${d2.description}` : ''; | |
const new_d1 = { | |
post_id: d1.id, | |
tags: combined_tags, | |
old_tags: d1.tags, | |
source: combined_sources, | |
description: combined_description, | |
reason: 'same image' | |
}; | |
const new_d2 = { | |
post_id: d2.id, | |
tags: combined_tags, | |
old_tags: d2.tags, | |
source: combined_sources, | |
description: combined_description, | |
parent_id: d1.id, | |
reason: 'same image' | |
}; | |
console.log(new_d1); | |
console.log(new_d2); | |
if(combined_sources.split('\n').length > 10 || d2.parent_id){ | |
console.log('Could not continue'); | |
console.log(combined_sources.length > 10, d2.parent_id); | |
return; | |
} | |
const a = await e621_api.post_update(new_d1); | |
console.log(a); | |
const b = await e621_api.post_update(new_d2); | |
console.log(b); | |
const c = await e621_api.$inferior_flag(d2.id, d1.id); | |
console.log(c); | |
console.log(new_d1, new_d2); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment