275 lines
6.4 KiB
Go
275 lines
6.4 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"github.com/arran4/golang-ical"
|
|
"io"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
API_URL = "https://api.events.ccc.de/congress/2024"
|
|
)
|
|
|
|
var ROOMS []Room;
|
|
|
|
type Token struct {
|
|
Token string `json:"token"`
|
|
}
|
|
|
|
type Auth struct {
|
|
Username string `json:"username"`
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
type Event struct {
|
|
ID string `json:"id"`
|
|
Kind string `json:"kind"`
|
|
Name string `json:"name"`
|
|
Slug string `json:"slug"`
|
|
URL string `json:"url"`
|
|
Track string `json:"track"`
|
|
Assembly string `json:"assembly"`
|
|
Room string `json:"room"`
|
|
Location string `json:"location"`
|
|
Language string `json:"language"`
|
|
Description string `json:"description"`
|
|
ScheduleStart time.Time `json:"schedule_start"`
|
|
ScheduleDuration string `json:"schedule_duration"`
|
|
ScheduleEnd time.Time `json:"schedule_end"`
|
|
}
|
|
|
|
// Link represents the structure of the "links" array in the JSON
|
|
type Link struct {
|
|
Type string `json:"type"`
|
|
Name string `json:"name"`
|
|
URI string `json:"uri"`
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
// Room represents the structure of the main JSON object
|
|
type Room struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Slug string `json:"slug"`
|
|
RoomType string `json:"room_type"`
|
|
Capacity int `json:"capacity"`
|
|
Links []Link `json:"links"`
|
|
Assembly string `json:"assembly"`
|
|
PublicURL string `json:"public_url"`
|
|
}
|
|
|
|
func scrape_hub() ([]Event, error) {
|
|
println("scrape hub")
|
|
resp, err := http.Get(fmt.Sprintf("%s/events", API_URL))
|
|
if err != nil {
|
|
fmt.Println("Error making GET request:", err)
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
fmt.Println("Error reading response body:", err)
|
|
return nil, err
|
|
}
|
|
|
|
var events []Event
|
|
|
|
if err := json.Unmarshal(body, &events); err != nil {
|
|
fmt.Println("Error unmarshalling JSON:", err)
|
|
return nil, err
|
|
}
|
|
|
|
// Use the events slice here
|
|
fmt.Println("Number of events:", len(events))
|
|
return events, nil
|
|
}
|
|
|
|
func get_rooms() ([]Room, error) {
|
|
println("get rooms")
|
|
resp, err := http.Get(fmt.Sprintf("%s/rooms", API_URL))
|
|
if err != nil {
|
|
fmt.Println("Error making GET request:", err)
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
fmt.Println("Error reading response body:", err)
|
|
return nil, err
|
|
}
|
|
|
|
var rooms []Room
|
|
|
|
if err := json.Unmarshal(body, &rooms); err != nil {
|
|
fmt.Println("Error unmarshalling JSON:", err)
|
|
return nil, err
|
|
}
|
|
|
|
// Use the events slice here
|
|
fmt.Println("Number of rooms:", len(rooms))
|
|
return rooms, nil
|
|
}
|
|
|
|
func get_token(username string, password string) (string, error) {
|
|
println("get token")
|
|
|
|
jsonBytes, err := json.Marshal(Auth{Username: username, Password: password})
|
|
if err != nil {
|
|
fmt.Println("Error unmarshalling JSON:", err)
|
|
return "", err
|
|
}
|
|
|
|
resp, err := http.Post(
|
|
fmt.Sprintf("%s/auth/get-token", API_URL),
|
|
"application/json",
|
|
bytes.NewBuffer(jsonBytes),
|
|
)
|
|
if err != nil {
|
|
fmt.Println("Error getting Token:", err)
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
fmt.Println("Error reading response body:", err)
|
|
return "", err
|
|
}
|
|
var token Token
|
|
if err := json.Unmarshal(body, &token); err != nil {
|
|
fmt.Println("Error unmarshalling JSON:", err)
|
|
return "", err
|
|
}
|
|
println(token.Token)
|
|
return token.Token, nil
|
|
}
|
|
|
|
func get_me_events(token string) ([]string, error) {
|
|
println("get personal events")
|
|
|
|
req, err := http.NewRequest(
|
|
"GET",
|
|
fmt.Sprintf("%s/me/events", API_URL),
|
|
nil,
|
|
)
|
|
req.Header.Set("Authorization", fmt.Sprintf("Token %s", token))
|
|
|
|
resp, err := (&http.Client{}).Do(req)
|
|
if err != nil {
|
|
fmt.Println("Error fetching personal events:", err)
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
fmt.Println("Error reading response body:", err)
|
|
return nil, err
|
|
}
|
|
println(string(body))
|
|
var uuids []string
|
|
if err := json.Unmarshal(body, &uuids); err != nil {
|
|
fmt.Println("Error unmarshalling JSON:", err)
|
|
return nil, err
|
|
}
|
|
return uuids, nil
|
|
}
|
|
|
|
func get_schedule(w http.ResponseWriter, r *http.Request) {
|
|
println("/event.ics")
|
|
auth_header := r.Header.Get("Authorization")
|
|
println(auth_header)
|
|
if auth_header == "" {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
credentials_encoded := auth_header[len("Basic "):]
|
|
credentials, err := base64.StdEncoding.DecodeString(credentials_encoded)
|
|
if err != nil {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
auth := strings.SplitN(string(credentials), ":", 2)
|
|
if len(auth) != 2 {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
username := auth[0]
|
|
password := auth[1]
|
|
|
|
println(username, password)
|
|
|
|
token, err := get_token(username, password)
|
|
if err != nil {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
ids, err := get_me_events(token)
|
|
if err != nil {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
schedule, err := scrape_hub()
|
|
if err != nil {
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
var my_events []Event
|
|
for _, event := range schedule {
|
|
for _, id := range ids {
|
|
if event.ID == id {
|
|
my_events = append(my_events, event)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
cal := ics.NewCalendar()
|
|
cal.SetMethod(ics.MethodRequest)
|
|
for _, event := range my_events {
|
|
var location = ""
|
|
for _, room := range ROOMS {
|
|
if event.Room == room.ID {
|
|
location = room.Name
|
|
}
|
|
}
|
|
|
|
cal_event := cal.AddEvent(event.ID)
|
|
cal_event.SetStartAt(event.ScheduleStart)
|
|
cal_event.SetEndAt(event.ScheduleEnd)
|
|
cal_event.SetSummary(event.Name)
|
|
cal_event.SetLocation(location)
|
|
cal_event.SetDescription(ics.ToText(strings.ReplaceAll(event.Description, "\r\n", "\n")))
|
|
cal_event.SetURL(event.URL)
|
|
cal_event.SetOrganizer(event.Assembly)
|
|
}
|
|
|
|
ics := cal.Serialize()
|
|
|
|
w.Header().Set("Content-Type", "text/calendar; charset=utf-8")
|
|
w.Write([]byte(ics))
|
|
}
|
|
|
|
func main() {
|
|
rooms, err := get_rooms()
|
|
if err != nil {
|
|
return
|
|
}
|
|
ROOMS = rooms
|
|
|
|
println("Stating Server")
|
|
// Start a web server to serve the iCal file
|
|
http.HandleFunc("/events.ics", get_schedule)
|
|
err = http.ListenAndServe(":8080", nil)
|
|
if err != nil {
|
|
println(err)
|
|
}
|
|
}
|