STEEM编程练习记录 三

今天早晨,正在我因为是星期五发懒,不知道想做什么事的时候,收到了村长@ericet圈给我的一条回复。在[Life]likeCoin 为什么不制作一个chrome 扩展程序?里,@jun0621提出了一个非常有趣的想法。就是制作一个like Coin的Chrome扩展程序。用一个Chrome Extension,在所有的STEEM的结点上添加“啪啪啪”的小手。
一个挺好玩的程序。于是我打算做一个试试。

设计思路

要想制作这么一个能够在所有和STEEMIT有关的结点拍手的扩展程序,必须要让这个程序知道自己在1)STEEM的节点上;2)正在浏览的文章是一个有注册LikeCoin的用户。

  1. 第一项非常简单,因为在Chrome扩展程序的manifest.json里面可以规定程序在哪些网站上运行。

  2. 第二项稍微复杂一点。需要用一种方法搜取文章的作者,查询作者是不是已经注册了Matters
    文章的作者可以从网址中截取。举一个例子:@huaren.news的文章武汉肺炎多国蔓延,该指责还是该合作?世界欠中国/武汉一份感谢!在不同的节点上的网址是:

    1. https://staging.busy.org/@huaren.news/48spug
    2. https://steem.buzz/hive-180932/@huaren.news/48spug
    3. https://steempeak.com/@huaren.news/48spug
    4. https://steemit.com/hive-180932/@huaren.news/48spug


    仔细观察的话,不难发现,每个网址后半段都是@huaren.news/48spug。如果程序在/之间切开整条网址的话,倒数第二段便是@huaren.news。如果我们观察非发表的文章的网址,比如说SteemCN社区(https://steemit.com/trending/hive-180932),这个网址中,没有@用户。我们可以利用这一特点从网址中筛选当前的网页是否是一片STEEM上的文章,并且得到作者的用户ID。

  3. 我们还要找出所有已经申请Matters的用户。



    上面一张截图中是我问村长SteemCN是怎么记录Matters用户的对话。看来只要我把@cn-likers关注的名单调出来就知道所有Matters用户了。我在STEEM编程练习记录 二介绍过如何利用SteemJs的API搜索follower和following名单。在回复中村长@erice还贴了他原来写过的follower的程序。由于他的代码比我的更加完整。我借用了他的代码,并改成搜索following的名单。

  4. 第四步是寻找当前文章用户的LikerId。我在写程序的时候,忽略了这一点。正巧我借用来测试的@windowglass【职场碎碎念】辞职@windowglassLikerIdSteemId是相同的。所以我在push我的commit到github上,再测试cn-fund的时候才发现找不到正确的LikerId



    我在STEEM编程练习记录 一介绍过如果搜索STEEM用户信息。在第二步通过网址截取用户ID之后,可以先用用户ID搜索用户信息,从中获得用户Location里面LikerId:xxx的数值。

  5. 第五步也是在push commit之后发现的。就是更换网址之后,Chrome扩展程序里面的网址和用户LikerId并没有更改。我需要在更换网址的时候,触发扩展程序里面的存储的数值的更新。
    本来想利用Chrome Extension里面的content.jsbackground.js利用Chrome自己的API做触发。但是由于想在半天内写出一个Beta版本供大家啪啪啪,所以我选择了一条更简单粗暴的方法:就是设置一个无限循环的程序环,每个200毫秒自动检查一下当前的网址是否和上一次检查时是不同的网址,如果有变化,则从新更新一遍数据。

编写程序

manifest.json是Chrome扩展程序的清单文件。里面记载着扩展程序的版本,导入的其它JavaScript库,注入的配置文件等等。

{
  "manifest_version": 2,
  "name": "Liker Clap Extension",
  "description": "This extension will add a Matters Likr Clap Button",
  "version": "0.1",
  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  },
  "content_scripts": [
    {
      "matches": [
        "*://*.busy.org/*",
        "*://*.steem.buzz/*",
        "*://*.steemit.com/*",
        "*://*.steempeak.com/*"
      ],
      "js": [
        "steem.js",
        "liker.js"
      ]
    }
  ],
  "permissions": [
    "tabs"
  ]
}


上段代码就是本程序的manifest.json。其中content_scripts里面规定了,在哪些网站上,这个扩展程序可以运行(matches)和运行哪些JavaScript的library(steem.jsliker.js)。
由于Chrome扩展程序不能调用CDN上面的程序库,我想STEEM API下载下来,放到了程序本地的目录下(steem.js)。
必须要主意程序库的排列顺序。liker.js里面要调取steem.js里面的变量steem,所以steem.js必须在liker.js的前面。

var url = window.location.href;
const button = document.createElement("div");
const css = document.createElement("style");
var href = url.split("/");
var steemId = href[href.length-2];
//steem.api.setOptions({ url: "https://anyx.io" });

const getFollowing = (start = 0, limit = 1000, following = []) => {
  return new Promise((resolve, reject) => {
    steem.api.getFollowing("cn-likers", start, 'blog', limit, async function (err, result) {
      if (result.length > 1) {
        let newResult = [];
        result.forEach(following => {
            if (following.follower != start) {
                newResult.push(following.following);
            }
        });
        following = [...following, ...newResult];
        let followingList = [];
        for (let i in newResult) {
            followingList.push(newResult[i].following);
        }
        getFollowing(result[result.length - 1].following, limit, following)
        .then(resolve)
        .catch(reject);
      } else {
        resolve(following);
      }
    });
  });
}

