diff --git a/db/structs.go b/db/structs.go index dd92798..f60a553 100644 --- a/db/structs.go +++ b/db/structs.go @@ -24,6 +24,13 @@ type AdminTopic struct { func (AdminTopic) Coll() string { return "admin_topics" } +type LogChannel struct { + tongo.Item `bson:",inline"` + ChatId int64 `bson:"chat_id"` +} + +func (LogChannel) Coll() string { return "log_channel" } + type User struct { tongo.Item `bson:",inline"` UserId int64 `bson:"user_id"` diff --git a/kicker/handlers.go b/kicker/handlers.go index 1f948c0..d0e18df 100644 --- a/kicker/handlers.go +++ b/kicker/handlers.go @@ -137,6 +137,24 @@ func muteUser(b *bot, update *echotron.Update) error { 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, @@ -149,6 +167,58 @@ func muteUser(b *bot, update *echotron.Update) error { 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 @@ -159,6 +229,17 @@ func userBanned(b *bot, update *echotron.Update) error { 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 } @@ -223,10 +304,15 @@ func checkCaptcha(b *bot, update *echotron.Update) error { } } 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) { diff --git a/kicker/kicker.go b/kicker/kicker.go index e62b991..57e79c2 100644 --- a/kicker/kicker.go +++ b/kicker/kicker.go @@ -61,6 +61,10 @@ func (b *bot) Update(update *echotron.Update) { muteUser(b, update) return } + if strings.HasPrefix(update.Message.Text, "/ban") { + banUser(b, update) + return + } } checkCaptcha(b, update) } @@ -168,14 +172,16 @@ func MentionUser(user *echotron.User) string { var chars = []string{"_", "\\*", "\\[", "\\]", "\\(", "\\)", "~", "`", ">", "#", "\\+", "\\-", "=", "|", "{", "}", "\\.", "!"} var r = strings.Join(chars, "") -var reg = regexp.MustCompile("[" + r + "]+") +var reg = regexp.MustCompile("[" + r + "]") func EscapeMd2(s string) string { return reg.ReplaceAllString(s, "\\$0") } func Mention(name string, id int64) string { - return fmt.Sprintf("[%s](tg://user?id=%d)", EscapeMd2(name), id) + text := fmt.Sprintf("[%s](tg://user?id=%d)", EscapeMd2(name), id) + log.Println(text) + return text } func UserMention(u *echotron.User) string { @@ -186,6 +192,26 @@ func UserMentionDB(u *db.User) string { return Mention(u.FirstName, u.UserId) } +func MentionWithData(u *echotron.User) string { + userid := strconv.FormatInt(u.ID, 10) + username := u.Username + userstr := fmt.Sprintf("userid: `%v`", userid) + if username != "" { + userstr += fmt.Sprintf(", username: @%s", u.Username) + } + return fmt.Sprintf("*%s* \\(%s\\)", UserMention(u), userstr) +} + +func MentionWithDataDB(u *db.User) string { + userid := strconv.FormatInt(u.UserId, 10) + username := u.Username + userstr := fmt.Sprintf("userid: `%v`", userid) + if username != "" { + userstr += fmt.Sprintf(", username: @%s", u.Username) + } + return fmt.Sprintf("*%s* \\(%s\\)", UserMentionDB(u), userstr) +} + func pluralRu(n int, single string, double string, five string) string { switch n { case 10, 11, 12, 13, 14, 15, 16, 17, 18, 19: diff --git a/kicker/tasks.go b/kicker/tasks.go index 80ce13e..dd6b119 100644 --- a/kicker/tasks.go +++ b/kicker/tasks.go @@ -22,16 +22,25 @@ func TaskKickOldUsers(b *echotron.API) { now := time.Now() old := now.Add(-10 * time.Minute) store := tongo.NewStore[db.User](Client) + logstore := tongo.NewStore[db.LogChannel](Client) + logchat, _ := logstore.GetOne(ctx) 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) } for _, user := range users { _, err := b.BanChatMember(user.ChatId, user.UserId, &echotron.BanOptions{RevokeMessages: true}) + if err != nil { log.Println("User was not banned: ", err) continue } + if logchat != nil { + b.SendMessage( + fmt.Sprintf("Пользователь %s не прошёл капчу \\#captcha", MentionWithDataDB(user)), + logchat.ChatId, + &echotron.MessageOptions{ParseMode: echotron.MarkdownV2}) + } log.Printf("User %s was banned", user.FirstName) _, err = b.DeleteMessage(user.ChatId, user.CaptchaMessage) if err != nil {