Python backend for a Slack's kudos plugin.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'develop' of github.com:Dekalabs/kefi-backend into develop

+148 -6
+4
kefi/constants.py
··· 16 16 class ViewType: 17 17 JOIN_MEET: str = "meet_join" 18 18 LEAVE_MEET: str = "meet_leave" 19 + 20 + 21 + class Actions: 22 + LEAVE_MEET: str = "meet_leave"
+13
kefi/dependencies.py
··· 1 1 from fastapi import Form 2 + from pydantic import BaseModel 2 3 from sqlmodel import Session 3 4 4 5 from kefi.models.database import engine ··· 7 8 class InteractionParams: 8 9 def __init__(self, payload: str = Form(...)): 9 10 self.payload = payload 11 + 12 + 13 + class EventType(BaseModel): 14 + type: str 15 + user: str 16 + 17 + 18 + class EventBody(BaseModel): 19 + token: str 20 + challenge: str | None 21 + type: str 22 + event: EventType 10 23 11 24 12 25 class SlashCommandParams:
+2 -1
kefi/main.py
··· 7 7 from kefi.models.core.helpers import create_users 8 8 from kefi.models.database import engine 9 9 from kefi.models.kudos.helpers import create_default_actions 10 - from kefi.routers import commands, interactivity 10 + from kefi.routers import commands, events, interactivity 11 11 12 12 app = FastAPI() 13 13 app.include_router(commands.router) 14 14 app.include_router(interactivity.router) 15 + app.include_router(events.router) 15 16 16 17 locale.setlocale(locale.LC_TIME, settings.LOCALE) 17 18
+19
kefi/routers/events.py
··· 1 + from fastapi import APIRouter, Depends 2 + from sqlmodel import Session 3 + 4 + from kefi.dependencies import EventBody, InteractionParams, get_session 5 + from kefi.routers.helpers import InteractionHandler 6 + from kefi.routers.views import HomeView 7 + from kefi.slack import Slack 8 + 9 + router = APIRouter() 10 + 11 + 12 + @router.post("/events/", tags=["events"]) 13 + def handle_events( 14 + body: EventBody, 15 + ): 16 + """Calls the events handler and gets the response.""" 17 + slack = Slack() 18 + slack.publish_view(user_id=body.event.user, view=HomeView()) 19 + return {"challenge": body.challenge}
+15 -4
kefi/routers/helpers.py
··· 27 27 delete_attendance, 28 28 is_attending, 29 29 ) 30 + from kefi.routers.messages import UserJoinedMeet, UserLeftMeet 30 31 from kefi.routers.responses import ( 31 32 ActionResponse, 32 33 HelpResponse, ··· 169 170 InteractionType.VIEW_SUBMISSION: self.interaction_view_submission, 170 171 } 171 172 _type = self.payload["type"] 173 + print(self.payload) 172 174 response = handlers.get(_type, self.not_found)() 173 175 return response 174 176 ··· 197 199 """Joins the plaza.""" 198 200 try: 199 201 create_attendance(user=self.user, session=self.session) 200 - return {"response_action": "clear"} 202 + self.slack.post_message_user( 203 + user_id=self.user.slack_user_id, 204 + blocks=UserJoinedMeet.blocks(), 205 + text=UserJoinedMeet.text(), 206 + ) 201 207 except NotEnoughKefi: 202 208 ... 203 209 except AlreadyAttending: 204 210 ... 205 - return [] 211 + return {"response_action": "clear"} 206 212 207 213 def _view_submission_meet_leave(self, view_id: str) -> list | dict: 208 214 """Leaves the plaza.""" 209 215 try: 210 216 delete_attendance(user=self.user, session=self.session) 211 - return {"response_action": "clear"} 217 + self.slack.post_message_user( 218 + user_id=self.user.slack_user_id, 219 + blocks=UserLeftMeet.blocks(), 220 + text=UserLeftMeet.text(), 221 + ) 212 222 except NotAttending: 213 223 ... 214 - return [] 224 + return {"response_action": "clear"} 215 225 216 226 def interaction_view_submission(self) -> list | dict: 217 227 """Handles the submissions from a view.""" 218 228 view_id = self.payload["view"]["id"] 229 + user_id = self.payload["user"]["id"] 219 230 view_callback_id = self.payload["view"]["callback_id"] 220 231 try: 221 232 return getattr(self, f"_view_submission_{view_callback_id}")(
+53
kefi/routers/messages.py
··· 1 + from slack_sdk.models.blocks import ( 2 + Block, 3 + ButtonElement, 4 + ContextBlock, 5 + DividerBlock, 6 + ImageElement, 7 + MarkdownTextObject, 8 + PlainTextObject, 9 + SectionBlock, 10 + ) 11 + 12 + from kefi.constants import Actions 13 + 14 + 15 + class UserJoinedMeet: 16 + @staticmethod 17 + def text() -> str: 18 + return ":tada: ¡Genial! ¡Nos vemos el próximo viernes!" 19 + 20 + @staticmethod 21 + def blocks() -> list[Block]: 22 + return [ 23 + SectionBlock( 24 + text=MarkdownTextObject( 25 + text=":tada: ¡Genial! ¡Nos vemos el próximo viernes!" 26 + ) 27 + ), 28 + DividerBlock(), 29 + SectionBlock( 30 + text=MarkdownTextObject(text="¿Cambio de planes?"), 31 + accessory=ButtonElement( 32 + text=PlainTextObject(text="No asistiré"), 33 + style="danger", 34 + action_id=Actions.LEAVE_MEET, 35 + ), 36 + ), 37 + ] 38 + 39 + 40 + class UserLeftMeet: 41 + @staticmethod 42 + def text() -> str: 43 + return ":disappointed: ¡Vaya! ¡Esperamos verte la próxima vez!" 44 + 45 + @staticmethod 46 + def blocks() -> list[Block]: 47 + return [ 48 + SectionBlock( 49 + text=MarkdownTextObject( 50 + text=":disappointed: ¡Vaya! ¡Esperamos verte la próxima vez!" 51 + ) 52 + ), 53 + ]
+2
kefi/routers/responses.py
··· 1 + from slack_sdk.models.blocks import Block 2 + 1 3 from kefi.models.database import Action, User 2 4 from kefi.models.outputs import ( 3 5 Context,
+35
kefi/routers/views.py
··· 1 1 from slack_sdk.models.blocks import ( 2 + ActionsBlock, 3 + ButtonElement, 2 4 ContextBlock, 3 5 DividerBlock, 4 6 ImageElement, ··· 82 84 SectionBlock(text=MarkdownTextObject(text="*¿Cambio de planes?*")), 83 85 ], 84 86 ) 87 + 88 + 89 + class HomeView(View): 90 + def __init__(self, *args, **kwargs): 91 + super().__init__( 92 + type="home", 93 + # TODO: not needed: submit=PlainTextObject(text="No asistiré"), 94 + blocks=[ 95 + SectionBlock( 96 + text=MarkdownTextObject( 97 + text="*:wave: ¡Ei! ¡Nos vemos en la Kefi Plaza!*" 98 + ) 99 + ), 100 + DividerBlock(), 101 + SectionBlock( 102 + text=MarkdownTextObject(text="This is so much fun!"), 103 + accessory=ImageElement( 104 + image_url="https://api.slack.com/img/blocks/bkb_template_images/notifications.png", 105 + alt_text="calendar thumbnail", 106 + ), 107 + ), 108 + DividerBlock(), 109 + ContextBlock( 110 + elements=[ 111 + MarkdownTextObject( 112 + text="*:white_check_mark: Has indicado que asistirás.*" 113 + ) 114 + ] 115 + ), 116 + DividerBlock(), 117 + SectionBlock(text=MarkdownTextObject(text="*¿Cambio de planes?*")), 118 + ], 119 + )
+5 -1
kefi/slack.py
··· 1 1 import uuid 2 2 from collections.abc import Sequence 3 3 4 + from slack_sdk.models.blocks import Block 4 5 from slack_sdk.models.views import View 5 6 from slack_sdk.web.client import WebClient 6 7 ··· 42 43 return user_chats 43 44 44 45 def post_message_user( 45 - self, user_id: str, blocks: Sequence[dict], text: str | None = None 46 + self, user_id: str, blocks: Sequence[dict | Block], text: str | None = None 46 47 ): 47 48 self.client.chat_postMessage(channel=user_id, blocks=blocks, text=text) 48 49 ··· 54 55 55 56 def push_view(self, trigger_id: str, view: View): 56 57 self.client.views_push(trigger_id=trigger_id, view=view) 58 + 59 + def publish_view(self, user_id: str, view: View): 60 + self.client.views_publish(user_id=user_id, view=view) 57 61 58 62 def create_group_call( 59 63 self, url: str, users: Sequence[str], external_unique_id: str | None = None