{{.Content}}
+{{.ContentHTML}}
From 3b2c8a4c78ade9e819b61aa055039ae00e517a3e Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Thu, 22 Jul 2021 13:50:11 -0700 Subject: fixed quote bug and moved comment parsing server side without javascript --- activityPubStruct.go | 11 +++-- client.go | 126 ++++++++++++++++++++++++++++++++++++++++++++++----- database.go | 42 +++++++++++++++++ main.go | 4 +- static/main.html | 9 +--- static/ncatalog.html | 5 +- static/posts.html | 35 +++----------- 7 files changed, 174 insertions(+), 58 deletions(-) diff --git a/activityPubStruct.go b/activityPubStruct.go index e790573..62fabe1 100644 --- a/activityPubStruct.go +++ b/activityPubStruct.go @@ -1,6 +1,9 @@ package main -import "encoding/json" +import ( + "encoding/json" + "html/template" +) type AtContextRaw struct { Context json.RawMessage `json:"@context,omitempty"` @@ -111,7 +114,8 @@ type ObjectBase struct { TripCode string `json:"tripcode,omitempty"` Actor string `json:"actor,omitempty"` Audience string `json:"audience,omitempty"` - Content string `json:"content,omitempty"` + ContentHTML template.HTML `json:"contenthtml,omitempty"` + Content string `json:"content,omitempty"` EndTime string `json:"endTime,omitempty"` Generator string `json:"generator,omitempty"` Icon string `json:"icon,omitempty"` @@ -156,7 +160,8 @@ type NestedObjectBase struct { TripCode string `json:"tripcode,omitempty"` Actor string `json:"actor,omitempty"` Audience string `json:"audience,omitempty"` - Content string `json:"content,omitempty"` + ContentHTML template.HTML `json:"contenthtml,omitempty"` + Content string `json:"content,omitempty"` EndTime string `json:"endTime,omitempty"` Generator string `json:"generator,omitempty"` Icon string `json:"icon,omitempty"` diff --git a/client.go b/client.go index af83a28..12e6147 100644 --- a/client.go +++ b/client.go @@ -1,14 +1,17 @@ package main -import "net/http" -import "html/template" -import "database/sql" -import _ "github.com/lib/pq" -import "strings" -import "strconv" -import "sort" -import "regexp" -import "time" +import ( + "net/http" + "html/template" + "database/sql" + _ "github.com/lib/pq" + "strings" + "strconv" + "sort" + "regexp" + "time" + "fmt" +) var Key *string = new(string) @@ -116,6 +119,7 @@ func IndexGet(w http.ResponseWriter, r *http.Request, db *sql.DB) { if(len(data.BoardRemainer) == 3){ data.BoardRemainer = make([]int, 0) } + data.InstanceIndex = GetCollectionFromReq("https://fchan.xyz/followers").Items data.NewsItems = getNewsFromDB(db, 3) @@ -184,6 +188,9 @@ func OutboxGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection Co "proxy": func(url string) string { return MediaProxy(url) }, + "short": func(actorName string, url string) string { + return shortURL(actorName, url) + }, "sub": func (i, j int) int { return i - j }}).ParseFiles("./static/main.html", "./static/nposts.html", "./static/top.html", "./static/bottom.html", "./static/posts.html")) @@ -219,6 +226,14 @@ func OutboxGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection Co returnData.Boards = Boards returnData.Posts = collection.OrderedItems + for i, e := range returnData.Posts { + returnData.Posts[i].ContentHTML = ParseContent(db, returnData.Board.Actor, e.Id, e.Content, e) + + for j, k := range e.Replies.OrderedItems { + returnData.Posts[i].Replies.OrderedItems[j].ContentHTML = ParseContent(db, returnData.Board.Actor, e.Id, k.Content, e) + } + } + var offset = 8 var pages []int pageLimit := (float64(collection.TotalItems) / float64(offset)) @@ -236,7 +251,10 @@ func CatalogGet(w http.ResponseWriter, r *http.Request, db *sql.DB, collection C t := template.Must(template.New("").Funcs(template.FuncMap{ "proxy": func(url string) string { return MediaProxy(url) - }, + }, + "short": func(actorName string, url string) string { + return shortURL(actorName, url) + }, "sub": func (i, j int) int { return i - j }}).ParseFiles("./static/main.html", "./static/ncatalog.html", "./static/top.html")) actor := collection.Actor @@ -273,7 +291,10 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ t := template.Must(template.New("").Funcs(template.FuncMap{ "proxy": func(url string) string { return MediaProxy(url) - }, + }, + "short": func(actorName string, url string) string { + return shortURL(actorName, url) + }, "sub": func (i, j int) int { return i - j }}).ParseFiles("./static/main.html", "./static/npost.html", "./static/top.html", "./static/bottom.html", "./static/posts.html")) path := r.URL.Path @@ -333,6 +354,14 @@ func PostGet(w http.ResponseWriter, r *http.Request, db *sql.DB){ if len(returnData.Posts) > 0 { returnData.PostId = shortURL(returnData.Board.To, returnData.Posts[0].Id) + + for i, e := range returnData.Posts { + returnData.Posts[i].ContentHTML = ParseContent(db, returnData.Board.Actor, e.Id, e.Content, e) + + for j, k := range e.Replies.OrderedItems { + returnData.Posts[i].Replies.OrderedItems[j].ContentHTML = ParseContent(db, returnData.Board.Actor, e.Id, k.Content, e) + } + } } t.ExecuteTemplate(w, "layout", returnData) @@ -596,7 +625,80 @@ func MediaProxy(url string) string { if re.MatchString(url) { return url } - + + fmt.Println("") MediaHashs[HashMedia(url)] = url return "/api/media?hash=" + HashMedia(url) } + +func ParseContent(db *sql.DB, board Actor, op string, content string, thread ObjectBase) template.HTML { + var nContent = content + + re := regexp.MustCompile(`(>>https?:\/\/[A-Za-z0-9_.-~]+\/[A-Za-z0-9_.-~]+\/\w+)`) + match := re.FindAllStringSubmatch(nContent, -1) + + //add url to each matched reply + for i, _ := range match { + link := strings.Replace(match[i][0], ">>", "", 1) + isOP := "" + + if link == op { + isOP = " (OP)" + } + + //formate the hover title text + var quoteTitle string + + // if the quoted content is local get it + // else get it from the database + if thread.Id == link { + quoteTitle = thread.Content + } else { + for _, e := range thread.Replies.OrderedItems { + if e.Id == link { + quoteTitle = e.Content + break + } + } + + if quoteTitle == "" { + obj := GetObjectFromDBFromID(db, link) + if len(obj.OrderedItems) > 0 { + quoteTitle = obj.OrderedItems[0].Content + } + } + } + + quoteTitle = strings.ReplaceAll(quoteTitle, ">", `/\<`) + quoteTitle = strings.ReplaceAll(quoteTitle, "\"", "") + quoteTitle = strings.ReplaceAll(quoteTitle, "'", "") + + + var style string + if board.Restricted { + style = "color: #af0a0f;" + } + + //replace link with quote format + nContent = strings.Replace(nContent, match[i][0], " >>" + shortURL(board.Outbox, link) + isOP + "", -1) + } + + // replace quotes + re = regexp.MustCompile(`((\r\n|^)>(.+)?[^\r\n])`) + match = re.FindAllStringSubmatch(nContent, -1) + + for i, _ := range match { + quote := strings.Replace(match[i][0], ">", ">", 1) + line := re.ReplaceAllString(match[i][0], "" + quote + "") + nContent = strings.Replace(nContent, match[i][0], line, 1) + } + + //replace isolated greater than symboles + re = regexp.MustCompile(`(\r\n)>`) + + nContent = re.ReplaceAllString(nContent, "\r\n>") + + nContent = strings.ReplaceAll(nContent, `/\<`, ">") + + return template.HTML(nContent) +} diff --git a/database.go b/database.go index 1b2ec3d..3a20cb4 100644 --- a/database.go +++ b/database.go @@ -554,6 +554,48 @@ func GetObjectFromDB(db *sql.DB, id string) Collection { return nColl } +func GetObjectFromDBFromID(db *sql.DB, id string) Collection { + var nColl Collection + var result []ObjectBase + + query := `select x.id, x.name, x.content, x.type, x.published, x.updated, x.attributedto, x.attachment, x.preview, x.actor, x.tripcode, x.sensitive from (select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from activitystream where id=$1 and type='Note' union select id, name, content, type, published, updated, attributedto, attachment, preview, actor, tripcode, sensitive from cacheactivitystream where id=$1 and type='Note') as x order by x.updated` + + rows, err := db.Query(query, id) + + CheckError(err, "error query object from db") + + defer rows.Close() + for rows.Next(){ + var post ObjectBase + var actor Actor + var attachID string + var previewID string + + err = rows.Scan(&post.Id, &post.Name, &post.Content, &post.Type, &post.Published, &post.Updated, &post.AttributedTo, &attachID, &previewID, &actor.Id, &post.TripCode, &post.Sensitive) + + CheckError(err, "error scan object into post struct") + + post.Actor = actor.Id + + var postCnt int + var imgCnt int + post.Replies, postCnt, imgCnt = GetObjectRepliesDB(db, post) + + post.Replies.TotalItems = postCnt + post.Replies.TotalImgs = imgCnt + + post.Attachment = GetObjectAttachment(db, attachID) + + post.Preview = GetObjectPreview(db, previewID) + + result = append(result, post) + } + + nColl.OrderedItems = result + + return nColl +} + func GetObjectFromDBCatalog(db *sql.DB, id string) Collection { var nColl Collection var result []ObjectBase diff --git a/main.go b/main.go index 4327e44..601394f 100644 --- a/main.go +++ b/main.go @@ -1640,8 +1640,8 @@ func CreateAttachmentObject(file multipart.File, header *multipart.FileHeader) ( } func ParseCommentForReplies(comment string) []ObjectBase { - - re := regexp.MustCompile("(>>)(https://|http://)?(www\\.)?.+\\/\\w+") + + re := regexp.MustCompile(`(>>https?:\/\/[A-z.:0-9]+\/[A-z0-9]+\/\w+)`) match := re.FindAllStringSubmatch(comment, -1) var links []string diff --git a/static/main.html b/static/main.html index 3cb8555..9209ed9 100644 --- a/static/main.html +++ b/static/main.html @@ -53,14 +53,6 @@ color: #789922; } - .reply { - {{ if .Board.Restricted }} - color:#af0a0f; - {{ else }} - color:#000080; - {{ end }} - } - .post { {{ if .Board.Restricted }} background-color: #d5daf0; @@ -85,6 +77,7 @@
{{.Content}}
+ {{ .Name }}{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }} {{ .TripCode }} {{ .Published }} No. {{ short $board.Actor.Outbox .Id }} {{ if ne .Type "Tombstone" }}[Report]{{ end }} +{{.ContentHTML}}
{{ if .Replies }} {{ $replies := .Replies }} {{ if gt $replies.TotalItems 5 }} @@ -94,7 +93,7 @@ {{ end }} {{ end }} {{ range $replies.OrderedItems }} -{{.Content}}
+{{.ContentHTML}}
Soon™.
v0.0.11-dev
+v0.0.12-dev
{{.ContentHTML}}
-- cgit v1.2.3 From def81637bc3d9f7ff73fed2786dbaa7a78086799 Mon Sep 17 00:00:00 2001 From: FChannel <> Date: Sun, 25 Jul 2021 13:25:47 -0700 Subject: fixed auto follow logic as well as out of sync following --- follow.go | 95 +++++++++++++++++++++++++++++++++++++++--------------- main.go | 4 ++- outboxPost.go | 17 +++++++--- static/manage.html | 2 +- 4 files changed, 86 insertions(+), 32 deletions(-) diff --git a/follow.go b/follow.go index 14bf165..3c07bcd 100644 --- a/follow.go +++ b/follow.go @@ -6,7 +6,6 @@ import ( "net/http" _ "github.com/lib/pq" - ) func GetActorFollowing(w http.ResponseWriter, db *sql.DB, id string) { @@ -180,69 +179,111 @@ func IsAlreadyFollower(db *sql.DB, actor string, follow string) bool { return false; } -func SetActorFollowerDB(db *sql.DB, activity Activity) Activity { +func SetActorFollowerDB(db *sql.DB, activity Activity) Activity { var query string alreadyFollow := IsAlreadyFollower(db, activity.Actor.Id, activity.Object.Actor) + activity.Type = "Reject" + if activity.Actor.Id == activity.Object.Actor { + return activity + } + if alreadyFollow { query = `delete from follower where id=$1 and follower=$2` activity.Summary = activity.Object.Actor + " Unfollow " + activity.Actor.Id _, err := db.Exec(query, activity.Actor.Id, activity.Object.Actor) - if CheckError(err, "error with follower db insert/delete") != nil { + if CheckError(err, "error with follower db delete") != nil { activity.Type = "Reject" return activity - } + } + + activity.Type = "Accept" + return activity } else { query = `insert into follower (id, follower) values ($1, $2)` - activity.Summary = activity.Object.Actor + " Follow " + activity.Actor.Id - } + activity.Summary = activity.Object.Actor + " Follow " + activity.Actor.Id - _, err := db.Exec(query, activity.Actor.Id, activity.Object.Actor) + _, err := db.Exec(query, activity.Actor.Id, activity.Object.Actor) + + if CheckError(err, "error with follower db insert") != nil { + activity.Type = "Reject" + return activity + } + + activity.Type = "Accept" + return activity + } - if CheckError(err, "error with follower db insert/delete") != nil { - activity.Type = "Reject" - return activity - } - activity.Type = "Accept" return activity } func SetActorFollowingDB(db *sql.DB, activity Activity) Activity { var query string - alreadyFollow := false + alreadyFollowing := false + alreadyFollower := false following := GetActorFollowingDB(db, activity.Object.Actor) + actor := FingerActor(activity.Actor.Id) + + remoteActorFollowerCol := GetCollectionFromReq(actor.Followers) + for _, e := range following { if e.Id == activity.Actor.Id { - alreadyFollow = true + alreadyFollowing = true } } + + for _, e := range remoteActorFollowerCol.Items { + if e.Id == activity.Object.Actor { + alreadyFollower = true + } + } + + activity.Type = "Reject" + + if activity.Actor.Id == activity.Object.Actor { + return activity + } - if alreadyFollow { + if alreadyFollowing && alreadyFollower { query = `delete from following where id=$1 and following=$2` activity.Summary = activity.Object.Actor + " Unfollowing " + activity.Actor.Id if !IsActorLocal(db, activity.Actor.Id) { go DeleteActorCache(db, activity.Actor.Id) - } - } else { + } + _, err := db.Exec(query, activity.Object.Actor, activity.Actor.Id) + + if CheckError(err, "error with following db delete") != nil { + activity.Type = "Reject" + return activity + } + + activity.Type = "Accept" + return activity + } + + if !alreadyFollowing && !alreadyFollower { + query = `insert into following (id, following) values ($1, $2)` activity.Summary = activity.Object.Actor + " Following " + activity.Actor.Id if !IsActorLocal(db, activity.Actor.Id) { go WriteActorToCache(db, activity.Actor.Id) } - } - - _, err := db.Exec(query, activity.Object.Actor, activity.Actor.Id) + _, err := db.Exec(query, activity.Object.Actor, activity.Actor.Id) - if CheckError(err, "error with following db insert/delete") != nil { - activity.Type = "Reject" + if CheckError(err, "error with following db insert") != nil { + activity.Type = "Reject" + return activity + } + + activity.Type = "Accept" return activity - } + } + - activity.Type = "Accept" return activity } @@ -260,9 +301,11 @@ func AutoFollow(db *sql.DB, actor string) { } if !isFollowing && e.Id != Domain && e.Id != actor { - followActivity := MakeFollowActivity(db, actor, e.Id) + followActivity := MakeFollowActivity(db, actor, e.Id) + + nActor := FingerActor(e.Id) - if FingerActor(e.Id).Id != "" { + if nActor.Id != "" { MakeActivityRequestOutbox(db, followActivity) } } diff --git a/main.go b/main.go index dc9370b..8547efd 100644 --- a/main.go +++ b/main.go @@ -2175,7 +2175,9 @@ func MakeActivityRequest(db *sql.DB, activity Activity) { _, err = RouteProxy(req) - CheckError(err, "error with sending activity resp to") + if err != nil { + fmt.Println("error with sending activity resp to actor " + instance) + } } } } diff --git a/outboxPost.go b/outboxPost.go index 1d658ea..88b9927 100644 --- a/outboxPost.go +++ b/outboxPost.go @@ -101,7 +101,7 @@ func ParseOutboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { var rActivity Activity if validActor && validLocalActor { rActivity = AcceptFollow(activity) - SetActorFollowingDB(db, rActivity) + rActivity = SetActorFollowingDB(db, rActivity) MakeActivityRequest(db, activity) } @@ -565,16 +565,26 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { MakeActivityRequest(db, response) alreadyFollow := false + alreadyFollowing := false autoSub := GetActorAutoSubscribeDB(db, response.Actor.Id) following := GetActorFollowingDB(db, response.Actor.Id) for _, e := range following { - if e.Id == activity.Actor.Id { + if e.Id == response.Object.Id { alreadyFollow = true } } - if autoSub && !alreadyFollow { + actor := FingerActor(response.Object.Actor) + remoteActorFollowingCol := GetCollectionFromReq(actor.Following) + + for _, e := range remoteActorFollowingCol.Items { + if e.Id == response.Actor.Id { + alreadyFollowing = true + } + } + + if autoSub && !alreadyFollow && alreadyFollowing { followActivity := MakeFollowActivity(db, response.Actor.Id, response.Object.Actor) if FingerActor(response.Object.Actor).Id != "" { @@ -597,7 +607,6 @@ func ParseInboxRequest(w http.ResponseWriter, r *http.Request, db *sql.DB) { } break } - } func MakeActivityFollowingReq(w http.ResponseWriter, r *http.Request, activity Activity) bool { diff --git a/static/manage.html b/static/manage.html index 2647532..3bd621b 100644 --- a/static/manage.html +++ b/static/manage.html @@ -22,7 +22,7 @@ {{ if .IsLocal }}{{.ContentHTML}}
-- cgit v1.2.3