Skip to content

Commit

Permalink
Merge pull request #7 from martinsabo/master
Browse files Browse the repository at this point in the history
Automatické postovanie do fóra, closes #2
  • Loading branch information
jsuchal committed Feb 4, 2016
2 parents 490fb5b + 0c3eacd commit ee9cf7c
Show file tree
Hide file tree
Showing 21 changed files with 2,856 additions and 161 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ source 'https://rubygems.org'
gem 'nokogiri'
gem 'curb'
gem 'dotenv'
gem 'discourse_api'
gem 'abstract_type'

group :development do
gem 'rspec'
Expand Down
13 changes: 13 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
GEM
remote: https://rubygems.org/
specs:
abstract_type (0.0.7)
ast (2.2.0)
codeclimate-test-reporter (0.4.7)
simplecov (>= 0.7.1, < 1.0.0)
curb (0.9.1)
diff-lcs (1.2.5)
discourse_api (0.7.0)
faraday (~> 0.9.0)
faraday_middleware (~> 0.9)
rack (~> 1.5)
docile (1.1.5)
dotenv (2.1.0)
faraday (0.9.2)
multipart-post (>= 1.2, < 3)
faraday_middleware (0.10.0)
faraday (>= 0.7.4, < 0.10)
json (1.8.3)
mini_portile2 (2.0.0)
multipart-post (2.0.0)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
parser (2.3.0.2)
ast (~> 2.2)
powerpack (0.1.1)
rack (1.6.4)
rainbow (2.1.0)
rake (10.4.2)
rspec (3.4.0)
Expand Down Expand Up @@ -46,8 +57,10 @@ PLATFORMS
ruby

DEPENDENCIES
abstract_type
codeclimate-test-reporter
curb
discourse_api
dotenv
nokogiri
rake
Expand Down
40 changes: 0 additions & 40 deletions lib/slack_notifier.rb

This file was deleted.

32 changes: 0 additions & 32 deletions lib/uvo_parser.rb

This file was deleted.

28 changes: 0 additions & 28 deletions lib/uvo_scraper.rb

This file was deleted.

34 changes: 9 additions & 25 deletions lib/uvobot.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,10 @@
class Uvobot
def initialize(notifier, scraper)
@notifier = notifier
@scraper = scraper
end

def run(release_date)
if @scraper.issue_ready?(release_date)
notify_announcements(release_date)
else
@notifier.new_issue_not_published
end
end

private

def notify_announcements(release_date)
page_info, announcements = @scraper.get_announcements(release_date)

if announcements.count > 0
@notifier.matching_announcements_found(page_info, announcements)
else
@notifier.no_announcements_found
end
end
require_relative 'uvobot/worker'
require_relative 'uvobot/uvo_scraper'
require_relative 'uvobot/uvo_parser'
require_relative 'uvobot/discourse_client'
require_relative 'uvobot/notifications/discourse_notifier'
require_relative 'uvobot/notifications/slack_notifier'
require_relative 'uvobot/worker'

module Uvobot
end
15 changes: 15 additions & 0 deletions lib/uvobot/discourse_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'discourse_api'

module Uvobot
class DiscourseClient
def initialize(host, api_key, api_username)
@client = DiscourseApi::Client.new(host, api_key, api_username)
end

def create_topic(args = {})
@client.create_topic(args)
rescue DiscourseApi::Error
return nil
end
end
end
54 changes: 54 additions & 0 deletions lib/uvobot/notifications/discourse_notifier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
require_relative 'notifier'

module Uvobot
module Notifications
class DiscourseNotifier < Notifier
def initialize(discourse_client, category, scraper)
@client = discourse_client
@category = category
@scraper = scraper
end

def no_announcements_found
# noop
end

def new_issue_not_published
# noop
end

def matching_announcements_found(_page_info, announcements)
announcements.each do |a|
topic = announcement_to_topic(a)
@client.create_topic(
title: topic[:title],
raw: topic[:body],
category: @category
)
end
end

private

def announcement_to_topic(announcement)
detail = @scraper.get_announcement_detail(announcement[:link][:href])

{
title: announcement[:procurement_subject].to_s,
body: ["**Obstarávateľ:** #{announcement[:procurer]} ",
"**Predmet obstarávania:** #{announcement[:procurement_subject]} ",
detail_message(detail),
"**Zdroj:** [#{announcement[:link][:text]}](#{announcement[:link][:href]})"].join("\n")
}
end

def detail_message(detail)
if detail
"**Cena:** #{detail[:amount]} EUR "
else
"**Detaily sa nepodarilo extrahovať.** "
end
end
end
end
end
13 changes: 13 additions & 0 deletions lib/uvobot/notifications/notifier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
require 'abstract_type'

module Uvobot
module Notifications
class Notifier
include AbstractType

abstract_method :matching_announcements_found
abstract_method :no_announcements_found
abstract_method :new_issue_not_published
end
end
end
45 changes: 45 additions & 0 deletions lib/uvobot/notifications/slack_notifier.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'json'
require 'curb'
require_relative 'notifier'

module Uvobot
module Notifications
class SlackNotifier < Notifier
def initialize(slack_webhook, http_client = Curl)
@slack_webhook = slack_webhook
@http_client = http_client
end

def new_issue_not_published
send_message('*Fíha, dnes na ÚVO nevyšlo nové vydanie vestníka?*')
end

def matching_announcements_found(page, announcements)
send_message("Našiel som niečo nové na ÚVO! (#{page})")

announcements.each do |a|
send_message("<#{a[:link][:href]}|#{a[:link][:text]}>: *#{a[:procurer]}* #{a[:procurement_subject]}")
end
end

def no_announcements_found
send_message('Dnes som nenašiel žiadne nové IT zákazky.')
end

private

def send_message(text)
@http_client.post(@slack_webhook, payload(text))
end

def payload(text)
{
text: text,
channel: '#general',
username: 'uvobot',
icon_emoji: ':mag_right:'
}.to_json
end
end
end
end
45 changes: 45 additions & 0 deletions lib/uvobot/uvo_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
require 'nokogiri'

module Uvobot
class UvoParser
def self.parse_announcements(html)
announcements = []

doc(html).css('.oznamenie').each do |a|
link = a.css('.ozn1 a').first
procurer = a.css('.ozn2').text.strip
procurement_subject = a.css('.ozn3').text.strip

announcements << {
link: { text: link.text, href: link['href'] },
procurer: procurer,
procurement_subject: procurement_subject
}
end
announcements
end

def self.parse_detail(html)
# there are multiple formats of detail page, this method does not handle them all for now
detail = {}
h_doc = doc(html)
amount_node = h_doc.xpath('//div[text()="Hodnota "]').css('span').first
return nil if amount_node.nil?

detail[:amount] = amount_node.text
detail
end

def self.parse_page_info(html)
doc(html).css('.search-results').first.text.strip
end

def self.parse_issue_header(html)
doc(html).css('h1')[1].text
end

def self.doc(html)
Nokogiri::HTML(html)
end
end
end
Loading

0 comments on commit ee9cf7c

Please sign in to comment.