From df21baf7f091e420f368ccfb10ee52c6dee7f59a Mon Sep 17 00:00:00 2001
From: Seok Won <alfex4936@gmail.com>
Date: Sun, 6 Dec 2020 12:02:10 +0900
Subject: [PATCH] =?UTF-8?q?Slack=20+=20Kafka:=20=EC=95=84=EC=A3=BC?=
 =?UTF-8?q?=EB=8C=80=ED=95=99=EA=B5=90=20=EA=B3=B5=EC=A7=80=20=EB=B4=87?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

1시간마다 모든 공지를 불러 {"TITLE": "제목", "DATE": "올린 날", "LINK": "http 주소", "WRITER": "글쓴이"}를 json 형태로 저장한다.

이 json 파일이나 새로운 공지가 있으면 기존 json과 비교해서 새로운 데이터를 Consumer로 보내고, Consumer는 새로운 데이터를 받으면, Slack API를 이용해, "#아주대" 채널에 공지를 올려준다.

마지막 파싱 시간도 기록해 종료 후 다시 불러도 1시간이 지나지 않으면 파싱하지 않는다.

결과)
Last parsing: 1972-12-01 07:00:00
Trying to parse new posts...
Sending a new post...: 12179
...

Last parsing: 2020-12-04 19:11:42.839219
Trying to parse new posts...
No new posts yet...
Resting 1 hour...
...
Last parsing: 2020-12-06 11:55:35.386262
Wait for 3494 seconds to sync new posts.
---
 img/slack_ajou.png               | Bin 0 -> 27773 bytes
 python/README.md                 |  30 ++++++
 python/src/AjouSlackConsumer.py  |  89 ++++++++++++++++++
 python/src/AjouSlackProducer.py  | 157 +++++++++++++++++++++++++++++++
 python/src/SlackKafkaConsumer.py |   7 +-
 python/src/SlackKafkaProducer.py |   4 +-
 python/src/config.py             |   1 +
 7 files changed, 285 insertions(+), 3 deletions(-)
 create mode 100644 img/slack_ajou.png
 create mode 100644 python/src/AjouSlackConsumer.py
 create mode 100644 python/src/AjouSlackProducer.py

diff --git a/img/slack_ajou.png b/img/slack_ajou.png
new file mode 100644
index 0000000000000000000000000000000000000000..46307c275336b55279f9bfe61b26cad09d1a4af3
GIT binary patch
literal 27773
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
TNEd%K16%3o>gTe~DWM4f3r03%

literal 0
HcmV?d00001

