Add discord data tools

This commit is contained in:
Melon 2022-03-28 21:22:34 +01:00
parent 98541ec502
commit a62611c6fa
Signed by: melon
GPG Key ID: CAE347AC84024FAE
18 changed files with 454 additions and 12 deletions

View File

@ -11,7 +11,7 @@ COMP_BIN := go
build:
mkdir -p dist/
${COMP_BIN} build -o "${BIN}" -ldflags="${LD_FLAGS}" ./cmd/gitea-tools
${COMP_BIN} build -o "${BIN}" -ldflags="${LD_FLAGS}" ./cmd/melon-tools
run:
make build

View File

@ -1,6 +1,7 @@
package main
import (
"code.mrmelon54.xyz/sean/melon-tools/module/discord"
"code.mrmelon54.xyz/sean/melon-tools/module/gitea"
"code.mrmelon54.xyz/sean/melon-tools/utils"
"encoding/gob"
@ -16,8 +17,9 @@ import (
)
var (
modules = map[string]utils.IModule{
"/gitea": gitea.New(),
modules = []utils.IModule{
gitea.New(),
discord.New(),
}
sessionStore = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
)
@ -36,15 +38,17 @@ func main() {
rw.Header().Set("Content-Type", "text/html")
rw.WriteHeader(http.StatusOK)
_, _ = rw.Write([]byte("Melon Tools<br/>\n"))
_, _ = rw.Write([]byte("<a href='/gitea'>Gitea</a>\n"))
for _, v := range modules {
_, _ = rw.Write([]byte(fmt.Sprintf("<a href=\"%s\">%s</a><br/>\n", v.GetEndpoint(), v.GetName())))
}
})
gob.Register(uuid.UUID{})
for k, v := range modules {
router.HandleFunc(k, func(rw http.ResponseWriter, req *http.Request) {
http.Redirect(rw, req, k+"/", http.StatusTemporaryRedirect)
for _, v := range modules {
router.HandleFunc(v.GetEndpoint(), func(rw http.ResponseWriter, req *http.Request) {
http.Redirect(rw, req, v.GetEndpoint()+"/", http.StatusTemporaryRedirect)
})
v.SetupModule(router.PathPrefix(k).Subrouter(), stateManager.sessionWrapper)
v.SetupModule(router.PathPrefix(v.GetEndpoint()).Subrouter(), stateManager.sessionWrapper)
}
s := &http.Server{

4
go.mod
View File

@ -12,10 +12,14 @@ require (
)
require (
github.com/bwmarrin/discordgo v0.24.0 // indirect
github.com/golang/protobuf v1.4.2 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/go-version v1.2.1 // indirect
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
google.golang.org/appengine v1.6.6 // indirect
google.golang.org/protobuf v1.25.0 // indirect
)

10
go.sum
View File

@ -36,6 +36,8 @@ code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMV
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/bwmarrin/discordgo v0.24.0 h1:Gw4MYxqHdvhO99A3nXnSLy97z5pmIKHZVJ1JY5ZDPqY=
github.com/bwmarrin/discordgo v0.24.0/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
@ -106,6 +108,8 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@ -139,6 +143,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -195,6 +201,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -237,8 +244,11 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@ -0,0 +1,17 @@
package discord
import (
"bytes"
"github.com/bwmarrin/discordgo"
"image"
)
func getGuildIcon(s *discordgo.Session, guild *discordgo.UserGuild) (image.Image, error) {
body, err := s.RequestWithBucketID("GET", discordgo.EndpointGuildIcon(guild.ID, guild.Icon), nil, discordgo.EndpointGuildIcon(guild.ID, ""))
if err != nil {
return nil, nil
}
img, _, err := image.Decode(bytes.NewReader(body))
return img, err
}

235
module/discord/main.go Normal file
View File

@ -0,0 +1,235 @@
package discord
import (
"bytes"
"code.mrmelon54.xyz/sean/melon-tools/utils"
"context"
"embed"
_ "embed"
"fmt"
"github.com/bwmarrin/discordgo"
"github.com/google/uuid"
"github.com/gorilla/mux"
"golang.org/x/oauth2"
"html/template"
"image"
"image/png"
"net/http"
"os"
)
var (
//go:embed pages/index.go.html
indexTemplate string
//go:embed pages/assets/icon
iconFiles embed.FS
)
type Module struct {
sessionWrapper func(cb func(http.ResponseWriter, *http.Request, *utils.State)) func(rw http.ResponseWriter, req *http.Request)
oauthClient *oauth2.Config
}
type discordKeyType int
const (
KeyOauthClient = discordKeyType(iota)
KeyUser
KeyState
KeyAccessToken
KeyRefreshToken
)
func New() *Module {
return &Module{}
}
func (m *Module) GetName() string { return "Discord" }
func (m *Module) GetEndpoint() string { return "/discord" }
func (m *Module) SetupModule(router *mux.Router, f func(cb func(http.ResponseWriter, *http.Request, *utils.State)) func(rw http.ResponseWriter, req *http.Request)) {
m.sessionWrapper = f
m.oauthClient = &oauth2.Config{
ClientID: os.Getenv("DISCORD_CLIENT_ID"),
ClientSecret: os.Getenv("DISCORD_CLIENT_SECRET"),
Scopes: []string{"identify", "guilds", "connections", "email"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://discord.com/oauth2/authorize",
TokenURL: "https://discord.com/api/oauth2/token",
},
RedirectURL: os.Getenv("DISCORD_REDIRECT_URL"),
}
router.HandleFunc("/", m.getClient(m.homepage))
router.HandleFunc("/login", m.sessionWrapper(m.loginPage))
router.HandleFunc("/user/avatar/{userId}/{avatarId}", m.getClient(m.userAvatar))
router.HandleFunc("/guild/icon/{guildId}/{iconId}", m.getClient(m.guildIcon))
router.PathPrefix("/assets/icon/{name}.svg").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
vars := mux.Vars(req)
b, err := iconFiles.ReadFile("pages/assets/icon/" + vars["name"] + ".svg")
if err != nil {
rw.WriteHeader(http.StatusNotFound)
} else {
rw.Header().Set("Content-Type", "image/svg+xml")
rw.WriteHeader(http.StatusOK)
_, _ = rw.Write(b)
}
})
}
func (m *Module) getClient(cb func(http.ResponseWriter, *http.Request, *utils.State, *discordgo.Session)) func(rw http.ResponseWriter, req *http.Request) {
return m.sessionWrapper(func(rw http.ResponseWriter, req *http.Request, state *utils.State) {
if v, ok := utils.GetStateValue[*discordgo.Session](state, KeyOauthClient); ok {
cb(rw, req, state, v)
return
}
http.Redirect(rw, req, "/discord/login", http.StatusTemporaryRedirect)
})
}
func (m *Module) homepage(rw http.ResponseWriter, req *http.Request, state *utils.State, discordClient *discordgo.Session) {
myUser, err := discordClient.User("@me")
if err != nil {
state.Del(KeyOauthClient)
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
myGuilds, err := discordClient.UserGuilds(100, "", "")
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
myConns, err := discordClient.UserConnections()
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
tmp, err := template.New("homepage").Funcs(template.FuncMap{
"checkFlag": func(a discordgo.UserFlags, b int) bool { return (int(a) & b) != 0 },
"connectedLink": connectedLinkFunc,
}).Parse(indexTemplate)
if err != nil {
fmt.Println("Template parse error:", err)
return
}
guildIcons := make([]template.HTMLAttr, len(myGuilds))
for i, j := range myGuilds {
guildIcons[i] = template.HTMLAttr(fmt.Sprintf("src=\"/discord/guild/icon/%s/%s\"", j.ID, j.Icon))
}
err = tmp.Execute(rw, struct {
User *discordgo.User
Avatar template.HTMLAttr
Guilds []*discordgo.UserGuild
GuildIcons []template.HTMLAttr
Connections []*discordgo.UserConnection
}{
User: myUser,
Avatar: template.HTMLAttr(fmt.Sprintf("src=\"/discord/user/avatar/%s/%s\"", myUser.ID, myUser.Avatar)),
Guilds: myGuilds,
GuildIcons: guildIcons,
Connections: myConns,
})
if err != nil {
fmt.Println("Template execute error:", err)
return
}
}
func (m *Module) loginPage(rw http.ResponseWriter, req *http.Request, state *utils.State) {
if myUser, ok := utils.GetStateValue[*string](state, KeyUser); ok {
if myUser != nil {
http.Redirect(rw, req, "/discord", http.StatusTemporaryRedirect)
return
}
}
if flowState, ok := utils.GetStateValue[uuid.UUID](state, KeyState); ok {
q := req.URL.Query()
if q.Has("code") && q.Has("state") {
if q.Get("state") == flowState.String() {
exchange, err := m.oauthClient.Exchange(context.Background(), q.Get("code"))
if err != nil {
fmt.Println("Exchange token error:", err)
return
}
c, err := discordgo.New("Bearer " + exchange.AccessToken)
if err != nil {
fmt.Println("Create client error:", err)
return
}
state.Put(KeyOauthClient, c)
state.Put(KeyAccessToken, exchange.AccessToken)
state.Put(KeyRefreshToken, exchange.RefreshToken)
http.Redirect(rw, req, "/discord", http.StatusTemporaryRedirect)
return
}
http.Error(rw, "OAuth flow state doesn't match\n", http.StatusBadRequest)
return
}
}
flowState := uuid.New()
state.Put(KeyState, flowState)
http.Redirect(rw, req, m.oauthClient.AuthCodeURL(flowState.String(), oauth2.AccessTypeOffline), http.StatusTemporaryRedirect)
}
func (m *Module) userAvatar(rw http.ResponseWriter, req *http.Request, state *utils.State, discordClient *discordgo.Session) {
vars := mux.Vars(req)
body, err := discordClient.RequestWithBucketID("GET", discordgo.EndpointUserAvatar(vars["userId"], vars["avatarId"]), nil, discordgo.EndpointUserAvatar("", ""))
if err != nil {
return
}
rw.Header().Set("Content-Type", "image/png")
rw.WriteHeader(200)
myAvatar, _, err := image.Decode(bytes.NewReader(body))
err = png.Encode(rw, myAvatar)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
}
func (m *Module) guildIcon(rw http.ResponseWriter, req *http.Request, state *utils.State, discordClient *discordgo.Session) {
vars := mux.Vars(req)
body, err := discordClient.RequestWithBucketID("GET", discordgo.EndpointGuildIcon(vars["guildId"], vars["iconId"]), nil, discordgo.EndpointGuildIcon(vars["guildId"], ""))
if err != nil {
return
}
rw.Header().Set("Content-Type", "image/png")
rw.WriteHeader(http.StatusOK)
myAvatar, _, err := image.Decode(bytes.NewReader(body))
err = png.Encode(rw, myAvatar)
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
}
}
func connectedLinkFunc(a *discordgo.UserConnection) string {
switch a.Type {
case "github":
return "https://github.com/" + a.Name
case "reddit":
return "https://www.reddit.com/u/" + a.Name
case "spotify":
return "https://open.spotify.com/user/" + a.ID
case "steam":
return "https://steamcommunity.com/profiles/" + a.ID
case "twitch":
return "https://www.twitch.tv/" + a.Name
case "twitter":
return "https://twitter.com/" + a.Name
case "xbox":
return "javascript:alert('No link to XBox profiles')"
case "youtube":
return "https://www.youtube.com/channel/" + a.ID
}
return "javascript:alert('Unknown profile type')"
}

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0001 2.00001C9.62368 1.99759 7.32411 2.84152 5.51345 4.38057C3.70279 5.91961 2.49939 8.05316 2.1189 10.3989C1.73842 12.7446 2.20572 15.1491 3.43706 17.1816C4.66841 19.2141 6.58331 20.7416 8.8386 21.4905C9.34198 21.5826 9.52 21.2695 9.52 21.0055V19.3051C6.72688 19.9128 6.13756 17.9607 6.13756 17.9607C5.95226 17.3557 5.5586 16.8359 5.02645 16.4936C4.12406 15.8797 5.10012 15.8797 5.10012 15.8797C5.41644 15.9244 5.71846 16.0404 5.98328 16.2191C6.24811 16.3978 6.46881 16.6344 6.62866 16.911C6.76449 17.1573 6.94761 17.3743 7.16752 17.5497C7.38743 17.725 7.6398 17.8552 7.91015 17.9328C8.18049 18.0103 8.4635 18.0337 8.74292 18.0017C9.02234 17.9696 9.29268 17.8827 9.53842 17.7459C9.57695 17.2409 9.79466 16.7662 10.1523 16.4076C7.93007 16.1559 5.59735 15.2965 5.59735 11.4966C5.58209 10.504 5.94915 9.54349 6.62252 8.814C6.32262 7.95091 6.35767 7.00668 6.72074 6.16821C6.72074 6.16821 7.56175 5.8981 9.47089 7.19338C11.1103 6.74326 12.8407 6.74326 14.4801 7.19338C16.3892 5.8981 17.2241 6.16821 17.2241 6.16821C17.5922 6.99781 17.6361 7.93527 17.3469 8.79558C18.0203 9.52507 18.3873 10.4856 18.3721 11.4782C18.3721 15.3211 16.0332 16.1621 13.8048 16.3892C14.0438 16.6294 14.2283 16.9181 14.3459 17.2359C14.4634 17.5536 14.5113 17.8929 14.4862 18.2308V20.9748C14.4862 21.3002 14.6643 21.5519 15.1738 21.4537C17.4034 20.685 19.2896 19.1531 20.4992 17.1286C21.7088 15.104 22.164 12.7171 21.7842 10.3895C21.4045 8.06189 20.2143 5.94338 18.424 4.40825C16.6336 2.87311 14.3583 2.02013 12.0001 2.00001Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.6562 12.0937C18.6273 11.2925 17.9576 10.6646 17.1562 10.6874C16.7947 10.7008 16.4503 10.8452 16.1874 11.0937C15.0534 10.3194 13.7167 9.89553 12.3437 9.87491L12.9999 6.74991L15.1249 7.18741C15.1745 7.67792 15.5742 8.05911 16.0665 8.08541C16.5588 8.11172 16.9968 7.77529 17.0984 7.29285C17.2 6.81041 16.9348 6.32593 16.4736 6.15151C16.0125 5.97709 15.4931 6.16477 15.2499 6.59366L12.8124 6.09366C12.7294 6.07532 12.6424 6.09161 12.5717 6.13879C12.5009 6.18597 12.4524 6.25995 12.4374 6.34366L11.6874 9.81241C10.295 9.83285 8.93822 10.2562 7.78117 11.0312C7.40378 10.6613 6.85593 10.5243 6.34889 10.6731C5.84184 10.8218 5.45482 11.2331 5.33704 11.7482C5.21927 12.2633 5.38915 12.8019 5.78117 13.1562C5.9047 13.2574 6.04136 13.3415 6.18742 13.4062V13.8437C6.18742 16.0937 8.81242 17.9062 12.0312 17.9062C15.2499 17.9062 17.8749 16.0937 17.8749 13.8437V13.4062C18.3572 13.146 18.6574 12.6417 18.6562 12.0937Z" fill="white"/>
<path d="M12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2ZM18.6562 12.0938C18.6274 11.2926 17.9576 10.6647 17.1562 10.6875C16.7947 10.7009 16.4504 10.8453 16.1875 11.0937C15.0534 10.3194 13.7168 9.89562 12.3438 9.875L13 6.75L15.125 7.1875C15.1746 7.67801 15.5743 8.0592 16.0666 8.0855C16.5589 8.1118 16.9969 7.77537 17.0985 7.29294C17.2 6.8105 16.9348 6.32602 16.4737 6.1516C16.0126 5.97718 15.4931 6.16486 15.25 6.59375L12.8125 6.09375C12.7295 6.07541 12.6425 6.0917 12.5718 6.13888C12.501 6.18605 12.4525 6.26004 12.4375 6.34375L11.6875 9.8125C10.295 9.83294 8.93829 10.2562 7.78125 11.0312C7.40385 10.6614 6.85601 10.5244 6.34897 10.6732C5.84192 10.8219 5.45489 11.2332 5.33712 11.7483C5.21934 12.2634 5.38922 12.8019 5.78125 13.1562C5.90478 13.2575 6.04143 13.3416 6.1875 13.4062V13.8438C6.1875 16.0937 8.8125 17.9062 12.0312 17.9062C15.25 17.9062 17.875 16.0937 17.875 13.8438V13.4062C18.3573 13.1461 18.6575 12.6418 18.6562 12.0938ZM8.65625 13.0937C8.65625 12.5415 9.10396 12.0938 9.65625 12.0938C10.2085 12.0938 10.6562 12.5415 10.6562 13.0937C10.6562 13.646 10.2085 14.0937 9.65625 14.0937C9.39001 14.0971 9.13368 13.9929 8.9454 13.8046C8.75712 13.6163 8.65286 13.36 8.65625 13.0937ZM14.4688 15.875C13.758 16.4015 12.8823 16.6564 12 16.5937C11.1126 16.6189 10.2427 16.3436 9.53125 15.8125C9.44565 15.6975 9.45905 15.5368 9.5625 15.4375C9.65758 15.3429 9.81117 15.3429 9.90625 15.4375C10.5135 15.8764 11.2515 16.0967 12 16.0625C12.7428 16.0882 13.475 15.8806 14.0938 15.4687C14.2107 15.3687 14.3831 15.3687 14.5 15.4687C14.625 15.5625 14.5625 15.7812 14.4688 15.875ZM13.2812 13.0937C13.2812 12.5415 13.729 12.0938 14.2812 12.0938C14.8335 12.0938 15.2813 12.5415 15.2813 13.0937C15.2813 13.646 14.8335 14.0937 14.2812 14.0937C14.015 14.0971 13.7587 13.9929 13.5704 13.8046C13.3821 13.6163 13.2779 13.36 13.2812 13.0937Z" fill="#FF4500"/>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 9.34784 20.9464 6.8043 19.0711 4.92893C17.1957 3.05357 14.6522 2 12 2ZM16.5625 16.4375C16.3791 16.7161 16.0145 16.8107 15.7188 16.6562C13.375 15.2188 10.4062 14.9062 6.9375 15.6875C6.71979 15.7377 6.49182 15.668 6.33945 15.5046C6.18709 15.3412 6.13348 15.1089 6.19883 14.8952C6.26417 14.6816 6.43854 14.519 6.65625 14.4688C10.4688 13.5938 13.7188 13.9688 16.375 15.5938C16.5149 15.6781 16.6141 15.816 16.6495 15.9755C16.685 16.1349 16.6535 16.3019 16.5625 16.4375ZM17.8125 13.6875C17.7053 13.8622 17.5328 13.9869 17.3333 14.0338C17.1338 14.0807 16.9238 14.0461 16.75 13.9375C14.0625 12.2812 9.96875 11.8125 6.78125 12.7812C6.5133 12.8594 6.22401 12.7887 6.02236 12.5957C5.8207 12.4027 5.73731 12.1168 5.80361 11.8457C5.8699 11.5746 6.0758 11.3594 6.34375 11.2812C9.96875 10.1875 14.5 10.7188 17.5625 12.625C17.9134 12.8575 18.0229 13.3229 17.8125 13.6875ZM17.9062 10.875C14.6875 8.96875 9.375 8.78125 6.28125 9.71875C5.81691 9.79284 5.36952 9.5115 5.23513 9.0609C5.10074 8.61031 5.32093 8.12986 5.75 7.9375C9.28125 6.875 15.1562 7.0625 18.875 9.28125C19.0893 9.40709 19.2434 9.61436 19.3023 9.85577C19.3612 10.0972 19.3198 10.3521 19.1875 10.5625C18.9054 10.9822 18.3499 11.1177 17.9062 10.875Z" fill="#1ED760"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.0041 2.00001C17.1763 2.00895 21.4906 5.95038 21.9587 11.0944C22.4268 16.2383 18.8947 20.8919 13.8088 21.8317C8.72289 22.7716 3.75714 19.6885 2.35093 14.7179L6.20594 16.3116C6.5198 17.7178 7.8363 18.6682 9.27188 18.5248C10.7075 18.3815 11.8094 17.1896 11.8382 15.7491V15.6241L15.2552 13.1868H15.3491C16.8821 13.1944 18.2684 12.278 18.8604 10.8657C19.4523 9.45349 19.133 7.82415 18.0517 6.7389C16.9704 5.65365 15.3404 5.32671 13.9233 5.91082C12.5062 6.49494 11.5817 7.8748 11.5817 9.40577V9.46827L9.1848 12.9368H9.02522C8.45433 12.9348 7.89684 13.1095 7.4294 13.4368L2.03802 11.2182C2.43122 6.01377 6.77779 1.9934 12.0041 2.00001ZM8.30554 17.1865L7.0852 16.6865C7.29931 17.1614 7.69311 17.5322 8.18037 17.7177C9.26649 18.1594 10.5059 17.643 10.9559 16.5616C11.1781 16.0427 11.1781 15.4555 10.9559 14.9367C10.5134 13.8748 9.31078 13.3508 8.23044 13.7492L9.51648 14.2804C9.90142 14.434 10.2081 14.7361 10.3671 15.1184C10.5261 15.5007 10.524 15.9308 10.3613 16.3116C10.0152 17.1067 9.10576 17.4914 8.29302 17.1865H8.30554ZM13.4591 9.40577C13.459 9.90903 13.6615 10.3912 14.0211 10.7438C14.3807 11.0964 14.8671 11.2899 15.371 11.2806C16.4135 11.2808 17.264 10.4468 17.2828 9.40577C17.2828 8.35132 16.4268 7.49652 15.371 7.49652C14.3151 7.49652 13.4591 8.35132 13.4591 9.40577ZM17.8398 9.43702C17.855 10.4668 17.2454 11.4037 16.2969 11.8081C15.3484 12.2125 14.2492 12.0042 13.515 11.2809C12.7807 10.5576 12.5572 9.46286 12.9491 8.51021C13.3411 7.55756 14.2708 6.93603 15.3021 6.93718C15.9737 6.92193 16.6228 7.17927 17.1011 7.6504C17.5794 8.12153 17.8459 8.76617 17.8398 9.43702Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,13 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M18.7013 11.2857L15.8465 14.1428H12.9917L10.4938 16.6428V14.1428H7.28217V3.42856H18.7013V11.2857Z" fill="white"/>
<path d="M6.56848 2L3 5.57143V18.4286H7.28218V22L10.8507 18.4286H13.7054L20.1287 12V2H6.56848ZM18.7013 11.2857L15.8465 14.1429H12.9917L10.4938 16.6429V14.1429H7.28218V3.42857H18.7013V11.2857Z" fill="#9146FF"/>
<path d="M16.5603 5.92856H15.1329V10.2143H16.5603V5.92856Z" fill="#9146FF"/>
<path d="M12.6349 5.92856H11.2075V10.2143H12.6349V5.92856Z" fill="#9146FF"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="17.1287" height="20" fill="white" transform="translate(3 2)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 747 B

View File

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.2177 20.2976C15.798 20.2976 19.9327 14.0329 19.9327 8.58261V8.05011C20.7362 7.46091 21.435 6.74089 22 5.92012C21.2567 6.26235 20.4637 6.48437 19.6507 6.57791C20.5139 6.06164 21.1597 5.24885 21.4675 4.2913C20.6598 4.76183 19.7822 5.10021 18.8677 5.29365C17.3053 3.64491 14.7069 3.56109 13.0415 5.10571C11.9701 6.10222 11.5157 7.59694 11.8512 9.02114C8.54594 8.85772 5.46574 7.29769 3.37823 4.72983C2.28095 6.60789 2.84519 9.01622 4.66249 10.2114C4.00661 10.1858 3.36464 10.0146 2.78309 9.71026V9.7729C2.78257 11.7293 4.15628 13.417 6.07204 13.8136C5.46884 13.9751 4.83671 13.9965 4.22396 13.8763C4.76475 15.5538 6.31437 16.7003 8.07674 16.7267C6.62377 17.8749 4.82287 18.4936 2.97103 18.4808C2.65779 18.4808 2.31323 18.4495 2 18.4182C3.84433 19.6513 6.0148 20.3057 8.23336 20.2976" fill="#1DA1F2"/>
</svg>

After

Width:  |  Height:  |  Size: 912 B

View File

@ -0,0 +1,10 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M11.0044 21.9585C9.46481 21.8119 7.90517 21.2589 6.56358 20.3839C5.44002 19.651 5.18638 19.3512 5.18638 18.7493C5.18638 17.5434 6.51463 15.4292 8.784 13.0218C10.0744 11.6537 11.8699 10.0503 12.0635 10.0925C12.4417 10.1769 15.4608 13.1217 16.5911 14.5053C18.3799 16.6995 19.2031 18.4939 18.7848 19.2935C18.4666 19.902 16.4976 21.0901 15.0515 21.5454C13.8589 21.9229 12.2926 22.0828 11.0044 21.9585ZM3.67125 17.4968C2.73903 16.0666 2.26736 14.6563 2.03819 12.6198C1.96255 11.9469 1.98925 11.5627 2.20951 10.1813C2.48317 8.46017 3.46211 6.47029 4.64129 5.24439C5.14411 4.72249 5.18861 4.70916 5.80045 4.9157C6.54355 5.16666 7.33561 5.71299 8.56596 6.82563L9.28459 7.47412L8.89302 7.95604C7.07085 10.1902 5.15079 13.3571 4.4277 15.3159C4.0339 16.3797 3.87594 17.4502 4.04503 17.8943C4.15849 18.1941 4.05393 18.0831 3.67125 17.4968ZM20.0463 17.7389C20.1375 17.2902 20.0218 16.4641 19.7482 15.6313C19.1586 13.828 17.1807 10.47 15.3652 8.18923L14.7934 7.47189L15.4119 6.90336C16.2195 6.1616 16.7802 5.71743 17.3853 5.3421C17.8637 5.04451 18.5445 4.78245 18.8382 4.78245C19.0184 4.78245 19.6547 5.44204 20.1687 6.16382C20.9652 7.27868 21.5503 8.6334 21.8462 10.0414C22.0375 10.952 22.0531 12.8996 21.8774 13.8057C21.7327 14.5497 21.4257 15.5158 21.1276 16.1732C20.9029 16.664 20.3466 17.6167 20.1019 17.9276C19.9773 18.0831 19.9773 18.0831 20.0463 17.7389ZM11.1691 4.44044C10.3303 4.01404 9.03763 3.55877 8.32345 3.4344C8.07204 3.3922 7.64709 3.36777 7.37343 3.3811C6.78384 3.40997 6.81054 3.3811 7.75611 2.93471C8.54149 2.56383 9.19782 2.34618 10.0878 2.15963C11.089 1.94865 12.969 1.94643 13.9546 2.15519C15.0181 2.3795 16.2707 2.84587 16.976 3.27894L17.1851 3.40775L16.7045 3.38332C15.7478 3.33446 14.3551 3.72089 12.8577 4.44932C12.4061 4.66919 12.0145 4.84463 11.9856 4.83797C11.9589 4.83353 11.5896 4.65364 11.1691 4.44044Z" fill="white"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="103.869" height="103.869" fill="white" transform="scale(0.231061)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.582 7.20034C21.352 6.3342 20.6744 5.65216 19.8139 5.42068C18.2542 5 12 5 12 5C12 5 5.7458 5 4.18614 5.42068C3.32568 5.65216 2.64795 6.3342 2.41795 7.20034C2 8.77011 2 12.0455 2 12.0455C2 12.0455 2 15.3207 2.41795 16.8907C2.64795 17.7567 3.32568 18.4387 4.18614 18.6703C5.7458 19.0909 12 19.0909 12 19.0909C12 19.0909 18.2542 19.0909 19.8139 18.6703C20.6744 18.4387 21.352 17.7567 21.582 16.8907C22 15.3207 22 12.0455 22 12.0455C22 12.0455 22 8.77011 21.582 7.20034Z" fill="#D9252A"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.95447 15.0192L15.1817 12.0455L9.95447 9.07169V15.0192Z" fill="#FFFFFE"/>
</svg>

After

Width:  |  Height:  |  Size: 764 B

View File

@ -0,0 +1,124 @@
<!DOCTYPE html>
<html lang="en-GB">
<head>
<title>Discord | Melon Tools</title>
<style>
.page-content {
display: flex;
flex-direction: row;
align-items: flex-start;
flex-wrap: wrap;
}
.connected-accounts li a[data-is-revoked=true] {
border: 2px solid red;
}
.guilds .guild-item {
margin: 8px;
border: 1px solid black;
padding: 8px;
border-radius: 8px;
}
</style>
</head>
<body>
<h1>Discord | Melon Tools</h1>
<div class="page-content">
<div>
{{with .User}}
<p>My User: {{.Username}}#{{.Discriminator}}</p>
<ul>
<li>ID: {{.ID}}</li>
<li>Email: {{.Email}}</li>
<li>Username: {{.Username}}</li>
<li>Avatar: <img {{$.Avatar}}/></li>
<li>Locale: {{.Locale}}</li>
<li>Discriminator: {{.Discriminator}}</li>
<li>Verified: {{.Verified}}</li>
<li>MFA: {{.MFAEnabled}}</li>
<li>Banner: {{.Banner}}</li>
<li>Accent Color: {{.AccentColor}}</li>
<li>Bot: {{.Bot}}</li>
<li>Premium: {{.PremiumType}}</li>
<li>System: {{.System}}</li>
</ul>
{{end}}
</div>
<div>
{{with .User.PublicFlags}}
<p>My Flags:</p>
<ul>
{{if checkFlag . 1}}
<li>Discord Employee</li>
{{end}}
{{if checkFlag . 2}}
<li>Discord Partner</li>
{{end}}
{{if checkFlag . 4}}
<li>Hype Squad Events</li>
{{end}}
{{if checkFlag . 8}}
<li>Bug Hunter Level 1</li>
{{end}}
{{if checkFlag . 64}}
<li>House Bravery</li>
{{end}}
{{if checkFlag . 128}}
<li>House Brilliance</li>
{{end}}
{{if checkFlag . 256}}
<li>House Balance</li>
{{end}}
{{if checkFlag . 512}}
<li>Early Supporter</li>
{{end}}
{{if checkFlag . 1024}}
<li>Team User</li>
{{end}}
{{if checkFlag . 4096}}
<li>System</li>
{{end}}
{{if checkFlag . 16384}}
<li>Bug Hunter Level 2</li>
{{end}}
{{if checkFlag . 65536}}
<li>Verified Bot</li>
{{end}}
{{if checkFlag . 131072}}
<li>Verified Bot Developer</li>
{{end}}
{{if checkFlag . 262144}}
<li>Discord Certified Moderator</li>
{{end}}
</ul>
{{end}}
</div>
<div>
<p>Connections ({{len .Connections}}):</p>
<ul class="connected-accounts">
{{range .Connections}}
<li>
<a href="{{connectedLink .}}" target="_blank" data-is-revoked="{{.Revoked}}">
<img src="/discord/assets/icon/{{.Type}}.svg"/>
<span>{{.Name}}</span>
</a>
</li>
{{end}}
</ul>
</div>
<div>
<p>Guilds ({{len .Guilds}}):</p>
<div class="page-content guilds">
{{range $k, $v := .Guilds}}
<div class="guild-item">
<div><img {{index $.GuildIcons $k}}/></div>
<div>{{$v.Name}}</div>
<div>@*{{$v.ID}}</div>
</div>
{{end}}
</div>
</div>
</div>
</body>
</html>

View File

@ -20,8 +20,8 @@ import (
var indexTemplate string
type Module struct {
oauthClient *oauth2.Config
sessionWrapper func(cb func(http.ResponseWriter, *http.Request, *utils.State)) func(rw http.ResponseWriter, req *http.Request)
oauthClient *oauth2.Config
}
type giteaKeyType int
@ -39,8 +39,11 @@ func New() *Module {
return &Module{}
}
func (m *Module) SetupModule(router *mux.Router, cb func(cb func(http.ResponseWriter, *http.Request, *utils.State)) func(rw http.ResponseWriter, req *http.Request)) {
m.sessionWrapper = cb
func (m *Module) GetName() string { return "Gitea" }
func (m *Module) GetEndpoint() string { return "/gitea" }
func (m *Module) SetupModule(router *mux.Router, f func(cb func(http.ResponseWriter, *http.Request, *utils.State)) func(rw http.ResponseWriter, req *http.Request)) {
m.sessionWrapper = f
m.oauthClient = &oauth2.Config{
ClientID: os.Getenv("GITEA_CLIENT_ID"),
ClientSecret: os.Getenv("GITEA_CLIENT_SECRET"),

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html>
<html lang="en-GB">
<head>
<title>Gitea | Melon Tools</title>
<style>

View File

@ -6,5 +6,7 @@ import (
)
type IModule interface {
GetName() string
GetEndpoint() string
SetupModule(*mux.Router, func(cb func(http.ResponseWriter, *http.Request, *State)) func(rw http.ResponseWriter, req *http.Request))
}