-
-
Save treed/9c933f860adedadf4916cabfe6932669 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"encoding/json" | |
"fmt" | |
"github.com/PuerkitoBio/goquery" | |
"io/ioutil" | |
"net/http" | |
"net/url" | |
"os" | |
"sort" | |
"strconv" | |
"strings" | |
"text/template" | |
"time" | |
) | |
func linkChannel(channels string) string { | |
if strings.Contains(channels, "ESPN2") { | |
return "[](http://espn.go.com/watchespn/index/_/sport/soccer-futbol/channel/espn2)" | |
} else if strings.Contains(channels, "Fox Sports 1") { | |
return "[](http://msn.foxsports.com/foxsports1)" | |
} else if strings.Contains(channels, "UniMas") { | |
return "[](http://tv.univision.com/unimas)" | |
} else if strings.Contains(channels, "MLS LIVE") { | |
return "[](http://live.mlssoccer.com/mlsmdl)" | |
} | |
return "" | |
} | |
func linkTeam(team string) string { | |
if strings.Contains(team, "Chicago") { | |
return "[CHI](http://www.chicago-fire.com)" | |
} else if strings.Contains(team, "Colorado") { | |
return "[COL](http://www.coloradorapids.com)" | |
} else if strings.Contains(team, "Columbus") { | |
return "[CLB](http://www.thecrew.com)" | |
} else if strings.Contains(team, "Dallas") { | |
return "[FCD](http://www.fcdallas.com)" | |
} else if strings.Contains(team, "D.C. United") { | |
return "[DC](http://www.dcunited.com)" | |
} else if strings.Contains(team, "Houston") { | |
return "[HOU](http://www.houstondynamo.com)" | |
} else if strings.Contains(team, "Montreal") { | |
return "[MTL](http://www.impactmontreal.com/en)" | |
} else if strings.Contains(team, "Galaxy") { | |
return "[LAG](http://www.lagalaxy.com)" | |
} else if strings.Contains(team, "Portland") { | |
return "[POR](http://www.portlandtimbers.com)" | |
} else if strings.Contains(team, "New England") { | |
return "[NE](http://www.revolutionsoccer.net)" | |
} else if strings.Contains(team, "Salt Lake") { | |
return "[RSL](http://www.realsaltlake.com)" | |
} else if strings.Contains(team, "New York City") { | |
return "[NYC](http://www.nycfc.com/)" | |
} else if strings.Contains(team, "San Jose") { | |
return "[SJ](http://www.sjearthquakes.com)" | |
} else if strings.Contains(team, "Red Bull") { | |
//return "Wings" | |
return "[NYRB](http://www.newyorkredbulls.com)" | |
} else if strings.Contains(team, "Orlando") { | |
return "[OCSC](http://www.orlandocitysc.com/)" | |
} else if strings.Contains(team, "Seattle") { | |
return "[SEA](http://www.soundersfc.com)" | |
} else if strings.Contains(team, "Philadelphia") { | |
return "[PHI](http://www.philadelphiaunion.com)" | |
} else if strings.Contains(team, "Sporting") { | |
return "[SKC](http://www.sportingkc.com)" | |
} else if strings.Contains(team, "Toronto") { | |
return "[TFC](http://torontofc.ca)" | |
} else if strings.Contains(team, "Vancouver") { | |
return "[VAN](http://www.whitecapsfc.com)" | |
} | |
return team | |
} | |
type WikiPage struct { | |
Data struct { | |
Content string `json:"content_md"` | |
} | |
} | |
func main() { | |
var err error | |
reddit := &Reddit{ | |
username: "none", | |
password: "--", | |
client: &http.Client{}, | |
} | |
reddit.Login() | |
sidebar, err := reddit.Get("https://www.reddit.com/r/mls/wiki/sidebar-template.json") | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Error getting template: %s\n", err) | |
return | |
} | |
var wikiTemplate WikiPage | |
err = json.Unmarshal(sidebar, &wikiTemplate) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Error unmarshalling template: %s\n", err) | |
return | |
} | |
templateData := make(map[string]interface{}) | |
templateData["Now"] = time.Now() | |
templateData["Standings"] = getStandings() | |
templateData["Schedule"], err = getSchedule() | |
if err != nil { | |
fmt.Printf("Error fetching schedule: %s\n", err) | |
return | |
} | |
t := template.New("sidebar") | |
t.Funcs(template.FuncMap{"link_team": linkTeam, "link_channel": linkChannel}) | |
_, err = t.Parse(wikiTemplate.Data.Content) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "got error parsing template: %s\n, template data:\n%s", err, wikiTemplate.Data.Content) | |
return | |
} | |
//newSidebar := new(bytes.Buffer) | |
err = t.Execute(os.Stdout, templateData) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "got error executing template: %s\n", err) | |
return | |
} | |
//err = reddit.EditWikiPage("MLSDev", "config/sidebar", newSidebar.String()) | |
//if err != nil { | |
// return | |
//} | |
} | |
type Reddit struct { | |
username, password string | |
cookie string | |
modhash string | |
client *http.Client | |
} | |
type RedditLoginResponse struct { | |
JSON struct { | |
Errors []interface{} | |
Data struct { | |
NeedHTTPS bool `json:"need_https"` | |
HTTSRedir string `json:"hsts_redir"` | |
Modhash string | |
Cookie string | |
} | |
} | |
} | |
func (r *Reddit) Login() (err error) { | |
loginURL := fmt.Sprintf("https://www.reddit.com/api/login/%s", r.username) | |
postValues := url.Values{ | |
"user": {r.username}, | |
"passwd": {r.password}, | |
"api_type": {"json"}, | |
} | |
resp, err := r.Post(loginURL, postValues) | |
if err != nil { | |
return | |
} | |
loginResponse := new(RedditLoginResponse) | |
err = json.Unmarshal(resp, loginResponse) | |
if err != nil { | |
return | |
} | |
r.cookie = loginResponse.JSON.Data.Cookie | |
r.modhash = loginResponse.JSON.Data.Modhash | |
return | |
return | |
} | |
type WikiEditRequest struct { | |
Content string `json:"content"` | |
Page string `json:"string"` | |
} | |
func (r *Reddit) EditWikiPage(subreddit, page, content string) (err error) { | |
body, err := json.Marshal(&WikiEditRequest{Content: content, Page: page}) | |
if err != nil { | |
return | |
} | |
req, err := http.NewRequest("POST", fmt.Sprintf("https://www.reddit.com/r/%s/api/wiki/edit.json", subreddit), bytes.NewBuffer(body)) | |
if err != nil { | |
return | |
} | |
req.Header.Set("Content-Type", "application/json") | |
req.Header.Set("User-Agent", "linux:rMLSBot:v0.9.0 (by /u/tedreed)") | |
if r.cookie != "" { | |
req.Header.Set("Cookie", "reddit_session="+r.cookie) | |
} | |
if r.modhash != "" { | |
req.Header.Set("X-Modhash", r.modhash) | |
} | |
resp, err := r.client.Do(req) | |
if err != nil { | |
return | |
} | |
defer resp.Body.Close() | |
body, err = ioutil.ReadAll(resp.Body) | |
fmt.Fprintf(os.Stderr, "%s", body) | |
return | |
} | |
func (r *Reddit) Post(url string, values url.Values) (body []byte, err error) { | |
req, err := http.NewRequest("POST", url, strings.NewReader(values.Encode())) | |
if err != nil { | |
return | |
} | |
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") | |
req.Header.Set("User-Agent", "linux:rMLSBot:v0.9.0 (by /u/tedreed)") | |
resp, err := r.client.Do(req) | |
if err != nil { | |
return | |
} | |
defer resp.Body.Close() | |
body, err = ioutil.ReadAll(resp.Body) | |
return | |
} | |
func (r *Reddit) Get(url string) (body []byte, err error) { | |
req, err := http.NewRequest("GET", url, nil) | |
if err != nil { | |
return | |
} | |
req.Header.Set("User-Agent", "linux:rMLSBot:v0.9.0 (by /u/tedreed)") | |
if r.cookie != "" { | |
req.Header.Set("Cookie", "reddit_session="+r.cookie) | |
} | |
if r.modhash != "" { | |
req.Header.Set("X-Modhash", r.modhash) | |
} | |
resp, err := r.client.Do(req) | |
if err != nil { | |
return | |
} | |
defer resp.Body.Close() | |
body, err = ioutil.ReadAll(resp.Body) | |
return | |
} | |
type Standing struct { | |
Conference, Team string | |
Position, Points, GamesPlayed, GoalsFor, GoalDifference, Wins int64 | |
} | |
func (s *Standing) String() string { | |
return fmt.Sprintf("%s%d %s %d %d %d %d", s.Conference, s.Position, s.Team, s.Points, s.GamesPlayed, s.GoalDifference, s.GoalsFor) | |
} | |
type Standings []*Standing | |
func (s Standings) Len() int { return len(s) } | |
func (s Standings) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | |
func (s Standings) Less(i, j int) bool { | |
if s[i].Points > s[j].Points { | |
return true | |
} | |
if s[i].Points < s[j].Points { | |
return false | |
} | |
if s[i].Wins > s[j].Wins { | |
return true | |
} | |
if s[i].Wins < s[j].Wins { | |
return false | |
} | |
if s[i].GoalDifference > s[j].GoalDifference { | |
return true | |
} | |
if s[i].GoalDifference < s[j].GoalDifference { | |
return false | |
} | |
if s[i].GoalsFor > s[j].GoalsFor { | |
return true | |
} | |
if s[i].GoalsFor < s[j].GoalsFor { | |
return false | |
} | |
return false | |
} | |
func getStandings() []*Standing { | |
scorePage, err := goquery.NewDocument("http://www.mlssoccer.com/standings") | |
if err != nil { | |
panic(err) | |
} | |
standings := make([]*Standing, 0, 20) | |
scorePage.Find(".eastern + .stats-table .standings-table tbody tr").Each(func(i int, s *goquery.Selection) { | |
standing := extractStanding(s.Children()) | |
standing.Conference = "E" | |
standings = append(standings, &standing) | |
}) | |
scorePage.Find(".western + .stats-table .standings-table tbody tr").Each(func(i int, s *goquery.Selection) { | |
standing := extractStanding(s.Children()) | |
standing.Conference = "W" | |
standings = append(standings, &standing) | |
}) | |
sort.Sort(Standings(standings)) | |
return standings | |
} | |
func extractStanding(record *goquery.Selection) (standing Standing) { | |
standing.Position, _ = strconv.ParseInt(strings.TrimSpace(record.Eq(0).Contents().Text()), 10, 64) | |
standing.Team = strings.TrimSpace(record.Eq(1).Contents().Text()) | |
standing.Points, _ = strconv.ParseInt(strings.TrimSpace(record.Eq(2).Contents().Text()), 10, 64) | |
standing.GamesPlayed, _ = strconv.ParseInt(strings.TrimSpace(record.Eq(3).Contents().Text()), 10, 64) | |
standing.GoalsFor, _ = strconv.ParseInt(strings.TrimSpace(record.Eq(8).Contents().Text()), 10, 64) | |
standing.GoalDifference, _ = strconv.ParseInt(strings.TrimSpace(record.Eq(10).Contents().Text()), 10, 64) | |
standing.Wins, _ = strconv.ParseInt(strings.TrimSpace(record.Eq(5).Contents().Text()), 10, 64) | |
return | |
} | |
type ScheduleEntry struct { | |
Time string | |
Home, Away string | |
Score, AggScore string | |
Channels string | |
} | |
func getSchedule() (schedule map[string][]ScheduleEntry, err error) { | |
var easternTime *time.Location | |
easternTime, err = time.LoadLocation("America/New_York") | |
if err != nil { | |
return | |
} | |
monday := getMostRecentMonday() | |
rawSchedule, err := goquery.NewDocument(fmt.Sprintf("http://matchcenter.mlssoccer.com/matches/%s", monday.Format("2006-01-02"))) | |
if err != nil { | |
return | |
} | |
schedule = make(map[string][]ScheduleEntry) | |
rawSchedule.Find(".sb-wrapper").Each(func(i int, s *goquery.Selection) { | |
comp := s.Find(".sb-match-comp").Contents().Text() | |
if comp != "MLS" { | |
return | |
} | |
var entry ScheduleEntry | |
entry.Home = s.Find(".sb-home .sb-club-name .sb-club-name-full").Contents().Text() | |
entry.Away = s.Find(".sb-away .sb-club-name .sb-club-name-full").Contents().Text() | |
d := s.Find(".sb-match-date").Contents().Text() | |
t := s.Find(".sb-match-time").Contents().Text() | |
dateTime, err := time.ParseInLocation("Mon, Jan 2 3:04 PM", d+" "+strings.Replace(t, " ET", "", 1), easternTime) | |
if err != nil { | |
fmt.Fprintf(os.Stderr, "Error parsing time: %s\n", err) | |
} | |
date := dateTime.Format("1/2") | |
entry.Time = dateTime.Format("3:04") | |
entry.Score = s.Find(".sb-score").Eq(0).Contents().Text() | |
entry.AggScore = s.Find(".sb-score").Eq(1).Contents().Text() | |
entry.Channels = s.Find(".sb-tv-listing span").Contents().Text() | |
if _, ok := schedule[date]; ok { | |
schedule[date] = append(schedule[date], entry) | |
} else { | |
schedule[date] = make([]ScheduleEntry, 0, 5) | |
schedule[date] = append(schedule[date], entry) | |
} | |
}) | |
return | |
} | |
func getMostRecentMonday() time.Time { | |
day := time.Now() | |
for day.Weekday() != time.Monday { | |
day = day.Add(-24 * time.Hour) | |
} | |
return day | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment