add botOwnerList.
allow botOwner to force update RSS
This commit is contained in:
@@ -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。
|
||||
|
||||
@@ -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
38
botOwner.py
Normal 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
|
||||
35
database.py
35
database.py
@@ -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
|
||||
|
||||
10
readset.py
10
readset.py
@@ -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:
|
||||
|
||||
13
rssbot.py
13
rssbot.py
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user