Compare commits
10 Commits
4e2c14275b
...
ef08ab7970
| Author | SHA1 | Date |
|---|---|---|
|
|
ef08ab7970 | |
|
|
d64bb1c84b | |
|
|
e2722c098b | |
|
|
9f58cf209f | |
|
|
ea5842d292 | |
|
|
1ac5f5c1f7 | |
|
|
08c231209c | |
|
|
35fbb4b695 | |
|
|
b62bf6fb34 | |
|
|
97aaaf1b85 |
File diff suppressed because it is too large
Load Diff
|
|
@ -10,20 +10,21 @@ class ParticipantController extends Controller
|
|||
{
|
||||
public static function createFromToken(Request $request, string $id)
|
||||
{
|
||||
if ($request->input('password') !== 'password123') {
|
||||
if ($request->input('password') !== env('ADMIN_PASSWORD')) {
|
||||
Log::info(env('ADMIN_PASSWORD'));
|
||||
return response()->json(['message' => 'Unauthorized: Wrong password'], 401);
|
||||
}
|
||||
|
||||
if(!Participant::where('id', $id)->exists()) {
|
||||
if(!Participant::where('user_id', $id)->exists()) {
|
||||
$participant = Participant::create([
|
||||
'user_id' => $id,
|
||||
'token' => (string) Str::ulid()
|
||||
'token' => uniqid()
|
||||
]);
|
||||
|
||||
return $participant->token;
|
||||
}
|
||||
else {
|
||||
$participant = Participant::where('id', $id)->first();
|
||||
$participant = Participant::where('user_id', $id)->first();
|
||||
return $participant->token;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,18 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class Participant extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
|
|
@ -18,18 +23,6 @@ class Participant extends Model
|
|||
'token',
|
||||
];
|
||||
|
||||
protected $appends = ['desperate'];
|
||||
|
||||
public function receiver()
|
||||
{
|
||||
return $this->belongsTo(Participant::class, 'giving_id');
|
||||
}
|
||||
|
||||
public function giver()
|
||||
{
|
||||
return $this->hasOne(Participant::class, 'giving_id', 'id');
|
||||
}
|
||||
|
||||
public static function findByToken(string $token): ?self
|
||||
{
|
||||
return self::where('token', $token)->first();
|
||||
|
|
@ -67,13 +60,60 @@ class Participant extends Model
|
|||
->whereDoesntHave('receiver');
|
||||
}
|
||||
|
||||
public function getDesperateAttribute(): bool
|
||||
public function receiver(): BelongsTo
|
||||
{
|
||||
$gives = !is_null($this->giver);
|
||||
$gets = !is_null($this->receiver);
|
||||
|
||||
return ($gives && !$gets) || (!$gives && $gets);
|
||||
return $this->belongsTo(Participant::class, 'giving_id');
|
||||
}
|
||||
|
||||
|
||||
public function giver(): HasOne
|
||||
{
|
||||
return $this->hasOne(Participant::class, 'giving_id', 'id');
|
||||
}
|
||||
|
||||
public function hasReceiver(): bool
|
||||
{
|
||||
return !is_null($this->giving_id); // The simplest and most direct check for an outgoing link
|
||||
}
|
||||
|
||||
public function hasGiver(): bool
|
||||
{
|
||||
return $this->hasOne(Participant::class, 'giving_id', 'id')
|
||||
->exists();
|
||||
}
|
||||
|
||||
protected function isUnmatched(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => !$this->hasReceiver() && !$this->hasGiver(),
|
||||
);
|
||||
}
|
||||
|
||||
protected function isDesperate(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn () => $this->hasReceiver() xor $this->hasGiver(),
|
||||
);
|
||||
}
|
||||
|
||||
public function withdraw(): bool
|
||||
{
|
||||
if (!$this->id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$giver = Participant::where('giving_id', $this->id)->first();
|
||||
if ($giver) {
|
||||
$giver->giving_id = null;
|
||||
$giver->save();
|
||||
}
|
||||
|
||||
$userB_id = $this->giving_id;
|
||||
if ($userB_id) {
|
||||
$this->giving_id = null;
|
||||
}
|
||||
|
||||
$this->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,92 +14,57 @@ class MatcherService
|
|||
return;
|
||||
}
|
||||
|
||||
// desperate denotes a user A that is supposed to give to a user B that dropped out
|
||||
// whilst user A has a user C giving to them, or vice versa
|
||||
$desperate = $participants->where('desperate', true)->values();
|
||||
$nonDesperate = $participants->where('desperate', false)->values();
|
||||
|
||||
$desperate = $desperate->shuffle();
|
||||
$nonDesperate = $nonDesperate->shuffle();
|
||||
|
||||
$assignments = [];
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 1. Match DESPERATE participants
|
||||
|--------------------------------------------------------------------------
|
||||
| A desperate user should match:
|
||||
| - First to another desperate participant
|
||||
| - If none left, match to a non-desperate participant
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
$newParticipantIds = $participants
|
||||
->filter(fn($p) => $p->is_unmatched)
|
||||
->pluck('id')
|
||||
->shuffle()
|
||||
->all();
|
||||
|
||||
foreach ($desperate as $p) {
|
||||
// Exclude self from possible options
|
||||
$pool = $desperate->filter(fn($d) => $d->id !== $p->id);
|
||||
$desperateGiverIds = $participants
|
||||
->filter(fn($p) => $p->is_desperate && !$p->hasReceiver())
|
||||
->pluck('id')
|
||||
->shuffle()
|
||||
->all();
|
||||
|
||||
if ($pool->isEmpty()) {
|
||||
$pool = $nonDesperate;
|
||||
}
|
||||
$desperateReceiverIds = $participants
|
||||
->filter(fn($p) => $p->is_desperate && !$p->hasGiver())
|
||||
->pluck('id')
|
||||
->shuffle()
|
||||
->all();
|
||||
|
||||
if ($pool->isEmpty()) {
|
||||
while (!empty($desperateGiverIds) && !empty($desperateReceiverIds)) {
|
||||
|
||||
$giverId = array_shift($desperateGiverIds);
|
||||
$receiverId = array_shift($desperateReceiverIds);
|
||||
|
||||
if ($giverId === $receiverId) {
|
||||
array_unshift($desperateReceiverIds, $receiverId);
|
||||
continue;
|
||||
}
|
||||
|
||||
$candidate = $this->pickValidTarget($p, $pool, $assignments);
|
||||
if ($candidate) {
|
||||
$assignments[$p->id] = $candidate->id;
|
||||
$assignments[$giverId] = $receiverId;
|
||||
}
|
||||
|
||||
$newCount = count($newParticipantIds);
|
||||
if ($newCount >= 2) {
|
||||
|
||||
for ($i = 0; $i < $newCount; $i++) {
|
||||
|
||||
$currentId = $newParticipantIds[$i];
|
||||
|
||||
$nextId = $newParticipantIds[($i + 1) % $newCount];
|
||||
|
||||
$assignments[$currentId] = $nextId;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 2. Match NON-DESPERATE participants
|
||||
|--------------------------------------------------------------------------
|
||||
| A non-desperate participant always matches to the non-desperate pool.
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
foreach ($nonDesperate as $p) {
|
||||
// Exclude self
|
||||
$pool = $nonDesperate->filter(fn($d) => $d->id !== $p->id);
|
||||
|
||||
if ($pool->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$candidate = $this->pickValidTarget($p, $pool, $assignments);
|
||||
if ($candidate) {
|
||||
$assignments[$p->id] = $candidate->id;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| 3. Write all results back to DB
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
foreach ($assignments as $id => $givingId) {
|
||||
Participant::where('id', $id)->update([
|
||||
'giving_id' => $givingId,
|
||||
foreach ($assignments as $giverId => $receiverId) {
|
||||
Participant::where('id', $giverId)->update([
|
||||
'giving_id' => $receiverId,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private function pickValidTarget(Participant $giver, Collection $pool, array $assignments): ?Participant
|
||||
{
|
||||
$shuffled = $pool->shuffle();
|
||||
|
||||
// Avoid duplicates
|
||||
foreach ($shuffled as $candidate) {
|
||||
if (($assignments[$candidate->id] ?? null) === $giver->id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
179
bot/main.py
179
bot/main.py
|
|
@ -1,5 +1,174 @@
|
|||
- get chats page
|
||||
- for each
|
||||
- if seen_at is less than latest[created_at]
|
||||
- get token url from api
|
||||
- respond to that user
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from typing import Optional, Dict, Any
|
||||
from dotenv import load_dotenv
|
||||
import urllib.parse
|
||||
import re
|
||||
|
||||
load_dotenv()
|
||||
|
||||
SKETCHERS_UNITED_SESSION = os.environ.get('SKETCHERS_UNITED_SESSION')
|
||||
REMEMBER_WEB = os.environ.get('REMEMBER_WEB')
|
||||
TARGET_URL = 'https://sketchersunited.org/chats'
|
||||
API_PASSWORD = os.environ.get('API_PASSWORD')
|
||||
XSRF_TOKEN = os.environ.get('XSRF_TOKEN')
|
||||
|
||||
def get_session_cookie_header() -> Optional[Dict[str, str]]:
|
||||
if not SKETCHERS_UNITED_SESSION or not REMEMBER_WEB:
|
||||
print("Missing required session or remember_web environment variables.", file=sys.stderr)
|
||||
return None
|
||||
|
||||
cookies_to_send = []
|
||||
cookies_to_send.append(f"sketchers_united_session={SKETCHERS_UNITED_SESSION}")
|
||||
cookies_to_send.append(f"remember_web_59ba36addc2b2f9401580f014c7f58ea4e30989d={REMEMBER_WEB}")
|
||||
|
||||
if XSRF_TOKEN:
|
||||
cookies_to_send.append(f"XSRF-TOKEN={XSRF_TOKEN}")
|
||||
|
||||
cookie_header = "; ".join(cookies_to_send)
|
||||
|
||||
return {
|
||||
'Cookie': cookie_header,
|
||||
'Accept': 'application/json',
|
||||
'User-Agent': 'usernames chat checking bot',
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
|
||||
def get_post_headers(xsrf_token: str) -> Dict[str, str]:
|
||||
return {
|
||||
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:133.0) Gecko/20100101 Firefox/133.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'en-US,en;q=0.5',
|
||||
'X-XSRF-TOKEN': xsrf_token,
|
||||
'Sec-GPC': '1',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'Priority': 'u=0',
|
||||
}
|
||||
|
||||
async def make_new_json_request(user_id: int):
|
||||
url = f'http://localhost/api/token/{user_id}'
|
||||
data = {
|
||||
'password': API_PASSWORD
|
||||
}
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post(url, json=data, headers=headers) as response:
|
||||
response_text = await response.text()
|
||||
if(len(response_text) < 14):
|
||||
return response_text
|
||||
else:
|
||||
return False
|
||||
|
||||
async def post_chat_update(session: aiohttp.ClientSession, chat_object: Dict[str, Any], xsrf_token: str):
|
||||
pattern = r'/(\d+)/'
|
||||
match = re.search(pattern, chat_object.get('icon_url'))
|
||||
|
||||
token = await make_new_json_request(match.group(1))
|
||||
form_data = aiohttp.FormData()
|
||||
if(token):
|
||||
form_data.add_field('text', f"""You've signed up for the Secret Sketchers event, your link is http://localhost/profile/{token} - it is private and should only be used by you.
|
||||
\nDon't want to be in it anymore? Go to http://localhost/withdraw/{token} - note that withdrawing is permanent and you cannot re-enter the event.""")
|
||||
else:
|
||||
form_data.add_field('text', f"""Something went wrong, Sorry I will fix it sooner or later... maybe""")
|
||||
|
||||
headers = get_post_headers(xsrf_token)
|
||||
|
||||
try:
|
||||
cookie_headers = get_session_cookie_header()
|
||||
if cookie_headers:
|
||||
headers['Cookie'] = cookie_headers['Cookie']
|
||||
|
||||
async with session.post(
|
||||
f"{TARGET_URL}/{chat_object.get('id')}/messages",
|
||||
data=form_data,
|
||||
headers=headers
|
||||
) as response:
|
||||
if response.status not in [200, 201]:
|
||||
text = await response.text()
|
||||
print(f" Status: {response.status}. Response: {text}", file=sys.stderr)
|
||||
except aiohttp.ClientError as e:
|
||||
print(f"-> POST ERROR: Client error during update for chat ID {chat_object.get('id', 'N/A')}: {e}", file=sys.stderr)
|
||||
except Exception as e:
|
||||
print(f"-> POST ERROR: Unexpected error during update for chat ID {chat_object.get('id', 'N/A')}: {e}", file=sys.stderr)
|
||||
|
||||
|
||||
|
||||
async def process_chats():
|
||||
global XSRF_TOKEN
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
headers = get_session_cookie_header()
|
||||
if not headers:
|
||||
return
|
||||
|
||||
async with session.get(TARGET_URL, headers=headers) as response:
|
||||
if response.status != 200:
|
||||
print(f"failed: {response.status}", file=sys.stderr)
|
||||
return
|
||||
|
||||
try:
|
||||
chat_data_array = await response.json()
|
||||
if not isinstance(chat_data_array, list):
|
||||
print("not json", file=sys.stderr)
|
||||
return
|
||||
|
||||
except aiohttp.ContentTypeError:
|
||||
print("also not json", file=sys.stderr)
|
||||
return
|
||||
|
||||
fresh_xsrf_token = None
|
||||
for cookie in session.cookie_jar:
|
||||
if cookie.key == "XSRF-TOKEN":
|
||||
fresh_xsrf_token = urllib.parse.unquote(cookie.value)
|
||||
break
|
||||
|
||||
XSRF_TOKEN = fresh_xsrf_token
|
||||
|
||||
for index, chat_object in enumerate(chat_data_array):
|
||||
if(chat_object.get('type') == 'private'):
|
||||
seen_at_value = chat_object.get('seen_at')
|
||||
is_unread = False
|
||||
|
||||
try:
|
||||
if isinstance(seen_at_value, dict):
|
||||
is_unread = True
|
||||
# print(f"Chat {index}: Status: DICT (New Chat) -> Unread: True")
|
||||
else:
|
||||
latest_created_at = chat_object.get('latest', {}).get('created_at', 0)
|
||||
seen_at_int = int(seen_at_value) if seen_at_value is not None else 0
|
||||
|
||||
is_unread = seen_at_int < latest_created_at
|
||||
# print(f"Chat {index}: Seen at ({seen_at_int}) < Latest message ({latest_created_at}) -> Unread: {is_unread}")
|
||||
|
||||
except (AttributeError, TypeError, ValueError) as e:
|
||||
is_unread = False
|
||||
|
||||
if is_unread:
|
||||
await post_chat_update(session, chat_object, fresh_xsrf_token)
|
||||
|
||||
|
||||
async def main_loop():
|
||||
while True:
|
||||
try:
|
||||
await process_chats()
|
||||
await asyncio.sleep(5)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
print("cancelled normally (ok knowing my luck the error is gonna be this one every single time even tho it never actually is an error like this!) cute puppy: Hiiii")
|
||||
break
|
||||
except Exception as e:
|
||||
print(f"error {e}")
|
||||
await asyncio.sleep(5)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main_loop())
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@
|
|||
"ext-curl": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"barryvdh/laravel-ide-helper": "^3.6",
|
||||
"fakerphp/faker": "^1.23",
|
||||
"laravel/pail": "^1.2.2",
|
||||
"laravel/pint": "^1.24",
|
||||
|
|
@ -25,8 +27,7 @@
|
|||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.6",
|
||||
"pestphp/pest": "^4.1",
|
||||
"pestphp/pest-plugin-laravel": "^4.0",
|
||||
"ext-curl": "*"
|
||||
"pestphp/pest-plugin-laravel": "^4.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "c78bcab45d4b86c31ecc79b40f1a12ad",
|
||||
"content-hash": "10384b5e2e1414abcd436d205f0114a6",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
|
|
@ -6496,6 +6496,152 @@
|
|||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "barryvdh/laravel-ide-helper",
|
||||
"version": "v3.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/laravel-ide-helper.git",
|
||||
"reference": "8d00250cba25728373e92c1d8dcebcbf64623d29"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/8d00250cba25728373e92c1d8dcebcbf64623d29",
|
||||
"reference": "8d00250cba25728373e92c1d8dcebcbf64623d29",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"barryvdh/reflection-docblock": "^2.4",
|
||||
"composer/class-map-generator": "^1.0",
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^11.15 || ^12",
|
||||
"illuminate/database": "^11.15 || ^12",
|
||||
"illuminate/filesystem": "^11.15 || ^12",
|
||||
"illuminate/support": "^11.15 || ^12",
|
||||
"php": "^8.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-pdo_sqlite": "*",
|
||||
"friendsofphp/php-cs-fixer": "^3",
|
||||
"illuminate/config": "^11.15 || ^12",
|
||||
"illuminate/view": "^11.15 || ^12",
|
||||
"mockery/mockery": "^1.4",
|
||||
"orchestra/testbench": "^9.2 || ^10",
|
||||
"phpunit/phpunit": "^10.5 || ^11.5.3",
|
||||
"spatie/phpunit-snapshot-assertions": "^4 || ^5",
|
||||
"vimeo/psalm": "^5.4",
|
||||
"vlucas/phpdotenv": "^5"
|
||||
},
|
||||
"suggest": {
|
||||
"illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "3.5-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Barryvdh\\LaravelIdeHelper\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Barry vd. Heuvel",
|
||||
"email": "barryvdh@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.",
|
||||
"keywords": [
|
||||
"autocomplete",
|
||||
"codeintel",
|
||||
"dev",
|
||||
"helper",
|
||||
"ide",
|
||||
"laravel",
|
||||
"netbeans",
|
||||
"phpdoc",
|
||||
"phpstorm",
|
||||
"sublime"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/barryvdh/laravel-ide-helper/issues",
|
||||
"source": "https://github.com/barryvdh/laravel-ide-helper/tree/v3.6.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://fruitcake.nl",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/barryvdh",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-17T20:11:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "barryvdh/reflection-docblock",
|
||||
"version": "v2.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/barryvdh/ReflectionDocBlock.git",
|
||||
"reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/d103774cbe7e94ddee7e4870f97f727b43fe7201",
|
||||
"reference": "d103774cbe7e94ddee7e4870f97f727b43fe7201",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^8.5.14|^9"
|
||||
},
|
||||
"suggest": {
|
||||
"dflydev/markdown": "~1.0",
|
||||
"erusev/parsedown": "~1.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Barryvdh": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike van Riel",
|
||||
"email": "mike.vanriel@naenius.com"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/barryvdh/ReflectionDocBlock/tree/v2.4.0"
|
||||
},
|
||||
"time": "2025-07-17T06:07:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brianium/paratest",
|
||||
"version": "v7.15.0",
|
||||
|
|
@ -6589,6 +6735,154 @@
|
|||
],
|
||||
"time": "2025-11-30T08:08:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/class-map-generator",
|
||||
"version": "1.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/class-map-generator.git",
|
||||
"reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/class-map-generator/zipball/2373419b7709815ed323ebf18c3c72d03ff4a8a6",
|
||||
"reference": "2373419b7709815ed323ebf18c3c72d03ff4a8a6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"composer/pcre": "^2.1 || ^3.1",
|
||||
"php": "^7.2 || ^8.0",
|
||||
"symfony/finder": "^4.4 || ^5.3 || ^6 || ^7 || ^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.12 || ^2",
|
||||
"phpstan/phpstan-deprecation-rules": "^1 || ^2",
|
||||
"phpstan/phpstan-phpunit": "^1 || ^2",
|
||||
"phpstan/phpstan-strict-rules": "^1.1 || ^2",
|
||||
"phpunit/phpunit": "^8",
|
||||
"symfony/filesystem": "^5.4 || ^6 || ^7 || ^8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Composer\\ClassMapGenerator\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "https://seld.be"
|
||||
}
|
||||
],
|
||||
"description": "Utilities to scan PHP code and generate class maps.",
|
||||
"keywords": [
|
||||
"classmap"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/composer/class-map-generator/issues",
|
||||
"source": "https://github.com/composer/class-map-generator/tree/1.7.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://packagist.com",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/composer",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-11-19T10:41:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/pcre",
|
||||
"version": "3.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/pcre.git",
|
||||
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
|
||||
"reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan": "<1.11.10"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.12 || ^2",
|
||||
"phpstan/phpstan-strict-rules": "^1 || ^2",
|
||||
"phpunit/phpunit": "^8 || ^9"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"phpstan": {
|
||||
"includes": [
|
||||
"extension.neon"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "3.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Composer\\Pcre\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jordi Boggiano",
|
||||
"email": "j.boggiano@seld.be",
|
||||
"homepage": "http://seld.be"
|
||||
}
|
||||
],
|
||||
"description": "PCRE wrapping library that offers type-safe preg_* replacements.",
|
||||
"keywords": [
|
||||
"PCRE",
|
||||
"preg",
|
||||
"regex",
|
||||
"regular expression"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/composer/pcre/issues",
|
||||
"source": "https://github.com/composer/pcre/tree/3.3.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://packagist.com",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/composer",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-12T16:29:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
"version": "1.1.5",
|
||||
|
|
@ -9840,5 +10134,5 @@
|
|||
"platform-dev": {
|
||||
"ext-curl": "*"
|
||||
},
|
||||
"plugin-api-version": "2.6.0"
|
||||
"plugin-api-version": "2.9.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class ParticipantFactory extends Factory
|
|||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'token' => (string) Str::ulid(),
|
||||
'token' => uniqid(),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ return new class extends Migration
|
|||
|
||||
$table->string('token');
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 9.6 KiB |
|
|
@ -1,6 +1,19 @@
|
|||
@livewireStyles
|
||||
|
||||
<flux:main>
|
||||
<!--
|
||||
######
|
||||
#### ### ####
|
||||
## ## ## ##
|
||||
## ## ## ##
|
||||
## ## ### ##
|
||||
## ## ## ##
|
||||
## ## ## ##
|
||||
#### ##########
|
||||
###### ##
|
||||
### ###
|
||||
########
|
||||
-->
|
||||
{{ $slot }}
|
||||
</flux:main>
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ rules([
|
|||
],
|
||||
]);
|
||||
|
||||
mount(function ($token) {
|
||||
mount(function ($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
if ($this->token) {
|
||||
$this->participant = Participant::findByToken($this->token);
|
||||
|
|
@ -50,7 +51,8 @@ mount(function ($token) {
|
|||
}
|
||||
});
|
||||
|
||||
$saveEditingPrompt = function () {
|
||||
$saveEditingPrompt = function ()
|
||||
{
|
||||
if ($this->participant) {
|
||||
$this->validate();
|
||||
|
||||
|
|
@ -60,12 +62,14 @@ $saveEditingPrompt = function () {
|
|||
}
|
||||
};
|
||||
|
||||
$cancelEditingPrompt = function () {
|
||||
$cancelEditingPrompt = function ()
|
||||
{
|
||||
$this->prompt = $this->participant->prompt ?? '';
|
||||
$this->isEditingPrompt = false;
|
||||
};
|
||||
|
||||
$saveEditingSubmission = function () {
|
||||
$saveEditingSubmission = function ()
|
||||
{
|
||||
if ($this->participant) {
|
||||
$this->validate();
|
||||
|
||||
|
|
@ -75,7 +79,8 @@ $saveEditingSubmission = function () {
|
|||
}
|
||||
};
|
||||
|
||||
$cancelEditingSubmission = function () {
|
||||
$cancelEditingSubmission = function ()
|
||||
{
|
||||
$this->submissionUrl = $this->participant->submission_url ?? '';
|
||||
$this->isEditingSubmission = false;
|
||||
};
|
||||
|
|
@ -91,7 +96,6 @@ $cancelEditingSubmission = function () {
|
|||
</div>
|
||||
<div class="username-container">
|
||||
You are... <h1 style="margin-bottom:0;padding:0"> @{{ $userData['data']['username'] ?? 'Unknown' }}</h1>
|
||||
<small style="color:rgba(255,255,255,0.5);">your password is {{$token}}</small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -158,9 +162,6 @@ $cancelEditingSubmission = function () {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
- withdraw
|
||||
- bot
|
||||
|
||||
<div class="other-content">
|
||||
<h2> Are you done? </h2>
|
||||
<p>You will be able to link to your gift below once submissions are accepted on 22nd December.</p>
|
||||
|
|
@ -202,14 +203,17 @@ $cancelEditingSubmission = function () {
|
|||
@elseif($participant->desperate)
|
||||
<p>You're in a bind!</p>
|
||||
@else
|
||||
<p>You haven't been assigned anyone.</p>
|
||||
<p>You haven't been assigned anyone yet. Initial assignments will be made on 13th December.</p>
|
||||
@endif
|
||||
</div>
|
||||
<div class="other-content">
|
||||
<a
|
||||
href="/withdraw"
|
||||
href="/withdraw/{{$token}}"
|
||||
>
|
||||
Withdraw from Secret Sketchers...
|
||||
Withdraw from Secret Sketchers...?
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@endif
|
||||
|
||||
<style>
|
||||
|
|
@ -234,12 +238,10 @@ $cancelEditingSubmission = function () {
|
|||
margin: 15px;
|
||||
}
|
||||
|
||||
/* Add spacing below the main prompt text */
|
||||
.prompt-content {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Reset H2 margin for better spacing */
|
||||
.prompt-content h2 {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
|
@ -275,51 +277,15 @@ $cancelEditingSubmission = function () {
|
|||
}
|
||||
|
||||
body {
|
||||
background-color: #222;
|
||||
background-color: #021202;
|
||||
margin: 10pt;
|
||||
font-family: sans-serif;
|
||||
background-image: url("../img/christmas-bg.png");
|
||||
background-size: 140px;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#viewer {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #222;
|
||||
background-image: url("image/su.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 400px;
|
||||
background-position: center;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.r6o-editor {
|
||||
z-index: 3;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.r6o-widget.comment {
|
||||
font-size: 1.5em;
|
||||
background-color: #333
|
||||
}
|
||||
|
||||
.r6o-editor .r6o-arrow:after {
|
||||
background-color: #333;
|
||||
border: 3px white solid;
|
||||
}
|
||||
|
||||
.r6o-editor .r6o-editor-inner .r6o-widget {
|
||||
border: 3px white solid;
|
||||
}
|
||||
|
||||
.r6o-editor .r6o-editor-inner {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.r6o-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div {
|
||||
color: white;
|
||||
}
|
||||
|
|
@ -329,183 +295,5 @@ $cancelEditingSubmission = function () {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
#here-now {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
font-size: 1.5vh;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
z-index: 2;
|
||||
background-color:#333;
|
||||
border-radius: 1rem;
|
||||
border: 3px solid white;
|
||||
margin: 5pt;
|
||||
}
|
||||
|
||||
#question {
|
||||
font-size: 3vh;
|
||||
width: 3vh;
|
||||
height: 3vh;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: 3px solid white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#question img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#banner {
|
||||
max-width: 100%;
|
||||
}
|
||||
#modal {
|
||||
display: none;
|
||||
font-size: 18pt;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
justify-content: center;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0 10px 10px #000;
|
||||
background-color: #333;
|
||||
align-items: center;
|
||||
z-index: 4;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#modal-content {
|
||||
position: relative;
|
||||
padding: 33px;
|
||||
text-align: center;
|
||||
max-height: 100%;
|
||||
font-size: 15pt;
|
||||
}
|
||||
|
||||
@keyframes animateBg {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 100% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.rainbow {
|
||||
background: linear-gradient(90deg, #f1672b, #fea419, #efd515, #89d842, #35c2f9, #9b5fe0, #ff68cf, #f1672b, #fea419, #efd515, #89d842, #35c2f9, #9b5fe0, #ff68cf, #f1672b, #fea419, #efd515, #89d842, #35c2f9, #9b5fe0, #ff68cf, #f1672b);
|
||||
background-size: 300% 100%;
|
||||
-webkit-background-clip: text;
|
||||
animation: animateBg 5s infinite linear;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.profile-container {
|
||||
display:flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.profileimg {
|
||||
flex: 0 0 20%; /* 10% of the container's width, not flexible, not growing, not shrinking */
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.profileimg img {
|
||||
width: 100%; /* Make sure the image fills its container */
|
||||
height: auto; /* Maintain aspect ratio */
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.profile {
|
||||
flex: 1; /* Take up remaining space */
|
||||
padding-left: 1rem; /* Add some space between image and text */
|
||||
}
|
||||
|
||||
#close-button {
|
||||
position: absolute;
|
||||
font-size: 3rem;
|
||||
top: 15px;
|
||||
right: 30px;
|
||||
cursor: pointer;
|
||||
width: 3vh;
|
||||
height: 3vh;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: 3px solid white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#open-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#mobile-banner {
|
||||
display:none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1081px) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
#viewer {
|
||||
width: 100vw;
|
||||
height: 66vh;
|
||||
border-bottom: 3px white solid;
|
||||
}
|
||||
#modal-content {
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
#container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#mobile-banner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
height: 27vh;
|
||||
overflow: hidden;
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
#mobile-banner img {
|
||||
max-width: 90%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
}</style>
|
||||
</style>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,9 +5,15 @@ use App\Services\MatcherService;
|
|||
use Illuminate\Database\Eloquent\Collection;
|
||||
use function Livewire\Volt\{state, mount};
|
||||
|
||||
state(['participants' => Collection::class]);
|
||||
state(['participants' => Collection::class,
|
||||
'render' => false]);
|
||||
state(['password']);
|
||||
|
||||
mount(function () {
|
||||
mount(function ($password) {
|
||||
$this->password = $password;
|
||||
if ($this->password == env('ADMIN_PASSWORD')) {
|
||||
$this->render = true;
|
||||
}
|
||||
$this->participants = Participant::all();
|
||||
});
|
||||
|
||||
|
|
@ -22,6 +28,7 @@ $runMatch = function () {
|
|||
?>
|
||||
|
||||
<div class="p-6 bg-gray-100 min-h-screen">
|
||||
@if($render)
|
||||
<div class="max-w-7xl mx-auto">
|
||||
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
|
|
@ -88,8 +95,8 @@ $runMatch = function () {
|
|||
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm">
|
||||
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
|
||||
{{ $participant->desperate ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' }}">
|
||||
{{ $participant->desperate ? 'YES' : 'No' }}
|
||||
{{ $participant->is_desperate ? 'bg-red-100 text-red-800' : 'bg-green-100 text-green-800' }}">
|
||||
{{ $participant->is_desperate ? 'YES' : 'No' }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
|
|
@ -102,4 +109,5 @@ $runMatch = function () {
|
|||
</table>
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1 +1,71 @@
|
|||
<?php
|
||||
|
||||
use App\Models\Participant;
|
||||
use App\Services\PostService;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use function Livewire\Volt\{state, mount, rules};
|
||||
|
||||
state(['token']);
|
||||
|
||||
|
||||
state([
|
||||
'participant' => null,
|
||||
]);
|
||||
|
||||
mount(function ($token)
|
||||
{
|
||||
$this->token = $token;
|
||||
if ($this->token) {
|
||||
$this->participant = Participant::findByToken($this->token);
|
||||
}
|
||||
});
|
||||
|
||||
$confirmWithdrawal = function ()
|
||||
{
|
||||
$this->js(<<<'JS'
|
||||
if (confirm('Are you sure you want to withdraw from the event? This action cannot be undone.')) {
|
||||
$wire.call('withdraw');
|
||||
}
|
||||
JS);
|
||||
};
|
||||
|
||||
$withdraw = function ()
|
||||
{
|
||||
if($this->participant->withdraw()) {
|
||||
$this->js('alert("Withdrawal complete")');
|
||||
return $this->redirect('/', navigate: true);
|
||||
}
|
||||
else {
|
||||
$this->js('alert("Something went wrong")');
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
?>
|
||||
|
||||
<div>
|
||||
<h1>Withdraw from the event?</h1>
|
||||
<p>Withdrawing is permanent, once you withdraw you cannot rejoin.</p>
|
||||
<button
|
||||
wire:click="confirmWithdrawal"
|
||||
class=""
|
||||
>
|
||||
Withdraw
|
||||
</button>
|
||||
|
||||
<style>
|
||||
body {
|
||||
background-color: #021202;
|
||||
margin: 10pt;
|
||||
font-family: sans-serif;
|
||||
background-image: url("../img/christmas-bg.png");
|
||||
background-size: 140px;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}</style>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,24 +9,328 @@
|
|||
<link rel="icon" href="/favicon.ico" sizes="any">
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml">
|
||||
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
||||
<style>
|
||||
.user-profile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 100%;
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
.other-content {
|
||||
display: block;
|
||||
padding: 15px;
|
||||
background-color: rgba(0,0,0,0.3);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
max-width: 100%;
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
/* Add spacing below the main prompt text */
|
||||
.prompt-content {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Reset H2 margin for better spacing */
|
||||
.prompt-content h2 {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin-right: 15px;
|
||||
background-color: #ccc;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.avatar-container img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.username-container {
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.username-container h1 {
|
||||
margin: 0;
|
||||
font-size: 1.2em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #021202;
|
||||
margin: 10pt;
|
||||
font-family: sans-serif;
|
||||
background-image: url("./img/christmas-bg.png");
|
||||
background-size: 140px;
|
||||
color: white;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h2, h3 {
|
||||
color: #ca494c
|
||||
}
|
||||
|
||||
#viewer {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #222;
|
||||
|
||||
background-repeat: no-repeat;
|
||||
background-size: 400px;
|
||||
background-position: center;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
||||
.r6o-editor {
|
||||
z-index: 3;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.r6o-widget.comment {
|
||||
font-size: 1.5em;
|
||||
background-color: #333
|
||||
}
|
||||
|
||||
.r6o-editor .r6o-arrow:after {
|
||||
background-color: #333;
|
||||
border: 3px white solid;
|
||||
}
|
||||
|
||||
.r6o-editor .r6o-editor-inner .r6o-widget {
|
||||
border: 3px white solid;
|
||||
}
|
||||
|
||||
.r6o-editor .r6o-editor-inner {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.r6o-footer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div {
|
||||
color: white;
|
||||
}
|
||||
|
||||
a {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#here-now {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
font-size: 1.5vh;
|
||||
color: white;
|
||||
padding: 15px;
|
||||
z-index: 2;
|
||||
background-color:#333;
|
||||
border-radius: 1rem;
|
||||
border: 3px solid white;
|
||||
margin: 5pt;
|
||||
}
|
||||
|
||||
#question {
|
||||
font-size: 3vh;
|
||||
width: 3vh;
|
||||
height: 3vh;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: 3px solid white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#question img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#banner {
|
||||
max-width: 100%;
|
||||
}
|
||||
#modal {
|
||||
display: none;
|
||||
font-size: 18pt;
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
justify-content: center;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 0 10px 10px #000;
|
||||
background-color: #333;
|
||||
align-items: center;
|
||||
z-index: 4;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#modal-content {
|
||||
position: relative;
|
||||
padding: 33px;
|
||||
text-align: center;
|
||||
max-height: 100%;
|
||||
font-size: 15pt;
|
||||
}
|
||||
|
||||
@keyframes animateBg {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: 100% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.rainbow {
|
||||
background: linear-gradient(90deg, #f1672b, #fea419, #efd515, #89d842, #35c2f9, #9b5fe0, #ff68cf, #f1672b, #fea419, #efd515, #89d842, #35c2f9, #9b5fe0, #ff68cf, #f1672b, #fea419, #efd515, #89d842, #35c2f9, #9b5fe0, #ff68cf, #f1672b);
|
||||
background-size: 300% 100%;
|
||||
-webkit-background-clip: text;
|
||||
animation: animateBg 5s infinite linear;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.profile-container {
|
||||
display:flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.profileimg {
|
||||
flex: 0 0 20%; /* 10% of the container's width, not flexible, not growing, not shrinking */
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.profileimg img {
|
||||
width: 100%; /* Make sure the image fills its container */
|
||||
height: auto; /* Maintain aspect ratio */
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.profile {
|
||||
flex: 1; /* Take up remaining space */
|
||||
padding-left: 1rem; /* Add some space between image and text */
|
||||
}
|
||||
|
||||
#close-button {
|
||||
position: absolute;
|
||||
font-size: 3rem;
|
||||
top: 15px;
|
||||
right: 30px;
|
||||
cursor: pointer;
|
||||
width: 3vh;
|
||||
height: 3vh;
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
border: 3px solid white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
#open-button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#mobile-banner {
|
||||
display:none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1081px) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
#viewer {
|
||||
width: 100vw;
|
||||
height: 66vh;
|
||||
border-bottom: 3px white solid;
|
||||
}
|
||||
#modal-content {
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
#container {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#mobile-banner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
height: 27vh;
|
||||
overflow: hidden;
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
#mobile-banner img {
|
||||
max-width: 90%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
}</style>
|
||||
</head>
|
||||
<body style="background-color: black; color: white; ">
|
||||
<body>
|
||||
<div>
|
||||
<h1>Sketchers United Secret Santa</h1>
|
||||
<p>To sign up please DM <a href ="https://sketchersunited.org/users/646464646464">this user</a> to get a sign in link</p>
|
||||
<h1>FAQ</h1>
|
||||
<h2>How does it work</h2>
|
||||
<h1>Sketchers United's Secret Sketcher Event 2025</h1>
|
||||
<p>To sign up please DM anything to <a href ="https://sketchersunited.org/users/277827">this bot</a> to get a sign in link</p>
|
||||
<hr>
|
||||
<h2>FAQ</h2>
|
||||
<h3>How does it work</h3>
|
||||
<p>Lalalala</p>
|
||||
<h2>How long do I have to do it</h2>
|
||||
<h3>How long do I have to do it</h3>
|
||||
<p>Lalalala</p>
|
||||
<h2>Can I withdraw</h2>
|
||||
<h3>Can I withdraw</h3>
|
||||
<p>Lalalala</p>
|
||||
<h2>Can I change who I'm drawing for </h2>
|
||||
<h3>Can I change who I'm drawing for </h3>
|
||||
<p>Lalalala</p>
|
||||
<h2>Any other questions </h2>
|
||||
<h3>Any other questions </h3>
|
||||
<p>Lalalala</p>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ Route::get('/', function () {
|
|||
})->name('home');
|
||||
|
||||
Volt::route('start', 'pages.start')->name('start');
|
||||
Volt::route('table', 'pages.table')->name('table');
|
||||
Volt::route('table/{password}', 'pages.table')->name('table');
|
||||
Volt::route('profile/{token}', 'pages.profile')->name('profile');
|
||||
Volt::route('withdraw', 'pages.withdraw')->name('withdraw');
|
||||
Volt::route('withdraw/{token}', 'pages.withdraw')->name('withdraw');
|
||||
|
|
|
|||
Loading…
Reference in New Issue