Forward irc errors to the client, improve command validation and feedback, handle topic changes
This commit is contained in:
parent
993d29242e
commit
aa59e71745
File diff suppressed because one or more lines are too long
|
@ -428,11 +428,23 @@ i[class^="icon-"]:before, i[class*=" icon-"]:before {
|
|||
color: #999;
|
||||
}
|
||||
|
||||
.message-error {
|
||||
color: #F6546A;
|
||||
}
|
||||
|
||||
.message-prompt {
|
||||
font-weight: 700;
|
||||
font-style: italic;
|
||||
color: #6BB758;
|
||||
}
|
||||
|
||||
.message-action {
|
||||
color: #FF6698;
|
||||
}
|
||||
|
||||
.message-time {
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import createCommandMiddleware from './middleware/command';
|
||||
import createCommandMiddleware, { beforeHandler, notFoundHandler } from './middleware/command';
|
||||
import { COMMAND } from './state/actions';
|
||||
import { join, part, invite, kick } from './state/channels';
|
||||
import { sendMessage, addMessage, raw } from './state/messages';
|
||||
import { join, part, invite, kick, setTopic } from './state/channels';
|
||||
import { sendMessage, raw } from './state/messages';
|
||||
import { setNick, disconnect, whois, away } from './state/servers';
|
||||
import { select } from './state/tab';
|
||||
import { find } from './util';
|
||||
|
||||
const help = [
|
||||
'/join <channel> - Join a channel',
|
||||
|
@ -11,35 +12,50 @@ const help = [
|
|||
'/nick <nick> - Change nick',
|
||||
'/quit - Disconnect from the current server',
|
||||
'/me <message> - Send action message',
|
||||
'/topic - Show topic for the current channel',
|
||||
'/topic [topic] - Show or set topic in the current channel',
|
||||
'/msg <target> <message> - Send message to the specified channel or user',
|
||||
'/say <message> - Send message to the current chat',
|
||||
'/invite <user> [channel] - Invite user to the current or specified channel',
|
||||
'/kick <user> - Kick user from the current channel',
|
||||
'/whois <user> - Get information about user',
|
||||
'/invite <nick> [channel] - Invite user to the current or specified channel',
|
||||
'/kick <nick> - Kick user from the current channel',
|
||||
'/whois <nick> - Get information about user',
|
||||
'/away [message] - Set or clear away message',
|
||||
'/raw [message] - Send raw IRC message to the current server'
|
||||
'/raw [message] - Send raw IRC message to the current server',
|
||||
'/help [command]... - Print help for all or the specified command(s)'
|
||||
];
|
||||
|
||||
const text = content => ({ content });
|
||||
const error = content => ({ content, type: 'error' });
|
||||
const prompt = content => ({ content, type: 'prompt' });
|
||||
const findHelp = cmd => find(help, line => line.slice(1, line.indexOf(' ')) === cmd);
|
||||
|
||||
export default createCommandMiddleware(COMMAND, {
|
||||
join({ dispatch, server }, channel) {
|
||||
if (channel) {
|
||||
if (channel[0] !== '#') {
|
||||
return error('Bad channel name');
|
||||
}
|
||||
dispatch(join([channel], server));
|
||||
dispatch(select(server, channel));
|
||||
} else {
|
||||
return error('Missing channel');
|
||||
}
|
||||
},
|
||||
|
||||
part({ dispatch, server, channel }, partChannel) {
|
||||
part({ dispatch, server, channel, isChannel }, partChannel) {
|
||||
if (partChannel) {
|
||||
dispatch(part([partChannel], server));
|
||||
} else {
|
||||
} else if (isChannel) {
|
||||
dispatch(part([channel], server));
|
||||
} else {
|
||||
return error('This is not a channel');
|
||||
}
|
||||
},
|
||||
|
||||
nick({ dispatch, server }, nick) {
|
||||
if (nick) {
|
||||
dispatch(setNick(nick, server));
|
||||
} else {
|
||||
return error('Missing nick');
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -47,74 +63,127 @@ export default createCommandMiddleware(COMMAND, {
|
|||
dispatch(disconnect(server));
|
||||
},
|
||||
|
||||
me({ dispatch, server, channel }, ...params) {
|
||||
if (params.length > 0) {
|
||||
dispatch(sendMessage(`\x01ACTION ${params.join(' ')}\x01`, channel, server));
|
||||
me({ dispatch, server, channel }, ...message) {
|
||||
const msg = message.join(' ');
|
||||
if (msg !== '') {
|
||||
dispatch(sendMessage(`\x01ACTION ${msg}\x01`, channel, server));
|
||||
} else {
|
||||
return error('Messages can not be empty');
|
||||
}
|
||||
},
|
||||
|
||||
topic({ dispatch, getState, server, channel }) {
|
||||
const topic = getState().channels.getIn([server, channel, 'topic']);
|
||||
if (topic) {
|
||||
dispatch(addMessage({
|
||||
server,
|
||||
to: channel,
|
||||
content: topic
|
||||
}));
|
||||
topic({ dispatch, getState, server, channel }, ...newTopic) {
|
||||
if (newTopic.length > 0) {
|
||||
dispatch(setTopic(newTopic.join(' '), channel, server));
|
||||
} else {
|
||||
const topic = getState().channels.getIn([server, channel, 'topic']);
|
||||
if (topic) {
|
||||
return text(topic);
|
||||
}
|
||||
return 'No topic set';
|
||||
}
|
||||
},
|
||||
|
||||
msg({ dispatch, server }, target, ...message) {
|
||||
if (target && message) {
|
||||
if (!target) {
|
||||
return error('Missing nick/channel');
|
||||
}
|
||||
|
||||
const msg = message.join(' ');
|
||||
if (msg !== '') {
|
||||
dispatch(sendMessage(message.join(' '), target, server));
|
||||
dispatch(select(server, target));
|
||||
} else {
|
||||
return error('Messages can not be empty');
|
||||
}
|
||||
},
|
||||
|
||||
say({ dispatch, server, channel }, ...message) {
|
||||
if (channel && message) {
|
||||
if (!channel) {
|
||||
return error('Messages can only be sent to channels or users');
|
||||
}
|
||||
|
||||
const msg = message.join(' ');
|
||||
if (msg !== '') {
|
||||
dispatch(sendMessage(message.join(' '), channel, server));
|
||||
} else {
|
||||
return error('Messages can not be empty');
|
||||
}
|
||||
},
|
||||
|
||||
invite({ dispatch, server, channel }, user, inviteChannel) {
|
||||
invite({ dispatch, server, channel, isChannel }, user, inviteChannel) {
|
||||
if (!inviteChannel && !isChannel) {
|
||||
return error('This is not a channel');
|
||||
}
|
||||
|
||||
if (user && inviteChannel) {
|
||||
dispatch(invite(user, inviteChannel, server));
|
||||
} else if (user && channel) {
|
||||
dispatch(invite(user, channel, server));
|
||||
} else {
|
||||
return error('Missing nick');
|
||||
}
|
||||
},
|
||||
|
||||
kick({ dispatch, server, channel }, user) {
|
||||
if (user && channel) {
|
||||
kick({ dispatch, server, channel, isChannel }, user) {
|
||||
if (!isChannel) {
|
||||
return error('This is not a channel');
|
||||
}
|
||||
|
||||
if (user) {
|
||||
dispatch(kick(user, channel, server));
|
||||
} else {
|
||||
return error('Missing nick');
|
||||
}
|
||||
},
|
||||
|
||||
whois({ dispatch, server }, user) {
|
||||
if (user) {
|
||||
dispatch(whois(user, server));
|
||||
} else {
|
||||
return error('Missing nick');
|
||||
}
|
||||
},
|
||||
|
||||
away({ dispatch, server }, message) {
|
||||
dispatch(away(message, server));
|
||||
away({ dispatch, server }, ...message) {
|
||||
const msg = message.join(' ');
|
||||
dispatch(away(msg, server));
|
||||
if (msg !== '') {
|
||||
return 'Away message set';
|
||||
}
|
||||
return 'Away message cleared';
|
||||
},
|
||||
|
||||
raw({ dispatch, server }, ...message) {
|
||||
if (message) {
|
||||
const cmd = message.join(' ');
|
||||
if (message.length > 0 && message[0] !== '') {
|
||||
const cmd = `${message[0].toUpperCase()} ${message.slice(1).join(' ')}`;
|
||||
dispatch(raw(cmd, server));
|
||||
return `=> ${cmd}`;
|
||||
return prompt(`=> ${cmd}`);
|
||||
}
|
||||
return [
|
||||
prompt('=> /raw'),
|
||||
error('Missing message')
|
||||
];
|
||||
},
|
||||
|
||||
help(_, ...commands) {
|
||||
if (commands.length > 0) {
|
||||
const cmdHelp = commands.filter(findHelp).map(findHelp);
|
||||
if (cmdHelp.length > 0) {
|
||||
return text(cmdHelp);
|
||||
}
|
||||
return error('Unable to find any help :(');
|
||||
}
|
||||
return text(help);
|
||||
},
|
||||
|
||||
[beforeHandler](_, command, ...params) {
|
||||
if (command !== 'raw') {
|
||||
return prompt(`=> /${command} ${params.join(' ')}`);
|
||||
}
|
||||
},
|
||||
|
||||
help() {
|
||||
return help;
|
||||
},
|
||||
|
||||
commandNotFound(_, command) {
|
||||
return `The command /${command} was not found`;
|
||||
[notFoundHandler](_, command) {
|
||||
return error(`=> /${command}: No such command`);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,9 +1,26 @@
|
|||
import { inform } from '../state/messages';
|
||||
import { addMessages, inform, print } from '../state/messages';
|
||||
import { isChannel } from '../util';
|
||||
|
||||
const notFound = 'commandNotFound';
|
||||
export const beforeHandler = '_before';
|
||||
export const notFoundHandler = 'commandNotFound';
|
||||
|
||||
function createContext({ dispatch, getState }, { server, channel }) {
|
||||
return { dispatch, getState, server, channel };
|
||||
return { dispatch, getState, server, channel, isChannel: isChannel(channel) };
|
||||
}
|
||||
|
||||
// TODO: Pull this out as convenience action
|
||||
function process({ dispatch, server, channel }, result) {
|
||||
if (typeof result === 'string') {
|
||||
dispatch(inform(result, server, channel));
|
||||
} else if (Array.isArray(result)) {
|
||||
if (typeof result[0] === 'string') {
|
||||
dispatch(inform(result, server, channel));
|
||||
} else if (typeof result[0] === 'object') {
|
||||
dispatch(addMessages(result, server, channel));
|
||||
}
|
||||
} else if (typeof result === 'object' && result) {
|
||||
dispatch(print(result.content, server, channel, result.type));
|
||||
}
|
||||
}
|
||||
|
||||
export default function createCommandMiddleware(type, handlers) {
|
||||
|
@ -13,16 +30,15 @@ export default function createCommandMiddleware(type, handlers) {
|
|||
const command = words[0];
|
||||
const params = words.slice(1);
|
||||
|
||||
let result;
|
||||
|
||||
if (command in handlers) {
|
||||
result = handlers[command](createContext(store, action), ...params);
|
||||
} else if (notFound in handlers) {
|
||||
result = handlers[notFound](createContext(store, action), command);
|
||||
}
|
||||
|
||||
if (typeof result === 'string' || Array.isArray(result)) {
|
||||
store.dispatch(inform(result, action.server, action.channel));
|
||||
const ctx = createContext(store, action);
|
||||
if (beforeHandler in handlers) {
|
||||
process(ctx, handlers[beforeHandler](ctx, command, ...params));
|
||||
}
|
||||
process(ctx, handlers[command](ctx, ...params));
|
||||
} else if (notFoundHandler in handlers) {
|
||||
const ctx = createContext(store, action);
|
||||
process(ctx, handlers[notFoundHandler](ctx, command));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { socketAction } from '../state/actions';
|
||||
import { broadcast, inform, addMessage, addMessages } from '../state/messages';
|
||||
import { broadcast, inform, print, addMessage, addMessages } from '../state/messages';
|
||||
import { select } from '../state/tab';
|
||||
import { normalizeChannel } from '../util';
|
||||
import { replace } from '../util/router';
|
||||
|
@ -71,25 +71,39 @@ export default function handleSocket({ socket, store: { dispatch, getState } })
|
|||
dispatch(broadcast(`${data.old} changed nick to ${data.new}`, data.server, channels));
|
||||
},
|
||||
|
||||
topic({ server, channel, topic, nick }) {
|
||||
if (nick) {
|
||||
if (topic) {
|
||||
dispatch(inform(`${nick} changed the topic to:`, server, channel));
|
||||
dispatch(print(topic, server, channel));
|
||||
} else {
|
||||
dispatch(inform(`${nick} cleared the topic`, server, channel));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
motd({ content, server }) {
|
||||
dispatch(addMessages(content.map(line => ({ content: line })), server));
|
||||
},
|
||||
|
||||
whois(data) {
|
||||
const tab = getState().tab.selected;
|
||||
if (data.nick) {
|
||||
const tab = getState().tab.selected;
|
||||
|
||||
dispatch(inform([
|
||||
`Nick: ${data.nick}`,
|
||||
`Username: ${data.username}`,
|
||||
`Realname: ${data.realname}`,
|
||||
`Host: ${data.host}`,
|
||||
`Server: ${data.server}`,
|
||||
`Channels: ${data.channels}`
|
||||
], tab.server, tab.name));
|
||||
dispatch(print([
|
||||
`Nick: ${data.nick}`,
|
||||
`Username: ${data.username}`,
|
||||
`Realname: ${data.realname}`,
|
||||
`Host: ${data.host}`,
|
||||
`Server: ${data.server}`,
|
||||
`Channels: ${data.channels}`
|
||||
], tab.server, tab.name));
|
||||
}
|
||||
},
|
||||
|
||||
print({ server, message }) {
|
||||
dispatch(inform(message, server));
|
||||
print(message) {
|
||||
const tab = getState().tab.selected;
|
||||
dispatch(addMessage(message, tab.server, tab.name));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ export const INVITE = 'INVITE';
|
|||
export const JOIN = 'JOIN';
|
||||
export const KICK = 'KICK';
|
||||
export const PART = 'PART';
|
||||
export const SET_TOPIC = 'SET_TOPIC';
|
||||
|
||||
export const SET_ENVIRONMENT = 'SET_ENVIRONMENT';
|
||||
|
||||
|
|
|
@ -269,3 +269,16 @@ export function kick(user, channel, server) {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function setTopic(topic, channel, server) {
|
||||
return {
|
||||
type: actions.SET_TOPIC,
|
||||
topic,
|
||||
channel,
|
||||
server,
|
||||
socket: {
|
||||
type: 'topic',
|
||||
data: { topic, channel, server }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -223,20 +223,24 @@ export function broadcast(message, server, channels) {
|
|||
})), server);
|
||||
}
|
||||
|
||||
export function inform(message, server, channel) {
|
||||
export function print(message, server, channel, type) {
|
||||
if (Array.isArray(message)) {
|
||||
return addMessages(message.map(line => ({
|
||||
content: line,
|
||||
type: 'info'
|
||||
type
|
||||
})), server, channel);
|
||||
}
|
||||
|
||||
return addMessage({
|
||||
content: message,
|
||||
type: 'info'
|
||||
type
|
||||
}, server, channel);
|
||||
}
|
||||
|
||||
export function inform(message, server, channel) {
|
||||
return print(message, server, channel, 'info');
|
||||
}
|
||||
|
||||
export function runCommand(command, channel, server) {
|
||||
return {
|
||||
type: actions.COMMAND,
|
||||
|
|
|
@ -11,6 +11,11 @@ export function normalizeChannel(channel) {
|
|||
return channel.split('#').join('').toLowerCase();
|
||||
}
|
||||
|
||||
export function isChannel(name) {
|
||||
// TODO: Handle other channel types
|
||||
return typeof name === 'string' && name[0] === '#';
|
||||
}
|
||||
|
||||
export function timestamp(date = new Date()) {
|
||||
const h = padStart(date.getHours(), 2, '0');
|
||||
const m = padStart(date.getMinutes(), 2, '0');
|
||||
|
|
|
@ -106,8 +106,12 @@ func (c *Client) Part(channels ...string) {
|
|||
c.Write("PART " + strings.Join(channels, ","))
|
||||
}
|
||||
|
||||
func (c *Client) Topic(channel string) {
|
||||
c.Write("TOPIC " + channel)
|
||||
func (c *Client) Topic(channel string, topic ...string) {
|
||||
msg := "TOPIC " + channel
|
||||
if len(topic) > 0 {
|
||||
msg += " :" + topic[0]
|
||||
}
|
||||
c.Write(msg)
|
||||
}
|
||||
|
||||
func (c *Client) Invite(nick, channel string) {
|
||||
|
|
|
@ -96,6 +96,10 @@ func TestTopic(t *testing.T) {
|
|||
c, out := testClientSend()
|
||||
c.Topic("#chan")
|
||||
assert.Equal(t, "TOPIC #chan\r\n", <-out)
|
||||
c.Topic("#chan", "apple pie")
|
||||
assert.Equal(t, "TOPIC #chan :apple pie\r\n", <-out)
|
||||
c.Topic("#chan", "")
|
||||
assert.Equal(t, "TOPIC #chan :\r\n", <-out)
|
||||
}
|
||||
|
||||
func TestInvite(t *testing.T) {
|
||||
|
|
|
@ -26,6 +26,7 @@ const (
|
|||
ReplyWhoisIdle = "317"
|
||||
ReplyEndOfWhois = "318"
|
||||
ReplyWhoisChannels = "319"
|
||||
ReplyNoTopic = "331"
|
||||
ReplyTopic = "332"
|
||||
ReplyNamReply = "353"
|
||||
ReplyEndOfNames = "366"
|
||||
|
|
|
@ -3,6 +3,7 @@ package server
|
|||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/kjk/betterguid"
|
||||
|
||||
|
@ -52,6 +53,10 @@ func (i *ircHandler) run() {
|
|||
}
|
||||
|
||||
func (i *ircHandler) dispatchMessage(msg *irc.Message) {
|
||||
if msg.Command[0] == '4' {
|
||||
i.session.printError(formatIRCError(msg))
|
||||
}
|
||||
|
||||
if handler, ok := i.handlers[msg.Command]; ok {
|
||||
handler(msg)
|
||||
}
|
||||
|
@ -78,14 +83,18 @@ func (i *ircHandler) join(msg *irc.Message) {
|
|||
Channels: msg.Params,
|
||||
})
|
||||
|
||||
channelStore.AddUser(msg.Nick, i.client.Host, msg.Params[0])
|
||||
channel := msg.Params[0]
|
||||
channelStore.AddUser(msg.Nick, i.client.Host, channel)
|
||||
|
||||
if msg.Nick == i.client.GetNick() {
|
||||
i.session.sendLastMessages(i.client.Host, msg.Params[0], 50)
|
||||
// Incase no topic is set and theres a cached one that needs to be cleared
|
||||
i.client.Topic(channel)
|
||||
|
||||
i.session.sendLastMessages(i.client.Host, channel, 50)
|
||||
|
||||
go i.session.user.AddChannel(storage.Channel{
|
||||
Server: i.client.Host,
|
||||
Name: msg.Params[0],
|
||||
Name: channel,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -193,13 +202,35 @@ func (i *ircHandler) whoisEnd(msg *irc.Message) {
|
|||
}
|
||||
|
||||
func (i *ircHandler) topic(msg *irc.Message) {
|
||||
var channel string
|
||||
var nick string
|
||||
|
||||
if msg.Command == irc.Topic {
|
||||
channel = msg.Params[0]
|
||||
nick = msg.Nick
|
||||
} else {
|
||||
channel = msg.Params[1]
|
||||
}
|
||||
|
||||
i.session.sendJSON("topic", Topic{
|
||||
Server: i.client.Host,
|
||||
Channel: msg.Params[1],
|
||||
Channel: channel,
|
||||
Topic: msg.LastParam(),
|
||||
Nick: nick,
|
||||
})
|
||||
|
||||
channelStore.SetTopic(msg.LastParam(), i.client.Host, msg.Params[1])
|
||||
channelStore.SetTopic(msg.LastParam(), i.client.Host, channel)
|
||||
}
|
||||
|
||||
func (i *ircHandler) noTopic(msg *irc.Message) {
|
||||
channel := msg.Params[1]
|
||||
|
||||
i.session.sendJSON("topic", Topic{
|
||||
Server: i.client.Host,
|
||||
Channel: channel,
|
||||
})
|
||||
|
||||
channelStore.SetTopic("", i.client.Host, channel)
|
||||
}
|
||||
|
||||
func (i *ircHandler) names(msg *irc.Message) {
|
||||
|
@ -245,6 +276,7 @@ func (i *ircHandler) initHandlers() {
|
|||
irc.Privmsg: i.message,
|
||||
irc.Notice: i.message,
|
||||
irc.Quit: i.quit,
|
||||
irc.Topic: i.topic,
|
||||
irc.ReplyWelcome: i.info,
|
||||
irc.ReplyYourHost: i.info,
|
||||
irc.ReplyCreated: i.info,
|
||||
|
@ -257,6 +289,7 @@ func (i *ircHandler) initHandlers() {
|
|||
irc.ReplyWhoisServer: i.whoisServer,
|
||||
irc.ReplyWhoisChannels: i.whoisChannels,
|
||||
irc.ReplyEndOfWhois: i.whoisEnd,
|
||||
irc.ReplyNoTopic: i.noTopic,
|
||||
irc.ReplyTopic: i.topic,
|
||||
irc.ReplyNamReply: i.names,
|
||||
irc.ReplyEndOfNames: i.namesEnd,
|
||||
|
@ -289,6 +322,19 @@ func isChannel(s string) bool {
|
|||
return strings.IndexAny(s, "&#+!") == 0
|
||||
}
|
||||
|
||||
func formatIRCError(msg *irc.Message) string {
|
||||
errMsg := strings.TrimSuffix(msg.LastParam(), ".")
|
||||
if len(msg.Params) > 2 {
|
||||
for _, c := range msg.LastParam() {
|
||||
if unicode.IsLower(c) {
|
||||
return msg.Params[1] + " " + errMsg
|
||||
}
|
||||
return msg.Params[1] + ": " + errMsg
|
||||
}
|
||||
}
|
||||
return errMsg
|
||||
}
|
||||
|
||||
func printMessage(msg *irc.Message, i *irc.Client) {
|
||||
log.Println(i.GetNick()+":", msg.Prefix, msg.Command, msg.Params, msg.LastParam())
|
||||
}
|
||||
|
|
|
@ -225,6 +225,31 @@ func TestHandleIRCTopic(t *testing.T) {
|
|||
Channel: "#chan",
|
||||
Topic: "the topic",
|
||||
}, res)
|
||||
|
||||
res = dispatchMessage(&irc.Message{
|
||||
Command: irc.Topic,
|
||||
Params: []string{"#chan", "the topic"},
|
||||
Nick: "bob",
|
||||
})
|
||||
|
||||
checkResponse(t, "topic", Topic{
|
||||
Server: "host.com",
|
||||
Channel: "#chan",
|
||||
Topic: "the topic",
|
||||
Nick: "bob",
|
||||
}, res)
|
||||
}
|
||||
|
||||
func TestHandleIRCNoTopic(t *testing.T) {
|
||||
res := dispatchMessage(&irc.Message{
|
||||
Command: irc.ReplyNoTopic,
|
||||
Params: []string{"target", "#chan", "No topic set."},
|
||||
})
|
||||
|
||||
checkResponse(t, "topic", Topic{
|
||||
Server: "host.com",
|
||||
Channel: "#chan",
|
||||
}, res)
|
||||
}
|
||||
|
||||
func TestHandleIRCNames(t *testing.T) {
|
||||
|
|
|
@ -61,11 +61,12 @@ type Quit struct {
|
|||
}
|
||||
|
||||
type Message struct {
|
||||
ID string `json:"id"`
|
||||
Server string `json:"server"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Server string `json:"server,omitempty"`
|
||||
From string `json:"from,omitempty"`
|
||||
To string `json:"to,omitempty"`
|
||||
Content string `json:"content"`
|
||||
Type string `json:"type,omitempty"`
|
||||
}
|
||||
|
||||
type Messages struct {
|
||||
|
@ -79,7 +80,8 @@ type Messages struct {
|
|||
type Topic struct {
|
||||
Server string `json:"server"`
|
||||
Channel string `json:"channel"`
|
||||
Topic string `json:"topic"`
|
||||
Topic string `json:"topic,omitempty"`
|
||||
Nick string `json:"nick,omitempty"`
|
||||
}
|
||||
|
||||
type Userlist struct {
|
||||
|
|
|
@ -162,13 +162,19 @@ func (s *Session) sendMessages(server, channel string, count int, fromID string)
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Session) print(server string, a ...interface{}) {
|
||||
func (s *Session) print(a ...interface{}) {
|
||||
s.sendJSON("print", Message{
|
||||
Server: server,
|
||||
Content: fmt.Sprintln(a...),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Session) printError(a ...interface{}) {
|
||||
s.sendJSON("print", Message{
|
||||
Content: fmt.Sprintln(a...),
|
||||
Type: "error",
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Session) resetExpirationIfEmpty() {
|
||||
if s.numIRC() == 0 && s.numWS() == 0 {
|
||||
s.reset <- AnonymousSessionExpiration
|
||||
|
|
|
@ -190,6 +190,15 @@ func (h *wsHandler) nick(b []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
func (h *wsHandler) topic(b []byte) {
|
||||
var data Topic
|
||||
json.Unmarshal(b, &data)
|
||||
|
||||
if i, ok := h.session.getIRC(data.Server); ok {
|
||||
i.Topic(data.Channel, data.Topic)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *wsHandler) invite(b []byte) {
|
||||
var data Invite
|
||||
json.Unmarshal(b, &data)
|
||||
|
@ -282,6 +291,7 @@ func (h *wsHandler) initHandlers() {
|
|||
"quit": h.quit,
|
||||
"message": h.message,
|
||||
"nick": h.nick,
|
||||
"topic": h.topic,
|
||||
"invite": h.invite,
|
||||
"kick": h.kick,
|
||||
"whois": h.whois,
|
||||
|
|
Loading…
Reference in New Issue