2025-01-17 15:30:01 +00:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
2025-01-29 16:48:12 +00:00
|
|
|
"database/sql/driver"
|
2025-01-17 15:30:01 +00:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
2025-02-10 14:36:13 +00:00
|
|
|
"math/rand"
|
2025-02-05 19:38:30 +00:00
|
|
|
"time"
|
2025-01-17 15:30:01 +00:00
|
|
|
)
|
|
|
|
|
2025-01-29 16:48:12 +00:00
|
|
|
type Actions map[string]json.RawMessage
|
|
|
|
|
|
|
|
func (a Actions) Value() (driver.Value, error) {
|
|
|
|
return json.Marshal(a)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *Actions) Scan(value interface{}) error {
|
|
|
|
return json.Unmarshal([]byte(value.(string)), a)
|
|
|
|
}
|
|
|
|
|
2025-01-17 15:30:01 +00:00
|
|
|
type Item struct {
|
2025-01-29 16:48:12 +00:00
|
|
|
Source string `json:"source"`
|
|
|
|
Id string `json:"id"`
|
|
|
|
Created int `json:"created"`
|
|
|
|
Active bool `json:"active"`
|
|
|
|
Title string `json:"title"`
|
|
|
|
Author string `json:"author"`
|
|
|
|
Body string `json:"body"`
|
|
|
|
Link string `json:"link"`
|
|
|
|
Time int `json:"time"`
|
2025-02-05 06:56:26 +00:00
|
|
|
Ttl int `json:"ttl"`
|
|
|
|
Ttd int `json:"ttd"`
|
|
|
|
Tts int `json:"tts"`
|
2025-01-29 16:48:12 +00:00
|
|
|
Action Actions `json:"action"`
|
2025-01-17 15:30:01 +00:00
|
|
|
}
|
|
|
|
|
2025-02-05 19:38:30 +00:00
|
|
|
func (item Item) TtlTime() time.Time {
|
|
|
|
return time.Unix(int64(item.Created)+int64(item.Ttl), 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (item Item) TtdTime() time.Time {
|
|
|
|
return time.Unix(int64(item.Created)+int64(item.Ttd), 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (item Item) TtsTime() time.Time {
|
|
|
|
return time.Unix(int64(item.Created)+int64(item.Tts), 0)
|
|
|
|
}
|
|
|
|
|
2025-02-06 15:28:53 +00:00
|
|
|
func (item Item) Visible() bool {
|
|
|
|
now := time.Now() // TODO pass this value in
|
|
|
|
return item.Active && now.After(item.TtsTime())
|
|
|
|
}
|
|
|
|
|
2025-01-23 20:26:21 +00:00
|
|
|
// Whether an item that no longer appears in a fetch can be deleted.
|
2025-02-05 19:38:30 +00:00
|
|
|
func (item Item) Deletable(now time.Time) bool {
|
|
|
|
if item.Ttl != 0 && item.TtlTime().After(now) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if item.Ttd != 0 && item.TtdTime().Before(now) {
|
|
|
|
return true
|
|
|
|
}
|
2025-01-23 20:26:21 +00:00
|
|
|
return !item.Active
|
|
|
|
}
|
|
|
|
|
2025-01-29 16:14:49 +00:00
|
|
|
func ItemsAreEqual(first Item, second Item) bool {
|
|
|
|
// Hacky but easy to use
|
|
|
|
return fmt.Sprintf("%#v", first) == fmt.Sprintf("%#v", second)
|
|
|
|
}
|
|
|
|
|
2025-01-17 15:30:01 +00:00
|
|
|
func FormatAsHeadline(item Item) string {
|
|
|
|
title := item.Title
|
|
|
|
if title == "" {
|
|
|
|
title = item.Id
|
|
|
|
}
|
|
|
|
return title
|
|
|
|
}
|
|
|
|
|
|
|
|
func FormatAsJson(item Item) string {
|
|
|
|
data, err := json.Marshal(item)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("error: failed to serialize %s/%s: %v", item.Source, item.Id, err)
|
|
|
|
}
|
|
|
|
return string(data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func FormatAsShort(item Item) string {
|
|
|
|
return fmt.Sprintf("%s/%s", item.Source, item.Id)
|
|
|
|
}
|
2025-01-17 21:49:23 +00:00
|
|
|
|
|
|
|
func FormatAs(format string) (func(item Item) string, error) {
|
|
|
|
switch format {
|
|
|
|
case "headlines":
|
|
|
|
return FormatAsHeadline, nil
|
|
|
|
case "json":
|
|
|
|
return FormatAsJson, nil
|
|
|
|
case "short":
|
|
|
|
return FormatAsShort, nil
|
|
|
|
default:
|
2025-01-23 17:08:17 +00:00
|
|
|
return nil, fmt.Errorf("invalid format '%s'", format)
|
2025-01-17 21:49:23 +00:00
|
|
|
}
|
|
|
|
}
|
2025-01-23 21:22:38 +00:00
|
|
|
|
|
|
|
var AvailableFormats = map[string]string{
|
|
|
|
"headlines": "Only item titles",
|
|
|
|
"json": "Full item JSON",
|
|
|
|
"short": "Item source and id",
|
|
|
|
}
|
2025-02-10 14:36:13 +00:00
|
|
|
|
|
|
|
const hexDigits = "0123456789abcdef"
|
|
|
|
|
|
|
|
func RandomHex(n int) string {
|
|
|
|
bytes := make([]byte, n)
|
|
|
|
for i := range bytes {
|
|
|
|
bytes[i] = hexDigits[rand.Intn(len(hexDigits))]
|
|
|
|
}
|
|
|
|
return string(bytes)
|
|
|
|
}
|