Upgrade Browser Extension for promoted/burnsteem Posts

in Steemit Dev Grouplast month (edited)

Introduction

Some time ago, @remlaps introduced an improved version of its browser extension. This marks posts in colour that either contain a beneficiary to @null or that have been promoted with a transfer to @null.

Especially the second possibility has been in the focus for some time. It was therefore interesting for me to find out how many users have promoted a post and whether it was also promoted by the author himself.
Since @remlaps has released his browser extension for further development, it made sense to upgrade it for this purpose ;-)

Solutions

In order to find out which user has promoted which post, the first step is to retrieve the transfers to @null.

Steemchiller has the right query for this with his SDS. I am currently analysing the last 250 transfers to @null. I think that should be enough for now. Then the data are prepared so that they can be accessed quickly with the post identifier.

It was more difficult to get to the post identifier on the Steemit page. @remlaps loops through all li elements. But the identifier is not contained in a list element. Here I had to loop through to the matching parent element. Obviously there is no method to retrieve all parent elements of an element.

At first I had considered displaying the number directly in the post card on the page. I then decided to display the data in the elements used by @remlaps in the payout pane after all.

I add the following data to the elements with the entry in each post:

  • Number of different users who have promoted the post,
  • "self" if the author himself has promoted the post.

Example

The last row contains the extended data.

Modifications

I have tried to change as few as possible the original code.

  • I had to modify the determination of promoAmount because there are now more characters after „Promotion cost“.
  • I have declared the essential new functions outside the highLight function. Thus, a call in the corresponding else if branch is sufficient for the new functionality.
  • An essential change is the time of the first call of the highLight function. I have inserted this into the then branch of fetch to provide for the asynchronous process. Otherwise, the promotedPosts would still be undefined when called for the first time.
  • In the manifest.json a major change was necessary: I had to enter a permission for the request, so that the script is allowed to load data from another server.
  • Finally… I also raised the version to "0.0.2". :-)

That was it!

Otherwise, I'm with @remlaps: Feel free to modify the extension. I would be happy to receive further ideas and implementations.

You find the code below.


German

Einführung

Vor einiger Zeit hat @remlaps seine Browser Extension. Damit werden Posts farblich markiert, die entweder eine Beneficiary an @null enthalten, oder für die mit einem Transfer an @null geworben wurde.

Besonders die zweite Möglichkeit rückt seit einiger Zeit wieder vermehrt in den Focus. Für mich war es daher interessant, zu erfahren, wie viele User haben einen Beitrag beworben und ob dieser auch vom Autor selbst beworben wurde. Da @remlaps seine Browser Extension zur weiteren Entwicklung freigegeben hat, lag es nahe, sie für diesen Zweck aufzubohren ;-)

Lösungen

Um herauszufinden, welcher User für welchen Post geworben hat, müssen als erstes die Transfers an @null ermittelt werden.

Steemchiller hat mit seinem SDS hierfür die passende Anfrage. Aktuell werte ich die letzten 250 Transfers an @null aus. Ich denke, das sollte aktuell genügen. Danach werden die Daten aufbereitet, so dass ein schneller Zugriff mit dem Post-Identifier erfolgen kann.

Schwieriger war es, an den Post-Identifier auf der Steemit-Seite zu gelangen. @remlaps durchläuft ja alles li-Elemente. Der Identifier ist aber nicht in einem Listenelement enthalten. Hier musste ich die eine Schleife bis zum passenden Eltern-Element einbauen. Offensichtlich gibt es keine Methode, alle Elternelemente eines Elements abzurufen.

Zunächst hatte ich überlegt, die Anzahl direkt in der Post-Card auf der Seite anzeigen zu lassen. Ich habe mich dann doch dafür entschieden, die Daten in den von @remlaps verwendeten Elemente in der Payout-Pane anzuzeigen.

Ich ergänze die Elemente mit dem Eintrag „Promotion Cost“ in jedem Post mit folgenden Daten:

  • Anzahl der verschiedenen User, die den Beitrag promoted haben,
  • „self“, wenn der Author selbst den Beitrag promoted hat.

Beispiel

Die letzten Zeile enthält die ergänzten Daten.

Änderungen

