func (self *Phase) Schedule(c common.SkinnyContext) error { if !self.Resolved { ep, err := epoch.Get(c.DB()) if err != nil { return err } timeout := self.Deadline - ep c.BetweenTransactions(func(c common.SkinnyContext) { if timeout > 0 { time.AfterFunc(timeout, func() { if err := self.autoResolve(c); err != nil { c.Errorf("Failed resolving %+v after %v: %v", self, timeout, err) } }) c.Debugf("Scheduled resolution of %v/%v in %v at %v", self.GameId, self.Id, timeout, time.Now().Add(timeout)) } else { c.Debugf("Resolving %v/%v immediately, it is %v overdue", self.GameId, self.Id, -timeout) if err := self.autoResolve(c); err != nil { c.Errorf("Failed resolving %+v immediately: %v", self, err) } } }) } return nil }
func IncomingMail(c common.SkinnyContext, msg *enmime.MIMEBody) (err error) { text := gmail.DecodeText(msg.Text, msg.GetHeader("Content-Type")) c.Debugf("Incoming mail to %#v\n%v", msg.GetHeader("To"), text) if match := gmail.AddrReg.FindString(msg.GetHeader("To")); match != "" { lines := []string{} mailUser := strings.Split(c.SendAddress(), "@")[0] for _, line := range strings.Split(text, "\n") { if !strings.Contains(line, mailUser) && strings.Index(line, ">") != 0 { lines = append(lines, line) } } for len(lines) > 0 && strings.TrimSpace(lines[0]) == "" { lines = lines[1:] } for len(lines) > 0 && strings.TrimSpace(lines[len(lines)-1]) == "" { lines = lines[:len(lines)-1] } if len(lines) > 0 { if match2 := emailPlusReg.FindStringSubmatch(match); match2 != nil { var tag *MailTag if tag, err = DecodeMailTag(c.Secret(), match2[1]); err == nil { sender := &Member{Id: tag.R} if err = c.DB().Get(sender); err != nil { return } parent := &Message{Id: tag.M} if err = c.DB().Get(parent); err != nil { return } game := &Game{Id: parent.GameId} if err = c.DB().Get(game); err != nil { return } message := &Message{ Body: strings.TrimSpace(strings.Join(lines, "\n")), GameId: game.Id, RecipientIds: parent.RecipientIds, } c.Infof("Mail resulted in %+v from %+v", message, sender.Nation) return message.Send(c, game, sender) } } } } return nil }
func Start(c common.SkinnyContext) (err error) { startedAt, err := Get(c.DB()) if err != nil { return } c.Infof("Started at epoch %v", startedAt) startedTime := time.Now() var currently time.Duration go func() { for { time.Sleep(time.Minute) currently = time.Now().Sub(startedTime) + startedAt atomic.StoreInt64(&deltaPoint, int64(time.Now().UnixNano())) if err = Set(c.DB(), currently); err != nil { panic(err) } c.Debugf("Epoch %v", currently) } }() return }
func (self *Message) Send(c common.SkinnyContext, game *Game, sender *Member) (err error) { c.Debugf("Sending %#v from %#v in %#v", self.Body, sender.Nation, game.Id.String()) // make sure the sender is correct self.SenderId = sender.Id senderUser := &user.User{Id: sender.UserId} if err = c.DB().Get(senderUser); err != nil { return } // make sure the sender is one of the recipients self.RecipientIds[sender.Id.String()] = true // The sender but nobody else saw it... self.SeenBy = map[string]bool{ sender.Id.String(): true, } // See what phase type the game is in var phaseType dip.PhaseType switch game.State { case common.GameStateCreated: phaseType = common.BeforeGamePhaseType case common.GameStateStarted: var phase *Phase if _, phase, err = game.Phase(c.DB(), 0); err != nil { return } phaseType = phase.Type case common.GameStateEnded: phaseType = common.AfterGamePhaseType default: err = fmt.Errorf("Unknown game state for %+v", game) return } // Find what chats are allowed during this phase type allowedFlags := game.ChatFlags[phaseType] // See if the recipient count is allowed recipients := len(self.RecipientIds) if recipients == 2 { if (allowedFlags & common.ChatPrivate) == 0 { err = IllegalMessageError{ Description: fmt.Sprintf("%+v does not allow %+v during %+v", game, self, phaseType), Phrase: "This kind of message is not allowed at this stage of the game", } return } } else if recipients == len(common.VariantMap[game.Variant].Nations) { if (allowedFlags & common.ChatConference) == 0 { err = IllegalMessageError{ Description: fmt.Sprintf("%+v does not allow %+v during %+v", game, self, phaseType), Phrase: "This kind of message is not allowed at this stage of the game", } return } } else if recipients > 2 { if (allowedFlags & common.ChatGroup) == 0 { err = IllegalMessageError{ Description: fmt.Sprintf("%+v does not allow %+v during %+v", game, self, phaseType), Phrase: "This kind of message is not allowed at this stage of the game", } return } } else { err = fmt.Errorf("%+v doesn't have any recipients", self) return } members, err := game.Members(c.DB()) if err != nil { return } if err = c.DB().Set(self); err != nil { return } recipNations := sort.StringSlice{} for memberId, _ := range self.RecipientIds { for _, member := range members { if memberId == member.Id.String() { if member.Nation != "" { recipNations = append(recipNations, string(member.Nation)) } } } } sort.Sort(recipNations) recipName := strings.Join(recipNations, ", ") subKey := fmt.Sprintf("/games/%v/messages", game.Id) for memberId, _ := range self.RecipientIds { for _, member := range members { if memberId == member.Id.String() && self.SenderId.String() != memberId { user := &user.User{Id: member.UserId} if err = c.DB().Get(user); err == nil { if !user.MessageEmailDisabled { if !c.IsSubscribing(user.Email, subKey) { memberCopy := member gameDescription := "" if gameDescription, err = game.Describe(c, user); err == nil { go self.EmailTo(c, game, sender, senderUser, &memberCopy, user, gameDescription, recipName) } else { c.Errorf("Trying to describe %+v to %+v: %v", game, user, err) } } else { c.Infof("Not sending to %#v, already subscribing to %#v", user.Id.String(), subKey) } } else { c.Infof("Not sending to %#v, message email disabled", user.Id.String()) } } else { c.Errorf("Trying to load user %#v: %v", member.UserId.String(), err) } } } } return }