Made onFollow overridable (untested)

pull/15/head
Michael Demetriou 2019-09-13 20:21:54 +03:00
parent 29ad8059ba
commit bf09851483
4 changed files with 89 additions and 62 deletions

View File

@ -40,6 +40,7 @@ type Actor struct {
publicKeyPem string
privateKeyPem string
publicKeyID string
OnFollow func(map[string]interface{})
}
// ActorToSave is a stripped down actor representation
@ -77,6 +78,9 @@ func MakeActor(name, summary, actorType string) (Actor, error) {
followersIRI: followersIRI,
publicKeyID: publicKeyID,
}
// set auto accept by default (this could be a configuration value)
actor.OnFollow = func(activity map[string]interface{}) {actor.Accept(activity)}
// create actor's keypair
rng := rand.Reader
@ -273,19 +277,19 @@ func (a *Actor) newIDurl() string {
func (a *Actor) CreateNote(content string) {
// for now I will just write this to the outbox
id := a.newIDurl()
hash := a.newIDhash()
create := make(map[string]interface{})
note := make(map[string]interface{})
create["@context"] = context()
create["actor"] = baseURL + a.name
create["cc"] = a.followersIRI
create["id"] = baseURL + a.name + "/" + id
create["id"] = baseURL + a.name + "/" + hash
create["object"] = note
note["attributedTo"] = baseURL + a.name
note["cc"] = a.followersIRI
note["content"] = content
// note["inReplyTo"] = "https://cybre.space/@qwazix/102688373602724023"
note["id"] = baseURL + a.name + "/note/" + id
note["id"] = baseURL + a.name + "/note/" + hash
note["published"] = time.Now().Format(time.RFC3339)
note["url"] = create["id"]
note["type"] = "Note"
@ -293,11 +297,11 @@ func (a *Actor) CreateNote(content string) {
create["published"] = note["published"]
create["type"] = "Create"
go a.sendToFollowers(create)
err := a.saveItem(id, create)
err := a.saveItem(hash, create)
if err != nil {
log.Info("Could not save note to disk")
}
err = a.appendToOutbox(id)
err = a.appendToOutbox(baseURL + a.name + "/" + hash)
if err != nil {
log.Info("Could not append Note to outbox.txt")
}
@ -589,3 +593,46 @@ func (a *Actor) followersSlice() []string {
}
return followersSlice
}
// Accept a follow request
func (a *Actor) Accept(follow map[string]interface{}){
// it's a follow, write it down
newFollower := follow["actor"].(string)
// check we aren't following ourselves
if newFollower == follow["object"] {
log.Info("You can't follow yourself")
return
}
follower, err := NewRemoteActor(follow["actor"].(string))
// check if this user is already following us
if _, ok := a.followers[newFollower]; ok {
log.Info("You're already following us, yay!")
// do nothing, they're already following us
} else {
a.NewFollower(newFollower, follower.inbox)
}
// send accept anyway even if they are following us already
// this is very verbose. I would prefer creating a map by hand
// remove @context from the inner activity
delete(follow, "@context")
accept := make(map[string]interface{})
accept["@context"] = "https://www.w3.org/ns/activitystreams"
accept["to"] = follow["actor"]
accept["id"] = a.newIDurl()
accept["actor"] = a.iri
accept["object"] = follow
accept["type"] = "Accept"
if err != nil {
log.Info("Couldn't retrieve remote actor info, maybe server is down?")
log.Info(err)
}
go a.signedHTTPPost(accept, follower.inbox)
}

View File

@ -91,14 +91,22 @@ func Serve() {
w.WriteHeader(http.StatusNotFound)
return
}
postsPerPage := 100
var response []byte
filename := storage + slash + "actors" + slash + actor.name + slash + "outbox.txt"
totalLines, err := lineCounter(filename)
if err != nil {
log.Info("Can't read outbox.txt")
log.Info(err)
return
}
if pageStr == "" {
//TODO fix total items
response = []byte(`{
"@context" : "https://www.w3.org/ns/activitystreams",
"first" : "` + baseURL + actor.name + `/outbox?page=true",
"first" : "` + baseURL + actor.name + `/outbox?page=1",
"id" : "` + baseURL + actor.name + `/outbox",
"last" : "` + baseURL + actor.name + `/outbox?min_id=0&page=1",
"last" : "` + baseURL + actor.name + `/outbox?page=` + strconv.Itoa(totalLines/postsPerPage+1) + `",
"totalItems" : 10,
"type" : "OrderedCollection"
}`)
@ -108,8 +116,6 @@ func Serve() {
log.Info("Page number not a number, assuming 1")
page = 1
}
postsPerPage := 100
filename := storage + slash + "actors" + slash + actor.name + slash + "outbox.txt"
lines, err := ReadLines(filename, (page-1)*postsPerPage, page*(postsPerPage+1)-1)
if err != nil {
log.Info("Can't read outbox file")
@ -119,12 +125,7 @@ func Serve() {
responseMap := make(map[string]interface{})
responseMap["@context"] = context()
responseMap["id"] = baseURL + actor.name + "/outbox?page=" + pageStr
totalLines, err := lineCounter(filename)
if err != nil {
log.Info("The file was deleted? It was okay a few lines above. Wtf?")
log.Info(err)
return
}
if page*postsPerPage < totalLines {
responseMap["next"] = baseURL + actor.name + "/outbox?page=" + strconv.Itoa(page+1)
}
@ -134,15 +135,28 @@ func Serve() {
responseMap["partOf"] = baseURL + actor.name + "/outbox"
responseMap["type"] = "OrderedCollectionPage"
orderedItems := make(map[string]interface{})
orderedItems := make([]interface{}, 0, postsPerPage)
for item := range lines {
// read the line
// parse the hash
for _, item := range lines {
// split the line
parts := strings.Split(item, "/")
// keep the hash
hash := parts[len(parts)-1]
// build the filename
filename := storage + slash + "actors" + slash + actor.name + slash + "items" + slash + hash + ".json"
// open the file
// unmarshal
activityJSON, err := ioutil.ReadFile(filename)
if err != nil {
log.Error("can't read activity")
log.Info(filename)
return
}
var temp map[string]interface{}
// put it into a map
json.Unmarshal(activityJSON, &temp)
// append to orderedItems
orderedItems = append(orderedItems, temp)
}
responseMap["orderedItems"] = orderedItems
@ -173,51 +187,13 @@ func Serve() {
// check if case is going to be an issue
switch activity["type"] {
case "Follow":
// it's a follow, write it down
newFollower := activity["actor"].(string)
// check we aren't following ourselves
if newFollower == activity["object"] {
log.Info("You can't follow yourself")
return
}
// load the object as actor
actor, err := LoadActor(mux.Vars(r)["actor"]) // load the actor from disk
if err != nil {
log.Error("No such actor")
return
}
follower, err := NewRemoteActor(activity["actor"].(string))
// check if this user is already following us
if _, ok := actor.followers[newFollower]; ok {
log.Info("You're already following us, yay!")
// do nothing, they're already following us
} else {
actor.NewFollower(newFollower, follower.inbox)
}
// send accept anyway even if they are following us already
// this is very verbose. I would prefer creating a map by hand
// remove @context from the inner activity
delete(activity, "@context")
accept := make(map[string]interface{})
accept["@context"] = "https://www.w3.org/ns/activitystreams"
accept["to"] = activity["actor"]
accept["id"] = actor.newIDurl()
accept["actor"] = actor.iri
accept["object"] = activity
accept["type"] = "Accept"
if err != nil {
log.Info("Couldn't retrieve remote actor info, maybe server is down?")
log.Info(err)
}
go actor.signedHTTPPost(accept, follower.inbox)
actor.OnFollow(activity)
case "Accept":
acceptor := activity["actor"].(string)
actor, err := LoadActor(mux.Vars(r)["actor"]) // load the actor from disk
@ -226,6 +202,8 @@ func Serve() {
return
}
// From here down this could be moved to Actor (TBD)
follow := activity["object"].(map[string]interface{})
id := follow["id"].(string)

View File

@ -58,7 +58,7 @@ func context() [1]string {
// ReadLines reads specific lines from a file and returns them as
// an array of strings
func ReadLines(filename string, from, to int) (lines []string, err error) {
lines = make([]string, to-from)
lines = make([]string, 0, to-from)
reader, err := os.Open(filename)
if err != nil {
log.Info("could not read file")

View File

@ -47,6 +47,7 @@ func main() {
if *debugFlag == true {
log.EnableLevel("info")
log.EnableLevel("error")
}
activityserve.Setup("config.ini", *debugFlag)
@ -56,8 +57,9 @@ func main() {
// actor.Follow("https://cybre.space/users/tzo")
// actor.CreateNote("Hello World!")
actor, _ := activityserve.LoadActor("activityserve_test_actor_2")
actor.CreateNote("Hello World, again!")
actor, _ :=
activityserve.LoadActor("activityserve_test_actor_2")
actor.CreateNote("I'm building #ActivityPub stuff")
activityserve.Serve()
}