init commit
This commit is contained in:
0
app/models/__init__.py
Normal file
0
app/models/__init__.py
Normal file
15
app/models/audit.py
Normal file
15
app/models/audit.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from sqlalchemy import ForeignKey, String, JSON, func, DateTime
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from app.db.session import Base
|
||||
|
||||
class AuditLog(Base):
|
||||
__tablename__ = "audit_logs"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
user_id: Mapped[Optional[int]] = mapped_column(ForeignKey("users.id", ondelete="SET NULL"))
|
||||
action: Mapped[str] = mapped_column(String(64))
|
||||
payload: Mapped[Optional[dict]] = mapped_column(JSON)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
17
app/models/bot.py
Normal file
17
app/models/bot.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from sqlalchemy import ForeignKey, String, func, DateTime
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from app.db.session import Base
|
||||
|
||||
class Bot(Base):
|
||||
__tablename__ = "bots"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
owner_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
|
||||
name: Mapped[str] = mapped_column(String(64))
|
||||
username: Mapped[str | None] = mapped_column(String(64))
|
||||
token_enc: Mapped[str] = mapped_column(String(512))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
owner = relationship("User")
|
||||
26
app/models/channel.py
Normal file
26
app/models/channel.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from sqlalchemy import ForeignKey, String, BigInteger, Boolean, UniqueConstraint, func, DateTime
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from app.db.session import Base
|
||||
|
||||
class Channel(Base):
|
||||
__tablename__ = "channels"
|
||||
__table_args__ = (UniqueConstraint("owner_id", "chat_id", name="uq_owner_chat"),)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
owner_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
|
||||
chat_id: Mapped[int] = mapped_column(BigInteger, index=True)
|
||||
title: Mapped[str | None] = mapped_column(String(128))
|
||||
username: Mapped[str | None] = mapped_column(String(64))
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
owner = relationship("User")
|
||||
|
||||
class BotChannel(Base):
|
||||
__tablename__ = "bot_channels"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
bot_id: Mapped[int] = mapped_column(ForeignKey("bots.id", ondelete="CASCADE"))
|
||||
channel_id: Mapped[int] = mapped_column(ForeignKey("channels.id", ondelete="CASCADE"))
|
||||
can_post: Mapped[bool] = mapped_column(Boolean, default=True)
|
||||
20
app/models/keyboard.py
Normal file
20
app/models/keyboard.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from __future__ import annotations
|
||||
from sqlalchemy import ForeignKey, Integer, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from app.db.session import Base
|
||||
|
||||
class Keyboard(Base):
|
||||
__tablename__ = "keyboards"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
owner_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
|
||||
name: Mapped[str] = mapped_column(String(64))
|
||||
|
||||
class KeyboardButton(Base):
|
||||
__tablename__ = "keyboard_buttons"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
keyboard_id: Mapped[int] = mapped_column(ForeignKey("keyboards.id", ondelete="CASCADE"))
|
||||
text: Mapped[str] = mapped_column(String(128))
|
||||
url: Mapped[str] = mapped_column(String(512))
|
||||
order_index: Mapped[int] = mapped_column(Integer, default=0)
|
||||
52
app/models/post.py
Normal file
52
app/models/post.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from __future__ import annotations
|
||||
import enum
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from sqlalchemy import ForeignKey, String, Enum, DateTime, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from app.db.session import Base
|
||||
|
||||
class PostType(str, enum.Enum):
|
||||
text = "text"
|
||||
photo = "photo"
|
||||
video = "video"
|
||||
animation = "animation"
|
||||
|
||||
class PostStatus(str, enum.Enum):
|
||||
draft = "draft"
|
||||
scheduled = "scheduled"
|
||||
sent = "sent"
|
||||
failed = "failed"
|
||||
|
||||
class Post(Base):
|
||||
__tablename__ = "posts"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
owner_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"))
|
||||
bot_id: Mapped[Optional[int]] = mapped_column(ForeignKey("bots.id", ondelete="SET NULL"), nullable=True)
|
||||
channel_id: Mapped[int] = mapped_column(ForeignKey("channels.id", ondelete="CASCADE"))
|
||||
|
||||
type: Mapped[PostType] = mapped_column(Enum(PostType))
|
||||
text: Mapped[Optional[str]] = mapped_column(String(4096))
|
||||
media_file_id: Mapped[Optional[str]] = mapped_column(String(512))
|
||||
parse_mode: Mapped[Optional[str]] = mapped_column(String(16))
|
||||
keyboard_id: Mapped[Optional[int]] = mapped_column(ForeignKey("keyboards.id", ondelete="SET NULL"))
|
||||
|
||||
status: Mapped[PostStatus] = mapped_column(Enum(PostStatus), default=PostStatus.draft)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
sent_at: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
class ScheduleStatus(str, enum.Enum):
|
||||
pending = "pending"
|
||||
done = "done"
|
||||
cancelled = "cancelled"
|
||||
|
||||
class Schedule(Base):
|
||||
__tablename__ = "schedules"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
post_id: Mapped[int] = mapped_column(ForeignKey("posts.id", ondelete="CASCADE"))
|
||||
due_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
||||
timezone: Mapped[str] = mapped_column(String(64))
|
||||
celery_task_id: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
status: Mapped[ScheduleStatus] = mapped_column(Enum(ScheduleStatus), default=ScheduleStatus.pending)
|
||||
33
app/models/templates.py
Normal file
33
app/models/templates.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from sqlalchemy import DateTime, Enum, ForeignKey, String, UniqueConstraint, func, Text, JSON, Boolean
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy.sql.sqltypes import Enum as EnumType # Явный импорт Enum для типизации
|
||||
from app.db.session import Base
|
||||
from app.models.post import PostType
|
||||
from enum import Enum as PyEnum
|
||||
|
||||
class TemplateVisibility(str, PyEnum):
|
||||
private = "private"
|
||||
org = "org"
|
||||
public = "public"
|
||||
|
||||
class Template(Base):
|
||||
__tablename__ = "templates"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("owner_id", "name", name="uq_template_owner_name"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
owner_id: Mapped[int] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True)
|
||||
name: Mapped[str] = mapped_column(String(64))
|
||||
title: Mapped[Optional[str]] = mapped_column(String(128))
|
||||
type: Mapped[PostType] = mapped_column(EnumType(PostType), default=PostType.text)
|
||||
content: Mapped[str] = mapped_column(Text)
|
||||
keyboard_tpl: Mapped[Optional[list[dict]]] = mapped_column(JSON, nullable=True)
|
||||
parse_mode: Mapped[Optional[str]] = mapped_column(String(16), default="HTML")
|
||||
visibility: Mapped[TemplateVisibility] = mapped_column(EnumType(TemplateVisibility), default=TemplateVisibility.private)
|
||||
is_archived: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
17
app/models/user.py
Normal file
17
app/models/user.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from __future__ import annotations
|
||||
from datetime import datetime
|
||||
from sqlalchemy import BigInteger, String, func, DateTime
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from app.db.session import Base
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
tg_user_id: Mapped[int] = mapped_column(BigInteger, unique=True, index=True)
|
||||
username: Mapped[str | None] = mapped_column(String(64))
|
||||
role: Mapped[str] = mapped_column(String(16), default="user")
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User(id={self.id}, tg_user_id={self.tg_user_id}, username={self.username}, role={self.role}, created_at={self.created_at})>"
|
||||
Reference in New Issue
Block a user