Compare commits
9 Commits
1a1247da7a
...
fb264b5a32
Author | SHA1 | Date |
---|---|---|
Charlotte | fb264b5a32 | |
Tulir Asokan | 333567f481 | |
Tulir Asokan | 125d057e44 | |
Tulir Asokan | e1038e7d1e | |
Tulir Asokan | 850668a9f6 | |
Tulir Asokan | 804014f3b4 | |
Tulir Asokan | 5da539ad84 | |
Tulir Asokan | 6332613e13 | |
Charlotte 🦝 Delenk | 2fe1ea6c7f |
|
@ -0,0 +1,10 @@
|
||||||
|
build giphy proxy docker:
|
||||||
|
image: docker:stable
|
||||||
|
stage: build
|
||||||
|
before_script:
|
||||||
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
script:
|
||||||
|
- docker build -t $CI_REGISTRY_IMAGE/giphyproxy:latest giphyproxy
|
||||||
|
- docker push $CI_REGISTRY_IMAGE/giphyproxy:latest
|
||||||
|
only:
|
||||||
|
- master
|
|
@ -0,0 +1,16 @@
|
||||||
|
FROM golang:1-alpine AS builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache ca-certificates
|
||||||
|
WORKDIR /build/giphyproxy
|
||||||
|
COPY . /build/giphyproxy
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
RUN go build -o /usr/bin/giphyproxy
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||||
|
COPY --from=builder /usr/bin/giphyproxy /usr/bin/giphyproxy
|
||||||
|
|
||||||
|
VOLUME /data
|
||||||
|
WORKDIR /data
|
||||||
|
CMD ["/usr/bin/giphyproxy"]
|
|
@ -0,0 +1,17 @@
|
||||||
|
# The server name to use for the custom mxc:// URIs.
|
||||||
|
# This server name will effectively be a real Matrix server, it just won't implement anything other than media.
|
||||||
|
# You must either set up .well-known delegation from this domain to this program, or proxy the domain directly to this program.
|
||||||
|
server_name: giphy.example.com
|
||||||
|
# Optionally a custom .well-known response. This defaults to `server_name:443` if empty.
|
||||||
|
well_known_response:
|
||||||
|
# The proxy will use MSC3860/MSC3916 media download redirects if the requester supports it.
|
||||||
|
# Optionally, you can force redirects and not allow proxying at all by setting this to false.
|
||||||
|
allow_proxy: false
|
||||||
|
# Matrix server signing key to make the federation tester pass, same format as synapse's .signing.key file.
|
||||||
|
# You can generate one using `giphyproxy -generate-key`.
|
||||||
|
server_key: CHANGE ME
|
||||||
|
|
||||||
|
# Hostname where the proxy should listen on
|
||||||
|
hostname: 0.0.0.0
|
||||||
|
# Port where the proxy should listen on
|
||||||
|
port: 8008
|
|
@ -0,0 +1,24 @@
|
||||||
|
module go.mau.fi/stickerpicker/giphyproxy
|
||||||
|
|
||||||
|
go 1.22.3
|
||||||
|
|
||||||
|
require (
|
||||||
|
go.mau.fi/util v0.5.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
maunium.net/go/mautrix v0.19.0-beta.1.0.20240619092812-451658374280
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gorilla/mux v1.8.0 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
|
github.com/rs/zerolog v1.33.0 // indirect
|
||||||
|
github.com/tidwall/gjson v1.17.1 // indirect
|
||||||
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
|
github.com/tidwall/sjson v1.2.5 // indirect
|
||||||
|
golang.org/x/crypto v0.24.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
|
||||||
|
golang.org/x/net v0.26.0 // indirect
|
||||||
|
golang.org/x/sys v0.21.0 // indirect
|
||||||
|
)
|
|
@ -0,0 +1,47 @@
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||||
|
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||||
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||||
|
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
|
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||||
|
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||||
|
go.mau.fi/util v0.5.0 h1:8yELAl+1CDRrwGe9NUmREgVclSs26Z68pTWePHVxuDo=
|
||||||
|
go.mau.fi/util v0.5.0/go.mod h1:DsJzUrJAG53lCZnnYvq9/mOyLuPScWwYhvETiTrpdP4=
|
||||||
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
|
||||||
|
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
|
||||||
|
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||||
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||||
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
maunium.net/go/mautrix v0.19.0-beta.1.0.20240619092812-451658374280 h1:+EHJF8h7obPow7kDnsmGoWN+bTCjHGxCKaH99MldZUI=
|
||||||
|
maunium.net/go/mautrix v0.19.0-beta.1.0.20240619092812-451658374280/go.mod h1:cxv1w6+syudmEpOewHYIQT9yO7TM5UOWmf6xEBVI4H4=
|
|
@ -0,0 +1,72 @@
|
||||||
|
// maunium-stickerpicker - A fast and simple Matrix sticker picker widget.
|
||||||
|
// Copyright (C) 2024 Tulir Asokan
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Affero General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Affero General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Affero General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.mau.fi/util/exerrors"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"maunium.net/go/mautrix/federation"
|
||||||
|
"maunium.net/go/mautrix/mediaproxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
mediaproxy.BasicConfig `yaml:",inline"`
|
||||||
|
mediaproxy.ServerConfig `yaml:",inline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var configPath = flag.String("config", "config.yaml", "config file path")
|
||||||
|
var generateServerKey = flag.Bool("generate-key", false, "generate a new server key and exit")
|
||||||
|
|
||||||
|
var giphyIDRegex = regexp.MustCompile(`^[a-zA-Z0-9-_]+$`)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
if *generateServerKey {
|
||||||
|
fmt.Println(federation.GenerateSigningKey().SynapseString())
|
||||||
|
} else {
|
||||||
|
cfgFile := exerrors.Must(os.ReadFile(*configPath))
|
||||||
|
var cfg Config
|
||||||
|
exerrors.PanicIfNotNil(yaml.Unmarshal(cfgFile, &cfg))
|
||||||
|
mp := exerrors.Must(mediaproxy.NewFromConfig(cfg.BasicConfig, getMedia))
|
||||||
|
mp.KeyServer.Version.Name = "maunium-stickerpicker giphy proxy"
|
||||||
|
mp.ForceProxyLegacyFederation = true
|
||||||
|
exerrors.PanicIfNotNil(mp.Listen(cfg.ServerConfig))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMedia(_ context.Context, id string) (response mediaproxy.GetMediaResponse, err error) {
|
||||||
|
// This is not related to giphy, but random cats are always fun
|
||||||
|
if id == "cat" {
|
||||||
|
return &mediaproxy.GetMediaResponseURL{
|
||||||
|
URL: "https://cataas.com/cat",
|
||||||
|
ExpiresAt: time.Now(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
if !giphyIDRegex.MatchString(id) {
|
||||||
|
return nil, mediaproxy.ErrInvalidMediaIDSyntax
|
||||||
|
}
|
||||||
|
return &mediaproxy.GetMediaResponseURL{
|
||||||
|
URL: fmt.Sprintf("https://i.giphy.com/%s.webp", id),
|
||||||
|
}, nil
|
||||||
|
}
|
|
@ -68,8 +68,6 @@ async def load_config(path: str) -> None:
|
||||||
"homeserver": homeserver_url,
|
"homeserver": homeserver_url,
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
"access_token": access_token,
|
"access_token": access_token,
|
||||||
"giphy_api_key": giphy_api_key,
|
|
||||||
"giphy_mxc_prefix": giphy_mxc_prefix,
|
|
||||||
}, config_file)
|
}, config_file)
|
||||||
print(f"Wrote config to {path}")
|
print(f"Wrote config to {path}")
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ def add_meta(document: Document, info: matrix.StickerInfo, pack: StickerSetFull)
|
||||||
if isinstance(attr, DocumentAttributeSticker):
|
if isinstance(attr, DocumentAttributeSticker):
|
||||||
info["body"] = attr.alt
|
info["body"] = attr.alt
|
||||||
info["id"] = f"tg-{document.id}"
|
info["id"] = f"tg-{document.id}"
|
||||||
info["net.maunium.telegram.sticker"] = {
|
info["info"]["net.maunium.telegram.sticker"] = {
|
||||||
"pack": {
|
"pack": {
|
||||||
"id": str(pack.set.id),
|
"id": str(pack.set.id),
|
||||||
"short_name": pack.set.short_name,
|
"short_name": pack.set.short_name,
|
||||||
|
@ -56,10 +56,6 @@ def add_meta(document: Document, info: matrix.StickerInfo, pack: StickerSetFull)
|
||||||
|
|
||||||
|
|
||||||
async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir: str) -> None:
|
async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir: str) -> None:
|
||||||
if pack.set.animated:
|
|
||||||
print("Animated stickerpacks are currently not supported")
|
|
||||||
return
|
|
||||||
|
|
||||||
pack_path = os.path.join(output_dir, f"{pack.set.short_name}.json")
|
pack_path = os.path.join(output_dir, f"{pack.set.short_name}.json")
|
||||||
try:
|
try:
|
||||||
os.mkdir(os.path.dirname(pack_path))
|
os.mkdir(os.path.dirname(pack_path))
|
||||||
|
@ -73,7 +69,7 @@ async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir
|
||||||
try:
|
try:
|
||||||
with util.open_utf8(pack_path) as pack_file:
|
with util.open_utf8(pack_path) as pack_file:
|
||||||
existing_pack = json.load(pack_file)
|
existing_pack = json.load(pack_file)
|
||||||
already_uploaded = {int(sticker["net.maunium.telegram.sticker"]["id"]): sticker
|
already_uploaded = {int(sticker["info"]["net.maunium.telegram.sticker"]["id"]): sticker
|
||||||
for sticker in existing_pack["stickers"]}
|
for sticker in existing_pack["stickers"]}
|
||||||
print(f"Found {len(already_uploaded)} already reuploaded stickers")
|
print(f"Found {len(already_uploaded)} already reuploaded stickers")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
|
@ -97,7 +93,7 @@ async def reupload_pack(client: TelegramClient, pack: StickerSetFull, output_dir
|
||||||
# If there was no sticker metadata, use the first emoji we find
|
# If there was no sticker metadata, use the first emoji we find
|
||||||
if doc["body"] == "":
|
if doc["body"] == "":
|
||||||
doc["body"] = sticker.emoticon
|
doc["body"] = sticker.emoticon
|
||||||
doc["net.maunium.telegram.sticker"]["emoticons"].append(sticker.emoticon)
|
doc["info"]["net.maunium.telegram.sticker"]["emoticons"].append(sticker.emoticon)
|
||||||
|
|
||||||
with util.open_utf8(pack_path, "w") as pack_file:
|
with util.open_utf8(pack_path, "w") as pack_file:
|
||||||
json.dump({
|
json.dump({
|
||||||
|
|
|
@ -68,9 +68,9 @@ export class GiphySearchTab extends Component {
|
||||||
widgetAPI.sendSticker({
|
widgetAPI.sendSticker({
|
||||||
"body": gif.title,
|
"body": gif.title,
|
||||||
"info": {
|
"info": {
|
||||||
"h": gif.images.original.height,
|
"h": +gif.images.original.height,
|
||||||
"w": gif.images.original.width,
|
"w": +gif.images.original.width,
|
||||||
"size": gif.images.original.size,
|
"size": +gif.images.original.size,
|
||||||
"mimetype": "image/webp",
|
"mimetype": "image/webp",
|
||||||
},
|
},
|
||||||
"msgtype": "m.image",
|
"msgtype": "m.image",
|
||||||
|
|
|
@ -47,6 +47,11 @@ window.onmessage = event => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendSticker(content) {
|
export function sendSticker(content) {
|
||||||
|
if (content["info"]["net.maunium.telegram.sticker"] === undefined) {
|
||||||
|
// Old sticker, move the property to its new location
|
||||||
|
content["info"]["net.maunium.telegram.sticker"] = content["net.maunium.telegram.sticker"];
|
||||||
|
delete content["net.maunium.telegram.sticker"];
|
||||||
|
}
|
||||||
const data = {
|
const data = {
|
||||||
content: { ...content },
|
content: { ...content },
|
||||||
// `name` is for Element Web (and also the spec)
|
// `name` is for Element Web (and also the spec)
|
||||||
|
|
Loading…
Reference in New Issue