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