feat: add rich token message renderer with conditional rows
Introduce unified rich token message rendering with fixed row order and conditional visibility based on available sources. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -2,13 +2,15 @@ package view
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
"me.thuanle/bbot/internal/helper/binancex"
|
||||
"me.thuanle/bbot/internal/services/tele/view/helper"
|
||||
"me.thuanle/bbot/internal/utils/timex"
|
||||
"time"
|
||||
)
|
||||
|
||||
func RenderPrice(price float64) string {
|
||||
@@ -63,6 +65,69 @@ func RenderOnAlphaPriceMessage(symbol string, price float64, change24h float64)
|
||||
)
|
||||
}
|
||||
|
||||
type RichTokenMessageInput struct {
|
||||
Token string
|
||||
|
||||
HasSpot bool
|
||||
SpotPrice float64
|
||||
|
||||
HasFuture bool
|
||||
FuturePrice float64
|
||||
FundingRate float64
|
||||
FundingTimeMs int64
|
||||
|
||||
HasAlpha bool
|
||||
AlphaPrice float64
|
||||
|
||||
HasAlpha24h bool
|
||||
Alpha24hChange float64
|
||||
|
||||
HasMarginAPR bool
|
||||
MarginAPRPercent float64
|
||||
}
|
||||
|
||||
func RenderRichTokenMessage(in RichTokenMessageInput) string {
|
||||
rows := []string{fmt.Sprintf("🪙 %s", strings.ToUpper(in.Token))}
|
||||
|
||||
if in.HasSpot {
|
||||
rows = append(rows, fmt.Sprintf("💵 Spot: $%s", RenderPrice(in.SpotPrice)))
|
||||
}
|
||||
if in.HasFuture {
|
||||
rows = append(rows, fmt.Sprintf("📈 Future: $%s", RenderPrice(in.FuturePrice)))
|
||||
}
|
||||
if in.HasAlpha {
|
||||
rows = append(rows, fmt.Sprintf("🅰️ Alpha: $%s", RenderPrice(in.AlphaPrice)))
|
||||
}
|
||||
|
||||
if in.HasSpot && in.HasFuture && in.SpotPrice > 0 {
|
||||
delta := in.FuturePrice - in.SpotPrice
|
||||
deltaPct := delta / in.SpotPrice * 100
|
||||
rows = append(rows, fmt.Sprintf("🧭 Basis: $%+s (%+.2f%%)", RenderPrice(delta), deltaPct))
|
||||
}
|
||||
|
||||
if in.HasFuture {
|
||||
rows = append(rows, fmt.Sprintf("💸 Funding: %.4f%% %s in %s",
|
||||
in.FundingRate*100,
|
||||
IconOfFundingFeeDirection(in.FundingRate),
|
||||
timex.CdMinuteStringTime(time.UnixMilli(in.FundingTimeMs)),
|
||||
))
|
||||
}
|
||||
|
||||
if in.HasMarginAPR {
|
||||
rows = append(rows, fmt.Sprintf("🏦 Margin: %.3f%% APR", in.MarginAPRPercent))
|
||||
}
|
||||
|
||||
if in.HasAlpha24h {
|
||||
icon := "🟢"
|
||||
if in.Alpha24hChange < 0 {
|
||||
icon = "🔴"
|
||||
}
|
||||
rows = append(rows, fmt.Sprintf("📊 Alpha 24h: %s%+.2f%%", icon, in.Alpha24hChange))
|
||||
}
|
||||
|
||||
return strings.Join(rows, "\n")
|
||||
}
|
||||
|
||||
func RenderOnGetTopPricesMessage(symbols []string, price []float64, fundRate []float64) string {
|
||||
t := helper.NewNoBorderTableWriter("", "Price", "Fund")
|
||||
mFmt := message.NewPrinter(language.AmericanEnglish)
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func assertContainsAll(t *testing.T, msg string, rows ...string) {
|
||||
t.Helper()
|
||||
for _, r := range rows {
|
||||
if !strings.Contains(msg, r) {
|
||||
t.Fatalf("expected row %q in message:\n%s", r, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func assertContainsNone(t *testing.T, msg string, rows ...string) {
|
||||
t.Helper()
|
||||
for _, r := range rows {
|
||||
if strings.Contains(msg, r) {
|
||||
t.Fatalf("unexpected row %q in message:\n%s", r, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderRichTokenMessage_AllSources(t *testing.T) {
|
||||
msg := RenderRichTokenMessage(RichTokenMessageInput{
|
||||
Token: "ETH",
|
||||
HasSpot: true,
|
||||
SpotPrice: 3245,
|
||||
HasFuture: true,
|
||||
FuturePrice: 3251,
|
||||
FundingRate: 0.000123,
|
||||
FundingTimeMs: 1740000000000,
|
||||
HasAlpha: true,
|
||||
AlphaPrice: 3248,
|
||||
HasAlpha24h: true,
|
||||
Alpha24hChange: -3.21,
|
||||
HasMarginAPR: true,
|
||||
MarginAPRPercent: 7.665,
|
||||
})
|
||||
|
||||
expectedOrder := []string{
|
||||
"🪙 ETH",
|
||||
"💵 Spot:",
|
||||
"📈 Future:",
|
||||
"🅰️ Alpha:",
|
||||
"🧭 Basis:",
|
||||
"💸 Funding:",
|
||||
"🏦 Margin:",
|
||||
"📊 Alpha 24h:",
|
||||
}
|
||||
|
||||
last := -1
|
||||
for _, part := range expectedOrder {
|
||||
i := strings.Index(msg, part)
|
||||
if i == -1 {
|
||||
t.Fatalf("missing row %q in message:\n%s", part, msg)
|
||||
}
|
||||
if i < last {
|
||||
t.Fatalf("row %q appears out of order in message:\n%s", part, msg)
|
||||
}
|
||||
last = i
|
||||
}
|
||||
}
|
||||
|
||||
func TestRenderRichTokenMessage_FutureOnly(t *testing.T) {
|
||||
msg := RenderRichTokenMessage(RichTokenMessageInput{
|
||||
Token: "ABC",
|
||||
HasFuture: true,
|
||||
FuturePrice: 1234,
|
||||
FundingRate: 0.000081,
|
||||
FundingTimeMs: 1740000000000,
|
||||
HasMarginAPR: true,
|
||||
MarginAPRPercent: 6.2,
|
||||
})
|
||||
|
||||
assertContainsAll(t, msg, "🪙 ABC", "📈 Future:", "💸 Funding:", "🏦 Margin:")
|
||||
assertContainsNone(t, msg, "💵 Spot:", "🅰️ Alpha:", "🧭 Basis:", "📊 Alpha 24h:")
|
||||
}
|
||||
|
||||
func TestRenderRichTokenMessage_SpotOnly(t *testing.T) {
|
||||
msg := RenderRichTokenMessage(RichTokenMessageInput{
|
||||
Token: "XRP",
|
||||
HasSpot: true,
|
||||
SpotPrice: 2.1,
|
||||
HasMarginAPR: true,
|
||||
MarginAPRPercent: 5.11,
|
||||
})
|
||||
|
||||
assertContainsAll(t, msg, "🪙 XRP", "💵 Spot:", "🏦 Margin:")
|
||||
assertContainsNone(t, msg, "📈 Future:", "🅰️ Alpha:", "🧭 Basis:", "💸 Funding:", "📊 Alpha 24h:")
|
||||
}
|
||||
|
||||
func TestRenderRichTokenMessage_AlphaOnly(t *testing.T) {
|
||||
msg := RenderRichTokenMessage(RichTokenMessageInput{
|
||||
Token: "ABC",
|
||||
HasAlpha: true,
|
||||
AlphaPrice: 0.1234,
|
||||
HasAlpha24h: true,
|
||||
Alpha24hChange: 12.4,
|
||||
})
|
||||
|
||||
assertContainsAll(t, msg, "🪙 ABC", "🅰️ Alpha:", "📊 Alpha 24h:")
|
||||
assertContainsNone(t, msg, "💵 Spot:", "📈 Future:", "🧭 Basis:", "💸 Funding:", "🏦 Margin:")
|
||||
}
|
||||
|
||||
func TestRenderRichTokenMessage_SpotAndFuture(t *testing.T) {
|
||||
msg := RenderRichTokenMessage(RichTokenMessageInput{
|
||||
Token: "SOL",
|
||||
HasSpot: true,
|
||||
SpotPrice: 182.4,
|
||||
HasFuture: true,
|
||||
FuturePrice: 183.0,
|
||||
FundingRate: -0.000041,
|
||||
FundingTimeMs: 1740000000000,
|
||||
HasMarginAPR: true,
|
||||
MarginAPRPercent: 5.11,
|
||||
})
|
||||
|
||||
assertContainsAll(t, msg, "🪙 SOL", "💵 Spot:", "📈 Future:", "🧭 Basis:", "💸 Funding:", "🏦 Margin:")
|
||||
assertContainsNone(t, msg, "🅰️ Alpha:", "📊 Alpha 24h:")
|
||||
}
|
||||
Reference in New Issue
Block a user