KickerBot/kicker/handlers.go

341 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
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(message.Chat.ID, message.ID)
if err != nil {
log.Printf("Can't delete message: %v", err)
}
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),
},
)
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 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
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
}
}
text_runes := []rune(message.Text)
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 {
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)
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
}