Initial commit
This commit is contained in:
commit
cb11018eba
|
@ -0,0 +1,101 @@
|
||||||
|
require "date"
|
||||||
|
def validate(*a)
|
||||||
|
# Check arguments against either a list of acceptable classes or just a class
|
||||||
|
raise ArgumentError, "expected an even number of arguments" if a.count%2 != 0
|
||||||
|
for i in (0..a.count-1).step(2) do
|
||||||
|
if a[i+1].kind_of?(Class) then
|
||||||
|
raise TypeError, "expected argument #{i/2} of type #{a[i+1].to_s}" unless a[i].kind_of?(a[i+1])
|
||||||
|
elsif a[i+1].kind_of?(Array) then
|
||||||
|
raise TypeError, "expected argument #{i/2} of any of the types #{a[i+1].to_s}" unless a[i+1].include?(a[i].class)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
module Heimdall
|
||||||
|
# Core protocol
|
||||||
|
class NetEntity
|
||||||
|
# Abstraction that stores basic object properties
|
||||||
|
@createdAt
|
||||||
|
def initialize()
|
||||||
|
@createdAt = DateTime.new
|
||||||
|
end
|
||||||
|
attr_reader :createdAt
|
||||||
|
end
|
||||||
|
|
||||||
|
class User < NetEntity
|
||||||
|
# User abstraction (not bound to a group chat)
|
||||||
|
@username
|
||||||
|
@nickname
|
||||||
|
def initialize(username,nickname = nil)
|
||||||
|
validate(
|
||||||
|
username, String,
|
||||||
|
nickname, [String,NilClass]
|
||||||
|
)
|
||||||
|
super()
|
||||||
|
nickname = username if not nickname
|
||||||
|
@username = username
|
||||||
|
@nickname = nickname
|
||||||
|
end
|
||||||
|
attr_reader :username
|
||||||
|
attr_accessor :nickname
|
||||||
|
end
|
||||||
|
|
||||||
|
class Channel < NetEntity
|
||||||
|
# Channel abstraction (not bound to a group)
|
||||||
|
# Practically acts as a message stack
|
||||||
|
# Read access to all elements
|
||||||
|
# Write access only to top
|
||||||
|
@messages
|
||||||
|
def initialize()
|
||||||
|
validate(
|
||||||
|
id, String
|
||||||
|
)
|
||||||
|
super()
|
||||||
|
@messages = []
|
||||||
|
end
|
||||||
|
def [](index)
|
||||||
|
@messages[id]
|
||||||
|
end
|
||||||
|
def <<(message)
|
||||||
|
@messages.append(message)
|
||||||
|
end
|
||||||
|
def top(count = 1)
|
||||||
|
return @messages[-1] if count == 1
|
||||||
|
@messages[(-1*count)..-1]
|
||||||
|
end
|
||||||
|
def filter(&block)
|
||||||
|
@messages.filter(block)
|
||||||
|
end
|
||||||
|
def snapshot()
|
||||||
|
@messages.clone
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Message < NetEntity
|
||||||
|
# Message abstraction with edits and content history (not bound to a group)
|
||||||
|
@content
|
||||||
|
@author
|
||||||
|
@editedAt
|
||||||
|
@contentHist
|
||||||
|
def initialize(content,author,channel)
|
||||||
|
validate(
|
||||||
|
content, String,
|
||||||
|
author, Heimdall::User,
|
||||||
|
channel, Heimdall::Channel
|
||||||
|
)
|
||||||
|
super()
|
||||||
|
@editedAt = DateTime.new
|
||||||
|
@contentHist = [content]
|
||||||
|
@content = content
|
||||||
|
@author = author
|
||||||
|
end
|
||||||
|
def edit(content)
|
||||||
|
@content = content
|
||||||
|
@editedAt = DateTime.new
|
||||||
|
@contentHist.append(content)
|
||||||
|
end
|
||||||
|
attr_reader :content
|
||||||
|
attr_reader :author
|
||||||
|
attr_reader :contentHist
|
||||||
|
attr_reader :editedAt
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,98 @@
|
||||||
|
# Prototype of the heimdall server
|
||||||
|
$LOAD_PATH << "."
|
||||||
|
$VERSION = "0.1"
|
||||||
|
$PORT = 9128
|
||||||
|
require "proto"
|
||||||
|
require "socket"
|
||||||
|
|
||||||
|
PROTO_CODES = {
|
||||||
|
:SUCCESS => 0,
|
||||||
|
:BADJSON => 1,
|
||||||
|
:NOMETHOD => 2,
|
||||||
|
:INVPARAM => 3,
|
||||||
|
:NOTIMPL => 4
|
||||||
|
}
|
||||||
|
|
||||||
|
PROTO_MESSAGES = {
|
||||||
|
:SUCCESS => "Done",
|
||||||
|
:BADJSON => "Invalid JSON object",
|
||||||
|
:NOMETHOD => "Invalid method",
|
||||||
|
:INVPARAM => "Invalid parameters",
|
||||||
|
:NOTIMPL => "Not implemented"
|
||||||
|
}
|
||||||
|
|
||||||
|
class Server
|
||||||
|
@channelPool
|
||||||
|
@channelListeners
|
||||||
|
@methods
|
||||||
|
def initialize()
|
||||||
|
@channelPool = {}
|
||||||
|
@channelListeners = {}
|
||||||
|
@methods = [
|
||||||
|
"join",
|
||||||
|
"useradd",
|
||||||
|
"userdel",
|
||||||
|
"userinfo",
|
||||||
|
"lusers",
|
||||||
|
"lchannels",
|
||||||
|
"chantop",
|
||||||
|
"chansend",
|
||||||
|
"chanedit"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def send(client,message)
|
||||||
|
client.puts JSON.dump(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def err(client,code)
|
||||||
|
client.puts JSON.dump({
|
||||||
|
:error=>PROTO_MESSAGES[code],
|
||||||
|
:code=>PROTO_CODES[code]
|
||||||
|
})
|
||||||
|
client.close
|
||||||
|
end
|
||||||
|
|
||||||
|
def serve(client)
|
||||||
|
# basic validation steps
|
||||||
|
begin
|
||||||
|
# 1. JSON validation
|
||||||
|
data = client.gets
|
||||||
|
data = JSON.load(data)
|
||||||
|
rescue JSON::ParserError
|
||||||
|
err(client,:BADJSON)
|
||||||
|
end
|
||||||
|
# 2. Method validation
|
||||||
|
method = data[:method]
|
||||||
|
err(client,:NOMETHOD) if not @methods.include?(method)
|
||||||
|
# last but not least: calling the given method
|
||||||
|
if not self.methods.include?(method) then
|
||||||
|
err(client,:NOTIMPL)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
send(client,self.method(method).call(client,data))
|
||||||
|
end
|
||||||
|
|
||||||
|
def join(client,data)
|
||||||
|
chaind = data["chanid"]
|
||||||
|
chanid = rand(89999999)+10000000 if not chanid
|
||||||
|
if not @channelPool[chanid] then
|
||||||
|
@channelPool[chanid] = Heimdall::Channel.new
|
||||||
|
@channelListeners
|
||||||
|
end
|
||||||
|
return {
|
||||||
|
:code=>PROTO_CODE[:SUCCESS],
|
||||||
|
:method=>"join",
|
||||||
|
:chanid=>chanid,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "Starting heimdall v#{$VERSION} server on port #{$PORT}..."
|
||||||
|
server = TCPServer.new 9128
|
||||||
|
loop do
|
||||||
|
Thread.start(server.accept) do |client|
|
||||||
|
puts "Received client connection on #{client.addr}"
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue