package kicker import ( "context" "errors" "fmt" "kickerbot/captchagen" "kickerbot/db" "log" "strconv" "strings" "time" "git.nefrace.ru/nefrace/tongo" "github.com/NicoNex/echotron/v3" "go.mongodb.org/mongo-driver/mongo" ) func userJoined(b *bot, update *echotron.Update) error { 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 _, err := b.DeleteMessage(message.Chat.ID, message.ID) if err != nil { log.Printf("Can't delete message: %v", err) } 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 } bytes, err := captcha.ToBytes() if err != nil { log.Printf("Error creating captcha bytes: %v", bytes) b.SendMessage("Не могу создать капчу, @nefrace, проверь логи.", update.Message.Chat.ID, &echotron.MessageOptions{MessageThreadID: int64(update.Message.ThreadID)}) } msg := fmt.Sprintf("Приветствую тебя, *%s*\\!\nДля подтверждения, что ты человек, выбери логотип движка, которому посвящен данный чат, и отправь его номер сюда\\.\n*_Я дам тебе десять минут на это\\._*", UserMention(usr)) options := echotron.PhotoOptions{ Caption: msg, ParseMode: echotron.MarkdownV2, } if message.Chat.IsForum { options.MessageThreadID = int(b.CaptchaTopic) } result, err := b.SendPhoto(echotron.NewInputFileBytes("logos.png", *bytes), message.Chat.ID, &options) if err != nil { return err } user.CaptchaMessage = result.Result.ID store.InsertOne(ctx, user) return nil } func userLeft(b *bot, update *echotron.Update) error { message := update.Message sender := message.From 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)); 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) } return nil } var muted = echotron.ChatPermissions{ CanSendMessages: false, CanSendAudios: false, CanSendDocuments: false, CanSendPhotos: false, CanSendVideos: false, CanSendVideoNotes: false, CanSendVoiceNotes: false, CanSendPolls: false, CanSendOtherMessages: false, } func muteUser(b *bot, update *echotron.Update) error { message := update.Message if message.ReplyToMessage == nil { return nil } reply := message.ReplyToMessage items := strings.SplitN(message.Text, " ", 3) days, msg := 1, reply.Text var err error if len(items) > 1 { days, err = strconv.Atoi(items[1]) if err != nil || days < 1 { res, _ := b.SendMessage("Неверно указано количество дней", message.Chat.ID, &echotron.MessageOptions{ReplyToMessageID: message.ID}) go waitAndDelete(&b.API, res.Result, 10*time.Second) return err } } if len(items) > 2 { msg = fmt.Sprintf("%s: \"%s\"", reply.From.FirstName, items[2]) } store := tongo.NewStore[db.Mute](Client) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() until := time.Now().AddDate(0, 0, days) _, err = b.RestrictChatMember(message.Chat.ID, reply.From.ID, muted, &echotron.RestrictOptions{UntilDate: int(until.Unix())}) if err != nil { b.SendMessage(fmt.Sprintf("Не могу дать молчанку юзеру: %v", err), b.chatID, &echotron.MessageOptions{ReplyToMessageID: message.ID}) return err } b.SendMessage( fmt.Sprintf("Пользователю *%s* выдана \"молчанка\" на %d %s\\.", UserMention(reply.From), days, pluralRu(days, "день", "дня", "дней")), message.Chat.ID, &echotron.MessageOptions{ ParseMode: echotron.MarkdownV2, MessageThreadID: int64(message.ThreadID), }, ) logstore := tongo.NewStore[db.LogChannel](Client) logchat, _ := logstore.GetOne(ctx) if logchat != nil { msg := fmt.Sprintf("Пользователю %s выдана молчанка админом *%s* \\#muted\n\nПричина: %s\\.", MentionWithData(reply.From), UserMention(message.From), EscapeText(echotron.MarkdownV2, msg)) if _, err := b.SendMessage( msg, logchat.ChatId, &echotron.MessageOptions{ ParseMode: echotron.MarkdownV2, }, ); err != nil { log.Printf("Can't log muted message with reason: %v", err) log.Println(msg) } b.ForwardMessage(logchat.ChatId, message.Chat.ID, reply.ID, &echotron.ForwardOptions{}) } store.InsertOne(ctx, &db.Mute{ Item: tongo.NewID(), UserId: reply.From.ID, ChatId: message.Chat.ID, Message: msg, Date: time.Now(), Until: until, MessageLink: fmt.Sprintf("https://t.me/c/%d/%d/%d", transformChatID(update.ChatID()), message.ThreadID, message.ID), }) return nil } func banUser(b *bot, update *echotron.Update) error { message := update.Message if message.ReplyToMessage == nil { return nil } reply := message.ReplyToMessage items := strings.SplitN(message.Text, " ", 2) reason := "" if len(items) == 2 { reason = items[1] } ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() _, err := b.BanChatMember(message.Chat.ID, reply.From.ID, &echotron.BanOptions{}) if err != nil { b.SendMessage(fmt.Sprintf("Не могу забанить юзера: %v", err), b.chatID, &echotron.MessageOptions{ReplyToMessageID: message.ID}) return err } opts := echotron.MessageOptions{ ParseMode: echotron.MarkdownV2, } if message.ThreadID != 0 { opts.MessageThreadID = int64(message.ThreadID) } if _, err := b.SendMessage( fmt.Sprintf("Пользователь %s забанен\\.", UserMention(reply.From)), message.Chat.ID, &opts, ); err != nil { log.Printf("Can't send ban message: %v", err) } logstore := tongo.NewStore[db.LogChannel](Client) logchat, _ := logstore.GetOne(ctx) if logchat != nil { msg := fmt.Sprintf("Пользователь %s забанен админом *%s* \\#banned\n\nПричина: %s\\.", MentionWithData(reply.From), UserMention(message.From), EscapeText(echotron.MarkdownV2, reason)) if _, err := b.SendMessage( msg, logchat.ChatId, &echotron.MessageOptions{ ParseMode: echotron.MarkdownV2, }, ); err != nil { log.Printf("Can't log banned message with reason: %v", err) log.Println(msg) } b.ForwardMessage(logchat.ChatId, message.Chat.ID, reply.ID, &echotron.ForwardOptions{}) } 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) } logstore := tongo.NewStore[db.LogChannel](Client) logchat, _ := logstore.GetOne(ctx) if logchat != nil && m.From.ID != b.Me.ID { b.SendMessage( fmt.Sprintf("Пользователь %s забанен админом *%s* \\#banned\\.", MentionWithData(u), UserMention(&m.From)), logchat.ChatId, &echotron.MessageOptions{ ParseMode: echotron.MarkdownV2, }, ) } return nil } func checkCaptcha(b *bot, update *echotron.Update) error { message := update.Message sender := message.From store := tongo.NewStore[db.User](Client) chatStore := tongo.NewStore[db.Chat](Client) // d := db.GetDatabase() ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() chat, err := chatStore.GetOne(ctx, tongo.E("chat_id", message.Chat.ID)) if err != nil { return err } 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 { if message.Chat.IsForum { if message.ThreadID != int(chat.TopicId) { b.DeleteMessage(message.Chat.ID, message.ID) text := fmt.Sprintf("*%s*, сначала пройди [капчу](https://t.me/c/%d/%d/%d)\\!", UserMention(sender), transformChatID(b.chatID), chat.TopicId, user.CaptchaMessage) res, _ := b.SendMessage(text, message.Chat.ID, &echotron.MessageOptions{ParseMode: echotron.MarkdownV2, MessageThreadID: int64(message.ThreadID)}) go waitAndDelete(&b.API, res.Result, 10*time.Second) return nil } } userText := message.Text if userText == "" { userText = message.Caption } text_runes := []rune(userText) guess := "" if len(text_runes) > 0 { guess = string(text_runes[0]) } solved := false if num, err := strconv.Atoi(guess); err == nil { if num == int(user.CorrectAnswer) { user.IsJoined = true store.ReplaceItem(ctx, *user, true) solved = true 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, } if message.Chat.IsForum { options.MessageThreadID = b.CaptchaTopic } res, err := b.SendMessage(msg, message.Chat.ID, &options) if err != nil { log.Printf("Can't send welcome message: %s", err) } go waitAndDelete(&b.API, res.Result, timeout) // time.Sleep(time.Second * 10) // _, err = b.DeleteMessage(message.Chat.ID, res.Result.ID) if err != nil { log.Printf("Can't delete welcome message: %s", err) } } } if !solved { logstore := tongo.NewStore[db.LogChannel](Client) b.DeleteMessage(message.Chat.ID, message.ID) b.DeleteMessage(message.Chat.ID, user.CaptchaMessage) b.DeleteMessage(message.Chat.ID, user.JoinedMessage) b.BanChatMember(message.Chat.ID, sender.ID, nil) if logchat, _ := logstore.GetOne(ctx); logchat != nil { b.SendMessage(fmt.Sprintf("Пользователь %s провалил капчу \\#banned \\#captcha", MentionWithData(sender)), logchat.ChatId, &echotron.MessageOptions{ParseMode: echotron.MarkdownV2}) } store.DeleteByID(ctx, user.Id) } } else if message.Chat.IsForum && message.ThreadID == int(chat.TopicId) { res, err := b.GetChatMember(update.ChatID(), sender.ID) if err != nil { return err } if res.Result.Status == "administrator" || res.Result.Status == "creator" { return nil } b.DeleteMessage(b.chatID, message.ID) } return nil } func setAdminTopic(b *bot, update *echotron.Update, set bool) error { message := update.Message chatID := update.ChatID() store := tongo.NewStore[db.AdminTopic](Client) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() topic, err := store.GetOne(ctx, tongo.E("chat_id", chatID), tongo.E("topic_id", message.ThreadID)) if set { if err != nil || topic == nil { _, err := store.InsertOne(ctx, &db.AdminTopic{ Item: tongo.NewID(), ChatId: chatID, TopicId: int64(message.ThreadID), }) if err != nil { log.Println("Can't set admintopic: ", err) return err } b.SendMessage("Данный топик теперь только для админов.", chatID, &echotron.MessageOptions{MessageThreadID: int64(message.ThreadID)}) } return nil } else { if err == nil { if err := store.DeleteByID(ctx, topic.Id); err != nil { log.Println("Can't unset admintopic: ", err) return err } b.SendMessage("Данный топик теперь доступен всем!", chatID, &echotron.MessageOptions{MessageThreadID: int64(message.ThreadID)}) } } return nil } func checkAdminTopics(b *bot, update *echotron.Update) error { message := update.Message chatID := update.ChatID() sender := message.From res, err := b.GetChatMember(message.Chat.ID, sender.ID) if err != nil { log.Println("muteUser: Can't get member: ", err) return err } if res.Result.Status == "administrator" || res.Result.Status == "creator" { return nil } store := tongo.NewStore[db.AdminTopic](Client) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if _, err := store.GetOne(ctx, tongo.E("chat_id", chatID), tongo.E("topic_id", message.ThreadID)); err == nil { b.DeleteMessage(chatID, message.ID) } return nil } func botAdded(b *bot, update *echotron.Update) error { m := update.Message store := tongo.NewStore[db.Chat](Client) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() chat, err := store.GetOne(ctx, tongo.E("chat_id", m.Chat.ID)) if err != nil { chat = &db.Chat{ Item: tongo.NewID(), ChatId: m.Chat.ID, Title: m.Chat.Title, TopicId: 0, } } _, err = store.InsertOne(ctx, chat) if err != nil { log.Print(err) } return nil } func setTopic(b *bot, update *echotron.Update) error { m := update.Message ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() store := tongo.NewStore[db.Chat](Client) chat, err := store.GetOne(ctx, tongo.E("chat_id", m.Chat.ID)) if err != nil { return err } chat.TopicId = int64(m.ThreadID) // upd := bson.D{{Key: "$set", Value: bson.D{{Key: "topic_id", Value: m.ThreadID}}}} b.CaptchaTopic = int64(m.ThreadID) err = store.ReplaceItem(ctx, *chat, false) if err != nil { return err } b.DeleteMessage(m.Chat.ID, m.ID) b.SendMessage("Данный топик выбран в качестве проверочного для пользователей", m.Chat.ID, &echotron.MessageOptions{MessageThreadID: int64(m.ThreadID)}) return nil }