lightningtipbot/internal/telegram/tooltip.go
gohumble bf643f103d
🪄 update telebot v3 🤖 (#325)
* 🚧 minor changes to all handlers...

* 🚧 passing callback to tryEditStack

* 🚧 edit and delete callback messages

* 🚧 refactor again

* 🚧 uncomment this

* 🚧 using ctx.Message().Text

* 🚧 refactor

* 🚧 refactor

* 🚧 uncomment

* context not queruy

* 🚧 update forked lightningtipbot

* 🚧 reset ArticleResult

* fix inline receive

* readd shop

* remove chat from shopView

Co-authored-by: callebtc <93376500+callebtc@users.noreply.github.com>
2022-03-27 22:19:38 +02:00

191 lines
6.2 KiB
Go

package telegram
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"
"github.com/LightningTipBot/LightningTipBot/internal/runtime"
"github.com/tidwall/buntdb"
"github.com/tidwall/gjson"
log "github.com/sirupsen/logrus"
tb "gopkg.in/lightningtipbot/telebot.v3"
)
const (
tooltipChatWithBotMessage = "🗑 Chat with %s 👈 to manage your wallet."
tooltipAndOthersMessage = " ... and others"
tooltipMultipleTipsMessage = "%s (%d tips by %s)"
tooltipSingleTipMessage = "%s (by %s)"
tooltipTipAmountMessage = "🏅 %d sat"
)
type TipTooltip struct {
Message
ID string `json:"id"`
TipAmount int64 `json:"tip_amount"`
Ntips int `json:"ntips"`
LastTip time.Time `json:"last_tip"`
Tippers []*tb.User `json:"tippers"`
}
func (ttt TipTooltip) Key() string {
return fmt.Sprintf("tip-tool-tip:%s", ttt.ID)
}
const maxNamesInTipperMessage = 5
type TipTooltipOption func(m *TipTooltip)
func TipAmount(amount int64) TipTooltipOption {
return func(m *TipTooltip) {
m.TipAmount = amount
}
}
func Tips(nTips int) TipTooltipOption {
return func(m *TipTooltip) {
m.LastTip = time.Now()
m.Ntips = nTips
}
}
func NewTipTooltip(m *tb.Message, opts ...TipTooltipOption) *TipTooltip {
tipTooltip := &TipTooltip{
ID: fmt.Sprintf("%d-%d", m.Chat.ID, m.ReplyTo.ID),
Message: Message{
Message: m,
},
}
for _, opt := range opts {
opt(tipTooltip)
}
return tipTooltip
}
// getUpdatedTipTooltipMessage will return the full tip tool tip
func (ttt TipTooltip) getUpdatedTipTooltipMessage(botUserName string, notInitializedWallet bool) string {
tippersStr := getTippersString(ttt.Tippers)
tipToolTipMessage := fmt.Sprintf(tooltipTipAmountMessage, ttt.TipAmount)
if len(ttt.Tippers) > 1 {
tipToolTipMessage = fmt.Sprintf(tooltipMultipleTipsMessage, tipToolTipMessage, ttt.Ntips, tippersStr)
} else {
tipToolTipMessage = fmt.Sprintf(tooltipSingleTipMessage, tipToolTipMessage, tippersStr)
}
if notInitializedWallet {
tipToolTipMessage = tipToolTipMessage + fmt.Sprintf("\n%s", fmt.Sprintf(tooltipChatWithBotMessage, botUserName))
}
return tipToolTipMessage
}
// getTippersString joins all tippers username or Telegram id's as mentions (@username or [inline mention of a user](tg://user?id=123456789))
func getTippersString(tippers []*tb.User) string {
var tippersStr string
for _, uniqueUser := range tippers {
userStr := GetUserStrMd(uniqueUser)
tippersStr += fmt.Sprintf("%s, ", userStr)
}
// get rid of the trailing comma
if len(tippersStr) > 2 {
tippersStr = tippersStr[:len(tippersStr)-2]
}
tippersSlice := strings.Split(tippersStr, " ")
// crop the message to the max length
if len(tippersSlice) > maxNamesInTipperMessage {
// tippersStr = tippersStr[:50]
tippersStr = strings.Join(tippersSlice[:maxNamesInTipperMessage], " ")
tippersStr = tippersStr + tooltipAndOthersMessage
}
return tippersStr
}
// tipTooltipExists checks if this tip is already known
func tipTooltipExists(m *tb.Message, bot *TipBot) (bool, *TipTooltip) {
message := NewTipTooltip(&tb.Message{Chat: &tb.Chat{ID: m.Chat.ID}, ReplyTo: &tb.Message{ID: m.ReplyTo.ID}})
err := bot.Bunt.Get(message)
if err != nil {
return false, message
}
return true, message
}
// tipTooltipHandler function to update the tooltip below a tipped message. either updates or creates initial tip tool tip
func tipTooltipHandler(m *tb.Message, bot *TipBot, amount int64, initializedWallet bool) (hasTip bool) {
// todo: this crashes if the tooltip message (maybe also the original tipped message) was deleted in the mean time!!! need to check for existence!
hasTip, ttt := tipTooltipExists(m, bot)
log.Debugf("[tip] %s has tip: %t", ttt.ID, hasTip)
if hasTip {
// update the tooltip with new tippers
err := ttt.updateTooltip(bot, m.Sender, amount, !initializedWallet)
if err != nil {
log.Errorln(err)
// could not update the message (return false to )
return false
}
} else {
newToolTip(m, bot, amount, initializedWallet)
}
// first call will return false, every following call will return true
return hasTip
}
func newToolTip(m *tb.Message, bot *TipBot, amount int64, initializedWallet bool) {
tipmsg := fmt.Sprintf(tooltipTipAmountMessage, amount)
userStr := GetUserStrMd(m.Sender)
tipmsg = fmt.Sprintf(tooltipSingleTipMessage, tipmsg, userStr)
if !initializedWallet {
tipmsg = tipmsg + fmt.Sprintf("\n%s", fmt.Sprintf(tooltipChatWithBotMessage, GetUserStrMd(bot.Telegram.Me)))
}
msg := bot.tryReplyMessage(m.ReplyTo, tipmsg, tb.Silent)
message := NewTipTooltip(msg, TipAmount(amount), Tips(1))
message.Tippers = appendUinqueUsersToSlice(message.Tippers, m.Sender)
runtime.IgnoreError(bot.Bunt.Set(message))
log.Debugf("[newToolTip]: New reply message: %d (Bunt: %s)", msg.ID, message.Key())
}
// updateToolTip updates existing tip tool tip in Telegram
func (ttt *TipTooltip) updateTooltip(bot *TipBot, user *tb.User, amount int64, notInitializedWallet bool) error {
ttt.TipAmount += amount
ttt.Ntips += 1
ttt.Tippers = appendUinqueUsersToSlice(ttt.Tippers, user)
ttt.LastTip = time.Now()
ttt.editTooltip(bot, notInitializedWallet)
log.Debugf("[updateTooltip]: Update tip tooltip (Bunt: %s)", ttt.Key())
return bot.Bunt.Set(ttt)
}
// tipTooltipInitializedHandler is called when the user initializes the wallet
func tipTooltipInitializedHandler(user *tb.User, bot TipBot) {
runtime.IgnoreError(bot.Bunt.View(func(tx *buntdb.Tx) error {
err := tx.Ascend(MessageOrderedByReplyToFrom, func(key, value string) bool {
replyToUserId := gjson.Get(value, MessageOrderedByReplyToFrom)
if replyToUserId.String() == strconv.FormatInt(user.ID, 10) {
log.Debugln("[tipTooltipInitializedHandler] loading persistent tip tool tip messages")
ttt := &TipTooltip{}
err := json.Unmarshal([]byte(value), ttt)
if err != nil {
log.Errorln(err)
}
// edit to remove the "chat with bot" message
ttt.editTooltip(&bot, false)
}
return true
})
return err
}))
}
// editTooltip updates the tooltip message with the new tip amount and tippers and edits it
func (ttt *TipTooltip) editTooltip(bot *TipBot, notInitializedWallet bool) {
tipToolTip := ttt.getUpdatedTipTooltipMessage(GetUserStrMd(bot.Telegram.Me), notInitializedWallet)
bot.tryEditMessage(ttt.Message.Message, tipToolTip)
// ttt.Message.Message.Text = m.Text
}