Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Slack profile fields #5

Merged
merged 2 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion secrets/consts.example.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
// The ID of the google sheet to log hours data to
export const hours_sheet_id = 'googlesheetid'

// The ID of a slack user to show admin settings to
export const slack_admin_id = "USLACKUSER"
// The ID of a slack channel to celebrate new certs in
export const slack_celebration_channel = "CSLACKCHANNEL"
// The ID of a slack channel that is restricted to managers, for use with /voidhours
export const slack_voider_channel = 'CSLACKCHANNEL'

export const slack_department_profile_field_id = 'XPROFILEFIELD'
export const slack_certs_profile_field_id = 'XPROFILEFIELD'



// The API endpoint of a CLUCK server
export const cluck_baseurl = 'http://localhost:4000'
// The Souper Secret CLUCK Api Key
export const cluck_api_id = 'supertestingkey'
export const cluck_api_key = 'cabbage'
export const cluck_api_key = 'cabbage'

1 change: 1 addition & 0 deletions secrets/slack_secrets.example.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
export const signing_secret = 'signing secret';
export const token = 'xoxb-token';
export const app_token = 'xapp-token'
export const user_token = 'xoxp-token'
14 changes: 6 additions & 8 deletions src/handlers/graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import { getMembers } from "../utils/drive";


export async function handleGraphCommand({ command, ack, respond, client }: SlackCommandMiddlewareArgs & AllMiddlewareArgs) {
await ack({ response_type: 'ephemeral', text: 'Generating...' })
await ack({ response_type: 'ephemeral', text: 'Generating graph...' })

const args = command.text.split(" ").filter(x => x.trim() != '')
let users: string[] = []
if (args.length == 0) {
const user = await client.users.info({ user: command.user_id })
users = [user.user!.real_name!]
} else if (args[0] == 'all') {
const user = await client.users.info({ user: command.user_id })
const users: string[] = [user.user!.real_name!]
if (args.length > 0 && args[0] == 'all') {
await respond({ text: "Hours graph", blocks: getGraphBlocks("https://docs.google.com/spreadsheets/d/e/2PACX-1vSHrWf9EtoNjuaGFuBy0IsnMQ5zDS1YLWCDwwyb0df0bjAf-13Nqt3z8bt7b3YA1_NhfHn6J2TjyLyl/pubchart?oid=1533918925&format=image", command.user_id, ["all"]), response_type: 'in_channel' })
return;
} else {
Expand Down Expand Up @@ -41,8 +39,8 @@ export async function handleGraphCommand({ command, ack, respond, client }: Slac
await respond({ replace_original: true, response_type: 'ephemeral', text: 'No users specified' })
return
}
createChart(users).then(async (image_url) => {
await respond({ text: "Hours graph", blocks: getGraphBlocks(image_url, command.user_id, users), response_type: 'in_channel' })
createChart([...new Set(users)]).then(async (image_url) => {
await respond({ text: "Hours graph", blocks: getGraphBlocks(image_url, command.user_id, users), response_type: command.channel_id.startsWith("D") ? 'in_channel' : "ephemeral" })
}).catch(async (e) => {
console.log(e)
await respond({ replace_original: true, response_type: 'ephemeral', text: 'Could not generate graph!' })
Expand Down
4 changes: 3 additions & 1 deletion src/handlers/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ import type { AllMiddlewareArgs, SlackViewMiddlewareArgs, ViewSubmitAction } fro
import { slack_admin_id } from "../../secrets/consts"
import type { ButtonActionMiddlewareArgs, Department } from "../types"
import { ensureSettingsExist, saveData, data } from "../utils/data"
import { getSettingsView } from "../views/settings"
import { departmentTitles, getSettingsView } from "../views/settings"
import { publishDefaultHomeView } from "./app_home"
import { setDepartment } from "../utils/profile"


export async function handleOpenSettingsModal({ ack, client, body, logger }: ButtonActionMiddlewareArgs & AllMiddlewareArgs) {
Expand All @@ -24,6 +25,7 @@ export async function handleSettingsSave({ack, view, body, client}:SlackViewMidd
}
await ensureSettingsExist(body.user.id)
data.userSettings[body.user.id].department = view.state.values.department_selector.selected_department.selected_option!.value! as Department
await setDepartment(body.user.id, departmentTitles[data.userSettings[body.user.id].department??""] ?? "")
await saveData()
await publishDefaultHomeView(body.user.id, client)
}
11 changes: 10 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { loadData, saveData, data, updateUsernames } from "./utils/data";
import { updateSlackMembers } from "./utils/drive";
import { getRequestBlocks } from "./views/new_request";
import { logger } from './logger';
import { setDepartment } from "./utils/profile";
import { departmentTitles } from "./views/settings";

// Initialize global data

Expand All @@ -32,11 +34,18 @@ export const slack_client = slack_app.client;
register_listeners(slack_app)

slack_client.on(WebClientEvent.RATE_LIMITED, (numSeconds) => {
console.debug(`A rate-limiting error occurred and the app is going to retry in ${numSeconds} seconds.`);
console.debug(`A rate-limiting error occurred and the app is going to retry in ${numSeconds} seconds. You may ignore this error`);
});

slack_app.start().then(async () => {
console.log("Bot started")

await Promise.all(Object.entries(data.userSettings).map(async ([id, settings]) => {
if (settings.department) {
await setDepartment(id, departmentTitles[settings.department])
}
}))

})

// Schedule Tasks
Expand Down
26 changes: 17 additions & 9 deletions src/tasks/certs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { slack_celebration_channel } from "../../secrets/consts";
import { Certification, Member } from "../types";
import { certs, saveData } from "../utils/data";
import { getMembers, getSlackMembers } from "../utils/drive";
import { setProfileCerts } from "../utils/profile";

const congratsMessages = [
"Hey! Congrats @ for you new {} Cert!",
Expand All @@ -12,7 +13,7 @@ const congratsMessages = [
"Friends! @ has earned a {}. May we all feast and be merry. :shallow_pan_of_food: ",
"Congrats to @ on getting a {} certification!",
"@ just earned a {}. Did you know: Software is the bread and butter of robotics.",
]
]

export async function celebrateMembers(client: WebClient) {
const slackMembers = await getSlackMembers();
Expand All @@ -26,21 +27,28 @@ export async function celebrateMembers(client: WebClient) {
}
})
certs[member.name] = member.certs
await saveData()
const user = slackMembers?.find((slack_member) => slack_member.real_name == member.name)
if (user == null) {
console.error(`Could not find user ${member.name}`)
return
}

if (newCerts.length > 0) {
const user = slackMembers?.find((slack_member) => slack_member.real_name == member.name)
const certnames = member.certs.map((cert) => cert.name)
console.log(`Setting certs for ${user.real_name?.padEnd(50, " ")} [${certnames.join(", ").length}]: ${certnames}`)
await setProfileCerts(user!.id!, certnames)

const userText = (user == null) ? member.name : `<@${user.id}>`;
newCerts.forEach(async (cert) => {
let message = congratsMessages[Math.floor(Math.random()*congratsMessages.length)]; // get random message
message = message.replace('@',userText) // set user mention
message = message.replace('{}',`*${cert.name}*`) // set cert name in *bold*
let message = congratsMessages[Math.floor(Math.random() * congratsMessages.length)]; // get random message
message = message.replace('@', userText) // set user mention
message = message.replace('{}', `*${cert.name}*`) // set cert name in *bold*

await client.chat.postMessage({ channel: slack_celebration_channel, text: message })
})
}

})

await Promise.all(promises)

await saveData()

}
41 changes: 41 additions & 0 deletions src/utils/profile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { WebClient } from "@slack/web-api";
import { user_token } from "../../secrets/slack_secrets";
import { slack_certs_profile_field_id, slack_department_profile_field_id } from "../../secrets/consts";

const profileApp = new WebClient(user_token)


export async function setDepartment(user:string, department:string):Promise<boolean> {
console.log(`Setting department for ${user} to ${department}`)
try {
const resp = await profileApp.users.profile.set({
user: user,
name: slack_department_profile_field_id,
value: department
})
if (!resp.ok) {
console.error(resp)
}
return resp.ok
} catch (e) {
console.error(e)
return false
}
}

export async function setProfileCerts(user:string, certs:string[]):Promise<boolean> {
try {
const resp = await profileApp.users.profile.set({
user: user,
name: slack_certs_profile_field_id,
value: certs.join(", ")
})
if (!resp.ok) {
console.error(resp)
}
return resp.ok
} catch (e) {
console.error(e)
return false
}
}
2 changes: 1 addition & 1 deletion src/views/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export const getSettingsBlocksDepartment = (user_id: string): KnownBlock[] => {


export const departmentTitles: { [key in Department]: string } = {
fab: "Fabrication",
fab: "Fabrication / Design",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"design is a separate department guys please" - fabricator/designers probably

controls: "Controls",
robotsw: "Robot Software",
appsw: "App Software",
Expand Down
Loading