293 lines
9.2 KiB
Go
293 lines
9.2 KiB
Go
package furcadia
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// CONSTANTS //////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const (
|
|
POUNCE_API_BASE_URL = "http://on.furcadia.com/q/"
|
|
)
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// TYPE: OnlineState //////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type OnlineState int
|
|
|
|
const (
|
|
StateUnknown = iota
|
|
StateOffline
|
|
StateOnline
|
|
)
|
|
|
|
var OnlineStateName = map[OnlineState]string{
|
|
StateUnknown: "unknown",
|
|
StateOffline: "offline",
|
|
StateOnline: "online",
|
|
}
|
|
|
|
func (state OnlineState) String() string {
|
|
return OnlineStateName[state]
|
|
}
|
|
|
|
func (state OnlineState) IsOnline() bool {
|
|
return state == StateOnline
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// TYPE: PounceFurre //////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type PounceFurre struct {
|
|
DisplayName string
|
|
LastState OnlineState
|
|
}
|
|
|
|
func (pf *PounceFurre) GetShortName() string {
|
|
return GetShortName(pf.DisplayName)
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// TYPE: PounceDream //////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type PounceDream struct {
|
|
Title string // title of the dream, or its full dream url
|
|
ShortURL string // dream url without the "furc://" prefix or the "/" suffix - just "uploader:name"
|
|
LastState OnlineState // last known state of this dream
|
|
}
|
|
|
|
// GetDreamURL returns a full Furcadia dream URL for this dream.
|
|
func (pd *PounceDream) GetDreamURL() string {
|
|
return "furc://" + pd.ShortURL + "/"
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// TYPE: PounceList ///////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type PounceList struct {
|
|
Furres map[string]*PounceFurre // shortname -> *PounceFurre
|
|
Dreams map[string]*PounceDream // shortname -> *PounceDream
|
|
}
|
|
|
|
func NewPounceList() *PounceList {
|
|
return &PounceList{
|
|
Furres: make(map[string]*PounceFurre),
|
|
Dreams: make(map[string]*PounceDream),
|
|
}
|
|
}
|
|
|
|
// GetQueryString returns an HTTP query element of a URL for an online check.
|
|
// The string contains all furrenly watched furres and dreams whose status our
|
|
// Pounce client wants to check.
|
|
func (p *PounceList) GetQueryString() string {
|
|
sb := strings.Builder{}
|
|
|
|
for _, furre := range p.Furres {
|
|
sb.WriteString(fmt.Sprintf("u[]=%s&", furre.GetShortName()))
|
|
}
|
|
|
|
for _, dream := range p.Dreams {
|
|
sb.WriteString(fmt.Sprintf("d[]=%s&", dream.ShortURL))
|
|
}
|
|
|
|
return strings.TrimSuffix(sb.String(), "&")
|
|
}
|
|
|
|
// AddFurre adds a furre to the Pounce list, and returns the record of that
|
|
// furre back to the caller.
|
|
//
|
|
// If a record for the given name already exists, then the existing record is
|
|
// returned along with ErrExists.
|
|
func (p *PounceList) AddFurre(name string) (*PounceFurre, error) {
|
|
shortname := GetShortName(name)
|
|
oldFurre, ok := p.Furres[shortname]
|
|
if ok {
|
|
return oldFurre, ErrExists
|
|
}
|
|
|
|
newFurre := p._newFurre(name)
|
|
if newFurre.GetShortName() != shortname {
|
|
panic(fmt.Sprintf("shortname inconsistency: newFurre.GetShortName()=%v; GetShortName(%v)=%v", newFurre.GetShortName(), name, shortname))
|
|
}
|
|
|
|
p.Furres[shortname] = newFurre
|
|
return newFurre, nil
|
|
}
|
|
|
|
// DeleteFurre deletes a furre from the Pounce list. If no furre by that name
|
|
// exists, the method returns ErrNotFound.
|
|
func (p *PounceList) DeleteFurre(name string) error {
|
|
shortname := GetShortName(name)
|
|
_, ok := p.Furres[shortname]
|
|
if !ok {
|
|
return ErrNotFound
|
|
}
|
|
|
|
delete(p.Furres, shortname)
|
|
return nil
|
|
}
|
|
|
|
// AddDream adds a dream to the Pounce list, and returns the record of that
|
|
// dream back to the caller.
|
|
//
|
|
// This method accepts the uploader character name, and the "dream name" string
|
|
// from which a dream url is constructed. The "dreamName" argument MAY be an
|
|
// empty string.
|
|
//
|
|
// If a dream record for the given dream url already exists, then the existing
|
|
// record is returned instead, along with ErrExists.
|
|
func (p *PounceList) AddDream(uploaderName, dreamName string) (*PounceDream, error) {
|
|
shortURL := GetShortDreamURLFromName(uploaderName, dreamName)
|
|
|
|
var title string
|
|
if dreamName == "" {
|
|
title = uploaderName
|
|
} else {
|
|
title = uploaderName + ": " + dreamName
|
|
}
|
|
|
|
return p.__addDream(shortURL, title)
|
|
}
|
|
|
|
// AddDreamURL adds a dream to the Pounce list, and returns the record of that
|
|
// dream back to the caller. This method accepts a Furcadia dream URL.
|
|
//
|
|
// If the URL is invalid, ErrInvalidDreamURL will be returned without a
|
|
// PounceDream object.
|
|
//
|
|
// If a dream record for the given dream URL already exists, then the existing
|
|
// record is returned instead, along with ErrExists.
|
|
func (p *PounceList) AddDreamURL(url string) (*PounceDream, error) {
|
|
shortURL, err := GetShortDreamURL(url)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return p.__addDream(shortURL, url)
|
|
}
|
|
|
|
// DeleteDream deletes a dream from the Pounce list.
|
|
// If no dream by this criteria exists, the method returns ErrNotFound.
|
|
func (p *PounceList) DeleteDream(uploaderName, dreamName string) error {
|
|
shortURL := GetShortDreamURLFromName(uploaderName, dreamName)
|
|
return p.__delDream(shortURL)
|
|
}
|
|
|
|
// DeleteDreamURL deletes a dream from the Pouince list.
|
|
// If no dream by this dream URL exists, the method returns ErrNotFound.
|
|
// If provided dream URL is invalid, the method returns ErrInvalidDreamURL.
|
|
func (p *PounceList) DeleteDreamURL(url string) error {
|
|
shortURL, err := GetShortDreamURL(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return p.__delDream(shortURL)
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
func (p *PounceList) __addDream(shortURL, title string) (*PounceDream, error) {
|
|
oldDream, ok := p.Dreams[shortURL]
|
|
if ok {
|
|
return oldDream, ErrExists
|
|
}
|
|
|
|
dream := p._newDream(shortURL, title)
|
|
p.Dreams[shortURL] = dream
|
|
return dream, nil
|
|
}
|
|
|
|
func (p *PounceList) __delDream(shortURL string) error {
|
|
_, ok := p.Dreams[shortURL]
|
|
if !ok {
|
|
return ErrNotFound
|
|
}
|
|
|
|
delete(p.Dreams, shortURL)
|
|
return nil
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// _newFurre creates a new PounceFurre object with the given display name.
|
|
//
|
|
// displayName is the full name of the furre; it may contain pipes or spaces
|
|
// in it.
|
|
func (p *PounceList) _newFurre(displayName string) *PounceFurre {
|
|
return &PounceFurre{
|
|
DisplayName: displayName,
|
|
LastState: StateUnknown,
|
|
}
|
|
}
|
|
|
|
// _newDream creates a new PounceDream object with the given parameters.
|
|
//
|
|
// The "title" argument specifies a "name" for this dream. If title is an empty
|
|
// string, then the title will be this dream's full dream url.
|
|
func (p *PounceList) _newDream(shortDreamURL, title string) *PounceDream {
|
|
pd := &PounceDream{
|
|
Title: title,
|
|
ShortURL: shortDreamURL,
|
|
LastState: StateUnknown,
|
|
}
|
|
|
|
// if no title provided, use the full dream URL as the title
|
|
if pd.Title == "" {
|
|
pd.Title = pd.GetDreamURL()
|
|
}
|
|
|
|
return pd
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// TYPE: PounceResponse ///////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
const MIN_POLL_INTERVAL_MSEC = 10000
|
|
|
|
type PounceResponse struct {
|
|
NumOnlineFurres int // O422
|
|
NumOnlineDreams int // D751
|
|
PollIntervalMsec int // T30000
|
|
OnlineFurres []string // @10 Albert|Quirky
|
|
OnlineDreams []string // #1furc://allegria:/ #1furc://allegria/
|
|
}
|
|
|
|
func ParsePounceResponse(r []byte) (*PounceResponse, error) {
|
|
//// Example Input:
|
|
// https://on.furcadia.com/q/?u[]=artex&u[]=albertquirky&d[]=allegria&d[]=ansteorrakingdom
|
|
//
|
|
//// Example Output:
|
|
// T30000 @10 Albert|Quirky O422 #1furc://allegria:/ #1furc://allegria/ #1furc://ansteorrakingdom:medievaltradecity/ #1furc://ansteorrakingdom/ #1furc://ansteorrakingdom:medievaltradecity/ #1furc://ansteorrakingdom/ D751
|
|
//
|
|
|
|
// TODO: Split into lines
|
|
// TODO: Process each line
|
|
return nil, ErrNotImplemented
|
|
}
|
|
|
|
// ToBytes creates a response string much like a pounce server would produce.
|
|
// This method is mostly useful for testing, or if you wish to implement your
|
|
// own pounce service that's compatible with the official Pounce client.
|
|
func (r *PounceResponse) ToBytes() (output []byte) {
|
|
output = fmt.Appendf(output, "T%d\n", r.PollIntervalMsec)
|
|
for _, furre := range r.OnlineFurres {
|
|
output = fmt.Appendf(output, "@10 %s\n", furre)
|
|
}
|
|
|
|
output = fmt.Appendf(output, "O%d\n", r.NumOnlineFurres)
|
|
for _, dreamUrl := range r.OnlineDreams {
|
|
output = fmt.Appendf(output, "#1%s\n", dreamUrl)
|
|
}
|
|
|
|
output = fmt.Appendf(output, "D%d\n", r.NumOnlineDreams)
|
|
return
|
|
}
|