meshchat 0.8.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +39 -11
  3. data/lib/meshchat.rb +48 -42
  4. data/lib/meshchat/configuration.rb +14 -0
  5. data/lib/meshchat/configuration/app_config.rb +63 -0
  6. data/lib/meshchat/configuration/database.rb +41 -0
  7. data/lib/meshchat/{config → configuration}/hash_file.rb +7 -6
  8. data/lib/meshchat/configuration/identity.rb +79 -0
  9. data/lib/meshchat/{config → configuration}/settings.rb +22 -26
  10. data/lib/meshchat/debug.rb +69 -0
  11. data/lib/meshchat/encryption.rb +7 -2
  12. data/lib/meshchat/encryption/aes_rsa.rb +2 -1
  13. data/lib/meshchat/encryption/passthrough.rb +5 -3
  14. data/lib/meshchat/locale/en.yml +14 -0
  15. data/lib/meshchat/models/node.rb +140 -0
  16. data/lib/meshchat/network.rb +19 -0
  17. data/lib/meshchat/network/dispatcher.rb +83 -0
  18. data/lib/meshchat/network/errors.rb +11 -0
  19. data/lib/meshchat/network/incoming.rb +13 -0
  20. data/lib/meshchat/network/incoming/message_decryptor.rb +51 -0
  21. data/lib/meshchat/network/incoming/message_processor.rb +75 -0
  22. data/lib/meshchat/network/incoming/request_processor.rb +30 -0
  23. data/lib/meshchat/network/local.rb +12 -0
  24. data/lib/meshchat/network/local/connection.rb +58 -0
  25. data/lib/meshchat/network/local/server.rb +69 -0
  26. data/lib/meshchat/network/message.rb +34 -0
  27. data/lib/meshchat/network/message/base.rb +139 -0
  28. data/lib/meshchat/network/message/chat.rb +9 -0
  29. data/lib/meshchat/network/message/disconnect.rb +21 -0
  30. data/lib/meshchat/network/message/emote.rb +9 -0
  31. data/lib/meshchat/network/message/factory.rb +80 -0
  32. data/lib/meshchat/network/message/node_list.rb +75 -0
  33. data/lib/meshchat/network/message/node_list_diff.rb +18 -0
  34. data/lib/meshchat/network/message/node_list_hash.rb +32 -0
  35. data/lib/meshchat/network/message/ping.rb +31 -0
  36. data/lib/meshchat/network/message/ping_reply.rb +12 -0
  37. data/lib/meshchat/network/message/whisper.rb +28 -0
  38. data/lib/meshchat/network/remote.rb +13 -0
  39. data/lib/meshchat/network/remote/connection.rb +28 -0
  40. data/lib/meshchat/network/remote/relay.rb +109 -0
  41. data/lib/meshchat/network/remote/relay_pool.rb +52 -0
  42. data/lib/meshchat/ui.rb +13 -0
  43. data/lib/meshchat/ui/cli.rb +48 -0
  44. data/lib/meshchat/ui/cli/base.rb +39 -0
  45. data/lib/meshchat/ui/cli/input_factory.rb +50 -0
  46. data/lib/meshchat/ui/cli/keyboard_line_input.rb +14 -0
  47. data/lib/meshchat/ui/command.rb +51 -0
  48. data/lib/meshchat/ui/command/base.rb +77 -0
  49. data/lib/meshchat/ui/command/bind.rb +47 -0
  50. data/lib/meshchat/ui/command/chat.rb +31 -0
  51. data/lib/meshchat/ui/command/config.rb +37 -0
  52. data/lib/meshchat/ui/command/emote.rb +23 -0
  53. data/lib/meshchat/ui/command/exit.rb +16 -0
  54. data/lib/meshchat/ui/command/help.rb +20 -0
  55. data/lib/meshchat/ui/command/identity.rb +16 -0
  56. data/lib/meshchat/ui/command/import.rb +42 -0
  57. data/lib/meshchat/ui/command/irb.rb +22 -0
  58. data/lib/meshchat/ui/command/offline.rb +23 -0
  59. data/lib/meshchat/ui/command/online.rb +18 -0
  60. data/lib/meshchat/ui/command/ping.rb +65 -0
  61. data/lib/meshchat/ui/command/ping_all.rb +19 -0
  62. data/lib/meshchat/ui/command/send_disconnect.rb +20 -0
  63. data/lib/meshchat/ui/command/server.rb +22 -0
  64. data/lib/meshchat/ui/command/share.rb +16 -0
  65. data/lib/meshchat/ui/command/whisper.rb +40 -0
  66. data/lib/meshchat/ui/display.rb +78 -0
  67. data/lib/meshchat/ui/display/base.rb +58 -0
  68. data/lib/meshchat/ui/display/manager.rb +59 -0
  69. data/lib/meshchat/ui/notifier.rb +9 -0
  70. data/lib/meshchat/ui/notifier/base.rb +33 -0
  71. data/lib/meshchat/version.rb +3 -2
  72. metadata +150 -80
  73. data/lib/meshchat/cli.rb +0 -188
  74. data/lib/meshchat/cli/base.rb +0 -13
  75. data/lib/meshchat/cli/input.rb +0 -37
  76. data/lib/meshchat/command/base.rb +0 -80
  77. data/lib/meshchat/command/bind.rb +0 -44
  78. data/lib/meshchat/command/chat.rb +0 -30
  79. data/lib/meshchat/command/config.rb +0 -34
  80. data/lib/meshchat/command/emote.rb +0 -20
  81. data/lib/meshchat/command/exit.rb +0 -13
  82. data/lib/meshchat/command/help.rb +0 -17
  83. data/lib/meshchat/command/identity.rb +0 -13
  84. data/lib/meshchat/command/import.rb +0 -41
  85. data/lib/meshchat/command/init.rb +0 -34
  86. data/lib/meshchat/command/irb.rb +0 -23
  87. data/lib/meshchat/command/listen.rb +0 -13
  88. data/lib/meshchat/command/offline.rb +0 -20
  89. data/lib/meshchat/command/online.rb +0 -15
  90. data/lib/meshchat/command/ping.rb +0 -65
  91. data/lib/meshchat/command/ping_all.rb +0 -15
  92. data/lib/meshchat/command/send_disconnect.rb +0 -15
  93. data/lib/meshchat/command/server.rb +0 -20
  94. data/lib/meshchat/command/share.rb +0 -13
  95. data/lib/meshchat/command/stop_listening.rb +0 -13
  96. data/lib/meshchat/command/whisper.rb +0 -38
  97. data/lib/meshchat/database.rb +0 -30
  98. data/lib/meshchat/display.rb +0 -33
  99. data/lib/meshchat/display/base.rb +0 -60
  100. data/lib/meshchat/display/manager.rb +0 -55
  101. data/lib/meshchat/instance.rb +0 -40
  102. data/lib/meshchat/message.rb +0 -41
  103. data/lib/meshchat/message/base.rb +0 -97
  104. data/lib/meshchat/message/chat.rb +0 -19
  105. data/lib/meshchat/message/disconnect.rb +0 -13
  106. data/lib/meshchat/message/emote.rb +0 -9
  107. data/lib/meshchat/message/node_list.rb +0 -63
  108. data/lib/meshchat/message/node_list_diff.rb +0 -15
  109. data/lib/meshchat/message/node_list_hash.rb +0 -33
  110. data/lib/meshchat/message/ping.rb +0 -32
  111. data/lib/meshchat/message/ping_reply.rb +0 -9
  112. data/lib/meshchat/message/relay.rb +0 -43
  113. data/lib/meshchat/message/whisper.rb +0 -36
  114. data/lib/meshchat/models/entry.rb +0 -104
  115. data/lib/meshchat/net/client.rb +0 -83
  116. data/lib/meshchat/net/listener/errors.rb +0 -11
  117. data/lib/meshchat/net/listener/request.rb +0 -48
  118. data/lib/meshchat/net/listener/request_processor.rb +0 -50
  119. data/lib/meshchat/net/listener/server.rb +0 -114
  120. data/lib/meshchat/net/request.rb +0 -29
  121. data/lib/meshchat/notifier/base.rb +0 -31
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Network
4
+ module Message
5
+ class Emote < Chat
6
+ end
7
+ end
8
+ end
9
+ end
@@ -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,12 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Network
4
+ module Message
5
+ class PingReply < Base
6
+ def display
7
+ 'ping successful' if APP_CONFIG.user.debug?
8
+ end
9
+ end
10
+ end
11
+ end
12
+ 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,13 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Network
4
+ module Remote
5
+ extend ActiveSupport::Autoload
6
+ eager_autoload do
7
+ autoload :Connection
8
+ autoload :RelayPool
9
+ autoload :Relay
10
+ end
11
+ end
12
+ end
13
+ 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