Ich habe versucht, so wenig wie möglich am ursprünglichen Code zu ändern.

  • Anpassen musste ich die Ermittlung von promoAmount, da jetzt hinter „Promotion Cost“ weitere Zeichen stehen.
  • Die wesentlichen neuen Funktionen habe ich außerhalb der highLight-Funktion deklariert. Somit genügt ein Aufruf im entsprechenden else if-Zweig für die neue Funktionalität.
  • Eine wesentliche Änderung ist der Zeitpunkt des erstmaligen Aufrufs der highLight-Funktion. Diese habe ich in den then-Zweig von fetch eingefügt, um den asyncronen Ablauf zu ermöglichen. Ansonsten wären beim ersten Aufruf die promotedPosts noch undefiniert.
  • In der manifest.json war eine wesentliche Änderungen notwendig: Ich musste eine Permission für die Request eintragen, damit das Script Daten von einem anderen Server laden darf.
  • Außerdem habe ich die Versionsnummer auf „0.0.2“ angehoben :-)

Das war's schon!

Ich halte es ansonsten wie @remlaps: Wer Lust und Laune hat, darf die Erweiterung gern weiterbearbeiten. Ich würde mich auf weitere Idee und Umsetzungen freuen.

Instructions:

Chrome-based Browser (Chrome/Brave/Edge)
Thanks to @michelangelo3 for the description and screenshots

  1. Create a new folder somewhere on the hard disk.
  2. Create a file "manifest.json" and a file "main.js" and save the content below with a text editor there.
  3. Open the page with the extensions:
    image.png
  4. Switch on developer mode
  5. Click on Load unpacked and select the folder you just created.

Done!
image.png

Installationsanleitung:

Auf Chrome basierende Browser (Chrome/Brave/Edge)
Dank an @michelangelo3 für die Beschreibung und Screenshots

  1. Einen neuen Ordner irgendwo der Festplatte erstellen.
  2. Eine Datei „manifest.json“ und eine Datei „main.js“ anlegen und den unten angegebenen Inhalt mit einem Texteditor dort speichern.
  3. Die Seite mit den Erweiterungen öffnen:
    image.png
  4. Entwicklermodus einschalten
  5. Auf Entpackte Erweiterung laden klicken und den eben neu erstellten Ordner auswählen.

Fertig!
image.png

Update (19.08.2022):

The main.js has been updated:
function prepareData: lines 96 - 98: else branch added.

Update (22.08.2022):

Intructions for Chrome added


Here is the code:

manifest.json:

{
  "manifest_version":2,
  "version":"0.0.2",
  "name":"Steem Curation Extension",
  "content_scripts":[
    {
      "matches":["https://steemit.com/*"],
      "js":["main.js"]
    }
  ],
  "permissions": [
    "https://sds.steemworld.org/*"
  ]
}

main.js:

console.log("The extension is up and running");
var promotedPosts = {}; // contains all transactions for promoted posts with accounts, count and whether self promoted
const urlRequest = "https://sds.steemworld.org/transfers_api/getTransfersByTypeTo/transfer/null/time/DESC/250/0";

const highLight = () => {

    var curatorBackgroundColor;
    const listItem = document.querySelectorAll('li');

    for (let i=listItem.length-1; i>=0; i--) {
        if ( listItem[i].textContent.match('null: .*%' ) && listItem[i].textContent.match('Promotion Cost .*\$') ) {
            console.log("Found a /promoted post in #burnsteem25 (outer block)");
            curatorBackgroundColor = '#1E90FF';
            listItem[i].style['background-color'] = curatorBackgroundColor;
        } else if ( listItem[i].textContent.match('null: .*%' )) {
            console.log("#burnsteem25 outer match: ");
            if ( listItem[i].textContent.match('^null:.*\%') ) {
                console.log("Found #burnsteem25");
                var str = listItem[i].textContent;
                var nullPct = str.substring(
                    str.indexOf(" ") + 1,
                    str.lastIndexOf("%")
                );
                if ( nullPct > 0 && nullPct < 25 ) {
                    curatorBackgroundColor = "coral";
                } else if ( nullPct < 50 ) {
                    curatorBackgroundColor = "orange";
                } else if ( nullPct < 75 ) {
                    curatorBackgroundColor = "darkorange";
                } else if ( nullPct > 0 ) {
                    curatorBackgroundColor = "orangered";
                }
            }       
            listItem[i].style['background-color'] = curatorBackgroundColor;
        } else if ( listItem[i].textContent.match('Promotion Cost .*\$') ) {
            console.log("Found a /promoted post (outer block)");

            if ( listItem[i].textContent.match('^Promotion Cost .*\$$') ) {
                console.log("Found a /promoted post");
                var str = listItem[i].textContent;
                var indexEnd = (str.indexOf("(") >= 0) ? str.indexOf("(") - 1 : str.length;
                var promoAmount = str.substring(
                    str.indexOf("$") + 1,
                    indexEnd
                );
                console.log ("Promotion amount: " + promoAmount);

                if ( promoAmount > 0 && promoAmount < 0.26 ) {
                    curatorBackgroundColor = "paleturquoise";
                } else if ( promoAmount < 0.51 ) {
                    curatorBackgroundColor = "aquamarine";
                } else if ( promoAmount < 1.01 ) {
                    curatorBackgroundColor = "turquoise";
                } else if ( promoAmount > 0 ) {
                    curatorBackgroundColor = "lightseagreen";
                }

                // now edit the textContent
                addText(listItem[i]);
            }
            listItem[i].style['background-color'] = curatorBackgroundColor;
        } else {
            listItem[i].style['background-color'] = "initial";
        }
    }
}

