Moving to multi-server support

This commit is contained in:
Neale Pickett 2015-02-09 16:53:43 -07:00
parent 30eaf147f1
commit c04d246d9e
23 changed files with 290 additions and 1938 deletions

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2015 Neale Pickett
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
The software is provided "as is", without warranty of any kind, express or
implied, including but not limited to the warranties of merchantability,
fitness for a particular purpose and noninfringement. In no event shall
the authors or copyright holders be liable for any claim, damages or
other liability, whether in an action of contract, tort or otherwise,
arising from, out of or in connection with the Software or the use or
other dealings in the Software.

View File

@ -1,24 +0,0 @@
ICONS += app/icon-16.png
ICONS += app/icon-32.png
ICONS += app/icon-48.png
ICONS += app/icon-128.png
ICONS += app/icon-256.png
all: icons serverside
serverside: spongy spongy.cgi
spongy: src/spongy/spongy.go
GOPATH=$(CURDIR) go build -v $@
spongy.cgi: src/spongy.cgi/spongy.cgi.go
GOPATH=$(CURDIR) go build -v $@
chmod +s $@
icons: $(ICONS)
app/icon-%.png: chat.svg
inkscape --export-png=$@ --export-width=$* $<
package: icons
cd app && zip -ru ../package.zip .

View File

@ -1,75 +0,0 @@
{
"appName": {
"description": "Application name",
"message": "Spongy Chat Client"
},
"appShortName": {
"description": "Short application name",
"message": "Spongy Chat"
},
"appDesc": {
"description": "Application description for app store listing",
"message": "Chat client for the Spongy bouncer thingamajiggy"
},
"unknownCommand": {
"description": "Text for unrecognized command",
"message": "??? $COMMAND$ $ARGS$ $TEXT$",
"placeholders": {
"fullSender": {
"content": "$1",
"example": "fritz!~bob@example.net"
},
"command": {
"content": "$2",
"example": "PRIVMSG"
},
"sender": {
"content": "$3",
"example": "fritz"
},
"forum": {
"content": "$4",
"example": "#hottub"
},
"args": {
"content": "$5",
"example": "+o,fred"
},
"text": {
"content": "$6",
"example": "Hello everybody"
}
}
},
"privmsgCommand": {
"description": "Text for a privmsg (a regular chat message)",
"message": "$6"
},
"noticeCommand": {
"description": "Text for a notice",
"message": "$6"
},
"nickCommand": {
"description": "Text for nickname change",
"message": "$1 is now known as $3"
},
"modeCommand": {
"message": "sets channel mode $5"
},
"joinCommand": {
"description": "Channel join",
"message": "joins the channel."
},
"faultCommand": {
"description": "Spongy fault",
"message": "Spongy error: $6"
}
}

View File

@ -1,10 +0,0 @@
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('wirc.html', {
state: 'normal',
width: 775,
height: 400,
minWidth: 320,
minHeight: 160,
id: 'spongy'
})
})

View File

@ -1 +0,0 @@
merf.

View File

