heystreamer/app/models.py
Bashy 6ad661c220 Initial commit — BashyOverlay v1
Twitch stream event platform with OBS browser source overlay.

Features:
- Twitch OAuth login (streamer + mod access)
- EventSub WebSocket for subs, gift subs, bits, channel points, follows, raids
- Chat commands via tmi.js in the overlay
- Gap audio — tone plays on new chat message after silence, volume/pitch scale with gap length
- Self-hosted media library (upload sounds and videos)
- Action configuration — map any event to sound, video, or alert
- Per-action cooldowns, test button, enable/disable toggle
- Multiple actions per event (all matching actions fire)
- Activity log dashboard with EventSub connection status
- Layout editor — iframe-based WYSIWYG with drag handles, live style preview
- Custom CSS and custom JS injection into overlay
- Custom DOM events (bashyoverlay:sub, bashyoverlay:raid, etc.) for custom JS hooks
- deploy.sh — one-shot setup and launch script

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 23:48:35 +03:00

79 lines
2.7 KiB
Python

from datetime import datetime
from typing import List, Optional
from sqlmodel import Field, Relationship, SQLModel
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
twitch_id: str = Field(unique=True, index=True)
twitch_login: str
twitch_display_name: str
broadcaster_id: str
access_token: str
refresh_token: str
is_admin: bool = False
created_at: datetime = Field(default_factory=datetime.utcnow)
class MediaFile(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
filename: str
original_name: str
file_type: str # "audio" | "video"
created_at: datetime = Field(default_factory=datetime.utcnow)
actions: List["EventAction"] = Relationship(back_populates="media_file")
class EventAction(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
event_type: str # "sub" | "gift_sub" | "bits" | "channel_points" | "command"
event_detail: Optional[str] = None # command name, reward ID, sub tier, bits minimum
action_type: str # "sound" | "video" | "alert"
media_file_id: Optional[int] = Field(default=None, foreign_key="mediafile.id")
alert_text: Optional[str] = None
cooldown_seconds: int = 0
enabled: bool = True
created_at: datetime = Field(default_factory=datetime.utcnow)
media_file: Optional["MediaFile"] = Relationship(back_populates="actions")
class ActivityLog(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
event_type: str
username: Optional[str] = None
detail: Optional[str] = None
action_taken: Optional[str] = None
triggered_at: datetime = Field(default_factory=datetime.utcnow)
class Settings(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
twitch_channel: str = ""
gap_silence_threshold: int = 30
gap_scale_end: int = 120
gap_pitch_scale_end: int = 300
custom_css: str = ""
custom_js: str = ""
class OverlayLayout(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
# Chat widget — position/size as % of canvas
chat_left: float = 72.0
chat_top: float = 5.0
chat_width: float = 27.0
chat_height: float = 88.0
chat_font_size: int = 14
chat_bg_opacity: float = 0.55
chat_text_color: str = "#ffffff"
chat_max_messages: int = 20
# Alert widget — position as % of canvas (centered via transform)
alert_left: float = 50.0
alert_top: float = 40.0
alert_font_size: int = 24
alert_bg_opacity: float = 0.8
alert_text_color: str = "#ffffff"
alert_duration: int = 4