aboutsummaryrefslogtreecommitdiff
path: root/service/service.go
diff options
context:
space:
mode:
authorr <r@freesoftwareextremist.com>2019-12-13 18:08:26 +0000
committerr <r@freesoftwareextremist.com>2019-12-13 18:26:24 +0000
commit5e4da01c3ae3ae2e870faba9085d9d9213c01c29 (patch)
tree39d6f1e76b901549f194ddbac3c6cb82e0abd019 /service/service.go
downloadbloat-5e4da01c3ae3ae2e870faba9085d9d9213c01c29.tar.gz
bloat-5e4da01c3ae3ae2e870faba9085d9d9213c01c29.zip
Initial commit
Diffstat (limited to 'service/service.go')
-rw-r--r--service/service.go285
1 files changed, 285 insertions, 0 deletions
diff --git a/service/service.go b/service/service.go
new file mode 100644
index 0000000..7088a9b
--- /dev/null
+++ b/service/service.go
@@ -0,0 +1,285 @@
+package service
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "path"
+ "strings"
+
+ "mastodon"
+ "web/model"
+ "web/renderer"
+ "web/util"
+)
+
+var (
+ ErrInvalidArgument = errors.New("invalid argument")
+ ErrInvalidToken = errors.New("invalid token")
+ ErrInvalidClient = errors.New("invalid client")
+)
+
+type Service interface {
+ ServeHomePage(ctx context.Context, client io.Writer) (err error)
+ GetAuthUrl(ctx context.Context, instance string) (url string, sessionID string, err error)
+ GetUserToken(ctx context.Context, sessionID string, c *mastodon.Client, token string) (accessToken string, err error)
+ ServeErrorPage(ctx context.Context, client io.Writer, err error)
+ ServeSigninPage(ctx context.Context, client io.Writer) (err error)
+ ServeTimelinePage(ctx context.Context, client io.Writer, c *mastodon.Client, maxID string, sinceID string, minID string) (err error)
+ ServeThreadPage(ctx context.Context, client io.Writer, c *mastodon.Client, id string, reply bool) (err error)
+ Like(ctx context.Context, client io.Writer, c *mastodon.Client, id string) (err error)
+ UnLike(ctx context.Context, client io.Writer, c *mastodon.Client, id string) (err error)
+ Retweet(ctx context.Context, client io.Writer, c *mastodon.Client, id string) (err error)
+ UnRetweet(ctx context.Context, client io.Writer, c *mastodon.Client, id string) (err error)
+ PostTweet(ctx context.Context, client io.Writer, c *mastodon.Client, content string, replyToID string) (err error)
+}
+
+type service struct {
+ clientName string
+ clientScope string
+ clientWebsite string
+ renderer renderer.Renderer
+ sessionRepo model.SessionRepository
+ appRepo model.AppRepository
+}
+
+func NewService(clientName string, clientScope string, clientWebsite string,
+ renderer renderer.Renderer, sessionRepo model.SessionRepository,
+ appRepo model.AppRepository) Service {
+ return &service{
+ clientName: clientName,
+ clientScope: clientScope,
+ clientWebsite: clientWebsite,
+ renderer: renderer,
+ sessionRepo: sessionRepo,
+ appRepo: appRepo,
+ }
+}
+
+func (svc *service) GetAuthUrl(ctx context.Context, instance string) (
+ redirectUrl string, sessionID string, err error) {
+ if !strings.HasPrefix(instance, "https://") {
+ instance = "https://" + instance
+ }
+
+ sessionID = util.NewSessionId()
+ err = svc.sessionRepo.Add(model.Session{
+ ID: sessionID,
+ InstanceURL: instance,
+ })
+ if err != nil {
+ return
+ }
+
+ app, err := svc.appRepo.Get(instance)
+ if err != nil {
+ if err != model.ErrAppNotFound {
+ return
+ }
+
+ var mastoApp *mastodon.Application
+ mastoApp, err = mastodon.RegisterApp(ctx, &mastodon.AppConfig{
+ Server: instance,
+ ClientName: svc.clientName,
+ Scopes: svc.clientScope,
+ Website: svc.clientWebsite,
+ RedirectURIs: svc.clientWebsite + "/oauth_callback",
+ })
+ if err != nil {
+ return
+ }
+
+ app = model.App{
+ InstanceURL: instance,
+ ClientID: mastoApp.ClientID,
+ ClientSecret: mastoApp.ClientSecret,
+ }
+
+ err = svc.appRepo.Add(app)
+ if err != nil {
+ return
+ }
+ }
+
+ u, err := url.Parse(path.Join(instance, "/oauth/authorize"))
+ if err != nil {
+ return
+ }
+
+ q := make(url.Values)
+ q.Set("scope", "read write follow")
+ q.Set("client_id", app.ClientID)
+ q.Set("response_type", "code")
+ q.Set("redirect_uri", svc.clientWebsite+"/oauth_callback")
+ u.RawQuery = q.Encode()
+
+ redirectUrl = u.String()
+
+ return
+}
+
+func (svc *service) GetUserToken(ctx context.Context, sessionID string, c *mastodon.Client,
+ code string) (token string, err error) {
+ if len(code) < 1 {
+ err = ErrInvalidArgument
+ return
+ }
+
+ session, err := svc.sessionRepo.Get(sessionID)
+ if err != nil {
+ return
+ }
+
+ app, err := svc.appRepo.Get(session.InstanceURL)
+ if err != nil {
+ return
+ }
+
+ data := &bytes.Buffer{}
+ err = json.NewEncoder(data).Encode(map[string]string{
+ "client_id": app.ClientID,
+ "client_secret": app.ClientSecret,
+ "grant_type": "authorization_code",
+ "code": code,
+ "redirect_uri": svc.clientWebsite + "/oauth_callback",
+ })
+ if err != nil {
+ return
+ }
+
+ resp, err := http.Post(app.InstanceURL+"/oauth/token", "application/json", data)
+ if err != nil {
+ return
+ }
+ defer resp.Body.Close()
+
+ var res struct {
+ AccessToken string `json:"access_token"`
+ }
+
+ err = json.NewDecoder(resp.Body).Decode(&res)
+ if err != nil {
+ return
+ }
+ /*
+ err = c.AuthenticateToken(ctx, code, svc.clientWebsite+"/oauth_callback")
+ if err != nil {
+ return
+ }
+ err = svc.sessionRepo.Update(sessionID, c.GetAccessToken(ctx))
+ */
+
+ return res.AccessToken, nil
+}
+
+func (svc *service) ServeHomePage(ctx context.Context, client io.Writer) (err error) {
+ err = svc.renderer.RenderHomePage(ctx, client)
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+func (svc *service) ServeErrorPage(ctx context.Context, client io.Writer, err error) {
+ svc.renderer.RenderErrorPage(ctx, client, err)
+}
+
+func (svc *service) ServeSigninPage(ctx context.Context, client io.Writer) (err error) {
+ err = svc.renderer.RenderSigninPage(ctx, client)
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+func (svc *service) ServeTimelinePage(ctx context.Context, client io.Writer,
+ c *mastodon.Client, maxID string, sinceID string, minID string) (err error) {
+
+ var hasNext, hasPrev bool
+ var nextLink, prevLink string
+
+ var pg = mastodon.Pagination{
+ MaxID: maxID,
+ SinceID: sinceID,
+ MinID: minID,
+ Limit: 20,
+ }
+
+ statuses, err := c.GetTimelineHome(ctx, &pg)
+ if err != nil {
+ return err
+ }
+
+ if len(pg.MaxID) > 0 {
+ hasNext = true
+ nextLink = fmt.Sprintf("/timeline?max_id=%s", pg.MaxID)
+ }
+ if len(pg.SinceID) > 0 {
+ hasPrev = true
+ prevLink = fmt.Sprintf("/timeline?since_id=%s", pg.SinceID)
+ }
+
+ data := renderer.NewTimelinePageTemplateData(statuses, hasNext, nextLink, hasPrev, prevLink)
+ err = svc.renderer.RenderTimelinePage(ctx, client, data)
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+func (svc *service) ServeThreadPage(ctx context.Context, client io.Writer, c *mastodon.Client, id string, reply bool) (err error) {
+ status, err := c.GetStatus(ctx, id)
+ if err != nil {
+ return
+ }
+
+ context, err := c.GetStatusContext(ctx, id)
+ if err != nil {
+ return
+ }
+
+ data := renderer.NewThreadPageTemplateData(status, context, reply, id)
+ err = svc.renderer.RenderThreadPage(ctx, client, data)
+ if err != nil {
+ return
+ }
+
+ return
+}
+
+func (svc *service) Like(ctx context.Context, client io.Writer, c *mastodon.Client, id string) (err error) {
+ _, err = c.Favourite(ctx, id)
+ return
+}
+
+func (svc *service) UnLike(ctx context.Context, client io.Writer, c *mastodon.Client, id string) (err error) {
+ _, err = c.Unfavourite(ctx, id)
+ return
+}
+
+func (svc *service) Retweet(ctx context.Context, client io.Writer, c *mastodon.Client, id string) (err error) {
+ _, err = c.Reblog(ctx, id)
+ return
+}
+
+func (svc *service) UnRetweet(ctx context.Context, client io.Writer, c *mastodon.Client, id string) (err error) {
+ _, err = c.Unreblog(ctx, id)
+ return
+}
+
+func (svc *service) PostTweet(ctx context.Context, client io.Writer, c *mastodon.Client, content string, replyToID string) (err error) {
+ tweet := &mastodon.Toot{
+ Status: content,
+ InReplyToID: replyToID,
+ }
+ _, err = c.PostStatus(ctx, tweet)
+ return
+}