simpbot/main.go

223 lines
6.4 KiB
Go

package main
import (
"fmt"
"log"
"net/url"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
)
var Homeserver string
var Username string
var Password string
var DimensionServer string
var HomeserverDomain string
var GitCommit string
var GitTag string
var Statefile string
var CurrStreamCnt int
var MostStreamCnt int
var StartTime time.Time
func main() {
viper.SetConfigName("config")
viper.AddConfigPath(".")
viper.AddConfigPath("/etc/simpbot")
err := viper.ReadInConfig()
if err != nil {
log.Fatalf("Fatal error config file: %v \n", err)
}
viper.SetConfigType("yaml")
Homeserver = viper.GetString("homeserver")
viper.SetDefault("domain", Homeserver)
viper.SetDefault("statefile", "simpstate")
Username = viper.GetString("username")
Password = viper.GetString("password")
DimensionServer = viper.GetString("dimension")
HomeserverDomain = viper.GetString("domain")
Statefile = viper.GetString("statefile")
CurrStreamCnt = 0
MostStreamCnt = 0
StartTime = time.Now()
var vtubers []*Vtuber
log.Println("Logging into", Homeserver, "as", Username)
client, err := mautrix.NewClient(Homeserver, "", "")
if err != nil {
panic(err)
}
client.Store = NewLazyMemStore(Statefile)
_, err = client.Login(&mautrix.ReqLogin{
Type: "m.login.password",
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: Username},
Password: Password,
StoreCredentials: true,
})
if err != nil {
panic(err)
}
fmt.Println("Login successful")
syncer := client.Syncer.(*mautrix.DefaultSyncer)
syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) {
if evt.Sender == client.UserID {
return //ignore events from self
}
fmt.Printf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body)
body := evt.Content.AsMessage().Body
body_s := strings.Split(body, " ")
if body_s[0] != "!simp" {
return
}
if len(body_s) < 2 {
return //nothing to parse
}
switch body_s[1] {
case "info":
// print info page
var infomsg string
vlist := []string{}
for _, vt := range vtubers {
ann := ""
if vt.AnnounceLive {
ann = "*"
}
vlist = append(vlist, fmt.Sprintf("%v%v", vt.Name, ann))
}
infomsg = fmt.Sprintf("Currently Simping For: \n%v", strings.Join(vlist, "\n"))
client.SendText(evt.RoomID, infomsg)
case "stats":
var statmsg string
vlist := []string{}
t := 0
for _, vt := range vtubers {
vlist = append(vlist, fmt.Sprintf("%v Total:%v", vt.Name, vt.TotalStreams))
t = t + vt.TotalStreams
}
statmsg = fmt.Sprintf("Current Stats Since %v:\n%v\n\nTotal Streams: %v\nMost Concurrent: %v/%v\n", StartTime, strings.Join(vlist, "\n"), t, MostStreamCnt, len(vtubers))
client.SendText(evt.RoomID, statmsg)
case "version":
// print version
if GitTag != "" {
client.SendText(evt.RoomID, "SimpBot version "+GitTag)
} else {
client.SendText(evt.RoomID, "SimpBot version "+GitCommit)
}
case "reload":
//reload config
client.SendText(evt.RoomID, "Reloading config")
fmt.Println("Reload requested,reloading vtubers")
vtubers = LoadVtubers()
case "help":
client.SendText(evt.RoomID, "Supported commands: info,version,stats")
default:
//command not found
client.SendText(evt.RoomID, "command not recognized")
}
})
syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) {
fmt.Printf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body)
if evt.Content.AsMember().Membership.IsInviteOrJoin() {
_, err = client.JoinRoomByID(evt.RoomID)
if err != nil {
fmt.Printf("error joining room %v", evt.RoomID)
} else {
fmt.Printf("joined room %v", evt.RoomID)
}
}
})
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGUSR1)
vtubers = LoadVtubers()
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed,reloading vtubers:", e.Name)
vtubers = LoadVtubers()
})
go func() {
for {
time.Sleep(30 * time.Second)
roomResp, err := client.JoinedRooms()
if err != nil {
log.Println("error getting joined rooms")
log.Println(err)
log.Println("Skipping iteration")
continue
}
rooms := roomResp.JoinedRooms
// We're going to assume they're only stream one video at a time
for _, v := range vtubers {
v.Update()
if v.IsLive() {
for _, room := range rooms {
//check to see if already embeded
var content YoutubeWidget
err = client.StateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, &content)
if content.ID == "" {
if v.AnnounceLive {
client.SendText(room, v.LiveMsg)
} else {
if isValidUrl(v.LiveMsg) {
client.SendNotice(room, fmt.Sprintf("%v has gone live", v.Name))
} else {
client.SendNotice(room, v.LiveMsg)
}
}
client.SendNotice(room, fmt.Sprintf("%v's Title: %v", v.Name, v.CurrentStreamTitle))
resp, err := client.SendStateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, NewYT(v.Name+"'s stream", v.CurrentStream, string(room)))
if err != nil {
log.Println("error embeding video")
log.Println(err)
}
log.Printf("Embed stream %v for %v ", resp, v.Name)
}
}
} else {
//Not live, check to see if there's any embeds and remove them
for _, room := range rooms {
var content YoutubeWidget
err = client.StateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, &content)
if err == nil && content.ID != "" {
//event found, kill it
resp, err := client.SendStateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, struct{}{})
if err != nil {
log.Println("error removed video embed")
log.Println(err)
}
log.Printf("Embed stream %v removed %v", resp, v.Name)
}
}
}
}
}
}()
err = client.Sync()
if err != nil {
panic(err)
}
}
func isValidUrl(toTest string) bool {
_, err := url.ParseRequestURI(toTest)
if err != nil {
return false
}
u, err := url.Parse(toTest)
if err != nil || u.Scheme == "" || u.Host == "" {
return false
}
return true
}