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

Configure Feed

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

give kudos modal

+142 -49
+8 -3
kefi/constants.py
··· 20 20 21 21 22 22 class Actions: 23 - LEAVE_MEET: str = "meet_leave" 24 - SHOW_MEETS_MODAL: str = "show_meets_modal" 25 - SHOW_GIVE_KUDOS_MODAL: str = "show_give_kudos_modal" 23 + LEAVE_MEET: str = "action_meet_leave" 24 + SHOW_MEETS_MODAL: str = "action_show_meets_modal" 25 + SHOW_GIVE_KUDOS_MODAL: str = "action_show_give_kudos_modal" 26 26 USER_SELECT: str = "action_user_receiver" 27 27 PLAIN_TEXT_INPUT: str = "action_message" 28 28 ··· 30 30 class EventBodyType: 31 31 URL_VERIFICATION: str = "url_verification" 32 32 EVENT_CALLBACK: str = "event_callback" 33 + 34 + 35 + class Block: 36 + RECEIVER: str = "block_receiver" 37 + MESSAGE: str = "block_message"
+49 -8
kefi/routers/helpers.py
··· 4 4 5 5 from sqlmodel import Session 6 6 7 - from kefi.constants import Actions, Command, EventBodyType, InteractionType 7 + from kefi.constants import Actions, Block, Command, EventBodyType, InteractionType 8 8 from kefi.dependencies import EventBody, InteractionParams, SlashCommandParams 9 9 from kefi.models.core.exceptions import NotEnoughKefi 10 10 from kefi.models.core.helpers import ( ··· 33 33 from kefi.routers.messages import ( 34 34 AlreadyAttendingMessage, 35 35 BaseMessage, 36 + KudosReceivedMessage, 37 + KudosSendedMessage, 36 38 NotAttendingMessage, 37 - NotEnoughKefiMessage, 39 + NotEnoughKefiToGiveMessage, 40 + NotEnoughKefiToJoinMessage, 41 + NotGiveToYourselfMessage, 42 + NotUserToGiveMessage, 38 43 UserJoinedMeetMessage, 39 44 UserLeftMeetMessage, 40 45 ) ··· 225 230 text=message.text(), 226 231 ) 227 232 except NotEnoughKefi: 228 - message = NotEnoughKefiMessage() 233 + message = NotEnoughKefiToJoinMessage() 229 234 self.slack.post_message_user( 230 235 user_id=self.user.slack_user_id, 231 236 blocks=message.blocks(), ··· 270 275 action = get_action(keyword=Command.KUDOS, session=self.session) 271 276 if not action: 272 277 return [] 273 - slack_user_id = state["values"]["receiver"][Actions.USER_SELECT][ 278 + slack_user_id = state["values"][Block.RECEIVER][Actions.USER_SELECT][ 274 279 "selected_user" 275 280 ] 276 - message = state["values"]["message"][Actions.PLAIN_TEXT_INPUT]["value"] 281 + 282 + message = state["values"][Block.MESSAGE][Actions.PLAIN_TEXT_INPUT]["value"] 277 283 receiver = find_user_by_slack_user_id( 278 284 slack_user_id=slack_user_id, session=self.session 279 285 ) 286 + 280 287 if not receiver: 281 - return [] 288 + not_user_to_give = NotUserToGiveMessage() 289 + self.slack.post_message_user( 290 + user_id=self.user.slack_user_id, 291 + blocks=not_user_to_give.blocks(), 292 + text=not_user_to_give.text(), 293 + ) 294 + return {"response_action": "clear"} 295 + 296 + if self.user.slack_user_id == receiver.id: 297 + not_give_to_yourself_message = NotGiveToYourselfMessage() 298 + self.slack.post_message_user( 299 + user_id=self.user.slack_user_id, 300 + blocks=not_give_to_yourself_message.blocks(), 301 + text=not_give_to_yourself_message.text(), 302 + ) 303 + return {"response_action": "clear"} 304 + 282 305 try: 283 306 send_action( 284 307 sender=self.user, ··· 287 310 message=message, 288 311 session=self.session, 289 312 ) 290 - # TODO Notify receiver in private chat 313 + 314 + sended_message = KudosSendedMessage() 315 + self.slack.post_message_user( 316 + user_id=self.user.slack_user_id, 317 + blocks=sended_message.blocks(), 318 + text=sended_message.text(), 319 + ) 320 + 321 + received_message = KudosReceivedMessage() 322 + self.slack.post_message_user( 323 + user_id=receiver.slack_user_id, 324 + blocks=received_message.blocks(), 325 + text=received_message.text(), 326 + ) 291 327 except NotEnoughKefi: 292 - ... 328 + not_enough_message = NotEnoughKefiToGiveMessage() 329 + self.slack.post_message_user( 330 + user_id=self.user.slack_user_id, 331 + blocks=not_enough_message.blocks(), 332 + text=not_enough_message.text(), 333 + ) 293 334 return {"response_action": "clear"} 294 335 295 336 def interaction_view_submission(self) -> list | dict:
+1
kefi/routers/interactivity.py
··· 13 13 interaction: InteractionParams = Depends(InteractionParams), 14 14 ): 15 15 """Calls the interaction handler and gets the response.""" 16 + print(interaction.payload) 16 17 interaction_handler = InteractionHandler(interaction=interaction, session=session) 17 18 return interaction_handler.response()
+51 -1
kefi/routers/messages.py
··· 70 70 ] 71 71 72 72 73 - class NotEnoughKefiMessage(BaseMessage): 73 + class NotEnoughKefiToJoinMessage(BaseMessage): 74 74 def text(self) -> str: 75 75 return ":money_with_wings: ¡Vaya! No tienes suficientes kefis para apuntarte." 76 76 ··· 110 110 ) 111 111 ) 112 112 ] 113 + 114 + 115 + class KudosSendedMessage(BaseMessage): 116 + def text(self) -> str: 117 + return "Test" 118 + 119 + def blocks(self) -> list[Block]: 120 + return [SectionBlock(text=MarkdownTextObject(text="Test"))] 121 + 122 + 123 + class KudosReceivedMessage(BaseMessage): 124 + def text(self) -> str: 125 + return "Test" 126 + 127 + def blocks(self) -> list[Block]: 128 + return [SectionBlock(text=MarkdownTextObject(text="Test"))] 129 + 130 + 131 + class NotEnoughKefiToGiveMessage(BaseMessage): 132 + def text(self) -> str: 133 + return "¡No tienes suficientes kefis! 💸" 134 + 135 + def blocks(self) -> list[Block]: 136 + return [ 137 + SectionBlock( 138 + text=MarkdownTextObject(text="¡No tienes suficientes kefis! 💸") 139 + ) 140 + ] 141 + 142 + 143 + class NotGiveToYourselfMessage(BaseMessage): 144 + def text(self) -> str: 145 + return "¡No te puedes dar kefis! 🙅" 146 + 147 + def blocks(self) -> list[Block]: 148 + return [ 149 + SectionBlock(text=MarkdownTextObject(text="¡No te puedes dar kefis! 🙅")) 150 + ] 151 + 152 + 153 + class NotUserToGiveMessage(BaseMessage): 154 + def text(self) -> str: 155 + return "No se ha especificado un destinatario" 156 + 157 + def blocks(self) -> list[Block]: 158 + return [ 159 + SectionBlock( 160 + text=MarkdownTextObject(text="No se ha especificado un destinatario") 161 + ) 162 + ]
+11 -11
kefi/routers/views.py
··· 15 15 from slack_sdk.models.views import View 16 16 17 17 from kefi.config import settings 18 - from kefi.constants import Actions, ViewType 18 + from kefi.constants import Actions, Block, ViewType 19 19 from kefi.models.plazas.helpers import get_or_create_current_plaza 20 20 21 21 ··· 49 49 ContextBlock( 50 50 elements=[ 51 51 PlainTextObject( 52 - text=f"Al apuntarte consumirás {settings.PLAZA_PRICE} Kefis." 52 + text=f"Al apuntarte consumirás {settings.PLAZA_PRICE} kefis." 53 53 ) 54 54 ] 55 55 ), ··· 109 109 HeaderBlock(text=PlainTextObject(text="¡Gracias!")), 110 110 SectionBlock( 111 111 text=MarkdownTextObject( 112 - text=f"Hazle llegar tu agradecimiento a un compañero por ayudarte en un proyecto.\nValor: {settings.KUDOS_PRICE} Kefis" 112 + text=f"Hazle llegar tu agradecimiento a un compañero por ayudarte en un proyecto.\nValor: {settings.KUDOS_PRICE} kefis" 113 113 ), 114 114 accessory=ImageElement( 115 115 image_url="https://storage.staging.dekaside.com/kefi/static/images/kudos.png", ··· 123 123 HeaderBlock(text=PlainTextObject(text="¡Enhorabuena!")), 124 124 SectionBlock( 125 125 text=MarkdownTextObject( 126 - text=f"Un trabajo bien hecho, una buena idea, la certificación en un nuevo curso... ¡se merecen una enhorabuena!\nValor: {settings.CONGRATS_PRICE} Kefis" 126 + text=f"Un trabajo bien hecho, una buena idea, la certificación en un nuevo curso... ¡se merecen una enhorabuena!\nValor: {settings.CONGRATS_PRICE} kefis" 127 127 ), 128 128 accessory=ImageElement( 129 129 image_url="https://storage.staging.dekaside.com/kefi/static/images/congrats.png", ··· 145 145 HeaderBlock(text=PlainTextObject(text="¡High Five!")), 146 146 SectionBlock( 147 147 text=MarkdownTextObject( 148 - text=f"Alguien ha sido un buen colega y te ha alegrado el día.\nValor: {settings.HIGH_FIVE_PRICE} Kefis" 148 + text=f"Alguien ha sido un buen colega y te ha alegrado el día.\nValor: {settings.HIGH_FIVE_PRICE} kefis" 149 149 ), 150 150 accessory=ImageElement( 151 151 image_url="https://storage.staging.dekaside.com/kefi/static/images/highfive.png", ··· 159 159 HeaderBlock(text=PlainTextObject(text="Kefi Plaza")), 160 160 SectionBlock( 161 161 text=MarkdownTextObject( 162 - text=f"Cada encuentro se asignan aleatoriamente grupos de {settings.PLAZA_SIZE} personas en una sala virtual para compartir un Kefi online y hablar de los que más os guste. ¿Te unes?\nCoste: {settings.PLAZA_PRICE} Kefis" 162 + text=f"Cada encuentro se asignan aleatoriamente grupos de {settings.PLAZA_SIZE} personas en una sala virtual para compartir un Kefi online y hablar de los que más os guste. ¿Te unes?\nCoste: {settings.PLAZA_PRICE} k" 163 163 ), 164 164 accessory=ImageElement( 165 165 image_url="https://storage.staging.dekaside.com/kefi/static/images/kefi_plaza.png", ··· 169 169 ActionsBlock( 170 170 elements=[ 171 171 ButtonElement( 172 - text=PlainTextObject(text="¡Me Apunto!"), 172 + text=PlainTextObject(text="¡Me apunto!"), 173 173 action_id=Actions.SHOW_MEETS_MODAL, 174 174 style="primary", 175 175 ) ··· 204 204 type="modal", 205 205 title=PlainTextObject(text="¡Gracias!"), 206 206 close=PlainTextObject(text="Cancelar"), 207 - submit=PlainTextObject(text="Enviar Kefis"), 207 + submit=PlainTextObject(text="Enviar kefis"), 208 208 blocks=[ 209 209 SectionBlock( 210 - block_id="receiver", 210 + block_id=Block.RECEIVER, 211 211 text=MarkdownTextObject(text="¿A quien quieres felicitar?"), 212 212 accessory=UserSelectElement( 213 213 placeholder=PlainTextObject(text="Selecciona un usuario"), ··· 215 215 ), 216 216 ), 217 217 InputBlock( 218 - block_id="message", 218 + block_id=Block.MESSAGE, 219 219 element=PlainTextInputElement( 220 220 multiline=True, action_id=Actions.PLAIN_TEXT_INPUT 221 221 ), ··· 223 223 ), 224 224 ContextBlock( 225 225 elements=[ 226 - PlainTextObject(text=f"Valor: {settings.KUDOS_PRICE} Kefis") 226 + PlainTextObject(text=f"Valor: {settings.KUDOS_PRICE} kefis") 227 227 ] 228 228 ), 229 229 ],
+19 -26
kefi/tests/mocks.py
··· 3 3 "team": {"id": "T0RS507QX", "domain": "dekalabs"}, 4 4 "user": { 5 5 "id": "user_1", 6 - "username": "marcos", 7 - "name": "marcos", 6 + "username": "javichaques", 7 + "name": "javichaques", 8 8 "team_id": "T0RS507QX", 9 9 }, 10 10 "api_app_id": "A0499FQH7V3", 11 11 "token": "7n993LRqLRydOnIkEhymVcYD", 12 - "trigger_id": "4353633465297.25889007847.861312cf1a82b53e7b2962c17c74e6d3", 12 + "trigger_id": "4327568797879.25889007847.0f6a80604ac4fac92933e384a7792030", 13 13 "view": { 14 - "id": "V049LD8L17Z", 14 + "id": "V04ARPPKCU8", 15 15 "team_id": "T0RS507QX", 16 16 "type": "modal", 17 17 "blocks": [ 18 18 { 19 19 "type": "section", 20 - "block_id": "9ch", 20 + "block_id": "block_receiver", 21 21 "text": { 22 22 "type": "mrkdwn", 23 - "text": "¿A quien quieres felicitar?", 23 + "text": "\u00bfA quien quieres felicitar?", 24 24 "verbatim": False, 25 25 }, 26 26 "accessory": { 27 27 "type": "users_select", 28 - "action_id": "user_select", 28 + "action_id": "action_user_receiver", 29 29 "placeholder": { 30 30 "type": "plain_text", 31 31 "text": "Selecciona un usuario", ··· 35 35 }, 36 36 { 37 37 "type": "input", 38 - "block_id": "V7=l", 38 + "block_id": "block_message", 39 39 "label": { 40 40 "type": "plain_text", 41 - "text": "Déjale un mensaje", 41 + "text": "D\u00e9jale un mensaje", 42 42 "emoji": True, 43 43 }, 44 44 "optional": False, 45 45 "dispatch_action": False, 46 46 "element": { 47 47 "type": "plain_text_input", 48 - "action_id": "plain_text_input", 48 + "action_id": "action_message", 49 49 "multiline": True, 50 50 "dispatch_action_config": { 51 51 "trigger_actions_on": ["on_enter_pressed"] ··· 54 54 }, 55 55 { 56 56 "type": "context", 57 - "block_id": "1w6w", 57 + "block_id": "pYpG", 58 58 "elements": [ 59 - { 60 - "type": "plain_text", 61 - "text": "Valor: 100 Kefis", 62 - "emoji": True, 63 - } 59 + {"type": "plain_text", "text": "Valor: 100 kefis", "emoji": True} 64 60 ], 65 61 }, 66 62 ], ··· 68 64 "callback_id": "give_kudos", 69 65 "state": { 70 66 "values": { 71 - "receiver": { 67 + "block_receiver": { 72 68 "action_user_receiver": { 73 69 "type": "users_select", 74 70 "selected_user": "user_2", 75 71 } 76 72 }, 77 - "message": { 78 - "action_message": { 79 - "type": "plain_text_input", 80 - "value": "dummy", 81 - } 73 + "block_message": { 74 + "action_message": {"type": "plain_text_input", "value": "Test"} 82 75 }, 83 76 } 84 77 }, 85 - "hash": "1667928027.cHsXEKGC", 86 - "title": {"type": "plain_text", "text": "¡Gracias!", "emoji": True}, 78 + "hash": "1667939174.QSF9AysY", 79 + "title": {"type": "plain_text", "text": "\u00a1Gracias!", "emoji": True}, 87 80 "clear_on_close": False, 88 81 "notify_on_close": False, 89 82 "close": {"type": "plain_text", "text": "Cancelar", "emoji": True}, 90 - "submit": {"type": "plain_text", "text": "Enviar Kefis", "emoji": True}, 83 + "submit": {"type": "plain_text", "text": "Enviar kefis", "emoji": True}, 91 84 "previous_view_id": None, 92 - "root_view_id": "V049LD8L17Z", 85 + "root_view_id": "V04ARPPKCU8", 93 86 "app_id": "A0499FQH7V3", 94 87 "external_id": "", 95 88 "app_installed_team_id": "T0RS507QX",
+3
kefi/tests/test_interactivity.py
··· 59 59 views_open = mocker.patch.object(WebClient, "views_open") 60 60 views_open_response: dict = {} # Adds expected response here 61 61 views_open.side_effect = [views_open_response] 62 + # post_message_user = mocker.patch.object(WebClient, "chat_postMessage") 63 + # post_message_user_response: dict = {} # Adds expected response here 64 + # post_message_user.side_effect = [post_message_user_response] 62 65 payload = json.dumps(INTERACTIVITY_VIEW_ACTION_KUDOS) 63 66 response = client.post("/interactivity/", data={"payload": payload}) 64 67 assert response.status_code == 200