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