arb-xmu-course 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rake_tasks~ +6 -0
- data/Gemfile +9 -0
- data/README.md +36 -0
- data/Rakefile +2 -0
- data/arb-xmu-course.gemspec +23 -0
- data/bin/arb-xmu-course +236 -0
- data/bin/check_code.jpeg +0 -0
- data/lib/arb/xmu/course/models/course_entity.rb +35 -0
- data/lib/arb/xmu/course/models/course_schedule.rb +8 -0
- data/lib/arb/xmu/course/util/simple_des.rb +32 -0
- data/lib/arb/xmu/course/version.rb +7 -0
- data/lib/arb/xmu/course.rb +5 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 033892d7b5333bf00107dbfec09930a09e51d68d
|
4
|
+
data.tar.gz: 6ab649181d6263af37af3248b21084e679b8a28c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bef7169c325840623e5af693600a9b694614dc54791185647a343d08bd761290e2e8f1949e705976821b2389028de7320a7e5ebb091bbe7eca7e73f3302b2ade
|
7
|
+
data.tar.gz: 0a02725936ec1a99a09fff4ca0b61704cd1a97309c482a875ffe9075025ff3df7f6ecf6c095665673c34442cf68fa82d5e05eb6091d0db5fc167d906830d8557
|
data/.gitignore
ADDED
data/.rake_tasks~
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Arb::Xmu::Course
|
2
|
+
|
3
|
+
Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/arb/xmu/course`. To experiment with that code, run `bin/console` for an interactive prompt.
|
4
|
+
|
5
|
+
TODO: Delete this and the text above, and describe your gem
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'arb-xmu-course'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install arb-xmu-course
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
TODO: Write usage instructions here
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
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.
|
30
|
+
|
31
|
+
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).
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/arb-xmu-course.
|
36
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'arb/xmu/course/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "arb-xmu-course"
|
8
|
+
spec.version = Arb::Xmu::Course::VERSION
|
9
|
+
spec.authors = ["arybin"]
|
10
|
+
spec.email = ["arybin@163.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{XMU Course}
|
13
|
+
spec.description = %q{XMU Course}
|
14
|
+
spec.homepage = 'https://github.com/arybin-cn/arb-xmu-course'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "bin"
|
18
|
+
spec.executables = 'arb-xmu-course'
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
end
|
data/bin/arb-xmu-course
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'slop'
|
3
|
+
require 'json'
|
4
|
+
require 'httpclient'
|
5
|
+
require 'nokogiri'
|
6
|
+
require 'terminal-table'
|
7
|
+
require 'arb/xmu/course'
|
8
|
+
|
9
|
+
include Arb::Util
|
10
|
+
include Arb::Xmu::Course
|
11
|
+
|
12
|
+
TOKEN_FILE_NAME = 'encrypted_token'
|
13
|
+
TOKEN_SEPARATOR = ':'
|
14
|
+
|
15
|
+
|
16
|
+
def filter_text(raw)
|
17
|
+
if Enumerable===raw
|
18
|
+
return [].tap do |arr|
|
19
|
+
raw.each do |item|
|
20
|
+
arr<<filter_text(item)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
black_list =([] << "\r" << "\n" << "\t" << "'")
|
25
|
+
black_list.each do |tmp|
|
26
|
+
raw.gsub! tmp, ''
|
27
|
+
end
|
28
|
+
raw
|
29
|
+
end
|
30
|
+
|
31
|
+
def set_up_account
|
32
|
+
$stdout.puts '请输入学号:'
|
33
|
+
stu_id= $stdin.gets.chomp
|
34
|
+
$stdout.puts '请输入密码:'
|
35
|
+
passwd= $stdin.gets.chomp
|
36
|
+
File.open TOKEN_FILE_NAME, 'w' do |file|
|
37
|
+
file.write (SimpleDes.encrypt(stu_id)+TOKEN_SEPARATOR+SimpleDes.encrypt(passwd))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def retrieve_check_code(client)
|
42
|
+
File.open 'check_code.jpeg', 'wb' do |file|
|
43
|
+
file.write(client.get('http://bkxk.xmu.edu.cn/xsxk/getCheckCode').body)
|
44
|
+
end
|
45
|
+
$stdout.puts '验证码图片已在当前目录生成,请查看后输入:'
|
46
|
+
$stdin.gets.chomp
|
47
|
+
end
|
48
|
+
|
49
|
+
def authorize_session(client, username, password)
|
50
|
+
checkCode = retrieve_check_code(client)
|
51
|
+
client.get 'http://bkxk.xmu.edu.cn/xsxk/login.html', username: username, password: password, checkCode: checkCode
|
52
|
+
#握手选课系统
|
53
|
+
client.get 'http://bkxk.xmu.edu.cn/xsxk/localInfo.html'
|
54
|
+
client.get 'http://bkxk.xmu.edu.cn/xsxk/index.html'
|
55
|
+
end
|
56
|
+
|
57
|
+
#获取选课轮次
|
58
|
+
def get_select_round(client, id)
|
59
|
+
round_id=nil
|
60
|
+
client.get('http://bkxk.xmu.edu.cn/xsxk/index.html').body.scan /addXklcList\((.*?)\)/m do |match|
|
61
|
+
tmpArr = filter_text(match[0].split(','))
|
62
|
+
if tmpArr[2]==id.to_s
|
63
|
+
round_id=tmpArr[1]
|
64
|
+
break
|
65
|
+
end
|
66
|
+
end
|
67
|
+
round_id
|
68
|
+
end
|
69
|
+
|
70
|
+
#获取主修选课轮次
|
71
|
+
def get_main_select_round(client)
|
72
|
+
get_select_round(client, 1)
|
73
|
+
end
|
74
|
+
|
75
|
+
def show_courses_in_table(courses)
|
76
|
+
table = Terminal::Table.new do |table|
|
77
|
+
table.add_row %w{ID 课程名称 学分 班号 主讲老师 总学时}
|
78
|
+
courses.each do |course_entity|
|
79
|
+
table.add_separator
|
80
|
+
table.add_row course_entity.to_a
|
81
|
+
end
|
82
|
+
end
|
83
|
+
$stdout.puts table
|
84
|
+
end
|
85
|
+
|
86
|
+
def parse_courses(raw_html)
|
87
|
+
|
88
|
+
course_entities = []
|
89
|
+
count=0
|
90
|
+
raw_html.scan /top\.createJxbObj\((.*?)\)/m do |match|
|
91
|
+
tmpArr = filter_text(match[0].split(','))
|
92
|
+
course_entities << CourseEntity.new do |entity|
|
93
|
+
entity.index= (count=count+1)
|
94
|
+
entity.id= tmpArr[1]
|
95
|
+
entity.name= tmpArr[4]
|
96
|
+
entity.credit= tmpArr[10]
|
97
|
+
entity.teacher= tmpArr[14]
|
98
|
+
entity.period= tmpArr[8]
|
99
|
+
entity.class_name= tmpArr[5]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
course_entities
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def request_select_course(client, course_entity, xxlx, lcid)
|
107
|
+
msg = client.get 'http://bkxk.xmu.edu.cn/xsxk/elect.html',
|
108
|
+
method: 'handleZxxk',
|
109
|
+
jxbid: course_entity.id,
|
110
|
+
xxlx: xxlx,
|
111
|
+
xklc: lcid
|
112
|
+
JSON::parse msg.body
|
113
|
+
end
|
114
|
+
|
115
|
+
def create_attempt_thread(max_interval, client, course_entity, xxlx, lcid)
|
116
|
+
Thread.new do
|
117
|
+
count=0
|
118
|
+
loop do
|
119
|
+
count+=1
|
120
|
+
interval = 1+rand(max_interval)
|
121
|
+
sleep interval
|
122
|
+
res = request_select_course(client, course_entity, xxlx, lcid)
|
123
|
+
if res[:success]
|
124
|
+
$stdout.puts "课程:《#{course_entity.name}》已经成功选课!"
|
125
|
+
break
|
126
|
+
else
|
127
|
+
$stdout.puts "课程:《#{course_entity.name}》第#{count}次尝试选课失败,#{interval}秒后将重试。"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def select_school_elective(client, username, password)
|
136
|
+
authorize_session(client, username, password)
|
137
|
+
lcid=get_main_select_round(client)
|
138
|
+
|
139
|
+
school_courses = parse_courses((client.get 'http://bkxk.xmu.edu.cn/xsxk/yxxx.html').body)
|
140
|
+
show_courses_in_table school_courses
|
141
|
+
|
142
|
+
$stdout.puts
|
143
|
+
$stdout.puts '输入要选的课程ID, 以空格分割:'
|
144
|
+
|
145
|
+
ids = $stdin.gets.chomp.split(' ')
|
146
|
+
|
147
|
+
$stdout.puts '输入最大重试间隔时间,默认3秒,要求不得低于2秒:'
|
148
|
+
|
149
|
+
max_interval = $stdin.gets.chomp.to_i
|
150
|
+
max_interval = 2 if max_interval<2
|
151
|
+
|
152
|
+
ids.each do |id|
|
153
|
+
create_attempt_thread(max_interval, client, school_courses.find { |course| course.index==id.to_i }, 1, lcid)
|
154
|
+
end
|
155
|
+
|
156
|
+
loop do
|
157
|
+
sleep 3
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
def select_general_elective
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
def select_public
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
unless (File.exists? TOKEN_FILE_NAME)
|
173
|
+
puts '请先设置账户信息(账户信息会先加密然后保存在本地,用于授权选课系统)'
|
174
|
+
set_up_account
|
175
|
+
exit
|
176
|
+
end
|
177
|
+
|
178
|
+
stu_id, passwd=nil
|
179
|
+
client = HTTPClient.new
|
180
|
+
|
181
|
+
File.open TOKEN_FILE_NAME, 'r' do |file|
|
182
|
+
stu_id, passwd=SimpleDes.decrypt(file.read.split(TOKEN_SEPARATOR))
|
183
|
+
end
|
184
|
+
|
185
|
+
opts = Slop.parse do |o|
|
186
|
+
o.bool *%w{-h --help}, '显示帮助'
|
187
|
+
o.bool *%w{-c --clear}, '清除本地账户信息'
|
188
|
+
o.bool *%w{-r --reset}, '重置账户信息'
|
189
|
+
|
190
|
+
#院选课程 xxlx=4
|
191
|
+
o.bool *%w{-s --school}, '院选选课'
|
192
|
+
#校选课程 xxlx=3
|
193
|
+
o.bool *%w{-g --general}, '校选选课'
|
194
|
+
#公共课程 xxlx=5
|
195
|
+
o.bool *%w{-p --public}, '公共课选课'
|
196
|
+
end
|
197
|
+
|
198
|
+
case true
|
199
|
+
when opts.help?
|
200
|
+
puts(opts) && exit
|
201
|
+
when opts.clear?
|
202
|
+
File.delete(TOKEN_FILE_NAME) && exit
|
203
|
+
when opts.reset?
|
204
|
+
set_up_account && exit
|
205
|
+
when opts.school?
|
206
|
+
select_school_elective(client, stu_id, passwd)
|
207
|
+
when opts.general?
|
208
|
+
# select_general_elective
|
209
|
+
when opts.public?
|
210
|
+
# select_public
|
211
|
+
end
|
212
|
+
|
213
|
+
# client = HTTPClient.new
|
214
|
+
|
215
|
+
# #session init
|
216
|
+
# client.get 'http://bkxk.xmu.edu.cn/xsxk/login.html'
|
217
|
+
#
|
218
|
+
# File.open 'checkcode.jpeg','wb' do |file|
|
219
|
+
# file.write(client.get('http://bkxk.xmu.edu.cn/xsxk/getCheckCode').body)
|
220
|
+
# end
|
221
|
+
#
|
222
|
+
# puts 'enter your student id'
|
223
|
+
# user= gets.chomp
|
224
|
+
# puts 'enter your password'
|
225
|
+
# passwd=gets.chomp
|
226
|
+
# puts 'enter the checkcode'
|
227
|
+
# checkcode=gets.chomp
|
228
|
+
#
|
229
|
+
# client.get 'http://bkxk.xmu.edu.cn/xsxk/login.html',username: user,password: passwd,checkCode: checkcode
|
230
|
+
#
|
231
|
+
# puts (client.get 'http://bkxk.xmu.edu.cn/xsxk/localInfo.html').body
|
232
|
+
#
|
233
|
+
# Nokogiri::parse((client.get 'http://bkxk.xmu.edu.cn/xsxk/index.html').body).css('script').each do |x|
|
234
|
+
# puts x.text
|
235
|
+
# end
|
236
|
+
#
|
data/bin/check_code.jpeg
ADDED
Binary file
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Arb
|
2
|
+
module Xmu
|
3
|
+
module Course
|
4
|
+
class CourseEntity
|
5
|
+
#选课系统课程标识
|
6
|
+
attr_accessor :id
|
7
|
+
#索引
|
8
|
+
attr_accessor :index
|
9
|
+
#课程名
|
10
|
+
attr_accessor :name
|
11
|
+
#学分
|
12
|
+
attr_accessor :credit
|
13
|
+
#教师名
|
14
|
+
attr_accessor :teacher
|
15
|
+
#班级
|
16
|
+
attr_accessor :class_name
|
17
|
+
#总学时
|
18
|
+
attr_accessor :period
|
19
|
+
|
20
|
+
#Entity.new(index: 1,credit: 3, ...)
|
21
|
+
def initialize(**hash_args)
|
22
|
+
yield self if block_given?
|
23
|
+
hash_args.keys.each do |key|
|
24
|
+
send "#{key}=", hash_args[key]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
#for row output
|
29
|
+
def to_a
|
30
|
+
[index, name, credit, class_name, teacher, period]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Arb
|
5
|
+
module Util
|
6
|
+
class SimpleDes
|
7
|
+
ALG = 'DES-EDE3-CBC'
|
8
|
+
KEY = (Dir.pwd.split('').select { |char| char=~/\w/ }.join+'bYaRyBin')[0..7]
|
9
|
+
DES_KEY = KEY.swapcase
|
10
|
+
class << self
|
11
|
+
%i{encrypt decrypt}.each do |type|
|
12
|
+
define_method type do |input|
|
13
|
+
if Enumerable===input
|
14
|
+
return [].tap do |arr|
|
15
|
+
input.each do |item|
|
16
|
+
arr<<(send type, item)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
des = OpenSSL::Cipher::Cipher.new(ALG)
|
21
|
+
des.pkcs5_keyivgen(KEY, DES_KEY)
|
22
|
+
des.send type
|
23
|
+
input=Base64.decode64(input) if :decrypt==type
|
24
|
+
res =(des.update(input)<<des.final)
|
25
|
+
return Base64.encode64(res).chomp if :encrypt==type
|
26
|
+
res
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: arb-xmu-course
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- arybin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-22 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
|
+
description: XMU Course
|
42
|
+
email:
|
43
|
+
- arybin@163.com
|
44
|
+
executables:
|
45
|
+
- arb-xmu-course
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- ".rake_tasks~"
|
51
|
+
- Gemfile
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- arb-xmu-course.gemspec
|
55
|
+
- bin/arb-xmu-course
|
56
|
+
- bin/check_code.jpeg
|
57
|
+
- lib/arb/xmu/course.rb
|
58
|
+
- lib/arb/xmu/course/models/course_entity.rb
|
59
|
+
- lib/arb/xmu/course/models/course_schedule.rb
|
60
|
+
- lib/arb/xmu/course/util/simple_des.rb
|
61
|
+
- lib/arb/xmu/course/version.rb
|
62
|
+
homepage: https://github.com/arybin-cn/arb-xmu-course
|
63
|
+
licenses: []
|
64
|
+
metadata: {}
|
65
|
+
post_install_message:
|
66
|
+
rdoc_options: []
|
67
|
+
require_paths:
|
68
|
+
- lib
|
69
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
requirements: []
|
80
|
+
rubyforge_project:
|
81
|
+
rubygems_version: 2.4.8
|
82
|
+
signing_key:
|
83
|
+
specification_version: 4
|
84
|
+
summary: XMU Course
|
85
|
+
test_files: []
|