ec2-3-230-143-213.compute-1.amazonaws.com | ToothyWiki | ToothyWikiInternals | RecentChanges | Login | Webcomic


There are several channels in active use, which can be roughly categorised as follows:

Current clients:

Updated 2006/12/14 - Added the ability to block users. Added a URL status bar thingy when you hover the mouse over a link. Fixed a couple of small bugs that have been mildly irritating me for a while. You can now copy from the chat. Also, it no longer randomly brings up the "File not found - blah" error when clicking URLs. Added the ability to load the log files into the client.

In addition to the above, there is a Toothycat discord:
though at the time of writing there's very little going on in there

There is now online chat available. The intention is that it will get tweaked as MoonShadow gets an idea of the sort of server load it causes, and will then be used as a backbone for other AJAX services - in particular, ToothyGDL.

Your nickname is generated the same way as for the wiki, so log in if you want a nice one! The server supports multiple channels, but MoonShadow has to create them - users cannot. In practice, MoonShadow intends there to only be one channel for general chat, any others being used for game data. You can use /me for emotes. That's pretty much it, really. Oh, the server source is [here].

TODO (Kazu-client):

MuHaHaHa!  (although it only does the basic pattern, no subpages or []) (but it does do "" dividers and colour matching) --K
Colour picker now skips the 30 degrees covering the orange-yellows in the HSV range, which should hopefully fix this.  It's still possible to get two random colours very close together though. --K


(what there is of one)

All messages from server to client are prefaced with an XML header and contained in a <chat> tag. The body of this tag consists of one or more of the following, one per line:
Declare the presence of a channel with the name given on the server.

say(integer, "text-string");
Echo a line of chat to the client. The integer increases with every new line of chat, so the client SHOULD ignore lines with integer less than or equal to the highest that has been seen. The count starts at -1.

setupdates("url", timeout);
Declare a url that the client MUST thereafter use when checking the current channel for new content. The client MUST NOT issue requests to the server more often than timeout milliseconds, unless submitting new content to the channel. HEAD requests to this URL will return only HTTP headers, not a body; the headers will contain the date/time of the last submission to the channel.

The server is located at https://www.toothycat.net/wiki/chat.pl. It will always give a full response body, both for GET and HEAD requests.
An HTTP request to the server URL with no other parameters will produce a list of channels.
An HTTP request to the server URL with the parameter channel=channelname will return the current content of channelname and also a setupdates() declaration for future polls.
An HTTP request to the server URL with the parameters channel=channelname and say=text will submit the supplied URI-escaped text to the channel channelname, and return the updated content of channelname and also a setupdates() declaration for future polls. The server will check for the presence of a ToothyWiki cookie in the request headers, and if one is supplied (this happens automatically if using XMLHttpRequest?) will look up the username and prepend it to the text; otherwise, it will prepend the hostname of the submitting machine.  If the submitted text starts with "/me ", those characters will be removed; otherwise, the sequence ": " will be inserted after the username or hostname. The server MAY choose to escape any or all symbols as equivalent HTML entities and will always escape at least ", < and &.

Plugin documentation

Client API requirements

The client provides an object called "client", with the following functionality:


this is a string containing the URL of the current server.

this is a string containing the URL to use when polling the server for updates without submitting new data.

this is the name of the current channel

this is the current rate at which the client polls the server for updates

this is the id of the highest message received from the channel

this is an array with one entry per buffered message


client.SetChannel? = function( _name )
this is called to set the current channel

client.GetChatHTML? = function()
this is called when the client wants to convert the scroll buffer to HTML.

client.GotChannel? = function(_name)
this is called when the client receives the name of an available channel from the server.

client.GotServerText? = function(_text)
this is called when the client receives text from the server.

client.SendFilter? = function(_text)
this is called when the client is about to send _text to the server. It must return the string to send.

client.getUpdateParam? = function( )
the string this returns must be appended to the URL when requesting updates without submitting data.

In addition, the client's active area should be wrapped in an HTML element with an id of MAIN, so that plugins can easily attach new HTML.

Also, CSS such as [menubar.css] or similar may be included somewhere in the document if support for pop-up menus is desired.

Plugins are XML files containing the single tag 'chat'. The body of this tag is evaluated. The first line of the body must be the text /*plugin*/. The total size of the file may not exceed 4kb. There is a template [here], which is probably overcomplicated for a lot of purposes but allows plugins to depend on other plugins and automatically pull them in before installing themselves.

