const API_VER = 2.3;
const API_BASE = "https://api.stackexchange.com";
const sleep = (sec = 1) => new Promise((r) => setTimeout(r, sec * 1e3));
const uniquify = (arr) => [...new Set(arr)];
const getQuestions = async(postIds, {
site = "stackoverflow",
page = 1,
...rest
}) => {
const url = new URL(`${API_BASE}/${API_VER}/questions/${postIds.join(";")}`);
const params = new URLSearchParams({
site,
page: page.toFixed(0),
...rest,
});
url.search = params.toString();
const res = await fetch(url.toString());
if (!res.ok)
return [];
const {
items = [], has_more = false, backoff,
} = (await res.json());
if (backoff) {
await sleep(backoff);
return getQuestions(postIds, {
site,
page,
...rest
});
}
if (!has_more)
return items;
const more = await getQuestions(postIds, {
page: page + 1,
site,
...rest,
});
return [...items, ...more];
};
const getReputation = async(userIds, {
page = 1,
site = "stackoverflow",
...rest
}) => {
const url = new URL(`${API_BASE}/${API_VER}/users/${userIds.join(";")}/reputation`);
const params = new URLSearchParams({
site,
page: page.toFixed(0),
...rest,
});
url.search = params.toString();
const res = await fetch(url.toString());
if (!res.ok)
return [];
const {
items = [], has_more = false, backoff,
} = (await res.json());
if (backoff) {
await sleep(backoff);
return getReputation(userIds, {
site,
page,
...rest
});
}
if (!has_more)
return items;
const more = await getReputation(userIds, {
page: page + 1,
site,
...rest,
});
return [...items, ...more];
};
const partition = (arr, perPart = 1) => {
const parts = Math.ceil(arr.length / perPart);
const partitions = [];
for (let i = 0; i < parts; i++) {
const start = i * perPart;
partitions.push(arr.slice(start, start + perPart));
}
return partitions;
};
(async() => {
const useridInput = document.getElementById("user_id");
const siteInput = document.getElementById("site");
const tagInput = document.getElementById("tag");
const progress = document.getElementById("load");
const submit = document.getElementById("search");
submit.addEventListener("click", async() => {
progress.value = 0;
const tag = tagInput.value;
const uids = [+useridInput.value];
const site = siteInput.value;
const key = "zhhhBNmsqZLZ967tc2dn8w((";
const common = { key, site, pagesize: 100 };
const changes = await getReputation(uids, common);
progress.value = 35;
const postIds = uniquify(changes.map(({
post_id
}) => post_id));
const partitionedIds = partition(postIds, 100);
const results = await Promise.all(partitionedIds.map((i) => getQuestions(i, common)));
const questions = results.flat();
progress.value = 70;
const tagged = questions.filter(({
tags = []
}) => tags.includes(tag));
const taggedIds = tagged.map(({
question_id
}) => question_id);
const relevantChanges = changes.filter(({
post_id
}) => taggedIds.includes(post_id));
const totals = {
up: 0,
down: 0,
tag
};
relevantChanges.forEach(({
reputation_change
}) => {
totals[reputation_change > 0 ? "up" : "down"] += Math.abs(reputation_change);
});
progress.value = 100;
const up = document.getElementById("up");
const down = document.getElementById("down");
up.textContent = totals.up;
down.textContent = totals.down;
});
})();
label {
display: block;
font-weight: 600;
}
caption,
label {
font-weight: 600;
margin: 1vh 0;
}
input {
display: block;
}
button, progress {
margin-top: 2vh;
margin-bottom: 2vh;
}
<form>
<label for="site">Site API slug</label>
<input id="site" type="text" title="API slug" placeholder="stackoverflow" value="stackoverflow" />
<label for="user_id">User Id</label>
<input id="user_id" type="text" title="User Id" placeholder="12345" value="" />
<label for="tag">Tag Name</label>
<input id="tag" type="text" title="Tag Name" placeholder="typescript" value="" />
<button id="search" type="button">Get Changes</button>
</form>
<progress id="load" max="100" value="0"></progress>
<p>Up: <span id="up"></span></p>
<p>Down: <span id="down"></span></p>
score
for a particular tag, or even the sum of thescore
for questions and answers in a particular tag. You gain reputation for upvotes, bounties, and accepts (both questions and answers, but not on accepting your own answers on your own questions). You loose rep for downvotes, but a different amount, so can't just usescore
, and bounties. Reputation gain is also limited to a daily max.