himbot/main.go

244 lines
6 KiB
Go
Raw Normal View History

2023-12-26 18:39:21 -07:00
package main
import (
2023-12-27 10:52:24 -07:00
"bytes"
2023-12-26 18:39:21 -07:00
"context"
2023-12-27 21:48:55 -07:00
"errors"
2023-12-26 18:39:21 -07:00
"fmt"
2023-12-27 15:02:23 -07:00
"himbot/lib"
2023-12-26 18:39:21 -07:00
"log"
2024-01-09 21:05:23 -07:00
"net"
2023-12-26 18:39:21 -07:00
"os"
"os/signal"
2024-01-19 14:33:37 -07:00
"strconv"
2024-01-03 14:32:37 -07:00
"time"
2023-12-26 18:39:21 -07:00
"github.com/diamondburned/arikawa/v3/api"
"github.com/diamondburned/arikawa/v3/api/cmdroute"
"github.com/diamondburned/arikawa/v3/discord"
"github.com/diamondburned/arikawa/v3/gateway"
"github.com/diamondburned/arikawa/v3/state"
"github.com/diamondburned/arikawa/v3/utils/json/option"
2023-12-27 10:52:24 -07:00
"github.com/diamondburned/arikawa/v3/utils/sendpart"
2023-12-26 18:39:21 -07:00
"github.com/joho/godotenv"
)
var commands = []api.CreateCommandData{
{
Name: "ping",
Description: "ping pong!",
},
{
2023-12-27 10:52:24 -07:00
Name: "ask",
2024-01-10 00:56:36 -07:00
Description: "Ask Himbot! Cooldown: 2 Minutes.",
2023-12-26 18:39:21 -07:00
Options: []discord.CommandOption{
&discord.StringOption{
2023-12-27 10:52:24 -07:00
OptionName: "prompt",
Description: "The prompt to send to Himbot.",
2023-12-26 18:39:21 -07:00
Required: true,
},
},
},
{
2023-12-27 10:52:24 -07:00
Name: "pic",
2024-01-10 00:28:31 -07:00
Description: "Generate an image! Cooldown: 1 Minute.",
2023-12-26 18:39:21 -07:00
Options: []discord.CommandOption{
&discord.StringOption{
2023-12-27 10:52:24 -07:00
OptionName: "prompt",
Description: "The prompt for the image generation.",
Required: true,
},
},
},
{
Name: "hs",
Description: "This command was your nickname in highschool!",
Options: []discord.CommandOption{
&discord.StringOption{
OptionName: "nickname",
Description: "Your nickname in highschool.",
2023-12-26 18:39:21 -07:00
Required: true,
},
},
},
}
func main() {
godotenv.Load(".env")
token := os.Getenv("DISCORD_TOKEN")
if token == "" {
log.Fatalln("No $DISCORD_TOKEN given.")
}
h := newHandler(state.New("Bot " + token))
h.s.AddInteractionHandler(h)
h.s.AddIntents(gateway.IntentGuilds)
h.s.AddHandler(func(*gateway.ReadyEvent) {
me, _ := h.s.Me()
log.Println("connected to the gateway as", me.Tag())
})
if err := cmdroute.OverwriteCommands(h.s, commands); err != nil {
log.Fatalln("cannot update commands:", err)
}
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
if err := h.s.Connect(ctx); err != nil {
log.Fatalln("cannot connect:", err)
}
}
type handler struct {
*cmdroute.Router
s *state.State
}
func newHandler(s *state.State) *handler {
h := &handler{s: s}
h.Router = cmdroute.NewRouter()
// Automatically defer handles if they're slow.
h.Use(cmdroute.Deferrable(s, cmdroute.DeferOpts{}))
h.AddFunc("ping", h.cmdPing)
h.AddFunc("ask", h.cmdAsk)
2023-12-27 10:52:24 -07:00
h.AddFunc("pic", h.cmdPic)
h.AddFunc("hs", h.cmdHS)
2023-12-26 18:39:21 -07:00
return h
}
2023-12-27 10:52:24 -07:00
func (h *handler) cmdPing(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
2023-12-27 12:47:14 -07:00
// Command Logic
2023-12-26 18:39:21 -07:00
return &api.InteractionResponseData{
Content: option.NewNullableString("Pong!"),
}
}
func (h *handler) cmdAsk(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
2023-12-27 12:47:14 -07:00
// Cooldown Logic
2024-01-10 00:56:36 -07:00
allowed, cooldownString := lib.CooldownHandler(*data.Event, "ask", time.Minute)
2023-12-27 15:29:37 -07:00
2023-12-29 01:31:36 -07:00
if !allowed {
2024-01-10 00:56:36 -07:00
return errorResponse(errors.New(cooldownString))
2023-12-27 15:02:23 -07:00
}
2023-12-27 12:47:14 -07:00
// Command Logic
2023-12-26 18:39:21 -07:00
var options struct {
2023-12-29 14:12:11 -07:00
Prompt string `discord:"prompt"`
2023-12-26 18:39:21 -07:00
}
if err := data.Options.Unmarshal(&options); err != nil {
2024-01-10 00:28:31 -07:00
lib.CancelCooldown(data.Event.User.ID.String(), "ask")
2023-12-26 18:39:21 -07:00
return errorResponse(err)
}
2024-01-09 23:05:32 -07:00
respString, err := lib.ReplicateTextGeneration(options.Prompt)
2023-12-26 18:39:21 -07:00
if err != nil {
fmt.Printf("ChatCompletion error: %v\n", err)
2024-01-10 00:28:31 -07:00
lib.CancelCooldown(data.Event.User.ID.String(), "ask")
return &api.InteractionResponseData{
Content: option.NewNullableString("ChatCompletion Error!"),
2024-01-09 23:05:32 -07:00
AllowedMentions: &api.AllowedMentions{},
}
2023-12-26 18:39:21 -07:00
}
2023-12-29 14:12:11 -07:00
if len(respString) > 1800 {
textFile := bytes.NewBuffer([]byte(respString))
file := sendpart.File{
Name: "himbot_response.txt",
Reader: textFile,
}
2024-01-09 21:05:23 -07:00
return &api.InteractionResponseData{
Content: option.NewNullableString("Prompt: " + options.Prompt + "\n" + "Response:\n"),
2024-01-09 23:05:32 -07:00
AllowedMentions: &api.AllowedMentions{},
Files: []sendpart.File{file},
}
}
2023-12-26 18:39:21 -07:00
return &api.InteractionResponseData{
2023-12-29 14:12:11 -07:00
Content: option.NewNullableString("Prompt: " + options.Prompt + "\n" + "Response: " + respString),
2024-01-09 23:05:32 -07:00
AllowedMentions: &api.AllowedMentions{},
2023-12-26 18:39:21 -07:00
}
}
2023-12-27 10:52:24 -07:00
func (h *handler) cmdPic(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
2023-12-27 12:47:14 -07:00
// Cooldown Logic
2024-01-10 00:56:36 -07:00
allowed, cooldownString := lib.CooldownHandler(*data.Event, "pic", time.Minute*2)
2023-12-27 15:29:37 -07:00
2023-12-29 01:31:36 -07:00
if !allowed {
2024-01-10 00:56:36 -07:00
return errorResponse(errors.New(cooldownString))
2023-12-27 15:02:23 -07:00
}
2023-12-27 12:47:14 -07:00
// Command Logic
2023-12-27 10:52:24 -07:00
var options struct {
Prompt string `discord:"prompt"`
}
if err := data.Options.Unmarshal(&options); err != nil {
2024-01-10 00:28:31 -07:00
lib.CancelCooldown(data.Event.User.ID.String(), "pic")
2023-12-27 10:52:24 -07:00
return errorResponse(err)
}
2024-01-19 14:33:37 -07:00
// Get current epoch timestamp
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
// Concatenate clean username and timestamp to form filename
filename := data.Event.Sender().Username + "_" + timestamp + ".jpg"
imageFile, err := lib.ReplicateImageGeneration(options.Prompt, filename)
2023-12-27 10:52:24 -07:00
if err != nil {
2024-01-10 00:28:31 -07:00
lib.CancelCooldown(data.Event.User.ID.String(), "pic")
2023-12-27 21:48:55 -07:00
return errorResponse(err)
2023-12-27 10:52:24 -07:00
}
file := sendpart.File{
2024-01-19 14:33:37 -07:00
Name: filename,
2023-12-27 10:52:24 -07:00
Reader: imageFile,
}
return &api.InteractionResponseData{
2023-12-27 11:58:10 -07:00
Content: option.NewNullableString("Prompt: " + options.Prompt),
Files: []sendpart.File{file},
2023-12-27 10:52:24 -07:00
}
}
func (h *handler) cmdHS(ctx context.Context, data cmdroute.CommandData) *api.InteractionResponseData {
var options struct {
Arg string `discord:"nickname"`
}
if err := data.Options.Unmarshal(&options); err != nil {
return errorResponse(err)
}
2023-12-27 17:13:32 -07:00
2023-12-27 17:56:21 -07:00
user := lib.GetUserObject(*data.Event)
2023-12-27 10:52:24 -07:00
2023-12-26 18:39:21 -07:00
return &api.InteractionResponseData{
2023-12-27 17:56:21 -07:00
Content: option.NewNullableString(options.Arg + " was " + user.DisplayName() + "'s nickname in highschool!"),
2023-12-26 18:39:21 -07:00
}
}
func errorResponse(err error) *api.InteractionResponseData {
2024-01-09 21:05:23 -07:00
var content string
switch e := err.(type) {
case *net.OpError:
content = "**Network Error:** " + e.Error()
case *os.PathError:
content = "**File Error:** " + e.Error()
default:
content = "**Error:** " + err.Error()
}
2023-12-26 18:39:21 -07:00
return &api.InteractionResponseData{
2024-01-09 21:05:23 -07:00
Content: option.NewNullableString(content),
2023-12-26 18:39:21 -07:00
Flags: discord.EphemeralMessage,
2024-01-09 21:05:23 -07:00
AllowedMentions: &api.AllowedMentions{},
2023-12-26 18:39:21 -07:00
}
}