microsoft-teams-apps 0.0.1a1__tar.gz

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.
Files changed (45) hide show
  1. microsoft_teams_apps-0.0.1a1/.gitignore +34 -0
  2. microsoft_teams_apps-0.0.1a1/PKG-INFO +24 -0
  3. microsoft_teams_apps-0.0.1a1/README.md +7 -0
  4. microsoft_teams_apps-0.0.1a1/pyproject.toml +41 -0
  5. microsoft_teams_apps-0.0.1a1/scripts/generate_handlers.py +203 -0
  6. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/__init__.py +20 -0
  7. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/app.py +443 -0
  8. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/app_events.py +54 -0
  9. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/app_oauth.py +137 -0
  10. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/app_plugins.py +149 -0
  11. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/app_process.py +237 -0
  12. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/app_tokens.py +17 -0
  13. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/auth/__init__.py +12 -0
  14. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/auth/jwt_middleware.py +69 -0
  15. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/auth/service_token_validator.py +102 -0
  16. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/container.py +10 -0
  17. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/events/__init__.py +30 -0
  18. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/events/base.py +14 -0
  19. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/events/registry.py +119 -0
  20. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/events/types.py +107 -0
  21. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/http_plugin.py +323 -0
  22. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/options.py +31 -0
  23. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/__init__.py +32 -0
  24. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/metadata.py +131 -0
  25. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/plugin_activity_event.py +27 -0
  26. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/plugin_activity_response_event.py +27 -0
  27. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/plugin_activity_sent_event.py +25 -0
  28. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/plugin_base.py +54 -0
  29. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/plugin_error_event.py +24 -0
  30. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/plugin_start_event.py +13 -0
  31. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/sender.py +26 -0
  32. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/plugins/streamer.py +53 -0
  33. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/routing/__init__.py +10 -0
  34. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/routing/activity_context.py +243 -0
  35. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/routing/activity_handlers.py +121 -0
  36. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/routing/activity_route_configs.py +565 -0
  37. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/routing/generated_handlers.py +3056 -0
  38. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/routing/router.py +33 -0
  39. microsoft_teams_apps-0.0.1a1/src/microsoft/teams/apps/routing/type_validation.py +67 -0
  40. microsoft_teams_apps-0.0.1a1/tests/conftest.py +59 -0
  41. microsoft_teams_apps-0.0.1a1/tests/test_app.py +485 -0
  42. microsoft_teams_apps-0.0.1a1/tests/test_app_oauth.py +350 -0
  43. microsoft_teams_apps-0.0.1a1/tests/test_http_plugin.py +290 -0
  44. microsoft_teams_apps-0.0.1a1/tests/test_plugin_decorator.py +36 -0
  45. microsoft_teams_apps-0.0.1a1/tests/test_service_token_validator.py +217 -0
