diff --git a/.env.example b/.env.example index 6f9a2956..1f003b4c 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,7 @@ SESSION_KEY= TRAQ_CALENDARID= CLIENT_ID= -WEBHOOK_ID= -WEBHOOK_SECRET= +TRAQ_BOT_TOKEN= ACTIVITY_CHANNEL_ID= DAILY_CHANNEL_ID= TOKEN_KEY= diff --git a/.gitignore b/.gitignore index dd9b185d..e60c2a35 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ debug # for development _development/* +.env \ No newline at end of file diff --git a/README.md b/README.md index 482d90ee..f8bc234f 100644 --- a/README.md +++ b/README.md @@ -35,8 +35,7 @@ knoQ の全ての機能を動作させるためには、追加の情報が必要 | SESSION_KEY | 環境変数 | `random32wordsXXXXXXXXXXXXXXXXXXX` | session を暗号化するもの | | TRAQ_CALENDARID | 環境変数 | | 進捗部屋の提供元(公開されている google calendar の id なら何でもいい) | | CLIENT_ID | 環境変数 | `d1hDlIRNfCBerXdZkH1VR8lJwCBpIuMgiv42` | 認証に必要 | -| WEBHOOK_ID | 環境変数 | | Bot 情報 | -| WEBHOOK_SECRET | 環境変数 | | Bot 情報 | +| TRAQ_BOT_TOKEN | 環境変数 | | Bot 情報 | | CHANNEL_ID | 環境変数 | | Bot の送信先チャンネル (deprecated) | | DAILY_CHANNEL_ID | 環境変数 | | Bot が毎日定時に投稿する先のチャンネル | | ACTIVITY_CHANNEL_ID | 環境変数 | | Bot が都度送信するチャンネル | diff --git a/compose.yml b/compose.yml index ec1953d3..f92f64fd 100755 --- a/compose.yml +++ b/compose.yml @@ -16,13 +16,12 @@ services: SESSION_KEY: ${SESSION_KEY:-random32wordsXXXXXXXXXXXXXXXXXXX} TRAQ_CALENDARID: ${TRAQ_CALENDARID} CLIENT_ID: ${CLIENT_ID:-d1hDlIRNfCBerXdZkH1VR8lJwCBpIuMgiv42} - WEBHOOK_ID: ${WEBHOOK_ID} - WEBHOOK_SECRET: ${WEBHOOK_SECRET} CHANNEL_ID: ${CHANNEL_ID} DAILY_CHANNEL_ID: ${DAILY_CHANNEL_ID} ACTIVITY_CHANNEL_ID: ${ACTIVITY_CHANNEL_ID} TOKEN_KEY: ${TOKEN_KEY:-random32wordsXXXXXXXXXXXXXXXXXXX} KNOQ_VERSION: ${KNOQ_VERSION:-dev} + TRAQ_BOT_TOKEN: ${TRAQ_BOT_TOKEN} DEVELOPMENT: true GORM_LOG_LEVEL: info ports: @@ -30,10 +29,6 @@ services: depends_on: db: condition: service_healthy - develop: - watch: - - action: rebuild - path: ./ db: image: mariadb:10.6.4 diff --git a/domain/domain.go b/domain/domain.go index 80806cab..0421baf4 100644 --- a/domain/domain.go +++ b/domain/domain.go @@ -30,4 +30,5 @@ type Repository interface { RoomRepository TagRepository UserRepository + PostRepository } diff --git a/domain/post.go b/domain/post.go new file mode 100644 index 00000000..57fe8dcb --- /dev/null +++ b/domain/post.go @@ -0,0 +1,18 @@ +package domain + +import "github.com/gofrs/uuid" + +type Post struct { + MessageID uuid.UUID + EventID uuid.UUID +} + +type WritePostParams struct { + MessageID uuid.UUID + EventID uuid.UUID +} + +type PostRepository interface { + CreatePost(params WritePostParams) (*Post, error) + GetPost(MessageID uuid.UUID) (*Post, error) +} diff --git a/go.mod b/go.mod index 097fef45..8de6aad6 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/stretchr/testify v1.9.0 github.com/traPtitech/go-traq v0.0.0-20240509050113-9343acbeec35 + github.com/traPtitech/traq-ws-bot v1.1.3 go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/oauth2 v0.20.0 @@ -54,6 +55,7 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/gorilla/context v1.1.2 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect diff --git a/go.sum b/go.sum index bca8f621..251d3ecd 100644 --- a/go.sum +++ b/go.sum @@ -102,6 +102,8 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= @@ -172,6 +174,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/traPtitech/go-traq v0.0.0-20240509050113-9343acbeec35 h1:NoEvHqFhmtyy7ELLp1chwM4R1vhXw2zKeEotkmKvSJw= github.com/traPtitech/go-traq v0.0.0-20240509050113-9343acbeec35/go.mod h1:7yJs1m/ddCG39XF78GA8FrXqyc4fNPfHp8BSLjMVMY8= +github.com/traPtitech/traq-ws-bot v1.1.3 h1:Cehz+ORmz/j2HexJHtm6yS/lmAq99REpojfwj0vjjBY= +github.com/traPtitech/traq-ws-bot v1.1.3/go.mod h1:mnUHbZiFLydoUxS2q8kzR1E+1RMRXrmEpUdSAxh0jvo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= diff --git a/infra/bot/bot.go b/infra/bot/bot.go new file mode 100644 index 00000000..55d7bfc8 --- /dev/null +++ b/infra/bot/bot.go @@ -0,0 +1,51 @@ +package bot + +import ( + "fmt" + "time" + + "github.com/gofrs/uuid" + "github.com/traPtitech/knoQ/domain" + "github.com/traPtitech/knoQ/infra/db" + traqwsbot "github.com/traPtitech/traq-ws-bot" + "github.com/traPtitech/traq-ws-bot/payload" +) + +var Bot *traqwsbot.Bot + +const ( + AttendanceStampID = "93d376c3-80c9-4bb2-909b-2bbe2fbf9e93" + AbsentStampID = "544c04db-9cc3-4c0e-935d-571d4cf103a2" + PendingStampID = "bc9a3814-f185-4b3d-ac1f-3c8f12ad7b52" +) + +func BotMessageStampsUpdatedHandler(p *payload.BotMessageStampsUpdated, gormRepo db.GormRepository) { + post, err := gormRepo.GetPost(uuid.FromStringOrNil(p.MessageID)) + if err != nil { + fmt.Println(err) + } + for _, stamp := range p.Stamps { + if time.Now().After(stamp.UpdatedAt.Add(3 * time.Second)) { + // 3秒以上経過したスタンプは無視 + continue + } + var scheduleStatus domain.ScheduleStatus + switch stamp.StampID { + case AttendanceStampID: + // 出席 + scheduleStatus = domain.Attendance + case AbsentStampID: + // 欠席 + scheduleStatus = domain.Absent + case PendingStampID: + // 未定 + scheduleStatus = domain.Pending + default: + continue + } + err := gormRepo.UpsertEventSchedule(post.EventID, uuid.FromStringOrNil(stamp.UserID), scheduleStatus) + if err != nil { + fmt.Println(err) + } + } +} diff --git a/infra/db/converter.go b/infra/db/converter.go index 59b142ed..f62cccd7 100644 --- a/infra/db/converter.go +++ b/infra/db/converter.go @@ -127,6 +127,12 @@ func ConvRoomTodomainRoom(src Room) (dst domain.Room) { return } +func ConvPostTodomainPost(src Post) (dst domain.Post) { + dst.MessageID = src.MessageID + dst.EventID = src.EventID + return +} + func ConvSEventAdminToSRoomAdmin(src []EventAdmin) (dst []RoomAdmin) { dst = make([]RoomAdmin, len(src)) for i := range src { @@ -261,6 +267,13 @@ func ConvWriteGroupParamsToGroup(src WriteGroupParams) (dst Group) { } return } + +func ConvWritePostParamsToPost(src WritePostParams) (dst Post) { + dst.MessageID = src.MessageID + dst.EventID = src.EventID + return +} + func ConvdomainEventTagParamsToEventTag(src domain.EventTagParams) (dst EventTag) { dst.Tag.Name = src.Name dst.Locked = src.Locked diff --git a/infra/db/model.go b/infra/db/model.go index d46c13ad..79464fbc 100644 --- a/infra/db/model.go +++ b/infra/db/model.go @@ -21,6 +21,7 @@ var tables = []interface{}{ EventTag{}, // Eventより下にないと、overrideされる EventAdmin{}, EventAttendee{}, + Post{}, } type Model struct { @@ -167,6 +168,11 @@ type EventAttendee struct { Schedule int } +type Post struct { + MessageID uuid.UUID `gorm:"type:char(36); primaryKey"` + EventID uuid.UUID `gorm:"type:char(36); not null"` +} + // Event is event for gorm // //go:generate go run github.com/fuji8/gotypeconverter/cmd/gotypeconverter@latest -s Event -d domain.Event -o converter.go . diff --git a/infra/db/post.go b/infra/db/post.go new file mode 100644 index 00000000..1fb886fd --- /dev/null +++ b/infra/db/post.go @@ -0,0 +1,36 @@ +package db + +import ( + "github.com/gofrs/uuid" + "github.com/traPtitech/knoQ/domain" + "gorm.io/gorm" +) + +type WritePostParams struct { + domain.WritePostParams +} + +func (repo *GormRepository) CreatePost(params WritePostParams) (*Post, error) { + p, err := createPost(repo.db, params) + return p, defaultErrorHandling(err) +} + +func (repo *GormRepository) GetPost(MessageID uuid.UUID) (*Post, error) { + post, err := getPost(repo.db, MessageID) + return post, defaultErrorHandling(err) +} + +func createPost(db *gorm.DB, params WritePostParams) (*Post, error) { + post := ConvWritePostParamsToPost(params) + err := db.Create(&post).Error + if err != nil { + return nil, err + } + return &post, nil +} + +func getPost(db *gorm.DB, messageID uuid.UUID) (*Post, error) { + post := Post{} + err := db.Take(&post, messageID).Error + return &post, err +} diff --git a/main.go b/main.go index 0e722c0a..2303c19e 100644 --- a/main.go +++ b/main.go @@ -9,11 +9,13 @@ import ( "time" "github.com/traPtitech/knoQ/domain" + "github.com/traPtitech/knoQ/infra/bot" "github.com/traPtitech/knoQ/infra/db" "github.com/traPtitech/knoQ/infra/traq" "github.com/traPtitech/knoQ/repository" "github.com/traPtitech/knoQ/utils" "github.com/traPtitech/knoQ/utils/tz" + "github.com/traPtitech/traq-ws-bot/payload" "golang.org/x/oauth2" "github.com/traPtitech/knoQ/router" @@ -21,6 +23,8 @@ import ( "github.com/gorilla/sessions" "github.com/robfig/cron/v3" "go.uber.org/zap" + + traqwsbot "github.com/traPtitech/traq-ws-bot" ) var ( @@ -39,8 +43,6 @@ var ( clientID = getenv("CLIENT_ID", "client_id") origin = getenv("ORIGIN", "http://localhost:3000") sessionKey = getenv("SESSION_KEY", "random32wordsXXXXXXXXXXXXXXXXXXX") - webhookID = getenv("WEBHOOK_ID", "") - webhookSecret = getenv("WEBHOOK_SECRET", "") activityChannelID = getenv("ACTIVITY_CHANNEL_ID", "") dailyChannelID = getenv("DAILY_CHANNEL_ID", "") ) @@ -83,8 +85,6 @@ func main() { HttpOnly: true, SameSite: http.SameSiteLaxMode, }, - WebhookID: webhookID, - WebhookSecret: webhookSecret, ActivityChannelID: activityChannelID, DailyChannelId: dailyChannelID, Origin: origin, @@ -92,15 +92,13 @@ func main() { e := handler.SetupRoute() - // webhook + // cron c := cron.New(cron.WithLocation(tz.JST)) _, err = c.AddFunc( "0 8 * * *", utils.InitPostEventToTraQ( &repo.GormRepo, - handler.WebhookSecret, handler.DailyChannelId, - handler.WebhookID, handler.Origin, ), ) @@ -115,10 +113,27 @@ func main() { e.Logger.Info("shutting down the server") } }() + + // bot + bot.Bot, err = traqwsbot.NewBot(&traqwsbot.Options{ + AccessToken: os.Getenv("TRAQ_BOT_TOKEN"), + }) + if err != nil { + panic(err) + } + bot.Bot.OnBotMessageStampsUpdated(func(p *payload.BotMessageStampsUpdated) { + bot.BotMessageStampsUpdatedHandler(p, gormRepo) + }) + + if err := bot.Bot.Start(); err != nil { + panic(err) + } + quit := make(chan os.Signal, 1) signal.Notify(quit, os.Interrupt) <-quit ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() if err := e.Shutdown(ctx); err != nil { e.Logger.Fatal(err) diff --git a/migration/current.go b/migration/current.go index b4790920..f0660043 100644 --- a/migration/current.go +++ b/migration/current.go @@ -19,5 +19,6 @@ func Migrations() []*gormigrate.Migration { v10(), v11(), v12(), + v13(), } } diff --git a/migration/v13.go b/migration/v13.go new file mode 100644 index 00000000..7b1a8915 --- /dev/null +++ b/migration/v13.go @@ -0,0 +1,25 @@ +package migration + +import ( + "github.com/go-gormigrate/gormigrate/v2" + "github.com/gofrs/uuid" + "gorm.io/gorm" +) + +type v13Post struct { + MessageID uuid.UUID `gorm:"type:char(36); primaryKey"` + EventID uuid.UUID `gorm:"type:char(36); not null"` +} + +func (*v13Post) TableName() string { + return "posts" +} + +func v13() *gormigrate.Migration { + return &gormigrate.Migration{ + ID: "13", + Migrate: func(db *gorm.DB) error { + return db.Migrator().CreateTable(&v13Post{}) + }, + } +} diff --git a/repository/post.go b/repository/post.go new file mode 100644 index 00000000..ff5b4607 --- /dev/null +++ b/repository/post.go @@ -0,0 +1,28 @@ +package repository + +import ( + "github.com/gofrs/uuid" + "github.com/traPtitech/knoQ/domain" + "github.com/traPtitech/knoQ/infra/db" +) + +func (repo *Repository) CreatePost(params domain.WritePostParams) (*domain.Post, error) { + p := db.WritePostParams{ + WritePostParams: params, + } + post, err := repo.GormRepo.CreatePost(p) + if err != nil { + return nil, defaultErrorHandling(err) + } + domainPost := db.ConvPostTodomainPost(*post) + return &domainPost, nil +} + +func (repo *Repository) GetPost(MessageID uuid.UUID) (*domain.Post, error) { + post, err := repo.GormRepo.GetPost(MessageID) + if err != nil { + return nil, defaultErrorHandling(err) + } + domainPost := db.ConvPostTodomainPost(*post) + return &domainPost, nil +} diff --git a/router/middleware.go b/router/middleware.go index 77feb07d..939c30fe 100644 --- a/router/middleware.go +++ b/router/middleware.go @@ -176,8 +176,8 @@ func (h *Handlers) RoomAdminsMiddleware(next echo.HandlerFunc) echo.HandlerFunc } } -// WebhookEventHandler is used with middleware.BodyDump -func (h *Handlers) WebhookEventHandler(c echo.Context, reqBody, resBody []byte) { +// BotEventHandler is used with middleware.BodyDump +func (h *Handlers) BotEventHandler(c echo.Context, reqBody, resBody []byte) { if c.Response().Status >= 400 { return } @@ -220,9 +220,19 @@ func (h *Handlers) WebhookEventHandler(c echo.Context, reqBody, resBody []byte) } } - content := presentation.GenerateEventWebhookContent(c.Request().Method, e, notificationTargets, h.Origin, !domain.DEVELOPMENT) + content := presentation.GenerateEventBotContent(c.Request().Method, e, notificationTargets, h.Origin, !domain.DEVELOPMENT) - _ = utils.RequestWebhook(content, h.WebhookSecret, h.ActivityChannelID, h.WebhookID, 1) + messageID, err := utils.RequestBotPost(content, h.ActivityChannelID) + if err != nil { + h.Logger.Error("failed to request bot post", zap.Error(err)) + return + } + err = utils.RequestBotStatusStamp(messageID) + if err != nil { + h.Logger.Error("failed to request bot status stamp", zap.Error(err)) + return + } + h.Repo.CreatePost(domain.WritePostParams{MessageID: messageID, EventID: e.ID}) } // getRequestUserID sessionからuserを返します diff --git a/router/presentation/event.go b/router/presentation/event.go index 04d55127..9c6c2347 100644 --- a/router/presentation/event.go +++ b/router/presentation/event.go @@ -162,7 +162,7 @@ func ICalFormat(events []*domain.Event, host string, userMap map[uuid.UUID]*doma return cal } -func GenerateEventWebhookContent(method string, e *EventDetailRes, nofiticationTargets []string, origin string, isMention bool) string { +func GenerateEventBotContent(method string, e *EventDetailRes, nofiticationTargets []string, origin string, isMention bool) string { timeFormat := "01/02(Mon) 15:04" var content string switch method { diff --git a/router/router.go b/router/router.go index 9855d1f1..dc87be3a 100644 --- a/router/router.go +++ b/router/router.go @@ -25,8 +25,6 @@ type Handlers struct { SessionKey []byte SessionOption sessions.Options ClientID string - WebhookID string - WebhookSecret string ActivityChannelID string DailyChannelId string Origin string @@ -90,7 +88,7 @@ func (h *Handlers) SetupRoute() *echo.Echo { eventsAPI := apiWithAuth.Group("/events") { eventsAPI.GET("", h.HandleGetEvents) - eventsAPI.POST("", h.HandlePostEvent, middleware.BodyDump(h.WebhookEventHandler)) + eventsAPI.POST("", h.HandlePostEvent, middleware.BodyDump(h.BotEventHandler)) eventsAPI.GET("/:eventid", h.HandleGetEvent) eventsAPI.PUT("/:eventid/attendees/me", h.HandleUpsertMeEventSchedule) eventsAPI.POST("/:eventid/tags", h.HandleAddEventTag) @@ -99,7 +97,7 @@ func (h *Handlers) SetupRoute() *echo.Echo { // イベント管理者権限が必要 eventsAPIWithAdminAuth := eventsAPI.Group("", h.EventAdminsMiddleware) { - eventsAPIWithAdminAuth.PUT("/:eventid", h.HandleUpdateEvent, middleware.BodyDump(h.WebhookEventHandler)) + eventsAPIWithAdminAuth.PUT("/:eventid", h.HandleUpdateEvent, middleware.BodyDump(h.BotEventHandler)) eventsAPIWithAdminAuth.DELETE("/:eventid", h.HandleDeleteEvent) } } diff --git a/utils/api.go b/utils/api.go index 1932e508..4fc27736 100644 --- a/utils/api.go +++ b/utils/api.go @@ -2,36 +2,33 @@ package utils import ( "context" - "crypto/hmac" - "crypto/sha1" - "encoding/hex" - "errors" - "net/http" + "github.com/gofrs/uuid" "github.com/traPtitech/go-traq" + "github.com/traPtitech/knoQ/infra/bot" ) -// RequestWebhook q.trap/jp にメッセージを送信します。 -func RequestWebhook(message, secret, channelID, webhookID string, embed int) error { - configuration := traq.NewConfiguration() - apiClient := traq.NewAPIClient(configuration) +// RequestBotPost q.trap/jp にメッセージを送信します。 +func RequestBotPost(messageText, channelID string) (uuid.UUID, error) { + message, _, err := bot.Bot.API(). + MessageApi. + PostMessage(context.Background(), channelID). + PostMessageRequest(traq.PostMessageRequest{ + Content: messageText, + }). + Execute() + return uuid.FromStringOrNil(message.Id), err +} - xTRAQSignature := calcSignature(message, secret) - res, err := apiClient.WebhookApi.PostWebhook(context.TODO(), webhookID). - XTRAQChannelId(channelID).XTRAQSignature(xTRAQSignature). - Embed(int32(embed)).Body(message).Execute() - if err != nil { - return err - } - if res.StatusCode >= 400 { - return errors.New(http.StatusText(res.StatusCode)) +// RequestBotStatusStamp :white_check_mark:, :no_entry_sign:, :loading: のスタンプを送信します。 +func RequestBotStatusStamp(messageID uuid.UUID) error { + stamps := []string{bot.AttendanceStampID, bot.AbsentStampID, bot.PendingStampID} + for _, stampID := range stamps { + _, err := bot.Bot.API(). + MessageApi.AddMessageStamp(context.Background(), messageID.String(), stampID).Execute() + if err != nil { + return err + } } - return nil } - -func calcSignature(message, secret string) string { - mac := hmac.New(sha1.New, []byte(secret)) - mac.Write([]byte(message)) - return hex.EncodeToString(mac.Sum(nil)) -} diff --git a/utils/cron.go b/utils/cron.go index 545f6644..ac1f9280 100644 --- a/utils/cron.go +++ b/utils/cron.go @@ -20,8 +20,8 @@ type timeTable struct { } // InitPostEventToTraQ 現在(job実行)から24時間以内に始まるイベントを取得し、 -// webhookでtraQに送るjobを作成。 -func InitPostEventToTraQ(repo *db.GormRepository, secret, channelID, webhookID, origin string) func() { +// botでtraQに送るjobを作成。 +func InitPostEventToTraQ(repo *db.GormRepository, channelID, origin string) func() { job := func() { now := setTimeFromString(time.Now().In(tz.JST), "06:00:00") tomorrow := now.AddDate(0, 0, 1) @@ -29,7 +29,7 @@ func InitPostEventToTraQ(repo *db.GormRepository, secret, channelID, webhookID, rooms, _ := repo.GetAllRooms(now, tomorrow, uuid.Nil) events, _ := repo.GetAllEvents(filter.FilterTime(now, tomorrow)) message := createMessage(now, rooms, events, origin) - err := RequestWebhook(message, secret, channelID, webhookID, 1) + _, err := RequestBotPost(message, channelID) if err != nil { fmt.Println(err) }