add botOwnerList.

allow botOwner to force update RSS
This commit is contained in:
2021-01-18 21:19:53 +08:00
parent a1b02cf3cd
commit f9f587f680
8 changed files with 106 additions and 9 deletions

View File

@@ -23,6 +23,7 @@ rssbotLib=rssbot.dll
- [rssbotLib](#rssbotlib)
- [databaseLocation](#databaselocation)
- [retryTTL](#retryttl)
- [botOwnerList](#botownerlist)
### token
必填参数。Telegram Bot API Token。向[@BotFather](https://t.me/BotFather)请求新建Bot,即可得到。
### maxCount
@@ -47,3 +48,5 @@ rssbotLib=rssbot.dll
可选参数。数据库位置。默认值为`data.db`
### retryTTL
可选参数。RSS更新发生错误后,再次更新的间隔时间。默认值为`30`。单位为分。
### botOwnerList
可选参数。具有特殊权限的用户ID,这些用户可以使用部分特殊功能。可以使用`,`分隔两个不同的ID。

View File

@@ -136,5 +136,8 @@ class RSSEntry:
self.lasterrortime = None
if data is not None and data[5] is not None:
self.lasterrortime = data[5]
self.forceupdate = None
if data is not None and data[6] is not None:
self.forceupdate = data[6]
self.chatList = []
self.hashList = HashEntries(maxCount)

38
botOwner.py Normal file
View File

@@ -0,0 +1,38 @@
# (C) 2021 lifegpc
# This file is part of rssbot.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from re import search
from typing import List
class BotOwnerList:
def __init__(self, m, s: str = None):
from rssbot import main
self._main: main = m
self.__list = []
if s is not None:
l = s.split(',')
for i in l:
if search(r'^[\+-]?[0-9]+$', i) is not None:
self.__list.append(int(i))
def getOwnerList(self) -> List[int]:
r = []
for i in self.__list:
r.append(i)
return r
def isOwner(self, chatId: int) -> bool:
return chatId in self.__list

View File

@@ -50,6 +50,11 @@ class database:
if v == [1, 0, 0, 0]:
self._db.execute('ALTER TABLE RSSList ADD lasterrortime INT;')
self._db.commit()
if v < [1, 0, 0, 2]:
self._db.execute(
'ALTER TABLE RSSList ADD forceupdate BOOLEAN;')
self._db.execute('UPDATE RSSList SET forceupdate=false;')
self._db.commit()
self.__write_version()
return True
@@ -70,6 +75,7 @@ interval INT,
lastupdatetime INT,
id TEXT,
lasterrortime INT,
forceupdate BOOLEAN,
PRIMARY KEY (id)
);''')
if 'chatList' not in self._exist_tables:
@@ -95,7 +101,7 @@ PRIMARY KEY (hash)
self._db.commit()
def __init__(self, m, loc: str):
self._version = [1, 0, 0, 1]
self._version = [1, 0, 0, 2]
self._value_lock = Lock()
self._db = sqlite3.connect(loc, check_same_thread=False)
ok = self.__check_database()
@@ -139,7 +145,7 @@ PRIMARY KEY (hash)
f"UPDATE RSSList SET title='{dealtext(title)}', interval={ttl if ttl is not None else 'null'} WHERE id='{hashd}'")
else:
self._db.execute(
f"INSERT INTO RSSList VALUES ('{dealtext(title)}', '{dealtext(url)}', {ttl if ttl is not None else 'null'}, {int(time())}, '{hashd}', null)")
f"INSERT INTO RSSList VALUES ('{dealtext(title)}', '{dealtext(url)}', {ttl if ttl is not None else 'null'}, {int(time())}, '{hashd}', null, false)")
cur = self._db.execute(
f'SELECT * FROM chatList WHERE id="{hashd}" AND chatId={chatId}')
has_data2 = False
@@ -193,11 +199,11 @@ PRIMARY KEY (hash)
def getRSSListByChatId(self, chatId: int) -> List[RSSEntry]:
with self._value_lock:
cur = self._db.execute(
f"SELECT RSSList.title, RSSList.url, RSSList.interval, RSSList.lastupdatetime, RSSList.id, RSSList.lasterrortime, chatList.config FROM RSSList, chatList WHERE chatList.chatId = {chatId} AND RSSList.id = chatList.id ORDER BY title")
f"SELECT RSSList.title, RSSList.url, RSSList.interval, RSSList.lastupdatetime, RSSList.id, RSSList.lasterrortime, RSSList.forceupdate, chatList.config FROM RSSList, chatList WHERE chatList.chatId = {chatId} AND RSSList.id = chatList.id ORDER BY title")
RSSEntries = []
for i in cur:
rssEntry = RSSEntry(i, self._main._setting._maxCount)
rssEntry.chatList.append(ChatEntry((chatId, i[4], i[6])))
rssEntry.chatList.append(ChatEntry((chatId, i[4], i[7])))
RSSEntries.append(rssEntry)
return RSSEntries
@@ -222,6 +228,25 @@ PRIMARY KEY (hash)
except:
return False
def setRSSForceUpdate(self, url: str, forceupdate: bool) -> bool:
with self._value_lock:
try:
hashd = sha256WithBase64(url)
cur = self._db.execute(
f'SELECT * FROM RSSList WHERE id="{hashd}"')
has_data = False
for i in cur: # pylint: disable=unused-variable
has_data = True
break
if not has_data:
return False
self._db.execute(
f"UPDATE RSSList SET forceupdate={'true' if forceupdate else 'false'} WHERE id='{hashd}'")
self._db.commit()
return True
except:
return False
def setUserStatus(self, userId: int, status: userStatus = userStatus.normalStatus, hashd: str = '') -> bool:
with self._value_lock:
try:
@@ -296,6 +321,7 @@ PRIMARY KEY (hash)
self._db.execute(
f"INSERT INTO hashList VALUES ('{v.id}', '{v.hash}', {v.time})")
self._db.commit()
return True
except:
return False
@@ -314,5 +340,6 @@ PRIMARY KEY (hash)
self._db.execute(
f"UPDATE RSSList SET lasterrortime={lasterrortime} WHERE id='{hashd}'")
self._db.commit()
return True
except:
return False

View File

@@ -15,10 +15,13 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import List
from getopt import getopt
from botOwner import BotOwnerList
class settings:
def __init__(self, fn: str = None):
def __init__(self, m, fn: str = None):
from rssbot import main
self._main: main = m
if fn is not None:
self.parse(fn)
@@ -46,7 +49,10 @@ class settings:
d['sendFileURLScheme'])) if 'sendFileURLScheme' in d and d['sendFileURLScheme'].isnumeric() else False
self._rssbotLib = d['rssbotLib'] if 'rssbotLib' in d and d['rssbotLib'] != '' else None
self._databaseLocation = d['databaseLocation'] if 'databaseLocation' in d and d['databaseLocation'] != '' else 'data.db'
self._retryTTL = int(d['retryTTL']) if 'retryTTL' in d and d['retryTTL'].isnumeric() and int(d['retryTTL']) > 0 else 30
self._retryTTL = int(d['retryTTL']) if 'retryTTL' in d and d['retryTTL'].isnumeric(
) and int(d['retryTTL']) > 0 else 30
self._botOwnerList = BotOwnerList(
self._main, d['botOwnerList']) if 'botOwnerList' in d and d['botOwnerList'] != '' else BotOwnerList(self._main)
class commandline:

View File

@@ -420,7 +420,7 @@ class main:
self._upi = i['update_id'] + 1
def start(self):
self._setting = settings('settings.txt')
self._setting = settings(self, 'settings.txt')
if self._setting._token is None:
print('没有机器人token')
return -1
@@ -1060,7 +1060,7 @@ class callbackQueryHandle(Thread):
di['text'] = getTextContentForRSSInList(rssList[ind])
di['parse_mode'] = 'HTML'
di['reply_markup'] = getInlineKeyBoardForRSSInList(
chatId, rssList[ind], ind)
chatId, rssList[ind], ind, self._main._setting._botOwnerList)
self._main._request("editMessageText", "post", json=di)
self.answer()
return
@@ -1156,6 +1156,15 @@ class callbackQueryHandle(Thread):
chatId, rssList[ind], ind)
self._main._request("editMessageText", "post", json=di)
return
elif self._inlineKeyBoardForRSSListCommand == InlineKeyBoardForRSSList.ForceUpdate:
rssList = self._main._db.getRSSListByChatId(chatId)
ind = int(self._inputList[3])
ind = max(min(ind, len(rssList)), 0)
if self._main._db.setRSSForceUpdate(rssList[ind].url, True):
self.answer('已发送强制更新请求。')
else:
self.answer('发送强制更新请求失败。')
return
else:
self.answer('未知的按钮。')
return

View File

@@ -69,6 +69,8 @@ class RSSCheckerThread(Thread):
except:
print(format_exc())
self._main._db.updateRSSWithError(rss.url, int(time()))
if rss.forceupdate:
self._main._db.setRSSForceUpdate(rss.url, False)
if self._main._commandLine._rebuildHashlist and self._main._commandLine._exitAfterRebuild:
_exit(0)
self._main._commandLine._rebuildHashlist = False
@@ -80,6 +82,8 @@ class RSSCheckerThread(Thread):
self._main: main = m
def __needUpdate(self, rss: RSSEntry):
if rss.forceupdate:
return True
if rss.lasterrortime is not None and rss.lasterrortime >= rss.lastupdatetime:
return True if int(time()) > rss.lasterrortime + self._main._setting._retryTTL * 60 else False
if rss.lastupdatetime is None:

View File

@@ -18,6 +18,7 @@ from typing import List
from enum import Enum, unique
from math import ceil, floor
from textc import textc, timeToStr
from botOwner import BotOwnerList
@unique
@@ -39,6 +40,7 @@ class InlineKeyBoardForRSSList(Enum):
ShowContentTitle = 14
ShowContent = 15
SendMedia = 16
ForceUpdate = 17
def getTextContentForRSSInList(rssEntry: RSSEntry) -> str:
@@ -113,13 +115,18 @@ def getInlineKeyBoardForRSSList(chatId: int, RSSEntries: List[RSSEntry], page: i
return {'inline_keyboard': d}
def getInlineKeyBoardForRSSInList(chatId: int, rssEntry: RSSEntry, index: int) -> dict:
def getInlineKeyBoardForRSSInList(chatId: int, rssEntry: RSSEntry, index: int, botOwnerList: BotOwnerList = None) -> dict:
d = []
i = -1
d.append([])
i = i + 1
d[i].append(
{'text': '取消订阅', 'callback_data': f'1,{chatId},{InlineKeyBoardForRSSList.Unsubscribe.value},{index}'})
if botOwnerList is not None and botOwnerList.isOwner(chatId):
d.append([])
i = i + 1
d[i].append(
{'text': '强制更新', 'callback_data': f'1,{chatId},{InlineKeyBoardForRSSList.ForceUpdate.value},{index}'})
d.append([])
i = i + 1
d[i].append(