Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions slackronyms/.slack/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
apps.dev.json
cache/
6 changes: 6 additions & 0 deletions slackronyms/.slack/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"manifest": {
"source": "local"
},
"project_id": "725579d7-7881-4412-8090-c011a560fa42"
}
5 changes: 5 additions & 0 deletions slackronyms/.slack/hooks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"hooks": {
"get-hooks": "python3 -m slack_cli_hooks.hooks.get_hooks"
}
}
25 changes: 25 additions & 0 deletions slackronyms/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import logging
import os

from listeners import register_listeners
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler

logging.basicConfig(level=logging.DEBUG)

# From OAuth & Permissions tab
bot_user_oauth_token = os.environ.get("SLACK_BOT_TOKEN")

# From Basic Information tab's App-Level Tokens section
app_token = os.environ.get("SLACK_APP_TOKEN")

# Initialization
app = App(token=bot_user_oauth_token)

# Register Listeners
register_listeners(app)

# Start Bolt app
if __name__ == "__main__":
# app.start(port=8080)
SocketModeHandler(app, app_token).start()
Empty file added slackronyms/data/.gitkeep
Empty file.
5 changes: 5 additions & 0 deletions slackronyms/listeners/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from listeners import commands


def register_listeners(app):
commands.register(app)
6 changes: 6 additions & 0 deletions slackronyms/listeners/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from slack_bolt import App

from .define import define_callback

def register(app: App):
app.command("/define")(define_callback)
57 changes: 57 additions & 0 deletions slackronyms/listeners/commands/define.py

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is where all the magic happens.

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import json
from datetime import datetime as dt
from logging import Logger

from slack_bolt import Ack, Respond


def define_callback(command, ack: Ack, respond: Respond, logger: Logger):
logger.debug(command)

# TODO: DETERMINE HOW TO HANDLE REMOTE STORAGE OF THIS JSON FILE

# load the current list of terms into a dict
with open("data/terms.json", mode="r", encoding="utf-8") as read_file:
terms = json.load(read_file)

# split command text at the first colon (if any) and detemine whether we have a term
text = command["text"]
text = text.split(":", maxsplit=1)
term = text[0].strip()

# check whether the term we ened up with isn't just an empty string
if term:
try:
if len(text) == 2:
# the split resulted in the list `text` having two items, so this is a request to set a definition
definition = text[1].strip()
if definition:
ack()
terms[term] = {
"definition": definition,
"added_by": command["user_name"],
"added_on": dt.now().strftime("%Y-%m-%d %H:%M:%S"),
}
with open("data/terms.json", mode="w", encoding="utf-8") as write_file:
json.dump(terms, write_file, indent=2)
respond(f'*{term}* is now defined as "{definition}"', response_type="ephemeral")
else:
ack("Please specify a definition after the colon.")
else:
# the split resulted in the list `text` having only one item, so this is a request to output a definition
ack()

# TODO: MATCH TERMS CASE INSENSITIVELY

if term_dict := terms.get(term):
# definition exists, so spit it out publicly
respond(
f"Someone asked what *{term}* means. It means: *{term_dict['definition']}*", response_type="in_channel"
)
else:
# definition doesn't exist, let the requester know that privately
respond(f"*{term}* not found. Use `/define {term}: <definition>` to add it.", response_type="ephemeral")
except Exception as e:
logger.error(e)
else:
ack("You must specify a term.")
58 changes: 58 additions & 0 deletions slackronyms/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"_metadata": {
"major_version": 1,
"minor_version": 1
},
"display_information": {
"name": "Slackronyms"
},
"features": {
"app_home": {
"home_tab_enabled": true,
"messages_tab_enabled": false,
"messages_tab_read_only_enabled": true
},
"bot_user": {
"display_name": "Slackronyms",
"always_online": false
},
"shortcuts": [
{
"name": "Run sample shortcut",
"type": "global",
"callback_id": "sample_shortcut_id",
"description": "Runs a sample shortcut"
}
],
"slash_commands": [
{
"command": "/define",
"description": "Explain the given term. Add a colon and definition to set it.",
"usage_hint": "term[: definition]",
"should_escape": false
},
{
"command": "/sample-command",
"description": "Runs a sample command",
"should_escape": false
}
]
},
"oauth_config": {
"scopes": {
"bot": ["chat:write", "chat:write.public", "commands"]
}
},
"settings": {
"event_subscriptions": {
"bot_events": ["app_home_opened", "message.channels"]
},
"interactivity": {
"is_enabled": true
},
"org_deploy_enabled": false,
"socket_mode_enabled": true,
"token_rotation_enabled": false,
"is_mcp_enabled": false
}
}
4 changes: 4 additions & 0 deletions slackronyms/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Flask==3.1.3
slack-bolt==1.28.0
slack-cli-hooks<1.0.0
slack-sdk==3.42.0
Empty file added slackronyms/tests/__init__.py
Empty file.
Empty file.
Empty file.
41 changes: 41 additions & 0 deletions slackronyms/tests/listeners/commands/test_define.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import logging
from unittest.mock import Mock

from listeners.commands.define import define_callback
from slack_bolt import Ack, Respond

test_logger = logging.getLogger(__name__)


class TestDefine:
def setup_method(self):
self.fake_ack = Mock(Ack)
self.fake_respond = Mock(Respond)
self.fake_command = {"text": "TEST"}

def test_define_callback_definition(self):
command = {"text": "who"}
define_callback(
command,
ack=self.fake_ack,
respond=self.fake_respond,
logger=test_logger,
)

self.fake_ack.assert_called_once()

self.fake_respond.assert_called_once()
args = self.fake_respond.call_args.args
assert "he's on first" in args[0]

def test_ack_exception(self, caplog):
self.fake_ack.side_effect = Exception("test exception")
define_callback(
self.fake_command,
ack=self.fake_ack,
respond=self.fake_respond,
logger=test_logger,
)

self.fake_ack.assert_called_once()
assert str(self.fake_ack.side_effect) in caplog.text