implement persistence
This commit is contained in:
		@@ -3,9 +3,15 @@ package glist
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var DataPath string
 | 
			
		||||
 | 
			
		||||
type Entry struct {
 | 
			
		||||
	Text    string `json:"text"`
 | 
			
		||||
	Checked bool   `json:"checked"`
 | 
			
		||||
@@ -27,6 +33,42 @@ func NewGList(chatID int, items ...string) *GList {
 | 
			
		||||
	return &g
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var reqs chan *GList
 | 
			
		||||
 | 
			
		||||
func DoPersist() {
 | 
			
		||||
	reqs = make(chan *GList, 50)
 | 
			
		||||
	go func() {
 | 
			
		||||
		for range time.Tick(5 * time.Second) {
 | 
			
		||||
			lists := map[*GList]struct{}{}
 | 
			
		||||
		loop:
 | 
			
		||||
			for {
 | 
			
		||||
				select {
 | 
			
		||||
				case g := <-reqs:
 | 
			
		||||
					lists[g] = struct{}{}
 | 
			
		||||
				default:
 | 
			
		||||
					break loop
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			for g := range lists {
 | 
			
		||||
				g.persist()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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(text string) error {
 | 
			
		||||
	for _, item := range g.Items {
 | 
			
		||||
		if item.Text == text {
 | 
			
		||||
@@ -34,6 +76,7 @@ func (g *GList) Add(text string) error {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	g.Items = append(g.Items, Entry{text, false})
 | 
			
		||||
	reqs <- g
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -41,6 +84,7 @@ func (g *GList) Toggle(text string) error {
 | 
			
		||||
	for i, item := range g.Items {
 | 
			
		||||
		if item.Text == text {
 | 
			
		||||
			g.Items[i].Checked = !g.Items[i].Checked
 | 
			
		||||
			reqs <- g
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -55,6 +99,7 @@ func (g *GList) ClearChecked() {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	g.Items = remaining
 | 
			
		||||
	reqs <- g
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type button struct {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										85
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										85
									
								
								main.go
									
									
									
									
									
								
							@@ -1,3 +1,4 @@
 | 
			
		||||
// This is the main package
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
@@ -7,18 +8,58 @@ import (
 | 
			
		||||
	"io"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"gitea.balki.me/telegram-msgchkbox/glist"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var apiToken = "421791796:AAE4wPbcqfLP1GNeGR3RTBiyX16fCj3HPAM"
 | 
			
		||||
var apiToken string
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
 | 
			
		||||
	apiToken = os.Getenv("CHKBOT_API_TOKEN")
 | 
			
		||||
 | 
			
		||||
	if apiToken == "" {
 | 
			
		||||
		log.Panicln("CHKBOT_API_TOKEN is empty")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	port := func() int {
 | 
			
		||||
		portStr := os.Getenv("CHKBOT_PORT")
 | 
			
		||||
		port, err := strconv.Atoi(portStr)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return port
 | 
			
		||||
		}
 | 
			
		||||
		return 28923
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	dataPath := func() string {
 | 
			
		||||
		dataPath := os.Getenv("CHKBOT_DATA_PATH")
 | 
			
		||||
		if dataPath == "" {
 | 
			
		||||
			dataPath = "."
 | 
			
		||||
		}
 | 
			
		||||
		err := os.MkdirAll(dataPath, 0755)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Panicf("Failed to create datapath, path: %s, err: %s", dataPath, err)
 | 
			
		||||
		}
 | 
			
		||||
		return dataPath
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	glist.DataPath = dataPath
 | 
			
		||||
	glist.DoPersist()
 | 
			
		||||
 | 
			
		||||
	log.Printf("List path starting with datapath:%s, port:%d\n", dataPath, port)
 | 
			
		||||
 | 
			
		||||
	var chats sync.Map
 | 
			
		||||
	fmt.Println("vim-go")
 | 
			
		||||
	http.HandleFunc("/zeexkfcsdjpmncirkyelwzotjmmefcqtcogrfwnafidionxiacwnslwuhbwfuppjgwzbmazd", func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	if err := loadData(dataPath, &chats); err != nil {
 | 
			
		||||
		log.Panicf("failed to load data, err: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	botPath := fmt.Sprintf("/bot%s", apiToken)
 | 
			
		||||
	http.HandleFunc(botPath, func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		w.Write([]byte("ok"))
 | 
			
		||||
		body, err := io.ReadAll(r.Body)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -57,7 +98,9 @@ func main() {
 | 
			
		||||
			g, _ := chats.LoadOrStore(chatID, glist.NewGList(chatID))
 | 
			
		||||
			gl := g.(*glist.GList)
 | 
			
		||||
			go handleTextAdded(gl, update.Message.Text)
 | 
			
		||||
 | 
			
		||||
		} else if update.CallbackQuery != nil {
 | 
			
		||||
 | 
			
		||||
			defer func() { go answerCallbackQuery(update.CallbackQuery.ID) }()
 | 
			
		||||
 | 
			
		||||
			chatID := update.CallbackQuery.Message.Chat.ID
 | 
			
		||||
@@ -71,7 +114,6 @@ func main() {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	port := 28923
 | 
			
		||||
	log.Panic(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -129,8 +171,8 @@ func sendList(gl *glist.GList, method string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func answerCallbackQuery(callbackQueryID string) {
 | 
			
		||||
	answerUrl := fmt.Sprintf("https://api.telegram.org/bot%s/answerCallbackQuery?callback_query_id=%stext=ok", apiToken, callbackQueryID)
 | 
			
		||||
	resp, err := http.Get(answerUrl)
 | 
			
		||||
	answerURL := fmt.Sprintf("https://api.telegram.org/bot%s/answerCallbackQuery?callback_query_id=%stext=ok", apiToken, callbackQueryID)
 | 
			
		||||
	resp, err := http.Get(answerURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
		return
 | 
			
		||||
@@ -139,8 +181,8 @@ func answerCallbackQuery(callbackQueryID string) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deleteMessage(chatID int, messageID int) {
 | 
			
		||||
	deleteUrl := fmt.Sprintf("https://api.telegram.org/bot%s/deleteMessage?chat_id=%d&message_id=%d", apiToken, chatID, messageID)
 | 
			
		||||
	resp, err := http.Get(deleteUrl)
 | 
			
		||||
	deleteURL := fmt.Sprintf("https://api.telegram.org/bot%s/deleteMessage?chat_id=%d&message_id=%d", apiToken, chatID, messageID)
 | 
			
		||||
	resp, err := http.Get(deleteURL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Println(err)
 | 
			
		||||
		return
 | 
			
		||||
@@ -163,6 +205,33 @@ func logBody(respBody io.ReadCloser) {
 | 
			
		||||
	log.Println(string(body))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func loadData(dataPath string, chats *sync.Map) error {
 | 
			
		||||
	items, err := os.ReadDir(dataPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	for _, de := range items {
 | 
			
		||||
		if de.IsDir() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		name := de.Name()
 | 
			
		||||
		if !strings.HasPrefix(name, "chkchat") {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		var gl glist.GList
 | 
			
		||||
		data, err := os.ReadFile(path.Join(dataPath, name))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("failed read file, name: %s, err:%w", name, err)
 | 
			
		||||
		}
 | 
			
		||||
		err = json.Unmarshal(data, &gl)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("failed to parse data, data:%s, err:%w", data, err)
 | 
			
		||||
		}
 | 
			
		||||
		chats.Store(gl.ChatID, &gl)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Example data
 | 
			
		||||
update_id: 547400623
 | 
			
		||||
message:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user