basic messaging works

This commit is contained in:
Yessiest 2023-04-30 22:52:32 +04:00
parent edc15522cd
commit 6a73d58574
3 changed files with 268 additions and 28 deletions

77
client.rb Executable file
View File

@ -0,0 +1,77 @@
#!/usr/bin/ruby
require 'readline'
require 'net/http'
require 'json'
require 'uri'
puts "Connecting to server #{ARGV[0]} on port #{ARGV[1]}"
def get(path)
where = URI("http://#{ARGV[0]}:#{ARGV[1]}/#{path}")
JSON.parse(Net::HTTP.get(where))
end
def post(path,data)
where = URI("http://#{ARGV[0]}:#{ARGV[1]}/#{path}")
Net::HTTP.post(where,data.to_json)
end
version = get("version")["version"]
puts "Server reported version: #{version}"
print "Nickname> "
nickname = $stdin.gets.strip
puts "Trying to log in..."
res = get("/user/exists?protocol_id=#{"heimdall-"+nickname}")
puts "Account exists! exiting" if res["exists"]
return if res["exists"]
puts "Creating account..."
test = post("/user/new",{username: nickname, protocol_id: "heimdall-"+nickname})
unless test.kind_of? Net::HTTPOK then
puts "Something went wrong! exiting"
exit
end
puts "Your id is: heimdall-#{nickname}"
Thread.new do
while true do
sleep 1
messages = get("/user/read?protocol_id=#{"heimdall-"+nickname}")
if messages["error"] then
puts "Error: #{messages["error"]}"
next
end
messages["messages"].each { |x|
puts "#{x["from"]} says: #{x["content"]}"
}
end
end
target = nil
at_exit do
post("/user/delete",{
"protocol_id"=> "heimdall-"+nickname
})
end
while buf = Readline.readline("> ", true)
if buf == "/help" then
puts "Commands:"
puts "/help - this message"
puts "/send <protcol_id> - direct messages to somebody"
puts "/exit - quit program"
end
if buf == "/exit" then
post("/user/delete",{
"protocol_id"=> "heimdall-"+nickname
})
exit
end
if target then
post("/user/send", {
"to" => target,
"content" => buf,
"from" => "heimdall-"+nickname
})
end
if buf.match(/^\/send .*$/) then
target = buf.match(/^\/send ([^\s]*)$/)[1]
end
end

View File

@ -1,6 +1,12 @@
UIDS = {} UIDS = {}
module Heimdall module Heimdall
VERSION = "0.1 alpha"
attr_reader :VERSION
class ProtocolError < StandardError
end
class UID class UID
def initialize def initialize
@UID_prefix = "abstract" if not @UID_prefix @UID_prefix = "abstract" if not @UID_prefix
@ -22,34 +28,47 @@ module Heimdall
@users[user.protoid] = user @users[user.protoid] = user
end end
def get(protoid) def get(protoid)
return @users[user.protoid] raise ProtocolError, "user not found" if not @users[protoid]
return @users[protoid]
end
def delete(protoid)
@users.delete protoid
end end
end end
class User < UID class User < UID
def initialize(username, protoid) def initialize(data)
@username = username @username = data["username"]
@protoid = protoid @protoid = data["protocol_id"]
@UID_prefix = "user" @UID_prefix = "user"
@direct = DirectChannel.new
super() super()
end end
attr_reader :username attr_reader :username
attr_reader :protoid attr_reader :protoid
end attr_reader :direct
class Server < UID
def initialize
@UID_prefix = "server"
super()
end
end end
class Channel < UID class Channel < UID
def initialize def initialize
@UID_prefix = "channel" @UID_prefix = "channel"
@messages = MsgStack.new
@read = 0
super() super()
end end
def send(msg)
@messages.push(msg)
@read = @read+1
end
def get(n = 1)
raise Heimdall::ProtocolError, "Invalid number of messages" if n < 0
return @messages.pull(n)
end
def read()
messages = @messages.pull(@read)
@read = 0
return messages
end
end end
class DirectChannel < Channel class DirectChannel < Channel
@ -57,7 +76,6 @@ module Heimdall
@UID_prefix = "dchannel" @UID_prefix = "dchannel"
super() super()
end end
end end
class ServerChannel < Channel class ServerChannel < Channel
@ -68,16 +86,36 @@ module Heimdall
end end
class Message < UID class Message < UID
def initialize def initialize(data)
@content = data["content"]
@from = data["from"]
@to = data["to"]
@UID_prefix = "message" @UID_prefix = "message"
super() super()
end end
def to_struct
return {
"content" => @content,
"from" => @from,
"to" => @to
}
end
attr_reader :content
attr_reader :from
attr_reader :to
end end
class MsgStack < UID class MsgStack < UID
def initialize def initialize
@UID_prefix = "msgstack" @UID_prefix = "msgstack"
@messages = []
super() super()
end end
def push(msg)
@messages.append(msg)
end
def pull(n)
@messages.last n
end
end end
end end

