machinaos 0.0.76 → 0.0.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +143 -107
- package/client/dist/assets/ActionBar-Du2MSFSz.js +1 -0
- package/client/dist/assets/ApiKeyInput-k2LBmBjb.js +1 -0
- package/client/dist/assets/ApiKeyPanel-C_bV9U0X.js +1 -0
- package/client/dist/assets/ApiUsageSection-CmVfwZzL.js +1 -0
- package/client/dist/assets/EmailPanel-CeKIMGu-.js +1 -0
- package/client/dist/assets/OAuthPanel-KA3t3Q2K.js +1 -0
- package/client/dist/assets/QrPairingPanel-NgNpJNuk.js +1 -0
- package/client/dist/assets/RateLimitSection-Du5YNVIA.js +1 -0
- package/client/dist/assets/StatusCard-DNLyayXc.js +1 -0
- package/client/dist/assets/index-DQ0nwhec.js +257 -0
- package/client/dist/assets/index-DxmbVskS.css +1 -0
- package/client/dist/assets/vendor-flow-CZmBvHRo.js +1 -0
- package/client/dist/assets/vendor-icons-CVrPjN2Q.js +22 -0
- package/client/dist/assets/vendor-markdown-CRou3yQ5.js +62 -0
- package/client/dist/assets/vendor-misc-C4VxKHs5.js +1 -0
- package/client/dist/assets/vendor-query-SzWcOU0G.js +1 -0
- package/client/dist/assets/vendor-radix-Dnos29jG.js +56 -0
- package/client/dist/assets/vendor-react-DvWIbVx0.js +1 -0
- package/client/dist/index.html +37 -3
- package/client/index.html +28 -1
- package/client/package.json +44 -40
- package/client/src/App.tsx +2 -0
- package/client/src/Dashboard.tsx +157 -45
- package/client/src/ParameterPanel.tsx +3 -5
- package/client/src/adapters/nodeSpecToDescription.ts +1 -0
- package/client/src/assets/icons/NodeIcon.tsx +32 -0
- package/client/src/assets/icons/index.ts +4 -0
- package/client/src/assets/icons/stripe.svg +1 -0
- package/client/src/assets/icons/themedGlyphs.ts +404 -0
- package/client/src/components/AIAgentNode.tsx +77 -53
- package/client/src/components/GenericNode.tsx +34 -52
- package/client/src/components/OutputPanel.tsx +64 -147
- package/client/src/components/ParameterRenderer.tsx +5 -3
- package/client/src/components/SkillEditorModal.tsx +9 -18
- package/client/src/components/SquareNode.tsx +97 -115
- package/client/src/components/StartNode.tsx +32 -42
- package/client/src/components/SvgFilterDefs.tsx +54 -0
- package/client/src/components/TeamMonitorNode.tsx +12 -14
- package/client/src/components/ToolkitNode.tsx +35 -60
- package/client/src/components/TriggerNode.tsx +43 -77
- package/client/src/components/__tests__/CredentialsModal.test.tsx +49 -45
- package/client/src/components/credentials/CredentialsModal.tsx +98 -30
- package/client/src/components/credentials/CredentialsPalette.tsx +73 -5
- package/client/src/components/credentials/catalogueAdapter.ts +17 -1
- package/client/src/components/credentials/panels/ApiKeyPanel.tsx +102 -37
- package/client/src/components/credentials/panels/EmailPanel.tsx +7 -19
- package/client/src/components/credentials/panels/OAuthPanel.tsx +5 -1
- package/client/src/components/credentials/panels/QrPairingPanel.tsx +1 -3
- package/client/src/components/credentials/primitives/ActionBar.tsx +7 -11
- package/client/src/components/credentials/primitives/OAuthConnect.tsx +19 -28
- package/client/src/components/credentials/sections/ProviderDefaultsSection.tsx +24 -3
- package/client/src/components/credentials/types.ts +12 -2
- package/client/src/components/credentials/useCredentialPanel.ts +43 -19
- package/client/src/components/icons/AIProviderIcons.tsx +16 -0
- package/client/src/components/onboarding/OnboardingWizard.tsx +23 -63
- package/client/src/components/onboarding/nodeRoleClasses.ts +23 -0
- package/client/src/components/onboarding/steps/CanvasStep.tsx +15 -21
- package/client/src/components/onboarding/steps/ConceptsStep.tsx +2 -11
- package/client/src/components/onboarding/steps/GetStartedStep.tsx +2 -10
- package/client/src/components/parameterPanel/InputSection.tsx +9 -7
- package/client/src/components/parameterPanel/MasterSkillEditor.tsx +84 -198
- package/client/src/components/parameterPanel/MiddleSection.tsx +57 -80
- package/client/src/components/parameterPanel/ToolSchemaEditor.tsx +31 -25
- package/client/src/components/parameterPanel/__tests__/InputSection.test.tsx +7 -2
- package/client/src/components/ui/AIResultModal.tsx +1 -1
- package/client/src/components/ui/CollapsibleSection.tsx +9 -5
- package/client/src/components/ui/CommandPalette.tsx +147 -0
- package/client/src/components/ui/CommandPaletteHost.tsx +189 -0
- package/client/src/components/ui/ComponentItem.tsx +13 -7
- package/client/src/components/ui/ComponentPalette.tsx +24 -13
- package/client/src/components/ui/ConsolePanel.tsx +19 -11
- package/client/src/components/ui/DropCap.tsx +28 -0
- package/client/src/components/ui/EditableNodeLabel.tsx +10 -2
- package/client/src/components/ui/InputNodesPanel.tsx +1 -1
- package/client/src/components/ui/Modal.tsx +38 -6
- package/client/src/components/ui/OutputDisplayPanel.tsx +1 -1
- package/client/src/components/ui/SettingsPanel.tsx +42 -13
- package/client/src/components/ui/StatusBar.tsx +108 -0
- package/client/src/components/ui/ThemeSwitcher.tsx +109 -0
- package/client/src/components/ui/TopToolbar.tsx +42 -25
- package/client/src/components/ui/WorkflowSidebar.tsx +32 -16
- package/client/src/components/ui/action-button.tsx +40 -15
- package/client/src/components/ui/button.tsx +24 -1
- package/client/src/components/ui/dropdown-menu.tsx +24 -2
- package/client/src/components/ui/input.tsx +19 -2
- package/client/src/components/ui/select.tsx +15 -0
- package/client/src/components/ui/textarea.tsx +15 -2
- package/client/src/contexts/AuthContext.tsx +148 -109
- package/client/src/contexts/ThemeContext.tsx +93 -17
- package/client/src/contexts/WebSocketContext.tsx +373 -206
- package/client/src/contexts/__tests__/AuthContext.test.tsx +221 -0
- package/client/src/hooks/__tests__/useDragVariable.test.ts +7 -1
- package/client/src/hooks/__tests__/useWorkflowOpsListener.test.ts +142 -0
- package/client/src/hooks/useAppTheme.ts +209 -7
- package/client/src/hooks/useAutoSkillEdges.ts +7 -2
- package/client/src/hooks/useCatalogueQuery.ts +67 -1
- package/client/src/hooks/useDragVariable.ts +1 -1
- package/client/src/hooks/useNodeAllowlist.ts +115 -8
- package/client/src/hooks/useOnboarding.ts +20 -8
- package/client/src/hooks/useParameterPanel.ts +2 -1
- package/client/src/hooks/useReactFlowNodes.ts +2 -1
- package/client/src/hooks/useSound.ts +185 -0
- package/client/src/hooks/useWorkflowManagement.ts +6 -8
- package/client/src/hooks/useWorkflowOpsListener.ts +90 -0
- package/client/src/index.css +65 -3
- package/client/src/lib/__tests__/connectionConfig.test.ts +91 -0
- package/client/src/lib/aiModelProviders.ts +8 -0
- package/client/src/lib/connectionConfig.ts +107 -0
- package/client/src/lib/queryPersist.ts +13 -5
- package/client/src/lib/sound.ts +393 -0
- package/client/src/main.tsx +20 -0
- package/client/src/store/useAppStore.ts +26 -0
- package/client/src/styles/canvasAnimations.ts +37 -36
- package/client/src/styles/theme.ts +36 -20
- package/client/src/test/setup.ts +1 -0
- package/client/src/themes/atomic.css +253 -0
- package/client/src/themes/base.css +373 -0
- package/client/src/themes/cyber.css +890 -0
- package/client/src/themes/dark.css +70 -0
- package/client/src/themes/edo.css +246 -0
- package/client/src/themes/greek.css +293 -0
- package/client/src/themes/light.css +78 -0
- package/client/src/themes/plague.css +253 -0
- package/client/src/themes/renaissance.css +727 -0
- package/client/src/themes/rot.css +249 -0
- package/client/src/themes/steampunk.css +272 -0
- package/client/src/themes/surveillance.css +289 -0
- package/client/src/themes/wasteland.css +250 -0
- package/client/src/types/INodeProperties.ts +5 -0
- package/client/src/types/NodeTypes.ts +11 -1
- package/client/src/types/__tests__/cloudEvents.test.ts +99 -0
- package/client/src/types/cloudEvents.ts +78 -0
- package/client/src/vite-env.d.ts +7 -0
- package/client/tsconfig.json +1 -1
- package/client/vite.config.js +62 -2
- package/install.ps1 +1 -1
- package/install.sh +1 -1
- package/machina/commands/build.py +51 -7
- package/machina/pyproject.toml +4 -0
- package/machina/supervisor.py +12 -2
- package/machina/tree.py +71 -21
- package/package.json +4 -4
- package/scripts/install.js +16 -1
- package/server/config/ai_cli_providers.json +54 -0
- package/server/config/credential_providers.json +109 -2
- package/server/config/llm_defaults.json +24 -0
- package/server/config/model_registry.json +338 -499
- package/server/config/node_allowlist.json +16 -1
- package/server/config/pricing.json +8 -0
- package/server/constants.py +38 -15
- package/server/core/container.py +2 -2
- package/server/core/credentials_database.py +35 -2
- package/server/core/logging.py +4 -3
- package/server/main.py +99 -13
- package/server/models/node_metadata.py +1 -0
- package/server/nodejs/package.json +8 -6
- package/server/nodejs/src/index.ts +22 -5
- package/server/nodes/README.md +31 -4
- package/server/nodes/agent/_inline.py +2 -0
- package/server/nodes/agent/_specialized.py +6 -3
- package/server/nodes/agent/ai_agent.py +13 -3
- package/server/nodes/agent/chat_agent.py +6 -3
- package/server/nodes/agent/claude_code_agent.py +287 -75
- package/server/nodes/agent/codex_agent.py +239 -0
- package/server/nodes/agent/deep_agent.py +3 -3
- package/server/nodes/agent/rlm_agent.py +3 -3
- package/server/nodes/android/__init__.py +31 -1
- package/server/nodes/android/_base.py +9 -5
- package/server/{services/android_service.py → nodes/android/_dispatcher.py} +2 -2
- package/server/nodes/android/_handlers.py +154 -0
- package/server/nodes/android/_option_loaders.py +44 -0
- package/server/nodes/android/_refresh.py +127 -0
- package/server/{services/android → nodes/android/_relay}/client.py +4 -4
- package/server/{routers/android.py → nodes/android/_router.py} +27 -8
- package/server/nodes/browser/browser.py +2 -2
- package/server/nodes/code/_base.py +6 -2
- package/server/nodes/code/_claude_code.py +134 -0
- package/server/nodes/document/embedding_generator.py +3 -3
- package/server/nodes/document/http_scraper.py +3 -3
- package/server/nodes/document/vector_store.py +5 -5
- package/server/nodes/email/__init__.py +11 -1
- package/server/nodes/email/_filters.py +21 -0
- package/server/{services/himalaya_service.py → nodes/email/_himalaya.py} +6 -10
- package/server/{services/email_service.py → nodes/email/_service.py} +9 -13
- package/server/nodes/email/email_read.py +1 -1
- package/server/nodes/email/email_receive.py +54 -5
- package/server/nodes/email/email_send.py +1 -1
- package/server/nodes/filesystem/shell.py +24 -1
- package/server/nodes/google/__init__.py +55 -1
- package/server/{services/handlers/google_auth.py → nodes/google/_auth_helper.py} +8 -5
- package/server/nodes/google/_base.py +2 -2
- package/server/nodes/google/_credentials.py +5 -5
- package/server/nodes/google/_filters.py +25 -0
- package/server/nodes/google/_handlers.py +57 -0
- package/server/{services/google_oauth.py → nodes/google/_oauth.py} +195 -162
- package/server/nodes/google/_option_loaders.py +107 -0
- package/server/nodes/google/_refresh.py +66 -0
- package/server/nodes/google/_router.py +131 -0
- package/server/nodes/google/gmail_receive.py +41 -4
- package/server/nodes/groups.py +1 -0
- package/server/nodes/location/_credentials.py +45 -1
- package/server/{services/maps.py → nodes/location/_service.py} +18 -3
- package/server/nodes/location/gmaps_create.py +4 -4
- package/server/nodes/location/gmaps_locations.py +4 -4
- package/server/nodes/location/gmaps_nearby_places.py +4 -4
- package/server/nodes/model/_base.py +8 -3
- package/server/nodes/model/_credentials.py +96 -8
- package/server/nodes/model/_local_validator.py +345 -0
- package/server/nodes/model/lmstudio_chat_model.py +23 -0
- package/server/nodes/model/ollama_chat_model.py +25 -0
- package/server/nodes/proxy/_usage.py +2 -2
- package/server/nodes/proxy/proxy_config.py +14 -14
- package/server/nodes/proxy/proxy_request.py +4 -4
- package/server/nodes/scraper/_credentials.py +29 -1
- package/server/nodes/scraper/apify_actor.py +9 -9
- package/server/nodes/scraper/crawlee_scraper.py +5 -5
- package/server/nodes/search/brave_search.py +4 -0
- package/server/nodes/search/perplexity_search.py +9 -0
- package/server/nodes/search/serper_search.py +3 -0
- package/server/nodes/skill/simple_memory.py +12 -0
- package/server/nodes/social/_base.py +2 -2
- package/server/nodes/stripe/__init__.py +46 -0
- package/server/nodes/stripe/_credentials.py +33 -0
- package/server/nodes/stripe/_handlers.py +270 -0
- package/server/nodes/stripe/_install.py +127 -0
- package/server/nodes/stripe/_source.py +174 -0
- package/server/nodes/stripe/stripe_action.py +81 -0
- package/server/nodes/stripe/stripe_receive.py +92 -0
- package/server/nodes/telegram/_credentials.py +52 -1
- package/server/nodes/telegram/_handlers.py +19 -18
- package/server/nodes/telegram/_service.py +134 -32
- package/server/nodes/telegram/telegram_send.py +5 -6
- package/server/nodes/text/file_handler.py +2 -2
- package/server/nodes/text/text_generator.py +2 -2
- package/server/nodes/tool/agent_builder.py +630 -0
- package/server/nodes/tool/task_manager.py +144 -2
- package/server/nodes/twitter/__init__.py +38 -1
- package/server/nodes/twitter/_base.py +7 -7
- package/server/nodes/twitter/_credentials.py +1 -1
- package/server/nodes/twitter/_filters.py +37 -0
- package/server/nodes/twitter/_handlers.py +77 -0
- package/server/nodes/twitter/_oauth.py +124 -0
- package/server/nodes/twitter/_refresh.py +78 -0
- package/server/nodes/twitter/_router.py +29 -0
- package/server/nodes/twitter/twitter_receive.py +4 -0
- package/server/nodes/visuals.json +64 -19
- package/server/nodes/whatsapp/__init__.py +45 -5
- package/server/nodes/whatsapp/_base.py +3 -3
- package/server/nodes/whatsapp/_filters.py +137 -0
- package/server/nodes/whatsapp/_handlers.py +167 -0
- package/server/nodes/whatsapp/_option_loaders.py +68 -0
- package/server/nodes/whatsapp/_refresh.py +62 -0
- package/server/nodes/whatsapp/_runtime.py +1 -1
- package/server/pyproject.toml +29 -7
- package/server/routers/schemas.py +2 -2
- package/server/routers/webhook.py +26 -9
- package/server/routers/websocket.py +149 -810
- package/server/services/ai.py +89 -8
- package/server/services/auth.py +220 -43
- package/server/services/claude_oauth.py +126 -100
- package/server/services/cli_agent/__init__.py +78 -0
- package/server/services/cli_agent/_handlers.py +237 -0
- package/server/services/cli_agent/config.py +112 -0
- package/server/services/cli_agent/factory.py +48 -0
- package/server/services/cli_agent/lockfile.py +141 -0
- package/server/services/cli_agent/mcp_server.py +482 -0
- package/server/services/cli_agent/protocol.py +173 -0
- package/server/services/cli_agent/providers/__init__.py +9 -0
- package/server/services/cli_agent/providers/anthropic_claude.py +419 -0
- package/server/services/cli_agent/providers/google_gemini.py +80 -0
- package/server/services/cli_agent/providers/openai_codex.py +310 -0
- package/server/services/cli_agent/service.py +607 -0
- package/server/services/cli_agent/session.py +618 -0
- package/server/services/cli_agent/types.py +227 -0
- package/server/services/cli_agent/workflow_tools.py +233 -0
- package/server/services/credential_registry.py +26 -1
- package/server/services/deployment/manager.py +26 -145
- package/server/services/deployment/poll_registry.py +59 -0
- package/server/services/event_waiter.py +76 -246
- package/server/services/events/__init__.py +54 -0
- package/server/services/events/cli.py +78 -0
- package/server/services/events/daemon.py +163 -0
- package/server/services/events/envelope.py +281 -0
- package/server/services/events/lifecycle.py +99 -0
- package/server/services/events/oauth_lifecycle.py +534 -0
- package/server/services/events/polling.py +60 -0
- package/server/services/events/push.py +36 -0
- package/server/services/events/source.py +63 -0
- package/server/services/events/triggers.py +118 -0
- package/server/services/events/verifiers/__init__.py +25 -0
- package/server/services/events/verifiers/base.py +28 -0
- package/server/services/events/verifiers/github.py +25 -0
- package/server/services/events/verifiers/hmac_basic.py +32 -0
- package/server/services/events/verifiers/standard_webhooks.py +47 -0
- package/server/services/events/verifiers/stripe.py +42 -0
- package/server/services/events/webhook.py +105 -0
- package/server/services/handlers/tools.py +28 -186
- package/server/services/llm/config.py +7 -0
- package/server/services/llm/factory.py +8 -2
- package/server/services/memory/__init__.py +52 -0
- package/server/services/memory/jsonl.py +80 -0
- package/server/services/memory/markdown.py +65 -0
- package/server/services/memory/state.py +112 -0
- package/server/services/memory/vector_store.py +40 -0
- package/server/services/model_registry.py +76 -0
- package/server/services/node_allowlist.py +71 -15
- package/server/services/node_executor.py +2 -2
- package/server/services/node_output_schemas.py +21 -10
- package/server/services/node_spec.py +1 -1
- package/server/services/oauth_utils.py +1 -1
- package/server/services/plugin/__init__.py +2 -0
- package/server/services/plugin/base.py +44 -2
- package/server/services/plugin/credential.py +288 -1
- package/server/services/plugin/deps.py +105 -0
- package/server/services/plugin/edge_walker.py +12 -4
- package/server/services/plugin/oauth.py +381 -0
- package/server/services/plugin/polling.py +247 -0
- package/server/services/plugin/registry.py +145 -0
- package/server/services/plugin/singleton.py +65 -0
- package/server/services/plugin/ws.py +81 -0
- package/server/services/process_service.py +31 -2
- package/server/services/status_broadcaster.py +155 -238
- package/server/services/temporal/workflow.py +7 -7
- package/server/services/workflow.py +21 -3
- package/server/services/ws_handler_registry.py +111 -28
- package/server/skills/GUIDE.md +16 -1
- package/server/skills/assistant/agent-builder-skill/SKILL.md +166 -0
- package/server/skills/payments_agent/stripe-skill/SKILL.md +306 -0
- package/server/tests/credentials/test_auth_service.py +16 -9
- package/server/tests/credentials/test_credential_broadcasts.py +219 -0
- package/server/tests/credentials/test_google_oauth.py +6 -6
- package/server/tests/credentials/test_oauth_utils.py +1 -1
- package/server/tests/credentials/test_twitter_oauth.py +2 -2
- package/server/tests/credentials/test_websocket_handlers.py +44 -20
- package/server/tests/llm/test_factory.py +1 -0
- package/server/tests/llm/test_wiring.py +5 -1
- package/server/tests/nodes/_compat.py +24 -24
- package/server/tests/nodes/test_agent_builder.py +439 -0
- package/server/tests/nodes/test_ai_tools.py +18 -14
- package/server/tests/nodes/test_code_fs_process.py +17 -8
- package/server/tests/nodes/test_email.py +10 -9
- package/server/tests/nodes/test_google_workspace.py +2 -2
- package/server/tests/nodes/test_specialized_agents.py +100 -53
- package/server/tests/nodes/test_stripe_plugin.py +293 -0
- package/server/tests/nodes/test_telegram_social.py +4 -4
- package/server/tests/nodes/test_twitter.py +1 -1
- package/server/tests/nodes/test_web_automation.py +2 -2
- package/server/tests/nodes/test_whatsapp.py +9 -9
- package/server/tests/services/cli_agent/__init__.py +0 -0
- package/server/tests/services/cli_agent/test_mcp_server.py +432 -0
- package/server/tests/services/cli_agent/test_providers.py +358 -0
- package/server/tests/services/cli_agent/test_service.py +298 -0
- package/server/tests/services/memory/__init__.py +0 -0
- package/server/tests/services/memory/test_jsonl.py +188 -0
- package/server/tests/services/test_events.py +333 -0
- package/server/tests/test_node_spec.py +56 -16
- package/server/tests/test_plugin_helpers.py +116 -0
- package/server/tests/test_plugin_self_containment.py +486 -0
- package/server/tests/test_status_broadcasts.py +425 -0
- package/workflows/{AI Assistant_workflow-1777421105154-0m4snkzjf.json → AI Assistant_workflow-1778504793388-ou1m1tz2x.json } +70 -266
- package/workflows/{AI Employee_workflow-1777720598005-u4cm858dv.json → AI Employee_example_workflow-1777720598005-u4cm858dv.json } +112 -112
- package/workflows/Claude Assistant_workflow-1778380124051-mdibn807c.json +709 -0
- package/client/dist/assets/ActionBar-vzPpSR77.js +0 -1
- package/client/dist/assets/ApiKeyInput-Ds7AKFe8.js +0 -1
- package/client/dist/assets/ApiKeyPanel-gfblELep.js +0 -1
- package/client/dist/assets/ApiUsageSection-BMNWTe2r.js +0 -1
- package/client/dist/assets/EmailPanel-B1Om64p5.js +0 -1
- package/client/dist/assets/OAuthPanel-CXyQYGBz.js +0 -1
- package/client/dist/assets/QrPairingPanel-BgNuI1we.js +0 -1
- package/client/dist/assets/RateLimitSection-YYK8sx1T.js +0 -1
- package/client/dist/assets/StatusCard-DuYA5hJR.js +0 -1
- package/client/dist/assets/index-D9tZfgvi.js +0 -363
- package/client/dist/assets/index-al7snTkG.css +0 -1
- package/client/src/components/credentials/providers.tsx +0 -177
- package/server/routers/google.py +0 -277
- package/server/routers/maps.py +0 -142
- package/server/routers/twitter.py +0 -365
- package/server/services/claude_code_service.py +0 -106
- package/server/services/memory.py +0 -159
- package/server/services/node_option_loaders/__init__.py +0 -77
- package/server/services/node_option_loaders/android_loaders.py +0 -55
- package/server/services/node_option_loaders/google_loaders.py +0 -97
- package/server/services/node_option_loaders/whatsapp_loaders.py +0 -69
- package/server/services/twitter_oauth.py +0 -411
- package/server/services/websocket_client.py +0 -29
- /package/server/{services/android → nodes/android/_relay}/__init__.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/broadcaster.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/manager.py +0 -0
- /package/server/{services/android → nodes/android/_relay}/protocol.py +0 -0
- /package/server/{services/browser_service.py → nodes/browser/_service.py} +0 -0
- /package/server/{services/whatsapp_service.py → nodes/whatsapp/_service.py} +0 -0
- /package/server/skills/{task_agent → assistant}/write-todos-skill/SKILL.md +0 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Stripe event source — supervised ``stripe listen`` daemon + the
|
|
2
|
+
companion ``WebhookSource`` that receives forwarded events.
|
|
3
|
+
|
|
4
|
+
Two cooperating sources:
|
|
5
|
+
|
|
6
|
+
* :class:`StripeListenSource` (DaemonEventSource) keeps the CLI alive,
|
|
7
|
+
captures the ``whsec_…`` signing secret from its stderr banner.
|
|
8
|
+
* :class:`StripeWebhookSource` (WebhookSource) is the actual event
|
|
9
|
+
producer — verifies the Stripe-Signature header on each forwarded
|
|
10
|
+
POST and turns the payload into a :class:`WorkflowEvent`.
|
|
11
|
+
|
|
12
|
+
Module-level singletons (``get_listen_source`` / ``get_webhook_source``)
|
|
13
|
+
plug into the framework registries from ``__init__.py``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
import shlex
|
|
22
|
+
from datetime import datetime, timezone
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
from typing import Any, Dict, Optional
|
|
25
|
+
|
|
26
|
+
from fastapi import Request
|
|
27
|
+
|
|
28
|
+
from core.logging import get_logger
|
|
29
|
+
from services.events import (
|
|
30
|
+
DaemonEventSource,
|
|
31
|
+
StripeVerifier,
|
|
32
|
+
WebhookSource,
|
|
33
|
+
WorkflowEvent,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
from ._credentials import StripeCredential
|
|
37
|
+
|
|
38
|
+
logger = get_logger(__name__)
|
|
39
|
+
|
|
40
|
+
_SECRET_RE = re.compile(r"whsec_[A-Za-z0-9_]+")
|
|
41
|
+
_SECRET_FIELD = "stripe_webhook_secret"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def stripe_config_path() -> Path:
|
|
45
|
+
"""Default location for the Stripe CLI's credentials file
|
|
46
|
+
(``~/.config/stripe/config.toml``, ``XDG_CONFIG_HOME``-aware)."""
|
|
47
|
+
base = os.environ.get("XDG_CONFIG_HOME")
|
|
48
|
+
return (Path(base) if base else Path.home() / ".config") / "stripe" / "config.toml"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def is_logged_in() -> bool:
|
|
52
|
+
"""Detect Stripe CLI login by sniffing its config file. Cheap
|
|
53
|
+
filesystem check — the CLI writes ``test_mode_api_key`` /
|
|
54
|
+
``live_mode_api_key`` under a profile section on success."""
|
|
55
|
+
cfg = stripe_config_path()
|
|
56
|
+
if not cfg.exists():
|
|
57
|
+
return False
|
|
58
|
+
try:
|
|
59
|
+
return "_api_key" in cfg.read_text(encoding="utf-8", errors="replace")
|
|
60
|
+
except OSError:
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class StripeListenSource(DaemonEventSource):
|
|
65
|
+
"""Supervises ``stripe listen`` and persists the captured webhook
|
|
66
|
+
signing secret. Emits no events itself — Stripe events arrive over
|
|
67
|
+
HTTP via :class:`StripeWebhookSource`."""
|
|
68
|
+
|
|
69
|
+
type = "stripe.listen"
|
|
70
|
+
process_name = "stripe-listen"
|
|
71
|
+
# Empty so DaemonEventSource skips its built-in PATH check; we
|
|
72
|
+
# do install + verification ourselves in start() via
|
|
73
|
+
# ensure_stripe_cli (which falls back to system PATH first, then
|
|
74
|
+
# workspace-local download).
|
|
75
|
+
binary_name = ""
|
|
76
|
+
workflow_namespace = "_stripe"
|
|
77
|
+
install_hint = "https://stripe.com/docs/stripe-cli#install"
|
|
78
|
+
credential = StripeCredential
|
|
79
|
+
|
|
80
|
+
def build_command(self, secrets: Dict) -> str:
|
|
81
|
+
# The CLI uses credentials it stored at ~/.config/stripe/config.toml
|
|
82
|
+
# (populated by `stripe login`). No --api-key flag here.
|
|
83
|
+
# Binary path is resolved by `ensure_stripe_cli` (called from
|
|
84
|
+
# the start() override below) and cached for sync access here.
|
|
85
|
+
# ``shlex.quote`` so Windows backslashes survive ProcessService's
|
|
86
|
+
# ``shlex.split`` round-trip (POSIX-mode parser eats raw ``\``).
|
|
87
|
+
from core.config import Settings
|
|
88
|
+
from ._install import stripe_cli_path
|
|
89
|
+
|
|
90
|
+
port = int(Settings().port)
|
|
91
|
+
binary = shlex.quote(str(stripe_cli_path() or "stripe"))
|
|
92
|
+
return (
|
|
93
|
+
f"{binary} listen --forward-to http://localhost:{port}/webhook/stripe "
|
|
94
|
+
f"--print-secret"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
async def start(self) -> Dict[str, Any]:
|
|
98
|
+
"""Ensure the CLI binary is downloaded before delegating to the
|
|
99
|
+
framework's daemon-start path (which calls ``build_command``)."""
|
|
100
|
+
from ._install import ensure_stripe_cli
|
|
101
|
+
try:
|
|
102
|
+
path = await ensure_stripe_cli()
|
|
103
|
+
logger.info("[Stripe] daemon start: CLI binary resolved at %s", path)
|
|
104
|
+
except Exception as e:
|
|
105
|
+
logger.warning("[Stripe] daemon start failed at install step: %s", e)
|
|
106
|
+
return {"success": False, "error": f"Stripe CLI install failed: {e}"}
|
|
107
|
+
return await super().start()
|
|
108
|
+
|
|
109
|
+
async def has_credential(self) -> bool:
|
|
110
|
+
"""The Stripe CLI manages auth state in its own config file —
|
|
111
|
+
that's our 'credential' for the daemon's start gate."""
|
|
112
|
+
return is_logged_in()
|
|
113
|
+
|
|
114
|
+
def parse_line(self, stream: str, line: str) -> Optional[WorkflowEvent]:
|
|
115
|
+
if stream == "stderr" and (m := _SECRET_RE.search(line)):
|
|
116
|
+
secret = m.group(0)
|
|
117
|
+
logger.info("[Stripe] whsec_… signing secret detected in daemon stderr — persisting")
|
|
118
|
+
asyncio.create_task(self._persist_secret(secret))
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
async def _persist_secret(self, secret: str) -> None:
|
|
122
|
+
try:
|
|
123
|
+
from services.plugin.deps import get_auth_service
|
|
124
|
+
await get_auth_service().store_api_key(_SECRET_FIELD, secret, models=[])
|
|
125
|
+
logger.info(
|
|
126
|
+
"[Stripe] webhook signing secret persisted (key=%s, len=%d)",
|
|
127
|
+
_SECRET_FIELD, len(secret),
|
|
128
|
+
)
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logger.warning("[Stripe] persist secret failed: %s", e)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class StripeWebhookSource(WebhookSource):
|
|
134
|
+
"""HTTP receiver for Stripe-forwarded events."""
|
|
135
|
+
|
|
136
|
+
type = "stripe.webhook"
|
|
137
|
+
path = "stripe"
|
|
138
|
+
verifier = StripeVerifier
|
|
139
|
+
secret_field = _SECRET_FIELD
|
|
140
|
+
credential = StripeCredential
|
|
141
|
+
|
|
142
|
+
async def shape(self, request: Request, body: bytes, payload: dict) -> WorkflowEvent:
|
|
143
|
+
created = payload.get("created")
|
|
144
|
+
try:
|
|
145
|
+
time = datetime.fromtimestamp(int(created), tz=timezone.utc) if created else datetime.now(timezone.utc)
|
|
146
|
+
except (TypeError, ValueError):
|
|
147
|
+
time = datetime.now(timezone.utc)
|
|
148
|
+
account = payload.get("account") or "default"
|
|
149
|
+
return WorkflowEvent(
|
|
150
|
+
id=payload.get("id") or "",
|
|
151
|
+
type=f"stripe.{payload.get('type', 'unknown')}",
|
|
152
|
+
source=f"stripe://{account}",
|
|
153
|
+
time=time,
|
|
154
|
+
data=payload.get("data") or {},
|
|
155
|
+
subject=payload.get("type"),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
_listen: Optional[StripeListenSource] = None
|
|
160
|
+
_webhook: Optional[StripeWebhookSource] = None
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def get_listen_source() -> StripeListenSource:
|
|
164
|
+
global _listen
|
|
165
|
+
if _listen is None:
|
|
166
|
+
_listen = StripeListenSource()
|
|
167
|
+
return _listen
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def get_webhook_source() -> StripeWebhookSource:
|
|
171
|
+
global _webhook
|
|
172
|
+
if _webhook is None:
|
|
173
|
+
_webhook = StripeWebhookSource()
|
|
174
|
+
return _webhook
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Stripe Action — pass-through over the Stripe CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import shlex
|
|
6
|
+
from typing import Any, Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
9
|
+
|
|
10
|
+
from services.events import run_cli_command
|
|
11
|
+
from services.plugin import ActionNode, NodeContext, Operation, TaskQueue
|
|
12
|
+
|
|
13
|
+
from ._credentials import StripeCredential
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class StripeActionParams(BaseModel):
|
|
17
|
+
command: str = Field(
|
|
18
|
+
default="",
|
|
19
|
+
description=(
|
|
20
|
+
"Stripe CLI command, exactly as you would type after 'stripe '. "
|
|
21
|
+
"Examples: 'customers create --email a@b.com', 'charges list --limit 10', "
|
|
22
|
+
"'trigger charge.succeeded'. Reference: https://stripe.com/docs/cli"
|
|
23
|
+
),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
model_config = ConfigDict(extra="ignore")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class StripeActionOutput(BaseModel):
|
|
30
|
+
command: Optional[str] = None
|
|
31
|
+
success: Optional[bool] = None
|
|
32
|
+
result: Optional[Any] = None
|
|
33
|
+
stdout: Optional[str] = None
|
|
34
|
+
error: Optional[str] = None
|
|
35
|
+
|
|
36
|
+
model_config = ConfigDict(extra="allow")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class StripeActionNode(ActionNode):
|
|
40
|
+
type = "stripeAction"
|
|
41
|
+
display_name = "Stripe"
|
|
42
|
+
subtitle = "CLI Command"
|
|
43
|
+
group = ("payments", "tool")
|
|
44
|
+
description = "Run any Stripe CLI command (customers, charges, payment_intents, refunds, invoices, trigger, …)"
|
|
45
|
+
component_kind = "square"
|
|
46
|
+
handles = (
|
|
47
|
+
{"name": "input-main", "kind": "input", "position": "left",
|
|
48
|
+
"label": "Input", "role": "main"},
|
|
49
|
+
{"name": "output-main", "kind": "output", "position": "right",
|
|
50
|
+
"label": "Output", "role": "main"},
|
|
51
|
+
)
|
|
52
|
+
annotations = {"destructive": False, "readonly": False, "open_world": True}
|
|
53
|
+
credentials = (StripeCredential,)
|
|
54
|
+
task_queue = TaskQueue.REST_API
|
|
55
|
+
usable_as_tool = True
|
|
56
|
+
|
|
57
|
+
Params = StripeActionParams
|
|
58
|
+
Output = StripeActionOutput
|
|
59
|
+
|
|
60
|
+
@Operation("run", cost={"service": "stripe", "action": "run", "count": 1})
|
|
61
|
+
async def run(self, ctx: NodeContext, params: StripeActionParams) -> Any:
|
|
62
|
+
from ._install import ensure_stripe_cli
|
|
63
|
+
|
|
64
|
+
cmd = params.command.strip()
|
|
65
|
+
if not cmd:
|
|
66
|
+
raise RuntimeError("command is required (e.g. 'customers create --email a@b.com')")
|
|
67
|
+
try:
|
|
68
|
+
binary = str(await ensure_stripe_cli())
|
|
69
|
+
except Exception as e:
|
|
70
|
+
raise RuntimeError(f"Stripe CLI install failed: {e}")
|
|
71
|
+
# No credential= — Stripe CLI reads its own creds from
|
|
72
|
+
# ~/.config/stripe/config.toml after `stripe login`.
|
|
73
|
+
result = await run_cli_command(binary=binary, argv=shlex.split(cmd))
|
|
74
|
+
if not result["success"]:
|
|
75
|
+
raise RuntimeError(result.get("error") or "Stripe CLI invocation failed")
|
|
76
|
+
return {
|
|
77
|
+
"command": cmd,
|
|
78
|
+
"success": True,
|
|
79
|
+
"result": result.get("result"),
|
|
80
|
+
"stdout": result.get("stdout"),
|
|
81
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Stripe Receive — fires when StripeWebhookSource accepts a verified
|
|
2
|
+
forwarded event."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from typing import Callable, Dict, Literal, Optional
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
9
|
+
|
|
10
|
+
from services.events import BaseTriggerParams, WebhookTriggerNode, WorkflowEvent
|
|
11
|
+
|
|
12
|
+
from ._credentials import StripeCredential
|
|
13
|
+
from ._source import StripeWebhookSource
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
_PREFIX = "stripe."
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class StripeReceiveParams(BaseTriggerParams):
|
|
20
|
+
livemode_filter: Literal["all", "test", "live"] = Field(
|
|
21
|
+
default="all",
|
|
22
|
+
description="Filter by livemode flag on the event.",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class StripeReceiveOutput(BaseModel):
|
|
27
|
+
event_id: Optional[str] = None
|
|
28
|
+
event_type: Optional[str] = None
|
|
29
|
+
created: Optional[int] = None
|
|
30
|
+
livemode: Optional[bool] = None
|
|
31
|
+
api_version: Optional[str] = None
|
|
32
|
+
request_id: Optional[str] = None
|
|
33
|
+
account: Optional[str] = None
|
|
34
|
+
data: Optional[dict] = None
|
|
35
|
+
|
|
36
|
+
model_config = ConfigDict(extra="allow")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class StripeReceiveNode(WebhookTriggerNode):
|
|
40
|
+
type = "stripeReceive"
|
|
41
|
+
display_name = "Stripe Receive"
|
|
42
|
+
subtitle = "Webhook Event"
|
|
43
|
+
group = ("payments", "trigger")
|
|
44
|
+
description = "Trigger workflow when Stripe webhook event arrives"
|
|
45
|
+
component_kind = "trigger"
|
|
46
|
+
handles = (
|
|
47
|
+
{"name": "output-main", "kind": "output", "position": "right",
|
|
48
|
+
"label": "Output", "role": "main"},
|
|
49
|
+
)
|
|
50
|
+
credentials = (StripeCredential,)
|
|
51
|
+
webhook_source = StripeWebhookSource
|
|
52
|
+
event_type_prefix = _PREFIX
|
|
53
|
+
Params = StripeReceiveParams
|
|
54
|
+
Output = StripeReceiveOutput
|
|
55
|
+
|
|
56
|
+
def _extra_filter(
|
|
57
|
+
self, params: StripeReceiveParams,
|
|
58
|
+
) -> Optional[Callable[[WorkflowEvent], bool]]:
|
|
59
|
+
if params.livemode_filter == "all":
|
|
60
|
+
return None
|
|
61
|
+
target_live = params.livemode_filter == "live"
|
|
62
|
+
|
|
63
|
+
def matches(ev: WorkflowEvent) -> bool:
|
|
64
|
+
payload = ev.data if isinstance(ev.data, dict) else {}
|
|
65
|
+
return bool(payload.get("livemode")) is target_live
|
|
66
|
+
|
|
67
|
+
return matches
|
|
68
|
+
|
|
69
|
+
async def _check_precondition(self) -> Optional[str]:
|
|
70
|
+
from ._source import get_listen_source
|
|
71
|
+
if not get_listen_source()._started:
|
|
72
|
+
return "Stripe daemon not running. Add Stripe API key in Credentials and connect."
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
def shape_output(self, event: WorkflowEvent) -> Dict:
|
|
76
|
+
payload = event.data if isinstance(event.data, dict) else {}
|
|
77
|
+
stripe_type = event.type[len(_PREFIX):] if event.type.startswith(_PREFIX) else event.type
|
|
78
|
+
request = payload.get("request")
|
|
79
|
+
request_id = request.get("id") if isinstance(request, dict) else request
|
|
80
|
+
nested = payload.get("data")
|
|
81
|
+
return {
|
|
82
|
+
"event_id": event.id,
|
|
83
|
+
"event_type": stripe_type,
|
|
84
|
+
"created": payload.get("created"),
|
|
85
|
+
"livemode": payload.get("livemode"),
|
|
86
|
+
"api_version": payload.get("api_version"),
|
|
87
|
+
"request_id": request_id,
|
|
88
|
+
"account": payload.get("account") or (
|
|
89
|
+
event.source.split("://", 1)[1] if "://" in event.source else None
|
|
90
|
+
),
|
|
91
|
+
"data": nested if isinstance(nested, dict) else payload,
|
|
92
|
+
}
|
|
@@ -5,7 +5,17 @@ Used by the two telegram plugins in this folder (telegram_send, telegram_receive
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
import httpx
|
|
9
|
+
|
|
10
|
+
from services.plugin.credential import ApiKeyCredential, ProbeResult
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Telegram embeds the bot token in the URL path (``/bot<token>/getMe``)
|
|
14
|
+
# rather than in a header / query / Authorization slot, so the
|
|
15
|
+
# declarative ``probe_url`` shape doesn't fit. We override ``_probe``
|
|
16
|
+
# directly but reuse :meth:`_handle_probe_response` from the base for
|
|
17
|
+
# the 200-with-failure-envelope case.
|
|
18
|
+
_TELEGRAM_API = "https://api.telegram.org"
|
|
9
19
|
|
|
10
20
|
|
|
11
21
|
class TelegramCredential(ApiKeyCredential):
|
|
@@ -17,3 +27,44 @@ class TelegramCredential(ApiKeyCredential):
|
|
|
17
27
|
key_location = "header"
|
|
18
28
|
extra_fields = ("telegram_owner_chat_id",)
|
|
19
29
|
docs_url = "https://core.telegram.org/bots"
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
async def _probe(cls, api_key: str) -> ProbeResult:
|
|
33
|
+
"""Validate via ``GET /bot<token>/getMe``.
|
|
34
|
+
|
|
35
|
+
Telegram returns 401 for revoked tokens and 404 for malformed
|
|
36
|
+
tokens; both surface as :class:`httpx.HTTPStatusError` and the
|
|
37
|
+
base :meth:`Credential.validate` translates them via
|
|
38
|
+
:func:`classify_credential_error`. A 200 with ``{ok: false}`` is
|
|
39
|
+
rare but possible (legacy token shapes) and is caught in
|
|
40
|
+
:meth:`_handle_probe_response`.
|
|
41
|
+
"""
|
|
42
|
+
url = f"{_TELEGRAM_API}/bot{api_key}/getMe"
|
|
43
|
+
async with httpx.AsyncClient(timeout=cls.probe_timeout_seconds) as client:
|
|
44
|
+
response = await client.get(url)
|
|
45
|
+
response.raise_for_status()
|
|
46
|
+
return cls._handle_probe_response(response)
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def _handle_probe_response(cls, response: httpx.Response) -> ProbeResult:
|
|
50
|
+
payload = response.json()
|
|
51
|
+
if not payload.get("ok"):
|
|
52
|
+
return ProbeResult(
|
|
53
|
+
valid=False,
|
|
54
|
+
message=payload.get("description", "Telegram rejected the bot token"),
|
|
55
|
+
)
|
|
56
|
+
bot = payload.get("result") or {}
|
|
57
|
+
username = bot.get("username")
|
|
58
|
+
return ProbeResult(
|
|
59
|
+
valid=True,
|
|
60
|
+
message=(
|
|
61
|
+
f"Telegram bot token is valid (@{username})"
|
|
62
|
+
if username
|
|
63
|
+
else "Telegram bot token is valid"
|
|
64
|
+
),
|
|
65
|
+
extra={
|
|
66
|
+
"bot_id": bot.get("id"),
|
|
67
|
+
"bot_username": username,
|
|
68
|
+
"bot_name": bot.get("first_name"),
|
|
69
|
+
},
|
|
70
|
+
)
|
|
@@ -19,6 +19,8 @@ from typing import Any, Awaitable, Callable, Dict
|
|
|
19
19
|
|
|
20
20
|
from fastapi import WebSocket
|
|
21
21
|
|
|
22
|
+
from services.plugin.ws import ws_response
|
|
23
|
+
|
|
22
24
|
from ._credentials import TelegramCredential
|
|
23
25
|
from ._service import get_telegram_service
|
|
24
26
|
|
|
@@ -112,8 +114,15 @@ async def handle_telegram_status(data: Dict[str, Any], websocket: WebSocket) ->
|
|
|
112
114
|
return {"success": True, "status": status}
|
|
113
115
|
|
|
114
116
|
|
|
117
|
+
@ws_response
|
|
115
118
|
async def handle_telegram_send(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
|
|
116
|
-
"""Direct send via WebSocket (not via workflow node).
|
|
119
|
+
"""Direct send via WebSocket (not via workflow node).
|
|
120
|
+
|
|
121
|
+
``@ws_response`` collapses the catch-all ``try / except Exception``
|
|
122
|
+
block at the bottom of the function -- exceptions become
|
|
123
|
+
``{success: False, error: str(e)}`` automatically. Early-return
|
|
124
|
+
validation stays since those are deterministic input checks.
|
|
125
|
+
"""
|
|
117
126
|
service = get_telegram_service()
|
|
118
127
|
if not service.connected:
|
|
119
128
|
return {"success": False, "error": "Telegram bot not connected"}
|
|
@@ -125,15 +134,11 @@ async def handle_telegram_send(data: Dict[str, Any], websocket: WebSocket) -> Di
|
|
|
125
134
|
if dispatch is None:
|
|
126
135
|
return {"success": False, "error": f"Unsupported message type: {message_type}"}
|
|
127
136
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return {"success": True, "result": result}
|
|
134
|
-
except Exception as e:
|
|
135
|
-
logger.error(f"[Telegram] Send error: {e}")
|
|
136
|
-
return {"success": False, "error": str(e)}
|
|
137
|
+
coro_or_none, err = dispatch(service, data, data.get("parse_mode"))
|
|
138
|
+
if err:
|
|
139
|
+
return {"success": False, "error": err}
|
|
140
|
+
result = await coro_or_none
|
|
141
|
+
return {"success": True, "result": result}
|
|
137
142
|
|
|
138
143
|
|
|
139
144
|
async def handle_telegram_reconnect(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
|
|
@@ -155,26 +160,22 @@ async def handle_telegram_reconnect(data: Dict[str, Any], websocket: WebSocket)
|
|
|
155
160
|
return await get_telegram_service().connect(secrets["api_key"])
|
|
156
161
|
|
|
157
162
|
|
|
163
|
+
@ws_response
|
|
158
164
|
async def handle_telegram_get_me(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
|
|
159
165
|
service = get_telegram_service()
|
|
160
166
|
if not service.connected:
|
|
161
167
|
return {"success": False, "error": "Telegram bot not connected"}
|
|
162
|
-
|
|
163
|
-
return {"success": True, "result": await service.get_me()}
|
|
164
|
-
except Exception as e:
|
|
165
|
-
return {"success": False, "error": str(e)}
|
|
168
|
+
return {"success": True, "result": await service.get_me()}
|
|
166
169
|
|
|
167
170
|
|
|
171
|
+
@ws_response
|
|
168
172
|
async def handle_telegram_get_chat(data: Dict[str, Any], websocket: WebSocket) -> Dict[str, Any]:
|
|
169
173
|
service = get_telegram_service()
|
|
170
174
|
if not service.connected:
|
|
171
175
|
return {"success": False, "error": "Telegram bot not connected"}
|
|
172
176
|
if not data.get("chat_id"):
|
|
173
177
|
return {"success": False, "error": "chat_id required"}
|
|
174
|
-
|
|
175
|
-
return {"success": True, "result": await service.get_chat(data["chat_id"])}
|
|
176
|
-
except Exception as e:
|
|
177
|
-
return {"success": False, "error": str(e)}
|
|
178
|
+
return {"success": True, "result": await service.get_chat(data["chat_id"])}
|
|
178
179
|
|
|
179
180
|
|
|
180
181
|
# ---------------------------------------------------------------------------
|