Initial commit
This commit is contained in:
292
furcadia/pounce.go
Normal file
292
furcadia/pounce.go
Normal file
@@ -0,0 +1,292 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user