PLT-1527 Add a slash command to set yourself away (#3752)
* added handlers for slash commands * added manual status persistance * added tests * removed extra debug output and comments * rebase - fixing the PR * making echo messages after slash commands ephemeral
Этот коммит содержится в:
коммит произвёл
Joram Wilander
родитель
db660bdf9c
Коммит
dc09b7781a
42
api/command_away.go
Обычный файл
42
api/command_away.go
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type AwayProvider struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CMD_AWAY = "away"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCommandProvider(&AwayProvider{})
|
||||
}
|
||||
|
||||
func (me *AwayProvider) GetTrigger() string {
|
||||
return CMD_AWAY
|
||||
}
|
||||
|
||||
func (me *AwayProvider) GetCommand(c *Context) *model.Command {
|
||||
return &model.Command{
|
||||
Trigger: CMD_AWAY,
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: c.T("api.command_away.desc"),
|
||||
DisplayName: c.T("api.command_away.name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *AwayProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
rmsg := c.T("api.command_away.success")
|
||||
if len(message) > 0 {
|
||||
rmsg = message + " " + rmsg
|
||||
}
|
||||
SetStatusAwayIfNeeded(c.Session.UserId, true)
|
||||
|
||||
return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: rmsg}
|
||||
}
|
||||
42
api/command_offline.go
Обычный файл
42
api/command_offline.go
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type OfflineProvider struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CMD_OFFLINE = "offline"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCommandProvider(&OfflineProvider{})
|
||||
}
|
||||
|
||||
func (me *OfflineProvider) GetTrigger() string {
|
||||
return CMD_OFFLINE
|
||||
}
|
||||
|
||||
func (me *OfflineProvider) GetCommand(c *Context) *model.Command {
|
||||
return &model.Command{
|
||||
Trigger: CMD_OFFLINE,
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: c.T("api.command_offline.desc"),
|
||||
DisplayName: c.T("api.command_offline.name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *OfflineProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
rmsg := c.T("api.command_offline.success")
|
||||
if len(message) > 0 {
|
||||
rmsg = message + " " + rmsg
|
||||
}
|
||||
SetStatusOffline(c.Session.UserId, true)
|
||||
|
||||
return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: rmsg}
|
||||
}
|
||||
42
api/command_online.go
Обычный файл
42
api/command_online.go
Обычный файл
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type OnlineProvider struct {
|
||||
}
|
||||
|
||||
const (
|
||||
CMD_ONLINE = "online"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterCommandProvider(&OnlineProvider{})
|
||||
}
|
||||
|
||||
func (me *OnlineProvider) GetTrigger() string {
|
||||
return CMD_ONLINE
|
||||
}
|
||||
|
||||
func (me *OnlineProvider) GetCommand(c *Context) *model.Command {
|
||||
return &model.Command{
|
||||
Trigger: CMD_ONLINE,
|
||||
AutoComplete: true,
|
||||
AutoCompleteDesc: c.T("api.command_online.desc"),
|
||||
DisplayName: c.T("api.command_online.name"),
|
||||
}
|
||||
}
|
||||
|
||||
func (me *OnlineProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
|
||||
rmsg := c.T("api.command_online.success")
|
||||
if len(message) > 0 {
|
||||
rmsg = message + " " + rmsg
|
||||
}
|
||||
SetStatusOnline(c.Session.UserId, c.Session.Id, true)
|
||||
|
||||
return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: rmsg}
|
||||
}
|
||||
40
api/command_statuses_test.go
Обычный файл
40
api/command_statuses_test.go
Обычный файл
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
func TestStatusCommands(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
commandAndTest(t, th, "away")
|
||||
commandAndTest(t, th, "offline")
|
||||
commandAndTest(t, th, "online")
|
||||
}
|
||||
|
||||
func commandAndTest(t *testing.T, th *TestHelper, status string) {
|
||||
Client := th.BasicClient
|
||||
channel := th.BasicChannel
|
||||
user := th.BasicUser
|
||||
|
||||
r1 := Client.Must(Client.Command(channel.Id, "/"+status, false)).Data.(*model.CommandResponse)
|
||||
if r1 == nil {
|
||||
t.Fatal("Command failed to execute")
|
||||
}
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
statuses := Client.Must(Client.GetStatuses()).Data.(map[string]string)
|
||||
|
||||
if status == "offline" {
|
||||
status = ""
|
||||
}
|
||||
if statuses[user.Id] != status {
|
||||
t.Fatal("Error setting status " + status)
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if c.Err == nil && h.isUserActivity && token != "" && len(c.Session.UserId) > 0 {
|
||||
SetStatusOnline(c.Session.UserId, c.Session.Id)
|
||||
SetStatusOnline(c.Session.UserId, c.Session.Id, false)
|
||||
}
|
||||
|
||||
if c.Err == nil {
|
||||
|
||||
@@ -679,7 +679,7 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
if status, err = GetStatus(id); err != nil {
|
||||
status = &model.Status{id, model.STATUS_OFFLINE, 0}
|
||||
status = &model.Status{id, model.STATUS_OFFLINE, false, 0}
|
||||
}
|
||||
|
||||
if userAllowsEmails && status.Status != model.STATUS_ONLINE {
|
||||
@@ -727,7 +727,7 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
if status, err = GetStatus(id); err != nil {
|
||||
status = &model.Status{id, model.STATUS_OFFLINE, 0}
|
||||
status = &model.Status{id, model.STATUS_OFFLINE, false, 0}
|
||||
}
|
||||
|
||||
if profileMap[id].StatusAllowsPushNotification(status) {
|
||||
@@ -740,7 +740,7 @@ func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
if status, err = GetStatus(id); err != nil {
|
||||
status = &model.Status{id, model.STATUS_OFFLINE, 0}
|
||||
status = &model.Status{id, model.STATUS_OFFLINE, false, 0}
|
||||
}
|
||||
|
||||
if profileMap[id].StatusAllowsPushNotification(status) {
|
||||
|
||||
@@ -65,19 +65,24 @@ func GetAllStatuses() (map[string]interface{}, *model.AppError) {
|
||||
}
|
||||
}
|
||||
|
||||
func SetStatusOnline(userId string, sessionId string) {
|
||||
func SetStatusOnline(userId string, sessionId string, manual bool) {
|
||||
l4g.Debug(userId, "online")
|
||||
broadcast := false
|
||||
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
if status, err = GetStatus(userId); err != nil {
|
||||
status = &model.Status{userId, model.STATUS_ONLINE, model.GetMillis()}
|
||||
status = &model.Status{userId, model.STATUS_ONLINE, false, model.GetMillis()}
|
||||
broadcast = true
|
||||
} else {
|
||||
if status.Manual && !manual {
|
||||
return // manually set status always overrides non-manual one
|
||||
}
|
||||
if status.Status != model.STATUS_ONLINE {
|
||||
broadcast = true
|
||||
}
|
||||
status.Status = model.STATUS_ONLINE
|
||||
status.Manual = false // for "online" there's no manually or auto set
|
||||
status.LastActivityAt = model.GetMillis()
|
||||
}
|
||||
|
||||
@@ -107,8 +112,14 @@ func SetStatusOnline(userId string, sessionId string) {
|
||||
}
|
||||
}
|
||||
|
||||
func SetStatusOffline(userId string) {
|
||||
status := &model.Status{userId, model.STATUS_OFFLINE, model.GetMillis()}
|
||||
func SetStatusOffline(userId string, manual bool) {
|
||||
l4g.Debug(userId, "offline")
|
||||
status, err := GetStatus(userId)
|
||||
if err == nil && status.Manual && !manual {
|
||||
return // manually set status always overrides non-manual one
|
||||
}
|
||||
|
||||
status = &model.Status{userId, model.STATUS_OFFLINE, manual, model.GetMillis()}
|
||||
|
||||
AddStatusCache(status)
|
||||
|
||||
@@ -121,21 +132,30 @@ func SetStatusOffline(userId string) {
|
||||
go Publish(event)
|
||||
}
|
||||
|
||||
func SetStatusAwayIfNeeded(userId string) {
|
||||
func SetStatusAwayIfNeeded(userId string, manual bool) {
|
||||
l4g.Debug(userId, "away")
|
||||
status, err := GetStatus(userId)
|
||||
|
||||
if err != nil {
|
||||
status = &model.Status{userId, model.STATUS_OFFLINE, 0}
|
||||
status = &model.Status{userId, model.STATUS_OFFLINE, manual, 0}
|
||||
}
|
||||
|
||||
if status.Status == model.STATUS_AWAY {
|
||||
return
|
||||
if !manual && status.Manual {
|
||||
return // manually set status always overrides non-manual one
|
||||
}
|
||||
|
||||
if !IsUserAway(status.LastActivityAt) {
|
||||
return
|
||||
if !manual {
|
||||
if status.Status == model.STATUS_AWAY {
|
||||
return
|
||||
}
|
||||
|
||||
if !IsUserAway(status.LastActivityAt) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
status.Status = model.STATUS_AWAY
|
||||
status.Manual = manual
|
||||
|
||||
AddStatusCache(status)
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ func TestStatuses(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
SetStatusAwayIfNeeded(th.BasicUser2.Id)
|
||||
SetStatusAwayIfNeeded(th.BasicUser2.Id, false)
|
||||
|
||||
awayTimeout := *utils.Cfg.TeamSettings.UserStatusAwayTimeout
|
||||
defer func() {
|
||||
@@ -93,8 +93,8 @@ func TestStatuses(t *testing.T) {
|
||||
|
||||
time.Sleep(1500 * time.Millisecond)
|
||||
|
||||
SetStatusAwayIfNeeded(th.BasicUser2.Id)
|
||||
SetStatusAwayIfNeeded(th.BasicUser2.Id)
|
||||
SetStatusAwayIfNeeded(th.BasicUser2.Id, false)
|
||||
SetStatusAwayIfNeeded(th.BasicUser2.Id, false)
|
||||
|
||||
WebSocketClient2.Close()
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
@@ -32,7 +32,7 @@ type WebConn struct {
|
||||
}
|
||||
|
||||
func NewWebConn(c *Context, ws *websocket.Conn) *WebConn {
|
||||
go SetStatusOnline(c.Session.UserId, c.Session.Id)
|
||||
go SetStatusOnline(c.Session.UserId, c.Session.Id, false)
|
||||
|
||||
return &WebConn{
|
||||
Send: make(chan model.WebSocketMessage, 64),
|
||||
@@ -55,7 +55,7 @@ func (c *WebConn) readPump() {
|
||||
c.WebSocket.SetReadDeadline(time.Now().Add(PONG_WAIT))
|
||||
c.WebSocket.SetPongHandler(func(string) error {
|
||||
c.WebSocket.SetReadDeadline(time.Now().Add(PONG_WAIT))
|
||||
go SetStatusAwayIfNeeded(c.UserId)
|
||||
go SetStatusAwayIfNeeded(c.UserId, false)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ func (h *Hub) Start() {
|
||||
}
|
||||
|
||||
if !found {
|
||||
go SetStatusOffline(userId)
|
||||
go SetStatusOffline(userId, false)
|
||||
}
|
||||
case userId := <-h.invalidateUser:
|
||||
for webCon := range h.connections {
|
||||
|
||||
Ссылка в новой задаче
Block a user