zombie_fans 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +9 -0
- data/README.md +52 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/config/schedule.rb +26 -0
- data/examples/log/.keep +0 -0
- data/examples/smtp_server.rb +4 -0
- data/examples/star_my_repo.rb +14 -0
- data/lib/zombie_fans.rb +6 -0
- data/lib/zombie_fans/actions/auth.rb +52 -0
- data/lib/zombie_fans/actions/avatar.rb +50 -0
- data/lib/zombie_fans/actions/confirm.rb +41 -0
- data/lib/zombie_fans/actions/logger.rb +17 -0
- data/lib/zombie_fans/actions/persist.rb +50 -0
- data/lib/zombie_fans/actions/query.rb +10 -0
- data/lib/zombie_fans/actions/repo.rb +64 -0
- data/lib/zombie_fans/actions/user.rb +55 -0
- data/lib/zombie_fans/robot.rb +57 -0
- data/lib/zombie_fans/smtp.rb +77 -0
- data/lib/zombie_fans/version.rb +3 -0
- data/zombie_fans.gemspec +35 -0
- metadata +138 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c9492ee42f9a335779037517faa678e1ceb34078
|
4
|
+
data.tar.gz: 5f27421ad585d25057d3785ac4622aeb4ccafe91
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e3474ce1c5a3db0f1a749f2fdddb9f63264160248df557500e8a2d4a21aa4dfb6bf879fcf25d0afecf04e228cff367e07fcc453f48c6f16f111b28efb7c69efd
|
7
|
+
data.tar.gz: a3f749329984eb3047ad30d9a1ec018699d5d3c2f4550adfa06d8aa4dd987c1db4ee66a875982de24c3f3904235d1e26ce7cc54450c44c0737ba8d375e3803e1
|
data/.gitignore
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, and in the interest of
|
4
|
+
fostering an open and welcoming community, we pledge to respect all people who
|
5
|
+
contribute through reporting issues, posting feature requests, updating
|
6
|
+
documentation, submitting pull requests or patches, and other activities.
|
7
|
+
|
8
|
+
We are committed to making participation in this project a harassment-free
|
9
|
+
experience for everyone, regardless of level of experience, gender, gender
|
10
|
+
identity and expression, sexual orientation, disability, personal appearance,
|
11
|
+
body size, race, ethnicity, age, religion, or nationality.
|
12
|
+
|
13
|
+
Examples of unacceptable behavior by participants include:
|
14
|
+
|
15
|
+
* The use of sexualized language or imagery
|
16
|
+
* Personal attacks
|
17
|
+
* Trolling or insulting/derogatory comments
|
18
|
+
* Public or private harassment
|
19
|
+
* Publishing other's private information, such as physical or electronic
|
20
|
+
addresses, without explicit permission
|
21
|
+
* Other unethical or unprofessional conduct
|
22
|
+
|
23
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
24
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
25
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
26
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
27
|
+
threatening, offensive, or harmful.
|
28
|
+
|
29
|
+
By adopting this Code of Conduct, project maintainers commit themselves to
|
30
|
+
fairly and consistently applying these principles to every aspect of managing
|
31
|
+
this project. Project maintainers who do not follow or enforce the Code of
|
32
|
+
Conduct may be permanently removed from the project team.
|
33
|
+
|
34
|
+
This code of conduct applies both within project spaces and in public spaces
|
35
|
+
when an individual is representing the project or its community.
|
36
|
+
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
38
|
+
reported by contacting a project maintainer at bbtfrr@gmail.com. All
|
39
|
+
complaints will be reviewed and investigated and will result in a response that
|
40
|
+
is deemed necessary and appropriate to the circumstances. Maintainers are
|
41
|
+
obligated to maintain confidentiality with regard to the reporter of an
|
42
|
+
incident.
|
43
|
+
|
44
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
45
|
+
version 1.3.0, available at
|
46
|
+
[http://contributor-covenant.org/version/1/3/0/][version]
|
47
|
+
|
48
|
+
[homepage]: http://contributor-covenant.org
|
49
|
+
[version]: http://contributor-covenant.org/version/1/3/0/
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# ZombieFans
|
2
|
+
|
3
|
+
Create github zombie fans to follow you / star your repo.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'zombie_fans'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install zombie_fans
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
|
25
|
+
robot = ZombieFans::Robot.new # create a robot
|
26
|
+
|
27
|
+
robot.sign_up # sign up with random login, email & password, these will be saved in db/zombie_fans.yml
|
28
|
+
robot.upload_avatar # upload random avatar
|
29
|
+
robot.update_profile # fill profile form with random company name & city name
|
30
|
+
|
31
|
+
# or you can use an existed account in db/zombie_fans.yml
|
32
|
+
robot = ZombieFans::Robot.find('username') # find user by username
|
33
|
+
robot = ZombieFans::Robot.sample # random pick an existed account
|
34
|
+
|
35
|
+
robot.sign_in # sign in
|
36
|
+
|
37
|
+
robot.star_repo 'bbtfr/zombie-fans' # star a repo
|
38
|
+
robot.follow_user 'bbtfr' # follow an user
|
39
|
+
```
|
40
|
+
|
41
|
+
You can find more examples [here](https://github.com/bbtfr/zombie-fans/tree/master/examples).
|
42
|
+
|
43
|
+
## Development
|
44
|
+
|
45
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
46
|
+
|
47
|
+
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).
|
48
|
+
|
49
|
+
## Contributing
|
50
|
+
|
51
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/bbtfr/zombie_fans. 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.
|
52
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "zombie_fans"
|
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,26 @@
|
|
1
|
+
# Use this file to easily define all of your cron jobs.
|
2
|
+
#
|
3
|
+
# It's helpful, but not entirely necessary to understand cron before proceeding.
|
4
|
+
# http://en.wikipedia.org/wiki/Cron
|
5
|
+
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# set :output, "/path/to/my/cron_log.log"
|
9
|
+
#
|
10
|
+
# every 2.hours do
|
11
|
+
# command "/usr/bin/some_great_command"
|
12
|
+
# runner "MyModel.some_method"
|
13
|
+
# rake "some:great:rake:task"
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# every 4.days do
|
17
|
+
# runner "AnotherModel.prune_old_records"
|
18
|
+
# end
|
19
|
+
|
20
|
+
# Learn more: http://github.com/javan/whenever
|
21
|
+
|
22
|
+
set :output, "log/crontab.log"
|
23
|
+
job_type :bundle_exec, "cd :path && bundle exec :task :output"
|
24
|
+
every 1.hours do
|
25
|
+
bundle_exec "ruby star_my_repo.rb"
|
26
|
+
end
|
data/examples/log/.keep
ADDED
File without changes
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'zombie_fans'
|
2
|
+
|
3
|
+
repos = %w(bbtfr/tailog bbtfr/tailog bbtfr/evil-proxy bbtfr/zombie-fans)
|
4
|
+
|
5
|
+
puts nil, "=== #{Time.now} ===".colorize(:blue)
|
6
|
+
robot = ZombieFans::Robot.new
|
7
|
+
robot.sign_up
|
8
|
+
robot.upload_avatar
|
9
|
+
robot.update_profile
|
10
|
+
robot.add_confirmable_email
|
11
|
+
robot.set_email_private
|
12
|
+
|
13
|
+
robot.star_repo repos.sample
|
14
|
+
robot.follow_user 'bbtfr'
|
data/lib/zombie_fans.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'faker'
|
2
|
+
|
3
|
+
module ZombieFans::Actions
|
4
|
+
module Auth
|
5
|
+
def sign_up
|
6
|
+
agent.get('https://github.com/join') do |page|
|
7
|
+
retry_register = true
|
8
|
+
while retry_register
|
9
|
+
page = fill_signup_page page
|
10
|
+
error_messages = page.search('.form-group.errored .error').map(&:text)
|
11
|
+
retry_register = error_messages.any?
|
12
|
+
log_error error_messages.join(', ').downcase if retry_register
|
13
|
+
end
|
14
|
+
save
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def sign_in
|
19
|
+
agent.get('https://github.com/settings/profile') do |page|
|
20
|
+
log_action 'SignIn', "with login: #{login}, email: #{email}, password: #{password}."
|
21
|
+
|
22
|
+
page.form_with(action: '/session') do |form|
|
23
|
+
form['login'] = @login
|
24
|
+
form['password'] = @password
|
25
|
+
end.submit
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def sample_login
|
32
|
+
names = name.split(' ')
|
33
|
+
[names.join('-').downcase, names.join('-').downcase, names.join.downcase, names.first.downcase, names.last.downcase, (name[0] + names.last).downcase, (name[0..1] + '-' + names.last).downcase, names.join, names.join, names.first, names.last, Faker::Internet.user_name, Faker::Internet.user_name].sample
|
34
|
+
end
|
35
|
+
|
36
|
+
def fill_signup_page page
|
37
|
+
page.form_with(action: '/join') do |form|
|
38
|
+
@name = Faker::Name.name
|
39
|
+
@login = sample_login
|
40
|
+
@email = Faker::Internet.free_email sample_login
|
41
|
+
@password = Faker::Internet.password(7, 20, true)
|
42
|
+
|
43
|
+
log_action 'SignUp', "with login: #{login}, password: #{password}."
|
44
|
+
|
45
|
+
form['user[login]'] = @login
|
46
|
+
form['user[email]'] = @email
|
47
|
+
form['user[password]'] = @password
|
48
|
+
end.submit
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ZombieFans::Actions
|
2
|
+
module Avatar
|
3
|
+
def upload_avatar
|
4
|
+
page = agent.get('http://randomavatar.com')
|
5
|
+
avatar_url = page.at('.RAFade').attr('src')
|
6
|
+
|
7
|
+
log_action 'UploadAvatar', "with #{avatar_url}."
|
8
|
+
|
9
|
+
page = agent.get('https://github.com/settings/profile')
|
10
|
+
form = page.at('form.js-upload-avatar-image')
|
11
|
+
owner_id = form.attr('data-alambic-owner-id')
|
12
|
+
owner_type = form.attr('data-alambic-owner-type')
|
13
|
+
upload_policy_url = form.attr('data-upload-policy-url')
|
14
|
+
authenticity_token = form.at('input[name=authenticity_token]').attr('value')
|
15
|
+
avatar_filename = "#{login}.jpg"
|
16
|
+
avatar = open(avatar_url)
|
17
|
+
|
18
|
+
query = {
|
19
|
+
name: avatar_filename,
|
20
|
+
size: avatar.size,
|
21
|
+
content_type: 'image/jpeg',
|
22
|
+
authenticity_token: authenticity_token,
|
23
|
+
owner_type: owner_type,
|
24
|
+
owner_id: owner_id
|
25
|
+
}
|
26
|
+
|
27
|
+
page = agent.post(upload_policy_url, query)
|
28
|
+
response = JSON.parse page.body
|
29
|
+
|
30
|
+
File.open avatar.path do |avatar_file|
|
31
|
+
query = response['asset'].merge({
|
32
|
+
authenticity_token: authenticity_token,
|
33
|
+
owner_type: owner_type,
|
34
|
+
owner_id: owner_id,
|
35
|
+
size: avatar.size,
|
36
|
+
content_type: 'image/jpeg',
|
37
|
+
file: avatar_file
|
38
|
+
})
|
39
|
+
|
40
|
+
# agent.agent.allowed_error_codes = [500]
|
41
|
+
page = agent.post(response['upload_url'], query, response['header'])
|
42
|
+
response = JSON.parse page.body
|
43
|
+
end
|
44
|
+
|
45
|
+
agent.get("https://github.com/settings/avatars/#{response['id']}") do |page|
|
46
|
+
page = page.form_with(action: "/settings/avatars/#{response['id']}").submit
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ZombieFans::Actions
|
2
|
+
module Confirm
|
3
|
+
CONFIRMABLE_EMAIL = "%s@theoli.gq"
|
4
|
+
|
5
|
+
def follow_confirm_link confirm_link
|
6
|
+
agent.get(confirm_link) do |page|
|
7
|
+
log_action 'FollowConfirmLink', "with login: #{login}, link: #{confirm_link}."
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_confirmable_email
|
12
|
+
agent.get('https://github.com/settings/emails') do |page|
|
13
|
+
confirmable_email = CONFIRMABLE_EMAIL % login
|
14
|
+
log_action 'AddConfirmableEmail', "with login: #{login}, email: #{confirmable_email}"
|
15
|
+
|
16
|
+
page = page.form_with(action: "/users/#{login}/emails") do |form|
|
17
|
+
form['user_email[email]'] = confirmable_email
|
18
|
+
end.submit
|
19
|
+
|
20
|
+
if error = page.at('.flash.flash-error:not(.ajax-error-message)')
|
21
|
+
log_error error.text.strip
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_email_private
|
27
|
+
agent.get('https://github.com/settings/emails') do |page|
|
28
|
+
log_action 'ToggleEmailVisibility', "with login: #{login}, visibility: #{false}"
|
29
|
+
|
30
|
+
page = page.form_with(action: "/users/#{login}/emails/toggle_visibility") do |form|
|
31
|
+
form['toggle_visibility'] = 'false'
|
32
|
+
end.submit
|
33
|
+
|
34
|
+
if error = page.at('.flash.flash-error:not(.ajax-error-message)')
|
35
|
+
log_error error.text.strip
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
|
3
|
+
module ZombieFans::Actions
|
4
|
+
module Logger
|
5
|
+
def log_action action, message
|
6
|
+
puts "#{action.colorize(:green)} #{message}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def log_error message
|
10
|
+
puts "#{"Error occurred:".colorize(:red)} #{message}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def debug_page page
|
14
|
+
File.write("debug.html", page.body)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module ZombieFans::Actions
|
6
|
+
module Persist
|
7
|
+
ATTRIBUTES = %w(name login email password repos)
|
8
|
+
|
9
|
+
def save
|
10
|
+
record = {}
|
11
|
+
ATTRIBUTES.each do |key|
|
12
|
+
attribute = instance_variable_get(:"@#{key}")
|
13
|
+
record[key] = attribute if attribute
|
14
|
+
end
|
15
|
+
|
16
|
+
yaml = Persist.load
|
17
|
+
yaml << record
|
18
|
+
Persist.dump yaml
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
attr_accessor :path
|
23
|
+
|
24
|
+
def path
|
25
|
+
@path ||= File.expand_path '../../../../db/zombie_fans.yml', __FILE__
|
26
|
+
end
|
27
|
+
|
28
|
+
def load
|
29
|
+
return [] unless File.exist? path
|
30
|
+
YAML.load(File.read(path)) || []
|
31
|
+
end
|
32
|
+
|
33
|
+
def dump yaml
|
34
|
+
FileUtils.mkdir_p(File.dirname(path))
|
35
|
+
File.write(path, YAML.dump(yaml))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
def init_attributes
|
41
|
+
attr_accessor *ATTRIBUTES
|
42
|
+
end
|
43
|
+
|
44
|
+
def sample
|
45
|
+
yaml = Persist.load
|
46
|
+
new yaml.sample
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module ZombieFans::Actions
|
2
|
+
module Repo
|
3
|
+
def create_repo
|
4
|
+
agent.get('https://github.com/new') do |page|
|
5
|
+
page.form_with(action: '/repositories') do |form|
|
6
|
+
name = Faker::Internet.domain_word
|
7
|
+
description = Faker::Hacker.say_something_smart
|
8
|
+
@repos ||= []
|
9
|
+
@repos << name
|
10
|
+
|
11
|
+
log_action 'CreateRepo', "with name: #{name}, description: #{description}."
|
12
|
+
|
13
|
+
form['repository[name]'] = name
|
14
|
+
form['repository[description]'] = description
|
15
|
+
end.submit
|
16
|
+
|
17
|
+
save
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def star_repo repo
|
22
|
+
page = agent.get("https://github.com/#{repo}")
|
23
|
+
form = page.at('.starring-container:not(.on) form.unstarred')
|
24
|
+
return unless form
|
25
|
+
|
26
|
+
log_action 'StarRepo', "#{repo}."
|
27
|
+
|
28
|
+
authenticity_token = form.at('input[name=authenticity_token]').attr('value')
|
29
|
+
star_repo_url = form.attr('action')
|
30
|
+
|
31
|
+
query = {
|
32
|
+
authenticity_token: authenticity_token
|
33
|
+
}
|
34
|
+
|
35
|
+
header = {
|
36
|
+
'X-Requested-With' => 'XMLHttpRequest'
|
37
|
+
}
|
38
|
+
|
39
|
+
# agent.agent.allowed_error_codes = [400]
|
40
|
+
page = agent.post(star_repo_url, query, header)
|
41
|
+
response = JSON.parse page.body
|
42
|
+
end
|
43
|
+
|
44
|
+
def make_repo_private repo
|
45
|
+
toggle_permission repo, "private"
|
46
|
+
end
|
47
|
+
|
48
|
+
def make_repo_public repo
|
49
|
+
toggle_permission repo, "public"
|
50
|
+
end
|
51
|
+
|
52
|
+
def toggle_permission repo, permission
|
53
|
+
page = agent.get("https://github.com/#{repo}/settings")
|
54
|
+
button = page.at('a[href="#visibility_confirm"]')
|
55
|
+
return unless button && button.text == "Make #{permission}"
|
56
|
+
|
57
|
+
log_action 'TogglePermission', "#{repo} #{permission}."
|
58
|
+
|
59
|
+
page = page.form_with(action: "/#{repo}/settings/toggle_permission") do |form|
|
60
|
+
form['verify'] = repo
|
61
|
+
end.submit
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module ZombieFans::Actions
|
2
|
+
module User
|
3
|
+
def update_profile
|
4
|
+
agent.get('https://github.com/settings/profile') do |page|
|
5
|
+
page.form_with(action: "/users/#{login}") do |form|
|
6
|
+
name = sample_display_name
|
7
|
+
blog = sample_result Faker::Internet.url, 2
|
8
|
+
company = sample_result Faker::Company.name, 1, 2
|
9
|
+
location = sample_result Faker::Address.city, 1, 2
|
10
|
+
|
11
|
+
log_action 'UpdateProfile', "with name: #{name}, blog: #{blog || 'nil'}, company: #{company || 'nil'}, location: #{location || 'nil'}."
|
12
|
+
|
13
|
+
form['user[profile_name]'] = name
|
14
|
+
form['user[profile_blog]'] = blog
|
15
|
+
form['user[profile_company]'] = company
|
16
|
+
form['user[profile_location]'] = location
|
17
|
+
end.submit
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def follow_user user
|
22
|
+
page = agent.get("https://github.com/#{user}")
|
23
|
+
form = page.at('.user-following-container:not(.on) .follow form')
|
24
|
+
return unless form
|
25
|
+
|
26
|
+
log_action 'FollowUser', "#{user}."
|
27
|
+
|
28
|
+
authenticity_token = form.at('input[name=authenticity_token]').attr('value')
|
29
|
+
follow_user_url = form.attr('action')
|
30
|
+
|
31
|
+
query = {
|
32
|
+
authenticity_token: authenticity_token
|
33
|
+
}
|
34
|
+
|
35
|
+
header = {
|
36
|
+
'X-Requested-With' => 'XMLHttpRequest'
|
37
|
+
}
|
38
|
+
|
39
|
+
# agent.agent.allowed_error_codes = [400]
|
40
|
+
page = agent.post(follow_user_url, query, header)
|
41
|
+
response = JSON.parse page.body
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def sample_display_name
|
47
|
+
names = name.split(' ')
|
48
|
+
names = [names.join('-').downcase, names.first.downcase, names.last.downcase, name, name, name, name, name, names.first, names.last].sample
|
49
|
+
end
|
50
|
+
|
51
|
+
def sample_result result, weight1 = 1, weight2 = 1
|
52
|
+
([nil] * weight1 + [result] * weight2).sample
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
# Don't allow downloaded files to be created as StringIO. Force a tempfile to be created.
|
3
|
+
OpenURI::Buffer.send :remove_const, 'StringMax' if OpenURI::Buffer.const_defined?('StringMax')
|
4
|
+
OpenURI::Buffer.const_set 'StringMax', 0
|
5
|
+
|
6
|
+
require 'mechanize'
|
7
|
+
|
8
|
+
glob = File.expand_path("../actions/*.rb", __FILE__)
|
9
|
+
Dir[glob].each do |file|
|
10
|
+
require file
|
11
|
+
end
|
12
|
+
|
13
|
+
class Mechanize::Page
|
14
|
+
def preview
|
15
|
+
file = Tempfile.new(['mechanize', '.html'])
|
16
|
+
file.write(self.content)
|
17
|
+
file.close
|
18
|
+
system("open #{file.path}")
|
19
|
+
sleep 3
|
20
|
+
file.unlink
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ZombieFans
|
25
|
+
class Robot
|
26
|
+
include Actions::Logger
|
27
|
+
include Actions::Auth
|
28
|
+
include Actions::Confirm
|
29
|
+
include Actions::Avatar
|
30
|
+
include Actions::Repo
|
31
|
+
include Actions::User
|
32
|
+
|
33
|
+
include Actions::Persist
|
34
|
+
extend Actions::Persist::ClassMethods
|
35
|
+
init_attributes
|
36
|
+
|
37
|
+
extend Actions::Query::ClassMethods
|
38
|
+
|
39
|
+
attr_reader :agent
|
40
|
+
|
41
|
+
AGENT_ALIASES = [
|
42
|
+
'Linux Firefox', 'Linux Konqueror', 'Linux Mozilla', 'Mac Firefox', 'Mac Mozilla', 'Mac Safari 4', 'Mac Safari', 'Windows Chrome', 'Windows IE 6', 'Windows IE 7', 'Windows IE 8', 'Windows IE 9', 'Windows IE 10', 'Windows IE 11', 'Windows Edge', 'Windows Mozilla', 'Windows Firefox'
|
43
|
+
]
|
44
|
+
|
45
|
+
def initialize attributes = {}
|
46
|
+
# assign attributes
|
47
|
+
attributes.each do |key, value|
|
48
|
+
instance_variable_set(:"@#{key}", value)
|
49
|
+
end
|
50
|
+
|
51
|
+
@agent = Mechanize.new do |agent|
|
52
|
+
agent.user_agent_alias = AGENT_ALIASES.sample
|
53
|
+
agent.agent.allowed_error_codes = [500]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'mail'
|
4
|
+
|
5
|
+
class SMTP < EventMachine::Protocols::SmtpServer
|
6
|
+
def receive_sender(sender)
|
7
|
+
current.sender = sender
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
def receive_recipient(recipient)
|
12
|
+
current.recipient = recipient
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def receive_message
|
17
|
+
current.received = true
|
18
|
+
current.completed_at = Time.now
|
19
|
+
|
20
|
+
puts "==> SMTP: Received message from '#{current.sender}' (#{current.data.length} bytes)"
|
21
|
+
|
22
|
+
if current.sender =~ /noreply@github.com/
|
23
|
+
mail = Mail.new current.data
|
24
|
+
plain_text = mail.body.parts.first.body.decoded
|
25
|
+
if plain_text =~ /https:\/\/github.com\/users\/(.*?)\/emails.*?(?=\n)/
|
26
|
+
confirm_link = $&
|
27
|
+
login = $1
|
28
|
+
|
29
|
+
robot = ZombieFans::Robot.find login
|
30
|
+
robot.sign_in
|
31
|
+
robot.follow_confirm_link confirm_link
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
true
|
36
|
+
rescue => error
|
37
|
+
puts "*** Error receiving message: #{current.inspect}"
|
38
|
+
puts " Exception: #{error}"
|
39
|
+
puts " Backtrace:"
|
40
|
+
$!.backtrace.each do |line|
|
41
|
+
puts " #{line}"
|
42
|
+
end
|
43
|
+
false
|
44
|
+
ensure
|
45
|
+
@current = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def receive_data_command
|
49
|
+
current.data = ""
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
def receive_data_chunk(data)
|
54
|
+
current.data << data.join("\n")
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def current
|
59
|
+
@current ||= OpenStruct.new
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.start(host = '0.0.0.0', port = 25)
|
63
|
+
@server = EventMachine.start_server host, port, self
|
64
|
+
puts "Starting MailCatcher on smtp://#{host}:#{port}"
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.stop
|
68
|
+
if @server
|
69
|
+
EventMachine.stop_server @server
|
70
|
+
@server = nil
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.running?
|
75
|
+
!!@server
|
76
|
+
end
|
77
|
+
end
|
data/zombie_fans.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'zombie_fans/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "zombie_fans"
|
8
|
+
spec.version = ZombieFans::VERSION
|
9
|
+
spec.authors = ["Theo Li"]
|
10
|
+
spec.email = ["bbtfrr@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Github Zombie Fans}
|
13
|
+
spec.description = %q{Github Zombie Fans to follow, star and many more.}
|
14
|
+
spec.homepage = "https://github.com/bbtfr/zombie-fans"
|
15
|
+
|
16
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
17
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
18
|
+
if spec.respond_to?(:metadata)
|
19
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
20
|
+
else
|
21
|
+
raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
|
22
|
+
end
|
23
|
+
|
24
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
spec.bindir = "exe"
|
26
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
spec.require_paths = ["lib"]
|
28
|
+
|
29
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
30
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
31
|
+
|
32
|
+
spec.add_dependency "mechanize", "~> 2.7.0"
|
33
|
+
spec.add_dependency "colorize", "~> 0.7.7"
|
34
|
+
spec.add_dependency "faker", "~> 1.6.3"
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zombie_fans
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Theo Li
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-24 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: mechanize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.7.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.7.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: colorize
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.7.7
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.7.7
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: faker
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.6.3
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.6.3
|
83
|
+
description: Github Zombie Fans to follow, star and many more.
|
84
|
+
email:
|
85
|
+
- bbtfrr@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- CODE_OF_CONDUCT.md
|
92
|
+
- Gemfile
|
93
|
+
- README.md
|
94
|
+
- Rakefile
|
95
|
+
- bin/console
|
96
|
+
- bin/setup
|
97
|
+
- examples/config/schedule.rb
|
98
|
+
- examples/log/.keep
|
99
|
+
- examples/smtp_server.rb
|
100
|
+
- examples/star_my_repo.rb
|
101
|
+
- lib/zombie_fans.rb
|
102
|
+
- lib/zombie_fans/actions/auth.rb
|
103
|
+
- lib/zombie_fans/actions/avatar.rb
|
104
|
+
- lib/zombie_fans/actions/confirm.rb
|
105
|
+
- lib/zombie_fans/actions/logger.rb
|
106
|
+
- lib/zombie_fans/actions/persist.rb
|
107
|
+
- lib/zombie_fans/actions/query.rb
|
108
|
+
- lib/zombie_fans/actions/repo.rb
|
109
|
+
- lib/zombie_fans/actions/user.rb
|
110
|
+
- lib/zombie_fans/robot.rb
|
111
|
+
- lib/zombie_fans/smtp.rb
|
112
|
+
- lib/zombie_fans/version.rb
|
113
|
+
- zombie_fans.gemspec
|
114
|
+
homepage: https://github.com/bbtfr/zombie-fans
|
115
|
+
licenses: []
|
116
|
+
metadata:
|
117
|
+
allowed_push_host: https://rubygems.org
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 2.5.1
|
135
|
+
signing_key:
|
136
|
+
specification_version: 4
|
137
|
+
summary: Github Zombie Fans
|
138
|
+
test_files: []
|