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;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.message-error {
|
||||||
|
color: #F6546A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-prompt {
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
color: #6BB758;
|
||||||
|
}
|
||||||
|
|
||||||
.message-action {
|
.message-action {
|
||||||
color: #FF6698;
|
color: #FF6698;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-time {
|
.message-time {
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import createCommandMiddleware from './middleware/command';
|
import createCommandMiddleware, { beforeHandler, notFoundHandler } from './middleware/command';
|
||||||
import { COMMAND } from './state/actions';
|
import { COMMAND } from './state/actions';
|
||||||
import { join, part, invite, kick } from './state/channels';
|
import { join, part, invite, kick, setTopic } from './state/channels';
|
||||||
import { sendMessage, addMessage, raw } from './state/messages';
|
import { sendMessage, raw } from './state/messages';
|
||||||
import { setNick, disconnect, whois, away } from './state/servers';
|
import { setNick, disconnect, whois, away } from './state/servers';
|
||||||
import { select } from './state/tab';
|
import { select } from './state/tab';
|
||||||
|
import { find } from './util';
|
||||||
|
|
||||||
const help = [
|
const help = [
|
||||||
'/join <channel> - Join a channel',
|
'/join <channel> - Join a channel',
|
||||||
@ -11,35 +12,50 @@ const help = [
|
|||||||
'/nick <nick> - Change nick',
|
'/nick <nick> - Change nick',
|
||||||
'/quit - Disconnect from the current server',
|
'/quit - Disconnect from the current server',
|
||||||
'/me <message> - Send action message',
|
'/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',
|
'/msg <target> <message> - Send message to the specified channel or user',
|
||||||
'/say <message> - Send message to the current chat',
|
'/say <message> - Send message to the current chat',
|
||||||
'/invite <user> [channel] - Invite user to the current or specified channel',
|
'/invite <nick> [channel] - Invite user to the current or specified channel',
|
||||||
'/kick <user> - Kick user from the current channel',
|
'/kick <nick> - Kick user from the current channel',
|
||||||
'/whois <user> - Get information about user',
|
'/whois <nick> - Get information about user',
|
||||||
'/away [message] - Set or clear away message',
|
'/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, {
|
export default createCommandMiddleware(COMMAND, {
|
||||||
join({ dispatch, server }, channel) {
|
join({ dispatch, server }, channel) {
|
||||||
if (channel) {
|
if (channel) {
|
||||||
|
if (channel[0] !== '#') {
|
||||||
|
return error('Bad channel name');
|
||||||
|
}
|
||||||
dispatch(join([channel], server));
|
dispatch(join([channel], server));
|
||||||
dispatch(select(server, channel));
|
dispatch(select(server, channel));
|
||||||
|
} else {
|
||||||
|
return error('Missing channel');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
part({ dispatch, server, channel }, partChannel) {
|
part({ dispatch, server, channel, isChannel }, partChannel) {
|
||||||
if (partChannel) {
|
if (partChannel) {
|
||||||
dispatch(part([partChannel], server));
|
dispatch(part([partChannel], server));
|
||||||
} else {
|
} else if (isChannel) {
|
||||||
dispatch(part([channel], server));
|
dispatch(part([channel], server));
|
||||||
|
} else {
|
||||||
|
return error('This is not a channel');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
nick({ dispatch, server }, nick) {
|
nick({ dispatch, server }, nick) {
|
||||||
if (nick) {
|
if (nick) {
|
||||||
dispatch(setNick(nick, server));
|
dispatch(setNick(nick, server));
|
||||||
|
} else {
|
||||||
|
return error('Missing nick');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -47,74 +63,127 @@ export default createCommandMiddleware(COMMAND, {
|
|||||||
dispatch(disconnect(server));
|
dispatch(disconnect(server));
|
||||||
},
|
},
|
||||||
|
|
||||||
me({ dispatch, server, channel }, ...params) {
|
me({ dispatch, server, channel }, ...message) {
|
||||||
if (params.length > 0) {
|
const msg = message.join(' ');
|
||||||
dispatch(sendMessage(`\x01ACTION ${params.join(' ')}\x01`, channel, server));
|
if (msg !== '') {
|
||||||
|
dispatch(sendMessage(`\x01ACTION ${msg}\x01`, channel, server));
|
||||||
|
} else {
|
||||||
|
return error('Messages can not be empty');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
topic({ dispatch, getState, server, channel }) {
|
topic({ dispatch, getState, server, channel }, ...newTopic) {
|
||||||
const topic = getState().channels.getIn([server, channel, 'topic']);
|
if (newTopic.length > 0) {
|
||||||
if (topic) {
|
dispatch(setTopic(newTopic.join(' '), channel, server));
|
||||||
dispatch(addMessage({
|
|
||||||
server,
|
|
||||||
to: channel,
|
|
||||||
content: topic
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
|
const topic = getState().channels.getIn([server, channel, 'topic']);
|
||||||
|
if (topic) {
|
||||||
|
return text(topic);
|
||||||
|
}
|
||||||
return 'No topic set';
|
return 'No topic set';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
msg({ dispatch, server }, target, ...message) {
|
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(sendMessage(message.join(' '), target, server));
|
||||||
|
dispatch(select(server, target));
|
||||||
|
} else {
|
||||||
|
return error('Messages can not be empty');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
say({ dispatch, server, channel }, ...message) {
|
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));
|
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) {
|
if (user && inviteChannel) {
|
||||||
dispatch(invite(user, inviteChannel, server));
|
dispatch(invite(user, inviteChannel, server));
|
||||||
} else if (user && channel) {
|
} else if (user && channel) {
|
||||||
dispatch(invite(user, channel, server));
|
dispatch(invite(user, channel, server));
|
||||||
|
} else {
|
||||||
|
return error('Missing nick');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
kick({ dispatch, server, channel }, user) {
|
kick({ dispatch, server, channel, isChannel }, user) {
|
||||||
if (user && channel) {
|
if (!isChannel) {
|
||||||
|
return error('This is not a channel');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user) {
|
||||||
dispatch(kick(user, channel, server));
|
dispatch(kick(user, channel, server));
|
||||||
|
} else {
|
||||||
|
return error('Missing nick');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
whois({ dispatch, server }, user) {
|
whois({ dispatch, server }, user) {
|
||||||
if (user) {
|
if (user) {
|
||||||
dispatch(whois(user, server));
|
dispatch(whois(user, server));
|
||||||
|
} else {
|
||||||
|
return error('Missing nick');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
away({ dispatch, server }, message) {
|
away({ dispatch, server }, ...message) {
|
||||||
dispatch(away(message, server));
|
const msg = message.join(' ');
|
||||||
|
dispatch(away(msg, server));
|
||||||
|
if (msg !== '') {
|
||||||
|
return 'Away message set';
|
||||||
|
}
|
||||||
|
return 'Away message cleared';
|
||||||
},
|
},
|
||||||
|
|
||||||
raw({ dispatch, server }, ...message) {
|
raw({ dispatch, server }, ...message) {
|
||||||
if (message) {
|
if (message.length > 0 && message[0] !== '') {
|
||||||
const cmd = message.join(' ');
|
const cmd = `${message[0].toUpperCase()} ${message.slice(1).join(' ')}`;
|
||||||
dispatch(raw(cmd, server));
|
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() {
|
[notFoundHandler](_, command) {
|
||||||
return help;
|
return error(`=> /${command}: No such command`);
|
||||||
},
|
|
||||||
|
|
||||||
commandNotFound(_, command) {
|
|
||||||
return `The command /${command} was not found`;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -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 }) {
|
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) {
|
export default function createCommandMiddleware(type, handlers) {
|
||||||
@ -13,16 +30,15 @@ export default function createCommandMiddleware(type, handlers) {
|
|||||||
const command = words[0];
|
const command = words[0];
|
||||||
const params = words.slice(1);
|
const params = words.slice(1);
|
||||||
|
|
||||||
let result;
|
|
||||||
|
|
||||||
if (command in handlers) {
|
if (command in handlers) {
|
||||||
result = handlers[command](createContext(store, action), ...params);
|
const ctx = createContext(store, action);
|
||||||
} else if (notFound in handlers) {
|
if (beforeHandler in handlers) {
|
||||||
result = handlers[notFound](createContext(store, action), command);
|
process(ctx, handlers[beforeHandler](ctx, command, ...params));
|
||||||
}
|
}
|
||||||
|
process(ctx, handlers[command](ctx, ...params));
|
||||||
if (typeof result === 'string' || Array.isArray(result)) {
|
} else if (notFoundHandler in handlers) {
|
||||||
store.dispatch(inform(result, action.server, action.channel));
|
const ctx = createContext(store, action);
|
||||||
|
process(ctx, handlers[notFoundHandler](ctx, command));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { socketAction } from '../state/actions';
|
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 { select } from '../state/tab';
|
||||||
import { normalizeChannel } from '../util';
|
import { normalizeChannel } from '../util';
|
||||||
import { replace } from '../util/router';
|
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));
|
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 }) {
|
motd({ content, server }) {
|
||||||
dispatch(addMessages(content.map(line => ({ content: line })), server));
|
dispatch(addMessages(content.map(line => ({ content: line })), server));
|
||||||
},
|
},
|
||||||
|
|
||||||
whois(data) {
|
whois(data) {
|
||||||
const tab = getState().tab.selected;
|
if (data.nick) {
|
||||||
|
const tab = getState().tab.selected;
|
||||||
|
|
||||||
dispatch(inform([
|
dispatch(print([
|
||||||
`Nick: ${data.nick}`,
|
`Nick: ${data.nick}`,
|
||||||
`Username: ${data.username}`,
|
`Username: ${data.username}`,
|
||||||
`Realname: ${data.realname}`,
|
`Realname: ${data.realname}`,
|
||||||
`Host: ${data.host}`,
|
`Host: ${data.host}`,
|
||||||
`Server: ${data.server}`,
|
`Server: ${data.server}`,
|
||||||
`Channels: ${data.channels}`
|
`Channels: ${data.channels}`
|
||||||
], tab.server, tab.name));
|
], tab.server, tab.name));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
print({ server, message }) {
|
print(message) {
|
||||||
dispatch(inform(message, server));
|
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 JOIN = 'JOIN';
|
||||||
export const KICK = 'KICK';
|
export const KICK = 'KICK';
|
||||||
export const PART = 'PART';
|
export const PART = 'PART';
|
||||||
|
export const SET_TOPIC = 'SET_TOPIC';
|
||||||
|
|
||||||
export const SET_ENVIRONMENT = 'SET_ENVIRONMENT';
|
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);
|
})), server);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function inform(message, server, channel) {
|
export function print(message, server, channel, type) {
|
||||||
if (Array.isArray(message)) {
|
if (Array.isArray(message)) {
|
||||||
return addMessages(message.map(line => ({
|
return addMessages(message.map(line => ({
|
||||||
content: line,
|
content: line,
|
||||||
type: 'info'
|
type
|
||||||
})), server, channel);
|
})), server, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return addMessage({
|
return addMessage({
|
||||||
content: message,
|
content: message,
|
||||||
type: 'info'
|
type
|
||||||
}, server, channel);
|
}, server, channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function inform(message, server, channel) {
|
||||||
|
return print(message, server, channel, 'info');
|
||||||
|
}
|
||||||
|
|
||||||
export function runCommand(command, channel, server) {
|
export function runCommand(command, channel, server) {
|
||||||
return {
|
return {
|
||||||
type: actions.COMMAND,
|
type: actions.COMMAND,
|
||||||
|
@ -11,6 +11,11 @@ export function normalizeChannel(channel) {
|
|||||||
return channel.split('#').join('').toLowerCase();
|
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()) {
|
export function timestamp(date = new Date()) {
|
||||||
const h = padStart(date.getHours(), 2, '0');
|
const h = padStart(date.getHours(), 2, '0');
|
||||||
const m = padStart(date.getMinutes(), 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, ","))
|
c.Write("PART " + strings.Join(channels, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Topic(channel string) {
|
func (c *Client) Topic(channel string, topic ...string) {
|
||||||
c.Write("TOPIC " + channel)
|
msg := "TOPIC " + channel
|
||||||
|
if len(topic) > 0 {
|
||||||
|
msg += " :" + topic[0]
|
||||||
|
}
|
||||||
|
c.Write(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Invite(nick, channel string) {
|
func (c *Client) Invite(nick, channel string) {
|
||||||
|
@ -96,6 +96,10 @@ func TestTopic(t *testing.T) {
|
|||||||
c, out := testClientSend()
|
c, out := testClientSend()
|
||||||
c.Topic("#chan")
|
c.Topic("#chan")
|
||||||
assert.Equal(t, "TOPIC #chan\r\n", <-out)
|
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) {
|
func TestInvite(t *testing.T) {
|
||||||
|
@ -26,6 +26,7 @@ const (
|
|||||||
ReplyWhoisIdle = "317"
|
ReplyWhoisIdle = "317"
|
||||||
ReplyEndOfWhois = "318"
|
ReplyEndOfWhois = "318"
|
||||||
ReplyWhoisChannels = "319"
|
ReplyWhoisChannels = "319"
|
||||||
|
ReplyNoTopic = "331"
|
||||||
ReplyTopic = "332"
|
ReplyTopic = "332"
|
||||||
ReplyNamReply = "353"
|
ReplyNamReply = "353"
|
||||||
ReplyEndOfNames = "366"
|
ReplyEndOfNames = "366"
|
||||||
|
@ -3,6 +3,7 @@ package server
|
|||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/kjk/betterguid"
|
"github.com/kjk/betterguid"
|
||||||
|
|
||||||
@ -52,6 +53,10 @@ func (i *ircHandler) run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *ircHandler) dispatchMessage(msg *irc.Message) {
|
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 {
|
if handler, ok := i.handlers[msg.Command]; ok {
|
||||||
handler(msg)
|
handler(msg)
|
||||||
}
|
}
|
||||||
@ -78,14 +83,18 @@ func (i *ircHandler) join(msg *irc.Message) {
|
|||||||
Channels: msg.Params,
|
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() {
|
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{
|
go i.session.user.AddChannel(storage.Channel{
|
||||||
Server: i.client.Host,
|
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) {
|
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{
|
i.session.sendJSON("topic", Topic{
|
||||||
Server: i.client.Host,
|
Server: i.client.Host,
|
||||||
Channel: msg.Params[1],
|
Channel: channel,
|
||||||
Topic: msg.LastParam(),
|
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) {
|
func (i *ircHandler) names(msg *irc.Message) {
|
||||||
@ -245,6 +276,7 @@ func (i *ircHandler) initHandlers() {
|
|||||||
irc.Privmsg: i.message,
|
irc.Privmsg: i.message,
|
||||||
irc.Notice: i.message,
|
irc.Notice: i.message,
|
||||||
irc.Quit: i.quit,
|
irc.Quit: i.quit,
|
||||||
|
irc.Topic: i.topic,
|
||||||
irc.ReplyWelcome: i.info,
|
irc.ReplyWelcome: i.info,
|
||||||
irc.ReplyYourHost: i.info,
|
irc.ReplyYourHost: i.info,
|
||||||
irc.ReplyCreated: i.info,
|
irc.ReplyCreated: i.info,
|
||||||
@ -257,6 +289,7 @@ func (i *ircHandler) initHandlers() {
|
|||||||
irc.ReplyWhoisServer: i.whoisServer,
|
irc.ReplyWhoisServer: i.whoisServer,
|
||||||
irc.ReplyWhoisChannels: i.whoisChannels,
|
irc.ReplyWhoisChannels: i.whoisChannels,
|
||||||
irc.ReplyEndOfWhois: i.whoisEnd,
|
irc.ReplyEndOfWhois: i.whoisEnd,
|
||||||
|
irc.ReplyNoTopic: i.noTopic,
|
||||||
irc.ReplyTopic: i.topic,
|
irc.ReplyTopic: i.topic,
|
||||||
irc.ReplyNamReply: i.names,
|
irc.ReplyNamReply: i.names,
|
||||||
irc.ReplyEndOfNames: i.namesEnd,
|
irc.ReplyEndOfNames: i.namesEnd,
|
||||||
@ -289,6 +322,19 @@ func isChannel(s string) bool {
|
|||||||
return strings.IndexAny(s, "&#+!") == 0
|
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) {
|
func printMessage(msg *irc.Message, i *irc.Client) {
|
||||||
log.Println(i.GetNick()+":", msg.Prefix, msg.Command, msg.Params, msg.LastParam())
|
log.Println(i.GetNick()+":", msg.Prefix, msg.Command, msg.Params, msg.LastParam())
|
||||||
}
|
}
|
||||||
|
@ -225,6 +225,31 @@ func TestHandleIRCTopic(t *testing.T) {
|
|||||||
Channel: "#chan",
|
Channel: "#chan",
|
||||||
Topic: "the topic",
|
Topic: "the topic",
|
||||||
}, res)
|
}, 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) {
|
func TestHandleIRCNames(t *testing.T) {
|
||||||
|
@ -61,11 +61,12 @@ type Quit struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id,omitempty"`
|
||||||
Server string `json:"server"`
|
Server string `json:"server,omitempty"`
|
||||||
From string `json:"from,omitempty"`
|
From string `json:"from,omitempty"`
|
||||||
To string `json:"to,omitempty"`
|
To string `json:"to,omitempty"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Messages struct {
|
type Messages struct {
|
||||||
@ -79,7 +80,8 @@ type Messages struct {
|
|||||||
type Topic struct {
|
type Topic struct {
|
||||||
Server string `json:"server"`
|
Server string `json:"server"`
|
||||||
Channel string `json:"channel"`
|
Channel string `json:"channel"`
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic,omitempty"`
|
||||||
|
Nick string `json:"nick,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Userlist struct {
|
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{
|
s.sendJSON("print", Message{
|
||||||
Server: server,
|
|
||||||
Content: fmt.Sprintln(a...),
|
Content: fmt.Sprintln(a...),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Session) printError(a ...interface{}) {
|
||||||
|
s.sendJSON("print", Message{
|
||||||
|
Content: fmt.Sprintln(a...),
|
||||||
|
Type: "error",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Session) resetExpirationIfEmpty() {
|
func (s *Session) resetExpirationIfEmpty() {
|
||||||
if s.numIRC() == 0 && s.numWS() == 0 {
|
if s.numIRC() == 0 && s.numWS() == 0 {
|
||||||
s.reset <- AnonymousSessionExpiration
|
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) {
|
func (h *wsHandler) invite(b []byte) {
|
||||||
var data Invite
|
var data Invite
|
||||||
json.Unmarshal(b, &data)
|
json.Unmarshal(b, &data)
|
||||||
@ -282,6 +291,7 @@ func (h *wsHandler) initHandlers() {
|
|||||||
"quit": h.quit,
|
"quit": h.quit,
|
||||||
"message": h.message,
|
"message": h.message,
|
||||||
"nick": h.nick,
|
"nick": h.nick,
|
||||||
|
"topic": h.topic,
|
||||||
"invite": h.invite,
|
"invite": h.invite,
|
||||||
"kick": h.kick,
|
"kick": h.kick,
|
||||||
"whois": h.whois,
|
"whois": h.whois,
|
||||||
|
Loading…
Reference in New Issue
Block a user