hector 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/hector.rb +42 -0
- data/lib/hector/boot.rb +62 -0
- data/lib/hector/channel.rb +106 -0
- data/lib/hector/commands/away.rb +16 -0
- data/lib/hector/commands/join.rb +16 -0
- data/lib/hector/commands/mode.rb +31 -0
- data/lib/hector/commands/names.rb +16 -0
- data/lib/hector/commands/nick.rb +11 -0
- data/lib/hector/commands/notice.rb +10 -0
- data/lib/hector/commands/part.rb +11 -0
- data/lib/hector/commands/ping.rb +9 -0
- data/lib/hector/commands/pong.rb +9 -0
- data/lib/hector/commands/privmsg.rb +16 -0
- data/lib/hector/commands/quit.rb +10 -0
- data/lib/hector/commands/realname.rb +10 -0
- data/lib/hector/commands/topic.rb +26 -0
- data/lib/hector/commands/who.rb +29 -0
- data/lib/hector/commands/whois.rb +28 -0
- data/lib/hector/concerns/authentication.rb +45 -0
- data/lib/hector/concerns/keep_alive.rb +24 -0
- data/lib/hector/concerns/presence.rb +59 -0
- data/lib/hector/connection.rb +85 -0
- data/lib/hector/deference.rb +11 -0
- data/lib/hector/errors.rb +30 -0
- data/lib/hector/heartbeat.rb +25 -0
- data/lib/hector/identity.rb +23 -0
- data/lib/hector/logging.rb +16 -0
- data/lib/hector/request.rb +42 -0
- data/lib/hector/response.rb +48 -0
- data/lib/hector/server.rb +14 -0
- data/lib/hector/service.rb +30 -0
- data/lib/hector/session.rb +185 -0
- data/lib/hector/user_session.rb +38 -0
- data/lib/hector/version.rb +3 -0
- data/lib/hector/yaml_identity_adapter.rb +56 -0
- metadata +133 -0
data/lib/hector.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require "digest/sha1"
|
2
|
+
require "eventmachine"
|
3
|
+
require "fileutils"
|
4
|
+
require "socket"
|
5
|
+
require "yaml"
|
6
|
+
|
7
|
+
require "hector/errors"
|
8
|
+
|
9
|
+
require "hector/concerns/authentication"
|
10
|
+
require "hector/concerns/keep_alive"
|
11
|
+
require "hector/concerns/presence"
|
12
|
+
|
13
|
+
require "hector/commands/away"
|
14
|
+
require "hector/commands/join"
|
15
|
+
require "hector/commands/mode"
|
16
|
+
require "hector/commands/names"
|
17
|
+
require "hector/commands/nick"
|
18
|
+
require "hector/commands/notice"
|
19
|
+
require "hector/commands/part"
|
20
|
+
require "hector/commands/ping"
|
21
|
+
require "hector/commands/pong"
|
22
|
+
require "hector/commands/privmsg"
|
23
|
+
require "hector/commands/quit"
|
24
|
+
require "hector/commands/realname"
|
25
|
+
require "hector/commands/topic"
|
26
|
+
require "hector/commands/who"
|
27
|
+
require "hector/commands/whois"
|
28
|
+
|
29
|
+
require "hector/channel"
|
30
|
+
require "hector/connection"
|
31
|
+
require "hector/deference"
|
32
|
+
require "hector/heartbeat"
|
33
|
+
require "hector/identity"
|
34
|
+
require "hector/logging"
|
35
|
+
require "hector/request"
|
36
|
+
require "hector/response"
|
37
|
+
require "hector/server"
|
38
|
+
require "hector/session"
|
39
|
+
require "hector/service"
|
40
|
+
require "hector/user_session"
|
41
|
+
require "hector/version"
|
42
|
+
require "hector/yaml_identity_adapter"
|
data/lib/hector/boot.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "logger"
|
3
|
+
|
4
|
+
module Hector
|
5
|
+
class << self
|
6
|
+
attr_accessor :lib, :root
|
7
|
+
|
8
|
+
def start
|
9
|
+
raise LoadError, "please specify HECTOR_ROOT" unless Hector.root
|
10
|
+
load_defaults
|
11
|
+
load_application
|
12
|
+
end
|
13
|
+
|
14
|
+
def load_root
|
15
|
+
if root = ENV["HECTOR_ROOT"]
|
16
|
+
Hector.root = Pathname.new(File.expand_path(root))
|
17
|
+
else
|
18
|
+
Hector.root = find_application_root_from(Dir.pwd)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_defaults
|
23
|
+
log_path = Hector.root.join("log/hector.log")
|
24
|
+
Hector.logger = Logger.new(log_path)
|
25
|
+
Hector.logger.datetime_format = "%Y-%m-%d %H:%M:%S"
|
26
|
+
|
27
|
+
identities_path = Hector.root.join("config/identities.yml")
|
28
|
+
Hector::Identity.adapter = Hector::YamlIdentityAdapter.new(identities_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def load_application
|
32
|
+
$:.unshift Hector.root.join("lib")
|
33
|
+
load Hector.root.join("init.rb")
|
34
|
+
end
|
35
|
+
|
36
|
+
def find_application_root_from(working_directory)
|
37
|
+
dir = Pathname.new(working_directory)
|
38
|
+
dir = dir.parent while dir != dir.parent && dir.basename.to_s !~ /\.hect$/
|
39
|
+
dir == dir.parent ? nil : dir
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Hector.lib = Pathname.new(File.expand_path(File.dirname(__FILE__) + "/.."))
|
45
|
+
Hector.load_root
|
46
|
+
|
47
|
+
if Hector.root
|
48
|
+
vendor_lib = Hector.root.join("vendor/hector/lib")
|
49
|
+
Hector.lib = vendor_lib if vendor_lib.exist?
|
50
|
+
end
|
51
|
+
|
52
|
+
$:.unshift Hector.lib
|
53
|
+
|
54
|
+
begin
|
55
|
+
require "hector"
|
56
|
+
rescue LoadError => e
|
57
|
+
if require "rubygems"
|
58
|
+
retry
|
59
|
+
else
|
60
|
+
raise e
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Hector
|
2
|
+
class Channel
|
3
|
+
attr_reader :name, :topic, :user_sessions, :created_at
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def find(name)
|
7
|
+
channels[normalize(name)]
|
8
|
+
end
|
9
|
+
|
10
|
+
def find_all_for_session(session)
|
11
|
+
channels.values.find_all do |channel|
|
12
|
+
channel.has_session?(session)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def create(name)
|
17
|
+
new(name).tap do |channel|
|
18
|
+
channels[normalize(name)] = channel
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_or_create(name)
|
23
|
+
find(name) || create(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete(name)
|
27
|
+
channels.delete(name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def normalize(name)
|
31
|
+
if name =~ /^#\w[\w-]{0,15}$/u
|
32
|
+
name.downcase
|
33
|
+
else
|
34
|
+
raise NoSuchChannel, name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def reset!
|
39
|
+
@channels = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
def channels
|
44
|
+
@channels ||= {}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def initialize(name)
|
49
|
+
@name = name
|
50
|
+
@user_sessions = []
|
51
|
+
@created_at = Time.now
|
52
|
+
end
|
53
|
+
|
54
|
+
def broadcast(command, *args)
|
55
|
+
catch :stop do
|
56
|
+
Session.broadcast_to(sessions, command, *args)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def change_topic(session, topic)
|
61
|
+
@topic = { :body => topic, :nickname => session.nickname, :time => Time.now }
|
62
|
+
end
|
63
|
+
|
64
|
+
def channel?
|
65
|
+
true
|
66
|
+
end
|
67
|
+
|
68
|
+
def deliver(message_type, session, options)
|
69
|
+
if has_session?(session)
|
70
|
+
broadcast(message_type, name, options.merge(:except => session))
|
71
|
+
else
|
72
|
+
raise CannotSendToChannel, name
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def destroy
|
77
|
+
self.class.delete(name)
|
78
|
+
end
|
79
|
+
|
80
|
+
def has_session?(session)
|
81
|
+
sessions.include?(session)
|
82
|
+
end
|
83
|
+
|
84
|
+
def join(session)
|
85
|
+
return if has_session?(session)
|
86
|
+
user_sessions.push(session)
|
87
|
+
end
|
88
|
+
|
89
|
+
def nicknames
|
90
|
+
user_sessions.map { |session| session.nickname }
|
91
|
+
end
|
92
|
+
|
93
|
+
def part(session)
|
94
|
+
user_sessions.delete(session)
|
95
|
+
destroy if user_sessions.empty?
|
96
|
+
end
|
97
|
+
|
98
|
+
def services
|
99
|
+
Service.all
|
100
|
+
end
|
101
|
+
|
102
|
+
def sessions
|
103
|
+
services + user_sessions
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Hector
|
2
|
+
module Commands
|
3
|
+
module Away
|
4
|
+
def on_away
|
5
|
+
away_message = request.args.first
|
6
|
+
if away_message and !away_message.empty?
|
7
|
+
@away_message = away_message
|
8
|
+
respond_with("306", :text => "You have been marked as being away")
|
9
|
+
else
|
10
|
+
@away_message = nil
|
11
|
+
respond_with("305", :text => "You are no longer marked as being away")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Hector
|
2
|
+
module Commands
|
3
|
+
module Join
|
4
|
+
def on_join
|
5
|
+
request.args.first.split(",").each do |channel_name|
|
6
|
+
channel = Channel.find_or_create(channel_name)
|
7
|
+
if channel.join(self)
|
8
|
+
channel.broadcast(:join, :source => source, :text => channel.name)
|
9
|
+
respond_to_topic(channel)
|
10
|
+
respond_to_names(channel)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Hector
|
2
|
+
module Commands
|
3
|
+
module Mode
|
4
|
+
def on_mode
|
5
|
+
subject = find(request.args.first)
|
6
|
+
|
7
|
+
if subject.channel?
|
8
|
+
if requesting_modes?
|
9
|
+
respond_with("324", nickname, subject.name, "+", :source => Hector.server_name)
|
10
|
+
respond_with("329", nickname, subject.name, subject.created_at.to_i, :source => Hector.server_name)
|
11
|
+
elsif requesting_bans?
|
12
|
+
respond_with("368", nickname, subject.name, :text => "End of Channel Ban List", :source => Hector.server_name)
|
13
|
+
end
|
14
|
+
else
|
15
|
+
if requesting_modes?
|
16
|
+
respond_with("221", nickname, "+", :source => Hector.server_name)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def requesting_modes?
|
23
|
+
request.args.length == 1
|
24
|
+
end
|
25
|
+
|
26
|
+
def requesting_bans?
|
27
|
+
request.args.length == 2 && request.args.last[/^\+?b$/]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Hector
|
2
|
+
module Commands
|
3
|
+
module Names
|
4
|
+
def on_names
|
5
|
+
channel = Channel.find(request.args.first)
|
6
|
+
respond_to_names(channel)
|
7
|
+
end
|
8
|
+
|
9
|
+
def respond_to_names(channel)
|
10
|
+
responses = Response.apportion_text(channel.nicknames, "353", nickname, "=", channel.name, :source => Hector.server_name)
|
11
|
+
responses.each { |response| respond_with(response) }
|
12
|
+
respond_with("366", nickname, channel.name, :source => Hector.server_name, :text => "End of /NAMES list.")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Hector
|
2
|
+
module Commands
|
3
|
+
module Privmsg
|
4
|
+
def on_privmsg
|
5
|
+
touch_presence
|
6
|
+
subject = find(request.args.first)
|
7
|
+
|
8
|
+
subject.deliver(:privmsg, self, :source => source, :text => request.text)
|
9
|
+
|
10
|
+
if !subject.channel? and subject.away?
|
11
|
+
respond_with("301", subject.nickname, :text => subject.away_message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Hector
|
2
|
+
module Commands
|
3
|
+
module Topic
|
4
|
+
def on_topic
|
5
|
+
channel = Channel.find(request.args.first)
|
6
|
+
|
7
|
+
if request.args.length > 1
|
8
|
+
topic = request.text
|
9
|
+
channel.change_topic(self, topic)
|
10
|
+
channel.broadcast(:topic, channel.name, :source => source, :text => topic)
|
11
|
+
else
|
12
|
+
respond_to_topic(channel)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def respond_to_topic(channel)
|
17
|
+
if topic = channel.topic
|
18
|
+
respond_with("332", nickname, channel.name, :source => Hector.server_name, :text => topic[:body])
|
19
|
+
respond_with("333", nickname, channel.name, topic[:nickname], topic[:time].to_i, :source => Hector.server_name)
|
20
|
+
else
|
21
|
+
respond_with("331", nickname, channel.name, :source => Hector.server_name, :text => "No topic is set.")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Hector
|
2
|
+
module Commands
|
3
|
+
module Who
|
4
|
+
def on_who
|
5
|
+
name = request.args.first
|
6
|
+
|
7
|
+
if destination = destination_klass_for(name).find(name)
|
8
|
+
sessions_for_who(destination).each do |session|
|
9
|
+
respond_with("352", nickname, name, session.who, :source => Hector.server_name)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
respond_with("315", nickname, name, :source => Hector.server_name, :text => "End of /WHO list.")
|
14
|
+
end
|
15
|
+
|
16
|
+
def sessions_for_who(destination)
|
17
|
+
if destination.respond_to?(:sessions)
|
18
|
+
destination.sessions
|
19
|
+
else
|
20
|
+
[destination]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def who
|
25
|
+
"#{username} #{Hector.server_name} #{Hector.server_name} #{nickname} H :0 #{realname}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|