Slack socket mode Implementation to save to notion with slack message or reaction(/stamp) in python

* .env
```sh
NOTION_API_KEY=...
NOTION_DATABASE_ID=...

SLACK_APP_TOKEN=...
SLACK_BOT_TOKEN=...
SLACK_USER_TOKEN=...
SLACK_WORKSPACE_NAME=...
SLACK_USER_ID=...
SLACK_CHANNEL_ID=...
SLACK_REACTION=...
```

* python
```python
import datetime
import json
import logging
import os
import requests
from slack_sdk import WebClient
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
from dotenv import load_dotenv
load_dotenv()

logging.basicConfig(level=logging.DEBUG)

app = App(token=os.getenv("SLACK_USER_TOKEN"))
notionkey = os.getenv("NOTION_API_KEY")
slackwname = os.getenv("SLACK_WORKSPACE_NAME")
slackuid = os.getenv("SLACK_USER_ID")
slackcid = os.getenv("SLACK_CHANNEL_ID")
slackreaction = os.getenv("SLACK_REACTION")

notionurl = "https://api.notion.com/v1/pages"
notionheaders = {
    "accept": "application/json",
    "Notion-Version": "2022-06-28",
    "Authorization": f"Bearer {notionkey}"
}

def save_to_notion(messagetext, messagets, slackurl, messageauthor):
    messagedt = datetime.datetime.fromtimestamp(float(messagets))
    json_data = {
      "parent": {
          "type": "database_id",
          "database_id": os.getenv("NOTION_DATABASE_ID")
      },
      "properties": {
          "title": {
              "title": [{
                "text": {
                    "content": messagetext
                }
              }]
          },
          "slackCreated": {
            "rich_text": [{"type":"text","text":{"content":str(messagedt)}}]
          },
          "author": {
            "rich_text": [{"type":"text","text":{"content":messageauthor}}]
          },
          "link": {
            "url": slackurl,
          },
      },
    }
    response = requests.post(notionurl, json=json_data, headers=notionheaders)
    return response

def to_notion(message):
    messagetext = message["text"]
    messagets = message["ts"]
    messagechannel = message["channel"]
    messageauthor = ""
    if "attachments" in message:
      messagea = message["attachments"][0]
      if "message_blocks" in messagea:
        messageblock = messagea['message_blocks'][0]
        messagetext = json.dumps(messageblock["message"]["blocks"][0]["elements"][0]["elements"], ensure_ascii=False)
      if "author_name" in messagea:
        messageauthor = messagea['author_name']
    slackurl = f"https://{slackwname}.slack.com/archives/{messagechannel}/" + f"p{messagets.replace('.', '')}"
    if "thread_ts" in message:
      slackurl = slackurl + "?thread_ts=" + message["thread_ts"]
    resp = save_to_notion(messagetext, message["ts"], slackurl, messageauthor)
    return resp

@app.event("message")
def handle_message_events(body, logger):
    logger.info("handle_message_events start")
    be = body["event"]
    eventchannel = be["channel"]
    eventuser = be["user"]
    if slackcid == eventchannel and slackuid == eventuser:
      logger.info(f"save_to_notion in message: {be}")
      if "message" in be:
        to_notion(be["message"])
      else:
        to_notion(be)

@app.event("reaction_added")
def handle_reaction_added(body, logger):
    reaction = body["event"]["reaction"]
    channel_id = body["event"]["item"]["channel"]
    ts = body["event"]["item"]["ts"]
    logger.info(f"reaction: {reaction}, channel_id:{channel_id}, ts:{ts}")
    if reaction == slackreaction:
        response = app.client.conversations_history(channel=channel_id, latest=ts, limit=1, inclusive=True,  include_all_metadata=True)
        messages = response["messages"]
        message = messages[0]
        messagets = message["ts"]
        messageuser = message["user"]
        messagetext = ""
        logger.info(f"[message] ts:{messagets}, user:{messageuser}")
        if slackuid == messageuser:
          logger.info(f"before save:{messages}")
          logger.info(f"save to notion:{message}")
          response = app.client.conversations_replies(
              channel=channel_id,
              ts=messagets
          )
          for message in response["messages"]:
              logger.info(f'{message["user"]}: {message["text"]}')
              if slackuid == message["user"]:
                logger.info(f"save_to_notion in reaction: {message}")
                message["channel"] = channel_id
                resp = to_notion(message)
                logger.info(f"Response from Notion: {resp.text}")

if __name__ == "__main__":
    handler = SocketModeHandler(app, os.getenv("SLACK_APP_TOKEN"))
    handler.start()
```

* crontab
```sh
@reboot python /path/to/script/app.py > /path/to/log/app_$( date '+\%Y\%m\%d\%H\%M\%S' ).log 2>&1
```