mirror of
https://github.com/ChuckNorrison/LightningTipBot.git
synced 2026-08-13 12:33:14 +02:00
* satdress rebase * check config * satdress rebase * check config * check for invoices * return error * Update inline photo v5 (#332) * update url * round logo * resize Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com> * new logo (#333) Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com> * help (#334) Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com> * add file (#335) Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com> * markdown (#336) Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com> * satdress rebase * check config * satdress rebase * check for invoices * return error * satdress works * add lnbits * timeout * error checking * small lnd * errors * button fix Co-authored-by: LightningTipBot <88730856+LightningTipBot@users.noreply.github.com>
481 lines
18 KiB
Go
481 lines
18 KiB
Go
package telegram
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/LightningTipBot/LightningTipBot/internal/telegram/intercept"
|
|
|
|
"github.com/LightningTipBot/LightningTipBot/internal"
|
|
"github.com/LightningTipBot/LightningTipBot/internal/errors"
|
|
"github.com/LightningTipBot/LightningTipBot/internal/i18n"
|
|
"github.com/LightningTipBot/LightningTipBot/internal/lnbits"
|
|
"github.com/LightningTipBot/LightningTipBot/internal/runtime"
|
|
"github.com/LightningTipBot/LightningTipBot/internal/runtime/mutex"
|
|
"github.com/LightningTipBot/LightningTipBot/internal/storage"
|
|
"github.com/LightningTipBot/LightningTipBot/internal/str"
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/skip2/go-qrcode"
|
|
tb "gopkg.in/lightningtipbot/telebot.v3"
|
|
)
|
|
|
|
type Ticket struct {
|
|
Price int64 `json:"price"`
|
|
Memo string `json:"memo"`
|
|
Creator *lnbits.User `gorm:"embedded;embeddedPrefix:creator_"`
|
|
Cut int64 `json:"cut"` // Percent to cut from ticket price
|
|
BaseFee int64 `json:"base_fee"`
|
|
CutCheap int64 `json:"cut_cheap"` // Percent to cut from ticket price
|
|
BaseFeeCheap int64 `json:"base_fee_cheap"`
|
|
}
|
|
type Group struct {
|
|
Name string `json:"name"`
|
|
Title string `json:"title"`
|
|
ID int64 `json:"id" gorm:"primaryKey"`
|
|
Owner *tb.User `gorm:"embedded;embeddedPrefix:owner_"`
|
|
// Chat *tb.Chat `gorm:"embedded;embeddedPrefix:chat_"`
|
|
Ticket *Ticket `gorm:"embedded;embeddedPrefix:ticket_"`
|
|
}
|
|
type CreateChatInviteLink struct {
|
|
ChatID int64 `json:"chat_id"`
|
|
Name string `json:"name"`
|
|
ExpiryDate int `json:"expiry_date"`
|
|
MemberLimit int `json:"member_limit"`
|
|
CreatesJoinRequest bool `json:"creates_join_request"`
|
|
}
|
|
type Creator struct {
|
|
ID int64 `json:"id"`
|
|
IsBot bool `json:"is_bot"`
|
|
Firstname string `json:"first_name"`
|
|
Username string `json:"username"`
|
|
}
|
|
type Result struct {
|
|
InviteLink string `json:"invite_link"`
|
|
Name string `json:"name"`
|
|
Creator Creator `json:"creator"`
|
|
CreatesJoinRequest bool `json:"creates_join_request"`
|
|
IsPrimary bool `json:"is_primary"`
|
|
IsRevoked bool `json:"is_revoked"`
|
|
}
|
|
type ChatInviteLink struct {
|
|
Ok bool `json:"ok"`
|
|
Result Result `json:"result"`
|
|
}
|
|
|
|
type TicketEvent struct {
|
|
*storage.Base
|
|
*InvoiceEvent
|
|
Group *Group `gorm:"embedded;embeddedPrefix:group_"`
|
|
}
|
|
|
|
func (ticketEvent TicketEvent) Type() EventType {
|
|
return EventTypeTicketInvoice
|
|
}
|
|
func (ticketEvent TicketEvent) Key() string {
|
|
return ticketEvent.Base.ID
|
|
}
|
|
|
|
var (
|
|
ticketPayConfirmationMenu = &tb.ReplyMarkup{ResizeKeyboard: true}
|
|
btnPayTicket = paymentConfirmationMenu.Data("✅ Pay", "pay_ticket")
|
|
)
|
|
|
|
var (
|
|
groupInvoiceMemo = "🎟 Ticket for group %s"
|
|
)
|
|
|
|
// groupHandler is called if the /group <cmd> command is invoked. It then decides with other
|
|
// handler to call depending on the <cmd> passed.
|
|
func (bot TipBot) groupHandler(ctx intercept.Context) (intercept.Context, error) {
|
|
m := ctx.Message()
|
|
splits := strings.Split(m.Text, " ")
|
|
user := LoadUser(ctx)
|
|
if len(splits) == 1 {
|
|
if ctx.Message().Private() {
|
|
bot.trySendMessage(ctx.Message().Chat, fmt.Sprintf(Translate(ctx, "groupHelpMessage"), GetUserStr(bot.Telegram.Me), GetUserStr(bot.Telegram.Me)))
|
|
} else {
|
|
if bot.isOwner(ctx.Message().Chat, user.Telegram) {
|
|
bot.trySendMessage(ctx.Message().Chat, fmt.Sprintf(Translate(ctx, "commandPrivateMessage"), GetUserStr(bot.Telegram.Me)))
|
|
}
|
|
}
|
|
return ctx, nil
|
|
} else if len(splits) > 1 {
|
|
if splits[1] == "join" {
|
|
return bot.groupRequestJoinHandler(ctx)
|
|
}
|
|
if splits[1] == "add" {
|
|
return bot.addGroupHandler(ctx)
|
|
}
|
|
if splits[1] == "remove" {
|
|
// todo -- implement this
|
|
// return bot.addGroupHandler(ctx, m)
|
|
}
|
|
}
|
|
return ctx, nil
|
|
}
|
|
|
|
// groupRequestJoinHandler sends a payment request to the user who wants to join a group
|
|
func (bot TipBot) groupRequestJoinHandler(ctx intercept.Context) (intercept.Context, error) {
|
|
user := LoadUser(ctx)
|
|
// // reply only in private message
|
|
if ctx.Chat().Type != tb.ChatPrivate {
|
|
return ctx, fmt.Errorf("not private chat")
|
|
}
|
|
splits := strings.Split(ctx.Message().Text, " ")
|
|
// if the command was /group join
|
|
splitIdx := 1
|
|
// we also have the simpler command /join that can be used
|
|
// also by users who don't have an account with the bot yet
|
|
if splits[0] == "/join" {
|
|
splitIdx = 0
|
|
}
|
|
if len(splits) != splitIdx+2 || len(ctx.Message().Text) > 100 {
|
|
bot.trySendMessage(ctx.Message().Chat, Translate(ctx, "groupJoinGroupHelpMessage"))
|
|
return ctx, nil
|
|
}
|
|
groupName := strings.ToLower(splits[splitIdx+1])
|
|
|
|
group := &Group{}
|
|
tx := bot.DB.Groups.Where("name = ? COLLATE NOCASE", groupName).First(group)
|
|
if tx.Error != nil {
|
|
bot.trySendMessage(ctx.Message().Chat, Translate(ctx, "groupNotFoundMessage"))
|
|
return ctx, fmt.Errorf("group not found")
|
|
}
|
|
|
|
// create tickets
|
|
id := fmt.Sprintf("ticket:%d", group.ID)
|
|
invoiceEvent := &InvoiceEvent{
|
|
Base: storage.New(storage.ID(id)),
|
|
User: group.Ticket.Creator,
|
|
LanguageCode: ctx.Value("publicLanguageCode").(string),
|
|
Payer: user,
|
|
Chat: &tb.Chat{ID: group.ID},
|
|
CallbackData: id,
|
|
}
|
|
ticketEvent := &TicketEvent{
|
|
Base: storage.New(storage.ID(id)),
|
|
InvoiceEvent: invoiceEvent,
|
|
Group: group,
|
|
}
|
|
// if no price is set, then we don't need to pay
|
|
if group.Ticket.Price == 0 {
|
|
// save ticketevent for later
|
|
runtime.IgnoreError(ticketEvent.Set(ticketEvent, bot.Bunt))
|
|
bot.groupGetInviteLinkHandler(invoiceEvent)
|
|
return ctx, nil
|
|
}
|
|
|
|
// create an invoice
|
|
memo := fmt.Sprintf(groupInvoiceMemo, groupName)
|
|
var err error
|
|
invoiceEvent, err = bot.createGroupTicketInvoice(ctx, user, group, memo, InvoiceCallbackGroupTicket, id)
|
|
if err != nil {
|
|
errmsg := fmt.Sprintf("[/invoice] Could not create an invoice: %s", err.Error())
|
|
bot.trySendMessage(user.Telegram, Translate(ctx, "errorTryLaterMessage"))
|
|
log.Errorln(errmsg)
|
|
return ctx, err
|
|
}
|
|
|
|
ticketEvent.InvoiceEvent = invoiceEvent
|
|
// save ticketevent for later
|
|
defer ticketEvent.Set(ticketEvent, bot.Bunt)
|
|
|
|
// // if the user has enough balance, we send him a payment button
|
|
balance, err := bot.GetUserBalance(user)
|
|
if err != nil {
|
|
errmsg := fmt.Sprintf("[/group] Error: Could not get user balance: %s", err.Error())
|
|
log.Errorln(errmsg)
|
|
bot.trySendMessage(ctx.Message().Sender, Translate(ctx, "errorTryLaterMessage"))
|
|
return ctx, errors.New(errors.GetBalanceError, err)
|
|
}
|
|
if balance >= group.Ticket.Price {
|
|
return bot.groupSendPayButtonHandler(ctx, *ticketEvent)
|
|
}
|
|
|
|
// otherwise we send a payment request
|
|
|
|
// create qr code
|
|
qr, err := qrcode.Encode(invoiceEvent.PaymentRequest, qrcode.Medium, 256)
|
|
if err != nil {
|
|
errmsg := fmt.Sprintf("[/invoice] Failed to create QR code for invoice: %s", err.Error())
|
|
bot.trySendMessage(user.Telegram, Translate(ctx, "errorTryLaterMessage"))
|
|
log.Errorln(errmsg)
|
|
return ctx, err
|
|
}
|
|
ticketEvent.Message = bot.trySendMessage(ctx.Message().Sender, &tb.Photo{File: tb.File{FileReader: bytes.NewReader(qr)}, Caption: fmt.Sprintf("`%s`", invoiceEvent.PaymentRequest)})
|
|
bot.trySendMessage(ctx.Message().Sender, fmt.Sprintf(Translate(ctx, "groupPayInvoiceMessage"), groupName))
|
|
return ctx, nil
|
|
}
|
|
|
|
// groupSendPayButtonHandler is invoked if the user has enough balance so a message with a
|
|
// pay button is sent to the user.
|
|
func (bot *TipBot) groupSendPayButtonHandler(ctx intercept.Context, ticket TicketEvent) (intercept.Context, error) {
|
|
// object that holds all information about the send payment
|
|
// // // create inline buttons
|
|
btnPayTicket := ticketPayConfirmationMenu.Data(Translate(ctx, "payButtonMessage"), "pay_ticket", ticket.Base.ID)
|
|
ticketPayConfirmationMenu.Inline(
|
|
ticketPayConfirmationMenu.Row(
|
|
btnPayTicket),
|
|
)
|
|
confirmText := fmt.Sprintf(Translate(ctx, "confirmPayInvoiceMessage"), ticket.Group.Ticket.Price)
|
|
if len(ticket.Group.Ticket.Memo) > 0 {
|
|
confirmText = confirmText + fmt.Sprintf(Translate(ctx, "confirmPayAppendMemo"), str.MarkdownEscape(ticket.Group.Ticket.Memo))
|
|
}
|
|
bot.trySendMessageEditable(ctx.Message().Chat, confirmText, ticketPayConfirmationMenu)
|
|
return ctx, nil
|
|
}
|
|
|
|
// groupConfirmPayButtonHandler is invoked if th user clicks the pay button.
|
|
func (bot *TipBot) groupConfirmPayButtonHandler(ctx intercept.Context) (intercept.Context, error) {
|
|
c := ctx.Callback()
|
|
tx := &TicketEvent{Base: storage.New(storage.ID(c.Data))}
|
|
mutex.LockWithContext(ctx, tx.ID)
|
|
defer mutex.UnlockWithContext(ctx, tx.ID)
|
|
sn, err := tx.Get(tx, bot.Bunt)
|
|
// immediatelly set intransaction to block duplicate calls
|
|
if err != nil {
|
|
log.Errorf("[groupConfirmPayButtonHandler] %s", err.Error())
|
|
return ctx, err
|
|
}
|
|
ticketEvent := sn.(*TicketEvent)
|
|
|
|
// onnly the correct user can press
|
|
if ticketEvent.Payer.Telegram.ID != c.Sender.ID {
|
|
return ctx, errors.Create(errors.UnknownError)
|
|
}
|
|
if !ticketEvent.Active {
|
|
log.Errorf("[confirmPayHandler] send not active anymore")
|
|
bot.tryEditMessage(c, i18n.Translate(ticketEvent.LanguageCode, "errorTryLaterMessage"), &tb.ReplyMarkup{})
|
|
bot.tryDeleteMessage(c)
|
|
return ctx, errors.Create(errors.NotActiveError)
|
|
}
|
|
defer ticketEvent.Set(ticketEvent, bot.Bunt)
|
|
|
|
user := LoadUser(ctx)
|
|
if user.Wallet == nil {
|
|
bot.tryDeleteMessage(c)
|
|
return ctx, errors.Create(errors.UserNoWalletError)
|
|
}
|
|
|
|
log.Infof("[/pay] Attempting %s's invoice %s (%d sat)", GetUserStr(user.Telegram), ticketEvent.ID, ticketEvent.Group.Ticket.Price)
|
|
// // pay invoice
|
|
_, err = user.Wallet.Pay(lnbits.PaymentParams{Out: true, Bolt11: ticketEvent.Invoice.PaymentRequest}, bot.Client)
|
|
if err != nil {
|
|
errmsg := fmt.Sprintf("[/pay] Could not pay invoice of %s: %s", GetUserStr(user.Telegram), err)
|
|
err = fmt.Errorf(i18n.Translate(ticketEvent.LanguageCode, "invoiceUndefinedErrorMessage"))
|
|
bot.tryEditMessage(c, fmt.Sprintf(i18n.Translate(ticketEvent.LanguageCode, "invoicePaymentFailedMessage"), err.Error()), &tb.ReplyMarkup{})
|
|
log.Errorln(errmsg)
|
|
return ctx, err
|
|
}
|
|
|
|
// update the message and remove the button
|
|
bot.tryEditMessage(c, i18n.Translate(ticketEvent.LanguageCode, "invoicePaidMessage"), &tb.ReplyMarkup{})
|
|
return ctx, nil
|
|
}
|
|
|
|
// groupGetInviteLinkHandler is called when the invoice is paid and sends a one-time group invite link to the payer
|
|
func (bot *TipBot) groupGetInviteLinkHandler(event Event) {
|
|
invoiceEvent := event.(*InvoiceEvent)
|
|
// take a cut
|
|
// amount_bot := int64(ticketEvent.Group.Ticket.Price * int64(ticketEvent.Group.Ticket.Cut) / 100)
|
|
|
|
log.Infof(invoiceEvent.CallbackData)
|
|
ticketEvent := &TicketEvent{Base: storage.New(storage.ID(invoiceEvent.CallbackData))}
|
|
err := bot.Bunt.Get(ticketEvent)
|
|
if err != nil {
|
|
log.Errorf("[groupGetInviteLinkHandler] %s", err.Error())
|
|
return
|
|
}
|
|
|
|
log.Infof("[groupGetInviteLinkHandler] group: %d", ticketEvent.Chat.ID)
|
|
params := map[string]interface {
|
|
}{
|
|
"chat_id": ticketEvent.Group.ID, // must be the chat ID of the group
|
|
"name": fmt.Sprintf("%s link for %s", GetUserStr(bot.Telegram.Me), GetUserStr(ticketEvent.Payer.Telegram)), // the name of the invite link
|
|
"member_limit": 1, // only one user can join with this link
|
|
// "expire_date": time.Now().AddDate(0, 0, 1), // expiry date of the invite link, add one day
|
|
// "creates_join_request": false, // True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified
|
|
}
|
|
data, err := bot.Telegram.Raw("createChatInviteLink", params)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var resp ChatInviteLink
|
|
if err := json.Unmarshal(data, &resp); err != nil {
|
|
return
|
|
}
|
|
|
|
if ticketEvent.Message != nil {
|
|
bot.tryDeleteMessage(ticketEvent.Message)
|
|
// do balance check for keyboard update
|
|
_, err = bot.GetUserBalance(ticketEvent.Payer)
|
|
if err != nil {
|
|
errmsg := fmt.Sprintf("could not get balance of user %s", GetUserStr(ticketEvent.Payer.Telegram))
|
|
log.Errorln(errmsg)
|
|
}
|
|
bot.trySendMessage(ticketEvent.Payer.Telegram, i18n.Translate(ticketEvent.LanguageCode, "invoicePaidText"))
|
|
}
|
|
|
|
// send confirmation text with the ticket to the user
|
|
bot.trySendMessage(ticketEvent.Payer.Telegram, fmt.Sprintf(i18n.Translate(ticketEvent.LanguageCode, "groupClickToJoinMessage"), resp.Result.InviteLink, ticketEvent.Group.Title))
|
|
|
|
// send a notification to the group that sold the ticket
|
|
bot.trySendMessage(&tb.Chat{ID: ticketEvent.Group.ID}, fmt.Sprintf(i18n.Translate(ticketEvent.LanguageCode, "groupTicketIssuedGroupMessage"), GetUserStr(ticketEvent.Payer.Telegram)))
|
|
|
|
// take a commission
|
|
ticketSat := ticketEvent.Group.Ticket.Price
|
|
if ticketEvent.Group.Ticket.Price > 20 {
|
|
me, err := GetUser(bot.Telegram.Me, *bot)
|
|
if err != nil {
|
|
log.Errorf("[groupGetInviteLinkHandler] Could not get bot user from DB: %s", err.Error())
|
|
return
|
|
}
|
|
// 2% cut + 100 sat base fee
|
|
commissionSat := ticketEvent.Group.Ticket.Price*ticketEvent.Group.Ticket.Cut/100 + ticketEvent.Group.Ticket.BaseFee
|
|
if ticketEvent.Group.Ticket.Price <= 1000 {
|
|
// if < 1000, then 10% cut + 10 sat base fee
|
|
commissionSat = ticketEvent.Group.Ticket.Price*ticketEvent.Group.Ticket.CutCheap/100 + ticketEvent.Group.Ticket.BaseFeeCheap
|
|
}
|
|
|
|
ticketSat = ticketEvent.Group.Ticket.Price - commissionSat
|
|
invoice, err := me.Wallet.Invoice(
|
|
lnbits.InvoiceParams{
|
|
Out: false,
|
|
Amount: commissionSat,
|
|
Memo: "🎟 Ticket commission for group " + ticketEvent.Group.Title,
|
|
Webhook: internal.Configuration.Lnbits.WebhookServer},
|
|
bot.Client)
|
|
if err != nil {
|
|
errmsg := fmt.Sprintf("[/invoice] Could not create an invoice: %s", err.Error())
|
|
log.Errorln(errmsg)
|
|
return
|
|
}
|
|
_, err = ticketEvent.User.Wallet.Pay(lnbits.PaymentParams{Out: true, Bolt11: invoice.PaymentRequest}, bot.Client)
|
|
if err != nil {
|
|
errmsg := fmt.Sprintf("[groupGetInviteLinkHandler] Could not pay commission of %s: %s", GetUserStr(ticketEvent.User.Telegram), err)
|
|
log.Errorln(errmsg)
|
|
return
|
|
}
|
|
// do balance check for keyboard update
|
|
_, err = bot.GetUserBalance(ticketEvent.User)
|
|
if err != nil {
|
|
errmsg := fmt.Sprintf("could not get balance of user %s", GetUserStr(ticketEvent.Payer.Telegram))
|
|
log.Errorln(errmsg)
|
|
}
|
|
bot.trySendMessage(ticketEvent.User.Telegram, fmt.Sprintf(i18n.Translate(ticketEvent.LanguageCode, "groupReceiveTicketInvoiceCommission"), ticketSat, commissionSat, ticketEvent.Group.Title, GetUserStr(ticketEvent.Payer.Telegram)))
|
|
} else {
|
|
bot.trySendMessage(ticketEvent.User.Telegram, fmt.Sprintf(i18n.Translate(ticketEvent.LanguageCode, "groupReceiveTicketInvoice"), ticketSat, ticketEvent.Group.Title, GetUserStr(ticketEvent.Payer.Telegram)))
|
|
}
|
|
}
|
|
|
|
// addGroupHandler is invoked if the user calls the "/group add" command
|
|
func (bot TipBot) addGroupHandler(ctx intercept.Context) (intercept.Context, error) {
|
|
m := ctx.Message()
|
|
if m.Chat.Type == tb.ChatPrivate {
|
|
bot.trySendMessage(m.Chat, Translate(ctx, "groupAddGroupHelpMessage"))
|
|
return ctx, fmt.Errorf("not in group")
|
|
}
|
|
// parse command "/group add <grou_name> [<amount>]"
|
|
splits := strings.Split(m.Text, " ")
|
|
if len(splits) < 3 || len(m.Text) > 100 {
|
|
bot.trySendMessage(m.Chat, Translate(ctx, "groupAddGroupHelpMessage"))
|
|
return ctx, nil
|
|
}
|
|
groupName := strings.ToLower(splits[2])
|
|
|
|
user := LoadUser(ctx)
|
|
// check if the user is the owner of the group
|
|
if !bot.isOwner(m.Chat, user.Telegram) {
|
|
return ctx, fmt.Errorf("not owner")
|
|
}
|
|
|
|
if !bot.isAdminAndCanInviteUsers(m.Chat, bot.Telegram.Me) {
|
|
bot.trySendMessage(m.Chat, Translate(ctx, "groupBotIsNotAdminMessage"))
|
|
return ctx, fmt.Errorf("bot is not admin")
|
|
}
|
|
|
|
// check if the group with this name is already in db
|
|
// only if a group with this name is owned by this user, it can be overwritten
|
|
group := &Group{}
|
|
tx := bot.DB.Groups.Where("name = ? COLLATE NOCASE", groupName).First(group)
|
|
if tx.Error == nil {
|
|
// if it is already added, check if this user is the admin
|
|
if user.Telegram.ID != group.Owner.ID || group.ID != m.Chat.ID {
|
|
bot.trySendMessage(m.Chat, Translate(ctx, "groupNameExists"))
|
|
return ctx, fmt.Errorf("not owner")
|
|
}
|
|
}
|
|
|
|
amount := int64(0) // default amount is zero
|
|
if amount_str, err := getArgumentFromCommand(m.Text, 3); err == nil {
|
|
amount, err = getAmount(amount_str)
|
|
if err != nil {
|
|
bot.trySendMessage(m.Sender, Translate(ctx, "lnurlInvalidAmountMessage"))
|
|
return ctx, err
|
|
}
|
|
}
|
|
|
|
ticket := &Ticket{
|
|
Price: amount,
|
|
Memo: "Ticket for group " + groupName,
|
|
Creator: user,
|
|
Cut: 2,
|
|
BaseFee: 100,
|
|
CutCheap: 10,
|
|
BaseFeeCheap: 10,
|
|
}
|
|
|
|
group = &Group{
|
|
Name: groupName,
|
|
Title: m.Chat.Title,
|
|
ID: m.Chat.ID,
|
|
Owner: user.Telegram,
|
|
Ticket: ticket,
|
|
}
|
|
|
|
bot.DB.Groups.Save(group)
|
|
log.Infof("[group] Ticket of %d sat added to group %s.", group.Ticket.Price, group.Name)
|
|
bot.trySendMessage(m.Chat, fmt.Sprintf(Translate(ctx, "groupAddedMessage"), str.MarkdownEscape(m.Chat.Title), group.Name, group.Ticket.Price, GetUserStrMd(bot.Telegram.Me), group.Name))
|
|
|
|
return ctx, nil
|
|
}
|
|
|
|
// createGroupTicketInvoice produces an invoice for the group ticket with a
|
|
// callback that then calls groupGetInviteLinkHandler upton payment
|
|
func (bot *TipBot) createGroupTicketInvoice(ctx context.Context, payer *lnbits.User, group *Group, memo string, callback int, callbackData string) (*InvoiceEvent, error) {
|
|
invoice, err := group.Ticket.Creator.Wallet.Invoice(
|
|
lnbits.InvoiceParams{
|
|
Out: false,
|
|
Amount: group.Ticket.Price,
|
|
Memo: memo,
|
|
Webhook: internal.Configuration.Lnbits.WebhookServer},
|
|
bot.Client)
|
|
if err != nil {
|
|
errmsg := fmt.Sprintf("[/invoice] Could not create an invoice: %s", err.Error())
|
|
log.Errorln(errmsg)
|
|
return &InvoiceEvent{}, err
|
|
}
|
|
|
|
// save the invoice event
|
|
id := fmt.Sprintf("invoice:%s", invoice.PaymentHash)
|
|
invoiceEvent := &InvoiceEvent{
|
|
Base: storage.New(storage.ID(id)),
|
|
Invoice: &Invoice{PaymentHash: invoice.PaymentHash,
|
|
PaymentRequest: invoice.PaymentRequest,
|
|
Amount: group.Ticket.Price,
|
|
Memo: memo},
|
|
User: group.Ticket.Creator,
|
|
Callback: callback,
|
|
CallbackData: callbackData,
|
|
LanguageCode: ctx.Value("publicLanguageCode").(string),
|
|
Payer: payer,
|
|
Chat: &tb.Chat{ID: group.ID},
|
|
}
|
|
// add result to persistent struct
|
|
runtime.IgnoreError(invoiceEvent.Set(invoiceEvent, bot.Bunt))
|
|
return invoiceEvent, nil
|
|
}
|