Compare commits
75 Commits
Author | SHA1 | Date |
---|---|---|
Maxim Lebedev | 7ce10980cc | |
Maxim Lebedev | 1b7945cb71 | |
Maxim Lebedev | 6e555e5b3f | |
Maxim Lebedev | 70cbac4ae7 | |
Maxim Lebedev | 9427686124 | |
Maxim Lebedev | 3880a984bd | |
Maxim Lebedev | d4573efbe7 | |
Maxim Lebedev | 0356043e9e | |
Maxim Lebedev | abb8cc1000 | |
Maxim Lebedev | 0163bc4250 | |
Maxim Lebedev | a92d262db4 | |
Maxim Lebedev | 681adc2417 | |
Maxim Lebedev | 13982a89e0 | |
Maxim Lebedev | 781b82ad3c | |
Maxim Lebedev | 43687c7e96 | |
Maxim Lebedev | 37ae8a0c60 | |
Maxim Lebedev | 1655495e02 | |
Maxim Lebedev | 83cf8e4640 | |
Maxim Lebedev | 0e032b366a | |
Maxim Lebedev | dc15309bd5 | |
Maxim Lebedev | 470f03f0ad | |
Maxim Lebedev | 36a95a03a4 | |
Maxim Lebedev | 0ba205822b | |
Maxim Lebedev | b6ce8ae8cb | |
Maxim Lebedev | ff052c888d | |
Maxim Lebedev | db48dcc904 | |
Maxim Lebedev | 17aa5037c9 | |
Maxim Lebedev | 1501ade093 | |
Maxim Lebedev | ef8d8b3cb0 | |
Maxim Lebedev | ca09c43b99 | |
Maxim Lebedev | fd141f0676 | |
Maxim Lebedev | 1726be5ab4 | |
Maxim Lebedev | 2ea08030e3 | |
Maxim Lebedev | 717c9ac4cb | |
Maxim Lebedev | fcb3ee966f | |
Maxim Lebedev | 3559881329 | |
Maxim Lebedev | ea054b2e3c | |
Maxim Lebedev | 875284ccbf | |
Maxim Lebedev | 67400fbab3 | |
Maxim Lebedev | 5619f740c1 | |
Maxim Lebedev | 5e855fb5f2 | |
Maxim Lebedev | 7d4ef75d72 | |
Maxim Lebedev | 520fcd639e | |
Maxim Lebedev | 2f9f98548c | |
Maxim Lebedev | ce736a80d9 | |
Maxim Lebedev | 1836b6dc63 | |
Maxim Lebedev | 93c8ce7863 | |
Maxim Lebedev | 98847153e7 | |
Maxim Lebedev | 900ec6fee1 | |
Maxim Lebedev | 0fa5661917 | |
Maxim Lebedev | da611ab04b | |
Maxim Lebedev | 5be629c14c | |
Maxim Lebedev | 96327f139d | |
Maxim Lebedev | feca5b3bd2 | |
Maxim Lebedev | 66b6ce4e9b | |
Maxim Lebedev | f95c2af53d | |
Maxim Lebedev | 20a688c62d | |
Maxim Lebedev | 9f632d1686 | |
Maxim Lebedev | 09b42aaae9 | |
Maxim Lebedev | 29808690ea | |
Maxim Lebedev | 4e21f2f296 | |
Maxim Lebedev | 86df2de85c | |
Maxim Lebedev | db98b9e322 | |
Maxim Lebedev | bf162fdff4 | |
Maxim Lebedev | f2c691e8b8 | |
Maxim Lebedev | 784e934128 | |
Maxim Lebedev | 2808ca58a9 | |
Maxim Lebedev | ecd223bc61 | |
Maxim Lebedev | 997315ed73 | |
Maxim Lebedev | 412341b3b0 | |
Maxim Lebedev | 5fdff653b9 | |
Maxim Lebedev | fce573dd59 | |
Maxim Lebedev | b0ab2c9226 | |
Maxim Lebedev | 17b1b162c9 | |
Maxim Lebedev | 5a59a23e20 |
|
@ -16,8 +16,9 @@
|
|||
.glide/
|
||||
|
||||
# Production files must not be in public repository
|
||||
MyPackBot
|
||||
vendor/
|
||||
/mypackbot
|
||||
stickers.db
|
||||
configs/config.yaml
|
||||
configs/production.yaml
|
||||
cert.key
|
||||
cert.pem
|
|
@ -1,47 +0,0 @@
|
|||
image: golang:alpine
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- /go/src/github.com
|
||||
- /go/src/gitlab.com
|
||||
- /go/src/golang.org
|
||||
- /go/src/google.golang.org
|
||||
- /go/src/gopkg.in
|
||||
|
||||
stages:
|
||||
- test
|
||||
- build
|
||||
|
||||
before_script:
|
||||
- apk add --no-cache git build-base bash
|
||||
- mkdir -p /go/src/gitlab.com/$CI_PROJECT_NAMESPACE /go/src/_/builds
|
||||
- cp -r $CI_PROJECT_DIR /go/src/gitlab.com/$CI_PROJECT_PATH
|
||||
- ln -s /go/src/gitlab.com/$CI_PROJECT_NAMESPACE /go/src/_/builds/$CI_PROJECT_NAMESPACE
|
||||
- make dep
|
||||
|
||||
unit_tests:
|
||||
stage: test
|
||||
script:
|
||||
- make test
|
||||
|
||||
.race_detector:
|
||||
stage: test
|
||||
script:
|
||||
- make race
|
||||
|
||||
code_coverage:
|
||||
stage: test
|
||||
script:
|
||||
- make coverage
|
||||
|
||||
lint_code:
|
||||
stage: test
|
||||
script:
|
||||
- go get github.com/go-critic/go-critic/cmd/gocritic
|
||||
- go install github.com/go-critic/go-critic/cmd/gocritic
|
||||
- make lint
|
||||
|
||||
build:
|
||||
stage: build
|
||||
script:
|
||||
- make
|
17
Makefile
17
Makefile
|
@ -1,32 +1,37 @@
|
|||
PROJECT_NAMESPACE := $(CI_PROJECT_NAMESPACE)
|
||||
PROJECT_NAME := $(CI_PROJECT_NAME)
|
||||
PROJECT_PATH := $(PROJECT_NAMESPACE)/$(PROJECT_NAME)
|
||||
PACKAGE_NAME := "gitlab.com/$(PROJECT_PATH)"
|
||||
PACKAGE_NAME := gitlab.com/$(PROJECT_PATH)
|
||||
PACKAGE_PATH := $(GOPATH)/src/$(PACKAGE_NAME)
|
||||
PACKAGE_LIST := $(shell go list $(PACKAGE_NAME)/... | grep -v /vendor/)
|
||||
GO_FILES := $(shell find . -name '*.go' | grep -v /vendor/ | grep -v _test.go)
|
||||
GIT_COMMIT := $(shell git rev-list -1 HEAD)
|
||||
|
||||
.PHONY: all lint test rase coverage dep build clean
|
||||
|
||||
all: build
|
||||
|
||||
lint: ## Lint the files
|
||||
@golangci-lint run $(PACKAGE_PATH)/...
|
||||
@gocritic check-project $(PACKAGE_PATH)
|
||||
|
||||
test: ## Run unittests
|
||||
@go test -short $(PACKAGE_NAME)/test/...
|
||||
|
||||
race: dep ## Run data race detector
|
||||
@go test -race -short ${PACKAGE_LIST}
|
||||
|
||||
coverage: ## Generate global code coverage report
|
||||
@go test -cover -v -coverpkg=$(PACKAGE_NAME) ${PACKAGE_LIST}
|
||||
@go test -cover -v -coverpkg=$(PACKAGE_NAME)/... ${PACKAGE_LIST}
|
||||
|
||||
dep: ## Get the dependencies
|
||||
@go get -v -d -t ${PACKAGE_LIST}
|
||||
@go get -v -d -t $(PACKAGE_NAME)/...
|
||||
|
||||
build: dep ## Build the binary file
|
||||
@go build -i -v $(PACKAGE_NAME)
|
||||
build: dep ## Build the binary files
|
||||
@go build -ldflags "-X main.gitCommit=$(GIT_COMMIT)" -i -v $(PACKAGE_NAME)/cmd/...
|
||||
|
||||
clean: ## Remove previous build
|
||||
@rm -f $(PROJECT_NAME)
|
||||
|
||||
help: ## Display this help screen
|
||||
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
@grep -h -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
image: golang:alpine
|
||||
|
||||
stages:
|
||||
- test
|
||||
- review
|
||||
- build
|
||||
|
||||
before_script:
|
||||
- apk add --no-cache git build-base bash make
|
||||
- mkdir -p /go/src/gitlab.com/$CI_PROJECT_NAMESPACE /go/src/_/builds
|
||||
- cp -r $CI_PROJECT_DIR /go/src/gitlab.com/$CI_PROJECT_NAMESPACE
|
||||
- ln -s /go/src/gitlab.com/$CI_PROJECT_NAMESPACE /go/src/_/builds/$CI_PROJECT_NAMESPACE
|
||||
- make dep
|
||||
|
||||
unit_tests:
|
||||
stage: test
|
||||
script:
|
||||
- make test
|
||||
|
||||
.race_detector:
|
||||
stage: test
|
||||
script:
|
||||
- make race
|
||||
|
||||
.code_coverage:
|
||||
stage: test
|
||||
script:
|
||||
- make coverage
|
||||
coverage: '/^coverage:\s(\d+(?:\.\d+)?%)/'
|
||||
|
||||
lint_code:
|
||||
stage: review
|
||||
script:
|
||||
- go get github.com/golangci/golangci-lint/cmd/golangci-lint github.com/go-critic/go-critic/cmd/gocritic
|
||||
- go install github.com/golangci/golangci-lint/cmd/golangci-lint github.com/go-critic/go-critic/cmd/gocritic
|
||||
- make lint
|
||||
allow_failure: true
|
||||
|
||||
stable:
|
||||
stage: build
|
||||
only:
|
||||
- master
|
||||
except:
|
||||
- develop
|
||||
script:
|
||||
- make
|
||||
|
||||
nightly:
|
||||
stage: build
|
||||
only:
|
||||
- schedules
|
||||
- develop
|
||||
except:
|
||||
- master
|
||||
script:
|
||||
- make
|
|
@ -0,0 +1,58 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"path/filepath"
|
||||
|
||||
bunt "github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/migrator"
|
||||
"gitlab.com/toby3d/mypackbot/internal/store"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
flagGroup = flag.Int64("group", 0, "proxy group for migration")
|
||||
flagNew = flag.String("new", filepath.Join(".", "new.db"), "filepath to new database file")
|
||||
flagOld = flag.String("old", filepath.Join(".", "old.db"), "filepath to old database file")
|
||||
flagToken = flag.String("token", "", "bot token")
|
||||
)
|
||||
|
||||
flag.Parse()
|
||||
|
||||
bot, err := tg.New(*flagToken)
|
||||
if err != nil {
|
||||
log.Fatalln("ERROR:", err.Error())
|
||||
}
|
||||
|
||||
oldDB, err := bunt.Open(*flagOld)
|
||||
if err != nil {
|
||||
log.Fatalln("ERROR OLD DB:", err.Error())
|
||||
}
|
||||
defer oldDB.Close()
|
||||
|
||||
newDB, err := db.Open(*flagNew)
|
||||
if err != nil {
|
||||
log.Fatalln("ERROR NEW DB:", err.Error())
|
||||
}
|
||||
defer newDB.Close()
|
||||
|
||||
users := store.NewUsersStore(newDB)
|
||||
stickers := store.NewStickersStore(newDB)
|
||||
usersStickers := store.NewUsersStickersStore(newDB, users, stickers)
|
||||
|
||||
if err = migrator.AutoMigrate(migrator.AutoMigrateConfig{
|
||||
Bot: bot,
|
||||
GroupID: *flagGroup,
|
||||
Stickers: stickers,
|
||||
UsersStickers: usersStickers,
|
||||
Users: users,
|
||||
OldDB: oldDB,
|
||||
}); err != nil {
|
||||
log.Fatalln("ERROR:", err.Error())
|
||||
}
|
||||
|
||||
log.Println("Done!")
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
// Code generated by running "go generate" in golang.org/x/text. DO NOT EDIT.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
"golang.org/x/text/message/catalog"
|
||||
)
|
||||
|
||||
type dictionary struct {
|
||||
index []uint32
|
||||
data string
|
||||
}
|
||||
|
||||
func (d *dictionary) Lookup(key string) (data string, ok bool) {
|
||||
p := messageKeyToIndex[key]
|
||||
start, end := d.index[p], d.index[p+1]
|
||||
if start == end {
|
||||
return "", false
|
||||
}
|
||||
return d.data[start:end], true
|
||||
}
|
||||
|
||||
func init() {
|
||||
dict := map[string]catalog.Dictionary{
|
||||
"en": &dictionary{index: enIndex, data: enData},
|
||||
"ru": &dictionary{index: ruIndex, data: ruData},
|
||||
}
|
||||
fallback := language.MustParse("en")
|
||||
cat, err := catalog.NewFromMap(dict, catalog.Fallback(fallback))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
message.DefaultCatalog = cat
|
||||
}
|
||||
|
||||
var messageKeyToIndex = map[string]int{
|
||||
"☺️ Oh, you missed @toby3d birthday on November 4th!\nIf you like this bot, why not send him some birthday greetings and a little birthday gift? It is not yet too late to make him happy!": 17,
|
||||
"👋 Hi %s, I'm %s!\nThanks to me, you can collect almost any media content in Telegram without any limits, in any chat via inline mode.": 0,
|
||||
"👍 Imported!": 5,
|
||||
"👍 Removed!": 7,
|
||||
"👍 Updated!": 6,
|
||||
"💡 Add any text and/or emoji(s) as an argument of this command to change its search properties.": 12,
|
||||
"💡 Use /del command as an reply to the sticker/photo to remove it from the feed of your collection.": 4,
|
||||
"💡 Use the /add command as a reply to the sticker/photo to add this media to your collection feed. Given an argument, the result of this command will be equivalent to the /edit command.": 2,
|
||||
"💡 Use the /edit command with an argument from any character set as a reply to a sticker/photo to change the search query of this media in the feed of your collection. If this media is not in the feed, then the result of this command will be equivalent to the /add command with the same argument.": 3,
|
||||
"💸 Make a donation": 18,
|
||||
"📥 Import %s set": 14,
|
||||
"📥 Import this photo": 10,
|
||||
"📥 Import this sticker": 13,
|
||||
"🔥 Remove %s set": 15,
|
||||
"🔥 Remove this photo": 11,
|
||||
"🔧 Let's hack!": 21,
|
||||
"🕵 Found %d result(s)": 8,
|
||||
"🕺 HacktoberFest is here!\n\nIf you are a beginner or already an experienced golang-developer, now is a great time to help improve the quality of the code of this bot. Choose issue to your taste and offer your PR!": 20,
|
||||
"🤔 What to do with this?": 9,
|
||||
"🤖 Here is a list of commands that I understand, some of them [may] or (should) contain an argument:\n/start - start all over again\n/help [other command] - get a list of available commands or help and a demonstration of a specific command\n/add [query] - add media from reply to your collection [with custom search query]\n/edit (query) - change query to reply media\n/del - remove reply media from your collection": 1,
|
||||
"🤝 Use referral links": 19,
|
||||
"🥳 4 November? It's a @toby3d birthday!\nIf you like this bot, then why not send him a congratulation along with a small gift? This will make him incredibly happy!": 16,
|
||||
}
|
||||
|
||||
var enIndex = []uint32{ // 23 elements
|
||||
0x00000000, 0x0000008e, 0x0000022b, 0x000002e7,
|
||||
0x00000412, 0x00000478, 0x00000487, 0x00000495,
|
||||
0x000004a3, 0x000004be, 0x000004d9, 0x000004f0,
|
||||
0x00000507, 0x00000569, 0x00000582, 0x00000598,
|
||||
0x000005ae, 0x00000653, 0x00000710, 0x00000725,
|
||||
0x0000073d, 0x00000813, 0x00000824,
|
||||
} // Size: 116 bytes
|
||||
|
||||
const enData string = "" + // Size: 2084 bytes
|
||||
"\x02👋 Hi %[1]s, I'm %[2]s!\x0aThanks to me, you can collect almost any m" +
|
||||
"edia content in Telegram without any limits, in any chat via inline mode" +
|
||||
".\x02🤖 Here is a list of commands that I understand, some of them [may] " +
|
||||
"or (should) contain an argument:\x0a/start - start all over again\x0a/he" +
|
||||
"lp [other command] - get a list of available commands or help and a demo" +
|
||||
"nstration of a specific command\x0a/add [query] - add media from reply t" +
|
||||
"o your collection [with custom search query]\x0a/edit (query) - change q" +
|
||||
"uery to reply media\x0a/del - remove reply media from your collection" +
|
||||
"\x02💡 Use the /add command as a reply to the sticker/photo to add this m" +
|
||||
"edia to your collection feed. Given an argument, the result of this comm" +
|
||||
"and will be equivalent to the /edit command.\x02💡 Use the /edit command " +
|
||||
"with an argument from any character set as a reply to a sticker/photo to" +
|
||||
" change the search query of this media in the feed of your collection. I" +
|
||||
"f this media is not in the feed, then the result of this command will be" +
|
||||
" equivalent to the /add command with the same argument.\x02💡 Use /del co" +
|
||||
"mmand as an reply to the sticker/photo to remove it from the feed of you" +
|
||||
"r collection.\x02👍 Imported!\x02👍 Updated!\x02👍 Removed!\x02🕵 Found %[1]" +
|
||||
"d result(s)\x02🤔 What to do with this?\x02📥 Import this photo\x02🔥 Remov" +
|
||||
"e this photo\x02💡 Add any text and/or emoji(s) as an argument of this co" +
|
||||
"mmand to change its search properties.\x02📥 Import this sticker\x02📥 Imp" +
|
||||
"ort %[1]s set\x02🔥 Remove %[1]s set\x02🥳 4 November? It's a @toby3d birt" +
|
||||
"hday!\x0aIf you like this bot, then why not send him a congratulation al" +
|
||||
"ong with a small gift? This will make him incredibly happy!\x02☺️ Oh, yo" +
|
||||
"u missed @toby3d birthday on November 4th!\x0aIf you like this bot, why " +
|
||||
"not send him some birthday greetings and a little birthday gift? It is n" +
|
||||
"ot yet too late to make him happy!\x02💸 Make a donation\x02🤝 Use referra" +
|
||||
"l links\x02🕺 HacktoberFest is here!\x0a\x0aIf you are a beginner or alre" +
|
||||
"ady an experienced golang-developer, now is a great time to help improve" +
|
||||
" the quality of the code of this bot. Choose issue to your taste and off" +
|
||||
"er your PR!\x02🔧 Let's hack!"
|
||||
|
||||
var ruIndex = []uint32{ // 23 elements
|
||||
0x00000000, 0x00000106, 0x000003c1, 0x00000514,
|
||||
0x00000714, 0x000007c6, 0x000007e7, 0x00000800,
|
||||
0x00000815, 0x00000846, 0x0000086c, 0x00000895,
|
||||
0x000008b0, 0x0000096f, 0x0000099c, 0x000009c2,
|
||||
0x000009da, 0x00000b0f, 0x00000c68, 0x00000c88,
|
||||
0x00000cb1, 0x00000dfd, 0x00000e0e,
|
||||
} // Size: 116 bytes
|
||||
|
||||
const ruData string = "" + // Size: 3598 bytes
|
||||
"\x02👋 Привет %[1]s, я %[2]s!\x0aБлагодаря мне, ты можешь коллекционирова" +
|
||||
"ть в inline-режиме практически любой медиа-контент Telegram без огранич" +
|
||||
"ений, в любом чате.\x02🤖 Вот список команд которые я понимаю, некоторые" +
|
||||
" из них [могут] или (должны) содержать аргументы:\x0a/start - начать всё" +
|
||||
" заново\x0a/help [другая команда] - получить список всех доступных коман" +
|
||||
"д или справку о конкретной команде\x0a/add [текст] - добавить медиа из " +
|
||||
"ответа в коллекцию [с указанным поисковым тегом]\x0a/edit (текст) - изм" +
|
||||
"енить поисковый тег медиа из ответа\x0a/del - удалить медиа из ответа и" +
|
||||
"з коллекции\x02💡 Используй команду /add как ответ на стикер/фото чтобы " +
|
||||
"добавить это медиа в ленту твоей коллекции. При наличии аргумента резул" +
|
||||
"ьтат действия этой команды будет эквивалентно команде /edit.\x02💡 Испол" +
|
||||
"ьзуй команду /edit с аргументом из любого набора символов как ответ на " +
|
||||
"стикер/фото чтобы изменить поисковую строку этого медиа в ленте твоей к" +
|
||||
"оллекции. Если это медиа отсутствует в ленте, то результат действия это" +
|
||||
"й команды будет эквивалентно команде /add с тем же аргументом.\x02💡 Исп" +
|
||||
"ользуй команду /del как ответ на стикер/фото чтобы убрать это медиа из " +
|
||||
"ленты твоей коллекции.\x02👍 Импортировано!\x02👍 Обновлено!\x02👍 Удалено" +
|
||||
"!\x02🕵 Найдено %[1]d результатов\x02🤔 Что с этим делать?\x02📥 Импортиров" +
|
||||
"ать фото\x02🔥 Убрать фото\x02💡 Добавь любой текст и/или эмодзи в качест" +
|
||||
"ве аргумента этой команды чтобы изменить поисковые свойства.\x02📥 Импор" +
|
||||
"тировать стикер\x02📥 Импортировать %[1]s\x02🔥 Убрать %[1]s\x02🥳 4-е Ноя" +
|
||||
"бря? Это день рождения @toby3d!\x0aЕсли тебе нравится этот бот, то поче" +
|
||||
"му бы не отправить ему поздравления вместе с небольшим подарком? Это не" +
|
||||
"сказанно его осчастливит!\x02☺️ Ой, ты пропустил день рождения @toby3d " +
|
||||
"4-го Ноября!\x0aЕсли тебе нравится этот бот, то почему бы не отправить е" +
|
||||
"му поздравления вместе с небольшим подарком? Ещё не слишком поздно его " +
|
||||
"порадовать!\x02💸 Пожертвование\x02🤝 Реферальные ссылки\x02🕺 Хактоберфес" +
|
||||
"т уже здесь!\x0a\x0aЕсли ты начинающий или уже опытный golang-разработч" +
|
||||
"ик, то сейчас хорошее время помочь улучшить качество кода этого бота. В" +
|
||||
"ыбери issue на свой вкус и предложи PR!\x02🔧 Let's hack!"
|
||||
|
||||
// Total table size 5914 bytes (5KiB); checksum: 490F86A
|
|
@ -0,0 +1,55 @@
|
|||
//go:generate gotext -dir=./../../ -srclang=en update -out=catalog.go -lang=en,ru .
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"golang.org/x/text/feature/plural"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
func init() {
|
||||
_ = message.Set(language.English, "🕵 Found %d result(s)", plural.Selectf(1, "%d",
|
||||
"zero", "🤷 Nothing found",
|
||||
"one", "🕵 Found %d result",
|
||||
"many", "🕵 Found %[1]d results",
|
||||
))
|
||||
|
||||
_ = message.Set(language.Russian, "🕵 Found %d result(s)", plural.Selectf(1, "%d",
|
||||
"zero", "🤷 Ничего не найдено",
|
||||
"one", "🕵 Найден %[1]d результат",
|
||||
"<5", "🕵 Найдено %[1]d результата",
|
||||
"many", "🕵 Найдено %[1]d результатов",
|
||||
))
|
||||
}
|
||||
|
||||
func main() {
|
||||
flagConfig := flag.String("config", filepath.Join(".", "config.yaml"), "set specific path to config")
|
||||
|
||||
flag.Parse()
|
||||
log.Println("Current build version:", common.Version.String())
|
||||
|
||||
bot, err := internal.New(*flagConfig)
|
||||
if err != nil {
|
||||
log.Fatalln("ERROR:", err.Error())
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err = bot.Run(); err != nil {
|
||||
log.Fatalln("ERROR:", err.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
<-sig
|
||||
}
|
|
@ -4,4 +4,19 @@ telegram:
|
|||
set: https://toby3d.github.io
|
||||
listen: /bot
|
||||
serve: 0.0.0.0:2368
|
||||
channel: -1000000000000
|
||||
allowed_updates:
|
||||
- inline_query
|
||||
- message
|
||||
- channel_post
|
||||
- callback_query
|
||||
long_poll:
|
||||
limit: 100
|
||||
offset: 0
|
||||
timeout: 1
|
||||
allowed_updates:
|
||||
- inline_query
|
||||
- message
|
||||
- channel_post
|
||||
- callback_query
|
||||
database:
|
||||
filepath: ./development.db
|
|
@ -0,0 +1,32 @@
|
|||
module gitlab.com/toby3d/mypackbot
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/json-iterator/go v1.1.9
|
||||
github.com/kirillDanshin/dlog v0.0.0-20170728000807-97d876b12bf9
|
||||
github.com/klauspost/compress v1.10.2 // indirect
|
||||
github.com/pelletier/go-toml v1.6.0 // indirect
|
||||
github.com/spf13/afero v1.2.2 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.6.2
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/tidwall/buntdb v1.1.2
|
||||
github.com/tidwall/gjson v1.5.0 // indirect
|
||||
github.com/tidwall/pretty v1.0.1 // indirect
|
||||
github.com/timshannon/bolthold v0.0.0-20200224174833-bf6268f136e7
|
||||
github.com/valyala/fasthttp v1.9.0
|
||||
github.com/valyala/fastjson v1.5.0
|
||||
gitlab.com/toby3d/telegram v0.0.0-20200228130040-d70608c147c0
|
||||
go.etcd.io/bbolt v1.3.3
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
|
||||
golang.org/x/text v0.3.2
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
gopkg.in/ini.v1 v1.52.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
)
|
||||
|
||||
replace go.etcd.io/bbolt v1.3.3 => github.com/etcd-io/bbolt v1.3.3
|
|
@ -0,0 +1,236 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
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=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/etcd-io/bbolt v1.3.3 h1:gSJmxrs37LgTqR/oyJBWok6k6SvXEUerFTbltIhXkBM=
|
||||
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/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/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kirillDanshin/dlog v0.0.0-20170728000807-97d876b12bf9 h1:mA7k8E2Vrmyj5CW/D1XZBFmohVNi7jf757vibGwzRbo=
|
||||
github.com/kirillDanshin/dlog v0.0.0-20170728000807-97d876b12bf9/go.mod h1:l8CN7iyX1k2xlsTYVTpCtwBPcxThf/jLWDGVcF6T/bM=
|
||||
github.com/kirillDanshin/myutils v0.0.0-20160713214838-182269b1fbcc h1:OkOhOn3WBUmfATC1NsA3rBlgHGkjk0KGnR5akl/8uXc=
|
||||
github.com/kirillDanshin/myutils v0.0.0-20160713214838-182269b1fbcc/go.mod h1:Bt95qRxLvpdmASW9s2tTxGdQ5ma4o4n8QFhCvzCew/M=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.9.7 h1:hYW1gP94JUmAhBtJ+LNz5My+gBobDxPR1iVuKug26aA=
|
||||
github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.10.2 h1:Znfn6hXZAHaLPNnlqUYRrBSReFHYybslgv4PTiyz6P0=
|
||||
github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
|
||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.6.2 h1:7aKfF+e8/k68gda3LOjo5RxiUqddoFxVq4BKBPrxk5E=
|
||||
github.com/spf13/viper v1.6.2/go.mod h1:t3iDnF5Jlj76alVNuyFBk5oUMCvsrkbvZK0WQdfDi5k=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 h1:G6Z6HvJuPjG6XfNGi/feOATzeJrfgTNJY+rGrHbA04E=
|
||||
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
|
||||
github.com/tidwall/buntdb v1.1.2 h1:noCrqQXL9EKMtcdwJcmuVKSEjqu1ua99RHHgbLTEHRo=
|
||||
github.com/tidwall/buntdb v1.1.2/go.mod h1:xAzi36Hir4FarpSHyfuZ6JzPJdjRZ8QlLZSntE2mqlI=
|
||||
github.com/tidwall/gjson v1.3.4 h1:On5waDnyKKk3SWE4EthbjjirAWXp43xx5cKCUZY1eZw=
|
||||
github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
||||
github.com/tidwall/gjson v1.5.0 h1:QCssIUI7J0RStkzIcI4A7O6P8rDA5wi5IPf70uqKSxg=
|
||||
github.com/tidwall/gjson v1.5.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
||||
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb h1:5NSYaAdrnblKByzd7XByQEJVT8+9v0W/tIY0Oo4OwrE=
|
||||
github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M=
|
||||
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
|
||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8=
|
||||
github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e h1:+NL1GDIUOKxVfbp2KoJQD9cTQ6dyP2co9q4yzmT9FZo=
|
||||
github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao=
|
||||
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 h1:Otn9S136ELckZ3KKDyCkxapfufrqDqwmGjcHfAyXRrE=
|
||||
github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ=
|
||||
github.com/timshannon/bolthold v0.0.0-20200224174833-bf6268f136e7 h1:jLXW7TX5THBQxw+sFH+TjBQuD8lksMwygI4BARqHdJI=
|
||||
github.com/timshannon/bolthold v0.0.0-20200224174833-bf6268f136e7/go.mod h1:jUigdmrbdCxcIDEFrq82t4X9805XZfwFZoYUap0ET/U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
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/fasthttp v1.7.1 h1:UHtt5/7O70RSUZTR/hSu0PNWMAfWx5AtsPp9Jk+g17M=
|
||||
github.com/valyala/fasthttp v1.7.1/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
||||
github.com/valyala/fasthttp v1.9.0 h1:hNpmUdy/+ZXYpGy0OBfm7K0UQTzb73W0T0U4iJIVrMw=
|
||||
github.com/valyala/fasthttp v1.9.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
|
||||
github.com/valyala/fastjson v1.5.0 h1:DGrb4wEYso2HdGLyLmNoyNCQnCWfjd8yhghPv5/5YQg=
|
||||
github.com/valyala/fastjson v1.5.0/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
gitlab.com/toby3d/telegram v0.0.0-20200218162808-858deeef8eeb h1:KY5aTKkQZGNFfjBoJf3lq374ekqRYQy4MB/tyi54MoQ=
|
||||
gitlab.com/toby3d/telegram v0.0.0-20200218162808-858deeef8eeb/go.mod h1:vV0ZvEaNCSTcjGgN8Y4osyg1Fv8eyCvPaIfSMAqfltE=
|
||||
gitlab.com/toby3d/telegram v0.0.0-20200228130040-d70608c147c0 h1:tVrOHMDJ6x0gA/t1f9S6tcyyygSQENHjvFEuLE0x6Fk=
|
||||
gitlab.com/toby3d/telegram v0.0.0-20200228130040-d70608c147c0/go.mod h1:vV0ZvEaNCSTcjGgN8Y4osyg1Fv8eyCvPaIfSMAqfltE=
|
||||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
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-20181221193216-37e7f081c4d4/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.52.0 h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4=
|
||||
gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
101
i18n/en.all.yaml
101
i18n/en.all.yaml
|
@ -1,101 +0,0 @@
|
|||
button_add_pack:
|
||||
other: "\U0001F4E6 Add pack"
|
||||
button_add_sticker:
|
||||
other: ➕ Add sticker
|
||||
button_cancel:
|
||||
other: ❌ Cancel
|
||||
button_del_pack:
|
||||
other: "\U0001F5D1 Delete pack"
|
||||
button_del_sticker:
|
||||
other: "\U0001F5D1 Delete sticker"
|
||||
button_inline_empty:
|
||||
other: Your pack is empty
|
||||
button_inline_nothing:
|
||||
other: Not found stickers for {{.Query}}
|
||||
button_inline_search:
|
||||
one: You have {{.Count}} sticker
|
||||
other: You have {{.Count}} stickers
|
||||
button_inline_select:
|
||||
other: Select sticker
|
||||
button_reset:
|
||||
other: "\U0001F525 Reset pack"
|
||||
button_share:
|
||||
other: Use your stickers pack!
|
||||
cancel_add_pack:
|
||||
other: You cancelled the process of adding a new packs to yours.
|
||||
cancel_add_sticker:
|
||||
other: You cancelled the process of adding a new stickers to your pack.
|
||||
cancel_del_pack:
|
||||
other: You cancelled the process of removing a sticker packs from yours.
|
||||
cancel_del_sticker:
|
||||
other: You cancelled the process of removing a stickers from your pack.
|
||||
cancel_error:
|
||||
other: Nothing to cancel.
|
||||
cancel_reset:
|
||||
other: You cancelled the process of reseting your stickers pack.
|
||||
error_already_add_pack:
|
||||
other: All stickers from *{{.SetTitle}}* pack is already in yours.
|
||||
error_already_add_sticker:
|
||||
other: This sticker is already in your pack.
|
||||
error_already_del_pack:
|
||||
other: Maybe this pack is already removed from yours.
|
||||
error_already_del_sticker:
|
||||
other: Maybe this sticker is already removed from your pack.
|
||||
error_already_reset:
|
||||
other: There is nothing to reset, pack is already empty.
|
||||
error_empty_add_pack:
|
||||
other: You try to add a sticker that exists outside the packs. Use /{{.AddStickerCommand}}
|
||||
instead.
|
||||
error_empty_del:
|
||||
other: There is nothing to remove, pack is empty.
|
||||
error_reset_phrase:
|
||||
other: Invalid phrase of resetting. This action has been canceled.
|
||||
error_unknown:
|
||||
other: |-
|
||||
I do not know what to do with this sticker.
|
||||
Please run /{{.AddStickerCommand}}, /{{.AddPackCommand}}, /{{.DeleteStickerCommand}} or /{{.DeletePackCommand}} command first.
|
||||
key_phrase:
|
||||
other: Yes, I am totally sure.
|
||||
reply_add_pack:
|
||||
other: Send an existing stickers from any other packs to add the entire packs to
|
||||
yourself.
|
||||
reply_add_sticker:
|
||||
other: Send an existing stickers from any other packs to add it to yourself.
|
||||
reply_del_pack:
|
||||
other: Send an existing stickers from your pack to delete its entire set.
|
||||
reply_del_sticker:
|
||||
other: Send an existing stickers from your pack for removing it.
|
||||
reply_help:
|
||||
other: |
|
||||
/{{.AddStickerCommand}} - add a single sticker to your pack
|
||||
/{{.AddPackCommand}} - add a full other pack to your pack
|
||||
/{{.DeleteStickerCommand}} - remove a single sticker from your pack
|
||||
/{{.DeletePackCommand}} - remove a sticker set from your pack
|
||||
/{{.ResetCommand}} - remove all stickers from your pack
|
||||
/{{.CancelCommand}} - cancel the current operation
|
||||
|
||||
To view and send stickers from your pack, just type `@{{.Username}}` (and space) in any chat.
|
||||
reply_reset:
|
||||
other: |
|
||||
This operation will remove *all* stickers from your pack and *this can't be undone*.
|
||||
|
||||
Send `{{.KeyPhrase}}` to confirm what you really want to reset my brain (oh god why).
|
||||
Or use /{{.CancelCommand}} for abort current operation.
|
||||
reply_start:
|
||||
other: |
|
||||
Hello, I'm the [@{{.Username}}](tg://user?id={{.ID}})!
|
||||
I can create your personal pack with stickers from others packs.
|
||||
Without limits and installing. In any chat. For free.
|
||||
reply_switch_button:
|
||||
other: This button will help you quickly call your pack to select the sticker you
|
||||
want.
|
||||
success_add_pack:
|
||||
other: The sticker pack *{{.SetTitle}}* was successfully added to yours!
|
||||
success_add_sticker:
|
||||
other: The sticker was successfully added to your pack!
|
||||
success_del_pack:
|
||||
other: The sticker pack *{{.SetTitle}}* was successfully removed from yours!
|
||||
success_del_sticker:
|
||||
other: The sticker was successfully removed from your pack!
|
||||
success_reset:
|
||||
other: The contents of your pack are completely reset!..
|
|
@ -1 +0,0 @@
|
|||
{}
|
101
i18n/ru.all.yaml
101
i18n/ru.all.yaml
|
@ -1,101 +0,0 @@
|
|||
button_add_pack:
|
||||
other: "\U0001F4E6 Добавить набор"
|
||||
button_add_sticker:
|
||||
other: ➕ Добавить стикер
|
||||
button_cancel:
|
||||
other: ❌ Отменить
|
||||
button_del_pack:
|
||||
other: "\U0001F5D1 Удалить набор"
|
||||
button_del_sticker:
|
||||
other: "\U0001F5D1 Удалить стикер"
|
||||
button_inline_empty:
|
||||
other: Твой набор пуст
|
||||
button_inline_nothing:
|
||||
other: Не найдены стикеры для {{.Query}}
|
||||
button_inline_search:
|
||||
few: У тебя {{.Count}} стикера
|
||||
many: У тебя {{.Count}} стикеров
|
||||
one: У тебя {{.Count}} стикер
|
||||
other: У тебя {{.Count}} стикеров
|
||||
button_inline_select:
|
||||
other: Выбрать стикер
|
||||
button_reset:
|
||||
other: "\U0001F525 Сбросить набор"
|
||||
button_share:
|
||||
other: Воспользоваться твоим набором!
|
||||
cancel_add_pack:
|
||||
other: Ты отменил процесс добавления новых наборов в твой.
|
||||
cancel_add_sticker:
|
||||
other: Ты отменил процесс добавления новых стикеров в твой набор.
|
||||
cancel_del_pack:
|
||||
other: Ты отменил процесс удаления наборов из твоего набора.
|
||||
cancel_del_sticker:
|
||||
other: Ты отменил процесс удаления стикера из твоего набора.
|
||||
cancel_error:
|
||||
other: Нечего отменять.
|
||||
cancel_reset:
|
||||
other: Ты отменил процесс сброса твоего набора.
|
||||
error_already_add_pack:
|
||||
other: Все стикеры *{{.SetTitle}}* уже в твоём наборе.
|
||||
error_already_add_sticker:
|
||||
other: Этот стикер уже в твоём наборе.
|
||||
error_already_del_pack:
|
||||
other: Вероятно этот набор уже удалён из твоего.
|
||||
error_already_del_sticker:
|
||||
other: Вероятно этот стикер уже удалён из твоего набора.
|
||||
error_already_reset:
|
||||
other: Нечего сбрасывать, набор уже пуст.
|
||||
error_empty_add_pack:
|
||||
other: Кажется ты пытаешься добавить собственный стикер. Используй для этого /{{.AddStickerCommand}}.
|
||||
error_empty_del:
|
||||
other: Нечего удалять, набор уже пуст.
|
||||
error_reset_phrase:
|
||||
other: Неправильная фраза для сброса. Действие было отменено.
|
||||
error_unknown:
|
||||
other: |-
|
||||
Я понятия не имею что делать с этим стикером.
|
||||
Пожалуйста, сначала примени /{{.AddStickerCommand}}, /{{.AddPackCommand}}, /{{.DeleteStickerCommand}} или /{{.DeletePackCommand}}.
|
||||
key_phrase:
|
||||
other: Да, я абсолютно уверен.
|
||||
reply_add_pack:
|
||||
other: Пришли стикеры из любых других наборов чтобы целиком добавить их наборы в
|
||||
свой.
|
||||
reply_add_sticker:
|
||||
other: Пришли стикеры из любых других наборов чтобы по-одному добавить их в свой.
|
||||
reply_del_pack:
|
||||
other: Пришли стикер из своего набора чтобы удалить весь его набор.
|
||||
reply_del_sticker:
|
||||
other: Пришли стикер из своего набора чтобы удалить его.
|
||||
reply_help:
|
||||
other: |
|
||||
/{{.AddStickerCommand}} - по-одному добавляет стикеры в твой набор
|
||||
/{{.AddPackCommand}} - добавляет сразу весь набор в твой
|
||||
/{{.DeleteStickerCommand}} - по-одному удаляет стикер из твоего набора
|
||||
/{{.DeletePackCommand}} - удаляет набор стикеров из твоего набора
|
||||
/{{.ResetCommand}} - удаляет все стикеры из твоего набора
|
||||
/{{.CancelCommand}} - отменяет текущую операцию
|
||||
|
||||
Для просмотра и отправки стикеров из твоего набора просто набери `@{{.Username}}` (и пробел) в любом чате.
|
||||
reply_reset:
|
||||
other: |
|
||||
Эта операция удалит *все* стикеры из твоего набора и *это не может быть отменено*.
|
||||
|
||||
Напиши `{{.KeyPhrase}}` чтобы подтвердить своё намерение обнулить мои мозги (о боже зачем).
|
||||
Или используй /{{.CancelCommand}} чтобы отменить текущую операцию.
|
||||
reply_start:
|
||||
other: |
|
||||
Привет, я [@{{.Username}}](tg://user?id={{.ID}})!
|
||||
Я могу создать твой персональный набор стикеров из других наборов.
|
||||
Без ограничений и установки. В любых чатах. Бесплатно.
|
||||
reply_switch_button:
|
||||
other: Эта кнопка поможет тебе быстро вызвать твой набор для выбора нужного стикера.
|
||||
success_add_pack:
|
||||
other: Набор *{{.SetTitle}}* успешно добавлен в твой!
|
||||
success_add_sticker:
|
||||
other: Стикер успешно добавлен в твой набор!
|
||||
success_del_pack:
|
||||
other: Набор *{{.SetTitle}}* успешно удалён из твоего набора!
|
||||
success_del_sticker:
|
||||
other: Стикер успешно удалён из твоего набора!
|
||||
success_reset:
|
||||
other: Сброс твоего набора успешно произведён!..
|
|
@ -1 +0,0 @@
|
|||
{}
|
|
@ -1,23 +0,0 @@
|
|||
button_inline_empty:
|
||||
other: Your pack is empty
|
||||
button_inline_nothing:
|
||||
other: Not found stickers for {{.Query}}
|
||||
button_inline_search:
|
||||
one: You have {{.Count}} sticker
|
||||
other: You have {{.Count}} stickers
|
||||
button_inline_select:
|
||||
other: Select sticker
|
||||
button_share:
|
||||
other: Use your stickers pack!
|
||||
button_add_sticker:
|
||||
other: ➕ Add sticker
|
||||
button_add_pack:
|
||||
other: 📦 Add set
|
||||
button_del_sticker:
|
||||
other: 🗑 Delete sticker
|
||||
button_del_pack:
|
||||
other: 🗑 Delete set
|
||||
button_reset:
|
||||
other: 🔥 Reset pack
|
||||
button_cancel:
|
||||
other: ❌ Cancel
|
|
@ -1,12 +0,0 @@
|
|||
cancel_add_sticker:
|
||||
other: You cancelled the process of adding a new stickers to your pack.
|
||||
cancel_add_pack:
|
||||
other: You cancelled the process of adding a new sets to your pack.
|
||||
cancel_del_sticker:
|
||||
other: You cancelled the process of removing a stickers from your pack.
|
||||
cancel_del_pack:
|
||||
other: You cancelled the process of removing a sets from your pack.
|
||||
cancel_reset:
|
||||
other: You cancelled the process of reseting your stickers pack.
|
||||
cancel_error:
|
||||
other: Nothing to cancel.
|
|
@ -1,20 +0,0 @@
|
|||
error_already_add_sticker:
|
||||
other: This sticker is already in your pack.
|
||||
error_already_add_pack:
|
||||
other: All stickers from *{{.SetTitle}}* set is already in yours.
|
||||
error_already_del_sticker:
|
||||
other: Maybe this sticker is already removed from your pack.
|
||||
error_already_del_pack:
|
||||
other: Maybe this set is already removed from your pack.
|
||||
error_already_reset:
|
||||
other: There is nothing to reset, pack is already empty.
|
||||
error_reset_phrase:
|
||||
other: Invalid phrase of resetting. This action has been canceled.
|
||||
error_empty_del:
|
||||
other: There is nothing to remove, your pack is empty.
|
||||
error_empty_add_pack:
|
||||
other: You try to add a sticker that exists outside the packs. Use /{{.AddStickerCommand}} instead.
|
||||
error_unknown:
|
||||
other: |
|
||||
I do not know what to do with this sticker.
|
||||
Please run /{{.AddStickerCommand}}, /{{.AddPackCommand}}, /{{.DeleteStickerCommand}} or /{{.DeletePackCommand}} command first.
|
|
@ -1,31 +0,0 @@
|
|||
reply_start:
|
||||
other: |
|
||||
Hello, I'm the [@{{.Username}}](tg://user?id={{.ID}})!
|
||||
I can create your personal stickers pack with stickers from others sets.
|
||||
Without limits and installing. In any chat. For free.
|
||||
reply_add_sticker:
|
||||
other: Send an existing stickers from any other sets to add it to your pack.
|
||||
reply_add_pack:
|
||||
other: Send an existing stickers from any other sets to add the entire sets to your pack.
|
||||
reply_del_sticker:
|
||||
other: Send an existing stickers from your pack for removing it.
|
||||
reply_del_pack:
|
||||
other: Send an existing stickers from your pack to delete its entire set.
|
||||
reply_reset:
|
||||
other: |
|
||||
This operation will remove *all* stickers from your pack and *this can't be undone*.
|
||||
|
||||
Send `{{.KeyPhrase}}` to confirm what you really want to reset my brain (oh god why).
|
||||
Or use /{{.CancelCommand}} for abort current operation.
|
||||
reply_help:
|
||||
other: |
|
||||
/{{.AddStickerCommand}} - add a single sticker to your pack
|
||||
/{{.AddPackCommand}} - add a full stickers set to your pack
|
||||
/{{.DeleteStickerCommand}} - remove a single sticker from your pack
|
||||
/{{.DeletePackCommand}} - remove a sticker set from your pack
|
||||
/{{.ResetCommand}} - remove all stickers from your pack
|
||||
/{{.CancelCommand}} - cancel the current operation
|
||||
|
||||
To view and send stickers from your pack, just type `@{{.Username}}` (and space) in any chat.
|
||||
reply_switch_button:
|
||||
other: This button will help you quickly call your pack to select the sticker you want.
|
|
@ -1,10 +0,0 @@
|
|||
success_add_sticker:
|
||||
other: The sticker was successfully added to your pack!
|
||||
success_add_pack:
|
||||
other: The sticker pack *{{.SetTitle}}* was successfully added to yours!
|
||||
success_del_sticker:
|
||||
other: The sticker was successfully removed from your pack!
|
||||
success_del_pack:
|
||||
other: The sticker pack *{{.SetTitle}}* was successfully removed from yours!
|
||||
success_reset:
|
||||
other: The contents of your pack are completely reset!
|
|
@ -1,2 +0,0 @@
|
|||
key_phrase:
|
||||
other: Yes, I am totally sure.
|
|
@ -1,25 +0,0 @@
|
|||
button_inline_empty:
|
||||
other: Твой набор пуст
|
||||
button_inline_nothing:
|
||||
other: Не найдены стикеры для {{.Query}}
|
||||
button_inline_search:
|
||||
few: У тебя {{.Count}} стикера
|
||||
one: У тебя {{.Count}} стикер
|
||||
many: У тебя {{.Count}} стикеров
|
||||
other: У тебя {{.Count}} стикеров
|
||||
button_inline_select:
|
||||
other: Выбрать стикер
|
||||
button_share:
|
||||
other: Воспользоваться твоим набором!
|
||||
button_add_sticker:
|
||||
other: ➕ Добавить стикер
|
||||
button_add_pack:
|
||||
other: 📦 Добавить набор
|
||||
button_del_sticker:
|
||||
other: 🗑 Удалить стикер
|
||||
button_del_pack:
|
||||
other: 🗑 Удалить набор
|
||||
button_reset:
|
||||
other: 🔥 Сбросить набор
|
||||
button_cancel:
|
||||
other: ❌ Отменить
|
|
@ -1,12 +0,0 @@
|
|||
cancel_add_sticker:
|
||||
other: Ты отменил процесс добавления новых стикеров в твой набор.
|
||||
cancel_add_pack:
|
||||
other: Ты отменил процесс добавления новых наборов в твой.
|
||||
cancel_del_sticker:
|
||||
other: Ты отменил процесс удаления стикера из твоего набора.
|
||||
cancel_del_pack:
|
||||
other: Ты отменил процесс удаления наборов из твоего набора.
|
||||
cancel_reset:
|
||||
other: Ты отменил процесс сброса твоего набора.
|
||||
cancel_error:
|
||||
other: Нечего отменять.
|
|
@ -1,20 +0,0 @@
|
|||
error_already_add_sticker:
|
||||
other: Этот стикер уже в твоём наборе.
|
||||
error_already_add_pack:
|
||||
other: Все стикеры *{{.SetTitle}}* уже в твоём наборе.
|
||||
error_already_del_sticker:
|
||||
other: Вероятно этот стикер уже удалён из твоего набора.
|
||||
error_already_del_pack:
|
||||
other: Вероятно этот набор уже удалён из твоего.
|
||||
error_already_reset:
|
||||
other: Нечего сбрасывать, набор уже пуст.
|
||||
error_reset_phrase:
|
||||
other: Неправильная фраза для сброса. Действие было отменено.
|
||||
error_empty_del:
|
||||
other: Нечего удалять, набор уже пуст.
|
||||
error_empty_add_pack:
|
||||
other: Кажется ты пытаешься добавить собственный стикер. Используй для этого /{{.AddStickerCommand}}.
|
||||
error_unknown:
|
||||
other: |
|
||||
Я понятия не имею что делать с этим стикером.
|
||||
Пожалуйста, сначала примени /{{.AddStickerCommand}}, /{{.AddPackCommand}}, /{{.DeleteStickerCommand}} или /{{.DeletePackCommand}}.
|
|
@ -1,31 +0,0 @@
|
|||
reply_start:
|
||||
other: |
|
||||
Привет, я [@{{.Username}}](tg://user?id={{.ID}})!
|
||||
Я могу создать твой персональный набор стикеров из других наборов.
|
||||
Без ограничений и установки. В любых чатах. Бесплатно.
|
||||
reply_add_sticker:
|
||||
other: Пришли стикеры из любых других наборов чтобы по-одному добавить их в свой.
|
||||
reply_add_pack:
|
||||
other: Пришли стикеры из любых других наборов чтобы целиком добавить их наборы в свой.
|
||||
reply_del_sticker:
|
||||
other: Пришли стикер из своего набора чтобы удалить его.
|
||||
reply_del_pack:
|
||||
other: Пришли стикер из своего набора чтобы удалить весь его набор.
|
||||
reply_reset:
|
||||
other: |
|
||||
Эта операция удалит *все* стикеры из твоего набора и *это не может быть отменено*.
|
||||
|
||||
Напиши `{{.KeyPhrase}}` чтобы подтвердить своё намерение обнулить мои мозги (о боже зачем).
|
||||
Или используй /{{.CancelCommand}} чтобы отменить текущую операцию.
|
||||
reply_help:
|
||||
other: |
|
||||
/{{.AddStickerCommand}} - по-одному добавляет стикеры в твой набор
|
||||
/{{.AddPackCommand}} - добавляет сразу весь набор в твой
|
||||
/{{.DeleteStickerCommand}} - по-одному удаляет стикер из твоего набора
|
||||
/{{.DeletePackCommand}} - удаляет набор стикеров из твоего набора
|
||||
/{{.ResetCommand}} - удаляет все стикеры из твоего набора
|
||||
/{{.CancelCommand}} - отменяет текущую операцию
|
||||
|
||||
Для просмотра и отправки стикеров из твоего набора просто набери `@{{.Username}}` (и пробел) в любом чате.
|
||||
reply_switch_button:
|
||||
other: Эта кнопка поможет тебе быстро вызвать твой набор для выбора нужного стикера.
|
|
@ -1,10 +0,0 @@
|
|||
success_add_sticker:
|
||||
other: Стикер успешно добавлен в твой набор!
|
||||
success_add_pack:
|
||||
other: Набор *{{.SetTitle}}* успешно добавлен в твой!
|
||||
success_del_sticker:
|
||||
other: Стикер успешно удалён из твоего набора!
|
||||
success_del_pack:
|
||||
other: Набор *{{.SetTitle}}* успешно удалён из твоего набора!
|
||||
success_reset:
|
||||
other: Сброс твоего набора успешно произведён!
|
|
@ -1,2 +0,0 @@
|
|||
key_phrase:
|
||||
other: Да, я абсолютно уверен.
|
|
@ -1,35 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Action function check Message update on commands, sended stickers or other
|
||||
// user stuff if user state is not 'none'
|
||||
func Action(msg *tg.Message) {
|
||||
state, err := db.DB.GetUserState(msg.From)
|
||||
errors.Check(err)
|
||||
|
||||
log.Ln("state:", state)
|
||||
switch state {
|
||||
case models.StateAddSticker:
|
||||
Add(msg, false)
|
||||
case models.StateAddPack:
|
||||
Add(msg, true)
|
||||
case models.StateDeleteSticker:
|
||||
Delete(msg, false)
|
||||
case models.StateDeletePack:
|
||||
Delete(msg, true)
|
||||
case models.StateReset:
|
||||
Reset(msg)
|
||||
default:
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
Error(msg)
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Add action add sticker or set to user's pack
|
||||
func Add(msg *tg.Message, pack bool) {
|
||||
if !msg.IsSticker() {
|
||||
return
|
||||
}
|
||||
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("success_add_sticker"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
|
||||
if !pack {
|
||||
var exist bool
|
||||
sticker := msg.Sticker
|
||||
exist, err = db.DB.AddSticker(msg.From, sticker)
|
||||
errors.Check(err)
|
||||
|
||||
if exist {
|
||||
reply.Text = t("error_already_add_sticker")
|
||||
}
|
||||
|
||||
reply.ReplyMarkup = utils.CancelButton(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
return
|
||||
}
|
||||
|
||||
reply.Text = t("error_empty_add_pack", map[string]interface{}{
|
||||
"AddStickerCommand": models.CommandAddSticker,
|
||||
})
|
||||
|
||||
if msg.Sticker.SetName != "" {
|
||||
var set *tg.StickerSet
|
||||
set, err = bot.Bot.GetStickerSet(msg.Sticker.SetName)
|
||||
errors.Check(err)
|
||||
|
||||
log.Ln("SetTitle:", set.Title)
|
||||
reply.Text = t("success_add_pack", map[string]interface{}{
|
||||
"SetTitle": set.Title,
|
||||
})
|
||||
|
||||
allExists := true
|
||||
for i := range set.Stickers {
|
||||
var exist bool
|
||||
exist, err = db.DB.AddSticker(msg.From, &set.Stickers[i])
|
||||
errors.Check(err)
|
||||
|
||||
if !exist {
|
||||
allExists = false
|
||||
}
|
||||
}
|
||||
|
||||
log.Ln("All exists?", allExists)
|
||||
if allExists {
|
||||
reply.Text = t("error_already_add_pack", map[string]interface{}{
|
||||
"SetTitle": set.Title,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
reply.ReplyMarkup = utils.CancelButton(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Delete action remove sticker or set from user's pack
|
||||
func Delete(msg *tg.Message, pack bool) {
|
||||
if !msg.IsSticker() {
|
||||
return
|
||||
}
|
||||
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("success_del_sticker"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.CancelButton(t)
|
||||
|
||||
var notExist bool
|
||||
if pack {
|
||||
var set *tg.StickerSet
|
||||
set, err = bot.Bot.GetStickerSet(msg.Sticker.SetName)
|
||||
errors.Check(err)
|
||||
|
||||
log.Ln("SetName:", set.Title)
|
||||
reply.Text = t("success_del_pack", map[string]interface{}{
|
||||
"SetTitle": set.Title,
|
||||
})
|
||||
|
||||
notExist, err = db.DB.DeletePack(msg.From, msg.Sticker)
|
||||
if notExist {
|
||||
reply.Text = t("error_already_del_pack")
|
||||
}
|
||||
} else {
|
||||
notExist, err = db.DB.DeleteSticker(msg.From, msg.Sticker)
|
||||
if notExist {
|
||||
reply.Text = t("error_already_del_sticker")
|
||||
}
|
||||
}
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Error action send error reply about invalid user request
|
||||
func Error(msg *tg.Message) {
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(
|
||||
msg.Chat.ID, t("error_unknown", map[string]interface{}{
|
||||
"AddStickerCommand": models.CommandAddSticker,
|
||||
"AddPackCommand": models.CommandAddPack,
|
||||
"DeleteStickerCommand": models.CommandDeleteSticker,
|
||||
"DeletePackCommand": models.CommandDeletePack,
|
||||
}),
|
||||
)
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Reset action checks key phrase and reset user's pack
|
||||
func Reset(msg *tg.Message) {
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
if !strings.EqualFold(msg.Text, t("key_phrase")) {
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("error_reset_phrase"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = db.DB.ResetUser(msg.From)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("success_reset"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
package bot
|
||||
|
||||
import tg "gitlab.com/toby3d/telegram"
|
||||
|
||||
// Bot is a main object of Telegram bot
|
||||
var Bot *tg.Bot
|
||||
|
||||
// New just create new bot by configuration credentials
|
||||
func New(accessToken string) (bot *tg.Bot, err error) {
|
||||
return tg.New(accessToken)
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Add command prepare user for adding some stickers or sets to his pack
|
||||
func Add(msg *tg.Message, pack bool) {
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("reply_add_sticker"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.CancelButton(t)
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateAddSticker)
|
||||
errors.Check(err)
|
||||
|
||||
if pack {
|
||||
reply.Text = t("reply_add_pack")
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateAddPack)
|
||||
errors.Check(err)
|
||||
}
|
||||
|
||||
log.Ln("Sending add reply...")
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Cancel just cancel current user operation
|
||||
func Cancel(msg *tg.Message) {
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
state, err := db.DB.GetUserState(msg.From)
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
var text string
|
||||
switch state {
|
||||
case models.StateAddSticker:
|
||||
text = t("cancel_add_sticker")
|
||||
case models.StateAddPack:
|
||||
text = t("cancel_add_pack")
|
||||
case models.StateDeleteSticker:
|
||||
text = t("cancel_del_sticker")
|
||||
case models.StateDeletePack:
|
||||
text = t("cancel_del_pack")
|
||||
case models.StateReset:
|
||||
text = t("cancel_reset")
|
||||
default:
|
||||
text = t("cancel_error")
|
||||
}
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, text)
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Command check's got user command
|
||||
func Command(msg *tg.Message) {
|
||||
log.Ln("command:", msg.Command())
|
||||
switch {
|
||||
case msg.IsCommandEqual(tg.CommandStart):
|
||||
Start(msg)
|
||||
case msg.IsCommandEqual(tg.CommandHelp):
|
||||
Help(msg)
|
||||
case msg.IsCommandEqual(models.CommandAddSticker):
|
||||
Add(msg, false)
|
||||
case msg.IsCommandEqual(models.CommandAddPack):
|
||||
Add(msg, true)
|
||||
case msg.IsCommandEqual(models.CommandDeleteSticker):
|
||||
Delete(msg, false)
|
||||
case msg.IsCommandEqual(models.CommandDeletePack):
|
||||
Delete(msg, true)
|
||||
case msg.IsCommandEqual(models.CommandReset):
|
||||
Reset(msg)
|
||||
case msg.IsCommandEqual(models.CommandCancel):
|
||||
Cancel(msg)
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Delete prepare user to remove some stickers or sets from his pack
|
||||
func Delete(msg *tg.Message, pack bool) {
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
stickers, err := db.DB.GetUserStickers(msg.From, &tg.InlineQuery{})
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
if len(stickers) <= 0 {
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("error_empty_del"))
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
return
|
||||
}
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("reply_del_sticker"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.CancelButton(t)
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateDeleteSticker)
|
||||
errors.Check(err)
|
||||
|
||||
if pack {
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateDeletePack)
|
||||
errors.Check(err)
|
||||
|
||||
reply.Text = t("reply_del_pack")
|
||||
}
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
reply = tg.NewMessage(msg.Chat.ID, t("reply_switch_button"))
|
||||
reply.ReplyMarkup = utils.SwitchButton(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Help just send instructions about bot usage
|
||||
func Help(msg *tg.Message) {
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(
|
||||
msg.Chat.ID, t("reply_help", map[string]interface{}{
|
||||
"AddStickerCommand": models.CommandAddSticker,
|
||||
"AddPackCommand": models.CommandAddPack,
|
||||
"DeleteStickerCommand": models.CommandDeleteSticker,
|
||||
"DeletePackCommand": models.CommandDeletePack,
|
||||
"ResetCommand": models.CommandReset,
|
||||
"CancelCommand": models.CommandCancel,
|
||||
"Username": bot.Bot.Username,
|
||||
}),
|
||||
)
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Reset prepare user to reset his pack
|
||||
func Reset(msg *tg.Message) {
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
stickers, err := db.DB.GetUserStickers(msg.From, &tg.InlineQuery{})
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
if len(stickers) <= 0 {
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("error_already_reset"))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = db.DB.ChangeUserState(msg.From, models.StateReset)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(msg.Chat.ID, t("reply_reset", map[string]interface{}{
|
||||
"KeyPhrase": t("key_phrase"),
|
||||
"CancelCommand": models.CommandCancel,
|
||||
}))
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.CancelButton(t)
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
package commands
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Start just send introduction about bot to user
|
||||
func Start(msg *tg.Message) {
|
||||
err := db.DB.ChangeUserState(msg.From, models.StateNone)
|
||||
errors.Check(err)
|
||||
|
||||
_, err = bot.Bot.SendChatAction(msg.Chat.ID, tg.ActionTyping)
|
||||
errors.Check(err)
|
||||
|
||||
if msg.HasCommandArgument() {
|
||||
log.Ln("Received a", msg.Command(), "command with", msg.CommandArgument(), "argument")
|
||||
if strings.EqualFold(msg.CommandArgument(), tg.CommandHelp) {
|
||||
Help(msg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
reply := tg.NewMessage(
|
||||
msg.Chat.ID,
|
||||
t("reply_start", map[string]interface{}{
|
||||
"Username": bot.Bot.Username,
|
||||
"ID": bot.Bot.ID,
|
||||
}),
|
||||
)
|
||||
reply.ParseMode = tg.StyleMarkdown
|
||||
reply.ReplyMarkup = utils.MenuKeyboard(t)
|
||||
|
||||
_, err = bot.Bot.SendMessage(reply)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
//nolint: gochecknoglobals
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/Masterminds/semver"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
const (
|
||||
CommandEdit string = "edit"
|
||||
|
||||
// NOTE(toby3d): DEPRECATED
|
||||
CommandAddPack string = "addpack"
|
||||
CommandAddSticker string = "add" + tg.TypeSticker
|
||||
CommandCancel string = "cancel"
|
||||
CommandDelPack string = "addpack"
|
||||
CommandDelSticker string = "add" + tg.TypeSticker
|
||||
CommandReset string = "reset"
|
||||
)
|
||||
|
||||
const (
|
||||
DataSeparator string = "@"
|
||||
DataAdd string = "add"
|
||||
DataDel string = "del"
|
||||
DataSet string = "set"
|
||||
|
||||
DataAddSet string = DataAdd + DataSeparator + DataSet
|
||||
DataDelSet string = DataDel + DataSeparator + DataSet
|
||||
)
|
||||
|
||||
const SetNameUploaded string = "uploaded_by_mypackbot"
|
||||
|
||||
var Version = semver.MustParse("2.0.0")
|
||||
|
||||
var (
|
||||
BucketPhotos = []byte("photos")
|
||||
BucketStickers = []byte("stickers")
|
||||
BucketUsers = []byte("users")
|
||||
BucketUsersPhotos = []byte("users_photos")
|
||||
BucketUsersStickers = []byte("users_stickers")
|
||||
Buckets = [...][]byte{
|
||||
BucketPhotos,
|
||||
BucketStickers,
|
||||
BucketUsers,
|
||||
BucketUsersPhotos,
|
||||
BucketUsersStickers,
|
||||
}
|
||||
)
|
|
@ -0,0 +1,25 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOpen(t *testing.T) {
|
||||
rootPath, err := os.Getwd()
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
cfg, err := Open(filepath.Join("/", "invalid", "directory"))
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, cfg)
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
cfg, err := Open(filepath.Join(rootPath, "..", "..", "configs", "config.example.yaml"))
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, cfg)
|
||||
})
|
||||
}
|
|
@ -1,17 +1,35 @@
|
|||
package config
|
||||
|
||||
import "github.com/spf13/viper"
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
|
||||
var Config *viper.Viper
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Reader interface {
|
||||
GetString(string) string
|
||||
GetInt64(string) int64
|
||||
}
|
||||
|
||||
// Open just open configuration file for parsing some data in other functions
|
||||
func Open(path string) (*viper.Viper, error) {
|
||||
cfg := viper.New()
|
||||
dir, file := filepath.Split(path)
|
||||
ext := filepath.Ext(file)
|
||||
|
||||
cfg.AddConfigPath(path)
|
||||
cfg.SetConfigName("config")
|
||||
cfg.SetConfigType("yaml")
|
||||
if file == "" || ext == "" {
|
||||
return nil, errors.New("invalid path to config file")
|
||||
}
|
||||
|
||||
err := cfg.ReadInConfig()
|
||||
return cfg, err
|
||||
fileExt := ext[1:]
|
||||
fileName := file[:(len(file)-len(fileExt))-1]
|
||||
|
||||
v := viper.New()
|
||||
v.AddConfigPath(dir)
|
||||
v.SetConfigName(fileName)
|
||||
v.SetConfigType(fileExt)
|
||||
|
||||
err := v.ReadInConfig()
|
||||
|
||||
return v, err
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// AddSticker add sticker FileID, Emoji and SetName meta for UserID
|
||||
func (db *DataBase) AddSticker(user *tg.User, sticker *tg.Sticker) (bool, error) {
|
||||
log.Ln("Trying to add", sticker.FileID, "sticker from", user.ID, "user")
|
||||
if sticker.SetName == "" {
|
||||
sticker.SetName = models.SetUploaded
|
||||
}
|
||||
|
||||
var exists bool
|
||||
err := db.Update(func(tx *buntdb.Tx) error {
|
||||
var err error
|
||||
_, exists, err = tx.Set(
|
||||
fmt.Sprint("user:", user.ID, ":set:", sticker.SetName, ":sticker:", sticker.FileID), // key
|
||||
sticker.Emoji, // value
|
||||
nil, // options
|
||||
)
|
||||
if err == buntdb.ErrIndexExists {
|
||||
exists = true
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
})
|
||||
|
||||
return exists, err
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// ChangeUserState change current user state on input state.
|
||||
func (db *DataBase) ChangeUserState(user *tg.User, state string) error {
|
||||
log.Ln("Trying to change", user.ID, "state to", state)
|
||||
return db.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, err := tx.Set(fmt.Sprint("user:", user.ID, ":state"), state, nil)
|
||||
return err
|
||||
})
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
func Open(path string) (*bolthold.Store, error) {
|
||||
db, err := bolthold.Open(path, os.ModePerm, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = db.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
for i := range common.Buckets {
|
||||
if _, err := tx.CreateBucketIfNotExists(common.Buckets[i]); err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return db, err
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOpen(t *testing.T) {
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
db, err := Open(filepath.Join("/", "invalid", "path"))
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, db)
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
rootPath, err := os.Getwd()
|
||||
assert.NoError(t, err)
|
||||
|
||||
testPath := filepath.Join(rootPath, "..", "..", "test", "testing.db")
|
||||
db, err := Open(testPath)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, db)
|
||||
defer func() {
|
||||
assert.NoError(t, db.Close())
|
||||
assert.NoError(t, os.Remove(testPath))
|
||||
}()
|
||||
})
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// DeletePack remove all keys for UserID which contains input SetName
|
||||
func (db *DataBase) DeletePack(user *tg.User, sticker *tg.Sticker) (bool, error) {
|
||||
log.Ln("Trying to remove all", sticker.SetName, "sticker from", user.ID, "user")
|
||||
if sticker.SetName == "" {
|
||||
sticker.SetName = models.SetUploaded
|
||||
}
|
||||
|
||||
var ids []string
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
return tx.AscendKeys(
|
||||
fmt.Sprint("user:", user.ID, ":set:", sticker.SetName, ":*"),
|
||||
func(key, val string) bool {
|
||||
keys := strings.Split(key, ":")
|
||||
ids = append(ids, keys[5])
|
||||
return true
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
if len(ids) == 0 {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
var notExist bool
|
||||
notExist, err = db.DeleteSticker(user, &tg.Sticker{FileID: id})
|
||||
if err != nil {
|
||||
return notExist, err
|
||||
}
|
||||
}
|
||||
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln(user.ID, "not found")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// DeleteSticker just remove specified sticker key from database.
|
||||
func (db *DataBase) DeleteSticker(user *tg.User, sticker *tg.Sticker) (bool, error) {
|
||||
log.Ln("Trying to remove", sticker.FileID, "sticker from", user.ID, "user")
|
||||
if sticker.SetName == "" {
|
||||
sticker.SetName = models.SetUploaded
|
||||
}
|
||||
|
||||
err := db.Update(func(tx *buntdb.Tx) error {
|
||||
_, err := tx.Delete(
|
||||
fmt.Sprint("user:", user.ID, ":set:", sticker.SetName, ":sticker:", sticker.FileID),
|
||||
)
|
||||
return err
|
||||
})
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln(user.ID, "not found, create new one")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// UserState return current state for UserID
|
||||
func (db *DataBase) GetUserState(user *tg.User) (string, error) {
|
||||
log.Ln("Trying to get", user.ID, "state")
|
||||
var state string
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
var err error
|
||||
state, err = tx.Get(fmt.Sprint("user:", user.ID, ":state"))
|
||||
return err
|
||||
})
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln(user.ID, "not found, create new one")
|
||||
if err = db.ChangeUserState(user, models.StateNone); err != nil {
|
||||
return state, err
|
||||
}
|
||||
}
|
||||
|
||||
return state, err
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// GetUserStickers return array of saved stickers for input UserID and his total count
|
||||
func (db *DataBase) GetUserStickers(user *tg.User, query *tg.InlineQuery) ([]string, error) {
|
||||
log.Ln("Trying to get", user.ID, "stickers")
|
||||
var i int
|
||||
var stickers []string
|
||||
offset, _ := strconv.Atoi(query.Offset)
|
||||
offset *= 50
|
||||
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
return tx.AscendKeys(
|
||||
fmt.Sprint("user:", user.ID, ":set:*"), // index
|
||||
func(key, val string) bool { // iterator
|
||||
subKeys := strings.Split(key, ":")
|
||||
if subKeys[1] != strconv.Itoa(user.ID) {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(stickers) == 50 {
|
||||
return false
|
||||
}
|
||||
|
||||
i++
|
||||
if i < offset {
|
||||
return true
|
||||
}
|
||||
|
||||
if query.Query != "" && !strings.Contains(query.Query, val) {
|
||||
return true
|
||||
}
|
||||
|
||||
stickers = append(stickers, subKeys[5])
|
||||
return true
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln("Not found stickers")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return stickers, err
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
// log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
)
|
||||
|
||||
// GetUsers return array of all available UserID in database
|
||||
func (db *DataBase) GetUsers() ([]int, error) {
|
||||
var users []int
|
||||
err := db.View(func(tx *buntdb.Tx) error {
|
||||
return tx.AscendKeys(
|
||||
"user:*:state",
|
||||
func(key, val string) bool {
|
||||
subKeys := strings.Split(key, ":")
|
||||
id, err := strconv.Atoi(subKeys[1])
|
||||
if err == nil {
|
||||
users = append(users, id)
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
)
|
||||
})
|
||||
if err == buntdb.ErrNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return users, err
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
)
|
||||
|
||||
type DataBase struct{ *buntdb.DB }
|
||||
|
||||
// DB is a main object of current database connection
|
||||
var DB *DataBase
|
||||
|
||||
// Open just open connection to database for work
|
||||
func Open(path string) (*DataBase, error) {
|
||||
log.Ln("Open database file...")
|
||||
db, err := buntdb.Open(path)
|
||||
return &DataBase{db}, err
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// ResetUser just drop out all stickers keys for input UserID
|
||||
func (db *DataBase) ResetUser(user *tg.User) error {
|
||||
log.Ln("Trying reset all stickers of", user.ID, "user")
|
||||
return db.Update(func(tx *buntdb.Tx) error {
|
||||
var keys []string
|
||||
if err := tx.AscendKeys(
|
||||
fmt.Sprint("user:", user.ID, ":set:*"), // index
|
||||
func(key, val string) bool { // iterator
|
||||
subKeys := strings.Split(key, ":")
|
||||
if subKeys[1] == strconv.Itoa(user.ID) {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
return true
|
||||
},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range keys {
|
||||
_, err := tx.Delete(keys[i])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// UserState return current state for UserID
|
||||
func (db *DataBase) UserState(usr *tg.User) (string, error) {
|
||||
log.Ln("Trying to get", usr.ID, "state")
|
||||
var state string
|
||||
err := DB.View(func(tx *buntdb.Tx) error {
|
||||
var err error
|
||||
state, err = tx.Get(fmt.Sprint("user:", usr.ID, ":state"))
|
||||
return err
|
||||
})
|
||||
|
||||
if err == buntdb.ErrNotFound {
|
||||
log.Ln(usr.ID, "not found, create new one")
|
||||
if err = db.ChangeUserState(usr, models.StateNone); err != nil {
|
||||
return state, err
|
||||
}
|
||||
}
|
||||
|
||||
return state, err
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"log"
|
||||
"log/syslog"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// WaitForwards is a wait group which wait send all announcements before panic
|
||||
WaitForwards = new(sync.WaitGroup)
|
||||
|
||||
sysLogger *syslog.Writer
|
||||
)
|
||||
|
||||
// Check helps debug critical errors without warnings from 'gocyclo' linter
|
||||
func Check(err error) {
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
|
||||
// Wait what all users get announcement message first
|
||||
WaitForwards.Wait()
|
||||
|
||||
err = sysLogger.Crit(err.Error())
|
||||
if err != nil {
|
||||
log.Panicln(err.Error())
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
func (h *Handler) IsCallbackQuery(ctx *model.Context) (err error) {
|
||||
if !ctx.Request.IsCallbackQuery() {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch ctx.Request.CallbackQuery.Data {
|
||||
case common.DataAdd, common.DataAddSet:
|
||||
err = h.CallbackAdd(ctx)
|
||||
case common.DataDel, common.DataDelSet:
|
||||
err = h.CallbackDel(ctx)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = ctx.AnswerCallbackQuery(tg.NewAnswerCallback(ctx.Request.CallbackQuery.ID))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) CallbackAdd(ctx *model.Context) (err error) {
|
||||
if !ctx.Request.IsCallbackQuery() {
|
||||
return err
|
||||
}
|
||||
|
||||
editMessage := tg.EditMessageReplyMarkup{
|
||||
ChatID: ctx.Request.CallbackQuery.Message.Chat.ID,
|
||||
InlineMessageID: ctx.Request.CallbackQuery.InlineMessageID,
|
||||
MessageID: ctx.Request.CallbackQuery.Message.ID,
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.Photo != nil:
|
||||
if err = h.CommandAddPhoto(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
editMessage.ReplyMarkup = h.GetPhotoKeyboard(ctx)
|
||||
case ctx.Sticker != nil:
|
||||
if ctx.Request.CallbackQuery.Data == common.DataAddSet {
|
||||
err = h.CommandAddSet(ctx)
|
||||
ctx.HasSet = true
|
||||
} else {
|
||||
err = h.CommandAddSticker(ctx)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.HasSticker = true
|
||||
editMessage.ReplyMarkup = h.GetStickerKeyboard(ctx)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = ctx.EditMessageReplyMarkup(editMessage)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) CallbackDel(ctx *model.Context) (err error) {
|
||||
if !ctx.Request.IsCallbackQuery() {
|
||||
return err
|
||||
}
|
||||
|
||||
editMessage := tg.EditMessageReplyMarkup{
|
||||
ChatID: ctx.Request.CallbackQuery.Message.Chat.ID,
|
||||
InlineMessageID: ctx.Request.CallbackQuery.InlineMessageID,
|
||||
MessageID: ctx.Request.CallbackQuery.Message.ID,
|
||||
}
|
||||
|
||||
switch {
|
||||
case ctx.Photo != nil:
|
||||
if err = h.CommandDelPhoto(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
editMessage.ReplyMarkup = h.GetPhotoKeyboard(ctx)
|
||||
case ctx.Sticker != nil:
|
||||
if ctx.Request.CallbackQuery.Data == common.DataDelSet {
|
||||
err = h.CommandDelSet(ctx)
|
||||
ctx.HasSet = false
|
||||
} else {
|
||||
err = h.CommandDelSticker(ctx)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx.HasSticker = false
|
||||
editMessage.ReplyMarkup = h.GetStickerKeyboard(ctx)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = ctx.EditMessageReplyMarkup(editMessage)
|
||||
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
// IsCommand defines actions for commands only
|
||||
func (h *Handler) IsCommand(ctx *model.Context) (err error) {
|
||||
if !ctx.Request.Message.IsCommand() {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch ctx.Request.Message.Command() {
|
||||
case tg.CommandStart:
|
||||
err = h.CommandStart(ctx)
|
||||
case tg.CommandHelp:
|
||||
err = h.CommandHelp(ctx)
|
||||
case "ping":
|
||||
err = h.CommandPing(ctx)
|
||||
case "add":
|
||||
err = h.CommandAdd(ctx)
|
||||
case "del":
|
||||
err = h.CommandDel(ctx)
|
||||
case "edit":
|
||||
err = h.CommandEdit(ctx)
|
||||
case "addsticker":
|
||||
err = h.CommandAddSticker(ctx)
|
||||
case "addpack":
|
||||
err = h.CommandAddSet(ctx)
|
||||
case "delsticker":
|
||||
err = h.CommandDelSticker(ctx)
|
||||
case "delpack":
|
||||
err = h.CommandDelSet(ctx)
|
||||
case "reset", "cancel", tg.CommandSettings:
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CommandPing send common ping message.
|
||||
func (h *Handler) CommandPing(ctx *model.Context) (err error) {
|
||||
reply := tg.NewMessage(int64(ctx.User.ID), "🏓")
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CommandStart send common welcome message.
|
||||
// NOTE(toby3d): REQUIRED by Telegram Bot API platform
|
||||
func (h *Handler) CommandStart(ctx *model.Context) (err error) {
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(int64(ctx.User.ID), p.Sprintf("👋 Hi %s, I'm %s!\nThanks to me, you can collect almost any"+
|
||||
" media content in Telegram without any limits, in any chat via inline mode.",
|
||||
ctx.Request.Message.From.FullName(), ctx.FullName()))
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CommandHelp send common message with list of available commands
|
||||
// NOTE(toby3d): REQUIRED by Telegram Bot API platform
|
||||
func (h *Handler) CommandHelp(ctx *model.Context) (err error) {
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(int64(ctx.User.ID), p.Sprintf("🤖 Here is a list of commands that I understand, some of"+
|
||||
" them [may] or (should) contain an argument:\n/start - start all over again\n/help [other command] "+
|
||||
"- get a list of available commands or help and a demonstration of a specific command\n/add [query] "+
|
||||
"- add media from reply to your collection [with custom search query]\n/edit (query) - change query "+
|
||||
"to reply media\n/del - remove reply media from your collection"))
|
||||
|
||||
if !ctx.Request.Message.HasCommandArgument() {
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
switch ctx.Request.Message.CommandArgument() {
|
||||
case "add":
|
||||
reply.Text = p.Sprintf("💡 Use the /add command as a reply to the sticker/photo to add this media to " +
|
||||
"your collection feed. Given an argument, the result of this command will be equivalent to " +
|
||||
"the /edit command.")
|
||||
case "edit":
|
||||
reply.Text = p.Sprintf("💡 Use the /edit command with an argument from any character set as a reply " +
|
||||
"to a sticker/photo to change the search query of this media in the feed of your collection." +
|
||||
" If this media is not in the feed, then the result of this command will be equivalent to " +
|
||||
"the /add command with the same argument.")
|
||||
case "del":
|
||||
reply.Text = p.Sprintf("💡 Use /del command as an reply to the sticker/photo to remove it from the " +
|
||||
"feed of your collection.")
|
||||
default: // NOTE(toby3d): do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// CommandSettings send common message with settings buttons
|
||||
// NOTE(toby3d): REQUIRED by Telegram Bot API platform
|
||||
func (h *Handler) CommandSettings(ctx *model.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommandUnknown reply common error message to any unkwnon commands.
|
||||
func (h *Handler) CommandUnknown(ctx *model.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) CommandAdd(ctx *model.Context) (err error) {
|
||||
switch {
|
||||
case ctx.Photo != nil:
|
||||
err = h.CommandAddPhoto(ctx)
|
||||
case ctx.Sticker != nil:
|
||||
err = h.CommandAddSticker(ctx)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ctx.Request.Message.HasCommandArgument() {
|
||||
return err
|
||||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(int64(ctx.User.ID), p.Sprintf("👍 Imported!"))
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) CommandEdit(ctx *model.Context) (err error) {
|
||||
switch {
|
||||
case ctx.Photo != nil:
|
||||
err = h.CommandEditPhoto(ctx)
|
||||
case ctx.Sticker != nil:
|
||||
err = h.CommandEditSticker(ctx)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ctx.Request.Message.HasCommandArgument() {
|
||||
return err
|
||||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(int64(ctx.User.ID), p.Sprintf("👍 Updated!"))
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *Handler) CommandDel(ctx *model.Context) (err error) {
|
||||
switch {
|
||||
case ctx.Photo != nil:
|
||||
err = h.CommandDelPhoto(ctx)
|
||||
case ctx.Sticker != nil:
|
||||
err = h.CommandDelSticker(ctx)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ctx.Request.Message.HasCommandArgument() {
|
||||
return err
|
||||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(int64(ctx.User.ID), p.Sprintf("👍 Removed!"))
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/photos"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/stickers"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/users"
|
||||
up "gitlab.com/toby3d/mypackbot/internal/model/users/photos"
|
||||
us "gitlab.com/toby3d/mypackbot/internal/model/users/stickers"
|
||||
"gitlab.com/toby3d/mypackbot/internal/store"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
users users.ReadWriter
|
||||
stickers stickers.ReadWriter
|
||||
photos photos.ReadWriter
|
||||
usersStickers us.ReadWriter
|
||||
usersPhotos up.ReadWriter
|
||||
store *store.Store
|
||||
}
|
||||
|
||||
func NewHandler(us users.ReadWriter, ss stickers.ReadWriter, ps photos.ReadWriter, uss us.ReadWriter,
|
||||
ups up.ReadWriter) *Handler {
|
||||
return &Handler{
|
||||
photos: ps,
|
||||
stickers: ss,
|
||||
users: us,
|
||||
usersPhotos: ups,
|
||||
usersStickers: uss,
|
||||
store: store.NewStore(uss, ups),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Handler) UpdateHandler(ctx *model.Context) (err error) {
|
||||
switch {
|
||||
case ctx.Request.IsMessage():
|
||||
err = h.IsMessage(ctx)
|
||||
case ctx.Request.IsCallbackQuery():
|
||||
err = h.IsCallbackQuery(ctx)
|
||||
case ctx.Request.IsInlineQuery():
|
||||
err = h.IsInlineQuery(ctx)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/store"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
const defaultLimit int = 50
|
||||
|
||||
func (h *Handler) IsInlineQuery(ctx *model.Context) (err error) {
|
||||
answer := tg.NewAnswerInline(ctx.Request.InlineQuery.ID)
|
||||
answer.CacheTime = 1 // NOTE(toby3d): add setting for change this
|
||||
filter := getFilter(ctx.Request.InlineQuery)
|
||||
|
||||
if answer.IsPersonal = !strings.Contains(ctx.Request.InlineQuery.Query, "personal:false"); answer.IsPersonal {
|
||||
filter.UserID = ctx.User.ID
|
||||
}
|
||||
|
||||
results, count, _ := h.store.GetList(filter.Offset, filter.Limit, filter)
|
||||
|
||||
if filter.Offset+filter.Limit < count {
|
||||
answer.NextOffset = strconv.Itoa(filter.Offset + filter.Limit)
|
||||
}
|
||||
|
||||
for i := range results {
|
||||
switch results[i].GetType() {
|
||||
case tg.TypeSticker:
|
||||
answer.Results = append(answer.Results, tg.NewInlineQueryResultCachedSticker(
|
||||
tg.TypeSticker+common.DataSeparator+results[i].GetID(), results[i].GetFileID(),
|
||||
))
|
||||
case tg.TypePhoto:
|
||||
answer.Results = append(answer.Results, tg.NewInlineQueryResultCachedPhoto(
|
||||
tg.TypePhoto+common.DataSeparator+results[i].GetID(), results[i].GetFileID(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
answer.SwitchPrivateMessageParameter = "inline"
|
||||
answer.SwitchPrivateMessageText = p.Sprintf("🕵 Found %d result(s)", count)
|
||||
_, err = ctx.AnswerInlineQuery(answer)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func getFilter(iq *tg.InlineQuery) *store.Filter {
|
||||
f := new(store.Filter)
|
||||
f.Limit = defaultLimit
|
||||
f.Offset, _ = strconv.Atoi(iq.Offset)
|
||||
|
||||
if !strings.Contains(iq.Query, "photos:false") {
|
||||
f.AllowedTypes = append(f.AllowedTypes, tg.TypePhoto)
|
||||
}
|
||||
|
||||
if !strings.Contains(iq.Query, "stickers:false") {
|
||||
f.AllowedTypes = append(f.AllowedTypes, tg.TypeSticker)
|
||||
}
|
||||
|
||||
if !iq.HasQuery() {
|
||||
return f
|
||||
}
|
||||
|
||||
f.Query, _ = utils.FixEmojiTone(strings.TrimSpace(iq.Query))
|
||||
|
||||
for _, field := range strings.Fields(f.Query) {
|
||||
i := strings.Index(f.Query, field)
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(field, "offset:"):
|
||||
f.Offset, _ = strconv.Atoi(strings.TrimPrefix(field, "offset:"))
|
||||
/*
|
||||
case strings.HasPrefix(field, "animated:"):
|
||||
isAnimated, _ := strconv.ParseBool(strings.TrimPrefix(field, "animated:"))
|
||||
f.IsAnimated = &isAnimated
|
||||
case strings.HasPrefix(field, "set:"):
|
||||
f.SetName = strings.TrimPrefix(field, "set:")
|
||||
*/
|
||||
case strings.HasPrefix(field, "photos:"), strings.HasPrefix(field, "stickers:"):
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
f.Query = f.Query[:i] + strings.TrimPrefix(f.Query[i:], field)
|
||||
}
|
||||
|
||||
f.Query = strings.ReplaceAll(f.Query, " ", "")
|
||||
|
||||
return f
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
func (h *Handler) IsMessage(ctx *model.Context) (err error) {
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
|
||||
reply := tg.NewMessage(ctx.Request.Message.Chat.ID, p.Sprintf("🤔 What to do with this?"))
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
|
||||
switch {
|
||||
case ctx.Request.Message.IsCommand():
|
||||
err = h.IsCommand(ctx)
|
||||
case ctx.Request.Message.IsSticker():
|
||||
reply.ReplyMarkup = h.GetStickerKeyboard(ctx)
|
||||
_, err = ctx.SendMessage(reply)
|
||||
case ctx.Request.Message.IsPhoto():
|
||||
reply.ReplyMarkup = h.GetPhotoKeyboard(ctx)
|
||||
_, err = ctx.SendMessage(reply)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
func (h *Handler) GetPhotoKeyboard(ctx *model.Context) *tg.InlineKeyboardMarkup {
|
||||
if ctx.Photo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
userPhoto := h.usersPhotos.Get(&model.UserPhoto{
|
||||
UserID: ctx.User.ID,
|
||||
PhotoID: ctx.Photo.ID,
|
||||
})
|
||||
markup := tg.NewInlineKeyboardMarkup(tg.NewInlineKeyboardRow(
|
||||
tg.NewInlineKeyboardButton(p.Sprintf("📥 Import this photo"), common.DataAdd),
|
||||
))
|
||||
|
||||
if userPhoto != nil {
|
||||
markup = tg.NewInlineKeyboardMarkup(tg.NewInlineKeyboardRow(
|
||||
tg.NewInlineKeyboardButton(p.Sprintf("🔥 Remove this photo"), common.DataDel),
|
||||
))
|
||||
}
|
||||
|
||||
return &markup
|
||||
}
|
||||
|
||||
func (h *Handler) CommandAddPhoto(ctx *model.Context) (err error) {
|
||||
if ctx.Photo == nil || ctx.HasPhoto {
|
||||
return nil
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
userPhoto := new(model.UserPhoto)
|
||||
userPhoto.CreatedAt, userPhoto.UpdatedAt = now, now
|
||||
userPhoto.UserID = ctx.User.ID
|
||||
userPhoto.PhotoID = ctx.Photo.ID
|
||||
|
||||
if ctx.Request.IsMessage() && ctx.Request.Message.HasCommandArgument() {
|
||||
userPhoto.Query = ctx.Request.Message.CommandArgument()
|
||||
}
|
||||
|
||||
return h.usersPhotos.Add(userPhoto)
|
||||
}
|
||||
|
||||
func (h *Handler) CommandEditPhoto(ctx *model.Context) (err error) {
|
||||
if ctx.Photo == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !ctx.Request.Message.HasCommandArgument() {
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(int64(ctx.User.ID), p.Sprintf("💡 Add any text and/or emoji(s) as an argument "+
|
||||
"of this command to change its search properties."))
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ParseMode = tg.ParseModeMarkdownV2
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if !ctx.HasPhoto {
|
||||
if err = h.CommandAddPhoto(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return h.usersPhotos.Update(&model.UserPhoto{
|
||||
UserID: ctx.User.ID,
|
||||
PhotoID: ctx.Photo.ID,
|
||||
UpdatedAt: ctx.Request.Message.Date,
|
||||
Query: ctx.Request.Message.CommandArgument(),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) CommandDelPhoto(ctx *model.Context) (err error) {
|
||||
if ctx.Photo == nil || !ctx.HasPhoto {
|
||||
return nil
|
||||
}
|
||||
|
||||
return h.usersPhotos.Remove(&model.UserPhoto{
|
||||
UserID: ctx.User.ID,
|
||||
PhotoID: ctx.Photo.ID,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
func (h *Handler) GetStickerKeyboard(ctx *model.Context) *tg.InlineKeyboardMarkup {
|
||||
if ctx.Sticker == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
markup := tg.NewInlineKeyboardMarkup(tg.NewInlineKeyboardRow(
|
||||
tg.NewInlineKeyboardButton("🔥 Remove this sticker", common.DataDel),
|
||||
))
|
||||
|
||||
if (ctx.Request.IsCallbackQuery() && ctx.Request.CallbackQuery.Data == common.DataDelSet) || !ctx.HasSticker {
|
||||
markup = tg.NewInlineKeyboardMarkup(tg.NewInlineKeyboardRow(
|
||||
tg.NewInlineKeyboardButton(p.Sprintf("📥 Import this sticker"), common.DataAdd),
|
||||
))
|
||||
}
|
||||
|
||||
if ctx.Sticker.InSet() {
|
||||
setName, _ := ctx.Get("set_name").(string)
|
||||
markup.InlineKeyboard = append(markup.InlineKeyboard, tg.NewInlineKeyboardRow(
|
||||
tg.NewInlineKeyboardButton(p.Sprintf("📥 Import %s set", setName), common.DataAddSet),
|
||||
))
|
||||
|
||||
if ctx.Request.IsCallbackQuery() && ctx.Request.CallbackQuery.Data == common.DataAddSet || ctx.HasSet {
|
||||
markup.InlineKeyboard[1][0] = tg.NewInlineKeyboardButton(
|
||||
p.Sprintf("🔥 Remove %s set", setName), common.DataDelSet,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return &markup
|
||||
}
|
||||
|
||||
// CommandAddSticker import single Sticker by ReplyMessage.
|
||||
// NOTE(toby3d): DEPRECATED, used for backward compatibility
|
||||
func (h *Handler) CommandAddSticker(ctx *model.Context) (err error) {
|
||||
if ctx.HasSticker || ctx.Sticker == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
userSticker := new(model.UserSticker)
|
||||
userSticker.CreatedAt, userSticker.UpdatedAt = now, now
|
||||
userSticker.Query = ctx.Sticker.Emoji
|
||||
userSticker.StickerID = ctx.Sticker.ID
|
||||
userSticker.UserID = ctx.User.ID
|
||||
|
||||
if ctx.Request.IsMessage() && ctx.Request.Message.HasCommandArgument() {
|
||||
userSticker.Query = ctx.Request.Message.CommandArgument()
|
||||
}
|
||||
|
||||
return h.usersStickers.Add(userSticker)
|
||||
}
|
||||
|
||||
// CommandAddPack import whole Sticker pack by ReplyMessage.
|
||||
// NOTE(toby3d): DEPRECATED, used for backward compatibility
|
||||
func (h *Handler) CommandAddSet(ctx *model.Context) (err error) {
|
||||
if ctx.Sticker == nil || !ctx.Sticker.InSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return h.usersStickers.AddSet(ctx.User.ID, ctx.Sticker.SetName)
|
||||
}
|
||||
|
||||
func (h *Handler) CommandEditSticker(ctx *model.Context) (err error) {
|
||||
if ctx.Sticker == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !ctx.Request.Message.HasCommandArgument() {
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(int64(ctx.User.ID), p.Sprintf("💡 Add any text and/or emoji(s) as an "+
|
||||
"argument of this command to change its search properties."))
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ParseMode = tg.ParseModeMarkdownV2
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if !ctx.HasSticker {
|
||||
if err = h.CommandAddSticker(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return h.usersStickers.Update(&model.UserSticker{
|
||||
UserID: ctx.User.ID,
|
||||
StickerID: ctx.Sticker.ID,
|
||||
UpdatedAt: ctx.Request.Message.Date,
|
||||
Query: ctx.Request.Message.CommandArgument(),
|
||||
})
|
||||
}
|
||||
|
||||
// CommandDelSticker remove single Sticker by ReplyMessage.
|
||||
// NOTE(toby3d): DEPRECATED, used for backward compatibility
|
||||
func (h *Handler) CommandDelSticker(ctx *model.Context) (err error) {
|
||||
if ctx.Sticker == nil || !ctx.HasSticker {
|
||||
return nil
|
||||
}
|
||||
|
||||
return h.usersStickers.Remove(&model.UserSticker{
|
||||
UserID: ctx.User.ID,
|
||||
StickerID: ctx.Sticker.ID,
|
||||
})
|
||||
}
|
||||
|
||||
// CommandDelPack remove whole Sticker pack by ReplyMessage.
|
||||
// NOTE(toby3d): DEPRECATED, used for backward compatibility
|
||||
func (h *Handler) CommandDelSet(ctx *model.Context) (err error) {
|
||||
if ctx.Sticker == nil || !ctx.Sticker.InSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return h.usersStickers.RemoveSet(ctx.User.ID, ctx.Sticker.SetName)
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/nicksnyder/go-i18n/i18n"
|
||||
)
|
||||
|
||||
// Open just walk in input path for preloading localization files
|
||||
func Open(path string) error {
|
||||
return filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
||||
if !strings.HasSuffix(path, ".all.yaml") {
|
||||
return err
|
||||
}
|
||||
|
||||
return i18n.LoadTranslationFile(path)
|
||||
})
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
package i18n
|
||||
|
||||
import (
|
||||
"github.com/nicksnyder/go-i18n/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
)
|
||||
|
||||
// SwitchTo try load locales by input language codes and return TranslateFunc
|
||||
func SwitchTo(codes ...string) (t i18n.TranslateFunc, err error) {
|
||||
codes = append(codes, models.LanguageFallback)
|
||||
t, err = i18n.Tfunc(codes[0], codes[1:]...)
|
||||
return
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package messages
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/actions"
|
||||
"gitlab.com/toby3d/mypackbot/internal/commands"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Message checks user message on response, stickers, reset key phrase, else do
|
||||
// Actions
|
||||
func Message(msg *tg.Message) {
|
||||
t, err := i18n.SwitchTo(msg.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
switch {
|
||||
case strings.EqualFold(msg.Text, t("button_add_sticker")):
|
||||
commands.Add(msg, false)
|
||||
case strings.EqualFold(msg.Text, t("button_add_pack")):
|
||||
commands.Add(msg, true)
|
||||
case strings.EqualFold(msg.Text, t("button_del_sticker")):
|
||||
commands.Delete(msg, false)
|
||||
case strings.EqualFold(msg.Text, t("button_del_pack")):
|
||||
commands.Delete(msg, true)
|
||||
case strings.EqualFold(msg.Text, t("button_reset")):
|
||||
commands.Reset(msg)
|
||||
case strings.EqualFold(msg.Text, t("button_cancel")):
|
||||
commands.Cancel(msg)
|
||||
default:
|
||||
actions.Action(msg)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/photos"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
func AcquirePhoto(store photos.ReadWriter) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) (err error) {
|
||||
switch {
|
||||
case ctx.Request.IsMessage():
|
||||
switch {
|
||||
case ctx.Request.Message.IsPhoto():
|
||||
ctx.Photo = photoToModel(ctx.Request.Message.Photo)
|
||||
ctx.Photo.CreatedAt = ctx.Request.Message.Date
|
||||
ctx.Photo.UpdatedAt = ctx.Request.Message.Date
|
||||
case ctx.Request.Message.IsReply() && ctx.Request.Message.ReplyToMessage.IsPhoto():
|
||||
ctx.Photo = photoToModel(ctx.Request.Message.ReplyToMessage.Photo)
|
||||
ctx.Photo.CreatedAt = ctx.Request.Message.Date
|
||||
ctx.Photo.UpdatedAt = ctx.Request.Message.Date
|
||||
default:
|
||||
return next(ctx)
|
||||
}
|
||||
case ctx.Request.IsCallbackQuery():
|
||||
if !ctx.Request.CallbackQuery.Message.IsReply() ||
|
||||
!ctx.Request.CallbackQuery.Message.ReplyToMessage.IsPhoto() {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
ctx.Photo = photoToModel(ctx.Request.CallbackQuery.Message.ReplyToMessage.Photo)
|
||||
ctx.Photo.CreatedAt = ctx.Request.CallbackQuery.Message.ReplyToMessage.Date
|
||||
ctx.Photo.UpdatedAt = ctx.Request.CallbackQuery.Message.ReplyToMessage.Date
|
||||
default:
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
if ctx.Photo, err = store.GetOrCreate(ctx.Photo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func photoToModel(photoSize tg.Photo) *model.Photo {
|
||||
p := photoSize[len(photoSize)-1]
|
||||
photo := new(model.Photo)
|
||||
photo.ID = p.FileUniqueID
|
||||
photo.Width = p.Width
|
||||
photo.Height = p.Height
|
||||
photo.FileID = p.FileID
|
||||
|
||||
return photo
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"golang.org/x/text/language"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
func AcquirePrinter() Interceptor {
|
||||
matcher := language.NewMatcher(message.DefaultCatalog.Languages())
|
||||
|
||||
return func(ctx *model.Context, next model.UpdateFunc) (err error) {
|
||||
tag, err := language.Parse(ctx.User.LanguageCode)
|
||||
if err != nil {
|
||||
tag = language.English
|
||||
}
|
||||
|
||||
tag, _, _ = matcher.Match(tag)
|
||||
p := message.NewPrinter(tag)
|
||||
ctx.Set("printer", p)
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/stickers"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
func AcquireSticker(store stickers.Manager) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) (err error) {
|
||||
switch {
|
||||
case ctx.Request.IsMessage():
|
||||
switch {
|
||||
case ctx.Request.Message.IsSticker():
|
||||
ctx.Sticker = stickerToModel(ctx.Request.Message.Sticker)
|
||||
ctx.Sticker.CreatedAt = ctx.Request.Message.Date
|
||||
ctx.Sticker.UpdatedAt = ctx.Request.Message.Date
|
||||
case ctx.Request.Message.IsReply() && ctx.Request.Message.ReplyToMessage.IsSticker():
|
||||
ctx.Sticker = stickerToModel(ctx.Request.Message.ReplyToMessage.Sticker)
|
||||
ctx.Sticker.CreatedAt = ctx.Request.Message.Date
|
||||
ctx.Sticker.UpdatedAt = ctx.Request.Message.Date
|
||||
default:
|
||||
return next(ctx)
|
||||
}
|
||||
case ctx.Request.IsCallbackQuery():
|
||||
if !ctx.Request.CallbackQuery.Message.IsReply() ||
|
||||
!ctx.Request.CallbackQuery.Message.ReplyToMessage.IsSticker() {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
ctx.Sticker = stickerToModel(ctx.Request.CallbackQuery.Message.ReplyToMessage.Sticker)
|
||||
ctx.Sticker.CreatedAt = ctx.Request.CallbackQuery.Message.ReplyToMessage.Date
|
||||
ctx.Sticker.UpdatedAt = ctx.Request.CallbackQuery.Message.ReplyToMessage.Date
|
||||
default:
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
if ctx.Sticker.InSet() {
|
||||
migrateSet(ctx, store)
|
||||
}
|
||||
|
||||
if ctx.Sticker, err = store.GetOrCreate(ctx.Sticker); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
func stickerToModel(s *tg.Sticker) *model.Sticker {
|
||||
sticker := new(model.Sticker)
|
||||
sticker.ID = s.FileUniqueID
|
||||
sticker.Emoji = s.Emoji
|
||||
sticker.Width = s.Width
|
||||
sticker.Height = s.Height
|
||||
sticker.IsAnimated = s.IsAnimated
|
||||
sticker.SetName = s.SetName
|
||||
sticker.FileID = s.FileID
|
||||
|
||||
if !sticker.InSet() {
|
||||
sticker.SetName = common.SetNameUploaded
|
||||
}
|
||||
|
||||
return sticker
|
||||
}
|
||||
|
||||
func migrateSet(ctx *model.Context, store stickers.Manager) {
|
||||
tgSet, err := ctx.GetStickerSet(ctx.Sticker.SetName)
|
||||
if err != nil || tgSet == nil || len(tgSet.Stickers) == 0 {
|
||||
stickers, _, _ := store.GetList(0, 0, &model.Sticker{SetName: ctx.Sticker.SetName})
|
||||
ctx.Sticker.SetName = common.SetNameUploaded
|
||||
|
||||
go func() {
|
||||
for i := range stickers {
|
||||
stickers[i].SetName = ctx.Sticker.SetName
|
||||
stickers[i].UpdatedAt = ctx.Sticker.UpdatedAt
|
||||
_ = store.Update(stickers[i])
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
ctx.Set("set_name", tgSet.Title)
|
||||
|
||||
for i := range tgSet.Stickers {
|
||||
for _, sticker := range store.GetSet(tgSet.Name) {
|
||||
if sticker.ID == tgSet.Stickers[i].FileUniqueID {
|
||||
continue
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
s := stickerToModel(tgSet.Stickers[i])
|
||||
s.CreatedAt, s.UpdatedAt = now, now
|
||||
|
||||
_, _ = store.GetOrCreate(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/users"
|
||||
)
|
||||
|
||||
func AcquireUser(us users.ReadWriter) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) (err error) {
|
||||
ctx.User = new(model.User)
|
||||
|
||||
switch {
|
||||
case ctx.Request.IsMessage():
|
||||
ctx.User.ID = ctx.Request.Message.From.ID
|
||||
ctx.User.CreatedAt = ctx.Request.Message.Date
|
||||
ctx.User.UpdatedAt = ctx.Request.Message.Date
|
||||
ctx.User.LanguageCode = ctx.Request.Message.From.LanguageCode
|
||||
ctx.User.LastSeen = ctx.Request.Message.Date
|
||||
case ctx.Request.IsInlineQuery():
|
||||
now := time.Now().UTC().Unix()
|
||||
ctx.User.ID = ctx.Request.InlineQuery.From.ID
|
||||
ctx.User.CreatedAt = now
|
||||
ctx.User.UpdatedAt = now
|
||||
ctx.User.LanguageCode = ctx.Request.InlineQuery.From.LanguageCode
|
||||
ctx.User.LastSeen = now
|
||||
case ctx.Request.IsCallbackQuery():
|
||||
ctx.User.ID = ctx.Request.CallbackQuery.From.ID
|
||||
ctx.User.CreatedAt = ctx.Request.CallbackQuery.Message.Date
|
||||
ctx.User.UpdatedAt = ctx.Request.CallbackQuery.Message.Date
|
||||
ctx.User.LanguageCode = ctx.Request.CallbackQuery.From.LanguageCode
|
||||
ctx.User.LastSeen = ctx.Request.CallbackQuery.Message.Date
|
||||
}
|
||||
|
||||
if ctx.User, err = us.GetOrCreate(ctx.User); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/users/photos"
|
||||
)
|
||||
|
||||
func AcquireUserPhoto(store photos.Reader) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) error {
|
||||
if ctx.Photo == nil {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
ctx.HasPhoto = store.Get(&model.UserPhoto{
|
||||
UserID: ctx.User.ID,
|
||||
PhotoID: ctx.Photo.ID,
|
||||
}) != nil
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/users/stickers"
|
||||
)
|
||||
|
||||
func AcquireUserSticker(store stickers.Reader) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) error {
|
||||
if ctx.Sticker == nil {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
ctx.HasSticker = store.Get(&model.UserSticker{
|
||||
UserID: ctx.User.ID,
|
||||
StickerID: ctx.Sticker.ID,
|
||||
}) != nil
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
func Birthday(bday time.Time) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) (err error) {
|
||||
if !ctx.Request.IsMessage() {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
lastSeen := time.Unix(ctx.User.LastSeen, 0)
|
||||
date := ctx.Request.Message.Time()
|
||||
before := time.Date(date.Year(), bday.Month(), bday.Day(), 0, 0, 0, 0, time.UTC)
|
||||
after := before.AddDate(0, 0, 7)
|
||||
if date.Before(before) || date.After(after) || lastSeen.After(before) {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
// NOTE(toby3d): do this middleware only after sending all previous messages
|
||||
if err = next(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(ctx.Request.Message.Chat.ID, p.Sprintf("🥳 4 November? It's a @toby3d birthday!"+
|
||||
"\nIf you like this bot, then why not send him a congratulation along with a small gift? This"+
|
||||
" will make him incredibly happy!"))
|
||||
if date.After(bday.AddDate(0, 0, 1)) {
|
||||
reply.Text = p.Sprintf("☺️ Oh, you missed @toby3d birthday on November 4th!\nIf you like this" +
|
||||
" bot, why not send him some birthday greetings and a little birthday gift? It is " +
|
||||
"not yet too late to make him happy!")
|
||||
}
|
||||
reply.DisableNotification = false
|
||||
reply.DisableWebPagePreview = false
|
||||
reply.ParseMode = tg.ParseModeMarkdownV2
|
||||
reply.ReplyMarkup = tg.NewInlineKeyboardMarkup(
|
||||
tg.NewInlineKeyboardRow(tg.NewInlineKeyboardButtonURL(
|
||||
p.Sprintf("💸 Make a donation"), "https://toby3d.me/donate",
|
||||
)), tg.NewInlineKeyboardRow(tg.NewInlineKeyboardButtonURL(
|
||||
p.Sprintf("🤝 Use referral links"), "https://toby3d.me/referrals",
|
||||
)),
|
||||
)
|
||||
|
||||
if _, err = ctx.SendMessage(reply); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
func ChatAction() Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) (err error) {
|
||||
if !ctx.Request.IsMessage() || !ctx.Request.Message.Chat.IsPrivate() {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
go func() { _, _ = ctx.SendChatAction(ctx.Request.Message.Chat.ID, tg.ActionTyping) }()
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
"golang.org/x/text/message"
|
||||
)
|
||||
|
||||
func Hacktober() Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) (err error) {
|
||||
if !ctx.Request.IsMessage() {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
lastSeen := time.Unix(ctx.User.LastSeen, 0)
|
||||
date := ctx.Request.Message.Time()
|
||||
before := time.Date(date.Year(), time.October, 1, 0, 0, 0, 0, time.UTC)
|
||||
// NOTE(toby3d): not November 1, use October 31
|
||||
after := before.AddDate(0, 1, 0).Add(-1 * 24 * time.Hour)
|
||||
if date.Before(before) || date.After(after) || lastSeen.After(before) {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
// NOTE(toby3d): do this middleware only after sending all previous messages
|
||||
if err = next(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(ctx.Request.Message.Chat.ID, p.Sprintf("🕺 HacktoberFest is here!\n\nIf you are"+
|
||||
" a beginner or already an experienced golang-developer, now is a great time to help improve"+
|
||||
" the quality of the code of this bot. Choose issue to your taste and offer your PR!"))
|
||||
reply.DisableNotification = false
|
||||
reply.DisableWebPagePreview = false
|
||||
reply.ParseMode = tg.ParseModeMarkdownV2
|
||||
reply.ReplyMarkup = tg.NewInlineKeyboardMarkup(tg.NewInlineKeyboardRow(tg.NewInlineKeyboardButtonURL(
|
||||
p.Sprintf("🔧 Let's hack!"),
|
||||
"https://gitlab.com/toby3d/mypackbot/issues?label_name%5B%5D=hacktoberfest",
|
||||
)))
|
||||
|
||||
if _, err = ctx.SendMessage(reply); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
)
|
||||
|
||||
type (
|
||||
Interceptor func(*model.Context, model.UpdateFunc) error
|
||||
UpdateHandler model.UpdateFunc
|
||||
Chain []Interceptor
|
||||
)
|
||||
|
||||
func (count UpdateHandler) Intercept(middleware Interceptor) UpdateHandler {
|
||||
return func(ctx *model.Context) error { return middleware(ctx, model.UpdateFunc(count)) }
|
||||
}
|
||||
|
||||
func (chain Chain) UpdateHandler(handler model.UpdateFunc) model.UpdateFunc {
|
||||
current := UpdateHandler(handler)
|
||||
|
||||
for i := len(chain) - 1; i >= 0; i-- {
|
||||
m := chain[i]
|
||||
current = current.Intercept(m)
|
||||
}
|
||||
|
||||
return model.UpdateFunc(current)
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/users"
|
||||
)
|
||||
|
||||
func UpdateLastSeen(us users.Manager) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) (err error) {
|
||||
timeStamp := time.Now().UTC().Unix()
|
||||
|
||||
switch {
|
||||
case ctx.Request.IsMessage():
|
||||
timeStamp = ctx.Request.Message.Date
|
||||
case ctx.Request.IsCallbackQuery():
|
||||
timeStamp = ctx.Request.CallbackQuery.Message.Date
|
||||
}
|
||||
|
||||
if time.Unix(ctx.User.LastSeen, 0).After(time.Unix(timeStamp, 0).Add(-1 * time.Hour)) {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
ctx.User.LastSeen = timeStamp
|
||||
if err = us.Update(ctx.User); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,333 @@
|
|||
package migrator
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
json "github.com/json-iterator/go"
|
||||
bunt "github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/stickers"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/users"
|
||||
usersstickers "gitlab.com/toby3d/mypackbot/internal/model/users/stickers"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
type (
|
||||
Data struct {
|
||||
Records Records
|
||||
*Backup
|
||||
}
|
||||
|
||||
Record struct {
|
||||
UserID int
|
||||
SetName string
|
||||
FileID string
|
||||
Emoji string
|
||||
}
|
||||
|
||||
Records []*Record
|
||||
|
||||
Backup struct {
|
||||
Users []int `json:"users"`
|
||||
Stickers []string `json:"stickers"`
|
||||
ImportedSets []string `json:"imported_sets"`
|
||||
BlockedSets []string `json:"blocked_sets"`
|
||||
}
|
||||
|
||||
AutoMigrateConfig struct {
|
||||
OldDB *bunt.DB
|
||||
Stickers stickers.Manager
|
||||
UsersStickers usersstickers.ReadWriter
|
||||
Users users.Manager
|
||||
Bot *tg.Bot
|
||||
GroupID int64
|
||||
Marshler json.API
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
partSet string = "set"
|
||||
partSticker string = "sticker"
|
||||
uploadedSetName string = "?"
|
||||
)
|
||||
|
||||
func AutoMigrate(cfg AutoMigrateConfig) (err error) {
|
||||
// NOTE(toby3d): preparing temp-stores for migrating
|
||||
data, err := cfg.importOldData()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// NOTE(toby3d): STEP 1: migrate users
|
||||
if err = cfg.migrateUsers(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// NOTE(toby3d): STEP 2: migrate sets
|
||||
if err = cfg.migrateSets(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// NOTE(toby3d): STEP 3: migrate stickers
|
||||
return cfg.migrateStickers(data)
|
||||
}
|
||||
|
||||
func (cfg *AutoMigrateConfig) importOldData() (data *Data, err error) {
|
||||
data = new(Data)
|
||||
|
||||
if data.Backup, err = cfg.readBackup(); err != nil && !os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = cfg.OldDB.View(func(tx *bunt.Tx) error {
|
||||
// NOTE(toby3d): read every key in buntdb database
|
||||
return tx.AscendKeys("user:*", func(key, val string) bool {
|
||||
r := new(Record)
|
||||
|
||||
// NOTE(toby3d): split key name on parts
|
||||
parts := strings.Split(key, ":")
|
||||
|
||||
// NOTE(toby3d): this part always contains user/chat id
|
||||
var err error
|
||||
r.UserID, err = strconv.Atoi(parts[1])
|
||||
if err != nil || r.UserID == 0 || !strings.EqualFold(parts[2], partSet) {
|
||||
return true
|
||||
}
|
||||
|
||||
switch parts[2] {
|
||||
case partSet:
|
||||
r.SetName = parts[3]
|
||||
r.FileID = parts[5]
|
||||
case partSticker:
|
||||
r.SetName = common.SetNameUploaded
|
||||
r.FileID = parts[3]
|
||||
default:
|
||||
return true
|
||||
}
|
||||
|
||||
if containsString(data.ImportedSets, r.SetName) || containsString(data.Stickers, r.FileID) {
|
||||
return true
|
||||
}
|
||||
|
||||
if containsString(data.BlockedSets, r.SetName) || r.SetName == uploadedSetName {
|
||||
r.SetName = common.SetNameUploaded
|
||||
}
|
||||
|
||||
r.Emoji = val
|
||||
|
||||
data.Records = append(data.Records, r)
|
||||
return true
|
||||
})
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sort.Slice(data.Records, func(i, j int) bool {
|
||||
return data.Records[i].UserID < data.Records[j].UserID ||
|
||||
data.Records[i].SetName < data.Records[j].SetName ||
|
||||
data.Records[i].FileID < data.Records[j].FileID
|
||||
})
|
||||
|
||||
if err = cfg.saveBackup(data.Backup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (cfg *AutoMigrateConfig) migrateUsers(data *Data) (err error) {
|
||||
if data.Backup, err = cfg.readBackup(); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range data.Records {
|
||||
if containsInt(data.Users, data.Records[i].UserID) {
|
||||
continue
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
|
||||
_ = cfg.Users.Create(&model.User{
|
||||
ID: data.Records[i].UserID,
|
||||
LanguageCode: "en",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
})
|
||||
|
||||
data.Users = append(data.Users, data.Records[i].UserID)
|
||||
_ = cfg.saveBackup(data.Backup)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *AutoMigrateConfig) migrateSets(data *Data) (err error) {
|
||||
if data.Backup, err = cfg.readBackup(); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range data.Records {
|
||||
if data.Records[i].SetName == uploadedSetName || data.Records[i].SetName == common.SetNameUploaded {
|
||||
data.Records[i].SetName = common.SetNameUploaded
|
||||
continue
|
||||
}
|
||||
|
||||
if containsString(data.ImportedSets, data.Records[i].SetName) {
|
||||
continue
|
||||
}
|
||||
|
||||
if containsString(data.BlockedSets, data.Records[i].SetName) {
|
||||
data.Records[i].SetName = common.SetNameUploaded
|
||||
continue
|
||||
}
|
||||
|
||||
set, err := cfg.Bot.GetStickerSet(data.Records[i].SetName)
|
||||
if err != nil {
|
||||
data.BlockedSets = append(data.BlockedSets, data.Records[i].SetName)
|
||||
data.Records[i].SetName = common.SetNameUploaded
|
||||
_ = cfg.saveBackup(data.Backup)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
for _, setSticker := range set.Stickers {
|
||||
setSticker := setSticker
|
||||
_ = cfg.Stickers.Create(stickerToModel(setSticker))
|
||||
}
|
||||
|
||||
u := cfg.Users.Get(data.Records[i].UserID)
|
||||
_ = cfg.UsersStickers.AddSet(u.ID, set.Name)
|
||||
data.ImportedSets = append(data.ImportedSets, set.Name)
|
||||
_ = cfg.saveBackup(data.Backup)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *AutoMigrateConfig) migrateStickers(data *Data) (err error) {
|
||||
if data.Backup, err = cfg.readBackup(); err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range data.Records {
|
||||
if data.Records[i].SetName == uploadedSetName {
|
||||
data.Records[i].SetName = common.SetNameUploaded
|
||||
}
|
||||
|
||||
if data.Records[i].SetName != common.SetNameUploaded {
|
||||
continue
|
||||
}
|
||||
|
||||
if containsString(data.Stickers, data.Records[i].FileID) {
|
||||
continue
|
||||
}
|
||||
|
||||
// NOTE(toby3d): send old sticker ID to get new
|
||||
result, err := cfg.Bot.SendSticker(tg.SendSticker{
|
||||
ChatID: cfg.GroupID,
|
||||
DisableNotification: true,
|
||||
Sticker: &tg.InputFile{ID: data.Records[i].FileID},
|
||||
})
|
||||
if err != nil || !result.IsSticker() {
|
||||
continue
|
||||
}
|
||||
|
||||
s := stickerToModel(result.Sticker)
|
||||
s.SetName = common.SetNameUploaded
|
||||
|
||||
if s.Emoji == "" {
|
||||
s.Emoji = data.Records[i].Emoji
|
||||
}
|
||||
|
||||
// NOTE(toby3d): store old-new stickers
|
||||
_ = cfg.Stickers.Create(s)
|
||||
u := cfg.Users.Get(data.Records[i].UserID)
|
||||
s = cfg.Stickers.Get(s.FileID)
|
||||
_ = cfg.UsersStickers.Add(&model.UserSticker{
|
||||
UserID: u.ID,
|
||||
StickerID: s.ID,
|
||||
})
|
||||
|
||||
data.Stickers = append(data.Stickers, data.Records[i].FileID)
|
||||
_ = cfg.saveBackup(data.Backup)
|
||||
_, _ = cfg.Bot.DeleteMessage(result.Chat.ID, result.ID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func containsInt(src []int, find int) bool {
|
||||
for i := range src {
|
||||
if src[i] != find {
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func containsString(src []string, find string) bool {
|
||||
for i := range src {
|
||||
if src[i] != find {
|
||||
continue
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func stickerToModel(s *tg.Sticker) *model.Sticker {
|
||||
sticker := new(model.Sticker)
|
||||
sticker.FileID = s.FileID
|
||||
sticker.Emoji = s.Emoji
|
||||
sticker.Width = s.Width
|
||||
sticker.Height = s.Height
|
||||
sticker.IsAnimated = s.IsAnimated
|
||||
sticker.SetName = s.SetName
|
||||
|
||||
if !sticker.InSet() {
|
||||
sticker.SetName = common.SetNameUploaded
|
||||
}
|
||||
|
||||
return sticker
|
||||
}
|
||||
|
||||
func (cfg *AutoMigrateConfig) readBackup() (bkp *Backup, err error) {
|
||||
bkp = new(Backup)
|
||||
filePath := filepath.Join(".", "backup.json")
|
||||
|
||||
if _, err = os.Stat(filePath); os.IsNotExist(err) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
src, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = cfg.Marshler.Unmarshal(src, bkp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return bkp, err
|
||||
}
|
||||
|
||||
func (cfg *AutoMigrateConfig) saveBackup(bkt *Backup) (err error) {
|
||||
src, err := cfg.Marshler.Marshal(bkt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filepath.Join(".", "buckup.json"), src, 0644)
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
type (
|
||||
UpdateFunc func(*Context) error
|
||||
|
||||
Context struct {
|
||||
*tg.Bot
|
||||
Request *tg.Update
|
||||
|
||||
User *User
|
||||
Sticker *Sticker
|
||||
HasSticker bool
|
||||
HasSet bool
|
||||
Photo *Photo
|
||||
HasPhoto bool
|
||||
|
||||
userValues context.Context
|
||||
}
|
||||
|
||||
contextKey string
|
||||
)
|
||||
|
||||
func (ctx *Context) Set(key string, val interface{}) {
|
||||
if ctx.userValues == nil {
|
||||
ctx.userValues = context.Background()
|
||||
}
|
||||
|
||||
ctx.userValues = context.WithValue(ctx.userValues, contextKey(key), val)
|
||||
}
|
||||
|
||||
func (ctx *Context) Get(key string) interface{} {
|
||||
if ctx.userValues == nil {
|
||||
ctx.userValues = context.Background()
|
||||
}
|
||||
|
||||
return ctx.userValues.Value(contextKey(key))
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
Message string
|
||||
Frame xerrors.Frame
|
||||
}
|
||||
|
||||
func (err Error) Error() string {
|
||||
return fmt.Sprint(err)
|
||||
}
|
||||
|
||||
func (err Error) Format(f fmt.State, c rune) {
|
||||
xerrors.FormatError(err, f, c)
|
||||
}
|
||||
|
||||
func (err Error) FormatError(p xerrors.Printer) error {
|
||||
p.Print(err.Message)
|
||||
|
||||
if p.Detail() {
|
||||
err.Frame.Format(p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
type (
|
||||
User struct {
|
||||
ID int `boltholdKey:"ID"`
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
LanguageCode string
|
||||
LastSeen int64
|
||||
}
|
||||
|
||||
Users []*User
|
||||
|
||||
Sticker struct {
|
||||
ID string `boltholdKey:"ID"`
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
FileID string
|
||||
Width int
|
||||
Height int
|
||||
IsAnimated bool
|
||||
SetName string
|
||||
Emoji string
|
||||
}
|
||||
|
||||
Stickers []*Sticker
|
||||
|
||||
Photo struct {
|
||||
ID string `boltholdKey:"ID"`
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
FileID string
|
||||
Width int
|
||||
Height int
|
||||
}
|
||||
|
||||
Photos []*Photo
|
||||
|
||||
UserSticker struct {
|
||||
ID uint64 `boltholdKey:"ID"`
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
UserID int
|
||||
StickerID string
|
||||
Query string
|
||||
}
|
||||
|
||||
UserStickers []*UserSticker
|
||||
|
||||
UserPhoto struct {
|
||||
ID uint64 `boltholdKey:"ID"`
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
UserID int
|
||||
PhotoID string
|
||||
Query string
|
||||
}
|
||||
|
||||
UserPhotos []*UserPhoto
|
||||
|
||||
InlineResult interface {
|
||||
GetType() string
|
||||
GetID() string
|
||||
GetFileID() string
|
||||
GetUpdatedAt() int64
|
||||
}
|
||||
)
|
||||
|
||||
func (s *Sticker) InSet() bool {
|
||||
return s.SetName != "" && !strings.EqualFold(s.SetName, common.SetNameUploaded)
|
||||
}
|
||||
|
||||
func (Sticker) GetType() string { return tg.TypeSticker }
|
||||
|
||||
func (s Sticker) GetID() string { return s.ID }
|
||||
|
||||
func (s Sticker) GetFileID() string { return s.FileID }
|
||||
|
||||
func (s Sticker) GetUpdatedAt() int64 { return s.UpdatedAt }
|
||||
|
||||
func (Photo) GetType() string { return tg.TypePhoto }
|
||||
|
||||
func (p Photo) GetID() string { return p.ID }
|
||||
|
||||
func (p Photo) GetFileID() string { return p.FileID }
|
||||
|
||||
func (p Photo) GetUpdatedAt() int64 { return p.UpdatedAt }
|
|
@ -0,0 +1,26 @@
|
|||
package photos
|
||||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type (
|
||||
Manager interface {
|
||||
Reader
|
||||
Writer
|
||||
ReadWriter
|
||||
}
|
||||
|
||||
ReadWriter interface {
|
||||
GetOrCreate(*model.Photo) (*model.Photo, error)
|
||||
}
|
||||
|
||||
Reader interface {
|
||||
Get(string) *model.Photo
|
||||
GetList(int, int, *model.Photo) (model.Photos, int, error)
|
||||
}
|
||||
|
||||
Writer interface {
|
||||
Create(*model.Photo) error
|
||||
Update(*model.Photo) error
|
||||
Remove(string) error
|
||||
}
|
||||
)
|
|
@ -0,0 +1,27 @@
|
|||
package stickers
|
||||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type (
|
||||
Manager interface {
|
||||
Reader
|
||||
Writer
|
||||
ReadWriter
|
||||
}
|
||||
|
||||
ReadWriter interface {
|
||||
GetOrCreate(s *model.Sticker) (*model.Sticker, error)
|
||||
}
|
||||
|
||||
Reader interface {
|
||||
Get(id string) *model.Sticker
|
||||
GetSet(name string) model.Stickers
|
||||
GetList(offset int, limit int, filter *model.Sticker) (model.Stickers, int, error)
|
||||
}
|
||||
|
||||
Writer interface {
|
||||
Create(s *model.Sticker) error
|
||||
Remove(id string) error
|
||||
Update(s *model.Sticker) error
|
||||
}
|
||||
)
|
|
@ -0,0 +1,21 @@
|
|||
package photos
|
||||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type (
|
||||
ReadWriter interface {
|
||||
Reader
|
||||
Writer
|
||||
}
|
||||
|
||||
Reader interface {
|
||||
Get(up *model.UserPhoto) *model.Photo
|
||||
GetList(offset int, limit int, filter *model.UserPhoto) (model.Photos, int, error)
|
||||
}
|
||||
|
||||
Writer interface {
|
||||
Add(up *model.UserPhoto) error
|
||||
Update(up *model.UserPhoto) error
|
||||
Remove(up *model.UserPhoto) error
|
||||
}
|
||||
)
|
|
@ -0,0 +1,23 @@
|
|||
package stickers
|
||||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type (
|
||||
ReadWriter interface {
|
||||
Reader
|
||||
Writer
|
||||
}
|
||||
|
||||
Reader interface {
|
||||
Get(up *model.UserSticker) *model.Sticker
|
||||
GetList(offset int, limit int, filter *model.UserSticker) (model.Stickers, int, error)
|
||||
}
|
||||
|
||||
Writer interface {
|
||||
Add(up *model.UserSticker) error
|
||||
AddSet(uid int, setName string) error
|
||||
Update(up *model.UserSticker) error
|
||||
Remove(up *model.UserSticker) error
|
||||
RemoveSet(uid int, setName string) error
|
||||
}
|
||||
)
|
|
@ -0,0 +1,25 @@
|
|||
package users
|
||||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type (
|
||||
Manager interface {
|
||||
Reader
|
||||
Writer
|
||||
ReadWriter
|
||||
}
|
||||
|
||||
ReadWriter interface {
|
||||
GetOrCreate(u *model.User) (*model.User, error)
|
||||
}
|
||||
|
||||
Reader interface {
|
||||
Get(id int) *model.User
|
||||
GetList(offset, limit int, filter *model.User) (model.Users, int, error)
|
||||
}
|
||||
|
||||
Writer interface {
|
||||
Create(u *model.User) error
|
||||
Update(u *model.User) error
|
||||
}
|
||||
)
|
|
@ -1,37 +0,0 @@
|
|||
package models
|
||||
|
||||
import tg "gitlab.com/toby3d/telegram"
|
||||
|
||||
// Commands... represents available and supported bot commands
|
||||
const (
|
||||
CommandAddPack = "addPack"
|
||||
CommandAddSticker = "addSticker"
|
||||
CommandCancel = "cancel"
|
||||
CommandDeleteSticker = "delSticker"
|
||||
CommandDeletePack = "delPack"
|
||||
CommandReset = "reset"
|
||||
)
|
||||
|
||||
// State... represents current state of user action
|
||||
const (
|
||||
StateNone = "none"
|
||||
StateAddSticker = CommandAddSticker
|
||||
StateAddPack = CommandAddPack
|
||||
StateDeleteSticker = CommandDeleteSticker
|
||||
StateDeletePack = CommandDeletePack
|
||||
StateReset = CommandReset
|
||||
)
|
||||
|
||||
// SetUploaded is a mimic set name of uploaded stickers without any parent set
|
||||
const SetUploaded = "?"
|
||||
|
||||
// LanguageFallback is a default language code in case what current user language
|
||||
// is not support yet
|
||||
const LanguageFallback = "en"
|
||||
|
||||
// AllowedUpdates is
|
||||
var AllowedUpdates = []string{
|
||||
tg.UpdateInlineQuery, // For searching and sending stickers
|
||||
tg.UpdateMessage, // For get commands and messages
|
||||
tg.UpdateChannelPost, // For forwarding announcements
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/toby3d/mypackbot/internal/config"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/photos"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/stickers"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/users"
|
||||
usersphotos "gitlab.com/toby3d/mypackbot/internal/model/users/photos"
|
||||
usersstickers "gitlab.com/toby3d/mypackbot/internal/model/users/stickers"
|
||||
"gitlab.com/toby3d/mypackbot/internal/store"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
type MyPackBot struct {
|
||||
bot *tg.Bot
|
||||
config *viper.Viper
|
||||
photos photos.Manager
|
||||
stickers stickers.Manager
|
||||
users users.Manager
|
||||
usersPhotos usersphotos.ReadWriter
|
||||
usersStickers usersstickers.ReadWriter
|
||||
}
|
||||
|
||||
func New(path string) (mpb *MyPackBot, err error) {
|
||||
mpb = new(MyPackBot)
|
||||
|
||||
if mpb.config, err = config.Open(path); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn, err := db.Open(mpb.config.GetString("database.filepath"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mpb.photos = store.NewPhotosStore(conn)
|
||||
mpb.stickers = store.NewStickersStore(conn)
|
||||
mpb.users = store.NewUsersStore(conn)
|
||||
mpb.usersPhotos = store.NewUsersPhotosStore(conn, mpb.users, mpb.photos)
|
||||
mpb.usersStickers = store.NewUsersStickersStore(conn, mpb.users, mpb.stickers)
|
||||
|
||||
if mpb.bot, err = tg.New(mpb.config.GetString("telegram.token")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mpb, nil
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/kirillDanshin/dlog"
|
||||
http "github.com/valyala/fasthttp"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/handler"
|
||||
"gitlab.com/toby3d/mypackbot/internal/middleware"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
func (mpb *MyPackBot) Run() error {
|
||||
shutdown, err := mpb.getUpdateChannel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() { _ = shutdown() }()
|
||||
|
||||
chain := middleware.Chain{
|
||||
middleware.AcquireUser(mpb.users),
|
||||
middleware.AcquirePrinter(),
|
||||
middleware.ChatAction(),
|
||||
middleware.AcquirePhoto(mpb.photos), middleware.AcquireUserPhoto(mpb.usersPhotos),
|
||||
middleware.AcquireSticker(mpb.stickers), middleware.AcquireUserSticker(mpb.usersStickers),
|
||||
func() middleware.Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) error {
|
||||
if ctx.Sticker == nil || ctx.Sticker.SetName == common.SetNameUploaded {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
for _, sticker := range mpb.stickers.GetSet(ctx.Sticker.SetName) {
|
||||
if mpb.usersStickers.Get(&model.UserSticker{
|
||||
UserID: ctx.User.ID,
|
||||
StickerID: sticker.ID,
|
||||
}) == nil {
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
ctx.HasSet = true
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
}(),
|
||||
middleware.Birthday(time.Date(0, time.November, 4, 0, 0, 0, 0, time.UTC)), middleware.Hacktober(),
|
||||
middleware.UpdateLastSeen(mpb.users),
|
||||
}
|
||||
h := chain.UpdateHandler(handler.NewHandler(
|
||||
mpb.users, mpb.stickers, mpb.photos, mpb.usersStickers, mpb.usersPhotos,
|
||||
).UpdateHandler)
|
||||
|
||||
for update := range mpb.bot.Updates {
|
||||
ctx := new(model.Context)
|
||||
ctx.Request = update
|
||||
ctx.Bot = mpb.bot
|
||||
|
||||
if err = h(ctx); err != nil {
|
||||
dlog.D(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mpb *MyPackBot) getUpdateChannel() (func() error, error) {
|
||||
switch {
|
||||
case mpb.config.IsSet("telegram.webhook"):
|
||||
cfg := mpb.config.Sub("telegram.webhook")
|
||||
u := http.AcquireURI()
|
||||
|
||||
defer http.ReleaseURI(u)
|
||||
|
||||
cert := make([]string, 0, 2)
|
||||
if cfg.IsSet("certificate") {
|
||||
cert = append(cert, cfg.GetString("certificate"))
|
||||
}
|
||||
|
||||
if cfg.IsSet("key") {
|
||||
cert = append(cert, cfg.GetString("key"))
|
||||
}
|
||||
|
||||
ln, err := net.Listen("tcp", cfg.GetString("serve"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
updates, release := mpb.bot.NewWebhookChannel(u, tg.SetWebhook{
|
||||
AllowedUpdates: cfg.GetStringSlice("allowed_updates"),
|
||||
}, ln, cert...)
|
||||
mpb.bot.Updates = updates
|
||||
return release, nil
|
||||
case mpb.config.IsSet("telegram.long_poll"):
|
||||
if _, err := mpb.bot.DeleteWebhook(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := mpb.config.Sub("telegram.long_poll")
|
||||
mpb.bot.Updates = mpb.bot.NewLongPollingChannel(&tg.GetUpdates{
|
||||
AllowedUpdates: cfg.GetStringSlice("allowed_updates"),
|
||||
Limit: cfg.GetInt("limit"),
|
||||
Offset: cfg.GetInt("offset"),
|
||||
Timeout: cfg.GetInt("timeout"),
|
||||
})
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type PhotosStore struct {
|
||||
conn *bolthold.Store
|
||||
}
|
||||
|
||||
func NewPhotosStore(conn *bolthold.Store) *PhotosStore {
|
||||
return &PhotosStore{conn: conn}
|
||||
}
|
||||
|
||||
func (store *PhotosStore) Create(p *model.Photo) error {
|
||||
if store.Get(p.ID) != nil {
|
||||
return bolthold.ErrKeyExists
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.InsertIntoBucket(tx.Bucket(common.BucketPhotos), p.ID, p)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *PhotosStore) Get(id string) *model.Photo {
|
||||
result := new(model.Photo)
|
||||
|
||||
if err := store.conn.Bolt().View(func(tx *bolt.Tx) error {
|
||||
return store.conn.GetFromBucket(tx.Bucket(common.BucketPhotos), id, result)
|
||||
}); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (store *PhotosStore) GetList(offset, limit int, filter *model.Photo) (list model.Photos, count int, err error) {
|
||||
q := bolthold.Where("ID").Ne("")
|
||||
|
||||
if offset > 0 {
|
||||
q = q.Skip(offset)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
q = q.Limit(limit)
|
||||
}
|
||||
|
||||
// TODO(toby3d): implement filter here
|
||||
list = make(model.Photos, limit)
|
||||
|
||||
if err := store.conn.Bolt().View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(common.BucketPhotos)
|
||||
|
||||
if count, err = store.conn.CountInBucket(bkt, &model.Photo{}, q); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.FindInBucket(bkt, &list, q)
|
||||
}); err != nil {
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
func (store *PhotosStore) Update(p *model.Photo) error {
|
||||
if store.Get(p.ID) == nil {
|
||||
return store.Create(p)
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.UpdateBucket(tx.Bucket(common.BucketPhotos), p.ID, p)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *PhotosStore) Remove(id string) error {
|
||||
if store.Get(id) == nil {
|
||||
return bolthold.ErrNotFound
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.DeleteFromBucket(tx.Bucket(common.BucketPhotos), id, &model.Photo{})
|
||||
})
|
||||
}
|
||||
|
||||
func (store *PhotosStore) GetOrCreate(p *model.Photo) (*model.Photo, error) {
|
||||
if photo := store.Get(p.ID); photo != nil {
|
||||
return photo, nil
|
||||
}
|
||||
|
||||
if err := store.Create(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return store.GetOrCreate(p)
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type StickersStore struct {
|
||||
conn *bolthold.Store
|
||||
}
|
||||
|
||||
func NewStickersStore(conn *bolthold.Store) *StickersStore {
|
||||
return &StickersStore{conn: conn}
|
||||
}
|
||||
|
||||
func (store *StickersStore) Create(s *model.Sticker) error {
|
||||
if store.Get(s.ID) != nil {
|
||||
return bolthold.ErrKeyExists
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.InsertIntoBucket(tx.Bucket(common.BucketStickers), s.ID, s)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *StickersStore) Get(id string) *model.Sticker {
|
||||
result := new(model.Sticker)
|
||||
|
||||
if err := store.conn.Bolt().View(func(tx *bolt.Tx) error {
|
||||
return store.conn.GetFromBucket(tx.Bucket(common.BucketStickers), id, result)
|
||||
}); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (store *StickersStore) GetSet(name string) model.Stickers {
|
||||
list, _, _ := store.GetList(0, 0, &model.Sticker{SetName: name})
|
||||
return list
|
||||
}
|
||||
|
||||
func (store *StickersStore) GetList(offset, limit int, filter *model.Sticker) (list model.Stickers, count int,
|
||||
err error) {
|
||||
q := bolthold.Where("ID").Ne("")
|
||||
|
||||
if offset > 0 {
|
||||
q = q.Skip(offset)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
q = q.Limit(limit)
|
||||
}
|
||||
|
||||
if filter != nil {
|
||||
if filter.Emoji != "" {
|
||||
q = q.And("Emoji").ContainsAny(filter.Emoji)
|
||||
}
|
||||
|
||||
if filter.SetName != "" {
|
||||
q = q.And("SetName").Eq(filter.SetName)
|
||||
}
|
||||
}
|
||||
|
||||
list = make(model.Stickers, limit)
|
||||
|
||||
if err := store.conn.Bolt().View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(common.BucketStickers)
|
||||
|
||||
if count, err = store.conn.CountInBucket(bkt, &model.Sticker{}, q); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.FindInBucket(bkt, &list, q)
|
||||
}); err != nil {
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
func (store *StickersStore) Update(s *model.Sticker) error {
|
||||
if store.Get(s.ID) == nil {
|
||||
return store.Create(s)
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.UpdateBucket(tx.Bucket(common.BucketStickers), s.ID, s)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *StickersStore) Remove(id string) error {
|
||||
if store.Get(id) == nil {
|
||||
return bolthold.ErrNotFound
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.DeleteFromBucket(tx.Bucket(common.BucketStickers), id, &model.Sticker{})
|
||||
})
|
||||
}
|
||||
|
||||
func (store *StickersStore) GetOrCreate(s *model.Sticker) (*model.Sticker, error) {
|
||||
if sticker := store.Get(s.ID); sticker != nil {
|
||||
return sticker, nil
|
||||
}
|
||||
|
||||
if err := store.Create(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return store.GetOrCreate(s)
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
usersphotos "gitlab.com/toby3d/mypackbot/internal/model/users/photos"
|
||||
usersstickers "gitlab.com/toby3d/mypackbot/internal/model/users/stickers"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
type (
|
||||
Store struct {
|
||||
usersStickers usersstickers.ReadWriter
|
||||
usersPhotos usersphotos.ReadWriter
|
||||
}
|
||||
|
||||
Filter struct {
|
||||
UserID int
|
||||
AllowedTypes []string
|
||||
Query string
|
||||
Offset int
|
||||
Limit int
|
||||
}
|
||||
)
|
||||
|
||||
func (f *Filter) offset() int {
|
||||
if len(f.AllowedTypes) == 0 || f.Offset <= len(f.AllowedTypes) {
|
||||
return f.Offset
|
||||
}
|
||||
|
||||
return f.Offset / len(f.AllowedTypes)
|
||||
}
|
||||
|
||||
func (f *Filter) limit() int {
|
||||
if len(f.AllowedTypes) == 0 || f.Limit <= len(f.AllowedTypes) {
|
||||
return f.Limit
|
||||
}
|
||||
|
||||
return f.Limit / len(f.AllowedTypes)
|
||||
}
|
||||
|
||||
func NewStore(us usersstickers.ReadWriter, up usersphotos.ReadWriter) *Store {
|
||||
return &Store{
|
||||
usersStickers: us,
|
||||
usersPhotos: up,
|
||||
}
|
||||
}
|
||||
|
||||
func (store *Store) GetList(offset, limit int, filter *Filter) (list []model.InlineResult, count int, err error) {
|
||||
if filter == nil {
|
||||
filter = new(Filter)
|
||||
filter.AllowedTypes = []string{tg.TypeSticker, tg.TypePhoto}
|
||||
}
|
||||
|
||||
list = make([]model.InlineResult, 0)
|
||||
|
||||
for _, t := range filter.AllowedTypes {
|
||||
switch t {
|
||||
case tg.TypeSticker:
|
||||
l, c, err := store.usersStickers.GetList(
|
||||
filter.offset(), filter.limit(), &model.UserSticker{
|
||||
UserID: filter.UserID,
|
||||
Query: filter.Query,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
for i := range l {
|
||||
list = append(list, l[i])
|
||||
}
|
||||
|
||||
count += c
|
||||
case tg.TypePhoto:
|
||||
l, c, err := store.usersPhotos.GetList(
|
||||
filter.offset(), filter.limit(), &model.UserPhoto{
|
||||
UserID: filter.UserID,
|
||||
Query: filter.Query,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
for i := range l {
|
||||
list = append(list, l[i])
|
||||
}
|
||||
|
||||
count += c
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].GetUpdatedAt() < list[j].GetUpdatedAt()
|
||||
})
|
||||
|
||||
return list, count, err
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type UsersStore struct {
|
||||
conn *bolthold.Store
|
||||
}
|
||||
|
||||
func NewUsersStore(conn *bolthold.Store) *UsersStore {
|
||||
return &UsersStore{conn: conn}
|
||||
}
|
||||
|
||||
func (store *UsersStore) Create(u *model.User) error {
|
||||
if store.Get(u.ID) != nil {
|
||||
return bolthold.ErrKeyExists
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.InsertIntoBucket(tx.Bucket(common.BucketUsers), u.ID, u)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *UsersStore) Get(id int) *model.User {
|
||||
result := new(model.User)
|
||||
|
||||
if err := store.conn.Bolt().View(func(tx *bolt.Tx) error {
|
||||
return store.conn.GetFromBucket(tx.Bucket(common.BucketUsers), id, result)
|
||||
}); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (store *UsersStore) GetList(offset, limit int, filter *model.User) (list model.Users, count int, err error) {
|
||||
list = make(model.Users, limit)
|
||||
q := bolthold.Where("ID").Ne("")
|
||||
|
||||
if offset > 0 {
|
||||
q = q.Skip(offset)
|
||||
}
|
||||
|
||||
if limit > 0 {
|
||||
q = q.Limit(limit)
|
||||
}
|
||||
|
||||
// TODO(toby3d): implement filter here
|
||||
|
||||
if err := store.conn.Bolt().View(func(tx *bolt.Tx) (err error) {
|
||||
bkt := tx.Bucket(common.BucketUsers)
|
||||
if count, err = store.conn.CountInBucket(bkt, &model.User{}, q); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.FindInBucket(bkt, &list, q)
|
||||
}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
func (store *UsersStore) Update(u *model.User) error {
|
||||
if store.Get(u.ID) == nil {
|
||||
return store.Create(u)
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) (err error) {
|
||||
return store.conn.UpdateBucket(tx.Bucket(common.BucketUsers), u.ID, u)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *UsersStore) GetOrCreate(u *model.User) (*model.User, error) {
|
||||
if user := store.Get(u.ID); user != nil {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
if err := store.Create(u); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/photos"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/users"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type UsersPhotosStore struct {
|
||||
conn *bolthold.Store
|
||||
photos photos.Reader
|
||||
users users.Reader
|
||||
}
|
||||
|
||||
func NewUsersPhotosStore(conn *bolthold.Store, us users.Reader, ps photos.Reader) *UsersPhotosStore {
|
||||
return &UsersPhotosStore{
|
||||
conn: conn,
|
||||
photos: ps,
|
||||
users: us,
|
||||
}
|
||||
}
|
||||
|
||||
func (store *UsersPhotosStore) Add(up *model.UserPhoto) (err error) {
|
||||
if store.users.Get(up.UserID) == nil || store.photos.Get(up.PhotoID) == nil {
|
||||
return bolthold.ErrNotFound
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.InsertIntoBucket(tx.Bucket(common.BucketUsersPhotos), bolthold.NextSequence(), up)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *UsersPhotosStore) Update(up *model.UserPhoto) (err error) {
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.UpdateMatchingInBucket(
|
||||
tx.Bucket(common.BucketUsersPhotos), &model.UserPhoto{},
|
||||
bolthold.Where("UserID").Eq(up.UserID).And("PhotoID").Eq(up.PhotoID),
|
||||
func(record interface{}) error {
|
||||
result, ok := record.(*model.UserPhoto) // record will always be a pointer
|
||||
if !ok {
|
||||
return xerrors.New("invalid type")
|
||||
}
|
||||
|
||||
result.Query, result.UpdatedAt = up.Query, up.UpdatedAt
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (store *UsersPhotosStore) Get(up *model.UserPhoto) *model.Photo {
|
||||
result := new(model.UserPhoto)
|
||||
|
||||
if err := store.conn.Bolt().View(func(tx *bolt.Tx) error {
|
||||
return store.conn.FindOneInBucket(
|
||||
tx.Bucket(common.BucketUsersPhotos), result,
|
||||
bolthold.Where("UserID").Eq(up.UserID).And("PhotoID").Eq(up.PhotoID),
|
||||
)
|
||||
}); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return store.photos.Get(result.PhotoID)
|
||||
}
|
||||
|
||||
func (store *UsersPhotosStore) GetList(offset, limit int, filter *model.UserPhoto) (list model.Photos, count int,
|
||||
err error) {
|
||||
|
||||
q := bolthold.Where("UserID").Ne(0).And("PhotoID").Ne("")
|
||||
qCount := bolthold.Where("UserID").Ne(0).And("PhotoID").Ne("")
|
||||
|
||||
if offset != 0 {
|
||||
q = q.Skip(offset)
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
q = q.Limit(limit)
|
||||
}
|
||||
|
||||
if filter != nil {
|
||||
if filter.UserID != 0 {
|
||||
q = q.And("UserID").Eq(filter.UserID)
|
||||
qCount.And("UserID").Eq(filter.UserID)
|
||||
}
|
||||
|
||||
if filter.Query != "" {
|
||||
q = q.And("Query").MatchFunc(func(field string) (bool, error) {
|
||||
return strings.ContainsAny(field, filter.Query), nil
|
||||
})
|
||||
qCount.And("Query").MatchFunc(func(field string) (bool, error) {
|
||||
return strings.ContainsAny(field, filter.Query), nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
results := make(model.UserPhotos, 0)
|
||||
|
||||
if err = store.conn.Bolt().View(func(tx *bolt.Tx) (err error) {
|
||||
bkt := tx.Bucket(common.BucketUsersPhotos)
|
||||
|
||||
if count, err = store.conn.CountInBucket(bkt, &model.UserPhoto{}, qCount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.FindInBucket(bkt, &results, q)
|
||||
}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list = make(model.Photos, 0)
|
||||
|
||||
for i := range results {
|
||||
list = append(list, store.photos.Get(results[i].PhotoID))
|
||||
}
|
||||
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
func (store *UsersPhotosStore) Remove(up *model.UserPhoto) (err error) {
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.DeleteMatchingFromBucket(
|
||||
tx.Bucket(common.BucketUsersPhotos), &model.UserPhoto{},
|
||||
bolthold.Where("UserID").Eq(up.UserID).And("PhotoID").Eq(up.PhotoID),
|
||||
)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/stickers"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/users"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
type UsersStickersStore struct {
|
||||
conn *bolthold.Store
|
||||
stickers stickers.Reader
|
||||
users users.Reader
|
||||
}
|
||||
|
||||
func NewUsersStickersStore(conn *bolthold.Store, us users.Reader, ss stickers.Reader) *UsersStickersStore {
|
||||
return &UsersStickersStore{
|
||||
conn: conn,
|
||||
stickers: ss,
|
||||
users: us,
|
||||
}
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) Add(us *model.UserSticker) error {
|
||||
if store.users.Get(us.UserID) == nil || store.stickers.Get(us.StickerID) == nil {
|
||||
return bolthold.ErrNotFound
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.InsertIntoBucket(tx.Bucket(common.BucketUsersStickers), bolthold.NextSequence(), us)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) AddSet(uid int, setName string) error {
|
||||
for _, sticker := range store.stickers.GetSet(setName) {
|
||||
us := model.UserSticker{UserID: uid, StickerID: sticker.ID}
|
||||
|
||||
if store.Get(&us) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
us.Query = sticker.Emoji
|
||||
us.CreatedAt = now
|
||||
us.UpdatedAt = now
|
||||
|
||||
if err := store.Add(&us); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) Update(us *model.UserSticker) (err error) {
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.UpdateMatchingInBucket(
|
||||
tx.Bucket(common.BucketUsersStickers), &model.UserSticker{},
|
||||
bolthold.Where("UserID").Eq(us.UserID).And("StickerID").Eq(us.StickerID),
|
||||
func(record interface{}) error {
|
||||
result, ok := record.(*model.UserSticker) // record will always be a pointer
|
||||
if !ok {
|
||||
return xerrors.New("invalid type")
|
||||
}
|
||||
|
||||
result.Query, result.UpdatedAt = us.Query, us.UpdatedAt
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) Get(us *model.UserSticker) *model.Sticker {
|
||||
result := new(model.UserSticker)
|
||||
|
||||
if err := store.conn.Bolt().View(func(tx *bolt.Tx) error {
|
||||
return store.conn.FindOneInBucket(
|
||||
tx.Bucket(common.BucketUsersStickers), result,
|
||||
bolthold.Where("UserID").Eq(us.UserID).And("StickerID").Eq(us.StickerID),
|
||||
)
|
||||
}); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return store.stickers.Get(result.StickerID)
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) GetList(offset, limit int, filter *model.UserSticker) (list model.Stickers, count int,
|
||||
err error) {
|
||||
|
||||
q := bolthold.Where("UserID").Ne(0).And("StickerID").Ne("")
|
||||
qCount := bolthold.Where("UserID").Ne(0).And("StickerID").Ne("")
|
||||
|
||||
if offset != 0 {
|
||||
q = q.Skip(offset)
|
||||
}
|
||||
|
||||
if limit != 0 {
|
||||
q = q.Limit(limit)
|
||||
}
|
||||
|
||||
if filter != nil {
|
||||
if filter.UserID != 0 {
|
||||
q = q.And("UserID").Eq(filter.UserID)
|
||||
qCount = qCount.And("UserID").Eq(filter.UserID)
|
||||
}
|
||||
|
||||
if filter.Query != "" {
|
||||
q = q.And("Query").MatchFunc(func(field string) (bool, error) {
|
||||
return strings.ContainsAny(field, filter.Query), nil
|
||||
})
|
||||
qCount = qCount.And("Query").MatchFunc(func(field string) (bool, error) {
|
||||
return strings.ContainsAny(field, filter.Query), nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
results := make(model.UserStickers, 0)
|
||||
|
||||
if err = store.conn.Bolt().View(func(tx *bolt.Tx) (err error) {
|
||||
bkt := tx.Bucket(common.BucketUsersStickers)
|
||||
|
||||
if count, err = store.conn.CountInBucket(bkt, &model.UserSticker{}, qCount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.FindInBucket(bkt, &results, q)
|
||||
}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
list = make(model.Stickers, 0)
|
||||
|
||||
for i := range results {
|
||||
list = append(list, store.stickers.Get(results[i].StickerID))
|
||||
}
|
||||
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) Remove(us *model.UserSticker) error {
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.DeleteMatchingFromBucket(
|
||||
tx.Bucket(common.BucketUsersStickers), &model.UserSticker{},
|
||||
bolthold.Where("UserID").Eq(us.UserID).And("StickerID").Eq(us.StickerID),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) RemoveSet(uid int, setName string) error {
|
||||
ids := make([]string, 0)
|
||||
|
||||
for _, sticker := range store.stickers.GetSet(setName) {
|
||||
ids = append(ids, sticker.ID)
|
||||
}
|
||||
|
||||
return store.conn.Bolt().Update(func(tx *bolt.Tx) error {
|
||||
return store.conn.DeleteMatchingFromBucket(
|
||||
tx.Bucket(common.BucketUsersStickers), &model.UserSticker{},
|
||||
bolthold.Where("UserID").Eq(uid).And("StickerID").ContainsAny(ids),
|
||||
)
|
||||
})
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/config"
|
||||
"gitlab.com/toby3d/mypackbot/internal/models"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Channel return webhook or long polling channel with bot updates
|
||||
func Channel(webhookMode bool) (updates tg.UpdatesChannel, err error) {
|
||||
log.Ln("Preparing channel for updates...")
|
||||
if !webhookMode {
|
||||
log.Ln("Use LongPolling updates")
|
||||
|
||||
var info *tg.WebhookInfo
|
||||
info, err = bot.Bot.GetWebhookInfo()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if info.URL != "" {
|
||||
log.Ln("Deleting webhook...")
|
||||
_, err = bot.Bot.DeleteWebhook()
|
||||
return
|
||||
}
|
||||
|
||||
updates = bot.Bot.NewLongPollingChannel(&tg.GetUpdatesParameters{
|
||||
Offset: 0,
|
||||
Limit: 100,
|
||||
Timeout: 60,
|
||||
AllowedUpdates: models.AllowedUpdates,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
set, err := url.Parse(config.Config.GetString("telegram.webhook.set"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listen := config.Config.GetString("telegram.webhook.listen")
|
||||
serve := config.Config.GetString("telegram.webhook.serve")
|
||||
|
||||
log.Ln(
|
||||
"Trying set webhook on address:",
|
||||
fmt.Sprint(set.String(), bot.Bot.AccessToken),
|
||||
)
|
||||
|
||||
log.Ln("Creating new webhook...")
|
||||
params := tg.NewWebhook(fmt.Sprint(set, listen, bot.Bot.AccessToken), nil)
|
||||
params.MaxConnections = 40
|
||||
params.AllowedUpdates = models.AllowedUpdates
|
||||
|
||||
updates = bot.Bot.NewWebhookChannel(
|
||||
set,
|
||||
params, // params
|
||||
"", // certFile
|
||||
"", // keyFile
|
||||
serve, // serve
|
||||
)
|
||||
|
||||
return
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/config"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// ChannelPost checks ChannelPost update for forwarding content to bot users
|
||||
func ChannelPost(post *tg.Message) {
|
||||
channelID := config.Config.GetInt64("telegram.channel")
|
||||
if post.Chat.ID != channelID {
|
||||
log.Ln(post.Chat.ID, "!=", channelID)
|
||||
return
|
||||
}
|
||||
|
||||
users, err := db.DB.GetUsers()
|
||||
errors.Check(err)
|
||||
|
||||
for i := range users {
|
||||
errors.WaitForwards.Add(1)
|
||||
if _, err = bot.Bot.ForwardMessage(
|
||||
tg.NewForwardMessage(post.Chat.ID, int64(users[i]), post.ID),
|
||||
); err != nil {
|
||||
log.Ln(err.Error())
|
||||
}
|
||||
errors.WaitForwards.Done()
|
||||
|
||||
time.Sleep(time.Second / 10) // For avoid spamming
|
||||
}
|
||||
}
|
|
@ -1,86 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/errors"
|
||||
"gitlab.com/toby3d/mypackbot/internal/i18n"
|
||||
"gitlab.com/toby3d/mypackbot/internal/utils"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// InlineQuery checks InlineQuery updates for answer with personal results
|
||||
func InlineQuery(inlineQuery *tg.InlineQuery) {
|
||||
fixedQuery, err := utils.FixEmoji(inlineQuery.Query)
|
||||
if err == nil {
|
||||
inlineQuery.Query = fixedQuery
|
||||
}
|
||||
|
||||
answer := new(tg.AnswerInlineQueryParameters)
|
||||
answer.InlineQueryID = inlineQuery.ID
|
||||
answer.CacheTime = 1
|
||||
answer.IsPersonal = true
|
||||
|
||||
if len([]rune(inlineQuery.Query)) >= 256 {
|
||||
_, err = bot.Bot.AnswerInlineQuery(answer)
|
||||
errors.Check(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Ln("Let's preparing answer...")
|
||||
t, err := i18n.SwitchTo(inlineQuery.From.LanguageCode)
|
||||
errors.Check(err)
|
||||
|
||||
log.Ln("INLINE OFFSET:", inlineQuery.Offset)
|
||||
if inlineQuery.Offset == "" {
|
||||
inlineQuery.Offset = "-1"
|
||||
}
|
||||
offset, err := strconv.Atoi(inlineQuery.Offset)
|
||||
errors.Check(err)
|
||||
offset++
|
||||
|
||||
stickers, err := db.DB.GetUserStickers(
|
||||
inlineQuery.From,
|
||||
&tg.InlineQuery{
|
||||
Offset: strconv.Itoa(offset),
|
||||
Query: inlineQuery.Query,
|
||||
},
|
||||
)
|
||||
errors.Check(err)
|
||||
|
||||
if len(stickers) == 0 {
|
||||
if offset == 0 && inlineQuery.Query != "" {
|
||||
// If search stickers by emoji return 0 results
|
||||
answer.SwitchPrivateMessageText = t(
|
||||
"button_inline_nothing", map[string]interface{}{
|
||||
"Query": inlineQuery.Query,
|
||||
},
|
||||
)
|
||||
|
||||
answer.SwitchPrivateMessageParameter = tg.CommandHelp
|
||||
}
|
||||
|
||||
answer.Results = nil
|
||||
} else {
|
||||
log.Ln("STICKERS FROM REQUEST:", len(stickers))
|
||||
if len(stickers) == 50 {
|
||||
answer.NextOffset = strconv.Itoa(offset)
|
||||
log.Ln("NEXT OFFSET:", answer.NextOffset)
|
||||
}
|
||||
|
||||
var results = make([]interface{}, len(stickers))
|
||||
for i, sticker := range stickers {
|
||||
results[i] = tg.NewInlineQueryResultCachedSticker(sticker, sticker)
|
||||
}
|
||||
|
||||
answer.Results = results
|
||||
}
|
||||
|
||||
log.Ln("CacheTime:", answer.CacheTime)
|
||||
|
||||
_, err = bot.Bot.AnswerInlineQuery(answer)
|
||||
errors.Check(err)
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package updates
|
||||
|
||||
import (
|
||||
log "github.com/kirillDanshin/dlog"
|
||||
"gitlab.com/toby3d/mypackbot/internal/actions"
|
||||
"gitlab.com/toby3d/mypackbot/internal/bot"
|
||||
"gitlab.com/toby3d/mypackbot/internal/commands"
|
||||
"gitlab.com/toby3d/mypackbot/internal/messages"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// Message checks Message updates for answer to user commands, replies or sended
|
||||
// stickers
|
||||
func Message(msg *tg.Message) {
|
||||
if bot.Bot.IsMessageFromMe(msg) ||
|
||||
bot.Bot.IsForwardFromMe(msg) {
|
||||
log.Ln("Ignore message update")
|
||||
return
|
||||
}
|
||||
|
||||
switch {
|
||||
case bot.Bot.IsCommandToMe(msg):
|
||||
commands.Command(msg)
|
||||
case msg.IsText():
|
||||
messages.Message(msg)
|
||||
default:
|
||||
actions.Action(msg)
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"github.com/nicksnyder/go-i18n/i18n"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
// CancelButton helper just generate ReplyMarkup with cancel button
|
||||
func CancelButton(t i18n.TranslateFunc) *tg.ReplyKeyboardMarkup {
|
||||
return tg.NewReplyKeyboardMarkup(
|
||||
tg.NewReplyKeyboardRow(
|
||||
tg.NewReplyKeyboardButton(t("button_cancel")),
|
||||
),
|
||||
)
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"golang.org/x/text/runes"
|
||||
"golang.org/x/text/transform"
|
||||
)
|
||||
|
||||
// Skin colors for remove
|
||||
var bannedSkins = []rune{127995, 127996, 127997, 127998, 127999}
|
||||
|
||||
// Transformer for remove skin colors
|
||||
var skinRemover = runes.Remove(runes.Predicate(
|
||||
func(r rune) bool {
|
||||
for _, skin := range bannedSkins {
|
||||
if r == skin {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
))
|
||||
|
||||
// FixEmoji fixes user input by remove all potential skin colors
|
||||
func FixEmoji(raw string) (string, error) {
|
||||
result, _, err := transform.String(skinRemover, raw)
|
||||
return result, err
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue