From 9533b43b987566511c13d8bce6ee02279ec68357 Mon Sep 17 00:00:00 2001 From: Nefrace Date: Sun, 10 Sep 2023 02:48:59 +0300 Subject: [PATCH] Greetings, storage of joined users --- captchagen/captchagen.go | 4 +- db/structs.go | 1 - kicker/handlers.go | 97 +++++++++++++++++++++++++++------------- kicker/kicker.go | 17 ++++++- kicker/tasks.go | 8 ++-- 5 files changed, 89 insertions(+), 38 deletions(-) diff --git a/captchagen/captchagen.go b/captchagen/captchagen.go index f313755..051484b 100644 --- a/captchagen/captchagen.go +++ b/captchagen/captchagen.go @@ -52,7 +52,7 @@ func initImage() *gg.Context { // На пустое изображение наносятся логотипы из списка, предварительно перемешанного. // К изображениям также добавляются порядковые номера (начиная с 1 вместо 0), // а правильный вариант возвращается вместе с итоговой картинкой -func GenCaptcha() Captcha { +func GenCaptcha() *Captcha { dc := initImage() rand.Shuffle(len(Logos), func(i, j int) { Logos[i], Logos[j] = Logos[j], Logos[i] }) // Перемешиваем логотипы rand.Shuffle(len(XPositions), func(i, j int) { XPositions[i], XPositions[j] = XPositions[j], XPositions[i] }) // И позиции @@ -76,7 +76,7 @@ func GenCaptcha() Captcha { CorrectAnswer: correct_answer, } - return captcha + return &captcha } func (captcha *Captcha) ToReader() *bytes.Reader { diff --git a/db/structs.go b/db/structs.go index 67e8b5c..1e6f9c4 100644 --- a/db/structs.go +++ b/db/structs.go @@ -25,7 +25,6 @@ type User struct { LastName string `bson:"last_name"` CorrectAnswer int8 `bson:"correct_answer"` CaptchaMessage int `bson:"captcha_message"` - IsBanned bool `bson:"is_banned"` IsJoined bool `bson:"is_joined"` DateJoined time.Time `bson:"date_joined"` JoinedMessage int `bson:"joined_message"` diff --git a/kicker/handlers.go b/kicker/handlers.go index 982c893..1c5b009 100644 --- a/kicker/handlers.go +++ b/kicker/handlers.go @@ -2,6 +2,7 @@ package kicker import ( "context" + "errors" "fmt" "kickerbot/captchagen" "kickerbot/db" @@ -11,10 +12,40 @@ import ( "git.nefrace.ru/nefrace/tongo" "github.com/NicoNex/echotron/v3" + "go.mongodb.org/mongo-driver/mongo" ) func userJoined(b *bot, update *echotron.Update) error { - captcha := captchagen.GenCaptcha() + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + store := tongo.NewStore[db.User](Client) + usr := update.Message.NewChatMembers[0] + message := update.Message + user, err := store.GetOne(ctx, tongo.E("chat_id", update.ChatID()), tongo.E("user_id", usr.ID)) + var captcha *captchagen.Captcha + if err != nil { + if errors.Is(err, mongo.ErrNoDocuments) { + captcha = captchagen.GenCaptcha() + user = &db.User{ + Item: tongo.NewID(), + UserId: usr.ID, + Username: usr.Username, + FirstName: usr.FirstName, + LastName: usr.LastName, + IsJoined: false, + ChatId: message.Chat.ID, + JoinedMessage: message.ID, + CorrectAnswer: int8(captcha.CorrectAnswer), + DateJoined: time.Now(), + LastNotification: time.Now(), + } + } else { + log.Printf("Can't find user: %v", err) + } + } + if captcha == nil { + return nil + } // _, err := b.DeleteMessage(update.Message.Chat.ID, update.Message.ID) // if err != nil { // log.Printf("Can't delete message: %v", err) @@ -22,27 +53,10 @@ func userJoined(b *bot, update *echotron.Update) error { bytes, err := captcha.ToBytes() if err != nil { log.Printf("Error creating captcha bytes: %v", bytes) - b.SendMessage("Не могу создать капчу, @nefrace, проверь логи.", update.Message.From.ID, &echotron.MessageOptions{MessageThreadID: int64(update.Message.ThreadID)}) - } - message := update.Message - store := tongo.NewStore[db.User](Client) - user := db.User{ - Item: tongo.NewID(), - UserId: message.From.ID, - Username: message.From.Username, - FirstName: message.From.FirstName, - LastName: message.From.LastName, - IsBanned: false, - IsJoined: false, - ChatId: message.Chat.ID, - JoinedMessage: message.ID, - CorrectAnswer: int8(captcha.CorrectAnswer), - DateJoined: time.Now(), - LastNotification: time.Now(), + b.SendMessage("Не могу создать капчу, @nefrace, проверь логи.", update.Message.Chat.ID, &echotron.MessageOptions{MessageThreadID: int64(update.Message.ThreadID)}) } + // user.CorrectAnswer = int8(captcha.CorrectAnswer) - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() log.Print(user) msg := fmt.Sprintf("Приветствую тебя, *[%s](tg://user?id=%d)*\\!\nДля подтверждения, что ты человек, выбери логотип движка, которому посвящен данный чат, и отправь его номер сюда\\.\n*_Я дам тебе десять минут на это\\._*", EscapeText(echotron.MarkdownV2, user.FirstName), user.UserId) options := echotron.PhotoOptions{ @@ -57,7 +71,7 @@ func userJoined(b *bot, update *echotron.Update) error { return err } user.CaptchaMessage = result.Result.ID - store.InsertOne(ctx, &user) + store.InsertOne(ctx, user) return nil } @@ -67,7 +81,7 @@ func userLeft(b *bot, update *echotron.Update) error { store := tongo.NewStore[db.User](Client) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() - if user, err := store.GetOne(ctx, tongo.E("user_id", sender.ID), tongo.E("chat_id", message.Chat.ID), tongo.E("is_joined", false), tongo.E("is_banned", false)); err == nil { //d.GetUser(ctx, db.User{UserId: sender.ID, ChatId: message.Chat.ID}); err == nil { + if user, err := store.GetOne(ctx, tongo.E("user_id", sender.ID), tongo.E("chat_id", message.Chat.ID), tongo.E("is_joined", false)); err == nil { //d.GetUser(ctx, db.User{UserId: sender.ID, ChatId: message.Chat.ID}); err == nil { store.DeleteByID(ctx, user.Id) b.DeleteMessage(message.Chat.ID, message.ID) b.DeleteMessage(message.Chat.ID, user.CaptchaMessage) @@ -75,6 +89,19 @@ func userLeft(b *bot, update *echotron.Update) error { return nil } +func userBanned(b *bot, update *echotron.Update) error { + m := update.ChatMember + c := m.Chat + u := m.NewChatMember.User + store := tongo.NewStore[db.User](Client) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + if user, err := store.GetOne(ctx, tongo.E("user_id", u.ID), tongo.E("chat_id", c.ID)); err == nil { //d.GetUser(ctx, db.User{UserId: sender.ID, ChatId: message.Chat.ID}); err == nil { + store.DeleteByID(ctx, user.Id) + } + return nil +} + func checkCaptcha(b *bot, update *echotron.Update) error { message := update.Message sender := message.From @@ -83,12 +110,12 @@ func checkCaptcha(b *bot, update *echotron.Update) error { // d := db.GetDatabase() ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() - if user, err := store.GetOne(ctx, tongo.E("user_id", sender.ID), tongo.E("chat_id", message.Chat.ID)); err == nil { //d.GetUser(ctx, db.User{UserId: sender.ID, ChatId: message.Chat.ID}); err == nil { + if user, err := store.GetOne(ctx, tongo.E("user_id", sender.ID), tongo.E("chat_id", message.Chat.ID), tongo.E("is_joined", false)); err == nil { //d.GetUser(ctx, db.User{UserId: sender.ID, ChatId: message.Chat.ID}); err == nil { + chat, err := chatStore.GetOne(ctx, tongo.E("chat_id", message.Chat.ID)) + if err != nil { + return err + } if message.Chat.IsForum { - chat, err := chatStore.GetOne(ctx, tongo.E("chat_id", message.Chat.ID)) - if err != nil { - return err - } if message.ThreadID != int(chat.TopicId) { b.DeleteMessage(message.Chat.ID, message.ID) text := fmt.Sprintf("*%s*, сначала пройди капчу\\!", UserMention(sender)) @@ -108,6 +135,11 @@ func checkCaptcha(b *bot, update *echotron.Update) error { b.DeleteMessage(message.Chat.ID, message.ID) b.DeleteMessage(message.Chat.ID, user.CaptchaMessage) msg := fmt.Sprintf("Капча успешно пройдена пользователем *%s*", UserMention(sender)) + timeout := 10 * time.Second + if chat.Greet != "" { + msg = fmt.Sprintf(chat.Greet, UserMention(sender)) + timeout = 2 * time.Minute + } options := echotron.MessageOptions{ ParseMode: echotron.MarkdownV2, } @@ -118,7 +150,7 @@ func checkCaptcha(b *bot, update *echotron.Update) error { if err != nil { log.Printf("Can't send welcome message: %s", err) } - go waitAndDelete(&b.API, res.Result, 10*time.Second) + go waitAndDelete(&b.API, res.Result, timeout*time.Second) // time.Sleep(time.Second * 10) // _, err = b.DeleteMessage(message.Chat.ID, res.Result.ID) if err != nil { @@ -131,8 +163,7 @@ func checkCaptcha(b *bot, update *echotron.Update) error { b.DeleteMessage(message.Chat.ID, user.CaptchaMessage) b.DeleteMessage(message.Chat.ID, user.JoinedMessage) b.BanChatMember(message.Chat.ID, sender.ID, nil) - user.IsBanned = true - store.ReplaceItem(ctx, *user, true) + store.DeleteByID(ctx, user.Id) } } return nil @@ -162,6 +193,12 @@ func botAdded(b *bot, update *echotron.Update) error { func setTopic(b *bot, update *echotron.Update) error { m := update.Message + if res, err := b.GetChatMember(m.Chat.ID, m.From.ID); err == nil { + m := res.Result + if !(m.Status == "administrator" || m.Status == "creator") { + return nil + } + } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() store := tongo.NewStore[db.Chat](Client) diff --git a/kicker/kicker.go b/kicker/kicker.go index 476f6ec..78379ff 100644 --- a/kicker/kicker.go +++ b/kicker/kicker.go @@ -45,6 +45,13 @@ func (b *bot) Update(update *echotron.Update) { checkCaptcha(b, update) } } + if update.ChatMember != nil { + m := update.ChatMember.NewChatMember + if m.Status == "kicked" { + userBanned(b, update) + return + } + } } // Базовая структура для бота @@ -95,7 +102,15 @@ func (b *Kicker) Init() error { } func (b *Kicker) Start() error { - return b.Dispatcher.Poll() + return b.Dispatcher.PollOptions(true, echotron.UpdateOptions{ + Timeout: 120, + AllowedUpdates: []echotron.UpdateType{ + echotron.MessageUpdate, + echotron.ChatMemberUpdate, + echotron.MyChatMemberUpdate, + echotron.CallbackQueryUpdate, + }, + }) } func EscapeText(parseMode echotron.ParseMode, text string) string { diff --git a/kicker/tasks.go b/kicker/tasks.go index b0d7408..18619e1 100644 --- a/kicker/tasks.go +++ b/kicker/tasks.go @@ -22,7 +22,7 @@ func TaskKickOldUsers(b *echotron.API) { now := time.Now() old := now.Add(-10 * time.Minute) store := tongo.NewStore[db.User](Client) - users, err := store.GetMany(ctx, tongo.E("date_joined", tongo.D(tongo.E("$lt", old))), tongo.E("is_joined", false), tongo.E("is_banned", false)) + users, err := store.GetMany(ctx, tongo.E("date_joined", tongo.D(tongo.E("$lt", old))), tongo.E("is_joined", false)) if err != nil { log.Printf("Error in deleting task: %v", err) } @@ -33,7 +33,7 @@ func TaskKickOldUsers(b *echotron.API) { continue } log.Printf("User %s was banned", user.FirstName) - user.IsBanned = true + store.DeleteByID(ctx, user.Id) } } @@ -42,7 +42,7 @@ func TaskNotifyUsers(b *echotron.API) { defer cancel() store := tongo.NewStore[db.User](Client) chatStore := tongo.NewStore[db.Chat](Client) - users, _ := store.GetMany(ctx, tongo.E("is_joined", false), tongo.E("is_banned", false)) + users, _ := store.GetMany(ctx, tongo.E("is_joined", false)) for _, user := range users { if time.Since(user.LastNotification) > 2*time.Minute { user.LastNotification = time.Now() @@ -56,7 +56,7 @@ func TaskNotifyUsers(b *echotron.API) { } else { topic = chat.TopicId } - res, err := b.SendMessage(text, user.ChatId, &echotron.MessageOptions{MessageThreadID: topic, ParseMode: echotron.MarkdownV2}) + res, err := b.SendMessage(text, user.ChatId, &echotron.MessageOptions{MessageThreadID: topic, ParseMode: echotron.MarkdownV2, ReplyToMessageID: user.CaptchaMessage}) if err != nil { log.Printf("Can't send notification to user: %s", err) }