392 lines
8.0 KiB
Go
392 lines
8.0 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"hummingbard/config"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"hummingbard/gomatrix"
|
|
|
|
"github.com/go-chi/chi"
|
|
"github.com/go-redis/redis"
|
|
"github.com/gorilla/sessions"
|
|
"github.com/robfig/cron/v3"
|
|
|
|
cache "hummingbard/cache"
|
|
)
|
|
|
|
func init() {
|
|
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
|
}
|
|
|
|
type Client struct {
|
|
Config *config.Config
|
|
Router *chi.Mux
|
|
HTTP *http.Server
|
|
Templates *Template
|
|
Sessions *sessions.CookieStore
|
|
Store *redis.Client
|
|
Matrix *gomatrix.Client
|
|
DefaultUser User
|
|
AnonymousUser User
|
|
Cache *cache.Cache
|
|
DB *DB
|
|
Cron *cron.Cron
|
|
}
|
|
|
|
func (c *Client) Activate() {
|
|
|
|
log.Println("started server")
|
|
|
|
idleConnsClosed := make(chan struct{})
|
|
|
|
go func() {
|
|
sigint := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(sigint, os.Interrupt)
|
|
signal.Notify(sigint, syscall.SIGTERM)
|
|
|
|
<-sigint
|
|
|
|
if err := c.HTTP.Shutdown(context.Background()); err != nil {
|
|
log.Printf("HTTP server Shutdown: %v", err)
|
|
log.Printf("Shutdown by user")
|
|
}
|
|
close(idleConnsClosed)
|
|
}()
|
|
|
|
if err := c.HTTP.ListenAndServe(); err != http.ErrServerClosed {
|
|
log.Printf("HTTP server ListenAndServe: %v", err)
|
|
}
|
|
|
|
<-idleConnsClosed
|
|
}
|
|
|
|
func Start() {
|
|
db, err := NewDB()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
conf, err := config.Read()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
tmpl, err := NewTemplate()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
router := chi.NewRouter()
|
|
|
|
redis := redis.NewClient(&redis.Options{
|
|
Addr: conf.Redis.Address,
|
|
Password: conf.Redis.Password,
|
|
DB: conf.Redis.DB,
|
|
})
|
|
|
|
server := fmt.Sprintf(`http://%s:%d`, conf.Matrix.Server, conf.Matrix.Port)
|
|
matrix, err := gomatrix.NewClient(server, "", "")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
//log into the default matrix account
|
|
defUser := User{}
|
|
|
|
username := conf.Client.Domain
|
|
password := conf.Matrix.Password
|
|
|
|
resp, err := matrix.Login(&gomatrix.ReqLogin{
|
|
Type: "m.login.password",
|
|
User: username,
|
|
Password: password,
|
|
})
|
|
|
|
if resp != nil {
|
|
defUser.UserID = resp.UserID
|
|
defUser.AccessToken = resp.AccessToken
|
|
matrix.SetCredentials(resp.UserID, resp.AccessToken)
|
|
}
|
|
|
|
//default account doesn't exist yet, let's create it
|
|
if err != nil {
|
|
log.Println(err)
|
|
|
|
av, err := matrix.RegisterAvailable(&gomatrix.ReqRegisterAvailable{
|
|
Username: username,
|
|
})
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
log.Println(av)
|
|
|
|
if av == nil || !av.Available {
|
|
panic(err)
|
|
}
|
|
|
|
type Auth struct {
|
|
Type string
|
|
Session string
|
|
}
|
|
resp, _, err := matrix.Register(&gomatrix.ReqRegister{
|
|
Username: username,
|
|
Password: password,
|
|
Auth: Auth{
|
|
Type: "m.login.dummy",
|
|
},
|
|
})
|
|
log.Println(resp)
|
|
|
|
if err != nil || resp == nil {
|
|
panic(err)
|
|
}
|
|
defUser.UserID = resp.UserID
|
|
defUser.AccessToken = resp.AccessToken
|
|
matrix.SetCredentials(resp.UserID, resp.AccessToken)
|
|
|
|
}
|
|
|
|
anonUser := User{}
|
|
|
|
//create @anonymous account if it doesn't exist yet
|
|
av, err := matrix.RegisterAvailable(&gomatrix.ReqRegisterAvailable{
|
|
Username: "anonymous",
|
|
})
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
log.Println(av)
|
|
|
|
if av == nil || !av.Available {
|
|
|
|
ma, err := gomatrix.NewClient(server, "", "")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
resp, err := ma.Login(&gomatrix.ReqLogin{
|
|
Type: "m.login.password",
|
|
User: "anonymous",
|
|
Password: conf.Matrix.AnonymousPassword,
|
|
})
|
|
|
|
if resp != nil {
|
|
anonUser.UserID = resp.UserID
|
|
anonUser.AccessToken = resp.AccessToken
|
|
}
|
|
|
|
} else {
|
|
|
|
type Auth struct {
|
|
Type string
|
|
Session string
|
|
}
|
|
rep, _, err := matrix.Register(&gomatrix.ReqRegister{
|
|
Username: "anonymous",
|
|
Password: conf.Matrix.AnonymousPassword,
|
|
Auth: Auth{
|
|
Type: "m.login.dummy",
|
|
},
|
|
})
|
|
log.Println(rep)
|
|
|
|
if err != nil || rep == nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
anonUser.UserID = rep.UserID
|
|
anonUser.AccessToken = rep.AccessToken
|
|
}
|
|
|
|
//create @anonymous user's profile
|
|
acr, err := matrix.CreateRoom(&gomatrix.ReqCreateRoom{
|
|
Visibility: "public",
|
|
Preset: "public_chat",
|
|
RoomAliasName: fmt.Sprintf(`@%s`, "anonymous"),
|
|
Name: fmt.Sprintf(`@%s's Timeline`, "anonymous"),
|
|
Topic: fmt.Sprintf(`This is @%s's hummingbard profile page. Follow them to post on their timeline.`, "anonymous"),
|
|
CreationContent: map[string]interface{}{
|
|
"m.federate": true,
|
|
},
|
|
InitialState: []gomatrix.Event{gomatrix.Event{
|
|
Type: "m.room.history_visibility",
|
|
Content: map[string]interface{}{
|
|
"history_visibility": "world_readable",
|
|
},
|
|
}, gomatrix.Event{
|
|
Type: "m.room.guest_access",
|
|
Content: map[string]interface{}{
|
|
"guest_access": "can_join",
|
|
},
|
|
}, gomatrix.Event{
|
|
Type: "com.hummingbard.room",
|
|
Content: map[string]interface{}{
|
|
"type": "profile",
|
|
},
|
|
}, gomatrix.Event{
|
|
Type: "m.room.power_levels",
|
|
Content: map[string]interface{}{
|
|
"ban": 50,
|
|
"events": map[string]interface{}{
|
|
"m.room.name": 100,
|
|
"m.room.power_levels": 100,
|
|
},
|
|
"events_default": 0,
|
|
"invite": 50,
|
|
"kick": 50,
|
|
"notifications": map[string]interface{}{
|
|
"room": 20,
|
|
},
|
|
"redact": 50,
|
|
"state_default": 50,
|
|
"users": map[string]interface{}{
|
|
anonUser.UserID: 100,
|
|
defUser.UserID: 100,
|
|
},
|
|
"users_default": 0,
|
|
},
|
|
}},
|
|
})
|
|
if err != nil || acr == nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
pub := fmt.Sprintf(`#@anonymous:%s`, conf.Client.Domain)
|
|
_, err = matrix.JoinRoom(pub, "", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
//does default #public room exist?
|
|
//create #public room
|
|
un := fmt.Sprintf(`#public:%s`, conf.Matrix.Server)
|
|
res, err := matrix.ResolveAlias(un)
|
|
if err != nil || res == nil {
|
|
log.Println(err)
|
|
//no, let's create it
|
|
crr, err := matrix.CreateRoom(&gomatrix.ReqCreateRoom{
|
|
Preset: "public_chat",
|
|
Visibility: "public",
|
|
RoomAliasName: "public",
|
|
Name: "Public",
|
|
CreationContent: map[string]interface{}{
|
|
"m.federate": true,
|
|
},
|
|
InitialState: []gomatrix.Event{gomatrix.Event{
|
|
Type: "m.room.history_visibility",
|
|
Content: map[string]interface{}{
|
|
"history_visibility": "world_readable",
|
|
},
|
|
}, gomatrix.Event{
|
|
Type: "m.room.guest_access",
|
|
Content: map[string]interface{}{
|
|
"guest_access": "can_join",
|
|
},
|
|
}},
|
|
})
|
|
if err != nil || crr == nil {
|
|
log.Println(err)
|
|
}
|
|
pub := fmt.Sprintf(`#public:%s`, conf.Client.Domain)
|
|
jr, err := matrix.JoinRoom(pub, "", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
log.Println("join?", jr)
|
|
|
|
nm, err := gomatrix.NewClient(server, anonUser.UserID, anonUser.AccessToken)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
_, err = nm.JoinRoom(pub, "", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
apub := fmt.Sprintf(`#@anonymous:%s`, conf.Client.Domain)
|
|
_, err = nm.JoinRoom(apub, "", nil)
|
|
if err != nil {
|
|
log.Println(err)
|
|
}
|
|
|
|
}
|
|
|
|
sess := NewSession(conf.Client.SecureCookie)
|
|
sess.Options.Domain = fmt.Sprintf(`.%s`, conf.Client.Domain)
|
|
|
|
cache, err := cache.NewCache()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
cron := cron.New()
|
|
|
|
c := &Client{
|
|
DB: db,
|
|
Config: conf,
|
|
Matrix: matrix,
|
|
HTTP: &http.Server{
|
|
ReadTimeout: 21 * time.Second,
|
|
WriteTimeout: 60 * time.Second,
|
|
IdleTimeout: 120 * time.Second,
|
|
Addr: conf.Client.Port,
|
|
Handler: router,
|
|
},
|
|
Router: router,
|
|
Templates: tmpl,
|
|
Sessions: sess,
|
|
Store: redis,
|
|
Cache: cache,
|
|
DefaultUser: defUser,
|
|
AnonymousUser: anonUser,
|
|
Cron: cron,
|
|
}
|
|
|
|
c.Middleware()
|
|
c.Routes()
|
|
|
|
c.Build()
|
|
|
|
//let's cache bigroom items from json dump
|
|
bigrooms, err := os.Open("bigrooms.json")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer bigrooms.Close()
|
|
|
|
b, err := ioutil.ReadAll(bigrooms)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var rpub *gomatrix.RespPublicRooms
|
|
|
|
json.Unmarshal(b, &rpub)
|
|
|
|
for i, _ := range rpub.Chunk {
|
|
if rpub.Chunk[i].CanonicalAlias != "" {
|
|
c.Cache.LargeRooms.Set(rpub.Chunk[i].CanonicalAlias[1:], true, 1)
|
|
}
|
|
}
|
|
|
|
go c.RefreshRoomsCache()
|
|
|
|
go c.Cron.AddFunc("*/15 * * * *", c.RefreshCache)
|
|
go c.Cron.Start()
|
|
|
|
c.Activate()
|
|
}
|