// load transfers to null and prepare promotedPosts for further use
fetch (urlRequest).then (function (response) {
    return response.json();
}).then (function (data) {
    prepareData(data);
    // execute hihgLight after providing the data
    highLight();
}).catch (function (error) {
    console.log ("error: " + error);
});

function prepareData(data) {
    if (data) {
        const cols = data.result.cols;
        const rows = data.result.rows;
        rows.forEach(trf => {
            let trfData = getAuthorPost(trf[cols["memo"]]);
            if (trfData) {
                let from = trf[cols["from"]];
                let self = (from == trfData["author"])
                let props = {"user": [from], "count": 1, "self": self};
                let key = trfData["post"];
                if (promotedPosts && key in promotedPosts) {
                    let oldProps = promotedPosts[key];
                    if ( !(oldProps["user"].includes(from)) ) {
                        props["user"] = props["user"].concat(oldProps["user"]);
                        props["count"] += oldProps["count"];
                        props["self"] = props["self"] || oldProps["self"];
                    } else {
                        props = oldProps;
                    }
                }
                promotedPosts[key] = props;
            }
        }); 
    };
}

function getAuthorPost(memoStr) {
    // const re = /^@(?<author>[\w-.]+)[\/](?<permlink>[\w-\|]+)$/;
    // const objMatch = memoStr.match(re);
    const objMatch = regexMatch(true, memoStr);
    let result = (objMatch && objMatch.length == 3) ? {
        "post": objMatch.groups["author"] + "/" + objMatch.groups["permlink"],
        "author": objMatch.groups["author"]
        } : null ;
    return result;
}

function addText(listItem) {
    var added = false;
    // console.log("include User? " + listItem.textContent.includes('User'));
    if ( !listItem.textContent.includes('User') ) {
    // get the postid
        let address = getAddress(listItem);
        // console.log("Address: " + address);
        if ( address !== null ) {
            let key = getPost(address);
            // console.log("Key: " + key);
            // console.log("Promoted: " + promotedPosts[key]);
            if ( promotedPosts[key] ) {
                let newText = ' (by ' + 
                    promotedPosts[key]["count"] + 
                    ( promotedPosts[key]["count"] == 1 ? ' User' : ' Users' ) + 
                    ( promotedPosts[key]["self"] ? ' incl. self)' : ')' );
                let newTextNode = document.createTextNode(newText);
                listItem.firstChild.appendChild(newTextNode);
                added = true
            }
        }        
    }
    if ( added ) {
        console.log("User added");
    } else {
        console.log("Adding User went wrong");
    }
}

function getPost(address) {
    const objMatch = regexMatch(false, address);
    return (objMatch && objMatch.length == 3) ? objMatch.groups["author"] + "/" + objMatch.groups["permlink"] : null;
}

function regexMatch(fromBegin, textStr) {
    re = fromBegin ? /^@(?<author>[\w-.]+)[\/](?<permlink>[\w-\|]+)$/ : /@(?<author>[\w-.]+)[\/](?<permlink>[\w-\|]+)$/;
    return textStr.match(re);
}

