mitten 0.0.1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ /configs/environment.yaml
2
+ /lib/mint.rb.pid
3
+ /amazonrc
4
+
5
+ ## MAC OS
6
+ .DS_Store
7
+
8
+ ## TEXTMATE
9
+ *.tmproj
10
+ tmtags
11
+
12
+ ## EMACS
13
+ *~
14
+ \#*
15
+ .\#*
16
+
17
+ ## VIM
18
+ *.swp
19
+
20
+ ## PROJECT::GENERAL
21
+ coverage
22
+ rdoc
23
+ pkg
24
+
25
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 Tomohiro, TAIRA
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,93 @@
1
+ = Mitten
2
+
3
+ == Description
4
+
5
+ Mitten is A Ruby IRC Bot Pluggable Framework.
6
+
7
+
8
+ == Require
9
+
10
+ === RubyGems
11
+
12
+ 1. rake
13
+
14
+ $ sudo gem install rake
15
+
16
+ 2. rspec
17
+
18
+ $ sudo gem install rspec
19
+
20
+ 3. net-irc
21
+
22
+ $ sudo gem install net-irc
23
+
24
+ 4. daemons
25
+
26
+ $ sudo gem install daemons
27
+
28
+
29
+ == Usage
30
+
31
+ 1. Usage
32
+
33
+ ./script/server -h
34
+ ./script/daemon run -- -h
35
+
36
+ 2. Start
37
+
38
+ ./script/daemon start
39
+
40
+ 3. Restart
41
+
42
+ ./script/daemon restart
43
+
44
+ 4. Stop
45
+
46
+ ./script/daemon stop
47
+
48
+ 5. Changed config file
49
+
50
+ ./script/daemon start -- -c lib/environment.yaml
51
+
52
+ 6. Run on the top(not daemonize)
53
+
54
+ ./script/server
55
+ ./script/daemon run
56
+
57
+
58
+ == Rake tasks
59
+
60
+ 1. Display Rake tasks
61
+
62
+ $ rake -T
63
+
64
+ 2. Unit Test
65
+
66
+ $ rake spec
67
+
68
+ 3. Generates Rdoc Documentation
69
+
70
+ $ rake rdoc
71
+
72
+
73
+ == Note on Patches/Pull Requests
74
+
75
+ - Fork the project.
76
+ - Make your feature addition or bug fix.
77
+ - Add tests for it. This is important so I don't break it in a
78
+ future version unintentionally.
79
+ - Commit, do not mess with rakefile, version, or history.
80
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
81
+ - Send me a pull request. Bonus points for topic branches.
82
+
83
+
84
+ == Copyright
85
+
86
+ Copyright (c) 2010 Tomohiro, TAIRA.
87
+
88
+
89
+ == Licence
90
+
91
+ The MIT License
92
+
93
+ See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = 'mitten'
8
+ gem.summary = 'IRC Bot Framework'
9
+ gem.description = 'Mitten is A Ruby IRC Bot Pluggable Framework'
10
+ gem.email = 'tomohiro.t@gmail.com'
11
+ gem.homepage = 'http://github.com/Tomohiro/mitten'
12
+ gem.authors = ['Tomohiro, TAIRA']
13
+ gem.add_development_dependency 'rspec', '>= 1.2.9'
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "mitten #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ rdoc.options = ['-c', 'utf-8', '-N']
46
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
data/bin/daemon ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'daemons'
5
+
6
+ root = File.expand_path(File.dirname(__FILE__))
7
+
8
+ Daemons.run("#{root}/server", {:app_name => 'mittend'})
data/bin/server ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ MITTEN_ROOT = File.expand_path('..', File.dirname(__FILE__))
4
+ Dir.chdir(MITTEN_ROOT)
5
+ $: << MITTEN_ROOT
6
+
7
+ require 'lib/mitten.rb'
8
+
9
+ puts 'Booting Mitten Server...'
10
+ Mitten::Core.boot
@@ -0,0 +1,28 @@
1
+ ---
2
+ production:
3
+ host: tiarra.example.com
4
+ port: 6667
5
+ user: Mitten
6
+ nick: Mitten
7
+ real: Mitten bot via Tiarra
8
+ pass:
9
+ channel: '#Mitten@example'
10
+ plugin_dir: example # normal => plugins
11
+ charset:
12
+ default: utf-8
13
+
14
+ development:
15
+ host: chat.freenode.net
16
+ port: 6667
17
+ user: Mitten
18
+ nick: Mitten
19
+ real: Mitten is IRC Bot Framework
20
+ pass:
21
+ channel: '#Mitten'
22
+ plugin_dir: example # normal => plugins
23
+ charset:
24
+ default: utf-8
25
+
26
+ plugins:
27
+ Sample:
28
+ channel: '#Mitten@example'
@@ -0,0 +1,14 @@
1
+ ---
2
+ time:
3
+ 0900: '9時をお知らせするよ♪'
4
+ 1000: '10時をお知らせするよ♪'
5
+ 1100: '11時をお知らせするよ♪'
6
+ 1200: '12時をお知らせするよ♪'
7
+ 1300: '13時をお知らせするよ♪'
8
+ 1400: '14時をお知らせするよ♪'
9
+ 1500: '15時をお知らせするよ♪'
10
+ 1600: '16時をお知らせするよ♪'
11
+ 1700: '17時をお知らせするよ♪'
12
+ 1800: '18時をお知らせするよ♪'
13
+ 2100: '21時をお知らせするよ♪'
14
+ 0000: '深夜0時をお知らせするよ♪'
data/example/sample.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Sample < Mitten::Plugin
2
+ def initialize(*args)
3
+ super
4
+ end
5
+
6
+ def on_privmsg(prefix, channel, message)
7
+ notice(channel, message)
8
+ end
9
+
10
+ def main
11
+ @channels.each { |channel| notice(channel, Time.now.to_s) }
12
+ end
13
+ end
data/lib/mitten.rb ADDED
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $KCODE = 'u'
4
+
5
+ require 'ostruct'
6
+ require 'yaml'
7
+ require 'optparse'
8
+ require 'pathname'
9
+
10
+ require 'rubygems'
11
+ require 'net/irc'
12
+
13
+ require 'lib/utils'
14
+ require 'lib/plugin'
15
+
16
+ module Mitten
17
+ DEFAULT_CONFIG_FILE_PATH = 'configs/environment.yaml'
18
+
19
+ class Core < Net::IRC::Client
20
+ def self.boot
21
+ new.boot
22
+ end
23
+
24
+ def initialize
25
+ load_configs
26
+
27
+ super(@server['host'], @server['port'], {
28
+ :nick => @server['nick'],
29
+ :user => @server['user'],
30
+ :real => @server['real'],
31
+ :pass => @server['pass'],
32
+ :channel => @server['channel']
33
+ })
34
+ end
35
+
36
+ def load_configs
37
+ @mode = 'production'
38
+ config_file = DEFAULT_CONFIG_FILE_PATH
39
+
40
+ ARGV.options do |o|
41
+ o.on('-c', "--config-file CONFIG_FILE", " (default: #{config_file})") { |v| config_file = v }
42
+ o.on('-d', "--development") { |v| @mode = 'development' }
43
+ o.parse!
44
+ end
45
+
46
+ @config = OpenStruct.new(File.open(config_file) { |f| YAML.load(f) })
47
+ @server = @config.method(@mode).call
48
+ end
49
+
50
+ def connect
51
+ puts "TCPSocket open to #{@server['host']}:#{@server['port']}"
52
+ TCPSocket.open(@server['host'], @server['port'])
53
+ end
54
+
55
+ def boot
56
+ begin
57
+ @socket = connect
58
+ @socket.gets
59
+
60
+ post(PASS, @opts.pass) if @opts.pass
61
+ post(NICK, @opts.nick)
62
+ post(USER, @opts.user, '0', '*', @opts.real)
63
+ post(JOIN, @opts.channel) if @opts.channel
64
+
65
+ run_plugins
66
+ rescue Exception => e
67
+ post(NOTICE, @opts.channel, "#{e.class} #{e.to_s}") if @opts.channel
68
+ @log.error "#{e.class} #{e.to_s}"
69
+ ensure
70
+ finish
71
+ end
72
+ end
73
+
74
+ def run_plugins
75
+ threads = []
76
+ @plugins = load_plugins(@server['plugin_dir'], @config.plugins)
77
+
78
+ threads.push(
79
+ Thread.fork do
80
+ while line = @socket.gets
81
+ message = Message.parse(line)
82
+ if message.command.upcase == 'PRIVMSG'
83
+ @plugins.each do |plugin|
84
+ plugin.on_privmsg(message.prefix, message[0], message[1])
85
+ end
86
+ end
87
+ end
88
+ end
89
+ )
90
+
91
+ @plugins.each do |plugin|
92
+ plugin.before_hook
93
+ threads.push(Thread.fork(plugin) { |p| p.notify })
94
+ end
95
+
96
+ threads.each { |t| t.join }
97
+ end
98
+
99
+ def load_plugins(plugin_dir, plugin_configs)
100
+ class_tables = {}
101
+
102
+ Pathname.glob("#{plugin_dir}/*.rb") do |file|
103
+ plugin = {}
104
+ m = Module.new
105
+ m.module_eval(file.read, file)
106
+ m.constants.each do |name|
107
+ break unless plugin_configs.has_key? name
108
+ const = m.const_get(name)
109
+ if const.is_a? Class
110
+ plugin[name] = {
111
+ :class => const,
112
+ :file => file,
113
+ :configs => plugin_configs[name]
114
+ }
115
+ end
116
+ end
117
+ class_tables.update(plugin)
118
+ end
119
+
120
+ plugins = instantiation(class_tables)
121
+ end
122
+
123
+ def instantiation(class_tables)
124
+ plugins = []
125
+ class_tables.each do |name, plugin|
126
+ plugins << plugin[:class].new(plugin[:configs], @socket)
127
+ puts "Plugin: #{name} is loaded"
128
+ end
129
+
130
+ plugins
131
+ end
132
+ end
133
+ end
data/lib/plugin.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'net/irc'
3
+
4
+ module Mitten
5
+ DEFAULT_SLEEP = 360
6
+
7
+ class Plugin < Net::IRC::Client
8
+ def initialize(config, socket)
9
+ @config = config || {}
10
+ @socket = socket
11
+ @channels = @config['channels'] || @config['channel']
12
+ @sleep = @config['sleep'] || DEFAULT_SLEEP
13
+ end
14
+
15
+ def post(command, *params)
16
+ @socket << Message.new(nil, command, params.map { |s| s.gsub(/\r|\n/, " ") })
17
+ end
18
+
19
+ def notice(*params)
20
+ post(NOTICE, *params)
21
+ end
22
+
23
+ def message(*params)
24
+ post(PRIVMSG, *params)
25
+ end
26
+
27
+ def on_privmsg(prefix, channel, message)
28
+ end
29
+
30
+ def notify
31
+ loop do
32
+ main
33
+ sleep @sleep
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/utils.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'open-uri'
3
+ require 'json'
4
+
5
+ module URI
6
+ def short(uri)
7
+ begin
8
+ login = 'tomohiro'
9
+ api_key = 'R_9ed4fae410ab7b729a15dc91c8f7687c'
10
+
11
+ query = "http://api.j.mp/shorten?version=2.0.1&longUrl=#{uri}&login=#{login}&apiKey=#{api_key}"
12
+ result = JSON.parse(open(query).read)
13
+
14
+ result.first[1].first[1]['shortUrl']
15
+ rescue
16
+ URI.encode(uri)
17
+ end
18
+ end
19
+
20
+ module_function :short
21
+ end
data/mitten.gemspec ADDED
@@ -0,0 +1,84 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mitten}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Tomohiro, TAIRA"]
12
+ s.date = %q{2010-02-21}
13
+ s.description = %q{Mitten is A Ruby IRC Bot Pluggable Framework}
14
+ s.email = %q{tomohiro.t@gmail.com}
15
+ s.executables = ["daemon", "server"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/daemon",
28
+ "bin/server",
29
+ "configs/example_environment.yaml",
30
+ "configs/time_call.yaml",
31
+ "example/sample.rb",
32
+ "lib/mitten.rb",
33
+ "lib/plugin.rb",
34
+ "lib/utils.rb",
35
+ "mitten.gemspec",
36
+ "plugins/amazon_search.rb",
37
+ "plugins/bmi.rb",
38
+ "plugins/codepad.rb",
39
+ "plugins/fortune.rb",
40
+ "plugins/gasoline.rb",
41
+ "plugins/gmail.rb",
42
+ "plugins/google_profile.rb",
43
+ "plugins/google_transit.rb",
44
+ "plugins/google_weather.rb",
45
+ "plugins/mixi_voice.rb",
46
+ "plugins/nanapi.rb",
47
+ "plugins/newspaper_headlines.rb",
48
+ "plugins/openpne_new_diary_check.rb",
49
+ "plugins/ramen.rb",
50
+ "plugins/rate.rb",
51
+ "plugins/screen_time_search.rb",
52
+ "plugins/sun_sign_astrology.rb",
53
+ "plugins/time_call.rb",
54
+ "plugins/tweet.rb",
55
+ "plugins/typhoon.rb",
56
+ "plugins/uri_shorten.rb",
57
+ "spec/mitten_spec.rb",
58
+ "spec/spec.opts",
59
+ "spec/spec_helper.rb"
60
+ ]
61
+ s.homepage = %q{http://github.com/Tomohiro/mitten}
62
+ s.rdoc_options = ["--charset=UTF-8"]
63
+ s.require_paths = ["lib"]
64
+ s.rubygems_version = %q{1.3.5}
65
+ s.summary = %q{IRC Bot Framework}
66
+ s.test_files = [
67
+ "spec/mitten_spec.rb",
68
+ "spec/spec_helper.rb"
69
+ ]
70
+
71
+ if s.respond_to? :specification_version then
72
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
73
+ s.specification_version = 3
74
+
75
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
76
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
77
+ else
78
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
79
+ end
80
+ else
81
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
82
+ end
83
+ end
84
+
@@ -0,0 +1,62 @@
1
+ require 'amazon/aws/search'
2
+
3
+ ENV['AMAZONRCDIR'] = '/usr/local/share/mitten'
4
+ ENV['AMAZONRCFILE'] = 'amazonrc'
5
+
6
+ #
7
+ # It is necessary to create a amazonrc
8
+ #
9
+ #[global]
10
+ # key_id = {AWS Access Key ID}
11
+ # secret_key_id = {AWS Secret Access Key}
12
+ # associate = {Amazon Associate ID}
13
+ # locale = jp
14
+ # cache = true
15
+ # cache_dir = {Cache Directory}
16
+ #
17
+
18
+ class AmazonSearch < Mitten::Plugin
19
+ def initialize(*args)
20
+ super
21
+
22
+ @suffix = @config['suffix'] || 'が欲しい'
23
+ @limit = @config['limit'] || 3
24
+ end
25
+
26
+ def on_privmsg(prefix, channel, message)
27
+ case message
28
+ when /^(.+)#{@suffix}$/
29
+ search($1).each { |item| notice(channel, item) }
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def search(keyword)
36
+ datas = []
37
+ begin
38
+ request = Amazon::AWS::Search::Request.new
39
+ query = Amazon::AWS::ItemSearch.new(:All, { :Keywords => keyword })
40
+ response = Amazon::AWS::ResponseGroup.new(:Small, :OfferSummary)
41
+
42
+ results = request.search(query, response).item_search_response.items.item
43
+
44
+ results[0...@limit].each do |item|
45
+ title = CGI.unescapeHTML(item.item_attributes.title.to_s)
46
+ uri = item.detail_page_url.to_s
47
+
48
+ prices = item.offer_summary
49
+ price = case
50
+ when prices.lowest_new_price : prices.lowest_new_price.formatted_price
51
+ when prices.lowest_used_price : "[中古] #{prices.lowest_used_price.formatted_price}"
52
+ when prices.lowest_collectible_price : "[コレクター] #{prices.lowest_collectible_price.formatted_price}"
53
+ end
54
+
55
+ datas << "Amazon : #{title} #{price} だよ♪ (#{URI.short(uri)})"
56
+ end
57
+ rescue Exception
58
+ datas << '/(^o^)\ 見つからにゃいっ'
59
+ end
60
+ datas
61
+ end
62
+ end
data/plugins/bmi.rb ADDED
@@ -0,0 +1,48 @@
1
+ class BMI < Mitten::Plugin
2
+ def initialize(*args)
3
+ super
4
+
5
+ @prefix = @config['prefix'] || 'BMI'
6
+ end
7
+
8
+ def on_privmsg(prefix, channel, message)
9
+ case message
10
+ when /^#{@prefix} ([0-9].+) ([0-9].+)$/
11
+ judge = judge($1, $2)
12
+ notice(channel, judge) unless judge.empty?
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def judge(height, weight)
19
+ bmi = bmi(height, weight)
20
+
21
+ judge = case
22
+ when 26.4 <= bmi
23
+ '肥満'
24
+ when 24.2 <= bmi
25
+ '過体重'
26
+ when 19.8 <= bmi && bmi < 24.2
27
+ '理想体重'
28
+ when bmi < 17.6
29
+ 'やせすぎ'
30
+ when bmi < 19.8
31
+ 'やせ気味'
32
+ end
33
+
34
+ "あなたの BMI は #{bmi} で #{judge} です! 理想体重は #{ideal_body_weight(height)}kg だよっ"
35
+ end
36
+
37
+ def bmi(height, weight)
38
+ height = height.to_f / 100
39
+ weight = weight.to_f
40
+
41
+ (weight / height / height).to_i
42
+ end
43
+
44
+ def ideal_body_weight(height)
45
+ height = height.to_f / 100
46
+ (22 * height * height).to_i
47
+ end
48
+ end