623 lines
19 KiB
Go
623 lines
19 KiB
Go
package tghandlers
|
||
|
||
import (
|
||
"fmt"
|
||
"math"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"write-server/internal/helpers"
|
||
"write-server/internal/sessions"
|
||
"write-server/internal/tgcore"
|
||
"write-server/internal/writeserver"
|
||
|
||
"write-server/pkg/posts"
|
||
"write-server/pkg/rssapi"
|
||
"write-server/pkg/recycle"
|
||
)
|
||
|
||
type Handlers struct {
|
||
cfg *Config
|
||
}
|
||
|
||
type Config struct {
|
||
WritePort int
|
||
ControlPort int
|
||
PreferIface string
|
||
}
|
||
|
||
func New(cfg *Config) *Handlers {
|
||
return &Handlers{cfg: cfg}
|
||
}
|
||
|
||
func (h *Handlers) dispatchList(chatID int64, pageStr string) error {
|
||
page, _ := strconv.Atoi(pageStr)
|
||
if page < 1 { page = 1 }
|
||
perPage := 5
|
||
|
||
list, total, _ := posts.ListPosts("", page, 9999, true)
|
||
if len(list) == 0 {
|
||
return tgcore.SendMessage(chatID, "📭 暂无文章。", nil)
|
||
}
|
||
|
||
totalPages := int(math.Ceil(float64(total) / float64(perPage)))
|
||
if page > totalPages { page = totalPages }
|
||
|
||
start := (page - 1) * perPage
|
||
end := start + perPage
|
||
if end > len(list) { end = len(list) }
|
||
pageItems := list[start:end]
|
||
|
||
header := fmt.Sprintf("📋 <b>文章列表</b> 共%d篇 · 第%d/%d页", total, page, totalPages)
|
||
lines := []string{header}
|
||
for _, p := range pageItems {
|
||
emoji := "✅"
|
||
if p.Draft { emoji = "📝" }
|
||
date := ""
|
||
if len(p.Date) >= 10 { date = p.Date[:10] }
|
||
lines = append(lines, fmt.Sprintf("%s %s <code>/edit_%s</code> %s", emoji, p.Title, p.Slug, date))
|
||
}
|
||
|
||
kb := map[string]interface{}{}
|
||
var nav []map[string]interface{}
|
||
if page > 1 {
|
||
nav = append(nav, map[string]interface{}{"text": "◀ 上一页", "callback_data": fmt.Sprintf("list_%d", page-1)})
|
||
}
|
||
if page < totalPages {
|
||
nav = append(nav, map[string]interface{}{"text": "下一页 ▶", "callback_data": fmt.Sprintf("list_%d", page+1)})
|
||
}
|
||
if len(nav) > 0 {
|
||
kb["inline_keyboard"] = []interface{}{nav}
|
||
tgcore.TG("sendMessage", map[string]interface{}{
|
||
"chat_id": chatID,
|
||
"text": strings.Join(lines, "\n"),
|
||
"parse_mode": "HTML",
|
||
"reply_markup": kb,
|
||
"disable_web_page_preview": true,
|
||
})
|
||
return nil
|
||
}
|
||
return tgcore.SendMessage(chatID, strings.Join(lines, "\n"), nil)
|
||
}
|
||
|
||
func (h *Handlers) List(chatID int64, page string) { h.dispatchList(chatID, page) }
|
||
|
||
|
||
func (h *Handlers) Start(chatID int64) {
|
||
ip := helpers.GetLanIP(h.cfg.PreferIface)
|
||
ready := EnsureWriteReady(chatID, h.cfg.WritePort)
|
||
if ready {
|
||
kb := map[string]interface{}{
|
||
"inline_keyboard": []interface{}{
|
||
[]map[string]interface{}{
|
||
{"text": "📝 开始写作", "callback_data": "menu_new"},
|
||
{"text": "📋 文章列表", "callback_data": "list_1"},
|
||
},
|
||
[]map[string]interface{}{
|
||
{"text": "🔗 友链管理", "callback_data": "links_1"},
|
||
{"text": "📡 订阅源", "callback_data": "feeds_1"},
|
||
},
|
||
[]map[string]interface{}{
|
||
{"text": "📊 网站数据", "callback_data": "menu_stats"},
|
||
{"text": "ℹ️ 帮助", "callback_data": "menu_help"},
|
||
},
|
||
},
|
||
}
|
||
tgcore.TG("sendMessage", map[string]interface{}{
|
||
"chat_id": chatID,
|
||
"text": fmt.Sprintf("✅ <b>Write 已就绪</b>\n\n📱 手机: http://%s:%d/\n💻 电脑: http://localhost:%d/\n📰 文章: http://%s:1313/posts/", ip, h.cfg.WritePort, h.cfg.WritePort, ip),
|
||
"parse_mode": "HTML",
|
||
"reply_markup": kb,
|
||
"disable_web_page_preview": true,
|
||
})
|
||
return
|
||
}
|
||
tgcore.SendMessage(chatID, "❌ 启动超时,请查看控制台日志。", nil)
|
||
}
|
||
|
||
func (h *Handlers) Stop(chatID int64) {
|
||
if writeserver.StopWriteServer() {
|
||
tgcore.SendMessage(chatID, "⚡ Write 已停止。", nil)
|
||
} else {
|
||
tgcore.SendMessage(chatID, "⏸ Write 未运行。", nil)
|
||
}
|
||
}
|
||
|
||
func (h *Handlers) Status(chatID int64) {
|
||
s := writeserver.GetWriteStatus()
|
||
status := "⏸ 未运行"
|
||
if s["running"].(bool) { status = "✅ 运行中" }
|
||
if s["starting"].(bool) { status = "⏳ 启动中..." }
|
||
lines := []string{"<b>Write 状态</b>: " + status}
|
||
if s["running"].(bool) {
|
||
ip := helpers.GetLanIP(h.cfg.PreferIface)
|
||
lines = append(lines, fmt.Sprintf("🌐 局域网: http://%s:%d", ip, h.cfg.WritePort))
|
||
lines = append(lines, fmt.Sprintf("💻 本地: http://localhost:%d", h.cfg.WritePort))
|
||
}
|
||
tgcore.SendMessage(chatID, strings.Join(lines, "\n"), nil)
|
||
}
|
||
|
||
func (h *Handlers) IP(chatID int64) {
|
||
ip := helpers.GetLanIP(h.cfg.PreferIface)
|
||
tgcore.SendMessage(chatID, fmt.Sprintf("📱 手机访问: http://%s:%d\n💻 电脑访问: http://localhost:%d", ip, h.cfg.WritePort, h.cfg.WritePort), nil)
|
||
}
|
||
|
||
func (h *Handlers) Help(chatID int64) {
|
||
kb := map[string]interface{}{
|
||
"inline_keyboard": []interface{}{
|
||
[]map[string]interface{}{{"text": "📝 开始写作", "callback_data": "menu_new"}},
|
||
[]map[string]interface{}{{"text": "📋 文章列表", "callback_data": "list_1"}},
|
||
[]map[string]interface{}{{"text": "🔗 友链", "callback_data": "links_1"}, {"text": "📡 订阅", "callback_data": "feeds_1"}},
|
||
},
|
||
}
|
||
tgcore.TG("sendMessage", map[string]interface{}{
|
||
"chat_id": chatID,
|
||
"text": "<b>🤖 WriteBot 命令</b>\n\n📝 /new 标题 — 开始写作\n✏️ /edit slug — 编辑文章\n📋 /list — 文章列表\n🔍 /view slug — 查看文章\n🗑 /delete slug — 删除文章\n\n✍️ 写作中:\n/publish — 发布\n/draft — 存草稿\n/preview — 预览\n/cancel — 丢弃\n\n🔗 友链 /links\n📡 订阅 /feeds\n📊 /stats 统计",
|
||
"parse_mode": "HTML",
|
||
"reply_markup": kb,
|
||
"disable_web_page_preview": true,
|
||
})
|
||
}
|
||
|
||
|
||
// --- Writing commands ---
|
||
|
||
func (h *Handlers) New(chatID int64, title string) {
|
||
if title == "" {
|
||
tgcore.SendMessage(chatID, "📝 请提供标题:/new 你的标题", nil)
|
||
return
|
||
}
|
||
s := sessions.GetSession(chatID)
|
||
s.Title = title
|
||
s.Slug = helpers.MakeSlug(title)
|
||
s.Content = ""
|
||
s.Categories = nil
|
||
s.Tags = nil
|
||
s.Draft = true
|
||
s.Author = "qunlin"
|
||
|
||
kb := map[string]interface{}{
|
||
"inline_keyboard": []interface{}{
|
||
[]map[string]interface{}{
|
||
{"text": "📂 分类", "callback_data": "session_categories"},
|
||
{"text": "🏷 标签", "callback_data": "session_tags"},
|
||
},
|
||
[]map[string]interface{}{
|
||
{"text": "🚀 发布", "callback_data": "session_publish"},
|
||
{"text": "📄 存草稿", "callback_data": "session_draft"},
|
||
},
|
||
[]map[string]interface{}{
|
||
{"text": "🔍 预览", "callback_data": "session_preview"},
|
||
{"text": "❌ 丢弃", "callback_data": "session_cancel"},
|
||
},
|
||
},
|
||
}
|
||
tgcore.TG("sendMessage", map[string]interface{}{
|
||
"chat_id": chatID,
|
||
"text": fmt.Sprintf("📝 <b>%s</b>\n\n直接发消息开始写作\n用下方按钮管理文章属性", title),
|
||
"parse_mode": "HTML",
|
||
"reply_markup": kb,
|
||
})
|
||
}
|
||
|
||
func (h *Handlers) Edit(chatID int64, slug string) {
|
||
if slug == "" {
|
||
tgcore.SendMessage(chatID, "✏️ 用法:/edit slug\n先用 /list 查看 slug。", nil)
|
||
return
|
||
}
|
||
p := posts.GetPost(slug)
|
||
if p == nil {
|
||
tgcore.SendMessage(chatID, "❌ 未找到该文章。", nil)
|
||
return
|
||
}
|
||
|
||
s := sessions.GetSession(chatID)
|
||
s.Title = p.FrontMatter.Title
|
||
s.Slug = p.FrontMatter.Slug
|
||
s.Content = p.Content
|
||
s.Categories = p.FrontMatter.Categories
|
||
s.Tags = p.FrontMatter.Tags
|
||
s.Author = p.FrontMatter.Author
|
||
if s.Author == "" { s.Author = "qunlin" }
|
||
s.Draft = p.FrontMatter.Draft
|
||
|
||
summary := helpers.SessionSummary(s.Title, s.Categories, s.Tags, s.Content)
|
||
kb := map[string]interface{}{
|
||
"inline_keyboard": []interface{}{
|
||
[]map[string]interface{}{
|
||
{"text": "📂 分类", "callback_data": "session_categories"},
|
||
{"text": "🏷 标签", "callback_data": "session_tags"},
|
||
},
|
||
[]map[string]interface{}{
|
||
{"text": "🚀 发布", "callback_data": "session_publish"},
|
||
{"text": "📄 存草稿", "callback_data": "session_draft"},
|
||
},
|
||
[]map[string]interface{}{
|
||
{"text": "🔍 预览", "callback_data": "session_preview"},
|
||
{"text": "❌ 丢弃", "callback_data": "session_cancel"},
|
||
},
|
||
},
|
||
}
|
||
tgcore.TG("sendMessage", map[string]interface{}{
|
||
"chat_id": chatID,
|
||
"text": fmt.Sprintf("✏️ <b>%s</b>\n\n%s\n\n直接发消息继续写作", s.Title, summary),
|
||
"parse_mode": "HTML",
|
||
"reply_markup": kb,
|
||
})
|
||
}
|
||
|
||
func (h *Handlers) Delete(chatID int64, slug string) {
|
||
if slug == "" {
|
||
tgcore.SendMessage(chatID, "🗑 用法:/delete slug\n先用 /list 查看。", nil)
|
||
return
|
||
}
|
||
p := posts.GetPost(slug)
|
||
if p == nil {
|
||
tgcore.SendMessage(chatID, "❌ 未找到该文章。", nil)
|
||
return
|
||
}
|
||
if err := recycle.MoveToRecycle(slug, p.DirPath, p.FrontMatter.Title); err != nil {
|
||
tgcore.SendMessage(chatID, "❌ "+err.Error(), nil)
|
||
return
|
||
}
|
||
tgcore.SendMessage(chatID, "🗑 <b>"+p.FrontMatter.Title+"</b> 已移入回收站(30天后自动清除)。", nil)
|
||
}
|
||
|
||
func (h *Handlers) View(chatID int64, slug string) {
|
||
if slug == "" {
|
||
tgcore.SendMessage(chatID, "🔍 用法:/view slug", nil)
|
||
return
|
||
}
|
||
p := posts.GetPost(slug)
|
||
if p == nil {
|
||
tgcore.SendMessage(chatID, "❌ 未找到该文章。", nil)
|
||
return
|
||
}
|
||
draftMark := "✅"
|
||
if p.FrontMatter.Draft { draftMark = "📝" }
|
||
header := fmt.Sprintf("%s <b>%s</b>\n📂 %s 🏷 %s ✍️ %s 📅 %s",
|
||
draftMark, p.FrontMatter.Title,
|
||
strings.Join(p.FrontMatter.Categories, "、"),
|
||
strings.Join(p.FrontMatter.Tags, "、"),
|
||
p.FrontMatter.Author,
|
||
p.FrontMatter.Date[:10])
|
||
|
||
content := p.Content
|
||
if len([]rune(content)) > 3500 {
|
||
content = string([]rune(content)[:3500]) + "\n\n..."
|
||
}
|
||
tgcore.SendMessage(chatID, header+"\n\n"+content, nil)
|
||
}
|
||
|
||
|
||
func (h *Handlers) Title(chatID int64, arg string) {
|
||
s := sessions.GetSession(chatID)
|
||
if arg == "" {
|
||
tgcore.SendMessage(chatID, fmt.Sprintf("当前标题:%s\n修改:/title 新标题", s.Title), nil)
|
||
return
|
||
}
|
||
s.Title = arg
|
||
tgcore.SendMessage(chatID, "✅ 标题已更新:<b>"+arg+"</b>", nil)
|
||
}
|
||
|
||
func (h *Handlers) Categories(chatID int64, arg string) {
|
||
s := sessions.GetSession(chatID)
|
||
if arg == "" {
|
||
tgcore.SendMessage(chatID, fmt.Sprintf("当前分类:%s\n修改:/categories 分类1,分类2", strings.Join(s.Categories, "、")), nil)
|
||
return
|
||
}
|
||
s.Categories = nil
|
||
for _, item := range strings.Split(arg, ",") {
|
||
item = strings.TrimSpace(item)
|
||
if item != "" { s.Categories = append(s.Categories, item) }
|
||
}
|
||
tgcore.SendMessage(chatID, "✅ 分类:<b>"+strings.Join(s.Categories, "、")+"</b>", nil)
|
||
}
|
||
|
||
func (h *Handlers) Tags(chatID int64, arg string) {
|
||
s := sessions.GetSession(chatID)
|
||
if arg == "" {
|
||
tgcore.SendMessage(chatID, fmt.Sprintf("当前标签:%s\n修改:/tags 标签1,标签2", strings.Join(s.Tags, "、")), nil)
|
||
return
|
||
}
|
||
s.Tags = nil
|
||
for _, item := range strings.Split(arg, ",") {
|
||
item = strings.TrimSpace(item)
|
||
if item != "" { s.Tags = append(s.Tags, item) }
|
||
}
|
||
tgcore.SendMessage(chatID, "✅ 标签:<b>"+strings.Join(s.Tags, "、")+"</b>", nil)
|
||
}
|
||
|
||
func (h *Handlers) Author(chatID int64, arg string) {
|
||
s := sessions.GetSession(chatID)
|
||
if arg == "" {
|
||
tgcore.SendMessage(chatID, fmt.Sprintf("当前作者:%s\n修改:/author 新作者", s.Author), nil)
|
||
return
|
||
}
|
||
s.Author = arg
|
||
tgcore.SendMessage(chatID, "✅ 作者:<b>"+arg+"</b>", nil)
|
||
}
|
||
|
||
func (h *Handlers) Publish(chatID int64) {
|
||
s := sessions.GetSession(chatID)
|
||
if s.Title == "" {
|
||
tgcore.SendMessage(chatID, "❌ 请先 /new 标题 开始写作。", nil)
|
||
return
|
||
}
|
||
if err := sessions.Save(s, false); err != nil {
|
||
tgcore.SendMessage(chatID, "❌ "+err.Error(), nil)
|
||
return
|
||
}
|
||
sessions.ClearSession(chatID)
|
||
tgcore.SendMessage(chatID, "🚀 <b>"+s.Title+"</b> 已发布!", nil)
|
||
}
|
||
|
||
func (h *Handlers) Draft(chatID int64) {
|
||
s := sessions.GetSession(chatID)
|
||
if s.Title == "" {
|
||
tgcore.SendMessage(chatID, "❌ 请先 /new 标题 开始写作。", nil)
|
||
return
|
||
}
|
||
if err := sessions.Save(s, true); err != nil {
|
||
tgcore.SendMessage(chatID, "❌ "+err.Error(), nil)
|
||
return
|
||
}
|
||
sessions.ClearSession(chatID)
|
||
tgcore.SendMessage(chatID, "📄 <b>"+s.Title+"</b> 已存为草稿。", nil)
|
||
}
|
||
|
||
func (h *Handlers) Preview(chatID int64) {
|
||
s := sessions.GetSession(chatID)
|
||
if s.Title == "" {
|
||
tgcore.SendMessage(chatID, "❌ 请先 /new 标题 开始写作。", nil)
|
||
return
|
||
}
|
||
summary := helpers.SessionSummary(s.Title, s.Categories, s.Tags, s.Content)
|
||
preview := s.Content
|
||
if len([]rune(preview)) > 3500 {
|
||
preview = string([]rune(preview)[:3500]) + "\n\n... (内容过长)"
|
||
}
|
||
tgcore.SendMessage(chatID, summary+"\n\n"+preview, nil)
|
||
}
|
||
|
||
func (h *Handlers) Cancel(chatID int64) {
|
||
sessions.ClearSession(chatID)
|
||
tgcore.SendMessage(chatID, "🗑 已丢弃当前写作内容。", nil)
|
||
}
|
||
|
||
|
||
// --- Links ---
|
||
|
||
func (h *Handlers) Links(chatID int64, page string) { h.dispatchLinks(chatID, page) }
|
||
|
||
func (h *Handlers) dispatchLinks(chatID int64, pageStr string) error {
|
||
page, _ := strconv.Atoi(pageStr)
|
||
if page < 1 { page = 1 }
|
||
perPage := 8
|
||
|
||
links, err := rssapi.GetLinks(true)
|
||
if err != nil || len(links) == 0 {
|
||
return tgcore.SendMessage(chatID, "🔗 暂无友链。", nil)
|
||
}
|
||
|
||
totalPages := int(math.Ceil(float64(len(links)) / float64(perPage)))
|
||
if page > totalPages { page = totalPages }
|
||
start := (page - 1) * perPage
|
||
end := start + perPage
|
||
if end > len(links) { end = len(links) }
|
||
|
||
lines := []string{fmt.Sprintf("🔗 <b>友链</b> 共%d个 · 第%d/%d页", len(links), page, totalPages)}
|
||
for _, l := range links[start:end] {
|
||
icon := "🌐"
|
||
if l.Hidden { icon = "🔒" }
|
||
lines = append(lines, fmt.Sprintf("%s %s\n %s", icon, l.Name, l.URL))
|
||
}
|
||
|
||
kb := map[string]interface{}{}
|
||
var nav []map[string]interface{}
|
||
if page > 1 {
|
||
nav = append(nav, map[string]interface{}{"text": "◀", "callback_data": fmt.Sprintf("links_%d", page-1)})
|
||
}
|
||
if page < totalPages {
|
||
nav = append(nav, map[string]interface{}{"text": "▶", "callback_data": fmt.Sprintf("links_%d", page+1)})
|
||
}
|
||
if len(nav) > 0 {
|
||
kb["inline_keyboard"] = []interface{}{nav}
|
||
tgcore.TG("sendMessage", map[string]interface{}{
|
||
"chat_id": chatID, "text": strings.Join(lines, "\n"),
|
||
"parse_mode": "HTML", "reply_markup": kb, "disable_web_page_preview": true,
|
||
})
|
||
return nil
|
||
}
|
||
return tgcore.SendMessage(chatID, strings.Join(lines, "\n"), nil)
|
||
}
|
||
|
||
func (h *Handlers) LinkAdd(chatID int64, arg string) {
|
||
if arg == "" {
|
||
tgcore.SendMessage(chatID, "用法:/link_add url 名称", nil)
|
||
return
|
||
}
|
||
parts := strings.SplitN(arg, " ", 2)
|
||
url := parts[0]
|
||
name := ""
|
||
if len(parts) > 1 { name = parts[1] }
|
||
if err := rssapi.AddLink(rssapi.Link{URL: url, Name: name}); err != nil {
|
||
tgcore.SendMessage(chatID, "❌ "+err.Error(), nil)
|
||
return
|
||
}
|
||
tgcore.SendMessage(chatID, "✅ 友链已添加:"+url, nil)
|
||
}
|
||
|
||
func (h *Handlers) LinkDel(chatID int64, url string) {
|
||
if url == "" { tgcore.SendMessage(chatID, "用法:/link_del url", nil); return }
|
||
if err := rssapi.DeleteLink(url); err != nil {
|
||
tgcore.SendMessage(chatID, "❌ "+err.Error(), nil)
|
||
return
|
||
}
|
||
tgcore.SendMessage(chatID, "✅ 友链已删除:"+url, nil)
|
||
}
|
||
|
||
func (h *Handlers) LinkToggle(chatID int64, url string) {
|
||
if url == "" { tgcore.SendMessage(chatID, "用法:/link_toggle url", nil); return }
|
||
links, _ := rssapi.GetLinks(true)
|
||
var target *rssapi.Link
|
||
for i := range links {
|
||
if links[i].URL == url { target = &links[i]; break }
|
||
}
|
||
if target == nil { tgcore.SendMessage(chatID, "❌ 未找到该友链。", nil); return }
|
||
|
||
newHidden := !target.Hidden
|
||
rssapi.UpdateLink(url, map[string]interface{}{"hidden": newHidden})
|
||
label := "🌐 已显示"
|
||
if newHidden { label = "🔒 已隐藏" }
|
||
tgcore.SendMessage(chatID, label+": "+url, nil)
|
||
}
|
||
|
||
// --- Feeds ---
|
||
|
||
func (h *Handlers) Feeds(chatID int64, page string) { h.dispatchFeeds(chatID, page) }
|
||
|
||
func (h *Handlers) dispatchFeeds(chatID int64, pageStr string) error {
|
||
page, _ := strconv.Atoi(pageStr)
|
||
if page < 1 { page = 1 }
|
||
perPage := 8
|
||
|
||
feeds, err := rssapi.GetFeeds()
|
||
if err != nil || len(feeds) == 0 {
|
||
return tgcore.SendMessage(chatID, "📡 暂无订阅源。", nil)
|
||
}
|
||
|
||
totalPages := int(math.Ceil(float64(len(feeds)) / float64(perPage)))
|
||
if page > totalPages { page = totalPages }
|
||
start := (page - 1) * perPage
|
||
end := start + perPage
|
||
if end > len(feeds) { end = len(feeds) }
|
||
|
||
lines := []string{fmt.Sprintf("📡 <b>订阅源</b> 共%d个 · 第%d/%d页", len(feeds), page, totalPages)}
|
||
for _, f := range feeds[start:end] {
|
||
title := f.FeedTitle
|
||
if title == "" { title = f.URL }
|
||
if len([]rune(title)) > 50 { title = string([]rune(title)[:50]) }
|
||
lines = append(lines, fmt.Sprintf("🗜 %s\n %s", title, f.URL))
|
||
}
|
||
|
||
kb := map[string]interface{}{}
|
||
var nav []map[string]interface{}
|
||
if page > 1 {
|
||
nav = append(nav, map[string]interface{}{"text": "◀", "callback_data": fmt.Sprintf("feeds_%d", page-1)})
|
||
}
|
||
if page < totalPages {
|
||
nav = append(nav, map[string]interface{}{"text": "▶", "callback_data": fmt.Sprintf("feeds_%d", page+1)})
|
||
}
|
||
if len(nav) > 0 {
|
||
kb["inline_keyboard"] = []interface{}{nav}
|
||
tgcore.TG("sendMessage", map[string]interface{}{
|
||
"chat_id": chatID, "text": strings.Join(lines, "\n"),
|
||
"parse_mode": "HTML", "reply_markup": kb, "disable_web_page_preview": true,
|
||
})
|
||
return nil
|
||
}
|
||
return tgcore.SendMessage(chatID, strings.Join(lines, "\n"), nil)
|
||
}
|
||
|
||
func (h *Handlers) FeedAdd(chatID int64, arg string) {
|
||
if arg == "" { tgcore.SendMessage(chatID, "用法:/feed_add url", nil); return }
|
||
parts := strings.SplitN(arg, " ", 2)
|
||
url := parts[0]
|
||
title := ""
|
||
if len(parts) > 1 { title = parts[1] }
|
||
if err := rssapi.AddFeed(url, title); err != nil {
|
||
tgcore.SendMessage(chatID, "❌ "+err.Error(), nil)
|
||
return
|
||
}
|
||
tgcore.SendMessage(chatID, "✅ 订阅源已添加:"+url, nil)
|
||
}
|
||
|
||
func (h *Handlers) FeedDel(chatID int64, url string) {
|
||
if url == "" { tgcore.SendMessage(chatID, "用法:/feed_del url", nil); return }
|
||
if err := rssapi.DeleteFeed(url); err != nil {
|
||
tgcore.SendMessage(chatID, "❌ "+err.Error(), nil)
|
||
return
|
||
}
|
||
tgcore.SendMessage(chatID, "✅ 订阅源已删除:"+url, nil)
|
||
}
|
||
|
||
// --- Stats ---
|
||
|
||
func (h *Handlers) Stats(chatID int64) {
|
||
postCount := posts.GetPostCount()
|
||
linkCount := 0
|
||
feedCount := 0
|
||
if links, err := rssapi.GetLinks(true); err == nil { linkCount = len(links) }
|
||
if feeds, err := rssapi.GetFeeds(); err == nil { feedCount = len(feeds) }
|
||
|
||
tgcore.SendMessage(chatID, fmt.Sprintf("📊 <b>网站数据</b>\n\n📝 文章:%d 篇\n🔗 友链:%d 个\n📡 订阅源:%d 个\n\n🌐 usj.cc", postCount, linkCount, feedCount), nil)
|
||
}
|
||
|
||
|
||
// --- EnsureWriteReady ---
|
||
|
||
func EnsureWriteReady(chatID int64, writePort int) bool {
|
||
s := writeserver.GetWriteStatus()
|
||
running, _ := s["running"].(bool)
|
||
starting, _ := s["starting"].(bool)
|
||
|
||
if running {
|
||
writeserver.StartHugoIfNeeded()
|
||
return true
|
||
}
|
||
if starting {
|
||
tgcore.SendMessage(chatID, "⌛ Write 正在启动...", nil)
|
||
if helpers.WaitForReady(writePort, 0) {
|
||
writeserver.SetWriteReady(true)
|
||
writeserver.StartHugoIfNeeded()
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
result := writeserver.StartWriteServer()
|
||
if started, ok := result["started"].(bool); !ok || !started {
|
||
return false
|
||
}
|
||
tgcore.SendMessage(chatID, "⌛ 正在启动 Write...", nil)
|
||
if helpers.WaitForReady(writePort, 0) {
|
||
writeserver.SetWriteReady(true)
|
||
writeserver.StartHugoIfNeeded()
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
// --- Callback handlers for inline buttons ---
|
||
|
||
func (h *Handlers) SessionCallback(chatID int64, action string) {
|
||
switch action {
|
||
case "categories":
|
||
s := sessions.GetSession(chatID)
|
||
tgcore.SendMessage(chatID, fmt.Sprintf("📂 当前分类:%s\n\n请发送新的分类(逗号分隔):", strings.Join(s.Categories, "、")), nil)
|
||
case "tags":
|
||
s := sessions.GetSession(chatID)
|
||
tgcore.SendMessage(chatID, fmt.Sprintf("🏷 当前标签:%s\n\n请发送新的标签(逗号分隔):", strings.Join(s.Tags, "、")), nil)
|
||
case "publish":
|
||
h.Publish(chatID)
|
||
case "draft":
|
||
h.Draft(chatID)
|
||
case "preview":
|
||
h.Preview(chatID)
|
||
case "cancel":
|
||
h.Cancel(chatID)
|
||
}
|
||
}
|
||
|
||
func (h *Handlers) MenuCallback(chatID int64, action string) {
|
||
switch action {
|
||
case "new":
|
||
tgcore.SendMessage(chatID, "📝 请输入:/new 文章标题", nil)
|
||
case "help":
|
||
h.Help(chatID)
|
||
case "stats":
|
||
h.Stats(chatID)
|
||
}
|
||
}
|