hector 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"
@@ -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,11 @@
1
+ module Hector
2
+ module Commands
3
+ module Nick
4
+ def on_nick
5
+ old_nickname = nickname
6
+ rename(request.args.first)
7
+ broadcast(:nick, nickname, :source => old_nickname)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ module Hector
2
+ module Commands
3
+ module Notice
4
+ def on_notice
5
+ touch_presence
6
+ find(request.args.first).deliver(:notice, self, :source => source, :text => request.text)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ module Hector
2
+ module Commands
3
+ module Part
4
+ def on_part
5
+ channel = Channel.find(request.args.first)
6
+ channel.broadcast(:part, channel.name, :source => source, :text => request.text)
7
+ channel.part(self)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module Hector
2
+ module Commands
3
+ module Ping
4
+ def on_ping
5
+ respond_with(:pong, :source => Hector.server_name, :text => request.text)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Hector
2
+ module Commands
3
+ module Pong
4
+ def on_pong
5
+ @received_pong = true
6
+ end
7
+ end
8
+ end
9
+ 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,10 @@
1
+ module Hector
2
+ module Commands
3
+ module Quit
4
+ def on_quit
5
+ @quit_message = "Quit: #{request.text}"
6
+ connection.close_connection(true)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Hector
2
+ module Commands
3
+ module Realname
4
+ def on_realname
5
+ @realname = request.text
6
+ broadcast("352", :$nickname, "*", who, :source => Hector.server_name)
7
+ end
8
+ end
9
+ end
10
+ 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