facy 1.2.9.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/facy/core.rb ADDED
@@ -0,0 +1,104 @@
1
+ module Facy
2
+ module Core
3
+ def config
4
+ @config ||= {}
5
+ end
6
+
7
+ def inits
8
+ @inits ||= []
9
+ end
10
+
11
+ def init(&block)
12
+ inits << block
13
+ end
14
+
15
+ def _init
16
+ load_config
17
+ login_flow
18
+ inits.each { |block| class_eval(&block) }
19
+ log(:info, "core module init success")
20
+ end
21
+
22
+ def load_config
23
+ config.reverse_update(default_config)
24
+ log(:info, "config loaded #{config.to_s}")
25
+ end
26
+
27
+ def default_config
28
+ config = YAML.load_file(File.expand_path("../../../config.yml", __FILE__))
29
+ {
30
+ session_file_folder: "/tmp",
31
+ session_file_name: ".facy_access_token.yml",
32
+ log_folder: "/tmp",
33
+ log_file_name: ".facy_log",
34
+ app_id: config['app_id'],
35
+ app_secret: config['app_secret'],
36
+ permission: config['permission'],
37
+ granted: config['granted'],
38
+ redirect_uri: "http://www.facebook.com/connect/login_success.html",
39
+ prompt: "\e[15;48;5;27m f \e[0m >> ",
40
+ stream_fetch_interval: 2,
41
+ notification_fetch_interval: 2,
42
+ output_interval: 3,
43
+ retry_interval: 2,
44
+ comments_view_num: 10,
45
+ }
46
+ end
47
+
48
+ def start(options={})
49
+ _init
50
+
51
+ EM.run do
52
+ Thread.start do
53
+ while buf = Readline.readline(config[:prompt], true)
54
+ execute(buf.strip)
55
+ end
56
+ end
57
+
58
+ Thread.start do
59
+ EM.add_periodic_timer(config[:stream_fetch_interval]) do
60
+ facebook_stream_fetch
61
+ end
62
+ end
63
+
64
+ Thread.start do
65
+ EM.add_periodic_timer(config[:output_interval]) do
66
+ periodic_output
67
+ end
68
+ end
69
+
70
+ Thread.start do
71
+ EM.add_periodic_timer(config[:notification_fetch_interval]) do
72
+ facebook_notification_fetch
73
+ end
74
+ end
75
+
76
+ Signal.trap("INT") { stop_process }
77
+ Signal.trap("TERM") { stop_process }
78
+ end
79
+ end
80
+
81
+ def mutex
82
+ @mutex ||= Mutex.new
83
+ end
84
+
85
+ def sync(&block)
86
+ mutex.synchronize do
87
+ block.call
88
+ end
89
+ end
90
+
91
+ def async(&block)
92
+ Thread.start { block.call }
93
+ end
94
+
95
+ def stop_process
96
+ puts "\nfacy going to stop..."
97
+ Thread.new {
98
+ EventMachine.stop
99
+ }.join
100
+ end
101
+ end
102
+
103
+ extend Core
104
+ end
@@ -0,0 +1,9 @@
1
+ class String
2
+ def colorize(*code)
3
+ "\e[#{code.join(";")}m#{self}\e[0m"
4
+ end
5
+
6
+ def strip
7
+ self.truncate(50)
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Facy
2
+ class FacebookGraphReqError < Exception ; end
3
+ end
@@ -0,0 +1,108 @@
1
+ module Facy
2
+ module Facebook
3
+ attr_reader :authen_hash, :rest
4
+
5
+ module ConnectionStatus
6
+ NORMAL = 0
7
+ ERROR = 1
8
+ SUSPENDED = 2
9
+ end
10
+
11
+ def facebook_status
12
+ @status ||= ConnectionStatus::NORMAL
13
+ end
14
+
15
+ #RULE: all facebook method should be prefix with facebook
16
+ def facebook_stream_fetch
17
+ return unless facebook_status == ConnectionStatus::NORMAL
18
+ streams = @graph.get_connections("me", "home")
19
+ streams.each { |post| stream_print_queue << graph2item(post) }
20
+ log(:info, "fetch stream ok")
21
+ rescue Koala::Facebook::ServerError
22
+ retry_wait
23
+ rescue Koala::Facebook::APIError
24
+ expired_session
25
+ rescue Exception => e
26
+ error e
27
+ end
28
+
29
+ def facebook_notification_fetch
30
+ return unless facebook_status == ConnectionStatus::NORMAL
31
+ notifications = @graph.get_connections("me", "notifications")
32
+ notifications.each { |notifi| notification_print_queue << graph2item(notifi) }
33
+ log(:info, "fetch notification ok")
34
+ rescue Koala::Facebook::ServerError
35
+ retry_wait
36
+ rescue Koala::Facebook::APIError
37
+ expired_session
38
+ rescue Exception => e
39
+ error e
40
+ end
41
+
42
+ def facebook_post(text)
43
+ @graph.put_wall_post(text)
44
+ rescue Koala::Facebook::ServerError
45
+ retry_wait
46
+ rescue Koala::Facebook::APIError
47
+ expired_session
48
+ rescue Exception => e
49
+ error e
50
+ end
51
+
52
+
53
+ def facebook_like(post_id)
54
+ @graph.put_like(post_id)
55
+ rescue Koala::Facebook::ServerError
56
+ retry_wait
57
+ rescue Koala::Facebook::APIError
58
+ expired_session
59
+ rescue Exception => e
60
+ error e
61
+ end
62
+
63
+ def facebook_set_seen(notification_id)
64
+ @graph.put_connection("#{notification_id}", "unread=false")
65
+ rescue Koala::Facebook::ServerError
66
+ retry_wait
67
+ rescue Koala::Facebook::APIError
68
+ expired_session
69
+ rescue Exception => e
70
+ error e
71
+ end
72
+
73
+
74
+ def facebook_comment(post_id, comment)
75
+ @graph.put_comment(post_id, comment)
76
+ rescue Koala::Facebook::ServerError
77
+ retry_wait
78
+ rescue Koala::Facebook::APIError
79
+ expired_session
80
+ rescue Exception => e
81
+ error e
82
+ end
83
+
84
+ def expired_session
85
+ FileUtils.rm(session_file)
86
+ instant_output(Item.new(info: :info, content: "Please restart facy to obtain new access token!"))
87
+ exit
88
+ end
89
+
90
+ def retry_wait
91
+ log(:error, "facebook server error, need retry")
92
+ instant_output(Item.new(info: :error, content: "facebook server error, retry in #{config[:retry_interval]} seconds"))
93
+ sleep(config[:retry_interval])
94
+ end
95
+
96
+ def login
97
+ token = config[:access_token]
98
+ @graph = Koala::Facebook::API.new(token)
99
+ log(:info, "login ok at facebook module: #{@graph}")
100
+ end
101
+ end
102
+
103
+ extend Facebook
104
+
105
+ init do
106
+ login
107
+ end
108
+ end
@@ -0,0 +1,112 @@
1
+ # coding: utf-8
2
+
3
+ module Facy
4
+ module GetToken
5
+ def session_file
6
+ File.expand_path(config[:session_file_name], config[:session_file_folder])
7
+ end
8
+
9
+ def setup_app_id
10
+ developer_page = "https://developers.facebook.com"
11
+ puts "★ go to #{developer_page} and enter our app_id: "
12
+ browse(developer_page)
13
+ config[:app_id] = STDIN.gets.chomp
14
+ log(:info, "app_id setup success #{config[:app_id]}")
15
+ end
16
+
17
+ def setup_app_secret
18
+ developer_page = "https://developers.facebook.com"
19
+ puts "★ go to #{developer_page} and enter our app_secret: "
20
+ config[:app_secret] = STDIN.gets.chomp
21
+ log(:info, "app_secret setup success #{config[:app_id]}")
22
+ end
23
+
24
+ def save_config_file
25
+ File.open(File.expand_path('../../../config.yml', __FILE__), 'w') do |f|
26
+ conf = {
27
+ "app_id" => config[:app_id],
28
+ "app_secret" => config[:app_secret],
29
+ "permission" => config[:permission],
30
+ "granted" => true
31
+ }
32
+ f.write conf.to_yaml
33
+ end
34
+ end
35
+
36
+ def load_session_file
37
+ session = YAML.load_file(session_file)
38
+ config[:access_token] = session["access_token"]
39
+ return (ret = config[:access_token].nil? ? false : true)
40
+ log(:info, "session file load success #{config[:access_token]}")
41
+ rescue Errno::ENOENT #file not found
42
+ return false
43
+ end
44
+
45
+ def save_session_file
46
+ hash = {"access_token" => config[:access_token]}
47
+ File.open(session_file, "w") { |f| f.write hash.to_yaml }
48
+ log(:info, "session file save success at #{session_file}")
49
+ end
50
+
51
+ def exchange_long_term_token
52
+ oauth = Koala::Facebook::OAuth.new(config[:app_id], config[:app_secret])
53
+ new_token = oauth.exchange_access_token_info(config[:access_token])
54
+ if new_token["access_token"]
55
+ config[:access_token] = new_token["access_token"]
56
+ log(:info, "long term access token exchanged success")
57
+ else
58
+ log(:error, "long term access token exchanged failed")
59
+ raise Exception.new("can not accquire new access token") unless new_token["access_token"]
60
+ end
61
+ end
62
+
63
+ def grant_access
64
+ app_id = config[:app_id]
65
+ redirect_uri = config[:redirect_uri]
66
+ permission = config[:permission]
67
+
68
+ get_access_url =
69
+ "https://www.facebook.com/dialog/oauth?client_id=#{app_id}&scope=#{permission}&redirect_uri=#{redirect_uri}"
70
+ puts "★ goto #{get_access_url} to grant access to our app"
71
+ browse(get_access_url)
72
+ puts "→ after access granted press enter"
73
+ STDIN.gets
74
+ end
75
+
76
+ def setup_token
77
+ developer_page = "https://developers.facebook.com/tools/accesstoken/"
78
+ puts "★ goto #{developer_page} and enter User access token: "
79
+ browse(developer_page)
80
+ token = STDIN.gets.chomp
81
+ config[:access_token] = token
82
+ log(:info, "setup access token success: #{token}")
83
+ end
84
+
85
+ def login_flow
86
+ unless config[:app_id]
87
+ setup_app_id
88
+ end
89
+ unless config[:app_secret]
90
+ setup_app_secret
91
+ end
92
+ unless config[:granted]
93
+ grant_access
94
+ end
95
+ save_config_file
96
+
97
+ unless load_session_file
98
+ setup_token
99
+ exchange_long_term_token
100
+ save_session_file
101
+ end
102
+ log(:info, "login flow success")
103
+ end
104
+
105
+ def browse(url)
106
+ Launchy.open(url)
107
+ rescue
108
+ puts "warning: can't open url"
109
+ end
110
+ end
111
+ extend GetToken
112
+ end
data/lib/facy/help.rb ADDED
@@ -0,0 +1,28 @@
1
+ module Facy
2
+ module Help
3
+ def helps
4
+ @helps ||= []
5
+ end
6
+
7
+ def help(target, usage, example=nil)
8
+ helps << {target: target, usage: usage, example: example}
9
+ end
10
+ end
11
+
12
+ extend Help
13
+ init do
14
+ command :help do |target|
15
+ target = target.tap{|t|t.strip!}.gsub(':','').to_sym
16
+ @helps.each do |h|
17
+ if h[:target] == target
18
+ instant_output(Item.new(
19
+ info: :help,
20
+ content: h[:usage],
21
+ extra: h[:example]
22
+ ))
23
+ end
24
+ end
25
+ end
26
+ help :help, "display usage for a command", ":help [command]"
27
+ end
28
+ end
@@ -0,0 +1,82 @@
1
+ module Facy
2
+ module ImageViewer
3
+ def view_img(path, options={})
4
+ Image.new(path, options).draw
5
+ end
6
+
7
+ module AnsiRGB
8
+ def self.wrap_with_code(string, rgb)
9
+ red, green, blue = rgb[0], rgb[1], rgb[2]
10
+ "\e[#{code(red, green, blue)}m" + string + "\e[0m"
11
+ end
12
+
13
+ def self.code(red, green, blue)
14
+ index = 16 +
15
+ to_ansi_domain(red) * 36 +
16
+ to_ansi_domain(green) * 6 +
17
+ to_ansi_domain(blue)
18
+ "48;5;#{index}"
19
+ end
20
+
21
+ def self.to_ansi_domain(value)
22
+ (6 * (value / 256.0)).to_i
23
+ end
24
+ end
25
+
26
+ class Image
27
+ def initialize(path, options={})
28
+ @image = Magick::ImageList.new(path)
29
+ @fit_terminal = !!options[:fit_terminal]
30
+ rescue Exception => e
31
+ p e.message
32
+ end
33
+
34
+ def draw
35
+ convert_to_fit_terminal_size if @fit_terminal
36
+ rgb_analyze
37
+ ansi_analyze
38
+ puts_ansi
39
+ end
40
+
41
+ def rgb_analyze
42
+ @rgb = []
43
+ cols = @image.columns
44
+ rows = @image.rows
45
+ rows.times do |y|
46
+ cols.times do |x|
47
+ @rgb[y] ||= []
48
+ pixcel = @image.pixel_color(x, y)
49
+ r = pixcel.red / 256
50
+ g = pixcel.green / 256
51
+ b = pixcel.blue / 256
52
+ @rgb[y] << [r, g, b]
53
+ end
54
+ end
55
+ end
56
+
57
+ def ansi_analyze
58
+ raise "use rgb_analyze before ansi_analyze" unless @rgb
59
+ ret = []
60
+ @rgb.map! do |row|
61
+ ret << row.map{|pixcel|
62
+ AnsiRGB.wrap_with_code(@double ? " " : " ", pixcel)
63
+ }.join
64
+ end
65
+ @ansi = ret.join("\n")
66
+ rescue Exception => e
67
+ error e.message
68
+ end
69
+
70
+ def puts_ansi
71
+ raise "use ansi_analyze before to_ansi" unless @ansi
72
+ puts @ansi
73
+ end
74
+
75
+ def get_term_size
76
+ `stty size`.split(" ").map(&:to_i)
77
+ end
78
+ end
79
+ end
80
+
81
+ extend ImageViewer
82
+ end