ekster/pkg/microsub/protocol.go

213 lines
6.2 KiB
Go
Raw Normal View History

/*
* Ekster is a microsub server
* Copyright (c) 2021 The Ekster authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
2018-03-27 19:25:39 +00:00
// Package microsub describes the protocol methods of the Microsub protocol
2018-02-16 21:13:01 +00:00
package microsub
2019-01-03 20:34:36 +00:00
import (
"encoding/json"
"fmt"
"p83.nl/go/ekster/pkg/sse"
2019-01-03 20:34:36 +00:00
)
2018-02-16 21:13:01 +00:00
/*
channels
search
preview
follow / unfollow
timeline
mute / unmute
block / unblock
*/
2019-03-07 19:55:25 +00:00
// Constants for Unread
2019-01-03 20:34:36 +00:00
const (
2019-01-03 21:06:16 +00:00
UnreadBool = 0
UnreadCount = 1
2019-01-03 20:34:36 +00:00
)
2019-03-07 19:55:25 +00:00
// Unread is a special int/bool value for the JSON response
2019-01-03 20:34:36 +00:00
type Unread struct {
Type int
Unread bool
UnreadCount int
}
2018-02-16 21:13:01 +00:00
// Channel contains information about a channel.
type Channel struct {
// UID is a unique id for the channel
2018-04-07 23:45:56 +00:00
UID string `json:"uid"`
Name string `json:"name"`
2019-01-03 21:06:16 +00:00
Unread Unread `json:"unread,omitempty"`
2018-02-16 21:13:01 +00:00
}
// Card contains the fields of an author or location.
type Card struct {
2018-12-08 15:56:08 +00:00
// Filled bool `json:"filled,omitempty"`
Type string `json:"type,omitempty"`
2018-09-12 20:35:49 +00:00
Name string `json:"name,omitempty" mf2:"name"`
URL string `json:"url,omitempty" mf2:"url"`
Photo string `json:"photo,omitempty" mf2:"photo"`
Locality string `json:"locality,omitempty" mf2:"locality"`
Region string `json:"region,omitempty" mf2:"region"`
CountryName string `json:"country-name,omitempty" mf2:"country-name"`
Longitude string `json:"longitude,omitempty" mf2:"longitude"`
Latitude string `json:"latitude,omitempty" mf2:"latitude"`
2018-02-16 21:13:01 +00:00
}
// Content contains the Text or HTML content of an Item.
2018-02-16 21:13:01 +00:00
type Content struct {
2018-09-12 20:35:49 +00:00
Text string `json:"text,omitempty" mf2:"value"`
HTML string `json:"html,omitempty" mf2:"html"`
2018-02-16 21:13:01 +00:00
}
// Item is a post object
type Item struct {
2018-08-05 15:23:50 +00:00
Type string `json:"type"`
2018-09-12 20:35:49 +00:00
Name string `json:"name,omitempty" mf2:"name"`
Published string `json:"published,omitempty" mf2:"published"`
Updated string `json:"updated,omitempty" mf2:"updated"`
URL string `json:"url,omitempty" mf2:"url"`
UID string `json:"uid,omitempty" mf2:"uid"`
Author *Card `json:"author,omitempty" mf2:"author"`
Category []string `json:"category,omitempty" mf2:"category"`
Photo []string `json:"photo,omitempty" mf2:"photo"`
LikeOf []string `json:"like-of,omitempty" mf2:"like-of"`
BookmarkOf []string `json:"bookmark-of,omitempty" mf2:"bookmark-of"`
RepostOf []string `json:"repost-of,omitempty" mf2:"repost-of"`
InReplyTo []string `json:"in-reply-to,omitempty" mf2:"in-reply-to"`
Content *Content `json:"content,omitempty" mf2:"content"`
2018-12-29 09:34:51 +00:00
Summary string `json:"summary,omitempty" mf2:"summary"`
2018-09-12 20:35:49 +00:00
Latitude string `json:"latitude,omitempty" mf2:"latitude"`
Longitude string `json:"longitude,omitempty" mf2:"longitude"`
Checkin *Card `json:"checkin,omitempty" mf2:"checkin"`
2018-08-05 15:23:50 +00:00
Refs map[string]Item `json:"refs,omitempty"`
ID string `json:"_id,omitempty"`
Read bool `json:"_is_read"`
2021-10-21 19:47:37 +00:00
Source *Source `json:"_source,omitempty"`
}
// Source is an Item source
type Source struct {
ID string `json:"_id"`
URL string `json:"url"`
Name string `json:"name"`
Photo string `json:"photo"`
2018-02-16 21:13:01 +00:00
}
// Pagination contains information about paging
type Pagination struct {
After string `json:"after,omitempty"`
Before string `json:"before,omitempty"`
}
// Timeline is a combination of items and paging information
type Timeline struct {
2018-04-06 23:27:27 +00:00
Items []Item `json:"items"`
Paging Pagination `json:"paging"`
2018-02-16 21:13:01 +00:00
}
// Feed is one microsub feed.
2018-02-16 21:13:01 +00:00
type Feed struct {
2018-04-08 09:44:41 +00:00
Type string `json:"type"`
URL string `json:"url"`
Name string `json:"name,omitempty"`
Photo string `json:"photo,omitempty"`
Description string `json:"description,omitempty"`
Author Card `json:"author,omitempty"`
2018-02-16 21:13:01 +00:00
}
// Microsub is the main protocol that should be implemented by a backend
type Microsub interface {
ChannelsGetList() ([]Channel, error)
ChannelsCreate(name string) (Channel, error)
ChannelsUpdate(uid, name string) (Channel, error)
ChannelsDelete(uid string) error
2018-02-16 21:13:01 +00:00
TimelineGet(before, after, channel string) (Timeline, error)
2018-02-16 21:13:01 +00:00
MarkRead(channel string, entry []string) error
2018-03-27 22:40:04 +00:00
FollowGetList(uid string) ([]Feed, error)
FollowURL(uid string, url string) (Feed, error)
2018-02-16 21:13:01 +00:00
UnfollowURL(uid string, url string) error
2018-02-16 21:13:01 +00:00
Search(query string) ([]Feed, error)
PreviewURL(url string) (Timeline, error)
ItemSearch(channel, query string) ([]Item, error)
Events() (chan sse.Message, error)
2018-02-16 21:13:01 +00:00
}
2019-01-03 20:34:36 +00:00
// MarshalJSON encodes an Unread value as JSON
2019-01-03 21:06:16 +00:00
func (unread Unread) MarshalJSON() ([]byte, error) {
switch unread.Type {
case UnreadBool:
return json.Marshal(unread.Unread)
case UnreadCount:
return json.Marshal(unread.UnreadCount)
}
return json.Marshal(nil)
2019-01-03 20:34:36 +00:00
}
// UnmarshalJSON decodes an Unread value from JSON
2019-01-03 20:34:36 +00:00
func (unread *Unread) UnmarshalJSON(bytes []byte) error {
var b bool
err := json.Unmarshal(bytes, &b)
if err == nil {
2019-01-03 21:06:16 +00:00
unread.Type = UnreadBool
2019-01-03 20:34:36 +00:00
unread.Unread = b
return nil
}
var count int
err = json.Unmarshal(bytes, &count)
if err == nil {
2019-01-03 21:06:16 +00:00
unread.Type = UnreadCount
2019-01-03 20:34:36 +00:00
unread.UnreadCount = count
return nil
}
return fmt.Errorf("can't unmarshal as bool or int")
}
// String returns a string of the unread value
2019-01-03 20:34:36 +00:00
func (unread Unread) String() string {
switch unread.Type {
2019-01-03 21:06:16 +00:00
case UnreadBool:
2019-01-03 20:34:36 +00:00
return fmt.Sprint(unread.Unread)
2019-01-03 21:06:16 +00:00
case UnreadCount:
2019-01-03 20:34:36 +00:00
return fmt.Sprint(unread.UnreadCount)
}
return ""
}
// HasUnread return true of there are unread items.
2019-01-03 20:34:36 +00:00
func (unread *Unread) HasUnread() bool {
switch unread.Type {
2019-01-03 21:06:16 +00:00
case UnreadBool:
2019-01-03 20:34:36 +00:00
return unread.Unread
2019-01-03 21:06:16 +00:00
case UnreadCount:
2019-01-03 20:34:36 +00:00
return unread.UnreadCount > 0
}
return false
}