♻️ Refactored stores by using bolthold package
This commit is contained in:
parent
1b7945cb71
commit
7ce10980cc
|
@ -5,7 +5,6 @@ import (
|
|||
"log"
|
||||
"path/filepath"
|
||||
|
||||
json "github.com/json-iterator/go"
|
||||
bunt "github.com/tidwall/buntdb"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
"gitlab.com/toby3d/mypackbot/internal/migrator"
|
||||
|
@ -40,10 +39,9 @@ func main() {
|
|||
}
|
||||
defer newDB.Close()
|
||||
|
||||
marshler := json.ConfigFastest
|
||||
users := store.NewUsersStore(newDB, marshler)
|
||||
stickers := store.NewStickersStore(newDB, marshler)
|
||||
usersStickers := store.NewUsersStickersStore(newDB, users, stickers, marshler)
|
||||
users := store.NewUsersStore(newDB)
|
||||
stickers := store.NewStickersStore(newDB)
|
||||
usersStickers := store.NewUsersStickersStore(newDB, users, stickers)
|
||||
|
||||
if err = migrator.AutoMigrate(migrator.AutoMigrateConfig{
|
||||
Bot: bot,
|
||||
|
@ -52,7 +50,6 @@ func main() {
|
|||
UsersStickers: usersStickers,
|
||||
Users: users,
|
||||
OldDB: oldDB,
|
||||
Marshler: marshler,
|
||||
}); err != nil {
|
||||
log.Fatalln("ERROR:", err.Error())
|
||||
}
|
||||
|
|
|
@ -36,90 +36,81 @@ func init() {
|
|||
}
|
||||
|
||||
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!": 1,
|
||||
"👋 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.": 6,
|
||||
"👍 Imported!": 11,
|
||||
"👍 Removed!": 13,
|
||||
"👍 Updated!": 12,
|
||||
"💡 Add any text and/or emoji(s) as an argument of this command to change its search properties.": 18,
|
||||
"💡 Use /del command as an reply to the sticker/photo to remove it from the feed of your collection.": 10,
|
||||
"💡 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.": 8,
|
||||
"💡 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.": 9,
|
||||
"💸 Make a donation": 2,
|
||||
"📥 Import %s set": 20,
|
||||
"📥 Import this photo": 16,
|
||||
"📥 Import this sticker": 19,
|
||||
"🔥 Remove %s set": 21,
|
||||
"🔥 Remove this photo": 17,
|
||||
"🔧 Let's hack!": 5,
|
||||
"🕵 Found %d result(s)": 14,
|
||||
"🕺 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!": 4,
|
||||
"🤔 What to do with this?": 15,
|
||||
"🤖 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": 7,
|
||||
"🤝 Use referral links": 3,
|
||||
"🥳 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!": 0,
|
||||
"☺️ 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, 0x000000a5, 0x00000162, 0x00000177,
|
||||
0x0000018f, 0x00000265, 0x00000276, 0x00000304,
|
||||
0x000004a1, 0x0000055d, 0x00000688, 0x000006ee,
|
||||
0x000006fd, 0x0000070b, 0x00000719, 0x00000734,
|
||||
0x0000074f, 0x00000766, 0x0000077d, 0x000007df,
|
||||
0x000007f8, 0x0000080e, 0x00000824,
|
||||
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🥳 4 November? It's a @toby3d birthday!\x0aIf you like this bot, then" +
|
||||
" why not send him a congratulation along with a small gift? This will ma" +
|
||||
"ke him incredibly happy!\x02☺️ Oh, you missed @toby3d birthday on Novemb" +
|
||||
"er 4th!\x0aIf you like this bot, why not send him some birthday greeting" +
|
||||
"s and a little birthday gift? It is not yet too late to make him happy!" +
|
||||
"\x02💸 Make a donation\x02🤝 Use referral links\x02🕺 HacktoberFest is here" +
|
||||
"!\x0a\x0aIf you are a beginner or already an experienced golang-develope" +
|
||||
"r, now is a great time to help improve the quality of the code of this b" +
|
||||
"ot. Choose issue to your taste and offer your PR!\x02🔧 Let's hack!\x02👋 " +
|
||||
"Hi %[1]s, I'm %[2]s!\x0aThanks to me, you can collect almost any media c" +
|
||||
"ontent 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 (sh" +
|
||||
"ould) contain an argument:\x0a/start - start all over again\x0a/help [ot" +
|
||||
"her command] - get a list of available commands or help and a demonstrat" +
|
||||
"ion of a specific command\x0a/add [query] - add media from reply to your" +
|
||||
" collection [with custom search query]\x0a/edit (query) - change query t" +
|
||||
"o 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 media to y" +
|
||||
"our collection feed. Given an argument, the result of this command will " +
|
||||
"be equivalent to the /edit command.\x02💡 Use the /edit command with an a" +
|
||||
"rgument from any character set as a reply to a sticker/photo to change t" +
|
||||
"he search query of this media in the feed of your collection. If this me" +
|
||||
"dia is not in the feed, then the result of this command will be equivale" +
|
||||
"nt to the /add command with the same argument.\x02💡 Use /del command as " +
|
||||
"an reply to the sticker/photo to remove it from the feed of your collect" +
|
||||
"ion.\x02👍 Imported!\x02👍 Updated!\x02👍 Removed!\x02🕵 Found %[1]d result(" +
|
||||
"s)\x02🤔 What to do with this?\x02📥 Import this photo\x02🔥 Remove this ph" +
|
||||
"oto\x02💡 Add any text and/or emoji(s) as an argument of this command to " +
|
||||
"change its search properties.\x02📥 Import this sticker\x02📥 Import %[1]s" +
|
||||
" set\x02🔥 Remove %[1]s set"
|
||||
"\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, 0x00000135, 0x0000028e, 0x000002ae,
|
||||
0x000002d7, 0x00000423, 0x00000434, 0x0000053a,
|
||||
0x000007f5, 0x00000948, 0x00000b48, 0x00000bfa,
|
||||
0x00000c1b, 0x00000c34, 0x00000c49, 0x00000c7a,
|
||||
0x00000ca0, 0x00000cc9, 0x00000ce4, 0x00000da3,
|
||||
0x00000dd0, 0x00000df6, 0x00000e0e,
|
||||
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🥳 4-е Ноября? Это день рождения @toby3d!\x0aЕсли тебе нравится этот " +
|
||||
"бот, то почему бы не отправить ему поздравления вместе с небольшим пода" +
|
||||
"рком? Это несказанно его осчастливит!\x02☺️ Ой, ты пропустил день рожде" +
|
||||
"ния @toby3d 4-го Ноября!\x0aЕсли тебе нравится этот бот, то почему бы н" +
|
||||
"е отправить ему поздравления вместе с небольшим подарком? Ещё не слишко" +
|
||||
"м поздно его порадовать!\x02💸 Пожертвование\x02🤝 Реферальные ссылки\x02" +
|
||||
"🕺 Хактоберфест уже здесь!\x0a\x0aЕсли ты начинающий или уже опытный g" +
|
||||
"olang-разработчик, то сейчас хорошее время помочь улучшить качество кода" +
|
||||
" этого бота. Выбери issue на свой вкус и предложи PR!\x02🔧 Let's hack!" +
|
||||
"\x02👋 Привет %[1]s, я %[2]s!\x0aБлагодаря мне, ты можешь коллекционирова" +
|
||||
"ть в inline-режиме практически любой медиа-контент Telegram без огранич" +
|
||||
"ений, в любом чате.\x02🤖 Вот список команд которые я понимаю, некоторые" +
|
||||
|
@ -140,6 +131,15 @@ const ruData string = "" + // Size: 3598 bytes
|
|||
"!\x02🕵 Найдено %[1]d результатов\x02🤔 Что с этим делать?\x02📥 Импортиров" +
|
||||
"ать фото\x02🔥 Убрать фото\x02💡 Добавь любой текст и/или эмодзи в качест" +
|
||||
"ве аргумента этой команды чтобы изменить поисковые свойства.\x02📥 Импор" +
|
||||
"тировать стикер\x02📥 Импортировать %[1]s\x02🔥 Убрать %[1]s"
|
||||
"тировать стикер\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: 7091C75C
|
||||
// Total table size 5914 bytes (5KiB); checksum: 490F86A
|
||||
|
|
7
go.mod
7
go.mod
|
@ -4,7 +4,6 @@ go 1.13
|
|||
|
||||
require (
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/etcd-io/bbolt v1.3.3
|
||||
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
|
||||
|
@ -18,12 +17,16 @@ require (
|
|||
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-20200218162808-858deeef8eeb
|
||||
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
|
||||
|
|
6
go.sum
6
go.sum
|
@ -156,6 +156,8 @@ github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e h1:+NL1GDIUOKxVfbp2K
|
|||
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=
|
||||
|
@ -171,8 +173,12 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
|
|||
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=
|
||||
|
|
|
@ -3,31 +3,28 @@ package db
|
|||
import (
|
||||
"os"
|
||||
|
||||
bolt "github.com/etcd-io/bbolt"
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
func Open(path string) (*bolt.DB, error) {
|
||||
db, err := bolt.Open(path, os.ModePerm, nil)
|
||||
func Open(path string) (*bolthold.Store, error) {
|
||||
db, err := bolthold.Open(path, os.ModePerm, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = AutoMigrate(db); err != nil {
|
||||
_ = db.Close()
|
||||
}
|
||||
|
||||
return db, err
|
||||
}
|
||||
|
||||
func AutoMigrate(db *bolt.DB) error {
|
||||
return db.Update(func(tx *bolt.Tx) (err error) {
|
||||
for _, bkt := range common.Buckets {
|
||||
if _, err = tx.CreateBucketIfNotExists(bkt); err != nil {
|
||||
return 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
|
||||
}
|
||||
|
|
|
@ -12,10 +12,7 @@ func TestOpen(t *testing.T) {
|
|||
t.Run("invalid", func(t *testing.T) {
|
||||
db, err := Open(filepath.Join("/", "invalid", "path"))
|
||||
assert.Error(t, err)
|
||||
|
||||
t.Run("automigrate", func(t *testing.T) {
|
||||
assert.Panics(t, func() { _ = AutoMigrate(db) })
|
||||
})
|
||||
assert.Nil(t, db)
|
||||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
rootPath, err := os.Getwd()
|
||||
|
@ -29,9 +26,5 @@ func TestOpen(t *testing.T) {
|
|||
assert.NoError(t, db.Close())
|
||||
assert.NoError(t, os.Remove(testPath))
|
||||
}()
|
||||
|
||||
t.Run("automigrate", func(t *testing.T) {
|
||||
assert.NoError(t, AutoMigrate(db))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ func (h *Handler) CallbackAdd(ctx *model.Context) (err error) {
|
|||
case ctx.Sticker != nil:
|
||||
if ctx.Request.CallbackQuery.Data == common.DataAddSet {
|
||||
err = h.CommandAddSet(ctx)
|
||||
ctx.HasSet = true
|
||||
} else {
|
||||
err = h.CommandAddSticker(ctx)
|
||||
}
|
||||
|
@ -56,6 +57,7 @@ func (h *Handler) CallbackAdd(ctx *model.Context) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
ctx.HasSticker = true
|
||||
editMessage.ReplyMarkup = h.GetStickerKeyboard(ctx)
|
||||
default:
|
||||
return err
|
||||
|
@ -87,6 +89,7 @@ func (h *Handler) CallbackDel(ctx *model.Context) (err error) {
|
|||
case ctx.Sticker != nil:
|
||||
if ctx.Request.CallbackQuery.Data == common.DataDelSet {
|
||||
err = h.CommandDelSet(ctx)
|
||||
ctx.HasSet = false
|
||||
} else {
|
||||
err = h.CommandDelSticker(ctx)
|
||||
}
|
||||
|
@ -95,6 +98,7 @@ func (h *Handler) CallbackDel(ctx *model.Context) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
ctx.HasSticker = false
|
||||
editMessage.ReplyMarkup = h.GetStickerKeyboard(ctx)
|
||||
default:
|
||||
return err
|
||||
|
|
|
@ -41,7 +41,7 @@ func (h *Handler) IsCommand(ctx *model.Context) (err error) {
|
|||
|
||||
// CommandPing send common ping message.
|
||||
func (h *Handler) CommandPing(ctx *model.Context) (err error) {
|
||||
reply := tg.NewMessage(ctx.User.UserID, "🏓")
|
||||
reply := tg.NewMessage(int64(ctx.User.ID), "🏓")
|
||||
reply.ReplyMarkup = tg.NewReplyKeyboardRemove(false)
|
||||
reply.ReplyToMessageID = ctx.Request.Message.ID
|
||||
_, err = ctx.SendMessage(reply)
|
||||
|
@ -53,7 +53,7 @@ func (h *Handler) CommandPing(ctx *model.Context) (err error) {
|
|||
// 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(ctx.User.UserID, p.Sprintf("👋 Hi %s, I'm %s!\nThanks to me, you can collect almost any"+
|
||||
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)
|
||||
|
@ -67,7 +67,7 @@ func (h *Handler) CommandStart(ctx *model.Context) (err error) {
|
|||
// 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(ctx.User.UserID, p.Sprintf("🤖 Here is a list of commands that I understand, some of"+
|
||||
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 "+
|
||||
|
@ -137,7 +137,7 @@ func (h *Handler) CommandAdd(ctx *model.Context) (err error) {
|
|||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(ctx.User.UserID, p.Sprintf("👍 Imported!"))
|
||||
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)
|
||||
|
@ -164,7 +164,7 @@ func (h *Handler) CommandEdit(ctx *model.Context) (err error) {
|
|||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(ctx.User.UserID, p.Sprintf("👍 Updated!"))
|
||||
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)
|
||||
|
@ -191,7 +191,7 @@ func (h *Handler) CommandDel(ctx *model.Context) (err error) {
|
|||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(ctx.User.UserID, p.Sprintf("👍 Removed!"))
|
||||
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)
|
||||
|
|
|
@ -11,15 +11,16 @@ import (
|
|||
)
|
||||
|
||||
type Handler struct {
|
||||
users users.Manager
|
||||
stickers stickers.Manager
|
||||
photos photos.Manager
|
||||
usersStickers us.Manager
|
||||
usersPhotos up.Manager
|
||||
users users.ReadWriter
|
||||
stickers stickers.ReadWriter
|
||||
photos photos.ReadWriter
|
||||
usersStickers us.ReadWriter
|
||||
usersPhotos up.ReadWriter
|
||||
store *store.Store
|
||||
}
|
||||
|
||||
func NewHandler(us users.Manager, ss stickers.Manager, ps photos.Manager, uss us.Manager, ups up.Manager) *Handler {
|
||||
func NewHandler(us users.ReadWriter, ss stickers.ReadWriter, ps photos.ReadWriter, uss us.ReadWriter,
|
||||
ups up.ReadWriter) *Handler {
|
||||
return &Handler{
|
||||
photos: ps,
|
||||
stickers: ss,
|
||||
|
|
|
@ -12,26 +12,32 @@ import (
|
|||
"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
|
||||
answer.IsPersonal = !strings.Contains(ctx.Request.InlineQuery.Query, "personal:false")
|
||||
filter := getFilter(ctx.Request.InlineQuery)
|
||||
items, count := h.store.GetList(ctx.User.ID, filter)
|
||||
|
||||
if filter.Offset+50 < count {
|
||||
answer.NextOffset = strconv.Itoa(filter.Offset + 50)
|
||||
if answer.IsPersonal = !strings.Contains(ctx.Request.InlineQuery.Query, "personal:false"); answer.IsPersonal {
|
||||
filter.UserID = ctx.User.ID
|
||||
}
|
||||
|
||||
for i := range items {
|
||||
switch item := items[i].(type) {
|
||||
case *model.Sticker:
|
||||
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+strconv.FormatUint(item.ID, 10), item.FileID,
|
||||
tg.TypeSticker+common.DataSeparator+results[i].GetID(), results[i].GetFileID(),
|
||||
))
|
||||
case *model.Photo:
|
||||
case tg.TypePhoto:
|
||||
answer.Results = append(answer.Results, tg.NewInlineQueryResultCachedPhoto(
|
||||
tg.TypePhoto+common.DataSeparator+strconv.FormatUint(item.ID, 10), item.FileID,
|
||||
tg.TypePhoto+common.DataSeparator+results[i].GetID(), results[i].GetFileID(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +52,7 @@ func (h *Handler) IsInlineQuery(ctx *model.Context) (err error) {
|
|||
|
||||
func getFilter(iq *tg.InlineQuery) *store.Filter {
|
||||
f := new(store.Filter)
|
||||
f.Limit = 50
|
||||
f.Limit = defaultLimit
|
||||
f.Offset, _ = strconv.Atoi(iq.Offset)
|
||||
|
||||
if !strings.Contains(iq.Query, "photos:false") {
|
||||
|
@ -62,17 +68,20 @@ func getFilter(iq *tg.InlineQuery) *store.Filter {
|
|||
}
|
||||
|
||||
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, "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
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
|
@ -13,16 +15,15 @@ func (h *Handler) GetPhotoKeyboard(ctx *model.Context) *tg.InlineKeyboardMarkup
|
|||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
ctx.UserPhoto = h.usersPhotos.Get(&model.UserPhoto{
|
||||
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 ctx.UserPhoto != nil {
|
||||
if userPhoto != nil {
|
||||
markup = tg.NewInlineKeyboardMarkup(tg.NewInlineKeyboardRow(
|
||||
tg.NewInlineKeyboardButton(p.Sprintf("🔥 Remove this photo"), common.DataDel),
|
||||
))
|
||||
|
@ -32,11 +33,13 @@ func (h *Handler) GetPhotoKeyboard(ctx *model.Context) *tg.InlineKeyboardMarkup
|
|||
}
|
||||
|
||||
func (h *Handler) CommandAddPhoto(ctx *model.Context) (err error) {
|
||||
if ctx.Photo == nil || ctx.UserPhoto != nil {
|
||||
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
|
||||
|
||||
|
@ -54,7 +57,7 @@ func (h *Handler) CommandEditPhoto(ctx *model.Context) (err error) {
|
|||
|
||||
if !ctx.Request.Message.HasCommandArgument() {
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(ctx.User.UserID, p.Sprintf("💡 Add any text and/or emoji(s) as an argument "+
|
||||
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
|
||||
|
@ -65,22 +68,27 @@ func (h *Handler) CommandEditPhoto(ctx *model.Context) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
if ctx.UserPhoto == nil {
|
||||
if !ctx.HasPhoto {
|
||||
if err = h.CommandAddPhoto(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ctx.UserPhoto.UpdatedAt = ctx.Request.Message.Date
|
||||
ctx.UserPhoto.Query = ctx.Request.Message.CommandArgument()
|
||||
|
||||
return h.usersPhotos.Update(ctx.UserPhoto)
|
||||
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.UserPhoto == nil {
|
||||
if ctx.Photo == nil || !ctx.HasPhoto {
|
||||
return nil
|
||||
}
|
||||
|
||||
return h.usersPhotos.Remove(ctx.UserPhoto)
|
||||
return h.usersPhotos.Remove(&model.UserPhoto{
|
||||
UserID: ctx.User.ID,
|
||||
PhotoID: ctx.Photo.ID,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package handler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
|
@ -13,17 +15,11 @@ func (h *Handler) GetStickerKeyboard(ctx *model.Context) *tg.InlineKeyboardMarku
|
|||
}
|
||||
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
ctx.UserSticker = h.usersStickers.Get(&model.UserSticker{
|
||||
UserID: ctx.User.ID,
|
||||
StickerID: ctx.Sticker.ID,
|
||||
})
|
||||
|
||||
markup := tg.NewInlineKeyboardMarkup(tg.NewInlineKeyboardRow(
|
||||
tg.NewInlineKeyboardButton("🔥 Remove this sticker", common.DataDel),
|
||||
))
|
||||
|
||||
if (ctx.Request.IsCallbackQuery() && ctx.Request.CallbackQuery.Data == common.DataDelSet) ||
|
||||
ctx.UserSticker == nil {
|
||||
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),
|
||||
))
|
||||
|
@ -35,8 +31,7 @@ func (h *Handler) GetStickerKeyboard(ctx *model.Context) *tg.InlineKeyboardMarku
|
|||
tg.NewInlineKeyboardButton(p.Sprintf("📥 Import %s set", setName), common.DataAddSet),
|
||||
))
|
||||
|
||||
if (ctx.Request.IsCallbackQuery() && ctx.Request.CallbackQuery.Data == common.DataAddSet) ||
|
||||
ctx.UserSticker != nil {
|
||||
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,
|
||||
)
|
||||
|
@ -49,13 +44,16 @@ func (h *Handler) GetStickerKeyboard(ctx *model.Context) *tg.InlineKeyboardMarku
|
|||
// CommandAddSticker import single Sticker by ReplyMessage.
|
||||
// NOTE(toby3d): DEPRECATED, used for backward compatibility
|
||||
func (h *Handler) CommandAddSticker(ctx *model.Context) (err error) {
|
||||
if ctx.Sticker == nil || ctx.UserSticker != nil {
|
||||
if ctx.HasSticker || ctx.Sticker == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
userSticker := new(model.UserSticker)
|
||||
userSticker.UserID = ctx.User.ID
|
||||
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()
|
||||
|
@ -81,8 +79,8 @@ func (h *Handler) CommandEditSticker(ctx *model.Context) (err error) {
|
|||
|
||||
if !ctx.Request.Message.HasCommandArgument() {
|
||||
p := ctx.Get("printer").(*message.Printer)
|
||||
reply := tg.NewMessage(ctx.User.UserID, p.Sprintf("💡 Add any text and/or emoji(s) as an argument "+
|
||||
"of this command to change its search properties."))
|
||||
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
|
||||
|
@ -92,26 +90,31 @@ func (h *Handler) CommandEditSticker(ctx *model.Context) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
if ctx.UserSticker == nil {
|
||||
if !ctx.HasSticker {
|
||||
if err = h.CommandAddSticker(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
ctx.UserSticker.UpdatedAt = ctx.Request.Message.Date
|
||||
ctx.UserSticker.Query = ctx.Request.Message.CommandArgument()
|
||||
|
||||
return h.usersStickers.Update(ctx.UserSticker)
|
||||
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.UserSticker == nil {
|
||||
if ctx.Sticker == nil || !ctx.HasSticker {
|
||||
return nil
|
||||
}
|
||||
|
||||
return h.usersStickers.Remove(ctx.UserSticker)
|
||||
return h.usersStickers.Remove(&model.UserSticker{
|
||||
UserID: ctx.User.ID,
|
||||
StickerID: ctx.Sticker.ID,
|
||||
})
|
||||
}
|
||||
|
||||
// CommandDelPack remove whole Sticker pack by ReplyMessage.
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
func AcquirePhoto(store photos.Manager) Interceptor {
|
||||
func AcquirePhoto(store photos.ReadWriter) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) (err error) {
|
||||
switch {
|
||||
case ctx.Request.IsMessage():
|
||||
|
@ -46,9 +46,10 @@ func AcquirePhoto(store photos.Manager) Interceptor {
|
|||
func photoToModel(photoSize tg.Photo) *model.Photo {
|
||||
p := photoSize[len(photoSize)-1]
|
||||
photo := new(model.Photo)
|
||||
photo.FileID = p.FileID
|
||||
photo.ID = p.FileUniqueID
|
||||
photo.Width = p.Width
|
||||
photo.Height = p.Height
|
||||
photo.FileID = p.FileID
|
||||
|
||||
return photo
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model/stickers"
|
||||
|
@ -50,12 +52,13 @@ func AcquireSticker(store stickers.Manager) Interceptor {
|
|||
|
||||
func stickerToModel(s *tg.Sticker) *model.Sticker {
|
||||
sticker := new(model.Sticker)
|
||||
sticker.FileID = s.FileID
|
||||
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
|
||||
|
@ -67,7 +70,7 @@ func stickerToModel(s *tg.Sticker) *model.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.GetSet(ctx.Sticker.SetName)
|
||||
stickers, _, _ := store.GetList(0, 0, &model.Sticker{SetName: ctx.Sticker.SetName})
|
||||
ctx.Sticker.SetName = common.SetNameUploaded
|
||||
|
||||
go func() {
|
||||
|
@ -80,14 +83,17 @@ func migrateSet(ctx *model.Context, store stickers.Manager) {
|
|||
} else {
|
||||
ctx.Set("set_name", tgSet.Title)
|
||||
|
||||
dbSet, _ := store.GetSet(tgSet.Name)
|
||||
for i := range tgSet.Stickers {
|
||||
for j := range dbSet {
|
||||
if tgSet.Stickers[i].FileID == dbSet[j].FileID {
|
||||
for _, sticker := range store.GetSet(tgSet.Name) {
|
||||
if sticker.ID == tgSet.Stickers[i].FileUniqueID {
|
||||
continue
|
||||
}
|
||||
|
||||
_ = store.Create(stickerToModel(tgSet.Stickers[i]))
|
||||
now := time.Now().UTC().Unix()
|
||||
s := stickerToModel(tgSet.Stickers[i])
|
||||
s.CreatedAt, s.UpdatedAt = now, now
|
||||
|
||||
_, _ = store.GetOrCreate(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,26 +7,26 @@ import (
|
|||
"gitlab.com/toby3d/mypackbot/internal/model/users"
|
||||
)
|
||||
|
||||
func AcquireUser(us users.Manager) Interceptor {
|
||||
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.UserID = int64(ctx.Request.Message.From.ID)
|
||||
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.UserID = int64(ctx.Request.InlineQuery.From.ID)
|
||||
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.UserID = int64(ctx.Request.CallbackQuery.From.ID)
|
||||
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
|
||||
|
|
|
@ -5,16 +5,16 @@ import (
|
|||
"gitlab.com/toby3d/mypackbot/internal/model/users/photos"
|
||||
)
|
||||
|
||||
func AcquireUserPhoto(store photos.Manager) Interceptor {
|
||||
func AcquireUserPhoto(store photos.Reader) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) error {
|
||||
if ctx.Photo == nil {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
ctx.UserPhoto = store.Get(&model.UserPhoto{
|
||||
ctx.HasPhoto = store.Get(&model.UserPhoto{
|
||||
UserID: ctx.User.ID,
|
||||
PhotoID: ctx.Photo.ID,
|
||||
})
|
||||
}) != nil
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
|
|
|
@ -5,16 +5,16 @@ import (
|
|||
"gitlab.com/toby3d/mypackbot/internal/model/users/stickers"
|
||||
)
|
||||
|
||||
func AcquireUserSticker(store stickers.Manager) Interceptor {
|
||||
func AcquireUserSticker(store stickers.Reader) Interceptor {
|
||||
return func(ctx *model.Context, next model.UpdateFunc) error {
|
||||
if ctx.Sticker == nil {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
ctx.UserSticker = store.Get(&model.UserSticker{
|
||||
ctx.HasSticker = store.Get(&model.UserSticker{
|
||||
UserID: ctx.User.ID,
|
||||
StickerID: ctx.Sticker.ID,
|
||||
})
|
||||
}) != nil
|
||||
|
||||
return next(ctx)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
json "github.com/json-iterator/go"
|
||||
bunt "github.com/tidwall/buntdb"
|
||||
|
@ -25,7 +26,7 @@ type (
|
|||
}
|
||||
|
||||
Record struct {
|
||||
UserID int64
|
||||
UserID int
|
||||
SetName string
|
||||
FileID string
|
||||
Emoji string
|
||||
|
@ -34,7 +35,7 @@ type (
|
|||
Records []*Record
|
||||
|
||||
Backup struct {
|
||||
Users []int64 `json:"users"`
|
||||
Users []int `json:"users"`
|
||||
Stickers []string `json:"stickers"`
|
||||
ImportedSets []string `json:"imported_sets"`
|
||||
BlockedSets []string `json:"blocked_sets"`
|
||||
|
@ -43,7 +44,7 @@ type (
|
|||
AutoMigrateConfig struct {
|
||||
OldDB *bunt.DB
|
||||
Stickers stickers.Manager
|
||||
UsersStickers usersstickers.Manager
|
||||
UsersStickers usersstickers.ReadWriter
|
||||
Users users.Manager
|
||||
Bot *tg.Bot
|
||||
GroupID int64
|
||||
|
@ -95,7 +96,7 @@ func (cfg *AutoMigrateConfig) importOldData() (data *Data, err error) {
|
|||
|
||||
// NOTE(toby3d): this part always contains user/chat id
|
||||
var err error
|
||||
r.UserID, err = strconv.ParseInt(parts[1], 10, 64)
|
||||
r.UserID, err = strconv.Atoi(parts[1])
|
||||
if err != nil || r.UserID == 0 || !strings.EqualFold(parts[2], partSet) {
|
||||
return true
|
||||
}
|
||||
|
@ -151,9 +152,13 @@ func (cfg *AutoMigrateConfig) migrateUsers(data *Data) (err error) {
|
|||
continue
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
|
||||
_ = cfg.Users.Create(&model.User{
|
||||
UserID: data.Records[i].UserID,
|
||||
ID: data.Records[i].UserID,
|
||||
LanguageCode: "en",
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
})
|
||||
|
||||
data.Users = append(data.Users, data.Records[i].UserID)
|
||||
|
@ -197,7 +202,7 @@ func (cfg *AutoMigrateConfig) migrateSets(data *Data) (err error) {
|
|||
_ = cfg.Stickers.Create(stickerToModel(setSticker))
|
||||
}
|
||||
|
||||
u := cfg.Users.GetByUserID(data.Records[i].UserID)
|
||||
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)
|
||||
|
@ -243,8 +248,8 @@ func (cfg *AutoMigrateConfig) migrateStickers(data *Data) (err error) {
|
|||
|
||||
// NOTE(toby3d): store old-new stickers
|
||||
_ = cfg.Stickers.Create(s)
|
||||
u := cfg.Users.GetByUserID(data.Records[i].UserID)
|
||||
s = cfg.Stickers.GetByFileID(s.FileID)
|
||||
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,
|
||||
|
@ -258,7 +263,7 @@ func (cfg *AutoMigrateConfig) migrateStickers(data *Data) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func containsInt(src []int64, find int64) bool {
|
||||
func containsInt(src []int, find int) bool {
|
||||
for i := range src {
|
||||
if src[i] != find {
|
||||
continue
|
||||
|
|
|
@ -13,11 +13,12 @@ type (
|
|||
*tg.Bot
|
||||
Request *tg.Update
|
||||
|
||||
User *User
|
||||
Sticker *Sticker
|
||||
Photo *Photo
|
||||
UserPhoto *UserPhoto
|
||||
UserSticker *UserSticker
|
||||
User *User
|
||||
Sticker *Sticker
|
||||
HasSticker bool
|
||||
HasSet bool
|
||||
Photo *Photo
|
||||
HasPhoto bool
|
||||
|
||||
userValues context.Context
|
||||
}
|
||||
|
|
|
@ -4,64 +4,91 @@ import (
|
|||
"strings"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
tg "gitlab.com/toby3d/telegram"
|
||||
)
|
||||
|
||||
type (
|
||||
Model struct {
|
||||
ID uint64 `json:"id"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
User struct {
|
||||
Model
|
||||
UserID int64 `json:"user_id"`
|
||||
LanguageCode string `json:"language_code"`
|
||||
LastSeen int64 `json:"last_seen"`
|
||||
ID int `boltholdKey:"ID"`
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
LanguageCode string
|
||||
LastSeen int64
|
||||
}
|
||||
|
||||
Users []*User
|
||||
|
||||
Sticker struct {
|
||||
Model
|
||||
FileID string `json:"file_id"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
IsAnimated bool `json:"is_animated"`
|
||||
SetName string `json:"set_name"`
|
||||
Emoji string `json:"emoji"`
|
||||
ID string `boltholdKey:"ID"`
|
||||
CreatedAt int64
|
||||
UpdatedAt int64
|
||||
FileID string
|
||||
Width int
|
||||
Height int
|
||||
IsAnimated bool
|
||||
SetName string
|
||||
Emoji string
|
||||
}
|
||||
|
||||
Stickers []*Sticker
|
||||
|
||||
UserSticker struct {
|
||||
Model
|
||||
StickerID uint64 `json:"sticker_id"`
|
||||
UserID uint64 `json:"user_id"`
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
UserStickers []*UserSticker
|
||||
|
||||
Photo struct {
|
||||
Model
|
||||
FileID string `json:"file_id"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
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 {
|
||||
Model
|
||||
PhotoID uint64 `json:"photo_id"`
|
||||
UserID uint64 `json:"user_id"`
|
||||
Query string `json:"query"`
|
||||
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 }
|
||||
|
|
|
@ -2,12 +2,25 @@ package photos
|
|||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type Manager interface {
|
||||
Create(*model.Photo) error
|
||||
Get(uint64) *model.Photo
|
||||
GetByFileID(string) *model.Photo
|
||||
GetList(int, int) (model.Photos, int)
|
||||
GetOrCreate(*model.Photo) (*model.Photo, error)
|
||||
Remove(uint64) error
|
||||
Update(*model.Photo) error
|
||||
}
|
||||
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
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,13 +2,26 @@ package stickers
|
|||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type Manager interface {
|
||||
Create(*model.Sticker) error
|
||||
Get(uint64) *model.Sticker
|
||||
GetByFileID(string) *model.Sticker
|
||||
GetList(int, int) (model.Stickers, int)
|
||||
GetOrCreate(*model.Sticker) (*model.Sticker, error)
|
||||
GetSet(string) (model.Stickers, int)
|
||||
Remove(uint64) error
|
||||
Update(*model.Sticker) error
|
||||
}
|
||||
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
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,10 +2,20 @@ package photos
|
|||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type Manager interface {
|
||||
Add(*model.UserPhoto) error
|
||||
Get(*model.UserPhoto) *model.UserPhoto
|
||||
GetList(uint64, int, int, string) (model.Photos, int)
|
||||
Remove(*model.UserPhoto) error
|
||||
Update(*model.UserPhoto) error
|
||||
}
|
||||
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
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,13 +2,22 @@ package stickers
|
|||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type Manager interface {
|
||||
Add(*model.UserSticker) error
|
||||
AddSet(uint64, string) error
|
||||
Get(*model.UserSticker) *model.UserSticker
|
||||
GetList(uint64, int, int, string) (model.Stickers, int)
|
||||
GetSet(uint64, int, int, string) (model.Stickers, int)
|
||||
Remove(*model.UserSticker) error
|
||||
RemoveSet(uint64, string) error
|
||||
Update(*model.UserSticker) error
|
||||
}
|
||||
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
|
||||
}
|
||||
)
|
||||
|
|
|
@ -2,10 +2,24 @@ package users
|
|||
|
||||
import "gitlab.com/toby3d/mypackbot/internal/model"
|
||||
|
||||
type Manager interface {
|
||||
Create(*model.User) error
|
||||
Get(uint64) *model.User
|
||||
GetByUserID(int64) *model.User
|
||||
GetOrCreate(*model.User) (*model.User, error)
|
||||
Update(*model.User) error
|
||||
}
|
||||
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,7 +1,6 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
json "github.com/json-iterator/go"
|
||||
"github.com/spf13/viper"
|
||||
"gitlab.com/toby3d/mypackbot/internal/config"
|
||||
"gitlab.com/toby3d/mypackbot/internal/db"
|
||||
|
@ -20,8 +19,8 @@ type MyPackBot struct {
|
|||
photos photos.Manager
|
||||
stickers stickers.Manager
|
||||
users users.Manager
|
||||
usersPhotos usersphotos.Manager
|
||||
usersStickers usersstickers.Manager
|
||||
usersPhotos usersphotos.ReadWriter
|
||||
usersStickers usersstickers.ReadWriter
|
||||
}
|
||||
|
||||
func New(path string) (mpb *MyPackBot, err error) {
|
||||
|
@ -36,12 +35,11 @@ func New(path string) (mpb *MyPackBot, err error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
marshler := json.ConfigFastest
|
||||
mpb.photos = store.NewPhotosStore(conn, marshler)
|
||||
mpb.stickers = store.NewStickersStore(conn, marshler)
|
||||
mpb.users = store.NewUsersStore(conn, marshler)
|
||||
mpb.usersPhotos = store.NewUsersPhotosStore(conn, mpb.users, mpb.photos, marshler)
|
||||
mpb.usersStickers = store.NewUsersStickersStore(conn, mpb.users, mpb.stickers, marshler)
|
||||
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
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"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"
|
||||
|
@ -24,27 +25,41 @@ func (mpb *MyPackBot) Run() error {
|
|||
middleware.AcquireUser(mpb.users),
|
||||
middleware.AcquirePrinter(),
|
||||
middleware.ChatAction(),
|
||||
middleware.AcquirePhoto(mpb.photos),
|
||||
middleware.AcquireUserPhoto(mpb.usersPhotos),
|
||||
middleware.AcquireSticker(mpb.stickers),
|
||||
middleware.AcquireUserSticker(mpb.usersStickers),
|
||||
middleware.Birthday(time.Date(0, time.November, 4, 0, 0, 0, 0, time.UTC)),
|
||||
middleware.Hacktober(),
|
||||
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,
|
||||
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 {
|
||||
|
||||
if err = h(ctx); err != nil {
|
||||
dlog.D(err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,226 +1,97 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
bolt "github.com/etcd-io/bbolt"
|
||||
json "github.com/json-iterator/go"
|
||||
"github.com/valyala/fastjson"
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"golang.org/x/xerrors"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type PhotosStore struct {
|
||||
conn *bolt.DB
|
||||
marshler json.API
|
||||
parser fastjson.Parser
|
||||
conn *bolthold.Store
|
||||
}
|
||||
|
||||
var (
|
||||
ErrPhotoInvalid = model.Error{
|
||||
Message: "Invalid photo",
|
||||
}
|
||||
|
||||
ErrPhotoExist = model.Error{
|
||||
Message: "Photo already exist",
|
||||
}
|
||||
|
||||
ErrPhotoNotExist = model.Error{
|
||||
Message: "Photo not exist",
|
||||
}
|
||||
)
|
||||
|
||||
func NewPhotosStore(conn *bolt.DB, marshler json.API) *PhotosStore {
|
||||
return &PhotosStore{
|
||||
conn: conn,
|
||||
marshler: marshler,
|
||||
parser: fastjson.Parser{},
|
||||
}
|
||||
func NewPhotosStore(conn *bolthold.Store) *PhotosStore {
|
||||
return &PhotosStore{conn: conn}
|
||||
}
|
||||
|
||||
func (store *PhotosStore) Create(p *model.Photo) error {
|
||||
if p == nil || p.FileID == "" {
|
||||
return ErrPhotoInvalid
|
||||
if store.Get(p.ID) != nil {
|
||||
return bolthold.ErrKeyExists
|
||||
}
|
||||
|
||||
if store.Get(p.ID) != nil || store.GetByFileID(p.FileID) != nil {
|
||||
return ErrPhotoExist
|
||||
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
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
return result
|
||||
}
|
||||
|
||||
if p.CreatedAt <= 0 {
|
||||
p.CreatedAt = now
|
||||
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 p.UpdatedAt <= 0 {
|
||||
p.UpdatedAt = now
|
||||
if limit > 0 {
|
||||
q = q.Limit(limit)
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) (err error) {
|
||||
// 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 p.ID, err = bkt.NextSequence(); err != nil {
|
||||
if count, err = store.conn.CountInBucket(bkt, &model.Photo{}, q); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bkt.Put([]byte(strconv.FormatUint(p.ID, 10)), src)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *PhotosStore) Get(id uint64) *model.Photo {
|
||||
p := new(model.Photo)
|
||||
|
||||
if err := store.conn.View(func(tx *bolt.Tx) error {
|
||||
return store.marshler.Unmarshal(
|
||||
tx.Bucket(common.BucketPhotos).Get([]byte(strconv.FormatUint(id, 10))), p,
|
||||
)
|
||||
}); err != nil || p.ID == 0 {
|
||||
return nil
|
||||
return store.conn.FindInBucket(bkt, &list, q)
|
||||
}); err != nil {
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (store *PhotosStore) GetByFileID(id string) *model.Photo {
|
||||
p := new(model.Photo)
|
||||
|
||||
if err := store.conn.View(func(tx *bolt.Tx) error {
|
||||
if err := tx.Bucket(common.BucketPhotos).ForEach(func(key, val []byte) error {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if string(v.GetStringBytes("file_id")) != id {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = store.marshler.Unmarshal(val, p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrForEachStop
|
||||
}); err != nil && !xerrors.Is(err, ErrForEachStop) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil || p.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (store *PhotosStore) GetList(offset, limit int) (model.Photos, int) {
|
||||
if limit <= 0 {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
count := 0
|
||||
photos := make(model.Photos, 0, limit)
|
||||
_ = store.conn.View(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(common.BucketPhotos).ForEach(func(key, val []byte) (err error) {
|
||||
count++
|
||||
|
||||
if count <= offset || (limit > 0 && count > offset+limit) {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := new(model.Photo)
|
||||
if err = store.marshler.Unmarshal(val, p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
photos = append(photos, p)
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
sort.Slice(photos, func(i, j int) bool {
|
||||
return photos[i].UpdatedAt < photos[j].UpdatedAt
|
||||
})
|
||||
|
||||
return photos, count
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
func (store *PhotosStore) Update(p *model.Photo) error {
|
||||
if p == nil || p.FileID == "" {
|
||||
return ErrPhotoInvalid
|
||||
}
|
||||
|
||||
if store.Get(p.ID) == nil && store.GetByFileID(p.FileID) == nil {
|
||||
if store.Get(p.ID) == nil {
|
||||
return store.Create(p)
|
||||
}
|
||||
|
||||
if p.UpdatedAt <= 0 {
|
||||
p.UpdatedAt = time.Now().UTC().Unix()
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(common.BucketPhotos).Put([]byte(strconv.FormatUint(p.ID, 10)), src)
|
||||
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 uint64) error {
|
||||
func (store *PhotosStore) Remove(id string) error {
|
||||
if store.Get(id) == nil {
|
||||
return ErrPhotoNotExist
|
||||
return bolthold.ErrNotFound
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) (err error) {
|
||||
if err = tx.Bucket(common.BucketPhotos).Delete([]byte(strconv.FormatUint(id, 10))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bkt := tx.Bucket(common.BucketUsersPhotos)
|
||||
if err = bkt.ForEach(func(key, val []byte) (err error) {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.GetUint64("photo_id") != id {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = bkt.Delete(key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrForEachStop
|
||||
}); err != nil && !xerrors.Is(err, ErrForEachStop) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
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) (photo *model.Photo, err error) {
|
||||
if photo = store.GetByFileID(p.FileID); photo != nil {
|
||||
func (store *PhotosStore) GetOrCreate(p *model.Photo) (*model.Photo, error) {
|
||||
if photo := store.Get(p.ID); photo != nil {
|
||||
return photo, nil
|
||||
}
|
||||
|
||||
if photo = store.Get(p.ID); photo != nil {
|
||||
return photo, nil
|
||||
}
|
||||
|
||||
if err = store.Create(p); err != nil {
|
||||
if err := store.Create(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,271 +1,112 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
bolt "github.com/etcd-io/bbolt"
|
||||
json "github.com/json-iterator/go"
|
||||
"github.com/valyala/fastjson"
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"golang.org/x/xerrors"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type StickersStore struct {
|
||||
conn *bolt.DB
|
||||
marshler json.API
|
||||
parser fastjson.Parser
|
||||
conn *bolthold.Store
|
||||
}
|
||||
|
||||
var (
|
||||
ErrStickerInvalid = model.Error{
|
||||
Message: "Invalid sticker",
|
||||
}
|
||||
|
||||
ErrStickerExist = model.Error{
|
||||
Message: "Sticker already exist",
|
||||
}
|
||||
|
||||
ErrStickerNotExist = model.Error{
|
||||
Message: "Sticker not exist",
|
||||
}
|
||||
)
|
||||
|
||||
func NewStickersStore(conn *bolt.DB, marshler json.API) *StickersStore {
|
||||
return &StickersStore{
|
||||
conn: conn,
|
||||
marshler: marshler,
|
||||
parser: fastjson.Parser{},
|
||||
}
|
||||
func NewStickersStore(conn *bolthold.Store) *StickersStore {
|
||||
return &StickersStore{conn: conn}
|
||||
}
|
||||
|
||||
func (store *StickersStore) Create(s *model.Sticker) error {
|
||||
if s == nil || s.FileID == "" {
|
||||
return ErrStickerInvalid
|
||||
if store.Get(s.ID) != nil {
|
||||
return bolthold.ErrKeyExists
|
||||
}
|
||||
|
||||
if store.Get(s.ID) != nil || store.GetByFileID(s.FileID) != nil {
|
||||
return ErrStickerExist
|
||||
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
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
return result
|
||||
}
|
||||
|
||||
if s.CreatedAt <= 0 {
|
||||
s.CreatedAt = now
|
||||
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 s.UpdatedAt <= 0 {
|
||||
s.UpdatedAt = now
|
||||
if limit > 0 {
|
||||
q = q.Limit(limit)
|
||||
}
|
||||
|
||||
if s.SetName == "" {
|
||||
s.SetName = common.SetNameUploaded
|
||||
if filter != nil {
|
||||
if filter.Emoji != "" {
|
||||
q = q.And("Emoji").ContainsAny(filter.Emoji)
|
||||
}
|
||||
|
||||
if filter.SetName != "" {
|
||||
q = q.And("SetName").Eq(filter.SetName)
|
||||
}
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) (err error) {
|
||||
list = make(model.Stickers, limit)
|
||||
|
||||
if err := store.conn.Bolt().View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(common.BucketStickers)
|
||||
|
||||
if s.ID, err = bkt.NextSequence(); err != nil {
|
||||
if count, err = store.conn.CountInBucket(bkt, &model.Sticker{}, q); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bkt.Put([]byte(strconv.FormatUint(s.ID, 10)), src)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *StickersStore) Get(id uint64) *model.Sticker {
|
||||
s := new(model.Sticker)
|
||||
|
||||
if err := store.conn.View(func(tx *bolt.Tx) error {
|
||||
return store.marshler.Unmarshal(
|
||||
tx.Bucket(common.BucketStickers).Get([]byte(strconv.FormatUint(id, 10))), s,
|
||||
)
|
||||
}); err != nil || s.ID == 0 {
|
||||
return nil
|
||||
return store.conn.FindInBucket(bkt, &list, q)
|
||||
}); err != nil {
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (store *StickersStore) GetByFileID(id string) *model.Sticker {
|
||||
s := new(model.Sticker)
|
||||
|
||||
if err := store.conn.View(func(tx *bolt.Tx) error {
|
||||
if err := tx.Bucket(common.BucketStickers).ForEach(func(key, val []byte) error {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if string(v.GetStringBytes("file_id")) != id {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = store.marshler.Unmarshal(val, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrForEachStop
|
||||
}); err != nil && !xerrors.Is(err, ErrForEachStop) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil || s.ID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func (store *StickersStore) GetList(offset, limit int) (model.Stickers, int) {
|
||||
if limit <= 0 {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
count := 0
|
||||
stickers := make(model.Stickers, 0, limit)
|
||||
_ = store.conn.View(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(common.BucketStickers).ForEach(func(key, val []byte) (err error) {
|
||||
count++
|
||||
|
||||
if count <= offset || (limit > 0 && count > offset+limit) {
|
||||
return nil
|
||||
}
|
||||
|
||||
s := new(model.Sticker)
|
||||
if err = store.marshler.Unmarshal(val, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stickers = append(stickers, s)
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
sort.Slice(stickers, func(i, j int) bool {
|
||||
return stickers[i].SetName < stickers[j].SetName || stickers[i].UpdatedAt < stickers[j].UpdatedAt
|
||||
})
|
||||
|
||||
return stickers, count
|
||||
}
|
||||
|
||||
func (store *StickersStore) GetSet(name string) (model.Stickers, int) {
|
||||
count := 0
|
||||
stickers := make(model.Stickers, 0)
|
||||
|
||||
_ = store.conn.View(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(common.BucketStickers).ForEach(func(key, val []byte) (err error) {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.EqualFold(string(v.GetStringBytes("set_name")), name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
s := new(model.Sticker)
|
||||
if err = store.marshler.Unmarshal(val, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stickers = append(stickers, s)
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
sort.Slice(stickers, func(i, j int) bool {
|
||||
return stickers[i].UpdatedAt < stickers[j].UpdatedAt
|
||||
})
|
||||
|
||||
return stickers, count
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
func (store *StickersStore) Update(s *model.Sticker) error {
|
||||
if s == nil || s.FileID == "" {
|
||||
return ErrStickerInvalid
|
||||
}
|
||||
|
||||
if store.Get(s.ID) == nil && store.GetByFileID(s.FileID) == nil {
|
||||
if store.Get(s.ID) == nil {
|
||||
return store.Create(s)
|
||||
}
|
||||
|
||||
if s.UpdatedAt <= 0 {
|
||||
s.UpdatedAt = time.Now().UTC().Unix()
|
||||
}
|
||||
|
||||
if s.SetName == "" {
|
||||
s.SetName = common.SetNameUploaded
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(common.BucketStickers).Put([]byte(strconv.FormatUint(s.ID, 10)), src)
|
||||
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 uint64) error {
|
||||
func (store *StickersStore) Remove(id string) error {
|
||||
if store.Get(id) == nil {
|
||||
return ErrStickerNotExist
|
||||
return bolthold.ErrNotFound
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) (err error) {
|
||||
if err = tx.Bucket(common.BucketStickers).Delete([]byte(strconv.FormatUint(id, 10))); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bkt := tx.Bucket(common.BucketUsersStickers)
|
||||
|
||||
if err = bkt.ForEach(func(key, val []byte) (err error) {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.GetUint64("sticker_id") != id {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = bkt.Delete(key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrForEachStop
|
||||
}); err != nil && !xerrors.Is(err, ErrForEachStop) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
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) (sticker *model.Sticker, err error) {
|
||||
if sticker = store.GetByFileID(s.FileID); sticker != nil {
|
||||
func (store *StickersStore) GetOrCreate(s *model.Sticker) (*model.Sticker, error) {
|
||||
if sticker := store.Get(s.ID); sticker != nil {
|
||||
return sticker, nil
|
||||
}
|
||||
|
||||
if sticker = store.Get(s.ID); sticker != nil {
|
||||
return sticker, nil
|
||||
}
|
||||
|
||||
if err = store.Create(s); err != nil {
|
||||
if err := store.Create(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
usersphotos "gitlab.com/toby3d/mypackbot/internal/model/users/photos"
|
||||
|
@ -13,97 +11,90 @@ import (
|
|||
|
||||
type (
|
||||
Store struct {
|
||||
usersStickers usersstickers.Manager
|
||||
usersPhotos usersphotos.Manager
|
||||
usersStickers usersstickers.ReadWriter
|
||||
usersPhotos usersphotos.ReadWriter
|
||||
}
|
||||
|
||||
Filter struct {
|
||||
UserID int
|
||||
AllowedTypes []string
|
||||
Query string
|
||||
Offset int
|
||||
Limit int
|
||||
IsPersonal bool
|
||||
IsAnimated *bool
|
||||
Width string
|
||||
Height string
|
||||
SetName string
|
||||
}
|
||||
)
|
||||
|
||||
// ErrForEachStop used in ForEach loops in database for forse stop iterations
|
||||
var ErrForEachStop = errors.New("for each stop stop")
|
||||
func (f *Filter) offset() int {
|
||||
if len(f.AllowedTypes) == 0 || f.Offset <= len(f.AllowedTypes) {
|
||||
return f.Offset
|
||||
}
|
||||
|
||||
func NewStore(us usersstickers.Manager, up usersphotos.Manager) *Store {
|
||||
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(uid uint64, f *Filter) ([]interface{}, int) {
|
||||
results := make([]interface{}, 0)
|
||||
count := 0
|
||||
|
||||
if len(f.AllowedTypes) == 0 {
|
||||
return results, count
|
||||
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}
|
||||
}
|
||||
|
||||
for _, t := range f.AllowedTypes {
|
||||
list = make([]model.InlineResult, 0)
|
||||
|
||||
for _, t := range filter.AllowedTypes {
|
||||
switch t {
|
||||
case tg.TypePhoto:
|
||||
if f.IsAnimated != nil && *f.IsAnimated {
|
||||
continue
|
||||
}
|
||||
|
||||
photos, photosCount := store.usersPhotos.GetList(uid, 0, -1, f.Query)
|
||||
for i := range photos {
|
||||
results = append(results, photos[i])
|
||||
}
|
||||
|
||||
count += photosCount
|
||||
case tg.TypeSticker:
|
||||
stickers, stickersCount := store.usersStickers.GetList(uid, 0, -1, f.Query)
|
||||
for i := range stickers {
|
||||
if f.IsAnimated != nil && stickers[i].IsAnimated != *f.IsAnimated ||
|
||||
f.SetName != "" && stickers[i].SetName != f.SetName {
|
||||
stickersCount--
|
||||
continue
|
||||
}
|
||||
|
||||
results = append(results, stickers[i])
|
||||
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
|
||||
}
|
||||
|
||||
count += stickersCount
|
||||
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(results, func(i, j int) bool {
|
||||
var a, b time.Time
|
||||
|
||||
switch result := results[i].(type) {
|
||||
case *model.Sticker:
|
||||
a = time.Unix(result.CreatedAt, 0)
|
||||
case *model.Photo:
|
||||
a = time.Unix(result.CreatedAt, 0)
|
||||
}
|
||||
|
||||
switch result := results[j].(type) {
|
||||
case *model.Sticker:
|
||||
b = time.Unix(result.CreatedAt, 0)
|
||||
case *model.Photo:
|
||||
b = time.Unix(result.CreatedAt, 0)
|
||||
}
|
||||
|
||||
return a.Before(b)
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].GetUpdatedAt() < list[j].GetUpdatedAt()
|
||||
})
|
||||
|
||||
if len(results) <= f.Offset {
|
||||
return make([]interface{}, 0), count
|
||||
}
|
||||
|
||||
if tail := len(results[f.Offset:]); tail < f.Limit {
|
||||
return results[f.Offset : f.Offset+tail], count
|
||||
}
|
||||
|
||||
return results[f.Offset : f.Offset+f.Limit-1], count
|
||||
return list, count, err
|
||||
}
|
||||
|
|
|
@ -1,152 +1,88 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
bolt "github.com/etcd-io/bbolt"
|
||||
json "github.com/json-iterator/go"
|
||||
"github.com/valyala/fastjson"
|
||||
"github.com/timshannon/bolthold"
|
||||
"gitlab.com/toby3d/mypackbot/internal/common"
|
||||
"gitlab.com/toby3d/mypackbot/internal/model"
|
||||
"golang.org/x/xerrors"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
)
|
||||
|
||||
type UsersStore struct {
|
||||
conn *bolt.DB
|
||||
marshler json.API
|
||||
parser fastjson.Parser
|
||||
conn *bolthold.Store
|
||||
}
|
||||
|
||||
var (
|
||||
ErrUserExist = model.Error{
|
||||
Message: "User already exist",
|
||||
}
|
||||
|
||||
ErrUserNotExist = model.Error{
|
||||
Message: "User not exist",
|
||||
}
|
||||
)
|
||||
|
||||
func NewUsersStore(conn *bolt.DB, marshler json.API) *UsersStore {
|
||||
return &UsersStore{
|
||||
conn: conn,
|
||||
marshler: marshler,
|
||||
parser: fastjson.Parser{},
|
||||
}
|
||||
func NewUsersStore(conn *bolthold.Store) *UsersStore {
|
||||
return &UsersStore{conn: conn}
|
||||
}
|
||||
|
||||
func (store *UsersStore) Create(u *model.User) error {
|
||||
if store.Get(u.ID) != nil || store.GetByUserID(u.UserID) != nil {
|
||||
return ErrUserExist
|
||||
if store.Get(u.ID) != nil {
|
||||
return bolthold.ErrKeyExists
|
||||
}
|
||||
|
||||
now := time.Now().UTC().Unix()
|
||||
|
||||
if u.CreatedAt <= 0 {
|
||||
u.CreatedAt = now
|
||||
}
|
||||
|
||||
if u.UpdatedAt <= 0 {
|
||||
u.UpdatedAt = now
|
||||
}
|
||||
|
||||
if u.LastSeen <= 0 {
|
||||
u.LastSeen = now
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) (err error) {
|
||||
bkt := tx.Bucket(common.BucketUsers)
|
||||
|
||||
if u.ID, err = bkt.NextSequence(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bkt.Put([]byte(strconv.FormatUint(u.ID, 10)), src)
|
||||
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 uint64) *model.User {
|
||||
u := new(model.User)
|
||||
func (store *UsersStore) Get(id int) *model.User {
|
||||
result := new(model.User)
|
||||
|
||||
if err := store.conn.View(func(tx *bolt.Tx) error {
|
||||
return store.marshler.Unmarshal(
|
||||
tx.Bucket(common.BucketUsers).Get([]byte(strconv.FormatUint(id, 10))), u,
|
||||
)
|
||||
}); err != nil || u.ID == 0 {
|
||||
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 u
|
||||
return result
|
||||
}
|
||||
|
||||
func (store *UsersStore) GetByUserID(id int64) *model.User {
|
||||
u := new(model.User)
|
||||
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 err := store.conn.View(func(tx *bolt.Tx) error {
|
||||
if err := tx.Bucket(common.BucketUsers).ForEach(func(key, val []byte) error {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if offset > 0 {
|
||||
q = q.Skip(offset)
|
||||
}
|
||||
|
||||
if v.GetInt64("user_id") != id {
|
||||
return nil
|
||||
}
|
||||
if limit > 0 {
|
||||
q = q.Limit(limit)
|
||||
}
|
||||
|
||||
if err = store.marshler.Unmarshal(val, u); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO(toby3d): implement filter here
|
||||
|
||||
return ErrForEachStop
|
||||
}); err != nil && !xerrors.Is(err, ErrForEachStop) {
|
||||
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 nil
|
||||
}); err != nil || u.ID == 0 {
|
||||
return nil
|
||||
return store.conn.FindInBucket(bkt, &list, q)
|
||||
}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return u
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
func (store *UsersStore) Update(u *model.User) error {
|
||||
if store.Get(u.ID) == nil && store.GetByUserID(u.UserID) == nil {
|
||||
if store.Get(u.ID) == nil {
|
||||
return store.Create(u)
|
||||
}
|
||||
|
||||
if u.UpdatedAt <= 0 {
|
||||
u.UpdatedAt = time.Now().UTC().Unix()
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(u)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(common.BucketUsers).Put([]byte(strconv.FormatUint(u.ID, 10)), src)
|
||||
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) (user *model.User, err error) {
|
||||
if user = store.Get(u.ID); user != nil {
|
||||
func (store *UsersStore) GetOrCreate(u *model.User) (*model.User, error) {
|
||||
if user := store.Get(u.ID); user != nil {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
if user = store.GetByUserID(u.UserID); user != nil {
|
||||
return user, nil
|
||||
}
|
||||
|
||||
if err = store.Create(u); err != nil {
|
||||
if err := store.Create(u); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return store.GetOrCreate(u)
|
||||
return u, nil
|
||||
}
|
||||
|
|
|
@ -1,228 +1,133 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
bolt "github.com/etcd-io/bbolt"
|
||||
json "github.com/json-iterator/go"
|
||||
"github.com/valyala/fastjson"
|
||||
"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 *bolt.DB
|
||||
marshler json.API
|
||||
parser fastjson.Parser
|
||||
photos photos.Manager
|
||||
users users.Manager
|
||||
conn *bolthold.Store
|
||||
photos photos.Reader
|
||||
users users.Reader
|
||||
}
|
||||
|
||||
var (
|
||||
ErrUserPhotoExist = model.Error{
|
||||
Message: "Photo already imported",
|
||||
}
|
||||
|
||||
ErrUserPhotoNotExist = model.Error{
|
||||
Message: "Photo already removed",
|
||||
}
|
||||
)
|
||||
|
||||
func NewUsersPhotosStore(conn *bolt.DB, us users.Manager, ps photos.Manager, marshler json.API) *UsersPhotosStore {
|
||||
func NewUsersPhotosStore(conn *bolthold.Store, us users.Reader, ps photos.Reader) *UsersPhotosStore {
|
||||
return &UsersPhotosStore{
|
||||
conn: conn,
|
||||
marshler: marshler,
|
||||
parser: fastjson.Parser{},
|
||||
photos: ps,
|
||||
users: us,
|
||||
conn: conn,
|
||||
photos: ps,
|
||||
users: us,
|
||||
}
|
||||
}
|
||||
|
||||
func (store *UsersPhotosStore) Add(up *model.UserPhoto) (err error) {
|
||||
if up == nil || up.UserID == 0 || up.PhotoID == 0 {
|
||||
return nil
|
||||
if store.users.Get(up.UserID) == nil || store.photos.Get(up.PhotoID) == nil {
|
||||
return bolthold.ErrNotFound
|
||||
}
|
||||
|
||||
userPhoto := store.Get(up)
|
||||
if userPhoto != nil {
|
||||
return ErrUserPhotoExist
|
||||
}
|
||||
|
||||
timeStamp := time.Now().UTC().Unix()
|
||||
|
||||
if up.CreatedAt == 0 {
|
||||
up.CreatedAt = timeStamp
|
||||
}
|
||||
|
||||
if up.UpdatedAt == 0 {
|
||||
up.UpdatedAt = timeStamp
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) (err error) {
|
||||
bkt := tx.Bucket(common.BucketUsersPhotos)
|
||||
|
||||
up.ID, err = bkt.NextSequence()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(up)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bkt.Put([]byte(strconv.FormatUint(up.ID, 10)), src)
|
||||
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) {
|
||||
if up == nil || up.UserID == 0 || up.PhotoID == 0 {
|
||||
return nil
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
userPhoto := store.Get(up)
|
||||
if userPhoto == nil {
|
||||
return store.Add(up)
|
||||
}
|
||||
result.Query, result.UpdatedAt = up.Query, up.UpdatedAt
|
||||
|
||||
if up.ID == 0 {
|
||||
up.ID = userPhoto.ID
|
||||
}
|
||||
|
||||
if up.CreatedAt == 0 {
|
||||
up.CreatedAt = userPhoto.CreatedAt
|
||||
}
|
||||
|
||||
if up.UpdatedAt <= userPhoto.UpdatedAt {
|
||||
up.UpdatedAt = time.Now().UTC().Unix()
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(up)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(common.BucketUsersPhotos).Put([]byte(strconv.FormatUint(up.ID, 10)), src)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (store *UsersPhotosStore) Get(up *model.UserPhoto) *model.UserPhoto {
|
||||
if up == nil || up.UserID == 0 || up.PhotoID == 0 {
|
||||
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
|
||||
}
|
||||
|
||||
userPhoto := new(model.UserPhoto)
|
||||
if err := store.conn.View(func(tx *bolt.Tx) (err error) {
|
||||
if err = tx.Bucket(common.BucketUsersPhotos).ForEach(func(key, val []byte) (err error) {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return store.photos.Get(result.PhotoID)
|
||||
}
|
||||
|
||||
if v.GetUint64("user_id") != up.UserID || v.GetUint64("photo_id") != up.PhotoID {
|
||||
return nil
|
||||
}
|
||||
func (store *UsersPhotosStore) GetList(offset, limit int, filter *model.UserPhoto) (list model.Photos, count int,
|
||||
err error) {
|
||||
|
||||
if err = store.marshler.Unmarshal(val, userPhoto); err != nil {
|
||||
return err
|
||||
}
|
||||
q := bolthold.Where("UserID").Ne(0).And("PhotoID").Ne("")
|
||||
qCount := bolthold.Where("UserID").Ne(0).And("PhotoID").Ne("")
|
||||
|
||||
return ErrForEachStop
|
||||
}); err != nil && !xerrors.Is(err, ErrForEachStop) {
|
||||
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 nil
|
||||
}); err != nil || userPhoto.PhotoID == 0 || userPhoto.UserID == 0 {
|
||||
return nil
|
||||
return store.conn.FindInBucket(bkt, &results, q)
|
||||
}); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return userPhoto
|
||||
}
|
||||
list = make(model.Photos, 0)
|
||||
|
||||
func (store *UsersPhotosStore) GetList(uid uint64, offset, limit int, query string) (model.Photos, int) {
|
||||
if limit <= 0 {
|
||||
limit = 0
|
||||
for i := range results {
|
||||
list = append(list, store.photos.Get(results[i].PhotoID))
|
||||
}
|
||||
|
||||
count := 0
|
||||
photos := make(model.Photos, 0, limit)
|
||||
_ = store.conn.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(common.BucketPhotos)
|
||||
return tx.Bucket(common.BucketUsersPhotos).ForEach(func(key, val []byte) error {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.GetUint64("user_id") != uid {
|
||||
return nil
|
||||
}
|
||||
|
||||
if query != "" && !strings.ContainsAny(string(v.GetStringBytes("query")), query) {
|
||||
return nil
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
if (offset != 0 && count <= offset) || (limit > 0 && count > offset+limit) {
|
||||
return nil
|
||||
}
|
||||
|
||||
p := new(model.Photo)
|
||||
if err = store.marshler.Unmarshal(
|
||||
bkt.Get([]byte(strconv.FormatUint(v.GetUint64("photo_id"), 10))), p,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
photos = append(photos, p)
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
sort.Slice(photos, func(i, j int) bool {
|
||||
return photos[i].UpdatedAt < photos[j].UpdatedAt
|
||||
})
|
||||
|
||||
return photos, count
|
||||
return list, count, err
|
||||
}
|
||||
|
||||
func (store *UsersPhotosStore) Remove(up *model.UserPhoto) (err error) {
|
||||
userPhoto := store.Get(up)
|
||||
if userPhoto == nil {
|
||||
return ErrUserPhotoNotExist
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) (err error) {
|
||||
bkt := tx.Bucket(common.BucketUsersPhotos)
|
||||
if err = bkt.ForEach(func(key, val []byte) (err error) {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.GetUint64("user_id") != up.UserID || v.GetUint64("photo_id") != up.PhotoID {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = bkt.Delete(key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrForEachStop
|
||||
}); err != nil && !xerrors.Is(err, ErrForEachStop) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
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),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,306 +1,169 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
bolt "github.com/etcd-io/bbolt"
|
||||
json "github.com/json-iterator/go"
|
||||
"github.com/valyala/fastjson"
|
||||
"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 *bolt.DB
|
||||
marshler json.API
|
||||
parser fastjson.Parser
|
||||
stickers stickers.Manager
|
||||
users users.Manager
|
||||
conn *bolthold.Store
|
||||
stickers stickers.Reader
|
||||
users users.Reader
|
||||
}
|
||||
|
||||
var (
|
||||
ErrUserStickerExist = model.Error{
|
||||
Message: "Sticker already imported",
|
||||
}
|
||||
|
||||
ErrUserStickerNotExist = model.Error{
|
||||
Message: "Sticker already removed",
|
||||
}
|
||||
)
|
||||
|
||||
func NewUsersStickersStore(db *bolt.DB, us users.Manager, ss stickers.Manager, m json.API) *UsersStickersStore {
|
||||
func NewUsersStickersStore(conn *bolthold.Store, us users.Reader, ss stickers.Reader) *UsersStickersStore {
|
||||
return &UsersStickersStore{
|
||||
conn: db,
|
||||
marshler: m,
|
||||
parser: fastjson.Parser{},
|
||||
conn: conn,
|
||||
stickers: ss,
|
||||
users: us,
|
||||
}
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) Add(us *model.UserSticker) (err error) {
|
||||
if us == nil || us.UserID == 0 || us.StickerID == 0 {
|
||||
return nil
|
||||
func (store *UsersStickersStore) Add(us *model.UserSticker) error {
|
||||
if store.users.Get(us.UserID) == nil || store.stickers.Get(us.StickerID) == nil {
|
||||
return bolthold.ErrNotFound
|
||||
}
|
||||
|
||||
userSticker := store.Get(us)
|
||||
if userSticker != nil {
|
||||
return ErrUserStickerExist
|
||||
}
|
||||
|
||||
timeStamp := time.Now().UTC().Unix()
|
||||
|
||||
if us.CreatedAt == 0 {
|
||||
us.CreatedAt = timeStamp
|
||||
}
|
||||
|
||||
if us.UpdatedAt == 0 {
|
||||
us.UpdatedAt = timeStamp
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) (err error) {
|
||||
bkt := tx.Bucket(common.BucketUsersStickers)
|
||||
|
||||
us.ID, err = bkt.NextSequence()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(us)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bkt.Put([]byte(strconv.FormatUint(us.ID, 10)), src)
|
||||
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 uint64, setName string) (err error) {
|
||||
set, _ := store.stickers.GetSet(setName)
|
||||
for i := range set {
|
||||
_ = store.Add(&model.UserSticker{
|
||||
UserID: uid,
|
||||
StickerID: set[i].ID,
|
||||
})
|
||||
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 err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) Update(us *model.UserSticker) (err error) {
|
||||
if us == nil || us.UserID == 0 || us.StickerID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
userSticker := store.Get(us)
|
||||
if userSticker == nil {
|
||||
return store.Add(us)
|
||||
}
|
||||
|
||||
if us.ID == 0 {
|
||||
us.ID = userSticker.ID
|
||||
}
|
||||
|
||||
if us.CreatedAt == 0 {
|
||||
us.CreatedAt = userSticker.CreatedAt
|
||||
}
|
||||
|
||||
if us.UpdatedAt <= userSticker.UpdatedAt {
|
||||
us.UpdatedAt = time.Now().UTC().Unix()
|
||||
}
|
||||
|
||||
src, err := store.marshler.Marshal(us)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return store.conn.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(common.BucketUsersStickers).Put([]byte(strconv.FormatUint(us.ID, 10)), src)
|
||||
})
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) Get(us *model.UserSticker) *model.UserSticker {
|
||||
if us == nil || us.UserID == 0 || us.StickerID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
userSticker := new(model.UserSticker)
|
||||
if err := store.conn.View(func(tx *bolt.Tx) (err error) {
|
||||
if err = tx.Bucket(common.BucketUsersStickers).ForEach(func(key, val []byte) (err error) {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.GetUint64("user_id") != us.UserID || v.GetUint64("sticker_id") != us.StickerID {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = store.marshler.Unmarshal(val, userSticker); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrForEachStop
|
||||
}); err != nil && !xerrors.Is(err, ErrForEachStop) {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil || userSticker.UserID == 0 || userSticker.StickerID == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return userSticker
|
||||
}
|
||||
|
||||
//nolint: gocognit
|
||||
func (store *UsersStickersStore) GetList(uid uint64, offset, limit int, q string) (list model.Stickers, count int) {
|
||||
if limit <= 0 {
|
||||
limit = 0
|
||||
}
|
||||
|
||||
list = make(model.Stickers, 0, limit)
|
||||
_ = store.conn.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(common.BucketStickers)
|
||||
return tx.Bucket(common.BucketUsersStickers).ForEach(func(key, val []byte) error {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil || v.GetUint64("user_id") != uid {
|
||||
return err
|
||||
}
|
||||
|
||||
src := bkt.Get([]byte(strconv.FormatUint(v.GetUint64("sticker_id"), 10)))
|
||||
if q != "" {
|
||||
vQuery := string(v.GetStringBytes("query"))
|
||||
switch {
|
||||
case vQuery != "" && !strings.ContainsAny(vQuery, q):
|
||||
return nil
|
||||
case vQuery == "":
|
||||
s, err := store.parser.ParseBytes(src)
|
||||
if err != nil || !strings.ContainsAny(string(s.GetStringBytes("emoji")), q) {
|
||||
return err
|
||||
}
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
if (offset != 0 && count <= offset) || (limit > 0 && count > offset+limit) {
|
||||
result.Query, result.UpdatedAt = us.Query, us.UpdatedAt
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
s := new(model.Sticker)
|
||||
if err = store.marshler.Unmarshal(src, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
list = append(list, s)
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
sort.Slice(list, func(i, j int) bool {
|
||||
return list[i].SetName < list[j].SetName || list[i].UpdatedAt < list[j].UpdatedAt
|
||||
})
|
||||
|
||||
return list, count
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) GetSet(uid uint64, offset, limit int, setName string) (model.Stickers, int) {
|
||||
count := 0
|
||||
stickers := make(model.Stickers, 0, limit)
|
||||
func (store *UsersStickersStore) Get(us *model.UserSticker) *model.Sticker {
|
||||
result := new(model.UserSticker)
|
||||
|
||||
_ = store.conn.View(func(tx *bolt.Tx) error {
|
||||
bkt := tx.Bucket(common.BucketStickers)
|
||||
return tx.Bucket(common.BucketUsersStickers).ForEach(func(key, val []byte) (err error) {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if v.GetUint64("user_id") != uid {
|
||||
return nil
|
||||
}
|
||||
|
||||
src := bkt.Get([]byte(strconv.FormatUint(v.GetUint64("sticker_id"), 10)))
|
||||
if v, err = store.parser.ParseBytes(src); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.EqualFold(string(v.GetStringBytes("set_name")), setName) {
|
||||
return nil
|
||||
}
|
||||
|
||||
count++
|
||||
|
||||
if count <= offset || count > limit {
|
||||
return nil
|
||||
}
|
||||
|
||||
s := new(model.Sticker)
|
||||
if err = store.marshler.Unmarshal(src, s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stickers = append(stickers, s)
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
sort.Slice(stickers, func(i, j int) bool {
|
||||
return stickers[i].UpdatedAt < stickers[j].UpdatedAt
|
||||
})
|
||||
|
||||
return stickers, count
|
||||
}
|
||||
|
||||
func (store *UsersStickersStore) Remove(us *model.UserSticker) (err error) {
|
||||
userSticker := store.Get(us)
|
||||
if userSticker == nil {
|
||||
return ErrUserStickerNotExist
|
||||
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.conn.Update(func(tx *bolt.Tx) (err error) {
|
||||
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 err = bkt.ForEach(func(key, val []byte) (err error) {
|
||||
v, err := store.parser.ParseBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if userSticker.UserID != v.GetUint64("user_id") ||
|
||||
(userSticker.StickerID != 0 && userSticker.StickerID != v.GetUint64("sticker_id")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = bkt.Delete(key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ErrForEachStop
|
||||
}); err != nil && !xerrors.Is(err, ErrForEachStop) {
|
||||
if count, err = store.conn.CountInBucket(bkt, &model.UserSticker{}, qCount); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
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 uint64, setName string) (err error) {
|
||||
set, _ := store.stickers.GetSet(setName)
|
||||
for i := range set {
|
||||
_ = store.Remove(&model.UserSticker{
|
||||
UserID: uid,
|
||||
StickerID: set[i].ID,
|
||||
})
|
||||
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 err
|
||||
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,48 +1,6 @@
|
|||
{
|
||||
"language": "en",
|
||||
"messages": [
|
||||
{
|
||||
"id": "🥳 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!",
|
||||
"message": "🥳 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!",
|
||||
"translation": "🥳 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!",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "☺️ 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!",
|
||||
"message": "☺️ 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!",
|
||||
"translation": "☺️ 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!",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "💸 Make a donation",
|
||||
"message": "💸 Make a donation",
|
||||
"translation": "💸 Make a donation",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "🤝 Use referral links",
|
||||
"message": "🤝 Use referral links",
|
||||
"translation": "🤝 Use referral links",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "🕺 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!",
|
||||
"message": "🕺 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!",
|
||||
"translation": "🕺 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!",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "🔧 Let's hack!",
|
||||
"message": "🔧 Let's hack!",
|
||||
"translation": "🔧 Let's hack!",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "👋 Hi {FullName}, I'm {FullName_1}!\nThanks to me, you can collect almost any media content in Telegram without any limits, in any chat via inline mode.",
|
||||
"message": "👋 Hi {FullName}, I'm {FullName_1}!\nThanks to me, you can collect almost any media content in Telegram without any limits, in any chat via inline mode.",
|
||||
|
@ -202,6 +160,48 @@
|
|||
}
|
||||
],
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "🥳 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!",
|
||||
"message": "🥳 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!",
|
||||
"translation": "🥳 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!",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "☺️ 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!",
|
||||
"message": "☺️ 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!",
|
||||
"translation": "☺️ 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!",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "💸 Make a donation",
|
||||
"message": "💸 Make a donation",
|
||||
"translation": "💸 Make a donation",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "🤝 Use referral links",
|
||||
"message": "🤝 Use referral links",
|
||||
"translation": "🤝 Use referral links",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "🕺 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!",
|
||||
"message": "🕺 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!",
|
||||
"translation": "🕺 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!",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
},
|
||||
{
|
||||
"id": "🔧 Let's hack!",
|
||||
"message": "🔧 Let's hack!",
|
||||
"translation": "🔧 Let's hack!",
|
||||
"translatorComment": "Copied from source.",
|
||||
"fuzzy": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,36 +1,6 @@
|
|||
{
|
||||
"language": "ru",
|
||||
"messages": [
|
||||
{
|
||||
"id": "🥳 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!",
|
||||
"message": "🥳 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!",
|
||||
"translation": "🥳 4-е Ноября? Это день рождения @toby3d!\nЕсли тебе нравится этот бот, то почему бы не отправить ему поздравления вместе с небольшим подарком? Это несказанно его осчастливит!"
|
||||
},
|
||||
{
|
||||
"id": "☺️ 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!",
|
||||
"message": "☺️ 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!",
|
||||
"translation": "☺️ Ой, ты пропустил день рождения @toby3d 4-го Ноября!\nЕсли тебе нравится этот бот, то почему бы не отправить ему поздравления вместе с небольшим подарком? Ещё не слишком поздно его порадовать!"
|
||||
},
|
||||
{
|
||||
"id": "💸 Make a donation",
|
||||
"message": "💸 Make a donation",
|
||||
"translation": "💸 Пожертвование"
|
||||
},
|
||||
{
|
||||
"id": "🤝 Use referral links",
|
||||
"message": "🤝 Use referral links",
|
||||
"translation": "🤝 Реферальные ссылки"
|
||||
},
|
||||
{
|
||||
"id": "🕺 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!",
|
||||
"message": "🕺 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!",
|
||||
"translation": "🕺 Хактоберфест уже здесь!\n\nЕсли ты начинающий или уже опытный golang-разработчик, то сейчас хорошее время помочь улучшить качество кода этого бота. Выбери issue на свой вкус и предложи PR!"
|
||||
},
|
||||
{
|
||||
"id": "🔧 Let's hack!",
|
||||
"message": "🔧 Let's hack!",
|
||||
"translation": "🔧 Let's hack!"
|
||||
},
|
||||
{
|
||||
"id": "👋 Hi {FullName}, I'm {FullName_1}!\nThanks to me, you can collect almost any media content in Telegram without any limits, in any chat via inline mode.",
|
||||
"message": "👋 Hi {FullName}, I'm {FullName_1}!\nThanks to me, you can collect almost any media content in Telegram without any limits, in any chat via inline mode.",
|
||||
|
@ -158,6 +128,36 @@
|
|||
"expr": "setName"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "🥳 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!",
|
||||
"message": "🥳 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!",
|
||||
"translation": "🥳 4-е Ноября? Это день рождения @toby3d!\nЕсли тебе нравится этот бот, то почему бы не отправить ему поздравления вместе с небольшим подарком? Это несказанно его осчастливит!"
|
||||
},
|
||||
{
|
||||
"id": "☺️ 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!",
|
||||
"message": "☺️ 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!",
|
||||
"translation": "☺️ Ой, ты пропустил день рождения @toby3d 4-го Ноября!\nЕсли тебе нравится этот бот, то почему бы не отправить ему поздравления вместе с небольшим подарком? Ещё не слишком поздно его порадовать!"
|
||||
},
|
||||
{
|
||||
"id": "💸 Make a donation",
|
||||
"message": "💸 Make a donation",
|
||||
"translation": "💸 Пожертвование"
|
||||
},
|
||||
{
|
||||
"id": "🤝 Use referral links",
|
||||
"message": "🤝 Use referral links",
|
||||
"translation": "🤝 Реферальные ссылки"
|
||||
},
|
||||
{
|
||||
"id": "🕺 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!",
|
||||
"message": "🕺 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!",
|
||||
"translation": "🕺 Хактоберфест уже здесь!\n\nЕсли ты начинающий или уже опытный golang-разработчик, то сейчас хорошее время помочь улучшить качество кода этого бота. Выбери issue на свой вкус и предложи PR!"
|
||||
},
|
||||
{
|
||||
"id": "🔧 Let's hack!",
|
||||
"message": "🔧 Let's hack!",
|
||||
"translation": "🔧 Let's hack!"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue