Log direct messages and keep track of open direct message tabs
This commit is contained in:
parent
e97bb519ed
commit
8305dd561d
17 changed files with 655 additions and 304 deletions
|
@ -31,6 +31,7 @@ type indexData struct {
|
|||
Defaults *config.Defaults
|
||||
Servers []Server
|
||||
Channels []*storage.Channel
|
||||
OpenDMs []storage.Tab
|
||||
HexIP bool
|
||||
Version dispatchVersion
|
||||
|
||||
|
@ -43,7 +44,7 @@ type indexData struct {
|
|||
Messages *Messages
|
||||
}
|
||||
|
||||
func (d *Dispatch) getIndexData(r *http.Request, path string, state *State) *indexData {
|
||||
func (d *Dispatch) getIndexData(r *http.Request, state *State) *indexData {
|
||||
cfg := d.Config()
|
||||
|
||||
data := indexData{
|
||||
|
@ -98,35 +99,37 @@ func (d *Dispatch) getIndexData(r *http.Request, path string, state *State) *ind
|
|||
}
|
||||
data.Channels = channels
|
||||
|
||||
server, channel := getTabFromPath(path)
|
||||
if isInChannel(channels, server, channel) {
|
||||
data.addUsersAndMessages(server, channel, state)
|
||||
return &data
|
||||
openDMs, err := state.user.GetOpenDMs()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
data.OpenDMs = openDMs
|
||||
|
||||
server, channel = parseTabCookie(r, path)
|
||||
if isInChannel(channels, server, channel) {
|
||||
data.addUsersAndMessages(server, channel, state)
|
||||
tab, err := tabFromRequest(r)
|
||||
if err == nil && hasTab(channels, openDMs, tab.Server, tab.Name) {
|
||||
data.addUsersAndMessages(tab.Server, tab.Name, state)
|
||||
}
|
||||
|
||||
return &data
|
||||
}
|
||||
|
||||
func (d *indexData) addUsersAndMessages(server, channel string, state *State) {
|
||||
users := channelStore.GetUsers(server, channel)
|
||||
if len(users) > 0 {
|
||||
d.Users = &Userlist{
|
||||
Server: server,
|
||||
Channel: channel,
|
||||
Users: users,
|
||||
func (d *indexData) addUsersAndMessages(server, name string, state *State) {
|
||||
if isChannel(name) {
|
||||
users := channelStore.GetUsers(server, name)
|
||||
if len(users) > 0 {
|
||||
d.Users = &Userlist{
|
||||
Server: server,
|
||||
Channel: name,
|
||||
Users: users,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
messages, hasMore, err := state.user.GetLastMessages(server, channel, 50)
|
||||
messages, hasMore, err := state.user.GetLastMessages(server, name, 50)
|
||||
if err == nil && len(messages) > 0 {
|
||||
m := Messages{
|
||||
Server: server,
|
||||
To: channel,
|
||||
To: name,
|
||||
Messages: messages,
|
||||
}
|
||||
|
||||
|
@ -138,10 +141,16 @@ func (d *indexData) addUsersAndMessages(server, channel string, state *State) {
|
|||
}
|
||||
}
|
||||
|
||||
func isInChannel(channels []*storage.Channel, server, channel string) bool {
|
||||
if channel != "" {
|
||||
func hasTab(channels []*storage.Channel, openDMs []storage.Tab, server, name string) bool {
|
||||
if name != "" {
|
||||
for _, ch := range channels {
|
||||
if server == ch.Server && channel == ch.Name {
|
||||
if server == ch.Server && name == ch.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, tab := range openDMs {
|
||||
if server == tab.Server && name == tab.Name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -149,30 +158,52 @@ func isInChannel(channels []*storage.Channel, server, channel string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func getTabFromPath(rawPath string) (string, string) {
|
||||
path := strings.Split(strings.Trim(rawPath, "/"), "/")
|
||||
if len(path) >= 2 {
|
||||
name, err := url.PathUnescape(path[len(path)-1])
|
||||
if err == nil && isChannel(name) {
|
||||
return path[len(path)-2], name
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
func tabFromRequest(r *http.Request) (Tab, error) {
|
||||
tab := Tab{}
|
||||
|
||||
var path string
|
||||
if strings.HasPrefix(r.URL.Path, "/ws") {
|
||||
path = r.URL.EscapedPath()[3:]
|
||||
} else {
|
||||
referer, err := url.Parse(r.Referer())
|
||||
if err != nil {
|
||||
return tab, err
|
||||
}
|
||||
|
||||
path = referer.EscapedPath()
|
||||
}
|
||||
|
||||
func parseTabCookie(r *http.Request, path string) (string, string) {
|
||||
if path == "/" {
|
||||
cookie, err := r.Cookie("tab")
|
||||
if err == nil {
|
||||
v, err := url.PathUnescape(cookie.Value)
|
||||
if err == nil {
|
||||
tab := strings.SplitN(v, ";", 2)
|
||||
if err != nil {
|
||||
return tab, err
|
||||
}
|
||||
|
||||
if len(tab) == 2 && isChannel(tab[1]) {
|
||||
return tab[0], tab[1]
|
||||
v, err := url.PathUnescape(cookie.Value)
|
||||
if err != nil {
|
||||
return tab, err
|
||||
}
|
||||
|
||||
parts := strings.SplitN(v, ";", 2)
|
||||
if len(parts) == 2 {
|
||||
tab.Server = parts[0]
|
||||
tab.Name = parts[1]
|
||||
}
|
||||
} else {
|
||||
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
if len(parts) > 0 && len(parts) < 3 {
|
||||
if len(parts) == 2 {
|
||||
name, err := url.PathUnescape(parts[1])
|
||||
if err != nil {
|
||||
return tab, err
|
||||
}
|
||||
|
||||
tab.Name = name
|
||||
}
|
||||
|
||||
tab.Server = parts[0]
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
|
||||
return tab, nil
|
||||
}
|
||||
|
|
|
@ -104,6 +104,29 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer(in *jlexer.Lexer, out
|
|||
}
|
||||
in.Delim(']')
|
||||
}
|
||||
case "openDMs":
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
out.OpenDMs = nil
|
||||
} else {
|
||||
in.Delim('[')
|
||||
if out.OpenDMs == nil {
|
||||
if !in.IsDelim(']') {
|
||||
out.OpenDMs = make([]storage.Tab, 0, 2)
|
||||
} else {
|
||||
out.OpenDMs = []storage.Tab{}
|
||||
}
|
||||
} else {
|
||||
out.OpenDMs = (out.OpenDMs)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
var v3 storage.Tab
|
||||
easyjson7e607aefDecodeGithubComKhliengDispatchStorage1(in, &v3)
|
||||
out.OpenDMs = append(out.OpenDMs, v3)
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
}
|
||||
case "hexIP":
|
||||
out.HexIP = bool(in.Bool())
|
||||
case "version":
|
||||
|
@ -176,11 +199,11 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer(out *jwriter.Writer, i
|
|||
}
|
||||
{
|
||||
out.RawByte('[')
|
||||
for v3, v4 := range in.Servers {
|
||||
if v3 > 0 {
|
||||
for v4, v5 := range in.Servers {
|
||||
if v4 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.Raw((v4).MarshalJSON())
|
||||
out.Raw((v5).MarshalJSON())
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
|
@ -195,19 +218,38 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer(out *jwriter.Writer, i
|
|||
}
|
||||
{
|
||||
out.RawByte('[')
|
||||
for v5, v6 := range in.Channels {
|
||||
if v5 > 0 {
|
||||
for v6, v7 := range in.Channels {
|
||||
if v6 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
if v6 == nil {
|
||||
if v7 == nil {
|
||||
out.RawString("null")
|
||||
} else {
|
||||
easyjson7e607aefEncodeGithubComKhliengDispatchStorage(out, *v6)
|
||||
easyjson7e607aefEncodeGithubComKhliengDispatchStorage(out, *v7)
|
||||
}
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
}
|
||||
if len(in.OpenDMs) != 0 {
|
||||
const prefix string = ",\"openDMs\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
} else {
|
||||
out.RawString(prefix)
|
||||
}
|
||||
{
|
||||
out.RawByte('[')
|
||||
for v8, v9 := range in.OpenDMs {
|
||||
if v8 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
easyjson7e607aefEncodeGithubComKhliengDispatchStorage1(out, v9)
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
}
|
||||
if in.HexIP {
|
||||
const prefix string = ",\"hexIP\":"
|
||||
if first {
|
||||
|
@ -284,6 +326,61 @@ func (v *indexData) UnmarshalJSON(data []byte) error {
|
|||
func (v *indexData) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson7e607aefDecodeGithubComKhliengDispatchServer(l, v)
|
||||
}
|
||||
func easyjson7e607aefDecodeGithubComKhliengDispatchStorage1(in *jlexer.Lexer, out *storage.Tab) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "server":
|
||||
out.Server = string(in.String())
|
||||
case "name":
|
||||
out.Name = string(in.String())
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson7e607aefEncodeGithubComKhliengDispatchStorage1(out *jwriter.Writer, in storage.Tab) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
if in.Server != "" {
|
||||
const prefix string = ",\"server\":"
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
out.String(string(in.Server))
|
||||
}
|
||||
if in.Name != "" {
|
||||
const prefix string = ",\"name\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
} else {
|
||||
out.RawString(prefix)
|
||||
}
|
||||
out.String(string(in.Name))
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
func easyjson7e607aefDecodeGithubComKhliengDispatchStorage(in *jlexer.Lexer, out *storage.Channel) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
|
@ -392,9 +489,9 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchConfig(in *jlexer.Lexer, out
|
|||
out.Channels = (out.Channels)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
var v7 string
|
||||
v7 = string(in.String())
|
||||
out.Channels = append(out.Channels, v7)
|
||||
var v10 string
|
||||
v10 = string(in.String())
|
||||
out.Channels = append(out.Channels, v10)
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
|
@ -457,11 +554,11 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchConfig(out *jwriter.Writer, i
|
|||
}
|
||||
{
|
||||
out.RawByte('[')
|
||||
for v8, v9 := range in.Channels {
|
||||
if v8 > 0 {
|
||||
for v11, v12 := range in.Channels {
|
||||
if v11 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.String(string(v9))
|
||||
out.String(string(v12))
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
|
@ -640,9 +737,9 @@ func easyjson7e607aefDecodeGithubComKhliengDispatchServer2(in *jlexer.Lexer, out
|
|||
out.Channels = (out.Channels)[:0]
|
||||
}
|
||||
for !in.IsDelim(']') {
|
||||
var v10 string
|
||||
v10 = string(in.String())
|
||||
out.Channels = append(out.Channels, v10)
|
||||
var v13 string
|
||||
v13 = string(in.String())
|
||||
out.Channels = append(out.Channels, v13)
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim(']')
|
||||
|
@ -705,11 +802,11 @@ func easyjson7e607aefEncodeGithubComKhliengDispatchServer2(out *jwriter.Writer,
|
|||
}
|
||||
{
|
||||
out.RawByte('[')
|
||||
for v11, v12 := range in.Channels {
|
||||
if v11 > 0 {
|
||||
for v14, v15 := range in.Channels {
|
||||
if v14 > 0 {
|
||||
out.RawByte(',')
|
||||
}
|
||||
out.String(string(v12))
|
||||
out.String(string(v15))
|
||||
}
|
||||
out.RawByte(']')
|
||||
}
|
||||
|
|
|
@ -1,47 +1,60 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/khlieng/dispatch/storage"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetTabFromPath(t *testing.T) {
|
||||
cases := []struct {
|
||||
input string
|
||||
expectedServer string
|
||||
expectedChannel string
|
||||
input *http.Request
|
||||
expectedTab Tab
|
||||
}{
|
||||
{
|
||||
"/chat.freenode.net/%23r%2Fstuff%2F/",
|
||||
"chat.freenode.net",
|
||||
"#r/stuff/",
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/init"},
|
||||
Header: http.Header{"Referer": []string{"/chat.freenode.net/%23r%2Fstuff%2F"}},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net", Name: "#r/stuff/"}},
|
||||
}, {
|
||||
"/chat.freenode.net/%23r%2Fstuff%2F",
|
||||
"chat.freenode.net",
|
||||
"#r/stuff/",
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/init"},
|
||||
Header: http.Header{"Referer": []string{"/chat.freenode.net/%23r%2Fstuff"}},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net", Name: "#r/stuff"}},
|
||||
}, {
|
||||
"/chat.freenode.net/%23r%2Fstuff",
|
||||
"chat.freenode.net",
|
||||
"#r/stuff",
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/init"},
|
||||
Header: http.Header{"Referer": []string{"/chat.freenode.net/%23stuff"}},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net", Name: "#stuff"}},
|
||||
}, {
|
||||
"/chat.freenode.net/%23stuff",
|
||||
"chat.freenode.net",
|
||||
"#stuff",
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/init"},
|
||||
Header: http.Header{"Referer": []string{"/chat.freenode.net/stuff"}},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net", Name: "stuff"}},
|
||||
}, {
|
||||
"/chat.freenode.net/%23stuff/cake",
|
||||
"",
|
||||
"",
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/init"},
|
||||
Header: http.Header{"Referer": []string{"/data/chat.freenode.net/%23apples"}},
|
||||
},
|
||||
Tab{},
|
||||
}, {
|
||||
"/data/chat.freenode.net/%23apples",
|
||||
"chat.freenode.net",
|
||||
"#apples",
|
||||
&http.Request{
|
||||
URL: &url.URL{Path: "/ws/chat.freenode.net"},
|
||||
},
|
||||
Tab{storage.Tab{Server: "chat.freenode.net"}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
server, channel := getTabFromPath(tc.input)
|
||||
assert.Equal(t, tc.expectedServer, server)
|
||||
assert.Equal(t, tc.expectedChannel, channel)
|
||||
tab, err := tabFromRequest(tc.input)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, tc.expectedTab, tab)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,17 +169,21 @@ func (i *ircHandler) message(msg *irc.Message) {
|
|||
From: msg.Nick,
|
||||
Content: msg.LastParam(),
|
||||
}
|
||||
target := msg.Params[0]
|
||||
|
||||
if msg.Params[0] == i.client.GetNick() {
|
||||
if target == i.client.GetNick() {
|
||||
i.state.sendJSON("pm", message)
|
||||
i.state.user.AddOpenDM(i.client.Host, message.From)
|
||||
|
||||
target = message.From
|
||||
} else {
|
||||
message.To = msg.Params[0]
|
||||
message.To = target
|
||||
i.state.sendJSON("message", message)
|
||||
}
|
||||
|
||||
if msg.Params[0] != "*" {
|
||||
if target != "*" {
|
||||
go i.state.user.LogMessage(message.ID,
|
||||
i.client.Host, msg.Nick, msg.Params[0], msg.LastParam())
|
||||
i.client.Host, msg.Nick, target, msg.LastParam())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -221,3 +221,7 @@ type ChannelForward struct {
|
|||
Old string
|
||||
New string
|
||||
}
|
||||
|
||||
type Tab struct {
|
||||
storage.Tab
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,7 +3,6 @@ package server
|
|||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
@ -163,14 +162,8 @@ func (d *Dispatch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if r.URL.Path == "/init" {
|
||||
referer, err := url.Parse(r.Header.Get("Referer"))
|
||||
if err != nil {
|
||||
fail(w, http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
state := d.handleAuth(w, r, true, true)
|
||||
data := d.getIndexData(r, referer.EscapedPath(), state)
|
||||
data := d.getIndexData(r, state)
|
||||
|
||||
writeJSON(w, r, data)
|
||||
} else if strings.HasPrefix(r.URL.Path, "/ws") {
|
||||
|
|
|
@ -68,17 +68,15 @@ func (h *wsHandler) init(r *http.Request) {
|
|||
h.state.numIRC(), "IRC connections |",
|
||||
h.state.numWS(), "WebSocket connections")
|
||||
|
||||
tab, err := tabFromRequest(r)
|
||||
|
||||
channels, err := h.state.user.GetChannels()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
path := r.URL.EscapedPath()
|
||||
pathServer, pathChannel := getTabFromPath(path)
|
||||
cookieServer, cookieChannel := parseTabCookie(r, path[3:])
|
||||
|
||||
for _, channel := range channels {
|
||||
if (channel.Server == pathServer && channel.Name == pathChannel) ||
|
||||
(channel.Server == cookieServer && channel.Name == cookieChannel) {
|
||||
if channel.Server == tab.Server && channel.Name == tab.Name {
|
||||
// Userlist and messages for this channel gets embedded in the index page
|
||||
continue
|
||||
}
|
||||
|
@ -91,6 +89,19 @@ func (h *wsHandler) init(r *http.Request) {
|
|||
|
||||
h.state.sendLastMessages(channel.Server, channel.Name, 50)
|
||||
}
|
||||
|
||||
openDMs, err := h.state.user.GetOpenDMs()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
for _, openDM := range openDMs {
|
||||
if openDM.Server == tab.Server && openDM.Name == tab.Name {
|
||||
continue
|
||||
}
|
||||
|
||||
h.state.sendLastMessages(openDM.Server, openDM.Name, 50)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *wsHandler) connect(b []byte) {
|
||||
|
@ -306,6 +317,21 @@ func (h *wsHandler) channelSearch(b []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *wsHandler) openDM(b []byte) {
|
||||
var data Tab
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
h.state.sendLastMessages(data.Server, data.Name, 50)
|
||||
h.state.user.AddOpenDM(data.Server, data.Name)
|
||||
}
|
||||
|
||||
func (h *wsHandler) closeDM(b []byte) {
|
||||
var data Tab
|
||||
data.UnmarshalJSON(b)
|
||||
|
||||
h.state.user.RemoveOpenDM(data.Server, data.Name)
|
||||
}
|
||||
|
||||
func (h *wsHandler) initHandlers() {
|
||||
h.handlers = map[string]func([]byte){
|
||||
"connect": h.connect,
|
||||
|
@ -327,6 +353,8 @@ func (h *wsHandler) initHandlers() {
|
|||
"set_server_name": h.setServerName,
|
||||
"settings_set": h.setSettings,
|
||||
"channel_search": h.channelSearch,
|
||||
"open_dm": h.openDM,
|
||||
"close_dm": h.closeDM,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue