/AboutIP /ClientPOE /ClientPerl /ClientQt /Clients /History /News /Protocol /SampleCode /Tools /Who |
deviantART messaging network (dAmn) Protocol (version 0.2) |
revision 0.8.71 -- 2005-12-10 |
NOTICE: This document is released under the GNU Free Documentation License. See the license info.
- General
- Basic Packet Format
- List of Commands
- Tablumps
- Properties
- A sample session
- Revision history
- Todo
- License
1. General
dAmn is the 'killer' (and basically, the only new) feature of deviantART v4. It's a simple web chat application that relies only on Flash and JavaScript.
The dAmn protocol is text-based and uses null-terminated strings (asciiz).
Throughout this document, I'll use a pseudo-encoded notation for the packet contents.
All normal letters and symbols will be spelled out, with the following additions:
' ' -> Space characters (20h/32d)
'\n' -> Line feeds (0ah/10d)
'\t' -> Horizontal tabs (09h/9d)
'\0' -> NUL (00h/0d)
Other non-printable data will be encoded in a fashion similar to URI encoding, by using a two-digit hex code, preceded by a percent sign, e.g. %ff = ffh = hexadecimal ff = decimal 255.
Variables will be written in a Perl-style notation: ${nick} means the value of the variable named 'nick', i.e. the user's nickname.
Outgoing packets (sent from the client to the server) will be marked by "-> ".
Incoming packets (sent from the server to the client) will be marked by "<- ".
To describe a packet's structure, I'll use a form of BNF which should be self-explanatory, in which 'SP' means a space, 'LF' a line feed, 'TAB' a horizontal tabulator, 'NUL' the NUL char. Parentheses mark optional parts.
dAmn runs on TCP port 3900. Currently, the only known server is chat.deviantart.com:3900 (69.28.181.43).
2. Basic Packet Format
Packets in general look like this:
Command ::= <string>
Parameter ::= <string>
Argument ::= <string>
Body ::= <string> | Body LF Body
Packet ::= Command ( SP Parameter ) LF ( Argument LF ) ( LF Body ) NUL
For descriptions of commands, parameters, arguments and body, see List of Commands.
../SampleCode to parse and build packets.
3. List of Commands
This list is almost certainly incomplete. Format is similar to tablumps.
command(param, arg, body)
If arg and/or body are not given, they are assumed to be empty. Variable names should be self-descriptive.
3.1. Client commands (Client -> Server)
Big update, don't have the time to make a better change now: Additional to the here used namespace chat there's now the namespace pchat for a private chat. The name of the private channel is user1:user2 with user1 < user2 (ascii sorted).
3.1.1. Handshake
- Description
Initial handshake.
- Syntax
dAmnClient(${client_version}, "agent=${agent}(\nyour_field=${your_field})*\n") // v0.2
dAmnClient(${client_version}) // v0.1- Notes
Please make your ${agent} specific to your client. After the required agent you may add as many fields as you want, for example language=${language}, but they are optional (v0.2).
The server will disconnect the client for an invalid version number, e.g. one not matching the server code.
Protocol version 0.1 is no longer supported.
3.1.2. Login
- Description
Login request.
- Syntax
login(${nick}, "pk=${authtoken}")
3.1.3. Join
- Description
Join a channel.
- Syntax
join("chat:${channel}")
- Example
/raw join chat:Devart
3.1.4. Part
- Description
part a channel.
- Syntax
part("chat:${channel}")
- Example
/raw part chat:Devart
3.1.5. Send
- Description
Send text to a channel.
- Syntax
send("chat:${channel}", null, "msg main\n\n${message}")
send("chat:${channel}", null, "npmsg main\n\n${message}") for a non-parsed message
- Example
/raw send chat:Devart\n\nmsg main\n\nHello
/raw send chat:Devart\n\nnpmsg main\n\nHello
- Notes
The max character limit appears to be determined by the size of the recv packet that the message would produce. Based on testing done by doofsmack, it seems that if the message sub-packet ("\n\nmsg main\nfrom=${username}\n\n${message}\0") is larger than 8192 bytes (8 KiB), the message is rejected and you are disconnected.
3.1.6. action
- Description
/me actions.
- Syntax
send("chat:${channel}", null, "action main\n\n${action}")
- Example
/raw send chat:Devart\n\naction main\n\nIdles
3.1.7. promote
- Description
Promote ${nick} to ${privclass}, or the next highest privclass if ${privclass} is empty.
- Syntax
send("chat:${channel}", null, "promote ${nick}\npc=${privclass}")
- Example
/raw send chat:Hell\n\npromote Blue-Six\n\nRedeemed
(note that privclass names are always case sensitive.)
3.1.8. demote
- Description
Demote ${nick} to ${privclass}, or the next lowest privclass if ${privclass} is empty.
- Syntax
send("chat:${channel}", null, "demote ${nick}\npc=${privclass}")
- Example
/raw send chat:Hell\n\ndemote Blue-Six\n\nMuted
3.1.9. ban
- Description
Ban ${nick} from ${channel}
- Syntax
send("chat:${channel}", null, "ban ${nick}")
- Example
/raw send chat:a\n\nban drama\n\n
3.1.10. unban
- Description
Move ${nick} to the ${channel}'s default privclass
- Syntax
send("chat:${channel}", null, "unban ${nick}")
- Example
/raw send chat:a\n\nunban doofsmack\n\n
3.1.11. kick
- Description
Kick ${nick} from ${channel} with ${reason}
- Syntax
kick("chat:${channel}", "u=${nick}", "${reason}");
- Example
/raw kick chat:Hell\nu=gfx-sheep\n\nKicked by raw :pointandlaugh:
Kicking of multiple users simultaneously does not appear to be possible at this time using the kick command.
3.1.12. set
- Description
Set ${channel}'s property (topic, title) to value
- Syntax
set("chat:${channel}", "p=${property}", ${value})
3.1.13. get
- Description
Request a property packet from the server
- Syntax
get("chat:${channel}", "p=${property}")
3.1.14. userinfo
- Description
Get userinfo for ${nick} as property userinfo
- Syntax
get ("login:${nick}", "p=info")
- Notes
You can use this to get the server to send any property of any channel you've joined. Example: get ("chat:damnhack", "p=topic") would have the server resend the topic
3.1.15. admin
- Description
Run an administrative command. For more information, see deviantART FAQ entry #540.
- Syntax
send("chat:${channel}", null, "admin\n\n${command}")
- Example
/raw send chat:caffeinelounge\n\nadmin\n\nupdate privclass Heart-Stealers -kick
3.1.16. pong
- Description
Answer to a ping from the server
- Syntax
pong()
- Notes
You can send a pong as often you want, for example to check the connection.
3.2. Server commands (Server -> Client)
3.2.1. Handshake
- Description
The server responds with a dAmnServer command and its version number, in response to a successful handshake.
- Syntax
dAmnServer(${server_version})
3.2.2. login
- Description
Response to a login packet by the client.
- Syntax
login(${nick}, "e=${event}")
- Notes
Known values for ${event}:
ok
login successful
authentication failed
username and authtoken supplied were invalid
3.2.3. property
- Description
Sent by server to set an object's property.
- Syntax
property("chat:${channel}", "p=${property}", ${value})
- Notes
Known values for ${property}:
privclasses
Privilege Classes
members
Channel user list
topic
Channel topic
title
Channel title
login:${username}
Whois information
For topic and title, the param is p=${property}\nby=${user}\nts=${timestamp} , with ${user} being the name of the user who set the topic/title, and ${timestamp} an UNIX timestamp (seconds since Jan 1 1970).
3.2.4. recv
- Description
Received a sub-packet for a namespace.
- Syntax
recv("chat:${channel}", null, "msg main\n\n${message}") Received channel message. recv("chat:${channel}", null, "action main\n\n${action}") Received channel action. recv("chat:${channel}", null, "propchg ${property}\nby=${nick}\n") Property change event (topic change is preceded by a property(channel,topic,value) command). recv("chat:${channel}", null, "privchg ${target}\nby=${nick}\npc=${privclass}\n") Privilege change event. recv("chat:${channel}", null, "kicked ${target}\nby=${nick}\n\n${message}") Kick user reply. recv("chat:${channel}", null, "join ${nick}\npc=${privclass}\nusericon=${usericon}\nsymbol=${symbol}\n realname=${realname}\ntypename=${typename}\ngpc=${gpc}\n") New user joined. recv("chat:${channel}", null, "part ${nick}\nr=${reason}\n") User has parted (reason may be empty).
3.2.5. send
- Description
Server response to failed sends.
- Syntax
send("chat:${channel}", "e=${event}")
- Notes
Known values for ${event}:
nothing to send
Trying to send an empty message.
not joined
Trying to send to a channel you never joined.
3.2.6. ping
- Description
A simple ping packet.
- Syntax
ping()
- Notes
If the server didn't receive a message from the client for a while, it'll send a ping. A pong() as answer is required.
3.2.7. kicked
- Description
Person who recieved this packet was kicked from a channel
- Syntax
kicked("chat:${channel}", null, "by=${nick}\n\n${reason}")
- Notes
Reason may be empty. If it is then no reason was given.
3.2.8. disconnect
- Description
The server has ended the chat-session and has closed the socket.
- Syntax
disconnect(null,null,"e=${reason}")
- Notes
Known values for ${reason}: killed
4. Tablumps
Some commands are encoded into what I call 'tablumps' - tab-separated lists. For example, if the server sends an emote:
<- &emote\t:P\t15\t15\t:P (Lick)\tletters/=p.gif\t
Tablumps are split on \t, and are named after their first item, in this case, 'emote'.
Tablump ::= & TablumpName ( TAB ParamN )* TAB
For better readability, the notation:
&emote(':P', 15, 15, ':P (Lick)', 'letters/=p.gif')
will be used. This example means that the TablumpName is "emote", and the parameters are :P, 15, 15, :P (Lick), letters/=p.gif. Please note that tablumps are always terminated by a tab, too! That means &/iframe() actually is '&/iframe\t', even though it has no parameters!
So far, the following tablumps are known:
&emote(emoticon, width, height, description, relative path to image)
emoticon
the text the user entered, e.g. ':p', ':stfu:', etc.
description
the title="" tag of the image, shown as a tooltip in compliant browsers
&iframe(url, width, height)
&/iframe()
&link(url, title)
&()
-
The empty tablump seems to act as a terminator, even though it's not neccessary.
&a(url)
&/a()
-
The other link.
&img(url, width, height)
&avatar(username, usericon)
usericon
0 to use default avatar
format of the avatar: 0='.gif', 1='.gif', 2='.jpg', 3='.png'
&dev(symbol, username)
symbol
the deviantART symbols - ~, *, $, etc.
&thumb(number, title, deviant, size, tnserver, url, flags)
number
the deviation number
deviant
includes the symbol
size
string in the form "WIDTHxHEIGHT"
if WIDTH < 100 you have to use the full-view image instead of a thumb, because there is no thumb.
tnserver
is the number of the thumbnail-server.
For example: if tnserver == 1, then the url of the thumbnail is
http://tn1.deviantart.com/100/url
^ ^ ^- url of the full-view image, see below.
| `- 100 = width of the thumb. Valid values are 100, 150, 200, and 300W.
`- there's the number from tnserver
so you get something like
http://t3.deviantart.com/150/fs4.deviantart.com/i/2004/198/8/3/The_Way_to_Heaven_I.jpg
(tnserver 3, width: 150, url fs4.deviantart.com/i/2004/198/8/3/The_Way_to_Heaven_I.jpg)
url
http://url is the full-view image if url begins with fsX.deviantart.com
fsX: in the url has to be replaced by fsX.deviantart.com/
for urls ending in .gif is normally no thumb used, use the fullview-image instead
flags
noshadow:mature:nopriv
noshadow: default 0. if != 0, don't paint a shadow behind the thumbnail.
mature: default 0. if != 0, don't display the thumbnail, but only a link with a notice saying 'mature deviation'.
nopriv: default 0. if != 0, don't display the thumbnail, but only a link to the deviation.
&b()
&/b()
-
For bold. Similarly, &i() and &/i() exist.
still not testet, just got from $mccann:
[22:00] <bzed> &bcode?
[22:01] <mccann> works out to <pre>&code ....&/code </pre>
[22:01] <mccann> works out to <pre><code>....</code></pre>
[22:01] <mccann> in html
A regular expression (PCRE, RegEx) to find and extract tablumps is: &((?:[^\t\&]+\t+)+|(?:\t))
5. Properties
A dAmn chat channel has the following properties, which are sent upon joining a channel or changing a property in a property packet.
property
body
privclasses
99:Founders\n75:Operators\n50:Members\n1:Banned\n62:Helpers\n25:Guests
members
member ${nick}\npc=${privclass}\nusericon=${usericon}\n
symbol=${symbol}\nrealname=${realname}\ntypename=${typename}topic
by=${username}\nts=${timestamp}\n${topic}
title
by=${username}\nts=${timestamp}\n${title}
login:${username}
p=info\nusericon=${usericon}\nsymbol=${symbol}\nrealname=${realname}\ntypename=${typename}\ngpc=${gpc}(\nconn\nlogin=${login}\nidle=${idle}\n(ns chat:${chat})*)+
A <- join packet is followed by four <- property packets - setting privclasses, members, topic and title in that order. The client should parse them and initialize its member list, channel topic and title.
The client should then update those properties when it encounters a property packet:
property("chat:${channel}", "p=${property}", "${value}")
5.1. members
Arguments:
argument
description
pc
one of the privclasses defined in the privclasses property
usericon
either 1 or 0:
1 --> http://a.deviantart.com/avatars/FIRSTLETTER/SECONDLETTER/username
0 --> http://a.deviantart.com/avatars/default.gifsymbol
The normal deviantART symbols (~, *, ^, etc.)
realname
Real name of the user
typename
The "deviant type"
6. A sample session
6.1. Handshake
The dAmnClient sends a login token to the server in the following format
-> dAmnClient 0.2\nagent=${agent}\nmisc=${misc}\n\0
-> login ${nick}\npk=${authtoken}\n\0
To get ${authtoken}, you must check your Cookies. When logging in, the deviantART servers generate a token of 32 hex digits (an md5 hash of a random string).
Look for a cookie called 'userinfo', which should be a mess of urlencoded text. If you can, urldecode it, it makes parsing much easier. Else just find the part where it says 'authtoken', followed by some encoded hex chars, then by the token you want.
A tool to decode the cookie is available online, just give it the part with the authtoken in it and you should get output that's more parseable.
../Tools to get the auth token for you.
The server then compares the auth token to the one stored in the dA database and replies with:
<- dAmnServer 0.1\n\0
<- login ${nick}\ne=${event}\n\0
${event} is 'ok' for a successful login, or 'authentication failed'.
6.2. Joining a channel
Let ${channel} be the name of an existing channel, without the leading hash mark '#'.
-> join chat:${channel}\n\0
<- join chat:${channel}\ne=ok\n\0
The server then sends the channel's properties:
<- property chat:${channel}\np=privclasses\n\n(${classNumber}:${className}\n)+\0
for example: property chat:${channel}\np=privclasses\n\n99:Founders\n75:Operators\n50:Members\n1:Banned\n62:Helpers\n25:Guests\n\0
<- property chat:${channel}\np=members\n\nmember ${nick}\npc=${privclass}\nusericon=${usericon}\n
symbol=${symbol}\nrealname=${realname}\ntypename=${typename}\n\n\0
<- property chat:${channel}\np=topic\n${topic}\n\0
<- property chat:${channel}\np=title\n${title}\n\0
The members property can be quite a lot of data, but it all has the same format:
${nick} is the member's nick
${privclass} is one of the classes defined earlier, e.g. 'Founders', 'Members'
${usericon} is either 0 (use default avatar), 1 (avatar is a .gif file), 2 (.jpg) or 3 (.png)
${symbol} is the deviantART symbol
${realname} is the name from your dA settings
${typename} is the deviant type, e.g. 'General Digital Photographer'
Topic and title can contain tablumps and should be parsed like a message.
6.3. Chatting
Chatting is very straighforward. It seems that privates might be possible, but are not implemented yet.
-> send chat:${channel}\n\nmsg main\n\n${message}\0
<- recv chat:${channel}\n\nmsg main\nfrom=${nick}\n\n${message}\0
Please note that the dAmn server sends back your own messages, so you don't need to handle user input in any special way.
7. Revision history
0.9.0 Kevin 'doofsmack' Wallace <kevin@doofsmack.com>
Added ban, unban, get
Fixed small error in promote
0.8.6 Matthias 'moeffju' Bauer <recv-damn-ezwrzovu@moeffju.net>
minor updates, some corrections
0.8.5 Bernd 'BZed' Zeimetz <deviantart@bzed.de>
new whois code
0.8.4 Kevin 'doofsmack' Wallace <damnhack@gotdoof.com>
revisited maximum message size issue
0.8.3 Fish 'IceShaman' Monger <iceshaman@gmail.com>
added helper permission level to the rest eg founders
0.8.3 Bernd 'BZed' Zeimetz <deviantart@bzed.de>
ping <> pong
0.8.2 Kevin 'doofsmack' Wallace <damnhack@gotdoof.com>
added note to userinfo client-command
0.8.1 Bernd 'BZed' Zeimetz <deviantart@bzed.de>
userinfo client-command + property (thanks to $mccann)
0.8.0 Matthias 'moeffju' Bauer <recv-damn-ezwrzovu@moeffju.net>
dAmn v0.2 (thanks to $mccann)
0.7.1 Bernd 'BZed' Zeimetz <deviantart@bzed.de>
DA is using shortcuts for the full-view servers in the thumb's URL sometimes. Added to tablumps/thumbs.
0.7.0 Matthias 'moeffju' Bauer <recv-damn-ezwrzovu@moeffju.net>
Updated property (p=topic, p=title)
Added kick client command
0.6.3 Bernd 'BZed' Zeimetz <deviantart@bzed.de>
Added info to the thumbnail-section of tablumps (how to get the thumbnail's URL)
0.6.2 David 'Rob' Robson <dAmnHack@colournet.org>
Formatting updates
Added notes regarding line length of a single chat message
0.6.1 Matthias 'moeffju' Bauer <recv-damn-ezwrzovu@moeffju.net>
Formatting changes, minor corrections
Added more descriptions
Removed some outdated information
0.6 David 'Rob' Robson <dAmnHack@colournet.org>
Added part client->server message.
0.5 David 'Rob' Robson <dAmnHack@colournet.org>
Added known error responses
Added start of descriptions for known commands
0.4 Matthias 'moeffju' Bauer <recv-damn-ezwrzovu@moeffju.net>
Fixed some errors in the protocol spec.
Linked to ../Tools and ../SampleCode subpages.
Added section on properties
0.3 Matthias 'moeffju' Bauer <recv-damn-ezwrzovu@moeffju.net>
Wikified.
0.2 Matthias 'moeffju' Bauer <recv-damn-ezwrzovu@moeffju.net>
Major rework.
0.1 Matthias 'moeffju' Bauer <recv-damn-ezwrzovu@moeffju.net>
First revision, based on dAmn 0.1.
8. Todo
Write a proper multiplatform GUI client.
Write a commandline tool for use in irssi.
Write a plug-in or sock script for mIRC.
Write an IRC <> dAmn gateway.
9. License
Copyright (c) 2004 Matthias Bauer, David Robson
Copyright (c) 2004-2005 Matthias Bauer, David Robson, Bernd Zeimetz and others
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license can be found at GNU FDL.