[Sample plugin to reverse the order of text] - [see it in action].
[Channel selector] - [actually, seems to work in MSIE too, now].
[Coloured text] - [see it in action]

Also in ~sham/chat_plugins:

In /~rachael:
In /~hologram:
In /~qqzm:

I have an idea for an improved version of my fixedcolours plugin, but I wanted to run it past MoonShadow before I implement it, to see what he thinks, especially from the POVs of security and server load. My idea is this: we create a ToothyWiki page consisting of a list of usernames and colours. People edit this page to indicate what colour they want their chat posts to appear in. When my plugin installs itself (rather than on GotServerText?, for load-minimising reasons) it reads and parses that wiki page to generate its colour array. Anyone who hasn't defined themselves a colour can be assigned one from a fixed list hardcoded in the plugin, as at present. The colour strings on the wiki page will be compared against "#XXXXXX" (where the Xs are hex digits) to make sure they don't contain EvilScript; I could also check the usernames only contain alphanumeric and underscores. --Rachael
Don't think I'd use that, personally: I might want to control how things look in my chat window, but I certainly don't want to give other people control. Why, they don't even know what colour background I have! If you want to write it, though, feel free - so long as the wiki page isn't fetched more often than once per chat session, I don't really mind. --MoonShadow
Good point. OK, I suppose the wiki page could have 2 levels of structure, with a users->colours mapping per user. Or the plugin could accept a parameter giving the location of an individual wiki page where that user keeps their users->colours mapping. (The original suggestion was inspired by things like MSN Messenger, in which each user sets their own colour and font, which all other users see.) --Rachael

Plugins may be combined. For example, ChrisHowlett, M-A and AlexChurchill use both colour and reverse, [like so]. Note that if using these two in combination, make reverse.xml load first by giving it the lower number. MoonShadow will rewrite reverse.xml so it doesn't conflict when he gets a RoundTuit.

You may also load plugins with the /plugin command, (eg. /plugin chat_plugins/rot13.xml to load the rot13 plugin.)
You may also load plugins using /js XMLRequest("plugin-url") (eg. /js XMLRequest("chat_plugins/rot13.xml") to load the rot13 plugin.) When loaded in this way, the plugin will only persist until you reload the page, which lets you try it out without committing to it.

Not a plugin (yet), but to widen the text box, you can paste this into the address bar:
Note: This is VERY much a FireFox only hack.  --Vitenka
Err... it works in IE for me. I wouldn't have expected it to work in FireFox at all - I thought it was just IE that interpreted javascript: entered into the address bar. --AC

Alternatively, in the text entry box type
  /store-js document.getElementById("intext").style.width="95%" 
..to append that to a cookie so it gets run every time the client restarts as well. /js will execute Javascript without storing it, /clear-js will clear the javascript cookie.

MS - is there any chance you could just restyle the entry box to be very long? I can't imagine there are users would would object to it being 95% wide, but every time I access chat from a new device I have to remember to do this store-js. --CH
So done --MS?

To display the channel name in the title type:
  /store-js function gup(name){name=name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); var regexS="[\\?&]"+name+"=([^&#]*)";var regex=new RegExp(regexS); var results=regex.exec(window.location.href); if(results==null) return ""; else return results[1];}document.title=gup('channel');

I am somewhat confused by the fact that my chat app still works.  Is the above a proposed, coming soon, thing? --K
I've retained all the things the server currently calls for now - they simply call client.whatever(). This means I didn't change the server at all and your app still works as is. - MoonShadow

Some thoughts/suggestions:

Could the number being passed as top be based on time() rather than just an incremental number?  This shouldn't break anything and would provide an in-built timestamp for text.
Not entirely. Guaranteeing uniqueness is more important than accurate timestamps. I could do something like increment it by one if it's less than or equal to that for the last one sent, but TBH I may as well just pass the timestamp along as a separate argument - and the username too, while I'm at it. - MoonShadow