@@ -0,0 +1,34 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.egg-info/
6
+
7
+ # Environments
8
+ .env
9
+ .venv
10
+ env/
11
+ venv/
12
+ ENV/
13
+ env.bak/
14
+ venv.bak/
15
+
16
+ # mypy
17
+ .mypy_cache/
18
+ .dmypy.json
19
+ dmypy.json
20
+
21
+ .copilot-instructions.md
22
+
23
+ # other
24
+ .DS_STORE
25
+ *.bak
26
+ *~
27
+ *.tmp
28
+
29
+ ref/
30
+ py.typed
31
+ CLAUDE.md
32
+
33
+ .env.claude/
34
+ .claude/
@@ -0,0 +1,24 @@
1
+ Metadata-Version: 2.4
2
+ Name: microsoft-teams-apps
3
+ Version: 0.0.1a1
4
+ Summary: The app package for a Microsoft Teams agent
5
+ Author-email: Microsoft <TeamsAISDKFeedback@microsoft.com>
6
+ License-Expression: MIT
7
+ Keywords: agents,ai,bot,microsoft,teams
8
+ Requires-Python: >=3.12
9
+ Requires-Dist: cryptography>=3.4.0
10
+ Requires-Dist: dependency-injector>=4.48.1
11
+ Requires-Dist: fastapi>=0.115.13
12
+ Requires-Dist: microsoft-teams-api
13
+ Requires-Dist: microsoft-teams-common
14
+ Requires-Dist: pyjwt[crypto]>=2.10.0
15
+ Requires-Dist: uvicorn>=0.34.3
16
+ Description-Content-Type: text/markdown
17
+
18
+ > [!CAUTION]
19
+ > This project is in active development and not ready for production use. It has not been publicly announced yet.
20
+
21
+ # Microsoft Teams App Framework
22
+
23
+ High-level framework for building Microsoft Teams bots and applications.
24
+ Handles routing, middleware, events, and provides OAuth integration for Teams apps.
@@ -0,0 +1,7 @@
1
+ > [!CAUTION]
2
+ > This project is in active development and not ready for production use. It has not been publicly announced yet.
3
+
4
+ # Microsoft Teams App Framework
5
+
6
+ High-level framework for building Microsoft Teams bots and applications.
7
+ Handles routing, middleware, events, and provides OAuth integration for Teams apps.
@@ -0,0 +1,41 @@
1
+ [project]
2
+ name = "microsoft-teams-apps"
3
+ version = "0.0.1-alpha.1"
4
+ description = "The app package for a Microsoft Teams agent"
5
+ authors = [{ name = "Microsoft", email = "TeamsAISDKFeedback@microsoft.com" }]
6
+ readme = "README.md"
7
+ requires-python = ">=3.12"
8
+ repository = "https://github.com/microsoft/teams.py"
9
+ keywords = ["microsoft", "teams", "ai", "bot", "agents"]
10
+ license = "MIT"
11
+
12
+ dependencies = [
13
+ "fastapi>=0.115.13",
14
+ "microsoft-teams-api",
15
+ "microsoft-teams-common",
16
+ "uvicorn>=0.34.3",
17
+ "cryptography>=3.4.0",
18
+ "pyjwt[crypto]>=2.10.0",
19
+ "dependency-injector>=4.48.1",
20
+ ]
21
+
22
+ [dependency-groups]
23
+ test = [
24
+ "pytest>=8.0.0",
25
+ "pytest-asyncio>=0.24.0",
26
+ "httpx>=0.27.0",
27
+ ]
28
+
29
+ [build-system]
30
+ requires = ["hatchling"]
31
+ build-backend = "hatchling.build"
32
+
33
+ [tool.hatch.build.targets.wheel]
34
+ packages = ["src/microsoft", "scripts"]
35
+
36
+ [project.scripts]
37
+ generate-activity-handlers = "scripts.generate_handlers:generate_activity_handlers"
38
+
39
+ [tool.uv.sources]
40
+ microsoft-teams-common = { workspace = true }
41
+ microsoft-teams-api = { workspace = true }
@@ -0,0 +1,203 @@
1
+ """
2
+ Copyright (c) Microsoft Corporation. All rights reserved.
3
+ Licensed under the MIT License.
4
+ """
5
+
6
+ import importlib.util
7
+ import subprocess
8
+ from pathlib import Path
9
+ from typing import TYPE_CHECKING
10
+
11
+ if TYPE_CHECKING:
12
+ import microsoft.teams.apps.routing.activity_route_configs as activity_config
13
+
14
+ # Import the activity config directly without going through the package hierarchy
15
+ activity_config_path = (
16
+ Path(__file__).parent.parent / "src" / "microsoft" / "teams" / "app" / "routing" / "activity_route_configs.py"
17
+ )
18
+
19
+ # Load the activity config module directly because we don't want to have a dependency on the package
20
+ # as it will lead to a circular dependency
21
+ # https://stackoverflow.com/questions/67631/how-can-i-import-a-module-dynamically-given-the-full-path
22
+ if not TYPE_CHECKING:
23
+ import sys
24
+
25
+ # Add the src directory to sys.path so relative imports work
26
+ src_path = str(Path(__file__).parent.parent / "src")
27
+ if src_path not in sys.path:
28
+ sys.path.insert(0, src_path)
29
+
30
+ spec = importlib.util.spec_from_file_location(
31
+ "microsoft.teams.apps.routing.activity_route_configs", activity_config_path
32
+ )
33
+ assert spec is not None, f"Could not find activity_route_configs.py at {activity_config_path}"
34
+ activity_config = importlib.util.module_from_spec(spec)
35
+ assert spec.loader is not None, f"Could not load activity_route_configs.py at {activity_config_path}"
36
+ spec.loader.exec_module(activity_config)
37
+
38
+ ACTIVITY_ROUTES = activity_config.ACTIVITY_ROUTES
39
+ ActivityConfig = activity_config.ActivityConfig
40
+
41
+
42
+ def generate_imports() -> str:
43
+ """Generate import statements for the generated file."""
44
+ imports = {
45
+ "from abc import ABC, abstractmethod",
46
+ "from typing import Callable, Optional, Union, Awaitable, overload",
47
+ "from .activity_context import ActivityContext",
48
+ "from .router import ActivityRouter",
49
+ "from .type_validation import validate_handler_type",
50
+ "from .activity_route_configs import ACTIVITY_ROUTES",
51
+ "from microsoft.teams.models import ActivityBase",
52
+ "from logging import Logger",
53
+ "from microsoft.teams.api import InvokeResponse",
54
+ }
55
+
56
+ # Add imports for each activity class
57
+ for config in ACTIVITY_ROUTES.values():
58
+ # Use explicit input_type_name if provided, otherwise fall back to __name__
59
+ class_name = config.input_model if isinstance(config.input_model, str) else config.input_model.__name__
60
+ if class_name == "ActivityBase":
61
+ imports.add(f"from microsoft.teams.api.models import {class_name}")
62
+ else:
63
+ imports.add(f"from microsoft.teams.api.activities import {class_name}")
64
+ if config.output_model or config.output_type_name:
65
+ # Use explicit output_type_name if provided, otherwise fall back to __name__
66
+ output_class_name = config.output_type_name
67
+ if not output_class_name:
68
+ if config.output_model:
69
+ output_class_name = config.output_model.__name__
70
+ else:
71
+ raise ValueError(f"Output type for {config.name} must be specified in the config or as a string.")
72
+ imports.add(f"from microsoft.teams.api.models.invoke_response import {output_class_name}")
73
+
74
+ return "\n".join(sorted(imports))
75
+
76
+
77
+ def generate_method(config: ActivityConfig, config_key: str) -> str:
78
+ """Generate a single handler method with strict typing and runtime validation."""
79
+ method_name = config.method_name
80
+ activity_name = config.name
81
+
82
+ # Use the explicit input_type_name if provided, otherwise fall back to __name__
83
+ input_class_name = config.input_model if isinstance(config.input_model, str) else config.input_model.__name__
84
+
85
+ # Determine output type
86
+ if config.output_type_name or config.output_model:
87
+ # Use explicit output_type_name if provided, otherwise fall back to __name__
88
+ output_class_name = config.output_type_name
89
+ if not output_class_name:
90
+ if config.output_model:
91
+ output_class_name = config.output_model.__name__
92
+ else:
93
+ raise ValueError(f"Output type for {method_name} must be specified in the config or as a string.")
94
+ if config.is_invoke:
95
+ output_class_name = f"Union[InvokeResponse[{output_class_name}], {output_class_name}]"
96
+ output_type = f"Awaitable[{output_class_name}]"
97
+ else:
98
+ if config.is_invoke:
99
+ output_type = "Awaitable[Union[InvokeResponse[None], None]]"
100
+ else:
101
+ output_type = "Awaitable[None]"
102
+
103
+ returnTypeStr = f"Callable[[ActivityContext[{input_class_name}]], {output_type}]"
104
+ returnTypeDecoratorStr = f"Callable[[{returnTypeStr}], {returnTypeStr}]"
105
+
106
+ return f''' @overload
107
+ def {method_name}(self, handler: {returnTypeStr}) -> {returnTypeStr}: ...
108
+
109
+ @overload
110
+ def {method_name}(self) -> {returnTypeDecoratorStr}: ...
111
+
112
+ def {method_name}(self, handler: Optional[{returnTypeStr}] = None) -> {returnTypeDecoratorStr} | {returnTypeStr}:
113
+ """Register a {activity_name} activity handler."""
114
+ def decorator(func: {returnTypeStr}) -> {returnTypeStr}:
115
+ validate_handler_type(self.logger, func, {input_class_name}, "{method_name}", "{input_class_name}")
116
+ config = ACTIVITY_ROUTES["{config_key}"]
117
+ self.router.add_handler(config.selector, func)
118
+ return func
119
+
120
+ if handler is not None:
121
+ return decorator(handler)
122
+ return decorator''' # noqa: E501, W291, W293, W391
123
+
124
+
125
+ def generate_mixin_class() -> str:
126
+ """Generate the complete ActivityHandlerMixin class."""
127
+ methods: list[str] = []
128
+
129
+ for config_key, config in ACTIVITY_ROUTES.items():
130
+ methods.append(generate_method(config, config_key))
131
+
132
+ methods_code = "\n\n".join(methods)
133
+
134
+ return f'''class GeneratedActivityHandlerMixin(ABC):
135
+ """Mixin class providing typed activity handler registration methods."""
136
+
137
+ @property
138
+ @abstractmethod
139
+ def router(self) -> ActivityRouter:
140
+ """The activity router instance. Must be implemented by the concrete class."""
141
+ pass
142
+
143
+ @property
144
+ @abstractmethod
145
+ def logger(self) -> Logger:
146
+ """The logger instance used by the app."""
147
+ pass
148
+
149
+ {methods_code}'''
150
+
151
+
152
+ def generate_file_header() -> str:
153
+ """Generate the file header with copyright and description."""
154
+ return '''"""
155
+ Copyright (c) Microsoft Corporation. All rights reserved.
156
+ Licensed under the MIT License.
157
+
158
+ GENERATED FILE - DO NOT EDIT MANUALLY
159
+ This file is generated by generate_handlers.py based on activity_config.py
160
+
161
+ To regenerate, run:
162
+ uv run generate-activity-handlers
163
+ """'''
164
+
165
+
166
+ def generate_activity_handlers():
167
+ """Generate the complete activity handlers file."""
168
+ print("🔧 Generating activity handlers...")
169
+
170
+ # Build the complete file content
171
+ content_parts = [
172
+ generate_file_header(),
173
+ "",
174
+ generate_imports(),
175
+ "",
176
+ "",
177
+ generate_mixin_class(),
178
+ ]
179
+
180
+ generated_code = "\n".join(content_parts)
181
+
182
+ # Write to the message_handler directory in the source code
183
+ # Use Path(__file__) to find this script's location, then navigate to the target
184
+ script_dir = Path(__file__).parent
185
+ source_dir = script_dir.parent / "src" / "microsoft" / "teams" / "app" / "routing"
186
+ output_path = source_dir / "generated_handlers.py"
187
+
188
+ # Ensure the target directory exists
189
+ output_path.parent.mkdir(parents=True, exist_ok=True)
190
+ output_path.write_text(generated_code)
191
+
192
+ print(f"✅ Generated {len(ACTIVITY_ROUTES)} activity handlers in {output_path}")
193
+ print("📝 Generated methods:")
194
+ for config in ACTIVITY_ROUTES.values():
195
+ print(f" - {config.method_name}() for {config.name} activities")
196
+
197
+ # execute poe fmt on the generated file
198
+ subprocess.run(["poe", "lint", "--select", "I", "--select", "F401", "--fix"], check=True)
199
+ subprocess.run(["poe", "fmt", str(output_path)], check=True)
200
+
201
+
202
+ if __name__ == "__main__":
203
+ generate_activity_handlers()
@@ -0,0 +1,20 @@
1
+ """
2
+ Copyright (c) Microsoft Corporation. All rights reserved.
3
+ Licensed under the MIT License.
4
+ """
5
+
6
+ from . import auth, events, plugins
7
+ from .app import App
8
+ from .app_tokens import AppTokens
9
+ from .auth import * # noqa: F403
10
+ from .events import * # noqa: F401, F403
11
+ from .http_plugin import HttpPlugin
12
+ from .options import AppOptions
13
+ from .plugins import * # noqa: F401, F403
14
+ from .routing import ActivityContext
15
+
16
+ # Combine all exports from submodules
17
+ __all__: list[str] = ["App", "AppOptions", "HttpPlugin", "ActivityContext", "AppTokens"]
18
+ __all__.extend(auth.__all__)
19
+ __all__.extend(events.__all__)
20
+ __all__.extend(plugins.__all__)