158 lines
3.2 KiB
Go
158 lines
3.2 KiB
Go
// Package glist handles the list processing
|
|
package glist
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var DataPath string
|
|
|
|
type Entry struct {
|
|
Text string `json:"text"`
|
|
Checked bool `json:"checked"`
|
|
}
|
|
|
|
type GList struct {
|
|
AllMsgCounter int `json:"-"`
|
|
Mutex sync.Mutex `json:"-"`
|
|
ChatID int `json:"chat_id"`
|
|
MessageID *int `json:"message_id"`
|
|
Items []Entry `json:"items"`
|
|
}
|
|
|
|
func NewGList(chatID int, items ...string) *GList {
|
|
g := GList{ChatID: chatID}
|
|
for _, text := range items {
|
|
g.Items = append(g.Items, Entry{text, false})
|
|
}
|
|
return &g
|
|
}
|
|
|
|
var persistReqC chan<- *GList
|
|
|
|
func startPersistenceGoR() {
|
|
reqs := make(chan *GList, 50)
|
|
persistReqC = reqs
|
|
go func() {
|
|
for g := range reqs {
|
|
lists := map[*GList]struct{}{
|
|
g: struct{}{},
|
|
}
|
|
time.Sleep(5 * time.Second) // Collect all persist requests for 5 seconds
|
|
for len(reqs) > 0 {
|
|
g := <-reqs
|
|
lists[g] = struct{}{}
|
|
}
|
|
for g := range lists {
|
|
g.persist()
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func init() {
|
|
startPersistenceGoR()
|
|
}
|
|
|
|
func (g *GList) persist() {
|
|
g.Mutex.Lock()
|
|
defer g.Mutex.Unlock()
|
|
data, err := json.Marshal(g)
|
|
if err != nil {
|
|
log.Panicln("failed to marshal")
|
|
}
|
|
filename := path.Join(DataPath, fmt.Sprintf("chkchat%d", g.ChatID))
|
|
if err := os.WriteFile(filename, data, 0644); err != nil {
|
|
log.Panicln("failed to write to file")
|
|
}
|
|
}
|
|
|
|
func (g *GList) Add(t string) {
|
|
outer:
|
|
for _, text := range strings.Split(t, "\n") {
|
|
for _, item := range g.Items {
|
|
if item.Text == text {
|
|
continue outer
|
|
}
|
|
}
|
|
g.Items = append(g.Items, Entry{text, false})
|
|
}
|
|
persistReqC <- g
|
|
}
|
|
|
|
func (g *GList) Toggle(text string) {
|
|
for i, item := range g.Items {
|
|
if item.Text == text {
|
|
g.Items[i].Checked = !g.Items[i].Checked
|
|
persistReqC <- g
|
|
return
|
|
}
|
|
}
|
|
log.Printf("item not found in toggle, chat: %d, item: %s\n", g.ChatID, text)
|
|
}
|
|
|
|
func (g *GList) ClearChecked() {
|
|
var remaining []Entry
|
|
for _, item := range g.Items {
|
|
if !item.Checked {
|
|
remaining = append(remaining, item)
|
|
}
|
|
}
|
|
g.Items = remaining
|
|
persistReqC <- g
|
|
}
|
|
|
|
type button struct {
|
|
Text string `json:"text"`
|
|
CallbackData string `json:"callback_data"`
|
|
}
|
|
|
|
type newListReq struct {
|
|
ChatID int `json:"chat_id"`
|
|
MessageID *int `json:"message_id,omitempty"`
|
|
Text string `json:"text"`
|
|
ReplyMarkup struct {
|
|
InlineKeyboard [][]button `json:"inline_keyboard"`
|
|
} `json:"reply_markup"`
|
|
}
|
|
|
|
func makeButton(e Entry) button {
|
|
b := button{Text: e.Text, CallbackData: e.Text}
|
|
if e.Checked {
|
|
b.Text = fmt.Sprintf("✓ %s", e.Text)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func makeButtons(items []Entry) [][]button {
|
|
var buttons [][]button
|
|
var current []button
|
|
for _, item := range items {
|
|
current = append(current, makeButton(item))
|
|
if len(current) == 2 {
|
|
buttons = append(buttons, current)
|
|
current = []button{}
|
|
}
|
|
}
|
|
if len(current) != 0 {
|
|
buttons = append(buttons, current)
|
|
}
|
|
return buttons
|
|
}
|
|
|
|
func (g *GList) GenSendListReq() ([]byte, error) {
|
|
req := newListReq{ChatID: g.ChatID, MessageID: g.MessageID, Text: "List:"}
|
|
itemButtons := makeButtons(g.Items)
|
|
controlButtons := []button{{"clear checked", "clear"}}
|
|
req.ReplyMarkup.InlineKeyboard = append(itemButtons, controlButtons)
|
|
data, err := json.Marshal(req)
|
|
return data, err
|
|
}
|