sacrifice 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b2a480e21311ddad2b06ed17eec6db93900d6ecf
4
+ data.tar.gz: 4698c126d90037eca38deceb3d46c64c54412a8d
5
+ SHA512:
6
+ metadata.gz: 9d77a7ec5f833a477a5fcf6fe0f396f1551ac9eb561cb5b31e06e657ab4ead589fbb0bcefacdb2b96847e537c418d9f3fbce1f0e7db3b5c52afd8557cf66c556
7
+ data.tar.gz: c539c41a2e6001c929883a846d89f4363feadebfa940886edeb4240192514c46b5fd348c5b7c9ee83b7d34b815cccecaa9580f2ce0ee451fad48e5a44753fa37
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.0
5
+ before_install: gem install bundler -v 1.13.0.rc.2
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at a_higuchi@r.recruit.co.jp. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [http://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: http://contributor-covenant.org
74
+ [version]: http://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sacrifice.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 樋口 彰
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.md ADDED
@@ -0,0 +1,47 @@
1
+ # Sacrifice
2
+
3
+ Sacrifice is command line tool to manage Facebook test users.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'sacrifice'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install sacrifice
20
+
21
+ ## Usage
22
+
23
+ $ sacrifice register -i [OpenGraph ID of the app] -n [Name of the app (so you don't have to remember its ID)] -s [App's secret key]
24
+ $ sacrifice users generate -a [Name of the app you registered] -f [Csv file like below]
25
+
26
+ _sample.csv_
27
+ ```
28
+ name,password,locale,gender
29
+ foo foo,foo_pass,ja_JP,male
30
+ bar bar,bar_pass,ja_JP,female
31
+ ```
32
+
33
+ ## Development
34
+
35
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
36
+
37
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
38
+
39
+ ## Contributing
40
+
41
+ Bug reports and pull requests are welcome on GitHub at https://github.com/toiroakr/sacrifice. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
42
+
43
+
44
+ ## License
45
+
46
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
47
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console.rb ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "sacrifice"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/sacrifice ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sacrifice'
4
+
5
+ Sacrifice::CLI.start
data/lib/sacrifice.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'sacrifice/version'
2
+ require 'sacrifice/cli'
3
+
4
+ module Sacrifice
5
+ # Your code goes here...
6
+ end
@@ -0,0 +1 @@
1
+ # sacrifice
@@ -0,0 +1,30 @@
1
+ require 'cgi'
2
+ require 'restclient'
3
+
4
+ # A million thanks to Filip Tepper for https://github.com/filiptepper/facebook-oauth-example
5
+
6
+ class AccessToken
7
+
8
+ OAUTH_BASE = 'https://graph.facebook.com/oauth/access_token'
9
+
10
+ def self.get(app_id, app_secret, oauth_base=OAUTH_BASE)
11
+ response = RestClient.get(
12
+ oauth_base,
13
+ :params => {
14
+ 'client_id' => app_id,
15
+ 'client_secret' => app_secret,
16
+ 'grant_type' => 'client_credentials' # FB magic string
17
+ })
18
+
19
+ extract_access_token(response)
20
+ end
21
+
22
+ private
23
+ def self.extract_access_token(response_body)
24
+ response_body.
25
+ match(/=(.*)/).# response is a string like "access_token=bunch-o-crap"
26
+ captures[0].
27
+ strip
28
+ end
29
+
30
+ end
@@ -0,0 +1,110 @@
1
+ require 'sacrifice/const'
2
+ require 'sacrifice/access_token'
3
+ require 'sacrifice/utils'
4
+
5
+ class App
6
+ include Utils
7
+
8
+ attr_reader :name, :id, :secret
9
+
10
+ def initialize(attrs)
11
+ @name, @id, @secret = attrs[:name].to_s, attrs[:id].to_s, attrs[:secret].to_s
12
+ validate!
13
+ end
14
+
15
+ def attrs
16
+ {name: name, id: id, secret: secret}
17
+ end
18
+
19
+ def self.create!(attrs)
20
+ new_app = new(attrs)
21
+
22
+ if all.find { |app| app.name == new_app.name }
23
+ raise ArgumentError, "App names must be unique, and there is already an app named \"#{new_app.name}\"."
24
+ end
25
+
26
+ DB.update do |data|
27
+ data[:apps] ||= []
28
+ data[:apps] << new_app.attrs
29
+ end
30
+ end
31
+
32
+ def users
33
+ handle_bad_request do
34
+ users_data = RestClient.get(users_url, params: {access_token: access_token})
35
+
36
+ JSON.parse(users_data)["data"].map do |user_data|
37
+ User.new(user_data)
38
+ end
39
+ end
40
+ end
41
+
42
+ def create_user(options = {})
43
+ handle_bad_request do
44
+ user_data = RestClient.post(users_url, {access_token: access_token}.merge(options))
45
+ User.new(JSON.parse(user_data))
46
+ end
47
+ end
48
+
49
+ def rm_user(uid)
50
+ handle_bad_request do
51
+ url = rm_user_url(uid, access_token)
52
+ RestClient.delete(url)
53
+ end
54
+ end
55
+
56
+ ## query methods
57
+ def self.all
58
+ if DB[:apps]
59
+ DB[:apps].map { |attrs| new(attrs) }
60
+ else
61
+ []
62
+ end
63
+ end
64
+
65
+ def self.find_by_name(name)
66
+ all.find { |a| a.name == name }
67
+ end
68
+
69
+ def access_token
70
+ @access_token ||= AccessToken.get(id, secret)
71
+ end
72
+
73
+ private
74
+
75
+ def users_url
76
+ "#{GRAPH_API_BASE}/#{id}/accounts/test-users"
77
+ end
78
+
79
+ def rm_user_url(uid, token)
80
+ "#{users_url}?uid=#{uid}&access_token=#{URI.escape(token)}"
81
+ end
82
+
83
+ def validate!
84
+ unless name && name =~ /\S/
85
+ raise ArgumentError, "App name must not be empty"
86
+ end
87
+
88
+ unless id && id =~ /^[0-9a-f]+$/i
89
+ raise ArgumentError, "App id must be a nonempty hex string, but was #{id.inspect}"
90
+ end
91
+
92
+ unless secret && secret =~ /^[0-9a-f]+$/i
93
+ raise ArgumentError, "App secret must be a nonempty hex string, but was #{secret.inspect}"
94
+ end
95
+ end
96
+
97
+ def self.find!(name)
98
+ app = App.find_by_name(name)
99
+ unless app
100
+ raise Thor::Error, "Unknown app #{name}. Run 'sacrifice apps' to see known apps."
101
+ end
102
+ app
103
+ end
104
+
105
+ def find_user(user_id)
106
+ users.find { |user|
107
+ user.id.to_s == user_id.to_s
108
+ }
109
+ end
110
+ end
@@ -0,0 +1,36 @@
1
+ require 'thor'
2
+ require 'sacrifice/utils'
3
+ require 'sacrifice/app'
4
+ require 'sacrifice/db'
5
+
6
+ class Apps < Thor
7
+ include Utils
8
+
9
+ check_unknown_options!
10
+
11
+ def self.exit_on_failure?()
12
+ true
13
+ end
14
+
15
+ default_task :list
16
+
17
+ desc 'register', 'Tell sacrifice about a new application (must already exist on Facebook)'
18
+ method_option 'app_id', aliases: %w[-i], :type => :string, :required => true, :banner => 'OpenGraph ID of the app'
19
+ method_option 'app_secret', aliases: %w[-s], :type => :string, :required => true, :banner => 'App\'s secret key'
20
+ method_option 'name', aliases: %w[-n], :type => :string, :required => true, :banner => 'Name of the app (so you don\'t have to remember its ID)'
21
+
22
+ def register
23
+ App.create!(:name => options[:name], :id => options[:app_id], :secret => options[:app_secret])
24
+ list
25
+ end
26
+
27
+ desc 'list', 'List the applications sacrifice knows about'
28
+ method_option 'verbose', aliases: %w[-v], :type => :boolean, :banner => 'Show app secret'
29
+
30
+ def list
31
+ App.all.each do |app|
32
+ puts "#{app.name} (id: #{app.id}#{", secret: #{app.secret}" if options[:verbose]})"
33
+ end
34
+ end
35
+
36
+ end # Apps
@@ -0,0 +1,15 @@
1
+ require 'sacrifice'
2
+ require 'thor'
3
+ require 'sacrifice/csv'
4
+ require 'sacrifice/apps'
5
+ require 'sacrifice/users'
6
+
7
+ module Sacrifice
8
+ class CLI < Thor
9
+ desc 'apps', 'Commands for managing Facebook applications'
10
+ subcommand :apps, Apps
11
+
12
+ desc 'users', 'Commands for managing Facebook applications test users'
13
+ subcommand :users, Users
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ GRAPH_API_BASE = 'https://graph.facebook.com'
@@ -0,0 +1,90 @@
1
+ require 'sacrifice/const'
2
+ require 'csv'
3
+ require 'json'
4
+
5
+ class Csv
6
+ CREATE_OPTIONS = [:name, :install, :locale]
7
+ CHANGE_OPTIONS = [:password]
8
+ OUTPUT_ORDER = [:id, :email, :password, :login_url, :access_token]
9
+
10
+ def self.generate app_name, file
11
+ app = App.find! app_name
12
+ output = init_output(app_name, file)
13
+
14
+ headers = []
15
+ CSV.read(file, headers: true, header_converters: :symbol).each { |data|
16
+ if headers.empty?
17
+ headers = CSV.read(file).first.map { |header| header.to_sym }
18
+ else
19
+ puts '==================================='
20
+ end
21
+
22
+ # set default options
23
+ create_options = {locale: 'ja_JP'}
24
+ change_options = {}
25
+
26
+ # read option
27
+ headers.each { |option|
28
+ create_options[option] = data[option] if CREATE_OPTIONS.include? option
29
+ change_options[option] = data[option] if CHANGE_OPTIONS.include? option
30
+ }
31
+
32
+ # execute create
33
+ user = nil
34
+ # repeat creating and destroying until user who has target gender created
35
+ begin
36
+ user.destroy if user
37
+ user = app.create_user(create_options)
38
+ end while user.invalid_gender(data[:gender])
39
+
40
+ # execute change
41
+ if change_options.any? and user.change(change_options)
42
+ user.password = change_options[:password] if change_options[:password]
43
+ else
44
+ puts "Failed to update password to #{change_options[:password]}"
45
+ end
46
+
47
+ # print created user
48
+ user_map = user.attrs
49
+ puts user_map.map { |key, value| "#{key.upcase} : #{value}" }
50
+
51
+ # log created user
52
+ CSV.open(output, 'a') { |line|
53
+ output_line = []
54
+ OUTPUT_ORDER.each { |key|
55
+ output_line.push user_map[key]
56
+ }
57
+ line << output_line
58
+ } unless user.id.nil?
59
+ }
60
+ end
61
+
62
+ def self.erase app_name, file
63
+ users = App.find!(app_name).users
64
+
65
+ headers = []
66
+ CSV.read(file, headers: true, header_converters: :symbol).each { |data|
67
+ if headers.empty?
68
+ headers = CSV.read(file).first.map { |header| header.to_sym }
69
+ end
70
+ user = users.find { |user|
71
+ user.id.to_s == data[:id].to_s
72
+ }
73
+ if user
74
+ user.destroy
75
+ puts "remove user ##{user.id}"
76
+ else
77
+ puts "user ##{data[:id]} not found"
78
+ end
79
+ }
80
+ end
81
+
82
+ def self.init_output(app_name, file)
83
+ output = "sacrificed_#{app_name}_#{file}"
84
+ File.open(output, 'a').close
85
+ output_file = File.open(output, 'r')
86
+ CSV.open(output, 'a') { |line| line << OUTPUT_ORDER } if output_file.size == 0
87
+ output_file.close
88
+ output
89
+ end
90
+ end
@@ -0,0 +1,48 @@
1
+ require 'yaml'
2
+
3
+ # This is about the dumbest DB you can get. It's a hash that knows
4
+ # how to serialize itself. Dumb, but it gets the job done.
5
+ class DB
6
+ class << self
7
+
8
+ def [](arg)
9
+ # FIXME deep-freeze this; shallow freezing is insufficient
10
+ result = yaml[arg]
11
+ result.freeze
12
+ result
13
+ end
14
+
15
+ def update
16
+ @cached_yaml = nil
17
+ data = _yaml
18
+ yield data
19
+
20
+ # do this *before* blowing away the db
21
+ data_as_yaml = data.to_yaml
22
+ File.open(filename, 'w') { |f| f.write(data_as_yaml) }
23
+ end
24
+
25
+ def filename
26
+ @filename || File.join(ENV['HOME'], '.sacrificerc')
27
+ end
28
+
29
+ def filename=(f)
30
+ @cached_yaml = nil
31
+ @filename = f
32
+ end
33
+
34
+ private
35
+
36
+ def yaml
37
+ @cached_yaml ||= _yaml
38
+ end
39
+
40
+ def _yaml
41
+ if File.exist?(filename)
42
+ YAML.load_file(filename) || {}
43
+ else
44
+ {}
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,61 @@
1
+ require 'uri'
2
+ require 'sacrifice/utils'
3
+ require 'sacrifice/const'
4
+
5
+ class User
6
+ include Utils
7
+
8
+ attr_accessor :id, :access_token, :login_url, :email, :password
9
+
10
+ def initialize(attrs)
11
+ attrs.each do |field, value|
12
+ instance_variable_set("@#{field}", value) if respond_to?(field)
13
+ end
14
+ end
15
+
16
+ def change(options = {})
17
+ handle_bad_request do
18
+ JSON.parse(RestClient.post("#{GRAPH_API_BASE}/#{id}", {access_token: access_token}.merge(options)))['success']
19
+ end
20
+ end
21
+
22
+ def owner_apps(app)
23
+ handle_bad_request do
24
+ RestClient.get("#{GRAPH_API_BASE}/#{id}/ownerapps?access_token=#{URI.escape(app.access_token.to_s)}")
25
+ end
26
+ end
27
+
28
+ def destroy
29
+ handle_bad_request(raise_error=false) do
30
+ RestClient.delete("#{GRAPH_API_BASE}/#{id}?access_token=#{URI.escape(access_token.to_s)}")
31
+ end
32
+ end
33
+
34
+ # Facebook test users all share the same birthday. Perhaps it's the developer's!
35
+ def birthday
36
+ Date.new(1980, 8, 8)
37
+ end
38
+
39
+ def send_friend_request_to(other)
40
+ handle_bad_request do
41
+ RestClient.post("#{GRAPH_API_BASE}/#{id}/friends/#{other.id}", 'access_token' => access_token.to_s)
42
+ end
43
+ end
44
+
45
+ def invalid_gender(gender)
46
+ if gender.nil?
47
+ return
48
+ end
49
+ handle_bad_request do
50
+ result = JSON.parse(RestClient.get("#{GRAPH_API_BASE}/#{id}?fields=gender&access_token=#{access_token}").body)
51
+ if result['gender'] == gender
52
+ return
53
+ end
54
+ end
55
+ true
56
+ end
57
+
58
+ def attrs
59
+ {id: id, access_token: access_token, login_url: login_url, email: email, password: password}
60
+ end
61
+ end
@@ -0,0 +1,160 @@
1
+ require 'thor'
2
+ require 'sacrifice/csv'
3
+ require 'sacrifice/user'
4
+
5
+ class Users < Thor
6
+
7
+ check_unknown_options!
8
+
9
+ def self.exit_on_failure?()
10
+ true
11
+ end
12
+
13
+ desc 'list', 'List available test users for an application'
14
+ method_option 'app', :aliases => %w[-a], :type => :string, :required => true, :banner => 'Name of the app'
15
+
16
+ def list
17
+ app = App.find!(options[:app])
18
+ if app.users.any?
19
+ shell.print_table([
20
+ ['User ID', 'Access Token', 'Login URL'],
21
+ *(app.users.map do |user|
22
+ [user.id, user.access_token, user.login_url]
23
+ end)
24
+ ])
25
+ else
26
+ puts "App #{app.name} has no users."
27
+ end
28
+ end
29
+
30
+ desc 'create', 'Create a new test user'
31
+ method_option 'app', :aliases => %w[-a], :type => :string, :required => true,
32
+ :banner => 'Name of the app'
33
+ method_option 'name', :aliases => %w[-n], :type => :string, :required => false,
34
+ :banner => 'Name of the new user'
35
+ method_option 'installed', :aliases => %w[-i], :type => :string, :required => false,
36
+ :banner => 'whether your app should be installed for the test user'
37
+ method_option 'locale', :aliases => %w[-l], :type => :string, :required => false,
38
+ :banner => 'the locale for the test user'
39
+
40
+ def create
41
+ app = App.find!(options[:app])
42
+ attrs = options.select { |k, v| %w(name installed locale).include? k.to_s }
43
+ user = app.create_user(attrs)
44
+ if user
45
+ puts "User ID: #{user.id}"
46
+ puts "Access Token: #{user.access_token}"
47
+ puts "Login URL: #{user.login_url}"
48
+ puts "Email: #{user.email}"
49
+ puts "Password: #{user.password}"
50
+ end
51
+ end
52
+
53
+ desc 'friend', 'Make two of an app\'s users friends'
54
+ method_option 'app', :aliases => %w[-a], :type => :string, :required => true, :banner => 'Name of the app'
55
+ method_option 'user1', :aliases => %w[-1 -u1], :type => :string, :required => true, :banner => 'First user ID'
56
+ method_option 'user2', :aliases => %w[-2 -u2], :type => :string, :required => true, :banner => 'Second user ID'
57
+
58
+ def friend
59
+ users = App.find!(options[:app]).users
60
+
61
+ friends = []
62
+ [options[:user1], options[:user2]].each { |user|
63
+ friends.push (users.find { |u| u.id.to_s == user } or raise Thor::Error, "No user found w/id #{user.inspect}")
64
+ }
65
+
66
+ # The first request is just a request, the second request accepts the first request.
67
+ friends.each_index { |idx| friends[idx].send_friend_request_to[(idx + 1) % 2] }
68
+ end
69
+
70
+ desc 'change', 'Change a test user\'s name and/or password'
71
+ method_option 'app', :aliases => %w[-a], :type => :string, :required => true,
72
+ :banner => 'Name of the app'
73
+ method_option 'user', :aliases => %w[-u], :type => :string, :required => true,
74
+ :banner => 'ID of the user to change'
75
+ method_option 'name', :aliases => %w[-n], :type => :string, :required => false,
76
+ :banner => 'New name for the user'
77
+ method_option 'password', :aliases => %w[-p], :type => :string, :required => false,
78
+ :banner => 'New password for the user'
79
+
80
+ def change
81
+ user = App.find!(options[:app]).find_user(options[:user])
82
+
83
+ puts user
84
+
85
+ if user
86
+ success = user.change(options)
87
+ if success
88
+ puts 'Successfully changed user'
89
+ else
90
+ puts 'Failed to change user'
91
+ end
92
+ else
93
+ raise Thor::Error, "Unknown user '#{options[:user]}'"
94
+ end
95
+ end
96
+
97
+ desc 'rm', 'Remove a test user from an application'
98
+ method_option 'app', :aliases => %w[-a], :type => :string, :required => true, :banner => 'Name of the app'
99
+ method_option 'user', :banner => 'ID of the user to remove', :aliases => %w[-u], :type => :string, :required => true
100
+
101
+ def rm
102
+ user = App.find!(options[:app]).find_user(options[:user])
103
+
104
+ if user
105
+ result = user.destroy
106
+ if result
107
+ puts "User ID #{user.id} removed"
108
+ else
109
+ if @message.match /(\(#2903\) Cannot delete this test account because it is associated with other applications.)/
110
+ error = <<-EOMSG.unindent.gsub(/^\|/, '')
111
+ #$1
112
+ Run:
113
+ |
114
+ sacrifice users list-apps --app #{options[:app]} --user #{user.id}
115
+ |
116
+ then for each of the other apps, run:
117
+ |
118
+ sacrifice apps rm-user --app APP-NAME --user #{user.id}
119
+ |
120
+ Then re-run this command.
121
+ EOMSG
122
+ else
123
+ error = @message
124
+ end
125
+ raise Thor::Error, error
126
+ end
127
+ else
128
+ raise Thor::Error, "Unknown user '#{options[:user]}'"
129
+ end
130
+ end
131
+
132
+ desc 'destroy', 'Remove all test users from an application. Use with care.'
133
+ method_option 'app', :aliases => %w[-a], :type => :string, :required => true, :banner => 'Name of the app'
134
+
135
+ def destroy
136
+ app = App.find!(options[:app])
137
+ while (users = app.users).size > 0
138
+ users.each { |user|
139
+ user.destroy
140
+ puts "remove user ##{user.id}"
141
+ }
142
+ end
143
+ end
144
+
145
+ desc 'generate', 'Generate facebook test users'
146
+ method_option 'app', aliases: %w[-a], type: :string, banner: 'app name that test user generate on' #, required: true
147
+ method_option 'file', aliases: %w[-f], type: :string, banner: 'csv file to read (required in type csv)', required: true
148
+
149
+ def generate
150
+ Csv.generate options[:app], options[:file]
151
+ end
152
+
153
+ desc 'erase', 'Erase facebook test users'
154
+ method_option 'app', aliases: %w[-a], type: :string, banner: 'app name that test user erase on' #, required: true
155
+ method_option 'file', aliases: %w[-f], type: :string, banner: 'file generated by command \'generate\' (required in type csv)', required: true
156
+
157
+ def erase
158
+ Csv.erase options[:app], options[:file]
159
+ end
160
+ end
@@ -0,0 +1,19 @@
1
+ require 'rest-client'
2
+
3
+ module Utils
4
+ def bad_request_message(bad_request)
5
+ response = bad_request.response
6
+ json = JSON.parse(response)
7
+ json['error']['message'] rescue json.inspect
8
+ end
9
+
10
+ def handle_bad_request(raise_error=true)
11
+ begin
12
+ yield
13
+ rescue RestClient::BadRequest => bad_request
14
+ @message = bad_request_message(bad_request)
15
+ raise Thor::Error, "#{bad_request.class}: #@message" if raise_error
16
+ nil
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module Sacrifice
2
+ VERSION = "0.0.9"
3
+ end
data/sacrifice.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'sacrifice/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "sacrifice"
8
+ spec.version = Sacrifice::VERSION
9
+ spec.authors = ['toiroakr']
10
+ spec.email = ['qazsewsxcd@gmail.com']
11
+
12
+ spec.summary = %q{Command line tool for management Facebook test users.}
13
+ spec.description = %q{Sacrifice}
14
+ spec.homepage = 'https://github.com/toiroakr/sacrifice'
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
21
+ else
22
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.12"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "rspec", "~> 3.0"
33
+ spec.add_dependency "thor"
34
+ spec.add_dependency "heredoc_unindent"
35
+ spec.add_dependency "rest-client"
36
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sacrifice
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.9
5
+ platform: ruby
6
+ authors:
7
+ - toiroakr
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: thor
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: heredoc_unindent
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rest-client
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Sacrifice
98
+ email:
99
+ - qazsewsxcd@gmail.com
100
+ executables:
101
+ - sacrifice
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".travis.yml"
107
+ - CODE_OF_CONDUCT.md
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - bin/console.rb
113
+ - bin/setup
114
+ - exe/sacrifice
115
+ - lib/sacrifice.rb
116
+ - lib/sacrifice/README.md
117
+ - lib/sacrifice/access_token.rb
118
+ - lib/sacrifice/app.rb
119
+ - lib/sacrifice/apps.rb
120
+ - lib/sacrifice/cli.rb
121
+ - lib/sacrifice/const.rb
122
+ - lib/sacrifice/csv.rb
123
+ - lib/sacrifice/db.rb
124
+ - lib/sacrifice/user.rb
125
+ - lib/sacrifice/users.rb
126
+ - lib/sacrifice/utils.rb
127
+ - lib/sacrifice/version.rb
128
+ - sacrifice.gemspec
129
+ homepage: https://github.com/toiroakr/sacrifice
130
+ licenses:
131
+ - MIT
132
+ metadata:
133
+ allowed_push_host: https://rubygems.org
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 2.5.1
151
+ signing_key:
152
+ specification_version: 4
153
+ summary: Command line tool for management Facebook test users.
154
+ test_files: []