function getAddress(elem) {
    var link;
    while ( elem.parentElement && elem.parentElement.nodeName.toLowerCase() != 'body' ) {
        elem = elem.parentElement;
        if ( elem.nodeName.toLowerCase() == 'div' && elem.className.includes('articles__content-block--text') ) {
            let titleElemList = elem.getElementsByClassName('entry-title');
            link = titleElemList[0].firstChild.href;
            break;
        }
    }
    return link ? link : null;
}

// highLight();
window.addEventListener('scroll', () => {
    highLight();
});
window.addEventListener('load', () => {
    highLight();
});

console.log("The extension is done.");


Deutsch Unplugged

auf https://moecki.online/hive-146118

Steemit Dev Group

auf https://moecki.online/hive-192037

15.08.2022

Banner with kind permission of chriddi

Sort:  

Irgendwie hab ich deinen Post erst jetzt gesehen... Hmm, wie konnte das passieren?

Jedenfalls sehr cool! Nach etwas rumspielen (hab mich noch nicht dem Erstellen einer Browsererweiterung beschäftigt) läuft es jetzt:
image.png

Hab mir auch, da ich gerade nicht an meinem PC bin, den Quellcode kurz hier reingeladen. Eijeijei, hab zwar schon hi und da etwas mit JavaScript gespielt, tu mich aber echt schwer das zu lesen bzw. zu verstehen. Aber schöne Dinge kann man damit schon anstellen, das muss ich schon sagen.

#burnsteem25 find ich ned verkehrt, war allerdings bisher zu knausrig das zu machen. Werd ich bei den nächsten Posts nochmal überdenken :-)

Zum Transfer an @null hab ich mal was gelesen, nur SBD (keine Steem) an @null mit der Postadresse als Memo soweit ich noch weiß. Da gäbe es auch einen Tag wenn ich nicht irre, unter dem man diese Posts abrufen kann - #promoted ist es anscheinend nicht. Weißt du wie man die findet? (EDIT: grpfff... https://steemit.com/promoted - geht leider nur nach Trending, auf "New" umstellen funzt nicht)

Ah, noch was. Wofür ist bei den SDS-Abfragen eigentlich der observer Parameter? Hab bisher einfach null übergeben, da es anscheinend keine Auswirkung auf die Ergebnisse hat.

So, genug geschwafelt. DANKE für das Tool!

Loading...

Thanks! Very useful. It might be a few days before I have time to try it out, but I look forward to upgrading. ;-)

No problem :-)
BTW: We should think about a full extension in the store so that it can be installed easily and without much effort. :-)

Agreed. I bookmarked an online training course for creating browser extensions with that in mind. Unfortunately, I haven't made much progress so far, and I know I'm going to be time-challenged for the next couple of weeks. I hope to get back to it, though. Another goal is to add a "ticker" window/pane/area that will scroll through the /promoted titles in proportion with their promotion costs, maybe with an option to filter by community/tag.

The other thing I was thinking was maybe to put it on github and create a steem community where people can collaborate to improve it into a full-blown curation tool.

I had a look at how extensions can be created and published. Putting them together is no big deal, but you need a dev account, e.g. at the Firefox Store. That's where I got stuck at first...

add a "ticker" window/pane/area that will scroll through the /promoted

You mean that on the steemit page a small area appears where the articles scroll through. Like a news or stock ticker? That sounds very cool! ... And shouldn't be that difficult to implement....

put it on github and create a steem community

Github would be okay, but I don't think a special community is necessary. The Steemit Dev Group could already be used for that. Then there are at least a few more posts there that match the topic ;-)

I had a look at how extensions can be created and published. Putting them together is no big deal, but you need a dev account, e.g. at the Firefox Store.

Yeah, it seems pretty easy with chrome/brave, too. But I just want to know if I'm missing any pieces. Plus I just want to learn more about extensions, in general. Also, apparently there's a need for some sort of logo. "Pretty pictures" is not really my thing. ;-)

Like a news or stock ticker?

Exactly. That's where I got the idea. I saw someone else had done an extension with a stock ticker. I definitely think that would make post promotion more valuable (if widely used).

The Steemit Dev Group could already be used for that.

Yeah, probably true. Not sure if the mods are still around, though.

