mirror of
https://github.com/tiennm99/store-scraper-bot.git
synced 2026-08-31 06:28:43 +00:00
- Switch Telegram parse mode to HTML to match Java client. - Rename command identifiers to Java's: delgroup/delapple/delgoogle/ checkappscore/rawappleapp/rawgoogleapp. - Move admin singleton to "common" collection at _id="admin". - Store group _id as string-of-int64 to match Java AbstractModel schema. - Add `class` discriminator field on persisted models. - Expand AppleAppResponse (42 fields) and GoogleAppResponse (63 fields, with nested Category/Feature) to match Java records. - Add AppleAppRequest (track id or bundle id) / GoogleAppRequest types. - Implement Raw* commands as Telegram document attachments instead of truncated text. - Group-authorization gate (admin.HasGroup(chatId)) on non-admin commands. - Cache TTL via millis timestamp; round score to 1 decimal; weekend silent send in scheduler. - Match Java table renderer: " │ " separators, "─┼─" rows every 5 lines. - Prefer MONGODB_CONNECTION_STRING env (Java parity); auto-extract DB name from URI with fallback to "store-scraper-bot". Note: This is an AI-assisted port (not human-verified). Behavior parity with the Java implementation has not been tested end-to-end; treat as a starting point.
96 lines
2.0 KiB
Go
96 lines
2.0 KiB
Go
package util
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// BuildTable mirrors Java bot/table/Table.java:
|
|
// - left-aligned columns padded to max(header, cell) width
|
|
// - "│" column separator
|
|
// - row separator inserted every 5 rows using "─" cells joined by "─┼─"
|
|
//
|
|
// Output is intended to be wrapped in <pre> for Telegram HTML rendering.
|
|
func BuildTable(headers []string, rows [][]string) string {
|
|
widths := computeWidths(headers, rows)
|
|
var sb strings.Builder
|
|
|
|
writeRow(&sb, headers, widths)
|
|
sb.WriteString("\n")
|
|
writeSeparator(&sb, widths)
|
|
|
|
for i, row := range rows {
|
|
sb.WriteString("\n")
|
|
if i > 0 && i%5 == 0 {
|
|
writeSeparator(&sb, widths)
|
|
sb.WriteString("\n")
|
|
}
|
|
writeRow(&sb, row, widths)
|
|
}
|
|
return sb.String()
|
|
}
|
|
|
|
func computeWidths(headers []string, rows [][]string) []int {
|
|
widths := make([]int, len(headers))
|
|
for i, h := range headers {
|
|
widths[i] = len(h)
|
|
}
|
|
for _, row := range rows {
|
|
for i, cell := range row {
|
|
if i < len(widths) && len(cell) > widths[i] {
|
|
widths[i] = len(cell)
|
|
}
|
|
}
|
|
}
|
|
return widths
|
|
}
|
|
|
|
func writeRow(sb *strings.Builder, cells []string, widths []int) {
|
|
for i, w := range widths {
|
|
var cell string
|
|
if i < len(cells) {
|
|
cell = cells[i]
|
|
}
|
|
sb.WriteString(padRight(cell, w))
|
|
if i < len(widths)-1 {
|
|
sb.WriteString(" │ ")
|
|
}
|
|
}
|
|
}
|
|
|
|
func writeSeparator(sb *strings.Builder, widths []int) {
|
|
for i, w := range widths {
|
|
sb.WriteString(strings.Repeat("─", w))
|
|
if i < len(widths)-1 {
|
|
sb.WriteString("─┼─")
|
|
}
|
|
}
|
|
}
|
|
|
|
func padRight(s string, length int) string {
|
|
if len(s) >= length {
|
|
return s
|
|
}
|
|
return s + strings.Repeat(" ", length-len(s))
|
|
}
|
|
|
|
func TruncateString(s string, maxLen int) string {
|
|
if len(s) <= maxLen {
|
|
return s
|
|
}
|
|
if maxLen <= 3 {
|
|
return s[:maxLen]
|
|
}
|
|
return s[:maxLen-3] + "..."
|
|
}
|
|
|
|
func FormatNumber(n int64) string {
|
|
if n >= 1_000_000 {
|
|
return fmt.Sprintf("%.1fM", float64(n)/1_000_000)
|
|
}
|
|
if n >= 1_000 {
|
|
return fmt.Sprintf("%.1fK", float64(n)/1_000)
|
|
}
|
|
return fmt.Sprintf("%d", n)
|
|
}
|