markify 0.1.0

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.
@@ -0,0 +1,41 @@
1
+ # Markify
2
+
3
+ Markify is a ruby script to detect new marks in the
4
+ Studierendeninformationssystem (SIS) of the University of Applied Sciences
5
+ Bonn-Rhein-Sieg. If markify detects new marks, they will send you a xmpp
6
+ message for every change.
7
+
8
+ ## Installation
9
+
10
+ Install Markify via rubygems:
11
+
12
+ $ gem install markify
13
+
14
+ ## Usage
15
+
16
+ To create the initial markify configuration run:
17
+
18
+ $ markify --init
19
+
20
+ The default configuration path is ``/home/$user/markify/config.yml``. You have to add your SIS and xmpp login data.
21
+
22
+ Possible options to run markify:
23
+
24
+ $ markify [options]
25
+
26
+ Optional options:
27
+ -s, --send Send xmpp messages
28
+ -n, --noop No operation, only stout output
29
+ -v, --[no-]verbose Run verbosely
30
+
31
+ -f, --config-file FILE Config file (default: ~/markify/config.yml)
32
+
33
+ Common options:
34
+ --init Create example configuration
35
+ --test Test configuration and accounts
36
+ -h, --help Show this message
37
+ --version Show version inforamtion
38
+
39
+ ## License
40
+
41
+ Released under the GNU GENERAL PUBLIC LICENSE Version 3. © Daniel Meißner, 2013
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new
5
+
6
+ task :default => :spec
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ =begin
4
+ Copyright Daniel Meißner <meise+markify@3st.be>, 2013
5
+
6
+ This file is part of Markify.
7
+
8
+ Markify is free software: you can redistribute it and/or modify
9
+ it under the terms of the GNU General Public License as published by
10
+ the Free Software Foundation, either version 3 of the License, or
11
+ (at your option) any later version.
12
+
13
+ Markify is distributed in the hope that it will be useful,
14
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ GNU General Public License for more details.
17
+
18
+ You should have received a copy of the GNU General Public License
19
+ along with Markify. If not, see <http://www.gnu.org/licenses/>.
20
+ =end
21
+
22
+ begin
23
+ require 'markify'
24
+ rescue LoadError
25
+ if File.symlink? __FILE__
26
+ require File.dirname(File.readlink(__FILE__)) + '/../lib/markify'
27
+ else
28
+ require File.dirname(__FILE__) + '/../lib/markify'
29
+ end
30
+ end
31
+
32
+ Markify.run!
@@ -0,0 +1,12 @@
1
+ sis:
2
+ login_name:
3
+ password:
4
+
5
+ xmpp:
6
+ bot_id:
7
+ bot_password:
8
+ recipients: ['']
9
+
10
+ general:
11
+ verbose: false
12
+ database_file: "/home/foobaz/markify/hashes.txt"
@@ -0,0 +1,3 @@
1
+ # markify hash database
2
+ fb4dc538d57d3c34b7fed1b83372e5aa07b79ae78852e5a986b71fb3e29ef0a8
3
+ 5a523e24796c9ff3e5c664f2c54fa0e87fc34b5696ad0a25c7869b018b27db4a
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+ =begin
3
+ Copyright Daniel Meißner <meise+markify@3st.be>, 2013
4
+
5
+ This file is part of Markify.
6
+
7
+ Markify is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Markify is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Markify. If not, see <http://www.gnu.org/licenses/>.
19
+ =end
20
+
21
+ require 'date'
22
+ require 'digest'
23
+ require 'yaml'
24
+ require 'pathname'
25
+
26
+ require "markify/version"
27
+ require "markify/scraper"
28
+ require "markify/database"
29
+ require "markify/bot"
30
+ require "markify/settings"
31
+ require "markify/optparser"
32
+ require 'markify/mark'
33
+
34
+ module Markify
35
+
36
+ def self.run!
37
+ @options = Markify::OptParser.parse!
38
+ @config = Markify::Settings.load!(@options[:config_file])
39
+
40
+ if @options[:test]
41
+ Markify::Settings.test_settings(@config)
42
+ exit
43
+ end
44
+
45
+ mark_database = Markify::Database.new(@config['general']['database_file'])
46
+
47
+ all_marks = Markify::Scraper.new(@config['sis']['login_name'], @config['sis']['login_password']).scrape!
48
+ new_marks = mark_database.check_for_new_marks(all_marks)
49
+
50
+ if new_marks.count == 0 && (@config['general']['verbose'] || @options[:noop])
51
+ puts "No new marks."
52
+ exit
53
+ end
54
+
55
+ bot = Markify::Bot.new(@config['xmpp']['bot_id'], @config['xmpp']['bot_password']) if @options[:send]
56
+
57
+ new_marks.sort_by{|mark| mark.date}.each do |mark|
58
+ unless @options[:noop]
59
+ mark_database.write_checksum(mark.hash)
60
+ end
61
+
62
+ bot.send_message(@config['xmpp']['recipients'], mark.to_s) if @options[:send]
63
+
64
+ puts mark.to_s if @config['general']['verbose'] || @options[:verbose]
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ =begin
3
+ Copyright Daniel Meißner <meise+markify@3st.be>, 2013
4
+
5
+ This file is part of Markify.
6
+
7
+ Markify is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Markify is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Markify. If not, see <http://www.gnu.org/licenses/>.
19
+ =end
20
+
21
+ require 'xmpp4r/client'
22
+
23
+ class Markify::Bot
24
+ def initialize(id, password)
25
+ @client = Jabber::Client.new(id)
26
+
27
+ @client.connect
28
+ @client.auth(password)
29
+ end
30
+
31
+ def send_message(receiver, message)
32
+ receiver.each do |r|
33
+ @client.send(Jabber::Message.new(r, message))
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ =begin
3
+ Copyright Daniel Meißner <meise+markify@3st.be>, 2013
4
+
5
+ This file is part of Markify.
6
+
7
+ Markify is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Markify is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Markify. If not, see <http://www.gnu.org/licenses/>.
19
+ =end
20
+
21
+ class Markify::Database
22
+
23
+ attr_reader :file_path
24
+
25
+ def initialize(path = nil)
26
+ @file_path = path || Pathname(ENV['HOME'] + '/markify/hashes.txt')
27
+ @checksums = read_checksum_file
28
+ end
29
+
30
+ def check_for_new_marks(marks)
31
+ new_marks = marks.clone
32
+
33
+ read_checksum_file.each do |line|
34
+ marks.each do |mark|
35
+ if mark.hash.match(line)
36
+ new_marks.delete(mark)
37
+ end
38
+ end
39
+ end
40
+
41
+ new_marks
42
+ end
43
+
44
+ def write_checksum(checksum)
45
+ File.open(@file_path, 'a+') do |file|
46
+ file.puts checksum
47
+ end
48
+ end
49
+
50
+ def checksums
51
+ checksums = read_checksum_file
52
+
53
+ checksums.each do |checksum|
54
+ if checksum.match(/#/)
55
+ checksums.delete(checksum)
56
+ end
57
+ end
58
+ end
59
+
60
+ protected
61
+
62
+ def create_checksum_file
63
+ unless @file_path.dirname.exist?
64
+ @file_path.dirname.mkdir
65
+ end
66
+
67
+ File.open(@file_path, 'a+') do |file|
68
+ file.puts "# markify hash database"
69
+ end
70
+ end
71
+
72
+ def read_checksum_file
73
+ hashes = []
74
+
75
+ if @file_path.exist?
76
+ hashes = File.open(@file_path, 'r').read.split(/\n/)
77
+ else
78
+ create_checksum_file
79
+ end
80
+
81
+ hashes
82
+ end
83
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+ =begin
3
+ Copyright Daniel Meißner <meise+markify@3st.be>, 2013
4
+
5
+ This file is part of Markify.
6
+
7
+ Markify is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Markify is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Markify. If not, see <http://www.gnu.org/licenses/>.
19
+ =end
20
+
21
+ class Markify::Mark
22
+
23
+ attr_reader :name, :id, :date, :mark, :passed, :try, :hash
24
+
25
+ def initialize(name, id, mark, passed, try, date)
26
+ @name = name
27
+ @id = id.to_i
28
+ @mark = mark.to_f
29
+ @passed = passed
30
+ @try = try.to_i
31
+ @date = Date.strptime(date, '%d.%m.%Y')
32
+ @hash = self.to_sha256.to_s
33
+ end
34
+
35
+ def to_s
36
+ message =<<MESSAGE
37
+ exam: #{@name}
38
+ mark: #{@mark}
39
+ passed: #{@passed}
40
+ try: #{@try}
41
+ date: #{@date}
42
+
43
+ hash: #{@hash}
44
+
45
+ MESSAGE
46
+ end
47
+
48
+ def to_sha256
49
+ Digest::SHA256.new << "#{@name}#{@id}#{@date}#{@mark}#{@try}#{@passed}"
50
+ end
51
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+ =begin
3
+ Copyright Daniel Meißner <meise+markify@3st.be>, 2013
4
+
5
+ This file is part of Markify.
6
+
7
+ Markify is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Markify is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Markify. If not, see <http://www.gnu.org/licenses/>.
19
+ =end
20
+
21
+ require 'optparse'
22
+
23
+ module Markify::OptParser
24
+
25
+ def self.parse!
26
+ parse
27
+ end
28
+
29
+ protected
30
+
31
+ def self.parse
32
+ options = {}
33
+
34
+ oparser = OptionParser.new do |opts|
35
+ opts.banner = "Usage: #{$0} [options]"
36
+
37
+ opts.separator ""
38
+ opts.separator "Optional options:"
39
+
40
+ opts.on('-s', '--send', 'Send xmpp messages') do |s|
41
+ options[:send] = s
42
+ end
43
+
44
+ opts.on('-n', '--noop', 'No operation, only stout output') do |n|
45
+ options[:noop] = n
46
+ end
47
+
48
+ opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
49
+ options[:verbose] = v
50
+ end
51
+
52
+ opts.separator ""
53
+
54
+ opts.on("-f", "--config-file FILE", "Config file (default: ~/markify/config.yml)") do |file|
55
+ options[:config_file] = Pathname(file)
56
+ end
57
+
58
+ opts.separator ''
59
+ opts.separator 'Common options:'
60
+
61
+ opts.on_tail('--init', 'Create example configuration') do
62
+ if options[:config_file]
63
+ Markify::Settings.create_exmple_config(options[:config_file])
64
+ else
65
+ Markify::Settings.create_example_config
66
+ end
67
+ end
68
+
69
+ opts.on_tail('--test', 'Test configuration and accounts') do
70
+ options[:test] = true
71
+ end
72
+
73
+ opts.on_tail('-h', '--help', 'Show this message') do
74
+ puts opts
75
+ exit
76
+ end
77
+
78
+ opts.on_tail('--version', 'Show version inforamtion') do
79
+ puts Markify::LICENCE
80
+ exit
81
+ end
82
+
83
+ if ARGV.size == 0
84
+ puts Markify::DESCRIPTION
85
+ puts
86
+ puts opts
87
+ puts
88
+ puts Markify::LICENCE
89
+
90
+ exit
91
+ end
92
+ end.parse!
93
+
94
+ options
95
+ end
96
+ end
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+ =begin
3
+ Copyright Daniel Meißner <meise+markify@3st.be>, 2013
4
+
5
+ This file is part of Markify.
6
+
7
+ Markify is free software: you can redistribute it and/or modify
8
+ it under the terms of the GNU General Public License as published by
9
+ the Free Software Foundation, either version 3 of the License, or
10
+ (at your option) any later version.
11
+
12
+ Markify is distributed in the hope that it will be useful,
13
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ GNU General Public License for more details.
16
+
17
+ You should have received a copy of the GNU General Public License
18
+ along with Markify. If not, see <http://www.gnu.org/licenses/>.
19
+ =end
20
+
21
+ require 'mechanize'
22
+
23
+ class Markify::Scraper
24
+
25
+ attr_reader :marks
26
+
27
+ def initialize(login_name, login_password, sis_login_page = nil)
28
+ @agent = Mechanize.new
29
+
30
+ @sis = {}
31
+ @sis[:login_page] = sis_login_page || 'https://dias.fh-bonn-rhein-sieg.de/d3/SISEgo.asp?UserAcc=Gast&DokID=DiasSWeb&ADias2Dction=Login'
32
+ @sis[:login_name] = login_name
33
+ @sis[:login_password] = login_password
34
+
35
+ @marks = []
36
+ end
37
+
38
+ def scrape!
39
+ scrape
40
+ end
41
+
42
+ def test_login
43
+ login_page = @agent.get(@sis[:login_page])
44
+ error_page = sis_login(@sis[:login_name], @sis[:login_password], login_page)
45
+
46
+ if error_page.search(".//*[@id='inhalt']/table/tbody/tr/td/form/center/table/tr[3]/td").text =~
47
+ /Benutzername unbekannt oder falsches Kennwort eingegeben!/
48
+ puts "SIS: Username and password wrong."
49
+ else
50
+ puts "SIS: Login works."
51
+ end
52
+ end
53
+
54
+ protected
55
+
56
+ def scrape
57
+ login_page = @agent.get(@sis[:login_page])
58
+
59
+ first_sis_page = sis_login(@sis[:login_name], @sis[:login_password], login_page)
60
+ marks_table = get_marks_table(first_sis_page)
61
+
62
+ get_marks(marks_table)
63
+ end
64
+
65
+ def get_marks(marks_table)
66
+ marks_table.each do |m|
67
+ mark = m.search('./td')
68
+
69
+ # use only lines with a id
70
+ unless mark[0].text =~ /\d{4,5}/
71
+ next
72
+ end
73
+
74
+ @marks << Markify::Mark.new(
75
+ mark[1].text, # curse name
76
+ mark[0].text, # id
77
+ mark[4].text, # mark
78
+ mark[7].text, # passed
79
+ mark[8].text, # try
80
+ mark[12].text # date
81
+ )
82
+ end
83
+
84
+ @marks
85
+ end
86
+
87
+ def get_marks_table(page_after_login)
88
+ marks_table = nil
89
+
90
+ if link = page_after_login.link_with(text: "Notenspiegel (vollständig)")
91
+ mark_page = link.click
92
+ marks_table = mark_page.search(".//*[@id='inhalt']/table/tbody/tr/td/center/table/tr")
93
+ else
94
+ puts "Page after login not available."
95
+ exit
96
+ end
97
+
98
+ marks_table
99
+ end
100
+
101
+ def sis_login(name, password, login_page)
102
+ page_after_login = login_page.form_with(action: '/d3/SISEgo.asp?formact=Login') do |form|
103
+ form.txtBName = name
104
+ form.txtKennwort = password
105
+ end.click_button
106
+ end
107
+ end