Support un-liking posts from the fediverse

Ref T906
pull/1122/head
Matt Baer 1 month ago
parent 0ce5d3ba26
commit 7f1cc6bf8f
  1. 116
      activitypub.go

@ -357,8 +357,8 @@ func handleFetchCollectionInbox(app *App, w http.ResponseWriter, r *http.Request
a := streams.NewAccept() a := streams.NewAccept()
p := c.PersonObject() p := c.PersonObject()
var to *url.URL var to *url.URL
var isFollow, isUnfollow, isLike bool var isFollow, isUnfollow, isLike, isUnlike bool
var likePostID string var likePostID, unlikePostID string
fullActor := &activitystreams.Person{} fullActor := &activitystreams.Person{}
var remoteUser *RemoteUser var remoteUser *RemoteUser
@ -392,29 +392,7 @@ func handleFetchCollectionInbox(app *App, w http.ResponseWriter, r *http.Request
}, 0) }, 0)
*/ */
// Get post ID from URL likePostID, err = parsePostIDFromURL(app, obj)
var collAlias, slug string
if m := apCollectionPostIRIRegex.FindStringSubmatch(obj.String()); len(m) == 3 {
collAlias = m[1]
slug = m[2]
} else if m = apDraftPostIRIRegex.FindStringSubmatch(obj.String()); len(m) == 2 {
likePostID = m[1]
} else {
return fmt.Errorf("unable to match objectIRI: %s", obj)
}
// Get postID if all we have is collection and slug
if collAlias != "" && slug != "" {
c, err := app.db.GetCollection(collAlias)
if err != nil {
return err
}
p, err := app.db.GetPost(slug, c.ID)
if err != nil {
return err
}
likePostID = p.ID
}
// Finally, get actor information // Finally, get actor information
_, from := l.GetActor(0) _, from := l.GetActor(0)
@ -465,8 +443,6 @@ func handleFetchCollectionInbox(app *App, w http.ResponseWriter, r *http.Request
return impart.RenderActivityJSON(w, m, http.StatusOK) return impart.RenderActivityJSON(w, m, http.StatusOK)
}, },
UndoCallback: func(u *streams.Undo) error { UndoCallback: func(u *streams.Undo) error {
isUnfollow = true
m["@context"] = []string{activitystreams.Namespace} m["@context"] = []string{activitystreams.Namespace}
b, _ := json.Marshal(m) b, _ := json.Marshal(m)
if debugging { if debugging {
@ -474,6 +450,31 @@ func handleFetchCollectionInbox(app *App, w http.ResponseWriter, r *http.Request
} }
a.AppendObject(u.Raw()) a.AppendObject(u.Raw())
// Check type -- we handle Undo:Like and Undo:Follow
_, err := u.ResolveObject(&streams.Resolver{
LikeCallback: func(like *streams.Like) error {
isUnlike = true
_, from := like.GetActor(0)
obj := like.Raw().GetObjectIRI(0)
unlikePostID, err = parsePostIDFromURL(app, obj)
fullActor, remoteUser, err = getActor(app, from.String())
if err != nil {
return err
}
return nil
},
// TODO: add FollowCallback for more robust handling
}, 0)
if err != nil {
return err
}
if isUnlike {
return nil
}
isUnfollow = true
_, to = u.GetActor(0) _, to = u.GetActor(0)
// TODO: get actor from object.object, not object // TODO: get actor from object.object, not object
obj := u.Raw().GetObjectIRI(0) obj := u.Raw().GetObjectIRI(0)
@ -546,6 +547,39 @@ func handleFetchCollectionInbox(app *App, w http.ResponseWriter, r *http.Request
log.Info("Successfully liked post %s by remote user %s", likePostID, remoteUser.URL) log.Info("Successfully liked post %s by remote user %s", likePostID, remoteUser.URL)
} }
return impart.RenderActivityJSON(w, "", http.StatusOK) return impart.RenderActivityJSON(w, "", http.StatusOK)
} else if isUnlike {
t, err := app.db.Begin()
if err != nil {
log.Error("Unable to start transaction: %v", err)
return fmt.Errorf("unable to start transaction: %v", err)
}
var remoteUserID int64
if remoteUser != nil {
remoteUserID = remoteUser.ID
} else {
remoteUserID, err = apAddRemoteUser(app, t, fullActor)
}
// Add follow
_, err = t.Exec("DELETE FROM remote_likes WHERE post_id = ? AND remote_user_id = ?", unlikePostID, remoteUserID)
if err != nil {
t.Rollback()
log.Error("Couldn't delete Like from DB: %v\n", err)
return fmt.Errorf("Couldn't delete Like from DB: %v", err)
}
err = t.Commit()
if err != nil {
t.Rollback()
log.Error("Rolling back after Commit(): %v\n", err)
return fmt.Errorf("Rolling back after Commit(): %v\n", err)
}
if debugging {
log.Info("Successfully un-liked post %s by remote user %s", unlikePostID, remoteUser.URL)
}
return impart.RenderActivityJSON(w, "", http.StatusOK)
} }
go func() { go func() {
@ -1078,6 +1112,34 @@ func unmarshalActor(actorResp []byte, actor *activitystreams.Person) error {
return nil return nil
} }
func parsePostIDFromURL(app *App, u *url.URL) (string, error) {
// Get post ID from URL
var collAlias, slug, postID string
if m := apCollectionPostIRIRegex.FindStringSubmatch(u.String()); len(m) == 3 {
collAlias = m[1]
slug = m[2]
} else if m = apDraftPostIRIRegex.FindStringSubmatch(u.String()); len(m) == 2 {
postID = m[1]
} else {
return "", fmt.Errorf("unable to match objectIRI: %s", u)
}
// Get postID if all we have is collection and slug
if collAlias != "" && slug != "" {
c, err := app.db.GetCollection(collAlias)
if err != nil {
return "", err
}
p, err := app.db.GetPost(slug, c.ID)
if err != nil {
return "", err
}
postID = p.ID
}
return postID, nil
}
func setCacheControl(w http.ResponseWriter, ttl time.Duration) { func setCacheControl(w http.ResponseWriter, ttl time.Duration) {
w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%.0f", ttl.Seconds())) w.Header().Set("Cache-Control", fmt.Sprintf("public, max-age=%.0f", ttl.Seconds()))
} }

Loading…
Cancel
Save