Mesh decentralized Unified P2P Fat Protocol Layer. Connectors between Libre Money, ScuttleButt, IPFS, ... Salamalec between Nodes and Human.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

475 lines
18 KiB

#!/bin/bash
########################################################################
# Author: Fred (support@qo-op.com)
# Version: 2020.03.18
# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/)
########################################################################
MY_PATH="`dirname \"$0\"`" # relative
MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized
ME="${0##*/}"
echo '
########################################################################
# \\///
# qo-op
############# '$ME' creates ZenTag (+ Passenger)'
ZEN="$1" # Zen amount
ZENSOURCE="$2" # G1 TX HASH OR PASSENGER timestamp ~/.zen/miam/timestamp/
PASSENGER="$3" # ~/.zen/miam/timestamp/src_id.ext in ZenTag (add to IPFS datastructure)
METADATA="$4"
READ="$5" # Zen amount asked to allow passenger reading
PARK="$6" # Zen amount payed for IPFS PIN by passenger everyday
# Add any "parameters" needed by CONTRACT.sh
echo "
########################################################################
# $MY_PATH/$ME $ZEN $ZENSOURCE $PASSENGER $READ $PARK $METADATA
# IPFS DATASTRUCTURE ~/.zen/tag/sha256sum(_tag.uid)
########################################################################
# [ASTROPORT](https://astroport.com)
########################################################################"
# If PASSENGER file, then must provide METADATA!
[[ $PASSENGER != "" ]] && [[ $METADATA == "" ]] && echo "ERROR PASSENGER needs METADATA !!! Please verify and correct..." && exit 1
[[ $READ == "" ]] && READ=1
[[ $PARK == "" ]] && PARK=10
##############################################
# NODE ENVIRONEMENT DETECTION
##############################################
IPFSNODEID=$(ipfs id -f='<id>\n')
[[ ! -f ~/.ssb/secret.dunikey ]] && $MY_PATH/tools/secret2dunikey.sh
G1PUB=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2)
echo "G1SSB Wallet: $G1PUB
IPFS: $IPFSNODEID
__ __ _ _ _______
_______ _ __ | \/ | / \ | |/ / ____|
|_ / _ \ '_ \ | |\/| | / _ \ | ' /| _|
/ / __/ | | | | | | |/ ___ \| . \| |___
/___\___|_| |_| |_| |_/_/ \_\_|\_\_____|
"
########################################################################
# ZenTag is an IPFS/IPNS datastructure shared into IPFS, publish
# ~~~ Draft ~~~~ Draft ~~~~ Draft ~~~~ Draft ~~~~ Draft ~~~~
# _chain # ZenTag IPFS Hash
# _chain.n # Sequence Number
# _chain.nanodate # Current nanodate
# _chain.prev # Previous IPFS Hash
# _g1.node.creator # G1SSB wallet pubkey
# _tag.issuer # G1TX ISSUER pubkey
# _tag.zensource # G1TX HASH or SSB timestamp
# _ipfs.node.creatorcat # NODE IPFS ID
# _ipfs.publishkey.BB.aes # BB SYMcypher of IPNS publish key
# _ipfs.publishkey.crypt # G1SSB ASYMcypher of IPNS publish key
# _tag.BB.sha # BB sha256sum
# _tag.uid # ZenTag UID
# _tag.zen # ZentTag balance
# _ipns # /ipns/ address
# PASSENGER FILE (put to IPFS and cypher link in IPNS ZenTag)
# _passenger.filename # Passenger filename
# _passenger.ipfs.crypt # G1SSB cyphered IPFS Passenger link
# _passenger.read # Zen value asked for READING
# _passenger.park # Zen value paid for PARKING
# Only if contract executed => add new cyphered links
# _passenger.contract.sh # Zen CONTRACT TODO
# COmpare with code to verify and extend Draft
# TODO: Could be converted into yaml or json if you like...
########################################################################
#TAG IS MANAGED BY NODE KEY OR BB KEY to change IPNS latest status
#INDEX UPDATE ON CURRENT NODE IPFS SWARM CHANNEL
[[ $ZEN == "" || $ZENSOURCE == "" ]] && echo "ERROR... Please provide Zen value" && exit 1
if [[ "$METADATA" == "" ]]; then
echo "
# LOVE BANK - PrePaid Card ZenTag
# FULLY BACKED ON IRL G1 LIBRE MONEY (https://cesium.app)
# ____ ____ _____ _____ _ _
# / ___| _ \| ____| ____| \ | | _______ _ __
# | | _| |_) | _| | _| | \| | |_ / _ \ '_ \
# | |_| | _ <| |___| |___| |\ | / / __/ | | |
# \____|_| \_\_____|_____|_| \_| /___\___|_| |_|
#
"
# COMMENT WILL BREAK (G1 <=> ZEN) STRICT RELATION
[[ ! -f ~/.zen/cache/g1_TX_inputs/zen.$ZENSOURCE ]] && echo "ERROR# UNKNOWN ~/.zen/cache/g1_TX_inputs/zen.$ZENSOURCE !!" && exit 1
ISSUER=$(cat ~/.zen/cache/g1_TX_inputs/zen.$ZENSOURCE)
[[ $ISSUER == "" ]] && echo "ERROR# NO ISSUER FOUND FOR TX $ZENSOURCE !!" && exit 1
echo "ISSUER = $ISSUER SENT TX $ZENSOURCE !!"
else
echo "
_|_ o ._ _ _ _ _|_ _. ._ _ ._
|_ | | | | (/_ _> |_ (_| | | | |_)
|
SSB PASSENGER ~/.zen/miam/$ZENSOURCE
"
[[ ! -d ~/.zen/miam/$ZENSOURCE ]] && echo "ERROR# UNKNOWN ~/.zen/miam/$ZENSOURCE !!" && exit 1
ISSUER=$(cat ~/.zen/miam/$ZENSOURCE/msg_key)
[[ $ISSUER == "" ]] && echo "ERROR# NO ISSUER FOUND FOR miam $ZENSOURCE !!" && exit 1
echo "ISSUER = SSB Message $ISSUER !!"
fi
########################################################################
# CREATE ZEN TAG
########################################################################
# Produce unique 6 words diceware to give a name _tag.uid
AA=$(echo $($MY_PATH/tools/diceware.sh 6 | xargs) | sed s/\ /_/g ) # ZenTag Name = Diceware_6_words
AAH=$(echo -n ${AA} | sha256sum | cut -d ' ' -f 1) # ZenTag Name SHA256
# TODO ACTIVATE SWARM UNICITY CHECK
# Create Unique Zentag for all IPFS SWARM!
while [[ $(grep -Rwl "$AA" ~/.zen/ipfs_swarm/.12D3KooW*/TAG/*/_tag.uid) ]]; do
AA=$(echo $($MY_PATH/tools/diceware.sh 6 | xargs) | sed s/\ /_/g )
AAH=$(echo -n ${AA} | sha256sum | cut -d ' ' -f 1)
done
# BB key is a 4 word diceware printed on QRCode for Human use.
# SHA256 PASSWORD CHECK FOR ALLOWING WRITE ACTION
BB=$($MY_PATH/tools/diceware.sh 4 | xargs);
BBH=$(echo -n "$BB" | sha256sum | cut -d ' ' -f 1)
########################################################################
# INITIAL DATA STRUCTURE
########################################################################
mkdir -p ~/.zen/tag/${AAH}
# LOG
echo "
_____ _____ _ ____
|__ /___ _ _|_ _|/ \ / ___| __/\__
/ // _ \ °_ \| | / _ \| | _ \ /
/ /| __/ | | | |/ ___ \ |_| | /_ _\
/____\___|_| |_|_/_/ \_\____| \/
*******************************************************
| $AA | $ZEN Zen
*******************************************************
CREATING ~/.zen/tag/${AAH}/
"
# COPY ZENTAG TO LOCAL ~/.zen/tag
echo "$AA" > ~/.zen/tag/${AAH}/_tag.uid # Nom du ZenTAG
echo "$ZEN" > ~/.zen/tag/${AAH}/_tag.zen # Tag ZEN amount
echo "${IPFSNODEID}" > ~/.zen/tag/${AAH}/_ipfs.node.creator # NODE IPFS ID
echo "$ZENSOURCE" > ~/.zen/tag/${AAH}/_tag.zensource # ZENSOURCE
echo "$G1PUB" > ~/.zen/tag/${AAH}/_g1.node.creator # CREATOR IPFS NODE G1PUBKEY
echo "$ISSUER" > ~/.zen/tag/${AAH}/_tag.issuer # TX ISSUER G1PUBKEY OR SSB_MESSAGE ID
########################################################################
# Create IPNS publishing key ${AA}.key
# =====================================
# BB encrypted version for IRL use
# G1PUB for current G1SSB Node later access...
# Using sha256sum / openssl / natools.py (libsodium)
########################################################################
#timestamp=$(date -u +%s%N | cut -b1-13)
keyname="${ZENSOURCE}_${AA}.key"
[[ ! $(ipfs key list | grep -F ${AA}.key) ]] && J0=$(ipfs key gen -t rsa -s 2048 $keyname)
# WRITE BBH for QRCode BB verification
echo "$BBH" > ~/.zen/tag/${AAH}/_tag.BB.sha
# BB pgp symetric publishkey
keyfilename=$(ls ~/.ipfs/keystore/ | tail -n 1)
openssl aes-256-cbc -pbkdf2 -k "$BB" -salt -in ~/.ipfs/keystore/${keyfilename} -out ~/.zen/tag/${AAH}/_ipfs.publishkey.BB.aes
# GIVE IPFS CREATOR NODE ACCESS TO ZEN TAG
$MY_PATH/tools/natools.py encrypt -p $G1PUB -i ~/.ipfs/keystore/${keyfilename} -o ~/.zen/tag/${AAH}/_ipfs.publishkey.crypt
# LOG
echo "
${AA}.key
CYPHERING PUBLISH KEY
__ _
_/_/ __/|_ __/|_ | |
/ / | / | / / /
/ / /_ __| /_ __| / /
/ / |/ |/ _/_/
|_| /_/
KEY : $BB
BBH : $BBH
"
#######################################################################
# PASSENGER FILE is added to IPFS (then link is cyphered: NO!)
# https://beechat.network/how-beechats-encryption-works/
# SECURITY IS OBSOLETE AS WE ARE SHARING COPY WITH FRIENDS ;)
# ACTIVATE ZEN CONTRACT... 100 readers impresari
# LOVE ECONOMY IS FREE. EVERYBODY IS DONATING TO THE OTHERS NOW.
# INCOME != MONEY FORGE (Realtive Moey Theory applies HERE)
# CODE WILL BE CERTIFIED AS IPFS HASH.
if [[ -f $PASSENGER ]]; then
echo "
_ _ _ _ _ _ _ _ _
/ \ / \ / \ / \ / \ / \ / \ / \ / \
( p | a | s | s | e | n | g | e | r )
\_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/
"
# SEARCH FOR SAME SOURCE in IPFS... Not to make it twice...
CHECKSWARM=$(grep -Rwl "$ZENSOURCE" ~/.zen/ipfs_swarm/.12D3KooW*/TAG/*/_tag.zensource | tail -n 1 | cut -f 6 -d '/')
[[ $CHECKSWARM == "" ]] && CHECKSWARM=$(grep -Rwl "$ZENSOURCE" ~/.zen/ipfs/.$IPFSNODEID/TAG/*/_tag.zensource | tail -n 1 | cut -f 6 -d '/')
[[ -s ~/.zen/ipfs/.$IPFSNODEID/TAG/$ZENSOURCE/_tag.issuer ]] && ISSUERSWARM=$(cat ~/.zen/ipfs/.$IPFSNODEID/TAG/$ZENSOURCE/_tag.issuer)
[[ "$CHECKSWARM" != "" && "$ISSUERSWARM" == "$ISSUER" ]] && ipfs key rm $keyname && rm -Rf ~/.zen/tag/${AAH} \
&& echo "$ZENSOURCE ALREADY COPIED IN IPFS SWARM. $CHECKSWARM CANCEL" && exit 1
# NO DUPLICATE
echo "ADDING TO IPFS.................$(date)"
# ADD PASSENGER TO IPFS # COULD BE SUPER LONG !!!
IPASSENGER=$(ipfs add -q "$PASSENGER" -w | tail -n 1)
# TODO COMPARE DISK WRITING SPEED AND AVAILABLE SPACE TO CHOOSE BEST IN SWARM
echo "$(date) ............... YES /ipfs/$IPASSENGER"
echo "$IPASSENGER" > ~/.zen/tag/${AAH}/_passenger.ipfs
# GET FILE NAME
PASSENGERNAME=$(basename -- "$PASSENGER")
echo "$PASSENGERNAME" > ~/.zen/tag/${AAH}/_passenger.filename
# G1SSB NODE ACCESS
$MY_PATH/tools/natools.py encrypt -p $G1PUB -i ~/.zen/tag/${AAH}/_passenger.ipfs -o ~/.zen/tag/${AAH}/_passenger.ipfs.crypt
# BB OWNER ACCESS
openssl aes-256-cbc -pbkdf2 -k "$BB" -salt -in ~/.zen/tag/${AAH}/_passenger.ipfs -out ~/.zen/tag/${AAH}/_passenger.ipfs.BB.aes
# CLEAN CLEARTEXT IPFS link ? LATER MAYBE
rm ~/.zen/tag/${AAH}/_passenger.ipfs
# ZEN ECONOMY: ZEN READ payment + PARK rental
echo "$READ" > ~/.zen/tag/${AAH}/_passenger.read
echo "$PARK" > ~/.zen/tag/${AAH}/_passenger.park
echo "
_passenger.filename : $PASSENGERNAME
_passenger.ipfs : $IPASSENGER
http://127.0.0.1:8080/ipfs/$IPASSENGER/$PASSENGERNAME
# #### ## ##### ###### #####
# # # # # # # # # #
# # # # # # # ##### # #
# # # ###### # # # # #
# # # # # # # # # #
###### #### # # ##### ###### #####
##################################################################
# DECODE AND READ _passenger.ipfs with BB Passphrase
##################################################################
openssl aes-256-cbc -pbkdf2 -k \"$BB\" -d -salt -in ~/.zen/tag/${AAH}/_passenger.ipfs.BB.aes -out /tmp/_passenger.ipfs
ipfs get -o /tmp \"/ipfs/$IPASSENGER/$PASSENGERNAME\" && vlc \"/tmp/$PASSENGERNAME\" 2>/dev/null
"
cat > ~/.zen/tag/${AAH}/CONTRACT.sh <<EOF
#!/bin/bash
# Version 0.0
{
echo
'# THIS CODE INITIATE A CONTRACT
# Established by/with _ipfs.node.creator
# Gives Access SSB friend + IPFS Swarm allowance to read ipfs link
#In order io get access file : $PASSENGERNAME
#You must execute that code.
# ..... TODO ......
# It is bash or javascript or python
# or any language code with connection with g1, ipfs or ssb
# or Send $READ Zen to $G1PUB with $PASSENGERNAME in comment
# .... TODO ....
# Once contract is validated
# NodeN will be able to decrypt _passenger.ipfs.NodeN.crypt'
exit 0
}
EOF
fi
# END PASSENGER FILE
####################
###########################################################
# Copy TAG into IPFS + ipfs name publish -k ${AA}.key
###########################################################
# ZenTAG CHAIN
echo "0" > ~/.zen/tag/${AAH}/_chain.n # Tag modification number (0 first)
NANODATE=$(date -u +%s%N)
echo "$NANODATE" > ~/.zen/tag/${AAH}/_chain.nanodate # Nanodate notification
I0=$(ipfs add -qr ~/.zen/tag/${AAH} | tail -n 1)
echo "${I0}" > ~/.zen/tag/${AAH}/_chain
# Double IPFS add for _chain INIT
cp -f ~/.zen/tag/${AAH}/_chain ~/.zen/tag/${AAH}/_chain.prev
I=$(ipfs add -qr ~/.zen/tag/${AAH} | tail -n 1)
echo "${I}" > ~/.zen/tag/${AAH}/_chain
echo "
_|_ _.o._
(_| |(_||| | : $NANODATE
J0=${J0}
I0=${I0}
I=${I}
"
# IPNS name publish
J=$(ipfs name publish -k $keyname --quieter /ipfs/${I})
[[ ! ${J} ]] && echo "ipfs pin rm ${I}; ipfs pin rm ${I0}; rm -Rf ~/.zen/tag/${AAH}; ipfs key rm $keyname "
[[ ! ${J} ]] && J=${J0}
echo "${J}" > ~/.zen/tag/${AAH}/_ipns
# INDEXING ZenTag into SWARM
mkdir -p ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/
echo "${AA}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.uid
echo "${ZENSOURCE}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.zensource
echo "${ISSUER}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.issuer
echo "${J}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.ipns
if [[ "${PASSENGERNAME}" != "" ]]; then
echo "${PASSENGERNAME}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.passenger.filename
# COPY METADATA!! TODO : Please Extend filesource FOR new metadata types. torrent ?
extractor=$(cat "$METADATA" | jq -r '.extractor')
youtubeid=$(cat "$METADATA" | jq -r '.id')
fulltitle=$(cat "$METADATA" | jq -r '.fulltitle')
echo "$fulltitle" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.passenger.fulltitle
# description=$(cat "$METADATA" | jq -r '.description')
artist=$(cat "$METADATA" | jq -r '.artist')
album=$(cat "$METADATA" | jq -r '.album')
duration=$(cat "$METADATA" | jq -r '.duration')
upload_date=$(cat "$METADATA" | jq -r '.upload_date')
uploader_id=$(cat "$METADATA" | jq -r '.uploader_id')
extractor=$(cat "$METADATA" | jq -r '.extractor')
[[ ! -f ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.passenger.metadata.${extractor}.json ]] \
&& cp -f "${METADATA}" /tmp/
mv /tmp/${youtubeid}.info.json ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.passenger.metadata.${extractor}.json
fi
ls ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/
IWALLETS=$(ipfs add -rHq ~/.zen/ipfs | tail -n 1)
NODEIPNS=$(ipfs name publish --quieter /ipfs/$IWALLETS)
echo "
_ ____
(_)___ / __/____
/ / __ \/ /_/ ___/
/ / /_/ / __(__ )
/_/ .___/_/ /____/
/_/
ipfs ls /ipfs/${I}
ZenTAG : ipfs ls /ipns/${J}
NODE index : ipfs ls /ipns/$IPFSNODEID/.$IPFSNODEID/TAG/${ZENSOURCE}
"
########################################################################
# CREATE ZenTAG READ/WRITE QRCODES
### PRINTER IS HOOKED TO G1SSB NODE = G1DAB
########################################################################
echo "
_ _ _ _ _ _
/ \|_) / / \| \|_
\_X| \ \_\_/|_/|_ .................................
"
# READ QRCODE
qrencode -s 5 -o ~/.zen/tag/${AAH}/_QRCODE.read.png "RJ:${AA}#${J}"
# WRITE QRCODE
qrencode -s 5 -o ~/.zen/tag/${AAH}/_QRCODE.write.png "BJ:${BB}#${J}"
## TODO PROD REMOVE WRITE FILE
echo "QRCodes CREATED !!
See it :
xviewer ~/.zen/tag/${AAH}/_QRCODE.read.png &
xviewer ~/.zen/tag/${AAH}/_QRCODE.write.png &
${AA}
"
[[ $(which xviewer) ]] && xviewer ~/.zen/tag/${AAH}/_QRCODE.read.png &
[[ $(which xviewer) ]] && xviewer ~/.zen/tag/${AAH}/_QRCODE.write.png &
########################################################################
# SBOT PUBLISH
########################################################################
if [[ "${PASSENGERNAME}" != "" ]]; then
echo "$ISSUER"
msg="$(sbotc get '{"id":"'"$ISSUER"'"}')"
# echo "$msg" | jq #DEBUG
[[ $msg == "" ]] && echo "ERROR No SSB message for PASSENGER (timestamp: $tstamp)" && exit 1
msg_root=$(printf %s "$msg" | jq -r .value.content.root)
[[ $msg_root == "null" ]] && msg_root=$ISSUER
msg_branch=$(printf %s "$msg" | jq -r .value.content.branch)
[[ $msg_branch == "null" ]] && msg_branch=$ISSUER
# ATTACH ~/.zen/miam/$ZENSOURCE/ssb_thumb.jpg
name="ssb_thumb.jpg"
id="$(sbotc blobs.add < ~/.zen/miam/$ZENSOURCE/ssb_thumb.jpg)"
type="$(file -b --mime-type ~/.zen/miam/$ZENSOURCE/ssb_thumb.jpg)"
size="$(wc -c < ~/.zen/miam/$ZENSOURCE/ssb_thumb.jpg)"
export MESSAGE=$(cat << EOF
[![ssb_thumb.jpg](${id})](http://127.0.0.1:8080/ipfs/$IPASSENGER/$PASSENGERNAME)
:extractor:${extractor}
:fulltitle:${fulltitle}
:duration:${duration}
:uploader_id:${uploader_id}
[:pig:IPFS:pig_nose:](http://127.0.0.1:8080/ipfs/$IPASSENGER/$PASSENGERNAME)
---
**ZEN=$ZEN($READ/$PARK)**
[:flying_saucer:Essaim IPFS:sparkle:](http://127.0.0.1:8080/ipns/${J}/CONTRACT.sh)
[:sunglasses:**ZenTAG**:heart:DEBUG:pager:](http://127.0.0.1:8080/ipns/$IPFSNODEID/.$IPFSNODEID/TAG/${ZENSOURCE})
#zenbot
#astroport
EOF
)
echo "
__ _ ____
(_ |_)/ \|
__)|_)\_/| POST
root: $msg_root
branch: $msg_branch
"
echo "$MESSAGE"
INLINE=$(node -p "JSON.stringify(process.env.MESSAGE)")
### TODO find sboyc text COMMA PROBLEM... Cannot send beautiful md formated SSB messages !!!
# echo '{"type":"post", "branch": "'"$msg_branch"'", "root": "'"$msg_root"'", "text":"'"${INLINE}"'","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":"'"$size"'","type":"'"$type"'"},{"link":"#zenbot"},{"link":"#astroport"}]}'
sbotc publish '{"type":"post", "branch": "'"$msg_branch"'", "root": "'"$msg_root"'", "text":'${INLINE}',"mentions":[{"link":"'"$id"'","name":"'"$name"'","size":"'"$size"'","type":"'"$type"'"},{"link":"#zenbot"},{"link":"#astroport"}]}'
else
# This code is activated for qrcode (zentx terminal) @piniche
# REGULAR ZenTAG, Send QRCode to INPUT TX emitter
### TODO PRINT AND SEND BY SSB WITH WHEN PASSENGER TOO
# CAREFULL IN THAT CASE ISSUER IS A G1WALLET
ssbid="@$(echo $ISSUER | base58 -d | base64).ed25519"
file=~/.zen/tag/${AAH}/_QRCODE.read.png
#recps='@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519 @5XaVcAJ5DklwuuIkjGz4lwm2rOnMHHovhNg7BFFnyJ8=.ed25519'
recps="$ssbid"
name=${file##*/}
link="$(sblob encrypt "$file")"
type="$(file -b --mime-type "$file")"
id=${link%?unbox=*}
key=${link#*?unbox=}
size="$(sbotc -e blobs.size "$id")"
recps_array="[$(for recp in $recps; do printf '"%s"\n' "$recp"; done | paste -sd,)]"
sbotc private.publish '{"type":"post","text":"['"$name"']('"$link"')","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":'"$size"',"type":"'"$type"'","query":{"unbox":"'"$key"'"}}],"recps":'"$recps_array"'}' "$recps_array"
fi
########################################################################
exit 0