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

Add support for creating a fun image with text (including the amount of satoshis) #15

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
PKG := github.com/michael1011/lightningtip

GOBUILD := go build -v
# Enable image support via ImageMagicks MagickWand API. Requires MagickWand C API to be installed
# IMAGICK := -tags imagick
GOBUILD := go build -v ${IMAGICK}
GOINSTALL := go install -v
vegardengen marked this conversation as resolved.
Show resolved Hide resolved

GO_BIN := ${GOPATH}/bin
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,14 @@ That's it! The only two things you need to take care about is keeping the LND no

First of all make sure [Golang](https://golang.org/) and [Dep](https://github.com/golang/dep) are both correctly installed. Golang version 1.10 or newer is recommended.

If you want to return a picture, make sure you install ImageMagick MagickWand C API. For examnple (Ubuntu/Debian): sudo apt-get install libmagickwand-dev
vegardengen marked this conversation as resolved.
Show resolved Hide resolved

To enable it, edit Makefile and enable the line with IMAGICK : = -tags imagick
```
go get -d github.com/michael1011/lightningtip
cd $GOPATH/src/github.com/michael1011/lightningtip


make && make install
```

Expand Down
2 changes: 1 addition & 1 deletion backends/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ type Backend interface {
Connect() error

// The amount is denominated in satoshis and the expiry in seconds
GetInvoice(description string, amount int64, expiry int64) (invoice string, rHash string, err error)
GetInvoice(description string, amount int64, expiry int64) (invoice string, rHash string, picture string, err error)

InvoiceSettled(rHash string) (settled bool, err error)

Expand Down
11 changes: 7 additions & 4 deletions backends/lnd.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io/ioutil"

"github.com/lightningnetwork/lnd/lnrpc"
"github.com/michael1011/lightningtip/image"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/metadata"
Expand Down Expand Up @@ -64,7 +65,7 @@ func (lnd *LND) Connect() error {
}

// GetInvoice gets and invoice from a node
func (lnd *LND) GetInvoice(message string, amount int64, expiry int64) (invoice string, rHash string, err error) {
func (lnd *LND) GetInvoice(message string, amount int64, expiry int64) (invoice string, rHash string, picture string, err error) {
var response *lnrpc.AddInvoiceResponse

response, err = lnd.client.AddInvoice(lnd.ctx, &lnrpc.Invoice{
Expand All @@ -74,10 +75,10 @@ func (lnd *LND) GetInvoice(message string, amount int64, expiry int64) (invoice
})

if err != nil {
return "", "", err
return "", "", "", err
}

return response.PaymentRequest, hex.EncodeToString(response.RHash), err
return response.PaymentRequest, hex.EncodeToString(response.RHash), image.GetImagePath(hex.EncodeToString(response.RHash)), err
}

// InvoiceSettled checks if an invoice is settled by looking it up
Expand All @@ -93,7 +94,7 @@ func (lnd *LND) InvoiceSettled(rHash string) (settled bool, err error) {
if err != nil {
return false, err
}

_ = image.GenerateImage(hex.EncodeToString(invoice.RHash), invoice.AmtPaid)
return invoice.Settled, err
}

Expand Down Expand Up @@ -128,6 +129,8 @@ func (lnd *LND) SubscribeInvoices(publish PublishInvoiceSettled, rescan RescanPe
}

if invoice.Settled {
_ = image.GenerateImage(hex.EncodeToString(invoice.RHash), invoice.AmtPaid)
log.Debug("Payment request settled: ", invoice.PaymentRequest)
go publish(invoice.PaymentRequest)
}

Expand Down
37 changes: 36 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/jessevdk/go-flags"
"github.com/michael1011/lightningtip/backends"
"github.com/michael1011/lightningtip/database"
"github.com/michael1011/lightningtip/image"
"github.com/michael1011/lightningtip/notifications"
"github.com/michael1011/lightningtip/version"
"github.com/op/go-logging"
Expand Down Expand Up @@ -50,6 +51,20 @@ const (
defaultSTMPSSL = false
defaultSTMPUser = ""
defaultSTMPPassword = ""

defaultImageDir = ""
defaultImageURLDir = ""
defaultImageFile = ""
defaultImageTextBeforeAmt = "I paid a random dude"
defaultImageTextAfterAmt = "satoshis with Lightning Network"
defaultImageTextSecondLine = "but all I got was this picture of his dog."
defaultImageTextFirstLineX = 25
defaultImageTextFirstLineY = 165
defaultImageTextSecondLineX = 25
defaultImageTextSecondLineY = 365
defaultImageTextColor = "black"
defaultImageTextFont = "Verdana-Bold-Italic"
defaultImageTextSize = 150
)

type helpOptions struct {
Expand Down Expand Up @@ -78,7 +93,8 @@ type config struct {
ReconnectInterval int64 `long:"reconnectinterval" description:"Reconnect interval to LND in seconds"`
KeepAliveInterval int64 `long:"keepaliveinterval" description:"Send a dummy request to LND to prevent timeouts "`

LND *backends.LND `group:"LND" namespace:"lnd"`
LND *backends.LND `group:"LND" namespace:"lnd"`
Image *image.Image `group:"Image" namespace:"image"`

Mail *notifications.Mail `group:"Mail" namespace:"mail"`

Expand Down Expand Up @@ -117,6 +133,22 @@ func initConfig() {
MacaroonFile: getDefaultMacaroon(),
},

Image: &image.Image{
ImageDir: defaultImageDir,
ImageURLDir: defaultImageURLDir,
ImageFile: defaultImageFile,
ImageTextBeforeAmt: defaultImageTextBeforeAmt,
ImageTextAfterAmt: defaultImageTextAfterAmt,
ImageTextSecondLine: defaultImageTextSecondLine,
ImageTextFirstLineX: defaultImageTextFirstLineX,
ImageTextFirstLineY: defaultImageTextFirstLineY,
ImageTextSecondLineX: defaultImageTextSecondLineX,
ImageTextSecondLineY: defaultImageTextSecondLineY,
ImageTextColor: defaultImageTextColor,
ImageTextFont: defaultImageTextFont,
ImageTextSize: defaultImageTextSize,
},

Mail: &notifications.Mail{
Recipient: defaultRecipient,
Sender: defaultSender,
Expand All @@ -133,6 +165,9 @@ func initConfig() {
parser.Parse()

errFile := flags.IniParse(cfg.ConfigFile, &cfg)
if cfg.Image.ImageDir != "" {
image.SetImageCfg(*cfg.Image)
}

// If the user just wants to see the version initializing everything else is irrelevant
if cfg.Help.ShowVersion {
Expand Down
17 changes: 10 additions & 7 deletions frontend/lightningTip.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Edit this variable if you are not running LightningTip on the same domain or IP address as your webserver or not on port 8081
// Don't forget the "/" at the end!
var requestUrl = window.location.protocol + "//" + window.location.hostname + ":8081/";
var requestUrl = window.location.protocol + "//" + window.location.hostname + "/tiprest/";

// To prohibit multiple requests at the same time
var requestPending = false;
Expand Down Expand Up @@ -69,7 +69,7 @@ function getInvoice() {

console.log("Starting listening for invoice to get settled");

listenInvoiceSettled(json.RHash);
listenInvoiceSettled(json.RHash, json.Picture);

invoice = json.Invoice;

Expand Down Expand Up @@ -135,7 +135,7 @@ function getInvoice() {

}

function listenInvoiceSettled(rHash) {
function listenInvoiceSettled(rHash, picture) {
try {
var eventSrc = new EventSource(requestUrl + "eventsource");

Expand All @@ -145,7 +145,7 @@ function listenInvoiceSettled(rHash) {

eventSrc.close();

showThankYouScreen();
showThankYouScreen(picture);
}

};
Expand All @@ -170,7 +170,7 @@ function listenInvoiceSettled(rHash) {

clearInterval(interval);

showThankYouScreen();
showThankYouScreen(json.Picture);
}

}
Expand All @@ -190,11 +190,14 @@ function listenInvoiceSettled(rHash) {

}

function showThankYouScreen() {
function showThankYouScreen(picture) {
var wrapper = document.getElementById("lightningTip");

wrapper.innerHTML = "<p id=\"lightningTipLogo\">⚡</p>";
wrapper.innerHTML += "<a id='lightningTipFinished'>Thank you for your tip!</a>";
if (picture !== "") {
wrapper.innerHTML += "<a href=" + picture + "><img height=150 src=" + picture + "></a>";
}
}

function starTimer(duration, element) {
Expand Down Expand Up @@ -320,4 +323,4 @@ function divRestorePlaceholder(element) {
if (element.innerHTML === "<br>" || element.innerHTML === "<div><br></div>") {
element.innerHTML = "";
}
}
}
73 changes: 73 additions & 0 deletions image/imageimagick.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// +build imagick

package image

import (
"gopkg.in/gographics/imagick.v2/imagick"
"strconv"
)

// Image type for holding configuration
type Image struct {
ImageDir string `long:"imagedir" description:"Directory where generated images are placed"`
ImageURLDir string `long:"imageurldir" description:"The directory part of the URL where browsers will find the images"`
ImageFile string `long:"imagefile" description:"Full path to the image that will be served"`
ImageTextBeforeAmt string `long:"imagetextbeforeamt" description:"The text on the first line before the amount"`
ImageTextAfterAmt string `long:"imagetextafteramt" description:"The text on the first line after the amount"`
ImageTextSecondLine string `long:"imagetextsecondline" description:"The text on the second line"`
vegardengen marked this conversation as resolved.
Show resolved Hide resolved
ImageTextFirstLineX float64 `long:"imagetextfirstlinex" description:"X position for start of first line"`
ImageTextFirstLineY float64 `long:"imagetextfirstliney" description:"Y position for start of first line"`
ImageTextSecondLineX float64 `long:"imagetextsecondlinex" description:"X position for start of second line"`
ImageTextSecondLineY float64 `long:"imagetextsecondliney" description:"Y position for start of second line"`
ImageTextColor string `long:"imagetextcolor" description:"Text Color"`
ImageTextFont string `long:"imagetextfont" description:"Text Font"`
ImageTextSize float64 `long:"imagetextsize" description:"Text size (heigth in pixels)"`
}

// ImageCfg is the Configuration variable
var ImageCfg Image

// SetImageCfg - Set package configuration from outside package
func SetImageCfg(Cfg Image) {
ImageCfg = Cfg
}

// GetImagePath - Get the web path for the picture based on the payment hash
func GetImagePath(rHash string) string {
return (ImageCfg.ImageURLDir + "/" + rHash + ".jpg")
}

// GetImagePath - Get the web path for the picture based on the payment hash
func GenerateImage(rHash string, Amount int64) string {
imagick.Initialize()
defer imagick.Terminate()
var imerr error

mw := imagick.NewMagickWand()
dw := imagick.NewDrawingWand()
pw := imagick.NewPixelWand()

imerr = mw.ReadImage(ImageCfg.ImageFile)
if imerr != nil {
panic(imerr)
}
pw.SetColor(ImageCfg.ImageTextColor)
dw.SetFillColor(pw)
dw.SetFont(ImageCfg.ImageTextFont)
dw.SetFontSize(ImageCfg.ImageTextSize)
dw.SetStrokeColor(pw)
dw.Annotation(ImageCfg.ImageTextFirstLineX, ImageCfg.ImageTextFirstLineY,
ImageCfg.ImageTextBeforeAmt+" "+strconv.FormatInt(Amount, 10)+" "+ImageCfg.ImageTextAfterAmt)
dw.Annotation(ImageCfg.ImageTextSecondLineX, ImageCfg.ImageTextSecondLineY, ImageCfg.ImageTextSecondLine)
mw.DrawImage(dw)
mw.WriteImage(ImageCfg.ImageDir + "/" + rHash + ".jpg")
newWidth := uint(250)
newHeight := uint(mw.GetImageHeight() * newWidth / mw.GetImageWidth())
imerr = mw.ResizeImage(newWidth, newHeight, imagick.FILTER_LANCZOS, 1)
if imerr != nil {
panic(imerr)
}
mw.WriteImage("/var/www/lnd/tips/" + rHash + ".jpg_small.jpg")

return GetImagePath(rHash)
}
39 changes: 39 additions & 0 deletions image/imagenone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// +build !imagick
// Stubs to make code compile and behave same if it has image supportr or not.

package image

// Image type for holding configuration
type Image struct {
vegardengen marked this conversation as resolved.
Show resolved Hide resolved
ImageDir string `long:"imagedir" description:"Directory where generated images are placed"`
ImageURLDir string `long:"imageurldir" description:"The directory part of the URL where browsers will find the images"`
ImageFile string `long:"imagefile" description:"Full path to the image that will be served"`
ImageTextBeforeAmt string `long:"imagetextbeforeamt" description:"The text on the first line before the amount"`
ImageTextAfterAmt string `long:"imagetextafteramt" description:"The text on the first line after the amount"`
ImageTextSecondLine string `long:"imagetextsecondline" description:"The text on the second line"`
ImageTextFirstLineX float64 `long:"imagetextfirstlinex" description:"X position for start of first line"`
ImageTextFirstLineY float64 `long:"imagetextfirstliney" description:"Y position for start of first line"`
ImageTextSecondLineX float64 `long:"imagetextsecondlinex" description:"X position for start of second line"`
ImageTextSecondLineY float64 `long:"imagetextsecondliney" description:"Y position for start of second line"`
ImageTextColor string `long:"imagetextcolor" description:"Text Color"`
ImageTextFont string `long:"imagetextfont" description:"Text Font"`
ImageTextSize float64 `long:"imagetextsize" description:"Text size (heigth in pixels)"`
}

// ImageCfg is the Configuration variable
var ImageCfg Image

// SetImageCfg - Set package configuration from outside package
func SetImageCfg(Cfg Image) {
ImageCfg = Cfg
}

// GetImagePath - Get the web path for the picture based on the payment hash
func GetImagePath(rHash string) string {
return ("")
}

// GenerateImage - Generate a picture from hash and amount
func GenerateImage(rHash string, Amount int64) string {
return ("")
}
Binary file added kasan.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion lightningtip.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ type invoiceRequest struct {
type invoiceResponse struct {
Invoice string
RHash string
Picture string
Expiry int64
}

Expand Down Expand Up @@ -319,7 +320,7 @@ func getInvoiceHandler(writer http.ResponseWriter, request *http.Request) {

if err == nil {
if body.Amount != 0 {
invoice, paymentHash, err := backend.GetInvoice(body.Message, body.Amount, cfg.TipExpiry)
invoice, paymentHash, picture, err := backend.GetInvoice(body.Message, body.Amount, cfg.TipExpiry)

if err == nil {
logMessage := "Created invoice with amount of " + strconv.FormatInt(body.Amount, 10) + " satoshis"
Expand All @@ -346,6 +347,7 @@ func getInvoiceHandler(writer http.ResponseWriter, request *http.Request) {
writer.Write(marshalJSON(invoiceResponse{
Invoice: invoice,
RHash: paymentHash,
Picture: picture,
Expiry: cfg.TipExpiry,
}))

Expand Down
Loading