About Posting to HIVE - STEEM & Blurt - Tampermonkey Script to Show Chain Activities

in #hive2 months ago

Hey everyone,

Today I came across a chat discussion that made me stop and think for a moment. The topic was about whether users who actively post their unique content on Hive should also publish the same content on other platforms. Some people were asking if this is allowed, if it should be done, and if it is even fair.

I want to share my personal opinion about this before I go into the details of the software I am currently building with ChatGPT.

For me, if a user decides to publish on Hive and becomes part of the reward pool, then the content should only be posted here. There are several reasons for this, but I want to focus on two main points.

The first point is about search engines. Content that is published on Hive is indexed and processed by Google and others. If the exact same text is also published on Steemit, Blurt, or any other platform, it creates duplicate content. This is not only bad for SEO in general, it can also hurt the reputation of articles published on Hive. Hive deserves to stand strong in search results and duplicate content works against that.

The second point is about mindset. I personally see it as a kind of greediness when someone tries to collect rewards from multiple platforms with the same article. Hive has its own reward system, its own pool, and its own community. By posting the same thing everywhere, the value of that content feels reduced. It makes me question if the person really believes in Hive or just wants to squeeze as much as possible out of every chain.

Of course, I know that people have different views and some will disagree with me. But my position is clear: if you want to be part of Hive, then publish on Hive. Keep your content unique here and give Hive the full benefit of your work.

image.png


A Solution?

Back when the discussion about the K-E Ratio was started by @azircon, I built a small extension for myself.
https://peakd.com/development/@louis88/the-ke-ratio-on-peakd-with-tampermonkey-just-a-prototype

The idea was simple. It injected the K-E Ratio right next to the username on the PeakD frontend. In the background, the extension sent a query, processed the result, and displayed it directly in the feed.

That experience gave me the foundation for another idea. With the help of ChatGPT, I created something similar, but this time it is focused on cross-chain activity. The logic works in a very straightforward way. Every user that appears in a feed — for example in Trending, in the Following feed, in comments, or in Recent Posts — triggers a background request. The extension then asks the Steemit and Blurt APIs whether that account has shown any activity in the last 30 days.

The result is then displayed right inside the Hive frontend. Next to the username you can immediately see if that person has been active or inactive on Steem or Blurt during the past month.

Right now, the script is set to look back exactly 30 days in the API. That way it is very clear at a glance whether a user is only active on Hive, or if they are still also posting or voting somewhere else.

I have taken some screenshots so you can see how this looks in different feeds. Whether it is Trending, Following, Comments, or Recent Posts, the status appears the same way: a clean little indicator next to the username showing “Active” or “Not Active” for Steem and Blurt.

image.png
Hidden to not blame

image.png

image.png

image.png

Showing to demonstrate greediness

What I like is that I have discovered little to no activity on external blockchains in the top trending posts. Occasionally, there was a user who at least votes on Steemit, but they probably just forgot to turn off their auto-votes and are not personally active there.

image.png

So let me sum it up once again. I believe that every content creator should make a decision for one blockchain. I do not want to see our Hive reward pool being shared with content that is also published somewhere else or going to people who are still supporting legacy chains.

  • Are you a Hiver? Then blog on Hive.
  • Are you a Steemian? Then stay on Steemit.
  • Are you a Blurter? Then publish on Blurt.

But please, do not dance at all weddings at the same time.

For my part, I will now keep my script running for a while. With it I can check activity on other blockchains and remove rewards from Hive users where I feel it makes sense. I will also clean up my own following feed. This means I will unfollow people who I actually like, but whose double or triple posting habits I cannot support. From me they will get a de-follow.

To be clear, this is not about starting a witch hunt or pointing fingers. It is about transparency. I want to make the current situation visible and give people a clearer picture. If you see the numbers and the activity right next to the usernames, it becomes very obvious who is loyal to Hive and who is trying to get rewards from everywhere.

In the end, everyone can decide for themselves. I simply share my view: if you want to be part of Hive, then publish here and stand behind it.

And finally, feel free to use the script I shared. Try it out, give me your feedback, and let me know your thoughts in the comments. I am very curious to hear how the community feels about this topic.

Thanks for reading.

The actual Code by ChatGPT for adding it to Tampermonkey (Chrome Extension)

