commit cffa26dfa81a323c8f77b93d90d963e50d3e6c66 Author: nefrace Date: Sun Dec 4 17:30:08 2022 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a35dd15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +fonts +database +doomer \ No newline at end of file diff --git a/bot.go b/bot.go new file mode 100644 index 0000000..c5d614a --- /dev/null +++ b/bot.go @@ -0,0 +1,55 @@ +package main + +import ( + "log" + "strings" + + "github.com/NicoNex/echotron/v3" +) + +type bot struct { + chatID int64 + doomer *Doomer + db *Db + logChatID int64 + echotron.API +} + +func createBot(token string, doomer *Doomer, db *Db) echotron.NewBotFn { + return func(chatID int64) echotron.Bot { + return &bot{ + chatID: chatID, + doomer: doomer, + db: db, + logChatID: -1001688182314, + API: echotron.NewAPI(token), + } + } +} + +func (b *bot) Update(update *echotron.Update) { + log.Println("We got update") + if update.InlineQuery != nil { + b.inlineHandler(update) + return + } + if update.Message != nil { + if update.Message.Text == "/help" { + b.showHelp(update) + return + } + if strings.HasPrefix(update.Message.Text, "/start") { + b.showHelp(update) + return + } + if strings.HasPrefix(update.Message.Text, "/gen") { + b.genHandler(update) + return + } + } +} + +func NewDispatcher(token string, doomer *Doomer, db *Db) *echotron.Dispatcher { + dp := echotron.NewDispatcher(token, createBot(token, doomer, db)) + return dp +} diff --git a/botHandlers.go b/botHandlers.go new file mode 100644 index 0000000..8b23891 --- /dev/null +++ b/botHandlers.go @@ -0,0 +1,110 @@ +package main + +import ( + "fmt" + "log" + "strings" + + "github.com/NicoNex/echotron/v3" + "github.com/google/uuid" +) + +func (b *bot) inlineHandler(update *echotron.Update) { + store := b.db.Prefix(FileIDItem) + log.Println("It's an inline") + if update.InlineQuery.Query == "" { + return + } + text := update.InlineQuery.Query + hashed := hash(text) + log.Printf("HASH OF QUERY: %s", hashed) + bgTypes := []BackgroundType{Black, White, Gradient} + results := []echotron.InlineQueryResult{} + for _, v := range bgTypes { + key := hashed + ":" + fmt.Sprint(v) + fileid, err := store.GetItem(key) + if err != nil { + log.Println("Image does not exist") + img, err := b.doomer.GenImage(text, v) + if err != nil { + log.Printf("Cannot gen image of type %v: %v\n", v, err) + continue + } else { + res, err := b.SendPhoto(echotron.NewInputFileBytes(hashed+".png", *img), b.logChatID, &echotron.PhotoOptions{}) + if err != nil { + log.Printf("Cannot upload image to log chat: %v", err) + continue + } + fileid = []byte(res.Result.Photo[0].FileID) + store.StoreItem(key, []byte(fileid)) + } + } + results = append(results, echotron.InlineQueryResultCachedPhoto{ + ID: uuid.NewString(), + PhotoFileID: string(fileid), + Type: echotron.InlinePhoto, + }) + } + log.Printf("%v", results) + _, err := b.AnswerInlineQuery(update.InlineQuery.ID, results, &echotron.InlineQueryOptions{ + CacheTime: 0, + SwitchPmText: "Сгенерировать PNG качеством повыше", + SwitchPmParameter: "doom", + }) + if err != nil { + log.Printf("Cannot send answer to query: %v", err) + } +} + +func (b *bot) genHandler(update *echotron.Update) { + store := b.db.Prefix(ImageItem) + text := update.Message.Text[4:] + if strings.TrimSpace(text) == "" { + b.SendMessage("Добавьте к команде любой текст", update.Message.Chat.ID, &echotron.MessageOptions{}) + return + } + hashed := hash(text) + + bgTypes := []BackgroundType{Transparent, Black, White, Gradient} + group := []echotron.GroupableInputMedia{} + for _, v := range bgTypes { + key := hashed + ":" + fmt.Sprint(v) + imgurl := "https://doom.nefrace.ru/" + key + _, err := store.GetItem(key) + if err != nil { + log.Printf("Image does not exist: %v", err) + image, err := b.doomer.GenImage(text, v) + if err != nil { + log.Printf("Cannot gen image of type %v: %v\n", v, err) + continue + } else { + // img = *image + err := store.StoreItem(key, *image) + if err != nil { + log.Printf("Cannot store image in db: %v", err) + continue + } + } + } + // filename := key + ".png" + group = append(group, echotron.InputMediaDocument{ + Type: echotron.MediaTypeDocument, + Media: echotron.NewInputFileID(imgurl), + }) + + } + _, err := b.SendMediaGroup(update.Message.Chat.ID, group, &echotron.MediaGroupOptions{}) + if err != nil { + log.Println(err) + } +} + +const helpMessage string = "1 \\- Используйте команду `/gen Ваш текст`, чтобы сгенерировать четыре PNG\\-изображения в разных цветовых исполнениях, включая прозрачный фон\\. Файлы отправляются документами для сохранения качества\\.\n" + + "2 \\- Просто упомяните бота в любом чате при помощи `@doomtextbot Ваш текст` с последующим текстом, чтобы быстро сгенерировать три варианта картинки для отправки в чат\\." + +func (b *bot) showHelp(update *echotron.Update) { + _, err := b.SendMessage(helpMessage, update.Message.Chat.ID, &echotron.MessageOptions{ParseMode: echotron.MarkdownV2}) + if err != nil { + log.Printf("Cannot show help message: %v", err) + } +} diff --git a/db.go b/db.go new file mode 100644 index 0000000..bef27b8 --- /dev/null +++ b/db.go @@ -0,0 +1,85 @@ +package main + +import ( + "time" + + badger "github.com/dgraph-io/badger/v3" +) + +type Db struct { + db *badger.DB +} + +func NewDatabase() (*Db, error) { + db, err := badger.Open(badger.DefaultOptions("./database")) + if err != nil { + return nil, err + } + return &Db{ + db: db, + }, nil +} + +type PrefixStore struct { + db *badger.DB + prefix ItemType +} + +type ItemType string + +const ( + GenericItem ItemType = "" + ImageItem ItemType = "images:" + FileIDItem ItemType = "fileid:" +) + +func (d *Db) Prefix(prefix ItemType) PrefixStore { + return PrefixStore{ + db: d.db, + prefix: prefix, + } +} + +func (d *PrefixStore) StoreItem(key string, input []byte) error { + fullKey := string(d.prefix) + key + return d.db.Update(func(txn *badger.Txn) error { + e := badger.NewEntry([]byte(fullKey), input).WithTTL(time.Hour * 60 * 24 * 10) + return txn.SetEntry(e) + }) +} + +func (d *PrefixStore) ItemExists(key string) bool { + exists := false + fullKey := string(d.prefix) + key + d.db.View(func(txn *badger.Txn) error { + _, err := txn.Get([]byte(fullKey)) + if err == nil { + exists = true + } + return err + }) + return exists +} + +func (d *PrefixStore) GetItem(key string) ([]byte, error) { + var image []byte + fullKey := string(d.prefix) + key + err := d.db.View(func(txn *badger.Txn) error { + result, err := txn.Get([]byte(fullKey)) + if err != nil { + return err + } + return result.Value(func(val []byte) error { + image = append(image, val...) + return nil + }) + }) + if err != nil { + return nil, err + } + return image, nil +} + +func (d *Db) Close() { + d.db.Close() +} diff --git a/doomer.go b/doomer.go new file mode 100644 index 0000000..a17165b --- /dev/null +++ b/doomer.go @@ -0,0 +1,43 @@ +package main + +import ( + "log" + "strings" + + "github.com/fogleman/gg" + "golang.org/x/image/font" +) + +type Doomer struct { + Face font.Face + Border int +} + +func NewDoomer() *Doomer { + face, err := gg.LoadFontFace("./fonts/AmazDooMLeft.ttf", 108) + if err != nil { + log.Fatalf("Error loading font: %v", err) + } + return &Doomer{ + Face: face, + Border: 12, + } +} + +func (d *Doomer) GenImage(text string, bgType BackgroundType) (*[]byte, error) { + textW, textH := d.measureText(text) + + imgW, imgH := int(textW)+d.Border*2, int(textH)+d.Border*2 + ctx := gg.NewContext(imgW, imgH) + img := NewImage(ctx, bgType, d.Face) + img.GenText(text) + return img.ToBytes() +} + +func (d *Doomer) measureText(text string) (float64, float64) { + textctx := gg.NewContext(1, 1) + textctx.SetFontFace(d.Face) + textWrapped := textctx.WordWrap(text, 600) + textW, textH := textctx.MeasureMultilineString(strings.Join(textWrapped, "\n"), 1) + return textW, textH +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..9d425aa --- /dev/null +++ b/go.mod @@ -0,0 +1,38 @@ +module nefrace.ru/doomer + +go 1.19 + +require ( + github.com/NicoNex/echotron/v3 v3.21.0 + github.com/fogleman/gg v1.3.0 + github.com/google/uuid v1.3.0 + golang.org/x/image v0.1.0 +) + +require ( + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.1.1 // indirect + github.com/dgraph-io/badger/v3 v3.2103.4 // indirect + github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect + github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect + github.com/golang/protobuf v1.3.1 // indirect + github.com/golang/snappy v0.0.3 // indirect + github.com/google/flatbuffers v1.12.1 // indirect + github.com/klauspost/compress v1.12.3 // indirect + github.com/labstack/echo/v4 v4.9.1 // indirect + github.com/labstack/gommon v0.4.0 // indirect + github.com/mattn/go-colorable v0.1.11 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.1 // indirect + go.opencensus.io v0.22.5 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect + golang.org/x/text v0.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..1113ab2 --- /dev/null +++ b/go.sum @@ -0,0 +1,175 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/NicoNex/echotron/v3 v3.21.0 h1:6YDSYA/AGlPxWoUdOIRrBvUZ0RhRa2W4taS4eSrbdXw= +github.com/NicoNex/echotron/v3 v3.21.0/go.mod h1:LpP5IyHw0y+DZUZMBgXEDAF9O8feXrQu7w7nlJzzoZI= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v3 v3.2103.4 h1:WE1B07YNTTJTtG9xjBcSW2wn0RJLyiV99h959RKZqM4= +github.com/dgraph-io/badger/v3 v3.2103.4/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= +github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= +github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.12.3 h1:G5AfA94pHPysR56qqrkO2pxEexdDzrpFJ6yt/VqWxVU= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/labstack/echo/v4 v4.9.1 h1:GliPYSpzGKlyOhqIbG8nmHBo3i1saKWFOgh41AN3b+Y= +github.com/labstack/echo/v4 v4.9.1/go.mod h1:Pop5HLc+xoc4qhTZ1ip6C0RtP7Z+4VzRLWZZFKqbbjo= +github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= +github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.1.11 h1:nQ+aFkoE2TMGc0b68U2OKSexC+eq46+XwZzWXHRmPYs= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52WA1u4= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk= +golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/hasher.go b/hasher.go new file mode 100644 index 0000000..af4fc37 --- /dev/null +++ b/hasher.go @@ -0,0 +1,12 @@ +package main + +import ( + "crypto/sha1" + "encoding/hex" +) + +func hash(text string) string { + hasher := sha1.New() + hasher.Write([]byte(text)) + return hex.EncodeToString(hasher.Sum(nil)) +} diff --git a/image.go b/image.go new file mode 100644 index 0000000..fb81a06 --- /dev/null +++ b/image.go @@ -0,0 +1,93 @@ +package main + +import ( + "bytes" + "image/color" + "image/png" + + "github.com/fogleman/gg" + "golang.org/x/image/font" +) + +type BackgroundType int + +const ( + Transparent BackgroundType = iota + White + Black + Gradient +) + +type Image struct { + ctx *gg.Context + face font.Face + bgType BackgroundType + fgColor color.Color +} + +func NewImage(ctx *gg.Context, bgType BackgroundType, face font.Face) Image { + fgColor := color.Black + if bgType == Transparent || bgType == Black || bgType == Gradient { + fgColor = color.White + } + return Image{ + ctx: ctx, + bgType: bgType, + fgColor: fgColor, + face: face, + } +} + +func (img *Image) GenText(text string) { + img.MakeBackground() + img.Print(text) +} + +func (img *Image) ToBytes() (*[]byte, error) { + buff := new(bytes.Buffer) + err := png.Encode(buff, img.ctx.Image()) + if err != nil { + return nil, err + } + b := buff.Bytes() + return &b, nil +} + +func (img *Image) MakeBackground() { + if img.bgType == Transparent { + return + } + if img.bgType == Gradient { + img.fillGradient() + } else { + img.fillBG() + } +} + +func (img *Image) Print(text string) { + imgW, imgH := img.ctx.Width(), img.ctx.Height() + img.ctx.SetColor(img.fgColor) + img.ctx.SetFontFace(img.face) + img.ctx.DrawStringWrapped(text, float64(imgW)/2, float64(imgH)/2, 0.5, 0.5, 600, 1, gg.AlignCenter) +} + +func (img *Image) fillGradient() { + imgW, imgH := img.ctx.Width(), img.ctx.Height() + grad := gg.NewLinearGradient(0, 0, 0, float64(imgH)) + grad.AddColorStop(0, color.RGBA{200, 50, 50, 255}) + grad.AddColorStop(1, color.RGBA{20, 0, 0, 255}) + img.ctx.SetFillStyle(grad) + img.ctx.DrawRectangle(0, 0, float64(imgW), float64(imgH)) + img.ctx.Fill() +} + +func (img *Image) fillBG() { + col := color.White + if img.bgType == Black { + col = color.Black + } + imgW, imgH := img.ctx.Width(), img.ctx.Height() + img.ctx.SetFillStyle(gg.NewSolidPattern(col)) + img.ctx.DrawRectangle(0, 0, float64(imgW), float64(imgH)) + img.ctx.Fill() +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..1da4d10 --- /dev/null +++ b/main.go @@ -0,0 +1,25 @@ +package main + +import ( + "flag" + "log" +) + +func main() { + db, err := NewDatabase() + if err != nil { + log.Fatalf("Cant open database: %v", err) + } + defer db.Close() + doomer := NewDoomer() + token := flag.String("token", "", "Telegram Bot API Token") + host := flag.String("host", ":3000", "Web server host ip:port") + flag.Parse() + dp := NewDispatcher(*token, doomer, db) + + server := CreateServer(db) + go func() { + log.Fatal(server.Start(*host)) + }() + log.Println(dp.Poll()) +} diff --git a/server.go b/server.go new file mode 100644 index 0000000..eed90b9 --- /dev/null +++ b/server.go @@ -0,0 +1,38 @@ +package main + +import ( + "bytes" + "image/jpeg" + "image/png" + + "github.com/labstack/echo/v4" +) + +func CreateServer(db *Db) *echo.Echo { + e := echo.New() + e.GET("/:key", ImageHandler(db)) + return e +} + +func ImageHandler(db *Db) echo.HandlerFunc { + store := db.Prefix(ImageItem) + return func(c echo.Context) error { + key := c.Param("key") + isJpeg := c.QueryParam("jpeg") + img, err := store.GetItem(key) + if err != nil { + return c.String(404, err.Error()) + } + + if isJpeg != "1" { + return c.Blob(200, "image/png", img) + } + png, _ := png.Decode(bytes.NewReader(img)) + jpg := new(bytes.Buffer) + err = jpeg.Encode(jpg, png, &jpeg.Options{Quality: 95}) + if err != nil { + return c.String(500, err.Error()) + } + return c.Blob(200, "image/jpeg", jpg.Bytes()) + } +}