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.
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