/
ufc.go
142 lines (113 loc) · 3.23 KB
/
ufc.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package main
import (
"fmt"
"os"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/PuerkitoBio/goquery"
"github.com/everdev/mack"
"github.com/robfig/cron"
)
var standardDate string = "Monday, January 02 2006" // 1-2-3-4-5-6 (01/02 03:04:05PM '06 -0700 = Mon Jan 2 15:04:05 MST 2006)
var wg sync.WaitGroup
func main() {
wg.Add(1)
c := cron.New()
c.AddFunc("@every 8h30m", check)
c.Start()
wg.Wait()
}
func check() {
doc, err := goquery.NewDocument("http://www.ufc.com/schedule/event")
if err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
// My filtering of content is obviously very fragile and error prone
// It'll likely need changing every time UFC change the structure of their page
doc.Find("#event_content > div").Each(func(i int, s *goquery.Selection) {
event := s.Find(".event-title").Text()
if invalid(event) {
return
}
// Stop after the first item
// We could try using `.First()` but we have no way with CSS to determine valid item
if i > 2 {
return
}
extractedDate := strings.TrimSpace(
s.Find(".event-info .date").Text(),
)
date := strings.Join(
[]string{
extractedDate,
strconv.Itoa(
time.Now().AddDate(verifyYear(extractedDate), 0, 0).Year(),
),
},
" ",
)
t, _ := time.Parse(standardDate, date)
daysAway := daysDiff(t, time.Now())
switch {
case withinAWeek(daysAway):
mack.Say(fmt.Sprintf("The UFC Event %s is coming up in a week", event))
mack.Notify(fmt.Sprintf("%s (%d days away)", date, daysAway), "UFC Event", event, "Ping")
case withinAFewDays(daysAway):
mack.Say(fmt.Sprintf("The UFC Event %s is coming up in a few days", event))
mack.Notify(fmt.Sprintf("%s (%d days away)", date, daysAway), "UFC Event", event, "Ping")
case startsToday(daysAway):
mack.Say(fmt.Sprintf("The UFC Event %s is TODAY!", event))
mack.Notify(fmt.Sprintf("%s (%d days away)", date, daysAway), "UFC Event", event, "Ping")
}
})
}
func withinAWeek(daysAway int) bool {
return daysAway <= 7 && daysAway > 3
}
func withinAFewDays(daysAway int) bool {
return daysAway <= 3 && daysAway > 0
}
func startsToday(daysAway int) bool {
return daysAway == 0
}
func invalid(e string) bool {
match, _ := regexp.MatchString("(?i)ufc\\s\\d{3}", e)
if e == "" || match == false {
return true
}
return false
}
func verifyYear(date string) int {
t, _ := time.Parse("Monday, January 2", date)
eventMonth := int(t.Month())
if eventMonth < int(time.Now().Month()) {
return 1 // if the event month is behind the current month, then we'll assume it is for next year
}
return 0
}
// Following functions borrowed from
// http://play.golang.org/p/nTcjGZQKAa
// https://groups.google.com/forum/#!topic/golang-nuts/O2NaRAH94GI
func lastDayOfYear(t time.Time) time.Time {
return time.Date(t.Year(), 12, 31, 0, 0, 0, 0, t.Location())
}
func firstDayOfNextYear(t time.Time) time.Time {
return time.Date(t.Year()+1, 1, 1, 0, 0, 0, 0, t.Location())
}
func daysDiff(a, b time.Time) (days int) {
cur := b
for cur.Year() < a.Year() {
// add 1 to count the last day of the year too.
days += lastDayOfYear(cur).YearDay() - cur.YearDay() + 1
cur = firstDayOfNextYear(cur)
}
days += a.YearDay() - cur.YearDay()
if b.AddDate(0, 0, days).After(a) {
days -= 1
}
return days
}