squash this
This commit is contained in:
parent
8aa400eef6
commit
727b5bf83c
13 changed files with 317 additions and 15 deletions
102
README.md
Normal file
102
README.md
Normal file
|
|
@ -0,0 +1,102 @@
|
||||||
|
# <div align="center"><img src="gametime_logo.png" alt="GameTime Logo" width="200"/></div>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
# 🏈 GameTime
|
||||||
|
|
||||||
|
**Your Fantasy Football Command Center**
|
||||||
|
|
||||||
|
*Track all your leagues, matchups, and players in one sleek dashboard*
|
||||||
|
|
||||||
|
[](https://python.org)
|
||||||
|
[](https://flask.palletsprojects.com/)
|
||||||
|
[](https://docker.com)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 🚀 What is GameTime?
|
||||||
|
|
||||||
|
GameTime is your all-in-one fantasy football dashboard that connects to Sleeper and ESPN to give you the ultimate game day experience. See all your leagues, matchups, and live scores in one beautiful interface.
|
||||||
|
|
||||||
|
### ✨ Features
|
||||||
|
|
||||||
|
- 📊 **Multi-League Dashboard** - View all your Sleeper leagues at once
|
||||||
|
- 🔴 **Live Score Updates** - Real-time score refreshes during game day
|
||||||
|
- 📅 **NFL Schedule Integration** - See when your players are playing
|
||||||
|
- 🌙 **Dark/Light Theme** - Switch themes for day or night viewing
|
||||||
|
- 📱 **Mobile Responsive** - Looks great on all devices
|
||||||
|
- 🐳 **Docker Ready** - Easy deployment with containers
|
||||||
|
|
||||||
|
## 🏃♂️ Quick Start
|
||||||
|
|
||||||
|
### Option 1: Local Development
|
||||||
|
```bash
|
||||||
|
# Install dependencies
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Run the app
|
||||||
|
python app.py
|
||||||
|
|
||||||
|
# Visit http://localhost:5000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Docker
|
||||||
|
```bash
|
||||||
|
# Build and run
|
||||||
|
docker-compose up
|
||||||
|
|
||||||
|
# Or run detached
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎮 How to Use
|
||||||
|
|
||||||
|
1. **Enter your Sleeper username** on the homepage
|
||||||
|
2. **View your dashboard** - see all leagues and current week matchups
|
||||||
|
3. **Navigate weeks** - check past weeks or look ahead
|
||||||
|
4. **Live updates** - scores refresh automatically during games
|
||||||
|
5. **Theme switching** - toggle between light and dark modes
|
||||||
|
|
||||||
|
## 🛠 Tech Stack
|
||||||
|
|
||||||
|
- **Backend**: Flask (Python)
|
||||||
|
- **APIs**: Sleeper API, ESPN API
|
||||||
|
- **Frontend**: HTML, CSS, JavaScript
|
||||||
|
- **Deployment**: Docker + Docker Compose
|
||||||
|
|
||||||
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
gametime/
|
||||||
|
├── app.py # Main Flask application
|
||||||
|
├── config.py # App configuration
|
||||||
|
├── services/ # API integrations
|
||||||
|
│ ├── sleeper_api.py # Sleeper API client
|
||||||
|
│ └── espn_api.py # ESPN API client
|
||||||
|
├── templates/ # HTML templates
|
||||||
|
├── static/ # CSS and assets
|
||||||
|
└── ContainerFile # Docker build config
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🤝 Contributing
|
||||||
|
|
||||||
|
Got ideas for GameTime? We'd love to hear them!
|
||||||
|
|
||||||
|
1. Fork the repo
|
||||||
|
2. Create a feature branch
|
||||||
|
3. Make your changes
|
||||||
|
4. Submit a pull request
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
This project is open source and available under the MIT License.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
**Ready for GameTime?** 🏈
|
||||||
|
|
||||||
|
*Built with ❤️ for fantasy football fanatics*
|
||||||
|
|
||||||
|
</div>
|
||||||
17
app.py
17
app.py
|
|
@ -1,4 +1,4 @@
|
||||||
from flask import Flask, render_template, jsonify, request, session, redirect, url_for
|
from flask import Flask, render_template, jsonify, request, session, redirect, url_for, send_file
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
@ -59,7 +59,7 @@ def inject_apis():
|
||||||
return dict(
|
return dict(
|
||||||
sleeper_api=sleeper_api,
|
sleeper_api=sleeper_api,
|
||||||
app_version=app.config['VERSION'],
|
app_version=app.config['VERSION'],
|
||||||
current_theme=session.get('theme', 'light')
|
current_theme=session.get('theme', 'dark')
|
||||||
)
|
)
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
|
|
@ -81,7 +81,7 @@ def dashboard_form():
|
||||||
@app.route('/toggle_theme', methods=['POST'])
|
@app.route('/toggle_theme', methods=['POST'])
|
||||||
def toggle_theme():
|
def toggle_theme():
|
||||||
"""Toggle between light and dark theme"""
|
"""Toggle between light and dark theme"""
|
||||||
current_theme = session.get('theme', 'light')
|
current_theme = session.get('theme', 'dark')
|
||||||
new_theme = 'dark' if current_theme == 'light' else 'light'
|
new_theme = 'dark' if current_theme == 'light' else 'light'
|
||||||
session['theme'] = new_theme
|
session['theme'] = new_theme
|
||||||
|
|
||||||
|
|
@ -359,6 +359,17 @@ def refresh_data(username, week):
|
||||||
print(f"ERROR: Refresh data exception: {str(e)}", flush=True)
|
print(f"ERROR: Refresh data exception: {str(e)}", flush=True)
|
||||||
return jsonify({'error': str(e)}), 500
|
return jsonify({'error': str(e)}), 500
|
||||||
|
|
||||||
|
# Static file routes
|
||||||
|
@app.route('/gametime_logo.png')
|
||||||
|
def logo():
|
||||||
|
"""Serve the logo file"""
|
||||||
|
return send_file('gametime_logo.png', mimetype='image/png')
|
||||||
|
|
||||||
|
@app.route('/favicon.ico')
|
||||||
|
def favicon():
|
||||||
|
"""Serve the favicon"""
|
||||||
|
return send_file('gametime_logo.png', mimetype='image/png')
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print("DEBUG: Starting Flask app on 0.0.0.0:5000", flush=True)
|
print("DEBUG: Starting Flask app on 0.0.0.0:5000", flush=True)
|
||||||
print(f"DEBUG: GameTime {app.config['VERSION']} ready!", flush=True)
|
print(f"DEBUG: GameTime {app.config['VERSION']} ready!", flush=True)
|
||||||
|
|
|
||||||
BIN
gametime_logo.png
Normal file
BIN
gametime_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 190 KiB |
BIN
services/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
services/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
services/__pycache__/espn_api.cpython-313.pyc
Normal file
BIN
services/__pycache__/espn_api.cpython-313.pyc
Normal file
Binary file not shown.
BIN
services/__pycache__/sleeper_api.cpython-313.pyc
Normal file
BIN
services/__pycache__/sleeper_api.cpython-313.pyc
Normal file
Binary file not shown.
|
|
@ -76,8 +76,15 @@ class ESPNAPI:
|
||||||
else:
|
else:
|
||||||
tz_abbr = 'Local'
|
tz_abbr = 'Local'
|
||||||
|
|
||||||
# Extract team information
|
# Extract team information and game status
|
||||||
competitors = event['competitions'][0]['competitors']
|
competition = event['competitions'][0]
|
||||||
|
competitors = competition['competitors']
|
||||||
|
|
||||||
|
# Get game status information
|
||||||
|
status = competition.get('status', {})
|
||||||
|
game_state = status.get('type', {}).get('state', 'pre') # pre, in, post
|
||||||
|
is_live = game_state == 'in'
|
||||||
|
status_display = status.get('type', {}).get('name', 'Scheduled')
|
||||||
|
|
||||||
home_team = None
|
home_team = None
|
||||||
away_team = None
|
away_team = None
|
||||||
|
|
@ -102,7 +109,10 @@ class ESPNAPI:
|
||||||
'home_team': home_team,
|
'home_team': home_team,
|
||||||
'away_team': away_team,
|
'away_team': away_team,
|
||||||
'teams': [home_team, away_team],
|
'teams': [home_team, away_team],
|
||||||
'is_past': game_date_local < datetime.now(game_date_local.tzinfo)
|
'is_past': game_date_local < datetime.now(game_date_local.tzinfo),
|
||||||
|
'is_live': is_live,
|
||||||
|
'game_state': game_state,
|
||||||
|
'status_display': status_display
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"ESPN API: Error processing event {i+1}: {str(e)}", flush=True)
|
print(f"ESPN API: Error processing event {i+1}: {str(e)}", flush=True)
|
||||||
|
|
|
||||||
21
static/manifest.json
Normal file
21
static/manifest.json
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
"name": "GameTime - Fantasy Football Dashboard",
|
||||||
|
"short_name": "GameTime",
|
||||||
|
"description": "View your Sleeper fantasy football leagues and player schedules",
|
||||||
|
"start_url": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"background_color": "#1e1e2e",
|
||||||
|
"theme_color": "#8b7ff5",
|
||||||
|
"orientation": "portrait-primary",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/gametime_logo.png",
|
||||||
|
"sizes": "any",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "any maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"categories": ["sports", "games"],
|
||||||
|
"scope": "/",
|
||||||
|
"lang": "en"
|
||||||
|
}
|
||||||
|
|
@ -72,6 +72,14 @@
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app-logo {
|
||||||
|
max-width: 120px;
|
||||||
|
height: auto;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
.app-name {
|
.app-name {
|
||||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
|
|
@ -1337,4 +1345,34 @@ body {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Live game indicator */
|
||||||
|
.live-indicator {
|
||||||
|
display: inline-block;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background-color: #ff0000;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-left: 8px;
|
||||||
|
margin-right: 6px;
|
||||||
|
animation: pulse-red 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.live-text {
|
||||||
|
color: #ff0000;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 11px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-red {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 0.3;
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
93
static/sw.js
Normal file
93
static/sw.js
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
// GameTime Service Worker
|
||||||
|
const CACHE_NAME = 'gametime-v1.0.0';
|
||||||
|
const STATIC_CACHE = [
|
||||||
|
'/',
|
||||||
|
'/static/style.css',
|
||||||
|
'/static/manifest.json',
|
||||||
|
'/gametime_logo.png',
|
||||||
|
'/favicon.ico'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Install event - cache static resources
|
||||||
|
self.addEventListener('install', event => {
|
||||||
|
console.log('GameTime SW: Installing...');
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE_NAME)
|
||||||
|
.then(cache => {
|
||||||
|
console.log('GameTime SW: Caching static resources');
|
||||||
|
return cache.addAll(STATIC_CACHE);
|
||||||
|
})
|
||||||
|
.then(() => self.skipWaiting())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Activate event - clean up old caches
|
||||||
|
self.addEventListener('activate', event => {
|
||||||
|
console.log('GameTime SW: Activating...');
|
||||||
|
event.waitUntil(
|
||||||
|
caches.keys().then(cacheNames => {
|
||||||
|
return Promise.all(
|
||||||
|
cacheNames.map(cacheName => {
|
||||||
|
if (cacheName !== CACHE_NAME) {
|
||||||
|
console.log('GameTime SW: Deleting old cache:', cacheName);
|
||||||
|
return caches.delete(cacheName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}).then(() => self.clients.claim())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch event - network first, fallback to cache for static resources
|
||||||
|
self.addEventListener('fetch', event => {
|
||||||
|
const { request } = event;
|
||||||
|
|
||||||
|
// Skip non-GET requests
|
||||||
|
if (request.method !== 'GET') return;
|
||||||
|
|
||||||
|
// For API calls and dynamic content, use network first
|
||||||
|
if (request.url.includes('/api/') ||
|
||||||
|
request.url.includes('/dashboard') ||
|
||||||
|
request.url.includes('/refresh')) {
|
||||||
|
event.respondWith(
|
||||||
|
fetch(request)
|
||||||
|
.catch(() => {
|
||||||
|
// If offline, show a basic offline page
|
||||||
|
if (request.destination === 'document') {
|
||||||
|
return new Response(`
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>GameTime - Offline</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
</head>
|
||||||
|
<body style="font-family: sans-serif; text-align: center; padding: 50px;">
|
||||||
|
<h1>You're offline</h1>
|
||||||
|
<p>Please check your internet connection and try again.</p>
|
||||||
|
<button onclick="window.location.reload()">Retry</button>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`, {
|
||||||
|
headers: { 'Content-Type': 'text/html' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// For static resources, cache first
|
||||||
|
event.respondWith(
|
||||||
|
caches.match(request)
|
||||||
|
.then(response => {
|
||||||
|
return response || fetch(request)
|
||||||
|
.then(response => {
|
||||||
|
// Cache successful responses
|
||||||
|
if (response.status === 200) {
|
||||||
|
const responseClone = response.clone();
|
||||||
|
caches.open(CACHE_NAME)
|
||||||
|
.then(cache => cache.put(request, responseClone));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -5,6 +5,12 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{% block title %}GameTime{% endblock %}</title>
|
<title>{% block title %}GameTime{% endblock %}</title>
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.ico">
|
||||||
|
<link rel="apple-touch-icon" href="/gametime_logo.png">
|
||||||
|
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
|
||||||
|
<meta name="theme-color" content="#8b7ff5">
|
||||||
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- Hamburger Menu Button - Uses CSS-only solution -->
|
<!-- Hamburger Menu Button - Uses CSS-only solution -->
|
||||||
|
|
@ -42,13 +48,13 @@
|
||||||
🏈 Sleeper App
|
🏈 Sleeper App
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com" target="_blank" class="sidebar-link">
|
<a href="https://github.com" target="_blank" class="sidebar-link">
|
||||||
💻 GitHub
|
💻 Source Code
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- About Section -->
|
<!-- About Section -->
|
||||||
<div class="sidebar-section">
|
<div class="sidebar-section">
|
||||||
<h3>About This App</h3>
|
<h3>About This WebApp</h3>
|
||||||
<div class="about-text">
|
<div class="about-text">
|
||||||
<p>GameTime gives you an at-a-glance view of your Sleeper leagues player's schedules and status.</p>
|
<p>GameTime gives you an at-a-glance view of your Sleeper leagues player's schedules and status.</p>
|
||||||
<p><strong>How to use:</strong></p>
|
<p><strong>How to use:</strong></p>
|
||||||
|
|
@ -57,8 +63,9 @@
|
||||||
<li>View all your leagues' matchup scores at the top</li>
|
<li>View all your leagues' matchup scores at the top</li>
|
||||||
<li>See when each of your players plays, grouped by league (indicated by colored borders)</li>
|
<li>See when each of your players plays, grouped by league (indicated by colored borders)</li>
|
||||||
<li>Benched players are right-aligned and greyed out</li>
|
<li>Benched players are right-aligned and greyed out</li>
|
||||||
|
<li>Games and Days are collapsed if they're over.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p class="cron-note">* * * * 0,1,4</p>
|
<p class="cron-note">* * * * 4,7,1</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -165,6 +172,19 @@
|
||||||
gameContent.style.display = 'none';
|
gameContent.style.display = 'none';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Register service worker for PWA functionality
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
window.addEventListener('load', function() {
|
||||||
|
navigator.serviceWorker.register('/static/sw.js')
|
||||||
|
.then(function(registration) {
|
||||||
|
console.log('GameTime SW: Registered successfully');
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.log('GameTime SW: Registration failed:', error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -84,16 +84,22 @@
|
||||||
<div class="day-games" id="day-{{ day_key }}">
|
<div class="day-games" id="day-{{ day_key }}">
|
||||||
<!-- Games for this day -->
|
<!-- Games for this day -->
|
||||||
{% for game in day_info.games %}
|
{% for game in day_info.games %}
|
||||||
<div class="game-card {% if game.is_past %}collapsed{% endif %}" data-game="{{ loop.index }}">
|
<div class="game-card {% if game.is_past and not game.is_live %}collapsed{% endif %}" data-game="{{ loop.index }}">
|
||||||
<div class="game-header" onclick="toggleGame('{{ day_key }}', '{{ loop.index }}')">
|
<div class="game-header" onclick="toggleGame('{{ day_key }}', '{{ loop.index }}')">
|
||||||
<div class="game-info">
|
<div class="game-info">
|
||||||
<div class="game-time">{{ game.time }}</div>
|
<div class="game-time">
|
||||||
|
{{ game.time }}
|
||||||
|
{% if game.is_live %}
|
||||||
|
<span class="live-indicator"></span>
|
||||||
|
<span class="live-text">LIVE</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
<div class="matchup">
|
<div class="matchup">
|
||||||
<span class="away-team">{{ game.away_team }}</span>
|
<span class="away-team">{{ game.away_team }}</span>
|
||||||
<span class="at">@</span>
|
<span class="at">@</span>
|
||||||
<span class="home-team">{{ game.home_team }}</span>
|
<span class="home-team">{{ game.home_team }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="game-collapse-indicator">{% if game.is_past %}▶{% else %}▼{% endif %}</span>
|
<span class="game-collapse-indicator">{% if game.is_past and not game.is_live %}▶{% else %}▼{% endif %}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="game-content" id="game-{{ day_key }}-{{ loop.index }}">
|
<div class="game-content" id="game-{{ day_key }}-{{ loop.index }}">
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,11 @@
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="welcome-container">
|
<div class="welcome-container">
|
||||||
<div class="brand">
|
<div class="brand">
|
||||||
|
<img src="/gametime_logo.png" alt="GameTime Logo" class="app-logo">
|
||||||
<h1 class="app-name">GameTime</h1>
|
<h1 class="app-name">GameTime</h1>
|
||||||
<div class="tagline">* * * * 0,1,4</div>
|
<div class="tagline">* * * * 4,7,1</div>
|
||||||
</div>
|
</div>
|
||||||
<p>Enter your Sleeper username to view your fantasy teams</p>
|
<p>Enter your Sleeper username to get your league schedules.</p>
|
||||||
|
|
||||||
<!-- Username input form -->
|
<!-- Username input form -->
|
||||||
<form class="username-form" method="get" action="/dashboard">
|
<form class="username-form" method="get" action="/dashboard">
|
||||||
|
|
@ -17,7 +18,7 @@
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="example">
|
<div class="example">
|
||||||
<p>Example: <code>sleeperuser</code></p>
|
<p>Example: <code>@mySleeperUsername</code></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue