From 47bb7de2630b48b1803283e85c754f6c319d05dc Mon Sep 17 00:00:00 2001 From: Tanner Kvarfordt Date: Tue, 7 Nov 2023 00:16:35 -0700 Subject: [PATCH] Add DALL-E 3 Support to /render (#148) * Updated dependencies * Updated dalle2 opt variable names for consistency * Stubbed out handleDalle3SubCmd * Added dalle3 subcommand to /render * Updated render.go to no longer use deprecated constants --- go.mod | 14 ++-- go.sum | 31 ++++++++ kardbot/commands.go | 6 ++ kardbot/render.go | 169 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 203 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index ad1608b..6681cec 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/TannerKvarfordt/Kard-bot go 1.18 require ( - github.com/TannerKvarfordt/gopenai v0.1.4-beta + github.com/TannerKvarfordt/gopenai v0.1.5-beta github.com/TannerKvarfordt/hfapigo v1.3.1 github.com/TannerKvarfordt/imgflipgo v1.0.8 github.com/TannerKvarfordt/ubiquity v0.2.3 @@ -25,15 +25,15 @@ require ( github.com/fatih/structtag v1.2.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/gorilla/schema v1.2.0 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/gorilla/schema v1.2.1 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect golang.org/x/crypto v0.14.0 // indirect - golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sys v0.13.0 // indirect - google.golang.org/appengine v1.6.7 // indirect + golang.org/x/oauth2 v0.13.0 // indirect + golang.org/x/sys v0.14.0 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.31.0 // indirect ) diff --git a/go.sum b/go.sum index 89d3250..1cbcd14 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,8 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/TannerKvarfordt/gopenai v0.1.4-beta h1:Xkizsqt8hRXnfj2igDM9uFDjJMeFv/lr5QYmJC4wrW8= github.com/TannerKvarfordt/gopenai v0.1.4-beta/go.mod h1:X42mdDQIih6TLzctpf8Wp/cIGpHaMiImMWZ+BQz7nT0= +github.com/TannerKvarfordt/gopenai v0.1.5-beta h1:7vbtwn3fqDrPUsjCz2SP0s+ivIeNbFAP6ACDfklARhc= +github.com/TannerKvarfordt/gopenai v0.1.5-beta/go.mod h1:X42mdDQIih6TLzctpf8Wp/cIGpHaMiImMWZ+BQz7nT0= github.com/TannerKvarfordt/hfapigo v1.3.1 h1:Q0nGBLI3p6kbY/PHdjWFKsXN9HDulPQPSIlACtiZ0rE= github.com/TannerKvarfordt/hfapigo v1.3.1/go.mod h1:QvdRtU6q3i/VURfTwUu/akYX6HtdIGn25t62zFLoXX4= github.com/TannerKvarfordt/imgflipgo v1.0.8 h1:n6MPCZYGY51EL+H85UGV71LRiTiWIyoEprawYlu2Tr0= @@ -26,6 +28,7 @@ github.com/go-co-op/gocron v1.35.3/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISk github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -38,9 +41,13 @@ github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc= github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU= +github.com/gorilla/schema v1.2.1 h1:tjDxcmdb+siIqkTNoV+qRH2mjYdr2hHe5MKXbp61ziM= +github.com/gorilla/schema v1.2.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -76,39 +83,63 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/vartanbeno/go-reddit/v2 v2.0.1 h1:P6ITpf5YHjdy7DHZIbUIDn/iNAoGcEoDQnMa+L4vutw= github.com/vartanbeno/go-reddit/v2 v2.0.1/go.mod h1:758/S10hwZSLm43NPtwoNQdZFSg3sjB5745Mwjb0ANI= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= +golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= diff --git a/kardbot/commands.go b/kardbot/commands.go index 074e616..a1dadb6 100644 --- a/kardbot/commands.go +++ b/kardbot/commands.go @@ -365,6 +365,12 @@ func getCommands() []*discordgo.ApplicationCommand { Type: discordgo.ApplicationCommandOptionSubCommand, Options: dalle2Opts(), }, + { + Name: dalle3SubCmd, + Description: "Ask Open AI's DALL·E 3 model to generate an image from a prompt.", + Type: discordgo.ApplicationCommandOptionSubCommand, + Options: dalle3Opts(), + }, { Name: hfSubCmd, Description: "Ask a HuggingFace model to generate an image from a prompt.", diff --git a/kardbot/render.go b/kardbot/render.go index 7eabc12..c48a73f 100644 --- a/kardbot/render.go +++ b/kardbot/render.go @@ -36,8 +36,14 @@ const ( hfModelsFilepath = "config/hugging-face-models.json" dalle2SubCmd = "dalle2" - dalle2PromptOpt = "prompt" - dalle2SizeOpt = "size" + dalle2OptPrompt = "prompt" + dalle2OptSize = "size" + + dalle3SubCmd = "dalle3" + dalle3OptPrompt = "prompt" + dalle3OptSize = "size" + dalle3OptQuality = "quality" + dalle3OptStyle = "style" ) func handleRenderCmd(s *discordgo.Session, i *discordgo.InteractionCreate) { @@ -55,6 +61,8 @@ func handleRenderCmd(s *discordgo.Session, i *discordgo.InteractionCreate) { handleHfSubCmd(s, i, i.ApplicationCommandData().Options[0].Options) case dalle2SubCmd: handleDalle2SubCmd(s, i, i.ApplicationCommandData().Options[0].Options) + case dalle3SubCmd: + handleDalle3SubCmd(s, i, i.ApplicationCommandData().Options[0].Options) default: err = fmt.Errorf("reached unreachable case") log.Error(err) @@ -246,27 +254,27 @@ func dalle2Opts() []*discordgo.ApplicationCommandOption { return []*discordgo.ApplicationCommandOption{ { Type: discordgo.ApplicationCommandOptionString, - Name: dalle2PromptOpt, + Name: dalle2OptPrompt, Description: "A prompt to generate an image from. This can be very specific.", Required: true, }, { Type: discordgo.ApplicationCommandOptionString, - Name: dalle2SizeOpt, + Name: dalle2OptSize, Description: "The size of the image to be generated", Required: true, Choices: []*discordgo.ApplicationCommandOptionChoice{ { - Name: images.SmallImage, - Value: images.SmallImage, + Name: images.Dalle2SmallImage, + Value: images.Dalle2SmallImage, }, { - Name: images.MediumImage, - Value: images.MediumImage, + Name: images.Dalle2MediumImage, + Value: images.Dalle2MediumImage, }, { - Name: images.LargeImage, - Value: images.LargeImage, + Name: images.Dalle2LargeImage, + Value: images.Dalle2LargeImage, }, }, }, @@ -300,6 +308,8 @@ func handleDalle2SubCmd(s *discordgo.Session, i *discordgo.InteractionCreate, op contentFlags = []byte("Whoops, couldn't retrieve the details of your violation.") } interactionFollowUpEphemeralError(s, i, false, fmt.Errorf("sorry! Your prompt does not appear to conform to [Open AI's Usage Policies]()\n```JSON\n%s\n```", contentFlags)) + } else if strings.Contains(err.Error(), "safety system") { + interactionFollowUpEphemeralError(s, i, false, err) } else { log.Error(err) interactionFollowUpEphemeralError(s, i, true, err) @@ -333,3 +343,142 @@ func handleDalle2SubCmd(s *discordgo.Session, i *discordgo.InteractionCreate, op interactionFollowUpEphemeralError(s, i, true, err) } } + +func dalle3Opts() []*discordgo.ApplicationCommandOption { + return []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: dalle3OptPrompt, + Description: "A prompt to generate an image from. This can be very detailed.", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: dalle3OptSize, + Description: "The size of the image to be generated.", + Required: true, + Choices: []*discordgo.ApplicationCommandOptionChoice{ + { + Name: images.Dalle3SquareImage, + Value: images.Dalle3SquareImage, + }, + { + Name: images.Dalle3LandscapeImage, + Value: images.Dalle3LandscapeImage, + }, + { + Name: images.Dalle3PortraitImage, + Value: images.Dalle3PortraitImage, + }, + }, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: dalle3OptQuality, + Description: "The quality of the image that will be generated.", + Required: false, + Choices: []*discordgo.ApplicationCommandOptionChoice{ + { + Name: images.QualityStandard, + Value: images.QualityStandard, + }, + { + Name: images.QualityHD, + Value: images.QualityHD, + }, + }, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: dalle3OptStyle, + Description: "Vivid produces more hyper-real images, natural produces less hyper-real images.", + Required: false, + Choices: []*discordgo.ApplicationCommandOptionChoice{ + { + Name: images.StyleVivid, + Value: images.StyleVivid, + }, + { + Name: images.StyleNatural, + Value: images.StyleNatural, + }, + }, + }, + } +} + +func handleDalle3SubCmd(s *discordgo.Session, i *discordgo.InteractionCreate, opts []*discordgo.ApplicationCommandInteractionDataOption) { + mdata, err := getInteractionMetaData(i) + if err != nil { + log.Error(err) + interactionFollowUpEphemeralError(s, i, true, err) + return + } + + imageCount := uint64(1) + request := &images.CreationRequest{ + Model: images.ModelDalle3, + N: &imageCount, + ResponseFormat: images.ResponseFormatB64JSON, + User: mdata.AuthorID, + } + for _, opt := range opts { + switch opt.Name { + case dalle3OptPrompt: + request.Prompt = opt.StringValue() + case dalle3OptSize: + request.Size = opt.StringValue() + case dalle3OptQuality: + request.Quality = opt.StringValue() + case dalle3OptStyle: + request.Style = opt.StringValue() + default: + log.Warn("Unknown option:", opt.Name) + } + } + + resp, modr, err := images.MakeModeratedCreationRequest(request, nil) + if err != nil { + targetErr := moderations.NewModerationFlagError() + if errors.As(err, &targetErr) { + contentFlags, err := json.MarshalIndent(modr.Results[0].Categories, "", " ") + if err != nil { + log.Error(err) + contentFlags = []byte("Whoops, couldn't retrieve the details of your violation.") + } + interactionFollowUpEphemeralError(s, i, false, fmt.Errorf("sorry! Your prompt does not appear to conform to [Open AI's Usage Policies]()\n```JSON\n%s\n```", contentFlags)) + } else if strings.Contains(err.Error(), "safety system") { + interactionFollowUpEphemeralError(s, i, false, err) + } else { + log.Error(err) + interactionFollowUpEphemeralError(s, i, true, err) + } + return + } + + unbased, err := base64.StdEncoding.DecodeString(resp.Data[0].B64JSON) + if err != nil { + log.Error(err) + interactionFollowUpEphemeralError(s, i, true, err) + return + } + + content := fmt.Sprintf("Prompt:\n> %s\n\nRevised Prompt:\n> %s\n\nImage generated using [DALL·E 3]().", request.Prompt, resp.Data[0].RevisedPrompt) + _, err = s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{ + Content: &content, + Files: []*discordgo.File{ + { + Name: "Dalle-2-Output.png", + ContentType: "image/png", + Reader: bytes.NewReader(unbased), + }, + }, + AllowedMentions: &discordgo.MessageAllowedMentions{ + Users: []string{mdata.AuthorID}, + }, + }) + if err != nil { + log.Error(err) + interactionFollowUpEphemeralError(s, i, true, err) + } +}