Initial Commit
This commit is contained in:
178
main.go
Normal file
178
main.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"local/sneedchatbridge/config"
|
||||
"local/sneedchatbridge/cookie"
|
||||
"local/sneedchatbridge/matrix"
|
||||
"local/sneedchatbridge/sneed"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// ------------------------------------------------------------
|
||||
// COMMAND-LINE FLAG HANDLING
|
||||
// ------------------------------------------------------------
|
||||
genReg := flag.Bool("generate-registration", false, "Generate registration.yaml and exit")
|
||||
outPath := flag.String("out", "registration.yaml", "Output path for generated registration.yaml")
|
||||
flag.Parse()
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// LOAD CONFIG
|
||||
// ------------------------------------------------------------
|
||||
cfg, err := config.Load(".env")
|
||||
if err != nil {
|
||||
log.Fatalf("❌ Failed to load configuration: %v", err)
|
||||
}
|
||||
|
||||
// If --generate-registration was passed, output registration.yaml and exit.
|
||||
if *genReg {
|
||||
err := generateRegistrationFile(cfg, *outPath)
|
||||
if err != nil {
|
||||
log.Fatalf("❌ Failed to generate registration file: %v", err)
|
||||
}
|
||||
log.Printf("🟢 Registration file written to %s", *outPath)
|
||||
return
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// NORMAL BRIDGE STARTUP (APPSERVICE MODE ONLY)
|
||||
// ------------------------------------------------------------
|
||||
fmt.Println("===============================================")
|
||||
fmt.Println(" Matrix Appservice ↔ Sneedchat Bridge")
|
||||
fmt.Println("===============================================")
|
||||
|
||||
log.Printf("Using Sneedchat room ID: %d", cfg.SneedchatRoomID)
|
||||
log.Printf("Matrix Appservice listen address: %s", cfg.AppserviceListenAddr)
|
||||
log.Printf("Ghost MXID domain: %s (prefix=%q)", cfg.MatrixGhostUserDomain, cfg.MatrixGhostUserPrefix)
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// COOKIE REFRESH SERVICE
|
||||
// ------------------------------------------------------------
|
||||
ck, err := cookie.NewCookieRefreshServiceWithDebug(
|
||||
cfg.BridgeUsername,
|
||||
cfg.BridgePassword,
|
||||
kiwiDomain(),
|
||||
cfg.Debug,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("❌ Cannot create cookie refresh service: %v", err)
|
||||
}
|
||||
|
||||
ck.Start()
|
||||
ck.WaitForCookie()
|
||||
log.Println("🟢 Initial XenForo session cookie acquired.")
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// SNEEDCHAT CLIENT INIT
|
||||
// ------------------------------------------------------------
|
||||
sneedClient := sneed.NewClient(cfg.SneedchatRoomID, ck)
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// MATRIX BRIDGE INIT (APPSERVICE)
|
||||
// ------------------------------------------------------------
|
||||
bridge := matrix.NewBridge(cfg, sneedClient)
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// START COMPONENTS
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Start Sneedchat WebSocket client
|
||||
if err := sneedClient.Connect(); err != nil {
|
||||
log.Fatalf("❌ Sneedchat initial connect failed: %v", err)
|
||||
}
|
||||
|
||||
// Start Matrix Appservice HTTP server
|
||||
go func() {
|
||||
if err := bridge.StartAppserviceServer(cfg); err != nil {
|
||||
log.Fatalf("❌ Appservice HTTP server error: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// WAIT FOR SHUTDOWN SIGNAL
|
||||
// ------------------------------------------------------------
|
||||
sigCh := make(chan os.Signal, 1)
|
||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
s := <-sigCh
|
||||
log.Printf("🔻 Shutdown signal received: %v", s)
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// SHUT DOWN GRACEFULLY
|
||||
// ------------------------------------------------------------
|
||||
// If Bridge implements Stop() we call it. (Optional)
|
||||
if stopper, ok := interface{}(bridge).(interface{ Stop() }); ok {
|
||||
stopper.Stop()
|
||||
}
|
||||
|
||||
sneedClient.Disconnect()
|
||||
ck.Stop()
|
||||
|
||||
log.Println("🟡 Bridge stopped.")
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// REGISTRATION GENERATOR
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// generateRegistrationFile writes the appservice registration.yaml
|
||||
// based on values loaded from .env.
|
||||
func generateRegistrationFile(cfg *config.Config, outPath string) error {
|
||||
template := `id: sneedchat
|
||||
url: "http://127.0.0.1:29333"
|
||||
as_token: "%s"
|
||||
hs_token: "%s"
|
||||
sender_localpart: "sneedbridge"
|
||||
rate_limited: false
|
||||
|
||||
namespaces:
|
||||
users:
|
||||
- regex: "^@sneedbridge:%s$"
|
||||
exclusive: true
|
||||
- regex: "^@.*:%s$"
|
||||
exclusive: true
|
||||
aliases: []
|
||||
rooms: []
|
||||
|
||||
push_ephemeral: true
|
||||
de.sorunome.msc2409.push_ephemeral: true
|
||||
`
|
||||
|
||||
var buf bytes.Buffer
|
||||
|
||||
hsToken := randomHex(64) // Synapse expects strong token
|
||||
|
||||
buf.WriteString(fmt.Sprintf(
|
||||
template,
|
||||
cfg.MatrixAppserviceToken,
|
||||
hsToken,
|
||||
cfg.MatrixGhostUserDomain,
|
||||
cfg.MatrixGhostUserDomain,
|
||||
))
|
||||
|
||||
return os.WriteFile(outPath, buf.Bytes(), 0600)
|
||||
}
|
||||
|
||||
// randomHex generates a cryptographically random hex token.
|
||||
func randomHex(n int) string {
|
||||
b := make([]byte, n/2)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
// kiwiDomain returns the XenForo/Sneedchat domain name.
|
||||
func kiwiDomain() string {
|
||||
return "kiwifarms.st"
|
||||
}
|
||||
Reference in New Issue
Block a user