Yessiest
2 years ago
commit
cb11018eba
2 changed files with 199 additions and 0 deletions
@ -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 |
Write
Preview
Loading…
Cancel
Save
Reference in new issue