Implement being followed.

master
Michael Demetriou 2019-09-10 11:21:39 +03:00
parent f67906c96a
commit 86eda3a0e9
8 changed files with 368 additions and 72 deletions

13
TODO
View File

@ -1,8 +1,3 @@
[ ] Load outbox of users and parse latest posts
[ ] Write these posts to local file
(normally we would need to sort by time but this is a
temporary solution until we really follow the actors
and get notifications in timely manner)
[ ] Follow users [ ] Follow users
[ ] Announcements (read up how boost json looks like) [ ] Announcements (read up how boost json looks like)
[ ] Federate the post to our followers (hardcoded for now) [ ] Federate the post to our followers (hardcoded for now)
@ -13,14 +8,14 @@
[ ] Handle the /actor endpoint [ ] Handle the /actor endpoint
[ ] Create configuration file [ ] Create configuration file
[ ] Implement database backend [ ] Implement database backend
[ ] Create a file with the actors we have, their following [] Create a file with the actors we have, their following
and their followers. and their followers.
[ ] `MakeActor` should create a file with that actor. [] `MakeActor` should create a file with that actor.
[ ] Implement `LoadActor` [] Implement `LoadActor`
[ ] All but `main.go` should run LoadActor instead of MakeActor [ ] All but `main.go` should run LoadActor instead of MakeActor
(Actually nobody should run LoadActor except GetActor) (Actually nobody should run LoadActor except GetActor)
[ ] `actor.Follow` should write the new following to file [ ] `actor.Follow` should write the new following to file
[ ] Handle being followed [] Handle being followed
[ ] When followed, the handler should write the new follower to file [ ] When followed, the handler should write the new follower to file
[ ] Make sure we send our boosts to all our followers [ ] Make sure we send our boosts to all our followers
Code is there but it works sometimes (I hate when this happens) Code is there but it works sometimes (I hate when this happens)

View File

