298 lines
8.1 KiB
Go
298 lines
8.1 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/charmbracelet/log"
|
|
"github.com/spf13/viper"
|
|
"maunium.net/go/mautrix"
|
|
"maunium.net/go/mautrix/event"
|
|
"maunium.net/go/mautrix/id"
|
|
)
|
|
|
|
type simp struct {
|
|
client *mautrix.Client
|
|
dimensionServer string
|
|
holodexToken string
|
|
startTime time.Time
|
|
currStream int
|
|
maxStream int
|
|
vtubers []*Vtuber
|
|
stop chan bool
|
|
rooms []id.RoomID
|
|
}
|
|
|
|
func newSimp() *simp {
|
|
return &simp{
|
|
startTime: time.Now(),
|
|
stop: make(chan bool),
|
|
}
|
|
}
|
|
|
|
func (s *simp) Run() {
|
|
ticker := time.NewTicker(time.Second * 45)
|
|
roomResp, err := s.client.JoinedRooms()
|
|
if err != nil {
|
|
log.Errorf("error getting joined rooms: %v", err)
|
|
return
|
|
}
|
|
s.rooms = roomResp.JoinedRooms
|
|
for {
|
|
select {
|
|
case <-s.stop:
|
|
return
|
|
case <-ticker.C:
|
|
// We're going to assume they're only stream one video at a time
|
|
for _, v := range s.vtubers {
|
|
err = v.Update(s.holodexToken)
|
|
if err != nil {
|
|
log.Error("error pinging holodex", "error", err)
|
|
}
|
|
for _, room := range s.rooms {
|
|
if v.IsLive() {
|
|
// check to see if already embeded
|
|
var content YoutubeWidget
|
|
err = s.client.StateEvent(
|
|
room,
|
|
event.NewEventType("im.vector.modular.widgets"),
|
|
"dimension-m.video-simp-"+v.Name,
|
|
&content,
|
|
)
|
|
if err != nil {
|
|
log.Errorf("error getting state event in room %v: %v", room, err)
|
|
}
|
|
if content.ID == "" {
|
|
if v.AnnounceLive {
|
|
s.client.SendText(room, v.LiveMsg)
|
|
} else {
|
|
if isValidURL(v.LiveMsg) {
|
|
s.client.SendNotice(room, fmt.Sprintf("%v has gone live", v.Name))
|
|
} else {
|
|
s.client.SendNotice(room, v.LiveMsg)
|
|
}
|
|
}
|
|
s.client.SendNotice(room, fmt.Sprintf("%v's Title: %v", v.Name, v.CurrentStreamTitle))
|
|
var subs string
|
|
for k := range v.Subs {
|
|
subs += k.String() + " "
|
|
}
|
|
if len(v.Subs) > 0 {
|
|
s.client.SendText(room, fmt.Sprintf("Pinging %v", subs))
|
|
}
|
|
resp, err := s.client.SendStateEvent(
|
|
room,
|
|
event.NewEventType("im.vector.modular.widgets"),
|
|
"dimension-m.video-simp-"+v.Name,
|
|
s.NewYT(v.Name+"'s stream", v.CurrentStream, string(room)),
|
|
)
|
|
if err != nil {
|
|
log.Errorf("error embeding video: %v", err)
|
|
}
|
|
v.TotalStreams = v.TotalStreams + 1
|
|
s.currStream++
|
|
if s.currStream > s.maxStream {
|
|
s.maxStream = s.currStream
|
|
}
|
|
log.Info("Embed stream added", "event", resp, "vtuber", v.Name)
|
|
}
|
|
} else {
|
|
var content YoutubeWidget
|
|
err = s.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 := s.client.SendStateEvent(room, event.NewEventType("im.vector.modular.widgets"), "dimension-m.video-simp-"+v.Name, struct{}{})
|
|
if err != nil {
|
|
log.Errorf("error removing embed: %v", err)
|
|
}
|
|
s.currStream--
|
|
log.Info("Embed stream removed", "event", resp, "vtuber", v.Name)
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (s *simp) SetupMatrix(uname, pass, token, hs, domain, dserver string) error {
|
|
uid := id.NewUserID(strings.ToLower(uname), strings.ToLower(domain))
|
|
if token == "" {
|
|
client, err := mautrix.NewClient(hs, "", "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.client = client
|
|
} else {
|
|
log.Info("using token login")
|
|
client, err := mautrix.NewClient(hs, uid, token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.client = client
|
|
}
|
|
dataFilter := &mautrix.Filter{
|
|
AccountData: mautrix.FilterPart{
|
|
Limit: 20,
|
|
NotTypes: []event.Type{
|
|
event.NewEventType("s.batch"),
|
|
},
|
|
},
|
|
}
|
|
store := mautrix.NewAccountDataStore("simp.batch", s.client)
|
|
fID, err := s.client.CreateFilter(dataFilter)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
store.SaveFilterID(uid, fID.FilterID)
|
|
s.client.Store = store
|
|
|
|
if token == "" {
|
|
loginRes, err := s.client.Login(&mautrix.ReqLogin{
|
|
Type: "m.login.password",
|
|
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: uname},
|
|
Password: pass,
|
|
StoreCredentials: true,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
token = loginRes.AccessToken
|
|
viper.Set("access_token", token)
|
|
log.Info("Login succesful, saving access_token to config file")
|
|
err = viper.WriteConfig()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
log.Info("skipping login since token provided")
|
|
}
|
|
syncer := s.client.Syncer.(*mautrix.DefaultSyncer)
|
|
syncer.OnEventType(event.EventMessage, func(source mautrix.EventSource, evt *event.Event) {
|
|
if evt.Sender == s.client.UserID {
|
|
return // ignore events from self
|
|
}
|
|
log.Debugf("<%[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
|
|
bodyS := strings.Split(body, " ")
|
|
if bodyS[0] != "!simp" {
|
|
return
|
|
}
|
|
if len(bodyS) < 2 {
|
|
return // nothing to parse
|
|
}
|
|
switch bodyS[1] {
|
|
case "info":
|
|
// print info page
|
|
var infomsg string
|
|
vlist := []string{}
|
|
for _, vt := range s.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"))
|
|
s.client.SendText(evt.RoomID, infomsg)
|
|
case "stats":
|
|
var statmsg string
|
|
vlist := []string{}
|
|
t := 0
|
|
for _, vt := range s.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",
|
|
s.startTime,
|
|
strings.Join(vlist, "\n"),
|
|
t,
|
|
s.maxStream,
|
|
len(s.vtubers),
|
|
)
|
|
s.client.SendText(evt.RoomID, statmsg)
|
|
case "version":
|
|
s.client.SendText(evt.RoomID, "not implemented")
|
|
|
|
case "reload":
|
|
// reload config
|
|
s.client.SendText(evt.RoomID, "Reloading config")
|
|
log.Info("Reload requested,reloading vtubers")
|
|
s.vtubers = loadVtubers()
|
|
case "subscribe":
|
|
if len(bodyS) < 3 {
|
|
s.client.SendText(evt.RoomID, "Need a member to subscribe to")
|
|
}
|
|
vt := bodyS[2]
|
|
var subbed bool
|
|
for _, v := range s.vtubers {
|
|
if strings.ToUpper(v.Name) == strings.ToUpper(vt) {
|
|
v.Subs[evt.Sender] = true
|
|
subbed = true
|
|
}
|
|
}
|
|
if subbed {
|
|
s.client.SendText(evt.RoomID, "subbed")
|
|
} else {
|
|
s.client.SendText(evt.RoomID, "could not identify talent to subscribe to")
|
|
}
|
|
|
|
case "help":
|
|
s.client.SendText(evt.RoomID, "Supported commands: info,version,stats,reload,subscribe")
|
|
default:
|
|
// command not found
|
|
s.client.SendText(evt.RoomID, "command not recognized")
|
|
}
|
|
})
|
|
syncer.OnEventType(event.StateMember, func(source mautrix.EventSource, evt *event.Event) {
|
|
log.Infof("<%[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 = s.client.JoinRoomByID(evt.RoomID)
|
|
if err != nil {
|
|
log.Errorf("error joining room %v", evt.RoomID)
|
|
} else {
|
|
log.Infof("joined room %v", evt.RoomID)
|
|
s.rooms = append(s.rooms, evt.RoomID)
|
|
}
|
|
}
|
|
})
|
|
s.dimensionServer = dserver
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *simp) Stop() {
|
|
s.stop <- true
|
|
s.client.StopSync()
|
|
}
|
|
|
|
func (s *simp) NewYT(videoName, videoID, roomID string) *YoutubeWidget {
|
|
encodedVod := url.QueryEscape("https://youtube.com/embed/" + videoID)
|
|
return &YoutubeWidget{
|
|
Type: "im.vector.modular.widgets",
|
|
URL: "https://" + s.dimensionServer + "/widgets/video?url=" + encodedVod,
|
|
Name: videoName,
|
|
Data: VideoData{
|
|
VideoURL: "https://www.youtube.com/watch?v=" + videoID,
|
|
URL: "https://youtube.com/embed/" + videoID,
|
|
DimensionAppMetadata: DimensionAppMetadata{
|
|
InRoomID: roomID,
|
|
WrapperURLBase: "https://" + s.dimensionServer + "/widgets/video?url=",
|
|
WrapperID: "video",
|
|
ScalarWrapperID: "youtube",
|
|
Integration: Integration{
|
|
Category: "widget",
|
|
Type: "youtube",
|
|
},
|
|
LastUpdatedTs: time.Now().UnixNano() / int64(time.Millisecond),
|
|
},
|
|
},
|
|
CreatorUserID: string(s.client.UserID),
|
|
ID: "dimension-m.video-simp",
|
|
}
|
|
}
|