diff --git a/src/botutils.go b/src/botutils.go index 9e5b2f6..58a15c9 100644 --- a/src/botutils.go +++ b/src/botutils.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math/rand" + "strconv" "time" "github.com/go-telegram/bot" @@ -37,7 +38,7 @@ func FetchMemberFromChat(ctx context.Context, b *bot.Bot, chatID int64, userID i return b.GetChatMember(ctx, &bot.GetChatMemberParams{ChatID: chatID, UserID: userID}) } -func IsAdmin(member models.ChatMember) bool { +func IsMemberAdmin(member models.ChatMember) bool { return member.Administrator != nil || member.Owner != nil } @@ -59,3 +60,20 @@ func Mention(name string, id int64) string { text := fmt.Sprintf("[%s](tg://user?id=%d)", bot.EscapeMarkdown(name), id) return text } + +func pluralRu(n int, single string, double string, five string) string { + switch n { + case 10, 11, 12, 13, 14, 15, 16, 17, 18, 19: + return five + default: + s := []rune(strconv.Itoa(n)) + switch s[len(s)-1] { + case '1': + return single + case '2', '3', '4': + return double + default: + return five + } + } +} diff --git a/src/db.go b/src/db.go index 20d7e76..8f4d5cc 100644 --- a/src/db.go +++ b/src/db.go @@ -38,15 +38,26 @@ name TEXT NOT NULL, username TEXT DEFAULT '' ); +create table if not exists admins +( +id INTEGER PRIMARY KEY AUTOINCREMENT, +user_id INTEGER NOT NULL, +chat_id INTEGER NOT NULL, +FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, +FOREIGN KEY (chat_id) REFERENCES chats (id) ON DELETE CASCADE +); + create table if not exists bans ( id INTEGER PRIMARY KEY AUTOINCREMENT, -chat_id INTEGER, -user_id INTEGER, -reason TEXT NOT NULL, +chat_id INTEGER NOT NULL, +user_id INTEGER NOT NULL, +text TEXT DEFAULT '', +reason TEXT DEFAULT '', ban_date INTEGER NOT NULL, unban_date INTEGER DEFAULT 0, -FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE +FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE, +FOREIGN KEY (chat_id) REFERENCES chats (id) ON DELETE CASCADE ); create table if not exists captchas @@ -62,7 +73,7 @@ FOREIGN KEY (chat_id) REFERENCES chats (id) ON DELETE CASCADE ) ` -type ChatSchema struct { +type Chat struct { Id int64 `json:"id" db:"id"` Name string `json:"name" db:"name"` Username string `json:"username" db:"username"` @@ -104,7 +115,7 @@ func InitDb() error { return nil } -func NewChat(chat ChatSchema) error { +func NewChat(chat Chat) error { _, err := db.NamedExec(`insert into chats (id, name, username, topic, active) values (:id, :name, :username, :topic, :active)`, chat) return err } @@ -128,8 +139,8 @@ func IsChatActive(id int64) bool { return active } -func GetChatById(id int64) (ChatSchema, error) { - c := ChatSchema{} +func GetChatById(id int64) (Chat, error) { + c := Chat{} err := db.Get(&c, `select * from chats where id = $1`, id) return c, err } diff --git a/src/handlers.go b/src/handlers.go index 5b24c16..beff811 100644 --- a/src/handlers.go +++ b/src/handlers.go @@ -31,7 +31,7 @@ func registerChat(ctx context.Context, b *bot.Bot, update *models.Update) { log.Println("registering", msg.Chat.ID) m, err := FetchMemberFromChat(ctx, b, msg.Chat.ID, msg.From.ID) if err == nil { - if !IsAdmin(*m) { + if !IsMemberAdmin(*m) { log.Println("register: user is not admin") return } @@ -72,7 +72,7 @@ func unregisterChat(ctx context.Context, b *bot.Bot, update *models.Update) { log.Println("unregistering", msg.Chat.ID) m, err := FetchMemberFromChat(ctx, b, msg.Chat.ID, msg.From.ID) if err == nil { - if !IsAdmin(*m) { + if !IsMemberAdmin(*m) { log.Println("register: user is not admin") return } @@ -105,7 +105,7 @@ var NewUserTemplate = ` ` func handleNewJoined(ctx context.Context, b *bot.Bot, u *models.Update) { - var chat ChatSchema + var chat Chat err := db.Get(&chat, "select * from chats where id = $1 and active = 1", u.Message.Chat.ID) if err != nil { log.Println("can't get chat for new joined: ", err) @@ -160,6 +160,89 @@ func handleNewJoined(ctx context.Context, b *bot.Bot, u *models.Update) { } func banUser(ctx context.Context, b *bot.Bot, u *models.Update) { + msg := u.Message + if msg.ReplyToMessage == nil { + newMsg, err := b.SendMessage(ctx, &bot.SendMessageParams{ + Text: "Хочешь себя забанить? 🙃", + ReplyParameters: &models.ReplyParameters{ + ChatID: msg.Chat.ID, + MessageID: msg.ID, + }, + }) + if err == nil { + AddMessageToDelete(MessageToDelete{MessageId: msg.ID, ChatId: msg.Chat.ID, DeleteDate: time.Now().Add(10 * time.Second).Unix()}) + AddMessageToDelete(MessageToDelete{MessageId: newMsg.ID, ChatId: msg.Chat.ID, DeleteDate: time.Now().Add(10 * time.Second).Unix()}) + } + return + } + reply := msg.ReplyToMessage + chatID := msg.Chat.ID + sender := msg.From + banned := reply.From + log.Println("banning user", banned.FirstName, "from", msg.Chat.ID) + m, err := FetchMemberFromChat(ctx, b, chatID, sender.ID) + if err == nil { + if !IsMemberAdmin(*m) { + log.Println("register: user is not admin") + return + } + text := reply.Text + if text == "" { + text = reply.Caption + } + reason := "" + days := 0 + args := strings.SplitN(msg.Text, " ", 2) + if len(args) > 1 { + num, err := strconv.Atoi(args[1]) + if err != nil { + reason = args[1] + } else { + if days > 0 { + days = num + } + if len(args) > 2 { + reason = args[2] + } + } + } + unbanDate := time.Now().Add(time.Duration(days) * time.Hour * 24).Unix() + b.DeleteMessage(ctx, &bot.DeleteMessageParams{ + ChatID: msg.Chat.ID, + MessageID: msg.ID, + }) + _, err := b.BanChatMember(ctx, &bot.BanChatMemberParams{ + ChatID: chatID, + UserID: banned.ID, + UntilDate: int(unbanDate), + }) + if err != nil { + log.Println("can't ban user: ", err) + return + } + newText := "Пользователь *%s* был забанен %s\\." + daysText := "навсегда" + if days > 0 { + daysText = fmt.Sprintf("на %d %s", days, pluralRu(days, "день", "дня", "дней")) + } + sent, err := b.SendMessage(ctx, &bot.SendMessageParams{ + ChatID: msg.Chat.ID, + Text: fmt.Sprintf(newText, Mention(banned.FirstName, banned.ID), daysText), + MessageThreadID: msg.MessageThreadID, + }) + db.Exec( + "insert into bans (chat_id, user_id, text, reason, ban_date, unban_date) values ($1, $2, $3, $4, $5, $6)", + chatID, banned.ID, text, reason, time.Now().Unix(), unbanDate, + ) + if err == nil { + err := DeleteAfterSeconds(msg.Chat.ID, sent.ID, 60) + if err != nil { + log.Println("register: failed to add to delete", err) + } + } + } else { + log.Println("register: error", err) + } }