Skip to content

Commit

Permalink
Merge pull request #292 from Decatur-Robotics/fix-slack-notifications
Browse files Browse the repository at this point in the history
Fix Slack notifications
  • Loading branch information
Tr01ler authored Dec 3, 2024
2 parents 623ab5b + 3ec586a commit 4a78fb1
Show file tree
Hide file tree
Showing 20 changed files with 257 additions and 201 deletions.
64 changes: 53 additions & 11 deletions components/AddToSlack.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,58 @@
import { useRouter } from "next/router";
import { useState } from "react";
import Modal, { hideModal, showModal } from "./Modal";
import ClientApi from "@/lib/api/ClientApi";

export default function AddToSlack() {
const host = window ? window.location.host : "4026.org";
const SLACK_WEBHOOK_INSTALL_URL = "https://my.slack.com/services/new/incoming-webhook/";

const api = new ClientApi();

function AddToSlackModal({ teamId }: { teamId: string }) {
const [webhookUrl, setWebhookUrl] = useState<string>();
const [errorMsg, setErrorMsg] = useState<string>("");

function save() {
if (!webhookUrl) {
setErrorMsg("Webhook URL is required");
return;
}

api.setSlackWebhook(teamId, webhookUrl)
.catch((err) => setErrorMsg(err.message))
.then(() => hideModal("add-to-slack"));
}

return (
<a className="link text-accent" href={`
https://slack.com/oauth/v2/authorize
?client_id=${process.env.NEXT_PUBLIC_SLACK_CLIENT_ID}
&scope=chat:write,commands&user_scope=email,openid
&redirect_uri=https://${host}/api/addSlackBot
`}>
Add Gearbox to Slack
</a>
<Modal id="add-to-slack" showCloseButton={false} className="flex flex-col gap-4">
<h2 className="text-xl">Add Gearbox to Slack</h2>
<div>
Install the Incoming Webhooks app here:{" "}
<a href={SLACK_WEBHOOK_INSTALL_URL} target="_blank" className="link link-accent">{SLACK_WEBHOOK_INSTALL_URL}</a>
</div>
<div>
Enter the Webhook URL here:
<input
value={webhookUrl}
onChange={(e) => setWebhookUrl(e.target.value)}
type="url"
placeholder="Webhook URL"
className="input input-bordered ml-2"
/>
</div>
<div className="flex gap-4 w-full justify-end">
<button className="btn btn-primary" onClick={save}>Save</button>
<button className="btn" onClick={() => hideModal("add-to-slack")}>Cancel</button>
</div>
{ errorMsg && <div className="text-red-500">{errorMsg}</div> }
</Modal>
);
}

export default function AddToSlack({ edit, teamId }: { edit: boolean, teamId: string }) {
return (<>
<button className="btn btn-accent btn-sm" onClick={() => showModal("add-to-slack")}>
{ edit ? "Change Slack Webhook" : "Add Gearbox to Slack" }
</button>
<AddToSlackModal teamId={teamId} />
</>
);
}
33 changes: 33 additions & 0 deletions components/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React from "react";

export default function Modal({ id, children, className, showCloseButton }: {
id: string,
children: React.ReactNode,
className?: string,
showCloseButton?: boolean
}) {
return <dialog id={id} className="modal w-screen h-screen">
<div className={`modal-box ${className}`}>
{children}
{ (showCloseButton === undefined || showCloseButton) &&
<div className="modal-action">
<form method="dialog">
{/* if there is a button in form, it will close the modal */}
<button className="btn">Close</button>
</form>
</div>
}
</div>
<form method="dialog" className="modal-backdrop">
<button>Close</button>
</form>
</dialog>
}

export function showModal(id: string) {
(document.getElementById(id) as HTMLDialogElement | undefined)?.showModal();
}

export function hideModal(id: string) {
(document.getElementById(id) as HTMLDialogElement | undefined)?.close();
}
6 changes: 3 additions & 3 deletions components/competition/CompetitionIndex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -516,9 +516,9 @@ export default function CompetitionIndex(props: {
api.setCompPublicData(comp?._id, e.target.checked);
}

function remindUserOnSlack(slackId: string) {
if (slackId && session?.user?.slackId && team?._id && isManager && confirm("Remind scouter on Slack?"))
api.remindSlack(team._id.toString(), slackId);
function remindUserOnSlack(userId: string) {
if (userId && team?._id && isManager && confirm("Remind scouter on Slack?"))
api.remindSlack(team._id.toString(), userId);
}

function addTeam() {
Expand Down
7 changes: 3 additions & 4 deletions components/competition/MatchScheduleCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,7 @@ export default function MatchScheduleCard(props: {
<div className="w-full flex flex-row items-center space-x-2">
{match.reports.map((reportId) => {
const report = reportsById[reportId];
//@ts-ignore
const user = usersById[report?.user];
const user = usersById[report?.user!];

return (
<div
Expand All @@ -194,7 +193,7 @@ export default function MatchScheduleCard(props: {
imgHeightOverride="h-12"
showLevel={false}
borderThickness={2}
onClick={() => remindUserOnSlack(user._id)}
onClick={() => remindUserOnSlack(user._id!)}
/>
: <div className="w-12 h-12"></div>
}
Expand All @@ -215,7 +214,7 @@ export default function MatchScheduleCard(props: {
<div className="tooltip" data-tip="Scouting in progress"><Loading size={24}/></div>}
{ isManager && usersById[match.subjectiveScouter ?? ""]?.slackId
? <button className="text-primary hover:underline mb-1"
onClick={() => remindUserOnSlack(usersById[match.subjectiveScouter ?? ""]?.slackId)}>
onClick={() => remindUserOnSlack(usersById[match.subjectiveScouter ?? ""]?._id!)}>
Subjective Scouter: {usersById[match.subjectiveScouter].name}
</button>
: <div>Subjective Scouter: {usersById[match.subjectiveScouter ?? ""].name}</div>
Expand Down
5 changes: 0 additions & 5 deletions enviroment.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,8 @@ declare global {
IMAGE_UPLOAD_DIR: string;
FILL_TEAMS: string;

SLACK_BOT_TOKEN: string;
NEXT_PUBLIC_SLACK_CLIENT_ID: string;
SLACK_CLIENT_SECRET: string;
SLACK_APP_TOKEN: string;
SLACK_SIGNING_SECRET: string;
SLACK_STATE_SECRET: string;
SLACK_PORT: string;

NEXT_PUBLIC_GOOGLE_ANALYTICS_ID: string;

Expand Down
5 changes: 0 additions & 5 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { createServer } from "https";
import { parse } from "url";
import next from "next";
import fs, { readFileSync } from "fs";
import { App } from "@slack/bolt";
import SlackCommands from "./lib/SlackCommands";
import { IncomingMessage, ServerResponse, request } from "http";
import { startSlackApp } from "./lib/Slack";

console.log("Starting server...");

Expand All @@ -24,8 +21,6 @@ const httpsOptions = {

console.log("HTTPS options set");

// startSlackApp();

console.log("App preparing...");
app.prepare().then(() => {
console.log("App prepared. Creating server...");
Expand Down
70 changes: 0 additions & 70 deletions lib/Slack.ts

This file was deleted.

12 changes: 12 additions & 0 deletions lib/SlackClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export interface SlackInterface {
sendMsg(webhookUrl: string, msg: string): Promise<any>;
}

export default class SlackClient implements SlackInterface {
sendMsg(webhookUrl: string, msg: string) {
return fetch(webhookUrl, {
method: "POST",
body: JSON.stringify({ text: msg }),
})
}
}
58 changes: 0 additions & 58 deletions lib/SlackCommands.ts

This file was deleted.

23 changes: 19 additions & 4 deletions lib/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,11 @@ export class Team {

seasons: string[];

slackChannel: string | undefined;
/**
* ID of the WebhookHolder object
* @see WebhookHolder
*/
slackWebhook: string | undefined;

constructor(
name: string,
Expand All @@ -104,7 +108,6 @@ export class Team {
subjectiveScouters: string[] = [],
requests: string[] = [],
seasons: string[] = [],
slackChannel: string | undefined = undefined
) {
this._id = new ObjectId();
this.name = name;
Expand All @@ -118,7 +121,6 @@ export class Team {
this.subjectiveScouters = subjectiveScouters;
this.seasons = seasons;
this.requests = requests;
this.slackChannel = slackChannel;
}
}

Expand Down Expand Up @@ -561,4 +563,17 @@ export type DbPicklist = {
*/
export type OmitCallSignature<T> =
{ [K in keyof T]: T[K] } &
(T extends new (...args: infer R) => infer S ? new (...args: R) => S : unknown)
(T extends new (...args: infer R) => infer S ? new (...args: R) => S : unknown)

/**
* DO NOT GIVE TO CLIENTS!
*/
export class WebhookHolder {
_id: ObjectId;
url: string;

constructor(url: string) {
this._id = new ObjectId();
this.url = url;
}
}
5 changes: 5 additions & 0 deletions lib/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { removeWhitespaceAndMakeLowerCase } from "./client/ClientUtils";
import CollectionId from "./client/CollectionId";
import DbInterface from "./client/dbinterfaces/DbInterface";
import { Redirect } from "next";
import { User } from "./Types";

/**
* Generates a SLUG from a supplied name- ensures it is unique
Expand Down Expand Up @@ -52,4 +53,8 @@ export function createRedirect(destination: string, query: Record<string, any> =

export function isDeveloper(email: string | undefined) {
return (JSON.parse(process.env.DEVELOPER_EMAILS) as string[]).includes(email ?? "");
}

export function mentionUserInSlack(user: User) {
return user?.slackId ? `<@${user!.slackId}>` : user!.name;
}
Loading

0 comments on commit 4a78fb1

Please sign in to comment.