├── .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 | ![](https://imgur.com/28owp7M.jpg) 27 | 28 | 本機器人吃的回報格式可以在聊天室輸入`格式`可以得到: 29 | 30 | ![](https://imgur.com/0uE4Ba8.jpg) 31 | 32 | 機器人可記錄的格式有兩種: 33 | 34 | **#1 使用預設格式:** 35 | 36 | ![](https://imgur.com/ZAXSMdL.jpg) 37 | 38 | **#2 使用自訂格式:** 39 | 40 | ![](https://i.imgur.com/iutYV03.png) 41 | 42 | 你可以在聊天室任意過程中輸入`回報統計`,來查看目前有誰回報成功: 43 | 44 | ![](https://imgur.com/2HmQrIE.jpg) 45 | 46 | 最後可以輸入`輸出回報`來得到整理後的訊息
47 | (**呼叫此步驟後會同時清除已記憶的回報資訊**) 48 | 49 | ![](https://imgur.com/TrXpmqg.jpg) 50 | 51 | 遇到未知錯誤可以輸入`清空`來重置資料
52 | (**呼叫此步驟後會同時清除已記憶的回報資訊**) 53 | 54 | ![](https://i.imgur.com/xhfEyzB.png) 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 | --------------------------------------------------------------------------------