@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
@ -57,7 +58,7 @@ func MakeActor(name, summary, actorType string) (Actor, error) {
following := make(map[string]interface{}) following := make(map[string]interface{})
followersIRI := baseURL + name + "/followers" followersIRI := baseURL + name + "/followers"
publicKeyID := baseURL + name + "#main-key" publicKeyID := baseURL + name + "#main-key"
iri := baseURL + "/" + name iri := baseURL + name
nuIri, err := url.Parse(iri) nuIri, err := url.Parse(iri)
if err != nil { if err != nil {
log.Info("Something went wrong when parsing the local actor uri into net/url") log.Info("Something went wrong when parsing the local actor uri into net/url")
@ -193,6 +194,10 @@ func LoadActor(name string) (Actor, error) {
return actor, nil return actor, nil
} }
// func LoadActorFromIRI(iri string) a Actor{
// }
// save the actor to file // save the actor to file
func (a *Actor) save() error { func (a *Actor) save() error {
@ -250,20 +255,22 @@ func (a *Actor) whoAmI() string {
}` }`
} }
func (a *Actor) newID() string { func (a *Actor) newIDhash() string {
return uniuri.New() return uniuri.New()
} }
func (a *Actor) newIDurl() string {
return baseURL + a.name + "/" + a.newIDhash()
}
// CreateNote posts an activityPub note to our followers // CreateNote posts an activityPub note to our followers
func (a *Actor) CreateNote(content string) { func (a *Actor) CreateNote(content string) {
// for now I will just write this to the outbox // for now I will just write this to the outbox
id := a.newID() id := a.newIDurl()
create := make(map[string]interface{}) create := make(map[string]interface{})
note := make(map[string]interface{}) note := make(map[string]interface{})
context := make([]string, 1) create["@context"] = context()
context[0] = "https://www.w3.org/ns/activitystreams"
create["@context"] = context
create["actor"] = baseURL + a.name create["actor"] = baseURL + a.name
create["cc"] = a.followersIRI create["cc"] = a.followersIRI
create["id"] = baseURL + a.name + "/" + id create["id"] = baseURL + a.name + "/" + id
@ -279,34 +286,6 @@ func (a *Actor) CreateNote(content string) {
note["to"] = "https://www.w3.org/ns/activitystreams#Public" note["to"] = "https://www.w3.org/ns/activitystreams#Public"
create["published"] = note["published"] create["published"] = note["published"]
create["type"] = "Create" create["type"] = "Create"
// note := `{
// "actor" : "https://` + baseURL + a.name + `",
// "cc" : [
// "https://` + baseURL + a.name + `/followers"
// ],
// "id" : "https://` + baseURL + a.name + `/` + id +`",
// "object" : {
// "attributedTo" : "https://` + baseURL + a.name + `",
// "cc" : [
// "https://` + baseURL + a.name + `/followers"
// ],
// "content" : "`+ content + `",
// "id" : "https://` + baseURL + a.name + `/` + id +`",
// "inReplyTo" : null,
// "published" : "2019-08-26T16:25:26Z",
// "to" : [
// "https://www.w3.org/ns/activitystreams#Public"
// ],
// "type" : "Note",
// "url" : "https://` + baseURL + a.name + `/` + id +`"
// },
// "published" : "2019-08-26T16:25:26Z",
// "to" : [
// "https://www.w3.org/ns/activitystreams#Public"
// ],
// "type" : "Create"
// }`
to, _ := url.Parse("https://cybre.space/inbox") to, _ := url.Parse("https://cybre.space/inbox")
go a.send(create, to) go a.send(create, to)
a.saveItem(id, create) a.saveItem(id, create)
@ -330,6 +309,33 @@ func (a *Actor) send(content map[string]interface{}, to *url.URL) (err error) {
return a.signedHTTPPost(content, to.String()) return a.signedHTTPPost(content, to.String())
} }
// GetFollowers returns a list of people that follow us
func (a *Actor) GetFollowers(page int) (response []byte, err error) {
// if there's no page parameter mastodon displays an
// OrderedCollection with info of where to find orderedCollectionPages
// with the actual information. We are mirroring that behavior
themap := make(map[string]interface{})
themap["@context"] = "https://www.w3.org/ns/activitystreams"
if page == 0 {
themap["first"] = baseURL + a.name + "/followers?page=1"
themap["id"] = baseURL + a.name + "/followers"
themap["totalItems"] = strconv.Itoa(len(a.followers))
themap["type"] = "OrderedCollection"
} else if page == 1 { // implement pagination
themap["id"] = baseURL + a.name + "followers?page=" + strconv.Itoa(page)
items := make([]string, 0, len(a.followers))
for k := range a.followers {
items = append(items, k)
}
themap["orderedItems"] = items
themap["partOf"] = baseURL + a.name + "/followers"
themap["totalItems"] = len(a.followers)
themap["type"] = "OrderedCollectionPage"
}
response, _ = json.Marshal(themap)
return
}
func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err error) { func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err error) {
b, err := json.Marshal(content) b, err := json.Marshal(content)
if err != nil { if err != nil {
@ -357,7 +363,7 @@ func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err e
} }
req.Header.Add("Accept-Charset", "utf-8") req.Header.Add("Accept-Charset", "utf-8")
req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT") req.Header.Add("Date", time.Now().UTC().Format("Mon, 02 Jan 2006 15:04:05")+" GMT")
req.Header.Add("User-Agent", fmt.Sprintf("activityserve 0.0")) req.Header.Add("User-Agent", userAgent + " " + version)
req.Header.Add("Host", iri.Host) req.Header.Add("Host", iri.Host)
req.Header.Add("Accept", "application/activity+json") req.Header.Add("Accept", "application/activity+json")
sum := sha256.Sum256(b) sum := sha256.Sum256(b)
@ -377,16 +383,16 @@ func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err e
defer resp.Body.Close() defer resp.Body.Close()
if !isSuccess(resp.StatusCode) { if !isSuccess(resp.StatusCode) {
responseData, _ := ioutil.ReadAll(resp.Body) responseData, _ := ioutil.ReadAll(resp.Body)
err = fmt.Errorf("POST request to %s failed (%d): %s\nResponse: %s \nRequest: %s \nHeaders: %s", to, resp.StatusCode, resp.Status, formatJSON(responseData), formatJSON(byteCopy), req.Header) err = fmt.Errorf("POST request to %s failed (%d): %s\nResponse: %s \nRequest: %s \nHeaders: %s", to, resp.StatusCode, resp.Status, FormatJSON(responseData), FormatJSON(byteCopy), FormatHeaders(req.Header))
log.Info(err) log.Info(err)
return return
} }
responseData, _ := ioutil.ReadAll(resp.Body) responseData, _ := ioutil.ReadAll(resp.Body)
fmt.Printf("POST request to %s succeeded (%d): %s \nResponse: %s \nRequest: %s \nHeaders: %s", to, resp.StatusCode, resp.Status, formatJSON(responseData), formatJSON(byteCopy), req.Header) fmt.Printf("POST request to %s succeeded (%d): %s \nResponse: %s \nRequest: %s \nHeaders: %s", to, resp.StatusCode, resp.Status, FormatJSON(responseData), FormatJSON(byteCopy), FormatHeaders(req.Header))
return return
} }
func (a *Actor) signedHTTPGet(address string) (string, error){ func (a *Actor) signedHTTPGet(address string) (string, error) {
req, err := http.NewRequest("GET", address, nil) req, err := http.NewRequest("GET", address, nil)
if err != nil { if err != nil {
log.Error("cannot create new http.request") log.Error("cannot create new http.request")
@ -413,7 +419,7 @@ func (a *Actor) signedHTTPGet(address string) (string, error){
log.Error("Can't sign the request") log.Error("Can't sign the request")
return "", err return "", err
} }
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
log.Error("Cannot perform the GET request") log.Error("Cannot perform the GET request")
@ -422,14 +428,20 @@ func (a *Actor) signedHTTPGet(address string) (string, error){
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
responseData, _ := ioutil.ReadAll(resp.Body) responseData, _ := ioutil.ReadAll(resp.Body)
return "", fmt.Errorf("GET request to %s failed (%d): %s \n%s", iri.String(), resp.StatusCode, resp.Status, formatJSON(responseData)) return "", fmt.Errorf("GET request to %s failed (%d): %s \n%s", iri.String(), resp.StatusCode, resp.Status, FormatJSON(responseData))
} }
responseData, _ := ioutil.ReadAll(resp.Body) responseData, _ := ioutil.ReadAll(resp.Body)
fmt.Println("GET request succeeded:", iri.String(), req.Header, resp.StatusCode, resp.Status, "\n", formatJSON(responseData)) fmt.Println("GET request succeeded:", iri.String(), req.Header, resp.StatusCode, resp.Status, "\n", FormatJSON(responseData))
responseText := string(responseData) responseText := string(responseData)
return responseText, nil return responseText, nil
} }
// NewFollower records a new follower to the actor file
func (a *Actor) NewFollower(iri string) error {
a.followers[iri] = struct{}{}
return a.save()
}

View File

@ -2,7 +2,9 @@ package activityserve
import ( import (
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"strconv"
"strings" "strings"
"github.com/gologme/log" "github.com/gologme/log"
@ -11,7 +13,7 @@ import (
"encoding/json" "encoding/json"
) )
// SetupHTTP starts an http server with all the required handlers // Serve starts an http server with all the required handlers
func Serve() { func Serve() {
var webfingerHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { var webfingerHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
@ -34,23 +36,26 @@ func Serve() {
responseMap := make(map[string]interface{}) responseMap := make(map[string]interface{})
responseMap["subject"] = "acct:" + actor.name + "@" + server responseMap["subject"] = "acct:" + actor.name + "@" + server
links := make(map[string]string) // links is a json array with a single element
links["rel"] = "self" var links [1]map[string]string
links["type"] = "application/activity+json" link1 := make(map[string]string)
links["href"] = baseURL + actor.name link1["rel"] = "self"
link1["type"] = "application/activity+json"
link1["href"] = baseURL + actor.name
links[0] = link1
responseMap["links"] = links responseMap["links"] = links
response, err := json.Marshal(responseMap) response, err := json.Marshal(responseMap)
if err != nil { if err != nil {
log.Error("problem creating the webfinger response json") log.Error("problem creating the webfinger response json")
} }
log.Info(string(response)) PrettyPrintJSON(response)
w.Write([]byte(response)) w.Write([]byte(response))
} }
var actorHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { var actorHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "application/activity+json; charset=utf-8") w.Header().Set("content-type", "application/activity+json; charset=utf-8")
log.Info("Remote server just fetched our /actor endpoint") log.Info("Remote server " + r.RemoteAddr + " just fetched our /actor endpoint")
username := mux.Vars(r)["actor"] username := mux.Vars(r)["actor"]
log.Info(username) log.Info(username)
if username == ".well-known" || username == "favicon.ico" { if username == ".well-known" || username == "favicon.ico" {
@ -66,9 +71,13 @@ func Serve() {
return return
} }
fmt.Fprintf(w, actor.whoAmI()) fmt.Fprintf(w, actor.whoAmI())
log.Info(r.RemoteAddr)
log.Info(r.Body) // Show some debugging information
log.Info(r.Header) printer.Info("")
body, _ := ioutil.ReadAll(r.Body)
PrettyPrintJSON(body)
log.Info(FormatHeaders(r.Header))
printer.Info("")
} }
var outboxHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) { var outboxHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
@ -137,13 +146,101 @@ func Serve() {
w.Write([]byte(response)) w.Write([]byte(response))
} }
var inboxHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
b, err := ioutil.ReadAll(r.Body)
if err != nil {
panic(err)
}
activity := make(map[string]interface{})
err = json.Unmarshal(b, &activity)
if err != nil {
log.Error("Probably this request didn't have (valid) JSON inside it")
return
}
// TODO check if it's actually an activity
// 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
}
// 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)
}
// 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"
follower, err := NewRemoteActor(activity["actor"].(string))
if err != nil {
log.Info("Couldn't retrieve remote actor info, maybe server is down?")
log.Info(err)
}
go actor.signedHTTPPost(accept, follower.inbox)
default:
}
}
var followersHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "application/activity+json; charset=utf-8")
username := mux.Vars(r)["actor"]
actor, err := LoadActor(username)
// error out if this actor does not exist
if err != nil {
log.Info("Can't create local actor")
return
}
var page int
pageS := r.URL.Query().Get("page")
if pageS == "" {
page = 0
} else {
page, _ = strconv.Atoi(pageS)
}
response, _ := actor.GetFollowers(page)
w.Write(response)
}
// Add the handlers to a HTTP server // Add the handlers to a HTTP server
gorilla := mux.NewRouter() gorilla := mux.NewRouter()
gorilla.HandleFunc("/.well-known/webfinger", webfingerHandler) gorilla.HandleFunc("/.well-known/webfinger", webfingerHandler)
gorilla.HandleFunc("/{actor}/followers", followersHandler)
gorilla.HandleFunc("/{actor}/outbox", outboxHandler) gorilla.HandleFunc("/{actor}/outbox", outboxHandler)
gorilla.HandleFunc("/{actor}/outbox/", outboxHandler) gorilla.HandleFunc("/{actor}/outbox/", outboxHandler)
// gorilla.HandleFunc("/{actor}/inbox", inboxHandler) gorilla.HandleFunc("/{actor}/inbox", inboxHandler)
// gorilla.HandleFunc("/{actor}/inbox/", inboxHandler) gorilla.HandleFunc("/{actor}/inbox/", inboxHandler)
gorilla.HandleFunc("/{actor}/", actorHandler) gorilla.HandleFunc("/{actor}/", actorHandler)
gorilla.HandleFunc("/{actor}", actorHandler) gorilla.HandleFunc("/{actor}", actorHandler)
// gorilla.HandleFunc("/{actor}/post/{hash}", postHandler) // gorilla.HandleFunc("/{actor}/post/{hash}", postHandler)