@ -1,361 +0,0 @@
<html><head>
<meta charset="utf-8">
<!-- generic -->
<script src="util.js"></script>
<script src="keycodes.js"></script>
<script src="message_handler.js"></script>
<script src="event_emitter.js"></script>
<script src="chat/notification.js"></script>
<script src="chat/notification_group.js"></script>
<script src="net/abstract_tcp_socket.js"></script>
<script src="event.js"></script>
<script src="timer.js"></script>
<script src="context.js"></script>
<!-- net -->
<script src="net/chrome_socket.js"></script>
<script src="net/ssl_socket.js"></script>
<script src="net/remote_socket.js"></script>
<!-- irc protocol -->
<script src="irc/irc.js"></script>
<script src="irc/irc_util.js"></script>
<script src="irc/ctcp_handler.js"></script>
<script src="irc/server_response_handler.js"></script>
<!-- scripting -->
<script src="script/script.js"></script>
<script src="script/prepackaged/source_array.js"></script>
<script src="script/script_handler.js"></script>
<script src="script/script_loader.js"></script>
<!-- one identity -->
<script src="remote_connection.js"></script>
<script src="chat/remote_connection_handler.js"></script>
<script src="remote_device.js"></script>
<!-- user input -->
<script src="chat/custom_command_parser.js"></script>
<script src="input_stack.js"></script>
<script src="completion_finder.js"></script>
<script src="auto_complete.js"></script>
<script src="keyboard_shortcut_map.js"></script>
<script src="user_input_handler.js"></script>
<script src="chat/user_command_handler.js"></script>
<script src="chat/user_command.js"></script>
<script src="chat/developer_commands.js"></script>
<!-- client and UI -->
<script src="chat/chat.js"></script>
<script src="chat/walkthrough.js"></script>
<script src="chat/storage.js"></script>
<script src="chat/chat_log.js"></script>
<script src="chat/notice.js"></script>
<script src="chat/message_formatter.js"></script>
<script src="chat/html_list.js"></script>
<script src="chat/nick_mentioned_notification.js"></script>
<script src="chat/irc_message_handler.js"></script>
<script src="chat/window.js"></script>
<script src="chat/window_message_renderer.js"></script>
<script src="chat/scrollable.js"></script>
<script src="chat/help_message_renderer.js"></script>
<script src="chat/nick_list.js"></script>
<script src="chat/channel_list.js"></script>
<script src="chat/window_list.js"></script>
<!-- third party -->
<script src="third_party/jquery.js"></script>
<script src="third_party/md5.js"></script>
<!-- forge SSL library -->
<script type="text/javascript" src="third_party/forge/debug.js"></script>
<script type="text/javascript" src="third_party/forge/util.js"></script>
<script type="text/javascript" src="third_party/forge/log.js"></script>
<script type="text/javascript" src="third_party/forge/socket.js"></script>
<script type="text/javascript" src="third_party/forge/md5.js"></script>
<script type="text/javascript" src="third_party/forge/sha1.js"></script>
<script type="text/javascript" src="third_party/forge/sha256.js"></script>
<script type="text/javascript" src="third_party/forge/hmac.js"></script>
<script type="text/javascript" src="third_party/forge/aes.js"></script>
<script type="text/javascript" src="third_party/forge/pem.js"></script>
<script type="text/javascript" src="third_party/forge/asn1.js"></script>
<script type="text/javascript" src="third_party/forge/jsbn.js"></script>
<script type="text/javascript" src="third_party/forge/prng.js"></script>
<script type="text/javascript" src="third_party/forge/random.js"></script>
<script type="text/javascript" src="third_party/forge/oids.js"></script>
<script type="text/javascript" src="third_party/forge/rsa.js"></script>
<script type="text/javascript" src="third_party/forge/pbe.js"></script>
<script type="text/javascript" src="third_party/forge/x509.js"></script>
<script type="text/javascript" src="third_party/forge/pki.js"></script>
<script type="text/javascript" src="third_party/forge/tls.js"></script>
<script type="text/javascript" src="third_party/forge/aesCipherSuites.js"></script>
<script type="text/javascript" src="third_party/forge/tlssocket.js"></script>
<script type="text/javascript" src="third_party/forge/http.js"></script>
<link id="main-style" rel="stylesheet" href="style.css">
<link rel="stylesheet" href="message_style.css">
<link rel="stylesheet" href="topbar.css">
<title>CIRC undefined</title></head>
<body>
<!-- templates -->
<div id="templates">
<div class="server-channels">
<div class="server room">
<div class="content-item"></div>
<div class="remove-button">-</div>
</div>
<ul class="channels"></ul>
</div>
<li class="channel room">
<div class="content-item"></div>
<div class="remove-button">-</div>
</li>
<ul class="nicks"></ul>
<li class="nick">
<div class="content-item"></div>
</li>
<ul class="messages"></ul>
<li class="message">
<div class="timestamp"></div>
<div class="source-content-container">
<div class="source"></div>
<div class="content"></div>
</div>
</li>
</div>
<!-- main -->
<div id="main">
<div id="main-top-border"></div>
<div id="rooms-and-nicks" class="">
<div id="rooms-container">
<h1>rooms</h1>
<div class="rooms"><div class="server-channels">
<div class="server room current-server">
<div class="content-item">us.slashnet.org</div>
<div class="remove-button">-</div>
</div>
<ul class="channels"><li class="channel room selected">
<div class="content-item">#tron</div>
<div class="remove-button">-</div>
</li><li class="channel room footer">
<div class="content-item">+ add channel</div>
<div class="remove-button">-</div>
</li></ul>
</div><div class="server-channels">
<div class="server room footer">
<div class="content-item">+ add server</div>
<div class="remove-button">-</div>
</div>
<ul class="channels"></ul>
</div></div>
</div>
<div id="nicks-container">
<h1>members</h1>
<ul class="nicks"><li class="nick">
<div class="content-item">also</div>
</li><li class="nick">
<div class="content-item">atob</div>
</li><li class="nick">
<div class="content-item">Bermuda</div>
</li><li class="nick">
<div class="content-item">bit</div>
</li><li class="nick">
<div class="content-item">bk</div>
</li><li class="nick">
<div class="content-item">bz2</div>
</li><li class="nick">
<div class="content-item">caycos</div>
</li><li class="nick">
<div class="content-item">ckape</div>
</li><li class="nick">
<div class="content-item">clavicle</div>
</li><li class="nick">
<div class="content-item">CrackMonkey</div>
</li><li class="nick">
<div class="content-item">Dumont</div>
</li><li class="nick">
<div class="content-item">dzho</div>
</li><li class="nick">
<div class="content-item">emad</div>
</li><li class="nick">
<div class="content-item">eythian</div>
</li><li class="nick">
<div class="content-item">felixc</div>
</li><li class="nick">
<div class="content-item">fo0bar</div>
</li><li class="nick">
<div class="content-item">fuzzie</div>
</li><li class="nick">
<div class="content-item">GodEater</div>
</li><li class="nick">
<div class="content-item">hollow</div>
</li><li class="nick">
<div class="content-item">hoylemd</div>
</li><li class="nick">
<div class="content-item">jhewl</div>
</li><li class="nick">
<div class="content-item">jv</div>
</li><li class="nick">
<div class="content-item">kees</div>
</li><li class="nick">
<div class="content-item">khmer</div>
</li><li class="nick">
<div class="content-item">lamont</div>
</li><li class="nick">
<div class="content-item">lexicondal</div>
</li><li class="nick">
<div class="content-item">neale</div>
</li><li class="nick">
<div class="content-item">nemo</div>
</li><li class="nick">
<div class="content-item">nerdtron3000</div>
</li><li class="nick">
<div class="content-item">nornagon</div>
</li><li class="nick">
<div class="content-item">Octal</div>
</li><li class="nick">
<div class="content-item">pdx6</div>
</li><li class="nick">
<div class="content-item">pedro</div>
</li><li class="nick">
<div class="content-item">Randall</div>
</li><li class="nick">
<div class="content-item">sarah</div>
</li><li class="nick">
<div class="content-item">Sciri</div>
</li><li class="nick">
<div class="content-item">scorche</div>
</li><li class="nick">
<div class="content-item">scorche|sh</div>
</li><li class="nick">
<div class="content-item">Screwtape</div>
</li><li class="nick">
<div class="content-item">sneakums</div>
</li><li class="nick">
<div class="content-item">squinky</div>
</li><li class="nick">
<div class="content-item">stat</div>
</li><li class="nick">
<div class="content-item">teferi</div>
</li><li class="nick">
<div class="content-item">tiaz</div>
</li><li class="nick">
<div class="content-item">watson</div>
</li><li class="nick">
<div class="content-item">wcarss</div>
</li><li class="nick">
<div class="content-item">wombat</div>
</li><li class="nick">
<div class="content-item">X11R5</div>
</li><li class="nick">
<div class="content-item">Zen</div>
</li><li class="nick footer">
<div class="content-item">footer</div>
</li></ul></div>
</div>
<div class="dragbar"></div>
<div id="messages-and-input">
<div id="topic-container">
<button id="hide-channels" class="topbar-button">=</button>
<div id="status" class="content-item"><span title="Spel werkt, vertel het door! | hype mismatch error | ERG DRUK | <ginnie> everything is awesome! | Save The Date: 20150212 is Dumont's 15th Birthday | sparrows form Voltron | Deksels! | sysadmin establishment &quot;hanged&quot; by lunatic devop | Levis dehydrates unisexual cleavage. | A fanfare for locked traffic --" class="topic">Spel werkt, vertel het door! | hype mismatch error | ERG DRUK | &lt;ginnie&gt; everything is awesome! | Save The Date: 20150212 is Dumont's 15th Birthday | sparrows form Voltron | Deksels! | sysadmin establishment "hanged" by lunatic devop | Levis dehydrates unisexual cleavage. | A fanfare for locked traffic --</span></div>
</div>
<div id="notice" class="hide">
<div class="content"></div>
<button class="close">X</button>
<button class="option2 hidden"></button>
<button class="option1 hidden"></button>
</div>
<div id="messages-container"><ul class="messages"><li class="message system">
<div class="timestamp">3:46:12 PM</div>
<div class="source-content-container">
<div class="source empty"></div>
<div class="content"><span>Awesome, you've connected to #tron.</span></div>
</div>
</li><li class="message system">
<div class="timestamp">3:46:12 PM</div>
<div class="source-content-container">
<div class="source empty"></div>
<div class="content"><span>If you're ever stuck, type /help to see a list of all commands.</span></div>
</div>
</li><li class="message system">
<div class="timestamp">3:46:12 PM</div>
<div class="source-content-container">
<div class="source empty"></div>
<div class="content"><span>You can switch windows with alt+[0-9] or click in the channel list on the left.</span></div>
</div>
</li><li class="message update join self">
<div class="timestamp">3:46:12 PM</div>
<div class="source-content-container">
<div class="source empty"></div>
<div class="content"><span>(You joined the channel)</span></div>
</div>
</li><li class="message notice topic">
<div class="timestamp">3:46:12 PM</div>
<div class="source-content-container">
<div class="source empty"></div>
<div class="content"><span>The topic is: Spel werkt, vertel het door! | hype mismatch error | ERG DRUK | &lt;ginnie&gt; everything is awesome! | Save The Date: 20150212 is Dumont's 15th Birthday | sparrows form Voltron | Deksels! | sysadmin establishment "hanged" by lunatic devop | Levis dehydrates unisexual cleavage. | A fanfare for locked traffic --</span></div>
</div>
</li><li class="message notice topic_info">
<div class="timestamp">3:46:12 PM</div>
<div class="source-content-container">
<div class="source empty"></div>
<div class="content"><span>Topic set by sneakums on Fri Oct 17 2014 16:05:00 GMT-0600 (MDT).</span></div>
</div>
</li><li class="message notice-group notice">
<div class="timestamp">3:46:12 PM</div>
<div class="source-content-container">
<div class="source empty"></div>
<div class="content"><span>Received a CTCP VERSION from squinky.</span></div>
</div>
</li><li class="message update privmsg self">
<div class="timestamp">3:47:13 PM</div>
<div class="source-content-container">
<div class="source" colornumber="22"><span>nerdtron3000</span></div>
<div class="content"><span>merf</span></div>
</div>
</li><li class="message update privmsg self">
<div class="timestamp">3:47:53 PM</div>
<div class="source-content-container">
<div class="source" colornumber="22"><span>nerdtron3000</span></div>
<div class="content"><span>X11R5: say something to me</span></div>
</div>
</li><li class="message update mention privmsg">
<div class="timestamp">3:47:55 PM</div>
<div class="source-content-container">
<div class="source" colornumber="22"><span>X11R5</span></div>
<div class="content"><span>nerdtron3000: Get me some of those things where 99 times out on to something.</span></div>
</div>
</li><li class="message update privmsg self">
<div class="timestamp">3:48:01 PM</div>
<div class="source-content-container">
<div class="source" colornumber="22"><span>nerdtron3000</span></div>
<div class="content"><span>...</span></div>
</div>
</li><li class="message update privmsg">
<div class="timestamp">3:48:02 PM</div>
<div class="source-content-container">
<div class="source" colornumber="12"><span>Dumont</span></div>
<div class="content"><span>[You have a sad feeling for a moment, then it passes.]</span></div>
</div>
</li></ul></div>
<div id="nick-and-input">
<div id="nick"><span class="name">nerdtron3000</span></div><input id="input" class="" style="-webkit-transition: 300ms; transition: 300ms;">
</div>
</div>
</div>
<script src="main.js"></script>
<iframe src="script_frame.html" style="display: none;"></iframe></body></html>

