From c151429e85abb7ad2a144a058e1b3912a1f32013 Mon Sep 17 00:00:00 2001 From: Yessiest Date: Sat, 13 May 2023 02:46:42 +0400 Subject: [PATCH] nearing beta release --- client.rb | 86 ++++++++++++-- proto.rb | 92 +++++++++++++- server.rb | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 504 insertions(+), 23 deletions(-) diff --git a/client.rb b/client.rb index b242e95..1cd6d4f 100755 --- a/client.rb +++ b/client.rb @@ -35,7 +35,7 @@ ws = WebSocket::Client::Simple.connect "ws://#{ARGV[0]}:#{ARGV[2]}" ws.on :message do |msg| data = JSON.parse(msg.data) if data.has_key? "websocket" then - uid = data["websocket"] + WEBSOCKET_UID = uid = data["websocket"] response = post("/user/listen",{ websocket: uid, protocol_id: "heimdall-"+nickname @@ -45,6 +45,8 @@ ws.on :message do |msg| end elsif data.has_key? "error" then puts "ERROR: #{data["error"]}" + elsif data.has_key? "room" then + puts "[#{data["room"]["name"]}] #{data["user"]["username"]}: #{data["content"]}" else puts "#{data["user"]["username"]}: #{data["content"]}" end @@ -59,6 +61,7 @@ at_exit do end target = nil +target_type = nil at_exit do post("/user/delete",{ @@ -66,7 +69,7 @@ at_exit do }) end -while buf = Readline.readline("> ", true) +while buf = Readline.readline("", true) if buf == "/help" then puts "Commands:" puts "/help - this message" @@ -74,6 +77,11 @@ while buf = Readline.readline("> ", true) puts "/exit - quit program" puts "/find - find a username by pattern" puts "/find-protoid - find by a protocol id" + puts "/username - set your username (does not change your protocol id" + puts "/create-room - create a room" + puts "/join - join a room" + puts "/leave - leave a room" + puts "/send-room - send messages to a room" next end @@ -86,12 +94,46 @@ while buf = Readline.readline("> ", true) if buf.match(/^\/send .*$/) then target = buf.match(/^\/send ([^\s]*)$/)[1] + target_type = "user" next end + + if buf.match(/^\/send-room .*$/) then + target = buf.match(/^\/send-room ([^\s]*)$/)[1] + target_type = "room" + next + end + + if buf.match(/^\/join .*$/) and defined? WEBSOCKET_UID then + target = buf.match(/^\/join ([^\s]*)$/)[1] + if get("/room/exists?protocol_id=#{target}")["exists"] then + target_type = "room" + post("/room/listen", { + websocket: WEBSOCKET_UID, + protocol_id: target + }) + else + target = nil + end + next + end + + if buf.match(/^\/leave .*$/) and defined? WEBSOCKET_UID then + target = buf.match(/^\/leave ([^\s]*)$/)[1] + if get("/room/exists?protocol_id=#{target}")["exists"] then + post("/room/unlisten",{ + websocket: WEBSOCKET_UID, + protocol_id: target + }) + if target_type == "room" then + target = nil + end + end + end if buf.match(/^\/find .*$/) then uname = (buf.match /^\/find (.*)$/)[1] - users = get("/user/find/by-username?username=#{uname}")["results"] + users = get("/user/find/by-name?username=#{uname}")["results"] puts "Found #{users.length} results: " users.each { |x| puts x[0] } next @@ -105,11 +147,39 @@ while buf = Readline.readline("> ", true) next end - if target then - post("/user/send", { - "to" => target, - "content" => buf, - "from" => "heimdall-"+nickname + if buf.match(/^\/username .*$/) then + uname = (buf.match /^\/username (.*)$/)[1] + post("/user/modify",{ + data: { + username: uname + }, + protocol_id: "heimdall-"+nickname }) + next + end + + if buf.match(/^\/create-room \S+$/) then + name = (buf.match /^\/create-room (\S+)$/)[1] + post("/room/new",{ + protocol_id: "heimdall-room-"+name, + name: name + }) + puts("Room id: heimdall-room-#{name}") + end + + if target and target_type then + if target_type == "user" then + post("/user/send", { + "to" => target, + "content" => buf, + "from" => "heimdall-"+nickname + }) + elsif target_type == "room" then + post("/room/send", { + "to" => target, + "content" => buf, + "from" => "heimdall-"+nickname + }) + end end end diff --git a/proto.rb b/proto.rb index 2e63889..4e3bb23 100644 --- a/proto.rb +++ b/proto.rb @@ -1,9 +1,9 @@ UIDS = {} module Heimdall - VERSION = "0.4 alpha" + VERSION = "0.99 beta" attr_reader :VERSION - + class ProtocolError < StandardError end @@ -24,6 +24,16 @@ module Heimdall def initialize @users = {} end + def sync(data) + data.each { |userdata| + if not @users[userdata["protocol_id"]] then + new_user = User.new userdata + @users[new_user.protoid] = new_user + else + @users[userdata["protocol_id"]].modify(userdata) + end + } + end def add(user) @users[user.protoid] = user end @@ -34,15 +44,55 @@ module Heimdall def search(name,n,&block) @users.select(&block).take(n) end - def search_by_username(name, n = 10) + def search_by_name(name, n = 10) search(name,n) { |k,v| v.username.match? name } end def search_by_protoid(name,n = 10) search(name,n) { |k,v| k.match? name } end + def filter(&block) + @users.filter &block + end def delete(protoid) @users.delete protoid end + def bulk_delete(protoid_list) + protoid_list.each { |x| @users.delete x } + end + end + + class RoomCache < UserCache + def get(protoid) + raise ProtocolError, "room not found" if not @users[protoid] + return @users[protoid] + end + def sync(data) + data.each { |userdata| + if not @users[userdata["protocol_id"]] then + new_user = Room.new userdata + @users[new_user.protoid] = new_user + else + @users[userdata["protocol_id"]].modify(userdata) + end + } + end + end + + class RoomFilter + def initialize(cache, room_protoid) + @cache = cache + @filter = room_protoid + end + def get(protoid) + raise ProtocolError, "user not found" if not _filter[protoid] + return _filter[protoid] + end + private + def _filter + @cache.filter { |k,v| + v.protoid == @filter + } + end end class User < UID @@ -50,7 +100,7 @@ module Heimdall @username = data["username"] @protoid = data["protocol_id"] @UID_prefix = "user" - @direct = DirectChannel.new + @channel = DirectChannel.new super() end def to_card() @@ -59,9 +109,36 @@ module Heimdall "protoid" => @protoid } end + def modify(data) + @username = data["username"] + end attr_reader :username attr_reader :protoid - attr_reader :direct + attr_reader :channel + end + + class Room < UID + def initialize(data) + @name = data["name"] + @protoid = data["protocol_id"] + @UID_prefix = "room" + @channel = RoomChannel.new + @users = {} + super() + end + def to_card() + return { + "name" => @name, + "protoid" => @protoid + } + end + def modify(data) + @name = data["name"] + end + attr_reader :users + attr_reader :username + attr_reader :protoid + attr_reader :channel end class Channel < UID @@ -101,6 +178,11 @@ module Heimdall @UID_prefix = "schannel" super() end + undef read + def get(n = 1) + @read = 0 + super(n) + end end class Message < UID diff --git a/server.rb b/server.rb index dede92e..b02efa8 100644 --- a/server.rb +++ b/server.rb @@ -4,6 +4,8 @@ require 'webrick/websocket' require "json" Users = Heimdall::UserCache.new +Rooms = Heimdall::RoomCache.new + SocketsMap = {} NotifyList = {} @@ -38,16 +40,16 @@ end server = Hyde::Server.new Port: 8000 do path "user" do path "find" do - index ["by-username"] + index ["by-name"] - get "by-username" do |ctx| + get "by-name" do |ctx| req,res = ctx.request,ctx.response begin _require_keys(req.query, { username: String }) _send_json(res, { - "results": Users.search_by_username(req.query['username']) + "results": Users.search_by_name(req.query['username']).map { |x| x[1].to_card } }) rescue KeyError => keyerror _throw_error(res,keyerror) @@ -61,7 +63,7 @@ server = Hyde::Server.new Port: 8000 do protocol_id: String }) _send_json(res, { - "results": Users.search_by_protoid(req.query['protocol_id']) + "results": Users.search_by_protoid(req.query['protocol_id']).map { |x| x[1].to_card; x } }) rescue KeyError => keyerror _throw_error(res,keyerror) @@ -69,6 +71,40 @@ server = Hyde::Server.new Port: 8000 do end end + post "sync" do |ctx| + req,res = ctx.request,ctx.response + begin + data = _parse_json(req.body, { + data: Array + }) + Users.sync(data["data"]) + _send_json(res, { + status: true + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + end + end + + post "bulk-delete" do |ctx| + req,res = ctx.request,ctx.response + begin + data = _parse_json(req.body, { + users: Array + }) + Users.bulk_delete(data["users"]) + _send_json(res, { + status: true + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + end + end + get "exists" do |ctx| req,res = ctx.request,ctx.response begin @@ -104,6 +140,29 @@ server = Hyde::Server.new Port: 8000 do _throw_error(res,jsonerror) rescue KeyError => keyerror _throw_error(res,keyerror) + rescue ProtocolError => protoerr + _throw_error(res,protoerr) + end + end + + post "modify" do |ctx| + req,res = ctx.request,ctx.response + begin + data = _parse_json(req.body, { + data: Hash, + protocol_id: String + }) + user = Users.get(data["protocol_id"]) + user.modify(data["data"]) + _send_json(res, { + status: true + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue ProtocolError => protoerr + _throw_error(res,protoerr) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) end end @@ -124,9 +183,9 @@ server = Hyde::Server.new Port: 8000 do msg["user"] = Users.get(msg["from"]).to_card sock.puts(JSON::fast_generate(msg)) } - user.direct.send_silent(new_message) + user.channel.send_silent(new_message) else - user.direct.send(new_message) + user.channel.send(new_message) end _send_json(res,{ status: true @@ -150,7 +209,7 @@ server = Hyde::Server.new Port: 8000 do number = req.query[:n] id = req.query["protocol_id"] user = Users.get(id) - messages = user.direct.get(number) + messages = user.channel.get(number) _send_json(res, { messages: messages.map { |x| x = x.to_struct @@ -171,7 +230,7 @@ server = Hyde::Server.new Port: 8000 do }) id = req.query["protocol_id"] user = Users.get(id) - messages = user.direct.read + messages = user.channel.read _send_json(res, { messages: messages.map { |x| x = x.to_struct @@ -199,6 +258,28 @@ server = Hyde::Server.new Port: 8000 do }) rescue KeyError => keyerror _throw_error(res,keyerror) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + end + end + + post "unlisten" do |ctx| + req,res = ctx.request, ctx.response + begin + data = _parse_json(req.body, { + websocket: String, + protocol_id: String + }) + uid = Users.get(data["protocol_id"]).UID + raise KeyError, "websocket does not exist" unless SocketsMap.has_key? data["websocket"] + NotfiyList[uid].delete data["websocket"] + _send_json(res, { + status: true + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) end end @@ -217,15 +298,263 @@ server = Hyde::Server.new Port: 8000 do Users.delete(id) rescue Heimdall::ProtocolError => protoerr _throw_error(res,protoerr) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) end end end - + + path "room" do + path "find" do + index ["by-name"] + + get "by-name" do |ctx| + req,res = ctx.request,ctx.response + begin + _require_keys(req.query, { + username: String + }) + _send_json(res, { + "results": Rooms.search_by_name(req.query['username']).map { |x| x[1].to_card } + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + end + end + + get "by-protoid" do |ctx| + req,res = ctx.request,ctx.response + begin + _require_keys(req.query, { + protocol_id: String + }) + _send_json(res, { + "results": Rooms.search_by_protoid(req.query['protocol_id']).map { |x| x[1].to_card; x } + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + end + end + end + + post "new" do |ctx| + req,res = ctx.request,ctx.response + begin + data = _parse_json(req.body, { + name: String, + protocol_id: String + }) + new_room = Heimdall::Room.new(data) + Rooms.add(new_room) + _send_json(res, { + status: true + }) + NotifyList[new_room.UID] = [] + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + end + end + + post "send" do |ctx| + req,res = ctx.request,ctx.response + begin + data = _parse_json(req.body, { + content: String, + from: String, + to: String + }) + new_message = Heimdall::Message.new(data) + room = Rooms.get(new_message.to) + if NotifyList[room.UID].length != 0 then + NotifyList[room.UID].each { |sockid| + sock = SocketsMap[sockid] + msg = new_message.to_struct + msg["user"] = Users.get(msg["from"]).to_card + msg["room"] = room.to_card + sock.puts(JSON::fast_generate(msg)) + } + room.channel.send_silent(new_message) + else + room.channel.send(new_message) + end + _send_json(res,{ + status: true + }) + 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"] + room = Rooms.get(id) + messages = room.channel.get(number) + _send_json(res, { + messages: messages.map { |x| + x = x.to_struct + x["user"] = Users.get(x["from"]).to_card + x["room"] = room.to_card + x + } + }) + rescue Heimdall::ProtocolError => protoerr + _throw_error(res,protoerr) + end + end + + post "listen" do |ctx| + req,res = ctx.request, ctx.response + begin + data = _parse_json(req.body, { + websocket: String, + protocol_id: String + }) + uid = Rooms.get(data["protocol_id"]).UID + raise KeyError, "websocket does not exist" unless SocketsMap.has_key? data["websocket"] + NotifyList[uid].append data["websocket"] + _send_json(res, { + status: true + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + end + end + + post "unlisten" do |ctx| + req,res = ctx.request, ctx.response + begin + data = _parse_json(req.body, { + websocket: String, + protocol_id: String + }) + uid = Rooms.get(data["protocol_id"]).UID + raise KeyError, "websocket does not exist" unless SocketsMap.has_key? data["websocket"] + NotfiyList[uid].delete data["websocket"] + _send_json(res, { + status: true + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + end + end + + post "modify" do |ctx| + req,res = ctx.request,ctx.response + begin + data = _parse_json(req.body, { + data: Hash, + protocol_id: String + }) + room = Rooms.get(data["protocol_id"]) + room.modify(data["data"]) + _send_json(res, { + status: true + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue ProtocolError => protoerr + _throw_error(res,protoerr) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + 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, { + status: true + }) + room = Rooms.get(id) + NotifyList.delete(room.UID) + Rooms.delete(id) + rescue Heimdall::ProtocolError => protoerr + _throw_error(res,protoerr) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + end + end + + post "sync" do |ctx| + req,res = ctx.request,ctx.response + begin + data = _parse_json(req.body, { + data: Array + }) + Rooms.sync(data["data"]) + _send_json(res, { + status: true + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + end + end + + post "bulk-delete" do |ctx| + req,res = ctx.request,ctx.response + begin + data = _parse_json(req.body, { + users: Array + }) + Rooms.bulk_delete(data["users"]) + _send_json(res, { + status: true + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue JSON::ParserError => jsonerror + _throw_error(res,jsonerror) + end + end + + get "exists" do |ctx| + req,res = ctx.request,ctx.response + begin + _require_keys(req.query, { + protocol_id: String + }) + _send_json(res,{ + exists: (Rooms.get(req.query["protocol_id"]) != nil) + }) + rescue KeyError => keyerror + _throw_error(res,keyerror) + rescue Heimdall::ProtocolError => protoerr + _send_json(res, { + exists: false + }) + end + end + + end + get "version" do |ctx| ctx.response.body = "{\"version\":\"#{Heimdall::VERSION}\"}" ctx.response['Content-Type'] = "application/json" end - + end at_exit do