Currently (from what I can tell) we are only using xml as a single root element.  Would it make sense to start passing chat as xml elements rather than javascript that must be eval'ed, or does the eval work faster?
The issue with this is my question on XML that I still amn't certain I know the answer to. I am currently returning and working with a copy of the string value of the node content, and working with that, which I know is safe. Supposing I were to return a reference to the xml fetcher's document's first child instead, and don't get around to walking over it until after the xml fetcher has started parsing the next request - what would I be walking over? Also, the code to walk the XML tree is slightly different in MSIE and FF, but that's just a very minor niggle. If I get time to confirm that something sane really does happen on all browsers, I may go this way. We could retain plugins by having an optional "eval the contents of this node" node or something; I also want to experiment with dynamically loading large lumps of javascript by appending SCRIPT SRC=s to the DOM - no idea if anything sane will happen, but you never know.. - MoonShadow
I was just thinking this would remove (or, at least, significantly lessen) the 4kb problem and would help with providing functionality in some clients rather than others.  OTOH, this would probably make the plug-ins idea harder.  Not sure --K
Currently, we seem to be mixing xml and javascript eval's... 
Really, the only reason I'm using XML at all right now is that it provides the way of making asynchronous requests to a server that requires the least programming effort on my part. I wanted a quick proof-of-concept and a "can I do this at all without killing the webserver" test - which I think it's passed, for now :) - MoonShadow
All of the above sounds good.  Just to check though, you are aware that there is nothing about XMLHttpRequest? / AJAX that implies the use of xml, yes? --K (who is probably missing the point but thought he should check)
...except the fact that the code I copy/pasted from the ticker does, and I know that already works so I don't need to think about it any more :) I basically write stuff by being extremely lazy. - MoonShadow
Laziness in a programmer is a virtue isn't it?  Leads to reusable code and that sort of thing :)  Hadn't realised you were already doing this in the ticker and stuff.  That's cool --K

Feature request: At the moment it's difficult to debug plugins, because if a plugin doesn't work you can't tell whether the file can't be found, the XML is malformed, or there are JS errors in the plugin (and if so, what the errors are). It would presumably be possible in principle for the client to output this information (possibly in an alert?)
I believe this has to be done in the client code rather than in another plugin, because it involves client functions such as loadPlugin, BICBW. If it can be done in a plugin I'd quite like to have a go myself :) --Rachael
IMO FireFox's javascript console (under Tools) is much more useful than anything I could do in a reasonable amount of time. There is also [Venkman], though I have found it to be overkill for chat plugins. Oh, and typing /dev in your chat box will give you a Javascript command window, which will let you examine members of client and invoke functions such as loadPlugin directly. - MoonShadow
I have heard good things about [FireBug] --RobHu
Indeed - I believe it's the same author and it supersedes venkman.  Firebug was very nice whilst I was playing around with EvilScript.  --Vitenka

La Wiki Dent Francais?  --Vitenka
??????? - MoonShadow
La Toothy [[Chat]]  --Vitenka
Owwwwwwwwwwwwwwww... - MoonShadow
Hmm, that would mean ToothyChat is eligible to be in CategoryKittenTechnicalMatters, wouldn't it? :D --AC

Can we support [[ToothyWiki/Subpage]] style links? --CH

A lot of the time at home (and somewhat more rarely at work), when I open ToothyChat, I get the chat history (correctly coloured and reversed), but the channels plugin doesn't load, and the spinny is dead. It takes many refreshes to make everything alive again. Any idea what might be happening? --CH
Javascript error console reports "Too much recursion" at line 448. --CH
Suspect plugin loading order badness. Try killing your plugins (chat.html?plugin0=*) - MoonShadow
Having hit this again painfully at work, I've put FireBug? over it. I can now tell you that
That's because it's cachebust.xml. - MoonShadow
Any suggestions on the rest short of blowing away my plugins?
No idea, I'm afraid - can't reproduce the problem... - MoonShadow
In case it helps, here is my Self URL. I seem to have two instances of fixed_colours, if that would make a difference? --CH
Doesn't work for me in IE7 (https://www.toothycat.net/~sham/chat.html?channel=kazu) - all I see is the input box, and I have a script error of "Invalid argument" on line 73 char 17. --ThePants999
I only have IE6 here and don't want to replace IE on a work machine in case it breaks stuff - the toolchain is flaky as it is. At home, I run Linux. So I don't think I'm in a position to support IE7 if it fails to behave the way IE6 and Firefox do, I'm afraid. I've made a speculative change to the corresponding line, but can't reproduce your problem or tell if I've fixed it. - MoonShadow

To stop yourself accidentally posting to a channel:
 /store-js if (document.location.href.indexOf("kazu") > 0) { document.getElementById("intext").style.display = "none"; }

ec2-3-230-143-213.compute-1.amazonaws.com | ToothyWiki | ToothyWikiInternals | RecentChanges | Login | Webcomic
Edit this page | View other revisions | Recently used referrers
Last edited October 22, 2020 9:49 am (viewing revision 167, which is the newest) (diff)