New Feature: Beneficiaries, Comment Options, and Metadata

View this thread on: d.buzz | hive.blog | peakd.com | ecency.com
·@r351574nc3·
0.000 HBD
New Feature: Beneficiaries, Comment Options, and Metadata
# It's Finally Useful
----
One of the biggest issues behind this project I came up with was that I didn't quite deliver on its most important feature (collaboration).  Sure, users can collaborate, but they cannot share rewards. It's not real collaboration if you can't officially give credit on the platform. Further, posts were just half-baked implementations of a post. It was still really more effective to post though steemit. I think with this PR, we now have a set of features that will make this a strong contender when deciding how to create a post.

## The Implementation

The way I decided to implement this is using `git notes`. This is one of the features of `git` that helps add more information around commit messages to fix a gap in functionality not provided by tools like `svn`. For example, one of the problems with svn is mistaken commits, maybe hasty commits, pre-commits, early commits, or the need to edit commits. While `commit --amend` really helps with this, it affects the actual commit itself, and sometimes you don't want to do that because sometimes it wreaks havoc with other users when you have already done `git push`.  The only way to get in your `--amend` is to `push --force` (may the fourth be with you). That is not idea is the only change you're making is a modification to a commit message or something like that. It is far better to use `notes` instead.

I decided to use this feature to also embed metadata about the content committed which is a totally valid use case.  Instead of using the actual commit to store details (would be really awkward), I store metadata in notes (kinda like an afterthought).

While add information to notes, I can organize it using a `ref` 

```shell
git notes --ref=beneficiaries add -m "{ \"beneficiaries\":\ "r351574nc3:2000\" }"
```
I can use `--ref=beneficiaries` or `--ref=metadata` to store my notes according to context and what the note is for. Further, I can avoid stepping on notes that might be relevant to the post.

### post-update

On `post-update`, I collect the notes and use them as input to the `steem-js` api.

### steemgit

For usability, had to make changes to this to be a facade on `git` instead of an alias. I should have just made actual `git` plugins (which may still happen), but I want to focus on working features before refining them.

## Usage

