DAmn/SampleParser
From Botdom Wiki
Here are some examples of dAmn packet parsers written in different programming languages.
Contents |
C#
This one was written by plaguethenet, parses packets the exact same way the official client does it.
public class dAmnArgs { private string _body; private Dictionary<string, string> _args; public Dictionary<string, string> args { get { return _args; } set { _args = value; } } public string body { get { return _body; } set { _body = value; } } public static dAmnArgs getArgsNData(string data) { dAmnArgs p = new dAmnArgs(); p.args = new Dictionary<string, string>(); p.body = null; while (true) { if (data.Length == 0 || data[0] == '\n') break; int i = data.IndexOf('\n'); int j = data.IndexOf('='); if (j > i) break; p.args[data.Substring(0, j)] = data.Substring(j + 1, i - (j + 1)); data = data.Substring(i + 1); } if (data != null && data.Length > 0) p.body = data.Substring(1); else p.body = ""; return p; } } public class dAmnPacket { private string _cmd = ""; private string _param = ""; //private string _body = ""; //private Dictionary<string, string> _args = new Dictionary<string,string>(); dAmnArgs argsAndBody; public string cmd { get { return _cmd; } set { _cmd = value; } } public string param { get { return _param; } set { _param = value; } } public string body { get { return argsAndBody.body; } set { argsAndBody.body = value; } } public Dictionary<string, string> args { get { return argsAndBody.args; } set { argsAndBody.args = value; } } public static dAmnPacket parse(string data) { if (data == null) throw new ArgumentNullException("data"); string orig_data = data; try { dAmnPacket p = new dAmnPacket(); int i = data.IndexOf('\n'); if (i < 0) throw new Exception("Parser error, No line break."); string tmp = data.Substring(0, i); p.cmd = tmp.Split(' ')[0]; int j = tmp.IndexOf(' '); if (j > 0) p.param = tmp.Substring(j + 1); p.argsAndBody = dAmnArgs.getArgsNData(data.Substring(i + 1)); return p; } catch (Exception e) { throw e; } //return null; //wtf? how did we get here? D: } }
It is very easy to use this parser:
dAmnPacket p = dAmnPacket.parse(data);
For the recv packet you can just parse the body again to extract the sub packet.
PHP
This is dbpc and xbot's dAmn packet parser. I added this here because ManjyomeThunder complained that all the other ones on this page were too complicated/slow/long.
You'll need this constant defined:
define('LF', chr(10));
Parser:
// converts a dAmn raw string to a dAmn packet data structure function str2dpkt($raw) { list($head, $body) = args($raw, 2, LF.LF); list($hair, $face) = args($head, 2, LF); $pkt = args($hair, 2); $pkt[2] = arghasht($face); $pkt[3] = $body; return $pkt; }
It uses two customs function, args() and arghasht(), included with the clients, but if you have no need of their extra functionality, these simpler ones will work fine.
function args($data, $r=0, $sep=' ') { $args = explode($sep,$data,$r); if (count($args)<$r) { $args = array_pad($args,$r,''); } return $args; }
function arghasht($data, $sep='=', $linesep=LF) { if (empty($data)) { return array(); } // most of the time this will be the case $lines = explode($linesep, $data); foreach ($lines as $line) { if (empty($line)) { continue; } list($k, $v) = args($line, 2, $sep); $hasht[$k] = $v; } return $hasht; }
And here's the inverse function:
// converts a dAmn packet data structure to a dAmn raw string function dpkt2str($pkt) { $str = empty($pkt[1])? $pkt[0].LF: $pkt[0].' '.$pkt[1].LF; if (isset($pkt[2])) { foreach ($pkt[2] as $k=>$v) { $str .= $k.'='.$v.LF; } } if (!empty($pkt[3])) { $str .= LF.$pkt[3]; } return $str; }
This one was written by photofroggy, official parser was not referred to when making it. Uses a much more simple concept. ManjyomeThunder helped design this version.
function parse_dAmn_packet($data, $sep = '=') { $packet = array( 'cmd' => Null, 'param' => Null, 'args' => array(), 'body' => Null, 'raw' => $data ); if(stristr($data, "\n\n")) { $packet['body'] = trim(stristr($data, "\n\n")); $data = substr($data, 0, strpos($data, "\n\n")); } $data = explode("\n", $data); foreach($data as $id => $str) { if(strpos($str, $sep) != 0) { $packet['args'][substr($str, 0, strpos($str, $sep))] = substr($str, strpos($str, $sep)+1); } elseif(strlen($str) >=1) { if($id == 0) { if(!stristr($str, ' ')) { $packet['cmd'] = $str; } else { $packet['cmd'] = substr($str, 0, strpos($str, ' ')); $packet['param'] = trim(stristr($str, ' ')); } } else { $packet['args'][] = $str; } } } return $packet; }
Using this parser is also easy:
$packet = parse_dAmn_packet($data); # Example of usage: $packet = parse_dAmn_packet("command parameter\narg=value\narg2=value2\n\nBody"); # This would result in the following: # $packet['cmd'] = 'command'; # $packet['param'] = 'parameter'; # $packet['args'] = array( # 'arg' => 'value', # 'arg2'=> 'value2' # ); # $packet['body'] = 'Body'; # $packet['raw'] = 'command parameter\narg=value\narg2=value2\n\nBody';
For the recv packet you can just parse the body again to extract the sub packet.
Python
Ported from plaguethenet's code by SubjectX52873M
import UserDict class Packet(UserDict.DictMixin): def __init__(self,data=None,subPacket=False): """if a string is passed the string is parsed as a dAmnserver packet otherwise creates an empty packet object""" self.cmd="" self.param=False self.body=False self.__args={} if not data==None: data=str(data) try: i = data.find("\n") if i==-1: raise DamnParserException,"Parser error, No line break." tmp = data[0:i].split(' ',1) self.cmd = tmp[0] if len(tmp)==2: self.param=tmp[1] data=data[(i+1):] while(true): if len(data) == 0 or data[0] == "\n": break; i = data.find("\n") j = data.find('=') if i==-1 and not j==-1: r[data[0:j]]=data[(j+1):] elif j > i: break; else: self.__args[data[0:j]]=data[(j+1):i] data=data[i:] #EndWhile if data[0:1]=="\n\n" and not subPacket and not self.cmd=="property": #Property packets can get nasty. self.body=Packet(data,True) elif not data==None and len(data) > 0: self.body = data except e: raise DamnParserException,"Unknown error: %s" % e def __str__(self): with self.__lock: """Returns a dAmn formated packet""" str="" str+=self.cmd if self.param: str+= " %s" % self.param str+="\n" if not self.__args == 0: for key,value in self.__args.iteritems(): str+= "%(k)s=%(v)s\n" % {'k':key,'v':value} if self.body: str+="\n" str+=str(self.body) return str def getArgs(self,data): """Returns a two truple with (dict,string/bool:False)""" #Useful for property packets, eventually add parsing for those types here. data= str(data) #enfore type r={} body=False while(true): if len(data) == 0 or data[0] == "\n": break; i = data.find("\n") j = data.find('=') if i==-1 and not j==-1: r[data[0:j]]=data[(j+1):] elif j > i: break; else: r[data[0:j]]=data[(j+1):i] data=data[i:] #EndWhile if not data==None and len(data) > 0: body = data return (r,body) #Implemented for DictMixin def __getitem__(self,key): #Thread safe return self.__args[key] def __setitem__(self,key,value): self.__args[key]=value def __delitem__(self,key): del self.__args[key] def __contains__(self,key): self.__args.__contains__(key) def __iter__(): return self.__args.__iter__() def __len__(): return len(self.__args) def iteritems(): return self.__args.iteritems() def keys(): return self.__args.keys()
Implemented as a dictionary.
- Parse a Message packet
recv chat:chatroom msg main from=username message
p = Packet("recv chat:chatroom\n\nmsg main\nfrom=username\n\nmessage\0")
- Access a argument
>>>print p["from"] username
- Access the command name
>>>print p.cmd
recv
- Access the command parameter
>>>print p.param
chat:chatroom
- Access the message
>>>print p.body.body message
- Access the type of message (action vs. msg)
>>>print p.body.cmd msg
- Create a send packet
p = Packet() p.cmd="send" p.param="chat:chatroom" p.body=Packet() p.body.cmd="msg" p.body.param="main" p.body.body="message"
- Convert for sending to the server
str(p)
This is a much simpler parser written in Python by photofroggy. It has less functionality but parses packets easily.
class Packet: ''' The Packet class is a simple parser which breaks the received dAmn packets into a more usable form. This could be easily achieved as a single function and would be virtually unchanged. ''' cmd = None param = None args = {} body = None raw = None def __init__(self, data = None, sep = '='): self.raw = data if not data == None: try: if not data.find("\n\n") == -1: self.body = data[data.find("\n\n")+2:] data = data[0:data.find("\n\n")] for st in data.split("\n"): if not st.find(sep) == -1: self.args[st[0:st.find(sep)]] = st[st.find(sep)+len(sep):] elif self.cmd == None: if st.find(' ') == -1: self.cmd = st else: self.cmd = st[0:st.find(' ')] self.param = st[st.find(' ')+1:] except: pass else: pass
The parser could be reduced to a single function and the changes would be minimal. Below is an example of how to use this parser.
# Create a packet object with some random data. pack = Packet("command parameter\narg=foo\narg2=bar\n\nbody") ''' This leaves you with the following: pack.cmd = 'command' pack.param = 'parameter' pack.args = { 'arg' : 'foo', 'arg2': 'bar' } pack.body = 'body' pack.raw = 'command param\narg=foo\narg2=bar\n\nbody' '''
Ruby
Written by ManjyomeThunder. Released under a GNU General Public License (v3 or above).
class Packet attr_reader :cmd, :param, :sub, :dAmn, :body, :args def initialize(data, dAmn = nil, separator = '=') @args, @body, @dAmn = {}, '', dAmn if data.include?("\n\n") @body = data[data.index("\n\n") + 2 .. -1] data = data[0 ... data.index("\n\n")] @sub = Packet.new(@body) unless @body.empty? end data.each_line { |line| if line.include?(separator) @args[line[0 ... line.index(separator)].to_sym] = line[line.index(separator) + separator.length .. -1] else @cmd = (line.include?(' ') ? line[0 ... line.index(' ')] : line).to_sym @param = line[line.index(' ') + 1 .. -1] if line.include?(' ') end } end def [](key) @args[key.to_sym] end def to_s packet = @cmd.to_s packet << ' ' + @param unless @param.nil? packet << "\n" unless packet.empty? @args.each { |key, val| packet << "#{key}=#{val}\n" } packet << "\n#{body}" unless @body.nil? end end # Create a packet object. packet = Packet.new("recv chat:Botdom\n\nmsg main\nf=ManjyomeThunder\n\nTest message packet", some_dAmn_object) # Access the command and param puts packet.cmd # prints "recv" puts packet.param # prints "chat:Botdom" # Access packet arguments puts packet[:f] # prints "ManjyomeThunder" # Access sub-packet and body puts packet.sub.cmd # prints "msg" puts packet.body # prints "msg main\nf=ManjyomeThunder\n\nTest message packet" # Access dAmn packet.dAmn.write_packet(packet) # writes the packet back to the server.
Java
- Not to be confused with JavaScript, which is an unrelated programming language.
This one was written by photofroggy, and ported to Java by Solitude12 (I realize this isn't that amazing, especially since it is not written cleanly. It is a more messy version. I will have to clean it up soon - Solitude12)
import java.util.HashMap; import java.util.ArrayList; public class Packet { public String cmd, param, body = null; public HashMap<String, String> args = new HashMap<String, String>(); public String raw; public Packet(String packet){ this(packet, "="); } public Packet(String packet, String splitter){ this.raw = packet; ArrayList<String> splitted = new ArrayList<String>(); ArrayList<String> splitteddos = new ArrayList<String>(); for (String splits : packet.split("\n\n", -1)){ splitted.add(splits); splitteddos.add(splits); } splitted.remove(0); for(int i = 0; i<splitted.size(); i++){ String bit = splitted.get(i); if(bit.length() > 0) { if (this.body==null) this.body = bit; else this.body = this.body + "\n\n" + bit; } } ArrayList<String> head = new ArrayList<String>(); for (String splits : splitteddos.get(0).split("\n", -1)) head.add(splits); ArrayList<String> splittedtres = new ArrayList<String>(); for (String splits : head.get(0).split(" ", -1)) splittedtres.add(splits); if(splittedtres.size()>0) { this.cmd = splittedtres.get(0); this.param = splittedtres.get(1); head.remove(0); } for(int i = 0; i<head.size(); i++){ String string = head.get(i); int pos = string.indexOf(splitter); if(pos < string.length()-1 && pos > 0) { String[] args2 = string.split(splitter, -1); this.args.put(args2[0], string.substring(pos+1, string.length())); } else if(string.length()>=1) if(i == 0 && this.cmd==null) this.cmd = string; } } }
Using this parser is also easy:
// Example of usage: Packet test = new Packet("command parameter\narg=value\narg2=value2\n\nBody"); /* test.cmd -> command test.param -> parameter test.args.get("arg") -> value test.args.get("arg2") -> value2 test.body -> Body */
For the recv packet you can just parse the body again to extract the sub packet.
JavaScript
- Not to be confused with Java, which is an unrelated programming language.
Experiemental: This is a packet parser developed by miksago, heavily based on Contra's php one:
function parse_pkt(data){ var packet = { cmd: null, param: null, args: {}, body: null, raw: data }; data = data.split("\n\n"); packet['body'] = data[1] || null; data = data[0].split("\n"); for(var i=0;i<data.length;i++){ if(i==0){ packet['cmd'] = data[i].split(' ')[0]; packet['param'] = data[i].split(' ')[1] || null; } else if(data[i].indexOf("=") != -1){ packet['args'][data[i].substr(0, data[i].indexOf("="))] = data[i].substr(data[i].indexOf("=")+1); } } return packet; }
If in Doubt, look and use the webchats packet parser:
function parseArgs(data){ var args = new Object(); var body; while(1){ if( !data.length || data.charAt(0) == '\n') break; var res = dAmn_packetArgs_re.exec( data ); args[res[1]] = res[2].replace(/\s*$/,''); data = data.substr(res[0].length); } // strip initial \n if( data && data.length ) body = data.substr(1); else body = ''; return {'args':args,'body':body}; } function parse(data){ if( !data ) alert("packet data is null"); var orig_data = data; try{ var cmd; var param; var i = data.search('\n'); if( 0 == i ) return null; var tmp = data.substr(0,i).replace(/\s*$/,''); cmd = tmp.match(/\S+/)[0]; var j = tmp.search(' '); if( j ) param = tmp.substr(j+1).match(/\S.*/)[0].replace(/\s*$/,''); var ad = parseArgs(data.substr(i+1)); return {'cmd':cmd,'param':param,'args':ad.args,'body':ad.body}; } catch(e){ alert('parse packet exception: \n' + orig_data + '\n\n----\n' + e ); return null; } }

