dAmn Interoperability Project — READ-ONLY ARCHIVE

 [FrontPage]


deviantART messaging network (dAmn) Protocol (version 0.2)

revision 0.8.71 -- 2005-12-10

NOTICE: This document is released under the [WWW] GNU Free Documentation License. See the license info.

  1. General
  2. Basic Packet Format
  3. List of Commands
    1. Client commands (Client -> Server)
      1. Handshake
      2. Login
      3. Join
      4. Part
      5. Send
      6. action
      7. promote
      8. demote
      9. ban
      10. unban
      11. kick
      12. set
      13. get
      14. userinfo
      15. admin
      16. pong
    2. Server commands (Server -> Client)
      1. Handshake
      2. login
      3. property
      4. recv
      5. send
      6. ping
      7. kicked
      8. disconnect
  4. Tablumps
  5. Properties
    1. members
  6. A sample session
    1. Handshake
    2. Joining a channel
    3. Chatting
  7. Revision history
  8. Todo
  9. License


1. General

[WWW] dAmn is the 'killer' (and basically, the only new) feature of [WWW] 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:

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

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)

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 [WWW] 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.

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:

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.

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:

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

8. Todo

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.


2013-03-28 12:59