mirror of
https://github.com/Fluffy-Bean/Lynxie.git
synced 2025-05-14 08:02:17 +00:00
Experimental image captions
This commit is contained in:
parent
72cad30983
commit
29bdf1b0ae
4 changed files with 177 additions and 6 deletions
|
@ -1,10 +1,20 @@
|
||||||
package img
|
package img
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.sr.ht/~sbinet/gg"
|
||||||
"github.com/Fluffy-Bean/lynxie/app"
|
"github.com/Fluffy-Bean/lynxie/app"
|
||||||
"github.com/Fluffy-Bean/lynxie/utils"
|
"github.com/Fluffy-Bean/lynxie/utils"
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
|
@ -16,10 +26,15 @@ var client = http.Client{
|
||||||
Timeout: 10 * time.Second,
|
Timeout: 10 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:embed resources/Impact.ttf
|
||||||
|
var resourceImpactFont []byte
|
||||||
|
|
||||||
func RegisterImgCommands(a *app.App) {
|
func RegisterImgCommands(a *app.App) {
|
||||||
a.RegisterCommand("saveable", registerSaveable(a))
|
a.RegisterCommand("saveable", registerSaveable(a))
|
||||||
|
|
||||||
a.RegisterCommandAlias("gif", "saveable")
|
a.RegisterCommandAlias("gif", "saveable")
|
||||||
|
|
||||||
|
a.RegisterCommand("caption", registerCaption(a))
|
||||||
|
a.RegisterCommandAlias("c", "caption")
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerSaveable(a *app.App) app.Callback {
|
func registerSaveable(a *app.App) app.Callback {
|
||||||
|
@ -67,7 +82,7 @@ func registerSaveable(a *app.App) app.Callback {
|
||||||
},
|
},
|
||||||
Files: []*discordgo.File{
|
Files: []*discordgo.File{
|
||||||
{
|
{
|
||||||
Name: "image.gif",
|
Name: "saveable.gif",
|
||||||
ContentType: "image/gif",
|
ContentType: "image/gif",
|
||||||
Reader: res.Body,
|
Reader: res.Body,
|
||||||
},
|
},
|
||||||
|
@ -79,6 +94,141 @@ func registerSaveable(a *app.App) app.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func registerCaption(a *app.App) app.Callback {
|
||||||
|
return func(h *app.Handler, args []string) app.Error {
|
||||||
|
fileEndpoint, err := getClosestImage(h)
|
||||||
|
if err != nil {
|
||||||
|
return app.Error{
|
||||||
|
Msg: "Could not get image",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, fileEndpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return app.Error{
|
||||||
|
Msg: "",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.ContentLength > maxFileSize {
|
||||||
|
return app.Error{
|
||||||
|
Msg: "Could not get image",
|
||||||
|
Err: errors.New("requested file is too big"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return app.Error{
|
||||||
|
Msg: "",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
|
|
||||||
|
buff, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return app.Error{
|
||||||
|
Msg: "Failed to read image",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var img image.Image
|
||||||
|
switch http.DetectContentType(buff) {
|
||||||
|
case "image/png":
|
||||||
|
img, err = png.Decode(bytes.NewReader(buff))
|
||||||
|
if err != nil {
|
||||||
|
return app.Error{
|
||||||
|
Msg: "Failed to decode PNG",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case "image/jpeg":
|
||||||
|
img, err = jpeg.Decode(bytes.NewReader(buff))
|
||||||
|
if err != nil {
|
||||||
|
return app.Error{
|
||||||
|
Msg: "Failed to decode JPEG",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return app.Error{
|
||||||
|
Msg: "Unknown or unsupported image format",
|
||||||
|
Err: errors.New("Unknown or unsupported image format " + http.DetectContentType(buff)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fontSize := float64(img.Bounds().Dx() / 25)
|
||||||
|
if fontSize < 16 {
|
||||||
|
fontSize = 16
|
||||||
|
} else if fontSize > 50 {
|
||||||
|
fontSize = 50
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas := gg.NewContext(img.Bounds().Dx(), img.Bounds().Dy()+200)
|
||||||
|
err = canvas.LoadFontFaceFromBytes(resourceImpactFont, fontSize)
|
||||||
|
if err != nil {
|
||||||
|
return app.Error{
|
||||||
|
Msg: "Failed to load font",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.SetColor(color.RGBA{R: 255, G: 255, B: 255, A: 255})
|
||||||
|
canvas.Clear()
|
||||||
|
|
||||||
|
canvas.SetColor(color.RGBA{R: 0, G: 0, B: 0, A: 255})
|
||||||
|
canvas.DrawStringWrapped(
|
||||||
|
strings.Join(args, " "),
|
||||||
|
float64(img.Bounds().Dx()/2),
|
||||||
|
100,
|
||||||
|
0.5,
|
||||||
|
0.5,
|
||||||
|
float64(img.Bounds().Dx()),
|
||||||
|
1.5,
|
||||||
|
gg.AlignCenter,
|
||||||
|
)
|
||||||
|
|
||||||
|
canvas.DrawImage(img, 0, 200)
|
||||||
|
|
||||||
|
var export bytes.Buffer
|
||||||
|
err = canvas.EncodeJPG(bufio.NewWriter(&export), &jpeg.Options{
|
||||||
|
Quality: 100,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return app.Error{
|
||||||
|
Msg: "Failed to encode JPEG",
|
||||||
|
Err: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Session.ChannelMessageSendComplex(h.Message.ChannelID, &discordgo.MessageSend{
|
||||||
|
Embed: &discordgo.MessageEmbed{
|
||||||
|
Title: "Caption",
|
||||||
|
Image: &discordgo.MessageEmbedImage{
|
||||||
|
URL: "attachment://caption.jpeg",
|
||||||
|
},
|
||||||
|
Color: utils.ColorFromRGB(255, 255, 255),
|
||||||
|
},
|
||||||
|
Files: []*discordgo.File{
|
||||||
|
{
|
||||||
|
Name: "caption.jpeg",
|
||||||
|
ContentType: "image/jpeg",
|
||||||
|
Reader: bytes.NewReader(export.Bytes()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Reference: h.Reference,
|
||||||
|
})
|
||||||
|
|
||||||
|
return app.Error{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getClosestImage(h *app.Handler) (string, error) {
|
func getClosestImage(h *app.Handler) (string, error) {
|
||||||
// Get message attachments
|
// Get message attachments
|
||||||
if len(h.Message.Attachments) >= 1 {
|
if len(h.Message.Attachments) >= 1 {
|
||||||
|
|
BIN
commands/img/resources/Impact.ttf
Normal file
BIN
commands/img/resources/Impact.ttf
Normal file
Binary file not shown.
10
go.mod
10
go.mod
|
@ -2,10 +2,18 @@ module github.com/Fluffy-Bean/lynxie
|
||||||
|
|
||||||
go 1.24.0
|
go 1.24.0
|
||||||
|
|
||||||
require github.com/bwmarrin/discordgo v0.28.1
|
require (
|
||||||
|
git.sr.ht/~sbinet/gg v0.6.0
|
||||||
|
github.com/bwmarrin/discordgo v0.28.1
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/campoy/embedmd v1.0.0 // indirect
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3 // indirect
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
golang.org/x/crypto v0.36.0 // indirect
|
golang.org/x/crypto v0.36.0 // indirect
|
||||||
|
golang.org/x/image v0.21.0 // indirect
|
||||||
golang.org/x/sys v0.31.0 // indirect
|
golang.org/x/sys v0.31.0 // indirect
|
||||||
|
golang.org/x/text v0.23.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
19
go.sum
19
go.sum
|
@ -1,18 +1,31 @@
|
||||||
|
git.sr.ht/~sbinet/cmpimg v0.1.0 h1:E0zPRk2muWuCqSKSVZIWsgtU9pjsw3eKHi8VmQeScxo=
|
||||||
|
git.sr.ht/~sbinet/cmpimg v0.1.0/go.mod h1:FU12psLbF4TfNXkKH2ZZQ29crIqoiqTZmeQ7dkp/pxE=
|
||||||
|
git.sr.ht/~sbinet/gg v0.6.0 h1:RIzgkizAk+9r7uPzf/VfbJHBMKUr0F5hRFxTUGMnt38=
|
||||||
|
git.sr.ht/~sbinet/gg v0.6.0/go.mod h1:uucygbfC9wVPQIfrmwM2et0imr8L7KQWywX0xpFMm94=
|
||||||
github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
|
github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
|
||||||
github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
github.com/campoy/embedmd v1.0.0 h1:V4kI2qTJJLf4J29RzI/MAt2c3Bl4dQSYPuflzwFH2hY=
|
||||||
|
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||||
|
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||||
|
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||||
|
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
|
||||||
|
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
|
Loading…
Add table
Reference in a new issue