Bumped to v0.0.22
This commit is contained in:
parent
cd71430167
commit
4aadbbc16c
114
channel.go
114
channel.go
|
@ -17,6 +17,7 @@ import (
|
||||||
|
|
||||||
// FetchBacklog is the number of messages to fake-fetch.
|
// FetchBacklog is the number of messages to fake-fetch.
|
||||||
const FetchBacklog = 35
|
const FetchBacklog = 35
|
||||||
|
const maxBacklog = FetchBacklog * 2
|
||||||
|
|
||||||
// max number to add to before the next author, with rand.Intn(limit) + incr.
|
// max number to add to before the next author, with rand.Intn(limit) + incr.
|
||||||
const sameAuthorLimit = 12
|
const sameAuthorLimit = 12
|
||||||
|
@ -30,8 +31,8 @@ type Channel struct {
|
||||||
edit chan Message // id
|
edit chan Message // id
|
||||||
|
|
||||||
messageMutex sync.Mutex
|
messageMutex sync.Mutex
|
||||||
messageIDs map[string]int
|
|
||||||
messages []Message
|
messages []Message
|
||||||
|
messageixs map[uint32]int // indices
|
||||||
|
|
||||||
// used for unique ID generation of messages
|
// used for unique ID generation of messages
|
||||||
incrID uint32
|
incrID uint32
|
||||||
|
@ -39,7 +40,6 @@ type Channel struct {
|
||||||
// up to about 12 or so. check sameAuthorLimit.
|
// up to about 12 or so. check sameAuthorLimit.
|
||||||
incrAuthor uint8
|
incrAuthor uint8
|
||||||
|
|
||||||
//
|
|
||||||
busyWg sync.WaitGroup
|
busyWg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,24 +57,26 @@ func (ch *Channel) ID() string {
|
||||||
return strconv.Itoa(int(ch.id))
|
return strconv.Itoa(int(ch.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) Name(labeler cchat.LabelContainer) (func(), error) {
|
func (ch *Channel) Name() text.Rich {
|
||||||
labeler.SetLabel(text.Rich{Content: ch.name})
|
return text.Rich{Content: ch.name}
|
||||||
return func() {}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) Nickname(labeler cchat.LabelContainer) (func(), error) {
|
func (ch *Channel) Nickname(labeler cchat.LabelContainer) error {
|
||||||
|
// Simulate IO.
|
||||||
|
simulateAustralianInternet()
|
||||||
|
|
||||||
labeler.SetLabel(ch.username)
|
labeler.SetLabel(ch.username)
|
||||||
return func() {}, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) JoinServer(container cchat.MessagesContainer) (func(), error) {
|
func (ch *Channel) JoinServer(container cchat.MessagesContainer) (func(), error) {
|
||||||
// Is this a fresh channel? If yes, generate messages with some IO latency.
|
// Is this a fresh channel? If yes, generate messages with some IO latency.
|
||||||
if ch.messageIDs == nil || len(ch.messages) == 0 {
|
if len(ch.messages) == 0 || ch.messageixs == nil {
|
||||||
// Emulate IO.
|
// Simulate IO.
|
||||||
emulateAustralianInternet()
|
simulateAustralianInternet()
|
||||||
|
|
||||||
// Initialize.
|
// Initialize.
|
||||||
ch.messageIDs = map[string]int{}
|
ch.messageixs = make(map[uint32]int, FetchBacklog)
|
||||||
ch.messages = make([]Message, 0, FetchBacklog)
|
ch.messages = make([]Message, 0, FetchBacklog)
|
||||||
|
|
||||||
// Allocate 2 channels that we won't clean up, because we're lazy.
|
// Allocate 2 channels that we won't clean up, because we're lazy.
|
||||||
|
@ -85,6 +87,11 @@ func (ch *Channel) JoinServer(container cchat.MessagesContainer) (func(), error)
|
||||||
for i := 0; i < FetchBacklog; i++ {
|
for i := 0; i < FetchBacklog; i++ {
|
||||||
ch.addMessage(randomMessage(ch.nextID()), container)
|
ch.addMessage(randomMessage(ch.nextID()), container)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Else, flush the old backlog over.
|
||||||
|
for i := range ch.messages {
|
||||||
|
container.CreateMessage(ch.messages[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize channels for use.
|
// Initialize channels for use.
|
||||||
|
@ -129,10 +136,15 @@ func (ch *Channel) JoinServer(container cchat.MessagesContainer) (func(), error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) RawMessageContent(id string) (string, error) {
|
func (ch *Channel) RawMessageContent(id string) (string, error) {
|
||||||
|
i, err := parseID(id)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
ch.messageMutex.Lock()
|
ch.messageMutex.Lock()
|
||||||
defer ch.messageMutex.Unlock()
|
defer ch.messageMutex.Unlock()
|
||||||
|
|
||||||
ix, ok := ch.messageIDs[id]
|
ix, ok := ch.messageixs[i]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", errors.New("Message not found")
|
return "", errors.New("Message not found")
|
||||||
}
|
}
|
||||||
|
@ -141,63 +153,81 @@ func (ch *Channel) RawMessageContent(id string) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) EditMessage(id, content string) error {
|
func (ch *Channel) EditMessage(id, content string) error {
|
||||||
emulateAustralianInternet()
|
i, err := parseID(id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
simulateAustralianInternet()
|
||||||
|
|
||||||
ch.messageMutex.Lock()
|
ch.messageMutex.Lock()
|
||||||
defer ch.messageMutex.Unlock()
|
defer ch.messageMutex.Unlock()
|
||||||
|
|
||||||
ix, ok := ch.messageIDs[id]
|
ix, ok := ch.messageixs[i]
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("ID not found")
|
return errors.New("Message not found.")
|
||||||
}
|
}
|
||||||
|
|
||||||
msg := ch.messages[ix]
|
m := ch.messages[ix]
|
||||||
msg.content = content
|
m.content = content
|
||||||
|
|
||||||
ch.messages[ix] = msg
|
ch.messages[ix] = m
|
||||||
ch.edit <- msg
|
ch.edit <- m
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) addMessage(msg Message, container cchat.MessagesContainer) {
|
func (ch *Channel) addMessage(msg Message, container cchat.MessagesContainer) {
|
||||||
ch.messageMutex.Lock()
|
ch.messageMutex.Lock()
|
||||||
defer ch.messageMutex.Unlock()
|
|
||||||
|
|
||||||
// Clean up the backlog.
|
// Clean up the backlog.
|
||||||
if len(ch.messages) > FetchBacklog*2 {
|
if clean := len(ch.messages) - maxBacklog; clean > 0 {
|
||||||
|
// Remove them from the map.
|
||||||
|
for _, m := range ch.messages[:clean] {
|
||||||
|
delete(ch.messageixs, m.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cut the message IDs away by shifting the slice.
|
||||||
|
ch.messages = append(ch.messages[:0], ch.messages[clean:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ch.messageixs[msg.id] = len(ch.messages)
|
||||||
ch.messages = append(ch.messages, msg)
|
ch.messages = append(ch.messages, msg)
|
||||||
|
|
||||||
|
ch.messageMutex.Unlock()
|
||||||
|
|
||||||
container.CreateMessage(msg)
|
container.CreateMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) updateMessage(msg Message, container cchat.MessagesContainer) {
|
func (ch *Channel) updateMessage(msg Message, container cchat.MessagesContainer) {
|
||||||
ch.messageMutex.Lock()
|
ch.messageMutex.Lock()
|
||||||
defer ch.messageMutex.Unlock()
|
|
||||||
|
|
||||||
ix, ok := ch.messageIDs[msg.ID()]
|
i, ok := ch.messageixs[msg.id]
|
||||||
if !ok {
|
if ok {
|
||||||
// Unknown message.
|
ch.messages[i] = msg
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ch.messages[ix] = msg
|
ch.messageMutex.Unlock()
|
||||||
container.UpdateMessage(msg)
|
|
||||||
|
if ok {
|
||||||
|
container.UpdateMessage(msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) deleteMessage(msg MessageHeader, container cchat.MessagesContainer) {
|
func (ch *Channel) deleteMessage(msg MessageHeader, container cchat.MessagesContainer) {
|
||||||
ch.messageMutex.Lock()
|
ch.messageMutex.Lock()
|
||||||
defer ch.messageMutex.Unlock()
|
|
||||||
|
|
||||||
ix, ok := ch.messageIDs[msg.ID()]
|
i, ok := ch.messageixs[msg.id]
|
||||||
if !ok {
|
if ok {
|
||||||
return
|
ch.messages = append(ch.messages[:i], ch.messages[i+1:]...)
|
||||||
|
delete(ch.messageixs, msg.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(ch.messageIDs, msg.ID())
|
ch.messageMutex.Unlock()
|
||||||
ch.messages = append(ch.messages[:ix], ch.messages[ix+1:]...)
|
|
||||||
container.DeleteMessage(msg)
|
if ok {
|
||||||
|
container.DeleteMessage(msg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// randomMsgID returns a random recent message ID.
|
// randomMsgID returns a random recent message ID.
|
||||||
|
@ -228,6 +258,7 @@ func (ch *Channel) randomMsg() (msg Message) {
|
||||||
// Should we generate a new author for the new message?
|
// Should we generate a new author for the new message?
|
||||||
if ch.incrAuthor > sameAuthorLimit {
|
if ch.incrAuthor > sameAuthorLimit {
|
||||||
msg = randomMessage(ch.nextID())
|
msg = randomMessage(ch.nextID())
|
||||||
|
ch.incrAuthor = 0 // reset
|
||||||
} else {
|
} else {
|
||||||
last := ch.messages[len(ch.messages)-1]
|
last := ch.messages[len(ch.messages)-1]
|
||||||
msg = randomMessageWithAuthor(ch.nextID(), last.author)
|
msg = randomMessageWithAuthor(ch.nextID(), last.author)
|
||||||
|
@ -241,7 +272,7 @@ func (ch *Channel) nextID() (id uint32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
|
func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
|
||||||
if emulateAustralianInternet() {
|
if simulateAustralianInternet() {
|
||||||
return errors.New("Failed to send message: Australian Internet unsupported.")
|
return errors.New("Failed to send message: Australian Internet unsupported.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +289,7 @@ func (ch *Channel) SendMessage(msg cchat.SendableMessage) error {
|
||||||
const (
|
const (
|
||||||
DeleteAction = "Delete"
|
DeleteAction = "Delete"
|
||||||
NoopAction = "No-op"
|
NoopAction = "No-op"
|
||||||
BestTrapAction = "Print best trap"
|
BestTrapAction = "What's the best trap?"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (ch *Channel) MessageActions() []string {
|
func (ch *Channel) MessageActions() []string {
|
||||||
|
@ -279,8 +310,8 @@ func (ch *Channel) DoMessageAction(c cchat.MessagesContainer, action, messageID
|
||||||
return errors.Wrap(err, "Invalid ID")
|
return errors.Wrap(err, "Invalid ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emulate IO.
|
// Simulate IO.
|
||||||
emulateAustralianInternet()
|
simulateAustralianInternet()
|
||||||
ch.deleteMessage(MessageHeader{uint32(i), time.Now()}, c)
|
ch.deleteMessage(MessageHeader{uint32(i), time.Now()}, c)
|
||||||
|
|
||||||
case NoopAction:
|
case NoopAction:
|
||||||
|
@ -321,6 +352,7 @@ func generateChannels(s *Session, amount int) []cchat.Server {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,8 +360,8 @@ func randClamp(min, max int) int {
|
||||||
return rand.Intn(max-min) + min
|
return rand.Intn(max-min) + min
|
||||||
}
|
}
|
||||||
|
|
||||||
// emulate network latency
|
// simulate network latency
|
||||||
func emulateAustralianInternet() (lost bool) {
|
func simulateAustralianInternet() (lost bool) {
|
||||||
var ms = randClamp(internetMinLatency, internetMaxLatency)
|
var ms = randClamp(internetMinLatency, internetMaxLatency)
|
||||||
<-time.After(time.Duration(ms) * time.Millisecond)
|
<-time.After(time.Duration(ms) * time.Millisecond)
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -4,6 +4,6 @@ go 1.14
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Pallinder/go-randomdata v1.2.0
|
github.com/Pallinder/go-randomdata v1.2.0
|
||||||
github.com/diamondburned/cchat v0.0.19
|
github.com/diamondburned/cchat v0.0.22
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
)
|
)
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -38,6 +38,12 @@ github.com/diamondburned/cchat v0.0.18 h1:1nTPcYEumpLCangEV/oblNkZrZG9dQ432Ov1qv
|
||||||
github.com/diamondburned/cchat v0.0.18/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
github.com/diamondburned/cchat v0.0.18/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
||||||
github.com/diamondburned/cchat v0.0.19 h1:XPZDqOR8P1tzTWVkYzRb6yZ1H6yU8tSUReGjklIqarw=
|
github.com/diamondburned/cchat v0.0.19 h1:XPZDqOR8P1tzTWVkYzRb6yZ1H6yU8tSUReGjklIqarw=
|
||||||
github.com/diamondburned/cchat v0.0.19/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
github.com/diamondburned/cchat v0.0.19/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
||||||
|
github.com/diamondburned/cchat v0.0.20 h1:jYveuCTscVo1GiixgEpjALBgRtsxv+ITvFm7jjNRh+k=
|
||||||
|
github.com/diamondburned/cchat v0.0.20/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
||||||
|
github.com/diamondburned/cchat v0.0.21 h1:2PeC6hC4fFmi3gr2roaF4ojA3PYuZ1Gb5NQUCEiym+M=
|
||||||
|
github.com/diamondburned/cchat v0.0.21/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
||||||
|
github.com/diamondburned/cchat v0.0.22 h1:jmt0y3rMlFLbBP15Pb/GeGmeL72nx7vOvZoBA3herFs=
|
||||||
|
github.com/diamondburned/cchat v0.0.22/go.mod h1:+zXktogE45A0om4fT6B/z6Ii7FXNafjxsNspI0rlhbU=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
|
|
|
@ -20,6 +20,14 @@ type MessageHeader struct {
|
||||||
|
|
||||||
var _ cchat.MessageHeader = (*Message)(nil)
|
var _ cchat.MessageHeader = (*Message)(nil)
|
||||||
|
|
||||||
|
func parseID(id string) (uint32, error) {
|
||||||
|
i, err := strconv.Atoi(id)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return uint32(i), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (m MessageHeader) ID() string {
|
func (m MessageHeader) ID() string {
|
||||||
return strconv.Itoa(int(m.id))
|
return strconv.Itoa(int(m.id))
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,8 @@ func (sv *Server) ID() string {
|
||||||
return strconv.Itoa(int(sv.id))
|
return strconv.Itoa(int(sv.id))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sv *Server) Name(labeler cchat.LabelContainer) (func(), error) {
|
func (sv *Server) Name() text.Rich {
|
||||||
labeler.SetLabel(text.Rich{Content: sv.name})
|
return text.Rich{Content: sv.name}
|
||||||
return func() {}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sv *Server) Servers(container cchat.ServersContainer) error {
|
func (sv *Server) Servers(container cchat.ServersContainer) error {
|
||||||
|
|
26
service.go
26
service.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/diamondburned/cchat"
|
"github.com/diamondburned/cchat"
|
||||||
"github.com/diamondburned/cchat/services"
|
"github.com/diamondburned/cchat/services"
|
||||||
|
@ -26,12 +27,12 @@ var (
|
||||||
_ cchat.SessionRestorer = (*Service)(nil)
|
_ cchat.SessionRestorer = (*Service)(nil)
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s Service) Name() string {
|
func (s Service) Name() text.Rich {
|
||||||
return "Mock"
|
return text.Rich{Content: "Mock"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s Service) RestoreSession(storage map[string]string) (cchat.Session, error) {
|
func (s Service) RestoreSession(storage map[string]string) (cchat.Session, error) {
|
||||||
if emulateAustralianInternet() {
|
if simulateAustralianInternet() {
|
||||||
return nil, errors.New("Restore failed: server machine broke")
|
return nil, errors.New("Restore failed: server machine broke")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ func (Authenticator) AuthenticateForm() []cchat.AuthenticateEntry {
|
||||||
|
|
||||||
func (Authenticator) Authenticate(form []string) (cchat.Session, error) {
|
func (Authenticator) Authenticate(form []string) (cchat.Session, error) {
|
||||||
// SLOW IO TIME.
|
// SLOW IO TIME.
|
||||||
if emulateAustralianInternet() {
|
if simulateAustralianInternet() {
|
||||||
return nil, errors.New("Authentication timed out.")
|
return nil, errors.New("Authentication timed out.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +78,7 @@ func (Authenticator) Authenticate(form []string) (cchat.Session, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// channel.go @ emulateAustralianInternet
|
// channel.go @ simulateAustralianInternet
|
||||||
internetCanFail = true
|
internetCanFail = true
|
||||||
// 500ms ~ 3s
|
// 500ms ~ 3s
|
||||||
internetMinLatency = 500
|
internetMinLatency = 500
|
||||||
|
@ -139,19 +140,24 @@ func (s *Session) ID() string {
|
||||||
return s.username
|
return s.username
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Name(labeler cchat.LabelContainer) (func(), error) {
|
func (s *Session) Name() text.Rich {
|
||||||
labeler.SetLabel(text.Rich{Content: s.username})
|
return text.Rich{Content: s.username}
|
||||||
return func() {}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Servers(container cchat.ServersContainer) error {
|
func (s *Session) Servers(container cchat.ServersContainer) error {
|
||||||
|
// Simulate slight IO.
|
||||||
|
<-time.After(time.Second)
|
||||||
|
|
||||||
container.SetServers(s.servers)
|
container.SetServers(s.servers)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Icon(iconer cchat.IconContainer) (func(), error) {
|
func (s *Session) Icon(iconer cchat.IconContainer) error {
|
||||||
|
// Simulate IO.
|
||||||
|
simulateAustralianInternet()
|
||||||
|
|
||||||
iconer.SetIcon(avatarURL)
|
iconer.SetIcon(avatarURL)
|
||||||
return func() {}, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Session) Save() (map[string]string, error) {
|
func (s *Session) Save() (map[string]string, error) {
|
||||||
|
|
Loading…
Reference in New Issue