mirror of
https://github.com/neongamerbot-qk/hackclub-nest
synced 2025-05-06 13:43:07 +00:00
feat: spotify + slack
Signed-off-by: Saahil <neon@saahild.com>
This commit is contained in:
parent
849f93cabc
commit
0c2543e157
8 changed files with 1684 additions and 0 deletions
15
hackclub-spotify-bot/src/TODO.md
Normal file
15
hackclub-spotify-bot/src/TODO.md
Normal file
|
@ -0,0 +1,15 @@
|
|||
- [ ] Slack stuff
|
||||
- - [ ] web api [docs](https://tools.slack.dev/node-slack-sdk/web-api/)
|
||||
- - [ ] oauth [docs](https://tools.slack.dev/node-slack-sdk/oauth)
|
||||
- - [ ] fix the [channel](https://app.slack.com/client/T0266FRGM/C07RE4N7S4B)
|
||||
- - - [ ] Add ping for new-song event & send message ovs
|
||||
- [ ] Spotify
|
||||
- - [ ] web api [docs](https://developer.spotify.com/documentation/web-api/)
|
||||
- - [ ] oauth [docs](https://developer.spotify.com/documentation/general/guides/authorization-guide/)
|
||||
- - [ ] refresh token [docs](https://developer.spotify.com/documentation/general/guides/authorization-guide/#refresh-an-access-token)
|
||||
- - [ ] playlist tools (creation,modifcation,deletion)
|
||||
- [ ] keydb (quick db or smthing)
|
||||
- [ ] express
|
||||
- [ ] transparency of added songs
|
||||
- - [ ] export db -> into csv with properties (slack_id, slack_name, spotify_id, spotify_url)
|
||||
|
40
hackclub-spotify-bot/src/index.js
Normal file
40
hackclub-spotify-bot/src/index.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
require('dotenv').config();
|
||||
const express = require('express');
|
||||
const session = require('express-session');
|
||||
const { WebClient } = require('@slack/web-api');
|
||||
const { Client } = require('@slack/oauth');
|
||||
const { getLoginUrl, refreshToken } = require('./spotify');
|
||||
const path = require('path');
|
||||
|
||||
const app = express();
|
||||
// Initialize
|
||||
const web = new WebClient(process.env.SLACK_TOKEN);
|
||||
const oauth = new Client({
|
||||
clientId: process.env.SLACK_CLIENT_ID,
|
||||
clientSecret: process.env.SLACK_CLIENT_SECRET,
|
||||
state: Math.random().toString(36).substring(2),
|
||||
});
|
||||
app.use(express.json());
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', 'src/views');
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(session({
|
||||
secret: Math.random().toString(36).substring(2),
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
cookie: { secure: true }
|
||||
}));
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.render('index', { title: 'Hack Club Spotify Bot', description: "Contribute to the hackclub spotify playlist!" });
|
||||
});
|
||||
app.get('/login', async (req, res) => {
|
||||
if(req.session.token) {
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
app.listen(process.env.PORT || 3000, () => {
|
||||
console.log('Example app listening on port 3000!');
|
||||
});
|
589
hackclub-spotify-bot/src/public/hackclub.css
Normal file
589
hackclub-spotify-bot/src/public/hackclub.css
Normal file
|
@ -0,0 +1,589 @@
|
|||
/* modified version of https://css.hackclub.com/theme.css */
|
||||
:root {
|
||||
/* why are the css vars swapped with incorrect names? well im to lazy to fix it. */
|
||||
--darker: #121217;
|
||||
--dark: #f9fafc;
|
||||
--darkless: #e0e6ed;
|
||||
--black: #fff;
|
||||
--steel: #273444;
|
||||
--slate: #3c4858;
|
||||
--muted: #8492a6;
|
||||
--smoke: #252429;
|
||||
--snow: #17171d;
|
||||
--white: #1f2d3d;
|
||||
--red: #ec3750;
|
||||
--orange: #ff8c37;
|
||||
--yellow: #f1c40f;
|
||||
--green: #33d6a6;
|
||||
--cyan: #5bc0de;
|
||||
--blue: #338eda;
|
||||
--purple: #a633d6;
|
||||
--text: var(--black);
|
||||
--background: var(--white);
|
||||
--elevated: var(--white);
|
||||
--sheet: var(--snow);
|
||||
--sunken: var(--smoke);
|
||||
--border: var(--smoke);
|
||||
--primary: #ec3750;
|
||||
--secondary: #8492a6;
|
||||
--accent: #5bc0de;
|
||||
--twitter: #1da1f2;
|
||||
--facebook: #3b5998;
|
||||
--instagram: #e1306c;
|
||||
--breakpoint-xs: 32em;
|
||||
--breakpoint-s: 48em;
|
||||
--breakpoint-m: 64em;
|
||||
--breakpoint-l: 96em;
|
||||
--breakpoint-xl: 128em;
|
||||
--spacing-0: 0px;
|
||||
--spacing-1: 4px;
|
||||
--spacing-2: 8px;
|
||||
--spacing-3: 16px;
|
||||
--spacing-4: 32px;
|
||||
--spacing-5: 64px;
|
||||
--spacing-6: 128px;
|
||||
--spacing-7: 256px;
|
||||
--spacing-8: 512px;
|
||||
--font-1: 12px;
|
||||
--font-2: 16px;
|
||||
--font-3: 20px;
|
||||
--font-4: 24px;
|
||||
--font-5: 32px;
|
||||
--font-6: 48px;
|
||||
--font-7: 64px;
|
||||
--font-8: 96px;
|
||||
--font-9: 128px;
|
||||
--font-10: 160px;
|
||||
--font-11: 192px;
|
||||
--line-height-limit: 0.875;
|
||||
--line-height-title: 1;
|
||||
--line-height-heading: 1.125;
|
||||
--line-height-subheading: 1.25;
|
||||
--line-height-caption: 1.375;
|
||||
--line-height-body: 1.5;
|
||||
--font-weight-body: 400;
|
||||
--font-weight-bold: 700;
|
||||
--font-weight-heading: var(--font-weight-bold);
|
||||
--letter-spacing-title: -0.009em;
|
||||
--letter-spacing-headline: 0.009em;
|
||||
--size-wide-plus: 2048px;
|
||||
--size-wide: 1536px;
|
||||
--size-layout-plus: 1200px;
|
||||
--size-layout: 1024px;
|
||||
--size-copy-ultra: 980px;
|
||||
--size-copy-plus: 768px;
|
||||
--size-copy: 680px;
|
||||
--size-narrow-plus: 600px;
|
||||
--size-narrow: 512px;
|
||||
--radii-small: 4px;
|
||||
--radii-default: 8px;
|
||||
--radii-extra: 12px;
|
||||
--radii-ultra: 16px;
|
||||
--radii-circle: 99999px;
|
||||
--shadow-text: 0 1px 2px rgba(0, 0, 0, 0.25), 0 2px 4px rgba(0, 0, 0, 0.125);
|
||||
--shadow-small: 0 1px 2px rgba(0, 0, 0, 0.0625),
|
||||
0 2px 4px rgba(0, 0, 0, 0.0625);
|
||||
--shadow-card: 0 4px 8px rgba(0, 0, 0, 0.125);
|
||||
--shadow-elevated: 0 1px 2px rgba(0, 0, 0, 0.0625),
|
||||
0 8px 12px rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Phantom Sans", system-ui, -apple-system, BlinkMacSystemFont,
|
||||
"Segoe UI", Roboto, sans-serif;
|
||||
line-height: var(--line-height-body);
|
||||
font-weight: var(--font-weight-body);
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-smooth: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
color: var(--text);
|
||||
background-color: var(--background);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.monospace {
|
||||
font-family: "SF Mono", "Roboto Mono", Menlo, Consolas, monospace;
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-heading);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ultratitle {
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-limit);
|
||||
letter-spacing: var(--letter-spacing-title);
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-title);
|
||||
letter-spacing: var(--letter-spacing-title);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: var(--spacing-3);
|
||||
font-weight: var(--font-weight-body);
|
||||
line-height: var(--line-height-subheading);
|
||||
letter-spacing: var(--letter-spacing-headline);
|
||||
}
|
||||
|
||||
.headline {
|
||||
margin-top: var(--spacing-3);
|
||||
margin-bottom: var(--spacing-3);
|
||||
font-size: var(--font-4);
|
||||
line-height: var(--line-height-heading);
|
||||
letter-spacing: var(--letter-spacing-headline);
|
||||
}
|
||||
|
||||
.subheadline {
|
||||
margin-top: var(--spacing-0);
|
||||
margin-bottom: var(--spacing-3);
|
||||
font-size: var(--font-2);
|
||||
line-height: var(--line-height-heading);
|
||||
letter-spacing: var(--letter-spacing-headline);
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
color: var(--muted);
|
||||
font-weight: var(--font-weight-heading);
|
||||
letter-spacing: var(--letter-spacing-headline);
|
||||
line-height: var(--line-height-subheading);
|
||||
text-transform: uppercase;
|
||||
margin-top: var(--spacing-0);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-weight: var(--font-weight-body);
|
||||
}
|
||||
|
||||
.caption {
|
||||
color: var(--muted);
|
||||
font-weight: var(--font-weight-body);
|
||||
letter-spacing: var(--letter-spacing-headline);
|
||||
line-height: var(--line-height-caption);
|
||||
}
|
||||
|
||||
.pill {
|
||||
border-radius: var(--radii-circle);
|
||||
padding-left: var(--spacing-3);
|
||||
padding-right: var(--spacing-3);
|
||||
padding-top: var(--spacing-1);
|
||||
padding-bottom: var(--spacing-1);
|
||||
font-size: var(--font-2);
|
||||
background: var(--primary);
|
||||
color: var(--background);
|
||||
font-weight: var(--font-weight-bold);
|
||||
}
|
||||
|
||||
.outline-badge {
|
||||
border-radius: var(--radii-circle);
|
||||
padding-left: var(--spacing-3);
|
||||
padding-right: var(--spacing-3);
|
||||
padding-top: var(--spacing-1);
|
||||
padding-bottom: var(--spacing-1);
|
||||
font-size: var(--font-2);
|
||||
background: none;
|
||||
color: var(--muted);
|
||||
border: 1px solid currentcolor;
|
||||
font-weight: var(--font-weight-body);
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
font-family: inherit;
|
||||
font-weight: var(--font-weight-bold);
|
||||
border-radius: var(--radii-circle);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: var(--shadow-card);
|
||||
letter-spacing: var(--letter-spacing-headline);
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
transition: transform 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
text-align: center;
|
||||
line-height: inherit;
|
||||
-webkit-text-decoration: none;
|
||||
text-decoration: none;
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
color: var(--theme-ui-colors-white, #ffffff);
|
||||
background-color: var(--theme-ui-colors-primary, #ec3750);
|
||||
border: 0;
|
||||
font-size: var(--font-2);
|
||||
}
|
||||
|
||||
button:focus,
|
||||
button:hover {
|
||||
box-shadow: var(--shadow-elevated);
|
||||
transform: scale(1.0625);
|
||||
}
|
||||
|
||||
button.lg {
|
||||
font-size: var(--font-3)!important;
|
||||
line-height: var(--line-height-title);
|
||||
padding-left: var(--spacing-4);
|
||||
padding-right: var(--spacing-4);
|
||||
padding-top: var(--spacing-3);
|
||||
padding-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
button.outline {
|
||||
background: none;
|
||||
color: var(--primary);
|
||||
border: 2px solid currentcolor;
|
||||
}
|
||||
|
||||
button.cta {
|
||||
font-size: var(--font-2);
|
||||
background-image: radial-gradient(
|
||||
ellipse farthest-corner at top left,
|
||||
var(--orange),
|
||||
var(--red)
|
||||
);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--elevated);
|
||||
color: var(--text);
|
||||
border-radius: var(--radii-extra);
|
||||
box-shadow: var(--shadow-card);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card.sunken {
|
||||
background: var(--sunken);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.card.interactive {
|
||||
text-decoration: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
transition: transform 0.125s ease-in-out, box-shadow 0.125s ease-in-out;
|
||||
}
|
||||
|
||||
.card.interactive:hover,
|
||||
.card.interactive:focus {
|
||||
transform: scale(1.0625);
|
||||
box-shadow: var(--shadow-elevated);
|
||||
}
|
||||
|
||||
input,
|
||||
textarea,
|
||||
select {
|
||||
background: var(--elevated);
|
||||
color: var(--text);
|
||||
font-family: inherit;
|
||||
border-radius: var(--radii-small);
|
||||
border: 0;
|
||||
font-size: inherit;
|
||||
padding: var(--spacing-2);
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
input::-webkit-input-placeholder,
|
||||
input::-moz-placeholder,
|
||||
input:-ms-input-placeholder,
|
||||
textarea::-webkit-input-placeholder,
|
||||
textarea::-moz-placeholder,
|
||||
textarea:-ms-input-placeholder,
|
||||
select::-webkit-input-placeholder,
|
||||
select::-moz-placeholder,
|
||||
select:-ms-input-placeholder {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
input[type="search"]::-webkit-search-decoration,
|
||||
textarea[type="search"]::-webkit-search-decoration,
|
||||
select[type="search"]::-webkit-search-decoration {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
-webkit-appearance: checkbox;
|
||||
-moz-appearance: checkbox;
|
||||
appearance: checkbox;
|
||||
}
|
||||
|
||||
label {
|
||||
color: var(--text);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: left;
|
||||
line-height: var(--line-height-caption);
|
||||
font-size: var(--font-3);
|
||||
}
|
||||
|
||||
label.horizontal {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.slider {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.form-hidden {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
padding-left: var(--spacing-3);
|
||||
padding-right: var(--spacing-3);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: var(--font-5);
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-heading);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: var(--font-4);
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-heading);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: var(--font-3);
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-heading);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: var(--font-2);
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-heading);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: var(--font-1);
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-heading);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h6 {
|
||||
font-weight: var(--font-weight-bold);
|
||||
line-height: var(--line-height-heading);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
color: var(--text);
|
||||
font-weight: var(--font-weight-body);
|
||||
line-height: var(--line-height-body);
|
||||
margin-top: var(--spacing-3);
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--primary);
|
||||
text-decoration: underline;
|
||||
text-underline-position: under;
|
||||
}
|
||||
|
||||
a:focus,
|
||||
a:hover {
|
||||
text-decoration-style: wavy;
|
||||
text-decoration-skip-ink: none;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: "SF Mono", "Roboto Mono", Menlo, Consolas, monospace;
|
||||
font-size: var(--font-1);
|
||||
padding: var(--spacing-3);
|
||||
color: var(--text);
|
||||
background: var(--sunken);
|
||||
overflow: auto;
|
||||
border-radius: var(--radii-default);
|
||||
white-space: inherit;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
color: inherit;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "SF Mono", "Roboto Mono", Menlo, Consolas, monospace;
|
||||
font-size: inherit;
|
||||
color: var(--purple);
|
||||
background: var(--sunken);
|
||||
overflow: auto;
|
||||
border-radius: var(--radii-small);
|
||||
margin-left: var(--spacing-1);
|
||||
margin-right: var(--spacing-1);
|
||||
padding-left: var(--spacing-1);
|
||||
padding-right: var(--spacing-1);
|
||||
}
|
||||
|
||||
p > code,
|
||||
li > code {
|
||||
color: var(--blue);
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
p > a > code,
|
||||
li > a > code {
|
||||
color: var(--blue);
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: var(--spacing-2);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
margin-top: var(--spacing-4);
|
||||
margin-bottom: var(--spacing-4);
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
table > th,
|
||||
table > td {
|
||||
text-align: left;
|
||||
padding: 4px;
|
||||
padding-left: 0px;
|
||||
border-color: var(--border);
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
th {
|
||||
vertical-align: bottom;
|
||||
border-bottom-width: 2px;
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (min-width: 32em) {
|
||||
.ultratitle {
|
||||
font-size: var(--font-5);
|
||||
}
|
||||
.title {
|
||||
font-size: var(--font-4);
|
||||
}
|
||||
.subtitle {
|
||||
font-size: var(--font-2);
|
||||
}
|
||||
.eyebrow {
|
||||
font-size: var(--font-3);
|
||||
}
|
||||
.lead {
|
||||
font-size: var(--font-2);
|
||||
margin-top: var(--spacing-2);
|
||||
margin-bottom: var(--spacing-2);
|
||||
}
|
||||
.card {
|
||||
padding: var(--spacing-3);
|
||||
}
|
||||
.container {
|
||||
max-width: var(--size-layout);
|
||||
}
|
||||
.container.copy {
|
||||
max-width: var(--size-copy);
|
||||
}
|
||||
.container.narrow {
|
||||
max-width: var(--size-narrow);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 48em) {
|
||||
.ultratitle {
|
||||
font-size: var(--font-6);
|
||||
}
|
||||
.title {
|
||||
font-size: var(--font-5);
|
||||
}
|
||||
.subtitle {
|
||||
font-size: var(--font-3);
|
||||
}
|
||||
.eyebrow {
|
||||
font-size: var(--font-4);
|
||||
}
|
||||
.lead {
|
||||
font-size: var(--font-3);
|
||||
margin-top: var(--spacing-3);
|
||||
margin-bottom: var(--spacing-3);
|
||||
}
|
||||
.card {
|
||||
padding: var(--spacing-4);
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 64em) {
|
||||
.ultratitle {
|
||||
font-size: var(--font-7);
|
||||
}
|
||||
.title {
|
||||
font-size: var(--font-6);
|
||||
}
|
||||
.container {
|
||||
max-width: var(--size-layout-plus);
|
||||
}
|
||||
.container.wide {
|
||||
max-width: var(--size-wide);
|
||||
}
|
||||
.container.copy {
|
||||
max-width: var(--size-copy-plus);
|
||||
}
|
||||
.container.narrow {
|
||||
max-width: var(--size-narrow-plus);
|
||||
}
|
||||
}
|
||||
|
113
hackclub-spotify-bot/src/spotify.js
Normal file
113
hackclub-spotify-bot/src/spotify.js
Normal file
|
@ -0,0 +1,113 @@
|
|||
|
||||
let token = null;
|
||||
let authStuff = null;
|
||||
const client_id = process.env.SPOTIFY_CLIENT_ID;
|
||||
const client_secret = process.env.SPOTIFY_CLIENT_SECRET;
|
||||
const redirect_uri = process.env.SPOTIFY_REDIRECT_URI;
|
||||
async function fetchWebApi(endpoint, method, body) {
|
||||
const res = await fetch(`https://api.spotify.com/${endpoint}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
method,
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const text = await res.text();
|
||||
// console.debug(text)
|
||||
// abs nothing is wrong
|
||||
return JSON.parse(text.trim());
|
||||
}
|
||||
|
||||
function getLoginUrl() {
|
||||
const state = generateRandomString(16);
|
||||
const scope = [
|
||||
"ugc-image-upload",
|
||||
"user-read-playback-state",
|
||||
"user-modify-playback-state",
|
||||
"user-read-currently-playing",
|
||||
"app-remote-control",
|
||||
"streaming",
|
||||
"playlist-read-private",
|
||||
"playlist-read-collaborative",
|
||||
"playlist-modify-private",
|
||||
"playlist-modify-public",
|
||||
"user-follow-modify",
|
||||
"user-follow-read",
|
||||
"user-read-playback-position",
|
||||
"user-top-read",
|
||||
"user-read-recently-played",
|
||||
"user-library-modify",
|
||||
"user-library-read",
|
||||
"user-read-email",
|
||||
"user-read-private",
|
||||
].join(" ");
|
||||
|
||||
return (
|
||||
"https://accounts.spotify.com/authorize?" +
|
||||
`response_type=code&grant_type=client_credentials&client_id=${client_id}&scope=${scope}&redirect_uri=${redirect_uri}&state=${state}`
|
||||
);
|
||||
}
|
||||
|
||||
function generateRandomString(length) {
|
||||
let result = "";
|
||||
const characters =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
const charactersLength = characters.length;
|
||||
let counter = 0;
|
||||
while (counter < length) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
counter += 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async function refreshToken(refresh_token) {
|
||||
try {
|
||||
// var refresh_token = req.query.refresh_token;
|
||||
const authOptions = {
|
||||
url: "https://accounts.spotify.com/api/token",
|
||||
headers: {
|
||||
"content-type": "application/x-www-form-urlencoded",
|
||||
Authorization:
|
||||
"Basic " +
|
||||
new Buffer.from(client_id + ":" + client_secret).toString("base64"),
|
||||
},
|
||||
form: {
|
||||
grant_type: "refresh_token",
|
||||
refresh_token: refresh_token,
|
||||
},
|
||||
json: true,
|
||||
};
|
||||
console.log(authOptions);
|
||||
const formdm = new URLSearchParams();
|
||||
|
||||
formdm.append("grant_type", "refresh_token");
|
||||
formdm.append("refresh_token", refresh_token);
|
||||
|
||||
fetch(authOptions.url, {
|
||||
body: formdm,
|
||||
headers: authOptions.headers,
|
||||
method: "POST",
|
||||
})
|
||||
.then(async (r) => {
|
||||
const text = await r.text();
|
||||
console.log(text);
|
||||
return JSON.parse(text);
|
||||
})
|
||||
.then((auth) => {
|
||||
if (!auth.refresh_token) auth.refresh_token = refresh_token;
|
||||
console.log(auth);
|
||||
authStuff = auth;
|
||||
token = auth.access_token;
|
||||
if (auth.expires_in) {
|
||||
setTimeout(() => {
|
||||
refreshToken(auth.refresh_token);
|
||||
}, auth.expires_in * 1000);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(`Welp it broke`);
|
||||
// try again asap because we NEED THAT TOKEN
|
||||
refreshToken(refresh_token);
|
||||
}
|
||||
}
|
||||
|
37
hackclub-spotify-bot/src/views/index.ejs
Normal file
37
hackclub-spotify-bot/src/views/index.ejs
Normal file
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= title %></title>
|
||||
|
||||
<meta property="og:title" content="<%=title%>" />
|
||||
<meta name="twitter:title" content="<%=title%>" />
|
||||
<meta name="description" content="<%=description %>" />
|
||||
<meta property="og:description" content="<%=description %>" />
|
||||
<meta name="twitter:description" content="<%=description %>" />
|
||||
<link rel="stylesheet" href="./hackclub.css" />
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<br>
|
||||
<header>
|
||||
<h1 class="ultratitle">Hackclub spotify bot</h1>
|
||||
<!-- <p class="headline">Looking for a basic website template that includes Hack Club's theme CSS? Look no further!</p> -->
|
||||
</header>
|
||||
<div>
|
||||
<div class="card container">
|
||||
<!-- <h2 class="headline"></h2> -->
|
||||
<p>
|
||||
This is a bot which will contribute to the spotify playlist which is for the hackclub community!
|
||||
<br>
|
||||
Note: WIP
|
||||
</p>
|
||||
</div>
|
||||
<footer>
|
||||
<a href="/login">Login Page</a>
|
||||
</footer>
|
||||
</div>
|
||||
</center>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue