package main import ( "encoding/json" "fmt" "log" "math/rand" "net/http" "net/url" "strings" "time" "git.nefrace.ru/nefrace/nechotron" "git.nefrace.ru/nefrace/tongo" "github.com/NicoNex/echotron/v3" ) var docApiURL = "https://docs.godotengine.org/_/api/v2/search/?q=%s&project=godot&version=%s&language=en" var docURL = "https://docs.godotengine.org/ru/stable/search.html?q=%s" type DocResponse struct { Count uint `json:"count"` Next string `json:"next"` Previous string `json:"previous"` Results []DocResult `json:"results"` } type DocResult struct { Title string `json:"title"` Domain string `json:"domain"` Path string `json:"path"` Highlights DocHighlights `json:"highlights"` } type DocHighlights struct { Title []string `json:"title"` } func getDocs(topic string, version string) (string, error) { topic_escaped := nechotron.EscapeMd2(topic) not_found := fmt.Sprintf("Извините, по запросу *%s* ничего не найдено.", topic_escaped) req, err := url.ParseRequestURI(fmt.Sprintf(docApiURL, url.QueryEscape(topic), version)) if err != nil { return not_found, err } result, err := http.Get(req.String()) if err != nil { return not_found, err } defer result.Body.Close() var response DocResponse err = json.NewDecoder(result.Body).Decode(&response) if err != nil { return not_found, err } textResults := "" for i, r := range response.Results { if i > 9 { break } text := nechotron.EscapeMd2(r.Title) link, _ := url.JoinPath(r.Domain, r.Path) textResults += fmt.Sprintf("%d\\. [%s](%s)\n", i+1, text, link) } text := fmt.Sprintf("Вот что я нашла по запросу *%s* \\(для версии `%s`\\): \n\n%s", topic_escaped, version, textResults) return text, nil } func handleDocRequest(u *nechotron.Update) error { topic := u.Ctx.Value(nechotron.FilteredValue("docTopic")).(string) kb := nechotron.NewInlineKeyboard(). Row( nechotron.InButtonCallback("Дай для 3.5", fmt.Sprintf("docs3:%s", topic)), nechotron.InButtonCallback("А на русском можно?", "rudocs")). Row(nechotron.InButtonURL("Поищу сам", fmt.Sprintf(docURL, url.QueryEscape(topic)))). Row(nechotron.InButtonCallback("Спасибо, не надо", "delete")) opts := nechotron.NewOptions(). MarkdownV2(). ReplyTo(u.MessageID()). ReplyMarkup(kb.Markup()). MessageOptions() text, docerr := getDocs(topic, "latest") if docerr != nil { log.Println("Can't get docs: ", docerr) } _, err := u.AnswerText(text, opts) return err } func handleDocRequest3(u *nechotron.Update) error { topic := strings.TrimPrefix(u.Callback(), "docs3:") kb := nechotron.NewInlineKeyboard(). Row( nechotron.InButtonCallback("А на русском можно?", "rudocs")). Row(nechotron.InButtonURL("Поищу сам", fmt.Sprintf(docURL, url.QueryEscape(topic)))). Row(nechotron.InButtonCallback("Спасибо, не надо", "delete")) opts := nechotron.NewOptions(). MarkdownV2(). ReplyMarkup(kb.Markup()). MessageTextOptions() text, docerr := getDocs(topic, "3.5") if docerr != nil { log.Println("Can't get docs: ", docerr) } _, err := u.EditText(text, opts) return err } func handleKarma(u *nechotron.Update) error { from, _ := u.Ctx.Value("userfrom").(*User) to, _ := u.Ctx.Value("userto").(*User) mentionFrom := nechotron.UserMention(u.Message.From) mentionTo := nechotron.UserMention(u.Message.ReplyToMessage.From) if from.ID == to.ID { res, err := u.AnswerMarkdown( fmt.Sprintf("Лайкать себя \\- плохая затея, *%s*", mentionFrom)) go func() { time.Sleep(10 * time.Second) u.Bot.DeleteMessage(u.ChatID(), res.Result.ID) }() return err } value := u.Ctx.Value(nechotron.FilteredValue("karmaValue")).(int) // trigger := u.Ctx.Value(nechotron.FilteredValue("karmaTrigger")).(string) store := tongo.NewStore[KarmaShot](db) fromKarma, _ := store.Count(u.Ctx, tongo.E("to", from.ID)) totalFromKarma := from.KarmaOffset + fromKarma if totalFromKarma < 0 { res, err := u.AnswerText( fmt.Sprintf("У тебя слишком маленькая карма *\\(%d\\), чтобы менять её другим\\.", totalFromKarma), &echotron.MessageOptions{ParseMode: echotron.MarkdownV2, ReplyToMessageID: u.MessageID()}) go waitAndDelete(u, u.ChatID(), res.Result.ID) return err } timeThreshold := time.Now().Add(1 * -time.Minute) recentShots, err := store.Count( u.Ctx, tongo.E("from", from.ID), tongo.E("to", to.ID), tongo.E("when", tongo.D(tongo.E("$gte", timeThreshold)))) if err != nil { return err } if recentShots > 0 { // u.DeleteMessage() res, err := u.AnswerMarkdown( fmt.Sprintf("*%s*, ты только недавно менял карму *%s*\\. Подожди минуту\\.", mentionFrom, mentionTo)) go waitAndDelete(u, u.ChatID(), res.Result.ID) return err } if to.ID == u.Bot.Me.ID { if value > 0 { u.AnswerMarkdown("*_Ой, это мне?_*") } else { res, err := u.AnswerMarkdown("*_Кажется, вы ошиблись адресатом :/_*") go waitAndDelete(u, u.ChatID(), res.Result.ID) return err } } newShot := KarmaShot{ Item: tongo.NewID(), From: from.ID, To: to.ID, MessageText: nechotron.GetText(u.Message.ReplyToMessage), When: time.Now(), Count: value, } store.InsertOne(u.Ctx, &newShot) newKarma, _ := store.Count(u.Ctx, tongo.E("to", to.ID)) totalToKarma := to.KarmaOffset + newKarma changeText := "повысил" if value < 0 { changeText = "понизил" } _, err = u.AnswerMarkdown( fmt.Sprintf("*%s \\(%d\\)* только что %s карму *%s \\(%d\\)*", mentionFrom, totalFromKarma, changeText, mentionTo, totalToKarma)) return err } func handleOfftop(u *nechotron.Update) error { text := "Держите [ссылку на оффтоп](%s)\\!" offtopLink := "https://t.me/Godot_Engine_Offtop" offtopAnswers, err := tongo.NewStore[TriggerText](db).GetMany(u.Ctx, tongo.E("trigger", "offtop")) if err == nil { if len(offtopAnswers) > 0 { text = offtopAnswers[rand.Intn(len(offtopAnswers)-1)].Text } } offtopUrl, err := tongo.NewStore[Config](db).GetOne(u.Ctx, tongo.E("name", "offtop_url")) if err == nil { offtopLink = offtopUrl.Value.(string) } text = fmt.Sprintf(text, offtopLink) kb := nechotron.NewInlineKeyboard().Row(nechotron.InButtonCallback("Спасибо, не надо", "delete")).Markup() opts := nechotron.NewOptions(). MarkdownV2(). ReplyTo(u.MessageID()). ReplyMarkup(kb). MessageOptions() u.AnswerText(text, opts) return nil } func handleDeleteCallback(u *nechotron.Update) error { u.DeleteMessage() return nil }