diff options
| author | Xe Iaso <me@xeiaso.net> | 2023-06-19 21:59:53 -0400 |
|---|---|---|
| committer | Xe Iaso <me@xeiaso.net> | 2023-06-19 21:59:53 -0400 |
| commit | 60264ef896ea876456ea3168882049405c3ed38d (patch) | |
| tree | ba1b981372b31af09d4e5cf292f707da28355663 /cmd | |
| parent | 390f313d38a39be9876176eed4b2a50c11657b1e (diff) | |
| download | x-60264ef896ea876456ea3168882049405c3ed38d.tar.xz x-60264ef896ea876456ea3168882049405c3ed38d.zip | |
cmd: add new command xatci
Signed-off-by: Xe Iaso <me@xeiaso.net>
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/marabot/discord.go | 12 | ||||
| -rw-r--r-- | cmd/xatci/main.go | 172 |
2 files changed, 181 insertions, 3 deletions
diff --git a/cmd/marabot/discord.go b/cmd/marabot/discord.go index a1e16c5..b34eb1e 100644 --- a/cmd/marabot/discord.go +++ b/cmd/marabot/discord.go @@ -77,10 +77,16 @@ func (mr *MaraRevolt) DiscordMessageDelete(s *discordgo.Session, m *discordgo.Me ln.Error(ctx, err, ln.Action("nuking deleted messages")) } - row := mr.db.QueryRowContext(ctx, "SELECT id FROM s3_uploads WHERE message_id = ?", m.ID) - if row.Err() == nil { + rows, err := mr.db.QueryContext(ctx, "SELECT id FROM s3_uploads WHERE message_id = ?", m.ID) + if err != nil { + ln.Error(ctx, err) + return + } + defer rows.Close() + + for rows.Next() { var id string - if err := row.Scan(&id); err != nil { + if err := rows.Scan(&id); err != nil { ln.Error(ctx, err) return } diff --git a/cmd/xatci/main.go b/cmd/xatci/main.go new file mode 100644 index 0000000..6cdb50d --- /dev/null +++ b/cmd/xatci/main.go @@ -0,0 +1,172 @@ +package main + +import ( + "context" + "flag" + "fmt" + "strings" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/go-shiori/go-readability" + "github.com/mymmrac/telego" + th "github.com/mymmrac/telego/telegohandler" + tu "github.com/mymmrac/telego/telegoutil" + "within.website/ln" + "within.website/ln/opname" + "within.website/x/internal" + "within.website/x/web/marginalia" + "within.website/x/web/openai/chatgpt" +) + +var ( + marginaliaToken = flag.String("marginalia-token", "", "Token for Marginalia internet search") + openAIToken = flag.String("openai-token", "", "OpenAI token") + openAIModel = flag.String("openai-model", "gpt-3.5-turbo-16k-0613", "OpenAI model to use") + telegramAdmin = flag.Int64("telegram-admin", 0, "Telegram bot admin") + telegramToken = flag.String("telegram-token", "", "Telegram bot token") +) + +func main() { + internal.HandleStartup() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + ctx = opname.With(ctx, "xatci") + + mc := marginalia.New(*marginaliaToken, nil) + + cGPT := chatgpt.NewClient(*openAIToken) + + // Note: Please keep in mind that default logger may expose sensitive information, + // use in development only + bot, err := telego.NewBot(*telegramToken) + if err != nil { + ln.FatalErr(ctx, err) + } + + // Get updates channel + updates, err := bot.UpdatesViaLongPolling(nil) + if err != nil { + ln.FatalErr(ctx, err) + } + + // Create bot handler and specify from where to get updates + bh, err := th.NewBotHandler(bot, updates) + if err != nil { + ln.FatalErr(ctx, err) + } + + // Stop handling updates + defer bh.Stop() + + // Stop getting updates + defer bot.StopLongPolling() + + // Register new handler with match on command `/start` + bh.Handle(func(bot *telego.Bot, update telego.Update) { + // Send message + if _, err := bot.SendMessage(tu.Message( + tu.ID(update.Message.Chat.ID), + fmt.Sprintf("Hello %s!", update.Message.From.FirstName), + )); err != nil { + ln.Error(ctx, err) + } + }, th.CommandEqual("start")) + + bh.Handle(func(bot *telego.Bot, update telego.Update) { + if update.Message.From.ID != *telegramAdmin { + bot.SendMessage(tu.Message( + tu.ID(update.Message.Chat.ID), + "unknown command", + )) + } + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute) + defer cancel() + ctx = opname.With(ctx, "xatci.marginalia.search") + + q := strings.Join(strings.Split(update.Message.Text, " ")[1:], " ") + + ctx = ln.WithF(ctx, ln.F{ + "telegram_requestor": update.Message.From.ID, + "telegram_requestor_name": fmt.Sprintf("%s %s", update.Message.From.FirstName, update.Message.From.LastName), + "search_query": q, + }) + + results, err := mc.Search(ctx, &marginalia.Request{ + Query: q, + Count: aws.Int(5), + }) + if err != nil { + ln.Error(ctx, err) + bot.SendMessage(tu.Message( + tu.ID(update.Message.Chat.ID), + fmt.Sprintf("Error: %v", err), + )) + return + } + + var sb strings.Builder + + fmt.Fprintf(&sb, "License: %s\n\n", results.License) + + for _, result := range results.Results { + fmt.Fprintf(&sb, "**%s** (%s):\n", result.Title, result.URL) + + ln.Log(ctx, ln.Action("resolving article"), ln.F{ + "result_title": result.Title, + "result_url": result.URL, + }) + + article, err := readability.FromURL(result.URL, 30*time.Second) + if err != nil { + fmt.Fprintf(&sb, "Can't parse article: %v", err) + continue + } + + resp, err := cGPT.Complete(ctx, chatgpt.Request{ + Model: *openAIModel, + Messages: []chatgpt.Message{ + { + Role: "system", + Content: "You are a programmer's research assistant, engaging users in thoughtful discussions on a wide range of topics, from ethics and metaphysics to programming and architectural design. Offer insights into the works of various philosophers, well-known programmers, their theories, and ideas. Encourage users to think critically and reflect on the nature of existence, knowledge, and values.", + }, + { + Role: "user", + Content: "Can you summarize this article by " + article.Byline + " in 3 sentences or less?\n\n" + article.TextContent, + }, + }, + }) + if err != nil { + ln.Error(ctx, err) + } + + fmt.Fprintf(&sb, "%s\n\n", resp.Choices[0].Message.Content) + } + + msg := tu.Message(tu.ID(update.Message.Chat.ID), sb.String()) + msg.ParseMode = telego.ModeMarkdown + + if _, err := bot.SendMessage(msg); err != nil { + ln.Error(ctx, err) + return + } + + ln.Log(ctx, ln.Action("query successful")) + }, th.CommandEqual("search")) + + // Register new handler with match on any command + // Handlers will match only once and in order of registration, + // so this handler will be called on any command except `/start` command + bh.Handle(func(bot *telego.Bot, update telego.Update) { + // Send message + _, _ = bot.SendMessage(tu.Message( + tu.ID(update.Message.Chat.ID), + "Unknown command, use /start", + )) + }, th.AnyCommand()) + + // Start handling updates + bh.Start() +} |
