implement persistence

This commit is contained in:
Balakrishnan Balasubramanian 2022-12-26 18:09:06 -05:00
parent ea082ccc89
commit 8711ec1245
2 changed files with 122 additions and 8 deletions

View File

@ -3,9 +3,15 @@ package glist
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"os"
"path"
"sync" "sync"
"time"
) )
var DataPath string
type Entry struct { type Entry struct {
Text string `json:"text"` Text string `json:"text"`
Checked bool `json:"checked"` Checked bool `json:"checked"`
@ -27,6 +33,42 @@ func NewGList(chatID int, items ...string) *GList {
return &g 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 { func (g *GList) Add(text string) error {
for _, item := range g.Items { for _, item := range g.Items {
if item.Text == text { if item.Text == text {
@ -34,6 +76,7 @@ func (g *GList) Add(text string) error {
} }
} }
g.Items = append(g.Items, Entry{text, false}) g.Items = append(g.Items, Entry{text, false})
reqs <- g
return nil return nil
} }
@ -41,6 +84,7 @@ func (g *GList) Toggle(text string) error {
for i, item := range g.Items { for i, item := range g.Items {
if item.Text == text { if item.Text == text {
g.Items[i].Checked = !g.Items[i].Checked g.Items[i].Checked = !g.Items[i].Checked
reqs <- g
return nil return nil
} }
} }
@ -55,6 +99,7 @@ func (g *GList) ClearChecked() {
} }
} }
g.Items = remaining g.Items = remaining
reqs <- g
} }
type button struct { type button struct {

85
main.go
View File

@ -1,3 +1,4 @@
// This is the main package
package main package main
import ( import (
@ -7,18 +8,58 @@ import (
"io" "io"
"log" "log"
"net/http" "net/http"
"os"
"path"
"strconv"
"strings"
"sync" "sync"
"time" "time"
"gitea.balki.me/telegram-msgchkbox/glist" "gitea.balki.me/telegram-msgchkbox/glist"
) )
var apiToken = "421791796:AAE4wPbcqfLP1GNeGR3RTBiyX16fCj3HPAM" var apiToken string
func main() { 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 var chats sync.Map
fmt.Println("vim-go") if err := loadData(dataPath, &chats); err != nil {
http.HandleFunc("/zeexkfcsdjpmncirkyelwzotjmmefcqtcogrfwnafidionxiacwnslwuhbwfuppjgwzbmazd", func(w http.ResponseWriter, r *http.Request) { 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")) w.Write([]byte("ok"))
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)
if err != nil { if err != nil {
@ -57,7 +98,9 @@ func main() {
g, _ := chats.LoadOrStore(chatID, glist.NewGList(chatID)) g, _ := chats.LoadOrStore(chatID, glist.NewGList(chatID))
gl := g.(*glist.GList) gl := g.(*glist.GList)
go handleTextAdded(gl, update.Message.Text) go handleTextAdded(gl, update.Message.Text)
} else if update.CallbackQuery != nil { } else if update.CallbackQuery != nil {
defer func() { go answerCallbackQuery(update.CallbackQuery.ID) }() defer func() { go answerCallbackQuery(update.CallbackQuery.ID) }()
chatID := update.CallbackQuery.Message.Chat.ID chatID := update.CallbackQuery.Message.Chat.ID
@ -71,7 +114,6 @@ func main() {
} }
}) })
port := 28923
log.Panic(http.ListenAndServe(fmt.Sprintf(":%v", port), nil)) log.Panic(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
} }
@ -129,8 +171,8 @@ func sendList(gl *glist.GList, method string) {
} }
func answerCallbackQuery(callbackQueryID string) { func answerCallbackQuery(callbackQueryID string) {
answerUrl := fmt.Sprintf("https://api.telegram.org/bot%s/answerCallbackQuery?callback_query_id=%stext=ok", apiToken, callbackQueryID) answerURL := fmt.Sprintf("https://api.telegram.org/bot%s/answerCallbackQuery?callback_query_id=%stext=ok", apiToken, callbackQueryID)
resp, err := http.Get(answerUrl) resp, err := http.Get(answerURL)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
@ -139,8 +181,8 @@ func answerCallbackQuery(callbackQueryID string) {
} }
func deleteMessage(chatID int, messageID int) { 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) deleteURL := fmt.Sprintf("https://api.telegram.org/bot%s/deleteMessage?chat_id=%d&message_id=%d", apiToken, chatID, messageID)
resp, err := http.Get(deleteUrl) resp, err := http.Get(deleteURL)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
return return
@ -163,6 +205,33 @@ func logBody(respBody io.ReadCloser) {
log.Println(string(body)) 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 /* Example data
update_id: 547400623 update_id: 547400623
message: message: