Create ugly website with Slack signin and signature collection

Co-authored-by: SrIzan10 <66965250+SrIzan10@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-10-12 20:08:46 +00:00
parent 2488e99a3c
commit 6cbf93428e
6 changed files with 1727 additions and 0 deletions

5
.env.example Normal file
View File

@@ -0,0 +1,5 @@
SLACK_CLIENT_ID=your_slack_client_id_here
SLACK_CLIENT_SECRET=your_slack_client_secret_here
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
SESSION_SECRET=your_random_session_secret_here
PORT=3000

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
node_modules/
.env
signatures.json
*.log
.DS_Store

View File

@@ -1,2 +1,69 @@
# lynn-come-out
lynn come out
## 🌈 The Ugliest Website Ever! 🌈
A beautifully ugly website where people can sign in with Slack and sign a petition for Lynn to come out!
### Features
- 🎨 Intentionally terrible design (Comic Sans, rainbow backgrounds, spinning animations!)
- 🔐 Slack OAuth sign-in
- ✍️ Signature collection system
- 📊 Real-time signature counter
- 💾 Persistent storage of signatures
### Setup Instructions
1. **Install dependencies:**
```bash
npm install
```
2. **Set up Slack App:**
- Go to https://api.slack.com/apps
- Create a new app
- Under "OAuth & Permissions", add redirect URL: `http://localhost:3000/auth/slack/callback`
- Under "OAuth & Permissions" > "Scopes", add these scopes:
- `identity.basic`
- `identity.avatar`
- Copy your Client ID and Client Secret
3. **Configure environment variables:**
```bash
cp .env.example .env
```
Edit `.env` and add your Slack credentials:
```
SLACK_CLIENT_ID=your_slack_client_id_here
SLACK_CLIENT_SECRET=your_slack_client_secret_here
SLACK_REDIRECT_URI=http://localhost:3000/auth/slack/callback
SESSION_SECRET=your_random_session_secret_here
PORT=3000
```
4. **Start the server:**
```bash
npm start
```
5. **Open your browser:**
Navigate to http://localhost:3000 and enjoy the ugliest website ever! 🎉
### How It Works
1. Users click "SIGN IN WITH SLACK" 🔐
2. They authenticate with their Slack account
3. Once signed in, they can sign the petition ✍️
4. Signatures are stored persistently and displayed on the page
5. Each user can only sign once
### Files
- `server.js` - Express server with Slack OAuth and signature handling
- `package.json` - Dependencies and scripts
- `.env.example` - Example environment variables
- `signatures.json` - Stored signatures (created automatically)
### Notes
- The website is intentionally designed to be as ugly as possible with clashing colors, Comic Sans, spinning animations, and more!
- Signatures are stored in a JSON file (signatures.json)
- Each user can only sign once (tracked by Slack user ID)

1201
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

19
package.json Normal file
View File

@@ -0,0 +1,19 @@
{
"name": "lynn-come-out",
"version": "1.0.0",
"description": "lynn come out",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "node server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"express": "^4.18.2",
"express-session": "^1.17.3",
"dotenv": "^16.3.1",
"@slack/web-api": "^6.9.0"
}
}

430
server.js Normal file
View File