View File

@ -1,27 +0,0 @@
{
"manifest_version": 2,
"version": "1.0",
"name": "__MSG_appName__",
"short_name": "__MSG_appShortName__",
"description": "__MSG_appDesc__",
"author": "Neale Pickett <neale@woozle.org>",
"icons": {
"16": "icon-16.png",
"32": "icon-32.png",
"48": "icon-48.png",
"128": "icon-128.png",
"256": "icon-256.png"
},
"app": {
"background": {
"scripts": ["background.js"]
}
},
"permissions": [
"storage",
"fileSystem",
"https://woozle.org/"
],
"default_locale": "en"
}

View File

@ -1,165 +0,0 @@
.message.mode, .message.nick, .message.join {
color: #606043;
}
.message.notice {
color: #97008B;
font-style: oblique;
}
.message.error {
color: #B33B2D;
}
.message.self {
color: #005816;
}
.message.privmsg {
color: #505053;
}
.message.privmsg.self {
color: gray;
}
.message.privmsg.mention {
color: darkred;
}
.message.privmsg.mention .source {
font-weight: bold;
}
.message.privmsg.direct .source {
color: #488AA8;
}
/* Nickname Colors. Taken from Textual */
.message.privmsg.self .source-content-container .source {
color: #ea0d68;
}
.message:not(.self) .source-content-container .source[colornumber='0'] {
color: #0080ff;
}
.message:not(.self) .source-content-container .source[colornumber='1'] {
color: #059005;
}
.message:not(.self) .source-content-container .source[colornumber='2'] {
color: #a80054;
}
.message:not(.self) .source-content-container .source[colornumber='3'] {
color: #9b0db1;
}
.message:not(.self) .source-content-container .source[colornumber='4'] {
color: #108860;
}
.message:not(.self) .source-content-container .source[colornumber='5'] {
color: #7F4FFF;
}
.message:not(.self) .source-content-container .source[colornumber='6'] {
color: #58701a;
}
.message:not(.self) .source-content-container .source[colornumber='7'] {
color: #620a8e;
}
.message:not(.self) .source-content-container .source[colornumber='8'] {
color: #BB0008;
}
.message:not(.self) .source-content-container .source[colornumber='9'] {
color: #44345f;
}
.message:not(.self) .source-content-container .source[colornumber='10'] {
color: #2f5353;
}
.message:not(.self) .source-content-container .source[colornumber='11'] {
color: #904000;
}
.message:not(.self) .source-content-container .source[colornumber='12'] {
color: #808000;
}
.message:not(.self) .source-content-container .source[colornumber='13'] {
color: #57797e;
}
.message:not(.self) .source-content-container .source[colornumber='14'] {
color: #3333dd;
}
.message:not(.self) .source-content-container .source[colornumber='15'] {
color: #5f4d22;
}
.message:not(.self) .source-content-container .source[colornumber='16'] {
color: #706616;
}
.message:not(.self) .source-content-container .source[colornumber='17'] {
color: #46799c;
}
.message:not(.self) .source-content-container .source[colornumber='18'] {
color: #80372e;
}
.message:not(.self) .source-content-container .source[colornumber='19'] {
color: #8F478E;
}
.message:not(.self) .source-content-container .source[colornumber='20'] {
color: #5b9e4c;
}
.message:not(.self) .source-content-container .source[colornumber='21'] {
color: #13826c;
}
.message:not(.self) .source-content-container .source[colornumber='22'] {
color: #b13637;
}
.message:not(.self) .source-content-container .source[colornumber='23'] {
color: #e45d59;
}
.message:not(.self) .source-content-container .source[colornumber='24'] {
color: #1b51ae;
}
.message:not(.self) .source-content-container .source[colornumber='25'] {
color: #4855ac;
}
.message:not(.self) .source-content-container .source[colornumber='26'] {
color: #7f1d86;
}
.message:not(.self) .source-content-container .source[colornumber='27'] {
color: #73643f;
}
.message:not(.self) .source-content-container .source[colornumber='28'] {
color: #0b9578;
}
.message:not(.self) .source-content-container .source[colornumber='29'] {
color: #569c96;
}
.message:not(.self) .source-content-container .source[colornumber='30'] {
color: #08465f;
}

View File

@ -1,102 +0,0 @@
// Functionality dealing with server-level things
var maxScrollback = 500;
var networks = {};
function networkConnect(network, baseURL, authtok) {
var eventSource;
var element = getTemplate("server-channels");
var channels = element.getElementsByClassName("channels")[0];
var roomElement = element.getElementsByClassName("server")[0];
var rooms = {".": roomElement};
newRoom(roomElement, element, network, maxScrollback);
function makeRoom(name) {
var rElement = getTemplate("channel");
newRoom(rElement, element, name, maxScrollback);
channels.appendChild(rElement);
rooms[name] = rElement;
return rElement;
}
function handleEventSourceLine(line) {
var lhs = line.split(" :", 1)[0];
var parts = lhs.split(' ');
var timestamp = new Date(parts[0] * 1000);
var fullSender = parts[1];
var command = parts[2].toLowerCase();
var sender = parts[3];
var forum = parts[4];
var args = parts.slice(5);
var txt = line.substr(lhs.length + 2);
switch (command) {
case "prevlog":
// Just ignore this
return;
}
var room = rooms[forum];
if (! room) {
room = makeRoom(forum);
}
// XXX: Handle differently based on command
room.addMessage(timestamp, fullSender, command, sender, args, txt);
}
function handleEventSourceMessage(oEvent) {
msgs = oEvent.data.split("\n");
var first = Math.max(0, msgs.length - maxScrollback);
for (var i = first; i < msgs.length; i += 1) {
handleEventSourceLine(msgs[i]);
}
}
function handleEventSourceError(oEvent) {
timestamp = new Date();
roomElement.addMessage(timestamp, ".", "fault", ".", [], "Unable to open events feed (permissions problem on server?)");
console.log(oEvent);
}
element.send = function(target, text) {
function handleError(oEvent) {
console.log("XXX: That didn't work out.", target, text)
}
var form = new FormData();
form.append("type", "command");
form.append("auth", authtok);
form.append("network", network);
form.append("target", target);
form.append("text", text);
var oReq = new XMLHttpRequest();
oReq.addEventListener("error", handleError);
oReq.open("POST", baseURL, true);
oReq.send(form);
}
element.close = function() {
console.log("Closing");
eventSource.close();
element.parentNode.removeChild(element);
roomElement.close();
// XXX: Close all associated rooms, too!
}
if (networks[network]) {
networks[network].close();
}
networks[network] = element;
var pullURL = baseURL + "?network=" + encodeURIComponent(network) + "&auth=" + encodeURIComponent(authtok);
eventSource = new EventSource(pullURL);
eventSource.addEventListener("message", handleEventSourceMessage);
eventSource.addEventListener("error", handleEventSourceError);
document.getElementsByClassName("rooms")[0].appendChild(element);
}

View File

@ -1,162 +0,0 @@
function djbhash(a) {
var r = 5381;
for (var i = 0; i < a.length; i += 1) {
r = (((r << 5) + r) + a.charCodeAt(i)) & 0xffff;
}
return r;
}
function purtify(text) {
// Look for a URL
var txtElement = document.createElement("span");
txtElement.className = "text";
var rhs = text;
var match;
while ((match = urlRe.exec(rhs)) != null) {
var before = rhs.substr(0, match.index);
var a = document.createElement("a");
var href = match[0];
if (href.indexOf("hxx") == 0) {
href = "htt" + href.substr(3);
}
a.href = href
a.target = "_blank";
a.appendChild(document.createTextNode(match[0]));
txtElement.appendChild(document.createTextNode(before));
txtElement.appendChild(a);
rhs = rhs.substr(match.index + match[0].length);
}
txtElement.appendChild(document.createTextNode(rhs));
return txtElement;
}
function kiboze(this_is_currently_busted) {
if ((kiboze) || (-1 != text.search(kibozeRe))) {
var k = document.getElementById("kiboze");
var p2 = p.cloneNode(true);
if (k) {
k.insertBefore(p2, k.firstChild);
p2.onclick = function() { focus(p); }
// Setting title makes the tab flash sorta
document.title = document.title;
}
}
}
var visibleRoom;
function newRoom(element, network, name, maxSize) {
var messages = getTemplate("messages");
var lastmsg;
if (! maxSize) {
maxSize = 500;
}
function purtify(fullSender, command, sender, args, txt) {
var txtElement = document.createElement("span");
var msg = chrome.i18n.getMessage(command + "Command", [fullSender, command, sender, name, args, txt]);
if (! msg) {
msg = chrome.i18n.getMessage("unknownCommand", [fullSender, command, sender, name, String(args), txt]);
}
var rhs = msg;
var match;
while ((match = urlRe.exec(rhs)) != null) {
var before = rhs.substr(0, match.index);
var a = document.createElement("a");
var href = match[0];
if (href.indexOf("hxx") == 0) {
href = "htt" + href.substr(3);
}
a.href = href
a.target = "_blank";
a.appendChild(document.createTextNode(match[0]));
txtElement.appendChild(document.createTextNode(before));
txtElement.appendChild(a);
rhs = rhs.substr(match.index + match[0].length);
}
txtElement.appendChild(document.createTextNode(rhs));
return txtElement;
}
element.addMessage = function(timestamp, fullSender, command, sender, args, txt) {
var message = getTemplate("message");
var eTimestamp = message.getElementsByClassName("timestamp")[0];
var eSource = message.getElementsByClassName("source")[0];
var eContent = message.getElementsByClassName("content")[0];
message.classList.add(command);
if (sender == ".") {
message.classList.add("self");
}
eTimestamp.textContent = timestamp.toLocaleTimeString();
eSource.textContent = sender;
eSource.setAttribute("colornumber", djbhash(sender) % 31);
eContent.appendChild(purtify(fullSender, command, sender, args, txt));
messages.appendChild(message);
while (messages.childNodes.length > maxSize) {
messages.removeChild(messages.firstChild);
}
lastmsg = message;
if (visibleRoom == element) {
lastmsg.scrollIntoView(false);
}
}
element.hide = function() {
element.classList.remove("selected");
messages.style.display = "none";
}
element.show = function() {
if (visibleRoom) {
visibleRoom.hide()
}
element.classList.add("selected");
messages.style.display = null;
lastmsg.scrollIntoView(false);
visibleRoom = element;
}
element.send = function(text) {
network.send(name, text);
}
element.close = function() {
console.log(messages);
console.log(messages.parent);
messages.parent.removeChild(messages);
element.parent.removeChild(element);
}
function clicked() {
element.show();
}
// start hidden
element.hide();
element.addEventListener("click", clicked);
element.getElementsByClassName("content-item")[0].textContent = name;
document.getElementById("messages-container").appendChild(messages);
return element;
}

View File

@ -1,484 +0,0 @@
html, body {
margin: 0;
padding: 0
}
*, *:before, *:after {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
body {
color: #505053;
/* font-family: sans-serif; */
font-size: 100%;
}
.hidden {
display: none;
}
#templates {
display: none;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
.footer {
display: none;
font-style: italic;
color: #9493A2;
}
.help-command {
display: inline-block;
width: 7em;
}
.content-item {
white-space: nowrap;
overflow-x: hidden;
text-overflow: ellipsis;
}
#main {
display: -webkit-box;
-webkit-box-orient: horizontal;
width: 100%;
height: 100%;
position: absolute;
border-top: 1px solid rgba(0,0,0,0.15);
}
#main-top-border {
position: absolute;
top: 0;
z-index: 100;
width: 100%;
height: 1px;
background-color: rgba(0,0,0,0.05);
pointer-events: none;
}
#rooms-and-nicks {
background-color: #F7F5E4;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-flex: 0;
width: 150px;
height: inherit;
padding: 4px 0px;
border-right: 1px solid rgba(0, 0, 0, .15);
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
#rooms-and-nicks h1 {
color: #406698;
text-transform: uppercase;
font-size: smaller;
padding: 8px 0px 2px 8px;
margin: 0px;
}
#rooms-and-nicks .nick,
#rooms-and-nicks .room {
padding: 0 8px;
line-height: 26px;
}
#rooms-and-nicks .room {
cursor: pointer;
}
#rooms-and-nicks .server.footer {
display: block;
}
.dragbar:hover {
width: 6px;
transition: width .1s;
}
.dragbar {
height: 100%;
width: 1px;
cursor: col-resize;
}
#ghostbar{
width:3px;
background-color:#000;
opacity:0.8;
position:absolute;
cursor: col-resize;
z-index:999
}
#rooms-container {
-webkit-box-flex: 0;
border-bottom: 1px solid #CCC;
padding-bottom: 10px;
}
.no-nicks #rooms-container {
border-bottom-style: none;
padding-bottom: 0px;
}
#rooms-and-nicks.hidden {
display: none;
}
#rooms-and-nicks .room.server {
background-color: #F2EFD3;
position: relative;
}
#rooms-and-nicks:hover .room.server .content-item {
width: 130px;
}
#rooms-and-nicks .add-channel {
position: absolute;
right: 2px;
top: -1px;
cursor: pointer;
font-size: larger;
display: none;
}
#rooms-and-nicks:hover .add-channel {
display: block;
}
#rooms-and-nicks .add-channel:hover {
color: #888;
}
#rooms-and-nicks .room.channel:first-child {
padding-top: 3px;
}
#rooms-and-nicks .room.channel:first-child .content-item {
padding-bottom: 3px;
line-height: 20px;
}
#rooms-and-nicks not(.current-server) + .channels .room.channel:nth-last-child(2) {
padding-bottom: 3px;
}
#rooms-and-nicks .room.channel:last-child {
padding-bottom: 3px;
}
#rooms-and-nicks not(.current-server) + .channels .room.channel:nth-last-child(2) .content-item {
padding-top: 3px;
line-height: 20px;
}
#rooms-and-nicks .room.channel:last-child .content-item {
padding-top: 3px;
line-height: 20px;
}
#rooms-and-nicks not(.current-server) + .channels .room.channel:nth-last-child(2):first-child .content-item {
line-height: 14px;
}
#rooms-and-nicks .room.channel:last-child:first-child .content-item {
line-height: 14px;
}
#rooms-and-nicks .current-server:not(.always-empty) + .channels .footer {
display: block;
}
#rooms-and-nicks .room.channel .content-item {
border-left: 1px solid rgba(0, 0, 0, .5);
padding-left: 7px;
}
#rooms-and-nicks .room.activity {
font-weight: bold;
}
#rooms-and-nicks .room.mention {
color: darkred;
}
#rooms-and-nicks .room.disconnected .content-item {
color: #9493A2;
font-style: italic;
}
#rooms-and-nicks .room.channel.disconnected .content-item {
border-left: 1px solid #9493A2;
}
#rooms-and-nicks .room.selected {
background-color: #F0E798;
}
#rooms-and-nicks .room {
position: relative;
}
#rooms-and-nicks .room .remove-button {
position: absolute;
right: 6px;
top: -1px;
cursor: pointer;
font-size: x-large;
color: rgba(0, 0, 0, .25);
display: none;
}
#rooms-and-nicks .room .remove-button:hover {
color: rgba(0, 0, 0, .5);
}
#rooms-and-nicks .room.selected:not(.footer):not(.always-empty) .content-item {
width: 123px;
}
#rooms-and-nicks .room:hover:not(.footer):not(.always-empty) .content-item {
width: 123px;
}
#rooms-and-nicks .room.selected:not(.footer):not(.always-empty) .remove-button {
display: inline-block;
}
#rooms-and-nicks .room:hover:not(.footer):not(.always-empty) .remove-button {
display: inline-block;
}
#nicks-container {
border-top: 1px solid #FFF;
-webkit-box-flex: 1;
}
.no-nicks #nicks-container {
display: none;
}
.rooms,
.nicks {
padding-top: 5px;
}
.nicks li:nth-child(odd) {
background-color: #F2EFD3;
}
#notice {
background-color: #406698;
box-shadow: 0px 1px 4px #888;
color: #FFF;
position: absolute;
width: 100%;
-webkit-transition: 150ms;
top: 0;
padding: 2px 0;
}
#notice.hide {
top: -38px;
}
#notice .content {
display: inline;
padding-left: 14px;
text-overflow: ellipsis;
white-space: nowrap;
overflow-x: hidden;
position: absolute;
top: 50%;
margin-top: -.5em;
}
#notice button {
float: right;
height: 22px;
font-size: 14px;
padding: 0px 4px;
margin: 4px;
border: none;
background-color: #ECECEC;
color: #505053;
cursor: pointer;
outline: none;
}
#notice button.close {
margin-right: 8px;
font-size: 12px;
border-radius: 42px;
width: 21px;
height: 21px;
}
#messages-and-input {
-webkit-box-flex: 1;
display: -webkit-box;
-webkit-box-orient: vertical;
box-shadow: 0px 0px 8px #CCC;
position: relative;
}
#messages-container {
overflow-y: auto;
-webkit-overflow-scrolling: touch;
-webkit-box-flex: 1;
border-bottom: 1px solid #CCC;
}
.messages {
display: table;
}
.message {
display: table-row;
-webkit-user-select: initial;
}
.message.activity-marker .source-content-container {
border-top: 1px solid rgb(224, 179, 179);
}
.message .timestamp {
color: #6060C0;
font-style: italic;
/* font-size: smaller; */
white-space: nowrap;
display: table-cell;
text-align: right;
padding: 0px 10px 0px 10px;
border-right: 1px solid rgba(0,0,0,0.15);
cursor: text;
}
.source-content-container {
display: table-cell;
padding: 1px 10px 1px 15px;
width: 100%;
}
.message .source {
font-weight: bold;
padding-right: 5px;
margin-left: -5px;
white-space: nowrap;
display: inline;
text-align: right;
cursor: text;
}
.message .source.empty {
padding-right: 0;
}
.message .content {
display: inline;
white-space: pre-wrap;
cursor: text;
word-break: break-word;
}
#messages-container .messages .message:first-child .source-content-container {
padding-top: 10px;
}
#messages-container .messages .message:last-child .source-content-container {
padding-bottom: 8px;
}
.message.list {
/* empty style for now */
}
.longword { word-break: break-all; }
#nick-and-input {
border-top: 1px solid #F9F9F9;
-webkit-box-flex: 0;
display: -webkit-box;
background-color: #ECECEC;
padding: 5px 10px 5px 10px;
}
#nick {
padding-top: 5px;
}
#nick > span {
padding-right: 10px;
}
#nick > .name:before {
font-weight: bold;
content: "[ "
}
#nick > .name:after {
font-weight: bold;
content: " ]"
}
#nick .away:before {
content: "("
}
#nick .away:after {
content: ")"
}
#input-bar {
-webkit-box-flex: 1;
display: -webkit-box;
}
#input {
background-color: #F9F9F9;
display: block;
-webkit-box-flex: 1;
width: 100%;
height: 30px;
border: 1px;
border-radius: 5px;
-webkit-box-shadow: 0px 0px 3px #888;
color: #505053;
font-size: 100%;
padding: 0px 8px;
outline: 0;
}
#input.blink {
-webkit-box-shadow: 0px 0px 6px #406698;
}
::-webkit-scrollbar {
width: 9px;
height: 9px;
}
::-webkit-scrollbar-button:start:decrement,
::-webkit-scrollbar-button:end:increment {
display: block;
height: 0;
}
::-webkit-scrollbar-track-piece {
background-color: rgba(0,0,0,0.1);
}
::-webkit-scrollbar-thumb:vertical {
height: 50px;
background-color: #999;
border-radius: 8px;
}
::-webkit-scrollbar-thumb:vertical:hover {
background-color: #888;
}
::-webkit-scrollbar-thumb:horizontal {
width: 50px;
background-color: #999;
border-radius: 8px;
}

View File

@ -1,43 +0,0 @@
#hide-channels {
display: inline-block;
font-size: 12px;
}
#topic-container {
border-bottom: 1px solid #CCC;
-webkit-box-flex: 0;
display: -webkit-box;
background-color: #ECECEC;
padding: 4px 6px;
}
#status {
-webkit-box-flex: 1;
padding: 4px 5px;
}
#status .topic {
font-style: italic;
}
.topbar-button {
background: rgba(0, 0, 0, .08);
border: 0 transparent;
text-decoration: none;
cursor: pointer;
border-radius: 2px;
-webkit-transition: .1s linear -webkit-box-shadow;
margin-right: 4px;
}
.topbar-button:active {
box-shadow: 0 0 0 1px rgba(0 ,0 ,0 ,.15) inset, 0 0 6px rgba(0, 0, 0, .2) inset;
}
.topbar-button:hover {
background: rgba(0, 0, 0, .13);
}
.topbar-button:focus {
outline: none;
}

View File

@ -1,82 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="message_style.css">
<link rel="stylesheet" href="topbar.css">
<script src="network.js"></script>
<script src="room.js"></script>
<script src="wirc.js"></script>
</head>
<body>
<!-- templates -->
<div id="templates">
<div class="server-channels">
<div class="server room">
<div class="content-item"></div>
<div class="remove-button">-</div>
</div>
<ul class="channels"></ul>
</div>
<li class="channel room">
<div class="content-item"></div>
<div class="remove-button">-</div>
</li>
<ul class="nicks"></ul>
<li class="nick">
<div class="content-item"></div>
</li>
<ul class="messages"></ul>
<li class="message">
<div class="timestamp"></div>
<div class="source-content-container">
<div class="source"></div>
<div class="content"></div>
</div>
</li>
</div>
<!-- main -->
<div id="main">
<div id="main-top-border"></div>
<div id="rooms-and-nicks" class="no-nicks">
<div id="rooms-container">
<h1>rooms</h1>
<div class="rooms"></div>
</div>
<div id="nicks-container">
<h1>members</h1>
</div>
</div>
<div class="dragbar"></div>
<div id="messages-and-input">
<div id="topic-container">
<button id="hide-channels" class="topbar-button">=</button>
<div id="status" class="content-item"></div>
</div>
<div id="notice" class="hide">
<div class="content"></div>
<button class="close">X</button>
<button class="option2 hidden"></button>
<button class="option1 hidden"></button>
</div>
<div id="messages-container"></div>
<div id="nick-and-input">
<div id="nick"></div><input id="input">
</div>
</div>
</div>
</body>
</html>

View File

@ -1,106 +0,0 @@
var msgRe = /([^ ]+) (<[^>]+>) (.*)/;
var kibozeRe = /[Nn]eal/;
var urlRe = /[a-z]+:\/\/[^ ]*/;
var nick = "Mme. M";
if (String.prototype.startsWith == null) {
String.prototype.startsWith = function(needle) {
return this.lastIndexOf(needle, 0) == 0;
}
}
function getTemplate(className) {
return templates.getElementsByClassName(className)[0].cloneNode(true);
}
function isinView(oObject) {
return (oObject.offsetParent.clientHeight <= oObject.offsetTop);
}
function addMessagePart(p, className, text) {
var e = document.createElement("span");
e.className = className;
e.appendChild(document.createTextNode(text));
p.appendChild(e);
p.appendChild(document.createTextNode(" "));
}
function focus(e) {
var pct = 1;
var timeout;
selectForum(e.parentNode);
e.scrollIntoView(false);
e.style.backgroundColor = "yellow";
timeout = setInterval(function() {
pct = pct - 0.1;
e.style.backgroundColor = "rgba(255, 255, 0, " + pct + ")";
if (pct <= 0) {
e.style.backgroundColor = "inherit";
clearInterval(timeout);
}
}, 50)
}
function handleInput(oEvent) {
console.log(oEvent);
var txt = oEvent.target.value;
if (txt.startsWith("/connect ")) {
// XXX: should allow tokens with spaces
var parts = txt.split(" ");
var network = parts[1];
var url = parts[2];
var authtok = parts[3];
networkConnect(network, url, authtok);
storedConnections[network] = [url, authtok];
chrome.storage.sync.set({"connections": storedConnections});
} else {
visibleRoom.send(txt);
}
oEvent.target.value = "";
return false;
}
function hideChannels(oEvent) {
var lhs = document.getElementById("rooms-and-nicks");
if (lhs.classList.contains("hidden")) {
lhs.classList.remove("hidden");
} else {
lhs.classList.add("hidden");
}
}
function keyPress(oEvent) {
document.getElementById("input").focus();
if (oEvent.keyIdentifier == "Enter") {
handleInput(oEvent);
}
}
function restore(items) {
storedConnections = items["connections"];
for (var network in storedConnections) {
var conn = storedConnections[network];
networkConnect(network, conn[0], conn[1]);
}
}
function init() {
chrome.storage.sync.get(["connections"], restore);
//document.getElementById("input").addEventListener("change", handleInput);
document.getElementById("hide-channels").addEventListener("click", hideChannels);
window.addEventListener("keypress", keyPress);
templates = document.getElementById("templates");
rooms = document.getElementById("rooms-container").getElementsByClassName("rooms")[0];
}
window.addEventListener("load", init);

View File

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Neale Pickett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,151 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg548"
inkscape:version="0.48.5 r10040"
inkscape:export-filename="/home/neale/tmp/chat.png"
viewBox="0 0 500 500"
sodipodi:version="0.32"
inkscape:export-xdpi="11.52"
version="1.0"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
inkscape:export-ydpi="11.52"
sodipodi:docname="chat.svg"
width="100%"
height="100%">
<defs
id="defs550" />
<sodipodi:namedview
id="base"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:zoom="0.89095454"
inkscape:current-layer="g3182"
inkscape:cx="-30.955534"
inkscape:cy="263.18068"
inkscape:window-width="1280"
showgrid="false"
inkscape:window-height="751"
inkscape:window-maximized="1" />
<g
id="g3182"
transform="translate(14.44,59.9282)">
<g
id="g2992"
transform="translate(21.564741,35.474988)">
<path
transform="matrix(1,0,0,1.1018519,-7.7056497,-111.52964)"
d="m 448.9567,198.07663 c 0,66.94702 -88.69335,121.21831 -198.10215,121.21831 -109.40879,0 -198.102139,-54.27129 -198.102139,-121.21831 0,-66.94702 88.693349,-121.218308 198.102139,-121.218308 109.4088,0 198.10215,54.271288 198.10215,121.218308 z"
sodipodi:ry="121.21831"
sodipodi:rx="198.10214"
sodipodi:cy="198.07663"
sodipodi:cx="250.85455"
id="path2984"
style="fill:#5f5fd3;fill-opacity:1;fill-rule:nonzero"
sodipodi:type="arc" />
<path
inkscape:transform-center-y="-12.189045"
inkscape:transform-center-x="7.8620881"
transform="matrix(0.9919143,-0.12690949,0.12690949,0.9919143,457.74643,65.434728)"
d="M -88.668932,214.91251 -260.45097,138.9978 -108.81589,28.187543 z"
inkscape:randomized="0"
inkscape:rounded="0"
inkscape:flatsided="true"
sodipodi:arg2="1.9869145"
sodipodi:arg1="0.93971694"
sodipodi:r2="54.215702"
sodipodi:r1="108.43141"
sodipodi:cy="127.36595"
sodipodi:cx="-152.64526"
sodipodi:sides="3"
id="path2986"
style="fill:#5f5fd3;fill-opacity:1;fill-rule:nonzero"
sodipodi:type="star" />
</g>
<g
transform="matrix(-0.74359868,0,0,0.69642414,335.20139,175.05754)"
id="g3000"
style="fill:#ffcc00">
<path
sodipodi:type="arc"
style="fill:#ffcc00;fill-opacity:1;fill-rule:nonzero"
id="path3002"
sodipodi:cx="250.85455"
sodipodi:cy="198.07663"
sodipodi:rx="198.10214"
sodipodi:ry="121.21831"
d="m 448.9567,198.07663 c 0,66.94702 -88.69335,121.21831 -198.10215,121.21831 -109.40879,0 -198.102139,-54.27129 -198.102139,-121.21831 0,-66.94702 88.693349,-121.218308 198.102139,-121.218308 109.4088,0 198.10215,54.271288 198.10215,121.218308 z"
transform="matrix(1,0,0,1.1018519,-7.7056497,-111.52964)" />
<path
sodipodi:type="star"
style="fill:#ffcc00;fill-opacity:1;fill-rule:nonzero"
id="path3004"
sodipodi:sides="3"
sodipodi:cx="-152.64526"
sodipodi:cy="127.36595"
sodipodi:r1="108.43141"
sodipodi:r2="54.215702"
sodipodi:arg1="0.93971694"
sodipodi:arg2="1.9869145"
inkscape:flatsided="true"
inkscape:rounded="0"
inkscape:randomized="0"
d="M -88.668932,214.91251 -260.45097,138.9978 -108.81589,28.187543 z"
transform="matrix(0.9919143,-0.12690949,0.12690949,0.9919143,457.74643,65.434728)"
inkscape:transform-center-x="7.8620881"
inkscape:transform-center-y="-12.189045" />
</g>
</g>
<metadata
id="metadata3045">
<rdf:RDF>
<cc:Work>
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
<dc:publisher>
<cc:Agent
rdf:about="http://openclipart.org/">
<dc:title>Openclipart</dc:title>
</cc:Agent>
</dc:publisher>
<dc:title />
<dc:date>2008-02-19T10:10:56</dc:date>
<dc:description />
<dc:source>https://openclipart.org/detail/14475/callout-chat-by-ericlemerdy</dc:source>
<dc:creator>
<cc:Agent>
<dc:title>ericlemerdy</dc:title>
</cc:Agent>
</dc:creator>
<dc:subject>
<rdf:Bag>
<rdf:li>balloon</rdf:li>
<rdf:li>bubble</rdf:li>
<rdf:li>callout</rdf:li>
<rdf:li>speech</rdf:li>
</rdf:Bag>
</dc:subject>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
</svg>

Before

Width:  |  Height:  |  Size: 5.5 KiB

BIN
spongy.cgi/spongy.cgi Executable file

Binary file not shown.

98
spongy/irc.go Normal file
View File

@ -0,0 +1,98 @@
package main
import (
"strconv"
"strings"
)
type Message struct {
Command string
FullSender string
Sender string
Forum string
Args []string
Text string
}
func Parse(v string) (Message, error) {
var m Message
var parts []string
var lhs string
parts = strings.SplitN(v, " :", 2)
if len(parts) == 2 {
lhs = parts[0]
m.Text = parts[1]
} else {
lhs = v
m.Text = ""
}
m.FullSender = "."
m.Forum = "."
m.Sender = "."
parts = strings.Split(lhs, " ")
if parts[0][0] == ':' {
m.FullSender = parts[0][1:]
parts = parts[1:]
n, u, _ := nuhost(m.FullSender)
if u != "" {
m.Sender = n
}
}
m.Command = strings.ToUpper(parts[0])
switch m.Command {
case "PRIVMSG", "NOTICE":
switch {
case isChannel(parts[1]):
m.Forum = parts[1]
case m.FullSender == ".":
m.Forum = parts[1]
default:
m.Forum = m.Sender
}
case "PART", "MODE", "TOPIC", "KICK":
m.Forum = parts[1]
m.Args = parts[2:]
case "JOIN":
if len(parts) == 1 {
m.Forum = m.Text
m.Text = ""
} else {
m.Forum = parts[1]
}
case "INVITE":
if m.Text != "" {
m.Forum = m.Text
m.Text = ""
} else {
m.Forum = parts[2]
}
case "NICK":
if len(parts) > 1 {
m.Sender = parts[1]
m.Args = parts[2:]
} else {
m.Sender = m.Text
m.Text = ""
m.Args = parts[1:]
}
m.Forum = m.Sender
case "353":
m.Forum = parts[3]
default:
numeric, _ := strconv.Atoi(m.Command)
if numeric >= 300 {
if len(parts) > 2 {
m.Forum = parts[2]
}
}
m.Args = parts[1:]
}
return m, nil
}

