@@ -8,6 +8,8 @@ import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"git.aiterp.net/rpdata/api/models/logs"
|
||||
|
||||
"git.aiterp.net/rpdata/api/graph2"
|
||||
"git.aiterp.net/rpdata/api/internal/auth"
|
||||
"git.aiterp.net/rpdata/api/internal/loader"
|
||||
@@ -24,6 +26,15 @@ func main() {
|
||||
http.Handle("/", handler.Playground("RPData API", "/graphql"))
|
||||
http.Handle("/graphql", queryHandler())
|
||||
|
||||
go func() {
|
||||
err := logs.RunFullUpdate()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
log.Println("Characters updated")
|
||||
}()
|
||||
|
||||
log.Fatal(http.ListenAndServe(":8081", nil))
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"git.aiterp.net/rpdata/api/internal/auth"
|
||||
"git.aiterp.net/rpdata/api/models"
|
||||
"git.aiterp.net/rpdata/api/models/characters"
|
||||
"git.aiterp.net/rpdata/api/models/logs"
|
||||
)
|
||||
|
||||
// Queries
|
||||
@@ -59,6 +60,8 @@ func (r *mutationResolver) AddCharacter(ctx context.Context, input input.Charact
|
||||
author = *input.Author
|
||||
}
|
||||
|
||||
logs.ScheduleFullUpdate()
|
||||
|
||||
return characters.Add(input.Nick, input.Name, shortName, author, description)
|
||||
}
|
||||
|
||||
@@ -77,6 +80,8 @@ func (r *mutationResolver) AddCharacterNick(ctx context.Context, input input.Cha
|
||||
return models.Character{}, errors.New("You are not permitted to edit this character")
|
||||
}
|
||||
|
||||
logs.ScheduleFullUpdate()
|
||||
|
||||
return characters.AddNick(character, input.Nick)
|
||||
}
|
||||
|
||||
@@ -91,6 +96,8 @@ func (r *mutationResolver) RemoveCharacterNick(ctx context.Context, input input.
|
||||
return models.Character{}, errors.New("You are not permitted to edit this character")
|
||||
}
|
||||
|
||||
logs.ScheduleFullUpdate()
|
||||
|
||||
return characters.RemoveNick(character, input.Nick)
|
||||
}
|
||||
|
||||
|
||||
+28
-1
@@ -52,7 +52,14 @@ func (r *mutationResolver) AddPost(ctx context.Context, input input.PostAddInput
|
||||
return models.Post{}, err
|
||||
}
|
||||
|
||||
return posts.Add(log, input.Time, input.Kind, input.Nick, input.Text)
|
||||
post, err := posts.Add(log, input.Time, input.Kind, input.Nick, input.Text)
|
||||
if err != nil {
|
||||
return models.Post{}, err
|
||||
}
|
||||
|
||||
go logs.UpdateCharacters(log)
|
||||
|
||||
return post, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInput) (models.Post, error) {
|
||||
@@ -66,6 +73,17 @@ func (r *mutationResolver) EditPost(ctx context.Context, input input.PostEditInp
|
||||
return models.Post{}, errors.New("Post not found")
|
||||
}
|
||||
|
||||
if input.Nick != nil {
|
||||
go func() {
|
||||
log, err := logs.FindID(post.LogID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logs.UpdateCharacters(log)
|
||||
}()
|
||||
}
|
||||
|
||||
return posts.Edit(post, input.Time, input.Kind, input.Nick, input.Text)
|
||||
}
|
||||
|
||||
@@ -94,5 +112,14 @@ func (r *mutationResolver) RemovePost(ctx context.Context, input input.PostRemov
|
||||
return models.Post{}, errors.New("Post not found (before removing, of course)")
|
||||
}
|
||||
|
||||
go func() {
|
||||
log, err := logs.FindID(post.LogID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
logs.UpdateCharacters(log)
|
||||
}()
|
||||
|
||||
return posts.Remove(post)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var globalCtx, globalCancel = context.WithCancel(context.Background())
|
||||
|
||||
// A Task is a wrapper around a function that offers scheduling
|
||||
// and syncronization.
|
||||
type Task struct {
|
||||
mutex sync.Mutex
|
||||
ctx context.Context
|
||||
ctxCancel context.CancelFunc
|
||||
waitDuration time.Duration
|
||||
scheduled bool
|
||||
callback func() error
|
||||
}
|
||||
|
||||
// Context gets the task's context.
|
||||
func (task *Task) Context() context.Context {
|
||||
return task.ctx
|
||||
}
|
||||
|
||||
// Stop stops a task. This should not be done to stop a single run,
|
||||
// but as a way to cancel it forever.
|
||||
func (task *Task) Stop() {
|
||||
task.ctxCancel()
|
||||
}
|
||||
|
||||
// Schedule schedules a task for later execution.
|
||||
func (task *Task) Schedule() {
|
||||
// Don't if the context is closed.
|
||||
select {
|
||||
case <-task.Context().Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
task.mutex.Lock()
|
||||
if task.scheduled {
|
||||
task.mutex.Unlock()
|
||||
return
|
||||
}
|
||||
task.scheduled = true
|
||||
task.mutex.Unlock()
|
||||
|
||||
go func() {
|
||||
<-time.After(task.waitDuration)
|
||||
|
||||
// Mark as no longer scheduled
|
||||
task.mutex.Lock()
|
||||
task.scheduled = false
|
||||
task.mutex.Unlock()
|
||||
|
||||
// Don't if the task is cancelled
|
||||
select {
|
||||
case <-task.Context().Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
task.callback()
|
||||
}()
|
||||
}
|
||||
|
||||
// New makes a new task with the callback.
|
||||
func New(waitDuration time.Duration, callback func() error) *Task {
|
||||
ctx, ctxCancel := context.WithCancel(globalCtx)
|
||||
|
||||
return &Task{
|
||||
callback: callback,
|
||||
ctx: ctx,
|
||||
ctxCancel: ctxCancel,
|
||||
waitDuration: waitDuration,
|
||||
}
|
||||
}
|
||||
|
||||
// StopAll stops all tasks.
|
||||
func StopAll() {
|
||||
globalCancel()
|
||||
}
|
||||
@@ -25,10 +25,10 @@ func List(filter *Filter) ([]models.Character, error) {
|
||||
query["_id"] = filter.IDs[0]
|
||||
}
|
||||
|
||||
if len(filter.Nicks) > 1 {
|
||||
query["nicks"] = bson.M{"$in": filter.Nicks}
|
||||
} else if len(filter.Nicks) == 1 {
|
||||
if len(filter.Nicks) == 1 {
|
||||
query["nicks"] = filter.Nicks[0]
|
||||
} else if filter.Nicks != nil {
|
||||
query["nicks"] = bson.M{"$in": filter.Nicks}
|
||||
}
|
||||
|
||||
if len(filter.Names) > 1 {
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package logs
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.aiterp.net/rpdata/api/internal/task"
|
||||
"git.aiterp.net/rpdata/api/models"
|
||||
"git.aiterp.net/rpdata/api/models/characters"
|
||||
"git.aiterp.net/rpdata/api/models/posts"
|
||||
"github.com/globalsign/mgo/bson"
|
||||
)
|
||||
|
||||
var updateTask = task.New(time.Second*60, RunFullUpdate)
|
||||
|
||||
// UpdateCharacters updates the characters for the given log.
|
||||
func UpdateCharacters(log models.Log) (models.Log, error) {
|
||||
posts, err := posts.List(&posts.Filter{LogID: &log.ShortID, Kind: []string{"action", "text", "chars"}, Limit: 0})
|
||||
if err != nil {
|
||||
return models.Log{}, err
|
||||
}
|
||||
|
||||
added := make(map[string]bool)
|
||||
removed := make(map[string]bool)
|
||||
for _, post := range posts {
|
||||
if post.Kind == "text" || post.Kind == "action" {
|
||||
if strings.HasPrefix(post.Text, "(") || strings.Contains(post.Nick, "(") || strings.Contains(post.Nick, "[E]") {
|
||||
continue
|
||||
}
|
||||
|
||||
// Clean up the nick (remove possessive suffix, comma, formatting stuff)
|
||||
if strings.HasSuffix(post.Nick, "'s") || strings.HasSuffix(post.Nick, "`s") {
|
||||
post.Nick = post.Nick[:len(post.Nick)-2]
|
||||
} else if strings.HasSuffix(post.Nick, "'") || strings.HasSuffix(post.Nick, "`") || strings.HasSuffix(post.Nick, ",") || strings.HasSuffix(post.Nick, "\x0f") {
|
||||
post.Nick = post.Nick[:len(post.Nick)-1]
|
||||
}
|
||||
|
||||
added[post.Nick] = true
|
||||
}
|
||||
if post.Kind == "chars" {
|
||||
tokens := strings.Fields(post.Text)
|
||||
for _, token := range tokens {
|
||||
if strings.HasPrefix(token, "-") {
|
||||
removed[token[1:]] = true
|
||||
} else {
|
||||
added[strings.Replace(token, "+", "", 1)] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nicks := make([]string, 0, len(added))
|
||||
for nick := range added {
|
||||
if added[nick] && !removed[nick] {
|
||||
nicks = append(nicks, nick)
|
||||
}
|
||||
}
|
||||
|
||||
characters, err := characters.List(&characters.Filter{Nicks: nicks})
|
||||
if err != nil {
|
||||
return models.Log{}, err
|
||||
}
|
||||
|
||||
characterIDs := make([]string, len(characters))
|
||||
for i, char := range characters {
|
||||
characterIDs[i] = char.ID
|
||||
}
|
||||
|
||||
err = collection.UpdateId(log.ID, bson.M{"$set": bson.M{"characterIds": characterIDs}})
|
||||
if err != nil {
|
||||
return models.Log{}, err
|
||||
}
|
||||
|
||||
log.CharacterIDs = characterIDs
|
||||
|
||||
return log, nil
|
||||
}
|
||||
|
||||
// RunFullUpdate runs a full update on all logs.
|
||||
func RunFullUpdate() error {
|
||||
iter := iter(bson.M{}, 0)
|
||||
err := iter.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log := models.Log{}
|
||||
for iter.Next(&log) {
|
||||
_, err = UpdateCharacters(log)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = iter.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ScheduleFullUpdate runs a full character update within the next 60 seconds.
|
||||
func ScheduleFullUpdate() {
|
||||
updateTask.Schedule()
|
||||
}
|
||||
Reference in New Issue
Block a user