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,52 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Network
4
+ module Remote
5
+ class RelayPool
6
+ # This channel is determine by the server, see
7
+ # https://github.com/NullVoxPopuli/mesh-relay/blob/master/app/channels/mesh_relay_channel.rb
8
+ CHANNEL = 'MeshRelayChannel'
9
+
10
+ attr_accessor :_message_dispatcher
11
+ attr_accessor :_active_relay
12
+ attr_accessor :_known_relays, :_available_relays
13
+
14
+ def initialize(message_dispatcher)
15
+ @_message_dispatcher = message_dispatcher
16
+ @_known_relays = APP_CONFIG.user['relays'] || []
17
+ @_available_relays = APP_CONFIG.user['relays'] || []
18
+
19
+ find_initial_relay if @_known_relays.present?
20
+ end
21
+
22
+ # TODO: add logic for just selecting the first available relay.
23
+ # we only need one connection.
24
+ # @return [Array] an array of action cable clients
25
+ def find_initial_relay(connected: nil)
26
+ url = _known_relays.first
27
+ @_active_relay = Relay.new(
28
+ url, _message_dispatcher,
29
+ connected: connected)
30
+ end
31
+
32
+ # @param [Hash] payload - the message payload
33
+ def send_payload(payload)
34
+ return if _active_relay.blank?
35
+
36
+ ensure_active_connection! do
37
+ _active_relay.perform('chat', payload)
38
+ end
39
+ end
40
+
41
+ def ensure_active_connection!(&block)
42
+ # TODO: make action_cable_client return a boolean
43
+ if !_active_relay.connected?
44
+ find_initial_relay(connected: block)
45
+ else
46
+ yield
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Ui
4
+ extend ActiveSupport::Autoload
5
+
6
+ eager_autoload do
7
+ autoload :CLI
8
+ autoload :Command
9
+ autoload :Display
10
+ autoload :Notifier
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Ui
4
+ # A user interface is responsible for for creating a client
5
+ # and sending messages to that client
6
+ class CLI
7
+ extend ActiveSupport::Autoload
8
+
9
+ eager_autoload do
10
+ autoload :InputFactory
11
+ autoload :Base
12
+ autoload :KeyboardLineInput
13
+ end
14
+
15
+ attr_reader :_message_dispatcher, :_message_factory, :_command_factory
16
+
17
+ def initialize(dispatcher, message_factory, _display)
18
+ @_message_dispatcher = dispatcher
19
+ @_message_factory = message_factory
20
+ @_command_factory = InputFactory.new(dispatcher, message_factory, self)
21
+ end
22
+
23
+ def create_input(msg)
24
+ handler = _command_factory.create(for_input: msg)
25
+ handler.handle
26
+ rescue => e
27
+ Debug.creating_input_failed(e)
28
+ end
29
+
30
+ # save config and exit
31
+ def shutdown
32
+ # close_server
33
+ Display.info 'saving config...'
34
+ APP_CONFIG.user.save
35
+ Display.info 'notifying of disconnection...'
36
+ send_disconnect
37
+ Display.alert "\n\nGoodbye. \n\nThank you for using #{Meshchat.name}"
38
+ exit
39
+ end
40
+
41
+ def send_disconnect
42
+ klass = Command::SendDisconnect
43
+ command = _command_factory.create(with_class: klass)
44
+ command.handle
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Ui
4
+ class CLI
5
+ # this class, and all subclasses are Keyboard Handlers
6
+ # that are used for EventMachine's open_keyboard
7
+ # @see https://github.com/eventmachine/eventmachine/wiki/Code-Snippets#keyboard-input-example
8
+ class Base < EventMachine::Connection
9
+ # The class used for interpeting the line input
10
+ attr_reader :_input_receiver
11
+ # An array of typed keystrokes
12
+ attr_reader :_input_buffer
13
+
14
+ def initialize(input_receiver)
15
+ @_input_receiver = input_receiver
16
+ @_input_buffer = []
17
+ end
18
+
19
+ # override this method to alter how input is
20
+ # interpreted by the receiver.
21
+ #
22
+ # hopefully, just by calling a different method
23
+ # on the receiver, as input processing shouldn't
24
+ # occurr in this class or any subclass unless it is
25
+ # raw keystroke input
26
+ #
27
+ # @note that this method receives raw keystrokes by default
28
+ # and does not send data to the input receiver
29
+ def receive_data(data)
30
+ _input_buffer.push(data)
31
+ end
32
+
33
+ def receive_line(line)
34
+ # only used when including EM::Protocols::LineText2
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Ui
4
+ class CLI
5
+ class InputFactory
6
+ WHISPER = '@'
7
+ COMMAND = '/'
8
+
9
+ attr_accessor :_message_dispatcher, :_message_factory, :_cli
10
+
11
+ def initialize(message_dispatcher, message_factory, cli)
12
+ self._message_dispatcher = message_dispatcher
13
+ self._message_factory = message_factory
14
+ self._cli = cli
15
+ end
16
+
17
+ def is_command?(input)
18
+ input[0, 1] == COMMAND
19
+ end
20
+
21
+ def is_whisper?(input)
22
+ input[0, 1] == WHISPER
23
+ end
24
+
25
+ def create(for_input: nil, with_class: nil)
26
+ return create_with_class(for_input, with_class) if with_class
27
+
28
+ create_for_input(for_input)
29
+ end
30
+
31
+ def create_with_class(input, klass)
32
+ klass.new(input, _message_dispatcher, _message_factory, self)
33
+ end
34
+
35
+ def create_for_input(input)
36
+ klass =
37
+ if is_command?(input)
38
+ Command::Base
39
+ elsif is_whisper?(input)
40
+ Command::Whisper
41
+ else
42
+ Command::Chat
43
+ end
44
+
45
+ create_with_class(input, klass)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Ui
4
+ class CLI
5
+ class KeyboardLineInput < CLI::Base
6
+ include EM::Protocols::LineText2
7
+
8
+ def receive_line(data)
9
+ _input_receiver.create_input(data)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Ui
4
+ module Command
5
+ extend ActiveSupport::Autoload
6
+
7
+ eager_autoload do
8
+ autoload :Base
9
+ autoload :Chat
10
+ autoload :Identity
11
+ autoload :Irb
12
+ autoload :Config
13
+ autoload :Ping
14
+ autoload :PingAll
15
+ autoload :Server
16
+ autoload :Whisper
17
+ autoload :Exit
18
+ autoload :SendDisconnect
19
+ autoload :Help
20
+ autoload :Bind
21
+ autoload :Online
22
+ autoload :Offline
23
+ autoload :Share
24
+ autoload :Import
25
+ autoload :Emote
26
+ end
27
+
28
+ COMMAND_MAP = {
29
+ Base::CONFIG => Config,
30
+ Base::PING => Ping,
31
+ Base::PING_ALL => PingAll,
32
+ Base::SERVERS => Server,
33
+ Base::SERVER => Server,
34
+ Base::EXIT => Exit,
35
+ Base::QUIT => Exit,
36
+ Base::IDENTITY => Identity,
37
+ Base::IRB => Irb,
38
+ Base::SHARE => Share,
39
+ Base::IMPORT => Import,
40
+ Base::EXPORT => Share,
41
+ Base::ONLINE => Online,
42
+ Base::OFFLINE => Offline,
43
+ Base::HELP => Help,
44
+ Base::BIND => Bind,
45
+ Base::SEND_DISCONNECT => SendDisconnect,
46
+ Base::EMOTE => Emote,
47
+ Base::CHAT => Chat
48
+ }.freeze
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Ui
4
+ module Command
5
+ class Base
6
+ # Commands
7
+ SET = 'set'
8
+ CONFIG = 'config'
9
+ DISPLAY = 'display'
10
+ EXIT = 'exit'
11
+ QUIT = 'quit'
12
+ CHAT = 'chat'
13
+ ADD = 'add'
14
+ REMOVE = 'remove'
15
+ RM = 'rm'
16
+ SERVERS = 'servers'
17
+ SERVER = 'server'
18
+ WHO = 'who'
19
+ PING = 'ping'
20
+ PING_ALL = 'pingall'
21
+ IDENTITY = 'identity'
22
+ IRB = 'c'
23
+ SHARE = 'share'
24
+ IMPORT = 'import'
25
+ EXPORT = 'export'
26
+ ONLINE = 'online'
27
+ OFFLINE = 'offline'
28
+ HELP = 'help'
29
+ BIND = 'bind'
30
+ SEND_DISCONNECT = 'senddisconnect'
31
+ EMOTE = 'me'
32
+
33
+ attr_reader :_input, :_message_dispatcher
34
+ attr_reader :_message_factory, :_input_factory
35
+
36
+ def initialize(input, message_dispatcher, message_factory, input_factory)
37
+ @_input = input&.chomp
38
+ @_message_dispatcher = message_dispatcher
39
+ @_message_factory = message_factory
40
+ @_input_factory = input_factory
41
+ end
42
+
43
+ def handle
44
+ klass = COMMAND_MAP[command]
45
+ Display.debug("INPUT: #{klass&.name} from #{command} derived from #{_input}")
46
+ if klass
47
+ _input_factory.create(for_input: _input, with_class: klass).handle
48
+ else
49
+ Display.alert 'not implemented...'
50
+ end
51
+ end
52
+
53
+ protected
54
+
55
+ def command_string
56
+ @command_string ||= _input[1, _input.length]
57
+ end
58
+
59
+ def command_args
60
+ @command_args ||= command_string.split(' ')
61
+ end
62
+
63
+ def command
64
+ @command ||= command_args.first
65
+ end
66
+
67
+ def sub_command_args
68
+ @sub_command_args ||= command_args[2..3]
69
+ end
70
+
71
+ def sub_command
72
+ @sub_command ||= command_args[1]
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ require 'socket'
3
+ require 'open-uri'
4
+
5
+ module Meshchat
6
+ module Ui
7
+ module Command
8
+ class Bind < Command::Base
9
+ def self.description
10
+ 'helper for choosing what ip address to use for yourself'
11
+ end
12
+
13
+ def handle
14
+ options = ip_addresses
15
+ option_numbers = []
16
+ Display.success 'Choose an ip:'
17
+ options.each_with_index do |ip, index|
18
+ Display.info "#{index}: #{ip}"
19
+ option_numbers << index
20
+ end
21
+
22
+ selected = gets
23
+
24
+ if option_numbers.include?(selected.to_i)
25
+ Display.success Settings.set('ip', with: options[selected.to_i])
26
+ else
27
+ Display.alert 'Invalid selection'
28
+ handle # repeat
29
+ end
30
+ end
31
+
32
+ def ip_addresses
33
+ local = Socket.getifaddrs.map { |i| i.addr.ip_address if i.addr.ipv4? }.compact
34
+ # get public
35
+ begin
36
+ remote_ip = open('http://whatismyip.akamai.com').read
37
+ local << remote_ip
38
+ rescue => e
39
+ Display.fatal e.message
40
+ Display.alert 'public ip lookup failed'
41
+ end
42
+ local
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Ui
4
+ module Command
5
+ class Chat < Command::Base
6
+ def handle
7
+ servers = Node.online
8
+ if !servers.empty?
9
+ type = self.class.name.demodulize.downcase
10
+ m = _message_factory.create(type, data: { message: _input })
11
+ show_myself(m)
12
+
13
+ # if sending to all, iterate thorugh list of
14
+ # servers, and send to each one
15
+ # TODO: do this async so that one server doesn't block
16
+ # the rest of the servers from receiving the messages
17
+ servers.each do |entry|
18
+ _message_dispatcher.send_message(node: entry, message: m)
19
+ end
20
+ else
21
+ Display.warning 'you have no servers'
22
+ end
23
+ end
24
+
25
+ def show_myself(message)
26
+ Display.chat message.display
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+ module Meshchat
3
+ module Ui
4
+ module Command
5
+ class Config < Command::Base
6
+ def self.description
7
+ 'interface for setting and displaying various config options'
8
+ end
9
+
10
+ def handle
11
+ case sub_command
12
+ when SET
13
+ if is_valid_set_command?
14
+ key, value = config_set_args
15
+
16
+ Display.info APP_CONFIG.user.set(key, with: value)
17
+ else
18
+ Display.alert 'set requires a key and a value'
19
+ end
20
+ when DISPLAY
21
+ Display.info APP_CONFIG.user.display
22
+ else
23
+ Display.alert 'config command not implemented...'
24
+ end
25
+ end
26
+
27
+ def config_set_args
28
+ command_args[2..3]
29
+ end
30
+
31
+ def is_valid_set_command?
32
+ sub_command == SET && command_args.length == 4
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end