|
|
|
|
package api
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
|
"unicode"
|
|
|
|
|
|
|
|
|
|
"github.com/RasmusLindroth/go-mastodon"
|
|
|
|
|
"github.com/RasmusLindroth/tut/util"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var id uint = 0
|
|
|
|
|
var idMux sync.Mutex
|
|
|
|
|
|
|
|
|
|
func newID() uint {
|
|
|
|
|
idMux.Lock()
|
|
|
|
|
defer idMux.Unlock()
|
|
|
|
|
id = id + 1
|
|
|
|
|
return id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Item interface {
|
|
|
|
|
ID() uint
|
|
|
|
|
Type() MastodonType
|
|
|
|
|
ToggleSpoiler()
|
|
|
|
|
ShowSpoiler() bool
|
|
|
|
|
Raw() interface{}
|
|
|
|
|
URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int)
|
|
|
|
|
Filtered() (bool, string)
|
|
|
|
|
Pinned() bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type filtered struct {
|
|
|
|
|
inUse bool
|
|
|
|
|
name string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func getUrlsStatus(status *mastodon.Status) ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) {
|
|
|
|
|
if status.Reblog != nil {
|
|
|
|
|
status = status.Reblog
|
|
|
|
|
}
|
|
|
|
|
_, urls := util.CleanHTML(status.Content)
|
|
|
|
|
if status.Sensitive {
|
|
|
|
|
_, u := util.CleanHTML(status.SpoilerText)
|
|
|
|
|
urls = append(urls, u...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
realUrls := []util.URL{}
|
|
|
|
|
for _, url := range urls {
|
|
|
|
|
isNotMention := true
|
|
|
|
|
for _, mention := range status.Mentions {
|
|
|
|
|
if mention.URL == url.URL {
|
|
|
|
|
isNotMention = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if isNotMention {
|
|
|
|
|
realUrls = append(realUrls, url)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
length := len(realUrls) + len(status.Mentions) + len(status.Tags)
|
|
|
|
|
return realUrls, status.Mentions, status.Tags, length
|
|
|
|
|
}
|
|
|
|
|
func getUrlsUser(user *mastodon.Account) ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) {
|
|
|
|
|
var urls []util.URL
|
|
|
|
|
user.Note, urls = util.CleanHTML(user.Note)
|
|
|
|
|
for _, f := range user.Fields {
|
|
|
|
|
_, fu := util.CleanHTML(f.Value)
|
|
|
|
|
urls = append(urls, fu...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return urls, []mastodon.Mention{}, []mastodon.Tag{}, len(urls)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewStatusItem(item *mastodon.Status, filters []*mastodon.Filter, timeline string, pinned bool) (sitem Item) {
|
|
|
|
|
filtered := filtered{inUse: false}
|
|
|
|
|
if item == nil {
|
|
|
|
|
return &StatusItem{id: newID(), item: item, showSpoiler: false, filtered: filtered, pinned: pinned}
|
|
|
|
|
}
|
|
|
|
|
s := util.StatusOrReblog(item)
|
|
|
|
|
content := s.Content
|
|
|
|
|
if s.Sensitive {
|
|
|
|
|
content += "\n" + s.SpoilerText
|
|
|
|
|
}
|
|
|
|
|
content = strings.ToLower(content)
|
|
|
|
|
for _, f := range filters {
|
|
|
|
|
apply := false
|
|
|
|
|
for _, c := range f.Context {
|
|
|
|
|
if timeline == c {
|
|
|
|
|
apply = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if !apply {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if f.WholeWord {
|
|
|
|
|
lines := strings.Split(content, "\n")
|
|
|
|
|
var stripped []string
|
|
|
|
|
for _, l := range lines {
|
|
|
|
|
var words []string
|
|
|
|
|
words = append(words, strings.Split(l, " ")...)
|
|
|
|
|
for _, w := range words {
|
|
|
|
|
ns := strings.TrimSpace(w)
|
|
|
|
|
ns = strings.TrimFunc(ns, func(r rune) bool {
|
|
|
|
|
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
|
|
|
|
|
})
|
|
|
|
|
stripped = append(stripped, ns)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
filter := strings.Split(strings.ToLower(f.Phrase), " ")
|
|
|
|
|
for i := 0; i+len(filter)-1 < len(stripped); i++ {
|
|
|
|
|
if strings.ToLower(f.Phrase) == strings.Join(stripped[i:i+len(filter)], " ") {
|
|
|
|
|
filtered.inUse = true
|
|
|
|
|
filtered.name = f.Phrase
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if strings.Contains(s.Content, strings.ToLower(f.Phrase)) {
|
|
|
|
|
filtered.inUse = true
|
|
|
|
|
filtered.name = f.Phrase
|
|
|
|
|
}
|
|
|
|
|
if strings.Contains(s.SpoilerText, strings.ToLower(f.Phrase)) {
|
|
|
|
|
filtered.inUse = true
|
|
|
|
|
filtered.name = f.Phrase
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if filtered.inUse {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sitem = &StatusItem{id: newID(), item: item, showSpoiler: false, filtered: filtered, pinned: pinned}
|
|
|
|
|
return sitem
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type StatusItem struct {
|
|
|
|
|
id uint
|
|
|
|
|
item *mastodon.Status
|
|
|
|
|
showSpoiler bool
|
|
|
|
|
filtered filtered
|
|
|
|
|
pinned bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusItem) ID() uint {
|
|
|
|
|
return s.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusItem) Type() MastodonType {
|
|
|
|
|
return StatusType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusItem) ToggleSpoiler() {
|
|
|
|
|
s.showSpoiler = !s.showSpoiler
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusItem) ShowSpoiler() bool {
|
|
|
|
|
return s.showSpoiler
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusItem) Raw() interface{} {
|
|
|
|
|
return s.item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) {
|
|
|
|
|
return getUrlsStatus(s.item)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusItem) Filtered() (bool, string) {
|
|
|
|
|
return s.filtered.inUse, s.filtered.name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusItem) Pinned() bool {
|
|
|
|
|
return s.pinned
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewStatusHistoryItem(item *mastodon.StatusHistory) (sitem Item) {
|
|
|
|
|
return &StatusHistoryItem{id: newID(), item: item, showSpoiler: false}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type StatusHistoryItem struct {
|
|
|
|
|
id uint
|
|
|
|
|
item *mastodon.StatusHistory
|
|
|
|
|
showSpoiler bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusHistoryItem) ID() uint {
|
|
|
|
|
return s.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusHistoryItem) Type() MastodonType {
|
|
|
|
|
return StatusHistoryType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusHistoryItem) ToggleSpoiler() {
|
|
|
|
|
s.showSpoiler = !s.showSpoiler
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusHistoryItem) ShowSpoiler() bool {
|
|
|
|
|
return s.showSpoiler
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusHistoryItem) Raw() interface{} {
|
|
|
|
|
return s.item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusHistoryItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) {
|
|
|
|
|
status := mastodon.Status{
|
|
|
|
|
Content: s.item.Content,
|
|
|
|
|
SpoilerText: s.item.SpoilerText,
|
|
|
|
|
Account: s.item.Account,
|
|
|
|
|
Sensitive: s.item.Sensitive,
|
|
|
|
|
CreatedAt: s.item.CreatedAt,
|
|
|
|
|
Emojis: s.item.Emojis,
|
|
|
|
|
MediaAttachments: s.item.MediaAttachments,
|
|
|
|
|
}
|
|
|
|
|
return getUrlsStatus(&status)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusHistoryItem) Filtered() (bool, string) {
|
|
|
|
|
return false, ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *StatusHistoryItem) Pinned() bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewUserItem(item *User, profile bool) Item {
|
|
|
|
|
return &UserItem{id: newID(), item: item, profile: profile}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type UserItem struct {
|
|
|
|
|
id uint
|
|
|
|
|
item *User
|
|
|
|
|
profile bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UserItem) ID() uint {
|
|
|
|
|
return u.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UserItem) Type() MastodonType {
|
|
|
|
|
if u.profile {
|
|
|
|
|
return ProfileType
|
|
|
|
|
}
|
|
|
|
|
return UserType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UserItem) ToggleSpoiler() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UserItem) ShowSpoiler() bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UserItem) Raw() interface{} {
|
|
|
|
|
return u.item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UserItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) {
|
|
|
|
|
return getUrlsUser(u.item.Data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *UserItem) Filtered() (bool, string) {
|
|
|
|
|
return false, ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (u *UserItem) Pinned() bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewNotificationItem(item *mastodon.Notification, user *User, filters []*mastodon.Filter) (nitem Item) {
|
|
|
|
|
status := NewStatusItem(item.Status, filters, "notifications", false)
|
|
|
|
|
nitem = &NotificationItem{
|
|
|
|
|
id: newID(),
|
|
|
|
|
item: item,
|
|
|
|
|
showSpoiler: false,
|
|
|
|
|
user: NewUserItem(user, false),
|
|
|
|
|
status: status,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nitem
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type NotificationItem struct {
|
|
|
|
|
id uint
|
|
|
|
|
item *mastodon.Notification
|
|
|
|
|
showSpoiler bool
|
|
|
|
|
status Item
|
|
|
|
|
user Item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type NotificationData struct {
|
|
|
|
|
Item *mastodon.Notification
|
|
|
|
|
Status Item
|
|
|
|
|
User Item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NotificationItem) ID() uint {
|
|
|
|
|
return n.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NotificationItem) Type() MastodonType {
|
|
|
|
|
return NotificationType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NotificationItem) ToggleSpoiler() {
|
|
|
|
|
n.showSpoiler = !n.showSpoiler
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NotificationItem) ShowSpoiler() bool {
|
|
|
|
|
return n.showSpoiler
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NotificationItem) Raw() interface{} {
|
|
|
|
|
return &NotificationData{
|
|
|
|
|
Item: n.item,
|
|
|
|
|
Status: n.status,
|
|
|
|
|
User: n.user,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NotificationItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) {
|
|
|
|
|
nd := n.Raw().(*NotificationData)
|
|
|
|
|
switch n.item.Type {
|
|
|
|
|
case "favourite":
|
|
|
|
|
return getUrlsStatus(nd.Status.Raw().(*mastodon.Status))
|
|
|
|
|
case "reblog":
|
|
|
|
|
return getUrlsStatus(nd.Status.Raw().(*mastodon.Status))
|
|
|
|
|
case "mention":
|
|
|
|
|
return getUrlsStatus(nd.Status.Raw().(*mastodon.Status))
|
|
|
|
|
case "status":
|
|
|
|
|
return getUrlsStatus(nd.Status.Raw().(*mastodon.Status))
|
|
|
|
|
case "poll":
|
|
|
|
|
return getUrlsStatus(nd.Status.Raw().(*mastodon.Status))
|
|
|
|
|
case "update":
|
|
|
|
|
return getUrlsStatus(nd.Status.Raw().(*mastodon.Status))
|
|
|
|
|
case "follow":
|
|
|
|
|
return getUrlsUser(nd.User.Raw().(*User).Data)
|
|
|
|
|
case "follow_request":
|
|
|
|
|
return getUrlsUser(nd.User.Raw().(*User).Data)
|
|
|
|
|
default:
|
|
|
|
|
return []util.URL{}, []mastodon.Mention{}, []mastodon.Tag{}, 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NotificationItem) Filtered() (bool, string) {
|
|
|
|
|
return false, ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *NotificationItem) Pinned() bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewListsItem(item *mastodon.List) Item {
|
|
|
|
|
return &ListItem{id: newID(), item: item, showSpoiler: true}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ListItem struct {
|
|
|
|
|
id uint
|
|
|
|
|
item *mastodon.List
|
|
|
|
|
showSpoiler bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *ListItem) ID() uint {
|
|
|
|
|
return s.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *ListItem) Type() MastodonType {
|
|
|
|
|
return ListsType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *ListItem) ToggleSpoiler() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *ListItem) ShowSpoiler() bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *ListItem) Raw() interface{} {
|
|
|
|
|
return s.item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *ListItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) {
|
|
|
|
|
return nil, nil, nil, 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *ListItem) Filtered() (bool, string) {
|
|
|
|
|
return false, ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (n *ListItem) Pinned() bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewTagItem(item *mastodon.Tag) Item {
|
|
|
|
|
return &TagItem{id: newID(), item: item, showSpoiler: true}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type TagItem struct {
|
|
|
|
|
id uint
|
|
|
|
|
item *mastodon.Tag
|
|
|
|
|
showSpoiler bool
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *TagItem) ID() uint {
|
|
|
|
|
return t.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *TagItem) Type() MastodonType {
|
|
|
|
|
return TagType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *TagItem) ToggleSpoiler() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *TagItem) ShowSpoiler() bool {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *TagItem) Raw() interface{} {
|
|
|
|
|
return t.item
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *TagItem) URLs() ([]util.URL, []mastodon.Mention, []mastodon.Tag, int) {
|
|
|
|
|
return nil, nil, nil, 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *TagItem) Filtered() (bool, string) {
|
|
|
|
|
return false, ""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (t *TagItem) Pinned() bool {
|
|
|
|
|
return false
|
|
|
|
|
}
|