And now please for people who are far from programming. What should I do with this code to install this browser extension?

I will write a short tutorial about it (...in the next days) :-)
Which browser do you use?

I use the Brave browser at work, and Google Chrome at home.

Now I added the instructions für Chrome and Brave above.
Good luck! :-)

Thank you very much. It doesn't look difficult, but I wouldn't know what to do without your instructions.

Apparently, the pattern matching in a permissions list is broken as of manifest version 3. The same goal can by accomplished by host_permissions. (link)

I have a github repo set up, but I didn't set it public yet - since I don't completely know what I'm doing with it ;-). I'll invite you to it, if you want to send me your github account. (If you don't want to make it public, you can encrypt it in a transfer memo by putting a '#' in the first character.)

Now i set up an account on github :-)
But I have only created a private repo so far. I still have to explore the whole thing a bit :-)

I just invited you to the repo. So far, I have only done private repos, too, since I'm still learning my way around.

I accepted the invitation. Now I just have to figure out how to work on it.... It's a nice exercise.
Surely I can link the repo locally (for VSCode)?!

Surely I can link the repo locally (for VSCode)?!

I am pretty sure you can. I linked Eclipse to github, and @cmp2020 linked pycharm to it. Since Microsoft owns github, they must make it pretty easy for VSCode.

The connection itself works. However, it was not clear to me how and whether it also works with external repos.
I have now cloned the repo locally and can now work with it.

Oh, right. Yeah, that's how we have been doing it, too.

the pattern matching in a permissions list is broken as of manifest version 3

Firefox already had a problem with manifest version 3, so I changed it to version 2.

I don't have a github account yet. But I think with the number of projects I have here in VC code, the necessity increases :-) I'll keep that in mind for the next time...

Wer Lust und Laune hat, darf die Erweiterung gern weiterbearbeiten.

Okay. Mach' ich... 😂

Halleluja, ich bin beeindruckt!
Werde gleich mal was ausprobieren - mit den Farben für "self" oder auch nicht... 😉

Jaja, was ausprobieren... hab's genau gesehen... ;-)
Danke! :-))

Haha und? Welche Farbe?! War gar nicht so einfach, dich an die zweite Position zu bringen... ;-)
Das "Resultat" ist dann ja auch recht imposant - und birgt neuen Stoff für die laufende Diskussion. Habe dazu einen ziemlich langen Kommentar abgeliefert (nicht den, in dem ich dich erwähnt habe).
Hatte eigentlich geplant, die Option für den Community-Account weiter zu nutzen, weil ich finde, dass das hochfrequentierte Scrabble durchaus eine gute Werbung für den Steem ist. Mal gucken. Alles andere könnte auch als "Vetternwirtschaft" ausgelegt werden, wenn man sich schon nicht selbst bewirbt. Fühlt sich nicht gut an, muss dann schon echt brillant sein.

Aber mal ernsthaft zum "Ausprobieren": Natürlich habe ich keine Farben gesehen, weil ich ja gar keine Browsererweiterung installiert habe. Wie geht das? Ich kann doch nicht einfach Codes verbasteln. Nein, das kann ich nicht.

So sieht's jetzt aus:
grafik.png

War gar nicht so einfach, dich an die zweite Position zu bringen... ;-)

Irgendwie ist er jetzt sogar an die erste Position gelangt...

birgt neuen Stoff für die laufende Diskussion. Habe dazu einen ziemlich langen Kommentar abgeliefert.

Ja, das ist nicht ausgeschlossen. Es ist mir auch etwas unangenehm. Aber nur etwas, da ich guten Gewissens bin. :-)
Für das Scrabble (und den DU-Account) finde ich das unproblematisch. Schließlich ist es wirklich eine Serie, die einige User auf die Chain ruft...
Deinen Kommentar habe ich gesehen, aber noch nicht richtig gelesen... heute ist es auch schon wieder zu spät...

Wie geht das?

Da nun schon zwei Leute gefragt haben, mache ich dazu gern mal eine kurze Anleitung unter dem Post in den nächsten Tagen. Welchen Browser nutzt du?

Irgendwie ist er jetzt sogar an die erste Position gelangt...

Jo. Der Verursacher wurde mit einem Blick in die @null Wallet auch schnell ausgemacht - remlaps hat noch einen draufgesetzt. Prima, so in etwa funktioniert dann das "Spiel", das er sich überlegt hat.

