meshchat 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|