package main import ( "context" "flag" "fmt" "log" "os" "regexp" "maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/event" ) var ( Version = "dev" BuildTime = "unknown" ) func main() { configPath := flag.String("config", "config.yaml", "Path to configuration file") dbPath := flag.String("database", "ntfy-bridge.db", "Path to SQLite database file") genReg := flag.Bool("g", false, "Generate registration file and exit") regPath := flag.String("r", "registration.yaml", "Path for the registration file") showVersion := flag.Bool("v", false, "Print version and exit") flag.Parse() if *showVersion { fmt.Printf("mautrix-ntfy %s (built %s)\n", Version, BuildTime) return } cfg, err := LoadConfig(*configPath) if err != nil { log.Fatalf("Load config: %v", err) } if *genReg { if err := generateRegistration(cfg, *configPath, *regPath); err != nil { log.Fatalf("Generate registration: %v", err) } return } if err := run(cfg, *configPath, *dbPath); err != nil { log.Fatalf("Fatal: %v", err) } } func generateRegistration(cfg *Config, configPath, regPath string) error { if cfg.Homeserver.Domain == "" || cfg.Homeserver.Domain == "example.com" { return fmt.Errorf("set homeserver.domain in %s first", configPath) } if cfg.Appservice.BotLocalpart == "" { return fmt.Errorf("set appservice.bot_localpart in %s first", configPath) } if cfg.Appservice.Address == "" { return fmt.Errorf("set appservice.address in %s first", configPath) } reg := appservice.CreateRegistration() reg.ID = cfg.Appservice.ID reg.URL = cfg.Appservice.Address reg.SenderLocalpart = cfg.Appservice.BotLocalpart rateLimited := false reg.RateLimited = &rateLimited botRegex := regexp.MustCompile(fmt.Sprintf("^@%s:%s$", regexp.QuoteMeta(cfg.Appservice.BotLocalpart), regexp.QuoteMeta(cfg.Homeserver.Domain))) reg.Namespaces.UserIDs.Register(botRegex, true) ghostRegex := regexp.MustCompile(fmt.Sprintf("^@%s_[^:]*:%s$", regexp.QuoteMeta(cfg.Appservice.BotLocalpart), regexp.QuoteMeta(cfg.Homeserver.Domain))) reg.Namespaces.UserIDs.Register(ghostRegex, true) if err := reg.Save(regPath); err != nil { return fmt.Errorf("save registration: %w", err) } cfg.Appservice.ASToken = reg.AppToken cfg.Appservice.HSToken = reg.ServerToken if err := cfg.Save(configPath); err != nil { return fmt.Errorf("save config with tokens: %w", err) } fmt.Printf("Registration saved to %s\n", regPath) fmt.Printf("Config updated at %s (tokens filled in)\n", configPath) fmt.Println("Copy the registration file to your homeserver and restart it.") return nil } func run(cfg *Config, configPath, dbPath string) error { db, err := NewDatabase(dbPath) if err != nil { return fmt.Errorf("open database: %w", err) } defer db.Close() reg := &appservice.Registration{ ID: cfg.Appservice.ID, AppToken: cfg.Appservice.ASToken, ServerToken: cfg.Appservice.HSToken, SenderLocalpart: cfg.Appservice.BotLocalpart, } as, err := appservice.CreateFull(appservice.CreateOpts{ Registration: reg, HomeserverDomain: cfg.Homeserver.Domain, HomeserverURL: cfg.Homeserver.URL, HostConfig: appservice.HostConfig{Hostname: cfg.Appservice.Address, Port: 8080}, }) if err != nil { return fmt.Errorf("create appservice: %w", err) } ntfy := NewNtfyClient(cfg.Bridge.NtfyURL) bridge := NewBridge(as, db, ntfy, cfg) handler := NewMatrixHandler(bridge, fmt.Sprintf("@%s_", cfg.Appservice.BotLocalpart)) ep := appservice.NewEventProcessor(as) ep.On(event.EventMessage, handler.HandleEvent) ep.On(event.StateMember, handler.HandleEvent) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ep.Start(ctx) bridge.StartExistingBridges(ctx) log.Printf("mautrix-ntfy %s starting on %s:8080", Version, cfg.Appservice.Address) as.Start() _ = os.Stdout return nil }