simpbot/main.go

300 lines
8.4 KiB
Go
Raw Normal View History

2021-05-21 12:45:24 -04:00
package main
import (
"fmt"
"log"
2021-10-26 14:14:26 -04:00
"net/url"
2021-08-07 13:02:50 -04:00
"os"
"os/signal"
2021-06-25 13:58:04 -04:00
"strings"
2021-08-07 13:02:50 -04:00
"syscall"
2021-05-21 12:45:24 -04:00
"time"
2021-08-07 13:02:50 -04:00
"github.com/fsnotify/fsnotify"
2021-05-21 13:39:54 -04:00
"github.com/spf13/viper"
2021-05-21 12:45:24 -04:00
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
2021-12-08 13:22:56 -05:00
"maunium.net/go/mautrix/id"
2021-05-21 12:45:24 -04:00
)
2021-05-21 13:39:54 -04:00
var Homeserver string
var Username string
var Password string
var DimensionServer string
var HomeserverDomain string
2021-12-08 13:22:56 -05:00
var Token string
2021-06-25 13:58:04 -04:00
var GitCommit string
2021-10-27 12:06:54 -04:00
var GitTag string
2021-06-25 14:05:57 -04:00
var Statefile string
2021-10-26 14:01:58 -04:00
var CurrStreamCnt int
var MostStreamCnt int
var StartTime time.Time
2023-04-24 18:01:40 -04:00
var HolodexToken string
2021-05-21 12:45:24 -04:00
func main() {
2021-05-21 13:39:54 -04:00
viper.SetConfigName("config")
viper.AddConfigPath(".")
2021-08-07 13:36:26 -04:00
viper.AddConfigPath("/etc/simpbot")
2021-05-21 13:39:54 -04:00
err := viper.ReadInConfig()
if err != nil {
log.Fatalf("Fatal error config file: %v \n", err)
2021-05-21 12:45:24 -04:00
}
2021-05-21 13:39:54 -04:00
viper.SetConfigType("yaml")
Homeserver = viper.GetString("homeserver")
viper.SetDefault("domain", Homeserver)
2021-06-25 14:05:57 -04:00
viper.SetDefault("statefile", "simpstate")
2021-05-21 13:39:54 -04:00
Username = viper.GetString("username")
Password = viper.GetString("password")
2021-12-08 13:22:56 -05:00
Token = viper.GetString("access_token")
2023-04-24 18:01:40 -04:00
HolodexToken = viper.GetString("api_token")
2021-05-21 13:39:54 -04:00
DimensionServer = viper.GetString("dimension")
HomeserverDomain = viper.GetString("domain")
2021-06-25 14:05:57 -04:00
Statefile = viper.GetString("statefile")
2021-10-26 14:01:58 -04:00
CurrStreamCnt = 0
MostStreamCnt = 0
StartTime = time.Now()
2021-06-25 13:58:04 -04:00
var vtubers []*Vtuber
2021-05-21 13:39:54 -04:00
log.Println("Logging into", Homeserver, "as", Username)
2021-12-08 13:22:56 -05:00
var client *mautrix.Client
2022-08-10 14:03:35 -04:00
uid := id.NewUserID(strings.ToLower(Username), strings.ToLower(HomeserverDomain))
2021-12-08 13:22:56 -05:00
if Token == "" {
client, err = mautrix.NewClient(Homeserver, "", "")
if err != nil {
panic(err)
}
} else {
log.Println("using token login")
2022-08-10 14:03:35 -04:00
client, err = mautrix.NewClient(Homeserver, uid, Token)
2021-12-08 13:22:56 -05:00
if err != nil {
panic(err)
}
2021-05-21 12:45:24 -04:00
}
dataFilter := &mautrix.Filter{
AccountData: mautrix.FilterPart{
Limit: 20,
NotTypes: []event.Type{
event.NewEventType("simp.batch"),
},
},
}
store := mautrix.NewAccountDataStore("simp.batch", client)
2022-08-10 14:03:35 -04:00
fID, err := client.CreateFilter(dataFilter)
store.SaveFilterID(uid, fID.FilterID)
if err != nil {
panic(err)
}
client.Store = store
2022-08-10 14:03:35 -04:00
2021-12-08 13:22:56 -05:00
if Token == "" {
login_res, 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)
}
Token = login_res.AccessToken
viper.Set("access_token", Token)
log.Println("Login succesful, saving access_token to config file")
err = viper.WriteConfig()
if err != nil {
panic(err)
}
} else {
log.Println("skipping login since token provided")
2021-05-21 12:45:24 -04:00
}
2023-04-24 18:01:40 -04:00
if HolodexToken == "" {
log.Println("No holodex API token provided, unlikely to be able to get streams")
}
2021-05-21 12:45:24 -04:00
syncer := client.Syncer.(*mautrix.DefaultSyncer)
syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) {
2021-08-23 19:55:54 -04:00
if evt.Sender == client.UserID {
return //ignore events from self
}
2021-05-21 12:45:24 -04:00
fmt.Printf("<%[1]s> %[4]s (%[2]s/%[3]s)\n", evt.Sender, evt.Type.String(), evt.ID, evt.Content.AsMessage().Body)
2021-06-25 13:58:04 -04:00
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 {
2021-08-23 19:49:20 -04:00
ann := ""
if vt.AnnounceLive {
ann = "*"
}
vlist = append(vlist, fmt.Sprintf("%v%v", vt.Name, ann))
2021-06-25 13:58:04 -04:00
}
infomsg = fmt.Sprintf("Currently Simping For: \n%v", strings.Join(vlist, "\n"))
client.SendText(evt.RoomID, infomsg)
2021-10-26 14:01:58 -04:00
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
}
2021-10-27 12:06:54 -04:00
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))
2021-10-26 14:01:58 -04:00
client.SendText(evt.RoomID, statmsg)
2021-06-25 13:58:04 -04:00
case "version":
// print version
2021-10-27 12:06:54 -04:00
if GitTag != "" {
client.SendText(evt.RoomID, "SimpBot version "+GitTag)
} else {
client.SendText(evt.RoomID, "SimpBot version "+GitCommit)
}
2021-06-25 13:58:04 -04:00
case "reload":
//reload config
2021-10-26 14:01:58 -04:00
client.SendText(evt.RoomID, "Reloading config")
fmt.Println("Reload requested,reloading vtubers")
vtubers = LoadVtubers()
2023-01-17 22:49:57 -05:00
case "subscribe":
if len(body_s) < 3 {
client.SendText(evt.RoomID, "Need a member to subscribe to")
}
vt := body_s[2]
2023-01-17 23:53:18 -05:00
var subbed bool
2023-01-17 22:49:57 -05:00
for _, v := range vtubers {
if strings.ToUpper(v.Name) == strings.ToUpper(vt) {
v.Subs[evt.Sender] = true
2023-01-17 23:53:18 -05:00
subbed = true
2023-01-17 22:49:57 -05:00
}
}
2023-01-17 23:53:18 -05:00
if subbed {
client.SendText(evt.RoomID, "subbed")
} else {
client.SendText(evt.RoomID, "could not identify talent to subscribe to")
}
2023-01-17 22:49:57 -05:00
2021-06-25 13:58:04 -04:00
case "help":
2023-01-17 22:49:57 -05:00
client.SendText(evt.RoomID, "Supported commands: info,version,stats,reload,subscribe")
2021-06-25 13:58:04 -04:00
default:
//command not found
client.SendText(evt.RoomID, "command not recognized")
}
2021-05-21 12:45:24 -04:00
})
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 {
2021-10-26 14:14:26 -04:00
fmt.Printf("joined room %v", evt.RoomID)
2021-05-21 12:45:24 -04:00
}
}
})
2021-08-07 13:02:50 -04:00
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()
})
2021-05-21 12:45:24 -04:00
go func() {
for {
time.Sleep(30 * time.Second)
roomResp, err := client.JoinedRooms()
if err != nil {
2021-09-16 16:46:15 -04:00
log.Println("error getting joined rooms")
2021-05-21 16:00:10 -04:00
log.Println(err)
log.Println("Skipping iteration")
continue
2021-05-21 12:45:24 -04:00
}
rooms := roomResp.JoinedRooms
// We're going to assume they're only stream one video at a time
for _, v := range vtubers {
2023-04-24 18:01:40 -04:00
err = v.Update(HolodexToken)
if err != nil {
log.Println(err)
}
2021-05-21 12:45:24 -04:00
if v.IsLive() {
2021-10-26 14:33:22 -04:00
2021-05-21 12:45:24 -04:00
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 {
2021-10-26 14:14:26 -04:00
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))
2023-01-17 22:49:57 -05:00
var subs string
for k := range v.Subs {
subs += k.String() + " "
}
2023-01-24 21:25:42 -05:00
if len(v.Subs) > 0 {
client.SendText(room, fmt.Sprintf("Pinging %v", subs))
}
2021-05-21 12:45:24 -04:00
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)
}
v.TotalStreams = v.TotalStreams + 1
CurrStreamCnt = CurrStreamCnt + 1
if CurrStreamCnt > MostStreamCnt {
MostStreamCnt = CurrStreamCnt
}
2021-10-26 14:14:26 -04:00
log.Printf("Embed stream %v for %v ", resp, v.Name)
2021-05-21 12:45:24 -04:00
}
}
} 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 {
2021-12-04 11:09:50 -05:00
log.Println("error removing video embed")
2021-05-21 12:45:24 -04:00
log.Println(err)
}
CurrStreamCnt = CurrStreamCnt - 1
2021-10-26 14:14:26 -04:00
log.Printf("Embed stream %v removed %v", resp, v.Name)
2021-05-21 12:45:24 -04:00
}
}
}
}
}
}()
err = client.Sync()
if err != nil {
panic(err)
}
}
2021-10-26 14:14:26 -04:00
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
}