BASE_URL = "https://api.flock.com/v1/" API_TIMEOUT = 30 ENDPOINT_SEND_MESSAGE = "chat.sendMessage" ENDPOINT_FETCH_MESSAGES = "chat.fetchMessages" ENDPOINT_GET_UPLOAD_URL = "chat.getUploadUrl" ENDPOINT_USER_GET_INFO = "users.getInfo" ENDPOINT_USER_PUBLIC_PROFILE = "users.getPublicProfile" ENDPOINT_CHANNEL_LIST = "channels.list" ENDPOINT_CHANNEL_GET_INFO = "channels.getInfo" ENDPOINT_CHANNEL_GET_MEMBERS = "channels.getMembers" BOT_TOKEN_PATTERN = r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" PREFIX_USER = "u:" PREFIX_GROUP = "g:" PREFIX_TEAM = "t:" PREFIX_BOT = "b:" FLOCK_USER_ID_PATTERN = r"^u:[a-zA-Z0-9]+$" FLOCK_GROUP_ID_PATTERN = r"^g:[a-zA-Z0-9]+$" MAX_TEXT_LENGTH = 7000 MAX_FLOCKML_LENGTH = 15000 MAX_ATTACHMENT_COUNT = 10 MAX_FILE_SIZE_MB = 100 MAX_POLL_INTERVAL = 30 POLL_TIMEOUT = 30 RATE_LIMIT_PER_MINUTE = 100 RATE_LIMIT_RETRY_AFTER_DEFAULT = 30 MAX_RETRIES = 3 RETRY_BASE_DELAY = 1.0 RETRY_MAX_DELAY = 30.0 RETRYABLE_ERRORS = frozenset( { "RateLimitExceeded", "ServiceUnavailable", "InternalError", } ) NON_RETRYABLE_ERRORS = frozenset( { "InvalidToken", "TokenExpired", "UserNotFound", "ChannelNotFound", "NotAuthorized", "InvalidRequest", "InvalidFlockML", "MessageTooLong", "AttachmentTooLarge", } ) EVENT_MESSAGE_RECEIVE = "chat.receiveMessage" EVENT_SLASH_COMMAND = "client.slashCommand" EVENT_CHAT_ACTION = "client.chatAction" EVENT_APP_INSTALL = "app.install" EVENT_APP_UNINSTALL = "app.uninstall" WEBHOOK_PATH_EVENT_LISTENER = "/api/channel/flock/events" WEBHOOK_PATH_OUTGOING = "/api/channel/flock/outgoing" X_FLOCK_EVENT_TOKEN = "X-Flock-Event-Token" ALLOWED_FLOCKML_TAGS = frozenset( { "b", "i", "u", "s", "strike", "em", "strong", "code", "pre", "a", "br", "hr", "ul", "ol", "li", "table", "tr", "td", "th", "img", "user", "channel", "blockquote", } ) PROBE_TIMEOUT = 10 PAIRING_CODE_LENGTH = 6 PAIRING_CODE_TTL_SECONDS = 600 PAIRING_RATE_LIMIT_PER_PEER = 5 PAIRING_MAX_ATTEMPTS = 5 DEDUPE_MAX_SIZE = 1000 DEDUPE_TTL_SECONDS = 300