Und wir haben einen "Bug"... ;-)
Die kleine App müsste jetzt "by 2 users" anzeigen... ;-)

eine kurze Anleitung

Weiß gar nicht, ob ich die Erweiterung überhaupt nutzen würde - hab ja dich... ;-D
Bin aber neugierig.

Ich nutze drei verschiedene Browser auf dem Mac: Brave, Chrome und Safari.

Bin aber neugierig.

Also bei Chrome geht es recht einfach:

Erstmal einen neuen Ordner irgendwo auf deiner Platte erstellen, dann den Inhalt der beiden Dateien manifest.json und main.js mit einem Texteditor dort speichern.

Bei Chrome die Seite mit den Erweiterungen öffnen...
image.png
...Entwicklermodus einschalten und dann mit Entpackte Erweiterung landen den eben neu erstellten Ordner auswählen.

Und tada!
image.png

Cool, dass du das mit der Anleitung übernommen hast. Danke! 👍

Im Firefox ist es auch ähnlich, nur dass man die debugging-Seite im Adressfeld öffnen muss... Das kann ich dann auch noch darstellen.
Deine Bilder verwende ich gern für den Chrome. Im Edge ist es tatsächlich genauso, denn der basiert ja auf Chrome.

Tschä, noch kannst du mich nicht als Chefentwickler einstellen - erste Hürde failed... ;-)

Hihi, danke!
Hab's gleich mal ausprobiert, aber...

Bildschirmfoto 2022-08-20 um 15.39.02.png

Oh, scheint als ob dein Programm mit dem du den Text gespeichert hast noch Formatierungen dazuschreibt. Es muss ein reiner Texteditor sein, bei Windows nennt sich das "Ding" z.B Editor. Der speichert nur den Text ohne irgendwelche Formatierungen.

Gut, dass ich das nicht so schnell gesehen habe... Ich hätte wahrscheinlich als Erstes ein paar von den Wollfusseln eurer Scheraktion als Übeltäter vermutet, die sich in die Tastatur verirrt haben könnten ;-))

Und wir haben einen "Bug"... ;-)
Die kleine App müsste jetzt "by 2 users" anzeigen... ;-)

Du hast sowas von recht! Fehlt doch ein else-Zweig :-(
Hab's jetzt ergänzt. Danke für die aufmerksame Suche!

Brave, Chrome und Safari

Echt jetzt?! Das sind genau die Browser, die ich nicht nutze. Dazu kann ich also gar keine Screenshots machen... Naja, vielleicht kriegen wir ja demnächst eine vollwertige Extension hin :-)

Bei Chrome basierten Browsern sieht es so oder so ähnlich wie im Kommentar oben an Chriddi aus. Kannst gerne verwenden falls du magst.

Bin eben nach Installation im Vivaldi-Browser über diese Meldung gestolpert:
image.png

Hier https://developer.chrome.com/blog/mv2-transition/ der Link, dann brauchst ihn evtl. nicht vom Screenshot abtippen, Dev-Zeiten sind teuer :-))

Erweiterung läuft ganz normal, konnte es aber nicht lassen, mit zwei kleinen Änderungen in der manifest.json ("manifest_version": auf 3 und "permissions" geändert auf "host_permissions") läuft es jetzt unter v3 bzw. mein Browser zeigt zumindest keine Fehler mehr:

{
  "manifest_version":3,
  "version":"0.0.2",
  "name":"Steem Curation Extension",
  "content_scripts":[
    {
      "matches":["https://steemit.com/*"],
      "js":["main.js"]
    }
  ],
  "host_permissions": [
    "https://sds.steemworld.org/*"
  ]
}

Ob das alles ist, kann dir leider nicht sagen - kenn mich mit dem Zeugs ja nicht so aus.

EDIT: Ups, voller Eifer gar nicht gesehen, hast es ja mit remlaps bereits besprochen.

Macht ja nix... Jetzt haben wir es noch auf Deutsch :-)
Danke!

This post has been featured in the latest edition of Steem News...

Upvoted! Thank you for supporting witness @jswit.
default.jpg

Coin Marketplace

STEEM 0.22
TRX 0.06
JST 0.025
BTC 18807.15
ETH 1295.03
USDT 1.00
SBD 2.51