From 741e8291c2d495088a45bc2181fce052bfc1f8f6 Mon Sep 17 00:00:00 2001 From: "Diogo Franco (Kovensky)" Date: Wed, 17 Jul 2013 17:58:06 -0300 Subject: [PATCH] anidb: Implement USER --- misc.go | 2 + user.go | 9 +++ usercache.go | 189 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+) create mode 100644 user.go create mode 100644 usercache.go diff --git a/misc.go b/misc.go index b6d9db8..8393a0f 100644 --- a/misc.go +++ b/misc.go @@ -15,6 +15,8 @@ var ( GroupCacheDuration = 4 * DefaultCacheDuration // They don't change that often. FileCacheDuration = 8 * DefaultCacheDuration // These change even less often. + UIDCacheDuration = 16 * DefaultCacheDuration // Can these even be changed? + // Used for anime that have already finished airing. // It's unlikely that they get any important updates. FinishedAnimeCacheDuration = 4 * AnimeCacheDuration diff --git a/user.go b/user.go new file mode 100644 index 0000000..33625bd --- /dev/null +++ b/user.go @@ -0,0 +1,9 @@ +package anidb + +type User struct { + UID UID + + Username string + + // MyList MyList +} diff --git a/usercache.go b/usercache.go new file mode 100644 index 0000000..9ad8113 --- /dev/null +++ b/usercache.go @@ -0,0 +1,189 @@ +package anidb + +import ( + "github.com/Kovensky/go-anidb/udp" + "github.com/Kovensky/go-fscache" + "strconv" + "strings" + "sync" + "time" +) + +type UID int + +func (adb *AniDB) GetCurrentUser() <-chan *User { + ch := make(chan *User, 1) + + if adb.udp.credentials == nil { + ch <- nil + close(ch) + return ch + } + + return adb.GetUserByName(decrypt(adb.udp.credentials.username)) +} + +// This is an (almost) entirely local representation. +func (adb *AniDB) GetUserByID(uid UID) <-chan *User { + key := []fscache.CacheKey{"user", uid} + ch := make(chan *User, 1) + + if uid < 1 { + ch <- nil + close(ch) + return ch + } + + ic := make(chan notification, 1) + go func() { ch <- (<-ic).(*User); close(ch) }() + if intentMap.Intent(ic, key...) { + return ch + } + + if !Cache.IsValid(InvalidKeyCacheDuration, key...) { + intentMap.NotifyClose((*User)(nil), key...) + return ch + } + + go func() { + var user *User + if CacheGet(&user, key...) == nil { + intentMap.NotifyClose(user, key...) + return + } + <-adb.GetUserName(uid) + + CacheGet(&user, key...) + intentMap.NotifyClose(user) + }() + return ch +} + +func (adb *AniDB) GetUserByName(username string) <-chan *User { + ch := make(chan *User, 1) + + if username == "" { + ch <- nil + close(ch) + return ch + } + + go func() { + ch <- <-adb.GetUserByID(<-adb.GetUserUID(username)) + close(ch) + }() + return ch +} + +func (adb *AniDB) GetUserUID(username string) <-chan UID { + key := []fscache.CacheKey{"user", "by-name", username} + ch := make(chan UID, 1) + + if username == "" { + ch <- 0 + close(ch) + return ch + } + + ic := make(chan notification, 1) + go func() { ch <- (<-ic).(UID); close(ch) }() + if intentMap.Intent(ic, key...) { + return ch + } + + if !Cache.IsValid(InvalidKeyCacheDuration, key...) { + intentMap.NotifyClose((UID)(0), key...) + return ch + } + + uid := UID(0) + switch ts, err := Cache.Get(&uid, key...); { + case err == nil && time.Now().Sub(ts) < UIDCacheDuration: + intentMap.NotifyClose(uid, key...) + return ch + } + + go func() { + reply := <-adb.udp.SendRecv("USER", + paramMap{"user": username}) + + switch reply.Code() { + case 295: + uid, _ = parseUserReply(reply) // caches + case 394: + Cache.SetInvalid(key...) + } + + intentMap.NotifyClose(uid, key...) + }() + return ch +} + +func (adb *AniDB) GetUserName(uid UID) <-chan string { + key := []fscache.CacheKey{"user", "by-uid", uid} + ch := make(chan string, 1) + + if uid < 1 { + ch <- "" + close(ch) + return ch + } + + ic := make(chan notification, 1) + go func() { ch <- (<-ic).(string); close(ch) }() + if intentMap.Intent(ic, key...) { + return ch + } + + if !Cache.IsValid(InvalidKeyCacheDuration, key...) { + intentMap.NotifyClose("", key...) + return ch + } + + name := "" + switch ts, err := Cache.Get(&name, key...); { + case err == nil && time.Now().Sub(ts) < UIDCacheDuration: + intentMap.NotifyClose(name, key...) + return ch + } + + go func() { + reply := <-adb.udp.SendRecv("USER", + paramMap{"uid": uid}) + + switch reply.Code() { + case 295: + _, name = parseUserReply(reply) // caches + case 394: + Cache.SetInvalid(key...) + } + + intentMap.NotifyClose(name, key...) + }() + return ch +} + +var userReplyMutex sync.Mutex + +func parseUserReply(reply udpapi.APIReply) (UID, string) { + userReplyMutex.Lock() + defer userReplyMutex.Unlock() + + if reply.Error() == nil { + parts := strings.Split(reply.Lines()[1], "|") + id, _ := strconv.ParseInt(parts[0], 10, 32) + + CacheSet(UID(id), "user", "by-name", parts[1]) + CacheSet(parts[1], "user", "by-uid", id) + + if _, err := Cache.Stat("user", id); err != nil { + CacheSet(&User{ + UID: UID(id), + Username: parts[1], + }, "user", id) + } + + return UID(id), parts[1] + } + return 0, "" +} -- 2.44.0