167
spongy/network.go Normal file
View File

@ -0,0 +1,167 @@
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"os"
"path"
"strings"
"time"
)
// This gets called a lot.
// So it's easy to fix stuff while running.
func ReadLines(fn string) ([]string, error) {
lines := make([]string, 0)
f, err := os.Open(fn)
if err != nil {
return lines, err
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
switch {
case line == "":
case line[0] == '#':
default:
lines = append(lines, line)
}
}
return lines, nil
}
type Network struct {
basePath string
conn io.ReadWriteCloser
logq chan Message
inq chan string
outq chan string
}
func NewNetwork(basePath string) (*Network, error) {
nicks, err := ReadLines(path.Join(basePath, "nicks"))
if err != nil {
return nil, err
}
gecoses, err := ReadLines(path.Join(basePath, "gecos"))
if err != nil {
return nil, err
}
return &Network{
basePath: basePath,
servers: servers,
nicks: nicks,
gecos: gecoses[0],
logq: make(chan Message),
inq: make(chan string),
outq: make(chan string),
}, err
}
func (n *Network) Close() {
close(n.logq)
close(n.inq)
close(n.outq)
}
func (n *Network) WatchOutqDirectory() {
outqDirname := path.Join(n.basePath, "outq")
dir, err := os.Open(outqDirname)
if err != nil {
log.Fatal(err)
}
defer dir.Close()
// XXX: Do this with fsnotify
for running {
entities, _ := dir.Readdirnames(0)
for _, fn := range entities {
pathname := path.Join(outqDirname, fn)
n.HandleInfile(pathname)
}
_, _ = dir.Seek(0, 0)
time.Sleep(500 * time.Millisecond)
}
}
func (n *Network) HandleInfile(fn string) {
f, err := os.Open(fn)
if err != nil {
return
}
defer f.Close()
// Do this after Open attempt.
// If Open fails, the file will stick around.
// Hopefully this is helpful for debugging.
os.Remove(fn)
inf := bufio.NewScanner(f)
for inf.Scan() {
txt := inf.Text()
n.outq <- txt
}
}
func (n *Network) WriteLoop() {
for v := range n.outq {
m, _ := Parse(v)
n.logq <- m
fmt.Fprintln(n.conn, v)
}
}
func (n *Network) ReadLoop() {
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
n.inq <- scanner.Text()
}
close(n.inq)
}
func
func (n *Network) Connect(){
serverIndex := 0
for running {
servers, err := ReadLines(path.Join(basePath, "servers"))
if err != nil {
log.Print(err)
serverIndex := 0
time.sleep(2 * time.Second)
continue
}
if serverIndex > len(servers) {
serverIndex = 0
}
server := servers[serverIndex]
switch (server[0]) {
case '|':
if dotls {
config := &tls.Config{
InsecureSkipVerify: true,
}
return tls.Dial("tcp", host, config)
} else {
return net.Dial("tcp", host)
}
}

View File

@ -5,24 +5,14 @@ import (
"crypto/tls" "crypto/tls"
"flag" "flag"
"fmt" "fmt"
"github.com/nealey/spongy/logfile"
"log" "log"
"logfile"
"net" "net"
"os" "os"
"strconv"
"strings" "strings"
"time" "time"
) )
type Message struct {
Command string
FullSender string
Sender string
Forum string
Args []string
Text string
}
var running bool = true var running bool = true
var nick string var nick string
var gecos string var gecos string
@ -30,7 +20,7 @@ var maxlogsize uint
var logq chan Message var logq chan Message
func isChannel(s string) bool { func isChannel(s string) bool {
if (s == "") { if s == "" {
return false return false
} }
@ -71,114 +61,6 @@ func nuhost(s string) (string, string, string) {
return n, parts[0], parts[1] return n, parts[0], parts[1]
} }
func connect(host string, dotls bool) (net.Conn, error) {
if dotls {
config := &tls.Config{
InsecureSkipVerify: true,
}
return tls.Dial("tcp", host, config)
} else {
return net.Dial("tcp", host)
}
}
func readLoop(conn net.Conn, inq chan<- string) {
scanner := bufio.NewScanner(conn)
for scanner.Scan() {
inq <- scanner.Text()
}
close(inq)
}
func writeLoop(conn net.Conn, outq <-chan string) {
for v := range outq {
m, _ := parse(v)
logq <- m
fmt.Fprintln(conn, v)
}
}
func parse(v string) (Message, error) {
var m Message
var parts []string
var lhs string
parts = strings.SplitN(v, " :", 2)
if len(parts) == 2 {
lhs = parts[0]
m.Text = parts[1]
} else {
lhs = v
m.Text = ""
}
m.FullSender = "."
m.Forum = "."
m.Sender = "."
parts = strings.Split(lhs, " ")
if parts[0][0] == ':' {
m.FullSender = parts[0][1:]
parts = parts[1:]
n, u, _ := nuhost(m.FullSender)
if u != "" {
m.Sender = n
}
}
m.Command = strings.ToUpper(parts[0])
switch m.Command {
case "PRIVMSG", "NOTICE":
switch {
case isChannel(parts[1]):
m.Forum = parts[1]
case m.FullSender == ".":
m.Forum = parts[1]
default:
m.Forum = m.Sender
}
case "PART", "MODE", "TOPIC", "KICK":
m.Forum = parts[1]
m.Args = parts[2:]
case "JOIN":
if len(parts) == 1 {
m.Forum = m.Text
m.Text = ""
} else {
m.Forum = parts[1]
}
case "INVITE":
if m.Text != "" {
m.Forum = m.Text
m.Text = ""
} else {
m.Forum = parts[2]
}
case "NICK":
if len(parts) > 1 {
m.Sender = parts[1]
m.Args = parts[2:]
} else {
m.Sender = m.Text
m.Text = ""
m.Args = parts[1:]
}
m.Forum = m.Sender
case "353":
m.Forum = parts[3]
default:
numeric, _ := strconv.Atoi(m.Command)
if numeric >= 300 {
if len(parts) > 2 {
m.Forum = parts[2]
}
}
m.Args = parts[1:]
}
return m, nil
}
func dispatch(outq chan<- string, m Message) { func dispatch(outq chan<- string, m Message) {
logq <- m logq <- m
@ -268,7 +150,7 @@ func main() {
outq <- fmt.Sprintf("NICK %s", nick) outq <- fmt.Sprintf("NICK %s", nick)
outq <- fmt.Sprintf("USER %s %s %s: %s", nick, nick, nick, gecos) outq <- fmt.Sprintf("USER %s %s %s: %s", nick, nick, nick, gecos)
for v := range inq { for v := range inq {
p, err := parse(v) p, err := Parse(v)
if err != nil { if err != nil {
continue continue
} }