├── .gitignore
├── Procfile
├── README.md
├── requirements.txt
└── run.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
--------------------------------------------------------------------------------
/Procfile:
--------------------------------------------------------------------------------
1 | web: python run.py
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 安全回報統計小幫手
2 | ### 接收群族內各用戶回報特定格式的訊息,統計回報完成的用戶,整理成照號排列的一則訊息。
3 |
4 | * 用途
5 | 當兵男孩一定都會遇到放假時,長官需要掌握你各位的安全狀態,
6 | 都會要求Line小群組內各個逐一回報安全狀態後統整回報Line大群組,此時負責複製的組員一定很頭大吧,
7 | 要複製很多用戶的訊息,還要按照號碼排列好後回報。
8 | 此時只要把這個Line機器人加入你們小群組,他就開始幫你們接收回報資訊後,輸出整理成一則訊息,
9 | 你只要按一下複製、回報、開心完成回報呢了!
10 |
11 | * demo使用教學
12 |
13 |
14 | 先加入LineBot帳號
15 |
16 | 可以點這裡加入:
17 |
18 | 
19 |
20 | 或是使用QRcode加入:
21 |
22 |
23 |
24 | 在聊天室輸入`使用說明`可以得到:
25 |
26 | 
27 |
28 | 本機器人吃的回報格式可以在聊天室輸入`格式`可以得到:
29 |
30 | 
31 |
32 | 機器人可記錄的格式有兩種:
33 |
34 | **#1 使用預設格式:**
35 |
36 | 
37 |
38 | **#2 使用自訂格式:**
39 |
40 | 
41 |
42 | 你可以在聊天室任意過程中輸入`回報統計`,來查看目前有誰回報成功:
43 |
44 | 
45 |
46 | 最後可以輸入`輸出回報`來得到整理後的訊息
47 | (**呼叫此步驟後會同時清除已記憶的回報資訊**)
48 |
49 | 
50 |
51 | 遇到未知錯誤可以輸入`清空`來重置資料
52 | (**呼叫此步驟後會同時清除已記憶的回報資訊**)
53 |
54 | 
55 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | line-bot-sdk==1.8.0
2 | flask==1.0.2
--------------------------------------------------------------------------------
/run.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, request, abort
2 | from linebot import LineBotApi, WebhookHandler
3 | from linebot.exceptions import InvalidSignatureError
4 | from linebot.models import *
5 | import sys
6 | import os
7 |
8 | app = Flask(__name__)
9 |
10 | # Channel Access Token
11 | line_bot_api = LineBotApi('Channel Access Token')
12 | # Channel Secret
13 | handler = WebhookHandler('Channel Secret')
14 |
15 | # 監聽所有來自 /callback 的 Post Request
16 | @app.route("/callback", methods=['POST'])
17 | def callback():
18 | # get X-Line-Signature header value
19 | signature = request.headers['X-Line-Signature']
20 | # get request body as text
21 | body = request.get_data(as_text=True)
22 | app.logger.info("Request body: " + body)
23 | # handle webhook body
24 | try:
25 | handler.handle(body, signature)
26 | except InvalidSignatureError:
27 | abort(400)
28 | return 'OK'
29 |
30 | # Added manual format -Garrett, 2021.05.10
31 | def msg_manual_report(user_msg, groupID, userName):
32 | user_msg = user_msg.replace('自訂回報','').strip()
33 | ID = str(userName)
34 | reportData[groupID][ID] = user_msg
35 | tmp_str = str(ID)+',回報成功。'
36 | return tmp_str
37 |
38 | def msg_report(user_msg, groupID):
39 | try:
40 | if ( # 檢查資料是否有填,字數注意有換行符
41 | len(user_msg.split('姓名')[-1].split('學號')[0])<3 and
42 | len(user_msg.split("學號")[-1].split('手機')[0])<3 and
43 | len(user_msg.split('手機')[-1].split('地點')[0])<12 and
44 | len(user_msg.split('地點')[-1].split('收假方式')[0])<3
45 | ):
46 | raise Exception
47 | # 得到學號
48 | ID = user_msg.split("學號")[-1].split('手機')[0][1:]
49 | # 直接完整save學號 -Garrett, 2021.01.28
50 | ID = str(int(ID)) #先數值再字串,避免換行困擾
51 | # 學號不再限定只有5碼 -Garrett, 2021.01.28
52 | #if len(ID)==6:
53 | # ID = int(ID[-4:])
54 | #elif len(ID)<=4:
55 | # ID = int(ID)
56 | except Exception:
57 | tmp_str = '姓名、學號、手機、地點,其中一項未填。'
58 | else:
59 | reportData[groupID][ID] = user_msg
60 | tmp_str = str(ID)+'號弟兄,回報成功。'
61 | return tmp_str
62 |
63 |
64 | def msg_readme():
65 | tmp_str = (
66 | '回報格式有兩種方法\n'
67 | '[1]固定格式。\n'
68 | '----------\n'
69 | '姓名:\n'
70 | '學號:\n'
71 | '手機:\n'
72 | '地點:\n'
73 | '收假方式:\n'
74 | '----------\n'
75 | '\n'
76 | '[2]開頭帶有自訂回報\n'
77 | '後帶的訊息皆會直接紀錄\n'
78 | '----------\n'
79 | '自訂回報\n'
80 | '王小明範例訊息\n'
81 | '----------\n'
82 | '\n'
83 | '指令\n'
84 | '----------\n'
85 | '•格式\n'
86 | '->預設格式範例。\n'
87 | '•回報統計\n'
88 | '->顯示完成回報的號碼。\n'
89 | '•輸出回報\n'
90 | '->貼出所有回報,並清空回報紀錄。\n'
91 | '•清空\n'
92 | '->可手動清空Data,除錯用。\n'
93 | '----------\n'
94 | '效果可參閱此說明\n'
95 | 'https://github.com/GarrettTW/linebot_reportmessage/blob/master/README.md'
96 | )
97 | return tmp_str
98 |
99 | def msg_cnt(groupID):
100 | tmp_str = ''
101 | try:
102 | tmp_str = (
103 | '完成回報的號碼有:\n'
104 | +str([number for number in sorted(reportData[groupID].keys())]).strip('[]')
105 | )
106 | except BaseException as err:
107 | tmp_str = '錯誤原因: '+str(err)
108 | return tmp_str
109 |
110 | def msg_output(groupID):
111 | try:
112 | tmp_str = ''
113 | for data in [reportData[groupID][number] for number in sorted(reportData[groupID].keys())]:
114 | tmp_str = tmp_str + data +'\n\n'
115 | except BaseException as err:
116 | tmp_str = '錯誤原因: '+str(err)
117 | else:
118 | reportData[groupID].clear()
119 | return tmp_str
120 | def msg_format():
121 | tmp_str = '姓名:\n學號:\n手機:\n地點:\n收假方式:'
122 | return tmp_str
123 |
124 | def msg_clear(groupID):
125 | reportData[groupID].clear()
126 | tmp_str = '資料已重置!'
127 | return tmp_str
128 |
129 | # 處理訊息
130 | @handler.add(MessageEvent, message=TextMessage)
131 | def handle_message(event):
132 | # 各群組的資訊互相獨立
133 | try:
134 | groupID = event.source.group_id
135 | except: # 此機器人設計給群組回報,單兵不可直接一對一回報給機器人
136 | message = TextSendMessage(text='我只接收群組內訊息,請先把我邀請到群組!')
137 | line_bot_api.reply_message(event.reply_token, message)
138 | else:
139 | userID = event.source.user_id
140 |
141 | g_profile = line_bot_api.get_group_summary(groupID)
142 | groupName = g_profile.group_name
143 |
144 | u_profile = line_bot_api.get_group_member_profile(groupID,userID)
145 | userName = u_profile.display_name
146 | userName = str(userName)
147 |
148 | if not reportData.get(groupID): # 如果此群組為新加入,會創立一個新的儲存區
149 | reportData[groupID]={}
150 | LineMessage = ''
151 | receivedmsg = event.message.text
152 |
153 | if '姓名' in receivedmsg and '學號' in receivedmsg and '手機' in receivedmsg:
154 | LineMessage = msg_report(receivedmsg,groupID)
155 | elif '自訂回報' in receivedmsg[:4]:
156 | LineMessage = msg_manual_report(receivedmsg,groupID,userName)
157 | elif '使用說明' in receivedmsg and len(receivedmsg)==4:
158 | LineMessage = msg_readme()
159 | elif '回報統計' in receivedmsg and len(receivedmsg)==4:
160 | LineMessage = msg_cnt(groupID)
161 | elif '格式' in receivedmsg and len(receivedmsg)==2:
162 | LineMessage = msg_format()
163 | elif '輸出回報' in receivedmsg and len(receivedmsg)==4:
164 | LineMessage = msg_output(groupID)
165 | # for Error Debug, Empty all data -Garrett, 2021.01.27
166 | elif '清空' in receivedmsg and len(receivedmsg)==2:
167 | LineMessage = msg_clear(groupID)
168 |
169 | if LineMessage :
170 | message = TextSendMessage(text=LineMessage)
171 | line_bot_api.reply_message(event.reply_token, message)
172 |
173 | if __name__ == "__main__":
174 | global reportData
175 | reportData = {}
176 | port = int(os.environ.get('PORT', 5000))
177 | app.run(host='0.0.0.0', port=port)
178 |
--------------------------------------------------------------------------------