Greetings, storage of joined users

This commit is contained in:
Nefrace 2023-09-10 02:48:59 +03:00
parent 635f27f5ed
commit 9533b43b98
5 changed files with 89 additions and 38 deletions

View File

@ -52,7 +52,7 @@ func initImage() *gg.Context {
// На пустое изображение наносятся логотипы из списка, предварительно перемешанного. // На пустое изображение наносятся логотипы из списка, предварительно перемешанного.
// К изображениям также добавляются порядковые номера (начиная с 1 вместо 0), // К изображениям также добавляются порядковые номера (начиная с 1 вместо 0),
// а правильный вариант возвращается вместе с итоговой картинкой // а правильный вариант возвращается вместе с итоговой картинкой
func GenCaptcha() Captcha { func GenCaptcha() *Captcha {
dc := initImage() dc := initImage()
rand.Shuffle(len(Logos), func(i, j int) { Logos[i], Logos[j] = Logos[j], Logos[i] }) // Перемешиваем логотипы 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] }) // И позиции 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, CorrectAnswer: correct_answer,
} }
return captcha return &captcha
} }
func (captcha *Captcha) ToReader() *bytes.Reader { func (captcha *Captcha) ToReader() *bytes.Reader {

View File

@ -25,7 +25,6 @@ type User struct {
LastName string `bson:"last_name"` LastName string `bson:"last_name"`
CorrectAnswer int8 `bson:"correct_answer"` CorrectAnswer int8 `bson:"correct_answer"`
CaptchaMessage int `bson:"captcha_message"` CaptchaMessage int `bson:"captcha_message"`
IsBanned bool `bson:"is_banned"`
IsJoined bool `bson:"is_joined"` IsJoined bool `bson:"is_joined"`
DateJoined time.Time `bson:"date_joined"` DateJoined time.Time `bson:"date_joined"`
JoinedMessage int `bson:"joined_message"` JoinedMessage int `bson:"joined_message"`

View File

@ -2,6 +2,7 @@ package kicker
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"kickerbot/captchagen" "kickerbot/captchagen"
"kickerbot/db" "kickerbot/db"
@ -11,10 +12,40 @@ import (
"git.nefrace.ru/nefrace/tongo" "git.nefrace.ru/nefrace/tongo"
"github.com/NicoNex/echotron/v3" "github.com/NicoNex/echotron/v3"
"go.mongodb.org/mongo-driver/mongo"
) )
func userJoined(b *bot, update *echotron.Update) error { 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) // _, err := b.DeleteMessage(update.Message.Chat.ID, update.Message.ID)
// if err != nil { // if err != nil {
// log.Printf("Can't delete message: %v", err) // log.Printf("Can't delete message: %v", err)
@ -22,27 +53,10 @@ func userJoined(b *bot, update *echotron.Update) error {
bytes, err := captcha.ToBytes() bytes, err := captcha.ToBytes()
if err != nil { if err != nil {
log.Printf("Error creating captcha bytes: %v", bytes) log.Printf("Error creating captcha bytes: %v", bytes)
b.SendMessage("Не могу создать капчу, @nefrace, проверь логи.", update.Message.From.ID, &echotron.MessageOptions{MessageThreadID: int64(update.Message.ThreadID)}) b.SendMessage("Не могу создать капчу, @nefrace, проверь логи.", update.Message.Chat.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(),
} }
// user.CorrectAnswer = int8(captcha.CorrectAnswer) // user.CorrectAnswer = int8(captcha.CorrectAnswer)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
log.Print(user) log.Print(user)
msg := fmt.Sprintf("Приветствую тебя, *[%s](tg://user?id=%d)*\\!\nДля подтверждения, что ты человек, выбери логотип движка, которому посвящен данный чат, и отправь его номер сюда\\.\n*_Я дам тебе десять минут на это\\._*", EscapeText(echotron.MarkdownV2, user.FirstName), user.UserId) msg := fmt.Sprintf("Приветствую тебя, *[%s](tg://user?id=%d)*\\!\nДля подтверждения, что ты человек, выбери логотип движка, которому посвящен данный чат, и отправь его номер сюда\\.\n*_Я дам тебе десять минут на это\\._*", EscapeText(echotron.MarkdownV2, user.FirstName), user.UserId)
options := echotron.PhotoOptions{ options := echotron.PhotoOptions{
@ -57,7 +71,7 @@ func userJoined(b *bot, update *echotron.Update) error {
return err return err
} }
user.CaptchaMessage = result.Result.ID user.CaptchaMessage = result.Result.ID
store.InsertOne(ctx, &user) store.InsertOne(ctx, user)
return nil return nil
} }
@ -67,7 +81,7 @@ func userLeft(b *bot, update *echotron.Update) error {
store := tongo.NewStore[db.User](Client) store := tongo.NewStore[db.User](Client)
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() 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) store.DeleteByID(ctx, user.Id)
b.DeleteMessage(message.Chat.ID, message.ID) b.DeleteMessage(message.Chat.ID, message.ID)
b.DeleteMessage(message.Chat.ID, user.CaptchaMessage) b.DeleteMessage(message.Chat.ID, user.CaptchaMessage)
@ -75,6 +89,19 @@ func userLeft(b *bot, update *echotron.Update) error {
return nil 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 { func checkCaptcha(b *bot, update *echotron.Update) error {
message := update.Message message := update.Message
sender := message.From sender := message.From
@ -83,12 +110,12 @@ func checkCaptcha(b *bot, update *echotron.Update) error {
// d := db.GetDatabase() // d := db.GetDatabase()
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel() 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 { 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) { if message.ThreadID != int(chat.TopicId) {
b.DeleteMessage(message.Chat.ID, message.ID) b.DeleteMessage(message.Chat.ID, message.ID)
text := fmt.Sprintf("*%s*, сначала пройди капчу\\!", UserMention(sender)) 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, message.ID)
b.DeleteMessage(message.Chat.ID, user.CaptchaMessage) b.DeleteMessage(message.Chat.ID, user.CaptchaMessage)
msg := fmt.Sprintf("Капча успешно пройдена пользователем *%s*", UserMention(sender)) 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{ options := echotron.MessageOptions{
ParseMode: echotron.MarkdownV2, ParseMode: echotron.MarkdownV2,
} }
@ -118,7 +150,7 @@ func checkCaptcha(b *bot, update *echotron.Update) error {
if err != nil { if err != nil {
log.Printf("Can't send welcome message: %s", err) 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) // time.Sleep(time.Second * 10)
// _, err = b.DeleteMessage(message.Chat.ID, res.Result.ID) // _, err = b.DeleteMessage(message.Chat.ID, res.Result.ID)
if err != nil { 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.CaptchaMessage)
b.DeleteMessage(message.Chat.ID, user.JoinedMessage) b.DeleteMessage(message.Chat.ID, user.JoinedMessage)
b.BanChatMember(message.Chat.ID, sender.ID, nil) b.BanChatMember(message.Chat.ID, sender.ID, nil)
user.IsBanned = true store.DeleteByID(ctx, user.Id)
store.ReplaceItem(ctx, *user, true)
} }
} }
return nil return nil
@ -162,6 +193,12 @@ func botAdded(b *bot, update *echotron.Update) error {
func setTopic(b *bot, update *echotron.Update) error { func setTopic(b *bot, update *echotron.Update) error {
m := update.Message 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) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel() defer cancel()
store := tongo.NewStore[db.Chat](Client) store := tongo.NewStore[db.Chat](Client)

View File

@ -45,6 +45,13 @@ func (b *bot) Update(update *echotron.Update) {
checkCaptcha(b, 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 { 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 { func EscapeText(parseMode echotron.ParseMode, text string) string {

View File

@ -22,7 +22,7 @@ func TaskKickOldUsers(b *echotron.API) {
now := time.Now() now := time.Now()
old := now.Add(-10 * time.Minute) old := now.Add(-10 * time.Minute)
store := tongo.NewStore[db.User](Client) 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 { if err != nil {
log.Printf("Error in deleting task: %v", err) log.Printf("Error in deleting task: %v", err)
} }
@ -33,7 +33,7 @@ func TaskKickOldUsers(b *echotron.API) {
continue continue
} }
log.Printf("User %s was banned", user.FirstName) 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() defer cancel()
store := tongo.NewStore[db.User](Client) store := tongo.NewStore[db.User](Client)
chatStore := tongo.NewStore[db.Chat](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 { for _, user := range users {
if time.Since(user.LastNotification) > 2*time.Minute { if time.Since(user.LastNotification) > 2*time.Minute {
user.LastNotification = time.Now() user.LastNotification = time.Now()
@ -56,7 +56,7 @@ func TaskNotifyUsers(b *echotron.API) {
} else { } else {
topic = chat.TopicId 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 { if err != nil {
log.Printf("Can't send notification to user: %s", err) log.Printf("Can't send notification to user: %s", err)
} }