From a6be456e6ff3ae5c8480a2a393de69b7b8996bee Mon Sep 17 00:00:00 2001 From: wrench Date: Tue, 28 Nov 2023 00:07:28 +0530 Subject: [PATCH] feat(cache): implement cache system --- cache/cache.go | 67 +++++++++++++++++++++++++++++++++++++++++++++++ commands/start.go | 2 +- go.mod | 6 +++-- go.sum | 4 +++ main.go | 6 +++-- routes/stream.go | 7 +---- utils/helpers.go | 39 +++++++++++++++++++++------ 7 files changed, 112 insertions(+), 19 deletions(-) create mode 100644 cache/cache.go diff --git a/cache/cache.go b/cache/cache.go new file mode 100644 index 00000000..772503f2 --- /dev/null +++ b/cache/cache.go @@ -0,0 +1,67 @@ +package cache + +import ( + "EverythingSuckz/fsb/types" + "bytes" + "encoding/gob" + "sync" + + "github.com/coocood/freecache" + "github.com/gotd/td/tg" + "go.uber.org/zap" +) + +var cache *Cache + +type Cache struct { + cache *freecache.Cache + mu sync.RWMutex + log *zap.Logger +} + +func InitCache(log *zap.Logger) { + log = log.Named("cache") + gob.Register(types.File{}) + gob.Register(tg.InputDocumentFileLocation{}) + defer log.Sugar().Info("Initialized") + cache = &Cache{cache: freecache.NewCache(10 * 1024 * 1024), log: log} +} + +func GetCache() *Cache { + return cache +} + +func (c *Cache) Get(key string, value *types.File) error { + c.mu.RLock() + defer c.mu.RUnlock() + data, err := cache.cache.Get([]byte(key)) + if err != nil { + return err + } + dec := gob.NewDecoder(bytes.NewReader(data)) + err = dec.Decode(&value) + if err != nil { + return err + } + return nil +} + +func (c *Cache) Set(key string, value *types.File, expireSeconds int) error { + c.mu.Lock() + defer c.mu.Unlock() + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + err := enc.Encode(value) + if err != nil { + return err + } + cache.cache.Set([]byte(key), buf.Bytes(), expireSeconds) + return nil +} + +func (c *Cache) Delete(key string) error { + c.mu.Lock() + defer c.mu.Unlock() + cache.cache.Del([]byte(key)) + return nil +} diff --git a/commands/start.go b/commands/start.go index a60e00e9..e04b7dc5 100644 --- a/commands/start.go +++ b/commands/start.go @@ -8,7 +8,7 @@ import ( func (m *command) LoadStart(dispatcher dispatcher.Dispatcher) { log := m.log.Named("start") - defer log.Sugar().Info("Loaded start command") + defer log.Sugar().Info("Loaded") dispatcher.AddHandler(handlers.NewCommand("start", start)) } diff --git a/go.mod b/go.mod index 0f77e9a6..f80e83e9 100644 --- a/go.mod +++ b/go.mod @@ -5,25 +5,26 @@ go 1.21.3 require ( github.com/celestix/gotgproto v1.0.0-beta13 github.com/gin-gonic/gin v1.9.1 + github.com/gotd/td v0.89.0 github.com/joho/godotenv v1.5.1 github.com/kelseyhightower/envconfig v1.4.0 + github.com/quantumsheep/range-parser v1.1.0 ) require ( github.com/AnimeKaizoku/cacher v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/go-faster/errors v0.7.0 // indirect github.com/go-faster/jx v1.1.0 // indirect github.com/go-faster/xor v1.0.0 // indirect github.com/gotd/ige v0.2.2 // indirect github.com/gotd/neo v0.1.5 // indirect - github.com/gotd/td v0.89.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/klauspost/compress v1.17.3 // indirect github.com/mattn/go-sqlite3 v1.14.18 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/quantumsheep/range-parser v1.1.0 // indirect github.com/segmentio/asm v1.2.0 // indirect go.opentelemetry.io/otel v1.21.0 // indirect go.opentelemetry.io/otel/trace v1.21.0 // indirect @@ -39,6 +40,7 @@ require ( require ( github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/coocood/freecache v1.2.4 github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect diff --git a/go.sum b/go.sum index 6e3f04f6..6d3bf0f9 100644 --- a/go.sum +++ b/go.sum @@ -7,9 +7,13 @@ github.com/celestix/gotgproto v1.0.0-beta13 h1:5BlGUJMwJmXrWD9RhBbHRuJhbPkv5CJd0 github.com/celestix/gotgproto v1.0.0-beta13/go.mod h1:WHwqFwgXEpFN/2ReP+vVnxCs2IvULaRK7n0N5ouVmDw= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M= +github.com/coocood/freecache v1.2.4/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/main.go b/main.go index 07f47964..29b489bf 100644 --- a/main.go +++ b/main.go @@ -2,6 +2,7 @@ package main import ( "EverythingSuckz/fsb/bot" + "EverythingSuckz/fsb/cache" "EverythingSuckz/fsb/config" "EverythingSuckz/fsb/routes" "EverythingSuckz/fsb/types" @@ -22,15 +23,16 @@ func main() { utils.InitLogger() log := utils.Logger mainLogger := log.Named("Main") - mainLogger.Info("Starting server...") + mainLogger.Info("Starting server") config.Load(log) router := getRouter(log) - + _, err := bot.StartClient(log) if err != nil { log.Info(err.Error()) return } + cache.InitCache(log) mainLogger.Info("Server started", zap.Int("port", config.ValueOf.Port)) mainLogger.Info("File Stream Bot", zap.String("version", versionString)) err = router.Run(fmt.Sprintf(":%d", config.ValueOf.Port)) diff --git a/routes/stream.go b/routes/stream.go index 50c5ea94..2eee3e48 100644 --- a/routes/stream.go +++ b/routes/stream.go @@ -43,12 +43,7 @@ func getStreamRoute(ctx *gin.Context) { var start, end int64 rangeHeader := r.Header.Get("Range") - messageMedia, err := utils.GetMessageMedia(ctx, bot.Bot.Client, messageID) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - file, err := utils.FileFromMedia(messageMedia) + file, err := utils.FileFromMessage(ctx, bot.Bot.Client, messageID) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return diff --git a/utils/helpers.go b/utils/helpers.go index 474ed023..cad92d23 100644 --- a/utils/helpers.go +++ b/utils/helpers.go @@ -1,6 +1,7 @@ package utils import ( + "EverythingSuckz/fsb/cache" "EverythingSuckz/fsb/config" "EverythingSuckz/fsb/types" "context" @@ -31,14 +32,6 @@ func GetTGMessage(ctx context.Context, client *telegram.Client, messageID int) ( } } -func GetMessageMedia(ctx context.Context, client *telegram.Client, messageID int) (tg.MessageMediaClass, error) { - message, err := GetTGMessage(ctx, client, messageID) - if err != nil { - return nil, err - } - return message.Media, nil -} - func FileFromMedia(media tg.MessageMediaClass) (*types.File, error) { switch media := media.(type) { case *tg.MessageMediaDocument: @@ -65,6 +58,36 @@ func FileFromMedia(media tg.MessageMediaClass) (*types.File, error) { return nil, fmt.Errorf("unexpected type %T", media) } +func FileFromMessage(ctx context.Context, client *telegram.Client, messageID int) (*types.File, error) { + key := fmt.Sprintf("file:%d", messageID) + log := Logger.Named("GetMessageMedia") + var cachedMedia types.File + err := cache.GetCache().Get(key, &cachedMedia) + if err == nil { + log.Sugar().Debug("Using cached media message properties") + return &cachedMedia, nil + } + log.Sugar().Debug("Fetching file properties from message ID") + message, err := GetTGMessage(ctx, client, messageID) + if err != nil { + return nil, err + } + file, err := FileFromMedia(message.Media) + if err != nil { + return nil, err + } + err = cache.GetCache().Set( + key, + file, + 3600, + ) + if err != nil { + return nil, err + } + return file, nil + // TODO: add photo support +} + func GetChannelById(ctx context.Context, client *telegram.Client) (*tg.InputChannel, error) { channel := &tg.InputChannel{} inputChannel := &tg.InputChannel{