irccat 0.4.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/.gitignore +4 -0
- data/Gemfile +6 -0
- data/README.txt +33 -0
- data/Rakefile +2 -0
- data/bin/icat +54 -0
- data/bin/iecho +49 -0
- data/bin/irccat +67 -0
- data/bin/irccat-config +67 -0
- data/examples/client.yml +4 -0
- data/examples/server.yml +29 -0
- data/irccat.gemspec +25 -0
- data/lib/irccat/bot.rb +148 -0
- data/lib/irccat/http_server.rb +134 -0
- data/lib/irccat/indifferent_access.rb +75 -0
- data/lib/irccat/tcp_client.rb +9 -0
- data/lib/irccat/tcp_server.rb +38 -0
- data/lib/irccat/version.rb +3 -0
- data/lib/irccat.rb +13 -0
- metadata +129 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.txt
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
irc_cat
|
2
|
+
---------
|
3
|
+
|
4
|
+
= Introduction
|
5
|
+
|
6
|
+
Irc_Cat is a Ruby IRC bot, inspired by irccat from Last.FM.
|
7
|
+
|
8
|
+
You can send messages to IRC from a TCP socket or from HTTP.
|
9
|
+
|
10
|
+
= Configuration
|
11
|
+
|
12
|
+
http://irccat.rubyforge.org/install.html
|
13
|
+
|
14
|
+
= How-to use
|
15
|
+
|
16
|
+
http://irccat.rubyforge.org/use.html
|
17
|
+
|
18
|
+
= Authentication
|
19
|
+
|
20
|
+
= Extend
|
21
|
+
|
22
|
+
= Links
|
23
|
+
|
24
|
+
* Website: http://irccat.rubyforge.org/
|
25
|
+
* Project: http://rubyforge.org/projects/irccat
|
26
|
+
* Downloads: http://rubyforge.org/frs/?group_id=5423
|
27
|
+
* Bugs: http://webs.lighthouseapp.com/projects/14710-irccat
|
28
|
+
* Repository: http://github.com/webs/irccat
|
29
|
+
|
30
|
+
----
|
31
|
+
Copyright (c) 2007-2008 Jordan Bracco <jordan@lifeisdead.net>
|
32
|
+
Inspired by the irccat bot of Last.FM.
|
33
|
+
Released under the MIT licence :)
|
data/Rakefile
ADDED
data/bin/icat
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# `cat` for irccat :)
|
4
|
+
#
|
5
|
+
# Created on 2008-2-17.
|
6
|
+
# Copyright (c) 2008. All rights reserved.
|
7
|
+
|
8
|
+
require 'optparse'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
OPTIONS = {
|
12
|
+
:profile => 'default'
|
13
|
+
}
|
14
|
+
MANDATORY_OPTIONS = %w( )
|
15
|
+
|
16
|
+
parser = OptionParser.new do |opts|
|
17
|
+
opts.banner = <<BANNER
|
18
|
+
icat - just like cat but for irccat
|
19
|
+
|
20
|
+
Usage: #{File.basename($0)} [options] <files or STDIN>
|
21
|
+
|
22
|
+
Options are:
|
23
|
+
BANNER
|
24
|
+
opts.separator ""
|
25
|
+
opts.on("-p", "--profile=PROFILE", String,
|
26
|
+
"Profile to use (in ~/.irccat.yml).",
|
27
|
+
"Default: 'default'") { |OPTIONS[:profile]| }
|
28
|
+
opts.on("-h", "--help",
|
29
|
+
"Show this help message.") { puts opts; exit }
|
30
|
+
opts.parse!(ARGV)
|
31
|
+
|
32
|
+
if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
|
33
|
+
puts opts; exit
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
raise "Can't read configuration file (~/.irccat.yml)" unless File.exists?(File.expand_path("~/.irccat.yml"))
|
38
|
+
@config = YAML.load_file(File.expand_path("~/.irccat.yml"))
|
39
|
+
|
40
|
+
@profile = @config["#{OPTIONS[:profile]}"]
|
41
|
+
raise "Can't find profile #{OPTIONS[:profile]} in ~/.irccat.yml" if @profile.nil?
|
42
|
+
|
43
|
+
require File.dirname(__FILE__) + '/../lib/irc_cat/tcp_client'
|
44
|
+
|
45
|
+
if ARGV.empty?
|
46
|
+
puts "has stdin"
|
47
|
+
message = STDIN.read
|
48
|
+
IrcCat::TcpClient.notify(@profile['host'], @profile['port'], message)
|
49
|
+
else
|
50
|
+
ARGV.each do |file|
|
51
|
+
raise "Can't read #{file}" unless File.exists?(File.expand_path(file))
|
52
|
+
IrcCat::TcpClient.notify(@profile['host'], @profile['port'], File.read(File.expand_path(file)))
|
53
|
+
end
|
54
|
+
end
|
data/bin/iecho
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# `echo` for irccat :)
|
4
|
+
#
|
5
|
+
# Created on 2008-2-17.
|
6
|
+
# Copyright (c) 2008. All rights reserved.
|
7
|
+
|
8
|
+
require 'optparse'
|
9
|
+
require 'yaml'
|
10
|
+
|
11
|
+
OPTIONS = {
|
12
|
+
:profile => 'default'
|
13
|
+
}
|
14
|
+
MANDATORY_OPTIONS = %w( )
|
15
|
+
|
16
|
+
parser = OptionParser.new do |opts|
|
17
|
+
opts.banner = <<BANNER
|
18
|
+
iecho - just like echo but for irccat
|
19
|
+
|
20
|
+
Usage: #{File.basename($0)} [options] text
|
21
|
+
|
22
|
+
Options are:
|
23
|
+
BANNER
|
24
|
+
opts.separator ""
|
25
|
+
opts.on("-p", "--profile=PROFILE", String,
|
26
|
+
"Profile to use (in ~/.irccat.yml).",
|
27
|
+
"Default: 'default'") { |OPTIONS[:profile]| }
|
28
|
+
opts.on("-h", "--help",
|
29
|
+
"Show this help message.") { puts opts; exit }
|
30
|
+
opts.parse!(ARGV)
|
31
|
+
|
32
|
+
if MANDATORY_OPTIONS && MANDATORY_OPTIONS.find { |option| OPTIONS[option.to_sym].nil? }
|
33
|
+
puts opts; exit
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
raise "Can't read configuration file (~/.irccat.yml)" unless File.exists?(File.expand_path("~/.irccat.yml"))
|
38
|
+
@config = YAML.load_file(File.expand_path("~/.irccat.yml"))
|
39
|
+
|
40
|
+
@profile = @config["#{OPTIONS[:profile]}"]
|
41
|
+
raise "Can't find profile #{OPTIONS[:profile]} in ~/.irccat.yml" if @profile.nil?
|
42
|
+
|
43
|
+
raise "Nothing to send" if ARGV.empty?
|
44
|
+
|
45
|
+
require File.dirname(__FILE__) + '/../lib/irc_cat/tcp_client'
|
46
|
+
|
47
|
+
message = ARGV.join(' ')
|
48
|
+
|
49
|
+
IrcCat::TcpClient.notify(@profile['host'], @profile['port'], message)
|
data/bin/irccat
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Created on 2008-2-17.
|
4
|
+
# Copyright (c) 2008. All rights reserved.
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
options = {
|
10
|
+
:config_filename => './config.yml'
|
11
|
+
}
|
12
|
+
|
13
|
+
parser = OptionParser.new do |opts|
|
14
|
+
opts.banner = <<BANNER
|
15
|
+
irc_cat - stdout for irc
|
16
|
+
|
17
|
+
Usage: #{File.basename($0)} [options]
|
18
|
+
|
19
|
+
Options are:
|
20
|
+
BANNER
|
21
|
+
|
22
|
+
opts.separator ""
|
23
|
+
|
24
|
+
opts.on("-c", "--config=CONFIG", String, "Path to the config file.", "Default: ./config.yml") do |filename|
|
25
|
+
options[:config_filename] = filename
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on("-h", "--help", "Show this help message.") {
|
29
|
+
puts opts
|
30
|
+
exit
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
parser.parse!(ARGV)
|
35
|
+
|
36
|
+
raise "Can't read configuration file (#{options[:config_filename]})" unless File.exists?(options[:config_filename])
|
37
|
+
config = YAML.load_file(options[:config_filename])
|
38
|
+
|
39
|
+
lib_dir = File.dirname(__FILE__) + '/../lib/'
|
40
|
+
require lib_dir + '/irccat'
|
41
|
+
|
42
|
+
threads = []
|
43
|
+
puts "irccat #{IrcCat::VERSION} (http://irccat.rubyforge.org/)"
|
44
|
+
|
45
|
+
Thread.abort_on_exception = true
|
46
|
+
|
47
|
+
irc_config = config['irc']
|
48
|
+
IrcCat::Bot.run(irc_config['host'], irc_config['port'], irc_config['nick'], irc_config['pass']) do |bot|
|
49
|
+
puts "Connecting..."
|
50
|
+
irc_config['channels'].each do |channel|
|
51
|
+
name, pass = channel.split(" ")
|
52
|
+
bot.join_channel(name, pass)
|
53
|
+
end
|
54
|
+
|
55
|
+
tcp_config = config['tcp']
|
56
|
+
if tcp_config['enabled']
|
57
|
+
require lib_dir + 'irccat/tcp_server'
|
58
|
+
IrcCat::TcpServer.run(bot, tcp_config)
|
59
|
+
end
|
60
|
+
|
61
|
+
http_config = config['http']
|
62
|
+
http_config['channels'] = config['irc']['channels']
|
63
|
+
if http_config['enabled']
|
64
|
+
require lib_dir + 'irccat/http_server'
|
65
|
+
IrcCat::HttpServer.run(bot, http_config)
|
66
|
+
end
|
67
|
+
end
|
data/bin/irccat-config
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Configuration generator for irccat
|
4
|
+
#
|
5
|
+
# Created on 2008-2-17.
|
6
|
+
# Copyright (c) 2008. All rights reserved.
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'optparse'
|
10
|
+
require 'yaml'
|
11
|
+
require 'highline/import'
|
12
|
+
|
13
|
+
def irccat_config
|
14
|
+
config = Hash.new
|
15
|
+
puts "== IRC Configuration"
|
16
|
+
config['irc'] = Hash.new
|
17
|
+
config['irc']['host'] = ask("Hostname: ") { |q| q.default = "irc.freenode.net" }
|
18
|
+
config['irc']['port'] = ask("Port: ") { |q| q.default = "6667" }
|
19
|
+
config['irc']['nick'] = ask("Nickname: ") { |q| q.default = "irc_cat" }
|
20
|
+
config['irc']['channel'] = ask("Channel: ") { |q| q.default = "#irc_cat" }
|
21
|
+
puts "\n== HTTP Configuration"
|
22
|
+
config['http'] = Hash.new
|
23
|
+
if agree("Enable HTTP? ", true); config['http']['enabled'] = true; else; config['http']['enabled'] = false; end
|
24
|
+
if config['http']['enabled']
|
25
|
+
config['http']['host'] = ask("Hostname: ") { |q| q.default = "0.0.0.0" }
|
26
|
+
config['http']['port'] = ask("Port: ") { |q| q.default = "3489" }
|
27
|
+
if agree("Enable Basic HTTP send? ", true); config['http']['send'] = true; else; config['http']['send'] = false; end
|
28
|
+
if agree("Enable Github? ", true); config['http']['github'] = true; else; config['http']['github'] = false; end
|
29
|
+
end
|
30
|
+
puts "\n== TCP Configuration"
|
31
|
+
config['tcp'] = Hash.new
|
32
|
+
if agree("Enable TCP? ", true); config['tcp']['enabled'] = true; else; config['tcp']['enabled'] = false; end
|
33
|
+
if config['tcp']['enabled']
|
34
|
+
config['tcp']['host'] = ask("Hostname: ") { |q| q.default = "0.0.0.0" }
|
35
|
+
config['tcp']['port'] = ask("Port: ") { |q| q.default = "5678" }
|
36
|
+
config['tcp']['size'] = ask("Socket size: ") { |q| q.default = "400" }
|
37
|
+
end
|
38
|
+
puts "\n== Final steps"
|
39
|
+
file = ask("Configuration filename: ") { |q| q.default = "config.yml" }
|
40
|
+
puts "Warning: File exists! If you choose yes now the file will be overwritten" if File.exists?(File.expand_path(file))
|
41
|
+
if agree("Write configuration file? ", true)
|
42
|
+
File.open(File.expand_path(file), "w") { |yfile| YAML.dump(config, yfile) }
|
43
|
+
else
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def help
|
49
|
+
puts "irccat-config - configuration generator for irccat\n"
|
50
|
+
puts "Usage: #{File.basename($0)} irccat|client > configfile.yml"
|
51
|
+
exit 0
|
52
|
+
end
|
53
|
+
|
54
|
+
if ARGV[0].nil?
|
55
|
+
help
|
56
|
+
else
|
57
|
+
case ARGV[0]
|
58
|
+
when 'irccat'
|
59
|
+
irccat_config
|
60
|
+
when 'client'
|
61
|
+
#client_config
|
62
|
+
# TODO: todo :D
|
63
|
+
help
|
64
|
+
else
|
65
|
+
help
|
66
|
+
end
|
67
|
+
end
|
data/examples/client.yml
ADDED
data/examples/server.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# IRC connection
|
2
|
+
irc:
|
3
|
+
host: 'irc.freenode.net'
|
4
|
+
port: '6667'
|
5
|
+
nick: irc_cat
|
6
|
+
channels:
|
7
|
+
- "#austinhackerspace"
|
8
|
+
# This is a channel with a key
|
9
|
+
#- "#secret password"
|
10
|
+
|
11
|
+
# HTTP Server
|
12
|
+
http:
|
13
|
+
enabled: true
|
14
|
+
# user: http_user
|
15
|
+
# pass: http_pass
|
16
|
+
host: 0.0.0.0
|
17
|
+
port: '3489'
|
18
|
+
# Enable post-receive stuff from Github
|
19
|
+
github: true
|
20
|
+
# Allow send with http
|
21
|
+
send: true
|
22
|
+
message_param: Message
|
23
|
+
|
24
|
+
# TCP Server
|
25
|
+
tcp:
|
26
|
+
enabled: true
|
27
|
+
host: 0.0.0.0
|
28
|
+
port: '5678'
|
29
|
+
size: 400
|
data/irccat.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "irccat/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "irccat"
|
7
|
+
s.version = IrcCat::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["people"]
|
10
|
+
s.email = ["folks"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Write a gem summary}
|
13
|
+
s.description = %q{Write a gem description}
|
14
|
+
|
15
|
+
s.rubyforge_project = "irccat"
|
16
|
+
|
17
|
+
s.add_dependency 'rack'
|
18
|
+
s.add_dependency 'json'
|
19
|
+
s.add_dependency 'mongrel'
|
20
|
+
|
21
|
+
s.files = `git ls-files`.split("\n")
|
22
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
23
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
24
|
+
s.require_paths = ["lib"]
|
25
|
+
end
|
data/lib/irccat/bot.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
# This is the bot.
|
2
|
+
# Original code by Madx (yapok.org)
|
3
|
+
|
4
|
+
module IrcCat
|
5
|
+
class Bot
|
6
|
+
def self.run(host, port, nick, nick_pass = nil, &block)
|
7
|
+
new(host, port, nick, nick_pass, &block).run
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(host, port, nick, nick_pass, &block)
|
11
|
+
@host, @port, @nick, @nick_pass = host, port, nick, nick_pass
|
12
|
+
@online, @connect_block = false, block
|
13
|
+
|
14
|
+
@realname = "irccat v#{VERSION}"
|
15
|
+
@refresh_rate = 10
|
16
|
+
@channels = {}
|
17
|
+
|
18
|
+
puts "Connecting to IRC #{@host}:#{@port}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def online?
|
22
|
+
@online
|
23
|
+
end
|
24
|
+
|
25
|
+
def run(&block)
|
26
|
+
@socket = TCPSocket.open(@host, @port)
|
27
|
+
login
|
28
|
+
|
29
|
+
trap(:INT) {
|
30
|
+
puts "Bye bye."
|
31
|
+
sexit('God^WConsole killed me')
|
32
|
+
sleep 1
|
33
|
+
@socket.close
|
34
|
+
exit
|
35
|
+
}
|
36
|
+
|
37
|
+
threads = []
|
38
|
+
|
39
|
+
threads << Thread.new {
|
40
|
+
begin
|
41
|
+
while line = @socket.gets do
|
42
|
+
# Remove all formatting
|
43
|
+
line.gsub!(/[\x02\x1f\x16\x0f]/,'')
|
44
|
+
# Remove CTCP ASCII
|
45
|
+
line.gsub!(/\001/,'')
|
46
|
+
# Send to event handler
|
47
|
+
handle line
|
48
|
+
# Handle Pings from Server
|
49
|
+
sendln "PONG #{$1}" if /^PING\s(.*)/ =~ line
|
50
|
+
end
|
51
|
+
rescue EOFError
|
52
|
+
err 'Server Reset Connection'
|
53
|
+
rescue Exception => e
|
54
|
+
err e
|
55
|
+
end
|
56
|
+
}
|
57
|
+
threads << Thread.new {
|
58
|
+
}
|
59
|
+
threads.each { |th| th.join }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Announces states
|
63
|
+
def announce(msg)
|
64
|
+
@channels.each do |channel,key|
|
65
|
+
say(channel, msg)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Say something funkeh
|
70
|
+
def say(chan, msg)
|
71
|
+
sendln "PRIVMSG #{chan} :#{msg}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def topic(chan, msg)
|
75
|
+
sendln "TOPIC #{chan} :#{msg}"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Send EXIT
|
79
|
+
def sexit(message = 'quit')
|
80
|
+
sendln "QUIT :#{message}"
|
81
|
+
end
|
82
|
+
|
83
|
+
# Sends a message to the server
|
84
|
+
|
85
|
+
def sendln(cmd)
|
86
|
+
puts "Send: #{cmd}"
|
87
|
+
if cmd.size <= 510
|
88
|
+
@socket.write("#{cmd}\r\n")
|
89
|
+
STDOUT.flush
|
90
|
+
else
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Handle a received message
|
95
|
+
|
96
|
+
def handle(line)
|
97
|
+
puts "Got: #{line}"
|
98
|
+
case line
|
99
|
+
when /^:.+\s376/
|
100
|
+
puts "We're online"
|
101
|
+
@online = true
|
102
|
+
@connect_block.call(self) if @connect_block
|
103
|
+
when /^:.+KICK (#[^\s]+)/
|
104
|
+
auto_rejoin($1)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Automatic events
|
109
|
+
|
110
|
+
def join_channel(channel, key)
|
111
|
+
@channels[channel] = key
|
112
|
+
$stdout.flush
|
113
|
+
if key
|
114
|
+
sendln "JOIN #{channel} #{key}"
|
115
|
+
else
|
116
|
+
sendln "JOIN #{channel}"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def auto_rejoin(channel)
|
121
|
+
join_channel(channel, @channels[channel])
|
122
|
+
end
|
123
|
+
|
124
|
+
def login
|
125
|
+
begin
|
126
|
+
sendln "NICK #{@nick}"
|
127
|
+
sendln "USER irc_cat . . :#{@realname}"
|
128
|
+
if @nick_pass
|
129
|
+
puts "logging in to NickServ"
|
130
|
+
sendln "PRIVMSG NICKSERV :identify #{@nick_pass}"
|
131
|
+
end
|
132
|
+
rescue Exception => e
|
133
|
+
err e
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Logging methods
|
138
|
+
|
139
|
+
def log(str)
|
140
|
+
$stdout.puts "DEBUG: #{str}"
|
141
|
+
end
|
142
|
+
|
143
|
+
def err(exception)
|
144
|
+
$stderr.puts "ERROR: #{exception}"
|
145
|
+
$stderr.puts "TRACE: #{exception.backtrace}"
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
module IrcCat
|
2
|
+
class HttpServer
|
3
|
+
class Unauthorized < StandardError; end
|
4
|
+
class BadRequest < StandardError; end
|
5
|
+
|
6
|
+
def self.run(bot, config)
|
7
|
+
new(bot, config).run
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(bot, config)
|
11
|
+
@bot, @config = bot, config
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
host, port = @config['host'], @config['port']
|
16
|
+
if handler = Rack::Handler.get(handler_name)
|
17
|
+
Thread.new {
|
18
|
+
handler.run(app, :Host => host, :Port => port)
|
19
|
+
}
|
20
|
+
else
|
21
|
+
raise "Could not find a valid Rack handler for #{handler_name.inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def handler_name
|
26
|
+
@config["server"] || "mongrel"
|
27
|
+
end
|
28
|
+
|
29
|
+
def app
|
30
|
+
endpoint = self
|
31
|
+
builder = Rack::Builder.new
|
32
|
+
if requires_auth?
|
33
|
+
builder.use Rack::Auth::Basic, "irccat" do |user,pass|
|
34
|
+
auth_user == user && auth_pass == pass
|
35
|
+
end
|
36
|
+
end
|
37
|
+
if prefix = @config["prefix"]
|
38
|
+
builder.map prefix do
|
39
|
+
run endpoint
|
40
|
+
end
|
41
|
+
else
|
42
|
+
builder.run endpoint
|
43
|
+
end
|
44
|
+
builder
|
45
|
+
end
|
46
|
+
|
47
|
+
def auth_user
|
48
|
+
@config["user"]
|
49
|
+
end
|
50
|
+
|
51
|
+
def auth_pass
|
52
|
+
@config["pass"]
|
53
|
+
end
|
54
|
+
|
55
|
+
def requires_auth?
|
56
|
+
if auth_user
|
57
|
+
if auth_pass
|
58
|
+
if auth_pass.empty?
|
59
|
+
raise "Empty HTTP password in config"
|
60
|
+
end
|
61
|
+
true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def call(env)
|
67
|
+
request = Rack::Request.new(env)
|
68
|
+
|
69
|
+
case request.path_info
|
70
|
+
when "/"
|
71
|
+
[200, {"Content-Type" => "text/plain"}, "OK"]
|
72
|
+
when "/topic"
|
73
|
+
message = request.params["message"]
|
74
|
+
if channel = request.params["channel"]
|
75
|
+
@bot.join_channel("##{channel}", request.params["key"])
|
76
|
+
@bot.topic("##{channel}", message)
|
77
|
+
else
|
78
|
+
raise "Need a channel"
|
79
|
+
end
|
80
|
+
|
81
|
+
[200, {"Content-Type" => "text/plain"}, "OK"]
|
82
|
+
when "/send"
|
83
|
+
raise "Send support is not enabled" unless @config["send"]
|
84
|
+
|
85
|
+
if @config['message_param']
|
86
|
+
message = request.params[@config['message_param']]
|
87
|
+
else
|
88
|
+
message = request.params["message"]
|
89
|
+
end
|
90
|
+
|
91
|
+
if channel = request.params["channel"]
|
92
|
+
@bot.join_channel("##{channel}", request.params["key"])
|
93
|
+
@bot.say("##{channel}", message)
|
94
|
+
elsif nick = request.params["nick"]
|
95
|
+
@bot.say(nick, message)
|
96
|
+
elsif channels = @config['channels']
|
97
|
+
channels.each do |channel|
|
98
|
+
@bot.say(channel, message)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
raise "Unkown action"
|
102
|
+
end
|
103
|
+
|
104
|
+
[200, {"Content-Type" => "text/plain"}, "OK"]
|
105
|
+
when "/github"
|
106
|
+
raise "GitHub support is not enabled" unless @config["github"]
|
107
|
+
|
108
|
+
data = JSON.parse(request.POST['payload'])
|
109
|
+
|
110
|
+
repository = data['repository']['name']
|
111
|
+
data['commits'].each do |commit|
|
112
|
+
topic = commit['message'].split("\n").first
|
113
|
+
ref = commit['id'][0,7]
|
114
|
+
author = commit['author']['name']
|
115
|
+
@bot.announce "#{topic} - #{ref} - #{author}"
|
116
|
+
end
|
117
|
+
|
118
|
+
[200, {"Content-Type" => "text/plain"}, "OK"]
|
119
|
+
else
|
120
|
+
[404, {}, ""]
|
121
|
+
end
|
122
|
+
rescue Unauthorized
|
123
|
+
[401, {'WWW-Authenticate' => %(Basic realm="IrcCat")}, 'Authorization Required']
|
124
|
+
rescue BadRequest
|
125
|
+
[400, {}, 'Bad Request']
|
126
|
+
rescue Exception => e
|
127
|
+
puts "Got an error: #{e.class}, #{e.message}"
|
128
|
+
e.backtrace.each do |b|
|
129
|
+
puts "- #{b}"
|
130
|
+
end
|
131
|
+
[503, {}, "ERR"]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# this class has dubious semantics and we only have it so that
|
2
|
+
# people can write params[:key] instead of params['key']
|
3
|
+
|
4
|
+
class HashWithIndifferentAccess < Hash
|
5
|
+
def initialize(constructor = {})
|
6
|
+
if constructor.is_a?(Hash)
|
7
|
+
super()
|
8
|
+
update(constructor)
|
9
|
+
else
|
10
|
+
super(constructor)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def default(key = nil)
|
15
|
+
if key.is_a?(Symbol) && include?(key = key.to_s)
|
16
|
+
self[key]
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
23
|
+
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
24
|
+
|
25
|
+
def []=(key, value)
|
26
|
+
regular_writer(convert_key(key), convert_value(value))
|
27
|
+
end
|
28
|
+
|
29
|
+
def update(other_hash)
|
30
|
+
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
alias_method :merge!, :update
|
35
|
+
|
36
|
+
def key?(key)
|
37
|
+
super(convert_key(key))
|
38
|
+
end
|
39
|
+
|
40
|
+
alias_method :include?, :key?
|
41
|
+
alias_method :has_key?, :key?
|
42
|
+
alias_method :member?, :key?
|
43
|
+
|
44
|
+
def fetch(key, *extras)
|
45
|
+
super(convert_key(key), *extras)
|
46
|
+
end
|
47
|
+
|
48
|
+
def values_at(*indices)
|
49
|
+
indices.collect {|key| self[convert_key(key)]}
|
50
|
+
end
|
51
|
+
|
52
|
+
def dup
|
53
|
+
HashWithIndifferentAccess.new(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
def merge(hash)
|
57
|
+
self.dup.update(hash)
|
58
|
+
end
|
59
|
+
|
60
|
+
def delete(key)
|
61
|
+
super(convert_key(key))
|
62
|
+
end
|
63
|
+
|
64
|
+
def stringify_keys!; self end
|
65
|
+
def symbolize_keys!; self end
|
66
|
+
|
67
|
+
protected
|
68
|
+
def convert_key(key)
|
69
|
+
key.kind_of?(Symbol) ? key.to_s : key
|
70
|
+
end
|
71
|
+
def convert_value(value)
|
72
|
+
value.is_a?(Hash) ? value.with_indifferent_access : value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# The TCP Server
|
2
|
+
module IrcCat
|
3
|
+
class TcpServer
|
4
|
+
def self.run(bot, config)
|
5
|
+
new(bot, config).run
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(bot, config)
|
9
|
+
@bot, @config = bot, config
|
10
|
+
end
|
11
|
+
|
12
|
+
def run
|
13
|
+
Thread.new do
|
14
|
+
socket = TCPserver.new(ip, port)
|
15
|
+
puts "Starting TCP (#{ip}:#{port})"
|
16
|
+
|
17
|
+
loop do
|
18
|
+
Thread.start(socket.accept) do |s|
|
19
|
+
str = s.recv(@config['size'])
|
20
|
+
sstr = str.split(/\n/)
|
21
|
+
sstr.each do |l|
|
22
|
+
@bot.announce("#{l}")
|
23
|
+
end
|
24
|
+
s.close
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def ip
|
31
|
+
@config["ip"] || '127.0.0.1'
|
32
|
+
end
|
33
|
+
|
34
|
+
def port
|
35
|
+
@config["port"] || '8080'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/irccat.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'rack'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
current_dir = File.dirname(__FILE__)
|
6
|
+
|
7
|
+
require current_dir + '/irccat/version'
|
8
|
+
require current_dir + '/irccat/indifferent_access'
|
9
|
+
require current_dir + '/irccat/bot'
|
10
|
+
|
11
|
+
module Irccat
|
12
|
+
# Your code goes here...
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: irccat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 15
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 4
|
9
|
+
- 0
|
10
|
+
version: 0.4.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- people
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-03-24 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rack
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: json
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 3
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
version: "0"
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id002
|
49
|
+
- !ruby/object:Gem::Dependency
|
50
|
+
name: mongrel
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
hash: 3
|
58
|
+
segments:
|
59
|
+
- 0
|
60
|
+
version: "0"
|
61
|
+
type: :runtime
|
62
|
+
version_requirements: *id003
|
63
|
+
description: Write a gem description
|
64
|
+
email:
|
65
|
+
- folks
|
66
|
+
executables:
|
67
|
+
- icat
|
68
|
+
- iecho
|
69
|
+
- irccat
|
70
|
+
- irccat-config
|
71
|
+
extensions: []
|
72
|
+
|
73
|
+
extra_rdoc_files: []
|
74
|
+
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- README.txt
|
79
|
+
- Rakefile
|
80
|
+
- bin/icat
|
81
|
+
- bin/iecho
|
82
|
+
- bin/irccat
|
83
|
+
- bin/irccat-config
|
84
|
+
- examples/client.yml
|
85
|
+
- examples/server.yml
|
86
|
+
- irccat.gemspec
|
87
|
+
- lib/irccat.rb
|
88
|
+
- lib/irccat/bot.rb
|
89
|
+
- lib/irccat/http_server.rb
|
90
|
+
- lib/irccat/indifferent_access.rb
|
91
|
+
- lib/irccat/tcp_client.rb
|
92
|
+
- lib/irccat/tcp_server.rb
|
93
|
+
- lib/irccat/version.rb
|
94
|
+
has_rdoc: true
|
95
|
+
homepage: ""
|
96
|
+
licenses: []
|
97
|
+
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
requirements: []
|
122
|
+
|
123
|
+
rubyforge_project: irccat
|
124
|
+
rubygems_version: 1.6.0
|
125
|
+
signing_key:
|
126
|
+
specification_version: 3
|
127
|
+
summary: Write a gem summary
|
128
|
+
test_files: []
|
129
|
+
|