bot_nyan 0.1.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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bot_nyan.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Taiki ONO
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,63 @@
1
+ # About
2
+
3
+ Bot_nyan is simple twitter-bot-framework with DSL like Sinatra.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'bot_nyan'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install bot_nyan
18
+
19
+ Or clone and build yourself:
20
+
21
+ $ git clone git://github.com/taiki45/bot_nyan.git && cd bot_nyan
22
+
23
+ $ gem build bot_nyan.gemspec
24
+
25
+ $ rake install
26
+
27
+ ## Usage
28
+
29
+ Simple echo and say-hello Bot.
30
+
31
+ ```ruby
32
+ # bot.rb
33
+ # -*- encoding: utf-8 -*-
34
+ require 'bot_nyan'
35
+
36
+ set :consumer_key, {:key => 'XXXXXX',
37
+ :secret => 'XXXXXX'}
38
+ set :access_token, {:token => 'XXXXXX',
39
+ :secret => 'XXXXXX'}
40
+ set :name, 'my_bot_name'
41
+
42
+ on_matched_reply /(^@my_bot_name\s)(Hello)/u do |status, user|
43
+ reply "@#{user.screen_name} Hello!"
44
+ end
45
+
46
+ on_replied do |status, user|
47
+ reply "@#{user.screen_name} #{status.text}"
48
+ end
49
+ ```
50
+
51
+ And Simply do it.
52
+
53
+ ```
54
+ $ ruby bot.rb
55
+ ```
56
+
57
+ ## Contributing
58
+
59
+ 1. Fork it
60
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
61
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
62
+ 4. Push to the branch (`git push origin my-new-feature`)
63
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/TODO.md ADDED
@@ -0,0 +1,5 @@
1
+ # TODOs
2
+
3
+ ## Add specs
4
+
5
+ ## Make twitter event object and Wrap twitter status
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bot_nyan/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "bot_nyan"
8
+ gem.version = BotNyan::VERSION
9
+ gem.authors = ["Taiki ONO"]
10
+ gem.email = ["taiks.4559@gmail.com"]
11
+ gem.description = %q{Bot_nyan is quickly creating twitter-bot in Ruby with Sinatra like DSL}
12
+ gem.summary = %q{Classy twitter-bot-framework in a DSL}
13
+ gem.homepage = "http://taiki45.github.com/bot_nyan"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'oauth', "~>0.4.7"
21
+ gem.add_dependency 'twitter', "~>3.7.0"
22
+ end
@@ -0,0 +1,4 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'bot_nyan/base'
4
+ require 'bot_nyan/main'
@@ -0,0 +1,304 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'net/https'
3
+ require 'uri'
4
+ require 'oauth'
5
+ require 'twitter'
6
+ require 'json'
7
+ require 'logger'
8
+
9
+ require "bot_nyan/version"
10
+
11
+ module BotNyan
12
+
13
+ # For print debugs, infos, warns
14
+ module Info
15
+ def logger_set!(cond)
16
+ @logger = Logger.new STDOUT
17
+ if cond
18
+ @logger.level = Logger::DEBUG
19
+ else
20
+ @logger.level = Logger::INFO
21
+ end
22
+ end
23
+
24
+ def info(msg)
25
+ @logger.info msg
26
+ end
27
+
28
+ def warn(msg)
29
+ @logger.warn msg
30
+ end
31
+
32
+ def error(msg)
33
+ @logger.error msg
34
+ end
35
+
36
+ def debug(msg)
37
+ @logger.debug msg
38
+ end
39
+ end
40
+
41
+ class Base
42
+ include BotNyan::Info
43
+
44
+ def self.run!
45
+ self.new.run
46
+ end
47
+
48
+ def initialize
49
+ logger_set! debug?
50
+ end
51
+
52
+ def debug?
53
+ nil
54
+ end
55
+
56
+ def run
57
+ @wrapper = set_wrapper
58
+ info "starting bot for @#{name}"
59
+ begin
60
+ loop do
61
+ begin
62
+ @wrapper.connect do |event|
63
+ catch :halt do
64
+ debug event.event
65
+ if event.text and event.text.match /(^@#{name}\s)/u
66
+ debug "tweet event"
67
+ debug event
68
+ match? event
69
+ else
70
+ debug 'not tweets'
71
+ debug event
72
+ end
73
+ end
74
+ end
75
+ rescue Timeout::Error
76
+ info "reconnectting to twitter..."
77
+ sleep 30
78
+ end
79
+ end
80
+ rescue Interrupt
81
+ info "\nexitting bot service for @#{name}..."
82
+ exit 0
83
+ end
84
+ end
85
+
86
+ def set_wrapper
87
+ Wrapper::TwitterWrapper.new name, consumer_key, access_token, debug?
88
+ end
89
+
90
+ def match?(event)
91
+ get_matched_reply_actions.each do |regexp, block|
92
+ if event.text.match regexp
93
+ debug "matched to #{regexp}"
94
+ instance_exec event, event.user, &block
95
+ throw :halt
96
+ end
97
+ end
98
+ if event.text.match(/(^@#{name}\s)/u) and get_relpy_action
99
+ debug "respond to default reply"
100
+ instance_exec event, event.user, &get_relpy_action
101
+ throw :halt
102
+ end
103
+ end
104
+
105
+ # Inner methods and called from given blocks
106
+ def status
107
+ @wrapper.status
108
+ end
109
+
110
+ def update(msg)
111
+ @wrapper.update msg
112
+ end
113
+
114
+ def reply(msg)
115
+ @wrapper.reply msg
116
+ end
117
+
118
+ # Inner methods that call self.class methods
119
+ def get_matched_reply_actions
120
+ self.class.get_matched_reply_actions
121
+ end
122
+
123
+ def get_relpy_action
124
+ self.class.get_relpy_action
125
+ end
126
+
127
+ def add_on_replied(&block)
128
+ @replied_action = block
129
+ end
130
+
131
+ class << self
132
+ # Outer methods that called from inner of Base
133
+ def get_matched_reply_actions
134
+ @matched_reply_actions
135
+ end
136
+
137
+ def get_relpy_action
138
+ @reply_action
139
+ end
140
+
141
+ # Outer methods that called from main objrct
142
+ def on_matched_reply(regexp, &block)
143
+ @matched_reply_actions ||= {}
144
+ @matched_reply_actions[regexp] = block
145
+ end
146
+
147
+ def on_replied(&block)
148
+ @reply_action ||= block
149
+ end
150
+
151
+ def set(key, value)
152
+ keys = [:consumer_key, :access_token, :name, :debug?]
153
+ if keys.include? key
154
+ self.instance_eval do
155
+ define_method key, lambda { value }
156
+ private key
157
+ end
158
+ else
159
+ raise NotImplementedError, "This option is not support, #{key}, #{value}"
160
+ end
161
+ end
162
+
163
+ def set!(key, value)
164
+ keys = [:run?]
165
+ if keys.include? key
166
+ define_singleton_method key, value
167
+ else
168
+ raise NotImplementedError, "This option is not support, #{key}, #{value}"
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ # Wrapper module
175
+ # it wrapping twitter connect or update methods
176
+ module Wrapper
177
+ class TwitterWrapper
178
+ include BotNyan::Info
179
+
180
+ def initialize(name, consumer_keys, access_tokens, cond)
181
+ logger_set! cond
182
+ @name = name
183
+ unless name and consumer_keys and access_tokens
184
+ error @name, @consumer_keys, @access_tokens
185
+ raise RuntimeError, "Necessarys are not difined!"
186
+ end
187
+ @consumer = OAuth::Consumer.new(
188
+ consumer_keys[:key],
189
+ consumer_keys[:secret],
190
+ :site => 'http://twitter.com'
191
+ )
192
+ @access_token = OAuth::AccessToken.new(
193
+ @consumer,
194
+ access_tokens[:token],
195
+ access_tokens[:secret]
196
+ )
197
+ Twitter.configure do |c|
198
+ c.consumer_key = consumer_keys[:key]
199
+ c.consumer_secret = consumer_keys[:secret]
200
+ c.oauth_token = access_tokens[:token]
201
+ c.oauth_token_secret = access_tokens[:secret]
202
+ end
203
+ @json = nil
204
+ end
205
+
206
+ def connect
207
+ uri = URI.parse("https://userstream.twitter.com/2/user.json?track=#{@name}")
208
+ https = Net::HTTP.new(uri.host, uri.port)
209
+ https.use_ssl = true
210
+
211
+ https.start do |https|
212
+ request = Net::HTTP::Post.new(uri.request_uri)
213
+ request["User-Agent"] = "bot servise for @#{@name}"
214
+ request.oauth!(https, @consumer, @access_token)
215
+
216
+ buf = String.new
217
+ https.request(request) do |response|
218
+ raise Exception.new "Authorize failed. #{request.body}" if response.code == '401'
219
+ response.read_body do |chunk|
220
+ buf << chunk
221
+ while (line = buf[/.+?(\r\n)+/m]) != nil
222
+ begin
223
+ buf.sub!(line,"")
224
+ line.strip!
225
+ status = JSON.parse(line)
226
+ rescue
227
+ break
228
+ end
229
+ @json = status
230
+ yield status
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end
236
+
237
+ # wrapping methods for twitter state
238
+ def status
239
+ @json
240
+ end
241
+
242
+ def update(msg)
243
+ update_core :update, msg, @json
244
+ end
245
+
246
+ def reply(msg)
247
+ update_core :reply, msg, @json
248
+ end
249
+
250
+ def update_core(mode, msg, json)
251
+ i = 0
252
+ if mode == :update
253
+ post_text = lambda do |m|
254
+ @access_token.post(
255
+ '/statuses/update.json',
256
+ 'status' => m,
257
+ )
258
+ end
259
+ elsif mode == :reply
260
+ post_text = lambda do |m|
261
+ @access_token.post(
262
+ '/statuses/update.json',
263
+ 'status' => m,
264
+ 'in_reply_to_status_id' => json['id']
265
+ )
266
+ end
267
+ end
268
+ while post_text.call(msg).code == '403' do
269
+ sleep 0.3
270
+ i += 1
271
+ msg << " ."
272
+ if i > 12
273
+ puts "error to post reply to below"
274
+ return false
275
+ end
276
+ end
277
+ puts "replied to #{json['id']}"
278
+ true
279
+ end
280
+ end
281
+ end
282
+
283
+ # Delegator module
284
+ # it delegate some DSL methods to main Object
285
+ module Delegator
286
+ def self.delegate(*methods)
287
+ methods.each do |method_name|
288
+ define_method(method_name) do |*args, &block|
289
+ return super(*args, &block) if respond_to? method_name
290
+ Bot.send(method_name, *args, &block)
291
+ end
292
+ private method_name
293
+ end
294
+ end
295
+
296
+ delegate :set, :on_matched_reply, :on_replied
297
+ end
298
+ end
299
+
300
+ class Hash
301
+ def method_missing(name, *args)
302
+ self[name.to_s]
303
+ end
304
+ end
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'bot_nyan/base'
4
+ require 'optparse'
5
+
6
+ module BotNyan
7
+ class Bot < Base
8
+ set! :run?, lambda { __FILE__ == $0 }
9
+ if ARGV.any?
10
+ OptionParser.new do |op|
11
+ op.on('-d', 'set the debug print is on') { set :debug?, true }
12
+ end.parse!(ARGV.dup)
13
+ end
14
+ end
15
+ at_exit { BotNyan::Bot.run! if $!.nil? }
16
+ end
17
+
18
+ extend BotNyan::Delegator
@@ -0,0 +1,3 @@
1
+ module BotNyan
2
+ VERSION = "0.1.1"
3
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bot_nyan
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Taiki ONO
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: oauth
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.4.7
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.4.7
30
+ - !ruby/object:Gem::Dependency
31
+ name: twitter
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 3.7.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 3.7.0
46
+ description: Bot_nyan is quickly creating twitter-bot in Ruby with Sinatra like DSL
47
+ email:
48
+ - taiks.4559@gmail.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - LICENSE.txt
56
+ - README.md
57
+ - Rakefile
58
+ - TODO.md
59
+ - bot_nyan.gemspec
60
+ - lib/bot_nyan.rb
61
+ - lib/bot_nyan/base.rb
62
+ - lib/bot_nyan/main.rb
63
+ - lib/bot_nyan/version.rb
64
+ homepage: http://taiki45.github.com/bot_nyan
65
+ licenses: []
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 1.8.23
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Classy twitter-bot-framework in a DSL
88
+ test_files: []