|
1 | | -import json |
2 | 1 | import logging |
3 | 2 | import os |
4 | | -from enum import Enum |
5 | | -from typing import TypedDict, List, Optional, NotRequired |
6 | 3 |
|
7 | | -from slack_bolt import Ack, App, Complete, Fail |
| 4 | +from slack_bolt import App |
8 | 5 | from slack_bolt.adapter.socket_mode import SocketModeHandler |
9 | | -from slack_sdk import WebClient |
10 | 6 |
|
11 | | -logging.basicConfig(level=logging.INFO) |
12 | | - |
13 | | -#---------------------- |
14 | | -# MODELS |
15 | | -#---------------------- |
16 | | - |
17 | | -class FilterType(Enum): |
18 | | - MULTI_SELECT = "multi_select" |
19 | | - TOGGLE = "toggle" |
20 | | - |
21 | | -class EntityReference(TypedDict): |
22 | | - id: str |
23 | | - type: Optional[str] |
24 | | - |
25 | | -class SearchResult(TypedDict): |
26 | | - title: str |
27 | | - description: str |
28 | | - link: str |
29 | | - date_updated: str |
30 | | - external_ref: EntityReference |
31 | | - content: NotRequired[str] |
32 | | - |
33 | | - |
34 | | -class FilterOptions(TypedDict): |
35 | | - name: str |
36 | | - value: str |
37 | | - |
38 | | - |
39 | | -class SearchFilter(TypedDict): |
40 | | - name: str |
41 | | - display_name: str |
42 | | - filter_type: FilterType |
43 | | - options: Optional[List[FilterOptions]] |
44 | | - |
45 | | - |
46 | | -client = WebClient(token=os.environ.get("SLACK_BOT_TOKEN"), base_url="https://slack.com/api/") |
47 | | -app = App(client=client) |
48 | | - |
49 | | - |
50 | | -@app.function("search", auto_acknowledge=False) |
51 | | -def handle_search_step_event( |
52 | | - ack: Ack, inputs: dict, body: dict, fail: Fail, complete: Complete, logger: logging.Logger |
53 | | -): |
54 | | - |
55 | | - try: |
56 | | - query = inputs.get("query") |
57 | | - languages_filter = inputs.get("filters", {}).get("languages", []) |
58 | | - type_filter = inputs.get("filters", {}).get("type", []) |
59 | | - |
60 | | - filters_payload = {} |
61 | | - if languages_filter: |
62 | | - filters_payload["languages"] = languages_filter |
63 | | - if type_filter: |
64 | | - if len(type_filter) == 1: |
65 | | - filters_payload["type"] = type_filter[0] |
66 | | - |
67 | | - params = { |
68 | | - "filters": json.dumps(filters_payload) |
69 | | - } |
70 | | - if query: |
71 | | - params["query"] = query |
72 | | - |
73 | | - logger.info(f"Calling developer.sampleData.get with filters: {filters_payload}") |
74 | | - |
75 | | - response = client.api_call( |
76 | | - api_method="developer.sampleData.get", |
77 | | - params=params |
78 | | - ) |
| 7 | +from listeners import register_listeners |
79 | 8 |
|
80 | | - if not response.get("ok"): |
81 | | - logger.error(f"API error: {response}") |
82 | | - fail(error="Failed to fetch sample data") |
83 | | - return |
84 | | - |
85 | | - samples = response.get("samples", []) |
86 | | - |
87 | | - results: List[SearchResult] = [ |
88 | | - { |
89 | | - "title": sample["title"], |
90 | | - "description": sample["description"], |
91 | | - "link": sample["link"], |
92 | | - "date_updated": sample["date_updated"], |
93 | | - "external_ref": sample["external_ref"], |
94 | | - **({"content": sample["content"]} if "content" in sample else {}), |
95 | | - } |
96 | | - for sample in samples |
97 | | - ] |
98 | | - |
99 | | - complete(outputs={"search_result": results}) |
100 | | - |
101 | | - except Exception as e: |
102 | | - logger.error(f"Error in search step: {str(e)}") |
103 | | - fail(error=str(e)) |
104 | | - finally: |
105 | | - ack() |
106 | | - |
107 | | - |
108 | | -@app.function("filters", auto_acknowledge=False) |
109 | | -def handle_filters_step_event( |
110 | | - ack: Ack, inputs: dict, fail: Fail, complete: Complete, logger: logging.Logger |
111 | | -): |
112 | | - try: |
113 | | - filters: List[SearchFilter] = [ |
114 | | - { |
115 | | - "name": "languages", |
116 | | - "display_name": "Languages", |
117 | | - "type": FilterType.MULTI_SELECT.value, |
118 | | - "options": [ |
119 | | - {"name": "Python", "value": "python"}, |
120 | | - {"name": "Java", "value": "java"}, |
121 | | - {"name": "JavaScript", "value": "javascript"}, |
122 | | - {"name": "TypeScript", "value": "typescript"}, |
123 | | - ], |
124 | | - }, |
125 | | - { |
126 | | - "name": "type", |
127 | | - "display_name": "Type", |
128 | | - "type": FilterType.MULTI_SELECT.value, |
129 | | - "options": [ |
130 | | - {"name": "Template", "value": "template"}, |
131 | | - {"name": "Sample", "value": "sample"}, |
132 | | - ], |
133 | | - }, |
134 | | - ] |
135 | | - |
136 | | - complete(outputs={"filters": filters}) |
137 | | - finally: |
138 | | - ack() |
139 | | - |
140 | | -@app.event("entity_details_requested") |
141 | | -def handle_flexpane_event(event, body, client, logger): |
142 | | - sample_id = event["external_ref"]["id"] |
143 | | - |
144 | | - response = client.api_call(api_method="developer.sampleData.get") |
145 | | - |
146 | | - if not response.get("ok"): |
147 | | - logger.error(f"Failed to fetch samples: {response}") |
148 | | - return |
149 | | - |
150 | | - samples = response.get("samples", []) |
151 | | - |
152 | | - sample = next((s for s in samples if s["external_ref"]["id"] == sample_id), None) |
153 | | - |
154 | | - if not sample: |
155 | | - logger.warning(f"No matching sample found for id={sample_id}") |
156 | | - return |
157 | | - |
158 | | - |
159 | | - custom_fields = [ |
160 | | - { |
161 | | - "key": "description", |
162 | | - "label": "Description of sample", |
163 | | - "type": "string", |
164 | | - "value": sample["description"], |
165 | | - }, |
166 | | - { |
167 | | - "key": "date_updated", |
168 | | - "label": "Last updated", |
169 | | - "type": "string", |
170 | | - "value": sample["date_updated"], |
171 | | - }, |
172 | | - ] |
173 | | - |
174 | | - if "content" in sample: |
175 | | - custom_fields.append({ |
176 | | - "key": "content", |
177 | | - "label": "Details of sample", |
178 | | - "type": "string", |
179 | | - "value": sample["content"], |
180 | | - }) |
| 9 | +logging.basicConfig(level=logging.INFO) |
181 | 10 |
|
182 | | - payload = { |
183 | | - "trigger_id": event["trigger_id"], |
184 | | - "metadata": { |
185 | | - "entity_type": "slack#/entities/item", |
186 | | - "url": event["link"]["url"], |
187 | | - "external_ref": {"id": sample_id}, |
188 | | - "entity_payload": { |
189 | | - "attributes": { |
190 | | - "title": { |
191 | | - "text": sample["title"], |
192 | | - "edit": { |
193 | | - "enabled": False, |
194 | | - "text": { |
195 | | - "max_length": 50 |
196 | | - } |
197 | | - } |
198 | | - }, |
199 | | - }, |
200 | | - "custom_fields": custom_fields, |
201 | | - }, |
202 | | - }, |
203 | | - } |
| 11 | +app = App(token=os.environ.get("SLACK_BOT_TOKEN")) |
204 | 12 |
|
205 | | - try: |
206 | | - client.api_call( |
207 | | - api_method="entity.presentDetails", |
208 | | - json=payload, |
209 | | - ) |
210 | | - except Exception as e: |
211 | | - logger.error(f"Error calling entity.presentDetails: {str(e)}") |
| 13 | +register_listeners(app) |
212 | 14 |
|
213 | 15 | if __name__ == "__main__": |
214 | 16 | SocketModeHandler(app, os.environ.get("SLACK_APP_TOKEN")).start() |
0 commit comments