There are now 3 new commands for `steemgit`
* `steemgit beneficiaries` allows modification of beneficiaries
* `steemgit comment_options` allows adding comment options`
* `steemgit metadata` for applying `json_metadata` (tags, custom metadata, etc...)

### Comment Options

Comment options allow users to specify the following:
* Allowing votes
* Allowing curation rewards
* Beneficiaries
* Maximum Payout
* SBD payout percentage

There are several cases for this. Two most common would be rewards refusal and beneficiary assignment. Let's look at both cases.

### Refusing Rewards

**No Voting**
```
steemgit add new-post.md
steemgit comment_options "0 SBD" 0 false false  // No rewards, no curation, no votes
steemgit commit -a -m "Title of Post"
```

**Voting, but no rewards**
```
steemgit add new-post.md
steemgit comment_options "0 SBD" 0 true false  // No rewards, no curation, yes votes
steemgit commit -a -m "Title of Post"
```


### Assigning Beneficiares

```
steemgit add new-post.md
steemgit beneficiaries gtg:2500,drakos:2500
steemgit commit -a -m "Title of Post"
```

### Options after the fact

Supposing that you already committed your post, you can still make changes before pushing.
```
steemgit add new-post.md
steemgit commit -a -m "Title of Post"
steemgit beneficiaries gtg:2500,drakos:2500
steemgit commit -a --amend
```

### Post Metadata

It's possible to add custom metadata to posts. Custom metadata has infinite use cases, so we won't go into them. This is how to do it if you need to:

```
steemgit add new-post.md
steemgit metadata metadata.json
steemgit commit -a -m "Title of Post"
```

### From stdin
```
steemgit add new-post.md
steemgit metadata <<EOF
json_metadata: {
    "tags": [ "awesome" ]
}
EOF
steemgit commit -a -m "Title of Post"
```

## Actual Changes

```patch
diff --git a/bin/steemgit b/bin/steemgit
new file mode 100755
index 0000000..4c6f7bf
--- /dev/null
+++ b/bin/steemgit
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+GIT="git -C /work/$STEEMIT_GIT_PROJECT"
+
+for i in "$@"
+do
+    case $i in
+        comment_options)
+        PREFIX="${i#*=}"
+        MAX_PAYOUT=$2
+        PERCENT_SBD=$3
+        ALLOW_VOTES=$4
+        ALLOW_CURATION=$5
+        $GIT notes --ref=comment_options add -F /dev/stdin <<EOF
+{
+    "extensions":[],
+    "operations":[
+        [
+            "comment_options",
+            {
+                "author":"",
+                "permlink":"",
+                "max_accepted_payout":"$2",
+                "percent_steem_dollars": $3,
+                "allow_votes":$4,
+                "allow_curation_rewards":$5,
+                "extensions":[]
+            }
+        ]
+    ]
+}
+EOF
+        exit
+        ;;
+        beneficiaries)
+        BENEFICIARIES=$2
+        $GIT notes --ref=beneficiaries add -F /dev/stdin <<EOF
+{ "beneficiaries": "$2" }
+EOF
+        exit;
+        ;;
+        metadata)
+        METADATA_FILE=$2
+        if [ "$METADATA_FILE" -eq "" ]
+        then
+            METADATA_FILE=/dev/stdin
+        fi
+        $GIT notes --ref=metadata add -F $METADATA_FILE
+        exit;
+        ;;
+        push)
+        $GIT push --tags steem master --force
+        ;;
+        *)
+        ;;
+    esac
+done
+
+exec $GIT "$@"
```
> New `steemgit` script for adding commands to `git`


```patch
diff --git a/hooks/index.js b/hooks/index.js
index 6bf74b8..beaefa9 100644
--- a/hooks/index.js
+++ b/hooks/index.js
@@ -1,21 +1,104 @@
 const Promise = require('bluebird')
 const steem = Promise.promisifyAll(require('steem'))
 const fs = Promise.promisifyAll(require('fs'))
+const shell = require('shelljs');
 
 
+const defaults = {
+    comment_options: {
+        "extensions":[],
+        "operations":[
+            [
+                "comment_options",
+                {
+                    "author":"",
+                    "permlink":"",
+                    "max_accepted_payout":"1000000.000 SBD",
+                    "percent_steem_dollars": "10000",
+                    "allow_votes": true,
+                    "allow_curation_rewards": true,
+                    "extensions":[]
+                }
+            ]
+        ]
+    },
+    metadata: { tags: [], app: 'r351574nc3/docker-git-steem-bot' },
+    beneficiaries: []
+}
 
 function load_post() {
     return fs.readFileAsync(0, 'utf8')
 }
 
+function load_beneficiaries(repo) {
+    if (shell.exec(`git -C ${repo} notes --ref=beneficiaries list`) == '') {
+        return defaults.beneficiaries
+    }
+    let ref = shell.exec(`git -C ${repo} notes --ref=beneficiaries list`).exec("cut -f 2 -d ' '")
+    let data = shell.exec(`git -C ${repo} notes --ref=beneficiaries show ${ref}`)
+
+    
+    if (data != '') {
+        let retval = [
+            [
+                0,
+                {
+                    beneficiaries: [
+                    ]
+                }
+            ]
+        ]
+        JSON.parse(data).beneficiaries.split(",").forEach((kvpair) => {
+            let { account, weight } = kvpair.split(":");
+            retval[0][1].beneficiaries.push({ account: account, weight: weight })            
+        })
+        return retval
+    }
+    return defaults.beneficiaries
+    
+}
+
+function load_metadata(repo) {
+    if (shell.exec(`git -C ${repo} notes --ref=metadata list`) == '') {
+        return defaults.metadata
+    }
+    let ref = shell.exec(`git -C ${repo} notes --ref=metadata list`).exec("cut -f 2 -d ' '")
+    let data = shell.exec(`git -C ${repo} notes --ref=metadata show ${ref}`)
+    if (data != '') {
+        let retval = JSON.parse(data)
+        retval.app = 'r351574nc3/docker-git-steem-bot'
+    }
+    return defaults.metadata
+}
+
+function load_comment_options(repo) {
+    if (shell.exec(`git -C ${repo} notes --ref=comment_options list`) == '') {
+        return defaults.comment_options
+    }
+
+    let ref = shell.exec(`git -C ${repo} notes --ref=comment_options list`).exec("cut -f 2 -d ' '")
+    let retval = shell.exec(`git -C ${repo} notes --ref=comment_options show ${ref}`)
+    if (retval != '') {
+        return JSON.parse(retval)
+    }
+    return defaults.comment_options
+}
+
 function main() {
     let user = process.env.STEEM_NAME || "Not set"
     let wif = process.env.STEEM_WIF || "Not set"
     let permlink = process.argv[2]
     let title = process.argv[3]
+    let repo  = process.argv[4]
+
+    let metadata = load_metadata(repo);
+    let comment_options = load_comment_options(repo);
+    comment_options.operations[0][1].author = user
+    comment_options.operations[0][1].permlink = permlink
+    comment_options.operations[0][1].extensions = load_beneficiaries(repo);
+
     load_post().then((body) => {
-        var permlink = new Date().toISOString().replace(/[^a-zA-Z0-9]+/g, '').toLowerCase();
+        console.log("Permlink ", permlink)
         return steem.broadcast.commentAsync(
             wif,
             '', // Leave parent author empty
@@ -24,12 +107,23 @@ function main() {
             permlink + '-post', // Permlink
             title, // Title
             body, // Body
-            { tags: [], app: 'r351574nc3/docker-git-steem-bot' }
+            metadata
         )
-    }).then((results) => {
-        console.log(results)
-    }) 
+    })
+    .then((results) => {
+        steem.broadcast.send(wif, comment_options, function(err, results) {
+            if (err) {
+                console.log("Unable to set comment options ", JSON.stringify(err));
+                return
+            }
+            console.log("Results ", results)
+        });
+    })
+        .catch((err) => {
+            console.log(err)
+        })
+    
+    console.log("Exiting")
 }
-main()
```
> Notes implementation for `post-update`

```patch
diff --git a/hooks/post-update b/hooks/post-update
new file mode 100755
index 0000000..764ede9
--- /dev/null
+++ b/hooks/post-update
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+#
+# To enable this hook, rename this file to "update".
+#
+# Config
+# ------
+# STEEM_USER
+# STEEM_WIF
+#
+
+# --- Command line
+set -x
+
+
+FILES_MODIFIED=$(git diff-tree  --name-only -r HEAD | tail -n $(expr $(git diff-tree  --name-only -r HEAD | wc -l) - 1))
+
+
+for file in $FILES_MODIFIED
+do
+    permlink=$(basename $file .md)
+    title=$(git show --pretty=tformat:%s HEAD | head -1)
+    git show HEAD:$file > /tmp/body
+    cd hooks
+    cat /tmp/body | npm run post $permlink "$title" "$OLDPWD"
+    cd -
+    rm /tmp/body
+done
+
+git push origin refs/notes/* master --force
+
+# --- Finished
+exit 0
```
> Removing the old `update` and replacing with `post-update`

## Roadmap
1.  New tutorial on how to use this tool. 
1. Actual `git` plugin to replace `steemgit`
1. Bug fixes

<br /><hr/><em>Posted on <a href="https://utopian.io/utopian-io/@r351574nc3/new-feature-beneficiaries-comment-options-and-metadata">Utopian.io -  Rewarding Open Source Contributors</a></em><hr/>
👍 , , , , , , , , , , , , , , , , , , , , , , ,