meshchat 0.8.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +39 -11
- data/lib/meshchat.rb +48 -42
- data/lib/meshchat/configuration.rb +14 -0
- data/lib/meshchat/configuration/app_config.rb +63 -0
- data/lib/meshchat/configuration/database.rb +41 -0
- data/lib/meshchat/{config → configuration}/hash_file.rb +7 -6
- data/lib/meshchat/configuration/identity.rb +79 -0
- data/lib/meshchat/{config → configuration}/settings.rb +22 -26
- data/lib/meshchat/debug.rb +69 -0
- data/lib/meshchat/encryption.rb +7 -2
- data/lib/meshchat/encryption/aes_rsa.rb +2 -1
- data/lib/meshchat/encryption/passthrough.rb +5 -3
- data/lib/meshchat/locale/en.yml +14 -0
- data/lib/meshchat/models/node.rb +140 -0
- data/lib/meshchat/network.rb +19 -0
- data/lib/meshchat/network/dispatcher.rb +83 -0
- data/lib/meshchat/network/errors.rb +11 -0
- data/lib/meshchat/network/incoming.rb +13 -0
- data/lib/meshchat/network/incoming/message_decryptor.rb +51 -0
- data/lib/meshchat/network/incoming/message_processor.rb +75 -0
- data/lib/meshchat/network/incoming/request_processor.rb +30 -0
- data/lib/meshchat/network/local.rb +12 -0
- data/lib/meshchat/network/local/connection.rb +58 -0
- data/lib/meshchat/network/local/server.rb +69 -0
- data/lib/meshchat/network/message.rb +34 -0
- data/lib/meshchat/network/message/base.rb +139 -0
- data/lib/meshchat/network/message/chat.rb +9 -0
- data/lib/meshchat/network/message/disconnect.rb +21 -0
- data/lib/meshchat/network/message/emote.rb +9 -0
- data/lib/meshchat/network/message/factory.rb +80 -0
- data/lib/meshchat/network/message/node_list.rb +75 -0
- data/lib/meshchat/network/message/node_list_diff.rb +18 -0
- data/lib/meshchat/network/message/node_list_hash.rb +32 -0
- data/lib/meshchat/network/message/ping.rb +31 -0
- data/lib/meshchat/network/message/ping_reply.rb +12 -0
- data/lib/meshchat/network/message/whisper.rb +28 -0
- data/lib/meshchat/network/remote.rb +13 -0
- data/lib/meshchat/network/remote/connection.rb +28 -0
- data/lib/meshchat/network/remote/relay.rb +109 -0
- data/lib/meshchat/network/remote/relay_pool.rb +52 -0
- data/lib/meshchat/ui.rb +13 -0
- data/lib/meshchat/ui/cli.rb +48 -0
- data/lib/meshchat/ui/cli/base.rb +39 -0
- data/lib/meshchat/ui/cli/input_factory.rb +50 -0
- data/lib/meshchat/ui/cli/keyboard_line_input.rb +14 -0
- data/lib/meshchat/ui/command.rb +51 -0
- data/lib/meshchat/ui/command/base.rb +77 -0
- data/lib/meshchat/ui/command/bind.rb +47 -0
- data/lib/meshchat/ui/command/chat.rb +31 -0
- data/lib/meshchat/ui/command/config.rb +37 -0
- data/lib/meshchat/ui/command/emote.rb +23 -0
- data/lib/meshchat/ui/command/exit.rb +16 -0
- data/lib/meshchat/ui/command/help.rb +20 -0
- data/lib/meshchat/ui/command/identity.rb +16 -0
- data/lib/meshchat/ui/command/import.rb +42 -0
- data/lib/meshchat/ui/command/irb.rb +22 -0
- data/lib/meshchat/ui/command/offline.rb +23 -0
- data/lib/meshchat/ui/command/online.rb +18 -0
- data/lib/meshchat/ui/command/ping.rb +65 -0
- data/lib/meshchat/ui/command/ping_all.rb +19 -0
- data/lib/meshchat/ui/command/send_disconnect.rb +20 -0
- data/lib/meshchat/ui/command/server.rb +22 -0
- data/lib/meshchat/ui/command/share.rb +16 -0
- data/lib/meshchat/ui/command/whisper.rb +40 -0
- data/lib/meshchat/ui/display.rb +78 -0
- data/lib/meshchat/ui/display/base.rb +58 -0
- data/lib/meshchat/ui/display/manager.rb +59 -0
- data/lib/meshchat/ui/notifier.rb +9 -0
- data/lib/meshchat/ui/notifier/base.rb +33 -0
- data/lib/meshchat/version.rb +3 -2
- metadata +150 -80
- data/lib/meshchat/cli.rb +0 -188
- data/lib/meshchat/cli/base.rb +0 -13
- data/lib/meshchat/cli/input.rb +0 -37
- data/lib/meshchat/command/base.rb +0 -80
- data/lib/meshchat/command/bind.rb +0 -44
- data/lib/meshchat/command/chat.rb +0 -30
- data/lib/meshchat/command/config.rb +0 -34
- data/lib/meshchat/command/emote.rb +0 -20
- data/lib/meshchat/command/exit.rb +0 -13
- data/lib/meshchat/command/help.rb +0 -17
- data/lib/meshchat/command/identity.rb +0 -13
- data/lib/meshchat/command/import.rb +0 -41
- data/lib/meshchat/command/init.rb +0 -34
- data/lib/meshchat/command/irb.rb +0 -23
- data/lib/meshchat/command/listen.rb +0 -13
- data/lib/meshchat/command/offline.rb +0 -20
- data/lib/meshchat/command/online.rb +0 -15
- data/lib/meshchat/command/ping.rb +0 -65
- data/lib/meshchat/command/ping_all.rb +0 -15
- data/lib/meshchat/command/send_disconnect.rb +0 -15
- data/lib/meshchat/command/server.rb +0 -20
- data/lib/meshchat/command/share.rb +0 -13
- data/lib/meshchat/command/stop_listening.rb +0 -13
- data/lib/meshchat/command/whisper.rb +0 -38
- data/lib/meshchat/database.rb +0 -30
- data/lib/meshchat/display.rb +0 -33
- data/lib/meshchat/display/base.rb +0 -60
- data/lib/meshchat/display/manager.rb +0 -55
- data/lib/meshchat/instance.rb +0 -40
- data/lib/meshchat/message.rb +0 -41
- data/lib/meshchat/message/base.rb +0 -97
- data/lib/meshchat/message/chat.rb +0 -19
- data/lib/meshchat/message/disconnect.rb +0 -13
- data/lib/meshchat/message/emote.rb +0 -9
- data/lib/meshchat/message/node_list.rb +0 -63
- data/lib/meshchat/message/node_list_diff.rb +0 -15
- data/lib/meshchat/message/node_list_hash.rb +0 -33
- data/lib/meshchat/message/ping.rb +0 -32
- data/lib/meshchat/message/ping_reply.rb +0 -9
- data/lib/meshchat/message/relay.rb +0 -43
- data/lib/meshchat/message/whisper.rb +0 -36
- data/lib/meshchat/models/entry.rb +0 -104
- data/lib/meshchat/net/client.rb +0 -83
- data/lib/meshchat/net/listener/errors.rb +0 -11
- data/lib/meshchat/net/listener/request.rb +0 -48
- data/lib/meshchat/net/listener/request_processor.rb +0 -50
- data/lib/meshchat/net/listener/server.rb +0 -114
- data/lib/meshchat/net/request.rb +0 -29
- data/lib/meshchat/notifier/base.rb +0 -31
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Meshchat
|
3
|
+
module Network
|
4
|
+
module Message
|
5
|
+
class Factory
|
6
|
+
# The left side of this map is all the
|
7
|
+
# lowercase un-underscored variant of
|
8
|
+
# the constant name
|
9
|
+
#
|
10
|
+
# e.g.: NODE_LIST == 'nodelist'
|
11
|
+
TYPES = {
|
12
|
+
CHAT => Chat,
|
13
|
+
EMOTE => Emote,
|
14
|
+
WHISPER => Whisper,
|
15
|
+
DISCONNECT => Disconnect,
|
16
|
+
PING => Ping,
|
17
|
+
PING_REPLY => PingReply,
|
18
|
+
NODE_LIST => NodeList,
|
19
|
+
NODE_LIST_DIFF => NodeListDiff,
|
20
|
+
NODE_LIST_HASH => NodeListHash
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
# the message dispatcher that is responsible for
|
24
|
+
# dispatching messages across either network
|
25
|
+
attr_accessor :_dispatcher
|
26
|
+
attr_accessor :_common_parameters
|
27
|
+
|
28
|
+
def initialize(dispatcher)
|
29
|
+
@_dispatcher = dispatcher
|
30
|
+
@_common_parameters = {
|
31
|
+
message_dispatcher: _dispatcher,
|
32
|
+
message_factory: self
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
# If data contains the payload key, we are receiving the message.
|
37
|
+
# If data does not caine the payload key, we are buliding the message
|
38
|
+
# to send
|
39
|
+
def create(type = '', data: {})
|
40
|
+
return Debug.message_type_not_found(type + 'not found') if type.blank?
|
41
|
+
data = data.deep_symbolize_keys
|
42
|
+
|
43
|
+
parameters = parameters_for(data)
|
44
|
+
klass = TYPES[type]
|
45
|
+
raise Errors::MessageTypeNotRecognized unless klass
|
46
|
+
klass.new(parameters)
|
47
|
+
end
|
48
|
+
|
49
|
+
def parameters_for(data)
|
50
|
+
if is_receiving?(data)
|
51
|
+
receiving_parameters_for(data)
|
52
|
+
else
|
53
|
+
sending_parameters_for(data)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def is_receiving?(data)
|
58
|
+
data[:payload].present?
|
59
|
+
end
|
60
|
+
|
61
|
+
# ensures a payload exists, as well as assigns the
|
62
|
+
# message dispatcher and message factory
|
63
|
+
def receiving_parameters_for(data)
|
64
|
+
{ payload: data[:payload] }.merge(_common_parameters)
|
65
|
+
end
|
66
|
+
|
67
|
+
def sending_parameters_for(data)
|
68
|
+
data.merge(
|
69
|
+
message: data[:message],
|
70
|
+
sender: {
|
71
|
+
'alias' => APP_CONFIG.user['alias'],
|
72
|
+
'location' => APP_CONFIG.user.location,
|
73
|
+
'uid' => APP_CONFIG.user['uid']
|
74
|
+
}
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Meshchat
|
3
|
+
module Network
|
4
|
+
module Message
|
5
|
+
class NodeList < Base
|
6
|
+
def message
|
7
|
+
@message ||= Node.as_json
|
8
|
+
end
|
9
|
+
|
10
|
+
def handle
|
11
|
+
respond
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
# only need to respond if this server has node entries that the
|
16
|
+
# sender of this message doesn't have
|
17
|
+
def respond
|
18
|
+
received_list = message
|
19
|
+
we_only_have, they_only_have = Node.diff(received_list)
|
20
|
+
|
21
|
+
Display.debug('node_list#respond: me: ' + we_only_have.to_s)
|
22
|
+
Display.debug('node_list#respond: they: ' + they_only_have.to_s)
|
23
|
+
|
24
|
+
if they_only_have.present?
|
25
|
+
they_only_have.each do |n|
|
26
|
+
Node.from_json(n).save!
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
uid = payload['sender']['uid']
|
31
|
+
|
32
|
+
if we_only_have.present?
|
33
|
+
respond_with_what_we_have(we_only_have, they_only_have, uid)
|
34
|
+
else
|
35
|
+
respond_with_confirmation_of_in_sync(uid)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def respond_with_confirmation_of_in_sync(uid)
|
40
|
+
Display.debug 'node lists are in sync'
|
41
|
+
nlh_message = _message_factory.create(NODE_LIST_HASH)
|
42
|
+
# lists are in sync, confirm with hash
|
43
|
+
_message_dispatcher.send_message(uid: uid, message: nlh_message)
|
44
|
+
end
|
45
|
+
|
46
|
+
def respond_with_what_we_have(we_only_have, they_only_have, uid)
|
47
|
+
Display.debug 'we have nodes that they do not'
|
48
|
+
|
49
|
+
we_only_have_message = _message_factory.create(
|
50
|
+
NODE_LIST_DIFF,
|
51
|
+
data: { message: we_only_have })
|
52
|
+
|
53
|
+
they_only_have_message = _message_factory.create(
|
54
|
+
NODE_LIST_DIFF,
|
55
|
+
data: { message: they_only_have })
|
56
|
+
|
57
|
+
# give the sender our list
|
58
|
+
_message_dispatcher.send_message(
|
59
|
+
uid: uid,
|
60
|
+
message: we_only_have_message
|
61
|
+
)
|
62
|
+
|
63
|
+
# give people we know about
|
64
|
+
# (but the sender of the Node List may not know about)
|
65
|
+
# our node list diff
|
66
|
+
Node.online.each do |node|
|
67
|
+
_message_dispatcher.send_message(
|
68
|
+
node: node,
|
69
|
+
message: they_only_have_message)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Meshchat
|
3
|
+
module Network
|
4
|
+
module Message
|
5
|
+
class NodeListDiff < Base
|
6
|
+
def handle
|
7
|
+
entries_we_do_not_have = message
|
8
|
+
|
9
|
+
entries_we_do_not_have.each do |entry_as_json|
|
10
|
+
# this will silently fail if there is a duplicate
|
11
|
+
# or if this is an invalid entry
|
12
|
+
Node.from_json(entry_as_json).save
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Meshchat
|
3
|
+
module Network
|
4
|
+
module Message
|
5
|
+
class NodeListHash < Base
|
6
|
+
def message
|
7
|
+
@_message ||= Node.as_sha512
|
8
|
+
end
|
9
|
+
|
10
|
+
# node list hash is received
|
11
|
+
# @return [NilClass] no output for this message type
|
12
|
+
def handle
|
13
|
+
respond
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def respond
|
18
|
+
if message != Node.as_sha512
|
19
|
+
Display.debug 'node list hashes do not match'
|
20
|
+
|
21
|
+
_message_dispatcher.send_message(
|
22
|
+
uid: payload['sender']['uid'],
|
23
|
+
message: NodeList.new(message: Node.as_json)
|
24
|
+
)
|
25
|
+
else
|
26
|
+
Display.debug 'node list hash matches'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Meshchat
|
3
|
+
module Network
|
4
|
+
module Message
|
5
|
+
class Ping < Base
|
6
|
+
def display
|
7
|
+
# we'll never display our own ping to someone else...
|
8
|
+
# or shouldn't.... or there should be different output
|
9
|
+
# TODO: display is a bad method name
|
10
|
+
name = payload['sender']['alias']
|
11
|
+
location = payload['sender']['location']
|
12
|
+
|
13
|
+
"#{name}@#{location} pinged you."
|
14
|
+
end
|
15
|
+
|
16
|
+
def handle
|
17
|
+
respond
|
18
|
+
display
|
19
|
+
end
|
20
|
+
|
21
|
+
def respond
|
22
|
+
reply = _message_factory.create(PING_REPLY)
|
23
|
+
_message_dispatcher.send_message(
|
24
|
+
uid: payload['sender']['uid'],
|
25
|
+
message: reply
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Meshchat
|
3
|
+
module Network
|
4
|
+
module Message
|
5
|
+
class Whisper < Base
|
6
|
+
attr_accessor :_to
|
7
|
+
|
8
|
+
def initialize(
|
9
|
+
message: nil,
|
10
|
+
sender: {},
|
11
|
+
payload: {},
|
12
|
+
to: '',
|
13
|
+
message_dispatcher: nil,
|
14
|
+
message_factory: nil)
|
15
|
+
|
16
|
+
super(
|
17
|
+
message: message,
|
18
|
+
sender: sender,
|
19
|
+
payload: payload,
|
20
|
+
message_dispatcher: message_dispatcher,
|
21
|
+
message_factory: message_factory)
|
22
|
+
|
23
|
+
@_to = to
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Meshchat
|
3
|
+
module Network
|
4
|
+
module Remote
|
5
|
+
class Connection
|
6
|
+
attr_reader :_message_factory, :_message_dispatcher
|
7
|
+
attr_reader :_relay_pool
|
8
|
+
|
9
|
+
def initialize(dispatcher, message_factory)
|
10
|
+
@_message_factory = message_factory
|
11
|
+
@_message_dispatcher = dispatcher
|
12
|
+
@_relay_pool = RelayPool.new(dispatcher)
|
13
|
+
end
|
14
|
+
|
15
|
+
def send_message(node, encrypted_message)
|
16
|
+
payload = payload_for(node.uid, encrypted_message)
|
17
|
+
_relay_pool.send_payload(payload)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [String] to - the uid of the person we are sending to
|
21
|
+
# @param [String] message - the encrypted message
|
22
|
+
def payload_for(to, encrypted_message)
|
23
|
+
{ to: to, message: encrypted_message }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'action_cable_client'
|
3
|
+
|
4
|
+
module Meshchat
|
5
|
+
module Network
|
6
|
+
module Remote
|
7
|
+
class Relay
|
8
|
+
# This channel is determine by the server, see
|
9
|
+
# https://github.com/NullVoxPopuli/mesh-relay/blob/master/app/channels/mesh_relay_channel.rb
|
10
|
+
CHANNEL = 'MeshRelayChannel'
|
11
|
+
|
12
|
+
attr_reader :_url, :_client, :_request_processor
|
13
|
+
attr_accessor :_connected
|
14
|
+
delegate :perform, to: :_client
|
15
|
+
|
16
|
+
def initialize(url, message_dispatcher, connected: nil)
|
17
|
+
@_url = url
|
18
|
+
@_request_processor = Incoming::RequestProcessor.new(
|
19
|
+
network: NETWORK_RELAY,
|
20
|
+
location: url,
|
21
|
+
message_dispatcher: message_dispatcher)
|
22
|
+
|
23
|
+
setup(connected: connected)
|
24
|
+
end
|
25
|
+
|
26
|
+
def setup(connected: nil)
|
27
|
+
path = "#{_url}?uid=#{APP_CONFIG.user['uid']}"
|
28
|
+
@_client = ActionCableClient.new(path, CHANNEL)
|
29
|
+
|
30
|
+
# don't output anything upon connecting
|
31
|
+
_client.connected {
|
32
|
+
self._connected = true
|
33
|
+
}
|
34
|
+
|
35
|
+
# If there are errors, report them!
|
36
|
+
_client.errored do |message|
|
37
|
+
process_error(message)
|
38
|
+
end
|
39
|
+
|
40
|
+
_client.subscribed do
|
41
|
+
connected.call if connected
|
42
|
+
end
|
43
|
+
|
44
|
+
# forward the encrypted messages to our RequestProcessor
|
45
|
+
# so that they can be decrypted
|
46
|
+
_client.received do |message|
|
47
|
+
process_message(message)
|
48
|
+
end
|
49
|
+
|
50
|
+
_client.disconnected do
|
51
|
+
self._connected = false
|
52
|
+
end
|
53
|
+
|
54
|
+
_client
|
55
|
+
end
|
56
|
+
|
57
|
+
def connected?
|
58
|
+
_connected
|
59
|
+
end
|
60
|
+
|
61
|
+
# example messages:
|
62
|
+
# {"identifier"=>"{\"channel\":\"MeshRelayChannel\"}", "message"=>{"message"=>"hi"}}
|
63
|
+
# {"identifier"=>"{\"channel\":\"MeshRelayChannel\"}", "message"=>{"error"=>"hi"}}
|
64
|
+
# {"identifier"=>"{\"channel\":\"MeshRelayChannel\"}", "type"=>"confirm_subscription"}
|
65
|
+
# {"identifier"=>"{\"channel\":\"MeshRelayChannel\"}", "message"=>{"error"=>"Member with UID user2 could not be found"}}
|
66
|
+
def process_message(message)
|
67
|
+
Debug.received_message_from_relay(message, _url)
|
68
|
+
|
69
|
+
_, type, message = message.values_at('identifier', 'type', 'message')
|
70
|
+
|
71
|
+
# do we want to do anything here?
|
72
|
+
return if type == 'confirm_subscription'
|
73
|
+
# are there any other types of websocket messages?
|
74
|
+
return unless message
|
75
|
+
|
76
|
+
if message['message']
|
77
|
+
chat_message_received(message)
|
78
|
+
elsif message['error']
|
79
|
+
error_message_received(message)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# TODO: what does an error message look like?
|
84
|
+
# TODO: what are situations in which we receive an error message?
|
85
|
+
def process_error(message)
|
86
|
+
Display.alert(message)
|
87
|
+
end
|
88
|
+
|
89
|
+
def chat_message_received(message)
|
90
|
+
_request_processor.process(message)
|
91
|
+
rescue => e
|
92
|
+
ap e.message
|
93
|
+
puts e.backtrace
|
94
|
+
end
|
95
|
+
|
96
|
+
def error_message_received(message)
|
97
|
+
Display.info message['error']
|
98
|
+
if message['status'] == 404
|
99
|
+
# mark the node as offline via relay
|
100
|
+
# TODO: find the intended node.
|
101
|
+
# if on_local_network is true, send to http_client
|
102
|
+
# Display.info "#{node.alias_name} has ventured offline"
|
103
|
+
# Debug.person_not_online(node, message, e)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|