Compare commits

...

3 Commits

Author SHA1 Message Date
Yessiest 25647e3e95 starting over 2023-04-30 11:56:21 +04:00
Yessiest 3e656163f9 now we're cooking with gasoline 2023-04-30 09:52:39 +04:00
Yessiest c1e2162ce2 starting everything from scratch 2023-04-30 09:48:12 +04:00
6 changed files with 157 additions and 189 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "hyde"]
path = hyde
url = /hyde

14
.rubocop.yml Normal file
View File

@ -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

22
.solargraph.yml Normal file
View File

@ -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

1
hyde Submodule

@ -0,0 +1 @@
Subproject commit fd76422b760e6c045581bcb11842ee42221d06e0

148
proto.rb
View File

@ -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
View File

@ -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