Implement being followed.
parent
f67906c96a
commit
86eda3a0e9
13
TODO
13
TODO
|
@ -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
|
||||
[ ] Announcements (read up how boost json looks like)
|
||||
[ ] Federate the post to our followers (hardcoded for now)
|
||||
|
@ -13,14 +8,14 @@
|
|||
[ ] Handle the /actor endpoint
|
||||
[ ] Create configuration file
|
||||
[ ] 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.
|
||||
[ ] `MakeActor` should create a file with that actor.
|
||||
[ ] Implement `LoadActor`
|
||||
[✔] `MakeActor` should create a file with that actor.
|
||||
[✔] Implement `LoadActor`
|
||||
[ ] All but `main.go` should run LoadActor instead of MakeActor
|
||||
(Actually nobody should run LoadActor except GetActor)
|
||||
[ ] `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
|
||||
[ ] Make sure we send our boosts to all our followers
|
||||
Code is there but it works sometimes (I hate when this happens)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -57,7 +58,7 @@ func MakeActor(name, summary, actorType string) (Actor, error) {
|
|||
following := make(map[string]interface{})
|
||||
followersIRI := baseURL + name + "/followers"
|
||||
publicKeyID := baseURL + name + "#main-key"
|
||||
iri := baseURL + "/" + name
|
||||
iri := baseURL + name
|
||||
nuIri, err := url.Parse(iri)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// func LoadActorFromIRI(iri string) a Actor{
|
||||
|
||||
// }
|
||||
|
||||
// save the actor to file
|
||||
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()
|
||||
}
|
||||
|
||||
func (a *Actor) newIDurl() string {
|
||||
return baseURL + a.name + "/" + a.newIDhash()
|
||||
}
|
||||
|
||||
// CreateNote posts an activityPub note to our followers
|
||||
func (a *Actor) CreateNote(content string) {
|
||||
// for now I will just write this to the outbox
|
||||
|
||||
id := a.newID()
|
||||
id := a.newIDurl()
|
||||
create := make(map[string]interface{})
|
||||
note := make(map[string]interface{})
|
||||
context := make([]string, 1)
|
||||
context[0] = "https://www.w3.org/ns/activitystreams"
|
||||
create["@context"] = context
|
||||
create["@context"] = context()
|
||||
create["actor"] = baseURL + a.name
|
||||
create["cc"] = a.followersIRI
|
||||
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"
|
||||
create["published"] = note["published"]
|
||||
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")
|
||||
go a.send(create, to)
|
||||
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())
|
||||
}
|
||||
|
||||
// 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) {
|
||||
b, err := json.Marshal(content)
|
||||
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("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("Accept", "application/activity+json")
|
||||
sum := sha256.Sum256(b)
|
||||
|
@ -377,12 +383,12 @@ func (a *Actor) signedHTTPPost(content map[string]interface{}, to string) (err e
|
|||
defer resp.Body.Close()
|
||||
if !isSuccess(resp.StatusCode) {
|
||||
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)
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -424,12 +430,18 @@ func (a *Actor) signedHTTPGet(address string) (string, error){
|
|||
if resp.StatusCode != http.StatusOK {
|
||||
|
||||
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)
|
||||
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)
|
||||
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()
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ package activityserve
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gologme/log"
|
||||
|
@ -11,7 +13,7 @@ import (
|
|||
"encoding/json"
|
||||
)
|
||||
|
||||
// SetupHTTP starts an http server with all the required handlers
|
||||
// Serve starts an http server with all the required handlers
|
||||
func Serve() {
|
||||
|
||||
var webfingerHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -34,23 +36,26 @@ func Serve() {
|
|||
responseMap := make(map[string]interface{})
|
||||
|
||||
responseMap["subject"] = "acct:" + actor.name + "@" + server
|
||||
links := make(map[string]string)
|
||||
links["rel"] = "self"
|
||||
links["type"] = "application/activity+json"
|
||||
links["href"] = baseURL + actor.name
|
||||
// links is a json array with a single element
|
||||
var links [1]map[string]string
|
||||
link1 := make(map[string]string)
|
||||
link1["rel"] = "self"
|
||||
link1["type"] = "application/activity+json"
|
||||
link1["href"] = baseURL + actor.name
|
||||
links[0] = link1
|
||||
responseMap["links"] = links
|
||||
|
||||
response, err := json.Marshal(responseMap)
|
||||
if err != nil {
|
||||
log.Error("problem creating the webfinger response json")
|
||||
}
|
||||
log.Info(string(response))
|
||||
PrettyPrintJSON(response)
|
||||
w.Write([]byte(response))
|
||||
}
|
||||
|
||||
var actorHandler http.HandlerFunc = func(w http.ResponseWriter, r *http.Request) {
|
||||
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"]
|
||||
log.Info(username)
|
||||
if username == ".well-known" || username == "favicon.ico" {
|
||||
|
@ -66,9 +71,13 @@ func Serve() {
|
|||
return
|
||||
}
|
||||
fmt.Fprintf(w, actor.whoAmI())
|
||||
log.Info(r.RemoteAddr)
|
||||
log.Info(r.Body)
|
||||
log.Info(r.Header)
|
||||
|
||||
// Show some debugging information
|
||||
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) {
|
||||
|
@ -137,13 +146,101 @@ func Serve() {
|
|||
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
|
||||
gorilla := mux.NewRouter()
|
||||
gorilla.HandleFunc("/.well-known/webfinger", webfingerHandler)
|
||||
gorilla.HandleFunc("/{actor}/followers", followersHandler)
|
||||
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}/post/{hash}", postHandler)
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -2,8 +2,8 @@ package activityserve
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gologme/log"
|
||||
"gopkg.in/ini.v1"
|
||||
|
@ -13,6 +13,7 @@ var slash = string(os.PathSeparator)
|
|||
var baseURL = "http://example.com/"
|
||||
var storage = "storage"
|
||||
var userAgent = "activityserve"
|
||||
var printer *log.Logger
|
||||
|
||||
const libName = "activityserve"
|
||||
const version = "0.99"
|
||||
|
@ -61,7 +62,7 @@ func Setup(configurationFile string, debug bool) {
|
|||
log.EnableLevel("warn")
|
||||
// create a logger with levels but without prefixes for easier to read
|
||||
// debug output
|
||||
printer := log.New(os.Stdout, " ", 0)
|
||||
printer = log.New(os.Stdout, " ", 0)
|
||||
|
||||
if debug == true {
|
||||
fmt.Println()
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
||||
```
|
||||
|
|
@ -34,8 +34,19 @@ func PrettyPrintJSON(theJSON []byte) {
|
|||
log.Info(dst)
|
||||
}
|
||||
|
||||
func formatJSON(theJSON []byte) string{
|
||||
func FormatJSON(theJSON []byte) string {
|
||||
dst := new(bytes.Buffer)
|
||||
json.Indent(dst, theJSON, "", "\t")
|
||||
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"}
|
||||
}
|
||||
|
|
10
main.go
10
main.go
|
@ -1,8 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
// "os"
|
||||
// "strings"
|
||||
|
||||
|
@ -26,7 +27,6 @@ import (
|
|||
"./activityserve"
|
||||
)
|
||||
|
||||
|
||||
var err error
|
||||
|
||||
func main() {
|
||||
|
@ -51,9 +51,11 @@ func main() {
|
|||
|
||||
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.CreateNote("Hello World!")
|
||||
// actor.CreateNote("Hello World!")
|
||||
|
||||
activityserve.LoadActor("activityserve_test_actor_2")
|
||||
|
||||
activityserve.Serve()
|
||||
}
|
Loading…
Reference in New Issue