rbitter 0.1.0 → 0.1.2

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +21 -21
  3. data/.rspec +2 -2
  4. data/.travis.yml +15 -15
  5. data/Gemfile +12 -12
  6. data/LICENSE.txt +22 -22
  7. data/Rakefile +8 -8
  8. data/XMLRPC.md +19 -19
  9. data/bin/rbitter +20 -20
  10. data/lib/rbitter/arcserver.rb +130 -126
  11. data/lib/rbitter/console.rb +93 -93
  12. data/lib/rbitter/default/config_json.rb +37 -38
  13. data/lib/rbitter/dlthread.rb +62 -65
  14. data/lib/rbitter/env.rb +62 -62
  15. data/lib/rbitter/libtwitter_connection_override.rb +45 -45
  16. data/lib/rbitter/records.rb +121 -121
  17. data/lib/rbitter/records_migrate/20150327_add_index.rb +11 -11
  18. data/lib/rbitter/records_migrate/20150504_add_replyto_column.rb +11 -11
  19. data/lib/rbitter/streaming.rb +104 -104
  20. data/lib/rbitter/version.rb +20 -20
  21. data/lib/rbitter/xmlrpc.rb +3 -3
  22. data/lib/rbitter/xmlrpcd/base.rb +24 -24
  23. data/lib/rbitter/xmlrpcd/rpchandles.rb +11 -11
  24. data/lib/rbitter/xmlrpcd/xmlrpc_auth_server.rb +82 -82
  25. data/lib/rbitter/xmlrpcd/xmlrpcd.rb +69 -69
  26. data/lib/rbitter.rb +86 -86
  27. data/rbitter.gemspec +46 -42
  28. data/spec/config/default.json +32 -32
  29. data/spec/rbitter/arcserver_spec.rb +30 -30
  30. data/spec/rbitter/console_spec.rb +9 -9
  31. data/spec/rbitter/default/config_json_spec.rb +3 -3
  32. data/spec/rbitter/dlthread_spec.rb +8 -13
  33. data/spec/rbitter/env_spec.rb +62 -62
  34. data/spec/rbitter/libtwitter_connection_override_spec.rb +8 -8
  35. data/spec/rbitter/records_spec.rb +13 -13
  36. data/spec/rbitter/streaming_spec.rb +9 -9
  37. data/spec/rbitter/version_spec.rb +8 -8
  38. data/spec/rbitter/xmlrpc_spec.rb +8 -8
  39. data/spec/rbitter/xmlrpcd/base_spec.rb +29 -29
  40. data/spec/rbitter/xmlrpcd/rpchandles_spec.rb +10 -10
  41. data/spec/rbitter/xmlrpcd/xmlrpc_auth_server_spec.rb +8 -8
  42. data/spec/rbitter/xmlrpcd/xmlrpcd_spec.rb +9 -9
  43. data/spec/rbitter_spec.rb +42 -42
  44. data/spec/spec_helper.rb +39 -39
  45. metadata +29 -56