const getLikerId = (profile) => {
  const location = profile.location;
  console.log(location)
  if(typeof location !=="undefined" && location !== "") {
    const loc = location.split(":");
    if( loc[0] === "likerid" ) { return loc[1]; }
    else { return false; }
  }
  else { return false; }
}

const isLiker = (steemId, following) => {
  let flag = false;
  if(steemId.startsWith("@")) {
    const id = steemId.substr(1);
    for(let i = 0; i < following.length; i++) {
      if(id === following[i]) {
        flag = true;
        break;
      }
    }
    if(flag) { return id; }
    else { return false; }
  }
  else{ return false; }
}

async function createLikerButton(url, steemId) {
  let following = await getFollowing();
  //url = encodeURIComponent(url);
  steemId = isLiker(steemId, following);
  console.log(url);
  console.log(steemId);
  if(steemId) {
    steem.api.getAccounts([steemId], function(err, result) {
      if(err === null) {
        const data = result[0];
        const profile = JSON.parse(data.json_metadata);
        const likerId = getLikerId(profile.profile);
        if(likerId) {
          const src = `https://button.like.co/in/embed/${likerId}/button?referrer=${url}`;
          //console.log(src);
          const iframe = document.createElement("iframe");
          iframe.setAttribute("src", src);
          iframe.setAttribute("frameborder", 0);
          iframe.setAttribute("scrolling", 0);
          iframe.setAttribute("target", "_top");
          button.appendChild(iframe);
        }
      }
      else{
        console.log(err);
      }
    });
  }
  else{
    console.log("Not a registered Liker");
  }
}

css.innerHTML = `
.likecoin-button {
  bottom: 20px;
  position: fixed;
  right: 20px;
  height: 240px;
  width: 500px;
  overflow: hidden;
  z-index: 2000
}
.likecoin-button > div {
  padding-top: 49.48454%;
}
.likecoin-button > iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  -ms-zoom: 0.75;
        -moz-transform: scale(0.75);
        -moz-transform-origin: 0 0;
        -o-transform: scale(0.75);
        -o-transform-origin: 0 0;
        -webkit-transform: scale(0.75);
        -webkit-transform-origin: 0 0;
}
`;
document.body.appendChild(css);
button.className = "likecoin-embed likecoin-button";
document.body.appendChild(button);


createLikerButton(url, steemId);
window.setInterval(function() {
  let tempurl = window.location.href;
  if(tempurl !== url) {
    button.textContent = "";
    url = tempurl;
    href = url.split("/");
    steemId = href[href.length-2];
    createLikerButton(url, steemId);
  }
}, 1000);

liker.js是本程序的主心骨。程序一开始先记录当前的网址,生成啪啪啪按键的iFrame。搜取用户的SteemId

我在测试中,可能是调用API太频繁了,惹得https://anyx.io生气把我给block掉了。对于我来说,用steem默认得结点没有任何问题。但是对于墙内的朋友,估计还得使用https://anyx.io这样的结点支持。使用的方法是uncommon第六行(去掉//

  1. getFollowing方程在原来的文章里面讲过,是用来搜索所有@cn-likers关注的名单。
  2. getLikerId的用处是搜索当前文章的作者是否在自己的Location里面填写了自己的LikerId
  3. isLiker是用来过滤非发表的文章的网页。上面介绍多,所有文章的网址里面都用@用户,只要我们将网址切开,倒数第二段里面是以@开头的网址,就都是文章。
  4. createLikerButton则是将所有前面的方程都连接起来。先收录所有@cn-likers关注的用户的名单;再搜索当前网页是否是一篇文章,如果是的话,则截取文章的作者;搜索作者的用户资料并检查是否记录了LikerId。如果拥有LikerId的话,则在网页上加上一个啪啪啪按键。上面提到的@huaren.news就没有注册Matters,所以文章上就没有出现LikeCoin的按键。

扩展程序使用效果


https://steemit.com


https://steempeak.com/


https://staging.busy.org

Screenshot_20200228_135153_com.oice.jpg
啪啪啪之后,在Liker Land*上可以看到Busy,Steemit的啪啪啪。


以后还可以做什么

我曾经听过一句形容完成程序开发的话:“没有完成开发的程序,只有不想再继续做下去的程序。”那么,这个我们还能再在这个程序上添加些什么呢?

  1. 可以建设访问@cn-likers关注名单的次数。前面提到了https://anyx.io暂时性限制了我访问他们API的权限。我们在第一次搜索@cn-likers关注名单之后,将名单存储下来,在规定的时间内不再访问Steem API结点。
  2. 提供Steem API的结点服务器很多,可以适当轮换当前使用的结点,这样可以减少过多使用同一个结点的频率。
  3. 加一个on/off开关。在不想使用Chrome Extension的时候,将其关掉。

安装Chrome扩展程序

我已经把扩展程序存在Github和码云上面。

  1. Github: https://github.com/xiangstan/likecoin-extension
  2. 码云: https://gitee.com/xiangstan/likecoin-extension

登陆不了Github的可以去码云下载。

代码资料库里面有介绍如何将扩展程序安装到自己的浏览器上(如图)。


感谢

  1. 设计啪啪啪按键得到了https://github.com/likecoin/likecoin-button-sdk的启发。其中CSS Style也是根据likecoin-button-sdk里面JavaScript的代码改编的。
  2. 村长@ericet的getFollowers代码
  3. 特别感谢,压轴的重头戏。扩展程序在浏览器里面的图标是女友当年练习Illustrator的时候设计的。别人都可以不感谢,这个必须感谢……
H2
H3
H4
3 columns
2 columns
1 column
38 Comments
Ecency