153
server.rb
View File

@ -4,41 +4,166 @@ require "json"
Users = Heimdall::UserCache.new Users = Heimdall::UserCache.new
def require_keys(dict,key_dict) def _require_keys(dict,key_dict)
raise KeyError, "not a dict" unless dict.kind_of? Hash raise KeyError, "not a dict" unless dict.kind_of? Hash
key_dict.each_pair { |k,v| key_dict.each_pair { |k,v|
unless (dict.has_key? k) and (dict[k].kind_of? v) then unless (dict.has_key? k.to_s) and (dict[k.to_s].kind_of? v) then
raise KeyError, "key #{k} of type #{v} required" raise KeyError, "key #{k} of type #{v} required"
end end
} }
end end
def _send_json(res,data,code: 200)
res.body = JSON::fast_generate(data)
res['Content-Type'] = "application/json"
res.status = code
end
def _throw_error(res,error)
_send_json(res,{
error: "#{error}"
},code: 400)
end
def _parse_json(body,key_dict)
data = JSON::Parser.new(body).parse
_require_keys(data,key_dict)
return data
end
server = Hyde::Server.new Port: 8000 do server = Hyde::Server.new Port: 8000 do
path "user" do path "user" do
preprocess do |ctx|
puts ctx.request.query.inspect
puts ctx.request.body.inspect
end
get "exists" do |ctx|
req,res = ctx.request,ctx.response
begin
_require_keys(req.query, {
protocol_id: String
})
_send_json(res,{
exists: (Users.get(req.query["protocol_id"]) != nil)
})
rescue KeyError => keyerror
_throw_error(res,keyerror)
rescue Heimdall::ProtocolError => protoerr
_send_json(res, {
exists: false
})
end
end
post "new" do |ctx| post "new" do |ctx|
req,res = ctx.request,ctx.response req,res = ctx.request,ctx.response
begin begin
data = JSON::Parser.new(req.body).parse data = _parse_json(req.body,{
require_keys(data,{
username: String, username: String,
protocol_id: String protocol_id: String
}) })
Users.add(Heimdall::User.new data[:username], data[:protocol_id]) new_user = Heimdall::User.new(data)
Users.add(new_user)
_send_json(res,{
uid: new_user.UID
})
rescue JSON::ParserError => jsonerror rescue JSON::ParserError => jsonerror
res.body = JSON::fast_generate({ _throw_error(res,jsonerror)
error: "#{jsonerror}"
})
res['Content-Type'] = "application/json"
res.status = 400
rescue KeyError => keyerror rescue KeyError => keyerror
res.body = JSON::fast_generate({ _throw_error(res,keyerror)
error: "#{keyerror}" end
end
post "send" do |ctx|
req,res = ctx.request,ctx.response
begin
data = _parse_json(req.body, {
content: String,
from: String,
to: String
}) })
res['Content-Type'] = "application/json" new_message = Heimdall::Message.new(data)
res.status = 400 Users.get(new_message.to).direct.send(new_message)
_send_json(res,{
uid: new_message.UID
})
rescue JSON::ParserError => jsonerror
_throw_error(res,jsonerror)
rescue KeyError => keyerror
_throw_error(res,keyerror)
rescue Heimdall::ProtocolError => protoerr
_throw_error(res,protoerr)
end
end
get "get" do |ctx|
req,res = ctx.request,ctx.response
begin
_require_keys(req.query,{
n: Integer,
protocol_id: String
})
number = req.query[:n]
id = req.query["protocol_id"]
user = Users.get(id)
messages = user.direct.get(number)
_send_json(res, {
messages: messages.map { |x|
x = x.to_struct
x["username"] = Users.get(x["from"]).username
x
}
})
rescue Heimdall::ProtocolError => protoerr
_throw_error(res,protoerr)
end
end
get "read" do |ctx|
req,res = ctx.request,ctx.response
begin
_require_keys(req.query,{
protocol_id: String
})
id = req.query["protocol_id"]
user = Users.get(id)
messages = user.direct.read
_send_json(res, {
messages: messages.map { |x|
x = x.to_struct
x["username"] = Users.get(x["from"]).username
x
}
})
rescue Heimdall::ProtocolError => protoerr
_throw_error(res,protoerr)
end
end
post "delete" do |ctx|
req,res = ctx.request, ctx.response
begin
data = _parse_json(req.body,{
protocol_id: String
})
id = data["protocol_id"]
_send_json(res, {
uid: Users.get(id).UID
})
Users.delete(id)
rescue Heimdall::ProtocolError => protoerr
_throw_error(res,protoerr)
end end
end end
end end
get "version" do |ctx|
ctx.response.body = "{\"version\":\"#{Heimdall::VERSION}\"}"
ctx.response['Content-Type'] = "application/json"
end
end end
server.start server.start