@@ -1,121 +1,121 @@
1
- # encoding: utf-8
2
-
3
- require "active_record"
4
- require "date"
5
-
6
- module Rbitter
7
- class Record < ActiveRecord::Base
8
- end
9
- end
10
-
11
- module ARSupport
12
- SCHEME_VERSION = 20150504
13
- SCHEME = {
14
- :marker => :integer, # 0 normal, 1 begin 2 halt
15
- :marker_msg => :string,
16
- :userid => :integer,
17
- :username => :string,
18
- :tweetid => :integer,
19
- :replyto => :integer,
20
- :tweet => :text, # with url unpacked
21
- :date => :datetime,
22
- :rt_count => :integer,
23
- :fav_count => :integer
24
- }
25
-
26
- module_function
27
- def prepared?
28
- ActiveRecord::Base.connection.table_exists?(:records)
29
- end
30
-
31
- def connect_database
32
- if Rbitter['activerecord'] == 'sqlite3'
33
- warn "Warning: If you enable XMLRPC access, using sqlite is not recommended."
34
- warn "Warning: Random crash can happen because of concurrency."
35
-
36
- if RUBY_PLATFORM == 'java'
37
- require "jdbc/sqlite3"
38
- Jdbc::SQLite3.load_driver
39
- ActiveRecord::Base.establish_connection(
40
- adapter: 'jdbcsqlite3',
41
- database: Rbitter['sqlite3']['dbfile'],
42
- timeout: 10000) # Long timeout for slow computer
43
- else
44
- ActiveRecord::Base.establish_connection(
45
- adapter: 'sqlite3',
46
- database: Rbitter['sqlite3']['dbfile'],
47
- timeout: 10000) # Long timeout for slow computer
48
- end
49
- elsif Rbitter['activerecord'] == 'mysql2'
50
- Jdbc::MySQL.load_driver if RUBY_PLATFORM == 'java'
51
-
52
- ActiveRecord::Base.establish_connection(
53
- adapter: (RUBY_PLATFORM == 'java' ? 'jdbcmysql' : 'mysql2'),
54
- host: Rbitter['mysql2']['host'],
55
- port: Rbitter['mysql2']['port'],
56
- database: Rbitter['mysql2']['dbname'],
57
- username: Rbitter['mysql2']['username'],
58
- password: Rbitter['mysql2']['password'],
59
- encoding: "utf8mb4",
60
- collation: "utf8mb4_unicode_ci")
61
- else
62
- raise RuntimeException.new("Unknown configuration value. 'activerecord' value should be sqlite3 or mysql2.")
63
- end
64
- end
65
-
66
- def update_database_scheme
67
- current_version = ActiveRecord::Migrator.current_version
68
- if current_version < SCHEME_VERSION
69
- warn "[records] Your ActiveRecord scheme is outdated."
70
- warn "[records] Migrate... #{current_version} => #{SCHEME_VERSION}"
71
- ActiveRecord::Migrator.migrate(File.expand_path("../records_migrate", __FILE__), SCHEME_VERSION)
72
- end
73
- end
74
-
75
- def prepare option_string=""
76
- ActiveRecord::Schema.define(version: SCHEME_VERSION) {
77
- # MySQL specific option_string:
78
- # utf8mb4 -> supporting UTF-8 4-byte characters (i.e. Emoji)
79
- create_table(:records, { :options => option_string }) do |t|
80
- SCHEME.each_key { |column|
81
- case SCHEME[column]
82
- when :string
83
- t.string column
84
- when :integer
85
- t.integer column, :limit => 8
86
- when :datetime
87
- t.datetime column
88
- when :text
89
- t.text column
90
- else
91
- puts "Unexpected column type '#{SCHEME[column]}' of #{column}"
92
- end
93
- }
94
- end
95
-
96
- add_index :records, :tweetid
97
- }
98
- end
99
-
100
- def any_to_datestring(obj)
101
- if obj.is_a?(String)
102
- # try to parse it
103
- DateTime.parse(obj).strftime("%Y-%m-%d %H:%M:%S")
104
- elsif obj.is_a?(DateTime) or obj.is_a?(Time)
105
- obj.strftime("%Y-%m-%d %H:%M:%S")
106
- else
107
- raise ArgumentError.new("Can\'t automatically extract DateTime info")
108
- end
109
- end
110
-
111
- def export_to_csv(csvfile)
112
- open(csvfile, 'w') { |f|
113
- f.write("marker,marker_msg,userid,username,tweetid,replyto,tweet,date,rt_count,fav_count")
114
- f.write("\n")
115
- Rbitter::Record.find_each { |t|
116
- f.write("#{t.marker},#{t.marker_msg},#{t.userid},#{t.username},#{t.tweetid},")
117
- f.write("#{t.replyto},#{t.tweet},#{t.date},#{t.rt_count},#{t.fav_count}\n")
118
- }
119
- }
120
- end
121
- end
1
+ # encoding: utf-8
2
+
3
+ require "active_record"
4
+ require "date"
5
+
6
+ module Rbitter
7
+ class Record < ActiveRecord::Base
8
+ end
9
+ end
10
+
11
+ module ARSupport
12
+ SCHEME_VERSION = 20150504
13
+ SCHEME = {
14
+ :marker => :integer, # 0 normal, 1 begin 2 halt
15
+ :marker_msg => :string,
16
+ :userid => :integer,
17
+ :username => :string,
18
+ :tweetid => :integer,
19
+ :replyto => :integer,
20
+ :tweet => :text, # with url unpacked
21
+ :date => :datetime,
22
+ :rt_count => :integer,
23
+ :fav_count => :integer
24
+ }
25
+
26
+ module_function
27
+ def prepared?
28
+ ActiveRecord::Base.connection.table_exists?(:records)
29
+ end
30
+
31
+ def connect_database
32
+ if Rbitter['activerecord'] == 'sqlite3'
33
+ warn "Warning: If you enable XMLRPC access, using sqlite is not recommended."
34
+ warn "Warning: Random crash can happen because of concurrency."
35
+
36
+ if RUBY_PLATFORM == 'java'
37
+ require "jdbc/sqlite3"
38
+ Jdbc::SQLite3.load_driver
39
+ ActiveRecord::Base.establish_connection(
40
+ adapter: 'jdbcsqlite3',
41
+ database: Rbitter['sqlite3']['dbfile'],
42
+ timeout: 10000) # Long timeout for slow computer
43
+ else
44
+ ActiveRecord::Base.establish_connection(
45
+ adapter: 'sqlite3',
46
+ database: Rbitter['sqlite3']['dbfile'],
47
+ timeout: 10000) # Long timeout for slow computer
48
+ end
49
+ elsif Rbitter['activerecord'] == 'mysql2'
50
+ Jdbc::MySQL.load_driver if RUBY_PLATFORM == 'java'
51
+
52
+ ActiveRecord::Base.establish_connection(
53
+ adapter: (RUBY_PLATFORM == 'java' ? 'jdbcmysql' : 'mysql2'),
54
+ host: Rbitter['mysql2']['host'],
55
+ port: Rbitter['mysql2']['port'],
56
+ database: Rbitter['mysql2']['dbname'],
57
+ username: Rbitter['mysql2']['username'],
58
+ password: Rbitter['mysql2']['password'],
59
+ encoding: "utf8mb4",
60
+ collation: "utf8mb4_unicode_ci")
61
+ else
62
+ raise RuntimeException.new("Unknown configuration value. 'activerecord' value should be sqlite3 or mysql2.")
63
+ end
64
+ end
65
+
66
+ def update_database_scheme
67
+ current_version = ActiveRecord::Migrator.current_version
68
+ if current_version < SCHEME_VERSION
69
+ warn "[records] Your ActiveRecord scheme is outdated."
70
+ warn "[records] Migrate... #{current_version} => #{SCHEME_VERSION}"
71
+ ActiveRecord::Migrator.migrate(File.expand_path("../records_migrate", __FILE__), SCHEME_VERSION)
72
+ end
73
+ end
74
+
75
+ def prepare option_string=""
76
+ ActiveRecord::Schema.define(version: SCHEME_VERSION) {
77
+ # MySQL specific option_string:
78
+ # utf8mb4 -> supporting UTF-8 4-byte characters (i.e. Emoji)
79
+ create_table(:records, { :options => option_string }) do |t|
80
+ SCHEME.each_key { |column|
81
+ case SCHEME[column]
82
+ when :string
83
+ t.string column
84
+ when :integer
85
+ t.integer column, :limit => 8
86
+ when :datetime
87
+ t.datetime column
88
+ when :text
89
+ t.text column
90
+ else
91
+ puts "Unexpected column type '#{SCHEME[column]}' of #{column}"
92
+ end
93
+ }
94
+ end
95
+
96
+ add_index :records, :tweetid
97
+ }
98
+ end
99
+
100
+ def any_to_datestring(obj)
101
+ if obj.is_a?(String)
102
+ # try to parse it
103
+ DateTime.parse(obj).strftime("%Y-%m-%d %H:%M:%S")
104
+ elsif obj.is_a?(DateTime) or obj.is_a?(Time)
105
+ obj.strftime("%Y-%m-%d %H:%M:%S")
106
+ else
107
+ raise ArgumentError.new("Can\'t automatically extract DateTime info")
108
+ end
109
+ end
110
+
111
+ def export_to_csv(csvfile)
112
+ open(csvfile, 'w') { |f|
113
+ f.write("marker,marker_msg,userid,username,tweetid,replyto,tweet,date,rt_count,fav_count")
114
+ f.write("\n")
115
+ Rbitter::Record.find_each { |t|
116
+ f.write("#{t.marker},#{t.marker_msg},#{t.userid},#{t.username},#{t.tweetid},")
117
+ f.write("#{t.replyto},#{t.tweet},#{t.date},#{t.rt_count},#{t.fav_count}\n")
118
+ }
119
+ }
120
+ end
121
+ end
@@ -1,12 +1,12 @@
1
- # encoding: utf-8
2
-
3
-
4
- class AddIndex < ActiveRecord::Migration
5
- def up
6
- add_index :records, :tweetid
7
- end
8
-
9
- def change
10
- up
11
- end
1
+ # encoding: utf-8
2
+
3
+
4
+ class AddIndex < ActiveRecord::Migration
5
+ def up
6
+ add_index :records, :tweetid
7
+ end
8
+
9
+ def change
10
+ up
11
+ end
12
12
  end
@@ -1,12 +1,12 @@
1
- # encoding: utf-8
2
-
3
-
4
- class AddReplytoColumn < ActiveRecord::Migration
5
- def up
6
- add_column :records, :replyto, :integer, :limit => 8
7
- end
8
-
9
- def change
10
- up
11
- end
1
+ # encoding: utf-8
2
+
3
+
4
+ class AddReplytoColumn < ActiveRecord::Migration
5
+ def up
6
+ add_column :records, :replyto, :integer, :limit => 8
7
+ end
8
+
9
+ def change
10
+ up
11
+ end
12
12
  end
@@ -1,105 +1,105 @@
1
- # encoding: utf-8
2
-
3
- require 'twitter'
4
-
5
- module Rbitter
6
- class DummyStreamClient
7
- def initialize(tokens); end
8
-
9
- def run(&operation_block)
10
- internal(&operation_block)
11
- end
12
-
13
- private
14
- def internal(&operation_block)
15
- tweets = [{
16
- "tweetid" => 1,
17
- "userid" => 1,
18
- "replyto" => nil,
19
- "tweet" => "test",
20
- "rt_count" => 0,
21
- "fav_count" => 0,
22
- "screen_name" => "twitter",
23
- "date" => "2015-01-01 12:11:10",
24
- "media_urls" => ["https://pbs.twimg.com/media/CEPWFtgUgAEmbcV.png"],
25
- "web_urls" => ["https://www.google.com/"]
26
- }]
27
-
28
- tweets.each { |tweet|
29
- yield tweet
30
- }
31
- end
32
- end
33
-
34
- class StreamClient
35
- def initialize(tokens)
36
- @t = Twitter::Streaming::Client.new do |object|
37
- object.consumer_key = tokens['consumer_key']
38
- object.consumer_secret = tokens['consumer_secret']
39
- object.access_token = tokens['access_token']
40
- object.access_token_secret = tokens['access_token_secret']
41
- end
42
- end
43
-
44
- def run(&operation_block)
45
- begin
46
- internal(&operation_block)
47
- rescue EOFError => e
48
- puts "Network unreachable. Retry in 3 seconds..."
49
- sleep 3
50
- retry
51
- end
52
- end
53
-
54
- private
55
- def internal(&operation_block)
56
- @t.user do |tweet|
57
- if tweet.is_a?(Twitter::Tweet)
58
- if tweet.retweet?
59
- tweet = tweet.retweeted_tweet
60
- end
61
-
62
- text = tweet.full_text.gsub(/(\r\n|\n)/, '')
63
-
64
- # unpack uris and media links
65
- media_urls = Array.new
66
- web_urls = Array.new
67
-
68
- if tweet.entities?
69
- if tweet.media?
70
- tweet.media.each { |media|
71
- media_urls.push("#{media.media_uri_https}")
72
- text.gsub!("#{media.url}", "#{media.display_url}")
73
- }
74
- end
75
-
76
- text += " "
77
- text += media_urls.join(" ")
78
-
79
- if tweet.uris?
80
- tweet.uris.each { |uri|
81
- web_urls.push("#{uri.expanded_url}")
82
- text.gsub!("#{uri.url}", "#{uri.expanded_url}")
83
- }
84
- end
85
- end
86
-
87
- res = {
88
- "tweetid" => tweet.id,
89
- "userid" => tweet.user.id,
90
- "replyto" => tweet.in_reply_to_status_id? ? tweet.in_reply_to_status_id : nil,
91
- "tweet" => text,
92
- "rt_count" => tweet.retweet_count,
93
- "fav_count" => tweet.favorite_count,
94
- "screen_name" => tweet.user.screen_name,
95
- "date" => tweet.created_at,
96
- "media_urls" => media_urls,
97
- "web_urls" => web_urls
98
- }
99
-
100
- yield res
101
- end
102
- end
103
- end
104
- end
1
+ # encoding: utf-8
2
+
3
+ require 'twitter'
4
+
5
+ module Rbitter
6
+ class DummyStreamClient
7
+ def initialize(tokens); end
8
+
9
+ def run(&operation_block)
10
+ internal(&operation_block)
11
+ end
12
+
13
+ private
14
+ def internal(&operation_block)
15
+ tweets = [{
16
+ "tweetid" => 1,
17
+ "userid" => 1,
18
+ "replyto" => nil,
19
+ "tweet" => "test",
20
+ "rt_count" => 0,
21
+ "fav_count" => 0,
22
+ "screen_name" => "twitter",
23
+ "date" => "2015-01-01 12:11:10",
24
+ "media_urls" => ["https://pbs.twimg.com/media/CEPWFtgUgAEmbcV.png"],
25
+ "web_urls" => ["https://www.google.com/"]
26
+ }]
27
+
28
+ tweets.each { |tweet|
29
+ yield tweet
30
+ }
31
+ end
32
+ end
33
+
34
+ class StreamClient
35
+ def initialize(tokens)
36
+ @t = Twitter::Streaming::Client.new do |object|
37
+ object.consumer_key = tokens['consumer_key']
38
+ object.consumer_secret = tokens['consumer_secret']
39
+ object.access_token = tokens['access_token']
40
+ object.access_token_secret = tokens['access_token_secret']
41
+ end
42
+ end
43
+
44
+ def run(&operation_block)
45
+ begin
46
+ internal(&operation_block)
47
+ rescue EOFError => e
48
+ puts "Network unreachable. Retry in 3 seconds..."
49
+ sleep 3
50
+ retry
51
+ end
52
+ end
53
+
54
+ private
55
+ def internal(&operation_block)
56
+ @t.user do |tweet|
57
+ if tweet.is_a?(Twitter::Tweet)
58
+ if tweet.retweet?
59
+ tweet = tweet.retweeted_tweet
60
+ end
61
+
62
+ text = tweet.full_text.gsub(/(\r\n|\n)/, '')
63
+
64
+ # unpack uris and media links
65
+ media_urls = Array.new
66
+ web_urls = Array.new
67
+
68
+ if tweet.entities?
69
+ if tweet.media?
70
+ tweet.media.each { |media|
71
+ media_urls.push("#{media.media_uri_https}")
72
+ text.gsub!("#{media.url}", "#{media.display_url}")
73
+ }
74
+ end
75
+
76
+ text += " "
77
+ text += media_urls.join(" ")
78
+
79
+ if tweet.uris?
80
+ tweet.uris.each { |uri|
81
+ web_urls.push("#{uri.expanded_url}")
82
+ text.gsub!("#{uri.url}", "#{uri.expanded_url}")
83
+ }
84
+ end
85
+ end
86
+
87
+ res = {
88
+ "tweetid" => tweet.id,
89
+ "userid" => tweet.user.id,
90
+ "replyto" => tweet.in_reply_to_status_id? ? tweet.in_reply_to_status_id : nil,
91
+ "tweet" => text,
92
+ "rt_count" => tweet.retweet_count,
93
+ "fav_count" => tweet.favorite_count,
94
+ "screen_name" => tweet.user.screen_name,
95
+ "date" => tweet.created_at,
96
+ "media_urls" => media_urls,
97
+ "web_urls" => web_urls
98
+ }
99
+
100
+ yield res
101
+ end
102
+ end
103
+ end
104
+ end
105
105
  end
@@ -1,20 +1,20 @@
1
- module Rbitter
2
- PRODUCT_NAME = "Rbitter"
3
- VERSION = "0.1.0"
4
-
5
- def major
6
- VERSION.match(/^([0-9]+)\./)[1]
7
- end
8
-
9
- def minor
10
- VERSION.match(/\.([0-9]+)\./)[1]
11
- end
12
-
13
- def patchlv
14
- VERSION.match(/\.([0-9]+)$/)[1]
15
- end
16
-
17
- def version_string
18
- "#{PRODUCT_NAME} #{VERSION}"
19
- end
20
- end
1
+ module Rbitter
2
+ PRODUCT_NAME = "Rbitter"
3
+ VERSION = "0.1.2"
4
+
5
+ def major
6
+ VERSION.match(/^([0-9]+)\./)[1]
7
+ end
8
+
9
+ def minor
10
+ VERSION.match(/\.([0-9]+)\./)[1]
11
+ end
12
+
13
+ def patchlv
14
+ VERSION.match(/\.([0-9]+)$/)[1]
15
+ end
16
+
17
+ def version_string
18
+ "#{PRODUCT_NAME} #{VERSION}"
19
+ end
20
+ end
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
2
- #
3
-
1
+ # encoding: utf-8
2
+ #
3
+
4
4
  require "rbitter/xmlrpcd/xmlrpcd"
@@ -1,25 +1,25 @@
1
- # encoding: utf-8
2
-
3
- module RPCHandles
4
- RH_INFO = Struct.new("RPCHANDLE_INFO", :name, :version, :author, :description) {
5
- def digest
6
- "<rpchandle: #{name}-#{version} (written by #{author}, #{description})>"
7
- end
8
- }
9
-
10
- module BaseHandle
11
- # If a handler doesn't require an authorization, please inherit below class
12
- class NoAuth < Object
13
- def self.auth?
14
- false
15
- end
16
- end
17
-
18
- # If a handler does require an authorization, please inherit below class
19
- class Auth < Object
20
- def self.auth?
21
- true
22
- end
23
- end
24
- end
1
+ # encoding: utf-8
2
+
3
+ module RPCHandles
4
+ RH_INFO = Struct.new("RPCHANDLE_INFO", :name, :version, :author, :description) {
5
+ def digest
6
+ "<rpchandle: #{name}-#{version} (written by #{author}, #{description})>"
7
+ end
8
+ }
9
+
10
+ module BaseHandle
11
+ # If a handler doesn't require an authorization, please inherit below class
12
+ class NoAuth < Object
13
+ def self.auth?
14
+ false
15
+ end
16
+ end
17
+
18
+ # If a handler does require an authorization, please inherit below class
19
+ class Auth < Object
20
+ def self.auth?
21
+ true
22
+ end
23
+ end
24
+ end
25
25
  end
@@ -1,12 +1,12 @@
1
- # encoding: utf-8
2
-
3
- module RPCHandles
4
- # Override this function will activate authentication feature.
5
- # You can write and add RPCHandle. See 'rpc' folder.
6
-
7
- @@auth_pool = nil
8
- module_function
9
- def auth
10
- @@auth_pool
11
- end
1
+ # encoding: utf-8
2
+
3
+ module RPCHandles
4
+ # Override this function will activate authentication feature.
5
+ # You can write and add RPCHandle. See 'rpc' folder.
6
+
7
+ @@auth_pool = nil
8
+ module_function
9
+ def auth
10
+ @@auth_pool
11
+ end
12
12
  end