// ==UserScript==
// @name         PeakD Cross-Activity (Steemit/Blurt) — Pretty Tooltip
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Shows if a user was active on Steemit/Blurt in the last N days, with a custom hover tooltip. More robust RPC payloads & Blurt nodes.
// @author       louis88
// @match        https://peakd.com/*
// @grant        GM_xmlhttpRequest
// @connect      api.steemit.com
// @connect      api.justyy.com
// @connect      rpc.blurt.world
// @connect      api.blurt.blog
// @connect      blurt-rpc.saboin.com
// @connect      blurtrpc.actifit.io
// @run-at       document-end
// ==/UserScript==

(function () {
  'use strict';

  // ---------- Config ----------
  const LOOKBACK_DAYS = 30;

  const STEEM_NODES = [
    'https://api.steemit.com',
    'https://api.justyy.com'
  ];

  // refreshed Blurt nodes (you can reorder)
  const BLURT_NODES = [
    'https://rpc.blurt.world',
    'https://api.blurt.blog',
    'https://blurt-rpc.saboin.com',
    'https://blurtrpc.actifit.io'
  ];

  // ---------- Helpers ----------
  const cache = new Map();
  const inflight = new Map();

  const usernameFromHref = href => (href.match(/\/@([\w\-.]+)/) || [])[1] || null;
  const toDateOrNull = s => (!s || s.startsWith('1970-01-01')) ? null : new Date(s.endsWith('Z') ? s : s + 'Z');
  const maxDate = arr => arr.filter(Boolean).sort((a,b)=>b-a)[0] || null;
  const daysAgo = d => Math.floor((Date.now()-d.getTime())/(1000*60*60*24));
  const within = d => d && (Date.now()-d.getTime()) <= LOOKBACK_DAYS*86400000;

  // Try multiple payload styles per node to handle different RPC implementations
  function rpcMulti(node, method, params){
    const payloads = [
      // JSON-RPC 2.0 (common on Steem/Blurt)
      { jsonrpc:'2.0', id:1, method, params },
      // Legacy Appbase style: call("condenser_api","get_accounts", [[username]])
      { jsonrpc:'2.0', id:2, method:'call', params:['condenser_api', 'get_accounts', params] }
    ];

    // Send one payload; resolve if any works
    return new Promise((resolve, reject) => {
      let idx = 0;
      const tryNext = () => {
        if (idx >= payloads.length) return reject(new Error('all-payloads-failed'));
        const body = JSON.stringify(payloads[idx++]);
        GM_xmlhttpRequest({
          method: 'POST',
          url: node,
          headers: { 'Content-Type': 'application/json' },
          data: body,
          timeout: 12000,
          onload: (res) => {
            try {
              const json = JSON.parse(res.responseText);
              if (json.error) return tryNext(); // try next payload
              resolve(json.result);
            } catch (e) {
              tryNext();
            }
          },
          onerror: () => tryNext(),
          ontimeout: () => tryNext()
        });
      };
      tryNext();
    });
  }

  async function getAccount(nodes, username){
    let lastErr;
    for (const n of nodes){
      try{
        const res = await rpcMulti(n, 'condenser_api.get_accounts', [[username]]);
        if (Array.isArray(res) && res[0]) return {account: res[0], node: n};
      }catch(e){ lastErr = e; }
    }
    throw lastErr || new Error('All nodes failed');
  }

  async function check(chain, nodes, username){
    const key = `${chain}:${username}`;
    if (cache.has(key)) return cache.get(key);
    if (inflight.has(key)) return inflight.get(key);

    const p = (async ()=>{
      try{
        const {account, node} = await getAccount(nodes, username);
        const lastVote = toDateOrNull(account.last_vote_time);
        const lastPost = toDateOrNull(account.last_post);
        const lastRoot = toDateOrNull(account.last_root_post);
        const last = maxDate([lastVote,lastPost,lastRoot]);
        const active = within(last);
        const sourceField =
          last && lastVote && +last===+lastVote ? 'last_vote_time' :
          last && lastPost && +last===+lastPost ? 'last_post' :
          last && lastRoot && +last===+lastRoot ? 'last_root_post' : null;
        const out = {active,last,sourceField,node,error:false};
        cache.set(key,out);
        return out;
      }catch(e){
        const out = {active:false,last:null,sourceField:null,node:null,error:true};
        cache.set(key,out);
        return out;
      }finally{
        inflight.delete(key);
      }
    })();

    inflight.set(key,p);
    return p;
  }

  // ---------- Pretty tooltip ----------
  const tip = document.createElement('div');
  Object.assign(tip.style, {
    position:'fixed', zIndex: 99999,
    maxWidth:'420px', padding:'10px 12px', borderRadius:'10px',
    background:'rgba(20,22,25,0.96)', color:'#e8e8e8',
    fontSize:'12px', lineHeight:'1.35',
    boxShadow:'0 8px 24px rgba(0,0,0,0.35)',
    border:'1px solid rgba(255,255,255,0.08)',
    pointerEvents:'none', display:'none', whiteSpace:'pre-line'
  });
  document.body.appendChild(tip);
  const showTip = (html, x, y) => {
    tip.innerHTML = html;
    tip.style.left = Math.min(x+12, window.innerWidth-440)+'px';
    tip.style.top = Math.min(y+12, window.innerHeight-20)+'px';
    tip.style.display = 'block';
  };
  const hideTip = ()=>{ tip.style.display='none'; };

  // ---------- UI ----------
  function makeBadge(label, active, brand){
    const el = document.createElement('span');
    el.textContent = `${label}: ${active ? 'ACTIVE' : 'NO'}`;
    Object.assign(el.style, {
      display:'inline-flex', alignItems:'center',
      padding:'3px 8px', fontSize:'90%', fontWeight:'600', color:'#fff',
      borderRadius:'999px', marginLeft:'6px', lineHeight:'1.3', letterSpacing:'.2px',
      background: active
        ? (brand==='STEEM' ? 'linear-gradient(135deg,#4CAF50,#2E7D32)'
           : 'linear-gradient(135deg,#3f7fbf,#274b7c)')
        : 'linear-gradient(135deg,#666,#444)',
      transition:'filter .2s'
    });
    el.addEventListener('mouseenter', ()=> el.style.filter='brightness(1.15)');
    el.addEventListener('mouseleave', ()=> el.style.filter='none');
    return el;
  }

  function ensureContainer(repRoot){
    let box = repRoot.parentElement.querySelector('.xchain-activity-badges');
    if (!box){
      box = document.createElement('span');
      box.className = 'xchain-activity-badges';
      box.style.display = 'inline-flex';
      box.style.alignItems = 'center';
      box.style.marginLeft = '6px';
      repRoot.parentElement.appendChild(box);
    }
    return box;
  }

  // ---------- Per-link processing ----------
  async function processLink(link){
    const username = usernameFromHref(link.getAttribute('href'));
    if (!username) return;

    const rep = link.parentElement?.querySelector('.reputation-label');
    if (!rep) return;

    // avoid double injection on the same rep area
    if (rep.parentElement.querySelector('.xchain-activity-badges')) return;

    const box = ensureContainer(rep);

    const ph = document.createElement('span');
    ph.textContent = 'checking…';
    ph.style.opacity = '0.6';
    ph.style.fontSize = '90%';
    ph.style.marginLeft = '6px';
    box.appendChild(ph);

    const [steem, blurt] = await Promise.all([
      check('STEEM', STEEM_NODES, username),
      check('BLURT', BLURT_NODES, username)
    ]);

    ph.remove();

    const bSteem = makeBadge('STEEM', !!steem.active, 'STEEM');
    bSteem.addEventListener('mouseenter', (ev)=>{
      const html = steem.error
        ? `<b>STEEM</b>\nFetch failed.`
        : (steem.last
            ? `<b>STEEM</b>\nLast: ${steem.last.toISOString().slice(0,19).replace('T',' ')} UTC\nRelative: ${daysAgo(steem.last)} days ago\nField: ${steem.sourceField}\nNode: ${steem.node}`
            : `<b>STEEM</b>\nNo activity found.`);
      showTip(html, ev.clientX, ev.clientY);
    });
    bSteem.addEventListener('mousemove', ev => showTip(tip.innerHTML, ev.clientX, ev.clientY));
    bSteem.addEventListener('mouseleave', hideTip);

    const bBlurt = makeBadge('BLURT', !!blurt.active, 'BLURT');
    bBlurt.addEventListener('mouseenter', (ev)=>{
      const html = blurt.error
        ? `<b>BLURT</b>\nFetch failed.`
        : (blurt.last
            ? `<b>BLURT</b>\nLast: ${blurt.last.toISOString().slice(0,19).replace('T',' ')} UTC\nRelative: ${daysAgo(blurt.last)} days ago\nField: ${blurt.sourceField}\nNode: ${blurt.node}`
            : `<b>BLURT</b>\nNo activity found.`);
      showTip(html, ev.clientX, ev.clientY);
    });
    bBlurt.addEventListener('mousemove', ev => showTip(tip.innerHTML, ev.clientX, ev.clientY));
    bBlurt.addEventListener('mouseleave', hideTip);

    box.appendChild(bSteem);
    box.appendChild(bBlurt);
  }

  function processUsers(){
    document.querySelectorAll('a[href^="/@"]').forEach(processLink);
  }

  let raf = null;
  const observer = new MutationObserver(()=> {
    if (raf) return;
    raf = requestAnimationFrame(()=> { raf = null; processUsers(); });
  });

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

I'm unlikely to support people who post on those other chains. If they like Hive then they will probably reach a bigger audience here. Steem needs to just die as it's corrupted and Blurt is irrelevant.

Loading...

I almost cried reading your post bot because of the topic, but about the idea!
The idea and how you solved with the algorithm and the final product !!! Well we are lucky to have you in the backend of Hive!!
Well i know that some people wont like much about this product at least what it delivers but I don’t think it is unfair… people who are curating that need to decide by their own what to curate and the more data that they get such as ke or crossposting ia good. The more data that we can access is better, it is always better and it matches with Hive ad to be a transparent blockchain! Well done!

I was cross posting my stuff at the very beginning of the fork because I wanted to stick it to Steemit and move as much of my rewards from there over to HIVE as I could. I quit after a while as I realized it was setting me up for downvotes. It was a good way to buy more HIVE for a time though.

Works great, thx @louis88!

image.png

@louis88, this is actually disturbing. Didn't think I would see so many people posting on Blurt and Steem....INSANE!!!

Geniale Idee.
Benutze ich jetzt auch!
Ich sehe es wie Du, man sollte nicht auf 2 oder 3 Chain gleichzeitig posten!

Love the idea, except the fact that the script will promote Steem and Blurt a lot. As people say, there’s no such thing as bad publicity. Maybe, it's better to use an abbreviation? Like S/B: Y/N or S/B: +/+, etc. Or some signs (not logos)?

Loading...

Not the right mindset to grow Hive, pointing fingers at accounts is not a great idea.

hm... but posting thesame content on the other networks and get rewarded is fine?

Who cares if they earn a few cents at Steem or Blurt.
People will get upset with the finger pointing and leave, not good publicity for this chain.

Let's talk about middle ground. Take my example, I don't like people sitting on the fence (atleast for steemit) so I don't upvote them. Downvoting them is a different story for me. So no upvote is also war for you? Or you can conclude it as MY STAKE MY CHOICE? If yes, then downvoters also exercise their downvoting rights. To them it's just MY STAKE MY CHOICE matter.

I hope you can understand my pov too.

If you think something is not worth an upvote, then don't upvote it. Seems totally fine to me. If you downvote someone solely because they post somewhere else, then you actively discourage them to post here. At least that's how I see it.

In regards to "my stake, my choice" - absolutely. We want to have and keep our choice of what to do with our stake. I guess the question is more about how we decide to exercise our choice that would be more beneficial.

There really is no justifiable reason to support steem or blurt or even post on them really.

Steem is centralized and has no developemt and is like earning a wage but your boss keeps your wallet and can do whatever the hell he likes with it. i.e centralized and easy to manipulate the chain.

Blurt removed downvoting and the DHF iirc and also completely ditched hivemind settling for hiveD handling all api requests.. which will never scale, not that they'll ever grow to a size that needs decent hardware and it's also completely worthless.

Hives the only thing that's been progressing. Sure it's got many problems that are not really blockchain related but societal and educational issues, as well as many frontend issues and user experience issues but at least we are moving forward.

Acrivity ist not moving forward, i wrote a post about it yesterday.

The script is a good idea, but for SEO purposes crossposting on Steem and Blurt doesn't matter. There are so many Hive front-ends which feature the same content while search engines see them as separate sites. Being centralized without independent apps makes Steem look better in this respect, unfortunately.

After the fork, I posted nonsense there for a while, but now I just ignore it.

There are so many Hive front-ends which feature the same content while search engines see them as separate sites

As long as we don't generate traffic on old content from within our existing userbase, it's very hard to rank on search engines. For them we r a content graveyard or like social media where most content dies after few days.

That's a great idea. I'm afraid to try adding the code myself and breaking something. Hopefully PeakD will incorporate this the same way they did with the tool you made for displaying KE ratio next to user names. I've found that incredibly helpful to clean up my follower feed and votes from people with an extremely high KE ratio.

It's easy and you are able to toggle on/off the script.

image.png

Maybe I will give it a shot. Do I do it from developer tools? I guess I shoudld ask Chat GPT for a tutorial.

Thank you!

Why don't you make it a script for us all ?
Idc abt steem but most of blurt poster are annoying about why blurt is so good and hive so bad : )

PIZZA!

$PIZZA slices delivered:
@mein-senf-dazu(1/5) tipped @louis88

Come get MOONed!

this is a declaration of war, but some would argue, quite merited!

hahahah

this might be the reason why i saw some people rage quitting this morning... i think i can make sense of it now.

Rage quit is good news.

I have a theory: those who rage quit are extractors

Don't worry about them. Dead weight.

its actually quite possibly correct 90% of the time at least

While I don't care about centralized crap, I do care about Hive and I want to see it thrive. And if I understood your attitude correctly, it seems to me not the most beneficial to this goal which I'm sure we share.

Disagreement on rewards, and hence downvoting, seems essential. But if I understood you, you are actively endeavoring to chase people away from here because you have a theory that they are xyz.

How do we find out if your theory holds true? Or rather, to what extent it holds true? I myself am quite sure that it holds true some of the time. But 100% of the time, or even 70% of the time? Don't know.

Maybe there is a more flexible approach that doesn't chase people away but also doesn't allow pure extraction to happen. Simply prevent extraction and let people know that you want to make this a place where genuine effort and integration with the community values is rewarded. Maybe this greater flexibility will even attract the kind of people you want to see, or even encourage some people change their behaviour when they get a better understanding of what is valued.

Curious about HivePakistan? Join us on Discord!

Delegate your HP to the Hivepakistan account and earn 90% of curation rewards in liquid hive!

50 HP
100 HP
200 HP
500 HP (Supporter Badge)
1000 HP

Follow our Curation Trail and don't miss voting!

Additional Perks: Delegate To @ pakx For Earning $PAKX Investment Token


Curated by dlmmqb

This will make more people leave Hive 😂😂😂😂😂😂

How do we address the issue that different front ends index the same article - so if I google one of mine - I get the peakd link, the ecency link, and also, amusingly, some front ends I've never heard of.

Which one is the canonical in that context?

On Hive.Blog -> Canonical PeakD (where it posted originally)
image.png

On Ecency.com -> Canonical PeakD (where it posted originally)
image.png

On Inleo.io -> Non Canonical Set -> LOL! -.-"

Oh sweet. I didn't know the front ends did that! :)

@louis88

Erweiter das doch mal, das man auch sieht, wenn der User Burnposts Votet und damit den Autoren Rewards klaut, aber sich selber die Currationrewards einstreicht

Das finde ich ne Million mal schlimmer

I post everywhere.

Even Instagram, X.com and YouTube.

I am an artist and a Filmmaker … this is what we do

Yes, I post on Blurt.blog and Steemit.com and earned over $ 100,000 so far .. it adds up.

Pretty good if you need a little $$$ to buy food and stuff .

Luckily I sold all my Hive for Bitcoin …

If I see someone posting on blurt and steem, automatic DV from me :)

