2020-05-20 07:13:12 +00:00
|
|
|
package mock
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"math/rand"
|
2020-05-20 21:23:44 +00:00
|
|
|
"strconv"
|
2020-05-22 20:57:35 +00:00
|
|
|
"strings"
|
2020-05-20 07:13:12 +00:00
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/Pallinder/go-randomdata"
|
|
|
|
"github.com/diamondburned/cchat"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Channel struct {
|
2020-05-23 02:44:50 +00:00
|
|
|
session *Session
|
2020-05-20 21:23:44 +00:00
|
|
|
id uint32
|
2020-05-20 07:13:12 +00:00
|
|
|
name string
|
|
|
|
done chan struct{}
|
2020-06-03 23:13:06 +00:00
|
|
|
send chan cchat.SendableMessage // ideally this should be another type
|
2020-05-20 07:13:12 +00:00
|
|
|
lastID uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2020-05-22 20:57:35 +00:00
|
|
|
_ cchat.Server = (*Channel)(nil)
|
|
|
|
_ cchat.ServerMessage = (*Channel)(nil)
|
|
|
|
_ cchat.ServerMessageSender = (*Channel)(nil)
|
|
|
|
_ cchat.ServerMessageSendCompleter = (*Channel)(nil)
|
2020-05-20 07:13:12 +00:00
|
|
|
)
|
|
|
|
|
2020-05-20 21:23:44 +00:00
|
|
|
func (ch *Channel) ID() string {
|
|
|
|
return strconv.Itoa(int(ch.id))
|
|
|
|
}
|
|
|
|
|
2020-05-20 07:13:12 +00:00
|
|
|
func (ch *Channel) Name() (string, error) {
|
|
|
|
return ch.name, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ch *Channel) JoinServer(container cchat.MessagesContainer) error {
|
2020-06-03 23:13:06 +00:00
|
|
|
var lastAuthor string
|
|
|
|
|
|
|
|
var nextID = func() uint32 {
|
|
|
|
id := ch.lastID
|
|
|
|
ch.lastID++
|
|
|
|
return id
|
|
|
|
}
|
|
|
|
var readID = func() uint32 {
|
|
|
|
return atomic.LoadUint32(&ch.lastID)
|
|
|
|
}
|
|
|
|
var randomMsg = func() Message {
|
|
|
|
msg := randomMessage(nextID())
|
|
|
|
lastAuthor = msg.author
|
|
|
|
return msg
|
2020-05-20 07:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write the backlog.
|
|
|
|
for i := 0; i < 30; i++ {
|
2020-06-03 23:13:06 +00:00
|
|
|
container.CreateMessage(randomMsg())
|
2020-05-20 07:13:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ch.done = make(chan struct{})
|
2020-06-03 23:13:06 +00:00
|
|
|
ch.send = make(chan cchat.SendableMessage)
|
|
|
|
|
2020-05-20 07:13:12 +00:00
|
|
|
go func() {
|
2020-06-03 23:13:06 +00:00
|
|
|
ticker := time.NewTicker(4 * time.Second)
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
|
editTick := time.NewTicker(10 * time.Second)
|
|
|
|
defer editTick.Stop()
|
|
|
|
|
|
|
|
deleteTick := time.NewTicker(15 * time.Second)
|
|
|
|
defer deleteTick.Stop()
|
|
|
|
|
2020-05-20 07:13:12 +00:00
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case msg := <-ch.send:
|
2020-06-03 23:13:06 +00:00
|
|
|
container.CreateMessage(echoMessage(msg, nextID(), ch.session.username))
|
|
|
|
case <-ticker.C:
|
|
|
|
container.CreateMessage(randomMsg())
|
|
|
|
case <-editTick.C:
|
|
|
|
container.UpdateMessage(newRandomMessage(readID(), lastAuthor))
|
|
|
|
case <-deleteTick.C:
|
|
|
|
container.DeleteMessage(newEmptyMessage(readID(), lastAuthor))
|
2020-05-20 07:13:12 +00:00
|
|
|
case <-ch.done:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ch *Channel) LeaveServer() error {
|
|
|
|
ch.done <- struct{}{}
|
2020-06-03 23:13:06 +00:00
|
|
|
ch.send = nil
|
2020-05-20 07:13:12 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
|
|
|
|
if emulateAustralianInternet() {
|
|
|
|
return errors.New("Failed to send message: Australian Internet unsupported.")
|
|
|
|
}
|
|
|
|
|
2020-06-03 23:13:06 +00:00
|
|
|
go func() {
|
|
|
|
// Make no guarantee that a message may arrive immediately when the
|
|
|
|
// function exits.
|
|
|
|
<-time.After(time.Second)
|
|
|
|
ch.send <- msg
|
|
|
|
}()
|
|
|
|
|
2020-05-20 07:13:12 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-05-22 20:57:35 +00:00
|
|
|
func (ch *Channel) CompleteMessage(words []string, i int) []string {
|
|
|
|
switch {
|
|
|
|
case strings.HasPrefix("complete", words[i]):
|
|
|
|
words[i] = "complete"
|
|
|
|
case strings.HasPrefix("me", words[i]) && i > 0 && words[i-1] == "complete":
|
|
|
|
words[i] = "me"
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return words
|
|
|
|
}
|
|
|
|
|
2020-05-23 02:44:50 +00:00
|
|
|
func generateChannels(s *Session, amount int) []cchat.Server {
|
2020-05-20 07:13:12 +00:00
|
|
|
var channels = make([]cchat.Server, amount)
|
|
|
|
for i := range channels {
|
2020-05-20 21:23:44 +00:00
|
|
|
channels[i] = &Channel{
|
|
|
|
session: s,
|
|
|
|
id: atomic.AddUint32(&s.lastid, 1),
|
|
|
|
name: "#" + randomdata.Noun(),
|
|
|
|
}
|
2020-05-20 07:13:12 +00:00
|
|
|
}
|
|
|
|
return channels
|
|
|
|
}
|
|
|
|
|
|
|
|
// emulate network latency
|
|
|
|
func emulateAustralianInternet() (lost bool) {
|
2020-05-29 18:41:40 +00:00
|
|
|
var ms = rand.Intn(internetMaxLatency-internetMinLatency) + internetMinLatency
|
2020-05-20 07:13:12 +00:00
|
|
|
<-time.After(time.Duration(ms) * time.Millisecond)
|
|
|
|
|
|
|
|
// because australia, drop packet 20% of the time if internetCanFail is
|
|
|
|
// true.
|
|
|
|
return internetCanFail && rand.Intn(100) < 20
|
|
|
|
}
|