Compare commits
3 Commits
cb11018eba
...
25647e3e95
Author | SHA1 | Date |
---|---|---|
Yessiest | 25647e3e95 | |
Yessiest | 3e656163f9 | |
Yessiest | c1e2162ce2 |
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "hyde"]
|
||||||
|
path = hyde
|
||||||
|
url = /hyde
|
|
@ -0,0 +1,14 @@
|
||||||
|
# The behavior of RuboCop can be controlled via the .rubocop.yml
|
||||||
|
# configuration file. It makes it possible to enable/disable
|
||||||
|
# certain cops (checks) and to alter their behavior if they accept
|
||||||
|
# any parameters. The file can be placed either in your home
|
||||||
|
# directory or in some project directory.
|
||||||
|
#
|
||||||
|
# RuboCop will start looking for the configuration file in the directory
|
||||||
|
# where the inspected file is and continue its way up to the root directory.
|
||||||
|
#
|
||||||
|
# See https://docs.rubocop.org/rubocop/configuration
|
||||||
|
require: standard
|
||||||
|
|
||||||
|
inherit_gem:
|
||||||
|
standard: config/base.yml
|
|
@ -0,0 +1,22 @@
|
||||||
|
---
|
||||||
|
include:
|
||||||
|
- "**/*.rb"
|
||||||
|
exclude:
|
||||||
|
- spec/**/*
|
||||||
|
- test/**/*
|
||||||
|
- vendor/**/*
|
||||||
|
- ".bundle/**/*"
|
||||||
|
require: []
|
||||||
|
domains: []
|
||||||
|
reporters:
|
||||||
|
- rubocop
|
||||||
|
- require_not_found
|
||||||
|
formatter:
|
||||||
|
rubocop:
|
||||||
|
cops: safe
|
||||||
|
except: []
|
||||||
|
only: []
|
||||||
|
extra_args: []
|
||||||
|
require_paths: []
|
||||||
|
plugins: []
|
||||||
|
max_files: 5000
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit fd76422b760e6c045581bcb11842ee42221d06e0
|
148
proto.rb
148
proto.rb
|
@ -1,101 +1,83 @@
|
||||||
require "date"
|
UIDS = {}
|
||||||
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
|
module Heimdall
|
||||||
# Core protocol
|
class UID
|
||||||
class NetEntity
|
def initialize
|
||||||
# Abstraction that stores basic object properties
|
@UID_prefix = "abstract" if not @UID_prefix
|
||||||
@createdAt
|
id = (1..32).map { |x| (rand()*10).floor }.join
|
||||||
def initialize()
|
while UIDS.has_key? id do
|
||||||
@createdAt = DateTime.new
|
id = (1..32).map { |x| (rand()*10).floor }.join
|
||||||
end
|
end
|
||||||
attr_reader :createdAt
|
UIDS[@UID_prefix+id] = self
|
||||||
|
@UID = id
|
||||||
|
end
|
||||||
|
attr_reader :UID
|
||||||
end
|
end
|
||||||
|
|
||||||
class User < NetEntity
|
class UserCache
|
||||||
# User abstraction (not bound to a group chat)
|
def initialize
|
||||||
@username
|
@users = {}
|
||||||
@nickname
|
end
|
||||||
def initialize(username,nickname = nil)
|
def add(user)
|
||||||
validate(
|
@users[user.protoid] = user
|
||||||
username, String,
|
end
|
||||||
nickname, [String,NilClass]
|
def get(protoid)
|
||||||
)
|
return @users[user.protoid]
|
||||||
super()
|
end
|
||||||
nickname = username if not nickname
|
end
|
||||||
|
|
||||||
|
class User < UID
|
||||||
|
def initialize(username, protoid)
|
||||||
@username = username
|
@username = username
|
||||||
@nickname = nickname
|
@protoid = protoid
|
||||||
|
@UID_prefix = "user"
|
||||||
|
super()
|
||||||
end
|
end
|
||||||
attr_reader :username
|
attr_reader :username
|
||||||
attr_accessor :nickname
|
attr_reader :protoid
|
||||||
end
|
end
|
||||||
|
|
||||||
class Channel < NetEntity
|
class Server < UID
|
||||||
# Channel abstraction (not bound to a group)
|
def initialize
|
||||||
# Practically acts as a message stack
|
@UID_prefix = "server"
|
||||||
# Read access to all elements
|
|
||||||
# Write access only to top
|
|
||||||
@messages
|
|
||||||
def initialize()
|
|
||||||
validate(
|
|
||||||
id, String
|
|
||||||
)
|
|
||||||
super()
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
class Message < NetEntity
|
class Channel < UID
|
||||||
# Message abstraction with edits and content history (not bound to a group)
|
def initialize
|
||||||
@content
|
@UID_prefix = "channel"
|
||||||
@author
|
|
||||||
@editedAt
|
|
||||||
@contentHist
|
|
||||||
def initialize(content,author,channel)
|
|
||||||
validate(
|
|
||||||
content, String,
|
|
||||||
author, Heimdall::User,
|
|
||||||
channel, Heimdall::Channel
|
|
||||||
)
|
|
||||||
super()
|
super()
|
||||||
@editedAt = DateTime.new
|
|
||||||
@contentHist = [content]
|
|
||||||
@content = content
|
|
||||||
@author = author
|
|
||||||
end
|
end
|
||||||
def edit(content)
|
|
||||||
@content = content
|
end
|
||||||
@editedAt = DateTime.new
|
|
||||||
@contentHist.append(content)
|
class DirectChannel < Channel
|
||||||
|
def initialize
|
||||||
|
@UID_prefix = "dchannel"
|
||||||
|
super()
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class ServerChannel < Channel
|
||||||
|
def initialize
|
||||||
|
@UID_prefix = "schannel"
|
||||||
|
super()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Message < UID
|
||||||
|
def initialize
|
||||||
|
@UID_prefix = "message"
|
||||||
|
super()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class MsgStack < UID
|
||||||
|
def initialize
|
||||||
|
@UID_prefix = "msgstack"
|
||||||
|
super()
|
||||||
end
|
end
|
||||||
attr_reader :content
|
|
||||||
attr_reader :author
|
|
||||||
attr_reader :contentHist
|
|
||||||
attr_reader :editedAt
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
122
server.rb
122
server.rb
|
@ -1,98 +1,44 @@
|
||||||
# Prototype of the heimdall server
|
require_relative "proto"
|
||||||
$LOAD_PATH << "."
|
require_relative "hyde/hyde"
|
||||||
$VERSION = "0.1"
|
require "json"
|
||||||
$PORT = 9128
|
|
||||||
require "proto"
|
|
||||||
require "socket"
|
|
||||||
|
|
||||||
PROTO_CODES = {
|
Users = Heimdall::UserCache.new
|
||||||
:SUCCESS => 0,
|
|
||||||
:BADJSON => 1,
|
def require_keys(dict,key_dict)
|
||||||
:NOMETHOD => 2,
|
raise KeyError, "not a dict" unless dict.kind_of? Hash
|
||||||
:INVPARAM => 3,
|
key_dict.each_pair { |k,v|
|
||||||
:NOTIMPL => 4
|
unless (dict.has_key? k) and (dict[k].kind_of? v) then
|
||||||
|
raise KeyError, "key #{k} of type #{v} required"
|
||||||
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
end
|
||||||
|
|
||||||
def send(client,message)
|
server = Hyde::Server.new Port: 8000 do
|
||||||
client.puts JSON.dump(message)
|
path "user" do
|
||||||
end
|
post "new" do |ctx|
|
||||||
|
req,res = ctx.request,ctx.response
|
||||||
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
|
begin
|
||||||
# 1. JSON validation
|
data = JSON::Parser.new(req.body).parse
|
||||||
data = client.gets
|
require_keys(data,{
|
||||||
data = JSON.load(data)
|
username: String,
|
||||||
rescue JSON::ParserError
|
protocol_id: String
|
||||||
err(client,:BADJSON)
|
})
|
||||||
|
Users.add(Heimdall::User.new data[:username], data[:protocol_id])
|
||||||
|
rescue JSON::ParserError => jsonerror
|
||||||
|
res.body = JSON::fast_generate({
|
||||||
|
error: "#{jsonerror}"
|
||||||
|
})
|
||||||
|
res['Content-Type'] = "application/json"
|
||||||
|
res.status = 400
|
||||||
|
rescue KeyError => keyerror
|
||||||
|
res.body = JSON::fast_generate({
|
||||||
|
error: "#{keyerror}"
|
||||||
|
})
|
||||||
|
res['Content-Type'] = "application/json"
|
||||||
|
res.status = 400
|
||||||
end
|
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
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
puts "Starting heimdall v#{$VERSION} server on port #{$PORT}..."
|
server.start
|
||||||
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