-module vexingworkshop.com/dead-tooter/v2
+module git.vexingworkshop.com/signal9/dead-tooter
-go 1.19
+go 1.18
package main
import (
- "encoding/json"
"fmt"
- "io"
- "net/http"
- "net/url"
- "os"
-)
-
-type App struct {
- ClientName string `json:"client_name"`
- RedirectUris string `json:"redirect_uris"`
- Scopes string `json:"scopes"`
- Website string `json:"website"`
-}
-
-func (a App) Create() (application Application, err error) {
- resp, err := http.PostForm("https://hackers.town/api/v1/apps",
- url.Values{
- "client_name": {a.ClientName},
- "redirect_uris": {a.RedirectUris},
- "scopes": {a.Scopes},
- "website": {a.Website},
- },
- )
- if err != nil {
- return
- }
- defer resp.Body.Close()
-
- body, err := io.ReadAll(resp.Body)
- err = json.Unmarshal(body, &application)
- return
-}
-
-func (a App) Load(name string) (Application, error) {
- err := os.Mkdir("foodir", 0750)
- if err != nil && !os.IsExist(err) {
- panic(err.Error())
- }
+ "git.vexingworkshop.com/signal9/dead-tooter/pkg/mastodon"
+)
- data, err := os.ReadFile("~/foodir/" + name)
- if err != nil && os.IsNotExist(err) {
- panic(err.Error())
- }
+func main() {
+ //app := Application{}
+ //err := app.Load("dead-tooter")
+ app, err := mastodon.App{
+ ClientName: "dead-tooter",
+ RedirectUris: "urn:ietf:wg:oauth:2.0:oob",
+ Scopes: "read write follow push",
+ Website: "https://dead-tooter.vexingworkshop.com",
+ }.Create()
- var application Application
- err = json.Unmarshal(data, &application)
if err != nil {
panic(err.Error())
}
+ app.Save()
- return application, nil
-}
-
-type Application struct {
- ID string `json:"id"`
- Name string `json:"name"`
- Website string `json:"website"`
- RedirectURI string `json:"redirect_uri"`
- ClientID string `json:"client_id"`
- ClientSecret string `json:"client_secret"`
- VapidKey string `json:"vapid_key"`
-}
-
-func (a Application) Login() {}
-
-/*
- func main() {
- app := App{
- ClientName: "dead-tooter",
- RedirectUris: "urn:ietf:wg:oauth:2.0:oob",
- Scopes: "read:follows",
- Website: "https://dead-tooter.vexingworkshop.com",
- }
-
- tooter, err := app.Create()
- if err != nil {
- panic(err.Error())
- }
+ fmt.Printf("%v", app)
- fmt.Printf("%v", tooter)
- }
-*/
-func main() {
-
- tooter, err := App{}.Load("dead-tooter")
- if err != nil {
- panic(err.Error())
- }
+ bearer := app.Login()
- fmt.Printf("%v", tooter)
+ fmt.Printf("%v", bearer)
}
--- /dev/null
+package mastodon
+
+import (
+ "encoding/json"
+ "io"
+ "net/http"
+ "net/url"
+)
+
+// App represents the basic and methods necessary to register an application
+// with a Mastodon server.
+type App struct {
+ ClientName string `json:"client_name"`
+ RedirectUris string `json:"redirect_uris"`
+ Scopes string `json:"scopes"`
+ Website string `json:"website"`
+}
+
+// Create calls the Mastodon API to register the App. It returns an Application
+// instance.
+func (a App) Create() (application Application, err error) {
+ resp, err := http.PostForm("https://hackers.town/api/v1/apps",
+ url.Values{
+ "client_name": {a.ClientName},
+ "redirect_uris": {a.RedirectUris},
+ "scopes": {a.Scopes},
+ "website": {a.Website},
+ },
+ )
+ if err != nil {
+ panic(err.Error())
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ err = json.Unmarshal(body, &application)
+
+ return
+}
--- /dev/null
+package mastodon
+
+import (
+ "encoding/json"
+ "io"
+ "net/http"
+ "net/url"
+ "os"
+)
+
+const configdir = "foodir/"
+
+// Application represents the data and functions that establish a
+// Mastodon API application.
+type Application struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Website string `json:"website"`
+ RedirectURI string `json:"redirect_uri"`
+ ClientID string `json:"client_id,omitempty"`
+ ClientSecret string `json:"client_secret,omitempty"`
+ VapidKey string `json:"vapid_key,omitempty"`
+}
+
+// Save will store a serialized version of the Application struct to a file.
+func (a Application) Save() {
+ err := os.Mkdir(configdir, 0750)
+ if err != nil && !os.IsExist(err) {
+ panic(err.Error())
+ }
+
+ data, err := json.Marshal(a)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ err = os.WriteFile(configdir+a.Name, data, 0666)
+ if err != nil {
+ panic(err.Error())
+ }
+}
+
+// Load will hydrate an Application instance based upon data stored in
+// a file.
+func (a Application) Load(name string) error {
+ data, err := os.ReadFile(configdir + name)
+ if err != nil && os.IsNotExist(err) {
+ panic(err.Error())
+ }
+
+ err = json.Unmarshal(data, &a)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ return nil
+}
+
+// Login authenticates the application to the Mastodon API, returning
+// a bearer token to be used with future requests.
+func (a Application) Login() (bearer Bearer) {
+ resp, err := http.PostForm("https://hackers.town/oauth/token",
+ url.Values{
+ "client_id": {a.ClientID},
+ "client_secret": {a.ClientSecret},
+ "redirect_uri": {a.RedirectURI},
+ "grant_type": {"client_credentials"},
+ })
+ if err != nil {
+ panic(err.Error())
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ err = json.Unmarshal(body, &bearer)
+ if err != nil {
+ panic(err.Error())
+ }
+
+ return
+}
+
+// Bearer struct contains the data returned by the Application login request.
+type Bearer struct {
+ AccessToken string `json:"access_token"`
+ TokenType string `json:"token_type"`
+ Scope string `json:"scope"`
+ CreatedAt int `json:"created_at"`
+}