View File

@ -0,0 +1,92 @@
package activityserve
import (
"fmt"
"io/ioutil"
"github.com/gologme/log"
// "github.com/go-fed/activity/pub"
// "github.com/go-fed/httpsig"
"net/http"
// "net/url"
"encoding/json"
"bytes"
)
// RemoteActor is a type that holds an actor
// that we want to interact with
type RemoteActor struct {
iri, outbox, inbox string
info map[string]interface{}
}
// NewRemoteActor returns a remoteActor which holds
// all the info required for an actor we want to
// interact with (not essentially sitting in our instance)
func NewRemoteActor(iri string) (RemoteActor, error) {
info, err := get(iri)
if err != nil {
log.Info("Couldn't get remote actor information")
log.Info(err)
return RemoteActor{}, err
}
outbox := info["outbox"].(string)
inbox := info["inbox"].(string)
return RemoteActor{
iri: iri,
outbox: outbox,
inbox: inbox,
}, err
}
func (ra RemoteActor) getLatestPosts(number int) (map[string]interface{}, error) {
return get(ra.outbox)
}
func get(iri string) (info map[string]interface{}, err error) {
buf := new(bytes.Buffer)
req, err := http.NewRequest("GET", iri, buf)
if err != nil {
log.Info(err)
return
}
req.Header.Add("Accept", "application/activity+json; profile=\"https://www.w3.org/ns/activitystreams\"")
req.Header.Add("User-Agent", userAgent+" "+version)
req.Header.Add("Accept-Charset", "utf-8")
resp, err := client.Do(req)
if err != nil {
log.Info("Cannot perform the request")
log.Info(err)
return
}
responseData, _ := ioutil.ReadAll(resp.Body)
if !isSuccess(resp.StatusCode) {
err = fmt.Errorf("GET request to %s failed (%d): %s\nResponse: %s \nHeaders: %s", iri, resp.StatusCode, resp.Status, FormatJSON(responseData), FormatHeaders(req.Header))
log.Info(err)
return
}
var e interface{}
err = json.Unmarshal(responseData, &e)
if err != nil {
log.Info("something went wrong when unmarshalling the json")
log.Info(err)
}
info = e.(map[string]interface{})
return
}

View File

@ -2,8 +2,8 @@ package activityserve
import ( import (
"fmt" "fmt"
"os"
"net/http" "net/http"
"os"
"github.com/gologme/log" "github.com/gologme/log"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
@ -13,6 +13,7 @@ var slash = string(os.PathSeparator)
var baseURL = "http://example.com/" var baseURL = "http://example.com/"
var storage = "storage" var storage = "storage"
var userAgent = "activityserve" var userAgent = "activityserve"
var printer *log.Logger
const libName = "activityserve" const libName = "activityserve"
const version = "0.99" const version = "0.99"
@ -61,7 +62,7 @@ func Setup(configurationFile string, debug bool) {
log.EnableLevel("warn") log.EnableLevel("warn")
// create a logger with levels but without prefixes for easier to read // create a logger with levels but without prefixes for easier to read
// debug output // debug output
printer := log.New(os.Stdout, " ", 0) printer = log.New(os.Stdout, " ", 0)
if debug == true { if debug == true {
fmt.Println() fmt.Println()

86
activityserve/snips.md Normal file
View File

@ -0,0 +1,86 @@
## When we follow someone from pherephone 1.00
``` json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "https://floorb.qwazix.com/myAwesomeList1",
"id": "https://floorb.qwazix.com/myAwesomeList1/Xm9UHyJXyFYduqXz",
"object": "https://cybre.space/users/qwazix",
"to": "https://cybre.space/users/qwazix",
"type": "Follow"
}
```
``` yaml
Accept: application/activity+json
Accept-Charset: utf-8
Date: Tue, 10 Sep 2019 05:31:22 GMT
Digest: SHA-256=uL1LvGU4+gSDm8Qci6XibZODTaNCsXWXWgkMWAqBvG8=
Host: cybre.space
Signature: keyId="https://floorb.qwazix.com/myAwesomeList1#main-key",algorithm="rsa-sha256",headers="(request-target) date host digest",signature="c6oipeXu/2zqX3qZF1x7KLNTYifcyqwwDySoslAowjpYlKWO3qAZMU1A//trYm23AtnItXkH2mY3tPq8X7fy9P1+CMFmiTzV01MGwwwJLDtEXKoq8W7L7lWuQhDD5rjiZqWyei4T13FW7MOCRbAtC4kZqkHrp5Z3l8HhPvmgUV5VOuSGWrtbmCN3hlAEHVugQTMPC6UjlaHva6Qm/SNlFmpUdG7WmUUPJIZ6a/ysBk4cLkF1+Hb03grXKexLHAU4bPIRcjwFpUl06yp8fZ8CCLhNhIsBACiizV85D3votmdxAollE5JXSwBp4f6jrZbgiJEusFoxiVKKqZRHRESQBQ=="
```
## Pherephone 1 Accept Activity
``` yaml
Accept: application/activity+json
Accept-Charset: utf-8
Date: Tue, 10 Sep 2019 07:28:49 GMT
Digest: SHA-256=GTy9bhYjOnbeCJzAzpqI/HEw/5p81NnoPLJkVAiZ4K0=
Host: cybre.space
Signature: keyId="https://floorb.qwazix.com/activityserve_test_actor_1#main-key",algorithm="rsa-sha256",headers="(request-target) date host digest",signature="jAeTEy9v1t+bCwQJB2R4Cscu/fGu5i4luHXlzJcJVyRbsHGqxbNEOxlk/G0S5BGbX3Kuoerq2oMpkFV5kCWPlpAmfhz38NKIrWhjnEUpFOfiG+ZJBpQsb3VQp7M3RGPZ9K4hmV6BSzkC8npsFGPI/HkAaj9u/txW5Cp4v6dMOYteoRLcKc3UVPK9j4hCbjq6SPhpwfM+StARSDnUFfpDe4YYQiVnO2WoINPUr4xvELmCYdBclSBCKcG66g8sBpnx4McjIlu0VISeBxzIHZYOONPteLY2uZW3Axi9JIAq88Y2Ecw4vV6Ctp7KcmD7M3kAJLqao2p/XZNZ3ExsTGfrXA=="
User-Agent: activityserve 0.0
```
``` json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "https://floorb.qwazix.com/myAwesomeList1",
"id": "https://floorb.qwazix.com/myAwesomeList1/SABRE7xlDAjtDcZb",
"object": {
"actor": "https://cybre.space/users/qwazix",
"id": "https://cybre.space/3e7336af-4bcd-4f77-aa69-6a145be824aa",
"object": "https://floorb.qwazix.com/myAwesomeList1",
"type": "Follow"
},
"to": "https://cybre.space/users/qwazix",
"type": "Accept"
}
```
## Pherephone 2 Accept Activity
``` yaml
Accept: application/activity+json
Accept-Charset: utf-8
Date: Tue, 10 Sep 2019 07:32:08 GMT
Digest: SHA-256=yKzA6srSMx0b5GXn9DyflXVdqWd6ADBGt5hO9t/yc44=
Host: cybre.space
Signature: keyId="https://floorb.qwazix.com/myAwesomeList1#main-key",algorithm="rsa-sha256",headers="(request-target) date host digest",signature="WERXWDRFS7aGiIoz+HSujtuv9XNFBPxHkJSsCPu7PNIUDoAB2jdwW3rZc5jbrSLxi9Aqhr2BiBV/VYELQ8gITPzzIYH5sizPcPyLyARPUw37t6zA3HinahpfBKXhf73q9u+CYE/7DMKQ2Pvv2lQPaZ8hl27R2KJmcc3Jhmn5nxrQ+kxAtn6qYpNT/BqLWlXKx5rpYM2r+mHjFyYRYsjlAmi+RQNDEmv/uwn+XuNKzEtrL8Oq7mM13Lsid0a3gJi/t0b/luoyRyvi3fHUM/b1epfVogG/FulsZ0A92310v8MbastceQjjUzTzjKHILl7qNewkqtlzn2ARm3cZlAprSg=="
User-Agent: pherephone (go-fed/activity v1.0.0)
```
``` json
{
"@context": "https://www.w3.org/ns/activitystreams",
"actor": "https://floorb.qwazix.com/activityserve_test_actor_1",
"id": "https://floorb.qwazix.com/activityserve_test_actor_1/4wJ9DrBab4eIE3Bt",
"object": {
"actor": "https://cybre.space/users/qwazix",
"id": "https://cybre.space/9123da78-21a5-44bc-bce5-4039a4072e4c",
"object": "https://floorb.qwazix.com/activityserve_test_actor_1",
"type": "Follow"
},
"to": "https://cybre.space/users/qwazix",
"type": "Accept"
}
```

View File

@ -34,8 +34,19 @@ func PrettyPrintJSON(theJSON []byte) {
log.Info(dst) log.Info(dst)
} }
func formatJSON(theJSON []byte) string{ func FormatJSON(theJSON []byte) string {
dst := new(bytes.Buffer) dst := new(bytes.Buffer)
json.Indent(dst, theJSON, "", "\t") json.Indent(dst, theJSON, "", "\t")
return dst.String() return dst.String()
} }
// FormatHeaders to string for printing
func FormatHeaders(header http.Header) string {
buf := new(bytes.Buffer)
header.Write(buf)
return buf.String()
}
func context() [1]string {
return [1]string{"https://www.w3.org/ns/activitystreams"}
}

12
main.go
View File

@ -1,8 +1,9 @@
package main package main
import ( import (
"fmt"
"flag" "flag"
"fmt"
// "os" // "os"
// "strings" // "strings"
@ -26,7 +27,6 @@ import (
"./activityserve" "./activityserve"
) )
var err error var err error
func main() { func main() {
@ -51,9 +51,11 @@ func main() {
activityserve.Setup("config.ini", *debugFlag) activityserve.Setup("config.ini", *debugFlag)
actor, _ := activityserve.MakeActor("activityserve_test_actor_2", "This is an activityserve test actor", "Service") // actor, _ := activityserve.MakeActor("activityserve_test_actor_2", "This is an activityserve test actor", "Service")
// actor, _ := activityserve.LoadActor("activityserve_test_actor_2") // actor, _ := activityserve.LoadActor("activityserve_test_actor_2")
actor.CreateNote("Hello World!") // actor.CreateNote("Hello World!")
activityserve.LoadActor("activityserve_test_actor_2")
activityserve.Serve() activityserve.Serve()
} }