From 2354e60b3d5ec26a52e1c607456c93ed9188ad03 Mon Sep 17 00:00:00 2001 From: James Coleman Date: Sun, 1 Oct 2023 07:58:52 -0500 Subject: [PATCH] Added support for creating slack channels with reference to a weekday, added support for specifying users to always add to a channel, maybe more? --- .gitignore | 3 +- README.md | 6 +++- api.go | 2 +- config.go | 5 +++- main.go | 2 +- update.go | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 92 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 77d522c..7aa22f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ config.yaml service-notifications -service-notifications.db \ No newline at end of file +service-notifications.db +sync.sh \ No newline at end of file diff --git a/README.md b/README.md index ced5665..8b8de79 100644 --- a/README.md +++ b/README.md @@ -110,6 +110,7 @@ Get Slack API token by creating an app at https://api.slack.com/apps then go to Get Planning Center API secrets at https://api.planningcenteronline.com/oauth/applications by creating a personal access token. +You can get a slack user ID by viewing the profile and under the 3 dot menu choose Copy member ID. ```yaml --- @@ -122,6 +123,9 @@ planning_center: slack: api_token: SLACK_API_TOKEN - admin_id: SLACK_UID + create_from_weekday: 3 + default_conversation: SLACK_UID + sticky_users: + - SLACK_UID ``` \ No newline at end of file diff --git a/api.go b/api.go index 5dd831d..a56428a 100644 --- a/api.go +++ b/api.go @@ -96,7 +96,7 @@ func (s *HTTPServer) RegisterAPIRoutes(r *mux.Router) { // Get current time and default conversation. now := time.Now().UTC() - conversation := app.config.Slack.AdminID + conversation := app.config.Slack.DefaultConversation // Find plan times that are occuring right now. var planTime PlanTimes diff --git a/config.go b/config.go index 5efd3d3..b34cde7 100644 --- a/config.go +++ b/config.go @@ -35,9 +35,11 @@ type PlanningCenterConfig struct { // Configurations relating to Slack API/channel creation. type SlackConfig struct { + CreateFromWeekday int `fig:"create_from_weekday"` // Create ahead from this weekday. -1 value is default and will instead create from the current time of operation. CreateChannelsAhead time.Duration `fig:"create_channels_ahead"` // Amount of time of future services to create channels head for. Defaults to 8 days head. APIToken string `fig:"api_token"` - AdminID string `fig:"admin_id"` // Slack user that administers this app. + StickyUsers []string `fig:"sticky_users"` // Users to add to every channel. + DefaultConversation string `fig:"default_conversation"` // Slack user that administers this app. } // Configuration Structure. @@ -86,6 +88,7 @@ func (a *App) ReadConfig() { Connection: "service-notifications.db", }, Slack: SlackConfig{ + CreateFromWeekday: -1, CreateChannelsAhead: time.Hour * 24 * 8, }, } diff --git a/main.go b/main.go index f03cfe1..2fc36ee 100644 --- a/main.go +++ b/main.go @@ -13,7 +13,7 @@ import ( const ( serviceName = "service-notifications" serviceDescription = "Notifications for church services" - serviceVersion = "0.1" + serviceVersion = "0.2" ) // App is the global application structure for communicating between servers and storing information. diff --git a/update.go b/update.go index a51e9d2..ab14447 100644 --- a/update.go +++ b/update.go @@ -13,6 +13,19 @@ import ( // Update planning center database tables with data from PC API. func UpdatePCData() { + // We don't need to update the archive if we already have data from the past, + // as such we get the current time and see if we already have an entry in the future. + // Its possible that some people only schedule services once a week, so we check with + // the current date subtracted by 14 days. + var updateFrom time.Time + now := time.Now().UTC() + var futurePlan Plans + app.db.Where("first_time_at >= ?", now.Add(time.Hour*24*14*-1)).Order("first_time_at ASC").First(&futurePlan) + if futurePlan.ID != 0 { + // If a future plan exists, we update from past 30 days. + updateFrom = now.Add(time.Hour * 24 * 30 * -1) + } + // Get all people. allPeople, err := PCGetAll("/services/v2/people") if err != nil { @@ -118,6 +131,13 @@ func UpdatePCData() { p.MultiDay = attributes.GetBool("multi_day") p.Dates = attributes.GetString("dates") + // If either updated at or first time at for the plan is before the update from date, + // we can process the update of data. Otherwise, we ignore this service as we do not care + // about updating historic data. Updating historic data causes more API traffic than needed. + if p.UpdatedAt.Before(updateFrom) && p.FirstTimeAt.Before(updateFrom) { + continue + } + // If plan wasn't already created, create it. if p.ID == 0 { p.ID = planID @@ -291,13 +311,41 @@ func UpdateSlackData() { } } +/* + +Delay on channel descript/topic may not be long enough. + +*/ + // Create slack channels for upcoming services. func CreateSlackChannels() { - // For now, we're using the start time of now. I want to update this later to allow - // setting a day of the week for slack channels to be created on. - // Doing a day of the week will allow for channels to be created ahead of time, then - // if people are added to the plan later on, they can be added at the next cron run. - startDate := time.Now().UTC() + // Start at now. + now := time.Now().UTC() + startDate := now + // If create from weekday is a valid weekday, attempt to turn back the clock to the + // most recently past weekday. Use that day as the stating point so we do not + // create channels in the future past the date we expect to have channels. + // This is useful if you want to run the cron every day to keep channel title + // and members up to date, but only want so many channels ahead of a certain weekday. + if app.config.Slack.CreateFromWeekday != -1 && app.config.Slack.CreateFromWeekday <= 6 { + // Get the current weekday and set the days to subtract to 0. + thisWeekday := int(now.Weekday()) + var daysSub int = 0 + + // If this weekday is the day we intend to create from, or if the weekday is + // after. We want to just subtract this weekday from create form weekday which + // should get us back to the most recent weekday. + if thisWeekday >= app.config.Slack.CreateFromWeekday { + daysSub = app.config.Slack.CreateFromWeekday - thisWeekday + } else { + // Otherwise, we have started a new week from that weekday and we need to + // add 7 days to the current weekday in our subtraction. This will bring us + // not to the next weekday, but the past weekday. + daysSub = app.config.Slack.CreateFromWeekday - (thisWeekday + 7) + } + // Subtract the number of days calculated to bring us to the weekday to create form. + startDate = now.Add(time.Hour * 24 * time.Duration(daysSub)) + } // Last date is start date plus duration of create channels ahead. lastDate := startDate.Add(app.config.Slack.CreateChannelsAhead) @@ -391,7 +439,7 @@ func CreateSlackChannels() { if topic != "" { // Sleep before, as it takes time for Slack APIs // to recongize the channel was created. - time.Sleep(10 * time.Second) + time.Sleep(120 * time.Second) _, err = app.slack.SetTopicOfConversation(channel.ID, topic) if err != nil { log.Println("Failed to set topic:", err) @@ -421,6 +469,31 @@ func CreateSlackChannels() { // Keep a list of users we need to invite as they are new. var usersToInvite []string + // For each sticky user, invite them. + for _, stickyUser := range app.config.Slack.StickyUsers { + // Check if they were already invited. + alreadyInvited := false + for _, uid := range invited { + if uid == stickyUser { + alreadyInvited = true + break + } + } + + // Make sure they were not already added to the list of users. + for _, uid := range usersToInvite { + if uid == stickyUser { + alreadyInvited = true + break + } + } + + // If not already invited, add to the list of users to invite. + if !alreadyInvited { + usersToInvite = append(usersToInvite, stickyUser) + } + } + // For each person on the plan, see if we need to invite them. for _, personOnPlan := range peopleOnPlan { // Find the slack user for the planning center person.