Config scan from bot channel implementation

This commit is contained in:
BreadTube 2025-09-23 22:48:35 +09:00 committed by Corentin
commit 157e8c1b17
6 changed files with 453 additions and 34 deletions

View file

@ -16,11 +16,16 @@ class FileMime(Enum):
TEXT_HTML = 'text/html'
TEXT_MARKDOWN = 'text/markdown'
TEXT_PLAIN = 'text/plain'
UNKNOWN = 'application/unknown'
VIDEO_MP4 = 'video/mp4'
VIDEO_MPEG = 'video/mpeg'
VIDEO_WEBM = 'video/webm'
ZIP = 'application/zip'
@classmethod
def _missing_(cls, value): # noqa: ARG003
return FileMime.UNKNOWN
class ChannelType(Enum):
GUILD_TEXT = 0
@ -256,6 +261,144 @@ class User: # TODO : complete attributes
global_name=info.get('global_name'))
class AttachmentFlags(IntFlag):
IS_REMIX = 1 << 2 # this attachment has been edited using the remix feature on mobile
@dataclass
class Attachment:
id: int # attachment id
filename: str # name of file attached
title: str | None # the title of the file
description: str | None # description for the file (max 1024 characters)
content_type: str | None # the attachment's media type
size: int # size of file in bytes
url: str # source url of file
proxy_url: str # a proxied url of file
height: int | None # height of file (if image)
width: int | None # width of file (if image)
ephemeral: bool | None # whether this attachment is ephemeral
duration_secs: float | None # the duration of the audio file (currently for voice messages)
waveform: str | None # base64 encoded bytearray representing a sampled waveform (currently for voice messages)
flags: int | None # attachment flags combined as a bitfield
@staticmethod
def from_dict(info: dict) -> Attachment:
height = info.get('height')
width = info.get('width')
duraction_secs = info.get('duration_secs')
flags = info.get('flags')
return Attachment(
id=int(info['id']),
filename=info['filename'],
title=info.get('title'),
description=info.get('description'),
content_type=info.get('content_type'),
size=int(info['size']),
url=info['url'],
proxy_url=info['proxy_url'],
height=int(height) if height is not None else None,
width=int(width) if width is not None else None,
ephemeral=info.get('ephemeral'),
duration_secs=float(duraction_secs) if duraction_secs is not None else None,
waveform=info.get('waveform'),
flags=AttachmentFlags(int(flags)) if flags is not None else None,
)
class MessageType(Enum):
DEFAULT = 0
RECIPIENT_ADD = 1
RECIPIENT_REMOVE = 2
CALL = 3
CHANNEL_NAME_CHANGE = 4
CHANNEL_ICON_CHANGE = 5
CHANNEL_PINNED_MESSAGE = 6
USER_JOIN = 7
GUILD_BOOST = 8
GUILD_BOOST_TIER_1 = 9
GUILD_BOOST_TIER_2 = 10
GUILD_BOOST_TIER_3 = 11
CHANNEL_FOLLOW_ADD = 12
GUILD_DISCOVERY_DISQUALIFIED = 14
GUILD_DISCOVERY_REQUALIFIED = 15
GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16
GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17
THREAD_CREATED = 18
REPLY = 19
CHAT_INPUT_COMMAND = 20
THREAD_STARTER_MESSAGE = 21
GUILD_INVITE_REMINDER = 22
CONTEXT_MENU_COMMAND = 23
AUTO_MODERATION_ACTION = 24
ROLE_SUBSCRIPTION_PURCHASE = 25
INTERACTION_PREMIUM_UPSELL = 26
STAGE_START = 27
STAGE_END = 28
STAGE_SPEAKER = 29
STAGE_TOPIC = 31
GUILD_APPLICATION_PREMIUM_SUBSCRIPTION = 32
GUILD_INCIDENT_ALERT_MODE_ENABLED = 36
GUILD_INCIDENT_ALERT_MODE_DISABLED = 37
GUILD_INCIDENT_REPORT_RAID = 38
GUILD_INCIDENT_REPORT_FALSE_ALARM = 39
PURCHASE_NOTIFICATION = 44
POLL_RESULT = 46
NON_DELETABLE_MESSAGE_TYPES = [
MessageType.RECIPIENT_ADD,
MessageType.RECIPIENT_REMOVE,
MessageType.CALL,
MessageType.CHANNEL_NAME_CHANGE,
MessageType.CHANNEL_ICON_CHANGE,
MessageType.THREAD_STARTER_MESSAGE]
class MessageFlags(IntFlag):
NONE = 0
CROSSPOSTED = 1 << 0 # this message has been published to subscribed channels (via Channel Following)
IS_CROSSPOST = 1 << 1 # this message originated from a message in another channel (via Channel Following)
SUPPRESS_EMBEDS = 1 << 2 # do not include any embeds when serializing this message
SOURCE_MESSAGE_DELETED = 1 << 3 # the source message for this crosspost has been deleted (via Channel Following)
URGENT = 1 << 4 # this message came from the urgent message system
HAS_THREAD = 1 << 5 # this message has an associated thread, with the same id as the message
EPHEMERAL = 1 << 6 # this message is only visible to the user who invoked the Interaction
LOADING = 1 << 7 # this message is an Interaction Response and the bot is "thinking"
# this message failed to mention some roles and add their members to the thread
FAILED_TO_MENTION_SOME_ROLES_IN_THREAD = 1 << 8
SUPPRESS_NOTIFICATIONS = 1 << 12 # this message will not trigger push and desktop notifications
IS_VOICE_MESSAGE = 1 << 13 # this message is a voice message
HAS_SNAPSHOT = 1 << 14 # this message has a snapshot (via Message Forwarding)
IS_COMPONENTS_V2 = 1 << 15 # allows you to create fully component-driven messages
class MessageReferenceType(Enum):
DEFAULT = 0 # A standard reference used by replies.
FORWARD = 1 # Reference used to point to a message at a point in time.
@dataclass
class MessageReference:
type: MessageReferenceType | None # type of reference.
message_id: int | None # id of the originating message
channel_id: int | None # id of the originating message's channel
guild_id: int | None # id of the originating message's guild
# when sending, whether to error if the referenced message doesn't exist
# instead of sending as a normal (non-reply) message, default true
fail_if_not_exists: bool | None
@staticmethod
def from_dict(info: dict) -> MessageReference:
ref_type: int | None = info.get('type')
return MessageReference(
type=MessageReferenceType(ref_type) if ref_type is not None else None,
message_id=info.get('message_id'),
channel_id=info.get('channel_id'),
guild_id=info.get('guild_id'),
fail_if_not_exists=info.get('fail_if_not_exists'))
@dataclass
class Message: # TODO : complete attributes
id: int
@ -264,10 +407,16 @@ class Message: # TODO : complete attributes
content: str
timestamp: datetime
edited_timestamp: datetime | None
attachments: list[Attachment]
type: MessageType
flags: MessageFlags | None
message_reference: MessageReference | None
@staticmethod
def from_dict(info: dict) -> Message:
edited_timestamp: str | None = info.get('edited_timestamp')
flags: int | None = info.get('flags')
message_reference = info.get('message_reference')
return Message(
id=int(info['id']),
channel_id=int(info['channel_id']),
@ -275,7 +424,11 @@ class Message: # TODO : complete attributes
id=info['webhook_id'], username='webhook', discriminator='webhook', global_name=None)),
content=info['content'],
timestamp=datetime.fromisoformat(info['timestamp']),
edited_timestamp=datetime.fromisoformat(edited_timestamp) if edited_timestamp is not None else None)
edited_timestamp=datetime.fromisoformat(edited_timestamp) if edited_timestamp is not None else None,
attachments=[Attachment.from_dict(a) for a in info['attachments']],
type=MessageType(info['type']),
flags=MessageFlags(flags) if flags is not None else None,
message_reference=MessageReference.from_dict(message_reference) if message_reference is not None else None)
@dataclass