@@ -0,0 +1,430 @@
const express = require('express');
const session = require('express-session');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(session({
secret: process.env.SESSION_SECRET || 'super-secret-key-change-this',
resave: false,
saveUninitialized: false,
cookie: { secure: false } // Set to true if using HTTPS
}));
// Serve static files
app.use(express.static('public'));
// Data file path
const SIGNATURES_FILE = path.join(__dirname, 'signatures.json');
// Initialize signatures file if it doesn't exist
if (!fs.existsSync(SIGNATURES_FILE)) {
fs.writeFileSync(SIGNATURES_FILE, JSON.stringify([]));
}
// Helper function to read signatures
function getSignatures() {
const data = fs.readFileSync(SIGNATURES_FILE, 'utf8');
return JSON.parse(data);
}
// Helper function to save signatures
function saveSignatures(signatures) {
fs.writeFileSync(SIGNATURES_FILE, JSON.stringify(signatures, null, 2));
}
// Routes
app.get('/', (req, res) => {
const signatures = getSignatures();
const isSignedIn = req.session.user ? true : false;
const userName = req.session.user ? req.session.user.name : '';
const userImage = req.session.user ? req.session.user.image : '';
const hasUserSigned = req.session.user ? signatures.some(s => s.userId === req.session.user.id) : false;
res.send(`
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LYNN COME OUT!!!</title>
<style>
@keyframes rainbow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-20px); }
}
@keyframes blink {
0%, 50%, 100% { opacity: 1; }
25%, 75% { opacity: 0; }
}
body {
font-family: 'Comic Sans MS', cursive, sans-serif;
background: linear-gradient(45deg, #ff00ff, #00ffff, #ffff00, #ff00ff);
background-size: 400% 400%;
animation: rainbow 3s ease infinite;
margin: 0;
padding: 20px;
color: #ff0000;
text-shadow: 2px 2px #00ff00;
overflow-x: hidden;
}
.container {
max-width: 900px;
margin: 0 auto;
background-color: #ffff00;
border: 10px dashed #ff00ff;
padding: 30px;
box-shadow: 0 0 50px rgba(255, 0, 255, 0.8);
}
h1 {
font-size: 72px;
text-align: center;
animation: bounce 1s infinite;
color: #ff0000;
text-shadow: 5px 5px #0000ff, 10px 10px #00ff00;
text-decoration: underline wavy #ff00ff;
margin: 0;
padding: 20px;
background: repeating-linear-gradient(
45deg,
#ff00ff,
#ff00ff 10px,
#00ffff 10px,
#00ffff 20px
);
}
.subtitle {
font-size: 36px;
text-align: center;
animation: blink 2s infinite;
color: #00ff00;
text-shadow: 3px 3px #ff0000;
margin: 20px 0;
}
.sign-in-btn {
display: block;
margin: 30px auto;
padding: 20px 40px;
font-size: 28px;
font-family: 'Comic Sans MS', cursive, sans-serif;
background-color: #ff00ff;
color: #ffff00;
border: 5px solid #00ff00;
cursor: pointer;
animation: spin 3s linear infinite;
text-decoration: none;
text-align: center;
font-weight: bold;
box-shadow: 0 0 20px rgba(255, 0, 0, 0.8);
}
.sign-in-btn:hover {
background-color: #00ffff;
color: #ff0000;
border-color: #ff00ff;
transform: scale(1.2);
}
.signature-btn {
display: block;
margin: 30px auto;
padding: 20px 40px;
font-size: 32px;
font-family: 'Comic Sans MS', cursive, sans-serif;
background-color: #00ff00;
color: #ff0000;
border: 8px double #0000ff;
cursor: pointer;
text-decoration: none;
text-align: center;
font-weight: bold;
box-shadow: 0 0 30px rgba(0, 255, 0, 0.9);
animation: bounce 0.5s infinite;
}
.signature-btn:hover {
background-color: #ff0000;
color: #ffff00;
transform: rotate(5deg) scale(1.1);
}
.signatures {
margin-top: 40px;
padding: 20px;
background-color: #00ffff;
border: 5px solid #ff0000;
}
.signatures h2 {
color: #ff00ff;
text-shadow: 2px 2px #ffff00;
font-size: 48px;
animation: blink 1.5s infinite;
}
.signature-item {
background-color: #ffff00;
border: 3px dotted #ff00ff;
padding: 15px;
margin: 10px 0;
display: flex;
align-items: center;
gap: 15px;
animation: bounce 2s infinite;
}
.signature-item img {
width: 50px;
height: 50px;
border-radius: 50%;
border: 3px solid #ff0000;
animation: spin 5s linear infinite;
}
.signature-item .name {
font-size: 24px;
color: #0000ff;
text-shadow: 1px 1px #ff00ff;
font-weight: bold;
}
.signature-item .timestamp {
font-size: 16px;
color: #ff0000;
margin-left: auto;
}
.user-info {
background-color: #ff00ff;
color: #ffff00;
padding: 20px;
margin: 20px 0;
border: 5px ridge #00ff00;
text-align: center;
font-size: 24px;
font-weight: bold;
text-shadow: 2px 2px #ff0000;
}
.user-info img {
width: 80px;
height: 80px;
border-radius: 50%;
border: 5px solid #00ffff;
animation: spin 4s linear infinite;
margin: 10px;
}
.logout-btn {
display: inline-block;
margin: 10px;
padding: 10px 20px;
font-size: 18px;
font-family: 'Comic Sans MS', cursive, sans-serif;
background-color: #ff0000;
color: #ffff00;
border: 3px solid #00ff00;
cursor: pointer;
text-decoration: none;
font-weight: bold;
}
.count {
font-size: 48px;
text-align: center;
color: #ff00ff;
text-shadow: 3px 3px #00ffff;
margin: 30px 0;
animation: bounce 1.5s infinite;
background-color: #00ff00;
padding: 20px;
border: 5px solid #ff0000;
}
marquee {
font-size: 32px;
color: #ff0000;
background-color: #ffff00;
padding: 10px;
border: 3px solid #ff00ff;
margin: 20px 0;
}
.already-signed {
background-color: #ff0000;
color: #ffff00;
padding: 20px;
text-align: center;
font-size: 24px;
border: 5px solid #00ff00;
margin: 20px 0;
animation: blink 1s infinite;
}
</style>
</head>
<body>
<div class="container">
<h1>🌈 LYNN COME OUT!!! 🌈</h1>
<marquee behavior="scroll" direction="left">✨ LYNN COME OUT ✨ LYNN COME OUT ✨ LYNN COME OUT ✨</marquee>
<div class="subtitle">💖 Sign the Petition NOW!!! 💖</div>
${isSignedIn ? `
<div class="user-info">
<img src="${userImage}" alt="User">
<div>Welcome, ${userName}! 🎉</div>
<a href="/logout" class="logout-btn">Sign Out</a>
</div>
${hasUserSigned ? `
<div class="already-signed">
✅ YOU ALREADY SIGNED! THANK YOU! ✅
</div>
` : `
<form method="POST" action="/sign">
<button type="submit" class="signature-btn">
✍️ SIGN THE PETITION ✍️
</button>
</form>
`}
` : `
<a href="/auth/slack" class="sign-in-btn">
🔐 SIGN IN WITH SLACK 🔐
</a>
`}
<div class="count">
📝 TOTAL SIGNATURES: ${signatures.length} 📝
</div>
<div class="signatures">
<h2>💫 SUPPORTERS 💫</h2>
${signatures.length === 0 ? '<p style="font-size: 24px; text-align: center; color: #ff0000;">No signatures yet! Be the first! 🎯</p>' : ''}
${signatures.map(sig => `
<div class="signature-item">
<img src="${sig.userImage}" alt="${sig.userName}">
<span class="name">${sig.userName}</span>
<span class="timestamp">${new Date(sig.timestamp).toLocaleString()}</span>
</div>
`).join('')}
</div>
<marquee behavior="scroll" direction="right">🏳️‍🌈 LYNN WE SUPPORT YOU 🏳️‍🌈 COME OUT COME OUT WHEREVER YOU ARE 🏳️‍🌈</marquee>
</div>
</body>
</html>
`);
});
// Slack OAuth - Start
app.get('/auth/slack', (req, res) => {
const clientId = process.env.SLACK_CLIENT_ID;
const redirectUri = process.env.SLACK_REDIRECT_URI;
const scopes = 'identity.basic,identity.avatar';
if (!clientId) {
return res.send('<h1>Error: SLACK_CLIENT_ID not configured</h1><p>Please set up your .env file with Slack credentials.</p>');
}
const authUrl = `https://slack.com/oauth/authorize?client_id=${clientId}&scope=${scopes}&redirect_uri=${encodeURIComponent(redirectUri)}`;
res.redirect(authUrl);
});
// Slack OAuth - Callback
app.get('/auth/slack/callback', async (req, res) => {
const code = req.query.code;
const clientId = process.env.SLACK_CLIENT_ID;
const clientSecret = process.env.SLACK_CLIENT_SECRET;
const redirectUri = process.env.SLACK_REDIRECT_URI;
if (!code) {
return res.send('<h1>Error: No authorization code received</h1><a href="/">Go back</a>');
}
try {
// Exchange code for access token
const tokenUrl = 'https://slack.com/api/oauth.access';
const params = new URLSearchParams({
client_id: clientId,
client_secret: clientSecret,
code: code,
redirect_uri: redirectUri
});
const response = await fetch(`${tokenUrl}?${params.toString()}`);
const data = await response.json();
if (data.ok) {
// Store user info in session
req.session.user = {
id: data.user_id,
name: data.user.name,
image: data.user.image_48 || data.user.image_72 || 'https://via.placeholder.com/48'
};
res.redirect('/');
} else {
res.send(`<h1>Error: ${data.error}</h1><p>Failed to authenticate with Slack</p><a href="/">Go back</a>`);
}
} catch (error) {
res.send(`<h1>Error: ${error.message}</h1><a href="/">Go back</a>`);
}
});
// Sign the petition
app.post('/sign', (req, res) => {
if (!req.session.user) {
return res.redirect('/');
}
const signatures = getSignatures();
// Check if user already signed
if (signatures.some(s => s.userId === req.session.user.id)) {
return res.redirect('/');
}
// Add signature
signatures.push({
userId: req.session.user.id,
userName: req.session.user.name,
userImage: req.session.user.image,
timestamp: new Date().toISOString()
});
saveSignatures(signatures);
res.redirect('/');
});
// Logout
app.get('/logout', (req, res) => {
req.session.destroy();
res.redirect('/');
});
// Start server
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
console.log('Make sure to set up your .env file with Slack credentials!');
});