IRC output gets queued until RPL_WELCOME, added tcp timeouts and error handling, store selected tab in localStorage, more design work, upgraded to lodash 3.0.0
This commit is contained in:
parent
5c6c43e017
commit
3c02b00303
@ -19,7 +19,7 @@
|
|||||||
"reactify": "~0.17.1"
|
"reactify": "~0.17.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "~2.4.1",
|
"lodash": "3.0.0",
|
||||||
"reflux": "~0.2.2",
|
"reflux": "~0.2.2",
|
||||||
"react-router": "~0.11.6",
|
"react-router": "~0.11.6",
|
||||||
"react": "~0.12.2"
|
"react": "~0.12.2"
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>IRC</title>
|
<title>IRC</title>
|
||||||
<link href='http://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
|
<link href="//fonts.googleapis.com/css?family=Montserrat|Roboto" rel="stylesheet">
|
||||||
<link href="style.css" rel="stylesheet">
|
<link href="style.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -8,11 +8,12 @@ var serverActions = Reflux.createActions([
|
|||||||
'load'
|
'load'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
serverActions.connect.preEmit = function(server, nick, username) {
|
serverActions.connect.preEmit = function(server, nick, username, tls) {
|
||||||
socket.send('connect', {
|
socket.send('connect', {
|
||||||
server: server,
|
server: server,
|
||||||
nick: nick,
|
nick: nick,
|
||||||
username: username
|
username: username,
|
||||||
|
tls: tls || false
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -14,8 +14,14 @@ socket.on('connect', function() {
|
|||||||
socket.send('uuid', uuid);
|
socket.send('uuid', uuid);
|
||||||
|
|
||||||
serverActions.connect('irc.freenode.net', nick, 'username');
|
serverActions.connect('irc.freenode.net', nick, 'username');
|
||||||
|
serverActions.connect('irc.quakenet.org', nick, 'username');
|
||||||
|
|
||||||
channelActions.join(['#stuff'], 'irc.freenode.net');
|
channelActions.join(['#stuff'], 'irc.freenode.net');
|
||||||
tabActions.select('irc.freenode.net');
|
channelActions.join(['#herp'], 'irc.quakenet.org');
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('error', function(error) {
|
||||||
|
console.log(error.server + ': ' + error.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
React.render(<App />, document.body);
|
React.render(<App />, document.body);
|
@ -20,18 +20,15 @@ var ChatTitle = React.createClass({
|
|||||||
render: function() {
|
render: function() {
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
var title;
|
var title;
|
||||||
|
var topic;
|
||||||
|
var usercount;
|
||||||
|
|
||||||
if (tab.channel) {
|
if (tab.channel && this.state.channels[tab.server]) {
|
||||||
var channel = this.state.channels[tab.server][tab.channel];
|
var channel = this.state.channels[tab.server][tab.channel];
|
||||||
if (channel) {
|
if (channel) {
|
||||||
title = tab.channel
|
title = tab.channel
|
||||||
title += ' [';
|
usercount = channel.users.length;
|
||||||
title += channel.users.length;
|
topic = channel.topic || '';
|
||||||
title += ']';
|
|
||||||
|
|
||||||
if (channel.topic) {
|
|
||||||
title += ': ' + channel.topic;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
title = tab.server;
|
title = tab.server;
|
||||||
@ -39,7 +36,11 @@ var ChatTitle = React.createClass({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="chat-title-bar">
|
<div className="chat-title-bar">
|
||||||
<span className="chat-title" title={title}>{title}</span>
|
<div>
|
||||||
|
<span className="chat-title">{title}</span>
|
||||||
|
<span className="chat-topic" title={topic}>{topic}</span>
|
||||||
|
</div>
|
||||||
|
<span className="chat-usercount">{usercount}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ var React = require('react');
|
|||||||
var Reflux = require('reflux');
|
var Reflux = require('reflux');
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
var util = require('../util');
|
||||||
var messageStore = require('../stores/message');
|
var messageStore = require('../stores/message');
|
||||||
var selectedTabStore = require('../stores/selectedTab');
|
var selectedTabStore = require('../stores/selectedTab');
|
||||||
|
|
||||||
@ -38,12 +39,20 @@ var MessageBox = React.createClass({
|
|||||||
if (this.state.messages[tab.server] && dest) {
|
if (this.state.messages[tab.server] && dest) {
|
||||||
messages = _.map(this.state.messages[tab.server][dest], function(message) {
|
messages = _.map(this.state.messages[tab.server][dest], function(message) {
|
||||||
var messageClass = 'message';
|
var messageClass = 'message';
|
||||||
|
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case 'info':
|
case 'info':
|
||||||
messageClass += ' message-info';
|
messageClass += ' message-info';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return <p className={messageClass}>{message.from ? message.from + ': ' : null}{message.message}</p>;
|
|
||||||
|
return (
|
||||||
|
<p className={messageClass}>
|
||||||
|
<span className="message-time">{util.timestamp(message.time)}</span>
|
||||||
|
{ message.from ? <span className="message-sender">{message.from}</span> : null }
|
||||||
|
{message.message}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,16 +21,41 @@ var TabList = React.createClass({
|
|||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var tabClass;
|
||||||
|
var selected = this.state.selectedTab;
|
||||||
|
|
||||||
var tabs = _.map(this.state.channels, function(server, address) {
|
var tabs = _.map(this.state.channels, function(server, address) {
|
||||||
var channels = _.map(server, function(channel, name) {
|
var channels = _.map(server, function(channel, name) {
|
||||||
return <p onClick={tabActions.select.bind(null, address, name)}>{name}</p>;
|
if (address === selected.server &&
|
||||||
|
name === selected.channel) {
|
||||||
|
tabClass = 'selected';
|
||||||
|
} else {
|
||||||
|
tabClass = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return <p className={tabClass} onClick={tabActions.select.bind(null, address, name)}>{name}</p>;
|
||||||
});
|
});
|
||||||
channels.unshift(<p onClick={tabActions.select.bind(null, address, null)}>{address}</p>);
|
|
||||||
|
if (address === selected.server &&
|
||||||
|
selected.channel === null) {
|
||||||
|
tabClass = 'tab-server selected';
|
||||||
|
} else {
|
||||||
|
tabClass = 'tab-server';
|
||||||
|
}
|
||||||
|
|
||||||
|
channels.unshift(<p className={tabClass} onClick={tabActions.select.bind(null, address, null)}>{address}</p>);
|
||||||
|
|
||||||
return channels;
|
return channels;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="tablist">{tabs}</div>
|
<div className="tablist">
|
||||||
|
<button className="button-connect">Add Network</button>
|
||||||
|
{tabs}
|
||||||
|
<div className="side-buttons">
|
||||||
|
<button>Settings</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -22,7 +22,7 @@ var UserList = React.createClass({
|
|||||||
var users = null;
|
var users = null;
|
||||||
var tab = this.state.selectedTab;
|
var tab = this.state.selectedTab;
|
||||||
|
|
||||||
if (tab.channel) {
|
if (tab.channel && this.state.channels[tab.server]) {
|
||||||
var channel = this.state.channels[tab.server][tab.channel];
|
var channel = this.state.channels[tab.server][tab.channel];
|
||||||
if (channel) {
|
if (channel) {
|
||||||
users = _.map(channel.users, function(user) {
|
users = _.map(channel.users, function(user) {
|
||||||
|
@ -2,6 +2,7 @@ var Reflux = require('reflux');
|
|||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
var actions = require('../actions/channel');
|
var actions = require('../actions/channel');
|
||||||
|
var serverActions = require('../actions/server');
|
||||||
|
|
||||||
var channels = {};
|
var channels = {};
|
||||||
|
|
||||||
@ -80,6 +81,7 @@ function sortUsers(server, channel) {
|
|||||||
var channelStore = Reflux.createStore({
|
var channelStore = Reflux.createStore({
|
||||||
init: function() {
|
init: function() {
|
||||||
this.listenToMany(actions);
|
this.listenToMany(actions);
|
||||||
|
this.listenTo(serverActions.connect, 'addServer');
|
||||||
},
|
},
|
||||||
|
|
||||||
part: function(partChannels, server) {
|
part: function(partChannels, server) {
|
||||||
@ -161,6 +163,13 @@ var channelStore = Reflux.createStore({
|
|||||||
this.trigger(channels);
|
this.trigger(channels);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addServer: function(server) {
|
||||||
|
if (!(server in channels)) {
|
||||||
|
channels[server] = {};
|
||||||
|
this.trigger(channels);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getState: function() {
|
getState: function() {
|
||||||
return channels;
|
return channels;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
var Reflux = require('reflux');
|
var Reflux = require('reflux');
|
||||||
|
|
||||||
var serverStore = require('../stores/server');
|
var serverStore = require('./server');
|
||||||
var actions = require('../actions/message');
|
var actions = require('../actions/message');
|
||||||
|
|
||||||
var messages = {};
|
var messages = {};
|
||||||
@ -26,7 +26,8 @@ var messageStore = Reflux.createStore({
|
|||||||
server: server,
|
server: server,
|
||||||
from: serverStore.getNick(server),
|
from: serverStore.getNick(server),
|
||||||
to: to,
|
to: to,
|
||||||
message: message
|
message: message,
|
||||||
|
time: new Date()
|
||||||
}, to);
|
}, to);
|
||||||
|
|
||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
@ -38,6 +39,8 @@ var messageStore = Reflux.createStore({
|
|||||||
dest = message.server;
|
dest = message.server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message.time = new Date();
|
||||||
|
|
||||||
addMessage(message, dest);
|
addMessage(message, dest);
|
||||||
this.trigger(messages);
|
this.trigger(messages);
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,11 @@ var actions = require('../actions/tab');
|
|||||||
var channelActions = require('../actions/channel');
|
var channelActions = require('../actions/channel');
|
||||||
|
|
||||||
var selectedTab = {};
|
var selectedTab = {};
|
||||||
|
var stored = localStorage.selectedTab;
|
||||||
|
|
||||||
|
if (stored) {
|
||||||
|
selectedTab = JSON.parse(stored);
|
||||||
|
}
|
||||||
|
|
||||||
var selectedTabStore = Reflux.createStore({
|
var selectedTabStore = Reflux.createStore({
|
||||||
init: function() {
|
init: function() {
|
||||||
@ -36,4 +41,8 @@ var selectedTabStore = Reflux.createStore({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
selectedTabStore.listen(function(selected) {
|
||||||
|
localStorage.selectedTab = JSON.stringify(selected);
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = selectedTabStore;
|
module.exports = selectedTabStore;
|
@ -1,6 +1,17 @@
|
|||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
exports.UUID = function() {
|
exports.UUID = function() {
|
||||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
||||||
return v.toString(16);
|
return v.toString(16);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.timestamp = function(date) {
|
||||||
|
date = date || new Date();
|
||||||
|
|
||||||
|
var h = _.padLeft(date.getHours(), 2, '0')
|
||||||
|
var m = _.padLeft(date.getMinutes(), 2, '0');
|
||||||
|
|
||||||
|
return h + ':' + m;
|
||||||
|
};
|
@ -5,17 +5,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: Inconsolata, sans-serif;
|
font-family: Roboto, sans-serif;
|
||||||
background: #f0f0f0;
|
background: #f0f0f0;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
font: 16px Inconsolata, sans-serif;
|
font: 16px Roboto, sans-serif;
|
||||||
border: none;
|
border: none;
|
||||||
border-top: 1px solid #DDD;
|
border-top: 1px solid #DDD;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font: 16px Montserrat, sans-serif;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
@ -26,20 +33,64 @@ p {
|
|||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
padding: 15px;
|
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
background: #272626;
|
background: #222;
|
||||||
color: #FFF;
|
color: #FFF;
|
||||||
|
font-family: Montserrat, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tablist p {
|
.tablist p {
|
||||||
|
padding: 3px 15px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tablist p:hover {
|
.tablist p:hover {
|
||||||
|
background: #111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tablist p.selected {
|
||||||
|
padding-left: 10px;
|
||||||
|
border-left: 5px solid #6BB758;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-server {
|
||||||
|
color: #AAA;
|
||||||
|
margin-top: 15px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-connect {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
background: #6BB758;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-connect:hover {
|
||||||
|
background: #7BBF6A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-connect:active {
|
||||||
|
background: #6BB758;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-buttons {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
height: 50px;
|
||||||
|
width: 200px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side-buttons button {
|
||||||
|
background: #333;
|
||||||
|
color: #FFF;
|
||||||
|
margin: 5px;
|
||||||
|
height: 40px;
|
||||||
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-title-bar {
|
.chat-title-bar {
|
||||||
|
font-family: Montserrat, sans-serif;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 200px;
|
left: 200px;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -47,14 +98,34 @@ p {
|
|||||||
height: 50px;
|
height: 50px;
|
||||||
padding: 0 15px;
|
padding: 0 15px;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
border-bottom: 1px solid #DDD;
|
border-bottom: 1px solid #DDD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chat-title-bar div {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 100px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.chat-title {
|
.chat-title {
|
||||||
font-size: 24px;
|
margin-left: 15px;
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-topic {
|
||||||
|
display: none;
|
||||||
|
font: 16px Roboto, sans-serif;
|
||||||
|
line-height: 50px;
|
||||||
|
vertical-align: top;
|
||||||
|
color: #222;
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-usercount {
|
||||||
|
font-size: 20px;
|
||||||
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.messagebox {
|
.messagebox {
|
||||||
@ -69,11 +140,25 @@ p {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.message {
|
.message {
|
||||||
|
padding-left: 50px;
|
||||||
|
text-indent: -50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message span {
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-info {
|
.message-info {
|
||||||
color: #666;
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-time {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.message-sender {
|
||||||
|
color: #6BB758;
|
||||||
|
font: 16px Montserrat, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-input-wrap {
|
.message-input-wrap {
|
||||||
@ -100,4 +185,5 @@ p {
|
|||||||
padding: 15px;
|
padding: 15px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
border-left: 1px solid #DDD;
|
border-left: 1px solid #DDD;
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
54
irc.go
54
irc.go
@ -6,6 +6,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -54,6 +56,8 @@ type Message struct {
|
|||||||
type IRC struct {
|
type IRC struct {
|
||||||
conn net.Conn
|
conn net.Conn
|
||||||
reader *bufio.Reader
|
reader *bufio.Reader
|
||||||
|
out chan string
|
||||||
|
ready sync.WaitGroup
|
||||||
|
|
||||||
Messages chan *Message
|
Messages chan *Message
|
||||||
Server string
|
Server string
|
||||||
@ -71,10 +75,11 @@ func NewIRC(nick, username string) *IRC {
|
|||||||
Username: username,
|
Username: username,
|
||||||
Realname: nick,
|
Realname: nick,
|
||||||
Messages: make(chan *Message, 32),
|
Messages: make(chan *Message, 32),
|
||||||
|
out: make(chan string, 32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) Connect(address string) {
|
func (i *IRC) Connect(address string) error {
|
||||||
if idx := strings.Index(address, ":"); idx < 0 {
|
if idx := strings.Index(address, ":"); idx < 0 {
|
||||||
i.Host = address
|
i.Host = address
|
||||||
|
|
||||||
@ -88,13 +93,24 @@ func (i *IRC) Connect(address string) {
|
|||||||
}
|
}
|
||||||
i.Server = address
|
i.Server = address
|
||||||
|
|
||||||
|
dialer := &net.Dialer{Timeout: 5 * time.Second}
|
||||||
|
|
||||||
if i.TLS {
|
if i.TLS {
|
||||||
if i.TLSConfig == nil {
|
if i.TLSConfig == nil {
|
||||||
i.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
i.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
}
|
}
|
||||||
i.conn, _ = tls.Dial("tcp", address, i.TLSConfig)
|
|
||||||
|
if conn, err := tls.DialWithDialer(dialer, "tcp", address, i.TLSConfig); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
i.conn = conn
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
i.conn, _ = net.Dial("tcp", address)
|
if conn, err := dialer.Dial("tcp", address); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
i.conn = conn
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i.reader = bufio.NewReader(i.conn)
|
i.reader = bufio.NewReader(i.conn)
|
||||||
@ -102,19 +118,23 @@ func (i *IRC) Connect(address string) {
|
|||||||
i.Nick(i.nick)
|
i.Nick(i.nick)
|
||||||
i.User(i.Username, i.Realname)
|
i.User(i.Username, i.Realname)
|
||||||
|
|
||||||
|
i.ready.Add(1)
|
||||||
|
go i.send()
|
||||||
go i.recv()
|
go i.recv()
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) Pass(password string) {
|
func (i *IRC) Pass(password string) {
|
||||||
i.Write("PASS " + password)
|
i.write("PASS " + password)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) Nick(nick string) {
|
func (i *IRC) Nick(nick string) {
|
||||||
i.Write("NICK " + nick)
|
i.write("NICK " + nick)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) User(username, realname string) {
|
func (i *IRC) User(username, realname string) {
|
||||||
i.Writef("USER %s 0 * :%s", username, realname)
|
i.writef("USER %s 0 * :%s", username, realname)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) Join(channels ...string) {
|
func (i *IRC) Join(channels ...string) {
|
||||||
@ -147,13 +167,28 @@ func (i *IRC) Quit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) Write(data string) {
|
func (i *IRC) Write(data string) {
|
||||||
fmt.Fprint(i.conn, data+"\r\n")
|
i.out <- data + "\r\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *IRC) Writef(format string, a ...interface{}) {
|
func (i *IRC) Writef(format string, a ...interface{}) {
|
||||||
|
i.out <- fmt.Sprintf(format+"\r\n", a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IRC) write(data string) {
|
||||||
|
fmt.Fprint(i.conn, data+"\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IRC) writef(format string, a ...interface{}) {
|
||||||
fmt.Fprintf(i.conn, format+"\r\n", a...)
|
fmt.Fprintf(i.conn, format+"\r\n", a...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *IRC) send() {
|
||||||
|
i.ready.Wait()
|
||||||
|
for message := range i.out {
|
||||||
|
fmt.Fprint(i.conn, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (i *IRC) recv() {
|
func (i *IRC) recv() {
|
||||||
defer i.conn.Close()
|
defer i.conn.Close()
|
||||||
for {
|
for {
|
||||||
@ -167,7 +202,10 @@ func (i *IRC) recv() {
|
|||||||
|
|
||||||
switch msg.Command {
|
switch msg.Command {
|
||||||
case PING:
|
case PING:
|
||||||
i.Write("PONG :" + msg.Trailing)
|
i.write("PONG :" + msg.Trailing)
|
||||||
|
|
||||||
|
case RPL_WELCOME:
|
||||||
|
i.ready.Done()
|
||||||
}
|
}
|
||||||
|
|
||||||
i.Messages <- msg
|
i.Messages <- msg
|
||||||
|
@ -16,6 +16,7 @@ type WSResponse struct {
|
|||||||
|
|
||||||
type Connect struct {
|
type Connect struct {
|
||||||
Server string `json:"server"`
|
Server string `json:"server"`
|
||||||
|
TLS bool `json:"tls"`
|
||||||
Nick string `json:"nick"`
|
Nick string `json:"nick"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
}
|
}
|
||||||
@ -63,3 +64,8 @@ type MOTD struct {
|
|||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Error struct {
|
||||||
|
Server string `json:"server"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
@ -65,11 +65,18 @@ func (s *Session) sendJSON(t string, v interface{}) {
|
|||||||
s.out <- res
|
s.out <- res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Session) sendError(err error, server string) {
|
||||||
|
s.sendJSON("error", Error{
|
||||||
|
Server: server,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Session) write() {
|
func (s *Session) write() {
|
||||||
for res := range s.out {
|
for res := range s.out {
|
||||||
s.wsLock.Lock()
|
s.wsLock.Lock()
|
||||||
for _, ws := range s.ws {
|
for _, ws := range s.ws {
|
||||||
ws.In <- res
|
ws.Out <- res
|
||||||
}
|
}
|
||||||
s.wsLock.Unlock()
|
s.wsLock.Unlock()
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,18 @@ import (
|
|||||||
type WebSocket struct {
|
type WebSocket struct {
|
||||||
conn *websocket.Conn
|
conn *websocket.Conn
|
||||||
|
|
||||||
In chan []byte
|
Out chan []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWebSocket(ws *websocket.Conn) *WebSocket {
|
func NewWebSocket(ws *websocket.Conn) *WebSocket {
|
||||||
return &WebSocket{
|
return &WebSocket{
|
||||||
conn: ws,
|
conn: ws,
|
||||||
In: make(chan []byte, 32),
|
Out: make(chan []byte, 32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WebSocket) write() {
|
func (w *WebSocket) write() {
|
||||||
for data := range w.In {
|
for data := range w.Out {
|
||||||
w.conn.Write(data)
|
w.conn.Write(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,18 +74,22 @@ func handleWS(ws *websocket.Conn) {
|
|||||||
log.Println(addr, "connecting to", data.Server)
|
log.Println(addr, "connecting to", data.Server)
|
||||||
|
|
||||||
irc := NewIRC(data.Nick, data.Username)
|
irc := NewIRC(data.Nick, data.Username)
|
||||||
irc.TLS = true
|
irc.TLS = data.TLS
|
||||||
irc.Connect(data.Server)
|
|
||||||
|
|
||||||
session.setIRC(irc.Host, irc)
|
if err := irc.Connect(data.Server); err != nil {
|
||||||
|
session.sendError(err, irc.Host)
|
||||||
|
log.Println(err)
|
||||||
|
} else {
|
||||||
|
session.setIRC(irc.Host, irc)
|
||||||
|
|
||||||
go handleMessages(irc, session)
|
go handleMessages(irc, session)
|
||||||
|
|
||||||
session.user.AddServer(storage.Server{
|
session.user.AddServer(storage.Server{
|
||||||
Address: irc.Host,
|
Address: irc.Host,
|
||||||
Nick: data.Nick,
|
Nick: data.Nick,
|
||||||
Username: data.Username,
|
Username: data.Username,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Println(addr, "already connected to", data.Server)
|
log.Println(addr, "already connected to", data.Server)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user