diff --git a/README.md b/README.md index 0f9b4c2..e0d13f8 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,12 @@ This is early on in its development, so items are rough around the edges. The collection currently consists of: * slack-uploader -> An http server allows for uploads of pictures and once uploaded can be autoposted to a slack channel. - * zoom-notifier -> A service that notifies slack or IRC when somebody joins or leaves a zoom meeting. * slack-emoji-grabber -> A quick utility to grab all the emojis from a slack team. +Previously included: + + * zoom-notifier -> Moved to its own repo at [stahnma/zoom-notifier](https://github.com/stahnma/zoom-notifier). + # License diff --git a/zoom-notifier/.gitignore b/zoom-notifier/.gitignore deleted file mode 100644 index 80fc94c..0000000 --- a/zoom-notifier/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -index.html -zoom-notifier -go.sum -*.creds diff --git a/zoom-notifier/LICENSE b/zoom-notifier/LICENSE deleted file mode 100644 index 6590a6d..0000000 --- a/zoom-notifier/LICENSE +++ /dev/null @@ -1,7 +0,0 @@ -Copyright 2023 Michael Stahnke - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/zoom-notifier/Makefile b/zoom-notifier/Makefile deleted file mode 100644 index 05589e8..0000000 --- a/zoom-notifier/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -NAME=zoom-notifier - -default: build - -all: platforms - -fmt: - go fmt . - -tidy: fmt - go mod tidy - -VERSION=$(shell git describe --tags --always --dirty) -COMMIT=$(shell git rev-parse --short HEAD) -BUILDDATE=$(shell date -u +'%Y-%m-%dT%H:%M:%SZ') - -build: tidy - go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.buildDate=$(BUILDDATE)" -o $(NAME) . - -clean: - rm -rf $(NAME) bin index.html - -linux-arm64: tidy - GOOS=linux GOARCH=arm64 go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.buildDate=$(BUILDDATE)" -o bin/$(NAME).linux-arm64 . - -linux-amd64: tidy - GOOS=linux GOARCH=amd64 go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.buildDate=$(BUILDDATE)" -o bin/$(NAME).linux-amd64 . - -darwin-arm64: tidy - GOOS=darwin GOARCH=arm64 go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.buildDate=$(BUILDDATE)" -o bin/$(NAME).mac-arm64 . - -darwin-amd64: - GOOS=darwin GOARCH=amd64 go build -ldflags "-X main.version=$(VERSION) -X main.commit=$(COMMIT) -X main.buildDate=$(BUILDDATE)" -o bin/$(NAME).mac-amd64 . - -mac-arm64: darwin-arm64 - -mac-amd64: darwin-amd64 - -linux: linux-arm64 linux-amd64 - -mac: darwin-arm64 darwin-amd64 - -install: build - sudo install -p -m0755 $(NAME) /usr/local/bin - -platforms: mac linux diff --git a/zoom-notifier/README.md b/zoom-notifier/README.md deleted file mode 100644 index 9de56be..0000000 --- a/zoom-notifier/README.md +++ /dev/null @@ -1,103 +0,0 @@ -# What is this? - -This is a small program designed to run on a public network that can receive webhooks from zoom for participants joining/leaving a meeting. It then translates those webhooks into webhook calls to slack to notify a specific slack channel about a participant joining or leaving. This was designed to work with long-standing/running meetings. - -It also can notify IRC channels using similar methodology to the slack send, however instead of using a webhook, it just speaks native IRC protocols. - -## Assumptions - -1. You have a long-standing zoom meeting that has a constant ID. I use a paid account, I'm not sure if you have to have one or not to do this. -1. You have access/permissions to create a zoom application. -1. You have access/permissions to create a slack webhook integration. -1. You optionally have an IRC server. - - -# Running Config - -you must set - -`ZOOM_SECRET` which is the `secret token` provided on your zoom application. This is how zoom knows your webhook listener is the intended target. - -`ZOOMWH_PORT` the port to put this webhook listener on. Default is 8888. - -`ZOOMWH_MSG_SUFFIX` the name of the call after "Person has " it defaults to "the zoom meeting". - -`ZOOMWH_MEETING_NAME` only notify on a particular meeting name. Default is any/all. Filter is a literal string, not a regex. - -## API calls for more advanced messaging - -If you'd like your message to contain a hyperlink to the zoom meeting that includes the meetind ID and passcode (meaning a 1-click join), you have to have a full web api account with zoom. If you do, you can set the following environment variables. - -# Lookup Meeting Info - -| Key | Description | Default | -|------------------------|-----------------------------------------------------------------------------------------------------------------|---------| -| ZOOM_API_ENABLE | Whether or not to attempt to use this feature set. | false | -| ZOOM_API_ACCOUNT_ID | ID of the ZOOM Account. | empty | -| ZOOM_API_CLIENT_ID | Client ID of the ZOOM App you've created in the developer portal. | empty | -| ZOOM_API_CLIENT_SECRET | Secret of the ZOOM App you've created in the developer portal. | empty | -| ZOOM_API_SECRET_TOKEN | Token given to you after doing a POST to the ZOOM API to get a JWT token. This is used to authenticate with it. | empty | - -## Slack Messaging -`ZOOMWH_SLACK_ENABLE` should be set to 'true' if using this feature. It defaults to true. - -`ZOOMWH_SLACK_WH_URI` is the slack webhook uri. This can be a comma separated list of URIs. Required if `ZOOMWH_SLACK_ENABLE` is `true`. No Default. - -## IRC Messaging -`ZOOMWH_IRC_ENABLE` true will enable this. Default is false. - -`ZOOMWH_IRC_SERVER` is the a URI and includes a port. Required. No Default. - -`ZOOMWH_IRC_NICK` the nick name to use when posting for IRC and authenticating against the server. Required. No Default. - -`ZOOMWH_IRC_AUTH_PASS` is the password to authenticate with for the IRC Server. - -`ZOOMWH_IRC_CHANNEL` is where to post the messages. There is not default. This is required if `ZOOMWH_IRC_ENABLE` is true. - -# Building - -Checkout the source code - - go mod tidy - go build . - -# Running via systemd - -There is a unit file in the `contrib` directory. It should get you most of the way there. - -Basically, create a user by doing something like `useradd zoomwh`. - -Then copy the unit file in `/usr/lib/systemd/system`. - -Make a file with your environment settings and place it in `/etc/sysconfig` or whereever your distribution does that stuff. - -`systemctl enable zoomwh.service` - -`systemctl start zoomwh.service` - - -# Limitations -1. This can currently be set up for one zoom meeting (because there is no logic looking what the meeting is called or anything). -1. You can't make the POST URL path anything other than "/" right now. Again, this is an easy fix, but I haven't done it yet. Ideally you could pass in a ENV var for that. - - -# Setup - -## Assumptions - -1. You have a long-standing zoom meeting that has a constant ID. I use a paid account, I'm not sure if you have to have one or not to do this. -1. You have access/permissions to create a zoom application. -1. You have access/permissions to create a slack webhook integration. -1. You are comfortable putting a service on a public network. (I front mine with a reverse proxy). -1. You are an IRC admin if you're using the IRC functionality. - -## Zoom - -See [Zoom Documenation](https://github.com/stahnma/zoomwh/blob/main/docs/zoom_app_creation.md) - -## Slack - -See [Slack App Documentation](https://github.com/stahnma/zoomwh/blob/main/docs/slack_integrations.md) - -# LICENSE -MIT diff --git a/zoom-notifier/TODO b/zoom-notifier/TODO deleted file mode 100644 index 231f07e..0000000 --- a/zoom-notifier/TODO +++ /dev/null @@ -1,15 +0,0 @@ - - -chore ---- -* Rename callZoomApi -* Show routes and stuff -* Have a debug mode to drop all web hook payloads in a directory - - -enhancements ---- -* godoc -* document zoom api usage -* slack client with slash commands -* slack slash command to query the meeting and status of the notifier diff --git a/zoom-notifier/contrib/simple_irc.go b/zoom-notifier/contrib/simple_irc.go deleted file mode 100644 index 766be3c..0000000 --- a/zoom-notifier/contrib/simple_irc.go +++ /dev/null @@ -1,67 +0,0 @@ -package main - -import ( - "crypto/tls" - "github.com/thoj/go-ircevent" - "log" - "os" - "time" -) - -// Replace these values with your own IRC server details -func validateEnvVars(key string) { - _, ok := os.LookupEnv(key) - if !ok { - log.Fatal("You must set " + key + " environment variable.") - } -} - -func main() { - - ircEnable, _ := os.LookupEnv("ZOOMWH_IRC_ENABLE") - var ( - ircServer string - ircChannel string - ircNick string - ircPassword string - ) - if ircEnable == "true" { - // validate all IRC variables - validateEnvVars("ZOOMWH_IRC_SERVER") - validateEnvVars("ZOOMWH_IRC_CHANNEL") - validateEnvVars("ZOOMWH_IRC_NICK") - validateEnvVars("ZOOMWH_IRC_PASS") - - ircServer, _ = os.LookupEnv("ZOOMWH_IRC_SERVER") - ircChannel, _ = os.LookupEnv("ZOOMWH_IRC_CHANNEL") - ircNick, _ = os.LookupEnv("ZOOMWH_IRC_NICK") - ircPassword, _ = os.LookupEnv("ZOOMWH_IRC_PASS") - } - - irccon := irc.IRC(ircNick, ircNick) - irccon.UseTLS = true - irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true} - irccon.Password = ircPassword - irccon.AddCallback("001", func(e *irc.Event) { - irccon.Join(ircChannel) - }) - - err := irccon.Connect(ircServer) - if err != nil { - log.Fatal("Error connecting to IRC server:", err) - } - defer irccon.Quit() - - message := "Hello, IRC world! (this is an automated message)" - sendMessageToIRC(irccon, ircChannel, message) - - time.AfterFunc(1*time.Second, func() { - irccon.Quit() - }) - - irccon.Loop() -} - -func sendMessageToIRC(irccon *irc.Connection, channel, message string) { - irccon.Privmsg(channel, message) -} diff --git a/zoom-notifier/contrib/use_examples.sh b/zoom-notifier/contrib/use_examples.sh deleted file mode 100644 index 0f55159..0000000 --- a/zoom-notifier/contrib/use_examples.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -if [ -z "$1" ] ; then - echo "Usage $0 json_file" - exit 1 -fi - -curl -X POST -H "Content-Type: application/json" -d @"$1" http://localhost:8888/ diff --git a/zoom-notifier/contrib/webhook_dump.go b/zoom-notifier/contrib/webhook_dump.go deleted file mode 100644 index 6861562..0000000 --- a/zoom-notifier/contrib/webhook_dump.go +++ /dev/null @@ -1,75 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "os" -) - -const ( - webhookPort = "8080" // Change this port to the desired port number - dataFile = "webhook_data.json" -) - -type WebhookData struct { - // Define the structure of your JSON data here - // For example: - // Field1 string `json:"field1"` - // Field2 int `json:"field2"` -} - -func handleWebhook(w http.ResponseWriter, r *http.Request) { - if r.Method != http.MethodPost { - http.Error(w, "Only POST requests are allowed", http.StatusMethodNotAllowed) - return - } - - body, err := ioutil.ReadAll(r.Body) - if err != nil { - http.Error(w, "Failed to read request body", http.StatusInternalServerError) - return - } - - var data WebhookData - err = json.Unmarshal(body, &data) - if err != nil { - http.Error(w, "Failed to parse JSON data", http.StatusBadRequest) - return - } - - err = saveDataToFile(body) - if err != nil { - http.Error(w, "Failed to save data to file", http.StatusInternalServerError) - return - } - - fmt.Fprintf(w, "Webhook data received and saved successfully!\n") -} - -func saveDataToFile(data []byte) error { - file, err := os.Create(dataFile) - if err != nil { - return err - } - defer file.Close() - - _, err = file.Write(data) - if err != nil { - return err - } - - return nil -} - -func main() { - http.HandleFunc("/", handleWebhook) - - fmt.Printf("Webhook server listening on port %s...\n", webhookPort) - err := http.ListenAndServe(":"+webhookPort, nil) - if err != nil { - fmt.Printf("Error starting webhook server: %s\n", err) - } -} - diff --git a/zoom-notifier/contrib/zoomwh.service b/zoom-notifier/contrib/zoomwh.service deleted file mode 100644 index 54c7933..0000000 --- a/zoom-notifier/contrib/zoomwh.service +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Zoom Webhook Processor - -[Service] -EnvironmentFile=-/etc/sysconfig/zoomwh.creds -ExecStart=/usr/local/bin/zoomwh -User=zoomwh - -[Install] -WantedBy=multi-user.target diff --git a/zoom-notifier/docs/slack_integrations.md b/zoom-notifier/docs/slack_integrations.md deleted file mode 100644 index ed7e56c..0000000 --- a/zoom-notifier/docs/slack_integrations.md +++ /dev/null @@ -1,28 +0,0 @@ -# Go to your slack workspace seettings - - -![plex-media-feed_-_stahnma_-_Slack](https://user-images.githubusercontent.com/6961/222059514-e0f0b6e2-58f5-4038-869b-ec40c22c7406.png) - - -# Click on "configure apps" -![slack_callout](https://user-images.githubusercontent.com/6961/222059629-79be2253-0348-48e7-8be4-04faa56088a6.png) - - -# Click on "custom integrations" - -![slack_custom_integrations](https://user-images.githubusercontent.com/6961/222059731-9705e03e-b898-4610-bb28-bc8ce8e6d59f.png) - -# Click custom integrations (again?) - -![slack-webhook-screen](https://user-images.githubusercontent.com/6961/222059814-d14e890c-4858-4cfb-acd1-4df010788080.png) - -# Add Incoming WebHooks -![slack-add_webhook](https://user-images.githubusercontent.com/6961/222059973-a7c35933-11a3-4df4-9560-b3cdbf276ebe.png) - -# Configure the webhook - -Take note of the URI. Copy it. You'll need to place it in `ZOOMWH_SLACK_WH_URI` env var to run the application. -![slack_webhook_config](https://user-images.githubusercontent.com/6961/222060029-f3ac52fa-7722-497f-8f29-4b782745c44a.png) - - -# Save the Settings. diff --git a/zoom-notifier/docs/zoom_api_enablement.md b/zoom-notifier/docs/zoom_api_enablement.md deleted file mode 100644 index 860e0ad..0000000 --- a/zoom-notifier/docs/zoom_api_enablement.md +++ /dev/null @@ -1,63 +0,0 @@ -# Zoom API Enablement - -To enable to the Zoom notifier including the link to the meeting including the passcode, you'll need a second application. - -## Go to the Zoom Marketplace - -Visit the [zoom marketplace](https://marketplace.zoom.us/). - -Log in with your credentials. - -## Create zoom application - -![App_Marketplace](https://user-images.githubusercontent.com/6961/222057177-c388df1b-4b49-4555-8867-535c86affe13.png) - - -Choose Server to Server OAuth application. - - -![App_Marketplace-2](https://github.com/user-attachments/assets/dc8fef45-dc6a-4ab1-8c4e-d47e8add0011) - - -Give your application a name, something like "lookup meeting info" would be ideal, so you remember later what it does. - - -## Configuration the application - -![App_Marketplace-3](https://github.com/user-attachments/assets/6746e3ad-dd82-4bc4-8725-aca696c31e4b) - - -Once the application is created, you can see the Account ID, Client ID, and Client Secret. You will need these in your configuration for zoom notifier. - -:camera: Record the required environment variables. - -From there, you need to fill out basic information for the application. - -![App_Marketplace-4](https://github.com/user-attachments/assets/143f2c51-0462-48bb-b797-1529c4ee4ed3) - - -Once you fill out that information, you'll get to the screen with the Tokens. Secret Token is what you need. - -# Scope the Application - -![App_Marketplace-6](https://github.com/user-attachments/assets/2d15490d-39bc-4b16-9ff4-aead45e3c2d3) - - -From there, you'll need to set scopes. The screenshot is probably the easiest way to see what you need. - -- [ ] dashboard:read:list_meeting_participants:admin -- [ ] dashboard:read:list_meetings:admin -- [ ] dashboard:read:list_meeting_participants:master -- [ ] meeting:read:meeting:admin -- [ ] meeting:read:invitation:admin -- [ ] meeting:read:list_meetings:admin -- [ ] report:read:meeting:admin -- [ ] report:read:list_meeting_participants:admin - -:warning: It is possible this is more scope than needed. Some items may have been added during development and not pulled back yet. Feel free to adjust the scopes and if you do, please open a PR to update this document. - -Set scopes, and then "Activate" the application. - -# Setup your configuration file - -From there, you'll need to add the environment variables documented in the primary [README.md](../README.md) file. diff --git a/zoom-notifier/docs/zoom_app_creation.md b/zoom-notifier/docs/zoom_app_creation.md deleted file mode 100644 index f201f5d..0000000 --- a/zoom-notifier/docs/zoom_app_creation.md +++ /dev/null @@ -1,48 +0,0 @@ -# Create a zoom app - -Visit the [zoom marketplace](https://marketplace.zoom.us/). - -## Create zoom application. - - -![App_Marketplace](https://user-images.githubusercontent.com/6961/222057177-c388df1b-4b49-4555-8867-535c86affe13.png) - - -## Choose Webhook Only Application - -![Choose Webhook Only](https://user-images.githubusercontent.com/6961/222057317-d87498ff-13eb-4b77-b1ff-2808e52e9e2a.png) - -## Give your application a name - -![Give it a name](https://user-images.githubusercontent.com/6961/222057676-2af00cab-416f-49f2-8430-78ae8d7f5512.png) - -## Fill out the application information. - -![Fill out](https://user-images.githubusercontent.com/6961/222057800-7c77061e-6f9f-47f4-aba8-07d5d2980aa9.png) - -## Click continue. - - -![Zoom app](https://user-images.githubusercontent.com/6961/222058197-a71f9aad-c673-470b-a8e5-3f642c64ed6a.png) - -## Toggle Event Subscriptions - -![Toggle Event Subscriptions](https://user-images.githubusercontent.com/6961/222058339-63fdf6ee-b00c-4b86-ac12-34af57609ee8.png) - - -## Press + Add Event Subscriptions - - - -You'll need a public endpoint for a listener -- which is where you'll run this app. You'll see a "validate" button. Zoom send validation payload through every so often for authentication and you need to respond to them with. The app has this built in and you just need to provide your `ZOOM_SECRET` (which is `secret token` on the zoom app configuration page). - -![App_Marketplace](https://user-images.githubusercontent.com/6961/222058586-1fdf418a-4255-4325-bb0e-d9adcfc12c2c.png) - -## Press the "Add Events" button. - -![zoom_event_types](https://user-images.githubusercontent.com/6961/222058647-4c058c29-5573-462a-a7f0-607c9e9a26b6.png) - - -Click on validate. If the webhook can't be validated, see the configuration portion of the documentation. - -## Save. diff --git a/zoom-notifier/examples/tests.sh b/zoom-notifier/examples/tests.sh deleted file mode 100644 index 1559806..0000000 --- a/zoom-notifier/examples/tests.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - - - -tt() { - curl -X POST -H "Content-Type: application/json" -d @./zoom/"$1" http://localhost:8889/ -} - -echo "Testing a join" -tt participant_joined.json -sleep 2 - -echo "Testing a leave" -tt participant_left.json -sleep 2 - -echo "Testing a join with a specific topic" -tt participant_joined_goodtopic.json -sleep 2 - -echo "Testing a join with a bad topic" -tt participant_special_meeting.json -sleep 2 diff --git a/zoom-notifier/examples/zoom/endpoint_url_validation.json b/zoom-notifier/examples/zoom/endpoint_url_validation.json deleted file mode 100644 index 854f43d..0000000 --- a/zoom-notifier/examples/zoom/endpoint_url_validation.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "payload": { - "plainToken": "qgg8vlvZRS6UYooatFL8Aw" - }, - "event_ts": 1654503849680, - "event": "endpoint.url_validation" -} \ No newline at end of file diff --git a/zoom-notifier/examples/zoom/invalid_zoom_example.json b/zoom-notifier/examples/zoom/invalid_zoom_example.json deleted file mode 100644 index d9922ec..0000000 --- a/zoom-notifier/examples/zoom/invalid_zoom_example.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "Name": "Sam", - "Age": 31 -} \ No newline at end of file diff --git a/zoom-notifier/examples/zoom/meeting_ended.json b/zoom-notifier/examples/zoom/meeting_ended.json deleted file mode 100644 index c4bfa1d..0000000 --- a/zoom-notifier/examples/zoom/meeting_ended.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "event": "meeting.ended", - "payload": { - "account_id": "uUpLA0YDRhWZvYIu_JxPpg", - "object": { - "duration": 30, - "start_time": "2023-02-24T21:12:33Z", - "timezone": "America/Chicago", - "end_time": "2023-02-24T23:31:25Z", - "topic": "Somebody's Personal Meeting Room", - "id": "6703648745", - "type": 4, - "uuid": "EeSOLAT+QCqGPwFzzX05SA==", - "host_id": "ct4AizHYRw6jGdqh-dUGAw" - } - }, - "event_ts": 1677281485304 -} \ No newline at end of file diff --git a/zoom-notifier/examples/zoom/meeting_started.json b/zoom-notifier/examples/zoom/meeting_started.json deleted file mode 100644 index 495e105..0000000 --- a/zoom-notifier/examples/zoom/meeting_started.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "event": "meeting.started", - "payload": { - "account_id": "uUpLO0YDAhWZvYIu_JxPpg", - "object": { - "duration": 30, - "start_time": "2023-02-24T21:12:33Z", - "timezone": "America/Chicago", - "topic": "Somebody's Personal Meeting Room", - "id": "6704648745", - "type": 4, - "uuid": "EeSOLFT+QCAGPwFzzX05SA==", - "host_id": "ct4IizHARw6jGdqh-dUGAw" - } - }, - "event_ts": 1677273153725 -} \ No newline at end of file diff --git a/zoom-notifier/examples/zoom/participant_joined.json b/zoom-notifier/examples/zoom/participant_joined.json deleted file mode 100644 index b041691..0000000 --- a/zoom-notifier/examples/zoom/participant_joined.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "payload": { - "account_id": "uUpLA0YDRhWZvYIu_JxPpg", - "object": { - "uuid": "EeSOLFT+QCqGPwFzza05SA==", - "participant": { - "user_id": "16784312", - "user_name": "Jonny Bananas", - "id": "", - "join_time": "2023-02-24T21:17:12Z", - "email": "", - "participant_uuid": "uSk4aoZVQkehFzdvMpns9w" - }, - "id": "6735648745", - "type": 4, - "topic": "Somebody's Personal Meeting Room", - "host_id": "ct4IizAYRw6jGdqh-dUGAw", - "duration": 30, - "start_time": "2023-02-24T21:12:33Z", - "timezone": "America/Chicago" - } - }, - "event_ts": 1677273434649, - "event": "meeting.participant_joined" -} \ No newline at end of file diff --git a/zoom-notifier/examples/zoom/participant_joined_goodtopic.json b/zoom-notifier/examples/zoom/participant_joined_goodtopic.json deleted file mode 100644 index b1e70ae..0000000 --- a/zoom-notifier/examples/zoom/participant_joined_goodtopic.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "payload": { - "account_id": "uUpLA0YDRhWZvYIu_JxPpg", - "object": { - "uuid": "EeSOLFT+QCqGPwFzza05SA==", - "participant": { - "user_id": "16784312", - "user_name": "Roger Acme", - "id": "", - "join_time": "2023-02-24T21:17:12Z", - "email": "", - "participant_uuid": "uSk4aoZVQkehFzdvMpns9w" - }, - "id": "6735648745", - "type": 4, - "topic": "Social Fun Time", - "host_id": "ct4IizAYRw6jGdqh-dUGAw", - "duration": 30, - "start_time": "2023-02-24T21:12:33Z", - "timezone": "America/Chicago" - } - }, - "event_ts": 1677273434649, - "event": "meeting.participant_joined" -} diff --git a/zoom-notifier/examples/zoom/participant_left.json b/zoom-notifier/examples/zoom/participant_left.json deleted file mode 100644 index 72ed673..0000000 --- a/zoom-notifier/examples/zoom/participant_left.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "payload": { - "account_id": "uGpLO0YDRhWZvYIu_JxPpg", - "object": { - "uuid": "C6H3TtnBSiyC8Nd48khUig==", - "participant": { - "leave_time": "2023-02-11T21:25:32Z", - "user_id": "16678240", - "user_name": "Tim Apple", - "registrant_id": "", - "participant_user_id": "ct4IizGYRw6jGdqh-dUGAw", - "id": "ct4IizHYRw6jGdqh-dUGAw", - "leave_reason": "left the meeting. Reason : left the meeting", - "email": "user@example.com", - "participant_uuid": "9oiwMpbBQGSKQlWOY_c9kA" - }, - "id": "6705648745", - "type": 4, - "topic": "A person's Personal Meeting Room", - "host_id": "ct4IidHYRw6jGdqh-dUGAw", - "duration": 30, - "start_time": "2023-02-11T21:24:49Z", - "timezone": "America/Chicago" - } - }, - "event_ts": 1676150733956, - "event": "meeting.participant_left" -} \ No newline at end of file diff --git a/zoom-notifier/examples/zoom/participant_special_meeting.json b/zoom-notifier/examples/zoom/participant_special_meeting.json deleted file mode 100644 index a987c51..0000000 --- a/zoom-notifier/examples/zoom/participant_special_meeting.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "payload": { - "account_id": "uUpLA0YDRhWZvYIu_JxPpg", - "object": { - "uuid": "EeSOLFT+QCqGPwFzza05SA==", - "participant": { - "user_id": "16784312", - "user_name": "Special Guest", - "id": "", - "join_time": "2023-02-24T21:17:12Z", - "email": "", - "participant_uuid": "uSk4aoZVQkehFzdvMpns9w" - }, - "id": "6735648745", - "type": 4, - "topic": "Extra Special Meeting", - "host_id": "ct4IizAYRw6jGdqh-dUGAw", - "duration": 30, - "start_time": "2023-02-24T21:12:33Z", - "timezone": "America/Chicago" - } - }, - "event_ts": 1677273434649, - "event": "meeting.participant_joined" -} diff --git a/zoom-notifier/go.mod b/zoom-notifier/go.mod deleted file mode 100644 index 5ff129c..0000000 --- a/zoom-notifier/go.mod +++ /dev/null @@ -1,52 +0,0 @@ -module github.com/stahnma/mandatoryFun/zoom-notifier - -go 1.25.5 - -require ( - github.com/gin-gonic/gin v1.9.1 - github.com/sirupsen/logrus v1.9.3 - github.com/spf13/viper v1.18.2 - github.com/thoj/go-ircevent v0.0.0-20210723090443-73e444401d64 -) - -require ( - github.com/bytedance/sonic v1.9.1 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect - github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.14.0 // indirect - github.com/goccy/go-json v0.10.2 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.4 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.6.0 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.9.0 // indirect - golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/text v0.31.0 // indirect - google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/zoom-notifier/irc.go b/zoom-notifier/irc.go deleted file mode 100644 index 58c026e..0000000 --- a/zoom-notifier/irc.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "crypto/tls" - "time" - - log "github.com/sirupsen/logrus" - "github.com/spf13/viper" - "github.com/thoj/go-ircevent" -) - -func sendIRC(message string) { - log.Debugln("(sendIRC) Sending IRC notification", message) - ircEnable := viper.GetString("irc_enable") - var ircServer, ircChannel, ircNick, ircPassword string - if ircEnable == "true" { - ircServer = viper.GetString("irc_server") - ircChannel = viper.GetString("irc_channel") - ircNick = viper.GetString("irc_nick") - ircPassword = viper.GetString("irc_pass") - } - - irccon := irc.IRC(ircNick, ircNick) - irccon.UseTLS = true - irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true} - irccon.Password = ircPassword - irccon.AddCallback("001", func(e *irc.Event) { - irccon.Join(ircChannel) - }) - - err := irccon.Connect(ircServer) - if err != nil { - log.Errorln("Error connecting to IRC server:", err) - } - defer irccon.Quit() - - irccon.Privmsg(ircChannel, message) - - time.AfterFunc(1*time.Second, func() { - irccon.Quit() - }) - irccon.Loop() -} diff --git a/zoom-notifier/main.go b/zoom-notifier/main.go deleted file mode 100644 index 4eb4363..0000000 --- a/zoom-notifier/main.go +++ /dev/null @@ -1,381 +0,0 @@ -package main - -import ( - "crypto/hmac" - "crypto/sha256" - "encoding/hex" - "flag" - "fmt" - "io" - "net/http" - "os" - "path/filepath" - "strings" - "time" - - "github.com/gin-gonic/gin" - log "github.com/sirupsen/logrus" - "github.com/spf13/viper" -) - -var ( - version = "dev" // Default to "dev" if not set - commit = "none" // Default to "none" if not set - buildDate = "unknown" -) - -type ZoomWebhook struct { - Payload struct { - PlainToken string `json:"plainToken"` - AccountID string `json:"account_id"` - Object struct { - UUID string `json:"uuid"` - Participant struct { - LeaveTime time.Time `json:"leave_time"` - JoinTime time.Time `json:"join_time"` - UserID string `json:"user_id"` - UserName string `json:"user_name"` - RegistrantID string `json:"registrant_id"` - ParticipantUserID string `json:"participant_user_id"` - ID string `json:"id"` - LeaveReason string `json:"leave_reason"` - Email string `json:"email"` - ParticipantUUID string `json:"participant_uuid"` - } `json:"participant"` - ID string `json:"id"` - Type int `json:"type"` - Topic string `json:"topic"` - HostID string `json:"host_id"` - Duration int `json:"duration"` - StartTime time.Time `json:"start_time"` - Timezone string `json:"timezone"` - } `json:"object"` - } `json:"payload"` - EventTs int64 `json:"event_ts"` - Event string `json:"event"` -} - -type ChallengeResponse struct { - PlainToken string `json:"plainToken"` - EncryptedToken string `json:"encryptedToken"` -} - -func zoomCrcValidation(jresp ZoomWebhook) (bool, ChallengeResponse) { - log.Debugln("(zoomCrcValidation)") - zoom_secret := viper.GetString("zoom_secret") - var crc ChallengeResponse - if jresp.Event == "endpoint.url_validation" { - log.Debugln("(zoomCrcValidation) Performing CRC verification.") - crc.PlainToken = jresp.Payload.PlainToken - data := jresp.Payload.PlainToken - // Create a new HMAC by defining the hash type and the key (as byte array) - h := hmac.New(sha256.New, []byte(zoom_secret)) - h.Write([]byte(data)) - // Get result and encode as hexadecimal string - crc.EncryptedToken = hex.EncodeToString(h.Sum(nil)) - log.Infoln("CRC Validation: ", crc) - return true, crc - } else { - log.Debugln("(zoomCrcValidation) Not a CRC validation request.") - return false, crc - } - -} - -func filterMeeting(jresp ZoomWebhook) bool { - // If the meeting is outside the topic scope, just ignore. - name := viper.GetString("meeting_name") - log.Debugln("(applyMeetingFilters) Topic " + jresp.Payload.Object.Topic) - if name != jresp.Payload.Object.Topic && name != "" { - log.Infoln("Received hook but dropping due to topic being filtered.") - log.Debugln("(applyMeetingFilter) Hook had topic '" + jresp.Payload.Object.Topic + "'") - log.Debugln("(applyMeetingFtiler)Filter only allows for " + name) - return true - } - return false -} - -func setMessageSuffix(jresp ZoomWebhook) string { - msg_suffix := viper.GetString("msg_suffix") - msg := "" - switch jresp.Event { - case "meeting.participant_left": - msg = jresp.Payload.Object.Participant.UserName + " has left " + msg_suffix - case "meeting.participant_joined": - msg = jresp.Payload.Object.Participant.UserName + " has joined " + msg_suffix - default: - return msg - } - return msg -} - -// savePostRequestToFile writes the payload of a POST request to a file in the "hooks" directory. -// -// It operates silently, meaning it does not modify the HTTP response or interfere with request processing. -// If the request is not a POST, has no body, or if an error occurs while reading or writing the body, -// the function simply returns without taking any action. -// -// The request payload is saved in a file named "hook-.json", where follows the -// "YYYYMMDD-HHMMSS.mmm" format to ensure unique filenames. The directory "hooks" is created if it does not exist. -// -// Parameters: -// - c: A *gin.Context representing the HTTP request and response context. -// -// Usage: -// -// This function can be used as middleware in a Gin application to capture incoming POST request payloads. -// -// Example: -// -// r := gin.Default() -// r.Use(savePostRequestToFile) // Logs POST request bodies to the "hooks" directory -// r.POST("/hook", func(c *gin.Context) { -// c.JSON(200, gin.H{"message": "Hook received"}) -// }) -// r.Run(":8080") -func savePostRequestToFile(c *gin.Context) { - if c.Request.Method != "POST" { - return // Only process POST requests - } - - // Read the request body - body, err := io.ReadAll(c.Request.Body) - if err != nil || len(body) == 0 { - return // If there's an error reading or body is empty, do nothing - } - - // Ensure the hooks directory exists - hooksDir := "hooks" - _ = os.MkdirAll(hooksDir, 0755) - - // Generate a timestamp-based filename - timestamp := time.Now().Format("20060102-150405.000") - filename := filepath.Join(hooksDir, "hook-"+timestamp+".json") - - // Write the body to a file (ignoring errors to avoid interfering with request processing) - _ = os.WriteFile(filename, body, 0644) -} - -func processWebHook(c *gin.Context) { - - log.Debugln("(processWebHook) Processing incoming webhook. at " + time.Now().String()) - if gin.IsDebugging() { - // savePostRequestToFile(c) - } - var jresp ZoomWebhook - if err := c.BindJSON(&jresp); err != nil { - log.Errorln("Error processing incoming webhook JSON", err) - } - - // Handle Zoom Webhook CRC validation - if jresp.Event == "endpoint.url_validation" { - crcvalid, crc := zoomCrcValidation(jresp) - if crcvalid { - log.Debugln("(processWebHook) CRC validation successful. Returning CRC response.") - c.JSON(http.StatusOK, crc) - return - } else { - log.Errorln("(processWebHook) CRC validation failed. Returning 400.") - c.JSON(http.StatusBadRequest, gin.H{"error": "CRC validation failed"}) - return - } - } - if filterMeeting(jresp) { - return - } - - // If the event type is not a participant join or leave, ignore it. - if jresp.Event != "meeting.participant_joined" && jresp.Event != "meeting.participant_left" { - log.Debugln("(processWebHook) Ignoring event type: " + jresp.Event) - return - } - - msg := setMessageSuffix(jresp) - log.Debugln("About to dispatch Message: " + msg) - dispatchMessage(msg, jresp) -} - -func getJoinURL(meetingId string) string { - /* - To enable this feature, you must set the following environment variables: - ZOOM_API_ENABLE=1 - ZOOM_API_CLIENT_ID - ZOOM_API_CLIENT_SECRET - ZOOM_API_ACCOUNT_ID - */ - joinurl := "" - if os.Getenv("ZOOM_API_ENABLE") == "1" { - // Get secret from the zoom API so we can get the meeting details - // Check to see that ZOOM_API_CLIENT_ID, ZOOM_API_CLIENT_SECRET, and ZOOM_API_ACCOUNT_ID are set - if os.Getenv("ZOOM_API_CLIENT_ID") != "" && os.Getenv("ZOOM_API_CLIENT_SECRET") != "" && os.Getenv("ZOOM_API_ACCOUNT_ID") != "" { - log.Debugln(meetingId) - joinurl := callZoomAPI(meetingId) - log.Debugln("Join URL: " + joinurl) - } else { - // This should be unreachable code, but it's there for debugging and defense. - log.Errorln("ZOOM_API environment credentials are not set. Skipping.") - } - } - return joinurl -} - -func dispatchMessage(msg string, jresp ZoomWebhook) { - - slack_enable := viper.GetString("slack_enable") - irc_enable := viper.GetString("irc_enable") - log.Debugln("(dispatchMessage) Slack enabled: " + slack_enable) - log.Debugln("(dispatchMessage) IRC enabled: " + irc_enable) - sent := 0 - - if strings.ToLower(slack_enable) == "true" { - log.Debugln("(dispatchMessage) Sending a slack message") - parseAndSplitSlackHooks(msg, jresp) - sent = 1 - } - if strings.ToLower(irc_enable) == "true" { - log.Debugln("(dispatchMessage) Sending an IRC message") - sendIRC(msg) - sent = 1 - } - if sent == 0 { - log.Fatal("You have no dispatchers configured (irc or slack). Quitting.") - } -} - -func inititalize() { - log.SetOutput(os.Stdout) - log.SetLevel(log.DebugLevel) - - viper.SetConfigType("env") - - viper.SetDefault("port", "8888") - viper.SetDefault("slack_enable", "true") - viper.SetDefault("irc_enable", "false") - viper.SetDefault("msg_suffix", "the zoom meeting.") - viper.SetDefault("zoom_api_enable", "false") - - viper.BindEnv("port", "ZOOMWH_PORT") - viper.BindEnv("slack_enable", "ZOOMWH_SLACK_ENABLE") - - bugout := false - if value := os.Getenv("ZOOM_SECRET"); value == "" { - bugout = true - log.Errorln("You must set ZOOM_SECRET environment variable.") - } else { - viper.BindEnv("zoom_secret", "ZOOM_SECRET") - } - - // Zoom API Specifics - viper.BindEnv("ZOOM_API_ENABLE") - zoomApiEnabled := viper.GetBool("ZOOM_API_ENABLE") - if zoomApiEnabled == false { - log.Infoln("Zoom Web API is disabled. Disabling active meeting links and quieries") - viper.Set("zoom_api_enable", "false") - } else { - log.Infoln("Zoom Web API is enabled.") - viper.MustBindEnv("zoom_api_client_id", "ZOOM_API_CLIENT_ID") - zoom_api_client_id := viper.GetString("zoom_api_client_id") - if zoom_api_client_id == "" { - log.Errorln("You must set ZOOM_API_CLIENT_ID environment variable if ZOOM_API_ENABLE=true.") - bugout = true - } - viper.MustBindEnv("zoom_api_client_secret", "ZOOM_API_CLIENT_SECRET") - zoom_api_client_secret := viper.GetString("zoom_api_client_secret") - if zoom_api_client_secret == "" { - log.Errorln("You must set ZOOM_API_CLIENT_SECRET environment variable if ZOOM_API_ENABLE=true.") - bugout = true - } - viper.MustBindEnv("zoom_api_account_id", "ZOOM_API_ACCOUNT_ID") - zoom_api_account_id := viper.GetString("zoom_api_account_id") - if zoom_api_account_id == "" { - log.Errorln("You must set ZOOM_API_ACCOUNT_ID environment variable if ZOOM_API_ENABLE=true.") - bugout = true - } - } - - // Slack Specifics - viper.GetString("slack_enable") - if value := os.Getenv("ZOOMWH_SLACK_ENABLE"); value == "false" { - log.Infoln("Slack is notification disabled.") - viper.Set("slack_enable", "false") - } else { - viper.MustBindEnv("slack_webhook_uri", "ZOOMWH_SLACK_WH_URI") - slack_webhook_uri := viper.GetString("slack_webhook_uri") - if slack_webhook_uri == "" { - log.Errorln("You must set ZOOMWH_SLACK_WH_URI environment variable unless ZOOMWH_SLACK_ENABLE=false.") - bugout = true - } - } - - // Filter Specifics - if value := os.Getenv("ZOOMWH_MEETING_NAME"); value == "" { - viper.BindEnv("meeting_filter", "ZOOMWH_MEETING_NAME") - } - - // IRC Specifics - value, ok := os.LookupEnv("ZOOMWH_IRC_ENABLE") - if value == "false" || !ok { - log.Infoln("IRC notifications are disabled.") - viper.Set("irc_enable", "false") - } else { - log.Infoln("IRC notifications are enabled.") - viper.Set("irc_enable", "true") - // Four IRC variables are required if IRC is enabled - if value := os.Getenv("ZOOMWH_IRC_SERVER"); value == "" { - log.Errorln("You must set ZOOMWH_IRC_SERVER environment variable if ZOOMWH_IRC_ENABLE is true.") - bugout = true - } else { - viper.MustBindEnv("irc_server", "ZOOMWH_IRC_SERVER") - } - if value := os.Getenv("ZOOMWH_IRC_CHANNEL"); value == "" { - log.Errorln("You must set ZOOMWH_IRC_CHANNEL environment variable if ZOOMWH_IRC_ENABLE is true.") - bugout = true - } else { - viper.MustBindEnv("irc_channel", "ZOOMWH_IRC_CHANNEL") - } - if value := os.Getenv("ZOOMWH_IRC_NICK"); value == "" { - log.Errorln("You must set ZOOMWH_IRC_NICK environment variable if ZOOMWH_IRC_ENABLE is true.") - bugout = true - } else { - viper.MustBindEnv("irc_nick", "ZOOMWH_IRC_NICK") - } - if value := os.Getenv("ZOOMWH_IRC_PASS"); value == "" { - log.Errorln("You must set ZOOMWH_IRC_PASS environment variable if ZOOMWH_IRC_ENABLE is true.") - bugout = true - } else { - viper.MustBindEnv("irc_pass", "ZOOMWH_IRC_PASS") - } - } - - // viper dump - fmt.Println(viper.AllSettings()) - - viper.MustBindEnv("zoom_secret", "ZOOM_SECRET") - if os.Getenv("ZOOMWH_MSG_SUFFIX") != "" { - viper.BindEnv("msg_suffix", "ZOOMWH_MSG_SUFFIX") - } - - if bugout == true { - os.Exit(1) - } -} - -func main() { - - showVersion := flag.Bool("version", false, "Show version information") - flag.Parse() - - if *showVersion { - fmt.Printf("Version: %s\nCommit: %s\nBuild Date: %s\n", version, commit, buildDate) - return - } - - inititalize() - - router := gin.Default() - router.POST("/", processWebHook) - port := viper.GetString("port") - serverstring := "localhost:" + port - log.Infoln("Listening on " + serverstring) - router.Run(serverstring) -} diff --git a/zoom-notifier/openapi.yml b/zoom-notifier/openapi.yml deleted file mode 100644 index bee1ac7..0000000 --- a/zoom-notifier/openapi.yml +++ /dev/null @@ -1,73 +0,0 @@ -openapi: 3.0.0 -info: - title: Zoom Notifier API - description: API for managing Zoom meeting notifications - version: 1.0.0 - -servers: - - url: http://localhost:8080 - -paths: - /notify: - post: - summary: Send a Zoom meeting notification - description: Sends a notification about an upcoming Zoom meeting - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/NotificationRequest' - responses: - '200': - description: Notification sent successfully - '400': - description: Bad request - '500': - description: Internal server error - - /health: - get: - summary: Check API health - description: Returns the health status of the API - responses: - '200': - description: API is healthy - content: - application/json: - schema: - $ref: '#/components/schemas/HealthResponse' - -components: - schemas: - NotificationRequest: - type: object - required: - - topic - - start_time - - duration - - join_url - properties: - topic: - type: string - description: The topic or name of the Zoom meeting - start_time: - type: string - format: date-time - description: The start time of the meeting in ISO 8601 format - duration: - type: integer - description: The duration of the meeting in minutes - join_url: - type: string - format: uri - description: The URL to join the Zoom meeting - - HealthResponse: - type: object - properties: - status: - type: string - enum: [healthy, unhealthy] - version: - type: string diff --git a/zoom-notifier/slack.go b/zoom-notifier/slack.go deleted file mode 100644 index 2cc87dc..0000000 --- a/zoom-notifier/slack.go +++ /dev/null @@ -1,92 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "net/http" - "strings" - - log "github.com/sirupsen/logrus" - "github.com/spf13/viper" -) - -func parseAndSplitSlackHooks(msg string, jresp ZoomWebhook) { - log.Debugln("(parseAndSplitSlackHooks) Parsing and splitting slack hooks.") - log.Debugln("(parseAndSplitSlackHooks) The message is:", msg) - log.Debugln("(parseAndSplitSlackHooks) The meeting ID is:", jresp.Payload.Object.ID) - slackHooks := viper.GetString("slack_webhook_uri") - - splitStrings := strings.Split(slackHooks, ",") - for i, s := range splitStrings { - splitStrings[i] = strings.ReplaceAll(s, "'", "") - splitStrings[i] = strings.ReplaceAll(s, "\"", "") - } - size := len(splitStrings) - log.Debugln("(parseAndSplitSlackHooks) Found", size, "slack hooks.") - msg = formatSlackMessage(msg, jresp) - for _, entry := range splitStrings { - postToSlack(msg, entry) - } -} - -func formatSlackMessage(msg string, jresp ZoomWebhook) string { - // Read in suffix, and make that the hot link - log.Debugln("XXXXXXX") - log.Debugln("(formatSlackMessage) msg is ", msg) - // return if ZOOM_API_ENABLE is not enabled - zae := viper.GetBool("zoom_api_enable") - log.Debugln("(formatSlackMessage) ZOOM_API_ENABLE zae is:", zae) - if !zae { - return msg - } - joinurl := callZoomAPI(jresp.Payload.Object.ID) - log.Debugln("(formatSlackMessage) The join URL is:", joinurl) - - msg_suffix := viper.GetString("msg_suffix") - log.Debugln("(formatSlackMessage) The suffix is:", msg_suffix) - msg_suffix = "<" + joinurl + "|" + msg_suffix + ">" - - msg = msg + " " + msg_suffix - log.Debugln("(formatSlackMessage) The message is:", msg) - - switch jresp.Event { - case "meeting.participant_left": - msg = jresp.Payload.Object.Participant.UserName + " has left " + msg_suffix - case "meeting.participant_joined": - msg = jresp.Payload.Object.Participant.UserName + " has joined " + msg_suffix - default: - msg = msg - - } - return msg -} - -func postToSlack(msg string, uri string) { - log.Debugln("(postToSlack) The message is:", msg) - log.Debugln("(postToSlack) The slack webhook uri is:", uri) - - // Constructing payload with Markdown and unfurl prevention - payload := map[string]interface{}{ - "text": msg, - "mrkdwn": true, - "unfurl_links": false, - "unfurl_media": false, - } - - // Convert to JSON - payloadBytes, err := json.Marshal(payload) - if err != nil { - log.Println("Error marshaling JSON:", err) - return - } - - // Make HTTP POST request - resp, err := http.Post(uri, "application/json", bytes.NewBuffer(payloadBytes)) - if err != nil { - log.Println("Error posting to Slack:", err) - return - } - defer resp.Body.Close() - - log.Debugln("(postToSlack) Response Status:", resp.Status) -} diff --git a/zoom-notifier/zoom_api.go b/zoom-notifier/zoom_api.go deleted file mode 100644 index e4da68e..0000000 --- a/zoom-notifier/zoom_api.go +++ /dev/null @@ -1,132 +0,0 @@ -package main - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - "net/url" - "os" - "strings" - - log "github.com/sirupsen/logrus" - // "github.com/spf13/viper" -) - -// Zoom API token URL -const zoomTokenURL = "https://zoom.us/oauth/token" - -type ZoomMeetingResponse struct { - JoinURL string `json:"join_url"` -} - -type ZoomTokenResponse struct { - AccessToken string `json:"access_token"` - TokenType string `json:"token_type"` - ExpiresIn int `json:"expires_in"` -} - -// Function to get a Zoom API access token -func GetZoomAccessToken() (string, error) { - - //TODO read these from Viper not os - clientID := os.Getenv("ZOOM_API_CLIENT_ID") - clientSecret := os.Getenv("ZOOM_API_CLIENT_SECRET") - accountID := os.Getenv("ZOOM_API_ACCOUNT_ID") - - log.Debugln("(GetZoomAccessToken)") - log.Debugln("clientID: ", clientID) - log.Debugln("clientSecret: ", clientSecret) - log.Debugln("accountID: ", accountID) - - // Encode client credentials in Base64 - authString := fmt.Sprintf("%s:%s", clientID, clientSecret) - authEncoded := base64.StdEncoding.EncodeToString([]byte(authString)) - - // Prepare request body - data := url.Values{} - data.Set("grant_type", "account_credentials") - data.Set("account_id", accountID) - - // Create request - req, err := http.NewRequest("POST", zoomTokenURL, strings.NewReader(data.Encode())) - if err != nil { - return "", fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Authorization", "Basic "+authEncoded) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - // Send request - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return "", fmt.Errorf("failed to make API call: %w", err) - } - defer resp.Body.Close() - - // Check for non-200 response - if resp.StatusCode != http.StatusOK { - body, _ := ioutil.ReadAll(resp.Body) - return "", fmt.Errorf("API error: %s", body) - } - - // Parse JSON response - var tokenResponse ZoomTokenResponse - if err := json.NewDecoder(resp.Body).Decode(&tokenResponse); err != nil { - return "", fmt.Errorf("failed to parse JSON response: %w", err) - } - - return tokenResponse.AccessToken, nil -} - -func getMeetingJoinLink(meetingID string, token string) (string, error) { - log.Debugln("(getMeetingJoinLink) Getting meeting join link") - url := fmt.Sprintf("https://api.zoom.us/v2/meetings/%s", meetingID) - - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return "", fmt.Errorf("failed to create request: %w", err) - } - - req.Header.Set("Authorization", "Bearer "+token) - req.Header.Set("Content-Type", "application/json") - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return "", fmt.Errorf("failed to send request: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("unexpected status code: %d", resp.StatusCode) - } - - var zoomResponse ZoomMeetingResponse - if err := json.NewDecoder(resp.Body).Decode(&zoomResponse); err != nil { - return "", fmt.Errorf("failed to decode response: %w", err) - } - return zoomResponse.JoinURL, nil -} - -func callZoomAPI(meeting_id string) (joinuri string) { - log.Debugln("(callZoomApi) Calling GetZoomAccessToken") - log.Debugln("meeting_id: ", meeting_id) - - // Fetch access token - token, err := GetZoomAccessToken() - - if err != nil { - fmt.Printf("Error getting access token: %v\n", err) - return - } - - // This is really long, should probably only show in trace mode - // log.Debugln("(callZoomAPI) Zoom Access Token: %s\n", token) - joinuri, err = getMeetingJoinLink(meeting_id, token) - log.Debugln("(callZoomAPI) joinuri: ", joinuri) - - return joinuri -}