Scaffolding for new captcha type

This commit is contained in:
Nefrace 2024-07-11 00:07:52 +03:00
parent aadc6e8a03
commit beeafa141a
5 changed files with 141 additions and 7 deletions

View File

@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"fmt" "fmt"
"time"
"github.com/go-telegram/bot" "github.com/go-telegram/bot"
"github.com/go-telegram/bot/models" "github.com/go-telegram/bot/models"
@ -38,3 +39,7 @@ func FetchMemberFromChat(ctx context.Context, b *bot.Bot, chatID int64, userID i
func IsAdmin(member models.ChatMember) bool { func IsAdmin(member models.ChatMember) bool {
return member.Administrator != nil || member.Owner != nil return member.Administrator != nil || member.Owner != nil
} }
func DeleteAfterSeconds(chatID int64, msgID int, seconds int) error {
return AddMessageToDelete(MessageToDelete{MessageId: msgID, ChatId: chatID, DeleteDate: time.Now().Add(time.Duration(seconds) * time.Second).Unix(), Tries: 0})
}

View File

@ -27,7 +27,8 @@ create table if not exists messagesToDelete
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
message_id INTEGER, message_id INTEGER,
chat_id INTEGER, chat_id INTEGER,
delete_date INTEGER delete_date INTEGER,
tries INTEGER
); );
create table if not exists users create table if not exists users
@ -71,6 +72,7 @@ type MessageToDelete struct {
MessageId int `db:"message_id"` MessageId int `db:"message_id"`
ChatId int64 `db:"chat_id"` ChatId int64 `db:"chat_id"`
DeleteDate int64 `db:"delete_date"` DeleteDate int64 `db:"delete_date"`
Tries int `db:"tries"`
} }
type User struct { type User struct {
@ -79,6 +81,14 @@ type User struct {
Username string `db:"username"` Username string `db:"username"`
} }
type Captcha struct {
Id int64 `db:"id"`
UserID int64 `db:"user_id"`
ChatID int64 `db:"chat_id"`
MessageID int `db:"message_id"`
CorrectAnswer int `db:"correct_answer"`
}
var db *sqlx.DB var db *sqlx.DB
func InitDb() error { func InitDb() error {
@ -127,7 +137,7 @@ func NewActivation(code string) error {
func UseActivation(code string) bool { func UseActivation(code string) bool {
exists := false exists := false
err := db.Get(&exists, `SELECT exists(SELECT 1 FROM chats WHERE code = $1);`, code) err := db.Get(&exists, `SELECT exists(SELECT 1 FROM activations WHERE code = $1);`, code)
if err != nil || !exists { if err != nil || !exists {
return false return false
} }
@ -141,7 +151,7 @@ func ActivateChat(id int64) error {
} }
func AddMessageToDelete(msg MessageToDelete) error { func AddMessageToDelete(msg MessageToDelete) error {
_, err := db.NamedExec(`insert into messagesToDelete (message_id, chat_id, delete_date) values (:message_id, :chat_id, :delete_date)`, msg) _, err := db.NamedExec(`insert into messagesToDelete (message_id, chat_id, delete_date, tries) values (:message_id, :chat_id, :delete_date, 0)`, msg)
return err return err
} }

View File

@ -2,9 +2,12 @@ package main
import ( import (
"context" "context"
"database/sql"
"errors"
"fmt"
"log" "log"
"strconv"
"strings" "strings"
"time"
"github.com/go-telegram/bot" "github.com/go-telegram/bot"
"github.com/go-telegram/bot/models" "github.com/go-telegram/bot/models"
@ -15,6 +18,9 @@ func defaultHandler(ctx context.Context, b *bot.Bot, update *models.Update) {
// ChatID: update.Message.Chat.ID, // ChatID: update.Message.Chat.ID,
// Text: "hello!", // Text: "hello!",
// }) // })
if update.Message != nil {
log.Println(update.Message.Text)
}
log.Println(*update) log.Println(*update)
} }
@ -33,7 +39,7 @@ func registerChat(ctx context.Context, b *bot.Bot, update *models.Update) {
return return
} }
if !UseActivation(args[1]) { if !UseActivation(args[1]) {
log.Println("register: wrong code") log.Println("register: wrong code: ", args[1])
return return
} }
if err := ActivateChat(msg.Chat.ID); err != nil { if err := ActivateChat(msg.Chat.ID); err != nil {
@ -43,7 +49,7 @@ func registerChat(ctx context.Context, b *bot.Bot, update *models.Update) {
b.DeleteMessage(ctx, &bot.DeleteMessageParams{ChatID: msg.Chat.ID, MessageID: msg.ID}) b.DeleteMessage(ctx, &bot.DeleteMessageParams{ChatID: msg.Chat.ID, MessageID: msg.ID})
sent, err := b.SendMessage(ctx, &bot.SendMessageParams{ChatID: msg.Chat.ID, Text: "Чат зарегистрирован", MessageThreadID: msg.MessageThreadID}) sent, err := b.SendMessage(ctx, &bot.SendMessageParams{ChatID: msg.Chat.ID, Text: "Чат зарегистрирован", MessageThreadID: msg.MessageThreadID})
if err == nil { if err == nil {
err := AddMessageToDelete(MessageToDelete{MessageId: sent.ID, ChatId: msg.Chat.ID, DeleteDate: time.Now().Add(1 * time.Minute).Unix()}) err := DeleteAfterSeconds(msg.Chat.ID, sent.ID, 60)
if err != nil { if err != nil {
log.Println("register: failed to add to delete", err) log.Println("register: failed to add to delete", err)
} }
@ -55,10 +61,79 @@ func registerChat(ctx context.Context, b *bot.Bot, update *models.Update) {
func handleNewJoined(ctx context.Context, b *bot.Bot, u *models.Update) { func handleNewJoined(ctx context.Context, b *bot.Bot, u *models.Update) {
for _, user := range u.Message.NewChatMembers { for _, user := range u.Message.NewChatMembers {
log.Println(user) _, err := db.Exec(`INSERT INTO captchas (user_id, chat_id, message_id, correct_answer) values ($1, $2, 0, 0)`, user.ID, u.Message.Chat.ID)
if err != nil {
log.Println("newusers: can't add to db: ", err)
}
b.SendMessage(ctx, &bot.SendMessageParams{
ChatID: u.Message.Chat.ID,
Text: "Check the capthca!",
ReplyMarkup: models.InlineKeyboardMarkup{
InlineKeyboard: [][]models.InlineKeyboardButton{
{
{URL: fmt.Sprintf("https://t.me/nefrace_php_test_bot?start=cap_%d", u.Message.Chat.ID), Text: "Check"},
},
},
},
})
} }
} }
func banUser(ctx context.Context, b *bot.Bot, u *models.Update) { func banUser(ctx context.Context, b *bot.Bot, u *models.Update) {
} }
func handlePrivateStartCaptcha(ctx context.Context, b *bot.Bot, u *models.Update) {
args := strings.Split(u.Message.Text, " ")
userID := u.Message.From.ID
chatID := 0
if len(args) > 1 {
chat_str := strings.TrimPrefix(args[1], "cap_")
id, err := strconv.Atoi(chat_str)
if err == nil {
chatID = id
}
}
captcha := Captcha{}
var err error
if chatID != 0 {
err = db.Get(&captcha, `select * from captchas where user_id = $1 and chat_id = $2`, userID, chatID)
if errors.Is(err, sql.ErrNoRows) {
b.SendMessage(ctx, &bot.SendMessageParams{
Text: " There's no captchas for that chat you came from.",
ChatID: u.Message.Chat.ID,
})
return
}
} else {
err = db.Get(&captcha, `select * from captchas where user_id = $1 and correct_answer != 0`, userID)
if err != nil {
b.SendMessage(ctx, &bot.SendMessageParams{
Text: " There's no captchas for that chat you came from.",
ChatID: u.Message.Chat.ID,
})
return
}
}
if captcha.CorrectAnswer == 0 {
captcha.CorrectAnswer = 42
msg, err := b.SendMessage(ctx, &bot.SendMessageParams{
Text: "Get me the answer!",
ChatID: u.Message.Chat.ID,
})
if err == nil {
captcha.MessageID = msg.ID
}
_, err = db.NamedExec("update captchas set correct_answer = :correct_answer, message_id = :message_id where id = :id", captcha)
if err != nil {
log.Println("Can't update captcha:", err)
}
} else {
b.SendMessage(ctx, &bot.SendMessageParams{
Text: fmt.Sprintf("You already have captcha for chat %d", captcha.ChatID),
ChatID: u.Message.Chat.ID,
})
}
}

View File

@ -2,8 +2,11 @@ package main
import ( import (
"context" "context"
"log"
"os" "os"
"os/signal" "os/signal"
"strings"
"time"
_ "github.com/glebarez/go-sqlite" _ "github.com/glebarez/go-sqlite"
"github.com/go-telegram/bot" "github.com/go-telegram/bot"
@ -36,5 +39,45 @@ func main() {
b.RegisterHandlerMatchFunc(func(update *models.Update) bool { b.RegisterHandlerMatchFunc(func(update *models.Update) bool {
return update.Message != nil && len(update.Message.NewChatMembers) > 0 return update.Message != nil && len(update.Message.NewChatMembers) > 0
}, handleNewJoined) }, handleNewJoined)
b.RegisterHandlerMatchFunc(func(update *models.Update) bool {
return update.Message != nil && update.Message.Chat.Type == "private" && strings.HasPrefix(update.Message.Text, "/start")
}, handlePrivateStartCaptcha)
go timedTask(ctx, b)
b.Start(ctx) b.Start(ctx)
} }
func timedTask(ctx context.Context, b *bot.Bot) {
timer := time.NewTicker(time.Second * 30)
task:
for {
select {
case <-timer.C:
deleteOldMessages(ctx, b)
case <-ctx.Done():
break task
}
}
}
func deleteOldMessages(ctx context.Context, b *bot.Bot) {
now := time.Now().Unix()
messages := []MessageToDelete{}
err := db.Select(&messages, `SELECT * from messagesToDelete WHERE delete_date < $1`, now)
if err != nil {
log.Println("delete_old: can't get messages: ", err)
}
for _, message := range messages {
success, err := b.DeleteMessage(ctx, &bot.DeleteMessageParams{ChatID: message.ChatId, MessageID: message.MessageId})
if !success {
log.Println("delete_old, message wasn't deleted: ", message.MessageId, " :: ", err)
db.Exec(`UPDATE messagesToDelete set tries = tries + 1 where id = $1`, message.Id)
if message.Tries < 3 {
continue
}
}
db.Exec(`DELETE FROM messagesToDelete WHERE id = $1`, message.Id)
}
}

View File

@ -17,6 +17,7 @@ func checkRegistered(next bot.HandlerFunc) bot.HandlerFunc {
} }
chat := update.Message.Chat chat := update.Message.Chat
if chat.Type == "private" { if chat.Type == "private" {
next(ctx, b, update)
return return
} }
if !IsChatActive(chat.ID) && !strings.HasPrefix(update.Message.Text, "/register") { if !IsChatActive(chat.ID) && !strings.HasPrefix(update.Message.Text, "/register") {