hector 1.0.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.
@@ -0,0 +1,14 @@
1
+ module Hector
2
+ class << self
3
+ attr_accessor :server_name
4
+
5
+ def start_server(address = "0.0.0.0", port = 6767, ssl_port = 6868)
6
+ EventMachine.start_server(address, port, Connection)
7
+ EventMachine.start_server(address, ssl_port, SSLConnection)
8
+ logger.info("Hector running on #{address}:#{port}")
9
+ logger.info("Secure Hector running on #{address}:#{ssl_port}")
10
+ end
11
+ end
12
+
13
+ self.server_name = "hector.irc"
14
+ end
@@ -0,0 +1,30 @@
1
+ module Hector
2
+ class Service < Session
3
+ protected
4
+ def deliver_message_from_origin(text)
5
+ deliver_message_from_session(origin, text)
6
+ end
7
+
8
+ def deliver_message_from_service(text)
9
+ deliver_message_from_session(self, text)
10
+ end
11
+
12
+ def deliver_message_from_session(session, text)
13
+ command, destination = response.command, find(response.args.first)
14
+ Hector.defer do
15
+ destination.deliver(command, session, :source => session.source, :text => text)
16
+ end
17
+ end
18
+
19
+ def intercept(pattern)
20
+ if response.text =~ pattern
21
+ yield *$~
22
+ throw :stop
23
+ end
24
+ end
25
+
26
+ def origin
27
+ find(response.source[/^([^!]+)!/, 1])
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,185 @@
1
+ module Hector
2
+ class Session
3
+ include Concerns::KeepAlive
4
+ include Concerns::Presence
5
+
6
+ include Commands::Join
7
+ include Commands::Mode
8
+ include Commands::Names
9
+ include Commands::Nick
10
+ include Commands::Notice
11
+ include Commands::Part
12
+ include Commands::Ping
13
+ include Commands::Pong
14
+ include Commands::Privmsg
15
+ include Commands::Quit
16
+ include Commands::Realname
17
+ include Commands::Topic
18
+ include Commands::Who
19
+ include Commands::Whois
20
+ include Commands::Away
21
+
22
+ attr_reader :nickname, :request, :response, :away_message
23
+
24
+ SESSIONS = {}
25
+
26
+ class << self
27
+ def all
28
+ sessions.values.grep(self)
29
+ end
30
+
31
+ def nicknames
32
+ sessions.keys
33
+ end
34
+
35
+ def find(nickname)
36
+ sessions[normalize(nickname)]
37
+ end
38
+
39
+ def rename(from, to)
40
+ if find(to)
41
+ raise NicknameInUse, to
42
+ else
43
+ find(from).tap do |session|
44
+ delete(from)
45
+ sessions[normalize(to)] = session
46
+ end
47
+ end
48
+ end
49
+
50
+ def delete(nickname)
51
+ sessions.delete(normalize(nickname))
52
+ end
53
+
54
+ def broadcast_to(sessions, command, *args)
55
+ except = args.last.delete(:except) if args.last.is_a?(Hash)
56
+ sessions.each do |session|
57
+ session.respond_with(command, *args) unless session == except
58
+ end
59
+ end
60
+
61
+ def normalize(nickname)
62
+ if nickname =~ /^\w[\w-]{0,15}$/u
63
+ nickname.downcase
64
+ else
65
+ raise ErroneousNickname, nickname
66
+ end
67
+ end
68
+
69
+ def register(session)
70
+ sessions[normalize(session.nickname)] = session
71
+ session
72
+ end
73
+
74
+ def reset!
75
+ sessions.clear
76
+ end
77
+
78
+ protected
79
+ def sessions
80
+ SESSIONS
81
+ end
82
+ end
83
+
84
+ def initialize(nickname)
85
+ @nickname = nickname
86
+ end
87
+
88
+ def broadcast(command, *args)
89
+ Session.broadcast_to(peer_sessions, command, *args)
90
+ end
91
+
92
+ def channel?
93
+ false
94
+ end
95
+
96
+ def deliver(message_type, session, options)
97
+ respond_with(message_type, nickname, options)
98
+ end
99
+
100
+ def destroy
101
+ self.class.delete(nickname)
102
+ end
103
+
104
+ def find(name)
105
+ destination_klass_for(name).find(name).tap do |destination|
106
+ raise NoSuchNickOrChannel, name unless destination
107
+ end
108
+ end
109
+
110
+ def hostname
111
+ Hector.server_name
112
+ end
113
+
114
+ def name
115
+ nickname
116
+ end
117
+
118
+ def realname
119
+ nickname
120
+ end
121
+
122
+ def receive(request)
123
+ @request = request
124
+ if respond_to?(@request.event_name)
125
+ send(@request.event_name)
126
+ end
127
+ ensure
128
+ @request = nil
129
+ end
130
+
131
+ def rename(new_nickname)
132
+ Session.rename(nickname, new_nickname)
133
+ @nickname = new_nickname
134
+ end
135
+
136
+ def respond_with(command, *args)
137
+ @response = command.is_a?(Response) ? command : Response.new(command, *preprocess_args(args))
138
+ if respond_to?(@response.event_name)
139
+ send(@response.event_name)
140
+ end
141
+ @response
142
+ ensure
143
+ @response = nil
144
+ end
145
+
146
+ def away(away_message)
147
+ @away_message = away_message
148
+ end
149
+
150
+ def away?
151
+ !@away_message.nil?
152
+ end
153
+
154
+ def back
155
+ @away_message = nil
156
+ end
157
+
158
+ def source
159
+ "#{nickname}!#{username}@#{hostname}"
160
+ end
161
+
162
+ def username
163
+ "~#{nickname}"
164
+ end
165
+
166
+ def who
167
+ "#{identity.username} #{Hector.server_name} #{Hector.server_name} #{nickname} H :0 #{realname}"
168
+ end
169
+
170
+ protected
171
+ def destination_klass_for(name)
172
+ name =~ /^#/ ? Channel : Session
173
+ end
174
+
175
+ def preprocess_args(args)
176
+ args.map do |arg|
177
+ if arg.is_a?(Symbol) && arg.to_s[0, 1] == "$"
178
+ send(arg.to_s[1..-1])
179
+ else
180
+ arg
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,38 @@
1
+ module Hector
2
+ class UserSession < Session
3
+ attr_reader :connection, :identity, :realname
4
+
5
+ class << self
6
+ def create(nickname, connection, identity, realname)
7
+ if find(nickname)
8
+ raise NicknameInUse, nickname
9
+ else
10
+ register UserSession.new(nickname, connection, identity, realname)
11
+ end
12
+ end
13
+ end
14
+
15
+ def initialize(nickname, connection, identity, realname)
16
+ super(nickname)
17
+ @connection = connection
18
+ @identity = identity
19
+ @realname = realname
20
+ initialize_keep_alive
21
+ initialize_presence
22
+ end
23
+
24
+ def destroy
25
+ super
26
+ destroy_presence
27
+ destroy_keep_alive
28
+ end
29
+
30
+ def respond_with(*)
31
+ connection.respond_with(super)
32
+ end
33
+
34
+ def username
35
+ identity.username
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Hector
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,56 @@
1
+ module Hector
2
+ # Identity adapters must implement the following public methods:
3
+ # - authenticate(username, password)
4
+ # - remember(username, password)
5
+ # - forget(username)
6
+ # - normalize(username)
7
+ #
8
+ class YamlIdentityAdapter
9
+ attr_reader :filename
10
+
11
+ def initialize(filename)
12
+ @filename = File.expand_path(filename)
13
+ end
14
+
15
+ def authenticate(username, password)
16
+ yield load_identities[normalize(username)] == hash(normalize(username), password)
17
+ end
18
+
19
+ def remember(username, password)
20
+ identities = load_identities
21
+ identities[normalize(username)] = hash(normalize(username), password)
22
+ store_identities(identities)
23
+ end
24
+
25
+ def forget(username)
26
+ identities = load_identities
27
+ identities.delete(normalize(username))
28
+ store_identities(identities)
29
+ end
30
+
31
+ def normalize(username)
32
+ username.strip.downcase
33
+ end
34
+
35
+ protected
36
+ def load_identities
37
+ ensure_file_exists
38
+ YAML.load_file(filename) || {}
39
+ end
40
+
41
+ def store_identities(identities)
42
+ File.open(filename, "w") do |file|
43
+ file.puts(identities.to_yaml)
44
+ end
45
+ end
46
+
47
+ def hash(username, password)
48
+ Digest::SHA1.hexdigest(Digest::SHA1.hexdigest(username) + password)
49
+ end
50
+
51
+ def ensure_file_exists
52
+ FileUtils.mkdir_p(File.dirname(filename))
53
+ FileUtils.touch(filename)
54
+ end
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hector
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Sam Stephenson
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-04 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: eventmachine
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 59
30
+ segments:
31
+ - 0
32
+ - 12
33
+ - 10
34
+ version: 0.12.10
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: mocha
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 41
46
+ segments:
47
+ - 0
48
+ - 9
49
+ - 9
50
+ version: 0.9.9
51
+ type: :development
52
+ version_requirements: *id002
53
+ description: A private group chat server for people you trust. Implements a limited subset of the IRC protocol.
54
+ email:
55
+ - sstephenson@gmail.com
56
+ executables: []
57
+
58
+ extensions: []
59
+
60
+ extra_rdoc_files: []
61
+
62
+ files:
63
+ - lib/hector/boot.rb
64
+ - lib/hector/channel.rb
65
+ - lib/hector/commands/away.rb
66
+ - lib/hector/commands/join.rb
67
+ - lib/hector/commands/mode.rb
68
+ - lib/hector/commands/names.rb
69
+ - lib/hector/commands/nick.rb
70
+ - lib/hector/commands/notice.rb
71
+ - lib/hector/commands/part.rb
72
+ - lib/hector/commands/ping.rb
73
+ - lib/hector/commands/pong.rb
74
+ - lib/hector/commands/privmsg.rb
75
+ - lib/hector/commands/quit.rb
76
+ - lib/hector/commands/realname.rb
77
+ - lib/hector/commands/topic.rb
78
+ - lib/hector/commands/who.rb
79
+ - lib/hector/commands/whois.rb
80
+ - lib/hector/concerns/authentication.rb
81
+ - lib/hector/concerns/keep_alive.rb
82
+ - lib/hector/concerns/presence.rb
83
+ - lib/hector/connection.rb
84
+ - lib/hector/deference.rb
85
+ - lib/hector/errors.rb
86
+ - lib/hector/heartbeat.rb
87
+ - lib/hector/identity.rb
88
+ - lib/hector/logging.rb
89
+ - lib/hector/request.rb
90
+ - lib/hector/response.rb
91
+ - lib/hector/server.rb
92
+ - lib/hector/service.rb
93
+ - lib/hector/session.rb
94
+ - lib/hector/user_session.rb
95
+ - lib/hector/version.rb
96
+ - lib/hector/yaml_identity_adapter.rb
97
+ - lib/hector.rb
98
+ has_rdoc: true
99
+ homepage: http://github.com/sstephenson/hector
100
+ licenses: []
101
+
102
+ post_install_message:
103
+ rdoc_options: []
104
+
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ hash: 3
113
+ segments:
114
+ - 0
115
+ version: "0"
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ hash: 3
122
+ segments:
123
+ - 0
124
+ version: "0"
125
+ requirements: []
126
+
127
+ rubyforge_project:
128
+ rubygems_version: 1.3.7
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: Private group chat server
132
+ test_files: []
133
+