目录保留消息上下文并增加直达链接
This commit is contained in:
@@ -9,12 +9,14 @@ import (
|
|||||||
bolt "go.etcd.io/bbolt"
|
bolt "go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Storage) CreateEntry(category, title, link string) (*Entry, error) {
|
func (s *Storage) CreateEntry(category, title, channelLink, directLink string) (*Entry, error) {
|
||||||
entry := &Entry{
|
entry := &Entry{
|
||||||
ID: shortid.MustGenerate(),
|
ID: shortid.MustGenerate(),
|
||||||
Category: category,
|
Category: category,
|
||||||
Title: title,
|
Title: title,
|
||||||
Link: link,
|
Link: channelLink,
|
||||||
|
ChannelLink: channelLink,
|
||||||
|
DirectLink: directLink,
|
||||||
Timestamp: time.Now(),
|
Timestamp: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
bolt "go.etcd.io/bbolt"
|
bolt "go.etcd.io/bbolt"
|
||||||
@@ -30,10 +31,24 @@ type Entry struct {
|
|||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Link string `json:"link"`
|
Link string `json:"link,omitempty"` // 兼容旧数据
|
||||||
|
ChannelLink string `json:"channel_link,omitempty"`
|
||||||
|
DirectLink string `json:"direct_link,omitempty"`
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp time.Time `json:"timestamp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e Entry) MessageLink() string {
|
||||||
|
if e.ChannelLink != "" {
|
||||||
|
return e.ChannelLink
|
||||||
|
}
|
||||||
|
return e.Link
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Entry) HasDirectLink() bool {
|
||||||
|
direct := strings.TrimSpace(e.DirectLink)
|
||||||
|
return direct != "" && direct != e.MessageLink()
|
||||||
|
}
|
||||||
|
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
db *bolt.DB
|
db *bolt.DB
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ func (b *Bot) handleDeleteEntryCallback(c tele.Context, entryID string, deleteCh
|
|||||||
// 根据用户选择决定是否删除频道消息
|
// 根据用户选择决定是否删除频道消息
|
||||||
msgDeleted := false
|
msgDeleted := false
|
||||||
if deleteChannelMsg {
|
if deleteChannelMsg {
|
||||||
if msgID := parseMessageIDFromLink(entry.Link); msgID != 0 {
|
if msgID := parseMessageIDFromLink(entry.MessageLink()); msgID != 0 {
|
||||||
channel := &tele.Chat{ID: b.cfg.Channel.ID}
|
channel := &tele.Chat{ID: b.cfg.Channel.ID}
|
||||||
msg := &tele.Message{ID: msgID, Chat: channel}
|
msg := &tele.Message{ID: msgID, Chat: channel}
|
||||||
if err := b.bot.Delete(msg); err == nil {
|
if err := b.bot.Delete(msg); err == nil {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package telegram
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -26,6 +27,8 @@ var (
|
|||||||
albumTimersMu sync.Mutex
|
albumTimersMu sync.Mutex
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var directURLPattern = regexp.MustCompile(`https?://[^\s<>"']+`)
|
||||||
|
|
||||||
func (b *Bot) handlePost(c tele.Context) error {
|
func (b *Bot) handlePost(c tele.Context) error {
|
||||||
msg := c.Message()
|
msg := c.Message()
|
||||||
payload := strings.TrimSpace(c.Message().Payload)
|
payload := strings.TrimSpace(c.Message().Payload)
|
||||||
@@ -69,11 +72,11 @@ func (b *Bot) handleQuickPost(c tele.Context, replyMsg *tele.Message, payload st
|
|||||||
title = extractTitle(replyMsg)
|
title = extractTitle(replyMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建链接
|
channelLink := buildMessageLinkFromReply(c.Message(), replyMsg)
|
||||||
link := buildMessageLinkFromReply(c.Message(), replyMsg)
|
directLink := extractDirectLink(replyMsg)
|
||||||
|
|
||||||
// 创建条目
|
// 创建条目
|
||||||
entry, err := b.storage.CreateEntry(category, title, link)
|
entry, err := b.storage.CreateEntry(category, title, channelLink, directLink)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Reply(fmt.Sprintf("❌ 保存失败: %v", err))
|
return c.Reply(fmt.Sprintf("❌ 保存失败: %v", err))
|
||||||
}
|
}
|
||||||
@@ -81,7 +84,7 @@ func (b *Bot) handleQuickPost(c tele.Context, replyMsg *tele.Message, payload st
|
|||||||
b.toc.TriggerUpdate()
|
b.toc.TriggerUpdate()
|
||||||
|
|
||||||
return c.Reply(fmt.Sprintf("✅ 已添加\n\nID: `%s`\n分类: %s\n标题: %s\n链接: %s",
|
return c.Reply(fmt.Sprintf("✅ 已添加\n\nID: `%s`\n分类: %s\n标题: %s\n链接: %s",
|
||||||
entry.ID, entry.Category, entry.Title, entry.Link), tele.ModeMarkdown)
|
entry.ID, entry.Category, entry.Title, entry.MessageLink()), tele.ModeMarkdown)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildMessageLinkFromReply(currentMsg, replyMsg *tele.Message) string {
|
func buildMessageLinkFromReply(currentMsg, replyMsg *tele.Message) string {
|
||||||
@@ -437,10 +440,10 @@ func (b *Bot) handleConfirmCallback(c tele.Context, userID int64, action string)
|
|||||||
return c.Respond(&tele.CallbackResponse{Text: "发送失败"})
|
return c.Respond(&tele.CallbackResponse{Text: "发送失败"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用频道消息的链接
|
channelLink := b.buildChannelLink(channelMsg.ID)
|
||||||
link := b.buildChannelLink(channelMsg.ID)
|
directLink := extractDirectLinkFromState(state)
|
||||||
|
|
||||||
entry, err := b.storage.CreateEntry(state.SelectedCat, title, link)
|
entry, err := b.storage.CreateEntry(state.SelectedCat, title, channelLink, directLink)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Edit(fmt.Sprintf("❌ 保存失败: %v", err))
|
c.Edit(fmt.Sprintf("❌ 保存失败: %v", err))
|
||||||
return c.Respond(&tele.CallbackResponse{Text: "保存失败"})
|
return c.Respond(&tele.CallbackResponse{Text: "保存失败"})
|
||||||
@@ -519,6 +522,56 @@ func extractTitle(msg *tele.Message) string {
|
|||||||
return title
|
return title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractDirectLinkFromMessages(msgs []*tele.Message) string {
|
||||||
|
for _, msg := range msgs {
|
||||||
|
if msg == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if link := extractDirectLink(msg); link != "" {
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractDirectLinkFromState(state *PostState) string {
|
||||||
|
if state == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if link := extractDirectLink(state.ForwardedMsg); link != "" {
|
||||||
|
return link
|
||||||
|
}
|
||||||
|
return extractDirectLinkFromMessages(state.ForwardedMsgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func extractDirectLink(msg *tele.Message) string {
|
||||||
|
if msg == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entity := range msg.Entities {
|
||||||
|
if entity.Type == tele.EntityTextLink && entity.URL != "" {
|
||||||
|
return entity.URL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, entity := range msg.CaptionEntities {
|
||||||
|
if entity.Type == tele.EntityTextLink && entity.URL != "" {
|
||||||
|
return entity.URL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, text := range []string{msg.Text, msg.Caption} {
|
||||||
|
if text == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if match := directURLPattern.FindString(text); match != "" {
|
||||||
|
return match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func buildMessageLink(msg *tele.Message) string {
|
func buildMessageLink(msg *tele.Message) string {
|
||||||
if msg.OriginalChat == nil {
|
if msg.OriginalChat == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -39,7 +39,11 @@ func (m *Manager) Render() (string, error) {
|
|||||||
sb.WriteString(" <i>暂无内容</i>\n")
|
sb.WriteString(" <i>暂无内容</i>\n")
|
||||||
} else {
|
} else {
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
sb.WriteString(fmt.Sprintf(" • <a href=\"%s\">%s</a>\n", entry.Link, html.EscapeString(entry.Title)))
|
sb.WriteString(fmt.Sprintf(" • <a href=\"%s\">%s</a>", html.EscapeString(entry.MessageLink()), html.EscapeString(entry.Title)))
|
||||||
|
if entry.HasDirectLink() {
|
||||||
|
sb.WriteString(fmt.Sprintf(" <a href=\"%s\">[直达]</a>", html.EscapeString(entry.DirectLink)))
|
||||||
|
}
|
||||||
|
sb.WriteString("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.WriteString("\n")
|
sb.WriteString("\n")
|
||||||
|
|||||||
Reference in New Issue
Block a user