Problem is there are numerous people who still post on steem. I can’t tell them what to do and what not to do, but I can remove their reward from hive.

Your model is good. If you want Steem or blurt post there. No problem. Just hotpot here.

I have a question, in rarest case, what if someone creates account on someone else name?

Those who joined after fork. I also don't like people sitting on the fence but I will give benefit of doubt IN RAREST case. Or I should join steemit and blurt to make usernames unavailable?

You have a valid point there bro. My username is a popular one, I wonder why none had taken it yet in past. One day someone might enter S or B and use the same username. Whether for good or bad, people might still think that's me.

Coincidence does happen sometime. I will try securing it later

Why would anyone create an account in someone’s else’s name?

That alone is a red flag!

To steal someone's content without their knowledge. We have tools to auto copy and paste same on other chains. All they need is dlmmqb on steemit and blurt and an api to steal.....

But even in those cases someone might think it's me there.

I used me as an example to say, maybe we shouldn't rely bindly on this tool and remember this exploit someone can do. Downvoting is fine as it's same rights as rights to upvote. But giving some a benefit of doubt to manually check before casting a full on nuke is what I am requesting.

Ofcourse it's for either high rep accounts or good content creators case only.

If someone claims username azircon in blurt, do you think people will believe that I am posting in blurt? Lol

Steem is covered. There is an azircon in Steem, the original account. No one can ever post on that account :) so that is simple

You will also have a Blurt account that belongs to you (don't you feel blessed?.. 😀). This could only happen to accounts created post divorce date as Blurt was a fork of Steem (March 2020).