mirror of
https://github.com/langbot-app/LangBot.git
synced 2025-11-25 19:37:36 +08:00
* Initial plan * Add package structure and resource path utilities - Created langbot/ package with __init__.py and __main__.py entry point - Added paths utility to find frontend and resource files from package installation - Updated config loading to use resource paths - Updated frontend serving to use resource paths - Added MANIFEST.in for package data inclusion - Updated pyproject.toml with build system and entry points Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Add PyPI publishing workflow and update license - Created GitHub Actions workflow to build frontend and publish to PyPI - Added license field to pyproject.toml to fix deprecation warning - Updated .gitignore to exclude build artifacts - Tested package building successfully Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Add PyPI installation documentation - Created PYPI_INSTALLATION.md with detailed installation and usage instructions - Updated README.md to feature uvx/pip installation as recommended method - Updated README_EN.md with same changes for English documentation Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Address code review feedback - Made package-data configuration more specific to langbot package only - Improved path detection with caching to avoid repeated file I/O - Removed sys.path searching which was incorrect for package data - Removed interactive input() call for non-interactive environment compatibility - Simplified error messages for version check Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Fix code review issues - Use specific exception types instead of bare except - Fix misleading comments about directory levels - Remove redundant existence check before makedirs with exist_ok=True - Use context manager for file opening to ensure proper cleanup Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * Simplify package configuration and document behavioral differences - Removed redundant package-data configuration, relying on MANIFEST.in - Added documentation about behavioral differences between package and source installation - Clarified that include-package-data=true uses MANIFEST.in for data files Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> * chore: update pyproject.toml * chore: try pack templates in langbot/ * chore: update * chore: update * chore: update * chore: update * chore: update * chore: adjust dir structure * chore: fix imports * fix: read default-pipeline-config.json * fix: read default-pipeline-config.json * fix: tests * ci: publish pypi * chore: bump version 4.6.0-beta.1 for testing * chore: add templates/** * fix: send adapters and requesters icons * chore: bump version 4.6.0b2 for testing * chore: add platform field for docker-compose.yaml --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: RockChinQ <45992437+RockChinQ@users.noreply.github.com> Co-authored-by: Junyan Qin <rockchinq@gmail.com>
88 lines
2.7 KiB
Python
88 lines
2.7 KiB
Python
from typing import Dict, Any, Optional
|
||
|
||
|
||
class SlackEvent(dict):
|
||
@staticmethod
|
||
def from_payload(payload: Dict[str, Any]) -> Optional['SlackEvent']:
|
||
try:
|
||
event = SlackEvent(payload)
|
||
return event
|
||
except KeyError:
|
||
return None
|
||
|
||
@property
|
||
def text(self) -> str:
|
||
if self.get('event', {}).get('channel_type') == 'im':
|
||
blocks = self.get('event', {}).get('blocks', [])
|
||
if not blocks:
|
||
return ''
|
||
|
||
elements = blocks[0].get('elements', [])
|
||
if not elements:
|
||
return ''
|
||
|
||
elements = elements[0].get('elements', [])
|
||
text = ''
|
||
|
||
for el in elements:
|
||
if el.get('type') == 'text':
|
||
text += el.get('text', '')
|
||
elif el.get('type') == 'link':
|
||
text += el.get('url', '')
|
||
|
||
return text
|
||
|
||
if self.get('event', {}).get('channel_type') == 'channel':
|
||
message_text = ''
|
||
for block in self.get('event', {}).get('blocks', []):
|
||
if block.get('type') == 'rich_text':
|
||
for element in block.get('elements', []):
|
||
if element.get('type') == 'rich_text_section':
|
||
parts = []
|
||
for el in element.get('elements', []):
|
||
if el.get('type') == 'text':
|
||
parts.append(el['text'])
|
||
elif el.get('type') == 'link':
|
||
parts.append(el['url'])
|
||
message_text = ''.join(parts)
|
||
|
||
return message_text
|
||
|
||
@property
|
||
def user_id(self) -> Optional[str]:
|
||
return self.get('event', {}).get('user', '')
|
||
|
||
@property
|
||
def channel_id(self) -> Optional[str]:
|
||
return self.get('event', {}).get('channel', '')
|
||
|
||
@property
|
||
def type(self) -> str:
|
||
"""message对应私聊,app_mention对应频道at"""
|
||
return self.get('event', {}).get('channel_type', '')
|
||
|
||
@property
|
||
def message_id(self) -> str:
|
||
return self.get('event_id', '')
|
||
|
||
@property
|
||
def pic_url(self) -> str:
|
||
"""提取 Slack 事件中的图片 URL"""
|
||
files = self.get('event', {}).get('files', [])
|
||
if files:
|
||
return files[0].get('url_private', '')
|
||
return None
|
||
|
||
@property
|
||
def sender_name(self) -> str:
|
||
return self.get('event', {}).get('user', '')
|
||
|
||
def __getattr__(self, key: str) -> Optional[Any]:
|
||
return self.get(key)
|
||
|
||
def __setattr__(self, key: str, value: Any) -> None:
|
||
self[key] = value
|
||
|
||
def __repr__(self) -> str:
|
||
return f'<SlackEvent {super().__repr__()}>'
|