KarmaShots, TimeLogger

This commit is contained in:
nefrace 2023-01-24 23:17:30 +03:00
parent 601174f764
commit 2a29f0b89d
9 changed files with 222 additions and 28 deletions

View File

@ -9,6 +9,7 @@ import (
var commandMe = neco.NewCommand("me", "Пишу ваш текст о вас в третьем лице", false) var commandMe = neco.NewCommand("me", "Пишу ваш текст о вас в третьем лице", false)
func handleMe(u *neco.Update) error { func handleMe(u *neco.Update) error {
u.DeleteMessage()
param := commandMe.Param(u.Text()) param := commandMe.Param(u.Text())
_, err := u.AnswerMarkdown(fmt.Sprintf("_*%s* %s_", neco.EscapeMd2(u.From().FirstName), neco.EscapeMd2(param))) _, err := u.AnswerMarkdown(fmt.Sprintf("_*%s* %s_", neco.EscapeMd2(u.From().FirstName), neco.EscapeMd2(param)))
return err return err
@ -31,9 +32,16 @@ func handleHelp(u *neco.Update) error {
var commandSay = neco.NewCommand("say", "Пишу ваш текст от своего имени.", true) var commandSay = neco.NewCommand("say", "Пишу ваш текст от своего имени.", true)
func handleSay(u *neco.Update) error { func handleSay(u *neco.Update) error {
u.DeleteMessage()
param := commandSay.Param(u.Text()) param := commandSay.Param(u.Text())
_, err := u.AnswerMarkdown(fmt.Sprintf("*_%s_*", neco.EscapeMd2(param))) _, err := u.AnswerMarkdown(fmt.Sprintf("*_%s_*", neco.EscapeMd2(param)))
return err return err
} }
var commandWarn = neco.NewCommand("warn", "Делаю предупреждение пользователю", true) var commandWarn = neco.NewCommand("warn", "Делаю предупреждение пользователю", true)
func handleWarn(u *neco.Update) error {
param := commandWarn.Param(u.Text())
_, err := u.AnswerMarkdown(fmt.Sprintf("*_%s_*", neco.EscapeMd2(param)))
return err
}

19
filters.go Normal file
View File

@ -0,0 +1,19 @@
package main
import (
"strings"
neco "git.nefrace.ru/nefrace/nechotron"
)
func karmaTriggers(u *neco.Update) bool {
good, bad := GetTriggers()
all := append(good, bad...)
text := u.Text()
for _, t := range all {
if strings.HasPrefix(text, t) {
return true
}
}
return false
}

View File

@ -1,8 +1,73 @@
package main package main
import "git.nefrace.ru/nefrace/nechotron" import (
"fmt"
"time"
"git.nefrace.ru/nefrace/nechotron"
"git.nefrace.ru/nefrace/tongo"
"github.com/NicoNex/echotron/v3"
)
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)
good, bad := GetTriggers()
value := 0
text := u.Text()
if StringHasPrefix(text, good...) {
value = 1
}
if StringHasPrefix(text, bad...) {
value = -1
}
store := tongo.NewStore[KarmaShot](db)
fromKarma, _ := store.Count(u.Ctx, tongo.E("to", from.ID))
totalFromKarma := from.KarmaOffset + fromKarma
if totalFromKarma < 0 {
_, err := u.AnswerText(
fmt.Sprintf("У тебя слишком маленькая карма *\\(%d\\), чтобы менять её другим\\.", totalFromKarma),
&echotron.MessageOptions{ParseMode: echotron.MarkdownV2, ReplyToMessageID: u.MessageID()})
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 func() {
time.Sleep(10 * time.Second)
u.Bot.DeleteMessage(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 handleAdmin(u *nechotron.Update) error {
u.AnswerPlain("Вы админ!")
return nil
} }

6
karma.go Normal file
View File

@ -0,0 +1,6 @@
package main
// Returns slices of good and bad triggers
func GetTriggers() ([]string, []string) {
return []string{"+", "спасибо", "благодарю", "👍"}, []string{"-", "👎"}
}

16
main.go
View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"context"
"log" "log"
"os" "os"
@ -8,21 +9,26 @@ import (
"git.nefrace.ru/nefrace/tongo" "git.nefrace.ru/nefrace/tongo"
"github.com/NicoNex/echotron/v3" "github.com/NicoNex/echotron/v3"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"go.mongodb.org/mongo-driver/mongo/options"
) )
var BuildTime string var BuildTime string
var db *tongo.Database
func main() { func main() {
godotenv.Load() godotenv.Load()
db, err := tongo.NewConnection(os.Getenv("MONGO_URI"), "godette") var err error
db, err = tongo.NewConnection(os.Getenv("MONGO_URI"), "godette")
if err != nil { if err != nil {
log.Fatalf("Can't connect to database: %v", err) log.Fatalf("Can't connect to database: %v", err)
} }
createIndexes(db)
token := os.Getenv("TELEGRAM_TOKEN") token := os.Getenv("TELEGRAM_TOKEN")
neco := nechotron.NewTron(token, &MainState) neco := nechotron.NewTron(token, &MainState)
neco. neco.
Use(UserDBUpdater(db)). Use(UserDBUpdater(db)).
Use(ErrorLogger) Use(ErrorLogger).
Use(ExecTimeLogger)
api := echotron.NewAPI(token) api := echotron.NewAPI(token)
defaultCommands := []*nechotron.Command{commandHelp, commandMe} defaultCommands := []*nechotron.Command{commandHelp, commandMe}
adminCommands := []*nechotron.Command{commandSay, commandWarn} adminCommands := []*nechotron.Command{commandSay, commandWarn}
@ -31,3 +37,9 @@ func main() {
nechotron.SetMyCommands(api, "", echotron.BotCommandScope{Type: echotron.BCSTAllChatAdministrators}, allCommands...) nechotron.SetMyCommands(api, "", echotron.BotCommandScope{Type: echotron.BCSTAllChatAdministrators}, allCommands...)
log.Fatal(neco.DispatchPoll()) log.Fatal(neco.DispatchPoll())
} }
func createIndexes(db *tongo.Database) error {
userStore := tongo.NewStore[User](db)
_, err := userStore.Coll.Indexes().CreateOne(context.Background(), userIndex, options.CreateIndexes())
return err
}

View File

@ -9,6 +9,16 @@ import (
"git.nefrace.ru/nefrace/tongo" "git.nefrace.ru/nefrace/tongo"
) )
func ExecTimeLogger(next nechotron.UpdateHandler) nechotron.UpdateHandler {
return func(u *nechotron.Update) error {
start := time.Now()
err := next(u)
t := time.Since(start)
log.Println("Update was handled in %d microseconds", t.Microseconds())
return err
}
}
func UserLogger(next nechotron.UpdateHandler) nechotron.UpdateHandler { func UserLogger(next nechotron.UpdateHandler) nechotron.UpdateHandler {
return func(u *nechotron.Update) error { return func(u *nechotron.Update) error {
log.Println(u.From().FirstName) log.Println(u.From().FirstName)
@ -20,24 +30,21 @@ func UserLogger(next nechotron.UpdateHandler) nechotron.UpdateHandler {
func UserDBUpdater(db *tongo.Database) nechotron.Middleware { func UserDBUpdater(db *tongo.Database) nechotron.Middleware {
return func(next nechotron.UpdateHandler) nechotron.UpdateHandler { return func(next nechotron.UpdateHandler) nechotron.UpdateHandler {
return func(u *nechotron.Update) error { return func(u *nechotron.Update) error {
store := tongo.NewStore[User](db)
from := u.From() from := u.From()
user, err := store.GetOne(u.Ctx, tongo.E("id", from.ID)) userFrom, err := UpdateUser(u.Ctx, db, from, true)
if err != nil { if err != nil {
user = &User{ return err
Item: tongo.NewID(), }
ID: from.ID, u.Ctx = context.WithValue(u.Ctx, "userfrom", userFrom)
if u.IsMessage() && u.Message.ReplyToMessage != nil {
to := u.Message.ReplyToMessage.From
userTo, err := UpdateUser(u.Ctx, db, to, false)
if err != nil {
return err
} }
u.Ctx = context.WithValue(u.Ctx, "userto", userTo)
} }
user.FirstName = from.FirstName
user.LastName = from.LastName
user.Username = from.Username
user.LastMessage = time.Now()
err = store.ReplaceItem(u.Ctx, *user, true)
if err != nil {
log.Println("Cant replace user: ", err)
}
u.Ctx = context.WithValue(u.Ctx, "dbuser", user)
return next(u) return next(u)
} }
} }

View File

@ -6,11 +6,16 @@ import (
var MainState = neco.State{ var MainState = neco.State{
Fn: func(u *neco.Update) error { Fn: func(u *neco.Update) error {
disp := neco.NewDispatcher(). mainCommands := neco.NewDispatcher().
HandleCommand(commandMe, handleMe). HandleCommand(commandMe, handleMe).
HandleCommand(commandHelp, handleHelp). HandleCommand(commandHelp, handleHelp).
HandleCommand(commandSay, handleSay) HandleCommand(commandSay, handleSay)
replyDispatcher := neco.NewDispatcher().
HandleCommand(commandWarn, handleWarn).
HandleFilter(karmaTriggers, handleKarma)
replies := neco.NewDispatcher().
HandleFilter(neco.IsReply, replyDispatcher.Run)
return disp.Run(u) return neco.ChainRun(u, mainCommands, replies)
}, },
} }

View File

@ -1,9 +1,12 @@
package main package main
import ( import (
"context"
"time" "time"
"git.nefrace.ru/nefrace/tongo" "git.nefrace.ru/nefrace/tongo"
"github.com/NicoNex/echotron/v3"
"go.mongodb.org/mongo-driver/mongo"
) )
var _ tongo.Collectable = &User{} var _ tongo.Collectable = &User{}
@ -14,20 +17,60 @@ type User struct {
FirstName string FirstName string
LastName string LastName string
ID int64 ID int64
KarmaOffset int KarmaOffset int64
LastMessage time.Time LastMessage time.Time
} }
func UpdateUser(ctx context.Context, db *tongo.Database, user *echotron.User, updateLastMessage bool) (*User, error) {
store := tongo.NewStore[User](db)
u, err := store.GetOne(ctx, tongo.E("id", user.ID))
if err != nil {
u = &User{
Item: tongo.NewID(),
ID: user.ID,
}
}
u.FirstName = user.FirstName
u.LastName = user.LastName
u.Username = user.Username
if updateLastMessage {
u.LastMessage = time.Now()
}
err = store.ReplaceItem(ctx, *u, true)
if err != nil {
// log.Println("Cant replace user: ", err)
return nil, err
}
return u, nil
}
var userIndex = mongo.IndexModel{
Keys: tongo.D(tongo.E("id", 1)),
}
func (User) Coll() string { return "users" } func (User) Coll() string { return "users" }
var _ tongo.Collectable = &KarmaShot{} var _ tongo.Collectable = &KarmaShot{}
type KarmaShot struct { type KarmaShot struct {
tongo.Item tongo.Item `bson:",inline"`
From tongo.OID From int64
To tongo.OID To int64
Count int Count int
When time.Time MessageText string
When time.Time
} }
func (KarmaShot) Coll() string { return "karma" } func (KarmaShot) Coll() string { return "karma" }
type Warn struct {
tongo.Item `bson:",inline"`
From int64
To int64
MessageText string
WarnText string
When time.Time
Active bool
}
func (Warn) Coll() string { return "warns" }

29
utils.go Normal file
View File

@ -0,0 +1,29 @@
package main
import "strings"
func StringHasAny(s string, subs ...string) bool {
for _, sub := range subs {
if strings.Contains(s, sub) {
return true
}
}
return false
}
func StringHasPrefix(s string, subs ...string) bool {
for _, sub := range subs {
if strings.HasPrefix(s, sub) {
return true
}
}
return false
}
func StringHasSuffix(s string, subs ...string) bool {
for _, sub := range subs {
if strings.HasSuffix(s, sub) {
return true
}
}
return false
}