This repository has been archived by the owner on Jan 20, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
group_reposter_bot.py
279 lines (223 loc) · 11.7 KB
/
group_reposter_bot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#!/usr/bin/env python3
"""
Author:hms5232
Repo:https://github.com/hms5232/NCNU-etutor-reposter-telegram-bot
Bug:https://github.com/hms5232/NCNU-etutor-reposter-telegram-bot/issues
"""
from telegram.ext import Updater, CommandHandler
from configparser import ConfigParser
import requests
import time
import threading
import os
import datetime
# 設定一些個人的環境變數
env = ConfigParser()
env.read('config.ini')
# If you don't want use config.ini, please edit following variables for your environment.
telegram_bot_token = env.get('reposter', 'telegram_bot_token')
fb_token = env['reposter']['fb_token']
fb_group_id = env['reposter']['fb_group_id']
telegram_group_id = env['reposter']['telegram_group_id']
telegram_channel_id = env['reposter']['telegram_channel_id']
listen_status = True # 機器人是否監聽新貼文的狀態
"""
尚未執行機器人之前,可傳送訊息給機器人後至下列網址查看:
https://api.telegram.org/bot{$token}/getUpdates
"""
updater = Updater(token=telegram_bot_token) # 呼叫 bot 用
"""
對應指令的函數們
@param bot: 機器人預設值一定要,如果沒有給的話,你的機器人不會回覆
@param update: Telegram update資訊
"""
# 歡迎訊息
def welcome(bot, update):
chat_id = update.message.from_user.id
about_bot = ''
about_bot = about_bot + '本機器人由 [hms5232](https://github.com/hms5232) 開發\n'
about_bot = about_bot + '採用 [Apache許可證](https://github.com/hms5232/NCNU-etutor-reposter-telegram-bot/blob/master/LICENSE)\n'
about_bot = about_bot + '原始碼公開於 [Github](https://github.com/hms5232/NCNU-etutor-reposter-telegram-bot)\n'
about_bot = about_bot + 'bug 回報及建議請[往這裡走](https://github.com/hms5232/NCNU-etutor-reposter-telegram-bot/issues)'
bot.send_message(chat_id, about_bot, parse_mode='Markdown')
# 顯示使用者資訊
def show_user_info(bot, update):
user_info = ''
user_info = user_info + '發送人 first name:{}\n'.format(update.message.from_user.first_name)
user_info = user_info + '發送人 last name:{}\n'.format(update.message.from_user.last_name)
user_info = user_info + '發送人 full name:{}\n'.format(update.message.from_user.full_name)
user_info = user_info + '發送人 username:{}\n'.format(update.message.from_user.username)
user_info = user_info + '發送人 id:{}\n'.format(update.message.from_user.id)
user_info = user_info + 'message_id:{}\n'.format(update.message.message_id)
user_info = user_info + '所在的聊天室 id:{}\n'.format(update.message.chat.id)
user_info = user_info + '所在的聊天室 type:{}\n'.format(update.message.chat.type)
user_info = user_info + '訊息內容:{}\n'.format(update.message.text)
update.message.reply_text(user_info, disable_notification="True")
# TODO: 顯示最新幾篇貼文的資訊
def show_latest_posts(bot, update):
# https://www.facebook.com/groups/{社團ID}/permalink/{貼文ID}/
pass
def hello(bot, update):
# 兩種方法傳送訊息予使用者
update.message.reply_text('Hello world!') #方法一
bot.sendMessage(update.message.from_user.id, 'Welcome to Telegram!') # 方法二
"""
方法二的 sendMessage 是 send_message 的別名
以 python 的使用習慣,應該是後者較為符合
https://python-telegram-bot.readthedocs.io/en/stable/telegram.bot.html#telegram.Bot.send_message
"""
# 更新設定檔
def reload_config(bot, update):
# 先檢查是不是 telegram 管理員
if not is_telegram_admin(update.message.from_user.id):
# 不是管理員更新個X
bot.sendMessage(telegram_group_id, '使用者 {}(username:{})在{}嘗試操作機器人遭拒'.format(update.message.from_user.full_name, update.message.from_user.username, time.strftime("%Y/%m/%d %H:%M:%S")))
update.message.reply_text('Permission denied!')
return
new_env = ConfigParser()
new_env.read('config.ini')
global telegram_bot_token, fb_token, fb_group_id, telegram_group_id, telegram_channel_id
telegram_bot_token = new_env.get('reposter', 'telegram_bot_token')
fb_token = new_env['reposter']['fb_token']
fb_group_id = new_env['reposter']['fb_group_id']
telegram_group_id = new_env['reposter']['telegram_group_id']
telegram_channel_id = new_env['reposter']['telegram_channel_id']
update.message.reply_text('OK, config updated!')
# 確認使用者是否為指定的 telegram 管理員
def is_telegram_admin(telegram_user_id):
telegram_user_id = str(telegram_user_id) # 當前使用者 user id
env = ConfigParser()
env.read('config.ini')
telegram_admins = [str_val for str_val in env['reposter']['telegram_admin_id'].split(',')]
return telegram_user_id in telegram_admins
# 監聽社團
def listen(bot):
print("[", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "]", 'thread start')
failed_request_times = 0
while listen_status:
r = requests.get('https://graph.facebook.com/{}/feed?fields=admin_creator,created_time,id,message,message_tags,permalink_url,link,from&access_token={}'.format(fb_group_id, fb_token))
print("[", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "]", r.status_code)
if r.status_code == 200: # OK
failed_request_times = 0 # 重設歸零
find_last_post_with_tag = False
is_new_post = False
for posts in r.json()['data']:
if find_last_post_with_tag:
break
if 'message_tags' in posts: # 有 hash tag
for tags in posts['message_tags']:
if tags['id'] == '276859169113184': # FB 上對「telegram」這個 hash tag 給的 id
with open('repost.txt', 'r', encoding='UTF-8') as f:
last_record = f.read() # 紀錄上最後一篇貼文/更新的時間戳記
if last_record < posts['created_time']: # 發現貼文時間大於紀錄的時間 => 推斷是新貼文
is_new_post = True
if is_new_post:
with open('repost.txt', 'w+', encoding='UTF-8') as f:
# 轉貼前要先更新紀錄
f.write(posts['created_time'])
print("[", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "]", posts['created_time'], posts['id'])
# 轉貼
# 檢查是否為指定關鍵字,是的話則採「無聲訊息」傳送
if posts['message'].find('#Telegram') != -1: # send without sound
repost_message = posts['message'].replace('#Telegram', '') + '\n原文連結:' + posts['permalink_url'] + '\n\n\n_等待管理員增加 hashtag_'
bot.send_message(telegram_channel_id, repost_message, parse_mode='Markdown', disable_notification="True")
else:
repost_message = posts['message'].replace('#telegram', '') + '\n原文連結:' + posts['permalink_url'] + '\n\n\n_等待管理員增加 hashtag_'
bot.send_message(telegram_channel_id, repost_message, parse_mode='Markdown')
find_last_post_with_tag = True
else:
failed_request_times += 1
# 失敗超過一定次數就停止
if failed_request_times >= 5:
print("[", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "]", "Attempt failed too many times!")
bot.send_message(telegram_group_id, "Not return 200 from Facebook API too many times, bot has paused.", parse_mode='Markdown')
return
time.sleep(20)
return
# 叫機器人起來工作了
def start_work(bot, update):
# 先檢查是不是 telegram 管理員
if not is_telegram_admin(update.message.from_user.id):
# 不是管理員用個X
bot.sendMessage(telegram_group_id, '使用者 {}(username:{})在{}嘗試操作機器人遭拒'.format(update.message.from_user.full_name, update.message.from_user.username, time.strftime("%Y/%m/%d %H:%M:%S")))
update.message.reply_text('Permission denied!')
return
# 再來,檢查工作必備的東西是否都準備好了
if before_work_check():
update.message.reply_text('Init error!')
return
global listen_status, listen_group
# 檢查是否已有監聽執行緒
if listen_group.is_alive():
print('thread already exists.')
'''
https://python-telegram-bot.readthedocs.io/en/stable/telegram.message.html
雖然 reply_text 方法不能設定解析模式,但可以呼叫其他方法來指定如何解析文字
例如:reply_html(),其為 bot.send_message(update.message.chat_id, parse_mode=ParseMode.HTML, *args, **kwargs) 的縮寫
'''
update.message.reply_html('<strike>ヽ(∀゚ )人(゚∀゚)人( ゚∀)人(∀゚ )人(゚∀゚)人( ゚∀)ノ</strike>影分身術禁止!')
return
listen_status = True
listen_group = threading.Thread(target = listen, args=(bot,)) # 重新設定執行緒
if listen_status:
listen_group.start() # 開新執行緒
# 確認執行緒是不是真的開啟了
if listen_group.is_alive():
update.message.reply_text('OK, I go to work now QQ.')
else:
update.message.reply_text('Oh no, something went wrong.')
else:
update.message.reply_text('Oh no, something went wrong.')
# 機器人可以下班休息下囉,可是還是要待命(慣老闆語氣)
def unlisten(bot, update):
# 先檢查是不是 telegram 管理員
if not is_telegram_admin(update.message.from_user.id):
# 不是管理員用個X
bot.sendMessage(telegram_group_id, '使用者 {}(username:{})在{}嘗試操作機器人遭拒'.format(update.message.from_user.full_name, update.message.from_user.username, time.strftime("%Y/%m/%d %H:%M:%S")))
update.message.reply_text('Permission denied!')
return
print("[", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "]", "stop thread")
global listen_status, listen_group
listen_status = False
listen_group.join() # 關閉執行緒
print("[", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "]", "thread killed")
if not listen_status and not listen_group.is_alive():
update.message.reply_text('OK, now I get off work. YA~!')
else:
update.message.reply_text('Oh no, something went wrong.')
# 看看 bot 是否正在監聽社團貼文
def bot_work_status(bot, update):
now_status = ''
if listen_group.is_alive():
now_status = now_status + 'ξ( ✿>◡❛)▄︻▇▇〓▄︻┻┳═一監聽社團貼文中\n'
else:
now_status = now_status + '現在是手動模式(:3[__]4\n'
update.message.reply_text(now_status)
# 開始工作之前的檢查
def before_work_check():
# 檢查必要檔案是否存在
if not os.path.isfile('repost.txt'):
find_last_post_with_tag = False
with open('repost.txt', 'w', encoding='UTF-8') as nf:
if not os.path.isfile('repost.txt'):
print("[", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "]", "An error occurred when try to create reposter.txt!")
return 1
print("[", time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()), "]", "Create repost.txt")
# 建立檔案後別忘記要填入現在的時間
nf.write(datetime.datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%S+0000"))
# 檢查完成
return 0
# CommandHandler('指令', 要執行的函數),使用者輸入「/指令」
updater.dispatcher.add_handler(CommandHandler(['start', 'about'], welcome)) # 歡迎訊息 / 機器人資訊
updater.dispatcher.add_handler(CommandHandler('info', show_user_info)) # 顯示使用者資訊
#updater.dispatcher.add_handler(CommandHandler('post', post)) # TODO: 發公告
updater.dispatcher.add_handler(CommandHandler('latest', show_latest_posts)) # 顯示最新幾篇貼文
updater.dispatcher.add_handler(CommandHandler(['hello', 'hi'], hello)) # Hello World!
updater.dispatcher.add_handler(CommandHandler('reload', reload_config)) # 重新讀取設定檔
updater.dispatcher.add_handler(CommandHandler('work', start_work)) # 開始社畜生活囉
updater.dispatcher.add_handler(CommandHandler('rest', unlisten)) # 可以下班了
updater.dispatcher.add_handler(CommandHandler('status', bot_work_status)) # 看看現在 bot 有在認真看貼文嗎
listen_group = threading.Thread(target = listen) # 採用多執行緒來監聽
# 執行機器人必須要的,讓機器人運作聽命
updater.start_polling()
updater.idle()