diff --git a/python/README.md b/python/README.md
index d9e159b..591b4bc 100644
--- a/python/README.md
+++ b/python/README.md
@@ -34,6 +34,36 @@ WIN10@DESKTOP:~$ pip install confluent-kafka
 
 ```
 
+## Slack API with Ajou University notices parser
+
+[Get Slack API here](https://api.slack.com/)
+
+First, invite your bot to your channel. (In this example, we set it as "#아주대")
+
+The [producer](https://github.com/Alfex4936/kafka-Studies/tree/main/python/src/AjouSlackProducer.py) will notify the [consumer](https://github.com/Alfex4936/kafka-Studies/tree/main/python/src/AjouSlackConsumer.py) whenever there are new notices.
+
+The *producer* checks new notices per an hour, saves latest 10 notices to json file,
+and sees if there is/are a new notice/s.
+
+If there is a new notice, it sends {"TITLE": "title", "DATE": "post date", "LINK": "http address", "WRITER": "writer"} to the consumer.
+
+The *consumer* checks new datas every 5 seconds, if it gets a new data,
+it consumes the data and leave a comment like this below image.
+
+<div align="center">
+<p>
+    <img width="480" src="https://github.com/Alfex4936/kafka-Studies/blob/main/img/slack_ajou.png">
+</p>
+</div>
+
+:b: Run the server first to see the results.
+
+```console
+WIN10@DESKTOP:~$ python AjouSlackProducer.py
+
+WIN10@DESKTOP:~$ python AjouSlackConsumer.py
+```
+
 ## Slack API Producer Usage
 
 [Get Slack API](https://api.slack.com/)
diff --git a/python/src/AjouSlackConsumer.py b/python/src/AjouSlackConsumer.py
new file mode 100644
index 0000000..60f60d8
--- /dev/null
+++ b/python/src/AjouSlackConsumer.py
@@ -0,0 +1,89 @@
+import json
+import time
+
+import os
+
+from config import Config
+from confluent_kafka import Consumer, KafkaError
+from slack import WebClient
+from slack.errors import SlackApiError
+
+
+# Bot User OAuth Access Token
+token = os.environ["SLACK_BOT_TOKEN"]
+
+sc = WebClient(token)
+
+# Set 'auto.offset.reset': 'smallest' if you want to consume all messages
+# from the beginning of the topic
+settings = {
+    "bootstrap.servers": Config.MY_SERVER,
+    "group.id": "ajou-notify",
+    "default.topic.config": {"auto.offset.reset": "largest"},
+}
+c = Consumer(settings)
+
+# Topic = "AJOU-NOTIFY
+c.subscribe([Config.AJOU_TOPIC_ID])
+
+try:
+    while True:
+        msg = c.poll(0.1)
+        time.sleep(5)
+        if msg is None:
+            continue
+        elif not msg.error():
+            print("Received a message: {0}".format(msg.value()))
+            if msg.value() is None:
+                continue
+
+            try:
+                app_msg = json.loads(msg.value().decode())
+            except:
+                app_msg = json.loads(msg.value())
+
+            try:
+                title = app_msg["TITLE"]
+                date = app_msg["DATE"]
+                href = app_msg["LINK"]
+                writer = app_msg["WRITER"]
+
+                channel = "아주대"
+                # TODO: 학사면 좀 더 중요하게?
+                text = ":star: `%s` 새로운 공지!\n>%s: %s\n>링크: <%s|공지 확인하기>" % (
+                    date,
+                    writer,
+                    title,
+                    href,
+                )
+                print('\nSending message "%s" to channel %s' % (text, channel))
+            except SlackApiError as e:
+                print("Failed to get channel/text from message.")
+                print(e.response["error"])
+                channel = "kafka"
+                text = msg.value()
+
+            try:
+                sc_response = sc.chat_postMessage(
+                    channel=channel, text=text, as_user=True, username="아주대 공지 봇"
+                )  # as_user은 new slack app에서 작동 안 함
+
+            except SlackApiError as e:
+                assert e.response["ok"] is False
+                print("\t** FAILED: %s" % e.response["error"])
+        elif msg.error().code() == KafkaError._PARTITION_EOF:
+            print(
+                "End of partition reached {0}/{1}".format(msg.topic(), msg.partition())
+            )
+        else:
+            print("Error occured: {0}".format(msg.error().str()))
+
+except Exception as e:
+    print(type(e))
+    print(dir(e))
+
+except KeyboardInterrupt:
+    print("Pressed CTRL+C...")
+
+finally:
+    c.close()
diff --git a/python/src/AjouSlackProducer.py b/python/src/AjouSlackProducer.py
new file mode 100644
index 0000000..e55943b
--- /dev/null
+++ b/python/src/AjouSlackProducer.py
@@ -0,0 +1,157 @@
+import datetime
+import json
+import os
+import time
+from contextlib import contextmanager
+from pathlib import Path
+
+import requests
+from bs4 import BeautifulSoup
+from config import Config
+from confluent_kafka import Producer
+from slack import WebClient
+from slack.errors import SlackApiError
+
+# Producer callback function
+def acked(err, msg):
+    if err is not None:
+        print("Failed to deliver message: {0}: {1}".format(msg.value(), err.str()))
+    else:
+        print("Message produced: {0}".format(msg.value()))  # binary
+
+
+# Make data into dictionary format
+def makeJson(postId, postTitle, postDate, postLink, postWriter):
+    return {
+        postId: {
+            "TITLE": postTitle,
+            "DATE": postDate,
+            "LINK": ADDRESS + postLink,
+            "WRITER": postWriter,
+        }
+    }
+
+
+# Ajou notices parser
+def parser():
+    req = requests.get(f"{ADDRESS}?mode=list&&articleLimit=10&article.offset=0")
+    req.encoding = "utf-8"
+    html = req.text
+    soup = BeautifulSoup(html, "html.parser")
+    ids = soup.select("table > tbody > tr > td.b-num-box")
+    posts = soup.select("table > tbody > tr > td.b-td-left > div > a")
+    dates = soup.select("table > tbody > tr > td.b-td-left > div > div > span.b-date")
+    writers = soup.select(
+        "table > tbody > tr > td.b-td-left > div > div.b-m-con > span.b-writer"
+    )
+    return ids, posts, dates, writers
+
+
+ADDRESS = "https://www.ajou.ac.kr/kr/ajou/notice.do"
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+JSON_PATH = os.path.join(BASE_DIR, "already_read.json")
+LENGTH = 10
+PRODUCED = 0
+
+# Bot User OAuth Access Token
+# used scopes: channels:history, channels:read, chat:write, im:history, mpim:history, users:read
+token = os.environ["SLACK_BOT_TOKEN"]
+
+read = None
+# 공지 Parser
+if not Path(JSON_PATH).is_file():  # 파일 없으면 기본 형식 만듬
+    base_data = {"POSTS": {}, "LAST_PARSED": "1972-12-01 07:00:00.000000"}
+
+    with open(JSON_PATH, "a+") as f:
+        f.write(json.dumps(base_data))
+
+# json read
+with open(JSON_PATH, "r+") as f_read:
+    read = json.load(f_read)
+
+# Set last parsed time to rest 1 hour well
+LAST_PARSED = datetime.datetime.strptime(read["LAST_PARSED"], "%Y-%m-%d %H:%M:%S.%f")
+
+# init parser
+ids, posts, dates, writers = parser()
+
+# Slack API 초기화
+sc = WebClient(token)
+channel = "C01G2CR5MEE"  # 아주대
+
+# Kafka Producer 만들기  "localhost:9092"
+settings = {"bootstrap.servers": Config.MY_SERVER}
+p = Producer(settings)
+
+try:
+    while True:  # 1시간마다 무한 반복
+        PRODUCED = 0
+        LAST_PARSED = datetime.datetime.strptime(
+            read["LAST_PARSED"], "%Y-%m-%d %H:%M:%S.%f"
+        )
+
+        try:
+            now = datetime.datetime.now()
+            diff = (now - LAST_PARSED).seconds
+            print("Last parsing:", LAST_PARSED)
+            if diff / 3600 < 1:  # 업데이트 후 1시간이 안 지났음, 대기
+                print(f"Wait for {3600 - diff} seconds to sync new posts.")
+                time.sleep(3600 - diff)
+
+            read["LAST_PARSED"] = now.strftime("%Y-%m-%d %H:%M:%S.%f")
+
+            print("Trying to parse new posts...")
+            ids, posts, dates, writers = parser()  # 다시 파싱
+            for i in range(LENGTH):
+                postId = ids[i].text.strip()
+                postLink = posts[i].get("href")
+                postTitle = posts[i].text.strip()
+                # postTitle = posts[i].get("title")
+                postDate = dates[i].text.strip()
+                postWriter = writers[i].text
+
+                data = makeJson(postId, postTitle, postDate, postLink, postWriter)
+                # {'10000': {'TITLE': '설문조사', 'DATE': '20.12.04', 'LINK': 'https', 'WRITER': '입학처'}}
+
+                if postId not in read["POSTS"]:
+                    print("Sending a new post...:", postId)
+                    read["POSTS"].update(data)
+
+                    PRODUCED += 1
+                    p.produce(
+                        Config.AJOU_TOPIC_ID,
+                        value=json.dumps(data[postId]),
+                        callback=acked,
+                    )
+                    p.poll(0.5)  # 데이터 Kafka에게 전송
+                else:
+                    continue
+            if PRODUCED:
+                print(f"Sent {PRODUCED} posts...")
+            else:
+                print("No new posts yet...")
+
+        except SlackApiError as e:
+            assert e.response["ok"] is False
+            print("\t** FAILED: %s" % e.response["error"])
+
+        with open(JSON_PATH, "w+") as f:
+            f.write(json.dumps(read))
+        with open(JSON_PATH, "r+") as f:
+            read = json.load(f)
+
+        print("Resting 1 hour...")
+
+        time.sleep(3600)
+
+
+except Exception as e:
+    print(type(e))
+    print(dir(e))
+
+except KeyboardInterrupt:
+    print("Pressed CTRL+C...")
+
+finally:
+    print("Exiting...")
+    p.flush(100)
diff --git a/python/src/SlackKafkaConsumer.py b/python/src/SlackKafkaConsumer.py
index 61eb482..74981df 100644
--- a/python/src/SlackKafkaConsumer.py
+++ b/python/src/SlackKafkaConsumer.py
@@ -1,6 +1,9 @@
 import json
 import time
 
+
+import os
+
 from confluent_kafka import Consumer, KafkaError
 from slack import WebClient
 from slack.errors import SlackApiError
@@ -8,7 +11,7 @@ from slack.errors import SlackApiError
 
 # Bot User OAuth Access Token
 # Scope = chat:write
-token = ""
+token = os.environ["SLACK_BOT_TOKEN"]
 
 sc = WebClient(token)
 
@@ -26,7 +29,7 @@ c.subscribe(["SLACK-KAFKA"])
 
 try:
     while True:
-        msg = c.poll(0.1)
+        msg = c.poll(0.1)  # read data
         time.sleep(5)
         if msg is None:
             continue
diff --git a/python/src/SlackKafkaProducer.py b/python/src/SlackKafkaProducer.py
index ddd438f..8ec7f86 100644
--- a/python/src/SlackKafkaProducer.py
+++ b/python/src/SlackKafkaProducer.py
@@ -1,6 +1,8 @@
 import json
 import time
 
+import os
+
 from config import Config
 from confluent_kafka import Producer
 from slack import WebClient
@@ -9,7 +11,7 @@ from slack.errors import SlackApiError
 
 # Bot User OAuth Access Token
 # used scopes: channels:history, channels:read, chat:write, im:history, mpim:history, users:read
-token = ""
+token = os.environ["SLACK_BOT_TOKEN"]
 
 # Slack API 초기화
 sc = WebClient(token)
diff --git a/python/src/config.py b/python/src/config.py
index 9df9e10..f4648f5 100644
--- a/python/src/config.py
+++ b/python/src/config.py
@@ -2,6 +2,7 @@ class Config:
     MY_SERVER = "localhost:9092"
     TOPIC_ID = "first-topic"
     SLACK_TOPID_ID = "SLACK-KAFKA"
+    AJOU_TOPIC_ID = "AJOU-NOTIFY"
     GROUP_ID = "group-one"
 
     CLIENT_ID = "client-1"
-- 
GitLab