eventify 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +11 -0
- data/Gemfile +16 -0
- data/Guardfile +12 -0
- data/LICENSE +22 -0
- data/README.md +67 -0
- data/Rakefile +10 -0
- data/eventify.gemspec +31 -0
- data/lib/eventify.rb +38 -0
- data/lib/eventify/configuration.rb +31 -0
- data/lib/eventify/database.rb +56 -0
- data/lib/eventify/mail.rb +37 -0
- data/lib/eventify/provider/base.rb +51 -0
- data/lib/eventify/provider/fbi.rb +17 -0
- data/lib/eventify/provider/piletilevi.rb +31 -0
- data/lib/eventify/provider/ticketpro.rb +15 -0
- data/lib/eventify/scheduler.rb +31 -0
- data/lib/eventify/version.rb +3 -0
- data/spec/eventify/configuration_spec.rb +46 -0
- data/spec/eventify/database_spec.rb +142 -0
- data/spec/eventify/mail_spec.rb +33 -0
- data/spec/eventify/provider/base_spec.rb +145 -0
- data/spec/eventify/provider/fbi_spec.rb +25 -0
- data/spec/eventify/provider/piletilevi_spec.rb +36 -0
- data/spec/eventify/provider/ticketpro_spec.rb +25 -0
- data/spec/eventify/scheduler_spec.rb +14 -0
- data/spec/eventify_spec.rb +150 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/fbi.html +802 -0
- data/spec/support/piletilevi-empty.xml +10 -0
- data/spec/support/piletilevi-music.xml +51 -0
- data/spec/support/piletilevi-news.xml +17 -0
- data/spec/support/ticketpro.xml +38 -0
- metadata +219 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1794610fc1c068a4f1d74d56f75bfab3ace97d6c
|
4
|
+
data.tar.gz: bbec3e34aeb04c91cc38316ef61abe58013af376
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2ad9d63a20ebdca21135cafa59fc54facab5a2b0c365aac974a10b65a9051136d5f4aa32b40152a29bc1cbd363dcaa144a4f3084234f6fa7a052656bb0b88c3f
|
7
|
+
data.tar.gz: c44b7b19adc7f91199b5cd9051dc0502c8883b2eb164428ec6bc935a6d853026d6c619e118ce2c1864b11df0ecd6fd70aa04b2f7d7995bce83f837cb7ce789d2
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
gem "rufus-scheduler"
|
4
|
+
|
5
|
+
group :development do
|
6
|
+
gem "guard"
|
7
|
+
gem "guard-rspec"
|
8
|
+
gem "guard-bundler"
|
9
|
+
gem "wdm", platform: :mingw
|
10
|
+
gem "win32console", platform: :mingw
|
11
|
+
gem "ruby_gntp"
|
12
|
+
end
|
13
|
+
|
14
|
+
gemspec
|
15
|
+
|
16
|
+
gem "coveralls", :require => false
|
data/Guardfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
notification :ruby_gntp
|
2
|
+
#interactor :off
|
3
|
+
|
4
|
+
guard 'rspec', version: 2 do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
end
|
9
|
+
|
10
|
+
guard 'bundler' do
|
11
|
+
watch('Gemfile')
|
12
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Jarmo Pertman
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Eventify
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/eventify.png)](http://badge.fury.io/rb/eventify)
|
3
|
+
[![Build Status](https://api.travis-ci.org/jarmo/eventify.png)](http://travis-ci.org/jarmo/eventify)
|
4
|
+
[![Coverage](https://coveralls.io/repos/jarmo/eventify/badge.png?branch=master)](https://coveralls.io/r/jarmo/eventify)
|
5
|
+
[![Dependency Status](https://gemnasium.com/jarmo/eventify.png)](https://gemnasium.com/jarmo/eventify)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/jarmo/eventify.png)](https://codeclimate.com/github/jarmo/eventify)
|
7
|
+
|
8
|
+
Are you tired of missing some cool events because you just didn't know them
|
9
|
+
happening? Are you tired of not getting good sitting places because you heard
|
10
|
+
of some event too late?
|
11
|
+
|
12
|
+
If the answer was yes to either of these questions then Eventify can help you!
|
13
|
+
|
14
|
+
Eventify will notify you about upcoming events from different
|
15
|
+
providers/organizers.
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
$ gem install eventify
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
Create configuration file:
|
24
|
+
|
25
|
+
$ ruby -reventify -e "Eventify::Configuration.new.save"
|
26
|
+
|
27
|
+
Edit configuration settings by adding your e-mail into `subscribers` list:
|
28
|
+
|
29
|
+
$ vi ~/.eventify/config.yaml
|
30
|
+
|
31
|
+
Run it from command line and add it into `cron`:
|
32
|
+
|
33
|
+
$ ruby -reventify -e "Eventify.new.process_new_events"
|
34
|
+
|
35
|
+
## Supported Providers
|
36
|
+
|
37
|
+
The following providers are currently supported:
|
38
|
+
|
39
|
+
* [FBI](http://fbi.ee)
|
40
|
+
* [Piletilevi](http://www.piletilevi.ee/)
|
41
|
+
* [Ticketpro](http://www.ticketpro.ee/)
|
42
|
+
|
43
|
+
## Adding New Providers
|
44
|
+
|
45
|
+
Adding new providers is easy. You just need to create a class with one method
|
46
|
+
satisfying the contract:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
require "eventify"
|
50
|
+
|
51
|
+
class MyCustomProvider < Eventify::Provider::Base
|
52
|
+
def self.fetch
|
53
|
+
# fetch some atom feed
|
54
|
+
rss = SimpleRSS.parse open("http://example.org/rss.xml")
|
55
|
+
rss.entries.map { |entry| new id: entry.guid, title: entry.title, link: entry.link, date: entry.pubDate }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# use that provider with Eventify
|
60
|
+
eventify = Eventify.new
|
61
|
+
eventify.providers = [MyCustomProvider]
|
62
|
+
eventify.process_new_events
|
63
|
+
```
|
64
|
+
|
65
|
+
## License
|
66
|
+
|
67
|
+
See [LICENSE](https://github.com/jarmo/eventify/blob/master/LICENSE).
|
data/Rakefile
ADDED
data/eventify.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'eventify/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "eventify"
|
8
|
+
spec.version = Eventify::VERSION
|
9
|
+
spec.authors = ["Jarmo Pertman"]
|
10
|
+
spec.email = ["jarmo.p@gmail.com"]
|
11
|
+
spec.description = %q{Get notifications about upcoming events from different providers/organizers.}
|
12
|
+
spec.summary = %q{Get notifications about upcoming events from different providers/organizers.}
|
13
|
+
spec.homepage = "https://github.com/jarmo/eventify"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "simple-rss"
|
22
|
+
spec.add_dependency "nokogiri"
|
23
|
+
spec.add_dependency "sqlite3"
|
24
|
+
spec.add_dependency "mail"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
spec.add_development_dependency "rspec"
|
29
|
+
spec.add_development_dependency "webmock"
|
30
|
+
spec.add_development_dependency "yard"
|
31
|
+
end
|
data/lib/eventify.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path("eventify/version", __dir__)
|
2
|
+
require File.expand_path("eventify/configuration", __dir__)
|
3
|
+
require File.expand_path("eventify/provider/base", __dir__)
|
4
|
+
require File.expand_path("eventify/provider/fbi", __dir__)
|
5
|
+
require File.expand_path("eventify/provider/piletilevi", __dir__)
|
6
|
+
require File.expand_path("eventify/provider/ticketpro", __dir__)
|
7
|
+
require File.expand_path("eventify/database", __dir__)
|
8
|
+
require File.expand_path("eventify/mail", __dir__)
|
9
|
+
|
10
|
+
class Eventify
|
11
|
+
attr_reader :configuration
|
12
|
+
|
13
|
+
def initialize(configuration=Eventify::Configuration.new)
|
14
|
+
@configuration = configuration
|
15
|
+
end
|
16
|
+
|
17
|
+
def all_events
|
18
|
+
@all_events ||= providers.flat_map(&:fetch).uniq
|
19
|
+
end
|
20
|
+
|
21
|
+
def new_events
|
22
|
+
@new_events ||= all_events.reject(&:exists?)
|
23
|
+
end
|
24
|
+
|
25
|
+
def process_new_events
|
26
|
+
all_new_events = new_events
|
27
|
+
return if all_new_events.empty?
|
28
|
+
|
29
|
+
Eventify::Mail.deliver all_new_events, @configuration
|
30
|
+
all_new_events.each(&:save)
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_writer :providers
|
34
|
+
|
35
|
+
def providers
|
36
|
+
@providers ||= [Eventify::Provider::Piletilevi, Eventify::Provider::Ticketpro, Eventify::Provider::FBI]
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
class Eventify::Configuration
|
4
|
+
PATH = File.expand_path("~/.eventify/config.yaml")
|
5
|
+
|
6
|
+
def initialize(configuration = {})
|
7
|
+
@configuration = default_configuration.merge(load).merge(configuration)
|
8
|
+
end
|
9
|
+
|
10
|
+
def save
|
11
|
+
File.open(PATH, "w") { |f| f.write YAML.dump(@configuration) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
@configuration[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def default_configuration
|
21
|
+
{
|
22
|
+
subscribers: ["user@example.org"],
|
23
|
+
mail: Mail.delivery_method.settings
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def load
|
28
|
+
YAML.load(File.read(PATH)) rescue {}
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "sqlite3"
|
2
|
+
|
3
|
+
class Eventify::Database
|
4
|
+
PATH = File.expand_path("~/.eventify/events.sqlite3", __dir__)
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def exists?(event)
|
8
|
+
results = sqlite.execute "select 1 from event where id=? and provider=? and link=?", event.id, event.provider, event.link
|
9
|
+
!results.empty?
|
10
|
+
end
|
11
|
+
|
12
|
+
def save(event)
|
13
|
+
sqlite.execute "insert into event values(?, ?, ?, ?, ?)",
|
14
|
+
event.id,
|
15
|
+
event.provider,
|
16
|
+
event.title,
|
17
|
+
event.link,
|
18
|
+
event.date.to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def events
|
22
|
+
translated_events = []
|
23
|
+
sqlite.execute("select * from event") do |event|
|
24
|
+
translated_events << const_get(event["provider"]).new(
|
25
|
+
id: event["id"],
|
26
|
+
title: event["title"],
|
27
|
+
link: event["link"],
|
28
|
+
date: Time.parse(event["date"])
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
translated_events
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def sqlite
|
38
|
+
@sqlite ||= begin
|
39
|
+
FileUtils.mkdir_p File.dirname(PATH)
|
40
|
+
database = SQLite3::Database.new PATH
|
41
|
+
database.results_as_hash = true
|
42
|
+
|
43
|
+
database.execute "create table if not exists event(
|
44
|
+
id text,
|
45
|
+
provider text,
|
46
|
+
title text,
|
47
|
+
link text,
|
48
|
+
date text,
|
49
|
+
primary key (id, provider, link)
|
50
|
+
)"
|
51
|
+
|
52
|
+
database
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "mail"
|
2
|
+
|
3
|
+
class Eventify::Mail
|
4
|
+
class << self
|
5
|
+
def deliver(new_events, configuration)
|
6
|
+
formatted_events = format(new_events)
|
7
|
+
|
8
|
+
configuration[:subscribers].each do |subscriber|
|
9
|
+
::Mail.deliver do
|
10
|
+
delivery_method :smtp, configuration[:mail]
|
11
|
+
|
12
|
+
content_type "text/plain; charset=utf-8"
|
13
|
+
|
14
|
+
to subscriber
|
15
|
+
from "no-reply@eventify.io"
|
16
|
+
subject "Event Rumours"
|
17
|
+
body formatted_events
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def format(events)
|
23
|
+
header = "There are some rumours going on about #{events.size} possible awesome events:"
|
24
|
+
|
25
|
+
formatted_events = events.sort.reduce([header, ""]) do |memo, event|
|
26
|
+
memo << "* #{event.title}".force_encoding("UTF-8")
|
27
|
+
memo << " #{event.link}"
|
28
|
+
memo << ""
|
29
|
+
end
|
30
|
+
|
31
|
+
footer = "Your Humble Servant,\nEventify"
|
32
|
+
|
33
|
+
formatted_events << footer
|
34
|
+
formatted_events.join("\n")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
class Eventify
|
2
|
+
module Provider
|
3
|
+
class Base
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def fetch
|
8
|
+
raise NotImplementedError
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :id, :title, :link, :date
|
13
|
+
|
14
|
+
def initialize(event)
|
15
|
+
@id = event[:id] or raise MissingAttributeError.new("id is missing")
|
16
|
+
@title = event[:title] or raise MissingAttributeError.new("title is missing")
|
17
|
+
@link = event[:link] or raise MissingAttributeError.new("link is missing")
|
18
|
+
@date = event[:date]
|
19
|
+
end
|
20
|
+
|
21
|
+
def provider
|
22
|
+
@provider ||= self.class.name
|
23
|
+
end
|
24
|
+
|
25
|
+
def save
|
26
|
+
Database.save self
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def exists?
|
31
|
+
Database.exists? self
|
32
|
+
end
|
33
|
+
|
34
|
+
def ==(other)
|
35
|
+
id == other.id && provider == other.provider
|
36
|
+
end
|
37
|
+
|
38
|
+
alias_method :eql?, :==
|
39
|
+
|
40
|
+
def hash
|
41
|
+
"#{id}-#{provider}-#{link}".hash
|
42
|
+
end
|
43
|
+
|
44
|
+
def <=>(other)
|
45
|
+
title <=> other.title
|
46
|
+
end
|
47
|
+
|
48
|
+
MissingAttributeError = Class.new(RuntimeError)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "open-uri"
|
2
|
+
require "nokogiri"
|
3
|
+
|
4
|
+
module Eventify::Provider
|
5
|
+
class FBI < Base
|
6
|
+
URL = "http://www.fbi.ee/"
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def fetch
|
10
|
+
doc = Nokogiri::HTML(open(URL))
|
11
|
+
doc.search(".article-list li h2 a").map do |a|
|
12
|
+
new id: a["href"], title: a.content, link: a["href"], date: Time.now
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "simple-rss"
|
2
|
+
require "open-uri"
|
3
|
+
|
4
|
+
module Eventify::Provider
|
5
|
+
class Piletilevi < Base
|
6
|
+
# http://www.piletilevi.ee/est/uldinfo/rss
|
7
|
+
URLS = [
|
8
|
+
"http://www.piletilevi.ee/news.rss.php?path=est/uudised",
|
9
|
+
"http://www.piletilevi.ee/news.rss.php?path=est/teatriuudised",
|
10
|
+
"http://www.piletilevi.ee/news.rss.php?path=est/muudatused",
|
11
|
+
"http://www.piletilevi.ee/category.rss.php?path=est/piletid/muusika",
|
12
|
+
# 404?
|
13
|
+
#"http://www.piletilevi.ee/category.rss.php?path=est/piletid/teater_-_kunst",
|
14
|
+
"http://www.piletilevi.ee/category.rss.php?path=est/piletid/kogupere",
|
15
|
+
"http://www.piletilevi.ee/category.rss.php?path=est/piletid/sport",
|
16
|
+
"http://www.piletilevi.ee/category.rss.php?path=est/piletid/festival",
|
17
|
+
"http://www.piletilevi.ee/category.rss.php?path=est/piletid/film",
|
18
|
+
"http://www.piletilevi.ee/category.rss.php?path=est/piletid/kinkekaardid",
|
19
|
+
"http://www.piletilevi.ee/category.rss.php?path=est/piletid/varia"
|
20
|
+
]
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def fetch
|
24
|
+
URLS.each.reduce([]) do |memo, url|
|
25
|
+
rss = SimpleRSS.parse open(url)
|
26
|
+
memo + rss.entries.map { |entry| new id: entry.guid, title: entry.title, link: entry.link, date: entry.pubDate }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|