diff --git a/Makefile b/Makefile
index 242bca4..348464c 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/cmd/melon-tools/main.go b/cmd/melon-tools/main.go
index 0f1bf27..a3ebf74 100644
--- a/cmd/melon-tools/main.go
+++ b/cmd/melon-tools/main.go
@@ -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
\n"))
- _, _ = rw.Write([]byte("Gitea\n"))
+ for _, v := range modules {
+ _, _ = rw.Write([]byte(fmt.Sprintf("%s
\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{
diff --git a/go.mod b/go.mod
index 848803d..6a688c5 100644
--- a/go.mod
+++ b/go.mod
@@ -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
)
diff --git a/go.sum b/go.sum
index 44a6211..d858c8c 100644
--- a/go.sum
+++ b/go.sum
@@ -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=
diff --git a/module/discord/guild-icon.go b/module/discord/guild-icon.go
new file mode 100644
index 0000000..cb96a38
--- /dev/null
+++ b/module/discord/guild-icon.go
@@ -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
+}
diff --git a/module/discord/main.go b/module/discord/main.go
new file mode 100644
index 0000000..ca5e052
--- /dev/null
+++ b/module/discord/main.go
@@ -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')"
+}
diff --git a/module/discord/pages/assets/icon/github.svg b/module/discord/pages/assets/icon/github.svg
new file mode 100644
index 0000000..1734266
--- /dev/null
+++ b/module/discord/pages/assets/icon/github.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/module/discord/pages/assets/icon/reddit.svg b/module/discord/pages/assets/icon/reddit.svg
new file mode 100644
index 0000000..62ed78d
--- /dev/null
+++ b/module/discord/pages/assets/icon/reddit.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/module/discord/pages/assets/icon/spotify.svg b/module/discord/pages/assets/icon/spotify.svg
new file mode 100644
index 0000000..901e15e
--- /dev/null
+++ b/module/discord/pages/assets/icon/spotify.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/module/discord/pages/assets/icon/steam.svg b/module/discord/pages/assets/icon/steam.svg
new file mode 100644
index 0000000..a8df1ae
--- /dev/null
+++ b/module/discord/pages/assets/icon/steam.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/module/discord/pages/assets/icon/twitch.svg b/module/discord/pages/assets/icon/twitch.svg
new file mode 100644
index 0000000..6f0a600
--- /dev/null
+++ b/module/discord/pages/assets/icon/twitch.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/module/discord/pages/assets/icon/twitter.svg b/module/discord/pages/assets/icon/twitter.svg
new file mode 100644
index 0000000..5ac575b
--- /dev/null
+++ b/module/discord/pages/assets/icon/twitter.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/module/discord/pages/assets/icon/xbox.svg b/module/discord/pages/assets/icon/xbox.svg
new file mode 100644
index 0000000..472690a
--- /dev/null
+++ b/module/discord/pages/assets/icon/xbox.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/module/discord/pages/assets/icon/youtube.svg b/module/discord/pages/assets/icon/youtube.svg
new file mode 100644
index 0000000..0e18462
--- /dev/null
+++ b/module/discord/pages/assets/icon/youtube.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/module/discord/pages/index.go.html b/module/discord/pages/index.go.html
new file mode 100644
index 0000000..2fa365c
--- /dev/null
+++ b/module/discord/pages/index.go.html
@@ -0,0 +1,124 @@
+
+
+
+ Discord | Melon Tools
+
+
+
+Discord | Melon Tools
+
+
+ {{with .User}}
+
My User: {{.Username}}#{{.Discriminator}}
+
+ - ID: {{.ID}}
+ - Email: {{.Email}}
+ - Username: {{.Username}}
+ - Avatar:
+ - Locale: {{.Locale}}
+ - Discriminator: {{.Discriminator}}
+ - Verified: {{.Verified}}
+ - MFA: {{.MFAEnabled}}
+ - Banner: {{.Banner}}
+ - Accent Color: {{.AccentColor}}
+ - Bot: {{.Bot}}
+ - Premium: {{.PremiumType}}
+ - System: {{.System}}
+
+ {{end}}
+
+
+ {{with .User.PublicFlags}}
+
My Flags:
+
+ {{if checkFlag . 1}}
+ - Discord Employee
+ {{end}}
+ {{if checkFlag . 2}}
+ - Discord Partner
+ {{end}}
+ {{if checkFlag . 4}}
+ - Hype Squad Events
+ {{end}}
+ {{if checkFlag . 8}}
+ - Bug Hunter Level 1
+ {{end}}
+ {{if checkFlag . 64}}
+ - House Bravery
+ {{end}}
+ {{if checkFlag . 128}}
+ - House Brilliance
+ {{end}}
+ {{if checkFlag . 256}}
+ - House Balance
+ {{end}}
+ {{if checkFlag . 512}}
+ - Early Supporter
+ {{end}}
+ {{if checkFlag . 1024}}
+ - Team User
+ {{end}}
+ {{if checkFlag . 4096}}
+ - System
+ {{end}}
+ {{if checkFlag . 16384}}
+ - Bug Hunter Level 2
+ {{end}}
+ {{if checkFlag . 65536}}
+ - Verified Bot
+ {{end}}
+ {{if checkFlag . 131072}}
+ - Verified Bot Developer
+ {{end}}
+ {{if checkFlag . 262144}}
+ - Discord Certified Moderator
+ {{end}}
+
+ {{end}}
+
+
+
Connections ({{len .Connections}}):
+
+
+
+
Guilds ({{len .Guilds}}):
+
+ {{range $k, $v := .Guilds}}
+
+
+
{{$v.Name}}
+
@*{{$v.ID}}
+
+ {{end}}
+
+
+
+
+
diff --git a/module/gitea/main.go b/module/gitea/main.go
index 8e08ebf..bf38542 100644
--- a/module/gitea/main.go
+++ b/module/gitea/main.go
@@ -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"),
diff --git a/module/gitea/pages/index.go.html b/module/gitea/pages/index.go.html
index a196799..4c9b4a5 100644
--- a/module/gitea/pages/index.go.html
+++ b/module/gitea/pages/index.go.html
@@ -1,5 +1,5 @@
-
+
Gitea | Melon Tools