pull down to refresh

One thing that's great about Stacker.news is that with some digging, we can actually SEE what is being hidden from us.

And right now, most of what's being hidden (what somebody is spending by far the most sats to hide, with downzaps) is anything that's critical of Israel and the Iran War.

A while back I created a browser extension called Stacker News Downzap Spotlight. Previously it just made it easier and more "top of mind" to see downvotes, by creating a Downzaps link right on the top menu.

Below is what my Stacker.news front page looks like (see instructions below on how to set up the extension yourself).

Having this, and looking at downzaps much more often (as my anti Iran War posts get downzapped to oblivion), had made me see some patterns.

  • I had almost zero downvotes before I started posting about the Iran War, Israel, and the Charlie Kirk assassination and potential Israeli involvement. A few scattered "-21" downvotes, probably when people thought I was being too much of a carnivore booster, but basically almost nothing. And nothing that seemed designed to push something off the top of the feed.
  • Since I started posting more on the Iran war and Israel, a lot of my posts - any of them that got traction - have been downzapped to oblivion. In other words, downzapped so far that they're not on the front few pages
  • In the past couple days another pattern has emerged. And that is - high amount "Red Herring" downzaps. Downzaps that are meant to obscure what the main pattern is (which is - anything that's anti Iran War or anti Zionist is downzapped). For instance, check out the top downzaps for the week (https://stacker.news/top/posts/week?by=downsats)

Notice - 5 of the top 7 downzapped posts are in some way anti Iran war, or about the downzapping issue. The 2 that are not?

I think 1 person did those downzaps. And I think they did it just to obscure the main pattern (anti war, anti censorship posts are being downzapped). Because seriously - what bitcoiner could be against this one - JoinMarket Maker DoS Attack: What's Happening and How to Fix It, for exactly 5000 sats?

They did it so that when you look at top downzaps for the week, it's not quite as blindingly obvious that most of the downzapping is to support the Zionist cause and the Iran war.

Installing Stacker News Downzap SpotlightInstalling Stacker News Downzap Spotlight

Here's the browser extension I wrote with the help of AI. It adds a downzap link on the front page, and shows the amount downzapped, in red. It should be easy to install, ask if you have issues. I had a few problems pasting in code so that it formatted correctly, h

Works on Chrome, Brave, Edge, Opera, and Arc.

Install

  1. Create a folder somewhere permanent. Call it StackerNewsDownzapSpotlight. If you delete the folder later, the extension stops working.
  2. Save the two files below (manifest.json and content.js) into that folder.
  3. Open chrome://extensions (or brave://extensions, edge://extensions, etc).
  4. Turn on the Developer mode toggle in the top-right.
  5. Click Load unpacked and select the folder.
  6. Open https://stacker.news — you should see the Downzaps link at the top, and red downzap badges next to post titles.

If your browser shows a yellow banner about disabling developer-mode extensions, ignore it. That's normal for anything not installed from the Chrome Web Store.

File 1: manifest.json (describes the extension)

{ "manifest_version": 3,

"name": "Stacker News Downzap Spotlight", "version": "1.0",

"description": "Adds a Downzaps tab to Stacker News and shows each post's downzap total next to the title.",

"content_scripts": [

{

"matches": ["*://stacker.news/*"],

"js": ["content.js"]

}

]

}

File 2: content.js (this file actually does the work)

// This function creates, places, and styles the button

function injectButton() {

// Check if we are currently on the 'downzaps' page

const isDownzapsPage = window.location.href.includes('by=downsats');

const allLinks = document.querySelectorAll('a');

let topLink = null;

// Loop through to find the 'top' link in the navigation bar

for (let target of allLinks) {

if (target.textContent.trim().toLowerCase() === 'top' && target.getAttribute('href') && target.getAttribute('href').includes('/top/')) {

topLink = target;

break;

}

}

// If we can't find the 'top' link, stop here

if (!topLink) return;

// Check if our custom button already exists

let newLink = document.getElementById('custom-downzap-btn');

// If it doesn't exist yet, we create and insert it

if (!newLink) {

let nodeToClone = topLink;

let insertTarget = topLink;

// Check if the "top" link is inside a layout wrapper (like an <li>)

const parent = topLink.parentElement;

if (parent && (parent.tagName === 'LI' || parent.children.length === 1)) {

nodeToClone = parent;

insertTarget = parent;

}

// Clone the element

const newContainer = nodeToClone.cloneNode(true);

// Find the actual <a> tag inside whatever we cloned

newLink = newContainer.tagName === 'A' ? newContainer : newContainer.querySelector('a');

if (newLink) {

newLink.id = 'custom-downzap-btn';

newLink.href = '/top/posts/day?by=downsats';

newLink.textContent = 'downzaps';

// Insert it into the page exactly after the 'top' link's container

insertTarget.parentNode.insertBefore(newContainer, insertTarget.nextSibling);

// Give it a little breathing room so it doesn't touch the 'top' link

newContainer.style.marginLeft = '12px';

}

}

// --- Styling / Bold Logic ---

if (newLink) {

if (isDownzapsPage) {

newLink.classList.add('active');

newLink.style.fontWeight = 'bold';

if (newLink.parentElement) newLink.parentElement.classList.add('active');

topLink.classList.remove('active');

topLink.style.fontWeight = 'normal';

if (topLink.parentElement) topLink.parentElement.classList.remove('active');

} else {

newLink.classList.remove('active');

newLink.style.fontWeight = 'normal';

if (newLink.parentElement && newLink.parentElement !== newLink) {

newLink.parentElement.classList.remove('active');

}

}

}

}

// Downsats-next-to-title decoration. Listings don't show downsats anywhere,

// so we fetch each visible item's downSats via the site's own GraphQL

// endpoint and stamp the value next to the title.

const downSatsCache = new Map();

const pendingIds = new Set();

let fetchDebounce = null;

let decorateDebounce = null;

function collectItemTitleAnchors() {

const out = [];

const anchors = document.querySelectorAll('a[class*="item_title"]');

for (const a of anchors) {

const href = a.getAttribute('href') || '';

const m = href.match(/^\/items\/(\d+)(?:[/?#].*)?$/);

if (!m) continue;

out.push({ anchor: a, id: m[1] });

}

return out;

}

function scheduleFetch(ids) {

for (const id of ids) pendingIds.add(id);

clearTimeout(fetchDebounce);

fetchDebounce = setTimeout(runFetch, 200);

}

async function runFetch() {

const ids = [...pendingIds];

pendingIds.clear();

if (!ids.length) return;

const aliases = ids

.map((id, i) => `i${i}: item(id: "${id}") { id downSats }`)

.join('\n');

const query = `query { ${aliases} }`;

try {

const res = await fetch('/api/graphql', {

method: 'POST',

headers: { 'Content-Type': 'application/json' },

credentials: 'include',

body: JSON.stringify({ query })

});

const json = await res.json();

if (json && json.data) {

for (const key of Object.keys(json.data)) {

const item = json.data[key];

if (item && item.id != null) {

downSatsCache.set(String(item.id), Number(item.downSats) || 0);

}

}

}

} catch (e) {

// Next mutation tick will retry.

}

decorateDownSats();

}

function decorateDownSats() {

const targets = collectItemTitleAnchors();

const toFetch = [];

for (const { anchor, id } of targets) {

if (anchor.dataset.downsatsDecorated === '1') continue;

if (!downSatsCache.has(id)) {

if (!pendingIds.has(id)) toFetch.push(id);

continue;

}

const value = downSatsCache.get(id);

anchor.dataset.downsatsDecorated = '1';

if (value > 0) {

const badge = document.createElement('span');

badge.className = 'custom-downsats-badge';

badge.textContent = `${value} sats \u2193`;

badge.style.cssText = [

'display: inline-block',

'margin-right: 8px',

'padding: 1px 7px',

'background: #c0392b',

'color: #fff',

'border-radius: 3px',

'font-size: 0.85em',

'font-weight: 600',

'white-space: nowrap',

'vertical-align: baseline'

].join('; ');

anchor.insertAdjacentElement('beforebegin', badge);

}

}

if (toFetch.length) scheduleFetch(toFetch);

}

const observer = new MutationObserver(() => {

injectButton();

clearTimeout(decorateDebounce);

decorateDebounce = setTimeout(decorateDownSats, 150);

});

observer.observe(document.body, { childList: true, subtree: true });

No need for that "extension".
LOL people have no idea how to use SN...

and all this US-Iran war is just a giant psyop. IGNORE

reply

This setting does not do what I want. My extension makes it easier to highlight downzapped posts, and also see (without a bunch of clicks for each item) exactly how much something has been downzapped.

reply

IRRELEVANT

reply

I was surprised to see that someone downzapped the join market post. It was good. I even reposted it on X and nostr yesterday.

I also reposted about Kudzai's post about internet censorship on X and nostr.

So perhaps that has something to do with why those two posts attracted downzapping attention? I have had posts of mine about Internet censorship get downzapped before.

I would also suggest that it's possible that these Iran/Israel posts wouldn't get downzapped as much if they weren't being boosted onto the front page. I realize not all of them were boosted, but some of what we are seeing is people using zaps to negotiate how visible a post should be. Rather than being a conspiracy, isn't it just the market figuring this out?

reply

What's cool is that they are forced to burn real money to censor you. Such a POW-powered shield is not present in other social media.

reply

True, it's good that you can see the censorship.

But I've had a good number of posts that gradually got to the top by people upzapping more normal amounts. And then somebody - I'll call them "the censor" downzaps them so that they've invisible to 95% of people.

I don't know what the answer is, but for now, I'm trying to help people understand what's happening.

reply

better go fishing...

reply

Lobbying existing on SN - it's getting funny

reply

Oddly enough, the down zap mechanics are a really good incentive to make a post that will get heavily down zapped.

reply

Really? I haven't looked into that.

Could you explain?

reply

It's a form if engagement that pushes more sats into the rewards pool.

If you have a day that you know you're going to be highly active, your best financial reward comes from getting as many sats pushed into the pool as possible, while simultaneously doing things that will give you rewards.

reply

Get ready for some good amount of downzapps for this post. 😂

reply

not worth wasting valuable sats for a meaningless post

reply
23 sats \ 0 replies \ @Taft 18 Apr

Yes, but have you seen what’s been going on lately and how heavily people are downzapping posts these days?

reply

I didn't check those other posts, but anything shitcoin-y or made by AI has always been likely to get downzapped into oblivion.

reply

True, that's always been around. But this is something new, in terms of the scale of what's happening.

reply

Partially, I think this saga has put downzaps more top of mind for people, which might be making them more likely to downzap low quality stuff.

reply

The beauty of FOSS

reply

Interestingly, doing the extension doesn't require you to see the back end code. AI asked me quite a few times to do an "inspect" of the front end, and was able to work from that.

reply