Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/extensions #2404

Open
wants to merge 3 commits into
base: v1.7
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@
- [Let's Encrypt - DNS-01](#lets-encrypt---dns-01)
- [Let's Encrypt - eMail Address](#lets-encrypt---email-address)
- [Let's Encrypt - Installation details](#lets-encrypt---installation-details)
- [How can I customize my RaspiBlitz or add other software?](#how-can-i-customize-my-raspiblitz-or-add-other-software)
- [How can I customize my RaspiBlitz or add other software?](#how-can-i-customize-my-raspiblitz-or-add-other-software)
- [Can I create my own extensions to distribute?](#can-i-create-my-own-extensions-to-distribute)
- [Versioning](#versioning)
- [GitHub Workflow](#github-workflow)
- [How do I find the IP address when running without a display?](#how-do-i-find-the-ip-address-when-running-without-a-display)
Expand Down Expand Up @@ -926,13 +927,22 @@ It is currently considered completely fine to leave this field empty and not pro
The `acme.sh` script is installed in `/home/admin/.acme.sh/` - the configuration and the certificates are stored on the
external hard disk in `/mnt/hdd/app-data/letsencrypt`.

### How can I customize my RaspiBlitz or add other software?
## How can I customize my RaspiBlitz or add other software?

The RaspiBlitz is your computer to experiment with. Feel free to add your own scripts, edit the system or install further software from the command line. Just keep in mind that after an update/recovery the RaspiBlitz starts with a fresh and clean operating system again. So all your editings and installs might be gone. To prevent this you should do the following:

- place your own scripts and data that should survive an update/recovery into the `/mnt/hdd/app-data` directory
- put all install commands & modification of the system into the script `/mnt/hdd/app-data/custom-installs.sh` which will be started automatically on a recovery/update.


### Can I create my own extensions to distribute?
The Extensions menu in Raspiblitz automatically populates with any extensions downloaded to `/home/admin/extensions/`
- To create your own extensions, your script must satisfy the following requirements:
- Near the top of the file should be the line `BLITZ_EXT_NAME="Your Extension's Description here"` This variable is used by Raspiblitz's extensions menu.
- The script filename must NOT begin with an underscore. If your script calls out to other files, it's beneficial to use an underscore prefix so it stays hidden from the menu. An example of a hidden script: `_recurringpayment.py`

See [blitz.recurringpayment.sh](home.admin/extensions/blitz.recurringpayment.sh) for an example of a proper extension.

## Versioning

* Major Updates: 1.0.0, 2.0.0, 3.0.0, ... are epic updates signaling that the software reached a new era.
Expand Down
4 changes: 4 additions & 0 deletions home.admin/00mainMenu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ OPTIONS+=(SERVICES "Additional Apps & Services")
OPTIONS+=(SYSTEM "Monitoring & Configuration")
OPTIONS+=(CONNECT "Connect Apps & Show Credentials")
OPTIONS+=(SUBSCRIBE "Manage Subscriptions")
OPTIONS+=(EXTENSIONS "Run Custom Scripts")
OPTIONS+=(PASSWORD "Change Passwords")

if [ "${touchscreen}" == "1" ]; then
Expand Down Expand Up @@ -302,6 +303,9 @@ case $CHOICE in
SUBSCRIBE)
/home/admin/config.scripts/blitz.subscriptions.py
;;
EXTENSIONS)
/home/admin/10extensions.sh
;;
SERVICES)
/home/admin/00settingsMenuServices.sh
;;
Expand Down
75 changes: 75 additions & 0 deletions home.admin/10extensions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Loop through extensions folder, adding scripts to the list
extensions_list=()
: ${DIALOG_YES=0}
: ${DIALOG_CANCEL=1}
: ${DIALOG_JUSTRUN=3}

HEIGHT=19
WIDTH=80

IFS=""
for FILE in /home/admin/extensions/*
do
fname=$(basename $FILE)

# Don't add files that begin with an underscore to script list (eg: _function.sh)
if [[ "$fname" == _* ]]; then
continue
fi

# Detect script name from head of file. Skip over scripts that don't have a name.
detected_name=$(head $FILE | grep BLITZ_EXT_NAME | awk -F "=" '{print $2}' | tr -d \")
if [[ ! -z "$detected_name" ]]; then
extensions_list+=( "$fname " )
extensions_list+=( "$detected_name " )
fi
done

# Display extensions list
CHOICE=$(dialog --clear \
--title "Extensions" \
--ok-label "Select" \
--cancel-label "Exit" \
--menu "Select a script to run" \
$HEIGHT $WIDTH $HEIGHT \
${extensions_list[@]} \
2>&1 >/dev/tty)

CHOICE=$(echo $CHOICE | tr -d "[:blank:]")
if [[ -z "$CHOICE" ]]; then
clear
echo "Extension selection cancelled."
exit 0
fi

# Display script to user, ask if they want to edit it.
dialog --title "Edit Script?" \
--extra-button \
--ok-label "Yes" \
--extra-label "No, just run it" \
--cr-wrap \
--yesno "$(cat /home/admin/extensions/$CHOICE)" \
$HEIGHT $WIDTH

return_value=$?

case $return_value in
$DIALOG_YES)
echo "$CHOICE"
/home/admin/config.scripts/blitz.setconf.sh /home/admin/extensions/$CHOICE "root"
dialog --title "Run script?" --yesno "Run $CHOICE?" 10 $WIDTH
return_value=$?
;;
$DIALOG_JUSTRUN)
return_value=$DIALOG_YES
;;
$DIALOG_CANCEL)
return_value=$DIALOG_CANCEL
;;
esac

# Don't run if script edit was cancelled.
if [[ "$return_value" -ne "$DIALOG_CANCEL" ]]; then
echo "running $CHOICE"
/home/admin/extensions/$CHOICE
fi
77 changes: 77 additions & 0 deletions home.admin/extensions/_recurringpayment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/python
import requests
import logging
import logging.handlers
import subprocess
import argparse
import sys
from time import sleep

# get an instance of the logger object this module will use
logging.basicConfig(level=logging.DEBUG, format="%(asctime)-15s %(levelname)-8s %(message)s")


def get_price_at(timestamp="now"):
requests_session = requests.Session()

currency = "usd"
if timestamp == "now":
price = requests_session.get(
"https://www.bitstamp.net/api/v2/ticker/btc{}".format(currency)
).json()["last"]
else:
price = requests_session.get(
"https://www.bitstamp.net/api/v2/ohlc/btc{}/?limit=1&step=86400&start={}".format(
currency, timestamp
)
).json()["data"]["ohlc"][0]["close"]
return price


def send_to_node(node, sats, message):
sats = str(int(sats))
logging.info("Sending {0} sats to {1}".format(sats, node))
cmd = ['lncli', 'sendpayment', '--dest='+node, '--amt='+sats, "--keysend"]

# Add keysend message, if available
if message is not None:
logging.warning("Keysend message not yet supported")
# cmd.append("-n")
# cmd.append(message)

p = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if p.returncode == 0:
logging.info("Successfully sent {0} sats".format(sats))
return True
else:
logging.info(p.stdout)
logging.error(p.stderr)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Parse some args')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--sats', type=int, help="Sends AMOUNT sats")
group.add_argument('--usd', type=float, help="Sends AMOUNT dollars (Fractions allowed)")
parser.add_argument('--node_id', required=True, help="Node address to send to")
parser.add_argument('--message', help="Optional, send a message to node")

args = parser.parse_args()

for send_attempt in range(0, 10):
logging.info("Attempting to send.. attempt {0}/{1}".format(send_attempt+1, 10))
try:
# Calculate price in dollars
if args.usd is not None:
price = get_price_at()
args.sats = args.usd * 100000000 / float(price) # Convert to sats

success = send_to_node(args.node_id, args.sats, args.message)
if success:
break

except Exception as e:
logging.error(e)
logging.error("Failed to hit bitstamp api")

sleep(60 * send_attempt+1)
110 changes: 110 additions & 0 deletions home.admin/extensions/blitz.recurringpayment.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# The variable below is pulled in by Raspiblitz and makes this script a "supported" extension.
BLITZ_EXT_NAME="Set up a recurring payment"
HEIGHT=19
WIDTH=120

# Denomination menu options
OPTIONS=(SATS "Send sats" \
USD "Send sats denominated in dollars")

# Crontab (send frequency) menu options
FREQUENCY_OPTIONS=(DAILY "Send sats every day" \
WEEKLY "Send sats once a week, every Sunday"
MONTHLY "Send sats once a month, on the 1st"
YEARLY "Send sats once a year, on January 1st")


# Detect if the user has cancelled running the script at any point in time.
function cancel_check(){
if [[ -z "$1" ]]; then
echo "Cancelled"
exit 0
fi
}

# User select sats or dollars to denominate in.
DENOMINATION=$(dialog --clear \
--backtitle "Recurring Payments" \
--title "Recurring Keysend" \
--ok-label "Select" \
--cancel-label "Exit" \
--menu "Automatically send some sats to another node on a daily/weekly/monthly basis." \
$HEIGHT $WIDTH $HEIGHT \
"${OPTIONS[@]}" \
2>&1 >/dev/tty)

cancel_check $DENOMINATION

# After choosing denomination, ask user how many dollars or sats to send
case $DENOMINATION in
SATS)
AMOUNT=$(dialog --backtitle "Recurring Payments" \
--title "Choose the amount" \
--inputbox "Enter the amount to send in $DENOMINATION" \
10 60 100 2>&1 >/dev/tty)
;;
USD)
AMOUNT=$(dialog --backtitle "Recurring Payments" \
--title "Choose the amount" \
--inputbox "Enter the amount to send in $DENOMINATION" \
10 60 0.50 2>&1 >/dev/tty)
;;
esac

cancel_check $AMOUNT

# Ask user for node ID to send to.
NODE_ID=$(whiptail --backtitle "Recurring Payments" \
--title "Node Address" \
--inputbox "Enter the 66-character public key of the node you'd like to send to.
\n(e.g: 02c3afc714b2ea1d4ec35e5d4c6a... )" \
10 60 2>&1 >/dev/tty)

cancel_check $NODE_ID

# Ask user how frequently they'd like to send sats
FREQUENCY=$(dialog --clear \
--backtitle "Recurring Payments" \
--title "Select Frequency" \
--ok-label "Select" \
--cancel-label "Exit" \
--menu "How often do you want to send sats to this node?" \
$HEIGHT $WIDTH $HEIGHT \
"${FREQUENCY_OPTIONS[@]}" \
2>&1 >/dev/tty)

case $FREQUENCY in
DAILY)
cron_prefix="0 0 * * *"
;;
WEEKLY)
cron_prefix="0 0 * * 0"
;;
MONTHLY)
cron_prefix="0 0 1 * *"
;;
YEARLY)
cron_prefix="0 0 1 1 *"
;;
esac

cancel_check $cron_prefix

# Generate a keysend script
short_node_id=$(echo $NODE_ID | cut -c 1-7)
script_name="/home/admin/extensions/_${short_node_id}_keysend.sh"
denomination=$(echo $DENOMINATION | tr '[:upper:]' '[:lower:]')
echo -n "/usr/bin/python /home/admin/extensions/_recurringpayment.py " \
"--$denomination $AMOUNT " \
"--node_id $NODE_ID " \
> $script_name
chmod +x $script_name

# Display crontab line
path='$HOME/.profile; PATH=$PATH:/usr/local/bin'
command="$cron_prefix . $path $script_name"
clear
printf "No sats are being sent yet! Type 'crontab -e' to edit your crontab, then paste in the following line:\n"
echo "$command"
printf "\nPress enter when done."
read