karper 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 13cf13577600ed624dee10e2ab9704512b09fa2844bce053e168e4d4c45ebd71
4
+ data.tar.gz: dca0a60d7746cda3a680c7d303bf02d0ea3c7e05b46819823388f05626c8a5f2
5
+ SHA512:
6
+ metadata.gz: 8e53572489481446c12507bc7c2c1a345329b7bd8ae36ea5ee9cc5162cefb62ff004e5e236159709393409536f87e3fcf6de1b53c276baaafc1dc6985fe44839
7
+ data.tar.gz: 3f1b257a533333a8fe5297aaa49db5829db6e01dfdb8a0e46b17bad35ea79b93fd2b442e29808f1915f490aa07268a34027c9bb8bab310bdd6627d08f99091e5
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) Fingertips
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # Karper Server
2
+
3
+ A single channel chat server.
4
+
5
+ ## Start
6
+
7
+ ```sh
8
+ gem install karper
9
+ karper server
10
+ ```
11
+
12
+ ## Technical design in a nutshell
13
+
14
+ * Communication over TCP
15
+ * Encoded using MessagePack
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karper
4
+ module Code
5
+ ERRORS = {
6
+ 'create_invitation' => {
7
+ '7a2f22' => 'Duplicate nick; someone else is using that nick, please choose another one.'
8
+ }
9
+ }.freeze
10
+ NOTES = {
11
+ '1fac60' => 'Server will pause for backup, please wait 10 seconds before attempting to send new messages.'
12
+ }.freeze
13
+ end
14
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karper
4
+ module Faker
5
+ FIRST = %w[mar lig erg tus fre urm ioa poh gaf def self.kel mee fad].freeze
6
+ LAST = %w[urg dee uts pee oop sed sad ddo ver cef laa lah gah].freeze
7
+ CONNECTORS = %w[- _ x].freeze
8
+
9
+ def self.generate_newt
10
+ Karper::Newt.generate
11
+ end
12
+
13
+ class << self
14
+ alias generate_user_id generate_newt
15
+ alias generate_invitation_token generate_newt
16
+ end
17
+
18
+ def self.generate_timestamp
19
+ # We only support up to 3 decimal places on milliseconds, so we roundtrip it through out own packing methods.
20
+ Karper::Unpacker.unpack_time(Karper::Packer.pack_time(Time.now.utc + rand(100) - 50))
21
+ end
22
+
23
+ def self.generate_word
24
+ (FIRST + LAST).sample
25
+ end
26
+
27
+ def self.generate_words
28
+ (0..rand(20)).map { generate_word }.join(' ')
29
+ end
30
+
31
+ def self.generate_contents
32
+ "#{generate_word.capitalize} #{generate_words}."
33
+ end
34
+
35
+ def self.generate_nick
36
+ connector = rand(4) == 1 ? ' ' : CONNECTORS.sample
37
+ nick = [FIRST.sample, connector, LAST.sample].compact.join
38
+ case rand(3)
39
+ when 0
40
+ nick.upcase
41
+ when 1
42
+ nick.downcase
43
+ else
44
+ nick.split.map { |part| "#{part[0].upcase}#{part[1..]}" }.join
45
+ end
46
+ end
47
+
48
+ def self.generate_password
49
+ "#{%w[zeewolf flipper winter].sample}#{rand(100).to_s[0, 2]}"
50
+ end
51
+
52
+ def self.generate_url
53
+ 'http://example.com'
54
+ end
55
+
56
+ def self.generate_document_attributes
57
+ {}
58
+ end
59
+
60
+ def self.generate_attributes(members)
61
+ members.map do |name|
62
+ send("generate_#{name}")
63
+ end
64
+ end
65
+
66
+ Karper::Message.constants.each do |constant|
67
+ next if %i[Error Note].include?(constant)
68
+
69
+ class_name = Karper::Message.const_get(constant)
70
+ method_name = constant.to_s.gsub(/[A-Z]/) { |a, b| "_#{a.downcase}#{b}" }[1..]
71
+ class_eval <<~METHOD, __FILE__, __LINE__ + 1
72
+ def self.#{method_name}
73
+ #{class_name}.new(*generate_attributes(#{class_name.members.inspect}))
74
+ end
75
+ METHOD
76
+ end
77
+
78
+ def self.error
79
+ command = Karper::Code::ERRORS.keys.sample
80
+ code = Karper::Code::ERRORS[command].keys.sample
81
+ Karper::Message::Error.new(
82
+ generate_newt,
83
+ command,
84
+ code,
85
+ Karper::Code::ERRORS[command][code],
86
+ {}
87
+ )
88
+ end
89
+
90
+ def self.note
91
+ code = Karper::Code::NOTES.keys.sample
92
+ Karper::Message::Note.new(
93
+ generate_newt,
94
+ code,
95
+ Karper::Code::NOTES[code],
96
+ {}
97
+ )
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karper
4
+ module Message
5
+ CreateInvitation = Struct.new(:newt, :nick)
6
+ Invitation = Struct.new(:newt, :timestamp, :nick, :invitation_token)
7
+
8
+ CreateRegistration = Struct.new(:newt, :invitation_token, :password)
9
+ Registration = Struct.new(:newt, :timestamp, :user_id, :invitation_token, :nick)
10
+
11
+ CreateAuthentication = Struct.new(:newt, :user_id, :password)
12
+ Authentication = Struct.new(:newt, :timestamp, :user_id, :nick)
13
+
14
+ CreateMessage = Struct.new(:newt, :contents)
15
+ Message = Struct.new(:newt, :timestamp, :user_id, :contents)
16
+
17
+ UpdateNick = Struct.new(:newt, :user_id, :nick)
18
+ Nick = Struct.new(:newt, :timestamp, :user_id, :nick)
19
+
20
+ CreateMedia = Struct.new(:newt, :url, :document_attributes)
21
+ Media = Struct.new(:newt, :timestamp, :user_id, :url, :document_attributes)
22
+
23
+ UpdateTopic = Struct.new(:newt, :contents)
24
+ Topic = Struct.new(:newt, :timestamp, :user_id, :contents)
25
+
26
+ Error = Struct.new(:newt, :command, :error_code, :error_message, :attributes)
27
+ Note = Struct.new(:newt, :error_code, :error_message, :attributes)
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Karper
6
+ module Newt
7
+ def self.generate
8
+ SecureRandom.bytes(12)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karper
4
+ module Packer
5
+ RLF = /\r\n/
6
+
7
+ def self.pack_time(value)
8
+ value.strftime('%Y-%m-%dT%H:%M:%S.%3NZ')
9
+ end
10
+
11
+ def self.map(values)
12
+ values.map do |value|
13
+ case value
14
+ when String
15
+ value.gsub(RLF, "\n")
16
+ when Time
17
+ pack_time(value)
18
+ else
19
+ value
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.pack(object)
25
+ MessagePack.pack(map(object.to_a))
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karper
4
+ class State
5
+ def initialize
6
+ @messages = []
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karper
4
+ module Unpacker
5
+ def self.unpack_time(value)
6
+ Time.parse(value)
7
+ end
8
+
9
+ def self.unpack(message_class, input)
10
+ values = MessagePack.load(input)
11
+ message_class.members.each.with_index do |name, index|
12
+ next unless name == :timestamp
13
+
14
+ values[index] = unpack_time(values[index])
15
+ end
16
+ message_class.new(*values)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Karper
4
+ VERSION = '0.1.0'
5
+ end
data/lib/karper.rb ADDED
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require 'msgpack'
5
+
6
+ require 'karper/message'
7
+
8
+ module Karper
9
+ autoload :Code, 'karper/code'
10
+ autoload :Faker, 'karper/faker'
11
+ autoload :Formatter, 'karper/parser'
12
+ autoload :Newt, 'karper/newt'
13
+ autoload :Packer, 'karper/packer'
14
+ autoload :State, 'karper/state'
15
+ autoload :Unpacker, 'karper/unpacker'
16
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: karper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Manfred Stienstra
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-03-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: msgpack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.4'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">"
32
+ - !ruby/object:Gem::Version
33
+ version: '5.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">"
39
+ - !ruby/object:Gem::Version
40
+ version: '5.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ description: ''
56
+ email:
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - LICENSE.txt
62
+ - README.md
63
+ - lib/karper.rb
64
+ - lib/karper/code.rb
65
+ - lib/karper/faker.rb
66
+ - lib/karper/message.rb
67
+ - lib/karper/newt.rb
68
+ - lib/karper/packer.rb
69
+ - lib/karper/state.rb
70
+ - lib/karper/unpacker.rb
71
+ - lib/karper/version.rb
72
+ homepage:
73
+ licenses: []
74
+ metadata:
75
+ rubygems_mfa_required: 'true'
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '3.1'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.3.7
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: Karper is a simple single channel chat server.
95
+ test_files: []