Hack Club Eligibility Bot™️
This commit is contained in:
commit
4224c88c9b
6 changed files with 2706 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.env
|
||||||
|
node_modules
|
35
README.md
Normal file
35
README.md
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
# Hack Club Eligiblity Bot
|
||||||
|
A bot that allows you to view the eligiblity of someone in Hack Club for certain swag (basically "are you a student?")
|
||||||
|
|
||||||
|
This uses the Hack Club Eligiblity API (duh).
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
Run `/check-eligiblity` in any Slack channel, and optionally ping a user to check their eligiblity (otherwise it shows your own).
|
||||||
|
|
||||||
|
If you view your own eligiblity, you can also see the raw response from the API.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
1. Clone the repo.
|
||||||
|
```sh
|
||||||
|
$ git clone https://git.hackclub.app/haroon/hack-club-eligiblity.git
|
||||||
|
```
|
||||||
|
2. Enter the newly created directory.
|
||||||
|
```sh
|
||||||
|
$ cd hack-club-eligiblity
|
||||||
|
```
|
||||||
|
3. Add your bot token and signing secret to .env:
|
||||||
|
```sh
|
||||||
|
$ <editor> .env
|
||||||
|
|
||||||
|
# Add:
|
||||||
|
SLACK_BOT_TOKEN=<>
|
||||||
|
SLACK_SIGNING_SECRET=<>
|
||||||
|
```
|
||||||
|
4. Install dependencies:
|
||||||
|
```
|
||||||
|
$ npm i
|
||||||
|
```
|
||||||
|
5. Run!
|
||||||
|
```
|
||||||
|
$ npx tsx .
|
||||||
|
```
|
73
index.ts
Normal file
73
index.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import type { User } from "@slack/web-api/dist/response/UsersInfoResponse";
|
||||||
|
|
||||||
|
const { App, ExpressReceiver } = (await import("@slack/bolt"));
|
||||||
|
import "dotenv/config";
|
||||||
|
|
||||||
|
const app = new App({
|
||||||
|
token: process.env.SLACK_BOT_TOKEN,
|
||||||
|
signingSecret: process.env.SLACK_SIGNING_SECRET,
|
||||||
|
});
|
||||||
|
|
||||||
|
app.command("/check-eligiblity", async ctx => {
|
||||||
|
await ctx.ack();
|
||||||
|
|
||||||
|
const text = ctx.command.text.slice();
|
||||||
|
let match;
|
||||||
|
let userId = ctx.context.userId;
|
||||||
|
let matchedBy = "no input"
|
||||||
|
|
||||||
|
if (match = text.match(/\<\@(.+)\|(.+)>/)) {
|
||||||
|
userId = match[1];
|
||||||
|
matchedBy = "user mention"
|
||||||
|
} else if (text)
|
||||||
|
matchedBy = "invalid input"
|
||||||
|
|
||||||
|
|
||||||
|
const res = await fetch("https://verify.hackclub.dev/api/status", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { 'content-type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
"slack_id": userId
|
||||||
|
}),
|
||||||
|
redirect: "follow"
|
||||||
|
}).then(res => res.json())
|
||||||
|
|
||||||
|
if (res === `User ${userId} not found!`)
|
||||||
|
return await ctx.respond({
|
||||||
|
response_type: 'ephemeral',
|
||||||
|
text: `Either ${matchedBy !== "user mention" ? "you haven't" : `<@${userId}> hasn't`} verified, or ${matchedBy !== "user mention" ? "your" : "their"} verification hasn't been accepted.${matchedBy !== "user mention" ? "\nCheck out the <https://forms.hackclub.com/eligibility|eligiblity form> to verify." : ""}`,
|
||||||
|
unfurl_links: true
|
||||||
|
})
|
||||||
|
|
||||||
|
else {
|
||||||
|
return await ctx.respond({
|
||||||
|
response_type: 'ephemeral',
|
||||||
|
text: `${matchedBy !== "user mention" ? "You have verified your" : `<@${userId}> has verified their`} student status, and ${matchedBy !== "user mention" ? "are" : "is"} ${res.status}.`,
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
type: 'section',
|
||||||
|
text: {
|
||||||
|
type: 'mrkdwn',
|
||||||
|
text: `${matchedBy !== "user mention" ? "You have verified your" : `<@${userId}> has verified their`} student status, and ${matchedBy !== "user mention" ? "are" : "is"} *${res.status}*.`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...(matchedBy == "user mention" ? [] : [
|
||||||
|
{
|
||||||
|
type: 'section',
|
||||||
|
// @ts-ignore silly typings
|
||||||
|
text: {
|
||||||
|
type: 'mrkdwn',
|
||||||
|
text: `*Raw JSON output from the Eligiblity API:*\n` + "```\n" + JSON.stringify(res, null, 2) + "\n```"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
;(async () => {
|
||||||
|
await app.start(60275);
|
||||||
|
|
||||||
|
console.log('⚡️ Bolt app is running!');
|
||||||
|
})();
|
2539
package-lock.json
generated
Normal file
2539
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
29
package.json
Normal file
29
package.json
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"name": "hack-club-eligiblity",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.ts",
|
||||||
|
"type": "module",
|
||||||
|
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.hackclub.app/haroon/hack-club-eligiblity.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"hack",
|
||||||
|
"club",
|
||||||
|
"eligiblity",
|
||||||
|
"slack"
|
||||||
|
],
|
||||||
|
"author": "Haroon <haroon@hackclub.app>",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"dependencies": {
|
||||||
|
"@slack/bolt": "^3.21.1",
|
||||||
|
"@slack/web-api": "^7.3.4",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"typescript": "^5.5.4"
|
||||||
|
}
|
||||||
|
}
|
28
tsconfig.json
Normal file
28
tsconfig.json
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Enable latest features
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue